| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918 |
- //-------------------------------------------------------------------------------------------------------
- // 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 "Language/AsmJs.h"
- #include "ConfigFlagsList.h"
- void EmitReference(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo);
- void EmitAssignment(ParseNode *asgnNode, ParseNode *lhs, Js::RegSlot rhsLocation, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo);
- void EmitLoad(ParseNode *rhs, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo);
- void EmitCall(ParseNodeCall* pnodeCall, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo, BOOL fReturnValue, BOOL fEvaluateComponents, Js::RegSlot overrideThisLocation = Js::Constants::NoRegister, Js::RegSlot newTargetLocation = Js::Constants::NoRegister);
- void EmitYield(Js::RegSlot inputLocation, Js::RegSlot resultLocation, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo, Js::RegSlot yieldStarIterator = Js::Constants::NoRegister);
- void EmitUseBeforeDeclaration(Symbol *sym, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo);
- void EmitUseBeforeDeclarationRuntimeError(ByteCodeGenerator *byteCodeGenerator, Js::RegSlot location);
- void VisitClearTmpRegs(ParseNode * pnode, ByteCodeGenerator * byteCodeGenerator, FuncInfo * funcInfo);
- bool CallTargetIsArray(ParseNode *pnode)
- {
- return pnode->nop == knopName && pnode->AsParseNodeName()->PropertyIdFromNameNode() == Js::PropertyIds::Array;
- }
- #define STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode) \
- if ((isTopLevel)) \
- { \
- byteCodeGenerator->StartStatement(pnode); \
- }
- #define ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode) \
- if ((isTopLevel)) \
- { \
- byteCodeGenerator->EndStatement(pnode); \
- }
- BOOL MayHaveSideEffectOnNode(ParseNode *pnode, ParseNode *pnodeSE, ByteCodeGenerator *byteCodeGenerator)
- {
- // Try to determine whether pnodeSE (SE = side effect) may kill the named var represented by pnode.
- if (pnode->nop == knopComputedName)
- {
- pnode = pnode->AsParseNodeUni()->pnode1;
- }
- if (pnode->nop != knopName)
- {
- // Only investigating named vars here.
- return false;
- }
- ArenaAllocator *alloc = byteCodeGenerator->GetAllocator();
- SList<ParseNode*> pNodeSEStack(alloc);
- pNodeSEStack.Push(pnodeSE);
- // A pnodeSE can have children that can cause a side effect on pnode. A stack is used to check
- // pnodeSE and all potential pnodeSE children that could cause a side effect on pnode. When a
- // child pnodeSE can cause a side effect on pnode, immediately return true. Otherwise continue
- // checking children of pnodeSE until none exist
- while (!pNodeSEStack.Empty())
- {
- ParseNode *currPnodeSE = pNodeSEStack.Pop();
- uint fnop = ParseNode::Grfnop(currPnodeSE->nop);
- if (fnop & fnopLeaf)
- {
- // pnodeSE is a leaf and can't kill anything.
- continue;
- }
- else if (fnop & fnopAsg)
- {
- // pnodeSE is an assignment (=, ++, +=, etc.)
- // Trying to examine the LHS of pnodeSE caused small perf regressions,
- // maybe because of code layout or some other subtle effect.
- return true;
- }
- else if (fnop & fnopUni)
- {
- // pnodeSE is a unary op, so recurse to the source (if present - e.g., [] may have no opnd).
- if (currPnodeSE->nop == knopTempRef)
- {
- continue;
- }
- else if (currPnodeSE->AsParseNodeUni()->pnode1)
- {
- pNodeSEStack.Push(currPnodeSE->AsParseNodeUni()->pnode1);
- }
- }
- else if (fnop & fnopBin)
- {
- // currPnodeSE is a binary (or ternary) op, so check sources (if present).
- pNodeSEStack.Push(currPnodeSE->AsParseNodeBin()->pnode1);
-
- if (currPnodeSE->AsParseNodeBin()->pnode2)
- {
- pNodeSEStack.Push(currPnodeSE->AsParseNodeBin()->pnode2);
- }
- }
- else if (currPnodeSE->nop == knopQmark)
- {
- ParseNodeTri * pnodeTriSE = currPnodeSE->AsParseNodeTri();
- pNodeSEStack.Push(pnodeTriSE->pnode1);
- pNodeSEStack.Push(pnodeTriSE->pnode2);
- pNodeSEStack.Push(pnodeTriSE->pnode3);
- }
- else if (currPnodeSE->nop == knopCall || currPnodeSE->nop == knopNew)
- {
- pNodeSEStack.Push(currPnodeSE->AsParseNodeCall()->pnodeTarget);
- if (currPnodeSE->AsParseNodeCall()->pnodeArgs)
- {
- pNodeSEStack.Push(currPnodeSE->AsParseNodeCall()->pnodeArgs);
- }
- }
- else if (currPnodeSE->nop == knopList)
- {
- return true;
- }
- }
- return false;
- }
- bool IsCallOfConstants(ParseNode *pnode);
- bool BlockHasOwnScope(ParseNodeBlock * pnodeBlock, ByteCodeGenerator *byteCodeGenerator);
- bool CreateNativeArrays(ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo);
- bool IsArguments(ParseNode *pnode)
- {
- for (;;)
- {
- switch (pnode->nop)
- {
- case knopName:
- return pnode->AsParseNodeName()->sym && pnode->AsParseNodeName()->sym->IsArguments();
- case knopCall:
- case knopNew:
- if (IsArguments(pnode->AsParseNodeCall()->pnodeTarget))
- {
- return true;
- }
- if (pnode->AsParseNodeCall()->pnodeArgs)
- {
- ParseNode *pnodeArg = pnode->AsParseNodeCall()->pnodeArgs;
- while (pnodeArg->nop == knopList)
- {
- if (IsArguments(pnodeArg->AsParseNodeBin()->pnode1))
- return true;
- pnodeArg = pnodeArg->AsParseNodeBin()->pnode2;
- }
- pnode = pnodeArg;
- break;
- }
- return false;
- case knopArray:
- if (pnode->AsParseNodeArrLit()->arrayOfNumbers || pnode->AsParseNodeArrLit()->count == 0)
- {
- return false;
- }
- pnode = pnode->AsParseNodeUni()->pnode1;
- break;
- case knopQmark:
- if (IsArguments(pnode->AsParseNodeTri()->pnode1) || IsArguments(pnode->AsParseNodeTri()->pnode2))
- {
- return true;
- }
- pnode = pnode->AsParseNodeTri()->pnode3;
- break;
- //
- // Cases where we don't check for "arguments" yet.
- // Assume that they might have it. Disable the optimization is such scenarios
- //
- case knopList:
- case knopObject:
- case knopVarDecl:
- case knopConstDecl:
- case knopLetDecl:
- case knopFncDecl:
- case knopClassDecl:
- case knopFor:
- case knopIf:
- case knopDoWhile:
- case knopWhile:
- case knopForIn:
- case knopForOf:
- case knopReturn:
- case knopBlock:
- case knopBreak:
- case knopContinue:
- case knopTypeof:
- case knopThrow:
- case knopWith:
- case knopFinally:
- case knopTry:
- case knopTryCatch:
- case knopTryFinally:
- case knopArrayPattern:
- case knopObjectPattern:
- case knopParamPattern:
- return true;
- default:
- {
- uint flags = ParseNode::Grfnop(pnode->nop);
- if (flags&fnopUni)
- {
- ParseNodeUni * pnodeUni = pnode->AsParseNodeUni();
- Assert(pnodeUni->pnode1);
- pnode = pnodeUni->pnode1;
- break;
- }
- else if (flags&fnopBin)
- {
- ParseNodeBin * pnodeBin = pnode->AsParseNodeBin();
- Assert(pnodeBin->pnode1 && pnodeBin->pnode2);
- if (IsArguments(pnodeBin->pnode1))
- {
- return true;
- }
- pnode = pnodeBin->pnode2;
- break;
- }
- return false;
- }
- }
- }
- }
- bool ApplyEnclosesArgs(ParseNode* fncDecl, ByteCodeGenerator* byteCodeGenerator);
- void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo, BOOL fReturnValue, bool isConstructorCall = false, ParseNode *bindPnode = nullptr, bool isTopLevel = false);
- void EmitBinaryOpnds(ParseNode *pnode1, ParseNode *pnode2, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo);
- bool IsExpressionStatement(ParseNode* stmt, const Js::ScriptContext *const scriptContext);
- void EmitInvoke(Js::RegSlot location, Js::RegSlot callObjLocation, Js::PropertyId propertyId, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo);
- void EmitInvoke(Js::RegSlot location, Js::RegSlot callObjLocation, Js::PropertyId propertyId, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo, Js::RegSlot arg1Location);
- static const Js::OpCode nopToOp[knopLim] =
- {
- #define OP(x) Br##x##_A
- #define PTNODE(nop,sn,pc,nk,grfnop,json) Js::OpCode::pc,
- #include "ptlist.h"
- };
- static const Js::OpCode nopToCMOp[knopLim] =
- {
- #define OP(x) Cm##x##_A
- #define PTNODE(nop,sn,pc,nk,grfnop,json) Js::OpCode::pc,
- #include "ptlist.h"
- };
- Js::OpCode ByteCodeGenerator::ToChkUndeclOp(Js::OpCode op) const
- {
- switch (op)
- {
- case Js::OpCode::StLocalSlot:
- return Js::OpCode::StLocalSlotChkUndecl;
- case Js::OpCode::StParamSlot:
- return Js::OpCode::StParamSlotChkUndecl;
- case Js::OpCode::StInnerSlot:
- return Js::OpCode::StInnerSlotChkUndecl;
- case Js::OpCode::StEnvSlot:
- return Js::OpCode::StEnvSlotChkUndecl;
- case Js::OpCode::StObjSlot:
- return Js::OpCode::StObjSlotChkUndecl;
- case Js::OpCode::StLocalObjSlot:
- return Js::OpCode::StLocalObjSlotChkUndecl;
- case Js::OpCode::StParamObjSlot:
- return Js::OpCode::StParamObjSlotChkUndecl;
- case Js::OpCode::StInnerObjSlot:
- return Js::OpCode::StInnerObjSlotChkUndecl;
- case Js::OpCode::StEnvObjSlot:
- return Js::OpCode::StEnvObjSlotChkUndecl;
- default:
- AssertMsg(false, "Unknown opcode for chk undecl mapping");
- return Js::OpCode::InvalidOpCode;
- }
- }
- // Tracks a register slot let/const property for the passed in debugger block/catch scope.
- // debuggerScope - The scope to add the variable to.
- // symbol - The symbol that represents the register property.
- // funcInfo - The function info used to store the property into the tracked debugger register slot list.
- // flags - The flags to assign to the property.
- // isFunctionDeclaration - Whether or not the register is a function declaration, which requires that its byte code offset be updated immediately.
- void ByteCodeGenerator::TrackRegisterPropertyForDebugger(
- Js::DebuggerScope *debuggerScope,
- Symbol *symbol,
- FuncInfo *funcInfo,
- Js::DebuggerScopePropertyFlags flags /*= Js::DebuggerScopePropertyFlags_None*/,
- bool isFunctionDeclaration /*= false*/)
- {
- Assert(debuggerScope);
- Assert(symbol);
- Assert(funcInfo);
- Js::RegSlot location = symbol->GetLocation();
- Js::DebuggerScope *correctDebuggerScope = debuggerScope;
- if (debuggerScope->scopeType != Js::DiagExtraScopesType::DiagBlockScopeDirect && debuggerScope->scopeType != Js::DiagExtraScopesType::DiagCatchScopeDirect)
- {
- // We have to get the appropriate scope and add property over there.
- // Make sure the scope is created whether we're in debug mode or not, because we
- // need the empty scopes present during reparsing for debug mode.
- correctDebuggerScope = debuggerScope->GetSiblingScope(location, Writer()->GetFunctionWrite());
- }
- if (this->ShouldTrackDebuggerMetadata() && !symbol->GetIsTrackedForDebugger())
- {
- // Only track the property if we're in debug mode since it's only needed by the debugger.
- Js::PropertyId propertyId = symbol->EnsurePosition(this);
- this->Writer()->AddPropertyToDebuggerScope(
- correctDebuggerScope,
- location,
- propertyId,
- /*shouldConsumeRegister*/ true,
- flags,
- isFunctionDeclaration);
- Js::FunctionBody *byteCodeFunction = funcInfo->GetParsedFunctionBody();
- byteCodeFunction->InsertSymbolToRegSlotList(location, propertyId, funcInfo->varRegsCount);
- symbol->SetIsTrackedForDebugger(true);
- }
- }
- void ByteCodeGenerator::TrackActivationObjectPropertyForDebugger(
- Js::DebuggerScope *debuggerScope,
- Symbol *symbol,
- Js::DebuggerScopePropertyFlags flags /*= Js::DebuggerScopePropertyFlags_None*/,
- bool isFunctionDeclaration /*= false*/)
- {
- Assert(debuggerScope);
- Assert(symbol);
- // Only need to track activation object properties in debug mode.
- if (ShouldTrackDebuggerMetadata() && !symbol->GetIsTrackedForDebugger())
- {
- Js::RegSlot location = symbol->GetLocation();
- Js::PropertyId propertyId = symbol->EnsurePosition(this);
- this->Writer()->AddPropertyToDebuggerScope(
- debuggerScope,
- location,
- propertyId,
- /*shouldConsumeRegister*/ false,
- flags,
- isFunctionDeclaration);
- symbol->SetIsTrackedForDebugger(true);
- }
- }
- void ByteCodeGenerator::TrackSlotArrayPropertyForDebugger(
- Js::DebuggerScope *debuggerScope,
- Symbol* symbol,
- Js::PropertyId propertyId,
- Js::DebuggerScopePropertyFlags flags /*= Js::DebuggerScopePropertyFlags_None*/,
- bool isFunctionDeclaration /*= false*/)
- {
- // Note: Slot array properties are tracked even in non-debug mode in order to support slot array serialization
- // of let/const variables between non-debug and debug mode (for example, when a slot array var escapes and is retrieved
- // after a debugger attach or for WWA apps). They are also needed for heap enumeration.
- Assert(debuggerScope);
- Assert(symbol);
- if (!symbol->GetIsTrackedForDebugger())
- {
- Js::RegSlot location = symbol->GetScopeSlot();
- Assert(location != Js::Constants::NoRegister);
- Assert(propertyId != Js::Constants::NoProperty);
- this->Writer()->AddPropertyToDebuggerScope(
- debuggerScope,
- location,
- propertyId,
- /*shouldConsumeRegister*/ false,
- flags,
- isFunctionDeclaration);
- symbol->SetIsTrackedForDebugger(true);
- }
- }
- // Tracks a function declaration inside a block scope for the debugger metadata's current scope (let binding).
- void ByteCodeGenerator::TrackFunctionDeclarationPropertyForDebugger(Symbol *functionDeclarationSymbol, FuncInfo *funcInfoParent)
- {
- Assert(functionDeclarationSymbol);
- Assert(funcInfoParent);
- AssertMsg(functionDeclarationSymbol->GetIsBlockVar(), "We should only track inner function let bindings for the debugger.");
- // Note: we don't have to check symbol->GetIsTrackedForDebugger, as we are not doing actual work here,
- // which is done in other Track* functions that we call.
- if (functionDeclarationSymbol->IsInSlot(this, funcInfoParent))
- {
- if (functionDeclarationSymbol->GetScope()->GetIsObject())
- {
- this->TrackActivationObjectPropertyForDebugger(
- this->Writer()->GetCurrentDebuggerScope(),
- functionDeclarationSymbol,
- Js::DebuggerScopePropertyFlags_None,
- true /*isFunctionDeclaration*/);
- }
- else
- {
- // Make sure the property has a slot. This will bump up the size of the slot array if necessary.
- // Note that slot array inner function bindings are tracked even in non-debug mode in order
- // to keep the lifetime of the closure binding that could escape around for heap enumeration.
- functionDeclarationSymbol->EnsureScopeSlot(this, funcInfoParent);
- functionDeclarationSymbol->EnsurePosition(this);
- this->TrackSlotArrayPropertyForDebugger(
- this->Writer()->GetCurrentDebuggerScope(),
- functionDeclarationSymbol,
- functionDeclarationSymbol->GetPosition(),
- Js::DebuggerScopePropertyFlags_None,
- true /*isFunctionDeclaration*/);
- }
- }
- else
- {
- this->TrackRegisterPropertyForDebugger(
- this->Writer()->GetCurrentDebuggerScope(),
- functionDeclarationSymbol,
- funcInfoParent,
- Js::DebuggerScopePropertyFlags_None,
- true /*isFunctionDeclaration*/);
- }
- }
- // Updates the byte code offset of the property with the passed in location and ID.
- // Used to track let/const variables that are in the dead zone debugger side.
- // location - The activation object, scope slot index, or register location for the property.
- // propertyId - The ID of the property to update.
- // shouldConsumeRegister - Whether or not the a register should be consumed (used for reg slot locations).
- void ByteCodeGenerator::UpdateDebuggerPropertyInitializationOffset(Js::RegSlot location, Js::PropertyId propertyId, bool shouldConsumeRegister)
- {
- Assert(this->Writer());
- Js::DebuggerScope* currentDebuggerScope = this->Writer()->GetCurrentDebuggerScope();
- Assert(currentDebuggerScope);
- if (currentDebuggerScope != nullptr)
- {
- this->Writer()->UpdateDebuggerPropertyInitializationOffset(
- currentDebuggerScope,
- location,
- propertyId,
- shouldConsumeRegister);
- }
- }
- void ByteCodeGenerator::LoadHeapArguments(FuncInfo *funcInfo)
- {
- if (funcInfo->GetHasCachedScope())
- {
- this->LoadCachedHeapArguments(funcInfo);
- }
- else
- {
- this->LoadUncachedHeapArguments(funcInfo);
- }
- }
- void GetFormalArgsArray(ByteCodeGenerator *byteCodeGenerator, FuncInfo * funcInfo, Js::PropertyIdArray *propIds)
- {
- Assert(funcInfo);
- Assert(propIds);
- Assert(byteCodeGenerator);
- bool hadDuplicates = false;
- Js::ArgSlot i = 0;
- auto processArg = [&](ParseNode *pnode)
- {
- if (pnode->IsVarLetOrConst())
- {
- Assert(i < propIds->count);
- Symbol *sym = pnode->AsParseNodeVar()->sym;
- Assert(sym);
- Js::PropertyId symPos = sym->EnsurePosition(byteCodeGenerator);
- //
- // Check if the function has any same name parameters
- // For the same name param, only the last one will be passed the correct propertyid
- // For remaining dup param names, pass Constants::NoProperty
- //
- for (Js::ArgSlot j = 0; j < i; j++)
- {
- if (propIds->elements[j] == symPos)
- {
- // Found a dup parameter name
- propIds->elements[j] = Js::Constants::NoProperty;
- hadDuplicates = true;
- break;
- }
- }
- propIds->elements[i] = symPos;
- }
- else
- {
- propIds->elements[i] = Js::Constants::NoProperty;
- }
- ++i;
- };
- MapFormals(funcInfo->root, processArg);
- propIds->hadDuplicates = hadDuplicates;
- }
- void ByteCodeGenerator::LoadUncachedHeapArguments(FuncInfo *funcInfo)
- {
- Assert(funcInfo->GetHasHeapArguments());
- Scope *scope = funcInfo->GetBodyScope();
- Assert(scope);
- Symbol *argSym = funcInfo->GetArgumentsSymbol();
- Assert(argSym && argSym->IsArguments());
- Js::RegSlot argumentsLoc = argSym->GetLocation();
- Js::OpCode opcode = !funcInfo->root->HasNonSimpleParameterList() ? Js::OpCode::LdHeapArguments : Js::OpCode::LdLetHeapArguments;
- bool hasRest = funcInfo->root->pnodeRest != nullptr;
- uint count = funcInfo->inArgsCount + (hasRest ? 1 : 0) - 1;
- if (count == 0)
- {
- // If no formals to function (only "this"), then no need to create the scope object.
- // Leave both the arguments location and the propertyIds location as null.
- Assert(funcInfo->root->pnodeParams == nullptr && !hasRest);
- }
- else if (!NeedScopeObjectForArguments(funcInfo, funcInfo->root))
- {
- // We may not need a scope object for arguments, e.g. strict mode with no eval.
- }
- else if (funcInfo->frameObjRegister != Js::Constants::NoRegister)
- {
- // Pass the frame object and ID array to the runtime, and put the resulting Arguments object
- // at the expected location.
- Js::PropertyIdArray *propIds = funcInfo->GetParsedFunctionBody()->AllocatePropertyIdArrayForFormals(UInt32Math::Mul(count, sizeof(Js::PropertyId)), count, 0);
- GetFormalArgsArray(this, funcInfo, propIds);
- }
- this->m_writer.Reg1(opcode, argumentsLoc);
- EmitLocalPropInit(argSym->GetLocation(), argSym, funcInfo);
- }
- void ByteCodeGenerator::LoadCachedHeapArguments(FuncInfo *funcInfo)
- {
- Assert(funcInfo->GetHasHeapArguments());
- Scope *scope = funcInfo->GetBodyScope();
- Assert(scope);
- Symbol *argSym = funcInfo->GetArgumentsSymbol();
- Assert(argSym && argSym->IsArguments());
- Js::RegSlot argumentsLoc = argSym->GetLocation();
- Js::OpCode op = !funcInfo->root->HasNonSimpleParameterList() ? Js::OpCode::LdHeapArgsCached : Js::OpCode::LdLetHeapArgsCached;
- this->m_writer.Reg1(op, argumentsLoc);
- EmitLocalPropInit(argumentsLoc, argSym, funcInfo);
- }
- Js::JavascriptArray* ByteCodeGenerator::BuildArrayFromStringList(ParseNode* stringNodeList, uint arrayLength, Js::ScriptContext* scriptContext)
- {
- Assert(stringNodeList);
- uint index = 0;
- Js::Var str;
- IdentPtr pid;
- Js::JavascriptArray* pArr = scriptContext->GetLibrary()->CreateArray(arrayLength);
- while (stringNodeList->nop == knopList)
- {
- Assert(stringNodeList->AsParseNodeBin()->pnode1->nop == knopStr);
- pid = stringNodeList->AsParseNodeBin()->pnode1->AsParseNodeStr()->pid;
- str = Js::JavascriptString::NewCopyBuffer(pid->Psz(), pid->Cch(), scriptContext);
- pArr->SetItemWithAttributes(index, str, PropertyEnumerable);
- stringNodeList = stringNodeList->AsParseNodeBin()->pnode2;
- index++;
- }
- Assert(stringNodeList->nop == knopStr);
- pid = stringNodeList->AsParseNodeStr()->pid;
- str = Js::JavascriptString::NewCopyBuffer(pid->Psz(), pid->Cch(), scriptContext);
- pArr->SetItemWithAttributes(index, str, PropertyEnumerable);
- return pArr;
- }
- // For now, this just assigns field ids for the current script.
- // Later, we will combine this information with the global field id map.
- // This temporary code will not work if a global member is accessed both with and without a LHS.
- void ByteCodeGenerator::AssignPropertyIds(Js::ParseableFunctionInfo* functionInfo)
- {
- globalScope->ForEachSymbol([this, functionInfo](Symbol * sym)
- {
- this->AssignPropertyId(sym, functionInfo);
- });
- }
- void ByteCodeGenerator::InitBlockScopedContent(ParseNodeBlock *pnodeBlock, Js::DebuggerScope* debuggerScope, FuncInfo *funcInfo)
- {
- Assert(pnodeBlock->nop == knopBlock);
- auto genBlockInit = [this, debuggerScope, funcInfo](ParseNode *pnode)
- {
- // Only check if the scope is valid when let/const vars are in the scope. If there are no let/const vars,
- // the debugger scope will not be created.
- AssertMsg(debuggerScope, "Missing a case of scope tracking in BeginEmitBlock.");
- FuncInfo *funcInfo = this->TopFuncInfo();
- Symbol *sym = pnode->AsParseNodeVar()->sym;
- Scope *scope = sym->GetScope();
- if (sym->GetIsGlobal())
- {
- Js::PropertyId propertyId = sym->EnsurePosition(this);
- if (this->flags & fscrEval)
- {
- AssertMsg(this->IsConsoleScopeEval(), "Let/Consts cannot be in global scope outside of console eval");
- Js::OpCode op = (sym->GetDecl()->nop == knopConstDecl) ? Js::OpCode::InitUndeclConsoleConstFld : Js::OpCode::InitUndeclConsoleLetFld;
- this->m_writer.ElementScopedU(op, funcInfo->FindOrAddReferencedPropertyId(propertyId));
- }
- else
- {
- Js::OpCode op = (sym->GetDecl()->nop == knopConstDecl) ?
- Js::OpCode::InitUndeclRootConstFld : Js::OpCode::InitUndeclRootLetFld;
- this->m_writer.ElementRootU(op, funcInfo->FindOrAddReferencedPropertyId(propertyId));
- }
- }
- else if (sym->IsInSlot(this, funcInfo) || (scope->GetIsObject() && sym->NeedsSlotAlloc(this, funcInfo)))
- {
- if (scope->GetIsObject())
- {
- Js::RegSlot scopeLocation = scope->GetLocation();
- Js::PropertyId propertyId = sym->EnsurePosition(this);
- if (scopeLocation != Js::Constants::NoRegister && scopeLocation == funcInfo->frameObjRegister)
- {
- uint cacheId = funcInfo->FindOrAddInlineCacheId(scopeLocation, propertyId, false, true);
- Js::OpCode op = (sym->GetDecl()->nop == knopConstDecl) ?
- Js::OpCode::InitUndeclLocalConstFld : Js::OpCode::InitUndeclLocalLetFld;
- this->m_writer.ElementP(op, ByteCodeGenerator::ReturnRegister, cacheId);
- }
- else
- {
- uint cacheId = funcInfo->FindOrAddInlineCacheId(funcInfo->InnerScopeToRegSlot(scope), propertyId, false, true);
- Js::OpCode op = (sym->GetDecl()->nop == knopConstDecl) ?
- Js::OpCode::InitUndeclConstFld : Js::OpCode::InitUndeclLetFld;
- this->m_writer.ElementPIndexed(op, ByteCodeGenerator::ReturnRegister, scope->GetInnerScopeIndex(), cacheId);
- }
- TrackActivationObjectPropertyForDebugger(debuggerScope, sym, pnode->nop == knopConstDecl ? Js::DebuggerScopePropertyFlags_Const : Js::DebuggerScopePropertyFlags_None);
- }
- else
- {
- Js::RegSlot tmpReg = funcInfo->AcquireTmpRegister();
- this->m_writer.Reg1(Js::OpCode::InitUndecl, tmpReg);
- this->EmitLocalPropInit(tmpReg, sym, funcInfo);
- funcInfo->ReleaseTmpRegister(tmpReg);
- // Slot array properties are tracked in non-debug mode as well because they need to stay
- // around for heap enumeration and escaping during attach/detach.
- TrackSlotArrayPropertyForDebugger(debuggerScope, sym, sym->EnsurePosition(this), pnode->nop == knopConstDecl ? Js::DebuggerScopePropertyFlags_Const : Js::DebuggerScopePropertyFlags_None);
- }
- }
- else if (!sym->GetIsModuleExportStorage())
- {
- if (sym->GetDecl()->AsParseNodeVar()->isSwitchStmtDecl)
- {
- // let/const declared in a switch is the only case of a variable that must be checked for
- // use-before-declaration dynamically within its own function.
- this->m_writer.Reg1(Js::OpCode::InitUndecl, sym->GetLocation());
- }
- // Syms that begin in register may be delay-captured. In debugger mode, such syms
- // will live only in slots, so tell the debugger to find them there.
- if (sym->NeedsSlotAlloc(this, funcInfo))
- {
- TrackSlotArrayPropertyForDebugger(debuggerScope, sym, sym->EnsurePosition(this), pnode->nop == knopConstDecl ? Js::DebuggerScopePropertyFlags_Const : Js::DebuggerScopePropertyFlags_None);
- }
- else
- {
- TrackRegisterPropertyForDebugger(debuggerScope, sym, funcInfo, pnode->nop == knopConstDecl ? Js::DebuggerScopePropertyFlags_Const : Js::DebuggerScopePropertyFlags_None);
- }
- }
- };
- IterateBlockScopedVariables(pnodeBlock, genBlockInit);
- }
- // Records the start of a debugger scope if the passed in node has any let/const variables (or is not a block node).
- // If it has no let/const variables, nullptr will be returned as no scope will be created.
- Js::DebuggerScope* ByteCodeGenerator::RecordStartScopeObject(ParseNode * pnode, Js::DiagExtraScopesType scopeType, Js::RegSlot scopeLocation /*= Js::Constants::NoRegister*/, int* index /*= nullptr*/)
- {
- Assert(pnode);
- if (pnode->nop == knopBlock && !pnode->AsParseNodeBlock()->HasBlockScopedContent())
- {
- // In order to reduce allocations now that we track debugger scopes in non-debug mode,
- // don't add a block to the chain if it has no let/const variables at all.
- return nullptr;
- }
- return this->Writer()->RecordStartScopeObject(scopeType, scopeLocation, index);
- }
- // Records the end of the current scope, but only if the current block has block scoped content.
- // Otherwise, a scope would not have been added (see ByteCodeGenerator::RecordStartScopeObject()).
- void ByteCodeGenerator::RecordEndScopeObject(ParseNode *pnodeBlock)
- {
- Assert(pnodeBlock);
- if (pnodeBlock->nop == knopBlock && !pnodeBlock->AsParseNodeBlock()->HasBlockScopedContent())
- {
- return;
- }
- this->Writer()->RecordEndScopeObject();
- }
- void BeginEmitBlock(ParseNodeBlock *pnodeBlock, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo)
- {
- Js::DebuggerScope* debuggerScope = nullptr;
- if (BlockHasOwnScope(pnodeBlock, byteCodeGenerator))
- {
- Scope *scope = pnodeBlock->scope;
- byteCodeGenerator->PushScope(scope);
- Js::RegSlot scopeLocation = scope->GetLocation();
- if (scope->GetMustInstantiate())
- {
- Assert(scopeLocation == Js::Constants::NoRegister);
- scopeLocation = funcInfo->FirstInnerScopeReg() + scope->GetInnerScopeIndex();
- if (scope->GetIsObject())
- {
- debuggerScope = byteCodeGenerator->RecordStartScopeObject(pnodeBlock, Js::DiagExtraScopesType::DiagBlockScopeInObject, scopeLocation);
- byteCodeGenerator->Writer()->Unsigned1(Js::OpCode::NewBlockScope, scope->GetInnerScopeIndex());
- }
- else
- {
- int scopeIndex = Js::DebuggerScope::InvalidScopeIndex;
- debuggerScope = byteCodeGenerator->RecordStartScopeObject(pnodeBlock, Js::DiagExtraScopesType::DiagBlockScopeInSlot, scopeLocation, &scopeIndex);
- // TODO: Handle heap enumeration
- int scopeSlotCount = scope->GetScopeSlotCount();
- byteCodeGenerator->Writer()->Num3(Js::OpCode::NewInnerScopeSlots, scope->GetInnerScopeIndex(), scopeSlotCount + Js::ScopeSlots::FirstSlotIndex, scopeIndex);
- }
- }
- else
- {
- // In the direct register access case, there is no block scope emitted but we can still track
- // the start and end offset of the block. The location registers for let/const variables will still be
- // captured along with this range in InitBlockScopedContent().
- debuggerScope = byteCodeGenerator->RecordStartScopeObject(pnodeBlock, Js::DiagExtraScopesType::DiagBlockScopeDirect);
- }
- bool const isGlobalEvalBlockScope = scope->IsGlobalEvalBlockScope();
- Js::RegSlot frameDisplayLoc = Js::Constants::NoRegister;
- Js::RegSlot tmpInnerEnvReg = Js::Constants::NoRegister;
- ParseNodePtr pnodeScope;
- for (pnodeScope = pnodeBlock->pnodeScopes; pnodeScope;)
- {
- switch (pnodeScope->nop)
- {
- case knopFncDecl:
- if (pnodeScope->AsParseNodeFnc()->IsDeclaration())
- {
- // The frameDisplayLoc register's lifetime has to be controlled by this function. We can't let
- // it be released by DefineOneFunction, because further iterations of this loop can allocate
- // temps, and we can't let frameDisplayLoc be re-purposed until this loop completes.
- // So we'll supply a temp that we allocate and release here.
- if (frameDisplayLoc == Js::Constants::NoRegister)
- {
- if (funcInfo->frameDisplayRegister != Js::Constants::NoRegister)
- {
- frameDisplayLoc = funcInfo->frameDisplayRegister;
- }
- else
- {
- frameDisplayLoc = funcInfo->GetEnvRegister();
- }
- tmpInnerEnvReg = funcInfo->AcquireTmpRegister();
- frameDisplayLoc = byteCodeGenerator->PrependLocalScopes(frameDisplayLoc, tmpInnerEnvReg, funcInfo);
- }
- byteCodeGenerator->DefineOneFunction(pnodeScope->AsParseNodeFnc(), funcInfo, true, frameDisplayLoc);
- }
- // If this is the global eval block scope, the function is actually assigned to the global
- // so we don't need to keep the registers.
- if (isGlobalEvalBlockScope)
- {
- funcInfo->ReleaseLoc(pnodeScope);
- pnodeScope->location = Js::Constants::NoRegister;
- }
- pnodeScope = pnodeScope->AsParseNodeFnc()->pnodeNext;
- break;
- case knopBlock:
- pnodeScope = pnodeScope->AsParseNodeBlock()->pnodeNext;
- break;
- case knopCatch:
- pnodeScope = pnodeScope->AsParseNodeCatch()->pnodeNext;
- break;
- case knopWith:
- pnodeScope = pnodeScope->AsParseNodeWith()->pnodeNext;
- break;
- }
- }
- if (tmpInnerEnvReg != Js::Constants::NoRegister)
- {
- funcInfo->ReleaseTmpRegister(tmpInnerEnvReg);
- }
- }
- else
- {
- Scope *scope = pnodeBlock->scope;
- if (scope)
- {
- if (scope->GetMustInstantiate())
- {
- debuggerScope = byteCodeGenerator->RecordStartScopeObject(pnodeBlock, Js::DiagExtraScopesType::DiagBlockScopeInObject);
- }
- else
- {
- debuggerScope = byteCodeGenerator->RecordStartScopeObject(pnodeBlock, Js::DiagExtraScopesType::DiagBlockScopeDirect);
- }
- }
- else
- {
- debuggerScope = byteCodeGenerator->RecordStartScopeObject(pnodeBlock, Js::DiagExtraScopesType::DiagBlockScopeInSlot);
- }
- }
- byteCodeGenerator->InitBlockScopedContent(pnodeBlock, debuggerScope, funcInfo);
- }
- void EndEmitBlock(ParseNodeBlock *pnodeBlock, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo)
- {
- if (BlockHasOwnScope(pnodeBlock, byteCodeGenerator))
- {
- Scope *scope = pnodeBlock->scope;
- Assert(scope);
- Assert(scope == byteCodeGenerator->GetCurrentScope());
- byteCodeGenerator->PopScope();
- }
- byteCodeGenerator->RecordEndScopeObject(pnodeBlock);
- }
- void CloneEmitBlock(ParseNodeBlock *pnodeBlock, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo)
- {
- if (BlockHasOwnScope(pnodeBlock, byteCodeGenerator))
- {
- // Only let variables have observable behavior when there are per iteration
- // bindings. const variables do not since they are immutable. Therefore,
- // (and the spec agrees), only create new scope clones if the loop variable
- // is a let declaration.
- bool isConst = false;
- pnodeBlock->scope->ForEachSymbolUntil([&isConst](Symbol * const sym) {
- // Exploit the fact that a for loop sxBlock can only have let and const
- // declarations, and can only have one or the other, regardless of how
- // many syms there might be. Thus only check the first sym.
- isConst = sym->GetDecl()->nop == knopConstDecl;
- return true;
- });
- if (!isConst)
- {
- Scope *scope = pnodeBlock->scope;
- Assert(scope == byteCodeGenerator->GetCurrentScope());
- if (scope->GetMustInstantiate())
- {
- Js::OpCode op = scope->GetIsObject() ? Js::OpCode::CloneBlockScope : Js::OpCode::CloneInnerScopeSlots;
- byteCodeGenerator->Writer()->Unsigned1(op, scope->GetInnerScopeIndex());
- }
- }
- }
- }
- void EmitBlock(ParseNodeBlock *pnodeBlock, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo, BOOL fReturnValue)
- {
- Assert(pnodeBlock->nop == knopBlock);
- ParseNode *pnode = pnodeBlock->pnodeStmt;
- if (pnode == nullptr)
- {
- return;
- }
- BeginEmitBlock(pnodeBlock, byteCodeGenerator, funcInfo);
- ParseNode *pnodeLastValStmt = pnodeBlock->pnodeLastValStmt;
- while (pnode->nop == knopList)
- {
- ParseNode* stmt = pnode->AsParseNodeBin()->pnode1;
- if (stmt == pnodeLastValStmt)
- {
- // This is the last guaranteed return value, so any potential return values have to be
- // copied to the return register from this point forward.
- pnodeLastValStmt = nullptr;
- }
- byteCodeGenerator->EmitTopLevelStatement(stmt, funcInfo, fReturnValue && (pnodeLastValStmt == nullptr));
- pnode = pnode->AsParseNodeBin()->pnode2;
- }
- if (pnode == pnodeLastValStmt)
- {
- pnodeLastValStmt = nullptr;
- }
- byteCodeGenerator->EmitTopLevelStatement(pnode, funcInfo, fReturnValue && (pnodeLastValStmt == nullptr));
- EndEmitBlock(pnodeBlock, byteCodeGenerator, funcInfo);
- }
- void ClearTmpRegs(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, FuncInfo* emitFunc)
- {
- if (emitFunc->IsTmpReg(pnode->location))
- {
- pnode->location = Js::Constants::NoRegister;
- }
- }
- void ByteCodeGenerator::EmitTopLevelStatement(ParseNode *stmt, FuncInfo *funcInfo, BOOL fReturnValue)
- {
- if (stmt->nop == knopFncDecl && stmt->AsParseNodeFnc()->IsDeclaration())
- {
- // Function declarations (not function-declaration RHS's) are already fully processed.
- // Skip them here so the temp registers don't get messed up.
- return;
- }
- if (stmt->nop == knopName || stmt->nop == knopDot)
- {
- // Generating span for top level names are mostly useful in debugging mode, because user can debug it even though no side-effect expected.
- // But the name can have runtime error, e.g., foo.bar; // where foo is not defined.
- // At this time we need to throw proper line number and offset. so recording on all modes will be useful.
- StartStatement(stmt);
- Writer()->Empty(Js::OpCode::Nop);
- EndStatement(stmt);
- }
- Emit(stmt, this, funcInfo, fReturnValue, false/*isConstructorCall*/, nullptr/*bindPnode*/, true/*isTopLevel*/);
- if (funcInfo->IsTmpReg(stmt->location))
- {
- if (!stmt->isUsed && !fReturnValue)
- {
- m_writer.Reg1(Js::OpCode::Unused, stmt->location);
- }
- funcInfo->ReleaseLoc(stmt);
- }
- }
- // ByteCodeGenerator::DefineFunctions
- //
- // Emit byte code for scope-wide function definitions before any calls in the scope, regardless of lexical
- // order. Note that stores to the closure array are not emitted until we see the knopFncDecl in the tree
- // to make sure that sources of the stores have been defined.
- void ByteCodeGenerator::DefineFunctions(FuncInfo *funcInfoParent)
- {
- // DefineCachedFunctions doesn't depend on whether the user vars are declared or not, so
- // we'll just overload this variable to mean that the functions getting called again and we don't need to do anything
- if (funcInfoParent->GetHasCachedScope())
- {
- this->DefineCachedFunctions(funcInfoParent);
- }
- else
- {
- this->DefineUncachedFunctions(funcInfoParent);
- }
- }
- // Iterate over all child functions in a function's parameter and body scopes.
- template<typename Fn>
- void MapContainerScopeFunctions(ParseNode* pnodeScope, Fn fn)
- {
- auto mapFncDeclsInScopeList = [&](ParseNode *pnodeHead)
- {
- for (ParseNode *pnode = pnodeHead; pnode != nullptr;)
- {
- switch (pnode->nop)
- {
- case knopFncDecl:
- fn(pnode);
- pnode = pnode->AsParseNodeFnc()->pnodeNext;
- break;
- case knopBlock:
- pnode = pnode->AsParseNodeBlock()->pnodeNext;
- break;
- case knopCatch:
- pnode = pnode->AsParseNodeCatch()->pnodeNext;
- break;
- case knopWith:
- pnode = pnode->AsParseNodeWith()->pnodeNext;
- break;
- default:
- AssertMsg(false, "Unexpected opcode in tree of scopes");
- return;
- }
- }
- };
- pnodeScope->AsParseNodeFnc()->MapContainerScopes(mapFncDeclsInScopeList);
- }
- void ByteCodeGenerator::DefineCachedFunctions(FuncInfo *funcInfoParent)
- {
- ParseNode *pnodeParent = funcInfoParent->root;
- uint slotCount = 0;
- auto countFncSlots = [&](ParseNode *pnodeFnc)
- {
- if (pnodeFnc->AsParseNodeFnc()->GetFuncSymbol() != nullptr && pnodeFnc->AsParseNodeFnc()->IsDeclaration())
- {
- slotCount++;
- }
- };
- MapContainerScopeFunctions(pnodeParent, countFncSlots);
- if (slotCount == 0)
- {
- return;
- }
- size_t extraBytesActual = AllocSizeMath::Mul(slotCount, sizeof(Js::FuncInfoEntry));
- // Reg2Aux takes int for byteCount so we need to convert to int. OOM if we can't because it would truncate data.
- if (extraBytesActual > INT_MAX)
- {
- Js::Throw::OutOfMemory();
- }
- int extraBytes = (int)extraBytesActual;
- Js::FuncInfoArray *info = AnewPlus(alloc, extraBytes, Js::FuncInfoArray, slotCount);
- // slotCount is guaranteed to be non-zero here.
- Js::AuxArray<uint32> * slotIdInCachedScopeToNestedIndexArray = funcInfoParent->GetParsedFunctionBody()->AllocateSlotIdInCachedScopeToNestedIndexArray(slotCount);
- slotCount = 0;
- auto fillEntries = [&](ParseNode *pnodeFnc)
- {
- Symbol *sym = pnodeFnc->AsParseNodeFnc()->GetFuncSymbol();
- if (sym != nullptr && (pnodeFnc->AsParseNodeFnc()->IsDeclaration()))
- {
- AssertMsg(!pnodeFnc->AsParseNodeFnc()->IsGenerator(), "Generator functions are not supported by InitCachedFuncs but since they always escape they should disable function caching");
- Js::FuncInfoEntry *entry = &info->elements[slotCount];
- entry->nestedIndex = pnodeFnc->AsParseNodeFnc()->nestedIndex;
- entry->scopeSlot = sym->GetScopeSlot();
- slotIdInCachedScopeToNestedIndexArray->elements[slotCount] = pnodeFnc->AsParseNodeFnc()->nestedIndex;
- slotCount++;
- }
- };
- MapContainerScopeFunctions(pnodeParent, fillEntries);
- m_writer.AuxNoReg(Js::OpCode::InitCachedFuncs,
- info,
- sizeof(Js::FuncInfoArray) + extraBytes,
- sizeof(Js::FuncInfoArray) + extraBytes);
- slotCount = 0;
- auto defineOrGetCachedFunc = [&](ParseNode *pnodeFnc)
- {
- Symbol *sym = pnodeFnc->AsParseNodeFnc()->GetFuncSymbol();
- if (pnodeFnc->AsParseNodeFnc()->IsDeclaration())
- {
- // Do we need to define the function here (i.e., is it not one of our cached locals)?
- // Only happens if the sym is null (e.g., function x.y(){}).
- if (sym == nullptr)
- {
- this->DefineOneFunction(pnodeFnc->AsParseNodeFnc(), funcInfoParent);
- }
- else if (!sym->IsInSlot(this, funcInfoParent) && sym->GetLocation() != Js::Constants::NoRegister)
- {
- // If it was defined by InitCachedFuncs, do we need to put it in a register rather than a slot?
- m_writer.Reg1Unsigned1(Js::OpCode::GetCachedFunc, sym->GetLocation(), slotCount);
- }
- // The "x = function() {...}" case is being generated on the fly, during emission,
- // so the caller expects to be able to release this register.
- funcInfoParent->ReleaseLoc(pnodeFnc);
- pnodeFnc->location = Js::Constants::NoRegister;
- slotCount++;
- }
- };
- MapContainerScopeFunctions(pnodeParent, defineOrGetCachedFunc);
- AdeletePlus(alloc, extraBytes, info);
- }
- void ByteCodeGenerator::DefineUncachedFunctions(FuncInfo *funcInfoParent)
- {
- ParseNode *pnodeParent = funcInfoParent->root;
- auto defineCheck = [&](ParseNode *pnodeFnc)
- {
- Assert(pnodeFnc->nop == knopFncDecl);
- //
- // Don't define the function upfront in following cases
- // 1. x = function() {...};
- // Don't define the function for all modes.
- // Such a function can only be accessed via the LHS, so we define it at the assignment point
- // rather than the scope entry to save a register (and possibly save the whole definition).
- //
- // 2. x = function f() {...};
- // f is not visible in the enclosing scope.
- // Such function expressions should be emitted only at the assignment point, as can be used only
- // after the assignment. Might save register.
- //
- if (pnodeFnc->AsParseNodeFnc()->IsDeclaration())
- {
- this->DefineOneFunction(pnodeFnc->AsParseNodeFnc(), funcInfoParent);
- // The "x = function() {...}" case is being generated on the fly, during emission,
- // so the caller expects to be able to release this register.
- funcInfoParent->ReleaseLoc(pnodeFnc);
- pnodeFnc->location = Js::Constants::NoRegister;
- }
- };
- MapContainerScopeFunctions(pnodeParent, defineCheck);
- }
- void EmitAssignmentToFuncName(ParseNodeFnc *pnodeFnc, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfoParent)
- {
- // Assign the location holding the func object reference to the given name.
- Assert(pnodeFnc->pnodeName->nop == knopVarDecl);
- Symbol *sym = pnodeFnc->pnodeName->sym;
- if (sym != nullptr && !sym->GetIsFuncExpr())
- {
- if (sym->GetIsModuleExportStorage())
- {
- byteCodeGenerator->EmitPropStore(pnodeFnc->location, sym, nullptr, funcInfoParent);
- }
- else if (sym->GetIsGlobal())
- {
- Js::PropertyId propertyId = sym->GetPosition();
- byteCodeGenerator->EmitGlobalFncDeclInit(pnodeFnc->location, propertyId, funcInfoParent);
- if (byteCodeGenerator->GetFlags() & fscrEval && !funcInfoParent->GetIsStrictMode())
- {
- byteCodeGenerator->EmitPropStore(pnodeFnc->location, sym, nullptr, funcInfoParent);
- }
- }
- else
- {
- if (sym->NeedsSlotAlloc(byteCodeGenerator, funcInfoParent))
- {
- if (!sym->GetHasNonCommittedReference() ||
- (funcInfoParent->GetParsedFunctionBody()->DoStackNestedFunc()))
- {
- // No point in trying to optimize if there are no references before we have to commit to slot.
- // And not safe to delay putting a stack function in the slot, since we may miss boxing.
- sym->SetIsCommittedToSlot();
- }
- }
- if (sym->GetScope()->GetFunc() != byteCodeGenerator->TopFuncInfo())
- {
- byteCodeGenerator->EmitPropStore(pnodeFnc->location, sym, nullptr, funcInfoParent);
- }
- else
- {
- byteCodeGenerator->EmitLocalPropInit(pnodeFnc->location, sym, funcInfoParent);
- }
- Symbol * fncScopeSym = sym->GetFuncScopeVarSym();
- if (fncScopeSym)
- {
- if (fncScopeSym->GetIsGlobal() && byteCodeGenerator->GetFlags() & fscrEval)
- {
- Js::PropertyId propertyId = fncScopeSym->GetPosition();
- byteCodeGenerator->EmitGlobalFncDeclInit(pnodeFnc->location, propertyId, funcInfoParent);
- }
- else
- {
- byteCodeGenerator->EmitPropStore(pnodeFnc->location, fncScopeSym, nullptr, funcInfoParent, false, false, /* isFncDeclVar */true);
- }
- }
- }
- }
- }
- Js::RegSlot ByteCodeGenerator::DefineOneFunction(ParseNodeFnc *pnodeFnc, FuncInfo *funcInfoParent, bool generateAssignment, Js::RegSlot regEnv, Js::RegSlot frameDisplayTemp)
- {
- Assert(pnodeFnc->nop == knopFncDecl);
- funcInfoParent->AcquireLoc(pnodeFnc);
- if (regEnv == Js::Constants::NoRegister)
- {
- // If the child needs a closure, find a heap-allocated frame to pass to it.
- if (frameDisplayTemp != Js::Constants::NoRegister)
- {
- // We allocated a temp to hold a local frame display value. Use that.
- // It's likely that the FD is on the stack, and we used the temp to load it back.
- regEnv = frameDisplayTemp;
- }
- else if (funcInfoParent->frameDisplayRegister != Js::Constants::NoRegister)
- {
- // This function has built a frame display, so pass it down.
- regEnv = funcInfoParent->frameDisplayRegister;
- }
- else
- {
- // This function has no captured locals but inherits a closure environment, so pass it down.
- regEnv = funcInfoParent->GetEnvRegister();
- }
- regEnv = this->PrependLocalScopes(regEnv, Js::Constants::NoRegister, funcInfoParent);
- }
- // AssertMsg(funcInfo->nonLocalSymbols == 0 || regEnv != funcInfoParent->nullConstantRegister,
- // "We need a closure for the nested function");
- Assert(pnodeFnc->nestedIndex != (uint)-1);
- // If we are in a parameter scope and it is not merged with body scope then we have to create the child function as an inner function
- if (regEnv == funcInfoParent->frameDisplayRegister || regEnv == funcInfoParent->GetEnvRegister())
- {
- m_writer.NewFunction(pnodeFnc->location, pnodeFnc->nestedIndex, pnodeFnc->IsCoroutine(), pnodeFnc->GetHomeObjLocation());
- }
- else
- {
- m_writer.NewInnerFunction(pnodeFnc->location, pnodeFnc->nestedIndex, regEnv, pnodeFnc->IsCoroutine(), pnodeFnc->GetHomeObjLocation());
- }
- if (funcInfoParent->IsGlobalFunction() && (this->flags & fscrEval))
- {
- // A function declared at global scope in eval is untrackable,
- // so make sure the caller's cached scope is invalidated.
- this->funcEscapes = true;
- }
- else
- {
- if (pnodeFnc->IsDeclaration())
- {
- Symbol * funcSymbol = pnodeFnc->GetFuncSymbol();
- if (funcSymbol)
- {
- // In the case where a let/const declaration is the same symbol name
- // as the function declaration (shadowing case), the let/const var and
- // the function declaration symbol are the same and share the same flags
- // (particularly, sym->GetIsBlockVar() for this code path).
- //
- // For example:
- // let a = 0; // <-- sym->GetIsBlockVar() = true
- // function b(){} // <-- sym2->GetIsBlockVar() = false
- //
- // let x = 0; // <-- sym3->GetIsBlockVar() = true
- // function x(){} // <-- sym3->GetIsBlockVar() = true
- //
- // In order to tell if the function is actually part
- // of a block scope, we compare against the function scope here.
- // Note that having a function with the same name as a let/const declaration
- // is a redeclaration error, but we're pushing the fix for this out since it's
- // a bit involved.
- Assert(funcInfoParent->GetBodyScope() != nullptr && funcSymbol->GetScope() != nullptr);
- bool isFunctionDeclarationInBlock = funcSymbol->GetIsBlockVar();
- // Track all vars/lets/consts register slot function declarations.
- if (ShouldTrackDebuggerMetadata()
- // If this is a let binding function declaration at global level, we want to
- // be sure to track the register location as well.
- && !(funcInfoParent->IsGlobalFunction() && !isFunctionDeclarationInBlock))
- {
- if (!funcSymbol->IsInSlot(this, funcInfoParent))
- {
- funcInfoParent->byteCodeFunction->GetFunctionBody()->InsertSymbolToRegSlotList(funcSymbol->GetName(), pnodeFnc->location, funcInfoParent->varRegsCount);
- }
- }
- if (isFunctionDeclarationInBlock)
- {
- // We only track inner let bindings for the debugger side.
- this->TrackFunctionDeclarationPropertyForDebugger(funcSymbol, funcInfoParent);
- }
- }
- }
- }
- if (pnodeFnc->IsDefaultModuleExport())
- {
- this->EmitAssignmentToDefaultModuleExport(pnodeFnc, funcInfoParent);
- }
- if (pnodeFnc->pnodeName == nullptr || !generateAssignment)
- {
- return regEnv;
- }
- EmitAssignmentToFuncName(pnodeFnc, this, funcInfoParent);
- return regEnv;
- }
- void ByteCodeGenerator::DefineUserVars(FuncInfo *funcInfo)
- {
- // Initialize scope-wide variables on entry to the scope. TODO: optimize by detecting uses that are always reached
- // by an existing initialization.
- BOOL fGlobal = funcInfo->IsGlobalFunction();
- ParseNode *pnode;
- Js::FunctionBody *byteCodeFunction = funcInfo->GetParsedFunctionBody();
- // Global declarations need a temp register to hold the init value, but the node shouldn't get a register.
- // Just assign one on the fly and re-use it for all initializations.
- Js::RegSlot tmpReg = fGlobal ? funcInfo->AcquireTmpRegister() : Js::Constants::NoRegister;
- for (pnode = funcInfo->root->pnodeVars; pnode; pnode = pnode->AsParseNodeVar()->pnodeNext)
- {
- Symbol* sym = pnode->AsParseNodeVar()->sym;
- if (sym != nullptr && !(pnode->AsParseNodeVar()->isBlockScopeFncDeclVar && sym->GetIsBlockVar()))
- {
- if (sym->IsSpecialSymbol())
- {
- // Special symbols have already had their initial values stored into their registers.
- // In default-argument case we've stored those values into their slot locations, as well.
- // We must do that because a default parameter may access a special symbol through a scope slot.
- // In the non-default-argument case, though, we didn't yet store the values into the
- // slots so let's do that now.
- if (!funcInfo->root->HasNonSimpleParameterList())
- {
- EmitPropStoreForSpecialSymbol(sym->GetLocation(), sym, sym->GetPid(), funcInfo, true);
- if (ShouldTrackDebuggerMetadata() && !sym->IsInSlot(this, funcInfo))
- {
- byteCodeFunction->InsertSymbolToRegSlotList(sym->GetName(), sym->GetLocation(), funcInfo->varRegsCount);
- }
- }
- continue;
- }
- if (sym->GetIsCatch() || (pnode->nop == knopVarDecl && sym->GetIsBlockVar()))
- {
- // The init node was bound to the catch object, because it's inside a catch and has the
- // same name as the catch object. But we want to define a user var at function scope,
- // so find the right symbol. (We'll still assign the RHS value to the catch object symbol.)
- // This also applies to a var declaration in the same scope as a let declaration.
- #if DBG
- if (sym->IsArguments())
- {
- // There is a block scoped var named arguments
- Assert(!funcInfo->GetHasArguments());
- continue;
- }
- else if (!sym->GetIsCatch())
- {
- // Assert that catch cannot be at function scope and let and var at function scope is redeclaration error.
- Assert(funcInfo->bodyScope != sym->GetScope());
- }
- #endif
- sym = funcInfo->bodyScope->FindLocalSymbol(sym->GetName());
- Assert(sym && !sym->GetIsCatch() && !sym->GetIsBlockVar());
- }
- if (sym->GetSymbolType() == STVariable && !sym->GetIsModuleExportStorage())
- {
- if (fGlobal)
- {
- Js::PropertyId propertyId = sym->EnsurePosition(this);
- // We do need to initialize some globals to avoid JS errors on loading undefined variables.
- // But we first need to make sure we're not trashing built-ins.
- if (this->flags & fscrEval)
- {
- if (funcInfo->byteCodeFunction->GetIsStrictMode())
- {
- // Check/Init the property of the frame object
- this->m_writer.ElementRootU(Js::OpCode::LdLocalElemUndef,
- funcInfo->FindOrAddReferencedPropertyId(propertyId));
- }
- else
- {
- // The check and the init involve the first element in the scope chain.
- this->m_writer.ElementScopedU(
- Js::OpCode::LdElemUndefScoped, funcInfo->FindOrAddReferencedPropertyId(propertyId));
- }
- }
- else
- {
- this->m_writer.ElementU(Js::OpCode::LdElemUndef, ByteCodeGenerator::RootObjectRegister,
- funcInfo->FindOrAddReferencedPropertyId(propertyId));
- }
- }
- else if (!sym->IsArguments())
- {
- if (sym->NeedsSlotAlloc(this, funcInfo))
- {
- if (!sym->GetHasNonCommittedReference() ||
- (sym->GetHasFuncAssignment() && funcInfo->GetParsedFunctionBody()->DoStackNestedFunc()))
- {
- // No point in trying to optimize if there are no references before we have to commit to slot.
- // And not safe to delay putting a stack function in the slot, since we may miss boxing.
- sym->SetIsCommittedToSlot();
- }
- }
- // Undef-initialize the home location if it is a register (not closure-captured, or else capture
- // is delayed) or a property of an object.
- if ((!sym->GetHasInit() && !sym->IsInSlot(this, funcInfo)) ||
- (funcInfo->bodyScope->GetIsObject() && !funcInfo->GetHasCachedScope()))
- {
- Js::RegSlot reg = sym->GetLocation();
- if (reg == Js::Constants::NoRegister)
- {
- Assert(sym->IsInSlot(this, funcInfo));
- reg = funcInfo->AcquireTmpRegister();
- }
- this->m_writer.Reg1(Js::OpCode::LdUndef, reg);
- this->EmitLocalPropInit(reg, sym, funcInfo);
- if (ShouldTrackDebuggerMetadata() && !sym->GetHasInit() && !sym->IsInSlot(this, funcInfo))
- {
- byteCodeFunction->InsertSymbolToRegSlotList(sym->GetName(), reg, funcInfo->varRegsCount);
- }
- funcInfo->ReleaseTmpRegister(reg);
- }
- }
- else if (ShouldTrackDebuggerMetadata())
- {
- if (!sym->GetHasInit() && !sym->IsInSlot(this, funcInfo))
- {
- Js::RegSlot reg = sym->GetLocation();
- if (reg != Js::Constants::NoRegister)
- {
- byteCodeFunction->InsertSymbolToRegSlotList(sym->GetName(), reg, funcInfo->varRegsCount);
- }
- }
- }
- sym->SetHasInit(TRUE);
- }
- }
- }
- if (tmpReg != Js::Constants::NoRegister)
- {
- funcInfo->ReleaseTmpRegister(tmpReg);
- }
- for (int i = 0; i < funcInfo->nonUserNonTempRegistersToInitialize.Count(); ++i)
- {
- m_writer.Reg1(Js::OpCode::LdUndef, funcInfo->nonUserNonTempRegistersToInitialize.Item(i));
- }
- }
- void ByteCodeGenerator::InitBlockScopedNonTemps(ParseNode *pnode, FuncInfo *funcInfo)
- {
- // Initialize all non-temp register variables on entry to the enclosing func - in particular,
- // those with lifetimes that begin after the start of user code and may not be initialized normally.
- // This protects us from, for instance, trying to restore garbage on bailout.
- // It was originally done in debugger mode only, but we do it always to avoid issues with boxing
- // garbage on exit from jitted loop bodies.
- while (pnode)
- {
- switch (pnode->nop)
- {
- case knopFncDecl:
- {
- // If this is a block-scoped function, initialize it.
- ParseNodeFnc * pnodeFnc = pnode->AsParseNodeFnc();
- ParseNodeVar *pnodeName = pnodeFnc->pnodeName;
- if (!pnodeFnc->IsMethod() && pnodeName != nullptr)
- {
- Symbol *sym = pnodeName->sym;
- Assert(sym);
- if (sym->GetLocation() != Js::Constants::NoRegister &&
- sym->GetScope()->IsBlockScope(funcInfo) &&
- sym->GetScope()->GetFunc() == funcInfo)
- {
- this->m_writer.Reg1(Js::OpCode::LdUndef, sym->GetLocation());
- }
- }
- // No need to recurse to the nested scopes, as they belong to a nested function.
- pnode = pnodeFnc->pnodeNext;
- break;
- }
- case knopBlock:
- {
- ParseNodeBlock * pnodeBlock = pnode->AsParseNodeBlock();
- Scope *scope = pnodeBlock->scope;
- if (scope)
- {
- if (scope->IsBlockScope(funcInfo))
- {
- Js::RegSlot scopeLoc = scope->GetLocation();
- if (scopeLoc != Js::Constants::NoRegister && !funcInfo->IsTmpReg(scopeLoc))
- {
- this->m_writer.Reg1(Js::OpCode::LdUndef, scopeLoc);
- }
- }
- auto fnInit = [this, funcInfo](ParseNode *pnode)
- {
- Symbol *sym = pnode->AsParseNodeVar()->sym;
- if (!sym->IsInSlot(this, funcInfo) && !sym->GetIsGlobal() && !sym->GetIsModuleImport())
- {
- this->m_writer.Reg1(Js::OpCode::InitUndecl, pnode->AsParseNodeVar()->sym->GetLocation());
- }
- };
- IterateBlockScopedVariables(pnodeBlock, fnInit);
- }
- InitBlockScopedNonTemps(pnodeBlock->pnodeScopes, funcInfo);
- pnode = pnodeBlock->pnodeNext;
- break;
- }
- case knopCatch:
- InitBlockScopedNonTemps(pnode->AsParseNodeCatch()->pnodeScopes, funcInfo);
- pnode = pnode->AsParseNodeCatch()->pnodeNext;
- break;
- case knopWith:
- {
- Js::RegSlot withLoc = pnode->location;
- AssertMsg(withLoc != Js::Constants::NoRegister && !funcInfo->IsTmpReg(withLoc),
- "We should put with objects at known stack locations in debug mode");
- this->m_writer.Reg1(Js::OpCode::LdUndef, withLoc);
- InitBlockScopedNonTemps(pnode->AsParseNodeWith()->pnodeScopes, funcInfo);
- pnode = pnode->AsParseNodeWith()->pnodeNext;
- break;
- }
- default:
- Assert(false);
- return;
- }
- }
- }
- void ByteCodeGenerator::EmitScopeObjectInit(FuncInfo *funcInfo)
- {
- Assert(!funcInfo->byteCodeFunction->GetFunctionBody()->DoStackNestedFunc());
- if (!funcInfo->GetHasCachedScope() /* || forcing scope/inner func caching */)
- {
- return;
- }
- Scope* currentScope = funcInfo->GetCurrentChildScope();
- uint slotCount = currentScope->GetScopeSlotCount();
- uint cachedFuncCount = 0;
- Js::PropertyId firstFuncSlot = Js::Constants::NoProperty;
- Js::PropertyId firstVarSlot = Js::Constants::NoProperty;
- uint extraAlloc = UInt32Math::Add(slotCount, Js::ActivationObjectEx::ExtraSlotCount());
- extraAlloc = UInt32Math::Mul(extraAlloc, sizeof(Js::PropertyId));
- // Create and fill the array of local property ID's.
- // They all have slots assigned to them already (if they need them): see StartEmitFunction.
- Js::PropertyIdArray *propIds = funcInfo->GetParsedFunctionBody()->AllocatePropertyIdArrayForFormals(extraAlloc, slotCount, Js::ActivationObjectEx::ExtraSlotCount());
- ParseNodeFnc *pnodeFnc = funcInfo->root;
- ParseNode *pnode;
- Symbol *sym;
- if (funcInfo->GetFuncExprNameReference() && pnodeFnc->GetFuncSymbol()->GetScope() == funcInfo->GetBodyScope())
- {
- Symbol::SaveToPropIdArray(pnodeFnc->GetFuncSymbol(), propIds, this);
- }
- if (funcInfo->GetHasArguments())
- {
- // Because the arguments object can access all instances of same-named formals ("function(x,x){...}"),
- // be sure we initialize any duplicate appearances of a formal parameter to "NoProperty".
- Js::PropertyId slot = 0;
- auto initArg = [&](ParseNode *pnode)
- {
- if (pnode->IsVarLetOrConst())
- {
- Symbol *sym = pnode->AsParseNodeVar()->sym;
- Assert(sym);
- if (sym->GetScopeSlot() == slot)
- {
- // This is the last appearance of the formal, so record the ID.
- Symbol::SaveToPropIdArray(sym, propIds, this);
- }
- else
- {
- // This is an earlier duplicate appearance of the formal, so use NoProperty as a placeholder
- // since this slot can't be accessed by name.
- Assert(sym->GetScopeSlot() != Js::Constants::NoProperty && sym->GetScopeSlot() > slot);
- propIds->elements[slot] = Js::Constants::NoProperty;
- }
- }
- else
- {
- // This is for patterns
- propIds->elements[slot] = Js::Constants::NoProperty;
- }
- slot++;
- };
- MapFormalsWithoutRest(pnodeFnc, initArg);
- // If the rest is in the slot - we need to keep that slot.
- if (pnodeFnc->pnodeRest != nullptr && pnodeFnc->pnodeRest->sym->IsInSlot(this, funcInfo))
- {
- Symbol::SaveToPropIdArray(pnodeFnc->pnodeRest->sym, propIds, this);
- }
- }
- else
- {
- MapFormals(pnodeFnc, [&](ParseNode *pnode)
- {
- if (pnode->IsVarLetOrConst())
- {
- Symbol::SaveToPropIdArray(pnode->AsParseNodeVar()->sym, propIds, this);
- }
- });
- }
- auto saveFunctionVarsToPropIdArray = [&](ParseNode *pnodeFunction)
- {
- if (pnodeFunction->AsParseNodeFnc()->IsDeclaration())
- {
- ParseNode *pnodeName = pnodeFunction->AsParseNodeFnc()->pnodeName;
- if (pnodeName != nullptr)
- {
- while (pnodeName->nop == knopList)
- {
- if (pnodeName->AsParseNodeBin()->pnode1->nop == knopVarDecl)
- {
- sym = pnodeName->AsParseNodeBin()->pnode1->AsParseNodeVar()->sym;
- if (sym)
- {
- Symbol::SaveToPropIdArray(sym, propIds, this, &firstFuncSlot);
- }
- }
- pnodeName = pnodeName->AsParseNodeBin()->pnode2;
- }
- if (pnodeName->nop == knopVarDecl)
- {
- sym = pnodeName->AsParseNodeVar()->sym;
- if (sym)
- {
- Symbol::SaveToPropIdArray(sym, propIds, this, &firstFuncSlot);
- cachedFuncCount++;
- }
- }
- }
- }
- };
- MapContainerScopeFunctions(pnodeFnc, saveFunctionVarsToPropIdArray);
- if (currentScope->GetScopeType() != ScopeType_Parameter)
- {
- for (pnode = pnodeFnc->pnodeVars; pnode; pnode = pnode->AsParseNodeVar()->pnodeNext)
- {
- sym = pnode->AsParseNodeVar()->sym;
- if (!(pnode->AsParseNodeVar()->isBlockScopeFncDeclVar && sym->GetIsBlockVar()))
- {
- if (sym->GetIsCatch() || (pnode->nop == knopVarDecl && sym->GetIsBlockVar()))
- {
- sym = currentScope->FindLocalSymbol(sym->GetName());
- }
- Symbol::SaveToPropIdArray(sym, propIds, this, &firstVarSlot);
- }
- }
- ParseNodeBlock *pnodeBlock = pnodeFnc->pnodeScopes;
- for (pnode = pnodeBlock->pnodeLexVars; pnode; pnode = pnode->AsParseNodeVar()->pnodeNext)
- {
- sym = pnode->AsParseNodeVar()->sym;
- Symbol::SaveToPropIdArray(sym, propIds, this, &firstVarSlot);
- }
- pnodeBlock = pnodeFnc->pnodeBodyScope;
- for (pnode = pnodeBlock->pnodeLexVars; pnode; pnode = pnode->AsParseNodeVar()->pnodeNext)
- {
- sym = pnode->AsParseNodeVar()->sym;
- Symbol::SaveToPropIdArray(sym, propIds, this, &firstVarSlot);
- }
- }
- else
- {
- Assert(!funcInfo->IsBodyAndParamScopeMerged());
- }
- // Write the first func slot and first var slot into the auxiliary data
- Js::PropertyId *slots = propIds->elements + slotCount;
- slots[0] = cachedFuncCount;
- slots[1] = firstFuncSlot;
- slots[2] = firstVarSlot;
- slots[3] = funcInfo->GetParsedFunctionBody()->NewObjectLiteral();
- propIds->hasNonSimpleParams = funcInfo->root->HasNonSimpleParameterList();
- funcInfo->GetParsedFunctionBody()->SetHasCachedScopePropIds(true);
- }
- void ByteCodeGenerator::SetClosureRegisters(FuncInfo* funcInfo, Js::FunctionBody* byteCodeFunction)
- {
- if (funcInfo->frameDisplayRegister != Js::Constants::NoRegister)
- {
- byteCodeFunction->MapAndSetLocalFrameDisplayRegister(funcInfo->frameDisplayRegister);
- }
- if (funcInfo->frameObjRegister != Js::Constants::NoRegister)
- {
- byteCodeFunction->MapAndSetLocalClosureRegister(funcInfo->frameObjRegister);
- byteCodeFunction->SetHasScopeObject(true);
- }
- else if (funcInfo->frameSlotsRegister != Js::Constants::NoRegister)
- {
- byteCodeFunction->MapAndSetLocalClosureRegister(funcInfo->frameSlotsRegister);
- }
- if (funcInfo->paramSlotsRegister != Js::Constants::NoRegister)
- {
- byteCodeFunction->MapAndSetParamClosureRegister(funcInfo->paramSlotsRegister);
- }
- }
- void ByteCodeGenerator::FinalizeRegisters(FuncInfo * funcInfo, Js::FunctionBody * byteCodeFunction)
- {
- if (byteCodeFunction->IsCoroutine())
- {
- // EmitYield uses 'false' to create the IteratorResult object
- funcInfo->AssignFalseConstRegister();
- }
- if (funcInfo->NeedEnvRegister())
- {
- bool constReg = !funcInfo->GetIsTopLevelEventHandler() && funcInfo->IsGlobalFunction() && !(this->flags & fscrEval);
- funcInfo->AssignEnvRegister(constReg);
- }
- // Set the function body's constant count before emitting anything so that the byte code writer
- // can distinguish constants from variables.
- byteCodeFunction->CheckAndSetConstantCount(funcInfo->constRegsCount);
- this->SetClosureRegisters(funcInfo, byteCodeFunction);
- if (this->IsInDebugMode() || byteCodeFunction->IsCoroutine())
- {
- // Give permanent registers to the inner scopes in debug mode.
- // TODO: We create seperate debuggerscopes for each block which has own scope. These are stored in the var registers
- // allocated below. Ideally we should change this logic to not allocate separate registers for these and save the debug
- // info in corresponding symbols and use it from there. This will also affect the temp register allocation logic in
- // EmitOneFunction.
- uint innerScopeCount = funcInfo->InnerScopeCount();
- byteCodeFunction->SetInnerScopeCount(innerScopeCount);
- if (innerScopeCount)
- {
- funcInfo->SetFirstInnerScopeReg(funcInfo->NextVarRegister());
- for (uint i = 1; i < innerScopeCount; i++)
- {
- funcInfo->NextVarRegister();
- }
- }
- }
- // NOTE: The FB expects the yield reg to be the final non-temp.
- if (byteCodeFunction->IsCoroutine())
- {
- funcInfo->AssignYieldRegister();
- }
- Js::RegSlot firstTmpReg = funcInfo->varRegsCount;
- funcInfo->SetFirstTmpReg(firstTmpReg);
- byteCodeFunction->SetFirstTmpReg(funcInfo->RegCount());
- }
- void ByteCodeGenerator::InitScopeSlotArray(FuncInfo * funcInfo)
- {
- // Record slots info for ScopeSlots/ScopeObject.
- uint scopeSlotCount = funcInfo->bodyScope->GetScopeSlotCount();
- bool isSplitScope = !funcInfo->IsBodyAndParamScopeMerged();
- Assert(funcInfo->paramScope == nullptr || funcInfo->paramScope->GetScopeSlotCount() == 0 || isSplitScope);
- uint scopeSlotCountForParamScope = funcInfo->paramScope != nullptr ? funcInfo->paramScope->GetScopeSlotCount() : 0;
- if (scopeSlotCount == 0 && scopeSlotCountForParamScope == 0)
- {
- return;
- }
- Js::FunctionBody *byteCodeFunction = funcInfo->GetParsedFunctionBody();
- if (scopeSlotCount > 0 || scopeSlotCountForParamScope > 0)
- {
- byteCodeFunction->SetScopeSlotArraySizes(scopeSlotCount, scopeSlotCountForParamScope);
- }
- // TODO: Need to add property ids for the case when scopeSlotCountForParamSCope is non-zero
- if (scopeSlotCount)
- {
- Js::PropertyId *propertyIdsForScopeSlotArray = RecyclerNewArrayLeafZ(scriptContext->GetRecycler(), Js::PropertyId, scopeSlotCount);
- byteCodeFunction->SetPropertyIdsForScopeSlotArray(propertyIdsForScopeSlotArray, scopeSlotCount, scopeSlotCountForParamScope);
- AssertMsg(!byteCodeFunction->IsReparsed() || byteCodeFunction->WasEverAsmJsMode() || byteCodeFunction->scopeSlotArraySize == scopeSlotCount,
- "The slot array size is different between debug and non-debug mode");
- #if DEBUG
- for (UINT i = 0; i < scopeSlotCount; i++)
- {
- propertyIdsForScopeSlotArray[i] = Js::Constants::NoProperty;
- }
- #endif
- auto setPropertyIdForScopeSlotArray =
- [scopeSlotCount, propertyIdsForScopeSlotArray]
- (Js::PropertyId slot, Js::PropertyId propId)
- {
- if (slot < 0 || (uint)slot >= scopeSlotCount)
- {
- Js::Throw::FatalInternalError();
- }
- propertyIdsForScopeSlotArray[slot] = propId;
- };
- auto setPropIdsForScopeSlotArray = [this, funcInfo, setPropertyIdForScopeSlotArray](Symbol *const sym)
- {
- if (sym->NeedsSlotAlloc(this, funcInfo))
- {
- // All properties should get correct propertyId here.
- Assert(sym->HasScopeSlot()); // We can't allocate scope slot now. Any symbol needing scope slot must have allocated it before this point.
- setPropertyIdForScopeSlotArray(sym->GetScopeSlot(), sym->EnsurePosition(funcInfo));
- }
- };
- funcInfo->GetBodyScope()->ForEachSymbol(setPropIdsForScopeSlotArray);
- #if DEBUG
- for (UINT i = 0; i < scopeSlotCount; i++)
- {
- Assert(propertyIdsForScopeSlotArray[i] != Js::Constants::NoProperty
- || funcInfo->frameObjRegister != Js::Constants::NoRegister); // ScopeObject may have unassigned entries, e.g. for same-named parameters
- }
- #endif
- }
- }
- // temporarily load all constants and special registers in a single block
- void ByteCodeGenerator::LoadAllConstants(FuncInfo *funcInfo)
- {
- Symbol *sym;
- Js::FunctionBody *byteCodeFunction = funcInfo->GetParsedFunctionBody();
- byteCodeFunction->CreateConstantTable();
- if (funcInfo->nullConstantRegister != Js::Constants::NoRegister)
- {
- byteCodeFunction->RecordNullObject(byteCodeFunction->MapRegSlot(funcInfo->nullConstantRegister));
- }
- if (funcInfo->undefinedConstantRegister != Js::Constants::NoRegister)
- {
- byteCodeFunction->RecordUndefinedObject(byteCodeFunction->MapRegSlot(funcInfo->undefinedConstantRegister));
- }
- if (funcInfo->trueConstantRegister != Js::Constants::NoRegister)
- {
- byteCodeFunction->RecordTrueObject(byteCodeFunction->MapRegSlot(funcInfo->trueConstantRegister));
- }
- if (funcInfo->falseConstantRegister != Js::Constants::NoRegister)
- {
- byteCodeFunction->RecordFalseObject(byteCodeFunction->MapRegSlot(funcInfo->falseConstantRegister));
- }
- if (funcInfo->frameObjRegister != Js::Constants::NoRegister)
- {
- m_writer.RecordObjectRegister(funcInfo->frameObjRegister);
- if (!funcInfo->GetApplyEnclosesArgs())
- {
- this->EmitScopeObjectInit(funcInfo);
- }
- #if DBG
- uint count = 0;
- funcInfo->GetBodyScope()->ForEachSymbol([&](Symbol *const sym)
- {
- if (sym->NeedsSlotAlloc(this, funcInfo))
- {
- // All properties should get correct propertyId here.
- count++;
- }
- });
- if (funcInfo->GetParamScope() != nullptr)
- {
- funcInfo->GetParamScope()->ForEachSymbol([&](Symbol *const sym)
- {
- if (sym->NeedsSlotAlloc(this, funcInfo))
- {
- // All properties should get correct propertyId here.
- count++;
- }
- });
- }
- // A reparse should result in the same size of the activation object.
- // Exclude functions which were created from the ByteCodeCache.
- AssertMsg(!byteCodeFunction->IsReparsed() || byteCodeFunction->HasGeneratedFromByteCodeCache() ||
- byteCodeFunction->scopeObjectSize == count || byteCodeFunction->WasEverAsmJsMode(),
- "The activation object size is different between debug and non-debug mode");
- byteCodeFunction->scopeObjectSize = count;
- #endif
- }
- else if (funcInfo->frameSlotsRegister != Js::Constants::NoRegister)
- {
- int scopeSlotCount = funcInfo->bodyScope->GetScopeSlotCount();
- int paramSlotCount = funcInfo->paramScope->GetScopeSlotCount();
- if (scopeSlotCount == 0 && paramSlotCount == 0)
- {
- AssertMsg(funcInfo->frameDisplayRegister != Js::Constants::NoRegister, "Why do we need scope slots?");
- m_writer.Reg1(Js::OpCode::LdC_A_Null, funcInfo->frameSlotsRegister);
- }
- }
- if (funcInfo->funcExprScope && funcInfo->funcExprScope->GetIsObject())
- {
- byteCodeFunction->MapAndSetFuncExprScopeRegister(funcInfo->funcExprScope->GetLocation());
- byteCodeFunction->SetEnvDepth((uint16)-1);
- }
- bool thisLoadedFromParams = false;
- if (funcInfo->NeedEnvRegister())
- {
- byteCodeFunction->MapAndSetEnvRegister(funcInfo->GetEnvRegister());
- if (funcInfo->GetIsTopLevelEventHandler())
- {
- if (funcInfo->GetThisSymbol())
- {
- byteCodeFunction->MapAndSetThisRegisterForEventHandler(funcInfo->GetThisSymbol()->GetLocation());
- }
- // The environment is the namespace hierarchy starting with "this".
- Assert(!funcInfo->RegIsConst(funcInfo->GetEnvRegister()));
- thisLoadedFromParams = true;
- this->InvalidateCachedOuterScopes(funcInfo);
- }
- else if (funcInfo->IsGlobalFunction() && !(this->flags & fscrEval))
- {
- Assert(funcInfo->RegIsConst(funcInfo->GetEnvRegister()));
- if (funcInfo->GetIsStrictMode())
- {
- byteCodeFunction->RecordStrictNullDisplayConstant(byteCodeFunction->MapRegSlot(funcInfo->GetEnvRegister()));
- }
- else
- {
- byteCodeFunction->RecordNullDisplayConstant(byteCodeFunction->MapRegSlot(funcInfo->GetEnvRegister()));
- }
- }
- else
- {
- // environment may be required to load "this"
- Assert(!funcInfo->RegIsConst(funcInfo->GetEnvRegister()));
- this->InvalidateCachedOuterScopes(funcInfo);
- }
- }
- if (funcInfo->frameDisplayRegister != Js::Constants::NoRegister)
- {
- m_writer.RecordFrameDisplayRegister(funcInfo->frameDisplayRegister);
- }
- this->RecordAllIntConstants(funcInfo);
- this->RecordAllStrConstants(funcInfo);
- this->RecordAllBigIntConstants(funcInfo);
- this->RecordAllStringTemplateCallsiteConstants(funcInfo);
- funcInfo->doubleConstantToRegister.Map([byteCodeFunction](double d, Js::RegSlot location)
- {
- byteCodeFunction->RecordFloatConstant(byteCodeFunction->MapRegSlot(location), d);
- });
- // WARNING !!!
- // DO NOT emit any bytecode before loading the heap arguments. This is because those opcodes may bail
- // out (unlikely, since opcodes emitted in this function should not correspond to user code, but possible)
- // and the Jit assumes that there cannot be any bailouts before LdHeapArguments (or its equivalent)
- if (funcInfo->GetHasArguments())
- {
- sym = funcInfo->GetArgumentsSymbol();
- Assert(sym);
- Assert(funcInfo->GetHasHeapArguments());
- if (funcInfo->GetCallsEval() || (!funcInfo->GetApplyEnclosesArgs()))
- {
- this->LoadHeapArguments(funcInfo);
- }
- }
- else if (!funcInfo->IsGlobalFunction() && !IsInNonDebugMode())
- {
- uint count = funcInfo->inArgsCount + (funcInfo->root->pnodeRest != nullptr ? 1 : 0) - 1;
- if (count != 0)
- {
- Js::PropertyIdArray *propIds = RecyclerNewPlus(scriptContext->GetRecycler(), UInt32Math::Mul(count, sizeof(Js::PropertyId)), Js::PropertyIdArray, count, 0);
- GetFormalArgsArray(this, funcInfo, propIds);
- byteCodeFunction->SetPropertyIdsOfFormals(propIds);
- }
- }
- // Class constructors do not have a [[call]] slot but we don't implement a generic way to express this.
- // What we do is emit a check for the new flag here. If we don't have CallFlags_New set, the opcode will throw.
- // We need to do this before emitting 'this' since the base class constructor will try to construct a new object.
- if (funcInfo->IsClassConstructor())
- {
- m_writer.Empty(Js::OpCode::ChkNewCallFlag);
- }
- // new.target may be used to construct the 'this' register so make sure to load it first
- if (funcInfo->GetNewTargetSymbol())
- {
- this->LoadNewTargetObject(funcInfo);
- }
- if (funcInfo->GetThisSymbol())
- {
- this->LoadThisObject(funcInfo, thisLoadedFromParams);
- }
- else if (ShouldLoadConstThis(funcInfo))
- {
- this->EmitThis(funcInfo, funcInfo->thisConstantRegister, funcInfo->nullConstantRegister);
- }
- if (funcInfo->GetSuperSymbol())
- {
- this->LoadSuperObject(funcInfo);
- }
- if (funcInfo->GetSuperConstructorSymbol())
- {
- this->LoadSuperConstructorObject(funcInfo);
- }
- //
- // If the function is a function expression with a name,
- // load the function object at runtime to its activation object.
- //
- sym = funcInfo->root->GetFuncSymbol();
- bool funcExprWithName = !funcInfo->IsGlobalFunction() && sym && sym->GetIsFuncExpr();
- if (funcExprWithName)
- {
- if (funcInfo->GetFuncExprNameReference() ||
- (funcInfo->funcExprScope && funcInfo->funcExprScope->GetIsObject()))
- {
- //
- // x = function f(...) { ... }
- // A named function expression's name (Symbol:f) belongs to the enclosing scope.
- // Thus there are no uses of 'f' within the scope of the function (as references to 'f'
- // are looked up in the closure). So, we can't use f's register as it is from the enclosing
- // scope's register namespace. So use a tmp register.
- // In ES5 mode though 'f' is *not* a part of the enclosing scope. So we always assign 'f' a register
- // from it's register namespace, which LdFuncExpr can use.
- //
- Js::RegSlot ldFuncExprDst = sym->GetLocation();
- this->m_writer.Reg1(Js::OpCode::LdFuncExpr, ldFuncExprDst);
- if (sym->IsInSlot(this, funcInfo))
- {
- Js::RegSlot scopeLocation;
- AnalysisAssert(funcInfo->funcExprScope);
- if (funcInfo->funcExprScope->GetIsObject())
- {
- scopeLocation = funcInfo->funcExprScope->GetLocation();
- this->m_writer.Property(Js::OpCode::StFuncExpr, sym->GetLocation(), scopeLocation,
- funcInfo->FindOrAddReferencedPropertyId(sym->GetPosition()));
- }
- else if (funcInfo->paramScope->GetIsObject() || (funcInfo->paramScope->GetCanMerge() && funcInfo->bodyScope->GetIsObject()))
- {
- this->m_writer.ElementU(Js::OpCode::StLocalFuncExpr, sym->GetLocation(),
- funcInfo->FindOrAddReferencedPropertyId(sym->GetPosition()));
- }
- else
- {
- Assert(sym->HasScopeSlot());
- this->m_writer.SlotI1(Js::OpCode::StLocalSlot, sym->GetLocation(),
- sym->GetScopeSlot() + Js::ScopeSlots::FirstSlotIndex);
- }
- }
- else if (ShouldTrackDebuggerMetadata())
- {
- funcInfo->byteCodeFunction->GetFunctionBody()->InsertSymbolToRegSlotList(sym->GetName(), sym->GetLocation(), funcInfo->varRegsCount);
- }
- }
- }
- }
- void ByteCodeGenerator::InvalidateCachedOuterScopes(FuncInfo *funcInfo)
- {
- Assert(funcInfo->GetEnvRegister() != Js::Constants::NoRegister);
- // Walk the scope stack, from funcInfo outward, looking for scopes that have been cached.
- Scope *scope = funcInfo->GetBodyScope()->GetEnclosingScope();
- uint32 envIndex = 0;
- while (scope && scope->GetFunc() == funcInfo)
- {
- // Skip over FuncExpr Scope and parameter scope for current funcInfo to get to the first enclosing scope of the outer function.
- scope = scope->GetEnclosingScope();
- }
- for (; scope; scope = scope->GetEnclosingScope())
- {
- FuncInfo *func = scope->GetFunc();
- if (scope == func->GetBodyScope())
- {
- if (func->Escapes() && func->GetHasCachedScope())
- {
- AssertOrFailFast(scope->GetIsObject());
- this->m_writer.Unsigned1(Js::OpCode::InvalCachedScope, envIndex);
- }
- }
- if (scope->GetMustInstantiate())
- {
- envIndex++;
- }
- }
- }
- void ByteCodeGenerator::LoadThisObject(FuncInfo *funcInfo, bool thisLoadedFromParams)
- {
- Symbol* thisSym = funcInfo->GetThisSymbol();
- Assert(thisSym);
- Assert(!funcInfo->IsLambda());
- if (this->scriptContext->GetConfig()->IsES6ClassAndExtendsEnabled() && funcInfo->IsClassConstructor())
- {
- // Derived class constructors initialize 'this' to be Undecl
- // - we'll check this value during a super call and during 'this' access
- //
- // Base class constructors initialize 'this' to a new object using new.target
- if (funcInfo->IsBaseClassConstructor())
- {
- Symbol* newTargetSym = funcInfo->GetNewTargetSymbol();
- Assert(newTargetSym);
- this->Writer()->Reg2(Js::OpCode::NewScObjectNoCtorFull, thisSym->GetLocation(), newTargetSym->GetLocation());
- }
- else
- {
- this->m_writer.Reg1(Js::OpCode::InitUndecl, thisSym->GetLocation());
- }
- }
- else if (!funcInfo->IsGlobalFunction())
- {
- //
- // thisLoadedFromParams would be true for the event Handler case,
- // "this" would have been loaded from parameters to put in the environment
- //
- if (!thisLoadedFromParams)
- {
- Js::RegSlot tmpReg = funcInfo->AcquireTmpRegister();
- m_writer.ArgIn0(tmpReg);
- EmitThis(funcInfo, thisSym->GetLocation(), tmpReg);
- funcInfo->ReleaseTmpRegister(tmpReg);
- }
- else
- {
- EmitThis(funcInfo, thisSym->GetLocation(), thisSym->GetLocation());
- }
- }
- else
- {
- Assert(funcInfo->IsGlobalFunction());
- Js::RegSlot root = funcInfo->nullConstantRegister;
- EmitThis(funcInfo, thisSym->GetLocation(), root);
- }
- }
- void ByteCodeGenerator::LoadNewTargetObject(FuncInfo *funcInfo)
- {
- Symbol* newTargetSym = funcInfo->GetNewTargetSymbol();
- Assert(newTargetSym);
- if (funcInfo->IsClassConstructor())
- {
- Assert(!funcInfo->IsLambda());
- m_writer.ArgIn0(newTargetSym->GetLocation());
- }
- else if (funcInfo->IsGlobalFunction())
- {
- m_writer.Reg1(Js::OpCode::LdUndef, newTargetSym->GetLocation());
- }
- else
- {
- m_writer.Reg1(Js::OpCode::LdNewTarget, newTargetSym->GetLocation());
- }
- }
- void ByteCodeGenerator::LoadSuperConstructorObject(FuncInfo *funcInfo)
- {
- Symbol* superConstructorSym = funcInfo->GetSuperConstructorSymbol();
- Assert(superConstructorSym);
- Assert(!funcInfo->IsLambda());
- if (funcInfo->IsDerivedClassConstructor())
- {
- m_writer.Reg1(Js::OpCode::LdFuncObj, superConstructorSym->GetLocation());
- }
- else
- {
- m_writer.Reg1(Js::OpCode::LdUndef, superConstructorSym->GetLocation());
- }
- }
- void ByteCodeGenerator::LoadSuperObject(FuncInfo *funcInfo)
- {
- Symbol* superSym = funcInfo->GetSuperSymbol();
- Assert(superSym);
- Assert(!funcInfo->IsLambda());
- m_writer.Reg1(Js::OpCode::LdHomeObj, superSym->GetLocation());
- }
- void ByteCodeGenerator::EmitSuperCall(FuncInfo* funcInfo, ParseNodeSuperCall * pnodeSuperCall, BOOL fReturnValue)
- {
- FuncInfo* nonLambdaFunc = funcInfo;
- bool isResultUsed = pnodeSuperCall->isUsed;
- if (funcInfo->IsLambda())
- {
- nonLambdaFunc = this->FindEnclosingNonLambda();
- }
- if (nonLambdaFunc->IsBaseClassConstructor())
- {
- // super() is not allowed in base class constructors. If we detect this, emit a ReferenceError and skip making the call.
- this->Writer()->W1(Js::OpCode::RuntimeReferenceError, SCODE_CODE(JSERR_ClassSuperInBaseClass));
- return;
- }
- pnodeSuperCall->isUsed = true;
- // pnode->location refers to two things: the result of the inner function call (`temp` in the pseudocode below),
- // and the result of the super() expression itself
- funcInfo->AcquireLoc(pnodeSuperCall);
- // We need to emit 'this' directly so we can skip throwing a reference error if 'this' is currently undecl (we want to get undecl if 'this' is undecl)
- funcInfo->AcquireLoc(pnodeSuperCall->pnodeThis);
- EmitPropLoadThis(pnodeSuperCall->pnodeThis->location, pnodeSuperCall->pnodeThis, funcInfo, false);
- EmitLoad(pnodeSuperCall->pnodeNewTarget, this, funcInfo);
- Assert(pnodeSuperCall->isSuperCall);
- EmitLoad(pnodeSuperCall->pnodeTarget, this, funcInfo);
- //
- // if (super is class constructor) {
- // _this = new.target;
- // } else {
- // _this = NewScObjFull(new.target);
- // }
- //
- // temp = super.call(_this, new.target); // CallFlag_New | CallFlag_NewTarget | CallFlag_ExtraArg
- // if (temp is object) {
- // _this = temp;
- // }
- //
- // if (UndeclBlockVar === this) {
- // this = _this;
- // } else {
- // throw ReferenceError;
- // }
- //
- Js::RegSlot thisForSuperCall = funcInfo->AcquireTmpRegister();
- Js::RegSlot valueForThis = funcInfo->AcquireTmpRegister();
- Js::RegSlot tmpUndeclReg = funcInfo->AcquireTmpRegister();
- Js::ByteCodeLabel useNewTargetForThisLabel = this->Writer()->DefineLabel();
- Js::ByteCodeLabel makeCallLabel = this->Writer()->DefineLabel();
- Js::ByteCodeLabel useSuperCallResultLabel = this->Writer()->DefineLabel();
- Js::ByteCodeLabel doneLabel = this->Writer()->DefineLabel();
- Js::RegSlot tmpReg = this->EmitLdObjProto(Js::OpCode::LdFuncObjProto, pnodeSuperCall->pnodeTarget->location, funcInfo);
- this->Writer()->BrReg1(Js::OpCode::BrOnClassConstructor, useNewTargetForThisLabel, tmpReg);
- this->Writer()->Reg2(Js::OpCode::NewScObjectNoCtorFull, thisForSuperCall, pnodeSuperCall->pnodeNewTarget->location);
- this->Writer()->Br(Js::OpCode::Br, makeCallLabel);
- this->Writer()->MarkLabel(useNewTargetForThisLabel);
- this->Writer()->Reg2(Js::OpCode::Ld_A, thisForSuperCall, pnodeSuperCall->pnodeNewTarget->location);
- this->Writer()->MarkLabel(makeCallLabel);
- EmitCall(pnodeSuperCall, this, funcInfo, fReturnValue, /*fEvaluateComponents*/ true, thisForSuperCall, pnodeSuperCall->pnodeNewTarget->location);
- // We have to use another temp for the this value before assigning to this register.
- // This is because IRBuilder does not expect us to use the value of a temp after potentially assigning to that same temp.
- // Ex:
- // _this = new.target;
- // temp = super.call(_this);
- // if (temp is object) {
- // _this = temp; // creates a new sym for _this as it was previously used
- // }
- // this = _this; // tries to loads a value from the old sym (which is dead)
- this->Writer()->BrReg1(Js::OpCode::BrOnObject_A, useSuperCallResultLabel, pnodeSuperCall->location);
- this->Writer()->Reg2(Js::OpCode::Ld_A, valueForThis, thisForSuperCall);
- this->Writer()->Br(Js::OpCode::Br, doneLabel);
- this->Writer()->MarkLabel(useSuperCallResultLabel);
- this->Writer()->Reg2(Js::OpCode::Ld_A, valueForThis, pnodeSuperCall->location);
- this->Writer()->MarkLabel(doneLabel);
- // The call is done and we know what we will bind to 'this' so let's check to see if 'this' is already decl.
- Js::ByteCodeLabel skipLabel = this->Writer()->DefineLabel();
- this->Writer()->Reg1(Js::OpCode::InitUndecl, tmpUndeclReg);
- this->Writer()->BrReg2(Js::OpCode::BrSrEq_A, skipLabel, pnodeSuperCall->pnodeThis->location, tmpUndeclReg);
- this->Writer()->W1(Js::OpCode::RuntimeReferenceError, SCODE_CODE(JSERR_ClassThisAlreadyAssigned));
- this->Writer()->MarkLabel(skipLabel);
- // If calling code cares about the return value, then move the selected `this` value into the result register.
- if (isResultUsed)
- {
- this->Writer()->Reg2(Js::OpCode::Ld_A, pnodeSuperCall->location, valueForThis);
- }
- Symbol* thisSym = pnodeSuperCall->pnodeThis->sym;
- this->Writer()->Reg2(Js::OpCode::StrictLdThis, pnodeSuperCall->pnodeThis->location, valueForThis);
- EmitPropStoreForSpecialSymbol(pnodeSuperCall->pnodeThis->location, thisSym, pnodeSuperCall->pnodeThis->pid, funcInfo, false);
- funcInfo->ReleaseTmpRegister(tmpUndeclReg);
- funcInfo->ReleaseTmpRegister(valueForThis);
- funcInfo->ReleaseTmpRegister(thisForSuperCall);
- funcInfo->ReleaseLoc(pnodeSuperCall->pnodeTarget);
- funcInfo->ReleaseLoc(pnodeSuperCall->pnodeNewTarget);
- funcInfo->ReleaseLoc(pnodeSuperCall->pnodeThis);
- }
- void ByteCodeGenerator::EmitClassConstructorEndCode(FuncInfo *funcInfo)
- {
- Symbol* thisSym = funcInfo->GetThisSymbol();
- if (thisSym && thisSym->GetLocation() != Js::Constants::NoRegister)
- {
- EmitPropLoad(ByteCodeGenerator::ReturnRegister, thisSym, thisSym->GetPid(), funcInfo, true);
- this->m_writer.Reg1(Js::OpCode::ChkUndecl, ByteCodeGenerator::ReturnRegister);
- }
- }
- void ByteCodeGenerator::EmitThis(FuncInfo *funcInfo, Js::RegSlot lhsLocation, Js::RegSlot fromRegister)
- {
- if (funcInfo->byteCodeFunction->GetIsStrictMode() && !funcInfo->IsGlobalFunction() && !funcInfo->IsLambda())
- {
- m_writer.Reg2(Js::OpCode::StrictLdThis, lhsLocation, fromRegister);
- }
- else
- {
- m_writer.Reg2Int1(Js::OpCode::LdThis, lhsLocation, fromRegister, this->GetModuleID());
- }
- }
- void ByteCodeGenerator::EmitLoadFormalIntoRegister(ParseNode *pnodeFormal, Js::RegSlot pos, FuncInfo *funcInfo)
- {
- if (pnodeFormal->IsVarLetOrConst())
- {
- // Get the param from its argument position into its assigned register.
- // The position should match the location, otherwise, it has been shadowed by parameter with the same name
- Symbol *formal = pnodeFormal->AsParseNodeVar()->sym;
- if (formal->GetLocation() + 1 == pos)
- {
- // Transfer to the frame object, etc., if necessary.
- this->EmitLocalPropInit(formal->GetLocation(), formal, funcInfo);
- }
- }
- }
- void ByteCodeGenerator::HomeArguments(FuncInfo *funcInfo)
- {
- if (ShouldTrackDebuggerMetadata())
- {
- // Add formals to the debugger propertyidcontainer for reg slots
- auto addFormalsToPropertyIdContainer = [this, funcInfo](ParseNode *pnodeFormal)
- {
- if (pnodeFormal->IsVarLetOrConst())
- {
- Symbol* formal = pnodeFormal->AsParseNodeVar()->sym;
- if (!formal->IsInSlot(this, funcInfo))
- {
- Assert(!formal->GetHasInit());
- funcInfo->GetParsedFunctionBody()->InsertSymbolToRegSlotList(formal->GetName(), formal->GetLocation(), funcInfo->varRegsCount);
- }
- }
- };
- MapFormals(funcInfo->root, addFormalsToPropertyIdContainer);
- }
- // Transfer formal parameters to their home locations on the local frame.
- if (funcInfo->GetHasArguments())
- {
- if (funcInfo->root->pnodeRest != nullptr)
- {
- // Since we don't have to iterate over arguments here, we'll trust the location to be correct.
- EmitLoadFormalIntoRegister(funcInfo->root->pnodeRest, funcInfo->root->pnodeRest->sym->GetLocation() + 1, funcInfo);
- }
- // The arguments object creation helper does this work for us.
- return;
- }
- Js::ArgSlot pos = 1;
- auto loadFormal = [&](ParseNode *pnodeFormal)
- {
- EmitLoadFormalIntoRegister(pnodeFormal, pos, funcInfo);
- pos++;
- };
- MapFormals(funcInfo->root, loadFormal);
- }
- void ByteCodeGenerator::DefineLabels(FuncInfo *funcInfo)
- {
- funcInfo->singleExit = m_writer.DefineLabel();
- SList<ParseNodeStmt *>::Iterator iter(&funcInfo->targetStatements);
- while (iter.Next())
- {
- ParseNodeStmt * node = iter.Data();
- node->breakLabel = m_writer.DefineLabel();
- node->continueLabel = m_writer.DefineLabel();
- node->emitLabels = true;
- }
- }
- void ByteCodeGenerator::EmitGlobalBody(FuncInfo *funcInfo)
- {
- // Emit global code (global scope or eval), fixing up the return register with the implicit
- // return value.
- ParseNode *pnode = funcInfo->root->pnodeBody;
- ParseNode *pnodeLastVal = funcInfo->root->AsParseNodeProg()->pnodeLastValStmt;
- if (pnodeLastVal == nullptr || pnodeLastVal->IsPatternDeclaration())
- {
- // We're not guaranteed to compute any values, so fix up the return register at the top
- // in case.
- this->m_writer.Reg1(Js::OpCode::LdUndef, ReturnRegister);
- }
- while (pnode->nop == knopList)
- {
- ParseNode *stmt = pnode->AsParseNodeBin()->pnode1;
- if (stmt == pnodeLastVal)
- {
- pnodeLastVal = nullptr;
- }
- if (pnodeLastVal == nullptr && (this->flags & fscrReturnExpression))
- {
- EmitTopLevelStatement(stmt, funcInfo, true);
- }
- else
- {
- // Haven't hit the post-dominating return value yet,
- // so don't bother with the return register.
- EmitTopLevelStatement(stmt, funcInfo, false);
- }
- pnode = pnode->AsParseNodeBin()->pnode2;
- }
- EmitTopLevelStatement(pnode, funcInfo, false);
- }
- void ByteCodeGenerator::EmitFunctionBody(FuncInfo *funcInfo)
- {
- // Emit a function body. Only explicit returns and the implicit "undef" at the bottom
- // get copied to the return register.
- ParseNode *pnodeBody = funcInfo->root->pnodeBody;
- ParseNode *pnode = pnodeBody;
- while (pnode->nop == knopList)
- {
- ParseNode *stmt = pnode->AsParseNodeBin()->pnode1;
- if (stmt->CapturesSyms())
- {
- CapturedSymMap *map = funcInfo->EnsureCapturedSymMap();
- SList<Symbol*> *list = map->Item(stmt);
- FOREACH_SLIST_ENTRY(Symbol*, sym, list)
- {
- if (!sym->GetIsCommittedToSlot())
- {
- Assert(sym->GetLocation() != Js::Constants::NoProperty);
- sym->SetIsCommittedToSlot();
- ParseNode *decl = sym->GetDecl();
- Assert(decl);
- if (PHASE_TRACE(Js::DelayCapturePhase, funcInfo->byteCodeFunction))
- {
- Output::Print(_u("--- DelayCapture: Committed symbol '%s' to slot.\n"),
- sym->GetName().GetBuffer());
- Output::Flush();
- }
- // REVIEW[ianhall]: HACK to work around this causing an error due to sym not yet being initialized
- // what is this doing? Why are we assigning sym to itself?
- bool old = sym->GetNeedDeclaration();
- sym->SetNeedDeclaration(false);
- this->EmitPropStore(sym->GetLocation(), sym, sym->GetPid(), funcInfo, decl->nop == knopLetDecl, decl->nop == knopConstDecl);
- sym->SetNeedDeclaration(old);
- }
- }
- NEXT_SLIST_ENTRY;
- }
- EmitTopLevelStatement(stmt, funcInfo, false);
- pnode = pnode->AsParseNodeBin()->pnode2;
- }
- Assert(!pnode->CapturesSyms());
- EmitTopLevelStatement(pnode, funcInfo, false);
- }
- void ByteCodeGenerator::EmitProgram(ParseNodeProg *pnodeProg)
- {
- // Indicate that the binding phase is over.
- this->isBinding = false;
- this->trackEnvDepth = true;
- AssignPropertyIds(pnodeProg->funcInfo->byteCodeFunction);
- int32 initSize = this->maxAstSize / AstBytecodeRatioEstimate;
- // Use the temp allocator in bytecode write temp buffer.
- m_writer.InitData(this->alloc, initSize);
- #ifdef LOG_BYTECODE_AST_RATIO
- // log the max Ast size
- Output::Print(_u("Max Ast size: %d"), initSize);
- #endif
- Assert(pnodeProg && pnodeProg->nop == knopProg);
- if (this->parentScopeInfo)
- {
- // Scope stack is already set up the way we want it, so don't visit the global scope.
- // Start emitting with the nested scope (i.e., the deferred function).
- this->EmitScopeList(pnodeProg->pnodeScopes);
- }
- else
- {
- this->EmitScopeList(pnodeProg);
- }
- }
- void EmitDestructuredObject(ParseNode *lhs, Js::RegSlot rhsLocation, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo);
- void EmitDestructuredValueOrInitializer(ParseNodePtr lhsElementNode, Js::RegSlot rhsLocation, ParseNodePtr initializer, bool isNonPatternAssignmentTarget, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo);
- void ByteCodeGenerator::PopulateFormalsScope(uint beginOffset, FuncInfo *funcInfo, ParseNodeFnc *pnodeFnc)
- {
- Js::DebuggerScope *debuggerScope = nullptr;
- auto processArg = [&](ParseNode *pnodeArg) {
- if (pnodeArg->IsVarLetOrConst())
- {
- if (debuggerScope == nullptr)
- {
- debuggerScope = RecordStartScopeObject(pnodeFnc, funcInfo->paramScope && funcInfo->paramScope->GetIsObject() ? Js::DiagParamScopeInObject : Js::DiagParamScope);
- debuggerScope->SetBegin(beginOffset);
- }
- InsertPropertyToDebuggerScope(funcInfo, debuggerScope, pnodeArg->AsParseNodeVar()->sym);
- }
- };
- MapFormals(pnodeFnc, processArg);
- MapFormalsFromPattern(pnodeFnc, processArg);
- if (debuggerScope != nullptr)
- {
- if (!funcInfo->GetParsedFunctionBody()->IsParamAndBodyScopeMerged())
- {
- InsertPropertyToDebuggerScope(funcInfo, debuggerScope, funcInfo->GetArgumentsSymbol());
- }
- RecordEndScopeObject(pnodeFnc);
- }
- }
- void ByteCodeGenerator::InsertPropertyToDebuggerScope(FuncInfo* funcInfo, Js::DebuggerScope* debuggerScope, Symbol* sym)
- {
- if (sym)
- {
- Js::FunctionBody* funcBody = funcInfo->GetParsedFunctionBody();
- Js::DebuggerScopePropertyFlags flag = Js::DebuggerScopePropertyFlags_None;
- Js::RegSlot location = sym->GetLocation();
- if (ShouldTrackDebuggerMetadata() && !funcInfo->IsBodyAndParamScopeMerged() && funcInfo->bodyScope->FindLocalSymbol(sym->GetName()) != nullptr)
- {
- flag |= Js::DebuggerScopePropertyFlags_HasDuplicateInBody;
- location = funcBody->MapRegSlot(location);
- }
- debuggerScope->AddProperty(location, sym->EnsurePosition(funcInfo), flag);
- }
- }
- void ByteCodeGenerator::EmitDefaultArgs(FuncInfo *funcInfo, ParseNodeFnc *pnodeFnc)
- {
- uint beginOffset = m_writer.GetCurrentOffset();
- auto emitDefaultArg = [&](ParseNode *pnodeArg)
- {
- if (pnodeArg->nop == knopParamPattern)
- {
- this->StartStatement(pnodeArg);
- Assert(pnodeArg->AsParseNodeParamPattern()->location != Js::Constants::NoRegister);
- ParseNodePtr pnode1 = pnodeArg->AsParseNodeParamPattern()->pnode1;
- if (pnode1->IsPattern())
- {
- EmitAssignment(nullptr, pnode1, pnodeArg->AsParseNodeParamPattern()->location, this, funcInfo);
- }
- else
- {
- Assert(pnode1->nop == knopAsg);
- Assert(pnode1->AsParseNodeBin()->pnode1->IsPattern());
- EmitDestructuredValueOrInitializer(pnode1->AsParseNodeBin()->pnode1,
- pnodeArg->AsParseNodeParamPattern()->location,
- pnode1->AsParseNodeBin()->pnode2,
- false /*isNonPatternAssignmentTarget*/,
- this,
- funcInfo);
- }
- this->EndStatement(pnodeArg);
- return;
- }
- else if (pnodeArg->IsVarLetOrConst())
- {
- Js::RegSlot location = pnodeArg->AsParseNodeVar()->sym->GetLocation();
- if (pnodeArg->AsParseNodeVar()->pnodeInit == nullptr)
- {
- // Since the formal hasn't been initialized in LdLetHeapArguments, we'll initialize it here.
- pnodeArg->AsParseNodeVar()->sym->SetNeedDeclaration(false);
- EmitPropStore(location, pnodeArg->AsParseNodeVar()->sym, pnodeArg->AsParseNodeVar()->pid, funcInfo, true);
- return;
- }
- // Load the default argument if we got undefined, skip RHS evaluation otherwise.
- Js::ByteCodeLabel noDefaultLabel = this->m_writer.DefineLabel();
- Js::ByteCodeLabel endLabel = this->m_writer.DefineLabel();
- this->StartStatement(pnodeArg);
- // Let us use strict not equal to differentiate between null and undefined
- m_writer.BrReg2(Js::OpCode::BrSrNeq_A, noDefaultLabel, location, funcInfo->undefinedConstantRegister);
- Emit(pnodeArg->AsParseNodeVar()->pnodeInit, this, funcInfo, false);
- pnodeArg->AsParseNodeVar()->sym->SetNeedDeclaration(false); // After emit to prevent foo(a = a)
- if (funcInfo->GetHasArguments() && pnodeArg->AsParseNodeVar()->sym->IsInSlot(this, funcInfo))
- {
- EmitPropStore(pnodeArg->AsParseNodeVar()->pnodeInit->location, pnodeArg->AsParseNodeVar()->sym, pnodeArg->AsParseNodeVar()->pid, funcInfo, true);
- m_writer.Br(endLabel);
- }
- else
- {
- EmitAssignment(nullptr, pnodeArg, pnodeArg->AsParseNodeVar()->pnodeInit->location, this, funcInfo);
- }
- funcInfo->ReleaseLoc(pnodeArg->AsParseNodeVar()->pnodeInit);
- m_writer.MarkLabel(noDefaultLabel);
- if (funcInfo->GetHasArguments() && pnodeArg->AsParseNodeVar()->sym->IsInSlot(this, funcInfo))
- {
- EmitPropStore(location, pnodeArg->AsParseNodeVar()->sym, pnodeArg->AsParseNodeVar()->pid, funcInfo, true);
- m_writer.MarkLabel(endLabel);
- }
- this->EndStatement(pnodeArg);
- }
- };
- // If the function is async, we wrap the default arguments in a try catch and reject a Promise in case of error.
- if (pnodeFnc->IsAsync())
- {
- uint cacheId;
- Js::ByteCodeLabel catchLabel = m_writer.DefineLabel();
- Js::ByteCodeLabel doneLabel = m_writer.DefineLabel();
- Js::RegSlot catchArgLocation = funcInfo->AcquireTmpRegister();
- Js::RegSlot promiseLocation = funcInfo->AcquireTmpRegister();
- Js::RegSlot rejectLocation = funcInfo->AcquireTmpRegister();
- // try
- m_writer.RecordCrossFrameEntryExitRecord(/* isEnterBlock = */ true);
- m_writer.Br(Js::OpCode::TryCatch, catchLabel);
- // Rest cannot have a default argument, so we ignore it.
- MapFormalsWithoutRest(pnodeFnc, emitDefaultArg);
- m_writer.RecordCrossFrameEntryExitRecord(/* isEnterBlock = */ false);
- m_writer.Empty(Js::OpCode::Leave);
- m_writer.Br(doneLabel);
- // catch
- m_writer.MarkLabel(catchLabel);
- m_writer.Reg1(Js::OpCode::Catch, catchArgLocation);
- m_writer.RecordCrossFrameEntryExitRecord(/* isEnterBlock = */ true);
- m_writer.Empty(Js::OpCode::Nop);
- // return Promise.reject(error);
- cacheId = funcInfo->FindOrAddRootObjectInlineCacheId(Js::PropertyIds::Promise, false, false);
- m_writer.PatchableRootProperty(Js::OpCode::LdRootFld, promiseLocation, cacheId, false, false);
- EmitInvoke(rejectLocation, promiseLocation, Js::PropertyIds::reject, this, funcInfo, catchArgLocation);
- m_writer.Reg2(Js::OpCode::Ld_A, ByteCodeGenerator::ReturnRegister, rejectLocation);
- m_writer.RecordCrossFrameEntryExitRecord(/* isEnterBlock = */ false);
- m_writer.Empty(Js::OpCode::Leave);
- m_writer.Br(funcInfo->singleExit);
- m_writer.Empty(Js::OpCode::Leave);
- m_writer.MarkLabel(doneLabel);
- this->SetHasTry(true);
- funcInfo->ReleaseTmpRegister(rejectLocation);
- funcInfo->ReleaseTmpRegister(promiseLocation);
- funcInfo->ReleaseTmpRegister(catchArgLocation);
- }
- else
- {
- // Rest cannot have a default argument, so we ignore it.
- MapFormalsWithoutRest(pnodeFnc, emitDefaultArg);
- }
- if (m_writer.GetCurrentOffset() > beginOffset)
- {
- PopulateFormalsScope(beginOffset, funcInfo, pnodeFnc);
- }
- }
- void ByteCodeGenerator::EmitOneFunction(ParseNodeFnc *pnodeFnc)
- {
- Assert(pnodeFnc && (pnodeFnc->nop == knopProg || pnodeFnc->nop == knopFncDecl));
- FuncInfo *funcInfo = pnodeFnc->funcInfo;
- Assert(funcInfo != nullptr);
- if (funcInfo->IsFakeGlobalFunction(this->flags))
- {
- return;
- }
- Js::ParseableFunctionInfo* deferParseFunction = funcInfo->byteCodeFunction;
- deferParseFunction->SetGrfscr(deferParseFunction->GetGrfscr() | (this->flags & ~fscrDeferredFncExpression));
- deferParseFunction->SetSourceInfo(this->GetCurrentSourceIndex(),
- funcInfo->root,
- !!(this->flags & fscrEvalCode),
- ((this->flags & fscrDynamicCode) && !(this->flags & fscrEvalCode)));
- deferParseFunction->SetInParamsCount(funcInfo->inArgsCount);
- if (pnodeFnc->HasDefaultArguments())
- {
- deferParseFunction->SetReportedInParamsCount(pnodeFnc->firstDefaultArg + 1);
- }
- else
- {
- deferParseFunction->SetReportedInParamsCount(funcInfo->inArgsCount);
- }
- // Note: Don't check the actual attributes on the functionInfo here, since CanDefer has been cleared while
- // we're generating byte code.
- if (deferParseFunction->IsDeferred() || funcInfo->canDefer)
- {
- Js::ScopeInfo::SaveEnclosingScopeInfo(this, funcInfo);
- }
- if (funcInfo->root->pnodeBody == nullptr)
- {
- if (!PHASE_OFF1(Js::SkipNestedDeferredPhase) && (this->GetFlags() & fscrCreateParserState) == fscrCreateParserState && deferParseFunction->GetCompileCount() == 0)
- {
- deferParseFunction->BuildDeferredStubs(funcInfo->root);
- }
- Assert(!deferParseFunction->IsFunctionBody() || deferParseFunction->GetFunctionBody()->GetByteCode() != nullptr);
- return;
- }
- Js::FunctionBody* byteCodeFunction = funcInfo->GetParsedFunctionBody();
- try
- {
- if (!funcInfo->IsGlobalFunction())
- {
- // Note: Do not set the stack nested func flag if the function has been redeferred and recompiled.
- // In that case the flag already has the value we want.
- if (CanStackNestedFunc(funcInfo, true) && byteCodeFunction->GetCompileCount() == 0)
- {
- #if DBG
- byteCodeFunction->SetCanDoStackNestedFunc();
- #endif
- if (funcInfo->root->astSize <= ParseNodeFnc::MaxStackClosureAST)
- {
- byteCodeFunction->SetStackNestedFunc(true);
- }
- }
- }
- if (byteCodeFunction->DoStackNestedFunc())
- {
- uint nestedCount = byteCodeFunction->GetNestedCount();
- for (uint i = 0; i < nestedCount; i++)
- {
- Js::FunctionProxy * nested = byteCodeFunction->GetNestedFunctionProxy(i);
- if (nested->IsFunctionBody())
- {
- nested->GetFunctionBody()->SetStackNestedFuncParent(byteCodeFunction->GetFunctionInfo());
- }
- }
- }
- if (byteCodeFunction->GetByteCode() != nullptr)
- {
- // Previously compiled function nested within a re-deferred and re-compiled function.
- return;
- }
- // Bug : 301517
- // In the debug mode the hasOnlyThis optimization needs to be disabled, since user can break in this function
- // and do operation on 'this' and its property, which may not be defined yet.
- if (funcInfo->root->HasOnlyThisStmts() && !IsInDebugMode())
- {
- byteCodeFunction->SetHasOnlyThisStmts(true);
- }
- if (byteCodeFunction->IsInlineApplyDisabled() || this->scriptContext->GetConfig()->IsNoNative())
- {
- if ((pnodeFnc->nop == knopFncDecl) && (funcInfo->GetHasHeapArguments()) && (!funcInfo->GetCallsEval()) && ApplyEnclosesArgs(pnodeFnc, this))
- {
- bool applyEnclosesArgs = true;
- for (ParseNode* pnodeVar = funcInfo->root->pnodeVars; pnodeVar; pnodeVar = pnodeVar->AsParseNodeVar()->pnodeNext)
- {
- Symbol* sym = pnodeVar->AsParseNodeVar()->sym;
- if (sym->GetSymbolType() == STVariable && !sym->IsArguments())
- {
- applyEnclosesArgs = false;
- break;
- }
- }
- auto constAndLetCheck = [](ParseNodeBlock *pnodeBlock, bool *applyEnclosesArgs)
- {
- if (*applyEnclosesArgs)
- {
- for (auto lexvar = pnodeBlock->pnodeLexVars; lexvar; lexvar = lexvar->AsParseNodeVar()->pnodeNext)
- {
- Symbol* sym = lexvar->AsParseNodeVar()->sym;
- if (sym->GetSymbolType() == STVariable && !sym->IsArguments())
- {
- *applyEnclosesArgs = false;
- break;
- }
- }
- }
- };
- constAndLetCheck(funcInfo->root->pnodeScopes, &applyEnclosesArgs);
- constAndLetCheck(funcInfo->root->pnodeBodyScope, &applyEnclosesArgs);
- funcInfo->SetApplyEnclosesArgs(applyEnclosesArgs);
- }
- }
-
- InitScopeSlotArray(funcInfo);
- FinalizeRegisters(funcInfo, byteCodeFunction);
- DebugOnly(Js::RegSlot firstTmpReg = funcInfo->varRegsCount);
- // Reserve temp registers for the inner scopes. We prefer temps because the JIT will then renumber them
- // and see different lifetimes. (Note that debug mode requires permanent registers. See FinalizeRegisters.)
- // Need to revisit the condition when enabling JitES6Generators.
- uint innerScopeCount = funcInfo->InnerScopeCount();
- if (!this->IsInDebugMode() && !byteCodeFunction->IsCoroutine())
- {
- byteCodeFunction->SetInnerScopeCount(innerScopeCount);
- if (innerScopeCount)
- {
- funcInfo->SetFirstInnerScopeReg(funcInfo->AcquireTmpRegister());
- for (uint i = 1; i < innerScopeCount; i++)
- {
- funcInfo->AcquireTmpRegister();
- }
- }
- }
- funcInfo->inlineCacheMap = Anew(alloc, FuncInfo::InlineCacheMap,
- alloc,
- funcInfo->RegCount() // Pass the actual register count. // TODO: Check if we can reduce this count
- );
- funcInfo->rootObjectLoadInlineCacheMap = Anew(alloc, FuncInfo::RootObjectInlineCacheIdMap,
- alloc,
- 10);
- funcInfo->rootObjectLoadMethodInlineCacheMap = Anew(alloc, FuncInfo::RootObjectInlineCacheIdMap,
- alloc,
- 10);
- funcInfo->rootObjectStoreInlineCacheMap = Anew(alloc, FuncInfo::RootObjectInlineCacheIdMap,
- alloc,
- 10);
- funcInfo->referencedPropertyIdToMapIndex = Anew(alloc, FuncInfo::RootObjectInlineCacheIdMap,
- alloc,
- 10);
- byteCodeFunction->AllocateLiteralRegexArray();
- m_callSiteId = 0;
- m_writer.Begin(byteCodeFunction, alloc, this->DoJitLoopBodies(funcInfo), funcInfo->hasLoop, this->IsInDebugMode());
- this->PushFuncInfo(_u("EmitOneFunction"), funcInfo);
- this->inPrologue = true;
- Scope* paramScope = funcInfo->GetParamScope();
- Scope* bodyScope = funcInfo->GetBodyScope();
- // For now, emit all constant loads at top of function (should instead put in closest dominator of uses).
- LoadAllConstants(funcInfo);
- HomeArguments(funcInfo);
- if (!funcInfo->IsBodyAndParamScopeMerged())
- {
- byteCodeFunction->SetParamAndBodyScopeNotMerged();
- // Pop the body scope before emitting the default args
- PopScope();
- Assert(this->GetCurrentScope() == paramScope);
- }
- if (funcInfo->root->pnodeRest != nullptr)
- {
- byteCodeFunction->SetHasRestParameter();
- }
- if (funcInfo->IsGlobalFunction())
- {
- EnsureNoRedeclarations(pnodeFnc->pnodeScopes, funcInfo);
- }
- ::BeginEmitBlock(pnodeFnc->pnodeScopes, this, funcInfo);
- DefineLabels(funcInfo);
- // We need to emit the storage for special symbols before we emit the default arguments in case the default
- // argument expressions reference those special names.
- if (pnodeFnc->HasNonSimpleParameterList())
- {
- // If the param and body scope are merged, the special symbol vars are located in the body scope so we
- // need to walk over the var list.
- if (funcInfo->IsBodyAndParamScopeMerged())
- {
- for (ParseNodePtr pnodeVar = pnodeFnc->pnodeVars; pnodeVar; pnodeVar = pnodeVar->AsParseNodeVar()->pnodeNext)
- {
- #if DBG
- bool reachedEndOfSpecialSymbols = false;
- #endif
- Symbol* sym = pnodeVar->AsParseNodeVar()->sym;
- if (sym != nullptr && sym->IsSpecialSymbol())
- {
- EmitPropStoreForSpecialSymbol(sym->GetLocation(), sym, sym->GetPid(), funcInfo, true);
- if (ShouldTrackDebuggerMetadata() && !sym->IsInSlot(this, funcInfo))
- {
- byteCodeFunction->InsertSymbolToRegSlotList(sym->GetName(), sym->GetLocation(), funcInfo->varRegsCount);
- }
- }
- else
- {
- #if DBG
- reachedEndOfSpecialSymbols = true;
- #else
- // All of the special symbols exist at the beginning of the var list (parser guarantees this and debug build asserts this)
- // so we can quit walking at the first non-special one we see.
- break;
- #endif
- }
- #if DBG
- if (reachedEndOfSpecialSymbols)
- {
- Assert(sym == nullptr || !sym->IsSpecialSymbol());
- }
- #endif
- }
- }
- else
- {
- paramScope->ForEachSymbol([&](Symbol* sym) {
- if (sym && sym->IsSpecialSymbol())
- {
- EmitPropStoreForSpecialSymbol(sym->GetLocation(), sym, sym->GetPid(), funcInfo, true);
- }
- });
- }
- }
- if (pnodeFnc->HasNonSimpleParameterList() || !funcInfo->IsBodyAndParamScopeMerged())
- {
- Assert(pnodeFnc->HasNonSimpleParameterList() || CONFIG_FLAG(ForceSplitScope));
- this->InitBlockScopedNonTemps(funcInfo->root->pnodeScopes, funcInfo);
- EmitDefaultArgs(funcInfo, pnodeFnc);
- if (!funcInfo->IsBodyAndParamScopeMerged())
- {
- Assert(this->GetCurrentScope() == paramScope);
- // Push the body scope
- PushScope(bodyScope);
- funcInfo->SetCurrentChildScope(bodyScope);
- // Mark the beginning of the body scope so that new scope slots can be created.
- this->Writer()->Empty(Js::OpCode::BeginBodyScope);
- }
- }
- // If the function has non simple parameter list, the params needs to be evaluated when the generator object is created
- // (that is when the function is called). This yield opcode is to mark the begining of the function body.
- // TODO: Inserting a yield should have almost no impact on perf as it is a direct return from the function. But this needs
- // to be verified. Ideally if the function has simple parameter list then we can avoid inserting the opcode and the additional call.
- if (pnodeFnc->IsGenerator())
- {
- Js::RegSlot tempReg = funcInfo->AcquireTmpRegister();
- EmitYield(funcInfo->AssignUndefinedConstRegister(), tempReg, this, funcInfo);
- m_writer.Reg1(Js::OpCode::Unused, tempReg);
- funcInfo->ReleaseTmpRegister(tempReg);
- }
- DefineUserVars(funcInfo);
- // Emit all scope-wide function definitions before emitting function bodies
- // so that calls may reference functions they precede lexically.
- // Note, global eval scope is a fake local scope and is handled as if it were
- // a lexical block instead of a true global scope, so do not define the functions
- // here. They will be defined during BeginEmitBlock.
- if (!(funcInfo->IsGlobalFunction() && this->IsEvalWithNoParentScopeInfo()))
- {
- // This only handles function declarations, which param scope cannot have any.
- DefineFunctions(funcInfo);
- }
- if (pnodeFnc->HasNonSimpleParameterList() || !funcInfo->IsBodyAndParamScopeMerged())
- {
- Assert(pnodeFnc->HasNonSimpleParameterList() || CONFIG_FLAG(ForceSplitScope));
- this->InitBlockScopedNonTemps(funcInfo->root->pnodeBodyScope, funcInfo);
- }
- else
- {
- this->InitBlockScopedNonTemps(funcInfo->root->pnodeScopes, funcInfo);
- }
- if (!pnodeFnc->HasNonSimpleParameterList() && funcInfo->GetHasArguments() && !NeedScopeObjectForArguments(funcInfo, pnodeFnc))
- {
- // If we didn't create a scope object and didn't have default args, we still need to transfer the formals to their slots.
- MapFormalsWithoutRest(pnodeFnc, [&](ParseNode *pnodeArg) { EmitPropStore(pnodeArg->AsParseNodeVar()->sym->GetLocation(), pnodeArg->AsParseNodeVar()->sym, pnodeArg->AsParseNodeVar()->pid, funcInfo); });
- }
- // Rest needs to trigger use before declaration until all default args have been processed.
- if (pnodeFnc->pnodeRest != nullptr)
- {
- pnodeFnc->pnodeRest->sym->SetNeedDeclaration(false);
- }
- Js::RegSlot formalsUpperBound = Js::Constants::NoRegister; // Needed for tracking the last RegSlot in the param scope
- if (!funcInfo->IsBodyAndParamScopeMerged())
- {
- // Emit bytecode to copy the initial values from param names to their corresponding body bindings.
- // We have to do this after the rest param is marked as false for need declaration.
- Symbol* funcSym = funcInfo->root->GetFuncSymbol();
- paramScope->ForEachSymbol([&](Symbol* param) {
- Symbol* varSym = funcInfo->GetBodyScope()->FindLocalSymbol(param->GetName());
- if ((funcSym == nullptr || funcSym != param) // Do not copy the symbol over to body as the function expression symbol
- // is expected to stay inside the function expression scope
- && (varSym && varSym->GetSymbolType() == STVariable && (varSym->IsInSlot(this, funcInfo) || varSym->GetLocation() != Js::Constants::NoRegister)))
- {
- if (!varSym->GetNeedDeclaration())
- {
- if (param->IsInSlot(this, funcInfo))
- {
- // Simulating EmitPropLoad here. We can't directly call the method as we have to use the param scope specifically.
- // Walking the scope chain is not possible at this time.
- Js::RegSlot tempReg = funcInfo->AcquireTmpRegister();
- Js::PropertyId slot = param->EnsureScopeSlot(this, funcInfo);
- Js::ProfileId profileId = funcInfo->FindOrAddSlotProfileId(paramScope, slot);
- Js::OpCode op = paramScope->GetIsObject() ? Js::OpCode::LdParamObjSlot : Js::OpCode::LdParamSlot;
- slot = slot + (paramScope->GetIsObject() ? 0 : Js::ScopeSlots::FirstSlotIndex);
- this->m_writer.SlotI1(op, tempReg, slot, profileId);
- this->EmitPropStore(tempReg, varSym, varSym->GetPid(), funcInfo);
- funcInfo->ReleaseTmpRegister(tempReg);
- }
- else if (param->GetLocation() != Js::Constants::NoRegister)
- {
- this->EmitPropStore(param->GetLocation(), varSym, varSym->GetPid(), funcInfo);
- }
- else
- {
- Assert(param->IsArguments() && !funcInfo->GetHasArguments());
- }
- }
- else
- {
- // There is a let redeclaration of arguments symbol. Any other var will cause a
- // re-declaration error.
- Assert(param->IsArguments());
- }
- }
- if (ShouldTrackDebuggerMetadata() && param->GetLocation() != Js::Constants::NoRegister)
- {
- if (formalsUpperBound == Js::Constants::NoRegister || formalsUpperBound < param->GetLocation())
- {
- formalsUpperBound = param->GetLocation();
- }
- }
- });
- }
- if (ShouldTrackDebuggerMetadata() && byteCodeFunction->GetPropertyIdOnRegSlotsContainer())
- {
- byteCodeFunction->GetPropertyIdOnRegSlotsContainer()->formalsUpperBound = formalsUpperBound;
- }
- if (pnodeFnc->pnodeBodyScope != nullptr)
- {
- ::BeginEmitBlock(pnodeFnc->pnodeBodyScope, this, funcInfo);
- }
- this->inPrologue = false;
- if (funcInfo->IsGlobalFunction())
- {
- EmitGlobalBody(funcInfo);
- }
- else
- {
- EmitFunctionBody(funcInfo);
- }
- if (pnodeFnc->pnodeBodyScope != nullptr)
- {
- ::EndEmitBlock(pnodeFnc->pnodeBodyScope, this, funcInfo);
- }
- ::EndEmitBlock(pnodeFnc->pnodeScopes, this, funcInfo);
- if (!this->IsInDebugMode())
- {
- // Release the temp registers that we reserved for inner scopes above.
- if (innerScopeCount)
- {
- Js::RegSlot tmpReg = funcInfo->FirstInnerScopeReg() + innerScopeCount - 1;
- for (uint i = 0; i < innerScopeCount; i++)
- {
- funcInfo->ReleaseTmpRegister(tmpReg);
- tmpReg--;
- }
- }
- }
- Assert(funcInfo->firstTmpReg == firstTmpReg);
- Assert(funcInfo->curTmpReg == firstTmpReg);
- Assert(byteCodeFunction->GetFirstTmpReg() == firstTmpReg + byteCodeFunction->GetConstantCount());
- byteCodeFunction->CheckAndSetVarCount(funcInfo->varRegsCount);
- byteCodeFunction->CheckAndSetOutParamMaxDepth(funcInfo->outArgsMaxDepth);
- byteCodeFunction->SetForInLoopDepth(funcInfo->GetMaxForInLoopLevel());
- // Do a uint32 add just to verify that we haven't overflowed the reg slot type.
- UInt32Math::Add(funcInfo->varRegsCount, funcInfo->constRegsCount);
- #if DBG_DUMP
- if (PHASE_STATS1(Js::ByteCodePhase))
- {
- Output::Print(_u(" BCode: %-10d, Aux: %-10d, AuxC: %-10d Total: %-10d, %s\n"),
- m_writer.ByteCodeDataSize(),
- m_writer.AuxiliaryDataSize(),
- m_writer.AuxiliaryContextDataSize(),
- m_writer.ByteCodeDataSize() + m_writer.AuxiliaryDataSize() + m_writer.AuxiliaryContextDataSize(),
- funcInfo->name);
- this->scriptContext->byteCodeDataSize += m_writer.ByteCodeDataSize();
- this->scriptContext->byteCodeAuxiliaryDataSize += m_writer.AuxiliaryDataSize();
- this->scriptContext->byteCodeAuxiliaryContextDataSize += m_writer.AuxiliaryContextDataSize();
- }
- #endif
- this->MapCacheIdsToPropertyIds(funcInfo);
- this->MapReferencedPropertyIds(funcInfo);
- Assert(this->TopFuncInfo() == funcInfo);
- PopFuncInfo(_u("EmitOneFunction"));
- m_writer.SetCallSiteCount(m_callSiteId);
- #ifdef LOG_BYTECODE_AST_RATIO
- m_writer.End(funcInfo->root->astSize, this->maxAstSize);
- #else
- m_writer.End();
- #endif
- }
- catch (...)
- {
- // Failed to generate byte-code for this function body (likely OOM or stack overflow). Notify the function body so that
- // it can revert intermediate state changes that may have taken place during byte code generation before the failure.
- byteCodeFunction->ResetByteCodeGenState();
- m_writer.Reset();
- throw;
- }
- #ifdef PERF_HINT
- if (PHASE_TRACE1(Js::PerfHintPhase) && !byteCodeFunction->GetIsGlobalFunc())
- {
- if (byteCodeFunction->GetHasTry())
- {
- WritePerfHint(PerfHints::HasTryBlock_Verbose, byteCodeFunction);
- }
- if (funcInfo->GetCallsEval())
- {
- WritePerfHint(PerfHints::CallsEval_Verbose, byteCodeFunction);
- }
- else if (funcInfo->GetChildCallsEval())
- {
- WritePerfHint(PerfHints::ChildCallsEval, byteCodeFunction);
- }
- }
- #endif
- if (!byteCodeFunction->GetSourceContextInfo()->IsDynamic() && byteCodeFunction->GetIsTopLevel() && !(this->flags & fscrEvalCode))
- {
- // Add the top level of nested functions to the tracking dictionary. Wait until this point so that all nested functions have gone
- // through the Emit API so source info, etc., is initialized, and these are not orphaned functions left behind by an unfinished pass.
- byteCodeFunction->ForEachNestedFunc([&](Js::FunctionProxy * nestedFunc, uint32 i)
- {
- if (nestedFunc && nestedFunc->IsDeferredParseFunction() && nestedFunc->GetParseableFunctionInfo()->GetIsDeclaration())
- {
- byteCodeFunction->GetUtf8SourceInfo()->TrackDeferredFunction(nestedFunc->GetLocalFunctionId(), nestedFunc->GetParseableFunctionInfo());
- }
- return true;
- });
- }
- byteCodeFunction->SetInitialDefaultEntryPoint();
- byteCodeFunction->SetCompileCount(UInt32Math::Add(byteCodeFunction->GetCompileCount(), 1));
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- if (byteCodeFunction->IsInDebugMode() != scriptContext->IsScriptContextInDebugMode()) // debug mode mismatch
- {
- if (m_utf8SourceInfo->GetIsLibraryCode())
- {
- Assert(!byteCodeFunction->IsInDebugMode()); // Library script byteCode is never in debug mode
- }
- else
- {
- Js::Throw::FatalInternalError();
- }
- }
- #endif
- #if DBG_DUMP
- if (PHASE_DUMP(Js::ByteCodePhase, funcInfo->byteCodeFunction) && Js::Configuration::Global.flags.Verbose)
- {
- pnodeFnc->Dump();
- }
- if (this->Trace() || PHASE_DUMP(Js::ByteCodePhase, funcInfo->byteCodeFunction))
- {
- Js::ByteCodeDumper::Dump(byteCodeFunction);
- }
- if (PHASE_DUMP(Js::DebuggerScopePhase, funcInfo->byteCodeFunction))
- {
- byteCodeFunction->DumpScopes();
- }
- #endif
- #if ENABLE_NATIVE_CODEGEN
- if ((!PHASE_OFF(Js::BackEndPhase, funcInfo->byteCodeFunction))
- && !this->forceNoNative
- && !this->scriptContext->GetConfig()->IsNoNative())
- {
- GenerateFunction(this->scriptContext->GetNativeCodeGenerator(), byteCodeFunction);
- }
- #endif
- }
- void ByteCodeGenerator::MapCacheIdsToPropertyIds(FuncInfo *funcInfo)
- {
- Js::FunctionBody *functionBody = funcInfo->GetParsedFunctionBody();
- uint rootObjectLoadInlineCacheStart = funcInfo->GetInlineCacheCount();
- uint rootObjectLoadMethodInlineCacheStart = rootObjectLoadInlineCacheStart + funcInfo->GetRootObjectLoadInlineCacheCount();
- uint rootObjectStoreInlineCacheStart = rootObjectLoadMethodInlineCacheStart + funcInfo->GetRootObjectLoadMethodInlineCacheCount();
- uint totalFieldAccessInlineCacheCount = rootObjectStoreInlineCacheStart + funcInfo->GetRootObjectStoreInlineCacheCount();
- functionBody->CreateCacheIdToPropertyIdMap(rootObjectLoadInlineCacheStart, rootObjectLoadMethodInlineCacheStart,
- rootObjectStoreInlineCacheStart, totalFieldAccessInlineCacheCount, funcInfo->GetIsInstInlineCacheCount());
- if (totalFieldAccessInlineCacheCount == 0)
- {
- return;
- }
- funcInfo->inlineCacheMap->Map([functionBody](Js::RegSlot regSlot, FuncInfo::InlineCacheIdMap *inlineCacheIdMap)
- {
- inlineCacheIdMap->Map([functionBody](Js::PropertyId propertyId, FuncInfo::InlineCacheList* inlineCacheList)
- {
- if (inlineCacheList)
- {
- inlineCacheList->Iterate([functionBody, propertyId](InlineCacheUnit cacheUnit)
- {
- CompileAssert(offsetof(InlineCacheUnit, cacheId) == offsetof(InlineCacheUnit, loadCacheId));
- if (cacheUnit.loadCacheId != -1)
- {
- functionBody->SetPropertyIdForCacheId(cacheUnit.loadCacheId, propertyId);
- }
- if (cacheUnit.loadMethodCacheId != -1)
- {
- functionBody->SetPropertyIdForCacheId(cacheUnit.loadMethodCacheId, propertyId);
- }
- if (cacheUnit.storeCacheId != -1)
- {
- functionBody->SetPropertyIdForCacheId(cacheUnit.storeCacheId, propertyId);
- }
- });
- }
- });
- });
- funcInfo->rootObjectLoadInlineCacheMap->Map([functionBody, rootObjectLoadInlineCacheStart](Js::PropertyId propertyId, uint cacheId)
- {
- functionBody->SetPropertyIdForCacheId(cacheId + rootObjectLoadInlineCacheStart, propertyId);
- });
- funcInfo->rootObjectLoadMethodInlineCacheMap->Map([functionBody, rootObjectLoadMethodInlineCacheStart](Js::PropertyId propertyId, uint cacheId)
- {
- functionBody->SetPropertyIdForCacheId(cacheId + rootObjectLoadMethodInlineCacheStart, propertyId);
- });
- funcInfo->rootObjectStoreInlineCacheMap->Map([functionBody, rootObjectStoreInlineCacheStart](Js::PropertyId propertyId, uint cacheId)
- {
- functionBody->SetPropertyIdForCacheId(cacheId + rootObjectStoreInlineCacheStart, propertyId);
- });
- SListBase<uint>::Iterator valueOfIter(&funcInfo->valueOfStoreCacheIds);
- while (valueOfIter.Next())
- {
- functionBody->SetPropertyIdForCacheId(valueOfIter.Data(), Js::PropertyIds::valueOf);
- }
- SListBase<uint>::Iterator toStringIter(&funcInfo->toStringStoreCacheIds);
- while (toStringIter.Next())
- {
- functionBody->SetPropertyIdForCacheId(toStringIter.Data(), Js::PropertyIds::toString);
- }
- #if DBG
- functionBody->VerifyCacheIdToPropertyIdMap();
- #endif
- }
- void ByteCodeGenerator::MapReferencedPropertyIds(FuncInfo * funcInfo)
- {
- Js::FunctionBody *functionBody = funcInfo->GetParsedFunctionBody();
- uint referencedPropertyIdCount = funcInfo->GetReferencedPropertyIdCount();
- functionBody->CreateReferencedPropertyIdMap(referencedPropertyIdCount);
- funcInfo->referencedPropertyIdToMapIndex->Map([functionBody](Js::PropertyId propertyId, uint mapIndex)
- {
- functionBody->SetReferencedPropertyIdWithMapIndex(mapIndex, propertyId);
- });
- #if DBG
- functionBody->VerifyReferencedPropertyIdMap();
- #endif
- }
- void ByteCodeGenerator::EmitScopeList(ParseNode *pnode, ParseNode *breakOnBodyScopeNode)
- {
- while (pnode)
- {
- if (breakOnBodyScopeNode != nullptr && breakOnBodyScopeNode == pnode)
- {
- break;
- }
- switch (pnode->nop)
- {
- case knopFncDecl:
- #ifdef ASMJS_PLAT
- if (pnode->AsParseNodeFnc()->GetAsmjsMode())
- {
- Js::ExclusiveContext context(this, GetScriptContext());
- if (Js::AsmJSCompiler::Compile(&context, pnode->AsParseNodeFnc(), pnode->AsParseNodeFnc()->pnodeParams))
- {
- pnode = pnode->AsParseNodeFnc()->pnodeNext;
- break;
- }
- else if (CONFIG_FLAG(AsmJsStopOnError))
- {
- exit(JSERR_AsmJsCompileError);
- }
- else
- {
- // If deferral is not allowed, throw and reparse everything with asm.js disabled.
- throw Js::AsmJsParseException();
- }
- }
- #endif
- // FALLTHROUGH
- case knopProg:
- if (pnode->AsParseNodeFnc()->funcInfo)
- {
- FuncInfo* funcInfo = pnode->AsParseNodeFnc()->funcInfo;
- Scope* paramScope = funcInfo->GetParamScope();
- if (!funcInfo->IsBodyAndParamScopeMerged())
- {
- funcInfo->SetCurrentChildScope(paramScope);
- }
- else
- {
- funcInfo->SetCurrentChildScope(funcInfo->GetBodyScope());
- }
- this->StartEmitFunction(pnode->AsParseNodeFnc());
- PushFuncInfo(_u("StartEmitFunction"), funcInfo);
- if (!funcInfo->IsBodyAndParamScopeMerged())
- {
- this->EmitScopeList(pnode->AsParseNodeFnc()->pnodeBodyScope->pnodeScopes);
- }
- else
- {
- this->EmitScopeList(pnode->AsParseNodeFnc()->pnodeScopes);
- }
- this->EmitOneFunction(pnode->AsParseNodeFnc());
- this->EndEmitFunction(pnode->AsParseNodeFnc());
- Assert(pnode->AsParseNodeFnc()->pnodeBody == nullptr || funcInfo->isReused || funcInfo->GetCurrentChildScope() == funcInfo->GetBodyScope());
- funcInfo->SetCurrentChildScope(nullptr);
- }
- pnode = pnode->AsParseNodeFnc()->pnodeNext;
- break;
- case knopBlock:
- {
- ParseNodeBlock * pnodeBlock = pnode->AsParseNodeBlock();
- this->StartEmitBlock(pnodeBlock);
- this->EmitScopeList(pnodeBlock->pnodeScopes);
- this->EndEmitBlock(pnodeBlock);
- pnode = pnodeBlock->pnodeNext;
- break;
- }
- case knopCatch:
- {
- ParseNodeCatch * pnodeCatch = pnode->AsParseNodeCatch();
- this->StartEmitCatch(pnodeCatch);
- this->EmitScopeList(pnodeCatch->pnodeScopes);
- this->EndEmitCatch(pnodeCatch);
- pnode = pnodeCatch->pnodeNext;
- break;
- }
- case knopWith:
- this->StartEmitWith(pnode);
- this->EmitScopeList(pnode->AsParseNodeWith()->pnodeScopes);
- this->EndEmitWith(pnode);
- pnode = pnode->AsParseNodeWith()->pnodeNext;
- break;
- default:
- AssertMsg(false, "Unexpected opcode in tree of scopes");
- break;
- }
- }
- }
- void ByteCodeGenerator::EnsureFncDeclScopeSlot(ParseNodeFnc *pnodeFnc, FuncInfo *funcInfo)
- {
- if (pnodeFnc->pnodeName)
- {
- Assert(pnodeFnc->pnodeName->nop == knopVarDecl);
- Symbol *sym = pnodeFnc->pnodeName->sym;
- // If this function is shadowing the arguments symbol in body then skip it.
- // We will allocate scope slot for the arguments symbol during EmitLocalPropInit.
- if (sym && !sym->IsArguments())
- {
- sym->EnsureScopeSlot(this, funcInfo);
- }
- }
- }
- // Similar to EnsureFncScopeSlot visitor function, but verifies that a slot is needed before assigning it.
- void ByteCodeGenerator::CheckFncDeclScopeSlot(ParseNodeFnc *pnodeFnc, FuncInfo *funcInfo)
- {
- if (pnodeFnc->pnodeName)
- {
- Assert(pnodeFnc->pnodeName->nop == knopVarDecl);
- Symbol *sym = pnodeFnc->pnodeName->sym;
- if (sym && sym->NeedsSlotAlloc(this, funcInfo))
- {
- sym->EnsureScopeSlot(this, funcInfo);
- }
- }
- }
- void ByteCodeGenerator::StartEmitFunction(ParseNodeFnc *pnodeFnc)
- {
- Assert(pnodeFnc->nop == knopFncDecl || pnodeFnc->nop == knopProg);
- FuncInfo *funcInfo = pnodeFnc->funcInfo;
- Scope * const bodyScope = funcInfo->GetBodyScope();
- Scope * const paramScope = funcInfo->GetParamScope();
- if (funcInfo->byteCodeFunction->IsFunctionParsed() && funcInfo->root->pnodeBody != nullptr)
- {
- if (funcInfo->GetParsedFunctionBody()->GetByteCode() == nullptr && !(flags & (fscrEval | fscrImplicitThis)))
- {
- // Only set the environment depth if it's truly known (i.e., not in eval or event handler).
- funcInfo->GetParsedFunctionBody()->SetEnvDepth(this->envDepth);
- }
- if (funcInfo->GetCallsEval())
- {
- funcInfo->byteCodeFunction->SetDontInline(true);
- }
- Scope * const funcExprScope = funcInfo->funcExprScope;
- if (funcExprScope)
- {
- if (funcInfo->GetCallsEval())
- {
- Assert(funcExprScope->GetIsObject());
- }
- if (funcExprScope->GetIsObject())
- {
- funcExprScope->SetCapturesAll(true);
- funcExprScope->SetMustInstantiate(true);
- PushScope(funcExprScope);
- }
- else
- {
- Symbol *sym = funcInfo->root->GetFuncSymbol();
- if (funcInfo->IsBodyAndParamScopeMerged())
- {
- funcInfo->bodyScope->AddSymbol(sym);
- }
- else
- {
- funcInfo->paramScope->AddSymbol(sym);
- }
- sym->EnsureScopeSlot(this, funcInfo);
- if (sym->GetHasNonLocalReference())
- {
- sym->GetScope()->SetHasOwnLocalInClosure(true);
- }
- }
- }
- if (pnodeFnc->nop != knopProg)
- {
- if (!bodyScope->GetIsObject() && NeedObjectAsFunctionScope(funcInfo, pnodeFnc))
- {
- Assert(bodyScope->GetIsObject());
- }
- if (bodyScope->GetIsObject())
- {
- bodyScope->SetLocation(funcInfo->frameObjRegister);
- }
- else
- {
- bodyScope->SetLocation(funcInfo->frameSlotsRegister);
- }
- if (!funcInfo->IsBodyAndParamScopeMerged())
- {
- if (paramScope->GetIsObject())
- {
- paramScope->SetLocation(funcInfo->frameObjRegister);
- }
- else
- {
- paramScope->SetLocation(funcInfo->frameSlotsRegister);
- }
- }
- if (bodyScope->GetIsObject())
- {
- // Win8 908700: Disable under F12 debugger because there are too many cached scopes holding onto locals.
- funcInfo->SetHasCachedScope(
- !PHASE_OFF(Js::CachedScopePhase, funcInfo->byteCodeFunction) &&
- !funcInfo->Escapes() &&
- funcInfo->frameObjRegister != Js::Constants::NoRegister &&
- !ApplyEnclosesArgs(pnodeFnc, this) &&
- funcInfo->IsBodyAndParamScopeMerged() && // There is eval in the param scope
- !pnodeFnc->HasDefaultArguments() &&
- !pnodeFnc->HasDestructuredParams() &&
- (PHASE_FORCE(Js::CachedScopePhase, funcInfo->byteCodeFunction) || !IsInDebugMode())
- #if ENABLE_TTD
- && !funcInfo->GetParsedFunctionBody()->GetScriptContext()->GetThreadContext()->IsRuntimeInTTDMode()
- #endif
- );
- if (funcInfo->GetHasCachedScope())
- {
- Assert(funcInfo->funcObjRegister == Js::Constants::NoRegister);
- Symbol *funcSym = funcInfo->root->GetFuncSymbol();
- if (funcSym && funcSym->GetIsFuncExpr())
- {
- if (funcSym->GetLocation() == Js::Constants::NoRegister)
- {
- funcInfo->funcObjRegister = funcInfo->NextVarRegister();
- }
- else
- {
- funcInfo->funcObjRegister = funcSym->GetLocation();
- }
- }
- else
- {
- funcInfo->funcObjRegister = funcInfo->NextVarRegister();
- }
- Assert(funcInfo->funcObjRegister != Js::Constants::NoRegister);
- }
- ParseNode *pnode;
- Symbol *sym;
- if (funcInfo->GetHasArguments())
- {
- // Process function's formal parameters
- MapFormals(pnodeFnc, [&](ParseNode *pnode)
- {
- if (pnode->IsVarLetOrConst())
- {
- pnode->AsParseNodeVar()->sym->EnsureScopeSlot(this, funcInfo);
- }
- });
- MapFormalsFromPattern(pnodeFnc, [&](ParseNode *pnode) { pnode->AsParseNodeVar()->sym->EnsureScopeSlot(this, funcInfo); });
- // Only allocate scope slot for "arguments" when really necessary. "hasDeferredChild"
- // doesn't require scope slot for "arguments" because inner functions can't access
- // outer function's arguments directly.
- sym = funcInfo->GetArgumentsSymbol();
- Assert(sym);
- if (sym->NeedsSlotAlloc(this, funcInfo))
- {
- sym->EnsureScopeSlot(this, funcInfo);
- }
- }
- sym = funcInfo->root->GetFuncSymbol();
- if (sym && sym->NeedsSlotAlloc(this, funcInfo))
- {
- if (funcInfo->funcExprScope && funcInfo->funcExprScope->GetIsObject())
- {
- sym->SetScopeSlot(0);
- }
- else if (funcInfo->GetFuncExprNameReference())
- {
- sym->EnsureScopeSlot(this, funcInfo);
- }
- }
- if (!funcInfo->GetHasArguments())
- {
- Symbol *formal;
- Js::ArgSlot pos = 1;
- auto moveArgToReg = [&](ParseNode *pnode)
- {
- if (pnode->IsVarLetOrConst())
- {
- formal = pnode->AsParseNodeVar()->sym;
- // Get the param from its argument position into its assigned register.
- // The position should match the location; otherwise, it has been shadowed by parameter with the same name.
- if (formal->GetLocation() + 1 == pos)
- {
- pnode->AsParseNodeVar()->sym->EnsureScopeSlot(this, funcInfo);
- }
- }
- pos++;
- };
- MapFormals(pnodeFnc, moveArgToReg);
- MapFormalsFromPattern(pnodeFnc, [&](ParseNode *pnode) { pnode->AsParseNodeVar()->sym->EnsureScopeSlot(this, funcInfo); });
- }
- for (pnode = pnodeFnc->pnodeVars; pnode; pnode = pnode->AsParseNodeVar()->pnodeNext)
- {
- sym = pnode->AsParseNodeVar()->sym;
- if (!(pnode->AsParseNodeVar()->isBlockScopeFncDeclVar && sym->GetIsBlockVar()))
- {
- if (sym->GetIsCatch() || (pnode->nop == knopVarDecl && sym->GetIsBlockVar()))
- {
- sym = funcInfo->bodyScope->FindLocalSymbol(sym->GetName());
- }
- if (sym->GetSymbolType() == STVariable && !sym->IsArguments())
- {
- sym->EnsureScopeSlot(this, funcInfo);
- }
- }
- }
- auto ensureFncDeclScopeSlots = [&](ParseNode *pnodeScope)
- {
- for (pnode = pnodeScope; pnode;)
- {
- switch (pnode->nop)
- {
- case knopFncDecl:
- if (pnode->AsParseNodeFnc()->IsDeclaration())
- {
- EnsureFncDeclScopeSlot(pnode->AsParseNodeFnc(), funcInfo);
- }
- pnode = pnode->AsParseNodeFnc()->pnodeNext;
- break;
- case knopBlock:
- pnode = pnode->AsParseNodeBlock()->pnodeNext;
- break;
- case knopCatch:
- pnode = pnode->AsParseNodeCatch()->pnodeNext;
- break;
- case knopWith:
- pnode = pnode->AsParseNodeWith()->pnodeNext;
- break;
- }
- }
- };
- pnodeFnc->MapContainerScopes(ensureFncDeclScopeSlots);
- if (pnodeFnc->pnodeBody)
- {
- Assert(pnodeFnc->pnodeScopes->nop == knopBlock);
- this->EnsureLetConstScopeSlots(pnodeFnc->pnodeBodyScope, funcInfo);
- }
- }
- else
- {
- ParseNode *pnode;
- Symbol *sym;
- pnodeFnc->MapContainerScopes([&](ParseNode *pnodeScope) { this->EnsureFncScopeSlots(pnodeScope, funcInfo); });
- for (pnode = pnodeFnc->pnodeVars; pnode; pnode = pnode->AsParseNodeVar()->pnodeNext)
- {
- sym = pnode->AsParseNodeVar()->sym;
- if (!(pnode->AsParseNodeVar()->isBlockScopeFncDeclVar && sym->GetIsBlockVar()))
- {
- if (sym->GetIsCatch() || (pnode->nop == knopVarDecl && sym->GetIsBlockVar()))
- {
- sym = funcInfo->bodyScope->FindLocalSymbol(sym->GetName());
- }
- if (sym->GetSymbolType() == STVariable && sym->NeedsSlotAlloc(this, funcInfo) && !sym->IsArguments())
- {
- sym->EnsureScopeSlot(this, funcInfo);
- }
- }
- }
- auto ensureScopeSlot = [&](ParseNode *pnode)
- {
- if (pnode->IsVarLetOrConst())
- {
- sym = pnode->AsParseNodeVar()->sym;
- if (sym->GetSymbolType() == STFormal && sym->NeedsSlotAlloc(this, funcInfo))
- {
- sym->EnsureScopeSlot(this, funcInfo);
- }
- }
- };
- // Process function's formal parameters
- MapFormals(pnodeFnc, ensureScopeSlot);
- MapFormalsFromPattern(pnodeFnc, ensureScopeSlot);
- if (funcInfo->GetHasArguments())
- {
- sym = funcInfo->GetArgumentsSymbol();
- Assert(sym);
- // There is no eval so the arguments may be captured in a lambda.
- // But we cannot relay on slots getting allocated while the lambda is emitted as the function body may be reparsed.
- sym->EnsureScopeSlot(this, funcInfo);
- }
- if (pnodeFnc->pnodeBody)
- {
- this->EnsureLetConstScopeSlots(pnodeFnc->pnodeScopes, funcInfo);
- this->EnsureLetConstScopeSlots(pnodeFnc->pnodeBodyScope, funcInfo);
- }
- }
- // When we have split scope and body scope does not have any scope slots allocated, we don't have to mark the body scope as mustinstantiate.
- if (funcInfo->frameObjRegister != Js::Constants::NoRegister)
- {
- bodyScope->SetMustInstantiate(true);
- }
- else if (pnodeFnc->IsBodyAndParamScopeMerged() || bodyScope->GetScopeSlotCount() != 0)
- {
- bodyScope->SetMustInstantiate(funcInfo->frameSlotsRegister != Js::Constants::NoRegister);
- }
- if (!pnodeFnc->IsBodyAndParamScopeMerged())
- {
- if (funcInfo->frameObjRegister != Js::Constants::NoRegister)
- {
- paramScope->SetMustInstantiate(true);
- }
- else
- {
- // In the case of function expression being captured in the param scope the hasownlocalinclosure will be false for param scope,
- // as function expression symbol stays in the function expression scope. We don't have to set mustinstantiate for param scope in that case.
- paramScope->SetMustInstantiate(paramScope->GetHasOwnLocalInClosure());
- }
- }
- }
- else
- {
- bool newScopeForEval = (funcInfo->byteCodeFunction->GetIsStrictMode() && (this->GetFlags() & fscrEval));
- if (newScopeForEval)
- {
- Assert(bodyScope->GetIsObject());
- }
- }
- }
- if (!funcInfo->IsBodyAndParamScopeMerged())
- {
- ParseNodeBlock * paramBlock = pnodeFnc->pnodeScopes;
- Assert(paramBlock->blockType == Parameter);
- PushScope(paramScope);
- // While emitting the functions we have to stop when we see the body scope block.
- // Otherwise functions defined in the body scope will not be able to get the right references.
- this->EmitScopeList(paramBlock->pnodeScopes, pnodeFnc->pnodeBodyScope);
- Assert(this->GetCurrentScope() == paramScope);
- }
- PushScope(bodyScope);
- }
- void ByteCodeGenerator::EmitModuleExportAccess(Symbol* sym, Js::OpCode opcode, Js::RegSlot location, FuncInfo* funcInfo)
- {
- if (EnsureSymbolModuleSlots(sym, funcInfo))
- {
- this->Writer()->SlotI2(opcode, location, sym->GetModuleIndex(), sym->GetScopeSlot());
- }
- else
- {
- this->Writer()->W1(Js::OpCode::RuntimeReferenceError, SCODE_CODE(ERRInvalidExportName));
- if (opcode == Js::OpCode::LdModuleSlot)
- {
- this->Writer()->Reg1(Js::OpCode::LdUndef, location);
- }
- }
- }
- bool ByteCodeGenerator::EnsureSymbolModuleSlots(Symbol* sym, FuncInfo* funcInfo)
- {
- Assert(sym->GetIsModuleExportStorage());
- if (sym->GetModuleIndex() != Js::Constants::NoProperty && sym->GetScopeSlot() != Js::Constants::NoProperty)
- {
- return true;
- }
- Js::JavascriptLibrary* library = this->GetScriptContext()->GetLibrary();
- library->EnsureModuleRecordList();
- uint moduleIndex = this->GetModuleID();
- uint moduleSlotIndex;
- Js::SourceTextModuleRecord* moduleRecord = library->GetModuleRecord(moduleIndex);
- if (sym->GetIsModuleImport())
- {
- Js::PropertyId localImportNameId = sym->EnsurePosition(funcInfo);
- Js::ModuleNameRecord* moduleNameRecord = nullptr;
- if (!moduleRecord->ResolveImport(localImportNameId, &moduleNameRecord))
- {
- return false;
- }
- AnalysisAssert(moduleNameRecord != nullptr);
- Assert(moduleNameRecord->module->IsSourceTextModuleRecord());
- Js::SourceTextModuleRecord* resolvedModuleRecord =
- (Js::SourceTextModuleRecord*)PointerValue(moduleNameRecord->module);
- moduleIndex = resolvedModuleRecord->GetModuleId();
- moduleSlotIndex = resolvedModuleRecord->GetLocalExportSlotIndexByLocalName(moduleNameRecord->bindingName);
- }
- else
- {
- Js::PropertyId exportNameId = sym->EnsurePosition(funcInfo);
- moduleSlotIndex = moduleRecord->GetLocalExportSlotIndexByLocalName(exportNameId);
- }
- sym->SetModuleIndex(moduleIndex);
- sym->SetScopeSlot(moduleSlotIndex);
- return true;
- }
- void ByteCodeGenerator::EmitAssignmentToDefaultModuleExport(ParseNode* pnode, FuncInfo* funcInfo)
- {
- // We are assigning pnode to the default export of the current module.
- uint moduleIndex = this->GetModuleID();
- Js::JavascriptLibrary* library = this->GetScriptContext()->GetLibrary();
- library->EnsureModuleRecordList();
- Js::SourceTextModuleRecord* moduleRecord = library->GetModuleRecord(moduleIndex);
- uint moduleSlotIndex = moduleRecord->GetLocalExportSlotIndexByExportName(Js::PropertyIds::default_);
- this->Writer()->SlotI2(Js::OpCode::StModuleSlot, pnode->location, moduleIndex, moduleSlotIndex);
- }
- void ByteCodeGenerator::EnsureLetConstScopeSlots(ParseNodeBlock *pnodeBlock, FuncInfo *funcInfo)
- {
- bool callsEval = pnodeBlock->GetCallsEval() || pnodeBlock->GetChildCallsEval();
- auto ensureLetConstSlots = ([this, funcInfo, callsEval](ParseNode *pnode)
- {
- Symbol *sym = pnode->AsParseNodeVar()->sym;
- if (callsEval || sym->NeedsSlotAlloc(this, funcInfo))
- {
- sym->EnsureScopeSlot(this, funcInfo);
- this->ProcessCapturedSym(sym);
- }
- });
- IterateBlockScopedVariables(pnodeBlock, ensureLetConstSlots);
- }
- void ByteCodeGenerator::EnsureFncScopeSlots(ParseNode *pnode, FuncInfo *funcInfo)
- {
- while (pnode)
- {
- switch (pnode->nop)
- {
- case knopFncDecl:
- if (pnode->AsParseNodeFnc()->IsDeclaration())
- {
- CheckFncDeclScopeSlot(pnode->AsParseNodeFnc(), funcInfo);
- }
- pnode = pnode->AsParseNodeFnc()->pnodeNext;
- break;
- case knopBlock:
- pnode = pnode->AsParseNodeBlock()->pnodeNext;
- break;
- case knopCatch:
- pnode = pnode->AsParseNodeCatch()->pnodeNext;
- break;
- case knopWith:
- pnode = pnode->AsParseNodeWith()->pnodeNext;
- break;
- }
- }
- }
- void ByteCodeGenerator::EndEmitFunction(ParseNodeFnc *pnodeFnc)
- {
- Assert(pnodeFnc->nop == knopFncDecl || pnodeFnc->nop == knopProg);
- Assert(pnodeFnc->nop == knopFncDecl && currentScope->GetEnclosingScope() != nullptr || pnodeFnc->nop == knopProg);
- PopScope(); // function body
- FuncInfo *funcInfo = pnodeFnc->funcInfo;
- Scope* paramScope = funcInfo->paramScope;
- if (!funcInfo->IsBodyAndParamScopeMerged())
- {
- Assert(this->GetCurrentScope() == paramScope);
- PopScope(); // Pop the param scope
- }
- if (funcInfo->byteCodeFunction->IsFunctionParsed() && funcInfo->root->pnodeBody != nullptr)
- {
- // StartEmitFunction omits the matching PushScope for already-parsed functions.
- // TODO: Refactor Start and EndEmitFunction for clarity.
- Scope *scope = funcInfo->funcExprScope;
- if (scope && scope->GetMustInstantiate())
- {
- Assert(currentScope == scope);
- PopScope();
- }
- }
- Assert(funcInfo == this->TopFuncInfo());
- PopFuncInfo(_u("EndEmitFunction"));
- }
- void ByteCodeGenerator::StartEmitCatch(ParseNodeCatch *pnodeCatch)
- {
- Assert(pnodeCatch->nop == knopCatch);
- Scope *scope = pnodeCatch->scope;
- FuncInfo *funcInfo = scope->GetFunc();
- // Catch scope is a dynamic object if it can be passed to a scoped lookup helper (i.e., eval is present or we're in an event handler).
- if (funcInfo->GetCallsEval() || funcInfo->GetChildCallsEval() || (this->flags & (fscrEval | fscrImplicitThis)))
- {
- scope->SetIsObject();
- }
- ParseNode * pnodeParam = pnodeCatch->GetParam();
- if (pnodeParam->nop == knopParamPattern)
- {
- scope->SetCapturesAll(funcInfo->GetCallsEval() || funcInfo->GetChildCallsEval());
- scope->SetMustInstantiate(scope->Count() > 0 && (scope->GetMustInstantiate() || scope->GetCapturesAll() || funcInfo->IsGlobalFunction()));
- Parser::MapBindIdentifier(pnodeParam->AsParseNodeParamPattern()->pnode1, [&](ParseNodePtr item)
- {
- Symbol *sym = item->AsParseNodeVar()->sym;
- if (funcInfo->IsGlobalFunction())
- {
- sym->SetIsGlobalCatch(true);
- }
- Assert(sym->GetScopeSlot() == Js::Constants::NoProperty);
- if (sym->NeedsSlotAlloc(this, funcInfo))
- {
- sym->EnsureScopeSlot(this, funcInfo);
- }
- });
- // In the case of pattern we will always going to push the scope.
- PushScope(scope);
- }
- else
- {
- Symbol *sym = pnodeParam->AsParseNodeName()->sym;
- // Catch object is stored in the catch scope if there may be an ambiguous lookup or a var declaration that hides it.
- scope->SetCapturesAll(funcInfo->GetCallsEval() || funcInfo->GetChildCallsEval() || sym->GetHasNonLocalReference());
- scope->SetMustInstantiate(scope->GetCapturesAll() || funcInfo->IsGlobalFunction());
- if (funcInfo->IsGlobalFunction())
- {
- sym->SetIsGlobalCatch(true);
- }
- if (scope->GetMustInstantiate())
- {
- if (sym->IsInSlot(this, funcInfo))
- {
- // Since there is only one symbol we are pushing to slot.
- // Also in order to make IsInSlot to return true - forcing the sym-has-non-local-reference.
- this->ProcessCapturedSym(sym);
- sym->EnsureScopeSlot(this, funcInfo);
- }
- }
- PushScope(scope);
- }
- }
- void ByteCodeGenerator::EndEmitCatch(ParseNodeCatch *pnodeCatch)
- {
- Assert(pnodeCatch->nop == knopCatch);
- Assert(currentScope == pnodeCatch->scope);
- PopScope();
- }
- void ByteCodeGenerator::StartEmitBlock(ParseNodeBlock *pnodeBlock)
- {
- if (!BlockHasOwnScope(pnodeBlock, this))
- {
- return;
- }
- Assert(pnodeBlock->nop == knopBlock);
- PushBlock(pnodeBlock);
- Scope *scope = pnodeBlock->scope;
- if (pnodeBlock->GetCallsEval() || pnodeBlock->GetChildCallsEval() || (this->flags & (fscrEval | fscrImplicitThis)))
- {
- Assert(scope->GetIsObject());
- }
- // TODO: Consider nested deferred parsing.
- if (scope->GetMustInstantiate())
- {
- FuncInfo *funcInfo = scope->GetFunc();
- this->EnsureFncScopeSlots(pnodeBlock->pnodeScopes, funcInfo);
- this->EnsureLetConstScopeSlots(pnodeBlock, funcInfo);
- PushScope(scope);
- }
- }
- void ByteCodeGenerator::EndEmitBlock(ParseNodeBlock *pnodeBlock)
- {
- if (!BlockHasOwnScope(pnodeBlock, this))
- {
- return;
- }
- Assert(pnodeBlock->nop == knopBlock);
- Scope *scope = pnodeBlock->scope;
- if (scope && scope->GetMustInstantiate())
- {
- Assert(currentScope == pnodeBlock->scope);
- PopScope();
- }
- PopBlock();
- }
- void ByteCodeGenerator::StartEmitWith(ParseNode *pnodeWith)
- {
- Assert(pnodeWith->nop == knopWith);
- Scope *scope = pnodeWith->AsParseNodeWith()->scope;
- AssertOrFailFast(scope->GetIsObject());
- PushScope(scope);
- }
- void ByteCodeGenerator::EndEmitWith(ParseNode *pnodeWith)
- {
- Assert(pnodeWith->nop == knopWith);
- Assert(currentScope == pnodeWith->AsParseNodeWith()->scope);
- PopScope();
- }
- Js::RegSlot ByteCodeGenerator::PrependLocalScopes(Js::RegSlot evalEnv, Js::RegSlot tempLoc, FuncInfo *funcInfo)
- {
- Scope *currScope = this->currentScope;
- Scope *funcScope = funcInfo->GetCurrentChildScope() ? funcInfo->GetCurrentChildScope() : funcInfo->GetBodyScope();
- if (currScope == funcScope)
- {
- return evalEnv;
- }
- bool acquireTempLoc = tempLoc == Js::Constants::NoRegister;
- if (acquireTempLoc)
- {
- tempLoc = funcInfo->AcquireTmpRegister();
- }
- // The with/catch objects must be prepended to the environment we pass to eval() or to a func declared inside with,
- // but the list must first be reversed so that innermost scopes appear first in the list.
- while (currScope != funcScope)
- {
- Scope *innerScope;
- for (innerScope = currScope; innerScope->GetEnclosingScope() != funcScope; innerScope = innerScope->GetEnclosingScope())
- ;
- if (innerScope->GetMustInstantiate())
- {
- if (!innerScope->HasInnerScopeIndex())
- {
- if (evalEnv == funcInfo->GetEnvRegister() || evalEnv == funcInfo->frameDisplayRegister)
- {
- this->m_writer.Reg2(Js::OpCode::LdInnerFrameDisplayNoParent, tempLoc, innerScope->GetLocation());
- }
- else
- {
- this->m_writer.Reg3(Js::OpCode::LdInnerFrameDisplay, tempLoc, innerScope->GetLocation(), evalEnv);
- }
- }
- else
- {
- if (evalEnv == funcInfo->GetEnvRegister() || evalEnv == funcInfo->frameDisplayRegister)
- {
- this->m_writer.Reg1Unsigned1(Js::OpCode::LdIndexedFrameDisplayNoParent, tempLoc, innerScope->GetInnerScopeIndex());
- }
- else
- {
- this->m_writer.Reg2Int1(Js::OpCode::LdIndexedFrameDisplay, tempLoc, evalEnv, innerScope->GetInnerScopeIndex());
- }
- }
- evalEnv = tempLoc;
- }
- funcScope = innerScope;
- }
- if (acquireTempLoc)
- {
- funcInfo->ReleaseTmpRegister(tempLoc);
- }
- return evalEnv;
- }
- void ByteCodeGenerator::EmitLoadInstance(Symbol *sym, IdentPtr pid, Js::RegSlot *pThisLocation, Js::RegSlot *pInstLocation, FuncInfo *funcInfo)
- {
- Js::ByteCodeLabel doneLabel = 0;
- bool fLabelDefined = false;
- Js::RegSlot scopeLocation = Js::Constants::NoRegister;
- Js::RegSlot thisLocation = *pThisLocation;
- Js::RegSlot instLocation = *pInstLocation;
- Js::PropertyId envIndex = -1;
- Scope *scope = nullptr;
- Scope *symScope = sym ? sym->GetScope() : this->globalScope;
- Assert(symScope);
- if (sym != nullptr && sym->GetIsModuleExportStorage())
- {
- *pInstLocation = Js::Constants::NoRegister;
- return;
- }
- for (;;)
- {
- scope = this->FindScopeForSym(symScope, scope, &envIndex, funcInfo);
- if (scope == this->globalScope)
- {
- break;
- }
- if (scope != symScope)
- {
- // We're not sure where the function is (eval/with/etc).
- // So we're going to need registers to hold the instance where we (dynamically) find
- // the function, and possibly to hold the "this" pointer we will pass to it.
- // Assign them here so that they can't overlap with the scopeLocation assigned below.
- // Otherwise we wind up with temp lifetime confusion in the IRBuilder. (Win8 281689)
- if (instLocation == Js::Constants::NoRegister)
- {
- instLocation = funcInfo->AcquireTmpRegister();
- // The "this" pointer will not be the same as the instance, so give it its own register.
- thisLocation = funcInfo->AcquireTmpRegister();
- }
- }
- if (envIndex == -1)
- {
- Assert(funcInfo == scope->GetFunc());
- scopeLocation = scope->GetLocation();
- }
- if (scope == symScope)
- {
- break;
- }
- // Found a scope to which the property may have been added.
- Assert(scope && scope->GetIsDynamic());
- if (!fLabelDefined)
- {
- fLabelDefined = true;
- doneLabel = this->m_writer.DefineLabel();
- }
- Js::ByteCodeLabel nextLabel = this->m_writer.DefineLabel();
- Js::PropertyId propertyId = sym ? sym->EnsurePosition(this) : pid->GetPropertyId();
- bool unwrapWithObj = scope->GetScopeType() == ScopeType_With && scriptContext->GetConfig()->IsES6UnscopablesEnabled();
- if (envIndex != -1)
- {
- this->m_writer.BrEnvProperty(
- Js::OpCode::BrOnNoEnvProperty, nextLabel,
- funcInfo->FindOrAddReferencedPropertyId(propertyId),
- envIndex + Js::FrameDisplay::GetOffsetOfScopes() / sizeof(Js::Var));
- Js::RegSlot tmpReg = funcInfo->AcquireTmpRegister();
- AssertOrFailFast(scope->GetIsObject());
- this->m_writer.SlotI1(Js::OpCode::LdEnvObj, tmpReg,
- envIndex + Js::FrameDisplay::GetOffsetOfScopes() / sizeof(Js::Var));
- Js::OpCode op = unwrapWithObj ? Js::OpCode::UnwrapWithObj : Js::OpCode::Ld_A;
- this->m_writer.Reg2(op, instLocation, tmpReg);
- if (thisLocation != Js::Constants::NoRegister)
- {
- this->m_writer.Reg2(op, thisLocation, tmpReg);
- }
- funcInfo->ReleaseTmpRegister(tmpReg);
- }
- else if (scopeLocation != Js::Constants::NoRegister && scopeLocation == funcInfo->frameObjRegister)
- {
- this->m_writer.BrLocalProperty(Js::OpCode::BrOnNoLocalProperty, nextLabel,
- funcInfo->FindOrAddReferencedPropertyId(propertyId));
- Assert(!unwrapWithObj);
- AssertOrFailFast(scope->GetIsObject());
- this->m_writer.Reg1(Js::OpCode::LdLocalObj, instLocation);
- if (thisLocation != Js::Constants::NoRegister)
- {
- this->m_writer.Reg1(Js::OpCode::LdLocalObj, thisLocation);
- }
- }
- else
- {
- this->m_writer.BrProperty(Js::OpCode::BrOnNoProperty, nextLabel, scopeLocation,
- funcInfo->FindOrAddReferencedPropertyId(propertyId));
- Js::OpCode op = unwrapWithObj ? Js::OpCode::UnwrapWithObj : Js::OpCode::Ld_A;
- this->m_writer.Reg2(op, instLocation, scopeLocation);
- if (thisLocation != Js::Constants::NoRegister)
- {
- this->m_writer.Reg2(op, thisLocation, scopeLocation);
- }
- }
- this->m_writer.Br(doneLabel);
- this->m_writer.MarkLabel(nextLabel);
- }
- if (sym == nullptr || sym->GetIsGlobal())
- {
- if (this->flags & (fscrEval | fscrImplicitThis))
- {
- // Load of a symbol with unknown scope from within eval.
- // Get it from the closure environment.
- if (instLocation == Js::Constants::NoRegister)
- {
- instLocation = funcInfo->AcquireTmpRegister();
- }
- // TODO: It should be possible to avoid this double call to ScopedLdInst by having it return both
- // results at once. The reason for the uncertainty here is that we don't know whether the callee
- // belongs to a "with" object. If it does, we have to pass the "with" object as "this"; in all other
- // cases, we pass "undefined". For now, there are apparently no significant performance issues.
- Js::PropertyId propertyId = sym ? sym->EnsurePosition(this) : pid->GetPropertyId();
- if (thisLocation == Js::Constants::NoRegister)
- {
- thisLocation = funcInfo->AcquireTmpRegister();
- }
- this->m_writer.ScopedProperty2(Js::OpCode::ScopedLdInst, instLocation,
- funcInfo->FindOrAddReferencedPropertyId(propertyId), thisLocation);
- }
- else
- {
- if (instLocation == Js::Constants::NoRegister)
- {
- instLocation = ByteCodeGenerator::RootObjectRegister;
- }
- else
- {
- this->m_writer.Reg2(Js::OpCode::Ld_A, instLocation, ByteCodeGenerator::RootObjectRegister);
- }
- if (thisLocation == Js::Constants::NoRegister)
- {
- thisLocation = funcInfo->undefinedConstantRegister;
- }
- else
- {
- this->m_writer.Reg2(Js::OpCode::Ld_A, thisLocation, funcInfo->undefinedConstantRegister);
- }
- }
- }
- else if (instLocation != Js::Constants::NoRegister)
- {
- if (envIndex != -1)
- {
- AssertOrFailFast(scope->GetIsObject());
- this->m_writer.SlotI1(Js::OpCode::LdEnvObj, instLocation,
- envIndex + Js::FrameDisplay::GetOffsetOfScopes() / sizeof(Js::Var));
- }
- else if (scope->HasInnerScopeIndex())
- {
- this->m_writer.Reg1Unsigned1(Js::OpCode::LdInnerScope, instLocation, scope->GetInnerScopeIndex());
- }
- else if (symScope == funcInfo->GetParamScope())
- {
- Assert(funcInfo->frameObjRegister != Js::Constants::NoRegister && !funcInfo->IsBodyAndParamScopeMerged());
- this->m_writer.Reg1(Js::OpCode::LdParamObj, instLocation);
- }
- else if (symScope != funcInfo->GetBodyScope())
- {
- this->m_writer.Reg2(Js::OpCode::Ld_A, instLocation, scopeLocation);
- }
- else
- {
- Assert(funcInfo->frameObjRegister != Js::Constants::NoRegister);
- this->m_writer.Reg1(Js::OpCode::LdLocalObj, instLocation);
- }
- if (thisLocation != Js::Constants::NoRegister)
- {
- this->m_writer.Reg2(Js::OpCode::Ld_A, thisLocation, funcInfo->undefinedConstantRegister);
- }
- else
- {
- thisLocation = funcInfo->undefinedConstantRegister;
- }
- }
- *pThisLocation = thisLocation;
- *pInstLocation = instLocation;
- if (fLabelDefined)
- {
- this->m_writer.MarkLabel(doneLabel);
- }
- }
- void ByteCodeGenerator::EmitGlobalFncDeclInit(Js::RegSlot rhsLocation, Js::PropertyId propertyId, FuncInfo * funcInfo)
- {
- // Note: declared variables and assignments in the global function go to the root object directly.
- if (this->flags & fscrEval)
- {
- // Func decl's always get their init values before any use, so we don't pre-initialize the property to undef.
- // That means that we have to use ScopedInitFld so that we initialize the property on the right instance
- // even if the instance doesn't have the property yet (i.e., collapse the init-to-undef and the store
- // into one operation). See WOOB 1121763 and 1120973.
- this->m_writer.ScopedProperty(Js::OpCode::ScopedInitFunc, rhsLocation,
- funcInfo->FindOrAddReferencedPropertyId(propertyId));
- }
- else
- {
- this->EmitPatchableRootProperty(Js::OpCode::InitRootFld, rhsLocation, propertyId, false, true, funcInfo);
- }
- }
- void
- ByteCodeGenerator::EmitPatchableRootProperty(Js::OpCode opcode,
- Js::RegSlot regSlot, Js::PropertyId propertyId, bool isLoadMethod, bool isStore, FuncInfo * funcInfo)
- {
- uint cacheId = funcInfo->FindOrAddRootObjectInlineCacheId(propertyId, isLoadMethod, isStore);
- this->m_writer.PatchableRootProperty(opcode, regSlot, cacheId, isLoadMethod, isStore);
- }
- void ByteCodeGenerator::EmitLocalPropInit(Js::RegSlot rhsLocation, Symbol *sym, FuncInfo *funcInfo)
- {
- Scope *scope = sym->GetScope();
- // Check consistency of sym->IsInSlot.
- Assert(sym->NeedsSlotAlloc(this, funcInfo) || sym->GetScopeSlot() == Js::Constants::NoProperty);
- // Arrived at the scope in which the property was defined.
- if (sym->NeedsSlotAlloc(this, funcInfo))
- {
- // The property is in memory rather than register. We'll have to load it from the slots.
- if (scope->GetIsObject())
- {
- Assert(!this->TopFuncInfo()->GetParsedFunctionBody()->DoStackNestedFunc());
- Js::PropertyId propertyId = sym->EnsurePosition(this);
- Js::RegSlot objReg;
- if (scope->HasInnerScopeIndex())
- {
- objReg = funcInfo->InnerScopeToRegSlot(scope);
- }
- else
- {
- objReg = scope->GetLocation();
- }
- uint cacheId = funcInfo->FindOrAddInlineCacheId(objReg, propertyId, false, true);
- Js::OpCode op = this->GetInitFldOp(scope, objReg, funcInfo, sym->GetIsNonSimpleParameter());
- if (objReg != Js::Constants::NoRegister && objReg == funcInfo->frameObjRegister)
- {
- this->m_writer.ElementP(op, rhsLocation, cacheId);
- }
- else if (scope->HasInnerScopeIndex())
- {
- this->m_writer.ElementPIndexed(op, rhsLocation, scope->GetInnerScopeIndex(), cacheId);
- }
- else
- {
- this->m_writer.PatchableProperty(op, rhsLocation, scope->GetLocation(), cacheId);
- }
- }
- else
- {
- // Make sure the property has a slot. This will bump up the size of the slot array if necessary.
- Js::PropertyId slot = sym->EnsureScopeSlot(this, funcInfo);
- Js::RegSlot slotReg = scope->GetCanMerge() ? funcInfo->frameSlotsRegister : scope->GetLocation();
- // Now store the property to its slot.
- Js::OpCode op = this->GetStSlotOp(scope, -1, slotReg, false, funcInfo);
- if (slotReg != Js::Constants::NoRegister && slotReg == funcInfo->frameSlotsRegister)
- {
- this->m_writer.SlotI1(op, rhsLocation, slot + Js::ScopeSlots::FirstSlotIndex);
- }
- else
- {
- this->m_writer.SlotI2(op, rhsLocation, scope->GetInnerScopeIndex(), slot + Js::ScopeSlots::FirstSlotIndex);
- }
- }
- }
- if (sym->GetLocation() != Js::Constants::NoRegister && rhsLocation != sym->GetLocation())
- {
- this->m_writer.Reg2(Js::OpCode::Ld_A, sym->GetLocation(), rhsLocation);
- }
- }
- Js::OpCode
- ByteCodeGenerator::GetStSlotOp(Scope *scope, int envIndex, Js::RegSlot scopeLocation, bool chkBlockVar, FuncInfo *funcInfo)
- {
- Js::OpCode op;
- if (envIndex != -1)
- {
- if (scope->GetIsObject())
- {
- op = Js::OpCode::StEnvObjSlot;
- }
- else
- {
- op = Js::OpCode::StEnvSlot;
- }
- }
- else if (scopeLocation != Js::Constants::NoRegister &&
- scopeLocation == funcInfo->frameSlotsRegister)
- {
- if (scope->GetScopeType() == ScopeType_Parameter && scope != scope->GetFunc()->GetCurrentChildScope())
- {
- // Symbol is from the param scope of a split scope function and we are emitting the body.
- // We should use the param scope's bytecode now.
- Assert(!funcInfo->IsBodyAndParamScopeMerged());
- op = Js::OpCode::StParamSlot;
- }
- else
- {
- op = Js::OpCode::StLocalSlot;
- }
- }
- else if (scopeLocation != Js::Constants::NoRegister &&
- scopeLocation == funcInfo->frameObjRegister)
- {
- if (scope->GetScopeType() == ScopeType_Parameter && scope != scope->GetFunc()->GetCurrentChildScope())
- {
- // Symbol is from the param scope of a split scope function and we are emitting the body.
- // We should use the param scope's bytecode now.
- Assert(!funcInfo->IsBodyAndParamScopeMerged());
- op = Js::OpCode::StParamObjSlot;
- }
- else
- {
- op = Js::OpCode::StLocalObjSlot;
- }
- }
- else
- {
- Assert(scope->HasInnerScopeIndex());
- if (scope->GetIsObject())
- {
- op = Js::OpCode::StInnerObjSlot;
- }
- else
- {
- op = Js::OpCode::StInnerSlot;
- }
- }
- if (chkBlockVar)
- {
- op = this->ToChkUndeclOp(op);
- }
- return op;
- }
- Js::OpCode
- ByteCodeGenerator::GetInitFldOp(Scope *scope, Js::RegSlot scopeLocation, FuncInfo *funcInfo, bool letDecl)
- {
- Js::OpCode op;
- if (scopeLocation != Js::Constants::NoRegister &&
- scopeLocation == funcInfo->frameObjRegister)
- {
- op = letDecl ? Js::OpCode::InitLocalLetFld : Js::OpCode::InitLocalFld;
- }
- else if (scope->HasInnerScopeIndex())
- {
- op = letDecl ? Js::OpCode::InitInnerLetFld : Js::OpCode::InitInnerFld;
- }
- else
- {
- op = letDecl ? Js::OpCode::InitLetFld : Js::OpCode::InitFld;
- }
- return op;
- }
- void ByteCodeGenerator::EmitPropStore(Js::RegSlot rhsLocation, Symbol *sym, IdentPtr pid, FuncInfo *funcInfo, bool isLetDecl, bool isConstDecl, bool isFncDeclVar, bool skipUseBeforeDeclarationCheck)
- {
- Js::ByteCodeLabel doneLabel = 0;
- bool fLabelDefined = false;
- Js::PropertyId envIndex = -1;
- Scope *symScope = sym == nullptr || sym->GetIsGlobal() ? this->globalScope : sym->GetScope();
- Assert(symScope);
- // isFncDeclVar denotes that the symbol being stored to here is the var
- // binding of a function declaration and we know we want to store directly
- // to it, skipping over any dynamic scopes that may lie in between.
- Scope *scope = nullptr;
- Js::RegSlot scopeLocation = Js::Constants::NoRegister;
- bool scopeAcquired = false;
- Js::OpCode op;
- if (sym && sym->GetIsModuleExportStorage())
- {
- if (!isConstDecl && sym->GetDecl() && sym->GetDecl()->nop == knopConstDecl)
- {
- this->m_writer.W1(Js::OpCode::RuntimeTypeError, SCODE_CODE(ERRAssignmentToConst));
- }
- EmitModuleExportAccess(sym, Js::OpCode::StModuleSlot, rhsLocation, funcInfo);
- return;
- }
- if (isFncDeclVar)
- {
- // async functions allow for the fncDeclVar to be in the body or parameter scope
- // of the parent function, so we need to calculate envIndex in lieu of the while
- // loop below.
- do
- {
- scope = this->FindScopeForSym(symScope, scope, &envIndex, funcInfo);
- } while (scope != symScope);
- Assert(scope == symScope);
- scopeLocation = scope->GetLocation();
- }
- while (!isFncDeclVar)
- {
- scope = this->FindScopeForSym(symScope, scope, &envIndex, funcInfo);
- if (scope == this->globalScope)
- {
- break;
- }
- if (envIndex == -1)
- {
- Assert(funcInfo == scope->GetFunc());
- scopeLocation = scope->GetLocation();
- }
- if (scope == symScope)
- {
- break;
- }
- // Found a scope to which the property may have been added.
- Assert(scope && scope->GetIsDynamic());
- if (!fLabelDefined)
- {
- fLabelDefined = true;
- doneLabel = this->m_writer.DefineLabel();
- }
- Js::ByteCodeLabel nextLabel = this->m_writer.DefineLabel();
- Js::PropertyId propertyId = sym ? sym->EnsurePosition(this) : pid->GetPropertyId();
- Js::RegSlot unwrappedScopeLocation = scopeLocation;
- bool unwrapWithObj = scope->GetScopeType() == ScopeType_With && scriptContext->GetConfig()->IsES6UnscopablesEnabled();
- if (envIndex != -1)
- {
- this->m_writer.BrEnvProperty(
- Js::OpCode::BrOnNoEnvProperty,
- nextLabel,
- funcInfo->FindOrAddReferencedPropertyId(propertyId),
- envIndex + Js::FrameDisplay::GetOffsetOfScopes() / sizeof(Js::Var));
- Js::RegSlot instLocation = funcInfo->AcquireTmpRegister();
- AssertOrFailFast(scope->GetIsObject());
- this->m_writer.SlotI1(
- Js::OpCode::LdEnvObj,
- instLocation,
- envIndex + Js::FrameDisplay::GetOffsetOfScopes() / sizeof(Js::Var));
- if (unwrapWithObj)
- {
- this->m_writer.Reg2(Js::OpCode::UnwrapWithObj, instLocation, instLocation);
- }
- this->m_writer.PatchableProperty(
- Js::OpCode::StFld,
- rhsLocation,
- instLocation,
- funcInfo->FindOrAddInlineCacheId(instLocation, propertyId, false, true));
- funcInfo->ReleaseTmpRegister(instLocation);
- }
- else if (scopeLocation != Js::Constants::NoRegister && scopeLocation == funcInfo->frameObjRegister)
- {
- this->m_writer.BrLocalProperty(Js::OpCode::BrOnNoLocalProperty, nextLabel,
- funcInfo->FindOrAddReferencedPropertyId(propertyId));
- Assert(!unwrapWithObj);
- this->m_writer.ElementP(Js::OpCode::StLocalFld, rhsLocation,
- funcInfo->FindOrAddInlineCacheId(scopeLocation, propertyId, false, true));
- }
- else
- {
- this->m_writer.BrProperty(Js::OpCode::BrOnNoProperty, nextLabel, scopeLocation,
- funcInfo->FindOrAddReferencedPropertyId(propertyId));
- if (unwrapWithObj)
- {
- unwrappedScopeLocation = funcInfo->AcquireTmpRegister();
- this->m_writer.Reg2(Js::OpCode::UnwrapWithObj, unwrappedScopeLocation, scopeLocation);
- scopeLocation = unwrappedScopeLocation;
- }
- uint cacheId = funcInfo->FindOrAddInlineCacheId(scopeLocation, propertyId, false, true);
- this->m_writer.PatchableProperty(Js::OpCode::StFld, rhsLocation, scopeLocation, cacheId);
- if (unwrapWithObj)
- {
- funcInfo->ReleaseTmpRegister(unwrappedScopeLocation);
- }
- }
- this->m_writer.Br(doneLabel);
- this->m_writer.MarkLabel(nextLabel);
- }
- // Arrived at the scope in which the property was defined.
- if (!skipUseBeforeDeclarationCheck && sym && sym->GetNeedDeclaration() && scope->GetFunc() == funcInfo)
- {
- EmitUseBeforeDeclarationRuntimeError(this, Js::Constants::NoRegister);
- // Intentionally continue on to do normal EmitPropStore behavior so
- // that the bytecode ends up well-formed for the backend. This is
- // in contrast to EmitPropLoad and EmitPropTypeof where they both
- // tell EmitUseBeforeDeclarationRuntimeError to emit a LdUndef in place
- // of their load and then they skip emitting their own bytecode.
- // Potayto potahto.
- }
- if (sym == nullptr || sym->GetIsGlobal())
- {
- Js::PropertyId propertyId = sym ? sym->EnsurePosition(this) : pid->GetPropertyId();
- bool isConsoleScopeLetConst = this->IsConsoleScopeEval() && (isLetDecl || isConstDecl);
- if (this->flags & fscrEval)
- {
- if (funcInfo->byteCodeFunction->GetIsStrictMode() && funcInfo->IsGlobalFunction())
- {
- uint cacheId = funcInfo->FindOrAddInlineCacheId(funcInfo->frameDisplayRegister, propertyId, false, true);
- this->m_writer.ElementP(GetScopedStFldOpCode(funcInfo, isConsoleScopeLetConst), rhsLocation, cacheId);
- }
- else
- {
- uint cacheId = funcInfo->FindOrAddInlineCacheId(funcInfo->GetEnvRegister(), propertyId, false, true);
- // In "eval", store to a symbol with unknown scope goes through the closure environment.
- this->m_writer.ElementP(GetScopedStFldOpCode(funcInfo, isConsoleScopeLetConst), rhsLocation, cacheId);
- }
- }
- else if (this->flags & fscrImplicitThis)
- {
- uint cacheId = funcInfo->FindOrAddInlineCacheId(funcInfo->GetEnvRegister(), propertyId, false, true);
- // In HTML event handler, store to a symbol with unknown scope goes through the closure environment.
- this->m_writer.ElementP(GetScopedStFldOpCode(funcInfo, isConsoleScopeLetConst), rhsLocation, cacheId);
- }
- else
- {
- this->EmitPatchableRootProperty(GetStFldOpCode(funcInfo, true, isLetDecl, isConstDecl, false, forceStrictModeForClassComputedPropertyName), rhsLocation, propertyId, false, true, funcInfo);
- }
- }
- else if (sym->GetIsFuncExpr())
- {
- // Store to function expr variable.
- // strict mode: we need to throw type error
- if (funcInfo->byteCodeFunction->GetIsStrictMode())
- {
- // Note that in this case the sym's location belongs to the parent function, so we can't use it.
- // It doesn't matter which register we use, as long as it's valid for this function.
- this->m_writer.W1(Js::OpCode::RuntimeTypeError, SCODE_CODE(JSERR_CantAssignToReadOnly));
- }
- }
- else if (sym->IsInSlot(this, funcInfo) || envIndex != -1)
- {
- if (!isConstDecl && sym->GetIsConst())
- {
- // This is a case where const reassignment can't be proven statically (e.g., eval, with) so
- // we have to catch it at runtime.
- this->m_writer.W1(
- Js::OpCode::RuntimeTypeError, SCODE_CODE(ERRAssignmentToConst));
- }
- // Make sure the property has a slot. This will bump up the size of the slot array if necessary.
- Js::PropertyId slot = sym->EnsureScopeSlot(this, funcInfo);
- bool chkBlockVar = !isLetDecl && !isConstDecl && NeedCheckBlockVar(sym, scope, funcInfo);
- // The property is in memory rather than register. We'll have to load it from the slots.
- op = this->GetStSlotOp(scope, envIndex, scopeLocation, chkBlockVar, funcInfo);
- if (envIndex != -1)
- {
- this->m_writer.SlotI2(op, rhsLocation,
- envIndex + Js::FrameDisplay::GetOffsetOfScopes() / sizeof(Js::Var),
- slot + (sym->GetScope()->GetIsObject() ? 0 : Js::ScopeSlots::FirstSlotIndex));
- }
- else if (scopeLocation != Js::Constants::NoRegister &&
- (scopeLocation == funcInfo->frameSlotsRegister || scopeLocation == funcInfo->frameObjRegister))
- {
- this->m_writer.SlotI1(op, rhsLocation,
- slot + (sym->GetScope()->GetIsObject() ? 0 : Js::ScopeSlots::FirstSlotIndex));
- }
- else
- {
- Assert(scope->HasInnerScopeIndex());
- this->m_writer.SlotI2(op, rhsLocation, scope->GetInnerScopeIndex(),
- slot + (sym->GetScope()->GetIsObject() ? 0 : Js::ScopeSlots::FirstSlotIndex));
- }
- if (this->ShouldTrackDebuggerMetadata() && (isLetDecl || isConstDecl))
- {
- Js::PropertyId location = scope->GetIsObject() ? sym->GetLocation() : slot;
- this->UpdateDebuggerPropertyInitializationOffset(location, sym->GetPosition(), false);
- }
- }
- else if (isConstDecl)
- {
- this->m_writer.Reg2(Js::OpCode::InitConst, sym->GetLocation(), rhsLocation);
- if (this->ShouldTrackDebuggerMetadata())
- {
- this->UpdateDebuggerPropertyInitializationOffset(sym->GetLocation(), sym->GetPosition());
- }
- }
- else
- {
- if (!isConstDecl && sym->GetDecl() && sym->GetDecl()->nop == knopConstDecl)
- {
- // This is a case where const reassignment can't be proven statically (e.g., eval, with) so
- // we have to catch it at runtime.
- this->m_writer.W1(Js::OpCode::RuntimeTypeError, SCODE_CODE(ERRAssignmentToConst));
- }
- if (rhsLocation != sym->GetLocation())
- {
- this->m_writer.Reg2(Js::OpCode::Ld_A, sym->GetLocation(), rhsLocation);
- if (this->ShouldTrackDebuggerMetadata() && isLetDecl)
- {
- this->UpdateDebuggerPropertyInitializationOffset(sym->GetLocation(), sym->GetPosition());
- }
- }
- }
- if (fLabelDefined)
- {
- this->m_writer.MarkLabel(doneLabel);
- }
- if (scopeAcquired)
- {
- funcInfo->ReleaseTmpRegister(scopeLocation);
- }
- }
- Js::OpCode
- ByteCodeGenerator::GetLdSlotOp(Scope *scope, int envIndex, Js::RegSlot scopeLocation, FuncInfo *funcInfo)
- {
- Js::OpCode op;
- if (envIndex != -1)
- {
- if (scope->GetIsObject())
- {
- op = Js::OpCode::LdEnvObjSlot;
- }
- else
- {
- op = Js::OpCode::LdEnvSlot;
- }
- }
- else if (scopeLocation != Js::Constants::NoRegister &&
- scopeLocation == funcInfo->frameSlotsRegister)
- {
- if (scope->GetScopeType() == ScopeType_Parameter && scope != scope->GetFunc()->GetCurrentChildScope())
- {
- // Symbol is from the param scope of a split scope function and we are emitting the body.
- // We should use the param scope's bytecode now.
- Assert(!funcInfo->IsBodyAndParamScopeMerged());
- op = Js::OpCode::LdParamSlot;
- }
- else
- {
- op = Js::OpCode::LdLocalSlot;
- }
- }
- else if (scopeLocation != Js::Constants::NoRegister &&
- scopeLocation == funcInfo->frameObjRegister)
- {
- if (scope->GetScopeType() == ScopeType_Parameter && scope != scope->GetFunc()->GetCurrentChildScope())
- {
- // Symbol is from the param scope of a split scope function and we are emitting the body.
- // We should use the param scope's bytecode now.
- Assert(!funcInfo->IsBodyAndParamScopeMerged());
- op = Js::OpCode::LdParamObjSlot;
- }
- else
- {
- op = Js::OpCode::LdLocalObjSlot;
- }
- }
- else if (scope->HasInnerScopeIndex())
- {
- if (scope->GetIsObject())
- {
- op = Js::OpCode::LdInnerObjSlot;
- }
- else
- {
- op = Js::OpCode::LdInnerSlot;
- }
- }
- else
- {
- AssertOrFailFast(scope->GetIsObject());
- op = Js::OpCode::LdObjSlot;
- }
- return op;
- }
- bool ByteCodeGenerator::ShouldLoadConstThis(FuncInfo* funcInfo)
- {
- #if DBG
- // We should load a const 'this' binding if the following holds
- // - The function has a 'this' name node
- // - We are in a global or global lambda function
- // - The function has no 'this' symbol (an indirect eval would have this symbol)
- if (funcInfo->thisConstantRegister != Js::Constants::NoRegister)
- {
- Assert((funcInfo->IsLambda() || funcInfo->IsGlobalFunction())
- && !funcInfo->GetThisSymbol()
- && !(this->flags & fscrEval));
- }
- #endif
- return funcInfo->thisConstantRegister != Js::Constants::NoRegister;
- }
- void ByteCodeGenerator::EmitPropLoadThis(Js::RegSlot lhsLocation, ParseNodeSpecialName *pnodeSpecialName, FuncInfo *funcInfo, bool chkUndecl)
- {
- Symbol* sym = pnodeSpecialName->sym;
- if (!sym && this->ShouldLoadConstThis(funcInfo))
- {
- this->Writer()->Reg2(Js::OpCode::Ld_A, lhsLocation, funcInfo->thisConstantRegister);
- }
- else
- {
- this->EmitPropLoad(lhsLocation, pnodeSpecialName->sym, pnodeSpecialName->pid, funcInfo, true);
- if ((!sym || sym->GetNeedDeclaration()) && chkUndecl)
- {
- this->Writer()->Reg1(Js::OpCode::ChkUndecl, lhsLocation);
- }
- }
- }
- void ByteCodeGenerator::EmitPropStoreForSpecialSymbol(Js::RegSlot rhsLocation, Symbol *sym, IdentPtr pid, FuncInfo *funcInfo, bool init)
- {
- if (!funcInfo->IsGlobalFunction() || (this->flags & fscrEval))
- {
- if (init)
- {
- EmitLocalPropInit(rhsLocation, sym, funcInfo);
- }
- else
- {
- EmitPropStore(rhsLocation, sym, pid, funcInfo, false, false, false, true);
- }
- }
- }
- void ByteCodeGenerator::EmitPropLoad(Js::RegSlot lhsLocation, Symbol *sym, IdentPtr pid, FuncInfo *funcInfo, bool skipUseBeforeDeclarationCheck)
- {
- // If sym belongs to a parent frame, get it from the closure environment.
- // If it belongs to this func, but there's a non-local reference, get it from the heap-allocated frame.
- // (TODO: optimize this by getting the sym from its normal location if there are no non-local defs.)
- // Otherwise, just copy the value to the lhsLocation.
- Js::ByteCodeLabel doneLabel = 0;
- bool fLabelDefined = false;
- Js::RegSlot scopeLocation = Js::Constants::NoRegister;
- Js::PropertyId envIndex = -1;
- Scope *scope = nullptr;
- Scope *symScope = sym ? sym->GetScope() : this->globalScope;
- Assert(symScope);
- if (sym && sym->GetIsModuleExportStorage())
- {
- EmitModuleExportAccess(sym, Js::OpCode::LdModuleSlot, lhsLocation, funcInfo);
- return;
- }
- for (;;)
- {
- scope = this->FindScopeForSym(symScope, scope, &envIndex, funcInfo);
- if (scope == this->globalScope)
- {
- break;
- }
- scopeLocation = scope->GetLocation();
- if (scope == symScope)
- {
- break;
- }
- // Found a scope to which the property may have been added.
- Assert(scope && scope->GetIsDynamic());
- if (!fLabelDefined)
- {
- fLabelDefined = true;
- doneLabel = this->m_writer.DefineLabel();
- }
- Js::ByteCodeLabel nextLabel = this->m_writer.DefineLabel();
- Js::PropertyId propertyId = sym ? sym->EnsurePosition(this) : pid->GetPropertyId();
- Js::RegSlot unwrappedScopeLocation = Js::Constants::NoRegister;
- bool unwrapWithObj = scope->GetScopeType() == ScopeType_With && scriptContext->GetConfig()->IsES6UnscopablesEnabled();
- if (envIndex != -1)
- {
- this->m_writer.BrEnvProperty(
- Js::OpCode::BrOnNoEnvProperty,
- nextLabel,
- funcInfo->FindOrAddReferencedPropertyId(propertyId),
- envIndex + Js::FrameDisplay::GetOffsetOfScopes() / sizeof(Js::Var));
- Js::RegSlot instLocation = funcInfo->AcquireTmpRegister();
- AssertOrFailFast(scope->GetIsObject());
- this->m_writer.SlotI1(
- Js::OpCode::LdEnvObj,
- instLocation,
- envIndex + Js::FrameDisplay::GetOffsetOfScopes() / sizeof(Js::Var));
- if (unwrapWithObj)
- {
- this->m_writer.Reg2(Js::OpCode::UnwrapWithObj, instLocation, instLocation);
- }
- this->m_writer.PatchableProperty(
- Js::OpCode::LdFld,
- lhsLocation,
- instLocation,
- funcInfo->FindOrAddInlineCacheId(instLocation, propertyId, false, false));
- funcInfo->ReleaseTmpRegister(instLocation);
- }
- else if (scopeLocation != Js::Constants::NoRegister && scopeLocation == funcInfo->frameObjRegister)
- {
- this->m_writer.BrLocalProperty(Js::OpCode::BrOnNoLocalProperty, nextLabel,
- funcInfo->FindOrAddReferencedPropertyId(propertyId));
- Assert(!unwrapWithObj);
- this->m_writer.ElementP(Js::OpCode::LdLocalFld, lhsLocation,
- funcInfo->FindOrAddInlineCacheId(scopeLocation, propertyId, false, false));
- }
- else
- {
- this->m_writer.BrProperty(Js::OpCode::BrOnNoProperty, nextLabel, scopeLocation,
- funcInfo->FindOrAddReferencedPropertyId(propertyId));
- if (unwrapWithObj)
- {
- unwrappedScopeLocation = funcInfo->AcquireTmpRegister();
- this->m_writer.Reg2(Js::OpCode::UnwrapWithObj, unwrappedScopeLocation, scopeLocation);
- scopeLocation = unwrappedScopeLocation;
- }
- uint cacheId = funcInfo->FindOrAddInlineCacheId(scopeLocation, propertyId, false, false);
- this->m_writer.PatchableProperty(Js::OpCode::LdFld, lhsLocation, scopeLocation, cacheId);
- if (unwrapWithObj)
- {
- funcInfo->ReleaseTmpRegister(unwrappedScopeLocation);
- }
- }
- this->m_writer.Br(doneLabel);
- this->m_writer.MarkLabel(nextLabel);
- }
- // Arrived at the scope in which the property was defined.
- if (sym && sym->GetNeedDeclaration() && scope->GetFunc() == funcInfo && !skipUseBeforeDeclarationCheck)
- {
- // Ensure this symbol has a slot if it needs one.
- if (sym->IsInSlot(this, funcInfo))
- {
- Js::PropertyId slot = sym->EnsureScopeSlot(this, funcInfo);
- funcInfo->FindOrAddSlotProfileId(scope, slot);
- }
- if (skipUseBeforeDeclarationCheck)
- {
- if (lhsLocation != Js::Constants::NoRegister)
- {
- this->m_writer.Reg1(Js::OpCode::InitUndecl, lhsLocation);
- }
- }
- else
- {
- EmitUseBeforeDeclarationRuntimeError(this, lhsLocation);
- }
- }
- else if (sym == nullptr || sym->GetIsGlobal())
- {
- Js::PropertyId propertyId = sym ? sym->EnsurePosition(this) : pid->GetPropertyId();
- if (this->flags & fscrEval)
- {
- if (funcInfo->byteCodeFunction->GetIsStrictMode() && funcInfo->IsGlobalFunction())
- {
- uint cacheId = funcInfo->FindOrAddInlineCacheId(funcInfo->frameDisplayRegister, propertyId, false, false);
- this->m_writer.ElementP(Js::OpCode::ScopedLdFld, lhsLocation, cacheId);
- }
- else
- {
- uint cacheId = funcInfo->FindOrAddInlineCacheId(funcInfo->GetEnvRegister(), propertyId, false, false);
- // Load of a symbol with unknown scope from within eval
- // Get it from the closure environment.
- this->m_writer.ElementP(Js::OpCode::ScopedLdFld, lhsLocation, cacheId);
- }
- }
- else if (this->flags & fscrImplicitThis)
- {
- uint cacheId = funcInfo->FindOrAddInlineCacheId(funcInfo->GetEnvRegister(), propertyId, false, false);
- // Load of a symbol with unknown scope from within event handler.
- // Get it from the closure environment.
- this->m_writer.ElementP(Js::OpCode::ScopedLdFld, lhsLocation, cacheId);
- }
- else
- {
- // Special case non-writable built-ins
- // TODO: support non-writable global property in general by detecting what attribute the property have current?
- // But can't be done if we are byte code serialized, because the attribute might be different for use fields
- // next time we run. May want to catch that in the JIT.
- Js::OpCode opcode = Js::OpCode::LdRootFld;
- // These properties are non-writable
- switch (propertyId)
- {
- case Js::PropertyIds::NaN:
- opcode = Js::OpCode::LdNaN;
- break;
- case Js::PropertyIds::Infinity:
- opcode = Js::OpCode::LdInfinity;
- break;
- case Js::PropertyIds::undefined:
- opcode = Js::OpCode::LdUndef;
- break;
- case Js::PropertyIds::__chakraLibrary:
- if (CONFIG_FLAG(LdChakraLib) || CONFIG_FLAG(TestChakraLib))
- {
- opcode = Js::OpCode::LdChakraLib;
- }
- break;
- }
- if (opcode == Js::OpCode::LdRootFld)
- {
- this->EmitPatchableRootProperty(Js::OpCode::LdRootFld, lhsLocation, propertyId, false, false, funcInfo);
- }
- else
- {
- this->Writer()->Reg1(opcode, lhsLocation);
- }
- }
- }
- else if (sym->IsInSlot(this, funcInfo) || envIndex != -1)
- {
- // Make sure the property has a slot. This will bump up the size of the slot array if necessary.
- Js::PropertyId slot = sym->EnsureScopeSlot(this, funcInfo);
- Js::ProfileId profileId = funcInfo->FindOrAddSlotProfileId(scope, slot);
- bool chkBlockVar = NeedCheckBlockVar(sym, scope, funcInfo);
- Js::OpCode op;
- // Now get the property from its slot.
- op = this->GetLdSlotOp(scope, envIndex, scopeLocation, funcInfo);
- slot = slot + (sym->GetScope()->GetIsObject() ? 0 : Js::ScopeSlots::FirstSlotIndex);
- if (envIndex != -1)
- {
- this->m_writer.SlotI2(op, lhsLocation, envIndex + Js::FrameDisplay::GetOffsetOfScopes() / sizeof(Js::Var), slot, profileId);
- }
- else if (scopeLocation != Js::Constants::NoRegister &&
- (scopeLocation == funcInfo->frameSlotsRegister || scopeLocation == funcInfo->frameObjRegister))
- {
- this->m_writer.SlotI1(op, lhsLocation, slot, profileId);
- }
- else if (scope->HasInnerScopeIndex())
- {
- this->m_writer.SlotI2(op, lhsLocation, scope->GetInnerScopeIndex(), slot, profileId);
- }
- else
- {
- AssertOrFailFast(scope->GetIsObject());
- this->m_writer.Slot(op, lhsLocation, scopeLocation, slot, profileId);
- }
- if (chkBlockVar)
- {
- this->m_writer.Reg1(Js::OpCode::ChkUndecl, lhsLocation);
- }
- }
- else
- {
- if (lhsLocation != sym->GetLocation())
- {
- this->m_writer.Reg2(Js::OpCode::Ld_A, lhsLocation, sym->GetLocation());
- }
- if (sym->GetIsBlockVar() && ((sym->GetDecl()->nop == knopLetDecl || sym->GetDecl()->nop == knopConstDecl) && sym->GetDecl()->AsParseNodeVar()->isSwitchStmtDecl))
- {
- this->m_writer.Reg1(Js::OpCode::ChkUndecl, lhsLocation);
- }
- }
- if (fLabelDefined)
- {
- this->m_writer.MarkLabel(doneLabel);
- }
- }
- bool ByteCodeGenerator::NeedCheckBlockVar(Symbol* sym, Scope* scope, FuncInfo* funcInfo) const
- {
- bool tdz = sym->GetIsBlockVar()
- && (scope->GetFunc() != funcInfo || ((sym->GetDecl()->nop == knopLetDecl || sym->GetDecl()->nop == knopConstDecl) && sym->GetDecl()->AsParseNodeVar()->isSwitchStmtDecl));
- return tdz || sym->GetIsNonSimpleParameter();
- }
- void ByteCodeGenerator::EmitPropDelete(Js::RegSlot lhsLocation, Symbol *sym, IdentPtr pid, FuncInfo *funcInfo)
- {
- // If sym belongs to a parent frame, delete it from the closure environment.
- // If it belongs to this func, but there's a non-local reference, get it from the heap-allocated frame.
- // (TODO: optimize this by getting the sym from its normal location if there are no non-local defs.)
- // Otherwise, just return false.
- Js::ByteCodeLabel doneLabel = 0;
- bool fLabelDefined = false;
- Js::RegSlot scopeLocation = Js::Constants::NoRegister;
- Js::PropertyId envIndex = -1;
- Scope *scope = nullptr;
- Scope *symScope = sym ? sym->GetScope() : this->globalScope;
- Assert(symScope);
- for (;;)
- {
- scope = this->FindScopeForSym(symScope, scope, &envIndex, funcInfo);
- if (scope == this->globalScope)
- {
- scopeLocation = ByteCodeGenerator::RootObjectRegister;
- }
- else if (envIndex == -1)
- {
- Assert(funcInfo == scope->GetFunc());
- scopeLocation = scope->GetLocation();
- }
- if (scope == symScope)
- {
- break;
- }
- // Found a scope to which the property may have been added.
- Assert(scope && scope->GetIsDynamic());
- if (!fLabelDefined)
- {
- fLabelDefined = true;
- doneLabel = this->m_writer.DefineLabel();
- }
- Js::ByteCodeLabel nextLabel = this->m_writer.DefineLabel();
- Js::PropertyId propertyId = sym ? sym->EnsurePosition(this) : pid->GetPropertyId();
- bool unwrapWithObj = scope->GetScopeType() == ScopeType_With && scriptContext->GetConfig()->IsES6UnscopablesEnabled();
- if (envIndex != -1)
- {
- this->m_writer.BrEnvProperty(
- Js::OpCode::BrOnNoEnvProperty,
- nextLabel,
- funcInfo->FindOrAddReferencedPropertyId(propertyId),
- envIndex + Js::FrameDisplay::GetOffsetOfScopes() / sizeof(Js::Var));
- Js::RegSlot instLocation = funcInfo->AcquireTmpRegister();
- AssertOrFailFast(scope->GetIsObject());
- this->m_writer.SlotI1(
- Js::OpCode::LdEnvObj,
- instLocation,
- envIndex + Js::FrameDisplay::GetOffsetOfScopes() / sizeof(Js::Var));
- if (unwrapWithObj)
- {
- this->m_writer.Reg2(Js::OpCode::UnwrapWithObj, instLocation, instLocation);
- }
- this->m_writer.Property(Js::OpCode::DeleteFld, lhsLocation, instLocation,
- funcInfo->FindOrAddReferencedPropertyId(propertyId));
- funcInfo->ReleaseTmpRegister(instLocation);
- }
- else if (scopeLocation != Js::Constants::NoRegister && scopeLocation == funcInfo->frameObjRegister)
- {
- this->m_writer.BrLocalProperty(Js::OpCode::BrOnNoLocalProperty, nextLabel,
- funcInfo->FindOrAddReferencedPropertyId(propertyId));
- Assert(!unwrapWithObj);
- this->m_writer.ElementU(Js::OpCode::DeleteLocalFld, lhsLocation,
- funcInfo->FindOrAddReferencedPropertyId(propertyId));
- }
- else
- {
- this->m_writer.BrProperty(Js::OpCode::BrOnNoProperty, nextLabel, scopeLocation,
- funcInfo->FindOrAddReferencedPropertyId(propertyId));
- Js::RegSlot unwrappedScopeLocation = Js::Constants::NoRegister;
- if (unwrapWithObj)
- {
- unwrappedScopeLocation = funcInfo->AcquireTmpRegister();
- this->m_writer.Reg2(Js::OpCode::UnwrapWithObj, unwrappedScopeLocation, scopeLocation);
- scopeLocation = unwrappedScopeLocation;
- }
- this->m_writer.Property(Js::OpCode::DeleteFld, lhsLocation, scopeLocation,
- funcInfo->FindOrAddReferencedPropertyId(propertyId));
- if (unwrapWithObj)
- {
- funcInfo->ReleaseTmpRegister(unwrappedScopeLocation);
- }
- }
- this->m_writer.Br(doneLabel);
- this->m_writer.MarkLabel(nextLabel);
- }
- // Arrived at the scope in which the property was defined.
- if (sym == nullptr || sym->GetIsGlobal())
- {
- Js::PropertyId propertyId = sym ? sym->EnsurePosition(this) : pid->GetPropertyId();
- if (this->flags & (fscrEval | fscrImplicitThis))
- {
- this->m_writer.ScopedProperty(Js::OpCode::ScopedDeleteFld, lhsLocation,
- funcInfo->FindOrAddReferencedPropertyId(propertyId), forceStrictModeForClassComputedPropertyName);
- }
- else
- {
- this->m_writer.Property(Js::OpCode::DeleteRootFld, lhsLocation, ByteCodeGenerator::RootObjectRegister,
- funcInfo->FindOrAddReferencedPropertyId(propertyId));
- }
- }
- else
- {
- // The delete will look like a non-local reference, so make sure a slot is reserved.
- sym->EnsureScopeSlot(this, funcInfo);
- this->m_writer.Reg1(Js::OpCode::LdFalse, lhsLocation);
- }
- if (fLabelDefined)
- {
- this->m_writer.MarkLabel(doneLabel);
- }
- }
- void ByteCodeGenerator::EmitTypeOfFld(FuncInfo * funcInfo, Js::PropertyId propertyId, Js::RegSlot value, Js::RegSlot instance, Js::OpCode ldFldOp)
- {
- uint cacheId;
- Js::RegSlot tmpReg = funcInfo->AcquireTmpRegister();
- switch (ldFldOp)
- {
- case Js::OpCode::LdRootFldForTypeOf:
- cacheId = funcInfo->FindOrAddRootObjectInlineCacheId(propertyId, false, false);
- this->Writer()->PatchableRootProperty(ldFldOp, tmpReg, cacheId, false, false);
- break;
- case Js::OpCode::LdLocalFld:
- case Js::OpCode::ScopedLdFldForTypeOf:
- cacheId = funcInfo->FindOrAddInlineCacheId(instance, propertyId, false, false);
- this->Writer()->ElementP(ldFldOp, tmpReg, cacheId);
- break;
- default:
- cacheId = funcInfo->FindOrAddInlineCacheId(instance, propertyId, false, false);
- this->Writer()->PatchableProperty(ldFldOp, tmpReg, instance, cacheId);
- break;
- }
- this->Writer()->Reg2(Js::OpCode::Typeof, value, tmpReg);
- funcInfo->ReleaseTmpRegister(tmpReg);
- }
- void ByteCodeGenerator::EmitPropTypeof(Js::RegSlot lhsLocation, Symbol *sym, IdentPtr pid, FuncInfo *funcInfo)
- {
- // If sym belongs to a parent frame, delete it from the closure environment.
- // If it belongs to this func, but there's a non-local reference, get it from the heap-allocated frame.
- // (TODO: optimize this by getting the sym from its normal location if there are no non-local defs.)
- // Otherwise, just return false
- Js::ByteCodeLabel doneLabel = 0;
- bool fLabelDefined = false;
- Js::RegSlot scopeLocation = Js::Constants::NoRegister;
- Js::PropertyId envIndex = -1;
- Scope *scope = nullptr;
- Scope *symScope = sym ? sym->GetScope() : this->globalScope;
- Assert(symScope);
- if (sym && sym->GetIsModuleExportStorage())
- {
- Js::RegSlot tmpLocation = funcInfo->AcquireTmpRegister();
- EmitModuleExportAccess(sym, Js::OpCode::LdModuleSlot, tmpLocation, funcInfo);
- this->m_writer.Reg2(Js::OpCode::Typeof, lhsLocation, tmpLocation);
- funcInfo->ReleaseTmpRegister(tmpLocation);
- return;
- }
- for (;;)
- {
- scope = this->FindScopeForSym(symScope, scope, &envIndex, funcInfo);
- if (scope == this->globalScope)
- {
- scopeLocation = ByteCodeGenerator::RootObjectRegister;
- }
- else if (envIndex == -1)
- {
- Assert(funcInfo == scope->GetFunc());
- scopeLocation = scope->GetLocation();
- }
- if (scope == symScope)
- {
- break;
- }
- // Found a scope to which the property may have been added.
- Assert(scope && scope->GetIsDynamic());
- if (!fLabelDefined)
- {
- fLabelDefined = true;
- doneLabel = this->m_writer.DefineLabel();
- }
- Js::ByteCodeLabel nextLabel = this->m_writer.DefineLabel();
- Js::PropertyId propertyId = sym ? sym->EnsurePosition(this) : pid->GetPropertyId();
- bool unwrapWithObj = scope->GetScopeType() == ScopeType_With && scriptContext->GetConfig()->IsES6UnscopablesEnabled();
- if (envIndex != -1)
- {
- this->m_writer.BrEnvProperty(Js::OpCode::BrOnNoEnvProperty, nextLabel,
- funcInfo->FindOrAddReferencedPropertyId(propertyId),
- envIndex + Js::FrameDisplay::GetOffsetOfScopes() / sizeof(Js::Var));
- Js::RegSlot instLocation = funcInfo->AcquireTmpRegister();
- AssertOrFailFast(scope->GetIsObject());
- this->m_writer.SlotI1(Js::OpCode::LdEnvObj,
- instLocation,
- envIndex + Js::FrameDisplay::GetOffsetOfScopes() / sizeof(Js::Var));
- if (unwrapWithObj)
- {
- this->m_writer.Reg2(Js::OpCode::UnwrapWithObj, instLocation, instLocation);
- }
- this->EmitTypeOfFld(funcInfo, propertyId, lhsLocation, instLocation, Js::OpCode::LdFldForTypeOf);
- funcInfo->ReleaseTmpRegister(instLocation);
- }
- else if (scopeLocation != Js::Constants::NoRegister && scopeLocation == funcInfo->frameObjRegister)
- {
- this->m_writer.BrLocalProperty(Js::OpCode::BrOnNoLocalProperty, nextLabel,
- funcInfo->FindOrAddReferencedPropertyId(propertyId));
- Assert(!unwrapWithObj);
- this->EmitTypeOfFld(funcInfo, propertyId, lhsLocation, scopeLocation, Js::OpCode::LdLocalFld);
- }
- else
- {
- this->m_writer.BrProperty(Js::OpCode::BrOnNoProperty, nextLabel, scopeLocation,
- funcInfo->FindOrAddReferencedPropertyId(propertyId));
- Js::RegSlot unwrappedScopeLocation = Js::Constants::NoRegister;
- if (unwrapWithObj)
- {
- unwrappedScopeLocation = funcInfo->AcquireTmpRegister();
- this->m_writer.Reg2(Js::OpCode::UnwrapWithObj, unwrappedScopeLocation, scopeLocation);
- scopeLocation = unwrappedScopeLocation;
- }
- this->EmitTypeOfFld(funcInfo, propertyId, lhsLocation, scopeLocation, Js::OpCode::LdFldForTypeOf);
- if (unwrapWithObj)
- {
- funcInfo->ReleaseTmpRegister(unwrappedScopeLocation);
- }
- }
- this->m_writer.Br(doneLabel);
- this->m_writer.MarkLabel(nextLabel);
- }
- // Arrived at the scope in which the property was defined.
- if (sym && sym->GetNeedDeclaration() && scope->GetFunc() == funcInfo)
- {
- // Ensure this symbol has a slot if it needs one.
- if (sym->IsInSlot(this, funcInfo))
- {
- Js::PropertyId slot = sym->EnsureScopeSlot(this, funcInfo);
- funcInfo->FindOrAddSlotProfileId(scope, slot);
- }
- EmitUseBeforeDeclarationRuntimeError(this, lhsLocation);
- }
- else if (sym == nullptr || sym->GetIsGlobal())
- {
- Js::PropertyId propertyId = sym ? sym->EnsurePosition(this) : pid->GetPropertyId();
- if (this->flags & fscrEval)
- {
- if (funcInfo->byteCodeFunction->GetIsStrictMode() && funcInfo->IsGlobalFunction())
- {
- this->EmitTypeOfFld(funcInfo, propertyId, lhsLocation, funcInfo->frameDisplayRegister, Js::OpCode::ScopedLdFldForTypeOf);
- }
- else
- {
- this->EmitTypeOfFld(funcInfo, propertyId, lhsLocation, funcInfo->GetEnvRegister(), Js::OpCode::ScopedLdFldForTypeOf);
- }
- }
- else if (this->flags & fscrImplicitThis)
- {
- this->EmitTypeOfFld(funcInfo, propertyId, lhsLocation, funcInfo->GetEnvRegister(), Js::OpCode::ScopedLdFldForTypeOf);
- }
- else
- {
- this->EmitTypeOfFld(funcInfo, propertyId, lhsLocation, ByteCodeGenerator::RootObjectRegister, Js::OpCode::LdRootFldForTypeOf);
- }
- }
- else if (sym->IsInSlot(this, funcInfo) || envIndex != -1)
- {
- // Make sure the property has a slot. This will bump up the size of the slot array if necessary.
- Js::PropertyId slot = sym->EnsureScopeSlot(this, funcInfo);
- Js::ProfileId profileId = funcInfo->FindOrAddSlotProfileId(scope, slot);
- Js::RegSlot tmpLocation = funcInfo->AcquireTmpRegister();
- bool chkBlockVar = NeedCheckBlockVar(sym, scope, funcInfo);
- Js::OpCode op;
- op = this->GetLdSlotOp(scope, envIndex, scopeLocation, funcInfo);
- slot = slot + (sym->GetScope()->GetIsObject() ? 0 : Js::ScopeSlots::FirstSlotIndex);
- if (envIndex != -1)
- {
- this->m_writer.SlotI2(op, tmpLocation, envIndex + Js::FrameDisplay::GetOffsetOfScopes() / sizeof(Js::Var), slot, profileId);
- }
- else if (scopeLocation != Js::Constants::NoRegister &&
- (scopeLocation == funcInfo->frameSlotsRegister || scopeLocation == funcInfo->frameObjRegister))
- {
- this->m_writer.SlotI1(op, tmpLocation, slot, profileId);
- }
- else if (scope->HasInnerScopeIndex())
- {
- this->m_writer.SlotI2(op, tmpLocation, scope->GetInnerScopeIndex(), slot, profileId);
- }
- else
- {
- AssertOrFailFast(scope->GetIsObject());
- this->m_writer.Slot(op, tmpLocation, scopeLocation, slot, profileId);
- }
- if (chkBlockVar)
- {
- this->m_writer.Reg1(Js::OpCode::ChkUndecl, tmpLocation);
- }
- this->m_writer.Reg2(Js::OpCode::Typeof, lhsLocation, tmpLocation);
- funcInfo->ReleaseTmpRegister(tmpLocation);
- }
- else
- {
- this->m_writer.Reg2(Js::OpCode::Typeof, lhsLocation, sym->GetLocation());
- }
- if (fLabelDefined)
- {
- this->m_writer.MarkLabel(doneLabel);
- }
- }
- void ByteCodeGenerator::EnsureNoRedeclarations(ParseNodeBlock *pnodeBlock, FuncInfo *funcInfo)
- {
- // Emit dynamic runtime checks for variable re-declarations. Only necessary for global functions (script or eval).
- // In eval only var declarations can cause redeclaration, and only in non-strict mode, because let/const variables
- // remain local to the eval code.
- Assert(pnodeBlock->nop == knopBlock);
- Assert(pnodeBlock->blockType == PnodeBlockType::Global || pnodeBlock->scope->GetScopeType() == ScopeType_GlobalEvalBlock);
- if (!(this->flags & fscrEvalCode))
- {
- IterateBlockScopedVariables(pnodeBlock, [this](ParseNode *pnode)
- {
- FuncInfo *funcInfo = this->TopFuncInfo();
- Symbol *sym = pnode->AsParseNodeVar()->sym;
- Assert(sym->GetIsGlobal());
- Js::PropertyId propertyId = sym->EnsurePosition(this);
- this->m_writer.ElementRootU(Js::OpCode::EnsureNoRootFld, funcInfo->FindOrAddReferencedPropertyId(propertyId));
- });
- }
- auto emitRedeclCheck = [this](Symbol * sym, FuncInfo * funcInfo, bool isFncDecl = false)
- {
- Js::PropertyId propertyId = sym->EnsurePosition(this);
- // Global function declarations must pass #sec-candeclareglobalfunction
- // which is enforced by EnsureCanDeclGloFunc
- if (isFncDecl)
- {
- this->m_writer.ElementRootU(Js::OpCode::EnsureCanDeclGloFunc, funcInfo->FindOrAddReferencedPropertyId(propertyId));
- }
- if (this->flags & fscrEval)
- {
- if (!funcInfo->byteCodeFunction->GetIsStrictMode())
- {
- this->m_writer.ScopedProperty(Js::OpCode::ScopedEnsureNoRedeclFld, ByteCodeGenerator::RootObjectRegister,
- funcInfo->FindOrAddReferencedPropertyId(propertyId));
- }
- }
- else
- {
- this->m_writer.ElementRootU(Js::OpCode::EnsureNoRootRedeclFld, funcInfo->FindOrAddReferencedPropertyId(propertyId));
- }
- };
- // scan for function declarations
- // these behave like "var" declarations
- for (ParseNodePtr pnode = pnodeBlock->pnodeScopes; pnode;)
- {
- switch (pnode->nop) {
- case knopFncDecl:
- if (pnode->AsParseNodeFnc()->IsDeclaration())
- {
- emitRedeclCheck(pnode->AsParseNodeFnc()->pnodeName->sym, funcInfo, true);
- }
- pnode = pnode->AsParseNodeFnc()->pnodeNext;
- break;
- case knopBlock:
- pnode = pnode->AsParseNodeBlock()->pnodeNext;
- break;
- case knopCatch:
- pnode = pnode->AsParseNodeCatch()->pnodeNext;
- break;
- case knopWith:
- pnode = pnode->AsParseNodeWith()->pnodeNext;
- break;
- default:
- Assert(UNREACHED);
- }
- }
- // scan for var declarations
- for (ParseNode *pnode = funcInfo->root->pnodeVars; pnode; pnode = pnode->AsParseNodeVar()->pnodeNext)
- {
- Symbol* sym = pnode->AsParseNodeVar()->sym;
- if (sym == nullptr || pnode->AsParseNodeVar()->isBlockScopeFncDeclVar || sym->IsSpecialSymbol())
- continue;
- if (sym->GetIsCatch() || (pnode->nop == knopVarDecl && sym->GetIsBlockVar()))
- {
- // The init node was bound to the catch object, because it's inside a catch and has the
- // same name as the catch object. But we want to define a user var at function scope,
- // so find the right symbol. (We'll still assign the RHS value to the catch object symbol.)
- // This also applies to a var declaration in the same scope as a let declaration.
- // Assert that catch cannot be at function scope and let and var at function scope is redeclaration error.
- Assert(sym->GetIsCatch() || funcInfo->bodyScope != sym->GetScope());
- sym = funcInfo->bodyScope->FindLocalSymbol(sym->GetName());
- Assert(sym && !sym->GetIsCatch() && !sym->GetIsBlockVar());
- }
- Assert(sym->GetIsGlobal());
- if (sym->GetSymbolType() == STVariable)
- {
- emitRedeclCheck(sym, funcInfo);
- }
- }
- }
- void ByteCodeGenerator::RecordAllIntConstants(FuncInfo * funcInfo)
- {
- Js::FunctionBody *byteCodeFunction = this->TopFuncInfo()->GetParsedFunctionBody();
- funcInfo->constantToRegister.Map([byteCodeFunction](unsigned int val, Js::RegSlot location)
- {
- byteCodeFunction->RecordIntConstant(byteCodeFunction->MapRegSlot(location), val);
- });
- }
- void ByteCodeGenerator::RecordAllStrConstants(FuncInfo * funcInfo)
- {
- Js::FunctionBody *byteCodeFunction = this->TopFuncInfo()->GetParsedFunctionBody();
- funcInfo->stringToRegister.Map([byteCodeFunction](IdentPtr pid, Js::RegSlot location)
- {
- byteCodeFunction->RecordStrConstant(byteCodeFunction->MapRegSlot(location), pid->Psz(), pid->Cch(), pid->IsUsedInLdElem());
- });
- }
- void ByteCodeGenerator::RecordAllBigIntConstants(FuncInfo * funcInfo)
- {
- Js::FunctionBody *byteCodeFunction = this->TopFuncInfo()->GetParsedFunctionBody();
- funcInfo->bigintToRegister.Map([byteCodeFunction](ParseNode* pnode, Js::RegSlot location)
- {
- IdentPtr pid = pnode->AsParseNodeBigInt()->pid;
- byteCodeFunction->RecordBigIntConstant(byteCodeFunction->MapRegSlot(location), pid->Psz(), pid->Cch(), pnode->AsParseNodeBigInt()->isNegative);
- });
- }
- void ByteCodeGenerator::RecordAllStringTemplateCallsiteConstants(FuncInfo* funcInfo)
- {
- Js::FunctionBody *byteCodeFunction = this->TopFuncInfo()->GetParsedFunctionBody();
- funcInfo->stringTemplateCallsiteRegisterMap.Map([byteCodeFunction](ParseNodePtr pnode, Js::RegSlot location)
- {
- Js::ScriptContext* scriptContext = byteCodeFunction->GetScriptContext();
-
- Js::RecyclableObject* rawArray = ByteCodeGenerator::BuildArrayFromStringList(pnode->AsParseNodeStrTemplate()->pnodeStringRawLiterals, pnode->AsParseNodeStrTemplate()->countStringLiterals, scriptContext);
- rawArray->Freeze();
- Js::RecyclableObject* callsiteObject = ByteCodeGenerator::BuildArrayFromStringList(pnode->AsParseNodeStrTemplate()->pnodeStringLiterals, pnode->AsParseNodeStrTemplate()->countStringLiterals, scriptContext);
- callsiteObject->SetPropertyWithAttributes(Js::PropertyIds::raw, rawArray, PropertyNone, nullptr);
- callsiteObject->Freeze();
- byteCodeFunction->RecordConstant(byteCodeFunction->MapRegSlot(location), callsiteObject);
- });
- }
- bool IsApplyArgs(ParseNodeCall* callNode)
- {
- ParseNode* target = callNode->pnodeTarget;
- ParseNode* args = callNode->pnodeArgs;
- if ((target != nullptr) && (target->nop == knopDot))
- {
- ParseNode* lhsNode = target->AsParseNodeBin()->pnode1;
- if ((lhsNode != nullptr) && ((lhsNode->nop == knopDot) || (lhsNode->nop == knopName)) && !IsArguments(lhsNode))
- {
- ParseNode* nameNode = target->AsParseNodeBin()->pnode2;
- if (nameNode != nullptr)
- {
- bool nameIsApply = nameNode->AsParseNodeName()->PropertyIdFromNameNode() == Js::PropertyIds::apply;
- if (nameIsApply && args != nullptr && args->nop == knopList)
- {
- ParseNode* arg1 = args->AsParseNodeBin()->pnode1;
- ParseNode* arg2 = args->AsParseNodeBin()->pnode2;
- if ((arg1 != nullptr) && ByteCodeGenerator::IsThis(arg1) && (arg2 != nullptr) && (arg2->nop == knopName) && (arg2->AsParseNodeName()->sym != nullptr))
- {
- return arg2->AsParseNodeName()->sym->IsArguments();
- }
- }
- }
- }
- }
- return false;
- }
- void PostCheckApplyEnclosesArgs(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, ApplyCheck* applyCheck)
- {
- if ((pnode == nullptr) || (!applyCheck->matches))
- {
- return;
- }
- if (pnode->nop == knopCall)
- {
- if ((!pnode->isUsed) && IsApplyArgs(pnode->AsParseNodeCall()))
- {
- if (!applyCheck->insideApplyCall)
- {
- applyCheck->matches = false;
- }
- applyCheck->insideApplyCall = false;
- }
- }
- }
- void CheckApplyEnclosesArgs(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, ApplyCheck* applyCheck)
- {
- if ((pnode == nullptr) || (!applyCheck->matches))
- {
- return;
- }
- switch (pnode->nop)
- {
- case knopName:
- {
- Symbol* sym = pnode->AsParseNodeName()->sym;
- if (sym != nullptr)
- {
- if (sym->IsArguments())
- {
- if (!applyCheck->insideApplyCall)
- {
- applyCheck->matches = false;
- }
- }
- }
- break;
- }
- case knopCall:
- if ((!pnode->isUsed) && IsApplyArgs(pnode->AsParseNodeCall()))
- {
- // no nested apply calls
- if (applyCheck->insideApplyCall)
- {
- applyCheck->matches = false;
- }
- else
- {
- applyCheck->insideApplyCall = true;
- applyCheck->sawApply = true;
- pnode->AsParseNodeCall()->isApplyCall = true;
- }
- }
- break;
- }
- }
- unsigned int CountArguments(ParseNode *pnode, BOOL *pSideEffect = nullptr)
- {
- // If the caller passed us a pSideEffect, it wants to know whether there are potential
- // side-effects in the argument list. We need to know this so that the call target
- // operands can be preserved if necessary.
- // For now, treat any non-leaf op as a potential side-effect. This causes no detectable slowdowns,
- // but we can be more precise if we need to be.
- if (pSideEffect)
- {
- *pSideEffect = FALSE;
- }
- unsigned int argCount = 1;
- if (pnode != nullptr)
- {
- while (pnode->nop == knopList)
- {
- argCount++;
- if (pSideEffect && !(ParseNode::Grfnop(pnode->AsParseNodeBin()->pnode1->nop) & fnopLeaf))
- {
- *pSideEffect = TRUE;
- }
- pnode = pnode->AsParseNodeBin()->pnode2;
- }
- argCount++;
- if (pSideEffect && !(ParseNode::Grfnop(pnode->nop) & fnopLeaf))
- {
- *pSideEffect = TRUE;
- }
- }
- AssertOrFailFastMsg(argCount < Js::Constants::UShortMaxValue, "Number of allowed arguments are already capped at parser level");
- return argCount;
- }
- void SaveOpndValue(ParseNode *pnode, FuncInfo *funcInfo)
- {
- // Save a local name to a register other than its home location.
- // This guards against side-effects in cases like x.foo(x = bar()).
- Symbol *sym = nullptr;
- if (pnode->nop == knopName)
- {
- sym = pnode->AsParseNodeName()->sym;
- }
- else if (pnode->nop == knopComputedName)
- {
- ParseNode *pnode1 = pnode->AsParseNodeUni()->pnode1;
- if (pnode1->nop == knopName)
- {
- sym = pnode1->AsParseNodeName()->sym;
- }
- }
- if (sym == nullptr)
- {
- return;
- }
- // If the target is a local being kept in its home location,
- // protect the target's value in the event the home location is overwritten.
- if (pnode->location != Js::Constants::NoRegister &&
- sym->GetScope()->GetFunc() == funcInfo &&
- pnode->location == sym->GetLocation())
- {
- pnode->location = funcInfo->AcquireTmpRegister();
- }
- }
- void ByteCodeGenerator::StartStatement(ParseNode* node)
- {
- Assert(TopFuncInfo() != nullptr);
- m_writer.StartStatement(node, TopFuncInfo()->curTmpReg - TopFuncInfo()->firstTmpReg);
- }
- void ByteCodeGenerator::EndStatement(ParseNode* node)
- {
- m_writer.EndStatement(node);
- }
- void ByteCodeGenerator::StartSubexpression(ParseNode* node)
- {
- Assert(TopFuncInfo() != nullptr);
- m_writer.StartSubexpression(node);
- }
- void ByteCodeGenerator::EndSubexpression(ParseNode* node)
- {
- m_writer.EndSubexpression(node);
- }
- void EmitReference(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo)
- {
- // Generate code for the LHS of an assignment.
- switch (pnode->nop)
- {
- case knopDot:
- Emit(pnode->AsParseNodeBin()->pnode1, byteCodeGenerator, funcInfo, false);
- break;
- case knopIndex:
- Emit(pnode->AsParseNodeBin()->pnode1, byteCodeGenerator, funcInfo, false);
- Emit(pnode->AsParseNodeBin()->pnode2, byteCodeGenerator, funcInfo, false);
- break;
- case knopName:
- break;
- case knopArrayPattern:
- case knopObjectPattern:
- break;
- case knopCall:
- case knopNew:
- // Emit the operands of a call that will be used as a LHS.
- // These have to be emitted before the RHS, but they have to persist until
- // the end of the expression.
- // Emit the call target operands first.
- switch (pnode->AsParseNodeCall()->pnodeTarget->nop)
- {
- case knopDot:
- case knopIndex:
- funcInfo->AcquireLoc(pnode->AsParseNodeCall()->pnodeTarget);
- EmitReference(pnode->AsParseNodeCall()->pnodeTarget, byteCodeGenerator, funcInfo);
- break;
- case knopName:
- {
- Symbol *sym = pnode->AsParseNodeCall()->pnodeTarget->AsParseNodeName()->sym;
- if (!sym || sym->GetLocation() == Js::Constants::NoRegister)
- {
- funcInfo->AcquireLoc(pnode->AsParseNodeCall()->pnodeTarget);
- }
- if (sym && (sym->IsInSlot(byteCodeGenerator, funcInfo) || sym->GetScope()->GetFunc() != funcInfo))
- {
- // Can't get the value from the assigned register, so load it here.
- EmitLoad(pnode->AsParseNodeCall()->pnodeTarget, byteCodeGenerator, funcInfo);
- }
- else
- {
- // EmitLoad will check for needsDeclaration and emit the Use Before Declaration error
- // bytecode op as necessary, but EmitReference does not check this (by design). So we
- // must manually check here.
- EmitUseBeforeDeclaration(pnode->AsParseNodeCall()->pnodeTarget->AsParseNodeName()->sym, byteCodeGenerator, funcInfo);
- EmitReference(pnode->AsParseNodeCall()->pnodeTarget, byteCodeGenerator, funcInfo);
- }
- break;
- }
- default:
- EmitLoad(pnode->AsParseNodeCall()->pnodeTarget, byteCodeGenerator, funcInfo);
- break;
- }
- // Now the arg list. We evaluate everything now and emit the ArgOut's later.
- if (pnode->AsParseNodeCall()->pnodeArgs)
- {
- ParseNode *pnodeArg = pnode->AsParseNodeCall()->pnodeArgs;
- while (pnodeArg->nop == knopList)
- {
- Emit(pnodeArg->AsParseNodeBin()->pnode1, byteCodeGenerator, funcInfo, false);
- pnodeArg = pnodeArg->AsParseNodeBin()->pnode2;
- }
- Emit(pnodeArg, byteCodeGenerator, funcInfo, false);
- }
- if (pnode->AsParseNodeCall()->isSuperCall)
- {
- Emit(pnode->AsParseNodeSuperCall()->pnodeThis, byteCodeGenerator, funcInfo, false);
- Emit(pnode->AsParseNodeSuperCall()->pnodeNewTarget, byteCodeGenerator, funcInfo, false);
- }
- break;
- default:
- Emit(pnode, byteCodeGenerator, funcInfo, false);
- break;
- }
- }
- void EmitGetIterator(Js::RegSlot iteratorLocation, Js::RegSlot iterableLocation, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo);
- void EmitIteratorNext(Js::RegSlot itemLocation, Js::RegSlot iteratorLocation, Js::RegSlot nextInputLocation, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo);
- void EmitIteratorClose(Js::RegSlot iteratorLocation, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo);
- void EmitIteratorComplete(Js::RegSlot doneLocation, Js::RegSlot iteratorResultLocation, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo);
- void EmitIteratorValue(Js::RegSlot valueLocation, Js::RegSlot iteratorResultLocation, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo);
- void EmitDestructuredElement(ParseNode *elem, Js::RegSlot sourceLocation, ByteCodeGenerator* byteCodeGenerator, FuncInfo *funcInfo)
- {
- switch (elem->nop)
- {
- case knopVarDecl:
- case knopLetDecl:
- case knopConstDecl:
- // We manually need to set NeedDeclaration since the node won't be visited.
- elem->AsParseNodeVar()->sym->SetNeedDeclaration(false);
- break;
- default:
- EmitReference(elem, byteCodeGenerator, funcInfo);
- }
- EmitAssignment(nullptr, elem, sourceLocation, byteCodeGenerator, funcInfo);
- funcInfo->ReleaseReference(elem);
- }
- void EmitDestructuredRestArray(ParseNode *elem,
- Js::RegSlot iteratorLocation,
- Js::RegSlot shouldCallReturnFunctionLocation,
- Js::RegSlot shouldCallReturnFunctionLocationFinally,
- ByteCodeGenerator *byteCodeGenerator,
- FuncInfo *funcInfo)
- {
- Js::RegSlot restArrayLocation = funcInfo->AcquireTmpRegister();
- bool isAssignmentTarget = !(elem->AsParseNodeUni()->pnode1->IsPattern() || elem->AsParseNodeUni()->pnode1->IsVarLetOrConst());
- if (isAssignmentTarget)
- {
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdTrue, shouldCallReturnFunctionLocation);
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdTrue, shouldCallReturnFunctionLocationFinally);
- EmitReference(elem->AsParseNodeUni()->pnode1, byteCodeGenerator, funcInfo);
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, shouldCallReturnFunctionLocation);
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, shouldCallReturnFunctionLocationFinally);
- }
- byteCodeGenerator->Writer()->Reg1Unsigned1(
- Js::OpCode::NewScArray,
- restArrayLocation,
- ByteCodeGenerator::DefaultArraySize);
- // BytecodeGen can't convey to IRBuilder that some of the temporaries used here are live. When we
- // have a rest parameter, a counter is used in a loop for the array index, but there is no way to
- // convey this is live on the back edge.
- // As a workaround, we have a persistent var reg that is used for the loop counter
- Js::RegSlot counterLocation = elem->location;
- // TODO[ianhall]: Is calling EnregisterConstant() during Emit phase allowed?
- Js::RegSlot zeroConstantReg = byteCodeGenerator->EnregisterConstant(0);
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, counterLocation, zeroConstantReg);
- // loopTop:
- Js::ByteCodeLabel loopTop = byteCodeGenerator->Writer()->DefineLabel();
- byteCodeGenerator->Writer()->MarkLabel(loopTop);
- Js::RegSlot itemLocation = funcInfo->AcquireTmpRegister();
- EmitIteratorNext(itemLocation, iteratorLocation, Js::Constants::NoRegister, byteCodeGenerator, funcInfo);
- Js::RegSlot doneLocation = funcInfo->AcquireTmpRegister();
- EmitIteratorComplete(doneLocation, itemLocation, byteCodeGenerator, funcInfo);
- Js::ByteCodeLabel iteratorDone = byteCodeGenerator->Writer()->DefineLabel();
- byteCodeGenerator->Writer()->BrReg1(Js::OpCode::BrTrue_A, iteratorDone, doneLocation);
- Js::RegSlot valueLocation = funcInfo->AcquireTmpRegister();
- EmitIteratorValue(valueLocation, itemLocation, byteCodeGenerator, funcInfo);
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdTrue, shouldCallReturnFunctionLocation);
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdTrue, shouldCallReturnFunctionLocationFinally);
- byteCodeGenerator->Writer()->Element(
- ByteCodeGenerator::GetStElemIOpCode(funcInfo),
- valueLocation, restArrayLocation, counterLocation);
- funcInfo->ReleaseTmpRegister(valueLocation);
- funcInfo->ReleaseTmpRegister(doneLocation);
- funcInfo->ReleaseTmpRegister(itemLocation);
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Incr_A, counterLocation, counterLocation);
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, shouldCallReturnFunctionLocation);
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, shouldCallReturnFunctionLocationFinally);
- byteCodeGenerator->Writer()->Br(loopTop);
- // iteratorDone:
- byteCodeGenerator->Writer()->MarkLabel(iteratorDone);
- ParseNode *restElem = elem->AsParseNodeUni()->pnode1;
- if (isAssignmentTarget)
- {
- EmitAssignment(nullptr, restElem, restArrayLocation, byteCodeGenerator, funcInfo);
- funcInfo->ReleaseReference(restElem);
- }
- else
- {
- EmitDestructuredElement(restElem, restArrayLocation, byteCodeGenerator, funcInfo);
- }
- funcInfo->ReleaseTmpRegister(restArrayLocation);
- }
- void EmitDestructuredArray(
- ParseNode *lhs,
- Js::RegSlot rhsLocation,
- ByteCodeGenerator *byteCodeGenerator,
- FuncInfo *funcInfo);
- void EmitIteratorCloseIfNotDone(Js::RegSlot iteratorLocation, Js::RegSlot doneLocation, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo)
- {
- Js::ByteCodeLabel skipCloseLabel = byteCodeGenerator->Writer()->DefineLabel();
- byteCodeGenerator->Writer()->BrReg1(Js::OpCode::BrTrue_A, skipCloseLabel, doneLocation);
- EmitIteratorClose(iteratorLocation, byteCodeGenerator, funcInfo);
- byteCodeGenerator->Writer()->MarkLabel(skipCloseLabel);
- }
- /*
- EmitDestructuredArray(lhsArray, rhs):
- iterator = rhs[@@iterator]
- if lhsArray empty
- return
- for each element in lhsArray except rest
- value = iterator.next()
- if element is a nested destructured array
- EmitDestructuredArray(element, value)
- else
- if value is undefined and there is an initializer
- evaluate initializer
- evaluate element reference
- element = initializer
- else
- element = value
- if lhsArray has a rest element
- rest = []
- while iterator is not done
- value = iterator.next()
- rest.append(value)
- */
- void EmitDestructuredArrayCore(
- ParseNode *list,
- Js::RegSlot iteratorLocation,
- Js::RegSlot shouldCallReturnFunctionLocation,
- Js::RegSlot shouldCallReturnFunctionLocationFinally,
- ByteCodeGenerator *byteCodeGenerator,
- FuncInfo *funcInfo
- )
- {
- Assert(list != nullptr);
- ParseNode *elem = nullptr;
- while (list != nullptr)
- {
- ParseNode *init = nullptr;
- if (list->nop == knopList)
- {
- elem = list->AsParseNodeBin()->pnode1;
- }
- else
- {
- elem = list;
- }
- if (elem->nop == knopEllipsis)
- {
- break;
- }
- switch (elem->nop)
- {
- case knopAsg:
- // An assignment node will always have an initializer
- init = elem->AsParseNodeBin()->pnode2;
- elem = elem->AsParseNodeBin()->pnode1;
- break;
- case knopVarDecl:
- case knopLetDecl:
- case knopConstDecl:
- init = elem->AsParseNodeVar()->pnodeInit;
- break;
- default:
- break;
- }
- byteCodeGenerator->StartStatement(elem);
- bool isAssignmentTarget = !(elem->IsPattern() || elem->IsVarLetOrConst());
- if (isAssignmentTarget)
- {
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdTrue, shouldCallReturnFunctionLocation);
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdTrue, shouldCallReturnFunctionLocationFinally);
- EmitReference(elem, byteCodeGenerator, funcInfo);
- }
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, shouldCallReturnFunctionLocation);
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, shouldCallReturnFunctionLocationFinally);
- Js::RegSlot itemLocation = funcInfo->AcquireTmpRegister();
- EmitIteratorNext(itemLocation, iteratorLocation, Js::Constants::NoRegister, byteCodeGenerator, funcInfo);
- Js::RegSlot doneLocation = funcInfo->AcquireTmpRegister();
- EmitIteratorComplete(doneLocation, itemLocation, byteCodeGenerator, funcInfo);
- if (elem->nop == knopEmpty)
- {
- if (list->nop == knopList)
- {
- list = list->AsParseNodeBin()->pnode2;
- funcInfo->ReleaseTmpRegister(doneLocation);
- funcInfo->ReleaseTmpRegister(itemLocation);
- continue;
- }
- else
- {
- Assert(list->nop == knopEmpty);
- EmitIteratorCloseIfNotDone(iteratorLocation, doneLocation, byteCodeGenerator, funcInfo);
- funcInfo->ReleaseTmpRegister(doneLocation);
- funcInfo->ReleaseTmpRegister(itemLocation);
- break;
- }
- }
- // If the iterator hasn't completed, skip assigning undefined.
- Js::ByteCodeLabel iteratorAlreadyDone = byteCodeGenerator->Writer()->DefineLabel();
- byteCodeGenerator->Writer()->BrReg1(Js::OpCode::BrTrue_A, iteratorAlreadyDone, doneLocation);
- // We're not done with the iterator, so assign the .next() value.
- Js::RegSlot valueLocation = funcInfo->AcquireTmpRegister();
- EmitIteratorValue(valueLocation, itemLocation, byteCodeGenerator, funcInfo);
- Js::ByteCodeLabel beforeDefaultAssign = byteCodeGenerator->Writer()->DefineLabel();
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdTrue, shouldCallReturnFunctionLocation);
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdTrue, shouldCallReturnFunctionLocationFinally);
- byteCodeGenerator->Writer()->Br(beforeDefaultAssign);
- // iteratorAlreadyDone:
- byteCodeGenerator->Writer()->MarkLabel(iteratorAlreadyDone);
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, valueLocation, funcInfo->undefinedConstantRegister);
- // beforeDefaultAssign:
- byteCodeGenerator->Writer()->MarkLabel(beforeDefaultAssign);
- if (elem->IsPattern())
- {
- // If we get an undefined value and have an initializer, use it in place of undefined.
- if (init != nullptr)
- {
- /*
- the IR builder uses two symbols for a temp register in the if else path
- R9 <- R3
- if (...)
- R9 <- R2
- R10 = R9.<property> // error -> IR creates a new lifetime for the if path, and the direct path dest is not referenced
- hence we have to create a new temp
- TEMP REG USED TO FIX THIS PRODUCES THIS
- R9 <- R3
- if (BrEq_A R9, R3)
- R10 <- R2 :
- else
- R10 <- R9 : skipdefault
- ... = R10[@@iterator] : loadIter
- */
- // Temp Register
- Js::RegSlot valueLocationTmp = funcInfo->AcquireTmpRegister();
- byteCodeGenerator->StartStatement(init);
- Js::ByteCodeLabel skipDefault = byteCodeGenerator->Writer()->DefineLabel();
- Js::ByteCodeLabel loadIter = byteCodeGenerator->Writer()->DefineLabel();
- // check value is undefined
- byteCodeGenerator->Writer()->BrReg2(Js::OpCode::BrSrNeq_A, skipDefault, valueLocation, funcInfo->undefinedConstantRegister);
- // Evaluate the default expression and assign it.
- Emit(init, byteCodeGenerator, funcInfo, false);
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, valueLocationTmp, init->location);
- funcInfo->ReleaseLoc(init);
- // jmp to loadIter
- byteCodeGenerator->Writer()->Br(loadIter);
- // skipDefault:
- byteCodeGenerator->Writer()->MarkLabel(skipDefault);
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, valueLocationTmp, valueLocation);
- // loadIter:
- // @@iterator
- byteCodeGenerator->Writer()->MarkLabel(loadIter);
- byteCodeGenerator->EndStatement(init);
- if (elem->nop == knopObjectPattern)
- {
- EmitDestructuredObject(elem, valueLocationTmp, byteCodeGenerator, funcInfo);
- }
- else
- {
- // Recursively emit a destructured array using the current .next() as the RHS.
- EmitDestructuredArray(elem, valueLocationTmp, byteCodeGenerator, funcInfo);
- }
- funcInfo->ReleaseTmpRegister(valueLocationTmp);
- }
- else
- {
- if (elem->nop == knopObjectPattern)
- {
- EmitDestructuredObject(elem, valueLocation, byteCodeGenerator, funcInfo);
- }
- else
- {
- // Recursively emit a destructured array using the current .next() as the RHS.
- EmitDestructuredArray(elem, valueLocation, byteCodeGenerator, funcInfo);
- }
- }
- }
- else
- {
- EmitDestructuredValueOrInitializer(elem, valueLocation, init, isAssignmentTarget, byteCodeGenerator, funcInfo);
- }
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, shouldCallReturnFunctionLocation);
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, shouldCallReturnFunctionLocationFinally);
- if (list->nop != knopList)
- {
- EmitIteratorCloseIfNotDone(iteratorLocation, doneLocation, byteCodeGenerator, funcInfo);
- }
- funcInfo->ReleaseTmpRegister(valueLocation);
- funcInfo->ReleaseTmpRegister(doneLocation);
- funcInfo->ReleaseTmpRegister(itemLocation);
- if (isAssignmentTarget)
- {
- funcInfo->ReleaseReference(elem);
- }
- byteCodeGenerator->EndStatement(elem);
- if (list->nop == knopList)
- {
- list = list->AsParseNodeBin()->pnode2;
- }
- else
- {
- break;
- }
- }
- // If we saw a rest element, emit the rest array.
- if (elem != nullptr && elem->nop == knopEllipsis)
- {
- EmitDestructuredRestArray(elem,
- iteratorLocation,
- shouldCallReturnFunctionLocation,
- shouldCallReturnFunctionLocationFinally,
- byteCodeGenerator,
- funcInfo);
- }
- }
- // Generating
- // try {
- // CallIteratorClose
- // } catch (e) {
- // do nothing
- // }
- void EmitTryCatchAroundClose(
- Js::RegSlot iteratorLocation,
- Js::ByteCodeLabel endLabel,
- ByteCodeGenerator *byteCodeGenerator,
- FuncInfo *funcInfo)
- {
- Js::ByteCodeLabel catchLabel = byteCodeGenerator->Writer()->DefineLabel();
- byteCodeGenerator->Writer()->Br(Js::OpCode::TryCatch, catchLabel);
- //
- // There is no need to add TryScopeRecord here as we are going to call 'return' function and there is not yield expression here.
- EmitIteratorClose(iteratorLocation, byteCodeGenerator, funcInfo);
- byteCodeGenerator->Writer()->Empty(Js::OpCode::Leave);
- byteCodeGenerator->Writer()->Br(endLabel);
- byteCodeGenerator->Writer()->MarkLabel(catchLabel);
- Js::RegSlot catchParamLocation = funcInfo->AcquireTmpRegister();
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::Catch, catchParamLocation);
- funcInfo->ReleaseTmpRegister(catchParamLocation);
- byteCodeGenerator->Writer()->Empty(Js::OpCode::Leave);
- }
- struct ByteCodeGenerator::TryScopeRecord : public JsUtil::DoublyLinkedListElement<TryScopeRecord>
- {
- Js::OpCode op;
- Js::ByteCodeLabel label;
- Js::RegSlot reg1;
- Js::RegSlot reg2;
- TryScopeRecord(Js::OpCode op, Js::ByteCodeLabel label) : op(op), label(label), reg1(Js::Constants::NoRegister), reg2(Js::Constants::NoRegister) { }
- TryScopeRecord(Js::OpCode op, Js::ByteCodeLabel label, Js::RegSlot r1, Js::RegSlot r2) : op(op), label(label), reg1(r1), reg2(r2) { }
- };
- // Generating
- // catch(e) {
- // if (shouldCallReturn)
- // CallReturnWhichWrappedByTryCatch
- // throw e;
- // }
- void EmitTopLevelCatch(Js::ByteCodeLabel catchLabel,
- Js::RegSlot iteratorLocation,
- Js::RegSlot shouldCallReturnLocation,
- Js::RegSlot shouldCallReturnLocationFinally,
- ByteCodeGenerator *byteCodeGenerator,
- FuncInfo *funcInfo)
- {
- Js::ByteCodeLabel afterCatchBlockLabel = byteCodeGenerator->Writer()->DefineLabel();
- byteCodeGenerator->Writer()->Empty(Js::OpCode::Leave);
- byteCodeGenerator->Writer()->Br(afterCatchBlockLabel);
- byteCodeGenerator->Writer()->MarkLabel(catchLabel);
- Js::RegSlot catchParamLocation = funcInfo->AcquireTmpRegister();
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::Catch, catchParamLocation);
- ByteCodeGenerator::TryScopeRecord tryRecForCatch(Js::OpCode::ResumeCatch, catchLabel);
- if (funcInfo->byteCodeFunction->IsCoroutine())
- {
- byteCodeGenerator->tryScopeRecordsList.LinkToEnd(&tryRecForCatch);
- }
- Js::ByteCodeLabel skipCallCloseLabel = byteCodeGenerator->Writer()->DefineLabel();
- byteCodeGenerator->Writer()->BrReg1(Js::OpCode::BrFalse_A, skipCallCloseLabel, shouldCallReturnLocation);
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, shouldCallReturnLocationFinally);
- EmitTryCatchAroundClose(iteratorLocation, skipCallCloseLabel, byteCodeGenerator, funcInfo);
- byteCodeGenerator->Writer()->MarkLabel(skipCallCloseLabel);
- // Rethrow the exception.
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::Throw, catchParamLocation);
- funcInfo->ReleaseTmpRegister(catchParamLocation);
- if (funcInfo->byteCodeFunction->IsCoroutine())
- {
- byteCodeGenerator->tryScopeRecordsList.UnlinkFromEnd();
- }
- byteCodeGenerator->Writer()->Empty(Js::OpCode::Leave);
- byteCodeGenerator->Writer()->MarkLabel(afterCatchBlockLabel);
- }
- // Generating
- // finally {
- // if (shouldCallReturn)
- // CallReturn
- // }
- void EmitTopLevelFinally(Js::ByteCodeLabel finallyLabel,
- Js::RegSlot iteratorLocation,
- Js::RegSlot shouldCallReturnLocation,
- Js::RegSlot yieldExceptionLocation,
- Js::RegSlot yieldOffsetLocation,
- ByteCodeGenerator *byteCodeGenerator,
- FuncInfo *funcInfo)
- {
- bool isCoroutine = funcInfo->byteCodeFunction->IsCoroutine();
- Js::ByteCodeLabel afterFinallyBlockLabel = byteCodeGenerator->Writer()->DefineLabel();
- byteCodeGenerator->Writer()->Empty(Js::OpCode::Leave);
- byteCodeGenerator->Writer()->RecordCrossFrameEntryExitRecord(false);
- byteCodeGenerator->Writer()->RecordCrossFrameEntryExitRecord(true);
- byteCodeGenerator->Writer()->Br(afterFinallyBlockLabel);
- byteCodeGenerator->Writer()->MarkLabel(finallyLabel);
- byteCodeGenerator->Writer()->Empty(Js::OpCode::Finally);
- ByteCodeGenerator::TryScopeRecord tryRecForFinally(Js::OpCode::ResumeFinally, finallyLabel, yieldExceptionLocation, yieldOffsetLocation);
- if (isCoroutine)
- {
- byteCodeGenerator->tryScopeRecordsList.LinkToEnd(&tryRecForFinally);
- }
- Js::ByteCodeLabel skipCallCloseLabel = byteCodeGenerator->Writer()->DefineLabel();
- byteCodeGenerator->Writer()->BrReg1(Js::OpCode::BrFalse_A, skipCallCloseLabel, shouldCallReturnLocation);
- EmitIteratorClose(iteratorLocation, byteCodeGenerator, funcInfo);
- byteCodeGenerator->Writer()->MarkLabel(skipCallCloseLabel);
- if (isCoroutine)
- {
- byteCodeGenerator->tryScopeRecordsList.UnlinkFromEnd();
- funcInfo->ReleaseTmpRegister(yieldOffsetLocation);
- funcInfo->ReleaseTmpRegister(yieldExceptionLocation);
- }
- byteCodeGenerator->Writer()->RecordCrossFrameEntryExitRecord(false);
- byteCodeGenerator->Writer()->Empty(Js::OpCode::LeaveNull);
- byteCodeGenerator->Writer()->MarkLabel(afterFinallyBlockLabel);
- }
- void EmitCatchAndFinallyBlocks(Js::ByteCodeLabel catchLabel,
- Js::ByteCodeLabel finallyLabel,
- Js::RegSlot iteratorLocation,
- Js::RegSlot shouldCallReturnFunctionLocation,
- Js::RegSlot shouldCallReturnFunctionLocationFinally,
- Js::RegSlot yieldExceptionLocation,
- Js::RegSlot yieldOffsetLocation,
- ByteCodeGenerator *byteCodeGenerator,
- FuncInfo *funcInfo
- )
- {
- bool isCoroutine = funcInfo->byteCodeFunction->IsCoroutine();
- if (isCoroutine)
- {
- byteCodeGenerator->tryScopeRecordsList.UnlinkFromEnd();
- }
- EmitTopLevelCatch(catchLabel,
- iteratorLocation,
- shouldCallReturnFunctionLocation,
- shouldCallReturnFunctionLocationFinally,
- byteCodeGenerator,
- funcInfo);
- if (isCoroutine)
- {
- byteCodeGenerator->tryScopeRecordsList.UnlinkFromEnd();
- }
- EmitTopLevelFinally(finallyLabel,
- iteratorLocation,
- shouldCallReturnFunctionLocationFinally,
- yieldExceptionLocation,
- yieldOffsetLocation,
- byteCodeGenerator,
- funcInfo);
- funcInfo->ReleaseTmpRegister(shouldCallReturnFunctionLocationFinally);
- funcInfo->ReleaseTmpRegister(shouldCallReturnFunctionLocation);
- }
- // Emit a wrapper try..finaly block around the destructuring elements
- void EmitDestructuredArray(
- ParseNode *lhs,
- Js::RegSlot rhsLocation,
- ByteCodeGenerator *byteCodeGenerator,
- FuncInfo *funcInfo)
- {
- byteCodeGenerator->StartStatement(lhs);
- Js::RegSlot iteratorLocation = funcInfo->AcquireTmpRegister();
- EmitGetIterator(iteratorLocation, rhsLocation, byteCodeGenerator, funcInfo);
- Assert(lhs->nop == knopArrayPattern);
- ParseNode *list = lhs->AsParseNodeArrLit()->pnode1;
- if (list == nullptr)
- { // Handline this case ([] = obj);
- EmitIteratorClose(iteratorLocation, byteCodeGenerator, funcInfo);
- // No elements to bind or assign.
- funcInfo->ReleaseTmpRegister(iteratorLocation);
- byteCodeGenerator->EndStatement(lhs);
- return;
- }
- // This variable facilitates on when to call the return function (which is Iterator close). When we are emitting bytecode for destructuring element
- // this variable will be set to true.
- Js::RegSlot shouldCallReturnFunctionLocation = funcInfo->AcquireTmpRegister();
- Js::RegSlot shouldCallReturnFunctionLocationFinally = funcInfo->AcquireTmpRegister();
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, shouldCallReturnFunctionLocation);
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, shouldCallReturnFunctionLocationFinally);
- byteCodeGenerator->SetHasFinally(true);
- byteCodeGenerator->SetHasTry(true);
- byteCodeGenerator->TopFuncInfo()->byteCodeFunction->SetDontInline(true);
- Js::RegSlot regException = Js::Constants::NoRegister;
- Js::RegSlot regOffset = Js::Constants::NoRegister;
- bool isCoroutine = funcInfo->byteCodeFunction->IsCoroutine();
- if (isCoroutine)
- {
- regException = funcInfo->AcquireTmpRegister();
- regOffset = funcInfo->AcquireTmpRegister();
- }
- // Insert try node here
- Js::ByteCodeLabel finallyLabel = byteCodeGenerator->Writer()->DefineLabel();
- Js::ByteCodeLabel catchLabel = byteCodeGenerator->Writer()->DefineLabel();
- byteCodeGenerator->Writer()->RecordCrossFrameEntryExitRecord(true);
- ByteCodeGenerator::TryScopeRecord tryRecForTryFinally(Js::OpCode::TryFinallyWithYield, finallyLabel);
- if (isCoroutine)
- {
- byteCodeGenerator->Writer()->BrReg2(Js::OpCode::TryFinallyWithYield, finallyLabel, regException, regOffset);
- tryRecForTryFinally.reg1 = regException;
- tryRecForTryFinally.reg2 = regOffset;
- byteCodeGenerator->tryScopeRecordsList.LinkToEnd(&tryRecForTryFinally);
- }
- else
- {
- byteCodeGenerator->Writer()->Br(Js::OpCode::TryFinally, finallyLabel);
- }
- byteCodeGenerator->Writer()->Br(Js::OpCode::TryCatch, catchLabel);
- ByteCodeGenerator::TryScopeRecord tryRecForTry(Js::OpCode::TryCatch, catchLabel);
- if (isCoroutine)
- {
- byteCodeGenerator->tryScopeRecordsList.LinkToEnd(&tryRecForTry);
- }
- EmitDestructuredArrayCore(list,
- iteratorLocation,
- shouldCallReturnFunctionLocation,
- shouldCallReturnFunctionLocationFinally,
- byteCodeGenerator,
- funcInfo);
- EmitCatchAndFinallyBlocks(catchLabel,
- finallyLabel,
- iteratorLocation,
- shouldCallReturnFunctionLocation,
- shouldCallReturnFunctionLocationFinally,
- regException,
- regOffset,
- byteCodeGenerator,
- funcInfo);
- funcInfo->ReleaseTmpRegister(iteratorLocation);
- byteCodeGenerator->EndStatement(lhs);
- }
- void EmitNameInvoke(Js::RegSlot lhsLocation,
- Js::RegSlot objectLocation,
- Js::RegSlot computedPropIdArrLocation,
- uint32 *computedIndex,
- bool hasRest,
- ParseNodePtr nameNode,
- ByteCodeGenerator* byteCodeGenerator,
- FuncInfo* funcInfo)
- {
- Assert(nameNode != nullptr);
- if (nameNode->nop == knopComputedName)
- {
- ParseNodePtr pnode1 = nameNode->AsParseNodeUni()->pnode1;
- Emit(pnode1, byteCodeGenerator, funcInfo, false/*isConstructorCall*/);
- byteCodeGenerator->Writer()->Element(Js::OpCode::LdElemI_A, lhsLocation, objectLocation, pnode1->location);
- if (hasRest)
- {
- byteCodeGenerator->Writer()->Slot(Js::OpCode::StPropIdArrFromVar, pnode1->location, computedPropIdArrLocation, *computedIndex);
- (*computedIndex)++;
- }
- funcInfo->ReleaseLoc(pnode1);
- }
- else
- {
- Assert(nameNode->nop == knopStr);
- Js::PropertyId propertyId = nameNode->AsParseNodeStr()->pid->GetPropertyId();
- uint cacheId = funcInfo->FindOrAddInlineCacheId(objectLocation, propertyId, false/*isLoadMethod*/, false/*isStore*/);
- byteCodeGenerator->Writer()->PatchableProperty(Js::OpCode::LdFld, lhsLocation, objectLocation, cacheId);
- }
- }
- void EmitDestructuredValueOrInitializer(ParseNodePtr lhsElementNode,
- Js::RegSlot rhsLocation,
- ParseNodePtr initializer,
- bool isNonPatternAssignmentTarget,
- ByteCodeGenerator *byteCodeGenerator,
- FuncInfo *funcInfo)
- {
- // If we have initializer we need to see if the destructured value is undefined or not - if it is undefined we need to assign initializer
- Js::ByteCodeLabel useDefault = -1;
- Js::ByteCodeLabel end = -1;
- Js::RegSlot rhsLocationTmp = rhsLocation;
- if (initializer != nullptr)
- {
- rhsLocationTmp = funcInfo->AcquireTmpRegister();
- useDefault = byteCodeGenerator->Writer()->DefineLabel();
- end = byteCodeGenerator->Writer()->DefineLabel();
- byteCodeGenerator->Writer()->BrReg2(Js::OpCode::BrSrEq_A, useDefault, rhsLocation, funcInfo->undefinedConstantRegister);
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, rhsLocationTmp, rhsLocation);
- byteCodeGenerator->Writer()->Br(end);
- byteCodeGenerator->Writer()->MarkLabel(useDefault);
- Emit(initializer, byteCodeGenerator, funcInfo, false/*isConstructorCall*/);
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, rhsLocationTmp, initializer->location);
- funcInfo->ReleaseLoc(initializer);
- byteCodeGenerator->Writer()->MarkLabel(end);
- }
- if (lhsElementNode->nop == knopArrayPattern)
- {
- EmitDestructuredArray(lhsElementNode, rhsLocationTmp, byteCodeGenerator, funcInfo);
- }
- else if (lhsElementNode->nop == knopObjectPattern)
- {
- EmitDestructuredObject(lhsElementNode, rhsLocationTmp, byteCodeGenerator, funcInfo);
- }
- else if (isNonPatternAssignmentTarget)
- {
- EmitAssignment(nullptr, lhsElementNode, rhsLocationTmp, byteCodeGenerator, funcInfo);
- }
- else
- {
- EmitDestructuredElement(lhsElementNode, rhsLocationTmp, byteCodeGenerator, funcInfo);
- }
- if (initializer != nullptr)
- {
- funcInfo->ReleaseTmpRegister(rhsLocationTmp);
- }
- }
- void EmitDestructuredObjectMember(ParseNodePtr memberNode,
- Js::RegSlot rhsLocation,
- Js::RegSlot propIdArrLocation,
- Js::RegSlot computedPropIdArrLocation,
- uint32 *computedIndex,
- bool hasRest,
- ByteCodeGenerator *byteCodeGenerator,
- FuncInfo *funcInfo)
- {
- Assert(memberNode->nop == knopObjectPatternMember || memberNode->nop == knopEllipsis);
- Js::RegSlot nameLocation = funcInfo->AcquireTmpRegister();
- ParseNodePtr lhsElementNode = nullptr;
- if (memberNode->nop == knopObjectPatternMember)
- {
- EmitNameInvoke(nameLocation, rhsLocation, computedPropIdArrLocation,
- computedIndex, hasRest, memberNode->AsParseNodeBin()->pnode1, byteCodeGenerator, funcInfo);
- // Imagine we are transforming
- // {x:x1} = {} to x1 = {}.x (here x1 is the second node of the member but that is our lhsnode)
- lhsElementNode = memberNode->AsParseNodeBin()->pnode2;
- }
- else
- {
- // memberNode->nop == knopEllipsis, aka we are performing Rest operation
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::NewScObjectSimple, nameLocation);
- byteCodeGenerator->Writer()->Reg4(Js::OpCode::Restify, rhsLocation, nameLocation, propIdArrLocation, computedPropIdArrLocation);
- lhsElementNode = memberNode->AsParseNodeUni()->pnode1;
- }
- ParseNodePtr init = nullptr;
- if (lhsElementNode->IsVarLetOrConst())
- {
- init = lhsElementNode->AsParseNodeVar()->pnodeInit;
- }
- else if (lhsElementNode->nop == knopAsg)
- {
- init = lhsElementNode->AsParseNodeBin()->pnode2;
- lhsElementNode = lhsElementNode->AsParseNodeBin()->pnode1;
- }
- EmitDestructuredValueOrInitializer(lhsElementNode, nameLocation, init, false /*isNonPatternAssignmentTarget*/, byteCodeGenerator, funcInfo);
- funcInfo->ReleaseTmpRegister(nameLocation);
- }
- void EmitObjectPropertyIdsToArray(ByteCodeGenerator *byteCodeGenerator,
- Js::PropertyId *ids,
- ParseNodePtr memberNodes,
- uint32 staticCount,
- bool *hasComputedProps)
- {
- uint32 index = 0;
- Parser::ForEachItemInList(memberNodes, [&](ParseNodePtr current) {
- if (current->nop != knopEllipsis)
- {
- ParseNodePtr nameNode = current->AsParseNodeBin()->pnode1;
- Assert(nameNode != nullptr);
- Assert(nameNode->nop == knopComputedName || nameNode->nop == knopStr);
- if (nameNode->nop == knopStr)
- {
- if (index >= staticCount)
- {
- Js::Throw::InternalError();
- return;
- }
- ids[index] = nameNode->AsParseNodeStr()->pid->GetPropertyId();
- index++;
- }
- else
- {
- *hasComputedProps = true;
- }
- }
- });
- }
- void EmitDestructuredObject(ParseNode *lhs,
- Js::RegSlot rhsLocationOrig,
- ByteCodeGenerator *byteCodeGenerator,
- FuncInfo *funcInfo)
- {
- Assert(lhs->nop == knopObjectPattern);
- ParseNodeObjLit *pnodeObjLit = lhs->AsParseNodeObjLit();
- ParseNodePtr pnode1 = pnodeObjLit->pnode1;
- uint32 staticCount = pnodeObjLit->staticCount;
- uint32 computedCount = pnodeObjLit->computedCount;
- bool hasRest = pnodeObjLit->hasRest;
- bool hasComputedProps = false;
- byteCodeGenerator->StartStatement(lhs);
- Js::ByteCodeLabel skipThrow = byteCodeGenerator->Writer()->DefineLabel();
- Js::RegSlot rhsLocation = funcInfo->AcquireTmpRegister();
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, rhsLocation, rhsLocationOrig);
- byteCodeGenerator->Writer()->BrReg2(Js::OpCode::BrNeq_A, skipThrow, rhsLocation, funcInfo->undefinedConstantRegister);
- byteCodeGenerator->Writer()->W1(Js::OpCode::RuntimeTypeError, SCODE_CODE(JSERR_ObjectCoercible));
- byteCodeGenerator->Writer()->MarkLabel(skipThrow);
- if (pnode1 != nullptr)
- {
- Js::RegSlot propIdArrLocation = Js::Constants::NoRegister;
- Js::RegSlot computedPropIdArrLocation = Js::Constants::NoRegister;
- if (hasRest)
- {
- uint extraAlloc = UInt32Math::Mul(staticCount, sizeof(Js::PropertyId));
- uint auxSize = UInt32Math::Add(sizeof(Js::PropertyIdArray), extraAlloc);
- Js::PropertyIdArray *propIds = AnewPlus(byteCodeGenerator->GetAllocator(), extraAlloc, Js::PropertyIdArray, staticCount, 0);
- Assert(pnode1->nop == knopList || pnode1->nop == knopObjectPatternMember || pnode1->nop == knopEllipsis);
- EmitObjectPropertyIdsToArray(byteCodeGenerator, propIds->elements, pnode1, staticCount, &hasComputedProps);
- // Load static PropertyIdArray here
- propIdArrLocation = funcInfo->AcquireTmpRegister();
- byteCodeGenerator->Writer()->Auxiliary(Js::OpCode::LdPropIds, propIdArrLocation, propIds, auxSize, staticCount);
- if (hasComputedProps)
- {
- computedPropIdArrLocation = funcInfo->AcquireTmpRegister();
- byteCodeGenerator->Writer()->Reg1Unsigned1(Js::OpCode::NewPropIdArrForCompProps, computedPropIdArrLocation, computedCount);
- }
- else
- {
- computedPropIdArrLocation = propIdArrLocation;
- }
- }
- uint32 index = 0;
- Parser::ForEachItemInList(pnode1, [&](ParseNodePtr memberNode) {
- EmitDestructuredObjectMember(memberNode, rhsLocation, propIdArrLocation, computedPropIdArrLocation,
- &index, hasRest, byteCodeGenerator, funcInfo);
- });
- if (hasRest)
- {
- if (hasComputedProps)
- {
- funcInfo->ReleaseTmpRegister(computedPropIdArrLocation);
- }
- funcInfo->ReleaseTmpRegister(propIdArrLocation);
- }
- }
- funcInfo->ReleaseTmpRegister(rhsLocation);
-
- byteCodeGenerator->EndStatement(lhs);
- }
- void EmitAssignment(
- ParseNode *asgnNode,
- ParseNode *lhs,
- Js::RegSlot rhsLocation,
- ByteCodeGenerator *byteCodeGenerator,
- FuncInfo *funcInfo)
- {
- switch (lhs->nop)
- {
- // assignment to a local or global variable
- case knopVarDecl:
- case knopLetDecl:
- case knopConstDecl:
- {
- Symbol *sym = lhs->AsParseNodeVar()->sym;
- Assert(sym != nullptr);
- byteCodeGenerator->EmitPropStore(rhsLocation, sym, nullptr, funcInfo, lhs->nop == knopLetDecl, lhs->nop == knopConstDecl);
- break;
- }
- case knopName:
- {
- // Special names like 'this' or 'new.target' cannot be assigned to
- ParseNodeName * pnodeNameLhs = lhs->AsParseNodeName();
- if (pnodeNameLhs->IsSpecialName())
- {
- byteCodeGenerator->Writer()->W1(Js::OpCode::RuntimeReferenceError, SCODE_CODE(JSERR_CantAssignTo));
- }
- else
- {
- byteCodeGenerator->EmitPropStore(rhsLocation, pnodeNameLhs->sym, pnodeNameLhs->pid, funcInfo);
- }
- break;
- }
- // x.y =
- case knopDot:
- {
- // PutValue(x, "y", rhs)
- Js::PropertyId propertyId = lhs->AsParseNodeBin()->pnode2->AsParseNodeName()->PropertyIdFromNameNode();
- if (ByteCodeGenerator::IsSuper(lhs->AsParseNodeBin()->pnode1))
- {
- Emit(lhs->AsParseNodeSuperReference()->pnodeThis, byteCodeGenerator, funcInfo, false);
- Js::RegSlot tmpReg = byteCodeGenerator->EmitLdObjProto(Js::OpCode::LdHomeObjProto, lhs->AsParseNodeBin()->pnode1->location, funcInfo);
- funcInfo->ReleaseLoc(lhs->AsParseNodeSuperReference()->pnodeThis);
- uint cacheId = funcInfo->FindOrAddInlineCacheId(tmpReg, propertyId, false, true);
- byteCodeGenerator->Writer()->PatchablePropertyWithThisPtr(Js::OpCode::StSuperFld, rhsLocation, tmpReg, lhs->AsParseNodeSuperReference()->pnodeThis->location, cacheId);
- }
- else
- {
- uint cacheId = funcInfo->FindOrAddInlineCacheId(lhs->AsParseNodeBin()->pnode1->location, propertyId, false, true);
- byteCodeGenerator->Writer()->PatchableProperty(
- ByteCodeGenerator::GetStFldOpCode(funcInfo, false, false, false, false, byteCodeGenerator->forceStrictModeForClassComputedPropertyName), rhsLocation, lhs->AsParseNodeBin()->pnode1->location, cacheId);
- }
- break;
- }
- case knopIndex:
- {
- Js::RegSlot targetLocation = lhs->AsParseNodeBin()->pnode1->location;
- if (ByteCodeGenerator::IsSuper(lhs->AsParseNodeBin()->pnode1))
- {
- // We need to emit the 'this' node for the super reference even if we aren't planning to use the 'this' value.
- // This is because we might be in a derived class constructor where we haven't yet called super() to bind the 'this' value.
- // See ecma262 abstract operation 'MakeSuperPropertyReference'
- Emit(lhs->AsParseNodeSuperReference()->pnodeThis, byteCodeGenerator, funcInfo, false);
- funcInfo->ReleaseLoc(lhs->AsParseNodeSuperReference()->pnodeThis);
- targetLocation = byteCodeGenerator->EmitLdObjProto(Js::OpCode::LdHomeObjProto, targetLocation, funcInfo);
- }
- byteCodeGenerator->Writer()->Element(
- ByteCodeGenerator::GetStElemIOpCode(funcInfo),
- rhsLocation, targetLocation, lhs->AsParseNodeBin()->pnode2->location);
- break;
- }
- case knopObjectPattern:
- {
- Assert(byteCodeGenerator->IsES6DestructuringEnabled());
- // Copy the rhs value to be the result of the assignment if needed.
- if (asgnNode != nullptr)
- {
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, asgnNode->location, rhsLocation);
- }
- return EmitDestructuredObject(lhs, rhsLocation, byteCodeGenerator, funcInfo);
- }
- case knopArrayPattern:
- {
- Assert(byteCodeGenerator->IsES6DestructuringEnabled());
- // Copy the rhs value to be the result of the assignment if needed.
- if (asgnNode != nullptr)
- {
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, asgnNode->location, rhsLocation);
- }
- return EmitDestructuredArray(lhs, rhsLocation, byteCodeGenerator, funcInfo);
- }
- case knopArray:
- case knopObject:
- // Assignment to array/object can get through to byte code gen when the parser fails to convert destructuring
- // assignment to pattern (because of structural mismatch between LHS & RHS?). Revisit when we nail
- // down early vs. runtime errors for destructuring.
- byteCodeGenerator->Writer()->W1(Js::OpCode::RuntimeReferenceError, SCODE_CODE(JSERR_CantAssignTo));
- break;
- default:
- byteCodeGenerator->Writer()->W1(Js::OpCode::RuntimeReferenceError, SCODE_CODE(JSERR_CantAssignTo));
- break;
- }
- if (asgnNode != nullptr)
- {
- // We leave it up to the caller to pass this node only if the assignment expression is used.
- if (asgnNode->location != rhsLocation)
- {
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, asgnNode->location, rhsLocation);
- }
- }
- }
- void EmitLoad(
- ParseNode *lhs,
- ByteCodeGenerator *byteCodeGenerator,
- FuncInfo *funcInfo)
- {
- // Emit the instructions to load the value into the LHS location. Do not assign/free any temps
- // in the process.
- // We usually get here as part of an op-equiv expression: x.y += z;
- // In such a case, x has to be emitted first, then the value of x.y loaded (by this function), then z emitted.
- switch (lhs->nop)
- {
- // load of a local or global variable
- case knopName:
- {
- funcInfo->AcquireLoc(lhs);
- byteCodeGenerator->EmitPropLoad(lhs->location, lhs->AsParseNodeName()->sym, lhs->AsParseNodeName()->pid, funcInfo);
- break;
- }
- // = x.y
- case knopDot:
- {
- // get field id for "y"
- Js::PropertyId propertyId = lhs->AsParseNodeBin()->pnode2->AsParseNodeName()->PropertyIdFromNameNode();
- funcInfo->AcquireLoc(lhs);
- EmitReference(lhs, byteCodeGenerator, funcInfo);
- uint cacheId = funcInfo->FindOrAddInlineCacheId(lhs->AsParseNodeBin()->pnode1->location, propertyId, false, false);
- byteCodeGenerator->Writer()->PatchableProperty(Js::OpCode::LdFld, lhs->location, lhs->AsParseNodeBin()->pnode1->location, cacheId);
- break;
- }
- case knopIndex:
- funcInfo->AcquireLoc(lhs);
- EmitReference(lhs, byteCodeGenerator, funcInfo);
- byteCodeGenerator->Writer()->Element(
- Js::OpCode::LdElemI_A, lhs->location, lhs->AsParseNodeBin()->pnode1->location, lhs->AsParseNodeBin()->pnode2->location);
- break;
- // f(x) +=
- case knopCall:
- {
- ParseNodeCall * pnodeCallLhs = lhs->AsParseNodeCall();
- if (pnodeCallLhs->pnodeTarget->nop == knopImport)
- {
- ParseNodePtr args = pnodeCallLhs->pnodeArgs;
- Assert(CountArguments(args) == 2); // import() takes one argument
- Emit(args, byteCodeGenerator, funcInfo, false);
- funcInfo->ReleaseLoc(args);
- funcInfo->AcquireLoc(pnodeCallLhs);
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::ImportCall, pnodeCallLhs->location, args->location);
- }
- else
- {
- funcInfo->AcquireLoc(pnodeCallLhs);
- EmitReference(pnodeCallLhs, byteCodeGenerator, funcInfo);
- EmitCall(pnodeCallLhs, byteCodeGenerator, funcInfo, /*fReturnValue=*/ false, /*fEvaluateComponents=*/ false);
- }
- break;
- }
- default:
- funcInfo->AcquireLoc(lhs);
- Emit(lhs, byteCodeGenerator, funcInfo, false);
- break;
- }
- }
- void EmitList(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo)
- {
- if (pnode != nullptr)
- {
- while (pnode->nop == knopList)
- {
- byteCodeGenerator->EmitTopLevelStatement(pnode->AsParseNodeBin()->pnode1, funcInfo, false);
- pnode = pnode->AsParseNodeBin()->pnode2;
- }
- byteCodeGenerator->EmitTopLevelStatement(pnode, funcInfo, false);
- }
- }
- void EmitOneArg(
- ParseNode *pnode,
- BOOL fAssignRegs,
- ByteCodeGenerator *byteCodeGenerator,
- FuncInfo *funcInfo,
- Js::ProfileId callSiteId,
- Js::ArgSlot &argIndex,
- Js::ArgSlot &spreadIndex,
- Js::RegSlot argTempLocation,
- bool emitProfiledArgout,
- Js::AuxArray<uint32> *spreadIndices = nullptr
- )
- {
- bool noArgOuts = argTempLocation != Js::Constants::NoRegister;
- // If this is a put, the arguments have already been evaluated (see EmitReference).
- // We just need to emit the ArgOut instructions.
- if (fAssignRegs)
- {
- Emit(pnode, byteCodeGenerator, funcInfo, false);
- }
- if (pnode->nop == knopEllipsis)
- {
- Assert(spreadIndices != nullptr);
- spreadIndices->elements[spreadIndex++] = argIndex + 1; // account for 'this'
- Js::RegSlot regVal = funcInfo->AcquireTmpRegister();
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::LdCustomSpreadIteratorList, regVal, pnode->location);
- if (noArgOuts)
- {
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, argTempLocation, regVal);
- }
- else
- {
- byteCodeGenerator->Writer()->ArgOut<true>(argIndex + 1, regVal, callSiteId, emitProfiledArgout);
- }
- funcInfo->ReleaseTmpRegister(regVal);
- }
- else
- {
- if (noArgOuts)
- {
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, argTempLocation, pnode->location);
- }
- else
- {
- byteCodeGenerator->Writer()->ArgOut<true>(argIndex + 1, pnode->location, callSiteId, emitProfiledArgout);
- }
- }
- argIndex++;
- if (fAssignRegs)
- {
- funcInfo->ReleaseLoc(pnode);
- }
- }
- size_t EmitArgsWithArgOutsAtEnd(
- ParseNode *pnode,
- BOOL fAssignRegs,
- ByteCodeGenerator *byteCodeGenerator,
- FuncInfo *funcInfo,
- Js::ProfileId callSiteId,
- Js::RegSlot thisLocation,
- Js::ArgSlot argsCountForStartCall,
- bool emitProfiledArgouts,
- Js::AuxArray<uint32> *spreadIndices = nullptr
- )
- {
- AssertOrFailFast(pnode != nullptr);
- Js::ArgSlot argIndex = 0;
- Js::ArgSlot spreadIndex = 0;
- Js::RegSlot argTempLocation = funcInfo->AcquireTmpRegister();
- Js::RegSlot firstArgTempLocation = argTempLocation;
- while (pnode->nop == knopList)
- {
- EmitOneArg(pnode->AsParseNodeBin()->pnode1, fAssignRegs, byteCodeGenerator, funcInfo, callSiteId, argIndex, spreadIndex, argTempLocation, false /*emitProfiledArgout*/, spreadIndices);
- pnode = pnode->AsParseNodeBin()->pnode2;
- argTempLocation = funcInfo->AcquireTmpRegister();
- }
- EmitOneArg(pnode, fAssignRegs, byteCodeGenerator, funcInfo, callSiteId, argIndex, spreadIndex, argTempLocation, false /*emitProfiledArgout*/, spreadIndices);
- byteCodeGenerator->Writer()->StartCall(Js::OpCode::StartCall, argsCountForStartCall);
- // Emit all argOuts now
- if (thisLocation != Js::Constants::NoRegister)
- {
- // Emit the "this" object.
- byteCodeGenerator->Writer()->ArgOut<true>(0, thisLocation, callSiteId, false /*emitProfiledArgouts*/);
- }
- for (Js::ArgSlot index = 0; index < argIndex; index++)
- {
- byteCodeGenerator->Writer()->ArgOut<true>(index + 1, firstArgTempLocation + index, callSiteId, emitProfiledArgouts);
- }
- // Now release all those temps register
- for (Js::ArgSlot index = argIndex; index > 0; index--)
- {
- funcInfo->ReleaseTmpRegister(argTempLocation--);
- }
- return argIndex;
- }
- size_t EmitArgs(
- ParseNode *pnode,
- BOOL fAssignRegs,
- ByteCodeGenerator *byteCodeGenerator,
- FuncInfo *funcInfo,
- Js::ProfileId callSiteId,
- bool emitProfiledArgouts,
- Js::AuxArray<uint32> *spreadIndices = nullptr
- )
- {
- Js::ArgSlot argIndex = 0;
- Js::ArgSlot spreadIndex = 0;
- if (pnode != nullptr)
- {
- while (pnode->nop == knopList)
- {
- EmitOneArg(pnode->AsParseNodeBin()->pnode1, fAssignRegs, byteCodeGenerator, funcInfo, callSiteId, argIndex, spreadIndex, Js::Constants::NoRegister, emitProfiledArgouts, spreadIndices);
- pnode = pnode->AsParseNodeBin()->pnode2;
- }
- EmitOneArg(pnode, fAssignRegs, byteCodeGenerator, funcInfo, callSiteId, argIndex, spreadIndex, Js::Constants::NoRegister, emitProfiledArgouts, spreadIndices);
- }
- return argIndex;
- }
- void EmitArgListStart(
- Js::RegSlot thisLocation,
- ByteCodeGenerator *byteCodeGenerator,
- FuncInfo *funcInfo,
- Js::ProfileId callSiteId)
- {
- if (thisLocation != Js::Constants::NoRegister)
- {
- // Emit the "this" object.
- byteCodeGenerator->Writer()->ArgOut<true>(0, thisLocation, callSiteId, false /*emitProfiledArgout*/);
- }
- }
- Js::ArgSlot EmitArgListEnd(
- ParseNode *pnode,
- Js::RegSlot thisLocation,
- Js::RegSlot evalLocation,
- Js::RegSlot newTargetLocation,
- ByteCodeGenerator *byteCodeGenerator,
- FuncInfo *funcInfo,
- size_t argIndex,
- Js::ProfileId callSiteId)
- {
- BOOL fEvalInModule = false;
- BOOL fIsEval = (evalLocation != Js::Constants::NoRegister);
- BOOL fHasNewTarget = (newTargetLocation != Js::Constants::NoRegister);
- static const size_t maxExtraArgSlot = 4; // max(extraEvalArg, extraArg), where extraEvalArg==2 (moduleRoot,env), extraArg==4 (this, eval, evalInModule, newTarget)
- AssertOrFailFastMsg(argIndex < Js::Constants::UShortMaxValue - maxExtraArgSlot, "Number of allowed arguments are already capped at parser level");
- Js::ArgSlot argSlotIndex = (Js::ArgSlot) argIndex;
- Js::ArgSlot evalIndex;
- if (fIsEval && argSlotIndex > 0)
- {
- Assert(!fHasNewTarget);
- // Pass the frame display as an extra argument to "eval".
- // Do this only if eval is called with some args
- Js::RegSlot evalEnv;
- if (funcInfo->IsGlobalFunction() && !(funcInfo->GetIsStrictMode() && byteCodeGenerator->GetFlags() & fscrEval))
- {
- // Use current environment as the environment for the function being called when:
- // - this is the root global function (not an eval's global function)
- // - this is an eval's global function that is not in strict mode (see else block)
- evalEnv = funcInfo->GetEnvRegister();
- }
- else
- {
- // Use the frame display as the environment for the function being called when:
- // - this is not a global function and thus it will have its own scope
- // - this is an eval's global function that is in strict mode, since in strict mode the eval's global function
- // has its own scope
- evalEnv = funcInfo->frameDisplayRegister;
- }
- evalEnv = byteCodeGenerator->PrependLocalScopes(evalEnv, evalLocation, funcInfo);
- // Passing the FrameDisplay as an extra argument
- evalIndex = argSlotIndex + 1;
- if (evalEnv == funcInfo->GetEnvRegister() || evalEnv == funcInfo->frameDisplayRegister)
- {
- byteCodeGenerator->Writer()->ArgOutEnv(evalIndex);
- }
- else
- {
- byteCodeGenerator->Writer()->ArgOut<false>(evalIndex, evalEnv, callSiteId, false /*emitProfiledArgout*/);
- }
- }
- if (fHasNewTarget)
- {
- Assert(!fIsEval);
- byteCodeGenerator->Writer()->ArgOut<true>(argSlotIndex + 1, newTargetLocation, callSiteId, false /*emitProfiledArgout*/);
- }
- Js::ArgSlot argIntCount = argSlotIndex + 1 + (Js::ArgSlot)fIsEval + (Js::ArgSlot)fEvalInModule + (Js::ArgSlot)fHasNewTarget;
- // eval and no args passed, return 1 as argument count
- if (fIsEval && pnode == nullptr)
- {
- return 1;
- }
- return argIntCount;
- }
- Js::ArgSlot EmitArgList(
- ParseNode *pnode,
- Js::RegSlot thisLocation,
- Js::RegSlot newTargetLocation,
- BOOL fIsEval,
- BOOL fAssignRegs,
- ByteCodeGenerator *byteCodeGenerator,
- FuncInfo *funcInfo,
- Js::ProfileId callSiteId,
- Js::ArgSlot argsCountForStartCall,
- bool emitArgOutsAtEnd,
- bool emitProfiledArgouts,
- uint16 spreadArgCount = 0,
- Js::AuxArray<uint32> **spreadIndices = nullptr)
- {
- // This function emits the arguments for a call.
- // ArgOut's with uses immediately following defs.
- if (!emitArgOutsAtEnd)
- {
- byteCodeGenerator->Writer()->StartCall(Js::OpCode::StartCall, argsCountForStartCall);
- EmitArgListStart(thisLocation, byteCodeGenerator, funcInfo, callSiteId);
- }
- Js::RegSlot evalLocation = Js::Constants::NoRegister;
- //
- // If Emitting arguments for eval and assigning registers, get a tmpLocation for eval.
- // This would be used while generating frameDisplay in EmitArgListEnd.
- //
- if (fIsEval)
- {
- evalLocation = funcInfo->AcquireTmpRegister();
- }
- if (spreadArgCount > 0)
- {
- const size_t extraAlloc = UInt32Math::Mul(spreadArgCount, sizeof(uint32));
- Assert(spreadIndices != nullptr);
- *spreadIndices = AnewPlus(byteCodeGenerator->GetAllocator(), extraAlloc, Js::AuxArray<uint32>, spreadArgCount);
- }
- size_t argIndex = 0;
- if (emitArgOutsAtEnd)
- {
- argIndex = EmitArgsWithArgOutsAtEnd(pnode, fAssignRegs, byteCodeGenerator, funcInfo, callSiteId, thisLocation, argsCountForStartCall, emitProfiledArgouts, spreadIndices == nullptr ? nullptr : *spreadIndices);
- }
- else
- {
- argIndex = EmitArgs(pnode, fAssignRegs, byteCodeGenerator, funcInfo, callSiteId, emitProfiledArgouts, spreadIndices == nullptr ? nullptr : *spreadIndices);
- }
- Js::ArgSlot argumentsCount = EmitArgListEnd(pnode, thisLocation, evalLocation, newTargetLocation, byteCodeGenerator, funcInfo, argIndex, callSiteId);
- if (fIsEval)
- {
- funcInfo->ReleaseTmpRegister(evalLocation);
- }
- return argumentsCount;
- }
- void EmitConstantArgsToVarArray(ByteCodeGenerator *byteCodeGenerator, __out_ecount(argCount) Js::Var *vars, ParseNode *args, uint argCount)
- {
- uint index = 0;
- while (args->nop == knopList && index < argCount)
- {
- if (args->AsParseNodeBin()->pnode1->nop == knopInt)
- {
- int value = args->AsParseNodeBin()->pnode1->AsParseNodeInt()->lw;
- vars[index++] = Js::TaggedInt::ToVarUnchecked(value);
- }
- else if (args->AsParseNodeBin()->pnode1->nop == knopFlt)
- {
- Js::Var number = Js::JavascriptNumber::New(args->AsParseNodeBin()->pnode1->AsParseNodeFloat()->dbl, byteCodeGenerator->GetScriptContext());
- #if ! FLOATVAR
- byteCodeGenerator->GetScriptContext()->BindReference(number);
- #endif
- vars[index++] = number;
- }
- else
- {
- AnalysisAssert(false);
- }
- args = args->AsParseNodeBin()->pnode2;
- }
- if (index == argCount)
- {
- Assert(false);
- Js::Throw::InternalError();
- return;
- }
- if (args->nop == knopInt)
- {
- int value = args->AsParseNodeInt()->lw;
- vars[index++] = Js::TaggedInt::ToVarUnchecked(value);
- }
- else if (args->nop == knopFlt)
- {
- Js::Var number = Js::JavascriptNumber::New(args->AsParseNodeFloat()->dbl, byteCodeGenerator->GetScriptContext());
- #if ! FLOATVAR
- byteCodeGenerator->GetScriptContext()->BindReference(number);
- #endif
- vars[index++] = number;
- }
- else
- {
- AnalysisAssert(false);
- }
- }
- void EmitConstantArgsToIntArray(ByteCodeGenerator *byteCodeGenerator, __out_ecount(argCount) int32 *vars, ParseNode *args, uint argCount)
- {
- uint index = 0;
- while (args->nop == knopList && index < argCount)
- {
- Assert(args->AsParseNodeBin()->pnode1->nop == knopInt);
- vars[index++] = args->AsParseNodeBin()->pnode1->AsParseNodeInt()->lw;
- args = args->AsParseNodeBin()->pnode2;
- }
- if (index >= argCount)
- {
- Js::Throw::InternalError();
- return;
- }
- Assert(args->nop == knopInt);
- vars[index++] = args->AsParseNodeInt()->lw;
- Assert(index == argCount);
- }
- void EmitConstantArgsToFltArray(ByteCodeGenerator *byteCodeGenerator, __out_ecount(argCount) double *vars, ParseNode *args, uint argCount)
- {
- uint index = 0;
- while (args->nop == knopList && index < argCount)
- {
- OpCode nop = args->AsParseNodeBin()->pnode1->nop;
- if (nop == knopInt)
- {
- vars[index++] = (double)args->AsParseNodeBin()->pnode1->AsParseNodeInt()->lw;
- }
- else
- {
- Assert(nop == knopFlt);
- vars[index++] = args->AsParseNodeBin()->pnode1->AsParseNodeFloat()->dbl;
- }
- args = args->AsParseNodeBin()->pnode2;
- }
- if (index >= argCount)
- {
- Js::Throw::InternalError();
- return;
- }
- if (args->nop == knopInt)
- {
- vars[index++] = (double)args->AsParseNodeInt()->lw;
- }
- else
- {
- Assert(args->nop == knopFlt);
- vars[index++] = args->AsParseNodeFloat()->dbl;
- }
- Assert(index == argCount);
- }
- //
- // Called when we have new Ctr(constant, constant...)
- //
- Js::ArgSlot EmitNewObjectOfConstants(
- ParseNode *pnode,
- ByteCodeGenerator *byteCodeGenerator,
- FuncInfo *funcInfo,
- unsigned int argCount)
- {
- EmitArgListStart(Js::Constants::NoRegister, byteCodeGenerator, funcInfo, Js::Constants::NoProfileId);
- // Create the vars array
- Js::VarArrayVarCount *vars = AnewPlus(byteCodeGenerator->GetAllocator(), UInt32Math::Mul((argCount - 1), sizeof(Js::Var)), Js::VarArrayVarCount, Js::TaggedInt::ToVarUnchecked(argCount - 1));
- // Emit all constants to the vars array
- EmitConstantArgsToVarArray(byteCodeGenerator, vars->elements, pnode->AsParseNodeCall()->pnodeArgs, argCount - 1);
- // Finish the arg list
- Js::ArgSlot actualArgCount = EmitArgListEnd(
- pnode->AsParseNodeCall()->pnodeArgs,
- Js::Constants::NoRegister,
- Js::Constants::NoRegister,
- Js::Constants::NoRegister,
- byteCodeGenerator,
- funcInfo,
- argCount - 1,
- Js::Constants::NoProfileId);
- // Make sure the cacheId to regSlot map in the ByteCodeWriter is left in a consistent state after writing NewScObject_A
- byteCodeGenerator->Writer()->RemoveEntryForRegSlotFromCacheIdMap(pnode->AsParseNodeCall()->pnodeTarget->location);
- // Generate the opcode with vars
- byteCodeGenerator->Writer()->AuxiliaryContext(
- Js::OpCode::NewScObject_A,
- funcInfo->AcquireLoc(pnode),
- vars,
- UInt32Math::MulAdd<sizeof(Js::Var), sizeof(Js::VarArray)>((argCount-1)),
- pnode->AsParseNodeCall()->pnodeTarget->location);
- AdeletePlus(byteCodeGenerator->GetAllocator(), UInt32Math::Mul((argCount-1), sizeof(Js::VarArrayVarCount)), vars);
- return actualArgCount;
- }
- void EmitMethodFld(bool isRoot, bool isScoped, Js::RegSlot location, Js::RegSlot callObjLocation, Js::PropertyId propertyId, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo, bool registerCacheIdForCall = true)
- {
- Js::OpCode opcode;
- if (!isRoot)
- {
- if (callObjLocation == funcInfo->frameObjRegister)
- {
- opcode = Js::OpCode::LdLocalMethodFld;
- }
- else
- {
- opcode = Js::OpCode::LdMethodFld;
- }
- }
- else if (isScoped)
- {
- opcode = Js::OpCode::ScopedLdMethodFld;
- }
- else
- {
- opcode = Js::OpCode::LdRootMethodFld;
- }
- if (isScoped || !isRoot)
- {
- Assert(isScoped || !isRoot || callObjLocation == ByteCodeGenerator::RootObjectRegister);
- uint cacheId = funcInfo->FindOrAddInlineCacheId(callObjLocation, propertyId, true, false);
- if (callObjLocation == funcInfo->frameObjRegister)
- {
- byteCodeGenerator->Writer()->ElementP(opcode, location, cacheId, false /*isCtor*/, registerCacheIdForCall);
- }
- else
- {
- byteCodeGenerator->Writer()->PatchableProperty(opcode, location, callObjLocation, cacheId, false /*isCtor*/, registerCacheIdForCall);
- }
- }
- else
- {
- uint cacheId = funcInfo->FindOrAddRootObjectInlineCacheId(propertyId, true, false);
- byteCodeGenerator->Writer()->PatchableRootProperty(opcode, location, cacheId, true, false, registerCacheIdForCall);
- }
- }
- void EmitMethodFld(ParseNode *pnode, Js::RegSlot callObjLocation, Js::PropertyId propertyId, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo, bool registerCacheIdForCall = true)
- {
- // Load a call target of the form x.y(). (Call target may be a plain knopName if we're getting it from
- // the global object, etc.)
- bool isRoot = pnode->nop == knopName && (pnode->AsParseNodeName()->sym == nullptr || pnode->AsParseNodeName()->sym->GetIsGlobal());
- bool isScoped = (byteCodeGenerator->GetFlags() & fscrEval) != 0 ||
- (isRoot && callObjLocation != ByteCodeGenerator::RootObjectRegister);
- EmitMethodFld(isRoot, isScoped, pnode->location, callObjLocation, propertyId, byteCodeGenerator, funcInfo, registerCacheIdForCall);
- }
- // lhs.apply(this, arguments);
- void EmitApplyCall(ParseNodeCall* pnodeCall, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo, BOOL fReturnValue)
- {
- ParseNode* applyNode = pnodeCall->pnodeTarget;
- ParseNode* thisNode = pnodeCall->pnodeArgs->AsParseNodeBin()->pnode1;
- Assert(applyNode->nop == knopDot);
- ParseNode* funcNode = applyNode->AsParseNodeBin()->pnode1;
- Js::ByteCodeLabel slowPath = byteCodeGenerator->Writer()->DefineLabel();
- Js::ByteCodeLabel afterSlowPath = byteCodeGenerator->Writer()->DefineLabel();
- Js::ByteCodeLabel argsAlreadyCreated = byteCodeGenerator->Writer()->DefineLabel();
- Assert(applyNode->nop == knopDot);
- Emit(funcNode, byteCodeGenerator, funcInfo, false);
- funcInfo->AcquireLoc(applyNode);
- Js::PropertyId propertyId = applyNode->AsParseNodeBin()->pnode2->AsParseNodeName()->PropertyIdFromNameNode();
- // As we won't be emitting a call instruction for apply, no need to register the cacheId for apply
- // load to be associated with the call. This is also required, as in the absence of a corresponding
- // call for apply, we won't remove the entry for "apply" cacheId from
- // ByteCodeWriter::callRegToLdFldCacheIndexMap, which is contrary to our assumption that we would
- // have removed an entry from a map upon seeing its corresponding call.
- EmitMethodFld(applyNode, funcNode->location, propertyId, byteCodeGenerator, funcInfo, false /*registerCacheIdForCall*/);
- Symbol *argSym = funcInfo->GetArgumentsSymbol();
- Assert(argSym && argSym->IsArguments());
- Js::RegSlot argumentsLoc = argSym->GetLocation();
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdArgumentsFromFrame, argumentsLoc);
- byteCodeGenerator->Writer()->BrReg1(Js::OpCode::BrNotNull_A, argsAlreadyCreated, argumentsLoc);
- // If apply is overridden, bail to slow path.
- byteCodeGenerator->Writer()->BrReg1(Js::OpCode::BrFncNeqApply, slowPath, applyNode->location);
- // Note: acquire and release a temp register for this stack arg pointer instead of trying to stash it
- // in funcInfo->stackArgReg. Otherwise, we'll needlessly load and store it in jitted loop bodies and
- // may crash if we try to unbox it on the store.
- Js::RegSlot stackArgReg = funcInfo->AcquireTmpRegister();
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdStackArgPtr, stackArgReg);
- Js::RegSlot argCountLocation = funcInfo->AcquireTmpRegister();
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdArgCnt, argCountLocation);
- byteCodeGenerator->Writer()->Reg5(Js::OpCode::ApplyArgs, funcNode->location, funcNode->location, thisNode->location, stackArgReg, argCountLocation);
- funcInfo->ReleaseTmpRegister(argCountLocation);
- funcInfo->ReleaseTmpRegister(stackArgReg);
- funcInfo->ReleaseLoc(applyNode);
- funcInfo->ReleaseLoc(funcNode);
- // Clear these nodes as they are going to be used to re-generate the slow path.
- VisitClearTmpRegs(applyNode, byteCodeGenerator, funcInfo);
- VisitClearTmpRegs(funcNode, byteCodeGenerator, funcInfo);
- byteCodeGenerator->Writer()->Br(afterSlowPath);
- // slow path
- byteCodeGenerator->Writer()->MarkLabel(slowPath);
- if (funcInfo->frameObjRegister != Js::Constants::NoRegister)
- {
- byteCodeGenerator->EmitScopeObjectInit(funcInfo);
- }
- byteCodeGenerator->LoadHeapArguments(funcInfo);
- byteCodeGenerator->Writer()->MarkLabel(argsAlreadyCreated);
- EmitCall(pnodeCall, byteCodeGenerator, funcInfo, fReturnValue, /*fEvaluateComponents*/true);
- byteCodeGenerator->Writer()->MarkLabel(afterSlowPath);
- }
- void EmitMethodElem(ParseNode *pnode, Js::RegSlot callObjLocation, Js::RegSlot indexLocation, ByteCodeGenerator *byteCodeGenerator)
- {
- // Load a call target of the form x[y]().
- byteCodeGenerator->Writer()->Element(Js::OpCode::LdMethodElem, pnode->location, callObjLocation, indexLocation);
- }
- void EmitCallTargetNoEvalComponents(
- ParseNode *pnodeTarget,
- BOOL fSideEffectArgs,
- Js::RegSlot *thisLocation,
- bool *releaseThisLocation,
- Js::RegSlot *callObjLocation,
- ByteCodeGenerator *byteCodeGenerator,
- FuncInfo *funcInfo)
- {
- // We first get a reference to the call target, then evaluate the arguments, then
- // evaluate the call target.
- // - emit reference to target
- // - copy instance to scratch reg if necessary.
- // - assign this
- // - assign instance for dynamic/global name
- // - emit args
- // - do call (CallFld/Elem/I)
- *releaseThisLocation = true;
- switch (pnodeTarget->nop)
- {
- case knopDot:
- *thisLocation = pnodeTarget->AsParseNodeBin()->pnode1->location;
- *callObjLocation = pnodeTarget->AsParseNodeBin()->pnode1->location;
- break;
- case knopIndex:
- *thisLocation = pnodeTarget->AsParseNodeBin()->pnode1->location;
- *callObjLocation = pnodeTarget->AsParseNodeBin()->pnode1->location;
- break;
- case knopName:
- // If the call target is a name, do some extra work to get its instance and the "this" pointer.
- byteCodeGenerator->EmitLoadInstance(pnodeTarget->AsParseNodeName()->sym, pnodeTarget->AsParseNodeName()->pid, thisLocation, callObjLocation, funcInfo);
- if (*thisLocation == Js::Constants::NoRegister)
- {
- *thisLocation = funcInfo->undefinedConstantRegister;
- }
- break;
- default:
- *thisLocation = funcInfo->undefinedConstantRegister;
- break;
- }
- }
- void EmitCallTarget(
- ParseNode *pnodeTarget,
- BOOL fSideEffectArgs,
- Js::RegSlot *thisLocation,
- bool *releaseThisLocation,
- Js::RegSlot *callObjLocation,
- ByteCodeGenerator *byteCodeGenerator,
- FuncInfo *funcInfo)
- {
- // - emit target
- // - assign this
- // - emit args
- // - do call
- // The call target is fully evaluated before the argument list. Note that we're not handling
- // put-call cases here currently, as such cases only apply to host objects
- // and are very unlikely to behave differently depending on the order of evaluation.
- *releaseThisLocation = true;
- switch (pnodeTarget->nop)
- {
- case knopDot:
- {
- ParseNodeBin * pnodeBinTarget = pnodeTarget->AsParseNodeBin();
- funcInfo->AcquireLoc(pnodeBinTarget);
- // Assign the call target operand(s), putting them into expression temps if necessary to protect
- // them from side-effects.
- if (fSideEffectArgs)
- {
- // Though we're done with target evaluation after this point, still protect opnd1 from
- // arg side-effects as it's the "this" pointer.
- SaveOpndValue(pnodeBinTarget->pnode1, funcInfo);
- }
- Assert(pnodeBinTarget->pnode2->nop == knopName);
- if ((pnodeBinTarget->pnode2->AsParseNodeName()->PropertyIdFromNameNode() == Js::PropertyIds::apply) || (pnodeTarget->AsParseNodeBin()->pnode2->AsParseNodeName()->PropertyIdFromNameNode() == Js::PropertyIds::call))
- {
- pnodeBinTarget->pnode1->SetIsCallApplyTargetLoad();
- }
- Emit(pnodeBinTarget->pnode1, byteCodeGenerator, funcInfo, false);
- Js::PropertyId propertyId = pnodeBinTarget->pnode2->AsParseNodeName()->PropertyIdFromNameNode();
- Js::RegSlot protoLocation = pnodeBinTarget->pnode1->location;
- if (ByteCodeGenerator::IsSuper(pnodeBinTarget->pnode1))
- {
- Emit(pnodeBinTarget->AsParseNodeSuperReference()->pnodeThis, byteCodeGenerator, funcInfo, false);
- protoLocation = byteCodeGenerator->EmitLdObjProto(Js::OpCode::LdHomeObjProto, protoLocation, funcInfo);
- funcInfo->ReleaseLoc(pnodeBinTarget->AsParseNodeSuperReference()->pnodeThis);
- funcInfo->ReleaseLoc(pnodeBinTarget->pnode1);
- // Function calls on the 'super' object should maintain current 'this' pointer.
- *thisLocation = pnodeBinTarget->AsParseNodeSuperReference()->pnodeThis->location;
- *releaseThisLocation = false;
- uint cacheId = funcInfo->FindOrAddInlineCacheId(protoLocation, propertyId, false, false);
- byteCodeGenerator->Writer()->PatchablePropertyWithThisPtr(Js::OpCode::LdSuperFld,
- pnodeTarget->location, protoLocation, *thisLocation, cacheId, false);
- }
- else
- {
- *thisLocation = pnodeBinTarget->pnode1->location;
- EmitMethodFld(pnodeBinTarget, protoLocation, propertyId, byteCodeGenerator, funcInfo);
- }
- break;
- }
- case knopIndex:
- {
- funcInfo->AcquireLoc(pnodeTarget);
- // Assign the call target operand(s), putting them into expression temps if necessary to protect
- // them from side-effects.
- if (fSideEffectArgs || !(ParseNode::Grfnop(pnodeTarget->AsParseNodeBin()->pnode2->nop) & fnopLeaf))
- {
- // Though we're done with target evaluation after this point, still protect opnd1 from
- // arg or opnd2 side-effects as it's the "this" pointer.
- SaveOpndValue(pnodeTarget->AsParseNodeBin()->pnode1, funcInfo);
- }
- Emit(pnodeTarget->AsParseNodeBin()->pnode1, byteCodeGenerator, funcInfo, false);
- Emit(pnodeTarget->AsParseNodeBin()->pnode2, byteCodeGenerator, funcInfo, false);
- Js::RegSlot indexLocation = pnodeTarget->AsParseNodeBin()->pnode2->location;
- Js::RegSlot protoLocation = pnodeTarget->AsParseNodeBin()->pnode1->location;
- if (ByteCodeGenerator::IsSuper(pnodeTarget->AsParseNodeBin()->pnode1))
- {
- Emit(pnodeTarget->AsParseNodeSuperReference()->pnodeThis, byteCodeGenerator, funcInfo, false);
- protoLocation = byteCodeGenerator->EmitLdObjProto(Js::OpCode::LdHomeObjProto, protoLocation, funcInfo);
- funcInfo->ReleaseLoc(pnodeTarget->AsParseNodeSuperReference()->pnodeThis);
- // Function calls on the 'super' object should maintain current 'this' pointer
- *thisLocation = pnodeTarget->AsParseNodeSuperReference()->pnodeThis->location;
- *releaseThisLocation = false;
- }
- else
- {
- *thisLocation = pnodeTarget->AsParseNodeBin()->pnode1->location;
- }
- EmitMethodElem(pnodeTarget, protoLocation, indexLocation, byteCodeGenerator);
- funcInfo->ReleaseLoc(pnodeTarget->AsParseNodeBin()->pnode2); // don't release indexLocation until after we use it.
- if (ByteCodeGenerator::IsSuper(pnodeTarget->AsParseNodeBin()->pnode1))
- {
- funcInfo->ReleaseLoc(pnodeTarget->AsParseNodeBin()->pnode1);
- }
- break;
- }
- case knopName:
- {
- ParseNodeName * pnodeNameTarget = pnodeTarget->AsParseNodeName();
- if (!pnodeNameTarget->IsSpecialName())
- {
- funcInfo->AcquireLoc(pnodeNameTarget);
- // Assign the call target operand(s), putting them into expression temps if necessary to protect
- // them from side-effects.
- if (fSideEffectArgs)
- {
- SaveOpndValue(pnodeNameTarget, funcInfo);
- }
- byteCodeGenerator->EmitLoadInstance(pnodeNameTarget->sym, pnodeNameTarget->pid, thisLocation, callObjLocation, funcInfo);
- if (*callObjLocation != Js::Constants::NoRegister)
- {
- // Load the call target as a property of the instance.
- Js::PropertyId propertyId = pnodeNameTarget->PropertyIdFromNameNode();
- EmitMethodFld(pnodeNameTarget, *callObjLocation, propertyId, byteCodeGenerator, funcInfo);
- break;
- }
- }
- // FALL THROUGH to evaluate call target.
- }
- default:
- // Assign the call target operand(s), putting them into expression temps if necessary to protect
- // them from side-effects.
- Emit(pnodeTarget, byteCodeGenerator, funcInfo, false);
- *thisLocation = funcInfo->undefinedConstantRegister;
- break;
- }
- // "This" pointer should have been assigned by the above.
- Assert(*thisLocation != Js::Constants::NoRegister);
- }
- void EmitCallI(
- ParseNodeCall *pnodeCall,
- BOOL fEvaluateComponents,
- BOOL fIsEval,
- BOOL fHasNewTarget,
- uint32 actualArgCount,
- ByteCodeGenerator *byteCodeGenerator,
- FuncInfo *funcInfo,
- Js::ProfileId callSiteId,
- Js::AuxArray<uint32> *spreadIndices = nullptr)
- {
- // Emit a call where the target is in a register, because it's either a local name or an expression we've
- // already evaluated.
- ParseNode *pnodeTarget = pnodeCall->pnodeTarget;
- Js::OpCode op;
- Js::CallFlags callFlags = Js::CallFlags::CallFlags_None;
- uint spreadExtraAlloc = 0;
- bool isSuperCall = pnodeCall->isSuperCall;
- Js::ArgSlot actualArgSlotCount = (Js::ArgSlot) actualArgCount;
- // check for integer overflow
- if ((size_t)actualArgSlotCount != actualArgCount)
- {
- Js::Throw::OutOfMemory();
- }
-
- if (fEvaluateComponents && !isSuperCall)
- {
- // Release the call target operands we assigned above. If we didn't assign them here,
- // we'll need them later, so we can't re-use them for the result of the call.
- funcInfo->ReleaseLoc(pnodeTarget);
- }
- // Grab a register for the call result.
- if (pnodeCall->isUsed)
- {
- funcInfo->AcquireLoc(pnodeCall);
- }
- if (fIsEval)
- {
- op = Js::OpCode::CallIExtendedFlags;
- callFlags = Js::CallFlags::CallFlags_ExtraArg;
- }
- else
- {
- if (isSuperCall)
- {
- callFlags = Js::CallFlags_New;
- }
- if (fHasNewTarget)
- {
- callFlags = (Js::CallFlags) (callFlags | Js::CallFlags::CallFlags_ExtraArg | Js::CallFlags::CallFlags_NewTarget);
- }
- if (pnodeCall->spreadArgCount > 0)
- {
- op = (isSuperCall || fHasNewTarget) ? Js::OpCode::CallIExtendedFlags : Js::OpCode::CallIExtended;
- }
- else
- {
- op = (isSuperCall || fHasNewTarget) ? Js::OpCode::CallIFlags : Js::OpCode::CallI;
- }
- }
- if (op == Js::OpCode::CallI || op == Js::OpCode::CallIFlags)
- {
- if (isSuperCall)
- {
- Js::RegSlot tmpReg = byteCodeGenerator->EmitLdObjProto(Js::OpCode::LdFuncObjProto, pnodeTarget->location, funcInfo);
- byteCodeGenerator->Writer()->CallI(op, pnodeCall->location, tmpReg, actualArgSlotCount, callSiteId, callFlags);
- }
- else
- {
- byteCodeGenerator->Writer()->CallI(op, pnodeCall->location, pnodeTarget->location, actualArgSlotCount, callSiteId, callFlags);
- }
- }
- else
- {
- uint spreadIndicesSize = 0;
- Js::CallIExtendedOptions options = Js::CallIExtended_None;
- if (pnodeCall->spreadArgCount > 0)
- {
- Assert(spreadIndices != nullptr);
- spreadExtraAlloc = UInt32Math::Mul(spreadIndices->count, sizeof(uint32));
- spreadIndicesSize = UInt32Math::Add(sizeof(*spreadIndices), spreadExtraAlloc);
- options = Js::CallIExtended_SpreadArgs;
- }
- if (isSuperCall)
- {
- Js::RegSlot tmpReg = byteCodeGenerator->EmitLdObjProto(Js::OpCode::LdFuncObjProto, pnodeTarget->location, funcInfo);
- byteCodeGenerator->Writer()->CallIExtended(op, pnodeCall->location, tmpReg, actualArgSlotCount, options, spreadIndices, spreadIndicesSize, callSiteId, callFlags);
- }
- else
- {
- byteCodeGenerator->Writer()->CallIExtended(op, pnodeCall->location, pnodeTarget->location, actualArgSlotCount, options, spreadIndices, spreadIndicesSize, callSiteId, callFlags);
- }
- }
- if (pnodeCall->spreadArgCount > 0)
- {
- Assert(spreadExtraAlloc != 0);
- AdeletePlus(byteCodeGenerator->GetAllocator(), spreadExtraAlloc, spreadIndices);
- }
- }
- void EmitCallInstrNoEvalComponents(
- ParseNodeCall *pnodeCall,
- BOOL fIsEval,
- Js::RegSlot thisLocation,
- Js::RegSlot callObjLocation,
- uint32 actualArgCount,
- ByteCodeGenerator *byteCodeGenerator,
- FuncInfo *funcInfo,
- Js::ProfileId callSiteId,
- Js::AuxArray<uint32> *spreadIndices = nullptr)
- {
- // Emit the call instruction. The call target is a reference at this point, and we evaluate
- // it as part of doing the actual call.
- // Note that we don't handle the (fEvaluateComponents == TRUE) case in this function.
- // (This function is only called on the !fEvaluateComponents branch in EmitCall.)
- ParseNode *pnodeTarget = pnodeCall->pnodeTarget;
- switch (pnodeTarget->nop)
- {
- case knopDot:
- {
- Assert(pnodeTarget->AsParseNodeBin()->pnode2->nop == knopName);
- Js::PropertyId propertyId = pnodeTarget->AsParseNodeBin()->pnode2->AsParseNodeName()->PropertyIdFromNameNode();
- EmitMethodFld(pnodeTarget, callObjLocation, propertyId, byteCodeGenerator, funcInfo);
- EmitCallI(pnodeCall, /*fEvaluateComponents*/ FALSE, fIsEval, /*fHasNewTarget*/ FALSE, actualArgCount, byteCodeGenerator, funcInfo, callSiteId, spreadIndices);
- }
- break;
- case knopIndex:
- {
- EmitMethodElem(pnodeTarget, pnodeTarget->AsParseNodeBin()->pnode1->location, pnodeTarget->AsParseNodeBin()->pnode2->location, byteCodeGenerator);
- EmitCallI(pnodeCall, /*fEvaluateComponents*/ FALSE, fIsEval, /*fHasNewTarget*/ FALSE, actualArgCount, byteCodeGenerator, funcInfo, callSiteId, spreadIndices);
- }
- break;
- case knopName:
- {
- if (callObjLocation != Js::Constants::NoRegister)
- {
- // We still have to get the property from its instance, so emit CallFld.
- if (thisLocation != callObjLocation)
- {
- funcInfo->ReleaseTmpRegister(thisLocation);
- }
- funcInfo->ReleaseTmpRegister(callObjLocation);
- Js::PropertyId propertyId = pnodeTarget->AsParseNodeName()->PropertyIdFromNameNode();
- EmitMethodFld(pnodeTarget, callObjLocation, propertyId, byteCodeGenerator, funcInfo);
- EmitCallI(pnodeCall, /*fEvaluateComponents*/ FALSE, fIsEval, /*fHasNewTarget*/ FALSE, actualArgCount, byteCodeGenerator, funcInfo, callSiteId, spreadIndices);
- break;
- }
- }
- // FALL THROUGH
- default:
- EmitCallI(pnodeCall, /*fEvaluateComponents*/ FALSE, fIsEval, /*fHasNewTarget*/ FALSE, actualArgCount, byteCodeGenerator, funcInfo, callSiteId, spreadIndices);
- break;
- }
- }
- void EmitCallInstr(
- ParseNodeCall *pnodeCall,
- BOOL fIsEval,
- BOOL fHasNewTarget,
- Js::RegSlot thisLocation,
- Js::RegSlot callObjLocation,
- uint32 actualArgCount,
- ByteCodeGenerator *byteCodeGenerator,
- FuncInfo *funcInfo,
- Js::ProfileId callSiteId,
- Js::AuxArray<uint32> *spreadIndices = nullptr)
- {
- // Emit a call instruction. The call target has been fully evaluated already, so we always
- // emit a CallI through the register that holds the target value.
- // Note that we don't handle !fEvaluateComponents cases at this point.
- // (This function is only called on the fEvaluateComponents branch in EmitCall.)
- if (thisLocation != Js::Constants::NoRegister)
- {
- funcInfo->ReleaseTmpRegister(thisLocation);
- }
- if (callObjLocation != Js::Constants::NoRegister &&
- callObjLocation != thisLocation)
- {
- funcInfo->ReleaseTmpRegister(callObjLocation);
- }
- EmitCallI(pnodeCall, /*fEvaluateComponents*/ TRUE, fIsEval, fHasNewTarget, actualArgCount, byteCodeGenerator, funcInfo, callSiteId, spreadIndices);
- }
- void EmitNew(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo)
- {
- Js::ArgSlot argCount = pnode->AsParseNodeCall()->argCount;
- argCount++; // include "this"
- BOOL fSideEffectArgs = FALSE;
- unsigned int tmpCount = CountArguments(pnode->AsParseNodeCall()->pnodeArgs, &fSideEffectArgs);
- AssertOrFailFastMsg(argCount == tmpCount, "argCount cannot overflow as max args capped at parser level");
- byteCodeGenerator->StartStatement(pnode);
- // Start call, allocate out param space
- funcInfo->StartRecordingOutArgs(argCount);
- // Assign the call target operand(s), putting them into expression temps if necessary to protect
- // them from side-effects.
- if (fSideEffectArgs)
- {
- SaveOpndValue(pnode->AsParseNodeCall()->pnodeTarget, funcInfo);
- }
- Emit(pnode->AsParseNodeCall()->pnodeTarget, byteCodeGenerator, funcInfo, false, true);
- if (pnode->AsParseNodeCall()->pnodeArgs == nullptr)
- {
- funcInfo->ReleaseLoc(pnode->AsParseNodeCall()->pnodeTarget);
- Js::OpCode op = (CreateNativeArrays(byteCodeGenerator, funcInfo)
- && CallTargetIsArray(pnode->AsParseNodeCall()->pnodeTarget))
- ? Js::OpCode::NewScObjArray : Js::OpCode::NewScObject;
- Assert(argCount == 1);
- Js::ProfileId callSiteId = byteCodeGenerator->GetNextCallSiteId(op);
- byteCodeGenerator->Writer()->StartCall(Js::OpCode::StartCall, argCount);
- byteCodeGenerator->Writer()->CallI(op, funcInfo->AcquireLoc(pnode),
- pnode->AsParseNodeCall()->pnodeTarget->location, argCount, callSiteId);
- }
- else
- {
- uint32 actualArgCount = 0;
- if (IsCallOfConstants(pnode))
- {
- byteCodeGenerator->Writer()->StartCall(Js::OpCode::StartCall, argCount);
- funcInfo->ReleaseLoc(pnode->AsParseNodeCall()->pnodeTarget);
- actualArgCount = EmitNewObjectOfConstants(pnode, byteCodeGenerator, funcInfo, argCount);
- }
- else
- {
- Js::OpCode op;
- if ((CreateNativeArrays(byteCodeGenerator, funcInfo) && CallTargetIsArray(pnode->AsParseNodeCall()->pnodeTarget)))
- {
- op = pnode->AsParseNodeCall()->spreadArgCount > 0 ? Js::OpCode::NewScObjArraySpread : Js::OpCode::NewScObjArray;
- }
- else
- {
- op = pnode->AsParseNodeCall()->spreadArgCount > 0 ? Js::OpCode::NewScObjectSpread : Js::OpCode::NewScObject;
- }
- Js::ProfileId callSiteId = byteCodeGenerator->GetNextCallSiteId(op);
- // Only emit profiled argouts if we're going to profile this call.
- bool emitProfiledArgouts = callSiteId != byteCodeGenerator->GetCurrentCallSiteId();
- Js::AuxArray<uint32> *spreadIndices = nullptr;
- actualArgCount = EmitArgList(pnode->AsParseNodeCall()->pnodeArgs, Js::Constants::NoRegister, Js::Constants::NoRegister,
- false, true, byteCodeGenerator, funcInfo, callSiteId, argCount, pnode->AsParseNodeCall()->hasDestructuring, emitProfiledArgouts, pnode->AsParseNodeCall()->spreadArgCount, &spreadIndices);
- funcInfo->ReleaseLoc(pnode->AsParseNodeCall()->pnodeTarget);
- if (pnode->AsParseNodeCall()->spreadArgCount > 0)
- {
- Assert(spreadIndices != nullptr);
- uint spreadExtraAlloc = UInt32Math::Mul(spreadIndices->count, sizeof(uint32));
- uint spreadIndicesSize = UInt32Math::Add(sizeof(*spreadIndices), spreadExtraAlloc);
- byteCodeGenerator->Writer()->CallIExtended(op, funcInfo->AcquireLoc(pnode), pnode->AsParseNodeCall()->pnodeTarget->location,
- (uint16)actualArgCount, Js::CallIExtended_SpreadArgs,
- spreadIndices, spreadIndicesSize, callSiteId);
- }
- else
- {
- byteCodeGenerator->Writer()->CallI(op, funcInfo->AcquireLoc(pnode), pnode->AsParseNodeCall()->pnodeTarget->location,
- (uint16)actualArgCount, callSiteId);
- }
- }
- Assert(argCount == actualArgCount);
- }
- // End call, pop param space
- funcInfo->EndRecordingOutArgs(argCount);
- return;
- }
- void EmitCall(
- ParseNodeCall * pnodeCall,
- ByteCodeGenerator* byteCodeGenerator,
- FuncInfo* funcInfo,
- BOOL fReturnValue,
- BOOL fEvaluateComponents,
- Js::RegSlot overrideThisLocation,
- Js::RegSlot newTargetLocation)
- {
- // If the call returns a float, we'll note this in the byte code.
- Js::RegSlot thisLocation = Js::Constants::NoRegister;
- Js::RegSlot callObjLocation = Js::Constants::NoRegister;
- BOOL fHasNewTarget = newTargetLocation != Js::Constants::NoRegister;
- BOOL fSideEffectArgs = FALSE;
- BOOL fIsSuperCall = pnodeCall->isSuperCall;
- ParseNode *pnodeTarget = pnodeCall->pnodeTarget;
- ParseNode *pnodeArgs = pnodeCall->pnodeArgs;
- uint16 spreadArgCount = pnodeCall->spreadArgCount;
- if (CreateNativeArrays(byteCodeGenerator, funcInfo) && CallTargetIsArray(pnodeTarget)) {
- // some minifiers (potentially incorrectly) assume that "v = new Array()" and "v = Array()" are equivalent,
- // and replace the former with the latter to save 4 characters. What that means for us is that it, at least
- // initially, uses the "Call" path. We want to guess that it _is_ just "new Array()" and change over to the
- // "new" path, since then our native array handling can kick in.
- /*EmitNew(pnode, byteCodeGenerator, funcInfo);
- return;*/
- }
- unsigned int argCount = CountArguments(pnodeArgs, &fSideEffectArgs);
- BOOL fIsEval = pnodeCall->isEvalCall;
- Js::ArgSlot argSlotCount = (Js::ArgSlot)argCount;
- if (fIsEval)
- {
- Assert(!fHasNewTarget);
- //
- // "eval" takes the closure environment as an extra argument
- // Pass the closure env only if some argument is passed
- // For just eval(), don't pass the closure environment
- //
- if (argCount > 1)
- {
- argCount++;
- }
- }
- else if (fHasNewTarget)
- {
- // When we need to pass new.target explicitly, it is passed as an extra argument.
- // This is similar to how eval passes an extra argument for the frame display and is
- // used to support cases where we need to pass both 'this' and new.target as part of
- // a function call.
- // OpCode::LdNewTarget knows how to look at the call flags and fetch this argument.
- argCount++;
- }
- // argCount indicates the total arguments count including the extra arguments.
- // argSlotCount indicates the actual arguments count. So argCount should always never be les sthan argSlotCount.
- if (argCount < (unsigned int)argSlotCount)
- {
- Js::Throw::OutOfMemory();
- }
- if (fReturnValue)
- {
- pnodeCall->isUsed = true;
- }
- //
- // Set up the call.
- //
- bool releaseThisLocation = true;
- // We already emit the call target for super calls in EmitSuperCall
- if (!fIsSuperCall)
- {
- if (!fEvaluateComponents)
- {
- EmitCallTargetNoEvalComponents(pnodeTarget, fSideEffectArgs, &thisLocation, &releaseThisLocation, &callObjLocation, byteCodeGenerator, funcInfo);
- }
- else
- {
- EmitCallTarget(pnodeTarget, fSideEffectArgs, &thisLocation, &releaseThisLocation, &callObjLocation, byteCodeGenerator, funcInfo);
- }
- }
- // If we are strictly overriding the this location, ignore what the call target set this location to.
- if (overrideThisLocation != Js::Constants::NoRegister)
- {
- thisLocation = overrideThisLocation;
- releaseThisLocation = false;
- }
- // Evaluate the arguments (nothing mode-specific here).
- // Start call, allocate out param space
- // We have to use the arguments count including the extra args to Start Call as we use it to allocated space for all the args
- funcInfo->StartRecordingOutArgs(argCount);
- Js::ProfileId callSiteId = byteCodeGenerator->GetNextCallSiteId(Js::OpCode::CallI);
- // Only emit profiled argouts if we're going to allocate callSiteInfo (on the DynamicProfileInfo) for this call.
- bool emitProfiledArgouts = callSiteId != byteCodeGenerator->GetCurrentCallSiteId();
- Js::AuxArray<uint32> *spreadIndices;
- EmitArgList(pnodeArgs, thisLocation, newTargetLocation, fIsEval, fEvaluateComponents, byteCodeGenerator, funcInfo, callSiteId, (Js::ArgSlot)argCount, pnodeCall->hasDestructuring, emitProfiledArgouts, spreadArgCount, &spreadIndices);
- if (!fEvaluateComponents)
- {
- EmitCallInstrNoEvalComponents(pnodeCall, fIsEval, thisLocation, callObjLocation, argSlotCount, byteCodeGenerator, funcInfo, callSiteId, spreadIndices);
- }
- else
- {
- EmitCallInstr(pnodeCall, fIsEval, fHasNewTarget, releaseThisLocation ? thisLocation : Js::Constants::NoRegister, callObjLocation, argSlotCount, byteCodeGenerator, funcInfo, callSiteId, spreadIndices);
- }
- // End call, pop param space
- funcInfo->EndRecordingOutArgs((Js::ArgSlot)argCount);
- }
- void EmitInvoke(
- Js::RegSlot location,
- Js::RegSlot callObjLocation,
- Js::PropertyId propertyId,
- ByteCodeGenerator* byteCodeGenerator,
- FuncInfo* funcInfo)
- {
- EmitMethodFld(false, false, location, callObjLocation, propertyId, byteCodeGenerator, funcInfo);
- funcInfo->StartRecordingOutArgs(1);
- Js::ProfileId callSiteId = byteCodeGenerator->GetNextCallSiteId(Js::OpCode::CallI);
- byteCodeGenerator->Writer()->StartCall(Js::OpCode::StartCall, 1);
- EmitArgListStart(callObjLocation, byteCodeGenerator, funcInfo, callSiteId);
- byteCodeGenerator->Writer()->CallI(Js::OpCode::CallI, location, location, 1, callSiteId);
- }
- void EmitInvoke(
- Js::RegSlot location,
- Js::RegSlot callObjLocation,
- Js::PropertyId propertyId,
- ByteCodeGenerator* byteCodeGenerator,
- FuncInfo* funcInfo,
- Js::RegSlot arg1Location)
- {
- EmitMethodFld(false, false, location, callObjLocation, propertyId, byteCodeGenerator, funcInfo);
- funcInfo->StartRecordingOutArgs(2);
- Js::ProfileId callSiteId = byteCodeGenerator->GetNextCallSiteId(Js::OpCode::CallI);
- byteCodeGenerator->Writer()->StartCall(Js::OpCode::StartCall, 2);
- EmitArgListStart(callObjLocation, byteCodeGenerator, funcInfo, callSiteId);
- byteCodeGenerator->Writer()->ArgOut<true>(1, arg1Location, callSiteId, false /*emitProfiledArgout*/);
- byteCodeGenerator->Writer()->CallI(Js::OpCode::CallI, location, location, 2, callSiteId);
- }
- void EmitComputedFunctionNameVar(ParseNode *nameNode, ParseNodeFnc *exprNode, ByteCodeGenerator *byteCodeGenerator)
- {
- AssertMsg(exprNode != nullptr, "callers of this function should pass in a valid expression Node");
- Assert(exprNode->HasComputedName());
- if (nameNode == nullptr)
- {
- return;
- }
- if (exprNode->pnodeName == nullptr)
- {
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::SetComputedNameVar, exprNode->location, nameNode->location);
- }
- }
- void EmitMemberNode(ParseNode *memberNode, Js::RegSlot objectLocation, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo, ParseNode* parentNode, bool useStore, bool* isObjectEmpty = nullptr)
- {
- ParseNode *nameNode = memberNode->AsParseNodeBin()->pnode1;
- ParseNode *exprNode = memberNode->AsParseNodeBin()->pnode2;
- bool isFncDecl = exprNode->nop == knopFncDecl;
- bool isClassMember = isFncDecl && exprNode->AsParseNodeFnc()->IsClassMember();
- if (isFncDecl)
- {
- Assert(exprNode->AsParseNodeFnc()->HasHomeObj());
- exprNode->AsParseNodeFnc()->SetHomeObjLocation(objectLocation);
- }
- // Moved SetComputedNameVar before LdFld of prototype because loading the prototype undefers the function TypeHandler
- // which makes this bytecode too late to influence the function.name.
- if (nameNode->nop == knopComputedName)
- {
- // Computed property name
- // Transparently pass the name expr
- // The Emit will replace this with a temp register if necessary to preserve the value.
- nameNode->location = nameNode->AsParseNodeUni()->pnode1->location;
- // Save the previous value of the flag to be restored later.
- bool prevFlag = byteCodeGenerator->forceStrictModeForClassComputedPropertyName;
- // Strict mode must be enforced on the evaluation of computed property names inside
- // classes, thus enable the flag if the computed property name is a class member.
- byteCodeGenerator->forceStrictModeForClassComputedPropertyName = isClassMember || prevFlag;
- EmitBinaryOpnds(nameNode, exprNode, byteCodeGenerator, funcInfo);
- // Restore the flag's previous value.
- byteCodeGenerator->forceStrictModeForClassComputedPropertyName = prevFlag;
- if (isFncDecl && !exprNode->AsParseNodeFnc()->IsClassConstructor())
- {
- EmitComputedFunctionNameVar(nameNode, exprNode->AsParseNodeFnc(), byteCodeGenerator);
- }
- }
- // Classes allocates a RegSlot as part of Instance Methods EmitClassInitializers,
- // but if we don't have any members then we don't need to load the prototype.
- Assert(isClassMember == (isObjectEmpty != nullptr));
- if (isClassMember && *isObjectEmpty)
- {
- *isObjectEmpty = false;
- int cacheId = funcInfo->FindOrAddInlineCacheId(parentNode->location, Js::PropertyIds::prototype, false, false);
- byteCodeGenerator->Writer()->PatchableProperty(Js::OpCode::LdFld, objectLocation, parentNode->location, cacheId);
- }
- if (nameNode->nop == knopComputedName)
- {
- AssertOrFailFast(memberNode->nop == knopGetMember || memberNode->nop == knopSetMember || memberNode->nop == knopMember);
- Js::OpCode setOp = memberNode->nop == knopGetMember ?
- (isClassMember ? Js::OpCode::InitClassMemberGetComputedName : Js::OpCode::InitGetElemI) :
- memberNode->nop == knopSetMember ?
- (isClassMember ? Js::OpCode::InitClassMemberSetComputedName : Js::OpCode::InitSetElemI) :
- (isClassMember ? Js::OpCode::InitClassMemberComputedName : Js::OpCode::InitComputedProperty);
- // Save the previous value of the flag to be restored later.
- bool prevFlag = byteCodeGenerator->forceStrictModeForClassComputedPropertyName;
- byteCodeGenerator->forceStrictModeForClassComputedPropertyName = isClassMember || prevFlag;
- // Strict mode must be enforced on the evaluation of computed property names inside
- // classes, thus enable the flag if the computed property name is a class member.
- byteCodeGenerator->Writer()->Element(setOp, exprNode->location, objectLocation, nameNode->location, true,
- byteCodeGenerator->forceStrictModeForClassComputedPropertyName);
-
- // Restore the flag's previous value.
- byteCodeGenerator->forceStrictModeForClassComputedPropertyName = prevFlag;
- funcInfo->ReleaseLoc(exprNode);
- funcInfo->ReleaseLoc(nameNode);
- return;
- }
- Js::OpCode stFldOpCode = (Js::OpCode)0;
- if (useStore)
- {
- stFldOpCode = ByteCodeGenerator::GetStFldOpCode(funcInfo, false, false, false, isClassMember);
- }
- Emit(exprNode, byteCodeGenerator, funcInfo, false);
- Js::PropertyId propertyId = nameNode->AsParseNodeStr()->pid->GetPropertyId();
- if (Js::PropertyIds::name == propertyId
- && exprNode->nop == knopFncDecl
- && exprNode->AsParseNodeFnc()->IsStaticMember()
- && parentNode != nullptr && parentNode->nop == knopClassDecl
- && parentNode->AsParseNodeClass()->pnodeConstructor != nullptr)
- {
- Js::ParseableFunctionInfo* nameFunc = parentNode->AsParseNodeClass()->pnodeConstructor->funcInfo->byteCodeFunction->GetParseableFunctionInfo();
- nameFunc->SetIsStaticNameFunction(true);
- }
- if (memberNode->nop == knopMember || memberNode->nop == knopMemberShort)
- {
- // The internal prototype should be set only if the production is of the form PropertyDefinition : PropertyName : AssignmentExpression
- if (propertyId == Js::PropertyIds::__proto__ && memberNode->nop != knopMemberShort && (exprNode->nop != knopFncDecl || !exprNode->AsParseNodeFnc()->IsMethod()))
- {
- byteCodeGenerator->Writer()->Property(Js::OpCode::InitProto, exprNode->location, objectLocation,
- funcInfo->FindOrAddReferencedPropertyId(propertyId));
- }
- else
- {
- uint cacheId = funcInfo->FindOrAddInlineCacheId(objectLocation, propertyId, false, true);
- Js::OpCode patchablePropertyOpCode;
- if (useStore)
- {
- patchablePropertyOpCode = stFldOpCode;
- }
- else if (isClassMember)
- {
- patchablePropertyOpCode = Js::OpCode::InitClassMember;
- }
- else
- {
- patchablePropertyOpCode = Js::OpCode::InitFld;
- }
- byteCodeGenerator->Writer()->PatchableProperty(patchablePropertyOpCode, exprNode->location, objectLocation, cacheId);
- }
- }
- else
- {
- AssertOrFailFast(memberNode->nop == knopGetMember || memberNode->nop == knopSetMember);
- Js::OpCode setOp = memberNode->nop == knopGetMember ?
- (isClassMember ? Js::OpCode::InitClassMemberGet : Js::OpCode::InitGetFld) :
- (isClassMember ? Js::OpCode::InitClassMemberSet : Js::OpCode::InitSetFld);
- byteCodeGenerator->Writer()->Property(setOp, exprNode->location, objectLocation, funcInfo->FindOrAddReferencedPropertyId(propertyId));
- }
- funcInfo->ReleaseLoc(exprNode);
- if (propertyId == Js::PropertyIds::valueOf)
- {
- byteCodeGenerator->GetScriptContext()->optimizationOverrides.SetSideEffects(Js::SideEffects_ValueOf);
- }
- else if (propertyId == Js::PropertyIds::toString)
- {
- byteCodeGenerator->GetScriptContext()->optimizationOverrides.SetSideEffects(Js::SideEffects_ToString);
- }
- }
- void EmitObjectSpreadNode(ParseNode *spreadNode, Js::RegSlot objectLocation, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo)
- {
- Js::RegSlot fromObjectLocation;
- ParseNode *exprNode = spreadNode->AsParseNodeUni()->pnode1;
- Emit(exprNode, byteCodeGenerator, funcInfo, false);
- fromObjectLocation = exprNode->location;
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::SpreadObjectLiteral, fromObjectLocation, objectLocation);
- funcInfo->ReleaseLoc(exprNode);
- }
- void EmitClassInitializers(ParseNode *memberList, Js::RegSlot objectLocation, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo, ParseNode* parentNode, bool isObjectEmpty)
- {
- if (memberList != nullptr)
- {
- while (memberList->nop == knopList)
- {
- ParseNode *memberNode = memberList->AsParseNodeBin()->pnode1;
- EmitMemberNode(memberNode, objectLocation, byteCodeGenerator, funcInfo, parentNode, /*useStore*/ false, &isObjectEmpty);
- memberList = memberList->AsParseNodeBin()->pnode2;
- }
- EmitMemberNode(memberList, objectLocation, byteCodeGenerator, funcInfo, parentNode, /*useStore*/ false, &isObjectEmpty);
- }
- }
- void EmitObjectInitializers(ParseNode *memberList, Js::RegSlot objectLocation, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo)
- {
- ParseNode *pmemberList = memberList;
- unsigned int argCount = 0;
- uint32 value;
- Js::PropertyId propertyId;
- //
- // 1. Add all non-int property ids to a dictionary propertyIds with value true
- // 2. Get the count of propertyIds
- // 3. Create a propertyId array of size count
- // 4. Put the propIds in the auxiliary area
- // 5. Get the objectLiteralCacheId
- // 6. Generate propId inits with values
- //
- // Handle propertyId collision
- typedef JsUtil::BaseHashSet<Js::PropertyId, ArenaAllocator, PowerOf2SizePolicy> PropertyIdSet;
- PropertyIdSet* propertyIds = Anew(byteCodeGenerator->GetAllocator(), PropertyIdSet, byteCodeGenerator->GetAllocator(), 17);
- bool hasComputedNameOrSpread = false;
- if (memberList != nullptr)
- {
- while (memberList->nop == knopList)
- {
- if (memberList->AsParseNodeBin()->pnode1->nop == knopEllipsis || memberList->AsParseNodeBin()->pnode1->AsParseNodeBin()->pnode1->nop == knopComputedName)
- {
- hasComputedNameOrSpread = true;
- break;
- }
- propertyId = memberList->AsParseNodeBin()->pnode1->AsParseNodeBin()->pnode1->AsParseNodeStr()->pid->GetPropertyId();
- if (!byteCodeGenerator->GetScriptContext()->IsNumericPropertyId(propertyId, &value))
- {
- propertyIds->Item(propertyId);
- }
- memberList = memberList->AsParseNodeBin()->pnode2;
- }
- if (memberList->nop != knopEllipsis && memberList->AsParseNodeBin()->pnode1->nop != knopComputedName && !hasComputedNameOrSpread)
- {
- propertyId = memberList->AsParseNodeBin()->pnode1->AsParseNodeStr()->pid->GetPropertyId();
- if (!byteCodeGenerator->GetScriptContext()->IsNumericPropertyId(propertyId, &value))
- {
- propertyIds->Item(propertyId);
- }
- }
- }
- argCount = propertyIds->Count();
- memberList = pmemberList;
- if ((memberList == nullptr) || (argCount == 0))
- {
- // Empty literal or numeric property only object literal
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::NewScObjectSimple, objectLocation);
- }
- else
- {
- uint32 allocSize = UInt32Math::Mul(argCount, sizeof(Js::PropertyId));
- Js::PropertyIdArray *propIds = AnewPlus(byteCodeGenerator->GetAllocator(), allocSize, Js::PropertyIdArray, argCount, 0);
- if (propertyIds->ContainsKey(Js::PropertyIds::__proto__))
- {
- // Always record whether the initializer contains __proto__ no matter if current environment has it enabled
- // or not, in case the bytecode is later run with __proto__ enabled.
- propIds->has__proto__ = true;
- }
- unsigned int argIndex = 0;
- while (memberList->nop == knopList)
- {
- if (memberList->AsParseNodeBin()->pnode1->nop == knopEllipsis || memberList->AsParseNodeBin()->pnode1->AsParseNodeBin()->pnode1->nop == knopComputedName)
- {
- break;
- }
- propertyId = memberList->AsParseNodeBin()->pnode1->AsParseNodeBin()->pnode1->AsParseNodeStr()->pid->GetPropertyId();
- if (!byteCodeGenerator->GetScriptContext()->IsNumericPropertyId(propertyId, &value) && propertyIds->Remove(propertyId))
- {
- propIds->elements[argIndex] = propertyId;
- argIndex++;
- }
- memberList = memberList->AsParseNodeBin()->pnode2;
- }
- if (memberList->nop != knopEllipsis && memberList->AsParseNodeBin()->pnode1->nop != knopComputedName && !hasComputedNameOrSpread)
- {
- propertyId = memberList->AsParseNodeBin()->pnode1->AsParseNodeStr()->pid->GetPropertyId();
- if (!byteCodeGenerator->GetScriptContext()->IsNumericPropertyId(propertyId, &value) && propertyIds->Remove(propertyId))
- {
- propIds->elements[argIndex] = propertyId;
- argIndex++;
- }
- }
- uint32 literalObjectId = funcInfo->GetParsedFunctionBody()->NewObjectLiteral();
- // Generate the opcode with propIds and cacheId
- byteCodeGenerator->Writer()->Auxiliary(Js::OpCode::NewScObjectLiteral, objectLocation, propIds, UInt32Math::Add(sizeof(Js::PropertyIdArray), allocSize), literalObjectId);
- Adelete(byteCodeGenerator->GetAllocator(), propertyIds);
- AdeletePlus(byteCodeGenerator->GetAllocator(), allocSize, propIds);
- }
- memberList = pmemberList;
- bool useStore = false;
- // Generate the actual assignment to those properties
- if (memberList != nullptr)
- {
- while (memberList->nop == knopList)
- {
- ParseNode *memberNode = memberList->AsParseNodeBin()->pnode1;
- if (memberNode->nop == knopEllipsis)
- {
- byteCodeGenerator->StartSubexpression(memberNode);
- EmitObjectSpreadNode(memberNode, objectLocation, byteCodeGenerator, funcInfo);
- byteCodeGenerator->EndSubexpression(memberNode);
- }
- else
- {
- if (memberNode->AsParseNodeBin()->pnode1->nop == knopComputedName)
- {
- useStore = true;
- }
- byteCodeGenerator->StartSubexpression(memberNode);
- EmitMemberNode(memberNode, objectLocation, byteCodeGenerator, funcInfo, nullptr, useStore);
- byteCodeGenerator->EndSubexpression(memberNode);
- }
- memberList = memberList->AsParseNodeBin()->pnode2;
- }
- byteCodeGenerator->StartSubexpression(memberList);
- if (memberList->nop == knopEllipsis)
- {
- EmitObjectSpreadNode(memberList, objectLocation, byteCodeGenerator, funcInfo);
- }
- else
- {
- EmitMemberNode(memberList, objectLocation, byteCodeGenerator, funcInfo, nullptr, useStore);
- }
- byteCodeGenerator->EndSubexpression(memberList);
- }
- }
- void EmitStringTemplate(ParseNodeStrTemplate *pnodeStrTemplate, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo)
- {
- Assert(pnodeStrTemplate->pnodeStringLiterals);
- // For a tagged string template, we will create the callsite constant object as part of the FunctionBody constants table.
- // We only need to emit code for non-tagged string templates here.
- if (!pnodeStrTemplate->isTaggedTemplate)
- {
- // If we have no substitutions and this is not a tagged template, we can emit just the single cooked string.
- if (pnodeStrTemplate->pnodeSubstitutionExpressions == nullptr)
- {
- Assert(pnodeStrTemplate->pnodeStringLiterals->nop != knopList);
- funcInfo->AcquireLoc(pnodeStrTemplate);
- Emit(pnodeStrTemplate->pnodeStringLiterals, byteCodeGenerator, funcInfo, false);
- Assert(pnodeStrTemplate->location != pnodeStrTemplate->pnodeStringLiterals->location);
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, pnodeStrTemplate->location, pnodeStrTemplate->pnodeStringLiterals->location);
- funcInfo->ReleaseLoc(pnodeStrTemplate->pnodeStringLiterals);
- }
- else
- {
- // If we have substitutions but no tag function, we can skip the callSite object construction (and also ignore raw strings).
- funcInfo->AcquireLoc(pnodeStrTemplate);
- // First string must be a list node since we have substitutions.
- AssertMsg(pnodeStrTemplate->pnodeStringLiterals->nop == knopList, "First string in the list must be a knopList node.");
- ParseNode* stringNodeList = pnodeStrTemplate->pnodeStringLiterals;
- // Emit the first string and load that into the pnode location.
- Emit(stringNodeList->AsParseNodeBin()->pnode1, byteCodeGenerator, funcInfo, false);
- Assert(pnodeStrTemplate->location != stringNodeList->AsParseNodeBin()->pnode1->location);
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, pnodeStrTemplate->location, stringNodeList->AsParseNodeBin()->pnode1->location);
- funcInfo->ReleaseLoc(stringNodeList->AsParseNodeBin()->pnode1);
- ParseNode* expressionNodeList = pnodeStrTemplate->pnodeSubstitutionExpressions;
- ParseNode* stringNode;
- ParseNode* expressionNode;
- // Now append the substitution expressions and remaining string constants via normal add operator
- // We will always have one more string constant than substitution expression
- // `strcon1 ${expr1} strcon2 ${expr2} strcon3` = strcon1 + expr1 + strcon2 + expr2 + strcon3
- //
- // strcon1 --- step 1 (above)
- // expr1 \__ step 2
- // strcon2 /
- // expr2 \__ step 3
- // strcon3 /
- while (stringNodeList->nop == knopList)
- {
- // If the current head of the expression list is a list, fetch the node and walk the list.
- if (expressionNodeList->nop == knopList)
- {
- expressionNode = expressionNodeList->AsParseNodeBin()->pnode1;
- expressionNodeList = expressionNodeList->AsParseNodeBin()->pnode2;
- }
- else
- {
- // This is the last element of the expression list.
- expressionNode = expressionNodeList;
- }
- // Emit the expression and append it to the string we're building.
- Emit(expressionNode, byteCodeGenerator, funcInfo, false);
- Js::RegSlot toStringLocation = funcInfo->AcquireTmpRegister();
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Conv_Str, toStringLocation, expressionNode->location);
- byteCodeGenerator->Writer()->Reg3(Js::OpCode::Add_A, pnodeStrTemplate->location, pnodeStrTemplate->location, toStringLocation);
- funcInfo->ReleaseTmpRegister(toStringLocation);
- funcInfo->ReleaseLoc(expressionNode);
- // Move to the next string in the list - we already got ahead of the expressions in the first string literal above.
- stringNodeList = stringNodeList->AsParseNodeBin()->pnode2;
- // If the current head of the string literal list is also a list node, need to fetch the actual string literal node.
- if (stringNodeList->nop == knopList)
- {
- stringNode = stringNodeList->AsParseNodeBin()->pnode1;
- }
- else
- {
- // This is the last element of the string literal list.
- stringNode = stringNodeList;
- }
- // Emit the string node following the previous expression and append it to the string.
- // This is either just some string in the list or it is the last string.
- Emit(stringNode, byteCodeGenerator, funcInfo, false);
- byteCodeGenerator->Writer()->Reg3(Js::OpCode::Add_A, pnodeStrTemplate->location, pnodeStrTemplate->location, stringNode->location);
- funcInfo->ReleaseLoc(stringNode);
- }
- }
- }
- }
- void SetNewArrayElements(ParseNode *pnode, Js::RegSlot arrayLocation, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo)
- {
- ParseNode *args = pnode->AsParseNodeUni()->pnode1;
- uint argCount = pnode->AsParseNodeArrLit()->count;
- uint spreadCount = pnode->AsParseNodeArrLit()->spreadCount;
- bool nativeArrays = CreateNativeArrays(byteCodeGenerator, funcInfo);
- bool arrayIntOpt = nativeArrays && pnode->AsParseNodeArrLit()->arrayOfInts;
- if (arrayIntOpt)
- {
- int extraAlloc = 0, auxSize = 0;
- if (Int32Math::Mul(argCount, sizeof(int32), &extraAlloc)
- || Int32Math::Add(sizeof(Js::AuxArray<int>), extraAlloc, &auxSize))
- {
- ::Math::DefaultOverflowPolicy();
- }
- Js::AuxArray<int> *ints = AnewPlus(byteCodeGenerator->GetAllocator(), extraAlloc, Js::AuxArray<int32>, argCount);
- EmitConstantArgsToIntArray(byteCodeGenerator, ints->elements, args, argCount);
- Assert(!pnode->AsParseNodeArrLit()->hasMissingValues);
- byteCodeGenerator->Writer()->Auxiliary(
- Js::OpCode::NewScIntArray,
- pnode->location,
- ints,
- auxSize,
- argCount);
- AdeletePlus(byteCodeGenerator->GetAllocator(), extraAlloc, ints);
- return;
- }
- bool arrayNumOpt = nativeArrays && pnode->AsParseNodeArrLit()->arrayOfNumbers;
- if (arrayNumOpt)
- {
- int extraAlloc = 0, auxSize = 0;
- if (Int32Math::Mul(argCount, sizeof(double), &extraAlloc)
- || Int32Math::Add(sizeof(Js::AuxArray<double>), extraAlloc, &auxSize))
- {
- ::Math::DefaultOverflowPolicy();
- }
- Js::AuxArray<double> *doubles = AnewPlus(byteCodeGenerator->GetAllocator(), extraAlloc, Js::AuxArray<double>, argCount);
- EmitConstantArgsToFltArray(byteCodeGenerator, doubles->elements, args, argCount);
- Assert(!pnode->AsParseNodeArrLit()->hasMissingValues);
- byteCodeGenerator->Writer()->Auxiliary(
- Js::OpCode::NewScFltArray,
- pnode->location,
- doubles,
- auxSize,
- argCount);
- AdeletePlus(byteCodeGenerator->GetAllocator(), extraAlloc, doubles);
- return;
- }
- bool arrayLitOpt = pnode->AsParseNodeArrLit()->arrayOfTaggedInts && pnode->AsParseNodeArrLit()->count > 1;
- Assert(!arrayLitOpt || !nativeArrays);
- Js::RegSlot spreadArrLoc = arrayLocation;
- Js::AuxArray<uint32> *spreadIndices = nullptr;
- const uint extraAlloc = UInt32Math::Mul(spreadCount, sizeof(uint32));
- if (pnode->AsParseNodeArrLit()->spreadCount > 0)
- {
- arrayLocation = funcInfo->AcquireTmpRegister();
- spreadIndices = AnewPlus(byteCodeGenerator->GetAllocator(), extraAlloc, Js::AuxArray<uint32>, spreadCount);
- }
- byteCodeGenerator->Writer()->Reg1Unsigned1(
- pnode->AsParseNodeArrLit()->hasMissingValues ? Js::OpCode::NewScArrayWithMissingValues : Js::OpCode::NewScArray,
- arrayLocation,
- argCount);
- if (args != nullptr)
- {
- Js::OpCode opcode;
- Js::RegSlot arrLoc;
- if (argCount == 1 && !byteCodeGenerator->Writer()->DoProfileNewScArrayOp(Js::OpCode::NewScArray))
- {
- opcode = Js::OpCode::StArrItemC_CI4;
- arrLoc = arrayLocation;
- }
- else if (arrayLitOpt)
- {
- opcode = Js::OpCode::StArrSegItem_A;
- arrLoc = funcInfo->AcquireTmpRegister();
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::LdArrHead, arrLoc, arrayLocation);
- }
- else if (Js::JavascriptArray::HasInlineHeadSegment(argCount))
- {
- // The head segment will be allocated inline as an interior pointer. To keep the array alive, the set operation
- // should be done relative to the array header to keep it alive (instead of the array segment).
- opcode = Js::OpCode::StArrInlineItem_CI4;
- arrLoc = arrayLocation;
- }
- else if (argCount <= Js::JavascriptArray::MaxInitialDenseLength)
- {
- opcode = Js::OpCode::StArrSegItem_CI4;
- arrLoc = funcInfo->AcquireTmpRegister();
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::LdArrHead, arrLoc, arrayLocation);
- }
- else
- {
- opcode = Js::OpCode::StArrItemI_CI4;
- arrLoc = arrayLocation;
- }
- if (arrayLitOpt)
- {
- uint32 allocSize = UInt32Math::Mul(argCount, sizeof(Js::Var));
- Js::VarArray *vars = AnewPlus(byteCodeGenerator->GetAllocator(), allocSize, Js::VarArray, argCount);
- EmitConstantArgsToVarArray(byteCodeGenerator, vars->elements, args, argCount);
- // Generate the opcode with vars
- byteCodeGenerator->Writer()->Auxiliary(Js::OpCode::StArrSegItem_A, arrLoc, vars, UInt32Math::Add(sizeof(Js::VarArray), allocSize), argCount);
- AdeletePlus(byteCodeGenerator->GetAllocator(), allocSize, vars);
- }
- else
- {
- uint i = 0;
- unsigned spreadIndex = 0;
- Js::RegSlot rhsLocation;
- while (args->nop == knopList)
- {
- if (args->AsParseNodeBin()->pnode1->nop != knopEmpty)
- {
- Emit(args->AsParseNodeBin()->pnode1, byteCodeGenerator, funcInfo, false);
- rhsLocation = args->AsParseNodeBin()->pnode1->location;
- Js::RegSlot regVal = rhsLocation;
- if (args->AsParseNodeBin()->pnode1->nop == knopEllipsis)
- {
- AnalysisAssert(spreadIndices);
- regVal = funcInfo->AcquireTmpRegister();
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::LdCustomSpreadIteratorList, regVal, rhsLocation);
- spreadIndices->elements[spreadIndex++] = i;
- }
- byteCodeGenerator->Writer()->ElementUnsigned1(opcode, regVal, arrLoc, i);
- if (args->AsParseNodeBin()->pnode1->nop == knopEllipsis)
- {
- funcInfo->ReleaseTmpRegister(regVal);
- }
- funcInfo->ReleaseLoc(args->AsParseNodeBin()->pnode1);
- }
- args = args->AsParseNodeBin()->pnode2;
- i++;
- }
- if (args->nop != knopEmpty)
- {
- Emit(args, byteCodeGenerator, funcInfo, false);
- rhsLocation = args->location;
- Js::RegSlot regVal = rhsLocation;
- if (args->nop == knopEllipsis)
- {
- regVal = funcInfo->AcquireTmpRegister();
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::LdCustomSpreadIteratorList, regVal, rhsLocation);
- AnalysisAssert(spreadIndices);
- spreadIndices->elements[spreadIndex] = i;
- }
- byteCodeGenerator->Writer()->ElementUnsigned1(opcode, regVal, arrLoc, i);
- if (args->nop == knopEllipsis)
- {
- funcInfo->ReleaseTmpRegister(regVal);
- }
- funcInfo->ReleaseLoc(args);
- i++;
- }
- Assert(i <= argCount);
- }
- if (arrLoc != arrayLocation)
- {
- funcInfo->ReleaseTmpRegister(arrLoc);
- }
- }
- if (pnode->AsParseNodeArrLit()->spreadCount > 0)
- {
- byteCodeGenerator->Writer()->Reg2Aux(Js::OpCode::SpreadArrayLiteral, spreadArrLoc, arrayLocation, spreadIndices, UInt32Math::Add(sizeof(Js::AuxArray<uint32>), extraAlloc), extraAlloc);
- AdeletePlus(byteCodeGenerator->GetAllocator(), extraAlloc, spreadIndices);
- funcInfo->ReleaseTmpRegister(arrayLocation);
- }
- }
- // FIX: TODO: mixed-mode expressions (arithmetic expressions mixed with boolean expressions); current solution
- // will not short-circuit in some cases and is not complete (for example: var i=(x==y))
- // This uses Aho and Ullman style double-branch generation (p. 494 ASU); we will need to peephole optimize or replace
- // with special case for single-branch style.
- void EmitBooleanExpression(
- _In_ ParseNode* expr,
- Js::ByteCodeLabel trueLabel,
- Js::ByteCodeLabel falseLabel,
- _In_ ByteCodeGenerator* byteCodeGenerator,
- _In_ FuncInfo* funcInfo,
- bool trueFallthrough,
- bool falseFallthrough)
- {
- Assert(!trueFallthrough || !falseFallthrough);
- byteCodeGenerator->StartStatement(expr);
- switch (expr->nop)
- {
- case knopLogOr:
- {
- Js::ByteCodeLabel leftFalse = byteCodeGenerator->Writer()->DefineLabel();
- EmitBooleanExpression(expr->AsParseNodeBin()->pnode1, trueLabel, leftFalse, byteCodeGenerator, funcInfo, false, true);
- funcInfo->ReleaseLoc(expr->AsParseNodeBin()->pnode1);
- byteCodeGenerator->Writer()->MarkLabel(leftFalse);
- EmitBooleanExpression(expr->AsParseNodeBin()->pnode2, trueLabel, falseLabel, byteCodeGenerator, funcInfo, trueFallthrough, falseFallthrough);
- funcInfo->ReleaseLoc(expr->AsParseNodeBin()->pnode2);
- break;
- }
- case knopLogAnd:
- {
- Js::ByteCodeLabel leftTrue = byteCodeGenerator->Writer()->DefineLabel();
- EmitBooleanExpression(expr->AsParseNodeBin()->pnode1, leftTrue, falseLabel, byteCodeGenerator, funcInfo, true, false);
- funcInfo->ReleaseLoc(expr->AsParseNodeBin()->pnode1);
- byteCodeGenerator->Writer()->MarkLabel(leftTrue);
- EmitBooleanExpression(expr->AsParseNodeBin()->pnode2, trueLabel, falseLabel, byteCodeGenerator, funcInfo, trueFallthrough, falseFallthrough);
- funcInfo->ReleaseLoc(expr->AsParseNodeBin()->pnode2);
- break;
- }
- case knopLogNot:
- EmitBooleanExpression(expr->AsParseNodeUni()->pnode1, falseLabel, trueLabel, byteCodeGenerator, funcInfo, falseFallthrough, trueFallthrough);
- funcInfo->ReleaseLoc(expr->AsParseNodeUni()->pnode1);
- break;
- case knopEq:
- case knopEqv:
- case knopNEqv:
- case knopNe:
- case knopLt:
- case knopLe:
- case knopGe:
- case knopGt:
- EmitBinaryOpnds(expr->AsParseNodeBin()->pnode1, expr->AsParseNodeBin()->pnode2, byteCodeGenerator, funcInfo);
- funcInfo->ReleaseLoc(expr->AsParseNodeBin()->pnode2);
- funcInfo->ReleaseLoc(expr->AsParseNodeBin()->pnode1);
- byteCodeGenerator->Writer()->BrReg2(nopToOp[expr->nop], trueLabel, expr->AsParseNodeBin()->pnode1->location,
- expr->AsParseNodeBin()->pnode2->location);
- if (!falseFallthrough)
- {
- byteCodeGenerator->Writer()->Br(falseLabel);
- }
- break;
- case knopTrue:
- if (!trueFallthrough)
- {
- byteCodeGenerator->Writer()->Br(trueLabel);
- }
- break;
- case knopFalse:
- if (!falseFallthrough)
- {
- byteCodeGenerator->Writer()->Br(falseLabel);
- }
- break;
- default:
- // Note: we usually release the temp assigned to a node after we Emit it.
- // But in this case, EmitBooleanExpression is just a wrapper around a normal Emit call,
- // and the caller of EmitBooleanExpression expects to be able to release this register.
- Emit(expr, byteCodeGenerator, funcInfo, false);
- if (trueFallthrough)
- {
- byteCodeGenerator->Writer()->BrReg1(Js::OpCode::BrFalse_A, falseLabel, expr->location);
- }
- else
- {
- byteCodeGenerator->Writer()->BrReg1(Js::OpCode::BrTrue_A, trueLabel, expr->location);
- if (!falseFallthrough)
- {
- byteCodeGenerator->Writer()->Br(falseLabel);
- }
- }
- break;
- }
- byteCodeGenerator->EndStatement(expr);
- }
- void EmitGeneratingBooleanExpression(ParseNode *expr, Js::ByteCodeLabel trueLabel, bool truefallthrough, Js::ByteCodeLabel falseLabel, bool falsefallthrough, Js::RegSlot writeto,
- ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo)
- {
- switch (expr->nop)
- {
- case knopLogOr:
- {
- byteCodeGenerator->StartStatement(expr);
- Js::ByteCodeLabel leftFalse = byteCodeGenerator->Writer()->DefineLabel();
- EmitGeneratingBooleanExpression(expr->AsParseNodeBin()->pnode1, trueLabel, false, leftFalse, true, writeto, byteCodeGenerator, funcInfo);
- funcInfo->ReleaseLoc(expr->AsParseNodeBin()->pnode1);
- byteCodeGenerator->Writer()->MarkLabel(leftFalse);
- EmitGeneratingBooleanExpression(expr->AsParseNodeBin()->pnode2, trueLabel, truefallthrough, falseLabel, falsefallthrough, writeto, byteCodeGenerator, funcInfo);
- funcInfo->ReleaseLoc(expr->AsParseNodeBin()->pnode2);
- byteCodeGenerator->EndStatement(expr);
- break;
- }
- case knopLogAnd:
- {
- byteCodeGenerator->StartStatement(expr);
- Js::ByteCodeLabel leftTrue = byteCodeGenerator->Writer()->DefineLabel();
- EmitGeneratingBooleanExpression(expr->AsParseNodeBin()->pnode1, leftTrue, true, falseLabel, false, writeto, byteCodeGenerator, funcInfo);
- funcInfo->ReleaseLoc(expr->AsParseNodeBin()->pnode1);
- byteCodeGenerator->Writer()->MarkLabel(leftTrue);
- EmitGeneratingBooleanExpression(expr->AsParseNodeBin()->pnode2, trueLabel, truefallthrough, falseLabel, falsefallthrough, writeto, byteCodeGenerator, funcInfo);
- funcInfo->ReleaseLoc(expr->AsParseNodeBin()->pnode2);
- byteCodeGenerator->EndStatement(expr);
- break;
- }
- case knopLogNot:
- {
- byteCodeGenerator->StartStatement(expr);
- // this time we want a boolean expression, since Logical Not is nice and only returns true or false
- Js::ByteCodeLabel emitTrue = byteCodeGenerator->Writer()->DefineLabel();
- Js::ByteCodeLabel emitFalse = byteCodeGenerator->Writer()->DefineLabel();
- EmitBooleanExpression(expr->AsParseNodeUni()->pnode1, emitFalse, emitTrue, byteCodeGenerator, funcInfo, false, true);
- byteCodeGenerator->Writer()->MarkLabel(emitTrue);
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdTrue, writeto);
- byteCodeGenerator->Writer()->Br(trueLabel);
- byteCodeGenerator->Writer()->MarkLabel(emitFalse);
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, writeto);
- if (!falsefallthrough)
- {
- byteCodeGenerator->Writer()->Br(falseLabel);
- }
- funcInfo->ReleaseLoc(expr->AsParseNodeUni()->pnode1);
- byteCodeGenerator->EndStatement(expr);
- break;
- }
- case knopEq:
- case knopEqv:
- case knopNEqv:
- case knopNe:
- case knopLt:
- case knopLe:
- case knopGe:
- case knopGt:
- byteCodeGenerator->StartStatement(expr);
- EmitBinaryOpnds(expr->AsParseNodeBin()->pnode1, expr->AsParseNodeBin()->pnode2, byteCodeGenerator, funcInfo);
- funcInfo->ReleaseLoc(expr->AsParseNodeBin()->pnode2);
- funcInfo->ReleaseLoc(expr->AsParseNodeBin()->pnode1);
- funcInfo->AcquireLoc(expr);
- byteCodeGenerator->Writer()->Reg3(nopToCMOp[expr->nop], expr->location, expr->AsParseNodeBin()->pnode1->location,
- expr->AsParseNodeBin()->pnode2->location);
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, writeto, expr->location);
- // The inliner likes small bytecode
- if (!(truefallthrough || falsefallthrough))
- {
- byteCodeGenerator->Writer()->BrReg1(Js::OpCode::BrTrue_A, trueLabel, expr->location);
- byteCodeGenerator->Writer()->Br(falseLabel);
- }
- else if (truefallthrough && !falsefallthrough) {
- byteCodeGenerator->Writer()->BrReg1(Js::OpCode::BrFalse_A, falseLabel, expr->location);
- }
- else if (falsefallthrough && !truefallthrough) {
- byteCodeGenerator->Writer()->BrReg1(Js::OpCode::BrTrue_A, trueLabel, expr->location);
- }
- byteCodeGenerator->EndStatement(expr);
- break;
- case knopTrue:
- byteCodeGenerator->StartStatement(expr);
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdTrue, writeto);
- if (!truefallthrough)
- {
- byteCodeGenerator->Writer()->Br(trueLabel);
- }
- byteCodeGenerator->EndStatement(expr);
- break;
- case knopFalse:
- byteCodeGenerator->StartStatement(expr);
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, writeto);
- if (!falsefallthrough)
- {
- byteCodeGenerator->Writer()->Br(falseLabel);
- }
- byteCodeGenerator->EndStatement(expr);
- break;
- default:
- // Note: we usually release the temp assigned to a node after we Emit it.
- // But in this case, EmitBooleanExpression is just a wrapper around a normal Emit call,
- // and the caller of EmitBooleanExpression expects to be able to release this register.
- // For diagnostics purposes, register the name and dot to the statement list.
- if (expr->nop == knopName || expr->nop == knopDot)
- {
- byteCodeGenerator->StartStatement(expr);
- Emit(expr, byteCodeGenerator, funcInfo, false);
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, writeto, expr->location);
- // The inliner likes small bytecode
- if (!(truefallthrough || falsefallthrough))
- {
- byteCodeGenerator->Writer()->BrReg1(Js::OpCode::BrTrue_A, trueLabel, expr->location);
- byteCodeGenerator->Writer()->Br(falseLabel);
- }
- else if (truefallthrough && !falsefallthrough) {
- byteCodeGenerator->Writer()->BrReg1(Js::OpCode::BrFalse_A, falseLabel, expr->location);
- }
- else if (falsefallthrough && !truefallthrough) {
- byteCodeGenerator->Writer()->BrReg1(Js::OpCode::BrTrue_A, trueLabel, expr->location);
- }
- byteCodeGenerator->EndStatement(expr);
- }
- else
- {
- Emit(expr, byteCodeGenerator, funcInfo, false);
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, writeto, expr->location);
- // The inliner likes small bytecode
- if (!(truefallthrough || falsefallthrough))
- {
- byteCodeGenerator->Writer()->BrReg1(Js::OpCode::BrTrue_A, trueLabel, expr->location);
- byteCodeGenerator->Writer()->Br(falseLabel);
- }
- else if (truefallthrough && !falsefallthrough) {
- byteCodeGenerator->Writer()->BrReg1(Js::OpCode::BrFalse_A, falseLabel, expr->location);
- }
- else if (falsefallthrough && !truefallthrough) {
- byteCodeGenerator->Writer()->BrReg1(Js::OpCode::BrTrue_A, trueLabel, expr->location);
- }
- }
- break;
- }
- }
- // used by while and for loops
- void EmitLoop(
- ParseNodeLoop *loopNode,
- ParseNode *cond,
- ParseNode *body,
- ParseNode *incr,
- ByteCodeGenerator *byteCodeGenerator,
- FuncInfo *funcInfo,
- BOOL fReturnValue,
- BOOL doWhile = FALSE,
- ParseNodeBlock *forLoopBlock = nullptr)
- {
- // Need to increment loop count whether we are going to profile or not for HasLoop()
- Js::ByteCodeLabel loopEntrance = byteCodeGenerator->Writer()->DefineLabel();
- Js::ByteCodeLabel continuePastLoop = byteCodeGenerator->Writer()->DefineLabel();
- uint loopId = byteCodeGenerator->Writer()->EnterLoop(loopEntrance);
- loopNode->loopId = loopId;
- if (doWhile)
- {
- Emit(body, byteCodeGenerator, funcInfo, fReturnValue);
- funcInfo->ReleaseLoc(body);
- if (loopNode->emitLabels)
- {
- byteCodeGenerator->Writer()->MarkLabel(loopNode->continueLabel);
- }
- if (!ByteCodeGenerator::IsFalse(cond) ||
- byteCodeGenerator->IsInDebugMode())
- {
- EmitBooleanExpression(cond, loopEntrance, continuePastLoop, byteCodeGenerator, funcInfo, false, false);
- }
- funcInfo->ReleaseLoc(cond);
- }
- else
- {
- if (cond)
- {
- if (!(cond->nop == knopInt &&
- cond->AsParseNodeInt()->lw != 0))
- {
- Js::ByteCodeLabel trueLabel = byteCodeGenerator->Writer()->DefineLabel();
- EmitBooleanExpression(cond, trueLabel, continuePastLoop, byteCodeGenerator, funcInfo, true, false);
- byteCodeGenerator->Writer()->MarkLabel(trueLabel);
- }
- funcInfo->ReleaseLoc(cond);
- }
- Emit(body, byteCodeGenerator, funcInfo, fReturnValue);
- funcInfo->ReleaseLoc(body);
- if (byteCodeGenerator->IsES6ForLoopSemanticsEnabled() &&
- forLoopBlock != nullptr)
- {
- CloneEmitBlock(forLoopBlock, byteCodeGenerator, funcInfo);
- }
- if (loopNode->emitLabels)
- {
- byteCodeGenerator->Writer()->MarkLabel(loopNode->continueLabel);
- }
- if (incr != nullptr)
- {
- Emit(incr, byteCodeGenerator, funcInfo, false);
- funcInfo->ReleaseLoc(incr);
- }
- byteCodeGenerator->Writer()->Br(loopEntrance);
- }
- byteCodeGenerator->Writer()->MarkLabel(continuePastLoop);
- if (loopNode->emitLabels)
- {
- byteCodeGenerator->Writer()->MarkLabel(loopNode->breakLabel);
- }
- byteCodeGenerator->Writer()->ExitLoop(loopId);
- }
- void ByteCodeGenerator::EmitInvertedLoop(ParseNodeLoop* outerLoop, ParseNodeFor* invertedLoop, FuncInfo* funcInfo)
- {
- Js::ByteCodeLabel invertedLoopLabel = this->m_writer.DefineLabel();
- Js::ByteCodeLabel afterInvertedLoop = this->m_writer.DefineLabel();
- // emit branch around original
- Emit(outerLoop->AsParseNodeFor()->pnodeInit, this, funcInfo, false);
- funcInfo->ReleaseLoc(outerLoop->AsParseNodeFor()->pnodeInit);
- this->m_writer.BrS(Js::OpCode::BrNotHasSideEffects, invertedLoopLabel, Js::SideEffects_Any);
- // emit original
- EmitLoop(outerLoop, outerLoop->AsParseNodeFor()->pnodeCond, outerLoop->AsParseNodeFor()->pnodeBody,
- outerLoop->AsParseNodeFor()->pnodeIncr, this, funcInfo, false);
- // clear temporary registers since inverted loop may share nodes with
- // emitted original loop
- VisitClearTmpRegs(outerLoop, this, funcInfo);
- // emit branch around inverted
- this->m_writer.Br(afterInvertedLoop);
- this->m_writer.MarkLabel(invertedLoopLabel);
- // Emit a zero trip test for the original outer-loop if the outer-loop
- // has a condition
- if (outerLoop->AsParseNodeFor()->pnodeCond)
- {
- Js::ByteCodeLabel zeroTrip = this->m_writer.DefineLabel();
- ParseNode* testNode = this->GetParser()->CopyPnode(outerLoop->AsParseNodeFor()->pnodeCond);
- EmitBooleanExpression(testNode, zeroTrip, afterInvertedLoop, this, funcInfo, true, false);
- this->m_writer.MarkLabel(zeroTrip);
- funcInfo->ReleaseLoc(testNode);
- }
- // emit inverted
- Emit(invertedLoop->pnodeInit, this, funcInfo, false);
- funcInfo->ReleaseLoc(invertedLoop->pnodeInit);
- EmitLoop(invertedLoop, invertedLoop->pnodeCond, invertedLoop->pnodeBody,
- invertedLoop->pnodeIncr, this, funcInfo, false);
- this->m_writer.MarkLabel(afterInvertedLoop);
- }
- void EmitGetIterator(Js::RegSlot iteratorLocation, Js::RegSlot iterableLocation, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo)
- {
- // get iterator object from the iterable
- EmitInvoke(iteratorLocation, iterableLocation, Js::PropertyIds::_symbolIterator, byteCodeGenerator, funcInfo);
- // throw TypeError if the result is not an object
- Js::ByteCodeLabel skipThrow = byteCodeGenerator->Writer()->DefineLabel();
- byteCodeGenerator->Writer()->BrReg1(Js::OpCode::BrOnObject_A, skipThrow, iteratorLocation);
- byteCodeGenerator->Writer()->W1(Js::OpCode::RuntimeTypeError, SCODE_CODE(JSERR_NeedObject));
- byteCodeGenerator->Writer()->MarkLabel(skipThrow);
- }
- void EmitIteratorNext(Js::RegSlot itemLocation, Js::RegSlot iteratorLocation, Js::RegSlot nextInputLocation, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo)
- {
- // invoke next() on the iterator
- if (nextInputLocation == Js::Constants::NoRegister)
- {
- EmitInvoke(itemLocation, iteratorLocation, Js::PropertyIds::next, byteCodeGenerator, funcInfo);
- }
- else
- {
- EmitInvoke(itemLocation, iteratorLocation, Js::PropertyIds::next, byteCodeGenerator, funcInfo, nextInputLocation);
- }
- // throw TypeError if the result is not an object
- Js::ByteCodeLabel skipThrow = byteCodeGenerator->Writer()->DefineLabel();
- byteCodeGenerator->Writer()->BrReg1(Js::OpCode::BrOnObject_A, skipThrow, itemLocation);
- byteCodeGenerator->Writer()->W1(Js::OpCode::RuntimeTypeError, SCODE_CODE(JSERR_NeedObject));
- byteCodeGenerator->Writer()->MarkLabel(skipThrow);
- }
- // Generating
- // if (hasReturnFunction) {
- // value = Call Retrun;
- // if (value != Object)
- // throw TypeError;
- // }
- void EmitIteratorClose(Js::RegSlot iteratorLocation, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo)
- {
- Js::RegSlot returnLocation = funcInfo->AcquireTmpRegister();
- Js::ByteCodeLabel skipThrow = byteCodeGenerator->Writer()->DefineLabel();
- Js::ByteCodeLabel noReturn = byteCodeGenerator->Writer()->DefineLabel();
- uint cacheId = funcInfo->FindOrAddInlineCacheId(iteratorLocation, Js::PropertyIds::return_, false, false);
- byteCodeGenerator->Writer()->PatchableProperty(Js::OpCode::LdFld, returnLocation, iteratorLocation, cacheId);
- byteCodeGenerator->Writer()->BrReg2(Js::OpCode::BrEq_A, noReturn, returnLocation, funcInfo->undefinedConstantRegister);
- EmitInvoke(returnLocation, iteratorLocation, Js::PropertyIds::return_, byteCodeGenerator, funcInfo);
- // throw TypeError if the result is not an Object
- byteCodeGenerator->Writer()->BrReg1(Js::OpCode::BrOnObject_A, skipThrow, returnLocation);
- byteCodeGenerator->Writer()->W1(Js::OpCode::RuntimeTypeError, SCODE_CODE(JSERR_NeedObject));
- byteCodeGenerator->Writer()->MarkLabel(skipThrow);
- byteCodeGenerator->Writer()->MarkLabel(noReturn);
- funcInfo->ReleaseTmpRegister(returnLocation);
- }
- void EmitIteratorComplete(Js::RegSlot doneLocation, Js::RegSlot iteratorResultLocation, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo)
- {
- // get the iterator result's "done" property
- uint cacheId = funcInfo->FindOrAddInlineCacheId(iteratorResultLocation, Js::PropertyIds::done, false, false);
- byteCodeGenerator->Writer()->PatchableProperty(Js::OpCode::LdFld, doneLocation, iteratorResultLocation, cacheId);
- // Do not need to do ToBoolean explicitly with current uses of EmitIteratorComplete since BrTrue_A does this.
- // Add a ToBoolean controlled by template flag if needed for new uses later on.
- }
- void EmitIteratorValue(Js::RegSlot valueLocation, Js::RegSlot iteratorResultLocation, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo)
- {
- // get the iterator result's "value" property
- uint cacheId = funcInfo->FindOrAddInlineCacheId(iteratorResultLocation, Js::PropertyIds::value, false, false);
- byteCodeGenerator->Writer()->PatchableProperty(Js::OpCode::LdFld, valueLocation, iteratorResultLocation, cacheId);
- }
- void EmitForInOfLoopBody(ParseNodeForInOrForOf *loopNode,
- Js::ByteCodeLabel loopEntrance,
- Js::ByteCodeLabel continuePastLoop,
- ByteCodeGenerator *byteCodeGenerator,
- FuncInfo *funcInfo,
- BOOL fReturnValue)
- {
- if (loopNode->pnodeLval->nop != knopVarDecl &&
- loopNode->pnodeLval->nop != knopLetDecl &&
- loopNode->pnodeLval->nop != knopConstDecl)
- {
- EmitReference(loopNode->pnodeLval, byteCodeGenerator, funcInfo);
- }
- else
- {
- Symbol * sym = loopNode->pnodeLval->AsParseNodeVar()->sym;
- sym->SetNeedDeclaration(false);
- }
- if (byteCodeGenerator->IsES6ForLoopSemanticsEnabled())
- {
- BeginEmitBlock(loopNode->pnodeBlock, byteCodeGenerator, funcInfo);
- }
- EmitAssignment(nullptr, loopNode->pnodeLval, loopNode->itemLocation, byteCodeGenerator, funcInfo);
- // The StartStatement is already done in the caller of this function.
- byteCodeGenerator->EndStatement(loopNode->pnodeLval);
- funcInfo->ReleaseReference(loopNode->pnodeLval);
- Emit(loopNode->pnodeBody, byteCodeGenerator, funcInfo, fReturnValue);
- funcInfo->ReleaseLoc(loopNode->pnodeBody);
- if (byteCodeGenerator->IsES6ForLoopSemanticsEnabled())
- {
- EndEmitBlock(loopNode->pnodeBlock, byteCodeGenerator, funcInfo);
- }
- funcInfo->ReleaseTmpRegister(loopNode->itemLocation);
- if (loopNode->emitLabels)
- {
- byteCodeGenerator->Writer()->MarkLabel(loopNode->continueLabel);
- }
- byteCodeGenerator->Writer()->Br(loopEntrance);
- byteCodeGenerator->Writer()->MarkLabel(continuePastLoop);
- if (loopNode->emitLabels)
- {
- byteCodeGenerator->Writer()->MarkLabel(loopNode->breakLabel);
- }
- }
- void EmitForIn(ParseNodeForInOrForOf *loopNode,
- Js::ByteCodeLabel loopEntrance,
- Js::ByteCodeLabel continuePastLoop,
- ByteCodeGenerator *byteCodeGenerator,
- FuncInfo *funcInfo,
- BOOL fReturnValue)
- {
- Assert(loopNode->nop == knopForIn);
- Assert(loopNode->location == Js::Constants::NoRegister);
- // Grab registers for the enumerator and for the current enumerated item.
- // The enumerator register will be released after this call returns.
- loopNode->itemLocation = funcInfo->AcquireTmpRegister();
- uint forInLoopLevel = funcInfo->AcquireForInLoopLevel();
- // get enumerator from the collection
- byteCodeGenerator->Writer()->Reg1Unsigned1(Js::OpCode::InitForInEnumerator, loopNode->pnodeObj->location, forInLoopLevel);
- // The StartStatement is already done in the caller of the current function, which is EmitForInOrForOf
- byteCodeGenerator->EndStatement(loopNode);
- // Need to increment loop count whether we are going into profile or not for HasLoop()
- uint loopId = byteCodeGenerator->Writer()->EnterLoop(loopEntrance);
- loopNode->loopId = loopId;
- // The EndStatement will happen in the EmitForInOfLoopBody function
- byteCodeGenerator->StartStatement(loopNode->pnodeLval);
- // branch past loop when MoveAndGetNext returns nullptr
- byteCodeGenerator->Writer()->BrReg1Unsigned1(Js::OpCode::BrOnEmpty, continuePastLoop, loopNode->itemLocation, forInLoopLevel);
- EmitForInOfLoopBody(loopNode, loopEntrance, continuePastLoop, byteCodeGenerator, funcInfo, fReturnValue);
- byteCodeGenerator->Writer()->ExitLoop(loopId);
- funcInfo->ReleaseForInLoopLevel(forInLoopLevel);
- if (!byteCodeGenerator->IsES6ForLoopSemanticsEnabled())
- {
- EndEmitBlock(loopNode->pnodeBlock, byteCodeGenerator, funcInfo);
- }
- }
- void EmitForInOrForOf(ParseNodeForInOrForOf *loopNode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo, BOOL fReturnValue)
- {
- bool isForIn = (loopNode->nop == knopForIn);
- Assert(isForIn || loopNode->nop == knopForOf);
- BeginEmitBlock(loopNode->pnodeBlock, byteCodeGenerator, funcInfo);
- byteCodeGenerator->StartStatement(loopNode);
- if (!isForIn)
- {
- funcInfo->AcquireLoc(loopNode);
- }
- // Record the branch bytecode offset.
- // This is used for "ignore exception" and "set next stmt" scenarios. See ProbeContainer::GetNextUserStatementOffsetForAdvance:
- // If there is a branch recorded between current offset and next stmt offset, we'll use offset of the branch recorded,
- // otherwise use offset of next stmt.
- // The idea here is that when we bail out after ignore exception, we need to bail out to the beginning of the ForIn,
- // but currently ForIn stmt starts at the condition part, which is needed for correct handling of break point on ForIn
- // (break every time on the loop back edge) and correct display of current statement under debugger.
- // See WinBlue 231880 for details.
- byteCodeGenerator->Writer()->RecordStatementAdjustment(Js::FunctionBody::SAT_All);
- if (byteCodeGenerator->IsES6ForLoopSemanticsEnabled() &&
- loopNode->pnodeBlock->HasBlockScopedContent())
- {
- byteCodeGenerator->Writer()->RecordForInOrOfCollectionScope();
- }
- Js::ByteCodeLabel loopEntrance = byteCodeGenerator->Writer()->DefineLabel();
- Js::ByteCodeLabel continuePastLoop = byteCodeGenerator->Writer()->DefineLabel();
- if (loopNode->pnodeLval->nop == knopVarDecl)
- {
- EmitReference(loopNode->pnodeLval, byteCodeGenerator, funcInfo);
- }
- Emit(loopNode->pnodeObj, byteCodeGenerator, funcInfo, false); // evaluate collection expression
- funcInfo->ReleaseLoc(loopNode->pnodeObj);
- if (byteCodeGenerator->IsES6ForLoopSemanticsEnabled())
- {
- EndEmitBlock(loopNode->pnodeBlock, byteCodeGenerator, funcInfo);
- if (loopNode->pnodeBlock->scope != nullptr)
- {
- loopNode->pnodeBlock->scope->ForEachSymbol([](Symbol *sym) {
- sym->SetIsTrackedForDebugger(false);
- });
- }
- }
- if (isForIn)
- {
- EmitForIn(loopNode, loopEntrance, continuePastLoop, byteCodeGenerator, funcInfo, fReturnValue);
- if (!byteCodeGenerator->IsES6ForLoopSemanticsEnabled())
- {
- EndEmitBlock(loopNode->pnodeBlock, byteCodeGenerator, funcInfo);
- }
- return;
- }
- Js::ByteCodeLabel skipThrow = byteCodeGenerator->Writer()->DefineLabel();
- byteCodeGenerator->Writer()->BrReg2(Js::OpCode::BrNeq_A, skipThrow, loopNode->pnodeObj->location, funcInfo->undefinedConstantRegister);
- byteCodeGenerator->Writer()->W1(Js::OpCode::RuntimeTypeError, SCODE_CODE(JSERR_ObjectCoercible));
- byteCodeGenerator->Writer()->MarkLabel(skipThrow);
- Js::RegSlot regException = Js::Constants::NoRegister;
- Js::RegSlot regOffset = Js::Constants::NoRegister;
- // These two temp variables store the information of return function to be called or not.
- // one variable is used for catch block and one is used for finally block. These variable will be set to true when we think that return function
- // to be called on abrupt loop break.
- // Why two variables? since these are temps and JIT does like not flow if single variable is used in multiple blocks.
- Js::RegSlot shouldCallReturnFunctionLocation = funcInfo->AcquireTmpRegister();
- Js::RegSlot shouldCallReturnFunctionLocationFinally = funcInfo->AcquireTmpRegister();
- bool isCoroutine = funcInfo->byteCodeFunction->IsCoroutine();
- if (isCoroutine)
- {
- regException = funcInfo->AcquireTmpRegister();
- regOffset = funcInfo->AcquireTmpRegister();
- }
- // Grab registers for the enumerator and for the current enumerated item.
- // The enumerator register will be released after this call returns.
- loopNode->itemLocation = funcInfo->AcquireTmpRegister();
- // We want call profile information on the @@iterator call, so instead of adding a GetForOfIterator bytecode op
- // to do all the following work in a helper do it explicitly in bytecode so that the @@iterator call is exposed
- // to the profiler and JIT.
- byteCodeGenerator->SetHasFinally(true);
- byteCodeGenerator->SetHasTry(true);
- byteCodeGenerator->TopFuncInfo()->byteCodeFunction->SetDontInline(true);
- // do a ToObject on the collection
- Js::RegSlot tmpObj = funcInfo->AcquireTmpRegister();
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Conv_Obj, tmpObj, loopNode->pnodeObj->location);
- EmitGetIterator(loopNode->location, tmpObj, byteCodeGenerator, funcInfo);
- funcInfo->ReleaseTmpRegister(tmpObj);
- // The whole loop is surrounded with try..catch..finally - in order to capture the abrupt completion.
- Js::ByteCodeLabel finallyLabel = byteCodeGenerator->Writer()->DefineLabel();
- Js::ByteCodeLabel catchLabel = byteCodeGenerator->Writer()->DefineLabel();
- byteCodeGenerator->Writer()->RecordCrossFrameEntryExitRecord(true);
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, shouldCallReturnFunctionLocation);
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, shouldCallReturnFunctionLocationFinally);
- ByteCodeGenerator::TryScopeRecord tryRecForTryFinally(Js::OpCode::TryFinallyWithYield, finallyLabel);
- if (isCoroutine)
- {
- byteCodeGenerator->Writer()->BrReg2(Js::OpCode::TryFinallyWithYield, finallyLabel, regException, regOffset);
- tryRecForTryFinally.reg1 = regException;
- tryRecForTryFinally.reg2 = regOffset;
- byteCodeGenerator->tryScopeRecordsList.LinkToEnd(&tryRecForTryFinally);
- }
- else
- {
- byteCodeGenerator->Writer()->Br(Js::OpCode::TryFinally, finallyLabel);
- }
- byteCodeGenerator->Writer()->Br(Js::OpCode::TryCatch, catchLabel);
- ByteCodeGenerator::TryScopeRecord tryRecForTry(Js::OpCode::TryCatch, catchLabel);
- if (isCoroutine)
- {
- byteCodeGenerator->tryScopeRecordsList.LinkToEnd(&tryRecForTry);
- }
- byteCodeGenerator->EndStatement(loopNode);
- // Need to increment loop count whether we are going into profile or not for HasLoop()
- uint loopId = byteCodeGenerator->Writer()->EnterLoop(loopEntrance);
- loopNode->loopId = loopId;
- byteCodeGenerator->StartStatement(loopNode->pnodeLval);
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, shouldCallReturnFunctionLocation);
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, shouldCallReturnFunctionLocationFinally);
- EmitIteratorNext(loopNode->itemLocation, loopNode->location, Js::Constants::NoRegister, byteCodeGenerator, funcInfo);
- Js::RegSlot doneLocation = funcInfo->AcquireTmpRegister();
- EmitIteratorComplete(doneLocation, loopNode->itemLocation, byteCodeGenerator, funcInfo);
- // branch past loop if the result's done property is truthy
- byteCodeGenerator->Writer()->BrReg1(Js::OpCode::BrTrue_A, continuePastLoop, doneLocation);
- funcInfo->ReleaseTmpRegister(doneLocation);
- // otherwise put result's value property in itemLocation
- EmitIteratorValue(loopNode->itemLocation, loopNode->itemLocation, byteCodeGenerator, funcInfo);
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdTrue, shouldCallReturnFunctionLocation);
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdTrue, shouldCallReturnFunctionLocationFinally);
- EmitForInOfLoopBody(loopNode, loopEntrance, continuePastLoop, byteCodeGenerator, funcInfo, fReturnValue);
- byteCodeGenerator->Writer()->ExitLoop(loopId);
- EmitCatchAndFinallyBlocks(catchLabel,
- finallyLabel,
- loopNode->location,
- shouldCallReturnFunctionLocation,
- shouldCallReturnFunctionLocationFinally,
- regException,
- regOffset,
- byteCodeGenerator,
- funcInfo);
- if (!byteCodeGenerator->IsES6ForLoopSemanticsEnabled())
- {
- EndEmitBlock(loopNode->pnodeBlock, byteCodeGenerator, funcInfo);
- }
- }
- void EmitArrayLiteral(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo)
- {
- funcInfo->AcquireLoc(pnode);
- ParseNode *args = pnode->AsParseNodeUni()->pnode1;
- if (args == nullptr)
- {
- byteCodeGenerator->Writer()->Reg1Unsigned1(
- pnode->AsParseNodeArrLit()->hasMissingValues ? Js::OpCode::NewScArrayWithMissingValues : Js::OpCode::NewScArray,
- pnode->location,
- ByteCodeGenerator::DefaultArraySize);
- }
- else
- {
- SetNewArrayElements(pnode, pnode->location, byteCodeGenerator, funcInfo);
- }
- }
- void EmitJumpCleanup(ParseNodeStmt *pnode, ParseNode *pnodeTarget, ByteCodeGenerator *byteCodeGenerator, FuncInfo * funcInfo)
- {
- for (; pnode != pnodeTarget; pnode = pnode->pnodeOuter)
- {
- switch (pnode->nop)
- {
- case knopTry:
- case knopCatch:
- case knopFinally:
- // We insert OpCode::Leave when there is a 'return' inside try/catch/finally.
- // This is for flow control and does not participate in identifying boundaries of try/catch blocks,
- // thus we shouldn't call RecordCrossFrameEntryExitRecord() here.
- byteCodeGenerator->Writer()->Empty(Js::OpCode::Leave);
- break;
- case knopForOf:
- #if ENABLE_PROFILE_INFO
- if (Js::DynamicProfileInfo::EnableImplicitCallFlags(funcInfo->GetParsedFunctionBody()))
- {
- byteCodeGenerator->Writer()->Unsigned1(Js::OpCode::ProfiledLoopEnd, pnode->AsParseNodeLoop()->loopId);
- }
- #endif
- // The ForOf loop code is wrapped around try..catch..finally - Forcing couple Leave bytecode over here
- byteCodeGenerator->Writer()->Empty(Js::OpCode::Leave);
- byteCodeGenerator->Writer()->Empty(Js::OpCode::Leave);
- break;
- #if ENABLE_PROFILE_INFO
- case knopWhile:
- case knopDoWhile:
- case knopFor:
- case knopForIn:
- if (Js::DynamicProfileInfo::EnableImplicitCallFlags(funcInfo->GetParsedFunctionBody()))
- {
- byteCodeGenerator->Writer()->Unsigned1(Js::OpCode::ProfiledLoopEnd, pnode->AsParseNodeLoop()->loopId);
- }
- break;
- #endif
- }
- }
- }
- void EmitBinaryOpnds(ParseNode *pnode1, ParseNode *pnode2, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo)
- {
- // If opnd2 can overwrite opnd1, make sure the value of opnd1 is stashed away.
- if (MayHaveSideEffectOnNode(pnode1, pnode2, byteCodeGenerator))
- {
- SaveOpndValue(pnode1, funcInfo);
- }
- Emit(pnode1, byteCodeGenerator, funcInfo, false);
- if (pnode1->nop == knopComputedName && pnode2->nop == knopClassDecl &&
- (pnode2->AsParseNodeClass()->pnodeConstructor == nullptr || pnode2->AsParseNodeClass()->pnodeConstructor->nop != knopVarDecl))
- {
- Emit(pnode2, byteCodeGenerator, funcInfo, false, false, pnode1);
- }
- else
- {
- Emit(pnode2, byteCodeGenerator, funcInfo, false);
- }
- }
- void EmitBinaryReference(ParseNode *pnode1, ParseNode *pnode2, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo, BOOL fLoadLhs)
- {
- // Make sure that the RHS of an assignment doesn't kill the opnd's of the expression on the LHS.
- switch (pnode1->nop)
- {
- case knopName:
- if (fLoadLhs && MayHaveSideEffectOnNode(pnode1, pnode2, byteCodeGenerator))
- {
- // Given x op y, y may kill x, so stash x.
- // Note that this only matters if we're loading x prior to the op.
- SaveOpndValue(pnode1, funcInfo);
- }
- break;
- case knopDot:
- if (fLoadLhs)
- {
- // We're loading the value of the LHS before the RHS, so make sure the LHS gets a register first.
- funcInfo->AcquireLoc(pnode1);
- }
- if (MayHaveSideEffectOnNode(pnode1->AsParseNodeBin()->pnode1, pnode2, byteCodeGenerator))
- {
- // Given x.y op z, z may kill x, so stash x away.
- SaveOpndValue(pnode1->AsParseNodeBin()->pnode1, funcInfo);
- }
- break;
- case knopIndex:
- if (fLoadLhs)
- {
- // We're loading the value of the LHS before the RHS, so make sure the LHS gets a register first.
- funcInfo->AcquireLoc(pnode1);
- }
- if (MayHaveSideEffectOnNode(pnode1->AsParseNodeBin()->pnode1, pnode2, byteCodeGenerator) ||
- MayHaveSideEffectOnNode(pnode1->AsParseNodeBin()->pnode1, pnode1->AsParseNodeBin()->pnode2, byteCodeGenerator))
- {
- // Given x[y] op z, y or z may kill x, so stash x away.
- SaveOpndValue(pnode1->AsParseNodeBin()->pnode1, funcInfo);
- }
- if (MayHaveSideEffectOnNode(pnode1->AsParseNodeBin()->pnode2, pnode2, byteCodeGenerator))
- {
- // Given x[y] op z, z may kill y, so stash y away.
- // But make sure that x gets a register before y.
- funcInfo->AcquireLoc(pnode1->AsParseNodeBin()->pnode1);
- SaveOpndValue(pnode1->AsParseNodeBin()->pnode2, funcInfo);
- }
- break;
- }
- if (fLoadLhs)
- {
- // Emit code to load the value of the LHS.
- EmitLoad(pnode1, byteCodeGenerator, funcInfo);
- }
- else
- {
- // Emit code to evaluate the LHS opnds, but don't load the LHS's value.
- EmitReference(pnode1, byteCodeGenerator, funcInfo);
- }
- // Evaluate the RHS.
- Emit(pnode2, byteCodeGenerator, funcInfo, false);
- }
- void EmitUseBeforeDeclarationRuntimeError(ByteCodeGenerator * byteCodeGenerator, Js::RegSlot location)
- {
- byteCodeGenerator->Writer()->W1(Js::OpCode::RuntimeReferenceError, SCODE_CODE(JSERR_UseBeforeDeclaration));
- if (location != Js::Constants::NoRegister)
- {
- // Optionally load something into register in order to do not confuse IRBuilder. This value will never be used.
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdUndef, location);
- }
- }
- void EmitUseBeforeDeclaration(Symbol *sym, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo)
- {
- // Don't emit static use-before-declaration error in a closure or dynamic scope case. We detect such cases with dynamic checks,
- // if necessary.
- if (sym != nullptr &&
- !sym->GetIsModuleExportStorage() &&
- sym->GetNeedDeclaration() &&
- byteCodeGenerator->GetCurrentScope()->HasStaticPathToAncestor(sym->GetScope()) &&
- sym->GetScope()->GetFunc() == funcInfo)
- {
- EmitUseBeforeDeclarationRuntimeError(byteCodeGenerator, Js::Constants::NoRegister);
- }
- }
- void EmitBinary(Js::OpCode opcode, ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo)
- {
- byteCodeGenerator->StartStatement(pnode);
- EmitBinaryOpnds(pnode->AsParseNodeBin()->pnode1, pnode->AsParseNodeBin()->pnode2, byteCodeGenerator, funcInfo);
- funcInfo->ReleaseLoc(pnode->AsParseNodeBin()->pnode2);
- funcInfo->ReleaseLoc(pnode->AsParseNodeBin()->pnode1);
- funcInfo->AcquireLoc(pnode);
- byteCodeGenerator->Writer()->Reg3(opcode,
- pnode->location,
- pnode->AsParseNodeBin()->pnode1->location,
- pnode->AsParseNodeBin()->pnode2->location);
- byteCodeGenerator->EndStatement(pnode);
- }
- bool CollectConcat(ParseNode *pnodeAdd, DListCounted<ParseNode *, ArenaAllocator>& concatOpnds, ArenaAllocator *arenaAllocator)
- {
- Assert(pnodeAdd->nop == knopAdd);
- Assert(pnodeAdd->CanFlattenConcatExpr());
- bool doConcatString = false;
- DList<ParseNode*, ArenaAllocator> pnodeStack(arenaAllocator);
- pnodeStack.Prepend(pnodeAdd->AsParseNodeBin()->pnode2);
- ParseNode * pnode = pnodeAdd->AsParseNodeBin()->pnode1;
- while (true)
- {
- if (!pnode->CanFlattenConcatExpr())
- {
- concatOpnds.Append(pnode);
- }
- else if (pnode->nop == knopStr)
- {
- concatOpnds.Append(pnode);
- // Detect if there are any string larger then the append size limit.
- // If there are, we can do concat; otherwise, still use add so we will not lose the AddLeftDead opportunities.
- doConcatString = doConcatString || !Js::CompoundString::ShouldAppendChars(pnode->AsParseNodeStr()->pid->Cch());
- }
- else
- {
- Assert(pnode->nop == knopAdd);
- pnodeStack.Prepend(pnode->AsParseNodeBin()->pnode2);
- pnode = pnode->AsParseNodeBin()->pnode1;
- continue;
- }
- if (pnodeStack.Empty())
- {
- break;
- }
- pnode = pnodeStack.Head();
- pnodeStack.RemoveHead();
- }
- return doConcatString;
- }
- void EmitConcat3(ParseNode *pnode, ParseNode *pnode1, ParseNode *pnode2, ParseNode *pnode3, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo)
- {
- byteCodeGenerator->StartStatement(pnode);
- if (MayHaveSideEffectOnNode(pnode1, pnode2, byteCodeGenerator) || MayHaveSideEffectOnNode(pnode1, pnode3, byteCodeGenerator))
- {
- SaveOpndValue(pnode1, funcInfo);
- }
- if (MayHaveSideEffectOnNode(pnode2, pnode3, byteCodeGenerator))
- {
- SaveOpndValue(pnode2, funcInfo);
- }
- Emit(pnode1, byteCodeGenerator, funcInfo, false);
- Emit(pnode2, byteCodeGenerator, funcInfo, false);
- Emit(pnode3, byteCodeGenerator, funcInfo, false);
- funcInfo->ReleaseLoc(pnode3);
- funcInfo->ReleaseLoc(pnode2);
- funcInfo->ReleaseLoc(pnode1);
- funcInfo->AcquireLoc(pnode);
- byteCodeGenerator->Writer()->Reg4(Js::OpCode::Concat3,
- pnode->location,
- pnode1->location,
- pnode2->location,
- pnode3->location);
- byteCodeGenerator->EndStatement(pnode);
- }
- void EmitNewConcatStrMulti(ParseNode *pnode, uint8 count, ParseNode *pnode1, ParseNode *pnode2, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo)
- {
- EmitBinaryOpnds(pnode1, pnode2, byteCodeGenerator, funcInfo);
- funcInfo->ReleaseLoc(pnode2);
- funcInfo->ReleaseLoc(pnode1);
- funcInfo->AcquireLoc(pnode);
- byteCodeGenerator->Writer()->Reg3B1(Js::OpCode::NewConcatStrMulti,
- pnode->location,
- pnode1->location,
- pnode2->location,
- count);
- }
- void EmitAdd(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo)
- {
- Assert(pnode->nop == knopAdd);
- if (pnode->CanFlattenConcatExpr())
- {
- // We should only have a string concat if the feature is on.
- Assert(!PHASE_OFF1(Js::ByteCodeConcatExprOptPhase));
- DListCounted<ParseNode*, ArenaAllocator> concatOpnds(byteCodeGenerator->GetAllocator());
- bool doConcatString = CollectConcat(pnode, concatOpnds, byteCodeGenerator->GetAllocator());
- if (doConcatString)
- {
- uint concatCount = concatOpnds.Count();
- Assert(concatCount >= 2);
- // Don't do concatN if the number is too high
- // CONSIDER: although we could have done multiple ConcatNs
- if (concatCount > 2 && concatCount <= UINT8_MAX)
- {
- #if DBG
- char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
- #endif
- ParseNode * pnode1 = concatOpnds.Head();
- concatOpnds.RemoveHead();
- ParseNode * pnode2 = concatOpnds.Head();
- concatOpnds.RemoveHead();
- if (concatCount == 3)
- {
- OUTPUT_TRACE_DEBUGONLY(Js::ByteCodeConcatExprOptPhase, _u("%s(%s) offset:#%d : Concat3\n"),
- funcInfo->GetParsedFunctionBody()->GetDisplayName(),
- funcInfo->GetParsedFunctionBody()->GetDebugNumberSet(debugStringBuffer),
- byteCodeGenerator->Writer()->ByteCodeDataSize());
- EmitConcat3(pnode, pnode1, pnode2, concatOpnds.Head(), byteCodeGenerator, funcInfo);
- return;
- }
- OUTPUT_TRACE_DEBUGONLY(Js::ByteCodeConcatExprOptPhase, _u("%s(%s) offset:#%d: ConcatMulti %d\n"),
- funcInfo->GetParsedFunctionBody()->GetDisplayName(),
- funcInfo->GetParsedFunctionBody()->GetDebugNumberSet(debugStringBuffer),
- byteCodeGenerator->Writer()->ByteCodeDataSize(), concatCount);
- byteCodeGenerator->StartStatement(pnode);
- funcInfo->AcquireLoc(pnode);
- // CONSIDER: this may cause the backend not able CSE repeating pattern within the concat.
- EmitNewConcatStrMulti(pnode, (uint8)concatCount, pnode1, pnode2, byteCodeGenerator, funcInfo);
- uint i = 2;
- do
- {
- ParseNode * currNode = concatOpnds.Head();
- concatOpnds.RemoveHead();
- ParseNode * currNode2 = concatOpnds.Head();
- concatOpnds.RemoveHead();
- EmitBinaryOpnds(currNode, currNode2, byteCodeGenerator, funcInfo);
- funcInfo->ReleaseLoc(currNode2);
- funcInfo->ReleaseLoc(currNode);
- byteCodeGenerator->Writer()->Reg3B1(
- Js::OpCode::SetConcatStrMultiItem2, pnode->location, currNode->location, currNode2->location, (uint8)i);
- i += 2;
- } while (concatOpnds.Count() > 1);
- if (!concatOpnds.Empty())
- {
- ParseNode * currNode = concatOpnds.Head();
- Emit(currNode, byteCodeGenerator, funcInfo, false);
- funcInfo->ReleaseLoc(currNode);
- byteCodeGenerator->Writer()->Reg2B1(
- Js::OpCode::SetConcatStrMultiItem, pnode->location, currNode->location, (uint8)i);
- i++;
- }
- Assert(concatCount == i);
- byteCodeGenerator->EndStatement(pnode);
- return;
- }
- }
- // Since we collected all the node already, let's just emit them instead of doing it recursively.
- byteCodeGenerator->StartStatement(pnode);
- ParseNode * currNode = concatOpnds.Head();
- concatOpnds.RemoveHead();
- ParseNode * currNode2 = concatOpnds.Head();
- concatOpnds.RemoveHead();
- EmitBinaryOpnds(currNode, currNode2, byteCodeGenerator, funcInfo);
- funcInfo->ReleaseLoc(currNode2);
- funcInfo->ReleaseLoc(currNode);
- Js::RegSlot dstReg = funcInfo->AcquireLoc(pnode);
- byteCodeGenerator->Writer()->Reg3(
- Js::OpCode::Add_A, dstReg, currNode->location, currNode2->location);
- while (!concatOpnds.Empty())
- {
- currNode = concatOpnds.Head();
- concatOpnds.RemoveHead();
- Emit(currNode, byteCodeGenerator, funcInfo, false);
- funcInfo->ReleaseLoc(currNode);
- byteCodeGenerator->Writer()->Reg3(
- Js::OpCode::Add_A, dstReg, dstReg, currNode->location);
- }
- byteCodeGenerator->EndStatement(pnode);
- }
- else
- {
- EmitBinary(Js::OpCode::Add_A, pnode, byteCodeGenerator, funcInfo);
- }
- }
- void ByteCodeGenerator::EmitLeaveOpCodesBeforeYield()
- {
- for (TryScopeRecord* node = this->tryScopeRecordsList.Tail(); node != nullptr; node = node->Previous())
- {
- switch (node->op)
- {
- case Js::OpCode::TryFinallyWithYield:
- this->Writer()->Empty(Js::OpCode::LeaveNull);
- break;
- case Js::OpCode::TryCatch:
- case Js::OpCode::ResumeFinally:
- case Js::OpCode::ResumeCatch:
- this->Writer()->Empty(Js::OpCode::Leave);
- break;
- default:
- AssertMsg(false, "Unexpected OpCode before Yield in the Try-Catch-Finally cache for generator!");
- break;
- }
- }
- }
- void ByteCodeGenerator::EmitTryBlockHeadersAfterYield()
- {
- for (TryScopeRecord* node = this->tryScopeRecordsList.Head(); node != nullptr; node = node->Next())
- {
- switch (node->op)
- {
- case Js::OpCode::TryCatch:
- this->Writer()->Br(node->op, node->label);
- break;
- case Js::OpCode::TryFinallyWithYield:
- case Js::OpCode::ResumeFinally:
- this->Writer()->BrReg2(node->op, node->label, node->reg1, node->reg2);
- break;
- case Js::OpCode::ResumeCatch:
- this->Writer()->Empty(node->op);
- break;
- default:
- AssertMsg(false, "Unexpected OpCode after yield in the Try-Catch-Finally cache for generator!");
- break;
- }
- }
- }
- void EmitYield(Js::RegSlot inputLocation, Js::RegSlot resultLocation, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo, Js::RegSlot yieldStarIterator)
- {
- // If the bytecode emitted by this function is part of 'yield*', inputLocation is the object
- // returned by the iterable's next/return/throw method. Otherwise, it is the yielded value.
- if (yieldStarIterator == Js::Constants::NoRegister)
- {
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::NewScObjectSimple, funcInfo->yieldRegister);
- uint cacheId = funcInfo->FindOrAddInlineCacheId(funcInfo->yieldRegister, Js::PropertyIds::value, false, true);
- byteCodeGenerator->Writer()->PatchableProperty(Js::OpCode::StFld, inputLocation, funcInfo->yieldRegister, cacheId);
- cacheId = funcInfo->FindOrAddInlineCacheId(funcInfo->yieldRegister, Js::PropertyIds::done, false, true);
- byteCodeGenerator->Writer()->PatchableProperty(Js::OpCode::StFld, funcInfo->falseConstantRegister, funcInfo->yieldRegister, cacheId);
- }
- else
- {
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, funcInfo->yieldRegister, inputLocation);
- }
- byteCodeGenerator->EmitLeaveOpCodesBeforeYield();
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Yield, funcInfo->yieldRegister, funcInfo->yieldRegister);
- byteCodeGenerator->EmitTryBlockHeadersAfterYield();
- if (yieldStarIterator == Js::Constants::NoRegister)
- {
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::ResumeYield, resultLocation, funcInfo->yieldRegister);
- }
- else
- {
- byteCodeGenerator->Writer()->Reg3(Js::OpCode::ResumeYieldStar, resultLocation, funcInfo->yieldRegister, yieldStarIterator);
- }
- }
- void EmitYieldStar(ParseNodeUni* yieldStarNode, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo)
- {
- funcInfo->AcquireLoc(yieldStarNode);
- Js::ByteCodeLabel loopEntrance = byteCodeGenerator->Writer()->DefineLabel();
- Js::ByteCodeLabel continuePastLoop = byteCodeGenerator->Writer()->DefineLabel();
- Js::RegSlot iteratorLocation = funcInfo->AcquireTmpRegister();
- // Evaluate operand
- Emit(yieldStarNode->pnode1, byteCodeGenerator, funcInfo, false);
- funcInfo->ReleaseLoc(yieldStarNode->pnode1);
- EmitGetIterator(iteratorLocation, yieldStarNode->pnode1->location, byteCodeGenerator, funcInfo);
- // Call the iterator's next()
- EmitIteratorNext(yieldStarNode->location, iteratorLocation, funcInfo->undefinedConstantRegister, byteCodeGenerator, funcInfo);
- uint loopId = byteCodeGenerator->Writer()->EnterLoop(loopEntrance);
- // since a yield* doesn't have a user defined body, we cannot return from this loop
- // which means we don't need to support EmitJumpCleanup() and there do not need to
- // remember the loopId like the loop statements do.
- Js::RegSlot doneLocation = funcInfo->AcquireTmpRegister();
- EmitIteratorComplete(doneLocation, yieldStarNode->location, byteCodeGenerator, funcInfo);
- // branch past the loop if the done property is truthy
- byteCodeGenerator->Writer()->BrReg1(Js::OpCode::BrTrue_A, continuePastLoop, doneLocation);
- funcInfo->ReleaseTmpRegister(doneLocation);
- EmitYield(yieldStarNode->location, yieldStarNode->location, byteCodeGenerator, funcInfo, iteratorLocation);
- funcInfo->ReleaseTmpRegister(iteratorLocation);
- byteCodeGenerator->Writer()->Br(loopEntrance);
- byteCodeGenerator->Writer()->MarkLabel(continuePastLoop);
- byteCodeGenerator->Writer()->ExitLoop(loopId);
- // Put the iterator result's value in yieldStarNode->location.
- // It will be used as the result value of the yield* operator expression.
- EmitIteratorValue(yieldStarNode->location, yieldStarNode->location, byteCodeGenerator, funcInfo);
- }
- void TrackIntConstantsOnGlobalUserObject(ByteCodeGenerator *byteCodeGenerator, bool isSymGlobalAndSingleAssignment, Js::PropertyId propertyId)
- {
- if (isSymGlobalAndSingleAssignment)
- {
- byteCodeGenerator->GetScriptContext()->TrackIntConstPropertyOnGlobalUserObject(propertyId);
- }
- }
- void TrackIntConstantsOnGlobalObject(ByteCodeGenerator *byteCodeGenerator, bool isSymGlobalAndSingleAssignment, Js::PropertyId propertyId)
- {
- if (isSymGlobalAndSingleAssignment)
- {
- byteCodeGenerator->GetScriptContext()->TrackIntConstPropertyOnGlobalObject(propertyId);
- }
- }
- void TrackIntConstantsOnGlobalObject(ByteCodeGenerator *byteCodeGenerator, Symbol *sym)
- {
- if (sym && sym->GetIsGlobal() && sym->IsAssignedOnce())
- {
- Js::PropertyId propertyId = sym->EnsurePosition(byteCodeGenerator);
- byteCodeGenerator->GetScriptContext()->TrackIntConstPropertyOnGlobalObject(propertyId);
- }
- }
- void TrackMemberNodesInObjectForIntConstants(ByteCodeGenerator *byteCodeGenerator, ParseNodePtr objNode)
- {
- Assert(objNode->nop == knopObject);
- ParseNodePtr memberList = objNode->AsParseNodeUni()->pnode1;
- while (memberList != nullptr)
- {
- ParseNodePtr memberNode = memberList->nop == knopList ? memberList->AsParseNodeBin()->pnode1 : memberList;
- if (memberNode->nop != knopEllipsis)
- {
- ParseNodePtr memberNameNode = memberNode->AsParseNodeBin()->pnode1;
- ParseNodePtr memberValNode = memberNode->AsParseNodeBin()->pnode2;
- if (memberNameNode->nop != knopComputedName && memberValNode->nop == knopInt)
- {
- Js::PropertyId propertyId = memberNameNode->AsParseNodeStr()->pid->GetPropertyId();
- TrackIntConstantsOnGlobalUserObject(byteCodeGenerator, true, propertyId);
- }
- }
- memberList = memberList->nop == knopList ? memberList->AsParseNodeBin()->pnode2 : nullptr;
- }
- }
- void TrackGlobalIntAssignmentsForknopDotProps(ParseNodePtr knopDotNode, ByteCodeGenerator * byteCodeGenerator)
- {
- Assert(knopDotNode->nop == knopDot);
- ParseNodePtr objectNode = knopDotNode->AsParseNodeBin()->pnode1;
- ParseNodeName * propertyNode = knopDotNode->AsParseNodeBin()->pnode2->AsParseNodeName();
- bool isSymGlobalAndSingleAssignment = false;
- if (objectNode->nop == knopName)
- {
- if (ByteCodeGenerator::IsThis(objectNode))
- {
- // Assume 'this' always refer to GlobalObject
- // Cases like "this.a = "
- isSymGlobalAndSingleAssignment = propertyNode->pid->IsSingleAssignment();
- Js::PropertyId propertyId = propertyNode->PropertyIdFromNameNode();
- TrackIntConstantsOnGlobalObject(byteCodeGenerator, isSymGlobalAndSingleAssignment, propertyId);
- }
- else
- {
- Symbol * sym = objectNode->AsParseNodeName()->sym;
- isSymGlobalAndSingleAssignment = sym && sym->GetIsGlobal() && sym->IsAssignedOnce() && propertyNode->pid->IsSingleAssignment();
- Js::PropertyId propertyId = propertyNode->PropertyIdFromNameNode();
- TrackIntConstantsOnGlobalUserObject(byteCodeGenerator, isSymGlobalAndSingleAssignment, propertyId);
- }
- }
- }
- void TrackGlobalIntAssignments(ParseNodePtr pnode, ByteCodeGenerator * byteCodeGenerator)
- {
- // Track the Global Int Constant properties' assignments here.
- uint nodeType = ParseNode::Grfnop(pnode->nop);
- if (nodeType & fnopAsg)
- {
- if (nodeType & fnopBin)
- {
- ParseNodePtr lhs = pnode->AsParseNodeBin()->pnode1;
- ParseNodePtr rhs = pnode->AsParseNodeBin()->pnode2;
- Assert(lhs && rhs);
- // Don't track other than integers and objects with member nodes.
- if (rhs->nop == knopObject)
- {
- TrackMemberNodesInObjectForIntConstants(byteCodeGenerator, rhs);
- }
- else if (rhs->nop != knopInt &&
- ((rhs->nop != knopLsh && rhs->nop != knopRsh) || (rhs->AsParseNodeBin()->pnode1->nop != knopInt || rhs->AsParseNodeBin()->pnode2->nop != knopInt)))
- {
- return;
- }
- if (lhs->nop == knopName)
- {
- // Handle "a = <Integer>" cases here
- Symbol * sym = lhs->AsParseNodeName()->sym;
- TrackIntConstantsOnGlobalObject(byteCodeGenerator, sym);
- }
- else if (lhs->nop == knopDot && lhs->AsParseNodeBin()->pnode2->nop == knopName)
- {
- // Cases like "obj.a = <Integer>"
- TrackGlobalIntAssignmentsForknopDotProps(lhs, byteCodeGenerator);
- }
- }
- else if (nodeType & fnopUni)
- {
- ParseNodePtr lhs = pnode->AsParseNodeUni()->pnode1;
- if (lhs->nop == knopName)
- {
- // Cases like "a++"
- Symbol * sym = lhs->AsParseNodeName()->sym;
- TrackIntConstantsOnGlobalObject(byteCodeGenerator, sym);
- }
- else if (lhs->nop == knopDot && lhs->AsParseNodeBin()->pnode2->nop == knopName)
- {
- // Cases like "obj.a++"
- TrackGlobalIntAssignmentsForknopDotProps(lhs, byteCodeGenerator);
- }
- }
- }
- }
- void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo, BOOL fReturnValue, bool isConstructorCall, ParseNode * bindPnode, bool isTopLevel)
- {
- if (pnode == nullptr)
- {
- return;
- }
- ThreadContext::ProbeCurrentStackNoDispose(Js::Constants::MinStackByteCodeVisitor, byteCodeGenerator->GetScriptContext());
- TrackGlobalIntAssignments(pnode, byteCodeGenerator);
- // printNop(pnode->nop);
- switch (pnode->nop)
- {
- case knopList:
- EmitList(pnode, byteCodeGenerator, funcInfo);
- break;
- case knopInt:
- // currently, these are loaded at the top
- break;
- // PTNODE(knopFlt , "flt const" ,None ,Flt ,fnopLeaf|fnopConst)
- case knopFlt:
- // currently, these are loaded at the top
- break;
- // PTNODE(knopStr , "str const" ,None ,Pid ,fnopLeaf|fnopConst)
- case knopStr:
- // TODO: protocol for combining string constants
- break;
- // PTNODE(knopRegExp , "reg expr" ,None ,Pid ,fnopLeaf|fnopConst)
- case knopBigInt:
- // PTNODE(knopBigInt , "bigint const" ,None ,Pid ,fnopLeaf|fnopConst)
- break;
- case knopRegExp:
- funcInfo->GetParsedFunctionBody()->SetLiteralRegex(pnode->AsParseNodeRegExp()->regexPatternIndex, pnode->AsParseNodeRegExp()->regexPattern);
- byteCodeGenerator->Writer()->Reg1Unsigned1(Js::OpCode::NewRegEx, funcInfo->AcquireLoc(pnode), pnode->AsParseNodeRegExp()->regexPatternIndex);
- break;
- // PTNODE(knopNull , "null" ,Null ,None ,fnopLeaf)
- case knopNull:
- // enregistered
- break;
- // PTNODE(knopFalse , "false" ,False ,None ,fnopLeaf)
- case knopFalse:
- // enregistered
- break;
- // PTNODE(knopTrue , "true" ,True ,None ,fnopLeaf)
- case knopTrue:
- // enregistered
- break;
- // PTNODE(knopEmpty , "empty" ,Empty ,None ,fnopLeaf)
- case knopEmpty:
- break;
- // Unary operators.
- // PTNODE(knopNot , "~" ,BitNot ,Uni ,fnopUni)
- case knopNot:
- STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
- Emit(pnode->AsParseNodeUni()->pnode1, byteCodeGenerator, funcInfo, false);
- funcInfo->ReleaseLoc(pnode->AsParseNodeUni()->pnode1);
- byteCodeGenerator->Writer()->Reg2(
- Js::OpCode::Not_A, funcInfo->AcquireLoc(pnode), pnode->AsParseNodeUni()->pnode1->location);
- ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
- break;
- // PTNODE(knopNeg , "unary -" ,Neg ,Uni ,fnopUni)
- case knopNeg:
- STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
- Emit(pnode->AsParseNodeUni()->pnode1, byteCodeGenerator, funcInfo, false);
- funcInfo->ReleaseLoc(pnode->AsParseNodeUni()->pnode1);
- funcInfo->AcquireLoc(pnode);
- byteCodeGenerator->Writer()->Reg2(
- Js::OpCode::Neg_A, pnode->location, pnode->AsParseNodeUni()->pnode1->location);
- ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
- break;
- // PTNODE(knopPos , "unary +" ,Pos ,Uni ,fnopUni)
- case knopPos:
- STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
- Emit(pnode->AsParseNodeUni()->pnode1, byteCodeGenerator, funcInfo, false);
- funcInfo->ReleaseLoc(pnode->AsParseNodeUni()->pnode1);
- byteCodeGenerator->Writer()->Reg2(
- Js::OpCode::Conv_Num, funcInfo->AcquireLoc(pnode), pnode->AsParseNodeUni()->pnode1->location);
- ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
- break;
- // PTNODE(knopLogNot , "!" ,LogNot ,Uni ,fnopUni)
- case knopLogNot:
- {
- STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
- Js::ByteCodeLabel doneLabel = byteCodeGenerator->Writer()->DefineLabel();
- // For boolean expressions that compute a result, we have to burn a register for the result
- // so that the back end can identify it cheaply as a single temp lifetime. Revisit this if we do
- // full-on renaming in the back end.
- funcInfo->AcquireLoc(pnode);
- if (pnode->AsParseNodeUni()->pnode1->nop == knopInt)
- {
- int32 value = pnode->AsParseNodeUni()->pnode1->AsParseNodeInt()->lw;
- Js::OpCode op = value ? Js::OpCode::LdFalse : Js::OpCode::LdTrue;
- byteCodeGenerator->Writer()->Reg1(op, pnode->location);
- }
- else
- {
- Emit(pnode->AsParseNodeUni()->pnode1, byteCodeGenerator, funcInfo, false);
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, pnode->location);
- byteCodeGenerator->Writer()->BrReg1(Js::OpCode::BrTrue_A, doneLabel, pnode->AsParseNodeUni()->pnode1->location);
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdTrue, pnode->location);
- byteCodeGenerator->Writer()->MarkLabel(doneLabel);
- }
- funcInfo->ReleaseLoc(pnode->AsParseNodeUni()->pnode1);
- ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
- break;
- }
- // PTNODE(knopEllipsis , "..." ,Spread ,Uni , fnopUni)
- case knopEllipsis:
- {
- Emit(pnode->AsParseNodeUni()->pnode1, byteCodeGenerator, funcInfo, false);
- // Transparently pass the location of the object or array.
- pnode->location = pnode->AsParseNodeUni()->pnode1->location;
- break;
- }
- // PTNODE(knopIncPost , "post++" ,Inc ,Uni ,fnopUni|fnopAsg)
- case knopIncPost:
- case knopDecPost:
- // FALL THROUGH to the faster pre-inc/dec case if the result of the expression is not needed.
- if (pnode->isUsed || fReturnValue)
- {
- byteCodeGenerator->StartStatement(pnode);
- const Js::OpCode op = (pnode->nop == knopDecPost) ? Js::OpCode::Sub_A : Js::OpCode::Add_A;
- ParseNode* pnode1 = pnode->AsParseNodeUni()->pnode1;
- // Grab a register for the expression result.
- funcInfo->AcquireLoc(pnode);
- // Load the initial value, convert it (this is the expression result), and increment it.
- EmitLoad(pnode1, byteCodeGenerator, funcInfo);
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Conv_Num, pnode->location, pnode1->location);
- // Use temporary register if lhs cannot be assigned
- Js::RegSlot incDecResult = pnode1->location;
- if (funcInfo->RegIsConst(incDecResult) ||
- (pnode1->nop == knopName && pnode1->AsParseNodeName()->sym && pnode1->AsParseNodeName()->sym->GetIsFuncExpr()))
- {
- incDecResult = funcInfo->AcquireTmpRegister();
- }
- Js::RegSlot oneReg = funcInfo->constantToRegister.LookupWithKey(1, Js::Constants::NoRegister);
- Assert(oneReg != Js::Constants::NoRegister);
- byteCodeGenerator->Writer()->Reg3(op, incDecResult, pnode->location, oneReg);
- // Store the incremented value.
- EmitAssignment(nullptr, pnode1, incDecResult, byteCodeGenerator, funcInfo);
- // Release the incremented value and the l-value.
- if (incDecResult != pnode1->location)
- {
- funcInfo->ReleaseTmpRegister(incDecResult);
- }
- funcInfo->ReleaseLoad(pnode1);
- byteCodeGenerator->EndStatement(pnode);
- break;
- }
- else
- {
- pnode->nop = (pnode->nop == knopIncPost) ? knopIncPre : knopDecPre;
- }
- // FALL THROUGH to the fast pre-inc/dec case if the result of the expression is not needed.
- // PTNODE(knopIncPre , "++ pre" ,Inc ,Uni ,fnopUni|fnopAsg)
- case knopIncPre:
- case knopDecPre:
- {
- byteCodeGenerator->StartStatement(pnode);
- const Js::OpCode op = (pnode->nop == knopDecPre) ? Js::OpCode::Decr_A : Js::OpCode::Incr_A;
- ParseNode* pnode1 = pnode->AsParseNodeUni()->pnode1;
- // Assign a register for the result only if the result is used or the LHS can't be assigned to
- // (i.e., is a constant).
- const bool need_result_location =
- pnode->isUsed
- || fReturnValue
- || funcInfo->RegIsConst(pnode1->location)
- || (pnode1->nop == knopName && pnode1->AsParseNodeName()->sym && pnode1->AsParseNodeName()->sym->GetIsFuncExpr());
- if (need_result_location)
- {
- const Js::RegSlot result_location = funcInfo->AcquireLoc(pnode);
- EmitLoad(pnode1, byteCodeGenerator, funcInfo);
- byteCodeGenerator->Writer()->Reg2(op, result_location, pnode1->location);
- // Store the incremented value and release the l-value.
- EmitAssignment(nullptr, pnode1, result_location, byteCodeGenerator, funcInfo);
- }
- else
- {
- EmitLoad(pnode1, byteCodeGenerator, funcInfo);
- byteCodeGenerator->Writer()->Reg2(op, pnode1->location, pnode1->location);
- // Store the incremented value and release the l-value.
- EmitAssignment(nullptr, pnode1, pnode1->location, byteCodeGenerator, funcInfo);
- }
- funcInfo->ReleaseLoad(pnode1);
- byteCodeGenerator->EndStatement(pnode);
- break;
- }
- // PTNODE(knopTypeof , "typeof" ,None ,Uni ,fnopUni)
- case knopTypeof:
- {
- STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
- ParseNode* pnodeOpnd = pnode->AsParseNodeUni()->pnode1;
- switch (pnodeOpnd->nop)
- {
- case knopDot:
- {
- Emit(pnodeOpnd->AsParseNodeBin()->pnode1, byteCodeGenerator, funcInfo, false);
- Js::PropertyId propertyId = pnodeOpnd->AsParseNodeBin()->pnode2->AsParseNodeName()->PropertyIdFromNameNode();
- Assert(pnodeOpnd->AsParseNodeBin()->pnode2->nop == knopName);
- funcInfo->ReleaseLoc(pnodeOpnd->AsParseNodeBin()->pnode1);
- funcInfo->AcquireLoc(pnode);
- byteCodeGenerator->EmitTypeOfFld(funcInfo, propertyId, pnode->location, pnodeOpnd->AsParseNodeBin()->pnode1->location, Js::OpCode::LdFldForTypeOf);
- break;
- }
- case knopIndex:
- {
- EmitBinaryOpnds(pnodeOpnd->AsParseNodeBin()->pnode1, pnodeOpnd->AsParseNodeBin()->pnode2, byteCodeGenerator, funcInfo);
- funcInfo->ReleaseLoc(pnodeOpnd->AsParseNodeBin()->pnode2);
- funcInfo->ReleaseLoc(pnodeOpnd->AsParseNodeBin()->pnode1);
- funcInfo->AcquireLoc(pnode);
- byteCodeGenerator->Writer()->Element(Js::OpCode::TypeofElem, pnode->location, pnodeOpnd->AsParseNodeBin()->pnode1->location, pnodeOpnd->AsParseNodeBin()->pnode2->location);
- break;
- }
- case knopName:
- {
- ParseNodeName * pnodeNameOpnd = pnodeOpnd->AsParseNodeName();
- if (pnodeNameOpnd->IsUserIdentifier())
- {
- funcInfo->AcquireLoc(pnode);
- byteCodeGenerator->EmitPropTypeof(pnode->location, pnodeNameOpnd->sym, pnodeNameOpnd->pid, funcInfo);
- break;
- }
- // Special names should fallthrough to default case
- }
- default:
- Emit(pnodeOpnd, byteCodeGenerator, funcInfo, false);
- funcInfo->ReleaseLoc(pnodeOpnd);
- byteCodeGenerator->Writer()->Reg2(
- Js::OpCode::Typeof, funcInfo->AcquireLoc(pnode), pnodeOpnd->location);
- break;
- }
- ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
- break;
- }
- // PTNODE(knopVoid , "void" ,Void ,Uni ,fnopUni)
- case knopVoid:
- Emit(pnode->AsParseNodeUni()->pnode1, byteCodeGenerator, funcInfo, false);
- funcInfo->ReleaseLoc(pnode->AsParseNodeUni()->pnode1);
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdUndef, funcInfo->AcquireLoc(pnode));
- break;
- // PTNODE(knopArray , "arr cnst" ,None ,Uni ,fnopUni)
- case knopArray:
- STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
- EmitArrayLiteral(pnode, byteCodeGenerator, funcInfo);
- ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
- break;
- // PTNODE(knopObject , "obj cnst" ,None ,Uni ,fnopUni)
- case knopObject:
- STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
- funcInfo->AcquireLoc(pnode);
- EmitObjectInitializers(pnode->AsParseNodeUni()->pnode1, pnode->location, byteCodeGenerator, funcInfo);
- ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
- break;
- // PTNODE(knopComputedName, "[name]" ,None ,Uni ,fnopUni)
- case knopComputedName:
- Emit(pnode->AsParseNodeUni()->pnode1, byteCodeGenerator, funcInfo, false);
- if (pnode->location == Js::Constants::NoRegister)
- {
- // The name is some expression with no home location. We can just re-use the register.
- pnode->location = pnode->AsParseNodeUni()->pnode1->location;
- }
- else if (pnode->location != pnode->AsParseNodeUni()->pnode1->location)
- {
- // The name had to be protected from side-effects of the RHS.
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, pnode->location, pnode->AsParseNodeUni()->pnode1->location);
- }
- break;
- // Binary and Ternary Operators
- case knopAdd:
- EmitAdd(pnode, byteCodeGenerator, funcInfo);
- break;
- case knopSub:
- case knopMul:
- case knopExpo:
- case knopDiv:
- case knopMod:
- case knopOr:
- case knopXor:
- case knopAnd:
- case knopLsh:
- case knopRsh:
- case knopRs2:
- case knopIn:
- EmitBinary(nopToOp[pnode->nop], pnode, byteCodeGenerator, funcInfo);
- break;
- case knopInstOf:
- {
- STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
- EmitBinaryOpnds(pnode->AsParseNodeBin()->pnode1, pnode->AsParseNodeBin()->pnode2, byteCodeGenerator, funcInfo);
- funcInfo->ReleaseLoc(pnode->AsParseNodeBin()->pnode2);
- funcInfo->ReleaseLoc(pnode->AsParseNodeBin()->pnode1);
- funcInfo->AcquireLoc(pnode);
- uint cacheId = funcInfo->NewIsInstInlineCache();
- byteCodeGenerator->Writer()->Reg3C(nopToOp[pnode->nop], pnode->location, pnode->AsParseNodeBin()->pnode1->location,
- pnode->AsParseNodeBin()->pnode2->location, cacheId);
- ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
- }
- break;
- case knopEq:
- case knopEqv:
- case knopNEqv:
- case knopNe:
- case knopLt:
- case knopLe:
- case knopGe:
- case knopGt:
- STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
- EmitBinaryOpnds(pnode->AsParseNodeBin()->pnode1, pnode->AsParseNodeBin()->pnode2, byteCodeGenerator, funcInfo);
- funcInfo->ReleaseLoc(pnode->AsParseNodeBin()->pnode2);
- funcInfo->ReleaseLoc(pnode->AsParseNodeBin()->pnode1);
- funcInfo->AcquireLoc(pnode);
- byteCodeGenerator->Writer()->Reg3(nopToCMOp[pnode->nop], pnode->location, pnode->AsParseNodeBin()->pnode1->location,
- pnode->AsParseNodeBin()->pnode2->location);
- ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
- break;
- case knopNew:
- {
- EmitNew(pnode, byteCodeGenerator, funcInfo);
- byteCodeGenerator->EndStatement(pnode);
- break;
- }
- case knopDelete:
- {
- ParseNode *pexpr = pnode->AsParseNodeUni()->pnode1;
- byteCodeGenerator->StartStatement(pnode);
- switch (pexpr->nop)
- {
- case knopName:
- {
- ParseNodeName * pnodeName = pexpr->AsParseNodeName();
- if (pnodeName->IsSpecialName())
- {
- funcInfo->AcquireLoc(pnode);
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdTrue, pnode->location);
- }
- else
- {
- funcInfo->AcquireLoc(pnode);
- byteCodeGenerator->EmitPropDelete(pnode->location, pnodeName->sym, pnodeName->pid, funcInfo);
- }
- break;
- }
- case knopDot:
- {
- if (ByteCodeGenerator::IsSuper(pexpr->AsParseNodeBin()->pnode1))
- {
- byteCodeGenerator->Writer()->W1(Js::OpCode::RuntimeReferenceError, SCODE_CODE(JSERR_DeletePropertyWithSuper));
- funcInfo->AcquireLoc(pnode);
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdUndef, pnode->location);
- }
- else
- {
- Emit(pexpr->AsParseNodeBin()->pnode1, byteCodeGenerator, funcInfo, false);
- funcInfo->ReleaseLoc(pexpr->AsParseNodeBin()->pnode1);
- Js::PropertyId propertyId = pexpr->AsParseNodeBin()->pnode2->AsParseNodeName()->PropertyIdFromNameNode();
- funcInfo->AcquireLoc(pnode);
- byteCodeGenerator->Writer()->Property(Js::OpCode::DeleteFld, pnode->location, pexpr->AsParseNodeBin()->pnode1->location,
- funcInfo->FindOrAddReferencedPropertyId(propertyId), byteCodeGenerator->forceStrictModeForClassComputedPropertyName);
- }
- break;
- }
- case knopIndex:
- {
- EmitBinaryOpnds(pexpr->AsParseNodeBin()->pnode1, pexpr->AsParseNodeBin()->pnode2, byteCodeGenerator, funcInfo);
- funcInfo->ReleaseLoc(pexpr->AsParseNodeBin()->pnode2);
- funcInfo->ReleaseLoc(pexpr->AsParseNodeBin()->pnode1);
- funcInfo->AcquireLoc(pnode);
- byteCodeGenerator->Writer()->Element(Js::OpCode::DeleteElemI_A, pnode->location, pexpr->AsParseNodeBin()->pnode1->location, pexpr->AsParseNodeBin()->pnode2->location);
- break;
- }
- default:
- {
- Emit(pexpr, byteCodeGenerator, funcInfo, false);
- funcInfo->ReleaseLoc(pexpr);
- byteCodeGenerator->Writer()->Reg2(
- Js::OpCode::Delete_A, funcInfo->AcquireLoc(pnode), pexpr->location);
- break;
- }
- }
- byteCodeGenerator->EndStatement(pnode);
- break;
- }
- case knopCall:
- {
- ParseNodeCall * pnodeCall = pnode->AsParseNodeCall();
- byteCodeGenerator->StartStatement(pnodeCall);
- if (pnodeCall->isSuperCall)
- {
- byteCodeGenerator->EmitSuperCall(funcInfo, pnodeCall->AsParseNodeSuperCall(), fReturnValue);
- }
- else if (pnodeCall->pnodeTarget->nop == knopImport)
- {
- ParseNodePtr args = pnodeCall->pnodeArgs;
- Assert(CountArguments(args) == 2); // import() takes one argument
- Emit(args, byteCodeGenerator, funcInfo, false);
- funcInfo->ReleaseLoc(args);
- funcInfo->AcquireLoc(pnodeCall);
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::ImportCall, pnodeCall->location, args->location);
- }
- else
- {
- if (pnodeCall->isApplyCall && funcInfo->GetApplyEnclosesArgs())
- {
- // TODO[ianhall]: Can we remove the ApplyCall bytecode gen time optimization?
- EmitApplyCall(pnodeCall, byteCodeGenerator, funcInfo, fReturnValue);
- }
- else
- {
- EmitCall(pnodeCall, byteCodeGenerator, funcInfo, fReturnValue, /*fEvaluateComponents*/ true);
- }
- }
- byteCodeGenerator->EndStatement(pnode);
- break;
- }
- case knopIndex:
- {
- STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
- EmitBinaryOpnds(pnode->AsParseNodeBin()->pnode1, pnode->AsParseNodeBin()->pnode2, byteCodeGenerator, funcInfo);
- Js::RegSlot callObjLocation = pnode->AsParseNodeBin()->pnode1->location;
- Js::RegSlot protoLocation = callObjLocation;
- if (ByteCodeGenerator::IsSuper(pnode->AsParseNodeBin()->pnode1))
- {
- Emit(pnode->AsParseNodeSuperReference()->pnodeThis, byteCodeGenerator, funcInfo, false);
- protoLocation = byteCodeGenerator->EmitLdObjProto(Js::OpCode::LdHomeObjProto, callObjLocation, funcInfo);
- funcInfo->ReleaseLoc(pnode->AsParseNodeSuperReference()->pnodeThis);
- }
- funcInfo->ReleaseLoc(pnode->AsParseNodeBin()->pnode2);
- funcInfo->ReleaseLoc(pnode->AsParseNodeBin()->pnode1);
- funcInfo->AcquireLoc(pnode);
- byteCodeGenerator->Writer()->Element(
- Js::OpCode::LdElemI_A, pnode->location, protoLocation, pnode->AsParseNodeBin()->pnode2->location);
- ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
- break;
- }
- // this is MemberExpression as rvalue
- case knopDot:
- {
- Emit(pnode->AsParseNodeBin()->pnode1, byteCodeGenerator, funcInfo, false);
- funcInfo->ReleaseLoc(pnode->AsParseNodeBin()->pnode1);
- funcInfo->AcquireLoc(pnode);
- Js::PropertyId propertyId = pnode->AsParseNodeBin()->pnode2->AsParseNodeName()->PropertyIdFromNameNode();
- Js::RegSlot callObjLocation = pnode->AsParseNodeBin()->pnode1->location;
- Js::RegSlot protoLocation = callObjLocation;
- if (propertyId == Js::PropertyIds::length)
- {
- uint cacheId = funcInfo->FindOrAddInlineCacheId(protoLocation, propertyId, false, false);
- byteCodeGenerator->Writer()->PatchableProperty(Js::OpCode::LdLen_A, pnode->location, protoLocation, cacheId);
- }
- else if (pnode->IsCallApplyTargetLoad())
- {
- if (ByteCodeGenerator::IsSuper(pnode->AsParseNodeBin()->pnode1))
- {
- Emit(pnode->AsParseNodeSuperReference()->pnodeThis, byteCodeGenerator, funcInfo, false);
- protoLocation = byteCodeGenerator->EmitLdObjProto(Js::OpCode::LdHomeObjProto, callObjLocation, funcInfo);
- funcInfo->ReleaseLoc(pnode->AsParseNodeSuperReference()->pnodeThis);
- }
- uint cacheId = funcInfo->FindOrAddInlineCacheId(protoLocation, propertyId, false, false);
- byteCodeGenerator->Writer()->PatchableProperty(Js::OpCode::LdFldForCallApplyTarget, pnode->location, protoLocation, cacheId);
- }
- else
- {
- if (ByteCodeGenerator::IsSuper(pnode->AsParseNodeBin()->pnode1))
- {
- Emit(pnode->AsParseNodeSuperReference()->pnodeThis, byteCodeGenerator, funcInfo, false);
- protoLocation = byteCodeGenerator->EmitLdObjProto(Js::OpCode::LdHomeObjProto, callObjLocation, funcInfo);
- funcInfo->ReleaseLoc(pnode->AsParseNodeSuperReference()->pnodeThis);
- uint cacheId = funcInfo->FindOrAddInlineCacheId(protoLocation, propertyId, false, false);
- byteCodeGenerator->Writer()->PatchablePropertyWithThisPtr(Js::OpCode::LdSuperFld, pnode->location, protoLocation, pnode->AsParseNodeSuperReference()->pnodeThis->location, cacheId, isConstructorCall);
- }
- else
- {
- uint cacheId = funcInfo->FindOrAddInlineCacheId(callObjLocation, propertyId, false, false);
- byteCodeGenerator->Writer()->PatchableProperty(Js::OpCode::LdFld, pnode->location, callObjLocation, cacheId, isConstructorCall);
- }
- }
- break;
- }
- // PTNODE(knopAsg , "=" ,None ,Bin ,fnopBin|fnopAsg)
- case knopAsg:
- {
- ParseNode *lhs = pnode->AsParseNodeBin()->pnode1;
- ParseNode *rhs = pnode->AsParseNodeBin()->pnode2;
- byteCodeGenerator->StartStatement(pnode);
- if (pnode->isUsed || fReturnValue)
- {
- // If the assignment result is used, grab a register to hold it and pass it to EmitAssignment,
- // which will copy the assigned value there.
- funcInfo->AcquireLoc(pnode);
- EmitBinaryReference(lhs, rhs, byteCodeGenerator, funcInfo, false);
- EmitAssignment(pnode, lhs, rhs->location, byteCodeGenerator, funcInfo);
- }
- else
- {
- EmitBinaryReference(lhs, rhs, byteCodeGenerator, funcInfo, false);
- EmitAssignment(nullptr, lhs, rhs->location, byteCodeGenerator, funcInfo);
- }
- funcInfo->ReleaseLoc(rhs);
- if (!(byteCodeGenerator->IsES6DestructuringEnabled() && (lhs->IsPattern())))
- {
- funcInfo->ReleaseReference(lhs);
- }
- byteCodeGenerator->EndStatement(pnode);
- break;
- }
- case knopName:
- funcInfo->AcquireLoc(pnode);
- if (ByteCodeGenerator::IsThis(pnode))
- {
- byteCodeGenerator->EmitPropLoadThis(pnode->location, pnode->AsParseNodeSpecialName(), funcInfo, true);
- }
- else
- {
- byteCodeGenerator->EmitPropLoad(pnode->location, pnode->AsParseNodeName()->sym, pnode->AsParseNodeName()->pid, funcInfo);
- }
- break;
- case knopComma:
- {
- STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
- // The parser marks binary opnd pnodes as used, but value of the first opnd of a comma is not used.
- // Easier to correct this here than to check every binary op in the parser.
- ParseNode *pnode1 = pnode->AsParseNodeBin()->pnode1;
- pnode1->isUsed = false;
- if (pnode1->nop == knopComma)
- {
- // Spot fix for giant comma expressions that send us into OOS if we use a simple recursive
- // algorithm. Instead of recursing on comma LHS's, iterate over them, pushing the RHS's onto
- // a stack. (This suggests a model for removing recursion from Emit altogether...)
- ArenaAllocator *alloc = byteCodeGenerator->GetAllocator();
- SList<ParseNode *> rhsStack(alloc);
- do
- {
- rhsStack.Push(pnode1->AsParseNodeBin()->pnode2);
- pnode1 = pnode1->AsParseNodeBin()->pnode1;
- pnode1->isUsed = false;
- } while (pnode1->nop == knopComma);
- Emit(pnode1, byteCodeGenerator, funcInfo, false);
- if (funcInfo->IsTmpReg(pnode1->location))
- {
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::Unused, pnode1->location);
- }
- while (!rhsStack.Empty())
- {
- ParseNode *pnodeRhs = rhsStack.Pop();
- pnodeRhs->isUsed = false;
- Emit(pnodeRhs, byteCodeGenerator, funcInfo, false);
- if (funcInfo->IsTmpReg(pnodeRhs->location))
- {
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::Unused, pnodeRhs->location);
- }
- funcInfo->ReleaseLoc(pnodeRhs);
- }
- }
- else
- {
- Emit(pnode1, byteCodeGenerator, funcInfo, false);
- if (funcInfo->IsTmpReg(pnode1->location))
- {
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::Unused, pnode1->location);
- }
- }
- funcInfo->ReleaseLoc(pnode1);
- pnode->AsParseNodeBin()->pnode2->isUsed = pnode->isUsed || fReturnValue;
- Emit(pnode->AsParseNodeBin()->pnode2, byteCodeGenerator, funcInfo, false);
- funcInfo->ReleaseLoc(pnode->AsParseNodeBin()->pnode2);
- funcInfo->AcquireLoc(pnode);
- if (pnode->AsParseNodeBin()->pnode2->isUsed && pnode->location != pnode->AsParseNodeBin()->pnode2->location)
- {
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, pnode->location, pnode->AsParseNodeBin()->pnode2->location);
- }
- ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
- }
- break;
- // The binary logical ops && and || resolve to the value of the left-hand expression if its
- // boolean value short-circuits the operation, and to the value of the right-hand expression
- // otherwise. (In other words, the "truth" of the right-hand expression is never tested.)
- // PTNODE(knopLogOr , "||" ,None ,Bin ,fnopBin)
- case knopLogOr:
- {
- STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
- Js::ByteCodeLabel doneLabel = byteCodeGenerator->Writer()->DefineLabel();
- // We use a single dest here for the whole generating boolean expr, because we were poorly
- // optimizing the previous version where we had a dest for each level
- funcInfo->AcquireLoc(pnode);
- EmitGeneratingBooleanExpression(pnode, doneLabel, true, doneLabel, true, pnode->location, byteCodeGenerator, funcInfo);
- byteCodeGenerator->Writer()->MarkLabel(doneLabel);
- ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
- break;
- }
- // PTNODE(knopLogAnd , "&&" ,None ,Bin ,fnopBin)
- case knopLogAnd:
- {
- STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
- Js::ByteCodeLabel doneLabel = byteCodeGenerator->Writer()->DefineLabel();
- // We use a single dest here for the whole generating boolean expr, because we were poorly
- // optimizing the previous version where we had a dest for each level
- funcInfo->AcquireLoc(pnode);
- EmitGeneratingBooleanExpression(pnode, doneLabel, true, doneLabel, true, pnode->location, byteCodeGenerator, funcInfo);
- byteCodeGenerator->Writer()->MarkLabel(doneLabel);
- ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
- break;
- }
- // PTNODE(knopQmark , "?" ,None ,Tri ,fnopBin)
- case knopQmark:
- {
- Js::ByteCodeLabel trueLabel = byteCodeGenerator->Writer()->DefineLabel();
- Js::ByteCodeLabel falseLabel = byteCodeGenerator->Writer()->DefineLabel();
- Js::ByteCodeLabel skipLabel = byteCodeGenerator->Writer()->DefineLabel();
- EmitBooleanExpression(pnode->AsParseNodeTri()->pnode1, trueLabel, falseLabel, byteCodeGenerator, funcInfo, true, false);
- byteCodeGenerator->Writer()->MarkLabel(trueLabel);
- funcInfo->ReleaseLoc(pnode->AsParseNodeTri()->pnode1);
- // For boolean expressions that compute a result, we have to burn a register for the result
- // so that the back end can identify it cheaply as a single temp lifetime. Revisit this if we do
- // full-on renaming in the back end.
- funcInfo->AcquireLoc(pnode);
- Emit(pnode->AsParseNodeTri()->pnode2, byteCodeGenerator, funcInfo, false);
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, pnode->location, pnode->AsParseNodeTri()->pnode2->location);
- funcInfo->ReleaseLoc(pnode->AsParseNodeTri()->pnode2);
- // Record the branch bytecode offset
- byteCodeGenerator->Writer()->RecordStatementAdjustment(Js::FunctionBody::SAT_FromCurrentToNext);
- byteCodeGenerator->Writer()->Br(skipLabel);
- byteCodeGenerator->Writer()->MarkLabel(falseLabel);
- Emit(pnode->AsParseNodeTri()->pnode3, byteCodeGenerator, funcInfo, false);
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, pnode->location, pnode->AsParseNodeTri()->pnode3->location);
- funcInfo->ReleaseLoc(pnode->AsParseNodeTri()->pnode3);
- byteCodeGenerator->Writer()->MarkLabel(skipLabel);
- break;
- }
- case knopAsgAdd:
- case knopAsgSub:
- case knopAsgMul:
- case knopAsgDiv:
- case knopAsgExpo:
- case knopAsgMod:
- case knopAsgAnd:
- case knopAsgXor:
- case knopAsgOr:
- case knopAsgLsh:
- case knopAsgRsh:
- case knopAsgRs2:
- {
- byteCodeGenerator->StartStatement(pnode);
- ParseNode *lhs = pnode->AsParseNodeBin()->pnode1;
- ParseNode *rhs = pnode->AsParseNodeBin()->pnode2;
- // Assign a register for the result only if the result is used or the LHS can't be assigned to
- // (i.e., is a constant).
- const bool need_result_location =
- pnode->isUsed
- || fReturnValue
- || funcInfo->RegIsConst(lhs->location)
- || (lhs->nop == knopName && lhs->AsParseNodeName()->sym && lhs->AsParseNodeName()->sym->GetIsFuncExpr());
- if (need_result_location)
- {
- const Js::RegSlot result_location = funcInfo->AcquireLoc(pnode);
- // Grab a register for the initial value and load it.
- EmitBinaryReference(lhs, rhs, byteCodeGenerator, funcInfo, true);
- funcInfo->ReleaseLoc(rhs);
- // Do the arithmetic, store the result, and release the l-value.
- byteCodeGenerator->Writer()->Reg3(nopToOp[pnode->nop], result_location, lhs->location, rhs->location);
- EmitAssignment(pnode, lhs, result_location, byteCodeGenerator, funcInfo);
- }
- else
- {
- // Grab a register for the initial value and load it. Might modify lhs->location.
- EmitBinaryReference(lhs, rhs, byteCodeGenerator, funcInfo, true);
- funcInfo->ReleaseLoc(rhs);
- // Do the arithmetic, store the result, and release the l-value.
- byteCodeGenerator->Writer()->Reg3(nopToOp[pnode->nop], lhs->location, lhs->location, rhs->location);
- EmitAssignment(nullptr, lhs, lhs->location, byteCodeGenerator, funcInfo);
- }
- funcInfo->ReleaseLoad(lhs);
- byteCodeGenerator->EndStatement(pnode);
- break;
- }
- // General nodes.
- // PTNODE(knopTempRef , "temp ref" ,None ,Uni ,fnopUni)
- case knopTempRef:
- // TODO: check whether mov is necessary
- funcInfo->AcquireLoc(pnode);
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, pnode->location, pnode->AsParseNodeUni()->pnode1->location);
- break;
- // PTNODE(knopTemp , "temp" ,None ,None ,fnopLeaf)
- case knopTemp:
- // Emit initialization code
- if (pnode->AsParseNodeVar()->pnodeInit != nullptr)
- {
- byteCodeGenerator->StartStatement(pnode);
- Emit(pnode->AsParseNodeVar()->pnodeInit, byteCodeGenerator, funcInfo, false);
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, pnode->location, pnode->AsParseNodeVar()->pnodeInit->location);
- funcInfo->ReleaseLoc(pnode->AsParseNodeVar()->pnodeInit);
- byteCodeGenerator->EndStatement(pnode);
- }
- break;
- // PTNODE(knopVarDecl , "varDcl" ,None ,Var ,fnopNone)
- case knopVarDecl:
- case knopConstDecl:
- case knopLetDecl:
- {
- // Emit initialization code
- ParseNodePtr initNode = pnode->AsParseNodeVar()->pnodeInit;
- AssertMsg(pnode->nop != knopConstDecl || initNode != nullptr, "knopConstDecl expected to have an initializer");
- if (initNode != nullptr || pnode->nop == knopLetDecl)
- {
- Symbol *sym = pnode->AsParseNodeVar()->sym;
- Js::RegSlot rhsLocation;
- byteCodeGenerator->StartStatement(pnode);
- if (initNode != nullptr)
- {
- Emit(initNode, byteCodeGenerator, funcInfo, false);
- rhsLocation = initNode->location;
- if (initNode->nop == knopObject)
- {
- TrackMemberNodesInObjectForIntConstants(byteCodeGenerator, initNode);
- }
- else if (initNode->nop == knopInt)
- {
- TrackIntConstantsOnGlobalObject(byteCodeGenerator, sym);
- }
- }
- else
- {
- Assert(pnode->nop == knopLetDecl);
- rhsLocation = funcInfo->AcquireTmpRegister();
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdUndef, rhsLocation);
- }
- if (pnode->nop != knopVarDecl)
- {
- Assert(sym->GetDecl() == pnode || (sym->IsArguments() && !funcInfo->GetHasArguments()));
- sym->SetNeedDeclaration(false);
- }
- EmitAssignment(nullptr, pnode, rhsLocation, byteCodeGenerator, funcInfo);
- funcInfo->ReleaseTmpRegister(rhsLocation);
- byteCodeGenerator->EndStatement(pnode);
- }
- break;
- }
- // PTNODE(knopFncDecl , "fncDcl" ,None ,Fnc ,fnopLeaf)
- case knopFncDecl:
- // The "function declarations" were emitted in DefineFunctions()
- if (!pnode->AsParseNodeFnc()->IsDeclaration())
- {
- byteCodeGenerator->DefineOneFunction(pnode->AsParseNodeFnc(), funcInfo, false);
- }
- break;
- // PTNODE(knopClassDecl, "class" ,None ,None ,fnopLeaf)
- case knopClassDecl:
- {
- ParseNodeClass * pnodeClass = pnode->AsParseNodeClass();
- funcInfo->AcquireLoc(pnodeClass);
- Assert(pnodeClass->pnodeConstructor);
- pnodeClass->pnodeConstructor->location = pnodeClass->location;
- BeginEmitBlock(pnodeClass->pnodeBlock, byteCodeGenerator, funcInfo);
- // Extends
- if (pnodeClass->pnodeExtends)
- {
- // We can't do StartStatement/EndStatement for pnodeExtends here because the load locations may differ between
- // defer and nondefer parse modes.
- Emit(pnodeClass->pnodeExtends, byteCodeGenerator, funcInfo, false);
- }
- // Constructor
- Emit(pnodeClass->pnodeConstructor, byteCodeGenerator, funcInfo, false);
- EmitComputedFunctionNameVar(bindPnode, pnodeClass->pnodeConstructor, byteCodeGenerator);
- if (pnodeClass->pnodeExtends)
- {
- byteCodeGenerator->StartStatement(pnodeClass->pnodeExtends);
- byteCodeGenerator->Writer()->InitClass(pnodeClass->location, pnodeClass->pnodeExtends->location);
- byteCodeGenerator->EndStatement(pnodeClass->pnodeExtends);
- }
- else
- {
- byteCodeGenerator->Writer()->InitClass(pnodeClass->location);
- }
- Js::RegSlot protoLoc = funcInfo->AcquireTmpRegister(); //register set if we have Instance Methods
- int cacheId = funcInfo->FindOrAddInlineCacheId(pnodeClass->location, Js::PropertyIds::prototype, false, false);
- byteCodeGenerator->Writer()->PatchableProperty(Js::OpCode::LdFld, protoLoc, pnodeClass->location, cacheId);
- // Static Methods
- EmitClassInitializers(pnodeClass->pnodeStaticMembers, pnodeClass->location, byteCodeGenerator, funcInfo, pnode, /*isObjectEmpty*/ false);
- // Instance Methods
- EmitClassInitializers(pnodeClass->pnodeMembers, protoLoc, byteCodeGenerator, funcInfo, pnode, /*isObjectEmpty*/ true);
- funcInfo->ReleaseTmpRegister(protoLoc);
- // Emit name binding.
- if (pnodeClass->pnodeName)
- {
- Symbol * sym = pnodeClass->pnodeName->sym;
- sym->SetNeedDeclaration(false);
- byteCodeGenerator->EmitPropStore(pnodeClass->location, sym, nullptr, funcInfo, false, true);
- }
- EndEmitBlock(pnodeClass->pnodeBlock, byteCodeGenerator, funcInfo);
- if (pnodeClass->pnodeExtends)
- {
- funcInfo->ReleaseLoc(pnodeClass->pnodeExtends);
- }
- if (pnodeClass->pnodeDeclName)
- {
- Symbol * sym = pnodeClass->pnodeDeclName->sym;
- sym->SetNeedDeclaration(false);
- byteCodeGenerator->EmitPropStore(pnodeClass->location, sym, nullptr, funcInfo, true, false);
- }
- if (pnodeClass->IsDefaultModuleExport())
- {
- byteCodeGenerator->EmitAssignmentToDefaultModuleExport(pnodeClass, funcInfo);
- }
- break;
- }
- case knopStrTemplate:
- STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
- EmitStringTemplate(pnode->AsParseNodeStrTemplate(), byteCodeGenerator, funcInfo);
- ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
- break;
- case knopEndCode:
- byteCodeGenerator->Writer()->RecordStatementAdjustment(Js::FunctionBody::SAT_All);
- // load undefined for the fallthrough case:
- if (!funcInfo->IsGlobalFunction())
- {
- if (funcInfo->IsClassConstructor())
- {
- // For class constructors, we need to explicitly load 'this' into the return register.
- byteCodeGenerator->EmitClassConstructorEndCode(funcInfo);
- }
- else
- {
- // In the global function, implicit return values are copied to the return register, and if
- // necessary the return register is initialized at the top. Don't clobber the value here.
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdUndef, ByteCodeGenerator::ReturnRegister);
- }
- }
- // Label for non-fall-through return
- byteCodeGenerator->Writer()->MarkLabel(funcInfo->singleExit);
- if (funcInfo->GetHasCachedScope())
- {
- byteCodeGenerator->Writer()->Empty(Js::OpCode::CommitScope);
- }
- byteCodeGenerator->StartStatement(pnode);
- byteCodeGenerator->Writer()->Empty(Js::OpCode::Ret);
- byteCodeGenerator->EndStatement(pnode);
- break;
- // PTNODE(knopDebugger , "debugger" ,None ,None ,fnopNone)
- case knopDebugger:
- byteCodeGenerator->StartStatement(pnode);
- byteCodeGenerator->Writer()->Empty(Js::OpCode::Break);
- byteCodeGenerator->EndStatement(pnode);
- break;
- // PTNODE(knopFor , "for" ,None ,For ,fnopBreak|fnopContinue)
- case knopFor:
- {
- ParseNodeFor * pnodeFor = pnode->AsParseNodeFor();
- if (pnodeFor->pnodeInverted != nullptr)
- {
- byteCodeGenerator->EmitInvertedLoop(pnodeFor, pnodeFor->pnodeInverted, funcInfo);
- }
- else
- {
- BeginEmitBlock(pnodeFor->pnodeBlock, byteCodeGenerator, funcInfo);
- Emit(pnodeFor->pnodeInit, byteCodeGenerator, funcInfo, false);
- funcInfo->ReleaseLoc(pnodeFor->pnodeInit);
- if (byteCodeGenerator->IsES6ForLoopSemanticsEnabled())
- {
- CloneEmitBlock(pnodeFor->pnodeBlock, byteCodeGenerator, funcInfo);
- }
- EmitLoop(pnodeFor,
- pnodeFor->pnodeCond,
- pnodeFor->pnodeBody,
- pnodeFor->pnodeIncr,
- byteCodeGenerator,
- funcInfo,
- fReturnValue,
- FALSE,
- pnodeFor->pnodeBlock);
- EndEmitBlock(pnodeFor->pnodeBlock, byteCodeGenerator, funcInfo);
- }
- break;
- }
- // PTNODE(knopIf , "if" ,None ,If ,fnopNone)
- case knopIf:
- {
- ParseNodeIf * pnodeIf = pnode->AsParseNodeIf();
- byteCodeGenerator->StartStatement(pnodeIf);
- Js::ByteCodeLabel trueLabel = byteCodeGenerator->Writer()->DefineLabel();
- Js::ByteCodeLabel falseLabel = byteCodeGenerator->Writer()->DefineLabel();
- EmitBooleanExpression(pnodeIf->pnodeCond, trueLabel, falseLabel, byteCodeGenerator, funcInfo, true, false);
- funcInfo->ReleaseLoc(pnodeIf->pnodeCond);
- byteCodeGenerator->EndStatement(pnodeIf);
- byteCodeGenerator->Writer()->MarkLabel(trueLabel);
- Emit(pnodeIf->pnodeTrue, byteCodeGenerator, funcInfo, fReturnValue);
- funcInfo->ReleaseLoc(pnodeIf->pnodeTrue);
- if (pnodeIf->pnodeFalse != nullptr)
- {
- // has else clause
- Js::ByteCodeLabel skipLabel = byteCodeGenerator->Writer()->DefineLabel();
- // Record the branch bytecode offset
- byteCodeGenerator->Writer()->RecordStatementAdjustment(Js::FunctionBody::SAT_FromCurrentToNext);
- // then clause skips else clause
- byteCodeGenerator->Writer()->Br(skipLabel);
- // generate code for else clause
- byteCodeGenerator->Writer()->MarkLabel(falseLabel);
- Emit(pnodeIf->pnodeFalse, byteCodeGenerator, funcInfo, fReturnValue);
- funcInfo->ReleaseLoc(pnodeIf->pnodeFalse);
- byteCodeGenerator->Writer()->MarkLabel(skipLabel);
- }
- else
- {
- byteCodeGenerator->Writer()->MarkLabel(falseLabel);
- }
- if (pnodeIf->emitLabels)
- {
- byteCodeGenerator->Writer()->MarkLabel(pnodeIf->breakLabel);
- }
- break;
- }
- case knopWhile:
- {
- ParseNodeWhile * pnodeWhile = pnode->AsParseNodeWhile();
- EmitLoop(pnodeWhile,
- pnodeWhile->pnodeCond,
- pnodeWhile->pnodeBody,
- nullptr,
- byteCodeGenerator,
- funcInfo,
- fReturnValue);
- break;
- }
- // PTNODE(knopDoWhile , "do-while" ,None ,While,fnopBreak|fnopContinue)
- case knopDoWhile:
- {
- ParseNodeWhile * pnodeWhile = pnode->AsParseNodeWhile();
- EmitLoop(pnodeWhile,
- pnodeWhile->pnodeCond,
- pnodeWhile->pnodeBody,
- nullptr,
- byteCodeGenerator,
- funcInfo,
- fReturnValue,
- true);
- break;
- }
- // PTNODE(knopForIn , "for in" ,None ,ForIn,fnopBreak|fnopContinue|fnopCleanup)
- case knopForIn:
- EmitForInOrForOf(pnode->AsParseNodeForInOrForOf(), byteCodeGenerator, funcInfo, fReturnValue);
- break;
- case knopForOf:
- EmitForInOrForOf(pnode->AsParseNodeForInOrForOf(), byteCodeGenerator, funcInfo, fReturnValue);
- break;
- // PTNODE(knopReturn , "return" ,None ,Uni ,fnopNone)
- case knopReturn:
- {
- ParseNodeReturn * pnodeReturn = pnode->AsParseNodeReturn();
- byteCodeGenerator->StartStatement(pnodeReturn);
- if (pnodeReturn->pnodeExpr != nullptr)
- {
- if (pnodeReturn->pnodeExpr->location == Js::Constants::NoRegister)
- {
- // No need to burn a register for the return value. If we need a temp, use R0 directly.
- pnodeReturn->pnodeExpr->location = ByteCodeGenerator::ReturnRegister;
- }
- Emit(pnodeReturn->pnodeExpr, byteCodeGenerator, funcInfo, fReturnValue);
- if (pnodeReturn->pnodeExpr->location != ByteCodeGenerator::ReturnRegister)
- {
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, ByteCodeGenerator::ReturnRegister, pnodeReturn->pnodeExpr->location);
- }
- funcInfo->GetParsedFunctionBody()->SetHasNoExplicitReturnValue(false);
- }
- else
- {
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdUndef, ByteCodeGenerator::ReturnRegister);
- }
- if (funcInfo->IsClassConstructor())
- {
- // return expr; // becomes like below:
- //
- // if (IsObject(expr)) {
- // return expr;
- // } else if (IsBaseClassConstructor) {
- // return this;
- // } else if (!IsUndefined(expr)) {
- // throw TypeError;
- // }
- Js::ByteCodeLabel returnExprLabel = byteCodeGenerator->Writer()->DefineLabel();
- byteCodeGenerator->Writer()->BrReg1(Js::OpCode::BrOnObject_A, returnExprLabel, ByteCodeGenerator::ReturnRegister);
- if (funcInfo->IsBaseClassConstructor())
- {
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, ByteCodeGenerator::ReturnRegister, funcInfo->GetThisSymbol()->GetLocation());
- }
- else
- {
- Js::ByteCodeLabel returnThisLabel = byteCodeGenerator->Writer()->DefineLabel();
- byteCodeGenerator->Writer()->BrReg2(Js::OpCode::BrSrEq_A, returnThisLabel, ByteCodeGenerator::ReturnRegister, funcInfo->undefinedConstantRegister);
- byteCodeGenerator->Writer()->W1(Js::OpCode::RuntimeTypeError, SCODE_CODE(JSERR_ClassDerivedConstructorInvalidReturnType));
- byteCodeGenerator->Writer()->MarkLabel(returnThisLabel);
- byteCodeGenerator->EmitClassConstructorEndCode(funcInfo);
- }
- byteCodeGenerator->Writer()->MarkLabel(returnExprLabel);
- }
- if (pnodeReturn->grfnop & fnopCleanup)
- {
- EmitJumpCleanup(pnodeReturn, nullptr, byteCodeGenerator, funcInfo);
- }
- byteCodeGenerator->Writer()->Br(funcInfo->singleExit);
- byteCodeGenerator->EndStatement(pnodeReturn);
- break;
- }
- // PTNODE(knopBlock , "{}" ,None ,Block,fnopNone)
- case knopBlock:
- {
- ParseNodeBlock * pnodeBlock = pnode->AsParseNodeBlock();
- if (pnodeBlock->pnodeStmt != nullptr)
- {
- EmitBlock(pnodeBlock, byteCodeGenerator, funcInfo, fReturnValue);
- if (pnodeBlock->emitLabels)
- {
- byteCodeGenerator->Writer()->MarkLabel(pnodeBlock->breakLabel);
- }
- }
- break;
- }
- // PTNODE(knopWith , "with" ,None ,With ,fnopCleanup)
- case knopWith:
- {
- ParseNodeWith * pnodeWith = pnode->AsParseNodeWith();
- Assert(pnodeWith->pnodeObj != nullptr);
- byteCodeGenerator->StartStatement(pnodeWith);
- // Copy the with object to a temp register (the location assigned to pnode) so that if the with object
- // is overwritten in the body, the lookups are not affected.
- funcInfo->AcquireLoc(pnodeWith);
- Emit(pnodeWith->pnodeObj, byteCodeGenerator, funcInfo, false);
- Js::RegSlot regVal = (byteCodeGenerator->GetScriptContext()->GetConfig()->IsES6UnscopablesEnabled()) ? funcInfo->AcquireTmpRegister() : pnodeWith->location;
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Conv_Obj, regVal, pnodeWith->pnodeObj->location);
- if (byteCodeGenerator->GetScriptContext()->GetConfig()->IsES6UnscopablesEnabled())
- {
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::NewWithObject, pnodeWith->location, regVal);
- }
- byteCodeGenerator->EndStatement(pnodeWith);
- #ifdef PERF_HINT
- if (PHASE_TRACE1(Js::PerfHintPhase))
- {
- WritePerfHint(PerfHints::HasWithBlock, funcInfo->byteCodeFunction->GetFunctionBody(), byteCodeGenerator->Writer()->GetCurrentOffset() - 1);
- }
- #endif
- if (pnodeWith->pnodeBody != nullptr)
- {
- Scope *scope = pnodeWith->scope;
- scope->SetLocation(pnodeWith->location);
- byteCodeGenerator->PushScope(scope);
- Js::DebuggerScope *debuggerScope = byteCodeGenerator->RecordStartScopeObject(pnodeWith, Js::DiagExtraScopesType::DiagWithScope, regVal);
- if (byteCodeGenerator->ShouldTrackDebuggerMetadata())
- {
- byteCodeGenerator->Writer()->AddPropertyToDebuggerScope(debuggerScope, regVal, Js::Constants::NoProperty, /*shouldConsumeRegister*/ true, Js::DebuggerScopePropertyFlags_WithObject);
- }
- Emit(pnodeWith->pnodeBody, byteCodeGenerator, funcInfo, fReturnValue);
- funcInfo->ReleaseLoc(pnodeWith->pnodeBody);
- byteCodeGenerator->PopScope();
- byteCodeGenerator->RecordEndScopeObject(pnodeWith);
- }
- if (pnodeWith->emitLabels)
- {
- byteCodeGenerator->Writer()->MarkLabel(pnodeWith->breakLabel);
- }
- if (byteCodeGenerator->GetScriptContext()->GetConfig()->IsES6UnscopablesEnabled())
- {
- funcInfo->ReleaseTmpRegister(regVal);
- }
- funcInfo->ReleaseLoc(pnodeWith->pnodeObj);
- break;
- }
- // PTNODE(knopBreak , "break" ,None ,Jump ,fnopNone)
- case knopBreak:
- {
- ParseNodeJump * pnodeJump = pnode->AsParseNodeJump();
- Assert(pnodeJump->pnodeTarget->emitLabels);
- byteCodeGenerator->StartStatement(pnodeJump);
- if (pnodeJump->grfnop & fnopCleanup)
- {
- EmitJumpCleanup(pnodeJump, pnodeJump->pnodeTarget, byteCodeGenerator, funcInfo);
- }
- byteCodeGenerator->Writer()->Br(pnodeJump->pnodeTarget->breakLabel);
- if (pnodeJump->emitLabels)
- {
- byteCodeGenerator->Writer()->MarkLabel(pnodeJump->breakLabel);
- }
- byteCodeGenerator->EndStatement(pnodeJump);
- break;
- }
- case knopContinue:
- {
- ParseNodeJump * pnodeJump = pnode->AsParseNodeJump();
- Assert(pnodeJump->pnodeTarget->emitLabels);
- byteCodeGenerator->StartStatement(pnodeJump);
- if (pnodeJump->grfnop & fnopCleanup)
- {
- EmitJumpCleanup(pnodeJump, pnodeJump->pnodeTarget, byteCodeGenerator, funcInfo);
- }
- byteCodeGenerator->Writer()->Br(pnodeJump->pnodeTarget->continueLabel);
- byteCodeGenerator->EndStatement(pnodeJump);
- break;
- }
- // PTNODE(knopContinue , "continue" ,None ,Jump ,fnopNone)
- case knopSwitch:
- {
- ParseNodeSwitch * pnodeSwitch = pnode->AsParseNodeSwitch();
- BOOL fHasDefault = false;
- Assert(pnodeSwitch->pnodeVal != nullptr);
- byteCodeGenerator->StartStatement(pnodeSwitch);
- Emit(pnodeSwitch->pnodeVal, byteCodeGenerator, funcInfo, false);
- Js::RegSlot regVal = funcInfo->AcquireTmpRegister();
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::BeginSwitch, regVal, pnodeSwitch->pnodeVal->location);
- BeginEmitBlock(pnodeSwitch->pnodeBlock, byteCodeGenerator, funcInfo);
- byteCodeGenerator->EndStatement(pnodeSwitch);
- // TODO: if all cases are compile-time constants, emit a switch statement in the byte
- // code so the BE can optimize it.
- ParseNodeCase *pnodeCase;
- for (pnodeCase = pnodeSwitch->pnodeCases; pnodeCase; pnodeCase = pnodeCase->pnodeNext)
- {
- // Jump to the first case body if this one doesn't match. Make sure any side-effects of the case
- // expression take place regardless.
- pnodeCase->labelCase = byteCodeGenerator->Writer()->DefineLabel();
- if (pnodeCase == pnodeSwitch->pnodeDefault)
- {
- fHasDefault = true;
- continue;
- }
- Emit(pnodeCase->pnodeExpr, byteCodeGenerator, funcInfo, false);
- byteCodeGenerator->Writer()->BrReg2(
- Js::OpCode::Case, pnodeCase->labelCase, regVal, pnodeCase->pnodeExpr->location);
- funcInfo->ReleaseLoc(pnodeCase->pnodeExpr);
- }
- // No explicit case value matches. Jump to the default arm (if any) or break out altogether.
- if (fHasDefault)
- {
- byteCodeGenerator->Writer()->Br(Js::OpCode::EndSwitch, pnodeSwitch->pnodeDefault->labelCase);
- }
- else
- {
- if (!pnodeSwitch->emitLabels)
- {
- pnodeSwitch->breakLabel = byteCodeGenerator->Writer()->DefineLabel();
- }
- byteCodeGenerator->Writer()->Br(Js::OpCode::EndSwitch, pnodeSwitch->breakLabel);
- }
- // Now emit the case arms to which we jump on matching a case value.
- for (pnodeCase = pnodeSwitch->pnodeCases; pnodeCase; pnodeCase = pnodeCase->pnodeNext)
- {
- byteCodeGenerator->Writer()->MarkLabel(pnodeCase->labelCase);
- Emit(pnodeCase->pnodeBody, byteCodeGenerator, funcInfo, fReturnValue);
- funcInfo->ReleaseLoc(pnodeCase->pnodeBody);
- }
- EndEmitBlock(pnodeSwitch->pnodeBlock, byteCodeGenerator, funcInfo);
- funcInfo->ReleaseTmpRegister(regVal);
- funcInfo->ReleaseLoc(pnodeSwitch->pnodeVal);
- if (!fHasDefault || pnodeSwitch->emitLabels)
- {
- byteCodeGenerator->Writer()->MarkLabel(pnodeSwitch->breakLabel);
- }
- break;
- }
- case knopTryCatch:
- {
- Js::ByteCodeLabel catchLabel = (Js::ByteCodeLabel) - 1;
- ParseNodeTryCatch * pnodeTryCatch = pnode->AsParseNodeTryCatch();
- ParseNodeTry *pnodeTry = pnodeTryCatch->pnodeTry;
- Assert(pnodeTry);
- ParseNodeCatch *pnodeCatch = pnodeTryCatch->pnodeCatch;
- Assert(pnodeCatch);
- catchLabel = byteCodeGenerator->Writer()->DefineLabel();
- // Note: try uses OpCode::Leave which causes a return to parent interpreter thunk,
- // same for catch block. Thus record cross interpreter frame entry/exit records for them.
- byteCodeGenerator->Writer()->RecordCrossFrameEntryExitRecord(/* isEnterBlock = */ true);
- byteCodeGenerator->Writer()->Br(Js::OpCode::TryCatch, catchLabel);
- ByteCodeGenerator::TryScopeRecord tryRecForTry(Js::OpCode::TryCatch, catchLabel);
- if (funcInfo->byteCodeFunction->IsCoroutine())
- {
- byteCodeGenerator->tryScopeRecordsList.LinkToEnd(&tryRecForTry);
- }
- Emit(pnodeTry->pnodeBody, byteCodeGenerator, funcInfo, fReturnValue);
- funcInfo->ReleaseLoc(pnodeTry->pnodeBody);
- if (funcInfo->byteCodeFunction->IsCoroutine())
- {
- byteCodeGenerator->tryScopeRecordsList.UnlinkFromEnd();
- }
- byteCodeGenerator->Writer()->RecordCrossFrameEntryExitRecord(/* isEnterBlock = */ false);
- byteCodeGenerator->Writer()->Empty(Js::OpCode::Leave);
- byteCodeGenerator->Writer()->Br(pnodeTryCatch->breakLabel);
- byteCodeGenerator->Writer()->MarkLabel(catchLabel);
-
- ParseNode *pnodeObj = pnodeCatch->GetParam();
- Assert(pnodeObj);
- Js::RegSlot location;
- bool acquiredTempLocation = false;
- Js::DebuggerScope *debuggerScope = nullptr;
- Js::DebuggerScopePropertyFlags debuggerPropertyFlags = Js::DebuggerScopePropertyFlags_CatchObject;
- bool isPattern = pnodeObj->nop == knopParamPattern;
- if (isPattern)
- {
- location = pnodeObj->AsParseNodeParamPattern()->location;
- }
- else
- {
- location = pnodeObj->AsParseNodeName()->sym->GetLocation();
- }
- if (location == Js::Constants::NoRegister)
- {
- location = funcInfo->AcquireLoc(pnodeObj);
- acquiredTempLocation = true;
- }
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::Catch, location);
- Scope *scope = pnodeCatch->scope;
- byteCodeGenerator->PushScope(scope);
- if (scope->GetMustInstantiate())
- {
- Assert(scope->GetLocation() == Js::Constants::NoRegister);
- if (scope->GetIsObject())
- {
- debuggerScope = byteCodeGenerator->RecordStartScopeObject(pnodeTryCatch, Js::DiagCatchScopeInObject, funcInfo->InnerScopeToRegSlot(scope));
- byteCodeGenerator->Writer()->Unsigned1(Js::OpCode::NewPseudoScope, scope->GetInnerScopeIndex());
- }
- else
- {
- int index = Js::DebuggerScope::InvalidScopeIndex;
- debuggerScope = byteCodeGenerator->RecordStartScopeObject(pnodeTryCatch, Js::DiagCatchScopeInSlot, funcInfo->InnerScopeToRegSlot(scope), &index);
- byteCodeGenerator->Writer()->Num3(Js::OpCode::NewInnerScopeSlots, scope->GetInnerScopeIndex(), scope->GetScopeSlotCount() + Js::ScopeSlots::FirstSlotIndex, index);
- }
- }
- else
- {
- debuggerScope = byteCodeGenerator->RecordStartScopeObject(pnodeTryCatch, Js::DiagCatchScopeDirect, location);
- }
- auto ParamTrackAndInitialization = [&](Symbol *sym, bool initializeParam, Js::RegSlot location)
- {
- if (sym->IsInSlot(byteCodeGenerator, funcInfo))
- {
- Assert(scope->GetMustInstantiate());
- if (scope->GetIsObject())
- {
- Js::OpCode op = (sym->GetDecl()->nop == knopLetDecl) ? Js::OpCode::InitUndeclLetFld :
- byteCodeGenerator->GetInitFldOp(scope, scope->GetLocation(), funcInfo, false);
- Js::PropertyId propertyId = sym->EnsurePosition(byteCodeGenerator);
- uint cacheId = funcInfo->FindOrAddInlineCacheId(funcInfo->InnerScopeToRegSlot(scope), propertyId, false, true);
- byteCodeGenerator->Writer()->ElementPIndexed(op, location, scope->GetInnerScopeIndex(), cacheId);
- byteCodeGenerator->TrackActivationObjectPropertyForDebugger(debuggerScope, sym, debuggerPropertyFlags);
- }
- else
- {
- byteCodeGenerator->TrackSlotArrayPropertyForDebugger(debuggerScope, sym, sym->EnsurePosition(byteCodeGenerator), debuggerPropertyFlags);
- if (initializeParam)
- {
- byteCodeGenerator->EmitLocalPropInit(location, sym, funcInfo);
- }
- else
- {
- Js::RegSlot tmpReg = funcInfo->AcquireTmpRegister();
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::InitUndecl, tmpReg);
- byteCodeGenerator->EmitLocalPropInit(tmpReg, sym, funcInfo);
- funcInfo->ReleaseTmpRegister(tmpReg);
- }
- }
- }
- else
- {
- byteCodeGenerator->TrackRegisterPropertyForDebugger(debuggerScope, sym, funcInfo, debuggerPropertyFlags);
- if (initializeParam)
- {
- byteCodeGenerator->EmitLocalPropInit(location, sym, funcInfo);
- }
- else
- {
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::InitUndecl, location);
- }
- }
- };
- ByteCodeGenerator::TryScopeRecord tryRecForCatch(Js::OpCode::ResumeCatch, catchLabel);
- if (isPattern)
- {
- Parser::MapBindIdentifier(pnodeObj->AsParseNodeParamPattern()->pnode1, [&](ParseNodePtr item)
- {
- Js::RegSlot itemLocation = item->AsParseNodeVar()->sym->GetLocation();
- if (itemLocation == Js::Constants::NoRegister)
- {
- // The var has no assigned register, meaning it's captured, so we have no reg to write to.
- // Emit the designated return reg in the byte code to avoid asserting on bad register.
- itemLocation = ByteCodeGenerator::ReturnRegister;
- }
- ParamTrackAndInitialization(item->AsParseNodeVar()->sym, false /*initializeParam*/, itemLocation);
- });
- byteCodeGenerator->Writer()->RecordCrossFrameEntryExitRecord(true);
- // Now emitting bytecode for destructuring pattern
- byteCodeGenerator->StartStatement(pnodeCatch);
- ParseNodePtr pnode1 = pnodeObj->AsParseNodeParamPattern()->pnode1;
- Assert(pnode1->IsPattern());
- if (funcInfo->byteCodeFunction->IsCoroutine())
- {
- byteCodeGenerator->tryScopeRecordsList.LinkToEnd(&tryRecForCatch);
- }
- EmitAssignment(nullptr, pnode1, location, byteCodeGenerator, funcInfo);
- byteCodeGenerator->EndStatement(pnodeCatch);
- }
- else
- {
- ParamTrackAndInitialization(pnodeObj->AsParseNodeName()->sym, true /*initializeParam*/, location);
- if (scope->GetMustInstantiate())
- {
- pnodeObj->AsParseNodeName()->sym->SetIsGlobalCatch(true);
- }
- byteCodeGenerator->Writer()->RecordCrossFrameEntryExitRecord(true);
- // Allow a debugger to stop on the 'catch (e)'
- byteCodeGenerator->StartStatement(pnodeCatch);
- byteCodeGenerator->Writer()->Empty(Js::OpCode::Nop);
- byteCodeGenerator->EndStatement(pnodeCatch);
- if (funcInfo->byteCodeFunction->IsCoroutine())
- {
- byteCodeGenerator->tryScopeRecordsList.LinkToEnd(&tryRecForCatch);
- }
- }
- Emit(pnodeCatch->pnodeBody, byteCodeGenerator, funcInfo, fReturnValue);
- if (funcInfo->byteCodeFunction->IsCoroutine())
- {
- byteCodeGenerator->tryScopeRecordsList.UnlinkFromEnd();
- }
- byteCodeGenerator->PopScope();
- byteCodeGenerator->RecordEndScopeObject(pnodeTryCatch);
- funcInfo->ReleaseLoc(pnodeCatch->pnodeBody);
- if (acquiredTempLocation)
- {
- funcInfo->ReleaseLoc(pnodeObj);
- }
- byteCodeGenerator->Writer()->RecordCrossFrameEntryExitRecord(false);
- byteCodeGenerator->Writer()->Empty(Js::OpCode::Leave);
- byteCodeGenerator->Writer()->MarkLabel(pnodeTryCatch->breakLabel);
- break;
- }
- case knopTryFinally:
- {
- Js::ByteCodeLabel finallyLabel = (Js::ByteCodeLabel) - 1;
- ParseNodeTryFinally * pnodeTryFinally = pnode->AsParseNodeTryFinally();
- ParseNodeTry *pnodeTry = pnodeTryFinally->pnodeTry;
- Assert(pnodeTry);
- ParseNodeFinally *pnodeFinally = pnodeTryFinally->pnodeFinally;
- Assert(pnodeFinally);
- // If we yield from the finally block after an exception, we have to store the exception object for the future next call.
- // When we yield from the Try-Finally the offset to the end of the Try block is needed for the branch instruction.
- Js::RegSlot regException = Js::Constants::NoRegister;
- Js::RegSlot regOffset = Js::Constants::NoRegister;
- finallyLabel = byteCodeGenerator->Writer()->DefineLabel();
- byteCodeGenerator->Writer()->RecordCrossFrameEntryExitRecord(true);
- // [CONSIDER][aneeshd] Ideally the TryFinallyWithYield opcode needs to be used only if there is a yield expression.
- // For now, if the function is generator we are using the TryFinallyWithYield.
- ByteCodeGenerator::TryScopeRecord tryRecForTry(Js::OpCode::TryFinallyWithYield, finallyLabel);
- if (funcInfo->byteCodeFunction->IsCoroutine())
- {
- regException = funcInfo->AcquireTmpRegister();
- regOffset = funcInfo->AcquireTmpRegister();
- byteCodeGenerator->Writer()->BrReg2(Js::OpCode::TryFinallyWithYield, finallyLabel, regException, regOffset);
- tryRecForTry.reg1 = regException;
- tryRecForTry.reg2 = regOffset;
- byteCodeGenerator->tryScopeRecordsList.LinkToEnd(&tryRecForTry);
- }
- else
- {
- byteCodeGenerator->Writer()->Br(Js::OpCode::TryFinally, finallyLabel);
- }
- // Increasing the stack as we will be storing the additional values when we enter try..finally.
- funcInfo->StartRecordingOutArgs(1);
- Emit(pnodeTry->pnodeBody, byteCodeGenerator, funcInfo, fReturnValue);
- funcInfo->ReleaseLoc(pnodeTry->pnodeBody);
- if (funcInfo->byteCodeFunction->IsCoroutine())
- {
- byteCodeGenerator->tryScopeRecordsList.UnlinkFromEnd();
- }
- byteCodeGenerator->Writer()->Empty(Js::OpCode::Leave);
- byteCodeGenerator->Writer()->RecordCrossFrameEntryExitRecord(false);
- // Note: although we don't use OpCode::Leave for finally block,
- // OpCode::LeaveNull causes a return to parent interpreter thunk.
- // This has to be on offset prior to offset of 1st statement of finally.
- byteCodeGenerator->Writer()->RecordCrossFrameEntryExitRecord(true);
- byteCodeGenerator->Writer()->Br(pnodeTryFinally->breakLabel);
- byteCodeGenerator->Writer()->MarkLabel(finallyLabel);
- byteCodeGenerator->Writer()->Empty(Js::OpCode::Finally);
- ByteCodeGenerator::TryScopeRecord tryRecForFinally(Js::OpCode::ResumeFinally, finallyLabel, regException, regOffset);
- if (funcInfo->byteCodeFunction->IsCoroutine())
- {
- byteCodeGenerator->tryScopeRecordsList.LinkToEnd(&tryRecForFinally);
- }
- Emit(pnodeFinally->pnodeBody, byteCodeGenerator, funcInfo, fReturnValue);
- funcInfo->ReleaseLoc(pnodeFinally->pnodeBody);
- if (funcInfo->byteCodeFunction->IsCoroutine())
- {
- byteCodeGenerator->tryScopeRecordsList.UnlinkFromEnd();
- funcInfo->ReleaseTmpRegister(regOffset);
- funcInfo->ReleaseTmpRegister(regException);
- }
- funcInfo->EndRecordingOutArgs(1);
- byteCodeGenerator->Writer()->RecordCrossFrameEntryExitRecord(false);
- byteCodeGenerator->Writer()->Empty(Js::OpCode::LeaveNull);
- byteCodeGenerator->Writer()->MarkLabel(pnodeTryFinally->breakLabel);
- break;
- }
- case knopThrow:
- byteCodeGenerator->StartStatement(pnode);
- Emit(pnode->AsParseNodeUni()->pnode1, byteCodeGenerator, funcInfo, false);
- byteCodeGenerator->Writer()->Reg1(Js::OpCode::Throw, pnode->AsParseNodeUni()->pnode1->location);
- funcInfo->ReleaseLoc(pnode->AsParseNodeUni()->pnode1);
- byteCodeGenerator->EndStatement(pnode);
- break;
- case knopYieldLeaf:
- byteCodeGenerator->StartStatement(pnode);
- funcInfo->AcquireLoc(pnode);
- EmitYield(funcInfo->undefinedConstantRegister, pnode->location, byteCodeGenerator, funcInfo);
- byteCodeGenerator->EndStatement(pnode);
- break;
- case knopAwait:
- case knopYield:
- byteCodeGenerator->StartStatement(pnode);
- funcInfo->AcquireLoc(pnode);
- Emit(pnode->AsParseNodeUni()->pnode1, byteCodeGenerator, funcInfo, false);
- EmitYield(pnode->AsParseNodeUni()->pnode1->location, pnode->location, byteCodeGenerator, funcInfo);
- funcInfo->ReleaseLoc(pnode->AsParseNodeUni()->pnode1);
- byteCodeGenerator->EndStatement(pnode);
- break;
- case knopYieldStar:
- byteCodeGenerator->StartStatement(pnode);
- EmitYieldStar(pnode->AsParseNodeUni(), byteCodeGenerator, funcInfo);
- byteCodeGenerator->EndStatement(pnode);
- break;
- case knopExportDefault:
- Emit(pnode->AsParseNodeExportDefault()->pnodeExpr, byteCodeGenerator, funcInfo, false);
- byteCodeGenerator->EmitAssignmentToDefaultModuleExport(pnode->AsParseNodeExportDefault()->pnodeExpr, funcInfo);
- funcInfo->ReleaseLoc(pnode->AsParseNodeExportDefault()->pnodeExpr);
- pnode = pnode->AsParseNodeExportDefault()->pnodeExpr;
- break;
- default:
- AssertMsg(0, "emit unhandled pnode op");
- break;
- }
- if (fReturnValue && IsExpressionStatement(pnode, byteCodeGenerator->GetScriptContext()) && !pnode->IsPatternDeclaration())
- {
- // If this statement may produce the global function's return value, copy its result to the return register.
- // fReturnValue implies global function, which implies that "return" is a parse error.
- Assert(funcInfo->IsGlobalFunction());
- Assert(pnode->nop != knopReturn);
- byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, ByteCodeGenerator::ReturnRegister, pnode->location);
- }
- }
|