| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #include "RuntimeLanguagePch.h"
- #include "Types/PathTypeHandler.h"
- #include "Types/PropertyIndexRanges.h"
- #include "Types/WithScopeObject.h"
- #include "Types/SpreadArgument.h"
- #include "Library/JavascriptPromise.h"
- #include "Library/JavascriptRegularExpression.h"
- #include "Library/ThrowErrorObject.h"
- #include "Library/JavascriptGeneratorFunction.h"
- #include "Library/ForInObjectEnumerator.h"
- #include "Library/ES5Array.h"
- #include "Types/SimpleDictionaryPropertyDescriptor.h"
- #include "Types/SimpleDictionaryTypeHandler.h"
- #include "Language/ModuleNamespace.h"
- #ifndef SCRIPT_DIRECT_TYPE
- typedef enum JsNativeValueType: int
- {
- JsInt8Type,
- JsUint8Type,
- JsInt16Type,
- JsUint16Type,
- JsInt32Type,
- JsUint32Type,
- JsInt64Type,
- JsUint64Type,
- JsFloatType,
- JsDoubleType,
- JsNativeStringType
- } JsNativeValueType;
- typedef struct JsNativeString
- {
- unsigned int length;
- LPCWSTR str;
- } JsNativeString;
- #endif
- using namespace Js;
- DEFINE_RECYCLER_TRACKER_ARRAY_PERF_COUNTER(Var);
- DEFINE_RECYCLER_TRACKER_PERF_COUNTER(FrameDisplay);
- enum IndexType
- {
- IndexType_Number,
- IndexType_PropertyId,
- IndexType_JavascriptString
- };
- IndexType GetIndexTypeFromString(char16 const * propertyName, charcount_t propertyLength, ScriptContext* scriptContext, uint32* index, PropertyRecord const** propertyRecord, bool createIfNotFound)
- {
- if (JavascriptOperators::TryConvertToUInt32(propertyName, propertyLength, index) &&
- (*index != JavascriptArray::InvalidIndex))
- {
- return IndexType_Number;
- }
- else
- {
- if (createIfNotFound)
- {
- scriptContext->GetOrAddPropertyRecord(propertyName, propertyLength, propertyRecord);
- }
- else
- {
- scriptContext->FindPropertyRecord(propertyName, propertyLength, propertyRecord);
- }
- return IndexType_PropertyId;
- }
- }
- IndexType GetIndexTypeFromPrimitive(Var indexVar, ScriptContext* scriptContext, uint32* index, PropertyRecord const ** propertyRecord, JavascriptString ** propertyNameString, bool createIfNotFound, bool preferJavascriptStringOverPropertyRecord)
- {
- // CONSIDER: Only OP_SetElementI and OP_GetElementI use and take advantage of the
- // IndexType_JavascriptString result. Consider modifying other callers of GetIndexType to take
- // advantage of non-interned property strings where appropriate.
- if (TaggedInt::Is(indexVar))
- {
- int indexInt = TaggedInt::ToInt32(indexVar);
- if (indexInt >= 0)
- {
- *index = (uint)indexInt;
- return IndexType_Number;
- }
- else
- {
- char16 stringBuffer[22];
- int pos = TaggedInt::ToBuffer(indexInt, stringBuffer, _countof(stringBuffer));
- charcount_t length = (_countof(stringBuffer) - 1) - pos;
- if (createIfNotFound || preferJavascriptStringOverPropertyRecord)
- {
- // When preferring JavascriptString objects, just return a PropertyRecord instead
- // of creating temporary JavascriptString objects for every negative integer that
- // comes through here.
- scriptContext->GetOrAddPropertyRecord(stringBuffer + pos, length, propertyRecord);
- }
- else
- {
- scriptContext->FindPropertyRecord(stringBuffer + pos, length, propertyRecord);
- }
- return IndexType_PropertyId;
- }
- }
- else
- {
- JavascriptSymbol * symbol = JavascriptOperators::TryFromVar<JavascriptSymbol>(indexVar);
- if (symbol)
- {
- // JavascriptSymbols cannot add a new PropertyRecord - they correspond to one and only one existing PropertyRecord.
- // We already know what the PropertyRecord is since it is stored in the JavascriptSymbol itself so just return it.
- *propertyRecord = symbol->GetValue();
- return IndexType_PropertyId;
- }
- else
- {
- JavascriptString* indexStr = JavascriptConversion::ToString(indexVar, scriptContext);
- char16 const * propertyName = indexStr->GetString();
- charcount_t const propertyLength = indexStr->GetLength();
- if (!createIfNotFound && preferJavascriptStringOverPropertyRecord)
- {
- if (JavascriptOperators::TryConvertToUInt32(propertyName, propertyLength, index) &&
- (*index != JavascriptArray::InvalidIndex))
- {
- return IndexType_Number;
- }
- *propertyNameString = indexStr;
- return IndexType_JavascriptString;
- }
- return GetIndexTypeFromString(propertyName, propertyLength, scriptContext, index, propertyRecord, createIfNotFound);
- }
- }
- }
- IndexType GetIndexTypeFromPrimitive(Var indexVar, ScriptContext* scriptContext, uint32* index, PropertyRecord const ** propertyRecord, bool createIfNotFound)
- {
- return GetIndexTypeFromPrimitive(indexVar, scriptContext, index, propertyRecord, nullptr, createIfNotFound, false);
- }
- IndexType GetIndexType(Var& indexVar, ScriptContext* scriptContext, uint32* index, PropertyRecord const ** propertyRecord, JavascriptString ** propertyNameString, bool createIfNotFound, bool preferJavascriptStringOverPropertyRecord)
- {
- indexVar = JavascriptConversion::ToPrimitive<JavascriptHint::HintString>(indexVar, scriptContext);
- return GetIndexTypeFromPrimitive(indexVar, scriptContext, index, propertyRecord, propertyNameString, createIfNotFound, preferJavascriptStringOverPropertyRecord);
- }
- IndexType GetIndexType(Var& indexVar, ScriptContext* scriptContext, uint32* index, PropertyRecord const ** propertyRecord, bool createIfNotFound)
- {
- return GetIndexType(indexVar, scriptContext, index, propertyRecord, nullptr, createIfNotFound, false);
- }
- BOOL FEqualDbl(double dbl1, double dbl2)
- {
- // If the low ulongs don't match, they can't be equal.
- if (Js::NumberUtilities::LuLoDbl(dbl1) != Js::NumberUtilities::LuLoDbl(dbl2))
- return FALSE;
- // If the high ulongs don't match, they can be equal iff one is -0 and
- // the other is +0.
- if (Js::NumberUtilities::LuHiDbl(dbl1) != Js::NumberUtilities::LuHiDbl(dbl2))
- {
- return 0x80000000 == (Js::NumberUtilities::LuHiDbl(dbl1) | Js::NumberUtilities::LuHiDbl(dbl2)) &&
- 0 == Js::NumberUtilities::LuLoDbl(dbl1);
- }
- // The bit patterns match. They are equal iff they are not Nan.
- return !Js::NumberUtilities::IsNan(dbl1);
- }
- Var JavascriptOperators::OP_ApplyArgs(Var func, Var instance, __in_xcount(8) void** stackPtr, CallInfo callInfo, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_OP_ApplyArgs);
- int argCount = callInfo.Count;
- ///
- /// Check func has internal [[Call]] property
- /// If not, throw TypeError
- ///
- if (!JavascriptConversion::IsCallable(func)) {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedFunction);
- }
- // Fix callInfo: expect result/value, and none of other flags are currently applicable.
- // OP_ApplyArgs expects a result. Neither of {jit, interpreted} mode sends correct callFlags:
- // LdArgCnt -- jit sends whatever was passed to current function, interpreter always sends 0.
- // See Win8 bug 490489.
- callInfo.Flags = CallFlags_Value;
- RecyclableObject *funcPtr = RecyclableObject::UnsafeFromVar(func);
- PROBE_STACK(scriptContext, Js::Constants::MinStackDefault + argCount * 4);
- JavascriptMethod entryPoint = funcPtr->GetEntryPoint();
- Var ret;
- switch (argCount) {
- case 0:
- Assert(false);
- ret = CALL_ENTRYPOINT_NOASSERT(entryPoint, funcPtr, callInfo);
- break;
- case 1:
- ret = CALL_ENTRYPOINT_NOASSERT(entryPoint, funcPtr, callInfo, instance);
- break;
- case 2:
- ret = CALL_ENTRYPOINT_NOASSERT(entryPoint, funcPtr, callInfo, instance, stackPtr[0]);
- break;
- case 3:
- ret = CALL_ENTRYPOINT_NOASSERT(entryPoint, funcPtr, callInfo, instance, stackPtr[0], stackPtr[1]);
- break;
- case 4:
- ret = CALL_ENTRYPOINT_NOASSERT(entryPoint, funcPtr, callInfo, instance, stackPtr[0], stackPtr[1], stackPtr[2]);
- break;
- case 5:
- ret = CALL_ENTRYPOINT_NOASSERT(entryPoint, funcPtr, callInfo, instance, stackPtr[0], stackPtr[1], stackPtr[2], stackPtr[3]);
- break;
- case 6:
- ret = CALL_ENTRYPOINT_NOASSERT(entryPoint, funcPtr, callInfo, instance, stackPtr[0], stackPtr[1], stackPtr[2], stackPtr[3], stackPtr[4]);
- break;
- case 7:
- ret = CALL_ENTRYPOINT_NOASSERT(entryPoint, funcPtr, callInfo, instance, stackPtr[0], stackPtr[1], stackPtr[2], stackPtr[3], stackPtr[4], stackPtr[5]);
- break;
- default:
- {
- // Don't need stack probe here- we just did so above
- Arguments args(callInfo, stackPtr - 1);
- BEGIN_SAFE_REENTRANT_CALL(scriptContext->GetThreadContext())
- {
- ret = JavascriptFunction::CallFunction<false>(funcPtr, entryPoint, args);
- }
- END_SAFE_REENTRANT_CALL
- break;
- }
- }
- return ret;
- JIT_HELPER_END(Op_OP_ApplyArgs);
- }
- #ifdef _M_IX86
- // Alias for overloaded JavascriptNumber::ToVar so it can be called unambiguously from native code
- Var JavascriptOperators::Int32ToVar(int32 value, ScriptContext* scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(Op_Int32ToAtom);
- return JavascriptNumber::ToVar(value, scriptContext);
- JIT_HELPER_END(Op_Int32ToAtom);
- }
- // Alias for overloaded JavascriptNumber::ToVar so it can be called unambiguously from native code
- Var JavascriptOperators::Int32ToVarInPlace(int32 value, ScriptContext* scriptContext, JavascriptNumber* result)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(Op_Int32ToAtomInPlace);
- return JavascriptNumber::ToVarInPlace(value, scriptContext, result);
- JIT_HELPER_END(Op_Int32ToAtomInPlace);
- }
- // Alias for overloaded JavascriptNumber::ToVar so it can be called unambiguously from native code
- Var JavascriptOperators::UInt32ToVar(uint32 value, ScriptContext* scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(Op_UInt32ToAtom);
- return JavascriptNumber::ToVar(value, scriptContext);
- JIT_HELPER_END(Op_UInt32ToAtom);
- }
- // Alias for overloaded JavascriptNumber::ToVar so it can be called unambiguously from native code
- Var JavascriptOperators::UInt32ToVarInPlace(uint32 value, ScriptContext* scriptContext, JavascriptNumber* result)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(Op_UInt32ToAtomInPlace);
- return JavascriptNumber::ToVarInPlace(value, scriptContext, result);
- JIT_HELPER_END(Op_UInt32ToAtomInPlace);
- }
- #endif
- Var JavascriptOperators::OP_FinishOddDivBy2(uint32 value, ScriptContext *scriptContext)
- {
- return JavascriptNumber::New((double)(value + 0.5), scriptContext);
- }
- Var JavascriptOperators::ToNumberInPlace(Var aRight, ScriptContext* scriptContext, JavascriptNumber* result)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_ConvNumberInPlace);
- if (TaggedInt::Is(aRight) || JavascriptNumber::Is_NoTaggedIntCheck(aRight))
- {
- return aRight;
- }
- return JavascriptNumber::ToVarInPlace(JavascriptConversion::ToNumber(aRight, scriptContext), scriptContext, result);
- JIT_HELPER_END(Op_ConvNumberInPlace);
- }
- Var JavascriptOperators::Typeof(Var var, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_Typeof);
- switch (JavascriptOperators::GetTypeId(var))
- {
- case TypeIds_Undefined:
- return scriptContext->GetLibrary()->GetUndefinedDisplayString();
- case TypeIds_Null:
- //null
- return scriptContext->GetLibrary()->GetObjectTypeDisplayString();
- case TypeIds_Integer:
- case TypeIds_Number:
- case TypeIds_Int64Number:
- case TypeIds_UInt64Number:
- return scriptContext->GetLibrary()->GetNumberTypeDisplayString();
- default:
- // Falsy objects are typeof 'undefined'.
- if (RecyclableObject::FromVar(var)->GetType()->IsFalsy())
- {
- return scriptContext->GetLibrary()->GetUndefinedDisplayString();
- }
- else
- {
- return RecyclableObject::FromVar(var)->GetTypeOfString(scriptContext);
- }
- }
- JIT_HELPER_END(Op_Typeof);
- }
- Var JavascriptOperators::TypeofFld(Var instance, PropertyId propertyId, ScriptContext* scriptContext)
- {
- return TypeofFld_Internal(instance, false, propertyId, scriptContext);
- }
- Var JavascriptOperators::TypeofRootFld(Var instance, PropertyId propertyId, ScriptContext* scriptContext)
- {
- return TypeofFld_Internal(instance, true, propertyId, scriptContext);
- }
- Var JavascriptOperators::TypeofFld_Internal(Var instance, const bool isRoot, PropertyId propertyId, ScriptContext* scriptContext)
- {
- RecyclableObject* object = nullptr;
- if (FALSE == JavascriptOperators::GetPropertyObject(instance, scriptContext, &object))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_CannotGet_NullOrUndefined , scriptContext->GetPropertyName(propertyId)->GetBuffer());
- }
- Var value = nullptr;
- try
- {
- Js::JavascriptExceptionOperators::AutoCatchHandlerExists autoCatchHandlerExists(scriptContext);
- // In edge mode, spec compat is more important than backward compat. Use spec/web behavior here
- if (isRoot
- ? !JavascriptOperators::GetRootProperty(instance, propertyId, &value, scriptContext)
- : !JavascriptOperators::GetProperty(instance, object, propertyId, &value, scriptContext))
- {
- return scriptContext->GetLibrary()->GetUndefinedDisplayString();
- }
- if (!scriptContext->IsUndeclBlockVar(value))
- {
- return JavascriptOperators::Typeof(value, scriptContext);
- }
- }
- catch(const JavascriptException& err)
- {
- err.GetAndClear(); // discard exception object
- return scriptContext->GetLibrary()->GetUndefinedDisplayString();
- }
- Assert(scriptContext->IsUndeclBlockVar(value));
- JavascriptError::ThrowReferenceError(scriptContext, JSERR_UseBeforeDeclaration);
- }
- Var JavascriptOperators::TypeofElem_UInt32(Var instance, uint32 index, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_TypeofElem_UInt32);
- if (JavascriptOperators::IsNumberFromNativeArray(instance, index, scriptContext))
- return scriptContext->GetLibrary()->GetNumberTypeDisplayString();
- #if FLOATVAR
- return TypeofElem(instance, Js::JavascriptNumber::ToVar(index, scriptContext), scriptContext);
- #else
- char buffer[sizeof(Js::JavascriptNumber)];
- return TypeofElem(instance, Js::JavascriptNumber::ToVarInPlace(index, scriptContext,
- (Js::JavascriptNumber *)buffer), scriptContext);
- #endif
- JIT_HELPER_END(Op_TypeofElem_UInt32);
- }
- Var JavascriptOperators::TypeofElem_Int32(Var instance, int32 index, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_TypeofElem_Int32);
- if (JavascriptOperators::IsNumberFromNativeArray(instance, index, scriptContext))
- return scriptContext->GetLibrary()->GetNumberTypeDisplayString();
- #if FLOATVAR
- return TypeofElem(instance, Js::JavascriptNumber::ToVar(index, scriptContext), scriptContext);
- #else
- char buffer[sizeof(Js::JavascriptNumber)];
- return TypeofElem(instance, Js::JavascriptNumber::ToVarInPlace(index, scriptContext,
- (Js::JavascriptNumber *)buffer), scriptContext);
- #endif
- JIT_HELPER_END(Op_TypeofElem_Int32);
- }
- Js::JavascriptString* GetPropertyDisplayNameForError(Var prop, ScriptContext* scriptContext)
- {
- JavascriptString* str;
- JavascriptSymbol *symbol = JavascriptOperators::TryFromVar<JavascriptSymbol>(prop);
- if (symbol)
- {
- str = JavascriptSymbol::ToString(symbol->GetValue(), scriptContext);
- }
- else
- {
- str = JavascriptConversion::ToString(prop, scriptContext);
- }
- return str;
- }
- Var JavascriptOperators::TypeofElem(Var instance, Var index, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_TypeofElem);
- RecyclableObject* object = nullptr;
- if (FALSE == JavascriptOperators::GetPropertyObject(instance, scriptContext, &object))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_CannotGet_NullOrUndefined, GetPropertyDisplayNameForError(index, scriptContext));
- }
- Var member = nullptr;
- uint32 indexVal;
- PropertyRecord const * propertyRecord = nullptr;
- ThreadContext* threadContext = scriptContext->GetThreadContext();
- ImplicitCallFlags savedImplicitCallFlags = threadContext->GetImplicitCallFlags();
- threadContext->ClearImplicitCallFlags();
- try
- {
- Js::JavascriptExceptionOperators::AutoCatchHandlerExists autoCatchHandlerExists(scriptContext);
- IndexType indexType = GetIndexType(index, scriptContext, &indexVal, &propertyRecord, false);
- // For JS Objects, don't create the propertyId if not already added
- if (indexType == IndexType_Number)
- {
- // In edge mode, we don't need to worry about the special "unknown" behavior. If the item is not available from Get,
- // just return undefined.
- if (!JavascriptOperators::GetItem(instance, object, indexVal, &member, scriptContext))
- {
- // If the instance doesn't have the item, typeof result is "undefined".
- threadContext->CheckAndResetImplicitCallAccessorFlag();
- threadContext->AddImplicitCallFlags(savedImplicitCallFlags);
- return scriptContext->GetLibrary()->GetUndefinedDisplayString();
- }
- }
- else
- {
- Assert(indexType == IndexType_PropertyId);
- if (propertyRecord == nullptr && !JavascriptOperators::CanShortcutOnUnknownPropertyName(object))
- {
- indexType = GetIndexTypeFromPrimitive(index, scriptContext, &indexVal, &propertyRecord, true);
- Assert(indexType == IndexType_PropertyId);
- Assert(propertyRecord != nullptr);
- }
- if (propertyRecord != nullptr)
- {
- if (!JavascriptOperators::GetProperty(instance, object, propertyRecord->GetPropertyId(), &member, scriptContext))
- {
- // If the instance doesn't have the property, typeof result is "undefined".
- threadContext->CheckAndResetImplicitCallAccessorFlag();
- threadContext->AddImplicitCallFlags(savedImplicitCallFlags);
- return scriptContext->GetLibrary()->GetUndefinedDisplayString();
- }
- }
- else
- {
- #if DBG
- JavascriptString* indexStr = JavascriptConversion::ToString(index, scriptContext);
- PropertyRecord const * debugPropertyRecord;
- scriptContext->GetOrAddPropertyRecord(indexStr, &debugPropertyRecord);
- AssertMsg(!JavascriptOperators::GetProperty(instance, object, debugPropertyRecord->GetPropertyId(), &member, scriptContext), "how did this property come? See OS Bug 2727708 if you see this come from the web");
- #endif
- // If the instance doesn't have the property, typeof result is "undefined".
- threadContext->CheckAndResetImplicitCallAccessorFlag();
- threadContext->AddImplicitCallFlags(savedImplicitCallFlags);
- return scriptContext->GetLibrary()->GetUndefinedDisplayString();
- }
- }
- threadContext->CheckAndResetImplicitCallAccessorFlag();
- threadContext->AddImplicitCallFlags(savedImplicitCallFlags);
- return JavascriptOperators::Typeof(member, scriptContext);
- }
- catch(const JavascriptException& err)
- {
- err.GetAndClear(); // discard exception object
- threadContext->CheckAndResetImplicitCallAccessorFlag();
- threadContext->AddImplicitCallFlags(savedImplicitCallFlags);
- return scriptContext->GetLibrary()->GetUndefinedDisplayString();
- }
- JIT_HELPER_END(Op_TypeofElem);
- }
- //
- // Delete the given Var
- //
- Var JavascriptOperators::Delete(Var var, ScriptContext* scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(Op_Delete);
- return scriptContext->GetLibrary()->GetTrue();
- JIT_HELPER_END(Op_Delete);
- }
- BOOL JavascriptOperators::Equal_Full(Var aLeft, Var aRight, ScriptContext* requestContext)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_Equal_Full);
- //
- // Fast-path SmInts and paired Number combinations.
- //
- if (aLeft == aRight)
- {
- if (JavascriptNumber::Is(aLeft) && JavascriptNumber::IsNan(JavascriptNumber::GetValue(aLeft)))
- {
- return false;
- }
- else if (JavascriptVariantDate::Is(aLeft) == false) // only need to check on aLeft - since they are the same var, aRight would do the same
- {
- return true;
- }
- else
- {
- //In ES5 mode strict equals (===) on same instance of object type VariantDate succeeds.
- //Hence equals needs to succeed.
- return true;
- }
- }
- BOOL result = false;
- if (TaggedInt::Is(aLeft))
- {
- if (TaggedInt::Is(aRight))
- {
- // If aLeft == aRight, we would already have returned true above.
- return false;
- }
- else if (JavascriptNumber::Is_NoTaggedIntCheck(aRight))
- {
- return TaggedInt::ToDouble(aLeft) == JavascriptNumber::GetValue(aRight);
- }
- else
- {
- BOOL res = RecyclableObject::UnsafeFromVar(aRight)->Equals(aLeft, &result, requestContext);
- AssertMsg(res, "Should have handled this");
- return result;
- }
- }
- else if (JavascriptNumber::Is_NoTaggedIntCheck(aLeft))
- {
- if (TaggedInt::Is(aRight))
- {
- return TaggedInt::ToDouble(aRight) == JavascriptNumber::GetValue(aLeft);
- }
- else if(JavascriptNumber::Is_NoTaggedIntCheck(aRight))
- {
- return JavascriptNumber::GetValue(aLeft) == JavascriptNumber::GetValue(aRight);
- }
- else
- {
- BOOL res = RecyclableObject::UnsafeFromVar(aRight)->Equals(aLeft, &result, requestContext);
- AssertMsg(res, "Should have handled this");
- return result;
- }
- }
- if (RecyclableObject::UnsafeFromVar(aLeft)->Equals(aRight, &result, requestContext))
- {
- return result;
- }
- else
- {
- return false;
- }
- JIT_HELPER_END(Op_Equal_Full);
- }
- BOOL JavascriptOperators::Greater_Full(Var aLeft,Var aRight,ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_Greater_Full);
- return RelationalComparisonHelper(aRight, aLeft, scriptContext, false, false);
- JIT_HELPER_END(Op_Greater_Full);
- }
- BOOL JavascriptOperators::Less_Full(Var aLeft, Var aRight, ScriptContext* scriptContext)
- {
- return RelationalComparisonHelper(aLeft, aRight, scriptContext, true, false);
- }
- BOOL JavascriptOperators::RelationalComparisonHelper(Var aLeft, Var aRight, ScriptContext* scriptContext, bool leftFirst, bool undefinedAs)
- {
- TypeId typeId = JavascriptOperators::GetTypeId(aLeft);
- if (typeId == TypeIds_Null)
- {
- aLeft=TaggedInt::ToVarUnchecked(0);
- }
- else if (typeId == TypeIds_Undefined)
- {
- aLeft=scriptContext->GetLibrary()->GetNaN();
- }
- typeId = JavascriptOperators::GetTypeId(aRight);
- if (typeId == TypeIds_Null)
- {
- aRight=TaggedInt::ToVarUnchecked(0);
- }
- else if (typeId == TypeIds_Undefined)
- {
- aRight=scriptContext->GetLibrary()->GetNaN();
- }
- double dblLeft, dblRight;
- TypeId leftType = JavascriptOperators::GetTypeId(aLeft);
- TypeId rightType = JavascriptOperators::GetTypeId(aRight);
- switch (leftType)
- {
- case TypeIds_Integer:
- dblLeft = TaggedInt::ToDouble(aLeft);
- switch (rightType)
- {
- case TypeIds_Integer:
- dblRight = TaggedInt::ToDouble(aRight);
- break;
- case TypeIds_Number:
- dblRight = JavascriptNumber::GetValue(aRight);
- break;
- default:
- dblRight = JavascriptConversion::ToNumber(aRight, scriptContext);
- break;
- }
- break;
- case TypeIds_Number:
- dblLeft = JavascriptNumber::GetValue(aLeft);
- switch (rightType)
- {
- case TypeIds_Integer:
- dblRight = TaggedInt::ToDouble(aRight);
- break;
- case TypeIds_Number:
- dblRight = JavascriptNumber::GetValue(aRight);
- break;
- default:
- dblRight = JavascriptConversion::ToNumber(aRight, scriptContext);
- break;
- }
- break;
- case TypeIds_Int64Number:
- {
- switch (rightType)
- {
- case TypeIds_Int64Number:
- {
- __int64 leftValue = JavascriptInt64Number::UnsafeFromVar(aLeft)->GetValue();
- __int64 rightValue = JavascriptInt64Number::UnsafeFromVar(aRight)->GetValue();
- return leftValue < rightValue;
- }
- break;
- case TypeIds_UInt64Number:
- {
- __int64 leftValue = JavascriptInt64Number::UnsafeFromVar(aLeft)->GetValue();
- unsigned __int64 rightValue = JavascriptUInt64Number::UnsafeFromVar(aRight)->GetValue();
- if (rightValue <= INT_MAX && leftValue >= 0)
- {
- return leftValue < (__int64)rightValue;
- }
- }
- break;
- }
- dblLeft = (double)JavascriptInt64Number::UnsafeFromVar(aLeft)->GetValue();
- dblRight = JavascriptConversion::ToNumber(aRight, scriptContext);
- }
- break;
- // we cannot do double conversion between 2 int64 numbers as we can get wrong result after conversion
- // i.e., two different numbers become the same after losing precision. We'll continue dbl comparison
- // if either number is not an int64 number.
- case TypeIds_UInt64Number:
- {
- switch (rightType)
- {
- case TypeIds_Int64Number:
- {
- unsigned __int64 leftValue = JavascriptUInt64Number::UnsafeFromVar(aLeft)->GetValue();
- __int64 rightValue = JavascriptInt64Number::UnsafeFromVar(aRight)->GetValue();
- if (leftValue < INT_MAX && rightValue >= 0)
- {
- return (__int64)leftValue < rightValue;
- }
- }
- break;
- case TypeIds_UInt64Number:
- {
- unsigned __int64 leftValue = JavascriptUInt64Number::UnsafeFromVar(aLeft)->GetValue();
- unsigned __int64 rightValue = JavascriptUInt64Number::UnsafeFromVar(aRight)->GetValue();
- return leftValue < rightValue;
- }
- break;
- }
- dblLeft = (double)JavascriptUInt64Number::UnsafeFromVar(aLeft)->GetValue();
- dblRight = JavascriptConversion::ToNumber(aRight, scriptContext);
- }
- break;
- case TypeIds_String:
- switch (rightType)
- {
- case TypeIds_Integer:
- case TypeIds_Number:
- case TypeIds_Boolean:
- break;
- default:
- aRight = JavascriptConversion::ToPrimitive<JavascriptHint::HintNumber>(aRight, scriptContext);
- rightType = JavascriptOperators::GetTypeId(aRight);
- if (rightType != TypeIds_String)
- {
- dblRight = JavascriptConversion::ToNumber(aRight, scriptContext);
- break;
- }
- case TypeIds_String:
- return JavascriptString::LessThan(aLeft, aRight);
- }
- dblLeft = JavascriptConversion::ToNumber(aLeft, scriptContext);
- dblRight = JavascriptConversion::ToNumber(aRight, scriptContext);
- break;
- case TypeIds_Boolean:
- case TypeIds_Null:
- case TypeIds_Undefined:
- case TypeIds_Symbol:
- dblLeft = JavascriptConversion::ToNumber(aLeft, scriptContext);
- dblRight = JavascriptConversion::ToNumber(aRight, scriptContext);
- break;
- default:
- if (leftFirst)
- {
- aLeft = JavascriptConversion::ToPrimitive<JavascriptHint::HintNumber>(aLeft, scriptContext);
- aRight = JavascriptConversion::ToPrimitive<JavascriptHint::HintNumber>(aRight, scriptContext);
- }
- else
- {
- aRight = JavascriptConversion::ToPrimitive<JavascriptHint::HintNumber>(aRight, scriptContext);
- aLeft = JavascriptConversion::ToPrimitive<JavascriptHint::HintNumber>(aLeft, scriptContext);
- }
- //BugFix: When @@ToPrimitive of an object is overridden with a function that returns null/undefined
- //this helper will fall into a inescapable goto loop as the checks for null/undefined were outside of the path
- return RelationalComparisonHelper(aLeft, aRight, scriptContext, leftFirst, undefinedAs);
- }
- //
- // And +0,-0 that is not implemented fully
- //
- if (JavascriptNumber::IsNan(dblLeft) || JavascriptNumber::IsNan(dblRight))
- {
- return undefinedAs;
- }
- // this will succeed for -0.0 == 0.0 case as well
- if (dblLeft == dblRight)
- {
- return false;
- }
- return dblLeft < dblRight;
- }
- BOOL JavascriptOperators::StrictEqualString(Var aLeft, JavascriptString* aRight)
- {
- JavascriptString* leftStr = TryFromVar<JavascriptString>(aLeft);
- if (!leftStr)
- {
- return false;
- }
- JIT_HELPER_REENTRANT_HEADER(Op_StrictEqualString);
- JIT_HELPER_SAME_ATTRIBUTES(Op_StrictEqualString, Op_StrictEqual);
- return JavascriptString::Equals(leftStr, aRight);
- JIT_HELPER_END(Op_StrictEqualString);
- }
- BOOL JavascriptOperators::StrictEqualEmptyString(Var aLeft)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(Op_StrictEqualEmptyString);
- JavascriptString * string = JavascriptOperators::TryFromVar<JavascriptString>(aLeft);
- if (!string)
- {
- return false;
- }
- Assert(string);
- return string->GetLength() == 0;
- JIT_HELPER_END(Op_StrictEqualEmptyString);
- }
- BOOL JavascriptOperators::StrictEqual(Var aLeft, Var aRight, ScriptContext* requestContext)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_StrictEqual);
- double dblLeft, dblRight;
- TypeId rightType, leftType;
- leftType = JavascriptOperators::GetTypeId(aLeft);
- // Because NaN !== NaN, we may not return TRUE when typeId is Number
- if (aLeft == aRight && leftType != TypeIds_Number) return TRUE;
- rightType = JavascriptOperators::GetTypeId(aRight);
- switch (leftType)
- {
- case TypeIds_String:
- switch (rightType)
- {
- case TypeIds_String:
- return JavascriptString::Equals(JavascriptString::UnsafeFromVar(aLeft), JavascriptString::UnsafeFromVar(aRight));
- }
- return FALSE;
- case TypeIds_Integer:
- switch (rightType)
- {
- case TypeIds_Integer:
- return aLeft == aRight;
- // we don't need to worry about int64: it cannot equal as we create
- // JavascriptInt64Number only in overflow scenarios.
- case TypeIds_Number:
- dblLeft = TaggedInt::ToDouble(aLeft);
- dblRight = JavascriptNumber::GetValue(aRight);
- goto CommonNumber;
- }
- return FALSE;
- case TypeIds_Int64Number:
- switch (rightType)
- {
- case TypeIds_Int64Number:
- {
- __int64 leftValue = JavascriptInt64Number::UnsafeFromVar(aLeft)->GetValue();
- __int64 rightValue = JavascriptInt64Number::UnsafeFromVar(aRight)->GetValue();
- return leftValue == rightValue;
- }
- case TypeIds_UInt64Number:
- {
- __int64 leftValue = JavascriptInt64Number::UnsafeFromVar(aLeft)->GetValue();
- unsigned __int64 rightValue = JavascriptInt64Number::FromVar(aRight)->GetValue();
- return ((unsigned __int64)leftValue == rightValue);
- }
- case TypeIds_Number:
- dblLeft = (double)JavascriptInt64Number::UnsafeFromVar(aLeft)->GetValue();
- dblRight = JavascriptNumber::GetValue(aRight);
- goto CommonNumber;
- }
- return FALSE;
- case TypeIds_UInt64Number:
- switch (rightType)
- {
- case TypeIds_Int64Number:
- {
- unsigned __int64 leftValue = JavascriptUInt64Number::UnsafeFromVar(aLeft)->GetValue();
- __int64 rightValue = JavascriptInt64Number::UnsafeFromVar(aRight)->GetValue();
- return (leftValue == (unsigned __int64)rightValue);
- }
- case TypeIds_UInt64Number:
- {
- unsigned __int64 leftValue = JavascriptUInt64Number::UnsafeFromVar(aLeft)->GetValue();
- unsigned __int64 rightValue = JavascriptInt64Number::FromVar(aRight)->GetValue();
- return leftValue == rightValue;
- }
- case TypeIds_Number:
- dblLeft = (double)JavascriptUInt64Number::UnsafeFromVar(aLeft)->GetValue();
- dblRight = JavascriptNumber::GetValue(aRight);
- goto CommonNumber;
- }
- return FALSE;
- case TypeIds_Number:
- switch (rightType)
- {
- case TypeIds_Integer:
- dblLeft = JavascriptNumber::GetValue(aLeft);
- dblRight = TaggedInt::ToDouble(aRight);
- goto CommonNumber;
- case TypeIds_Int64Number:
- dblLeft = JavascriptNumber::GetValue(aLeft);
- dblRight = (double)JavascriptInt64Number::FromVar(aRight)->GetValue();
- goto CommonNumber;
- case TypeIds_UInt64Number:
- dblLeft = JavascriptNumber::GetValue(aLeft);
- dblRight = (double)JavascriptUInt64Number::UnsafeFromVar(aRight)->GetValue();
- goto CommonNumber;
- case TypeIds_Number:
- dblLeft = JavascriptNumber::GetValue(aLeft);
- dblRight = JavascriptNumber::GetValue(aRight);
- CommonNumber:
- return FEqualDbl(dblLeft, dblRight);
- }
- return FALSE;
- case TypeIds_Boolean:
- switch (rightType)
- {
- case TypeIds_Boolean:
- return aLeft == aRight;
- }
- return FALSE;
- case TypeIds_Undefined:
- return rightType == TypeIds_Undefined;
- case TypeIds_Null:
- return rightType == TypeIds_Null;
- case TypeIds_Array:
- return (rightType == TypeIds_Array && aLeft == aRight);
- #if DBG
- case TypeIds_Symbol:
- if (rightType == TypeIds_Symbol)
- {
- const PropertyRecord* leftValue = JavascriptSymbol::UnsafeFromVar(aLeft)->GetValue();
- const PropertyRecord* rightValue = JavascriptSymbol::UnsafeFromVar(aRight)->GetValue();
- Assert(leftValue != rightValue);
- }
- break;
- #endif
- case TypeIds_GlobalObject:
- case TypeIds_HostDispatch:
- switch (rightType)
- {
- case TypeIds_HostDispatch:
- case TypeIds_GlobalObject:
- {
- BOOL result;
- if(RecyclableObject::UnsafeFromVar(aLeft)->StrictEquals(aRight, &result, requestContext))
- {
- return result;
- }
- return false;
- }
- }
- break;
- }
- if (RecyclableObject::FromVar(aLeft)->IsExternal())
- {
- BOOL result;
- if (RecyclableObject::FromVar(aLeft)->StrictEquals(aRight, &result, requestContext))
- {
- if (result)
- {
- return TRUE;
- }
- }
- }
- if (!TaggedNumber::Is(aRight) && RecyclableObject::FromVar(aRight)->IsExternal())
- {
- BOOL result;
- if (RecyclableObject::FromVar(aRight)->StrictEquals(aLeft, &result, requestContext))
- {
- if (result)
- {
- return TRUE;
- }
- }
- }
- return aLeft == aRight;
- JIT_HELPER_END(Op_StrictEqual);
- }
- BOOL JavascriptOperators::HasOwnProperty(
- Var instance,
- PropertyId propertyId,
- _In_ ScriptContext* requestContext,
- _In_opt_ PropertyString* propString)
- {
- if (TaggedNumber::Is(instance))
- {
- return FALSE;
- }
- RecyclableObject* object = RecyclableObject::UnsafeFromVar(instance);
- if (JavascriptProxy::Is(instance))
- {
- PropertyDescriptor desc;
- return GetOwnPropertyDescriptor(object, propertyId, requestContext, &desc);
- }
- // If we have a PropertyString, attempt to shortcut the lookup by using its caches
- if (propString != nullptr)
- {
- PropertyCacheOperationInfo info;
- if (propString->GetLdElemInlineCache()->PretendTryGetProperty(object->GetType(), &info))
- {
- switch (info.cacheType)
- {
- case CacheType_Local:
- Assert(object->HasOwnProperty(propertyId));
- return TRUE;
- case CacheType_Proto:
- Assert(!object->HasOwnProperty(propertyId));
- return FALSE;
- default:
- // We had a cache hit, but cache doesn't tell us if we have an own property
- break;
- }
- }
- if (propString->GetStElemInlineCache()->PretendTrySetProperty(object->GetType(), object->GetType(), &info))
- {
- switch (info.cacheType)
- {
- case CacheType_Local:
- Assert(object->HasOwnProperty(propertyId));
- return TRUE;
- case CacheType_LocalWithoutProperty:
- Assert(!object->HasOwnProperty(propertyId));
- return FALSE;
- default:
- // We had a cache hit, but cache doesn't tell us if we have an own property
- break;
- }
- }
- }
- return object && object->HasOwnProperty(propertyId);
- }
- BOOL JavascriptOperators::GetOwnAccessors(Var instance, PropertyId propertyId, Var* getter, Var* setter, ScriptContext * requestContext)
- {
- BOOL result;
- if (TaggedNumber::Is(instance))
- {
- result = false;
- }
- else
- {
- RecyclableObject* object = RecyclableObject::UnsafeFromVar(instance);
- result = object && object->GetAccessors(propertyId, getter, setter, requestContext);
- }
- return result;
- }
- JavascriptArray* JavascriptOperators::GetOwnPropertyNames(Var instance, ScriptContext *scriptContext)
- {
- RecyclableObject *object = RecyclableObject::FromVar(ToObject(instance, scriptContext));
- JavascriptProxy * proxy = JavascriptOperators::TryFromVar<JavascriptProxy>(instance);
- if (proxy)
- {
- return proxy->PropertyKeysTrap(JavascriptProxy::KeysTrapKind::GetOwnPropertyNamesKind, scriptContext);
- }
- return JavascriptObject::CreateOwnStringPropertiesHelper(object, scriptContext);
- }
- JavascriptArray* JavascriptOperators::GetOwnPropertySymbols(Var instance, ScriptContext *scriptContext)
- {
- RecyclableObject *object = RecyclableObject::FromVar(ToObject(instance, scriptContext));
- CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Object_Constructor_getOwnPropertySymbols);
- JavascriptProxy* proxy = JavascriptOperators::TryFromVar<JavascriptProxy>(instance);
- if (proxy)
- {
- return proxy->PropertyKeysTrap(JavascriptProxy::KeysTrapKind::GetOwnPropertySymbolKind, scriptContext);
- }
- return JavascriptObject::CreateOwnSymbolPropertiesHelper(object, scriptContext);
- }
- JavascriptArray* JavascriptOperators::GetOwnPropertyKeys(Var instance, ScriptContext* scriptContext)
- {
- RecyclableObject *object = RecyclableObject::FromVar(ToObject(instance, scriptContext));
- JavascriptProxy* proxy = JavascriptOperators::TryFromVar<JavascriptProxy>(instance);
- if (proxy)
- {
- return proxy->PropertyKeysTrap(JavascriptProxy::KeysTrapKind::KeysKind, scriptContext);
- }
- return JavascriptObject::CreateOwnStringSymbolPropertiesHelper(object, scriptContext);
- }
- JavascriptArray* JavascriptOperators::GetOwnEnumerablePropertyNames(RecyclableObject* object, ScriptContext* scriptContext)
- {
- JavascriptProxy* proxy = JavascriptOperators::TryFromVar<JavascriptProxy>(object);
- if (proxy)
- {
- JavascriptArray* proxyResult = proxy->PropertyKeysTrap(JavascriptProxy::KeysTrapKind::GetOwnPropertyNamesKind, scriptContext);
- JavascriptArray* proxyResultToReturn = scriptContext->GetLibrary()->CreateArray(0);
- // filter enumerable keys
- uint32 resultLength = proxyResult->GetLength();
- Var element;
- const Js::PropertyRecord *propertyRecord = nullptr;
- uint32 index = 0;
- for (uint32 i = 0; i < resultLength; i++)
- {
- element = proxyResult->DirectGetItem(i);
- Assert(!JavascriptSymbol::Is(element));
- PropertyDescriptor propertyDescriptor;
- JavascriptConversion::ToPropertyKey(element, scriptContext, &propertyRecord, nullptr);
- if (JavascriptOperators::GetOwnPropertyDescriptor(object, propertyRecord->GetPropertyId(), scriptContext, &propertyDescriptor))
- {
- if (propertyDescriptor.IsEnumerable())
- {
- proxyResultToReturn->DirectSetItemAt(index++, CrossSite::MarshalVar(scriptContext, element));
- }
- }
- }
- return proxyResultToReturn;
- }
- return JavascriptObject::CreateOwnEnumerableStringPropertiesHelper(object, scriptContext);
- }
- JavascriptArray* JavascriptOperators::GetOwnEnumerablePropertyNamesSymbols(RecyclableObject* object, ScriptContext* scriptContext)
- {
- JavascriptProxy* proxy = JavascriptOperators::TryFromVar<JavascriptProxy>(object);
- if (proxy)
- {
- return proxy->PropertyKeysTrap(JavascriptProxy::KeysTrapKind::KeysKind, scriptContext);
- }
- return JavascriptObject::CreateOwnEnumerableStringSymbolPropertiesHelper(object, scriptContext);
- }
- BOOL JavascriptOperators::GetOwnProperty(Var instance, PropertyId propertyId, Var* value, ScriptContext* requestContext, PropertyValueInfo * propertyValueInfo)
- {
- BOOL result;
- if (TaggedNumber::Is(instance))
- {
- result = false;
- }
- else
- {
- RecyclableObject* object = RecyclableObject::FromVar(instance);
- result = object && object->GetProperty(object, propertyId, value, propertyValueInfo, requestContext);
- if (propertyValueInfo && result)
- {
- // We can only update the cache in case a property was found, because if it wasn't found, we don't know if it is missing or on a prototype
- CacheOperators::CachePropertyRead(instance, object, false /* isRoot */, propertyId, false /* isMissing */, propertyValueInfo, requestContext);
- }
- }
- return result;
- }
- BOOL JavascriptOperators::GetOwnPropertyDescriptor(RecyclableObject* obj, JavascriptString* propertyKey, ScriptContext* scriptContext, PropertyDescriptor* propertyDescriptor)
- {
- return JavascriptOperators::GetOwnPropertyDescriptor(obj, JavascriptOperators::GetPropertyId(propertyKey, scriptContext), scriptContext, propertyDescriptor);
- }
- // ES5's [[GetOwnProperty]].
- // Return value:
- // FALSE means "undefined" PD.
- // TRUE means success. The propertyDescriptor parameter gets the descriptor.
- //
- BOOL JavascriptOperators::GetOwnPropertyDescriptor(RecyclableObject* obj, PropertyId propertyId, ScriptContext* scriptContext, PropertyDescriptor* propertyDescriptor)
- {
- Assert(obj);
- Assert(scriptContext);
- Assert(propertyDescriptor);
- if (JavascriptProxy::Is(obj))
- {
- return JavascriptProxy::GetOwnPropertyDescriptor(obj, propertyId, scriptContext, propertyDescriptor);
- }
- Var getter, setter;
- if (false == JavascriptOperators::GetOwnAccessors(obj, propertyId, &getter, &setter, scriptContext))
- {
- Var value = nullptr;
- if (false == JavascriptOperators::GetOwnProperty(obj, propertyId, &value, scriptContext, nullptr))
- {
- return FALSE;
- }
- if (nullptr != value)
- {
- propertyDescriptor->SetValue(value);
- }
- //CONSIDER : Its expensive to query for each flag from type system. Combine this with the GetOwnProperty to get all the flags
- //at once. This will require a new API from type system and override in all the types which overrides IsEnumerable etc.
- //Currently there is no performance tuning for ES5. This should be ok.
- propertyDescriptor->SetWritable(FALSE != obj->IsWritable(propertyId));
- }
- else
- {
- if (nullptr == getter)
- {
- getter = scriptContext->GetLibrary()->GetUndefined();
- }
- propertyDescriptor->SetGetter(getter);
- if (nullptr == setter)
- {
- setter = scriptContext->GetLibrary()->GetUndefined();
- }
- propertyDescriptor->SetSetter(setter);
- }
- propertyDescriptor->SetConfigurable(FALSE != obj->IsConfigurable(propertyId));
- propertyDescriptor->SetEnumerable(FALSE != obj->IsEnumerable(propertyId));
- return TRUE;
- }
- inline RecyclableObject* JavascriptOperators::GetPrototypeNoTrap(RecyclableObject* instance)
- {
- Type* type = instance->GetType();
- if (type->HasSpecialPrototype())
- {
- if (type->GetTypeId() == TypeIds_Proxy)
- {
- // get back null
- Assert(type->GetPrototype() == instance->GetScriptContext()->GetLibrary()->GetNull());
- return type->GetPrototype();
- }
- else
- {
- return instance->GetPrototypeSpecial();
- }
- }
- return type->GetPrototype();
- }
- BOOL JavascriptOperators::IsRemoteArray(RecyclableObject* instance)
- {
- TypeId remoteTypeId = TypeIds_Limit;
- return (JavascriptOperators::GetRemoteTypeId(instance, &remoteTypeId) &&
- DynamicObject::IsAnyArrayTypeId(remoteTypeId));
- }
- bool JavascriptOperators::IsArray(_In_ JavascriptProxy * instance)
- {
- // If it is a proxy, follow to the end of the proxy chain before checking if it is an array again.
- JavascriptProxy * proxy = instance;
- while (true)
- {
- RecyclableObject * targetInstance = proxy->GetTarget();
- proxy = JavascriptOperators::TryFromVar<JavascriptProxy>(targetInstance);
- if (proxy == nullptr)
- {
- return DynamicObject::IsAnyArray(targetInstance) || IsRemoteArray(targetInstance);
- }
- }
- }
- bool JavascriptOperators::IsArray(_In_ RecyclableObject* instance)
- {
- if (DynamicObject::IsAnyArray(instance))
- {
- return TRUE;
- }
- JavascriptProxy* proxy = JavascriptOperators::TryFromVar<JavascriptProxy>(instance);
- if (proxy)
- {
- return IsArray(proxy);
- }
- return IsRemoteArray(instance);
- }
- bool JavascriptOperators::IsArray(_In_ Var instanceVar)
- {
- RecyclableObject* instanceObj = TryFromVar<RecyclableObject>(instanceVar);
- return instanceObj && IsArray(instanceObj);
- }
- bool JavascriptOperators::IsConstructor(_In_ JavascriptProxy * instance)
- {
- // If it is a proxy, follow to the end of the proxy chain before checking if it is a constructor again.
- JavascriptProxy * proxy = instance;
- while (true)
- {
- RecyclableObject* targetInstance = proxy->GetTarget();
- proxy = JavascriptOperators::TryFromVar<JavascriptProxy>(targetInstance);
- if (proxy == nullptr)
- {
- JavascriptFunction* function = JavascriptOperators::TryFromVar<JavascriptFunction>(targetInstance);
- return function && function->IsConstructor();
- }
- }
- }
- bool JavascriptOperators::IsConstructor(_In_ RecyclableObject* instanceObj)
- {
- JavascriptProxy* proxy = JavascriptOperators::TryFromVar<JavascriptProxy>(instanceObj);
- if (proxy)
- {
- return IsConstructor(proxy);
- }
- JavascriptFunction* function = JavascriptOperators::TryFromVar<JavascriptFunction>(instanceObj);
- return function && function->IsConstructor();
- }
- bool JavascriptOperators::IsConstructor(_In_ Var instanceVar)
- {
- RecyclableObject* instanceObj = TryFromVar<RecyclableObject>(instanceVar);
- return instanceObj && IsConstructor(instanceObj);
- }
- BOOL JavascriptOperators::IsConcatSpreadable(Var instanceVar)
- {
- // an object is spreadable under two condition, either it is a JsArray
- // or you define an isconcatSpreadable flag on it.
- if (!JavascriptOperators::IsObject(instanceVar))
- {
- return false;
- }
- RecyclableObject* instance = RecyclableObject::UnsafeFromVar(instanceVar);
- ScriptContext* scriptContext = instance->GetScriptContext();
- if (!PHASE_OFF1(IsConcatSpreadableCachePhase))
- {
- BOOL retVal = FALSE;
- Type *instanceType = instance->GetType();
- IsConcatSpreadableCache *isConcatSpreadableCache = scriptContext->GetThreadContext()->GetIsConcatSpreadableCache();
- if (isConcatSpreadableCache->TryGetIsConcatSpreadable(instanceType, &retVal))
- {
- OUTPUT_TRACE(Phase::IsConcatSpreadableCachePhase, _u("IsConcatSpreadableCache hit: %p\n"), instanceType);
- return retVal;
- }
- Var spreadable = nullptr;
- BOOL hasUserDefinedSpreadable = JavascriptOperators::GetProperty(instance, instance, PropertyIds::_symbolIsConcatSpreadable, &spreadable, scriptContext);
- if (hasUserDefinedSpreadable && spreadable != scriptContext->GetLibrary()->GetUndefined())
- {
- return JavascriptConversion::ToBoolean(spreadable, scriptContext);
- }
- retVal = JavascriptOperators::IsArray(instance);
- if (!hasUserDefinedSpreadable)
- {
- OUTPUT_TRACE(Phase::IsConcatSpreadableCachePhase, _u("IsConcatSpreadableCache saved: %p\n"), instanceType);
- isConcatSpreadableCache->CacheIsConcatSpreadable(instanceType, retVal);
- }
- return retVal;
- }
- Var spreadable = JavascriptOperators::GetProperty(instance, PropertyIds::_symbolIsConcatSpreadable, scriptContext);
- if (spreadable != scriptContext->GetLibrary()->GetUndefined())
- {
- return JavascriptConversion::ToBoolean(spreadable, scriptContext);
- }
- return JavascriptOperators::IsArray(instance);
- }
- bool JavascriptOperators::IsConstructorSuperCall(Arguments args)
- {
- Var newTarget = args.GetNewTarget();
- return args.IsNewCall() && newTarget != nullptr
- && !JavascriptOperators::IsUndefined(newTarget);
- }
- bool JavascriptOperators::GetAndAssertIsConstructorSuperCall(Arguments args)
- {
- bool isCtorSuperCall = JavascriptOperators::IsConstructorSuperCall(args);
- Assert(isCtorSuperCall || !args.IsNewCall()
- || args[0] == nullptr || JavascriptOperators::GetTypeId(args[0]) == TypeIds_HostDispatch);
- return isCtorSuperCall;
- }
- Var JavascriptOperators::OP_LdCustomSpreadIteratorList(Var aRight, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_ToSpreadedFunctionArgument);
- #if ENABLE_COPYONACCESS_ARRAY
- // We know we're going to read from this array. Do the conversion before we try to perform checks on the head segment.
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray(aRight);
- #endif
- #ifdef ENABLE_JS_BUILTINS
- scriptContext->GetLibrary()->EnsureBuiltInEngineIsReady();
- #endif
- RecyclableObject* function = GetIteratorFunction(aRight, scriptContext);
- JavascriptMethod method = function->GetEntryPoint();
- if (((JavascriptArray::Is(aRight) &&
- (
- JavascriptLibrary::IsDefaultArrayValuesFunction(function, scriptContext)
- // Verify that the head segment of the array covers all elements with no gaps.
- // Accessing an element on the prototype could have side-effects that would invalidate the optimization.
- && JavascriptArray::UnsafeFromVar(aRight)->GetHead()->next == nullptr
- && JavascriptArray::UnsafeFromVar(aRight)->GetHead()->left == 0
- && JavascriptArray::UnsafeFromVar(aRight)->GetHead()->length == JavascriptArray::FromVar(aRight)->GetLength()
- && JavascriptArray::UnsafeFromVar(aRight)->HasNoMissingValues()
- && !JavascriptArray::UnsafeFromVar(aRight)->IsCrossSiteObject()
- )) ||
- (TypedArrayBase::Is(aRight) && method == TypedArrayBase::EntryInfo::Values.GetOriginalEntryPoint()))
- // We can't optimize away the iterator if the array iterator prototype is user defined.
- && !JavascriptLibrary::ArrayIteratorPrototypeHasUserDefinedNext(scriptContext))
- {
- return RecyclerNew(scriptContext->GetRecycler(), SpreadArgument, aRight, true /*useDirectCall*/, scriptContext->GetLibrary()->GetSpreadArgumentType());
- }
- ThreadContext *threadContext = scriptContext->GetThreadContext();
- Var iteratorVar =
- threadContext->ExecuteImplicitCall(function, ImplicitCall_Accessor, [=]() -> Var
- {
- return CALL_FUNCTION(threadContext, function, CallInfo(Js::CallFlags_Value, 1), aRight);
- });
- if (!JavascriptOperators::IsObject(iteratorVar))
- {
- if (!threadContext->RecordImplicitException())
- {
- return scriptContext->GetLibrary()->GetUndefined();
- }
- JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedObject);
- }
- return RecyclerNew(scriptContext->GetRecycler(), SpreadArgument, iteratorVar, false /*useDirectCall*/, scriptContext->GetLibrary()->GetSpreadArgumentType());
- JIT_HELPER_END(Op_ToSpreadedFunctionArgument);
- }
- BOOL JavascriptOperators::IsPropertyUnscopable(Var instanceVar, JavascriptString *propertyString)
- {
- // This never gets called.
- Throw::InternalError();
- }
- BOOL JavascriptOperators::IsPropertyUnscopable(Var instanceVar, PropertyId propertyId)
- {
- RecyclableObject* instance = RecyclableObject::FromVar(instanceVar);
- ScriptContext * scriptContext = instance->GetScriptContext();
- Var unscopables = JavascriptOperators::GetProperty(instance, PropertyIds::_symbolUnscopables, scriptContext);
- if (JavascriptOperators::IsObject(unscopables))
- {
- DynamicObject *unscopablesList = DynamicObject::FromVar(unscopables);
- Var value = nullptr;
- //8.1.1.2.1.9.c If blocked is not undefined
- if (JavascriptOperators::GetProperty(unscopablesList, propertyId, &value, scriptContext))
- {
- return JavascriptConversion::ToBoolean(value, scriptContext);
- }
- }
- return false;
- }
- BOOL JavascriptOperators::HasProperty(RecyclableObject* instance, PropertyId propertyId)
- {
- while (!JavascriptOperators::IsNull(instance))
- {
- PropertyQueryFlags result = instance->HasPropertyQuery(propertyId, nullptr /*info*/);
- if (result != PropertyQueryFlags::Property_NotFound)
- {
- return JavascriptConversion::PropertyQueryFlagsToBoolean(result); // return false if instance is typed array and HasPropertyQuery() returns PropertyQueryFlags::Property_Found_Undefined
- }
- instance = JavascriptOperators::GetPrototypeNoTrap(instance);
- }
- return false;
- }
- BOOL JavascriptOperators::HasPropertyUnscopables(RecyclableObject* instance, PropertyId propertyId)
- {
- return JavascriptOperators::HasProperty(instance, propertyId)
- && !IsPropertyUnscopable(instance, propertyId);
- }
- BOOL JavascriptOperators::HasRootProperty(RecyclableObject* instance, PropertyId propertyId)
- {
- Assert(RootObjectBase::Is(instance));
- RootObjectBase* rootObject = static_cast<RootObjectBase*>(instance);
- if (rootObject->HasRootProperty(propertyId))
- {
- return true;
- }
- instance = instance->GetPrototype();
- return HasProperty(instance, propertyId);
- }
- BOOL JavascriptOperators::HasProxyOrPrototypeInlineCacheProperty(RecyclableObject* instance, PropertyId propertyId)
- {
- TypeId typeId;
- typeId = JavascriptOperators::GetTypeId(instance);
- if (typeId == Js::TypeIds_Proxy)
- {
- // let's be more aggressive to disable inline prototype cache when proxy is presented in the prototypechain
- return true;
- }
- do
- {
- instance = instance->GetPrototype();
- typeId = JavascriptOperators::GetTypeId(instance);
- if (typeId == Js::TypeIds_Proxy)
- {
- // let's be more aggressive to disable inline prototype cache when proxy is presented in the prototypechain
- return true;
- }
- if (typeId == TypeIds_Null)
- {
- break;
- }
- /* We can rule out object with deferred type handler, because they would have expanded if they are in the cache */
- if (!instance->HasDeferredTypeHandler() && instance->HasProperty(propertyId)) { return true; }
- } while (typeId != TypeIds_Null);
- return false;
- }
- BOOL JavascriptOperators::OP_HasProperty(Var instance, PropertyId propertyId, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_HasProperty);
- RecyclableObject* object = TaggedNumber::Is(instance) ?
- scriptContext->GetLibrary()->GetNumberPrototype() :
- RecyclableObject::FromVar(instance);
- BOOL result = HasProperty(object, propertyId);
- return result;
- JIT_HELPER_END(Op_HasProperty);
- }
- BOOL JavascriptOperators::OP_HasOwnProperty(Var instance, PropertyId propertyId, ScriptContext* scriptContext)
- {
- RecyclableObject* object = TaggedNumber::Is(instance) ?
- scriptContext->GetLibrary()->GetNumberPrototype() :
- RecyclableObject::FromVar(instance);
- BOOL result = HasOwnProperty(object, propertyId, scriptContext, nullptr);
- return result;
- }
- // CONSIDER: Have logic similar to HasOwnPropertyNoHostObjectForHeapEnum
- BOOL JavascriptOperators::HasOwnPropertyNoHostObject(Var instance, PropertyId propertyId)
- {
- AssertMsg(!TaggedNumber::Is(instance), "HasOwnPropertyNoHostObject int passed");
- RecyclableObject* object = RecyclableObject::FromVar(instance);
- return object && object->HasOwnPropertyNoHostObject(propertyId);
- }
- // CONSIDER: Remove HasOwnPropertyNoHostObjectForHeapEnum and use GetOwnPropertyNoHostObjectForHeapEnum in its place by changing it
- // to return BOOL, true or false with whether the property exists or not, and return the value if not getter/setter as an out param.
- BOOL JavascriptOperators::HasOwnPropertyNoHostObjectForHeapEnum(Var instance, PropertyId propertyId, ScriptContext* requestContext, Var& getter, Var& setter)
- {
- AssertMsg(!TaggedNumber::Is(instance), "HasOwnPropertyNoHostObjectForHeapEnum int passed");
- RecyclableObject * object = RecyclableObject::FromVar(instance);
- if (StaticType::Is(object->GetTypeId()))
- {
- return FALSE;
- }
- getter = setter = NULL;
- DynamicObject* dynamicObject = DynamicObject::FromVar(instance);
- Assert(dynamicObject->GetScriptContext()->IsHeapEnumInProgress());
- if (dynamicObject->UseDynamicObjectForNoHostObjectAccess())
- {
- if (!dynamicObject->DynamicObject::GetAccessors(propertyId, &getter, &setter, requestContext))
- {
- Var value = nullptr;
- if (!JavascriptConversion::PropertyQueryFlagsToBoolean(dynamicObject->DynamicObject::GetPropertyQuery(instance, propertyId, &value, NULL, requestContext)) ||
- (requestContext->IsUndeclBlockVar(value) && (ActivationObject::Is(instance) || RootObjectBase::Is(instance))))
- {
- return FALSE;
- }
- }
- }
- else
- {
- if (!object->GetAccessors(propertyId, &getter, &setter, requestContext))
- {
- Var value = nullptr;
- if (!object->GetProperty(instance, propertyId, &value, NULL, requestContext) ||
- (requestContext->IsUndeclBlockVar(value) && (ActivationObject::Is(instance) || RootObjectBase::Is(instance))))
- {
- return FALSE;
- }
- }
- }
- return TRUE;
- }
- Var JavascriptOperators::GetOwnPropertyNoHostObjectForHeapEnum(Var instance, PropertyId propertyId, ScriptContext* requestContext, Var& getter, Var& setter)
- {
- AssertMsg(!TaggedNumber::Is(instance), "GetDataPropertyNoHostObject int passed");
- Assert(HasOwnPropertyNoHostObjectForHeapEnum(instance, propertyId, requestContext, getter, setter) || getter || setter);
- DynamicObject* dynamicObject = DynamicObject::FromVar(instance);
- getter = setter = NULL;
- if (NULL == dynamicObject)
- {
- return requestContext->GetLibrary()->GetUndefined();
- }
- Var returnVar = requestContext->GetLibrary()->GetUndefined();
- BOOL result = FALSE;
- if (dynamicObject->UseDynamicObjectForNoHostObjectAccess())
- {
- if (! dynamicObject->DynamicObject::GetAccessors(propertyId, &getter, &setter, requestContext))
- {
- result = JavascriptConversion::PropertyQueryFlagsToBoolean((dynamicObject->DynamicObject::GetPropertyQuery(instance, propertyId, &returnVar, NULL, requestContext)));
- }
- }
- else
- {
- if (! dynamicObject->GetAccessors(propertyId, &getter, &setter, requestContext))
- {
- result = dynamicObject->GetProperty(instance, propertyId, &returnVar, NULL, requestContext);
- }
- }
- if (result)
- {
- return returnVar;
- }
- return requestContext->GetLibrary()->GetUndefined();
- }
- BOOL JavascriptOperators::OP_HasOwnPropScoped(Var scope, PropertyId propertyId, Var defaultInstance, ScriptContext* scriptContext)
- {
- AssertMsg(scope == scriptContext->GetLibrary()->GetNull() || JavascriptArray::Is(scope),
- "Invalid scope chain pointer passed - should be null or an array");
- JavascriptArray* arrScope = JavascriptOperators::TryFromVar<JavascriptArray>(scope);
- if (arrScope)
- {
- Var instance = arrScope->DirectGetItem(0);
- return JavascriptOperators::OP_HasOwnProperty(instance, propertyId, scriptContext);
- }
- return JavascriptOperators::OP_HasOwnProperty(defaultInstance, propertyId, scriptContext);
- }
- BOOL JavascriptOperators::GetPropertyUnscopable(Var instance, RecyclableObject* propertyObject, PropertyId propertyId, Var* value, ScriptContext* requestContext, PropertyValueInfo* info)
- {
- return GetProperty_Internal<true>(instance, propertyObject, false, propertyId, value, requestContext, info);
- }
- BOOL JavascriptOperators::GetProperty(Var instance, RecyclableObject* propertyObject, PropertyId propertyId, Var* value, ScriptContext* requestContext, PropertyValueInfo* info)
- {
- return GetProperty_Internal<false>(instance, propertyObject, false, propertyId, value, requestContext, info);
- }
- BOOL JavascriptOperators::GetRootProperty(Var instance, PropertyId propertyId, Var* value, ScriptContext* requestContext, PropertyValueInfo* info)
- {
- return GetProperty_Internal<false>(instance, RecyclableObject::FromVar(instance), true, propertyId, value, requestContext, info);
- }
- BOOL JavascriptOperators::GetProperty_InternalSimple(Var instance, RecyclableObject* object, PropertyId propertyId, _Outptr_result_maybenull_ Var* value, ScriptContext* requestContext)
- {
- BOOL foundProperty = FALSE;
- Assert(value != nullptr);
- while (!JavascriptOperators::IsNull(object))
- {
- PropertyQueryFlags result = object->GetPropertyQuery(instance, propertyId, value, nullptr, requestContext);
- if (result != PropertyQueryFlags::Property_NotFound)
- {
- foundProperty = JavascriptConversion::PropertyQueryFlagsToBoolean(result);
- break;
- }
- if (object->SkipsPrototype())
- {
- break;
- }
- object = JavascriptOperators::GetPrototypeNoTrap(object);
- }
- if (!foundProperty)
- {
- *value = requestContext->GetMissingPropertyResult();
- }
- return foundProperty;
- }
- template <bool unscopables>
- BOOL JavascriptOperators::GetProperty_Internal(Var instance, RecyclableObject* propertyObject, const bool isRoot, PropertyId propertyId, Var* value, ScriptContext* requestContext, PropertyValueInfo* info)
- {
- if (TaggedNumber::Is(instance))
- {
- PropertyValueInfo::ClearCacheInfo(info);
- }
- RecyclableObject* object = propertyObject;
- BOOL foundProperty = FALSE;
- if (isRoot)
- {
- Assert(RootObjectBase::Is(object));
- RootObjectBase* rootObject = static_cast<RootObjectBase*>(object);
- foundProperty = rootObject->GetRootProperty(instance, propertyId, value, info, requestContext);
- }
- while (!foundProperty && !JavascriptOperators::IsNull(object))
- {
- if (unscopables && IsPropertyUnscopable(object, propertyId))
- {
- break;
- }
- else
- {
- PropertyQueryFlags result = object->GetPropertyQuery(instance, propertyId, value, info, requestContext);
- if (result != PropertyQueryFlags::Property_NotFound)
- {
- foundProperty = JavascriptConversion::PropertyQueryFlagsToBoolean(result);
- break;
- }
- }
- if (object->SkipsPrototype())
- {
- break;
- }
- object = JavascriptOperators::GetPrototypeNoTrap(object);
- }
- if (foundProperty)
- {
- #if ENABLE_FIXED_FIELDS && DBG
- if (DynamicObject::Is(object))
- {
- DynamicObject* dynamicObject = (DynamicObject*)object;
- DynamicTypeHandler* dynamicTypeHandler = dynamicObject->GetDynamicType()->GetTypeHandler();
- Var property;
- if (dynamicTypeHandler->CheckFixedProperty(requestContext->GetPropertyName(propertyId), &property, requestContext))
- {
- bool skipAssert = false;
- if (value != nullptr && Js::RecyclableObject::Is(property))
- {
- Js::RecyclableObject* pObject = Js::RecyclableObject::FromVar(property);
- Js::RecyclableObject* pValue = Js::RecyclableObject::FromVar(*value);
- if (pValue->GetScriptContext() != pObject->GetScriptContext())
- {
- // value was marshaled. skip check
- skipAssert = true;
- }
- }
- Assert(skipAssert || value == nullptr || *value == property);
- }
- }
- #endif
- // Don't cache the information if the value is undecl block var
- // REVIEW: We might want to only check this if we need to (For LdRootFld or ScopedLdFld)
- // Also we might want to throw here instead of checking it again in the caller
- if (value && !requestContext->IsUndeclBlockVar(*value) && !WithScopeObject::Is(object))
- {
- CacheOperators::CachePropertyRead(propertyObject, object, isRoot, propertyId, false, info, requestContext);
- }
- #ifdef TELEMETRY_JSO
- if (TELEMETRY_PROPERTY_OPCODE_FILTER(propertyId))
- {
- requestContext->GetTelemetry().GetOpcodeTelemetry().GetProperty(instance, propertyId, value, /*successful: */true);
- }
- #endif
- return TRUE;
- }
- else
- {
- #ifdef MISSING_PROPERTY_STATS
- if (PHASE_STATS1(MissingPropertyCachePhase))
- {
- requestContext->RecordMissingPropertyMiss();
- }
- #endif
- if (PHASE_TRACE1(MissingPropertyCachePhase))
- {
- Output::Print(_u("MissingPropertyCaching: Missing property %d on slow path.\n"), propertyId);
- }
- TryCacheMissingProperty(instance, propertyObject, isRoot, propertyId, requestContext, info);
- #if defined(TELEMETRY_JSO) || defined(TELEMETRY_AddToCache) // enabled for `TELEMETRY_AddToCache`, because this is the property-not-found codepath where the normal TELEMETRY_AddToCache code wouldn't be executed.
- if (TELEMETRY_PROPERTY_OPCODE_FILTER(propertyId))
- {
- if (info && info->AllowResizingPolymorphicInlineCache()) // If in interpreted mode, not JIT.
- {
- requestContext->GetTelemetry().GetOpcodeTelemetry().GetProperty(instance, propertyId, nullptr);
- }
- }
- #endif
- *value = requestContext->GetMissingPropertyResult();
- return FALSE;
- }
- }
- // If the given instance is a type where we can cache missing properties, then cache that the given property ID is missing.
- // cacheInstance is used as startingObject in CachePropertyRead, and might be instance's proto if we are fetching a super property (see #3064).
- void JavascriptOperators::TryCacheMissingProperty(Var instance, Var cacheInstance, bool isRoot, PropertyId propertyId, ScriptContext* requestContext, _Inout_ PropertyValueInfo * info)
- {
- // Here, any well-behaved subclasses of DynamicObject can opt in to getting included in the missing property cache.
- // For now, we only include basic objects and arrays. CustomExternalObject in particular is problematic because in
- // some cases it can add new properties without transitioning its type handler.
- if (PHASE_OFF1(MissingPropertyCachePhase) || isRoot || !(DynamicObject::Is(instance) || DynamicObject::IsAnyArray(instance)))
- {
- return;
- }
- DynamicTypeHandler* handler = DynamicObject::UnsafeFromVar(instance)->GetDynamicType()->GetTypeHandler();
- // Only cache missing property lookups for non-root field loads on objects that have PathTypeHandlers, because only these types have the right behavior
- // when the missing property is later added. DictionaryTypeHandler's introduce the possibility that a stale TypePropertyCache entry with isMissing==true can
- // be left in the cache after the property has been installed in the object's prototype chain. Other changes to optimize accesses to objects that don't
- // override special symbols make it unnecessary to introduce an invalidation scheme to deal with DictionaryTypeHandler's.
- if (!handler->IsPathTypeHandler())
- {
- return;
- }
- #ifdef MISSING_PROPERTY_STATS
- if (PHASE_STATS1(MissingPropertyCachePhase))
- {
- requestContext->RecordMissingPropertyCacheAttempt();
- }
- #endif
- if (PHASE_TRACE1(MissingPropertyCachePhase))
- {
- Output::Print(_u("MissingPropertyCache: Caching missing property for property %d.\n"), propertyId);
- }
- PropertyValueInfo::Set(info, requestContext->GetLibrary()->GetMissingPropertyHolder(), 0);
- CacheOperators::CachePropertyRead(cacheInstance, requestContext->GetLibrary()->GetMissingPropertyHolder(), isRoot, propertyId, true /*isMissing*/, info, requestContext);
- }
- template<bool OutputExistence, typename PropertyKeyType> PropertyQueryFlags QueryGetOrHasProperty(
- Var originalInstance, RecyclableObject* object, PropertyKeyType propertyKey, Var* value, PropertyValueInfo* info, ScriptContext* requestContext);
- template<> PropertyQueryFlags QueryGetOrHasProperty<false /*OutputExistence*/, PropertyId /*PropertyKeyType*/>(
- Var originalInstance, RecyclableObject* object, PropertyId propertyKey, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
- {
- return object->GetPropertyQuery(originalInstance, propertyKey, value, info, requestContext);
- }
- template<> PropertyQueryFlags QueryGetOrHasProperty<false /*OutputExistence*/, JavascriptString* /*PropertyKeyType*/>(
- Var originalInstance, RecyclableObject* object, JavascriptString* propertyKey, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
- {
- return object->GetPropertyQuery(originalInstance, propertyKey, value, info, requestContext);
- }
- template<> PropertyQueryFlags QueryGetOrHasProperty<true /*OutputExistence*/, PropertyId /*PropertyKeyType*/>(
- Var originalInstance, RecyclableObject* object, PropertyId propertyKey, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
- {
- PropertyQueryFlags result = object->HasPropertyQuery(propertyKey, info);
- *value = JavascriptBoolean::ToVar(JavascriptConversion::PropertyQueryFlagsToBoolean(result), requestContext);
- return result;
- }
- template<bool OutputExistence, typename PropertyKeyType>
- BOOL JavascriptOperators::GetPropertyWPCache(Var instance, RecyclableObject* propertyObject, PropertyKeyType propertyKey, Var* value, ScriptContext* requestContext, _Inout_ PropertyValueInfo * info)
- {
- Assert(value);
- RecyclableObject* object = propertyObject;
- while (!JavascriptOperators::IsNull(object))
- {
- PropertyQueryFlags result = QueryGetOrHasProperty<OutputExistence>(instance, object, propertyKey, value, info, requestContext);
- if (result != PropertyQueryFlags::Property_NotFound)
- {
- if (!WithScopeObject::Is(object) && info->GetPropertyRecordUsageCache())
- {
- PropertyId propertyId = info->GetPropertyRecordUsageCache()->GetPropertyRecord()->GetPropertyId();
- CacheOperators::CachePropertyRead(instance, object, false, propertyId, false, info, requestContext);
- }
- return JavascriptConversion::PropertyQueryFlagsToBoolean(result);
- }
- // SkipsPrototype refers only to the Get operation, not Has. Some objects like CustomExternalObject respond
- // to HasPropertyQuery with info only about the object itself and GetPropertyQuery with info about its prototype chain.
- // For consistency with the behavior of JavascriptOperators::HasProperty, don't skip prototypes when outputting existence.
- if (!OutputExistence && object->SkipsPrototype())
- {
- break;
- }
- object = JavascriptOperators::GetPrototypeNoTrap(object);
- }
- if (info->GetPropertyRecordUsageCache())
- {
- TryCacheMissingProperty(instance, instance, false /*isRoot*/, info->GetPropertyRecordUsageCache()->GetPropertyRecord()->GetPropertyId(), requestContext, info);
- }
- *value = OutputExistence
- ? requestContext->GetLibrary()->GetFalse()
- : requestContext->GetMissingPropertyResult();
- return FALSE;
- }
- bool JavascriptOperators::GetPropertyObjectForElementAccess(
- _In_ Var instance,
- _In_ Var index,
- _In_ ScriptContext* scriptContext,
- _Out_ RecyclableObject** propertyObject,
- _In_ rtErrors error)
- {
- BOOL isNullOrUndefined = !GetPropertyObject(instance, scriptContext, propertyObject);
- Assert(*propertyObject == instance || TaggedNumber::Is(instance));
- if (isNullOrUndefined)
- {
- if (!scriptContext->GetThreadContext()->RecordImplicitException())
- {
- return false;
- }
- JavascriptError::ThrowTypeError(scriptContext, error, GetPropertyDisplayNameForError(index, scriptContext));
- }
- return true;
- }
- bool JavascriptOperators::GetPropertyObjectForSetElementI(
- _In_ Var instance,
- _In_ Var index,
- _In_ ScriptContext* scriptContext,
- _Out_ RecyclableObject** propertyObject)
- {
- return GetPropertyObjectForElementAccess(instance, index, scriptContext, propertyObject, JSERR_Property_CannotSet_NullOrUndefined);
- }
- bool JavascriptOperators::GetPropertyObjectForGetElementI(
- _In_ Var instance,
- _In_ Var index,
- _In_ ScriptContext* scriptContext,
- _Out_ RecyclableObject** propertyObject)
- {
- return GetPropertyObjectForElementAccess(instance, index, scriptContext, propertyObject, JSERR_Property_CannotGet_NullOrUndefined);
- }
- BOOL JavascriptOperators::GetPropertyObject(Var instance, ScriptContext * scriptContext, RecyclableObject** propertyObject)
- {
- Assert(propertyObject);
- if (TaggedNumber::Is(instance))
- {
- *propertyObject = scriptContext->GetLibrary()->GetNumberPrototype();
- return TRUE;
- }
- RecyclableObject* object = RecyclableObject::UnsafeFromVar(instance);
- *propertyObject = object;
- if (JavascriptOperators::IsUndefinedOrNull(object))
- {
- return FALSE;
- }
- return TRUE;
- }
- #if DBG
- BOOL JavascriptOperators::IsPropertyObject(RecyclableObject * instance)
- {
- TypeId typeId = JavascriptOperators::GetTypeId(instance);
- return (typeId != TypeIds_Integer && typeId != TypeIds_Null && typeId != TypeIds_Undefined);
- }
- #endif
- Var JavascriptOperators::OP_GetProperty(Var instance, PropertyId propertyId, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_GetProperty);
- RecyclableObject* object = nullptr;
- if (FALSE == JavascriptOperators::GetPropertyObject(instance, scriptContext, &object))
- {
- if (scriptContext->GetThreadContext()->RecordImplicitException())
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_CannotGet_NullOrUndefined, scriptContext->GetPropertyName(propertyId)->GetBuffer());
- }
- else
- {
- return scriptContext->GetLibrary()->GetUndefined();
- }
- }
- Var result = JavascriptOperators::GetPropertyNoCache(instance, object, propertyId, scriptContext);
- AssertMsg(result != nullptr, "result null in OP_GetProperty");
- return result;
- JIT_HELPER_END(Op_GetProperty);
- }
- Var JavascriptOperators::OP_GetRootProperty(Var instance, PropertyId propertyId, PropertyValueInfo * info, ScriptContext* scriptContext)
- {
- AssertMsg(RootObjectBase::Is(instance), "Root must be an object!");
- Var value = nullptr;
- if (JavascriptOperators::GetRootProperty(RecyclableObject::FromVar(instance), propertyId, &value, scriptContext, info))
- {
- if (scriptContext->IsUndeclBlockVar(value) && scriptContext->GetThreadContext()->RecordImplicitException())
- {
- JavascriptError::ThrowReferenceError(scriptContext, JSERR_UseBeforeDeclaration);
- }
- return value;
- }
- const char16* propertyName = scriptContext->GetPropertyName(propertyId)->GetBuffer();
- JavascriptFunction * caller = nullptr;
- if (JavascriptStackWalker::GetCaller(&caller, scriptContext))
- {
- FunctionBody * callerBody = caller->GetFunctionBody();
- if (callerBody && callerBody->GetUtf8SourceInfo()->GetIsXDomain())
- {
- propertyName = nullptr;
- }
- }
- // Don't error if we disabled implicit calls
- if (scriptContext->GetThreadContext()->RecordImplicitException())
- {
- JavascriptError::ThrowReferenceError(scriptContext, JSERR_UndefVariable, propertyName);
- }
- return scriptContext->GetMissingPropertyResult();
- }
- Var JavascriptOperators::OP_GetThisScoped(FrameDisplay *pScope, Var defaultInstance, ScriptContext* scriptContext)
- {
- // NOTE: If changes are made to this logic be sure to update the debuggers as well
- int length = pScope->GetLength();
- for (int i = 0; i < length; i += 1)
- {
- Var value = nullptr;
- RecyclableObject *obj = RecyclableObject::FromVar(pScope->GetItem(i));
- if (JavascriptOperators::GetProperty(obj, Js::PropertyIds::_this, &value, scriptContext))
- {
- return value;
- }
- }
- return defaultInstance;
- }
- Var JavascriptOperators::OP_UnwrapWithObj(Var aValue)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(Op_UnwrapWithObj);
- return RecyclableObject::FromVar(aValue)->GetThisObjectOrUnWrap();
- JIT_HELPER_END(Op_UnwrapWithObj);
- }
- Var JavascriptOperators::OP_GetInstanceScoped(FrameDisplay *pScope, PropertyId propertyId, Var rootObject, Var* thisVar, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_GetInstanceScoped);
- // Similar to GetPropertyScoped, but instead of returning the property value, we return the instance that
- // owns it, or the global object if no instance is found.
- int i;
- int length = pScope->GetLength();
- for (i = 0; i < length; i++)
- {
- RecyclableObject *obj = (RecyclableObject*)pScope->GetItem(i);
- if (JavascriptOperators::HasProperty(obj, propertyId))
- {
- // HasProperty will call WithObjects HasProperty which will do the filtering
- // All we have to do here is unwrap the object hence the api call
- *thisVar = obj->GetThisObjectOrUnWrap();
- return *thisVar;
- }
- }
- *thisVar = scriptContext->GetLibrary()->GetUndefined();
- if (rootObject != scriptContext->GetGlobalObject())
- {
- if (JavascriptOperators::OP_HasProperty(rootObject, propertyId, scriptContext))
- {
- return rootObject;
- }
- }
- return scriptContext->GetGlobalObject();
- JIT_HELPER_END(Op_GetInstanceScoped);
- }
- Var JavascriptOperators::GetPropertyReference(RecyclableObject *instance, PropertyId propertyId, ScriptContext* requestContext)
- {
- Var value = nullptr;
- PropertyValueInfo info;
- if (JavascriptOperators::GetPropertyReference(instance, propertyId, &value, requestContext, &info))
- {
- Assert(value != nullptr);
- return value;
- }
- return requestContext->GetMissingPropertyResult();
- }
- BOOL JavascriptOperators::GetPropertyReference(Var instance, RecyclableObject* propertyObject, PropertyId propertyId, Var* value, ScriptContext* requestContext, PropertyValueInfo* info)
- {
- return GetPropertyReference_Internal(instance, propertyObject, false, propertyId, value, requestContext, info);
- }
- BOOL JavascriptOperators::GetRootPropertyReference(RecyclableObject* instance, PropertyId propertyId, Var* value, ScriptContext* requestContext, PropertyValueInfo* info)
- {
- return GetPropertyReference_Internal(instance, instance, true, propertyId, value, requestContext, info);
- }
- BOOL JavascriptOperators::PropertyReferenceWalkUnscopable(Var instance, RecyclableObject** propertyObject, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
- {
- return PropertyReferenceWalk_Impl<true>(instance, propertyObject, propertyId, value, info, requestContext);
- }
- BOOL JavascriptOperators::PropertyReferenceWalk(Var instance, RecyclableObject** propertyObject, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
- {
- return PropertyReferenceWalk_Impl<false>(instance, propertyObject, propertyId, value, info, requestContext);
- }
- template <bool unscopables>
- BOOL JavascriptOperators::PropertyReferenceWalk_Impl(Var instance, RecyclableObject** propertyObject, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
- {
- BOOL foundProperty = false;
- RecyclableObject* object = *propertyObject;
- while (!foundProperty && !JavascriptOperators::IsNull(object))
- {
- if (unscopables && JavascriptOperators::IsPropertyUnscopable(object, propertyId))
- {
- break;
- }
- else
- {
- PropertyQueryFlags result = object->GetPropertyReferenceQuery(instance, propertyId, value, info, requestContext);
- if (result != PropertyQueryFlags::Property_NotFound)
- {
- foundProperty = JavascriptConversion::PropertyQueryFlagsToBoolean(result);
- break;
- }
- }
- if (object->SkipsPrototype())
- {
- break; // will return false
- }
- object = JavascriptOperators::GetPrototypeNoTrap(object);
- }
- *propertyObject = object;
- return foundProperty;
- }
- BOOL JavascriptOperators::GetPropertyReference_Internal(Var instance, RecyclableObject* propertyObject, const bool isRoot, PropertyId propertyId, Var* value, ScriptContext* requestContext, PropertyValueInfo* info)
- {
- if (TaggedNumber::Is(instance))
- {
- PropertyValueInfo::ClearCacheInfo(info);
- }
- BOOL foundProperty = FALSE;
- RecyclableObject* object = propertyObject;
- if (isRoot)
- {
- foundProperty = RootObjectBase::FromVar(object)->GetRootPropertyReference(instance, propertyId, value, info, requestContext);
- }
- if (!foundProperty)
- {
- foundProperty = PropertyReferenceWalk(instance, &object, propertyId, value, info, requestContext);
- }
- if (!foundProperty)
- {
- #if defined(TELEMETRY_JSO) || defined(TELEMETRY_AddToCache) // enabled for `TELEMETRY_AddToCache`, because this is the property-not-found codepath where the normal TELEMETRY_AddToCache code wouldn't be executed.
- if (TELEMETRY_PROPERTY_OPCODE_FILTER(propertyId))
- {
- if (info && info->AllowResizingPolymorphicInlineCache()) // If in interpreted mode, not JIT.
- {
- requestContext->GetTelemetry().GetOpcodeTelemetry().GetProperty(instance, propertyId, nullptr);
- }
- }
- #endif
- *value = requestContext->GetMissingPropertyResult();
- return foundProperty;
- }
- if (requestContext->IsUndeclBlockVar(*value))
- {
- JavascriptError::ThrowReferenceError(requestContext, JSERR_UseBeforeDeclaration);
- }
- #if ENABLE_FIXED_FIELDS && DBG
- if (DynamicObject::Is(object))
- {
- DynamicObject* dynamicObject = (DynamicObject*)object;
- DynamicTypeHandler* dynamicTypeHandler = dynamicObject->GetDynamicType()->GetTypeHandler();
- Var property = nullptr;
- if (dynamicTypeHandler->CheckFixedProperty(requestContext->GetPropertyName(propertyId), &property, requestContext))
- {
- Assert(value == nullptr || *value == property);
- }
- }
- #endif
- CacheOperators::CachePropertyRead(instance, object, isRoot, propertyId, false, info, requestContext);
- return TRUE;
- }
- template <typename PropertyKeyType, bool unscopable>
- DescriptorFlags JavascriptOperators::GetterSetter_Impl(RecyclableObject* instance, PropertyKeyType propertyKey, Var* setterValue, PropertyValueInfo* info, ScriptContext* scriptContext)
- {
- DescriptorFlags flags = None;
- RecyclableObject* object = instance;
- while (flags == None && !JavascriptOperators::IsNull(object))
- {
- if (unscopable && IsPropertyUnscopable(object, propertyKey))
- {
- break;
- }
- else
- {
- flags = object->GetSetter(propertyKey, setterValue, info, scriptContext);
- if (flags != None)
- {
- break;
- }
- }
- // CONSIDER: we should add SkipsPrototype support. DOM has no ES 5 concepts built in that aren't
- // already part of our prototype objects which are chakra objects.
- object = object->GetPrototype();
- }
- return flags;
- }
- DescriptorFlags JavascriptOperators::GetterSetterUnscopable(RecyclableObject* instance, PropertyId propertyId, Var* setterValue, PropertyValueInfo* info, ScriptContext* scriptContext)
- {
- return GetterSetter_Impl<PropertyId, true>(instance, propertyId, setterValue, info, scriptContext);
- }
- DescriptorFlags JavascriptOperators::GetterSetter(RecyclableObject* instance, PropertyId propertyId, Var* setterValue, PropertyValueInfo* info, ScriptContext* scriptContext)
- {
- return GetterSetter_Impl<PropertyId, false>(instance, propertyId, setterValue, info, scriptContext);
- }
- DescriptorFlags JavascriptOperators::GetterSetter(RecyclableObject* instance, JavascriptString * propertyName, Var* setterValue, PropertyValueInfo* info, ScriptContext* scriptContext)
- {
- return GetterSetter_Impl<JavascriptString*, false>(instance, propertyName, setterValue, info, scriptContext);
- }
- void JavascriptOperators::OP_InvalidateProtoCaches(PropertyId propertyId, ScriptContext *scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_HEADER(InvalidateProtoCaches, reentrancylock, scriptContext->GetThreadContext());
- scriptContext->InvalidateProtoCaches(propertyId);
- JIT_HELPER_END(InvalidateProtoCaches);
- }
- // Checks to see if any object in the prototype chain has a property descriptor for the given index
- // that specifies either an accessor or a non-writable attribute.
- // If TRUE, check flags for details.
- BOOL JavascriptOperators::CheckPrototypesForAccessorOrNonWritableItem(RecyclableObject* instance, uint32 index,
- Var* setterValue, DescriptorFlags *flags, ScriptContext* scriptContext, BOOL skipPrototypeCheck /* = FALSE */)
- {
- Assert(setterValue);
- Assert(flags);
- // Do a quick walk up the prototype chain to see if any of the prototypes has ever had ANY setter or non-writable property.
- if (CheckIfObjectAndPrototypeChainHasOnlyWritableDataProperties(instance))
- {
- return FALSE;
- }
- RecyclableObject* object = instance;
- while (!JavascriptOperators::IsNull(object))
- {
- *flags = object->GetItemSetter(index, setterValue, scriptContext);
- if (*flags != None || skipPrototypeCheck)
- {
- break;
- }
- object = object->GetPrototype();
- }
- return ((*flags & Accessor) == Accessor) || ((*flags & Proxy) == Proxy) || ((*flags & Data) == Data && (*flags & Writable) == None);
- }
- BOOL JavascriptOperators::SetGlobalPropertyNoHost(char16 const * propertyName, charcount_t propertyLength, Var value, ScriptContext * scriptContext)
- {
- GlobalObject * globalObject = scriptContext->GetGlobalObject();
- uint32 index;
- PropertyRecord const * propertyRecord = nullptr;
- IndexType indexType = GetIndexTypeFromString(propertyName, propertyLength, scriptContext, &index, &propertyRecord, true);
- if (indexType == IndexType_Number)
- {
- return globalObject->DynamicObject::SetItem(index, value, PropertyOperation_None);
- }
- return globalObject->DynamicObject::SetProperty(propertyRecord->GetPropertyId(), value, PropertyOperation_None, NULL);
- }
- template<typename PropertyKeyType>
- BOOL JavascriptOperators::SetPropertyWPCache(Var receiver, RecyclableObject* object, PropertyKeyType propertyKey, Var newValue, ScriptContext* requestContext, PropertyOperationFlags propertyOperationFlags, _Inout_ PropertyValueInfo * info)
- {
- if (receiver)
- {
- AnalysisAssert(object);
- Assert(!TaggedNumber::Is(receiver));
- Var setterValueOrProxy = nullptr;
- DescriptorFlags flags = None;
- if (JavascriptOperators::CheckPrototypesForAccessorOrNonWritableProperty(object, propertyKey, &setterValueOrProxy, &flags, info, requestContext))
- {
- if ((flags & Accessor) == Accessor)
- {
- if (JavascriptError::ThrowIfStrictModeUndefinedSetter(propertyOperationFlags, setterValueOrProxy, requestContext))
- {
- return TRUE;
- }
- if (setterValueOrProxy)
- {
- if (!WithScopeObject::Is(receiver) && info->GetPropertyRecordUsageCache() && !JavascriptOperators::IsUndefinedAccessor(setterValueOrProxy, requestContext))
- {
- CacheOperators::CachePropertyWrite(RecyclableObject::FromVar(receiver), false, object->GetType(), info->GetPropertyRecordUsageCache()->GetPropertyRecord()->GetPropertyId(), info, requestContext);
- }
- receiver = (RecyclableObject::FromVar(receiver))->GetThisObjectOrUnWrap();
- RecyclableObject* func = RecyclableObject::FromVar(setterValueOrProxy);
- JavascriptOperators::CallSetter(func, receiver, newValue, requestContext);
- }
- return TRUE;
- }
- else if ((flags & Proxy) == Proxy)
- {
- Assert(JavascriptProxy::Is(setterValueOrProxy));
- JavascriptProxy* proxy = JavascriptProxy::FromVar(setterValueOrProxy);
- auto fn = [&](RecyclableObject* target) -> BOOL {
- return JavascriptOperators::SetPropertyWPCache(receiver, target, propertyKey, newValue, requestContext, propertyOperationFlags, info);
- };
- if (info->GetPropertyRecordUsageCache())
- {
- PropertyValueInfo::SetNoCache(info, proxy);
- PropertyValueInfo::DisablePrototypeCache(info, proxy);
- }
- return proxy->SetPropertyTrap(receiver, JavascriptProxy::SetPropertyTrapKind::SetPropertyWPCacheKind, propertyKey, newValue, requestContext);
- }
- else
- {
- Assert((flags & Data) == Data && (flags & Writable) == None);
- requestContext->GetThreadContext()->AddImplicitCallFlags(ImplicitCall_NoOpSet);
- JavascriptError::ThrowCantAssignIfStrictMode(propertyOperationFlags, requestContext);
- return FALSE;
- }
- }
- else if (!JavascriptOperators::IsObject(receiver))
- {
- JavascriptError::ThrowCantAssignIfStrictMode(propertyOperationFlags, requestContext);
- return FALSE;
- }
- RecyclableObject* receiverObject = RecyclableObject::FromVar(receiver);
- if (receiver != object)
- {
- // If the receiver object has the property and it is an accessor then return false
- PropertyDescriptor existingDesc;
- if (JavascriptOperators::GetOwnPropertyDescriptor(receiverObject, propertyKey, requestContext, &existingDesc)
- && existingDesc.IsAccessorDescriptor())
- {
- return FALSE;
- }
- }
- Type *typeWithoutProperty = object->GetType();
- // in 9.1.9, step 5, we should return false if receiver is not object, and that will happen in default RecyclableObject operation anyhow.
- if (receiverObject->SetProperty(propertyKey, newValue, propertyOperationFlags, info))
- {
- if (!JavascriptProxy::Is(receiver) && info->GetPropertyRecordUsageCache() && info->GetFlags() != InlineCacheSetterFlag && !object->IsExternal())
- {
- CacheOperators::CachePropertyWrite(RecyclableObject::FromVar(receiver), false, typeWithoutProperty, info->GetPropertyRecordUsageCache()->GetPropertyRecord()->GetPropertyId(), info, requestContext);
- if (info->GetInstance() == receiverObject)
- {
- PropertyValueInfo::SetCacheInfo(info, info->GetPropertyRecordUsageCache()->GetLdElemInlineCache(), info->AllowResizingPolymorphicInlineCache());
- CacheOperators::CachePropertyRead(object, receiverObject, false, info->GetPropertyRecordUsageCache()->GetPropertyRecord()->GetPropertyId(), false, info, requestContext);
- }
- }
- return TRUE;
- }
- }
- return FALSE;
- }
- BOOL JavascriptOperators::SetItemOnTaggedNumber(Var receiver, RecyclableObject* object, uint32 index, Var newValue, ScriptContext* requestContext,
- PropertyOperationFlags propertyOperationFlags)
- {
- Assert(TaggedNumber::Is(receiver));
- if (requestContext->optimizationOverrides.GetSideEffects() & SideEffects_Accessor)
- {
- Var setterValueOrProxy = nullptr;
- DescriptorFlags flags = None;
- if (object == nullptr)
- {
- GetPropertyObject(receiver, requestContext, &object);
- }
- if (JavascriptOperators::CheckPrototypesForAccessorOrNonWritableItem(object, index, &setterValueOrProxy, &flags, requestContext))
- {
- if ((flags & Accessor) == Accessor)
- {
- if (JavascriptError::ThrowIfStrictModeUndefinedSetter(propertyOperationFlags, setterValueOrProxy, requestContext))
- {
- return TRUE;
- }
- if (setterValueOrProxy)
- {
- RecyclableObject* func = RecyclableObject::FromVar(setterValueOrProxy);
- JavascriptOperators::CallSetter(func, receiver, newValue, requestContext);
- return TRUE;
- }
- }
- else if ((flags & Proxy) == Proxy)
- {
- Assert(JavascriptProxy::Is(setterValueOrProxy));
- JavascriptProxy* proxy = JavascriptProxy::FromVar(setterValueOrProxy);
- const PropertyRecord* propertyRecord = nullptr;
- proxy->PropertyIdFromInt(index, &propertyRecord);
- return proxy->SetPropertyTrap(receiver, JavascriptProxy::SetPropertyTrapKind::SetItemOnTaggedNumberKind, propertyRecord->GetPropertyId(), newValue, requestContext);
- }
- else
- {
- Assert((flags & Data) == Data && (flags & Writable) == None);
- JavascriptError::ThrowCantAssignIfStrictMode(propertyOperationFlags, requestContext);
- }
- }
- }
- JavascriptError::ThrowCantAssignIfStrictMode(propertyOperationFlags, requestContext);
- return FALSE;
- }
- BOOL JavascriptOperators::SetPropertyOnTaggedNumber(Var receiver, RecyclableObject* object, PropertyId propertyId, Var newValue, ScriptContext* requestContext,
- PropertyOperationFlags propertyOperationFlags)
- {
- Assert (TaggedNumber::Is(receiver));
- if (requestContext->optimizationOverrides.GetSideEffects() & SideEffects_Accessor)
- {
- Var setterValueOrProxy = nullptr;
- PropertyValueInfo info;
- DescriptorFlags flags = None;
- if (object == nullptr)
- {
- GetPropertyObject(receiver, requestContext, &object);
- }
- if (JavascriptOperators::CheckPrototypesForAccessorOrNonWritableProperty(object, propertyId, &setterValueOrProxy, &flags, &info, requestContext))
- {
- if ((flags & Accessor) == Accessor)
- {
- if (JavascriptError::ThrowIfStrictModeUndefinedSetter(propertyOperationFlags, setterValueOrProxy, requestContext))
- {
- return TRUE;
- }
- if (setterValueOrProxy)
- {
- RecyclableObject* func = RecyclableObject::FromVar(setterValueOrProxy);
- Assert(info.GetFlags() == InlineCacheSetterFlag || info.GetPropertyIndex() == Constants::NoSlot);
- JavascriptOperators::CallSetter(func, receiver, newValue, requestContext);
- return TRUE;
- }
- }
- else if ((flags & Proxy) == Proxy)
- {
- Assert(JavascriptProxy::Is(setterValueOrProxy));
- JavascriptProxy* proxy = JavascriptProxy::FromVar(setterValueOrProxy);
- return proxy->SetPropertyTrap(receiver, JavascriptProxy::SetPropertyTrapKind::SetPropertyOnTaggedNumberKind, propertyId, newValue, requestContext);
- }
- else
- {
- Assert((flags & Data) == Data && (flags & Writable) == None);
- JavascriptError::ThrowCantAssignIfStrictMode(propertyOperationFlags, requestContext);
- }
- }
- }
- // Add implicit call flags, to bail out if field copy prop may propagate the wrong value.
- requestContext->GetThreadContext()->AddImplicitCallFlags(ImplicitCall_NoOpSet);
- JavascriptError::ThrowCantAssignIfStrictMode(propertyOperationFlags, requestContext);
- return FALSE;
- }
- BOOL JavascriptOperators::SetPropertyUnscopable(Var instance, RecyclableObject* receiver, PropertyId propertyId, Var newValue, PropertyValueInfo * info, ScriptContext* requestContext, PropertyOperationFlags propertyOperationFlags)
- {
- return SetProperty_Internal<true>(instance, receiver, false, propertyId, newValue, info, requestContext, propertyOperationFlags);
- }
- BOOL JavascriptOperators::SetProperty(Var receiver, RecyclableObject* object, PropertyId propertyId, Var newValue, PropertyValueInfo * info, ScriptContext* requestContext, PropertyOperationFlags propertyOperationFlags)
- {
- return SetProperty_Internal<false>(receiver, object, false, propertyId, newValue, info, requestContext, propertyOperationFlags);
- }
- BOOL JavascriptOperators::SetRootProperty(RecyclableObject* instance, PropertyId propertyId, Var newValue, PropertyValueInfo * info, ScriptContext* requestContext, PropertyOperationFlags propertyOperationFlags)
- {
- return SetProperty_Internal<false>(instance, instance, true, propertyId, newValue, info, requestContext, propertyOperationFlags);
- }
- // Returns true if a result was written.
- bool JavascriptOperators::SetAccessorOrNonWritableProperty(
- Var receiver,
- RecyclableObject* object,
- PropertyId propertyId,
- Var newValue,
- PropertyValueInfo * info,
- ScriptContext* requestContext,
- PropertyOperationFlags propertyOperationFlags,
- bool isRoot,
- bool allowUndecInConsoleScope,
- BOOL *result)
- {
- *result = FALSE;
- Var setterValueOrProxy = nullptr;
- DescriptorFlags flags = None;
- if ((isRoot && JavascriptOperators::CheckPrototypesForAccessorOrNonWritableRootProperty(object, propertyId, &setterValueOrProxy, &flags, info, requestContext)) ||
- (!isRoot && JavascriptOperators::CheckPrototypesForAccessorOrNonWritableProperty(object, propertyId, &setterValueOrProxy, &flags, info, requestContext)))
- {
- if ((flags & Accessor) == Accessor)
- {
- if (JavascriptError::ThrowIfStrictModeUndefinedSetter(propertyOperationFlags, setterValueOrProxy, requestContext) ||
- JavascriptError::ThrowIfNotExtensibleUndefinedSetter(propertyOperationFlags, setterValueOrProxy, requestContext))
- {
- *result = TRUE;
- return true;
- }
- if (setterValueOrProxy)
- {
- RecyclableObject* func = RecyclableObject::FromVar(setterValueOrProxy);
- Assert(!info || info->GetFlags() == InlineCacheSetterFlag || info->GetPropertyIndex() == Constants::NoSlot);
- if (WithScopeObject::Is(receiver))
- {
- receiver = (RecyclableObject::FromVar(receiver))->GetThisObjectOrUnWrap();
- }
- else if (!JavascriptOperators::IsUndefinedAccessor(setterValueOrProxy, requestContext))
- {
- CacheOperators::CachePropertyWrite(RecyclableObject::FromVar(receiver), isRoot, object->GetType(), propertyId, info, requestContext);
- }
- #ifdef ENABLE_MUTATION_BREAKPOINT
- if (MutationBreakpoint::IsFeatureEnabled(requestContext))
- {
- MutationBreakpoint::HandleSetProperty(requestContext, object, propertyId, newValue);
- }
- #endif
- JavascriptOperators::CallSetter(func, receiver, newValue, requestContext);
- }
- *result = TRUE;
- return true;
- }
- else if ((flags & Proxy) == Proxy)
- {
- Assert(JavascriptProxy::Is(setterValueOrProxy));
- JavascriptProxy* proxy = JavascriptProxy::FromVar(setterValueOrProxy);
- // We can't cache the property at this time. both target and handler can be changed outside of the proxy, so the inline cache needs to be
- // invalidate when target, handler, or handler prototype has changed. We don't have a way to achieve this yet.
- PropertyValueInfo::SetNoCache(info, proxy);
- PropertyValueInfo::DisablePrototypeCache(info, proxy); // We can't cache prototype property either
- *result = proxy->SetPropertyTrap(receiver, JavascriptProxy::SetPropertyTrapKind::SetPropertyKind, propertyId, newValue, requestContext);
- return true;
- }
- else
- {
- Assert((flags & Data) == Data && (flags & Writable) == None);
- if (!allowUndecInConsoleScope)
- {
- if (flags & Const)
- {
- JavascriptError::ThrowTypeError(requestContext, ERRAssignmentToConst);
- }
- JavascriptError::ThrowCantAssign(propertyOperationFlags, requestContext, propertyId);
- JavascriptError::ThrowCantAssignIfStrictMode(propertyOperationFlags, requestContext);
- *result = FALSE;
- return true;
- }
- }
- }
- return false;
- }
- template <bool unscopables>
- BOOL JavascriptOperators::SetProperty_Internal(Var receiver, RecyclableObject* object, const bool isRoot, PropertyId propertyId, Var newValue, PropertyValueInfo * info, ScriptContext* requestContext, PropertyOperationFlags propertyOperationFlags)
- {
- if (receiver == nullptr)
- {
- return FALSE;
- }
- Assert(!TaggedNumber::Is(receiver));
- BOOL setAccessorResult = FALSE;
- if (SetAccessorOrNonWritableProperty(receiver, object, propertyId, newValue, info, requestContext, propertyOperationFlags, isRoot, false, &setAccessorResult))
- {
- return setAccessorResult;
- }
- else if (!JavascriptOperators::IsObject(receiver))
- {
- JavascriptError::ThrowCantAssignIfStrictMode(propertyOperationFlags, requestContext);
- return FALSE;
- }
- #ifdef ENABLE_MUTATION_BREAKPOINT
- // Break on mutation if needed
- bool doNotUpdateCacheForMbp = MutationBreakpoint::IsFeatureEnabled(requestContext) ?
- MutationBreakpoint::HandleSetProperty(requestContext, object, propertyId, newValue) : false;
- #endif
- // Get the original type before setting the property
- Type *typeWithoutProperty = object->GetType();
- BOOL didSetProperty = false;
- if (isRoot)
- {
- AssertMsg(JavascriptOperators::GetTypeId(receiver) == TypeIds_GlobalObject
- || JavascriptOperators::GetTypeId(receiver) == TypeIds_ModuleRoot,
- "Root must be a global object!");
- RootObjectBase* rootObject = static_cast<RootObjectBase*>(receiver);
- didSetProperty = rootObject->SetRootProperty(propertyId, newValue, propertyOperationFlags, info);
- }
- else
- {
- RecyclableObject* instanceObject = RecyclableObject::FromVar(receiver);
- while (!JavascriptOperators::IsNull(instanceObject))
- {
- if (unscopables && JavascriptOperators::IsPropertyUnscopable(instanceObject, propertyId))
- {
- break;
- }
- else
- {
- didSetProperty = instanceObject->SetProperty(propertyId, newValue, propertyOperationFlags, info);
- if (didSetProperty || !unscopables)
- {
- break;
- }
- }
- instanceObject = JavascriptOperators::GetPrototypeNoTrap(instanceObject);
- }
- }
- if (didSetProperty)
- {
- bool updateCache = true;
- #ifdef ENABLE_MUTATION_BREAKPOINT
- updateCache = updateCache && !doNotUpdateCacheForMbp;
- #endif
- if (updateCache)
- {
- if (!JavascriptProxy::Is(receiver))
- {
- CacheOperators::CachePropertyWrite(RecyclableObject::FromVar(receiver), isRoot, typeWithoutProperty, propertyId, info, requestContext);
- }
- }
- return TRUE;
- }
- return FALSE;
- }
- BOOL JavascriptOperators::IsNumberFromNativeArray(Var instance, uint32 index, ScriptContext* scriptContext)
- {
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(instance);
- #endif
- Js::TypeId instanceType = JavascriptOperators::GetTypeId(instance);
- // Fast path for native and typed arrays.
- bool isNativeArray = instanceType == TypeIds_NativeIntArray || instanceType == TypeIds_NativeFloatArray;
- bool isTypedArray = instanceType >= TypeIds_Int8Array && instanceType <= TypeIds_Uint64Array;
- if (isNativeArray || isTypedArray)
- {
- // Check if the typed array is detached to prevent an exception in GetOwnItem
- if (isTypedArray && TypedArrayBase::IsDetachedTypedArray(instance))
- {
- return FALSE;
- }
- RecyclableObject* object = RecyclableObject::FromVar(instance);
- Var member = nullptr;
- // If the item is found in the array own body, then it is a number
- if (JavascriptOperators::GetOwnItem(object, index, &member, scriptContext)
- && !JavascriptOperators::IsUndefined(member))
- {
- return TRUE;
- }
- }
- return FALSE;
- }
- BOOL _Check_return_ _Success_(return) JavascriptOperators::GetAccessors(RecyclableObject* instance, PropertyId propertyId, ScriptContext* requestContext, _Out_ Var* getter, _Out_ Var* setter)
- {
- RecyclableObject* object = instance;
- while (!JavascriptOperators::IsNull(object))
- {
- if (object->GetAccessors(propertyId, getter, setter, requestContext))
- {
- *getter = JavascriptOperators::CanonicalizeAccessor(*getter, requestContext);
- *setter = JavascriptOperators::CanonicalizeAccessor(*setter, requestContext);
- return TRUE;
- }
- if (object->SkipsPrototype())
- {
- break;
- }
- object = JavascriptOperators::GetPrototype(object);
- }
- return FALSE;
- }
- BOOL JavascriptOperators::SetAccessors(RecyclableObject* instance, PropertyId propertyId, Var getter, Var setter, PropertyOperationFlags flags)
- {
- BOOL result = instance && instance->SetAccessors(propertyId, getter, setter, flags);
- return result;
- }
- BOOL JavascriptOperators::OP_SetProperty(Var instance, PropertyId propertyId, Var newValue, ScriptContext* scriptContext, PropertyValueInfo * info, PropertyOperationFlags flags, Var thisInstance)
- {
- // The call into ToObject(dynamicObject) is avoided here by checking for null and undefined and doing nothing when dynamicObject is a primitive value.
- if (thisInstance == nullptr)
- {
- thisInstance = instance;
- }
- TypeId typeId = JavascriptOperators::GetTypeId(instance);
- if (JavascriptOperators::IsUndefinedOrNullType(typeId))
- {
- if (scriptContext->GetThreadContext()->RecordImplicitException())
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_CannotSet_NullOrUndefined, scriptContext->GetPropertyName(propertyId)->GetBuffer());
- }
- return TRUE;
- }
- else if (typeId == TypeIds_VariantDate)
- {
- if (scriptContext->GetThreadContext()->RecordImplicitException())
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_VarDate, scriptContext->GetPropertyName(propertyId)->GetBuffer());
- }
- return TRUE;
- }
- if (!TaggedNumber::Is(instance) && !TaggedNumber::Is(thisInstance))
- {
- return JavascriptOperators::SetProperty(RecyclableObject::UnsafeFromVar(thisInstance), RecyclableObject::UnsafeFromVar(instance), propertyId, newValue, info, scriptContext, flags);
- }
- JavascriptError::ThrowCantAssignIfStrictMode(flags, scriptContext);
- return false;
- }
- BOOL JavascriptOperators::OP_StFunctionExpression(Var obj, PropertyId propertyId, Var newValue)
- {
- RecyclableObject* instance = RecyclableObject::FromVar(obj);
- JIT_HELPER_NOT_REENTRANT_HEADER(Op_StFunctionExpression, reentrancylock, instance->GetScriptContext()->GetThreadContext());
- instance->SetProperty(propertyId, newValue, PropertyOperation_None, NULL);
- instance->SetWritable(propertyId, FALSE);
- instance->SetConfigurable(propertyId, FALSE);
- return TRUE;
- JIT_HELPER_END(Op_StFunctionExpression);
- }
- BOOL JavascriptOperators::OP_InitClassMember(Var obj, PropertyId propertyId, Var newValue)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_InitClassMember);
- RecyclableObject* instance = RecyclableObject::FromVar(obj);
- PropertyOperationFlags flags = PropertyOperation_None;
- PropertyAttributes attributes = PropertyClassMemberDefaults;
- instance->SetPropertyWithAttributes(propertyId, newValue, attributes, NULL, flags);
- return TRUE;
- JIT_HELPER_END(Op_InitClassMember);
- }
- BOOL JavascriptOperators::OP_InitLetProperty(Var obj, PropertyId propertyId, Var newValue)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_InitLetFld);
- RecyclableObject* instance = RecyclableObject::FromVar(obj);
- PropertyOperationFlags flags = instance->GetScriptContext()->IsUndeclBlockVar(newValue) ? PropertyOperation_SpecialValue : PropertyOperation_None;
- PropertyAttributes attributes = PropertyLetDefaults;
- if (RootObjectBase::Is(instance))
- {
- attributes |= PropertyLetConstGlobal;
- }
- instance->SetPropertyWithAttributes(propertyId, newValue, attributes, NULL, (PropertyOperationFlags)(flags | PropertyOperation_AllowUndecl));
- return TRUE;
- JIT_HELPER_END(Op_InitLetFld);
- }
- BOOL JavascriptOperators::OP_InitConstProperty(Var obj, PropertyId propertyId, Var newValue)
- {
- RecyclableObject* instance = RecyclableObject::FromVar(obj);
- JIT_HELPER_REENTRANT_HEADER(Op_InitConstFld);
- PropertyOperationFlags flags = instance->GetScriptContext()->IsUndeclBlockVar(newValue) ? PropertyOperation_SpecialValue : PropertyOperation_None;
- PropertyAttributes attributes = PropertyConstDefaults;
- if (RootObjectBase::Is(instance))
- {
- attributes |= PropertyLetConstGlobal;
- }
- instance->SetPropertyWithAttributes(propertyId, newValue, attributes, NULL, (PropertyOperationFlags)(flags | PropertyOperation_AllowUndecl));
- return TRUE;
- JIT_HELPER_END(Op_InitConstFld);
- }
- BOOL JavascriptOperators::OP_InitUndeclRootLetProperty(Var obj, PropertyId propertyId)
- {
- RecyclableObject* instance = RecyclableObject::FromVar(obj);
- JIT_HELPER_NOT_REENTRANT_HEADER(Op_InitUndeclRootLetFld, reentrancylock, instance->GetScriptContext()->GetThreadContext());
- PropertyOperationFlags flags = static_cast<PropertyOperationFlags>(PropertyOperation_SpecialValue | PropertyOperation_AllowUndecl);
- PropertyAttributes attributes = PropertyLetDefaults | PropertyLetConstGlobal;
- instance->SetPropertyWithAttributes(propertyId, instance->GetLibrary()->GetUndeclBlockVar(), attributes, NULL, flags);
- return TRUE;
- JIT_HELPER_END(Op_InitUndeclRootLetFld);
- }
- BOOL JavascriptOperators::OP_InitUndeclRootConstProperty(Var obj, PropertyId propertyId)
- {
- RecyclableObject* instance = RecyclableObject::FromVar(obj);
- JIT_HELPER_NOT_REENTRANT_HEADER(Op_InitUndeclRootConstFld, reentrancylock, instance->GetScriptContext()->GetThreadContext());
- PropertyOperationFlags flags = static_cast<PropertyOperationFlags>(PropertyOperation_SpecialValue | PropertyOperation_AllowUndecl);
- PropertyAttributes attributes = PropertyConstDefaults | PropertyLetConstGlobal;
- instance->SetPropertyWithAttributes(propertyId, instance->GetLibrary()->GetUndeclBlockVar(), attributes, NULL, flags);
- return TRUE;
- JIT_HELPER_END(Op_InitUndeclRootConstFld);
- }
- BOOL JavascriptOperators::OP_InitUndeclConsoleLetProperty(Var obj, PropertyId propertyId)
- {
- FrameDisplay *pScope = (FrameDisplay*)obj;
- AssertMsg(ConsoleScopeActivationObject::Is((DynamicObject*)pScope->GetItem(pScope->GetLength() - 1)), "How come we got this opcode without ConsoleScopeActivationObject?");
- RecyclableObject* instance = RecyclableObject::FromVar(pScope->GetItem(0));
- JIT_HELPER_NOT_REENTRANT_HEADER(Op_InitUndeclConsoleLetFld, reentrancylock, instance->GetScriptContext()->GetThreadContext());
- PropertyOperationFlags flags = static_cast<PropertyOperationFlags>(PropertyOperation_SpecialValue | PropertyOperation_AllowUndecl);
- PropertyAttributes attributes = PropertyLetDefaults;
- instance->SetPropertyWithAttributes(propertyId, instance->GetLibrary()->GetUndeclBlockVar(), attributes, NULL, flags);
- return TRUE;
- JIT_HELPER_END(Op_InitUndeclConsoleLetFld);
- }
- BOOL JavascriptOperators::OP_InitUndeclConsoleConstProperty(Var obj, PropertyId propertyId)
- {
- FrameDisplay *pScope = (FrameDisplay*)obj;
- AssertMsg(ConsoleScopeActivationObject::Is((DynamicObject*)pScope->GetItem(pScope->GetLength() - 1)), "How come we got this opcode without ConsoleScopeActivationObject?");
- RecyclableObject* instance = RecyclableObject::FromVar(pScope->GetItem(0));
- JIT_HELPER_NOT_REENTRANT_HEADER(Op_InitUndeclConsoleConstFld, reentrancylock, instance->GetScriptContext()->GetThreadContext());
- PropertyOperationFlags flags = static_cast<PropertyOperationFlags>(PropertyOperation_SpecialValue | PropertyOperation_AllowUndecl);
- PropertyAttributes attributes = PropertyConstDefaults;
- instance->SetPropertyWithAttributes(propertyId, instance->GetLibrary()->GetUndeclBlockVar(), attributes, NULL, flags);
- return TRUE;
- JIT_HELPER_END(Op_InitUndeclConsoleConstFld);
- }
- BOOL JavascriptOperators::InitProperty(RecyclableObject* instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags)
- {
- return instance && instance->InitProperty(propertyId, newValue, flags);
- }
- BOOL JavascriptOperators::OP_InitProperty(Var instance, PropertyId propertyId, Var newValue)
- {
- if(TaggedNumber::Is(instance)) { return false; }
- return JavascriptOperators::InitProperty(RecyclableObject::FromVar(instance), propertyId, newValue);
- }
- BOOL JavascriptOperators::DeleteProperty(RecyclableObject* instance, PropertyId propertyId, PropertyOperationFlags propertyOperationFlags)
- {
- return DeleteProperty_Impl<false>(instance, propertyId, propertyOperationFlags);
- }
- bool JavascriptOperators::ShouldTryDeleteProperty(RecyclableObject* instance, JavascriptString *propertyNameString, PropertyRecord const **pPropertyRecord)
- {
- PropertyRecord const *propertyRecord = nullptr;
- if (!JavascriptOperators::CanShortcutOnUnknownPropertyName(instance))
- {
- instance->GetScriptContext()->GetOrAddPropertyRecord(propertyNameString, &propertyRecord);
- }
- else
- {
- instance->GetScriptContext()->FindPropertyRecord(propertyNameString, &propertyRecord);
- }
- if (propertyRecord == nullptr)
- {
- return false;
- }
- *pPropertyRecord = propertyRecord;
- return true;
- }
- BOOL JavascriptOperators::DeleteProperty(RecyclableObject* instance, JavascriptString *propertyNameString, PropertyOperationFlags propertyOperationFlags)
- {
- #ifdef ENABLE_MUTATION_BREAKPOINT
- ScriptContext *scriptContext = instance->GetScriptContext();
- if (MutationBreakpoint::IsFeatureEnabled(scriptContext)
- && scriptContext->HasMutationBreakpoints())
- {
- MutationBreakpoint::HandleDeleteProperty(scriptContext, instance, propertyNameString);
- }
- #endif
- return instance->DeleteProperty(propertyNameString, propertyOperationFlags);
- }
- BOOL JavascriptOperators::DeletePropertyUnscopables(RecyclableObject* instance, PropertyId propertyId, PropertyOperationFlags propertyOperationFlags)
- {
- return DeleteProperty_Impl<true>(instance, propertyId, propertyOperationFlags);
- }
- template<bool unscopables>
- BOOL JavascriptOperators::DeleteProperty_Impl(RecyclableObject* instance, PropertyId propertyId, PropertyOperationFlags propertyOperationFlags)
- {
- if (unscopables && JavascriptOperators::IsPropertyUnscopable(instance, propertyId))
- {
- return false;
- }
- #ifdef ENABLE_MUTATION_BREAKPOINT
- ScriptContext *scriptContext = instance->GetScriptContext();
- if (MutationBreakpoint::IsFeatureEnabled(scriptContext)
- && scriptContext->HasMutationBreakpoints())
- {
- MutationBreakpoint::HandleDeleteProperty(scriptContext, instance, propertyId);
- }
- #endif
- // !unscopables will hit the return statement on the first iteration
- return instance->DeleteProperty(propertyId, propertyOperationFlags);
- }
- Var JavascriptOperators::OP_DeleteProperty(Var instance, PropertyId propertyId, ScriptContext* scriptContext, PropertyOperationFlags propertyOperationFlags)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_DeleteProperty);
- if(TaggedNumber::Is(instance))
- {
- return scriptContext->GetLibrary()->GetTrue();
- }
- RecyclableObject* recyclableObject = RecyclableObject::FromVar(instance);
- if (JavascriptOperators::IsUndefinedOrNull(recyclableObject))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_CannotDelete_NullOrUndefined,
- scriptContext->GetPropertyName(propertyId)->GetBuffer());
- }
- return scriptContext->GetLibrary()->CreateBoolean(
- JavascriptOperators::DeleteProperty(recyclableObject, propertyId, propertyOperationFlags));
- JIT_HELPER_END(Op_DeleteProperty);
- }
- Var JavascriptOperators::OP_DeleteRootProperty(Var instance, PropertyId propertyId, ScriptContext* scriptContext, PropertyOperationFlags propertyOperationFlags)
- {
- // In Edge the root is an External Object which can call Dispose and thus, can have reentrancy.
- JIT_HELPER_REENTRANT_HEADER(Op_DeleteRootProperty);
- AssertMsg(RootObjectBase::Is(instance), "Root must be a global object!");
- RootObjectBase* rootObject = static_cast<RootObjectBase*>(instance);
- return scriptContext->GetLibrary()->CreateBoolean(
- rootObject->DeleteRootProperty(propertyId, propertyOperationFlags));
- JIT_HELPER_END(Op_DeleteRootProperty);
- }
- template <bool IsFromFullJit, class TInlineCache>
- inline void JavascriptOperators::PatchSetPropertyScoped(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, FrameDisplay *pDisplay, PropertyId propertyId, Var newValue, Var defaultInstance, PropertyOperationFlags propertyOperationFlags)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_PatchSetPropertyScoped);
- // Set the property using a scope stack rather than an individual instance.
- // Walk the stack until we find an instance that has the property and store
- // the new value there.
- ScriptContext *const scriptContext = functionBody->GetScriptContext();
- uint16 length = pDisplay->GetLength();
- RecyclableObject *object;
- PropertyValueInfo info;
- PropertyValueInfo::SetCacheInfo(&info, functionBody, inlineCache, inlineCacheIndex, !IsFromFullJit);
- bool allowUndecInConsoleScope = (propertyOperationFlags & PropertyOperation_AllowUndeclInConsoleScope) == PropertyOperation_AllowUndeclInConsoleScope;
- bool isLexicalThisSlotSymbol = (propertyId == PropertyIds::_this);
- for (uint16 i = 0; i < length; i++)
- {
- object = RecyclableObject::UnsafeFromVar(pDisplay->GetItem(i));
- AssertMsg(!ConsoleScopeActivationObject::Is(object) || (i == length - 1), "Invalid location for ConsoleScopeActivationObject");
- Type* type = object->GetType();
- if (CacheOperators::TrySetProperty<true, true, true, true, true, !TInlineCache::IsPolymorphic, TInlineCache::IsPolymorphic, false>(
- object, false, propertyId, newValue, scriptContext, propertyOperationFlags, nullptr, &info))
- {
- return;
- }
- // In scoped set property, we need to set the property when it is available; it could be a setter
- // or normal property. we need to check setter first, and if no setter is available, but HasProperty
- // is true, this must be a normal property.
- // TODO: merge OP_HasProperty and GetSetter in one pass if there is perf problem. In fastDOM we have quite
- // a lot of setters so separating the two might be actually faster.
- BOOL setAccessorResult = FALSE;
- if (SetAccessorOrNonWritableProperty(object, object, propertyId, newValue, &info, scriptContext, propertyOperationFlags, false, allowUndecInConsoleScope, &setAccessorResult))
- {
- return;
- }
- else if (!JavascriptOperators::IsObject(object))
- {
- JavascriptError::ThrowCantAssignIfStrictMode(propertyOperationFlags, scriptContext);
- }
- // Need to do a "get" of the current value (if any) to make sure that we're not writing to
- // let/const before declaration, but we need to disable implicit calls around the "get",
- // so we need to do a "has" first to make sure the "get" is valid (e.g., "get" on a HostDispatch
- // with implicit calls disabled will always "succeed").
- if (JavascriptOperators::HasProperty(object, propertyId))
- {
- DisableImplicitFlags disableImplicitFlags = scriptContext->GetThreadContext()->GetDisableImplicitFlags();
- scriptContext->GetThreadContext()->SetDisableImplicitFlags(DisableImplicitCallAndExceptionFlag);
- Var value;
- BOOL result = JavascriptOperators::GetProperty(object, propertyId, &value, scriptContext, nullptr);
- scriptContext->GetThreadContext()->SetDisableImplicitFlags(disableImplicitFlags);
- if (result && scriptContext->IsUndeclBlockVar(value) && !allowUndecInConsoleScope && !isLexicalThisSlotSymbol)
- {
- JavascriptError::ThrowReferenceError(scriptContext, JSERR_UseBeforeDeclaration);
- }
- PropertyValueInfo info2;
- PropertyValueInfo::SetCacheInfo(&info2, functionBody, inlineCache, inlineCacheIndex, !IsFromFullJit);
- PropertyOperationFlags setPropertyOpFlags = allowUndecInConsoleScope ? PropertyOperation_AllowUndeclInConsoleScope : PropertyOperation_None;
- object->SetProperty(propertyId, newValue, setPropertyOpFlags, &info2);
- #if DBG_DUMP
- if (PHASE_VERBOSE_TRACE1(Js::InlineCachePhase))
- {
- CacheOperators::TraceCache(inlineCache, _u("PatchSetPropertyScoped"), propertyId, scriptContext, object);
- }
- #endif
- if (!JavascriptProxy::Is(object) && !allowUndecInConsoleScope)
- {
- CacheOperators::CachePropertyWrite(object, false, type, propertyId, &info2, scriptContext);
- }
- return;
- }
- }
- Assert(!isLexicalThisSlotSymbol);
- // If we have console scope and no one in the scope had the property add it to console scope
- if ((length > 0) && ConsoleScopeActivationObject::Is(pDisplay->GetItem(length - 1)))
- {
- // CheckPrototypesForAccessorOrNonWritableProperty does not check for const in global object. We should check it here.
- if (length > 1)
- {
- Js::GlobalObject * globalObject = JavascriptOperators::TryFromVar<Js::GlobalObject>(pDisplay->GetItem(length - 2));
- if (globalObject)
- {
- Var setterValue = nullptr;
- DescriptorFlags flags = JavascriptOperators::GetRootSetter(globalObject, propertyId, &setterValue, &info, scriptContext);
- Assert((flags & Accessor) != Accessor);
- Assert((flags & Proxy) != Proxy);
- if ((flags & Data) == Data && (flags & Writable) == None)
- {
- if (!allowUndecInConsoleScope)
- {
- if (flags & Const)
- {
- JavascriptError::ThrowTypeError(scriptContext, ERRAssignmentToConst);
- }
- Assert(!isLexicalThisSlotSymbol);
- return;
- }
- }
- }
- }
- RecyclableObject* obj = RecyclableObject::FromVar((DynamicObject*)pDisplay->GetItem(length - 1));
- OUTPUT_TRACE(Js::ConsoleScopePhase, _u("Adding property '%s' to console scope object\n"), scriptContext->GetPropertyName(propertyId)->GetBuffer());
- JavascriptOperators::SetProperty(obj, obj, propertyId, newValue, scriptContext, propertyOperationFlags);
- return;
- }
- // No one in the scope stack has the property, so add it to the default instance provided by the caller.
- AssertMsg(!TaggedNumber::Is(defaultInstance), "Root object is an int or tagged float?");
- Assert(defaultInstance != nullptr);
- RecyclableObject* obj = RecyclableObject::FromVar(defaultInstance);
- {
- //SetPropertyScoped does not use inline cache for default instance
- PropertyValueInfo info2;
- JavascriptOperators::SetRootProperty(obj, propertyId, newValue, &info2, scriptContext, (PropertyOperationFlags)(propertyOperationFlags | PropertyOperation_Root));
- }
- JIT_HELPER_END(Op_PatchSetPropertyScoped);
- }
- JIT_HELPER_TEMPLATE(Op_PatchSetPropertyScoped, Op_ConsolePatchSetPropertyScoped)
- template void JavascriptOperators::PatchSetPropertyScoped<false, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, FrameDisplay *pDisplay, PropertyId propertyId, Var newValue, Var defaultInstance, PropertyOperationFlags propertyOperationFlags);
- template void JavascriptOperators::PatchSetPropertyScoped<true, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, FrameDisplay *pDisplay, PropertyId propertyId, Var newValue, Var defaultInstance, PropertyOperationFlags propertyOperationFlags);
- template void JavascriptOperators::PatchSetPropertyScoped<false, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, FrameDisplay *pDisplay, PropertyId propertyId, Var newValue, Var defaultInstance, PropertyOperationFlags propertyOperationFlags);
- template void JavascriptOperators::PatchSetPropertyScoped<true, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, FrameDisplay *pDisplay, PropertyId propertyId, Var newValue, Var defaultInstance, PropertyOperationFlags propertyOperationFlags);
- BOOL JavascriptOperators::OP_InitFuncScoped(FrameDisplay *pScope, PropertyId propertyId, Var newValue, Var defaultInstance, ScriptContext* scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_HEADER(Op_InitFuncScoped, reentrancylock, scriptContext->GetThreadContext());
- int i;
- int length = pScope->GetLength();
- DynamicObject *obj;
- for (i = 0; i < length; i++)
- {
- obj = (DynamicObject*)pScope->GetItem(i);
- if (obj->InitFuncScoped(propertyId, newValue))
- {
- return TRUE;
- }
- }
- AssertMsg(!TaggedNumber::Is(defaultInstance), "Root object is an int or tagged float?");
- return RecyclableObject::FromVar(defaultInstance)->InitFuncScoped(propertyId, newValue);
- JIT_HELPER_END(Op_InitFuncScoped);
- }
- BOOL JavascriptOperators::OP_InitPropertyScoped(FrameDisplay *pScope, PropertyId propertyId, Var newValue, Var defaultInstance, ScriptContext* scriptContext)
- {
- int i;
- int length = pScope->GetLength();
- DynamicObject *obj;
- for (i = 0; i < length; i++)
- {
- obj = (DynamicObject*)pScope->GetItem(i);
- if (obj->InitPropertyScoped(propertyId, newValue))
- {
- return TRUE;
- }
- }
- AssertMsg(!TaggedNumber::Is(defaultInstance), "Root object is an int or tagged float?");
- return RecyclableObject::FromVar(defaultInstance)->InitPropertyScoped(propertyId, newValue);
- }
- Var JavascriptOperators::OP_DeletePropertyScoped(
- FrameDisplay *pScope,
- PropertyId propertyId,
- Var defaultInstance,
- ScriptContext* scriptContext,
- PropertyOperationFlags propertyOperationFlags)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_DeletePropertyScoped);
- JIT_HELPER_SAME_ATTRIBUTES(Op_DeleteRootProperty, Op_DeletePropertyScoped);
- int i;
- int length = pScope->GetLength();
- for (i = 0; i < length; i++)
- {
- DynamicObject *obj = (DynamicObject*)pScope->GetItem(i);
- if (JavascriptOperators::HasProperty(obj, propertyId))
- {
- return scriptContext->GetLibrary()->CreateBoolean(JavascriptOperators::DeleteProperty(obj, propertyId, propertyOperationFlags));
- }
- }
- return JavascriptOperators::OP_DeleteRootProperty(RecyclableObject::FromVar(defaultInstance), propertyId, scriptContext, propertyOperationFlags);
- JIT_HELPER_END(Op_DeletePropertyScoped);
- }
- Var JavascriptOperators::OP_TypeofPropertyScoped(FrameDisplay *pScope, PropertyId propertyId, Var defaultInstance, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_TypeofPropertyScoped);
- int i;
- int length = pScope->GetLength();
- for (i = 0; i < length; i++)
- {
- DynamicObject *obj = (DynamicObject*)pScope->GetItem(i);
- if (JavascriptOperators::HasProperty(obj, propertyId))
- {
- return JavascriptOperators::TypeofFld(obj, propertyId, scriptContext);
- }
- }
- return JavascriptOperators::TypeofRootFld(RecyclableObject::FromVar(defaultInstance), propertyId, scriptContext);
- JIT_HELPER_END(Op_TypeofPropertyScoped);
- }
- BOOL JavascriptOperators::HasOwnItem(RecyclableObject* object, uint32 index)
- {
- return object->HasOwnItem(index);
- }
- BOOL JavascriptOperators::HasItem(RecyclableObject* object, uint64 index)
- {
- PropertyRecord const * propertyRecord = nullptr;
- ScriptContext* scriptContext = object->GetScriptContext();
- JavascriptOperators::GetPropertyIdForInt(index, scriptContext, &propertyRecord);
- return JavascriptOperators::HasProperty(object, propertyRecord->GetPropertyId());
- }
- BOOL JavascriptOperators::HasItem(RecyclableObject* object, uint32 index)
- {
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(object);
- #endif
- while (!JavascriptOperators::IsNull(object))
- {
- PropertyQueryFlags result;
- if ((result = object->HasItemQuery(index)) != PropertyQueryFlags::Property_NotFound)
- {
- return JavascriptConversion::PropertyQueryFlagsToBoolean(result);
- }
- // CONSIDER: Numeric property values shouldn't be on the prototype for now but if this changes
- // we should add SkipsPrototype support here as well
- object = JavascriptOperators::GetPrototypeNoTrap(object);
- }
- return false;
- }
- BOOL JavascriptOperators::GetOwnItem(RecyclableObject* object, uint32 index, Var* value, ScriptContext* requestContext)
- {
- return object->GetItem(object, index, value, requestContext);
- }
- BOOL JavascriptOperators::GetItem(Var instance, RecyclableObject* propertyObject, uint32 index, Var* value, ScriptContext* requestContext)
- {
- RecyclableObject* object = propertyObject;
- while (!JavascriptOperators::IsNull(object))
- {
- PropertyQueryFlags result;
- if ((result = object->GetItemQuery(instance, index, value, requestContext)) != PropertyQueryFlags::Property_NotFound)
- {
- return JavascriptConversion::PropertyQueryFlagsToBoolean(result);
- }
- if (object->SkipsPrototype())
- {
- break;
- }
- object = JavascriptOperators::GetPrototypeNoTrap(object);
- }
- *value = requestContext->GetMissingItemResult();
- return false;
- }
- BOOL JavascriptOperators::GetItemReference(Var instance, RecyclableObject* propertyObject, uint32 index, Var* value, ScriptContext* requestContext)
- {
- RecyclableObject* object = propertyObject;
- while (!JavascriptOperators::IsNull(object))
- {
- PropertyQueryFlags result;
- if ((result = object->GetItemReferenceQuery(instance, index, value, requestContext)) != PropertyQueryFlags::Property_NotFound)
- {
- return JavascriptConversion::PropertyQueryFlagsToBoolean(result);
- }
- if (object->SkipsPrototype())
- {
- break;
- }
- object = JavascriptOperators::GetPrototypeNoTrap(object);
- }
- *value = requestContext->GetMissingItemResult();
- return false;
- }
- BOOL JavascriptOperators::SetItem(Var receiver, RecyclableObject* object, uint64 index, Var value, ScriptContext* scriptContext, PropertyOperationFlags propertyOperationFlags)
- {
- PropertyRecord const * propertyRecord = nullptr;
- JavascriptOperators::GetPropertyIdForInt(index, scriptContext, &propertyRecord);
- return JavascriptOperators::SetProperty(receiver, object, propertyRecord->GetPropertyId(), value, scriptContext, propertyOperationFlags);
- }
- BOOL JavascriptOperators::SetItem(Var receiver, RecyclableObject* object, uint32 index, Var value, ScriptContext* scriptContext, PropertyOperationFlags propertyOperationFlags, BOOL skipPrototypeCheck /* = FALSE */)
- {
- Var setterValueOrProxy = nullptr;
- DescriptorFlags flags = None;
- Assert(!TaggedNumber::Is(receiver));
- if (JavascriptOperators::CheckPrototypesForAccessorOrNonWritableItem(object, index, &setterValueOrProxy, &flags, scriptContext, skipPrototypeCheck))
- {
- scriptContext->GetThreadContext()->AddImplicitCallFlags(ImplicitCall_NoOpSet);
- if ((flags & Accessor) == Accessor)
- {
- if (JavascriptError::ThrowIfStrictModeUndefinedSetter(propertyOperationFlags, setterValueOrProxy, scriptContext) ||
- JavascriptError::ThrowIfNotExtensibleUndefinedSetter(propertyOperationFlags, setterValueOrProxy, scriptContext))
- {
- return TRUE;
- }
- if (setterValueOrProxy)
- {
- RecyclableObject* func = RecyclableObject::FromVar(setterValueOrProxy);
- JavascriptOperators::CallSetter(func, receiver, value, scriptContext);
- }
- return TRUE;
- }
- else if ((flags & Proxy) == Proxy)
- {
- Assert(JavascriptProxy::Is(setterValueOrProxy));
- JavascriptProxy* proxy = JavascriptProxy::FromVar(setterValueOrProxy);
- const PropertyRecord* propertyRecord = nullptr;
- proxy->PropertyIdFromInt(index, &propertyRecord);
- return proxy->SetPropertyTrap(receiver, JavascriptProxy::SetPropertyTrapKind::SetItemKind, propertyRecord->GetPropertyId(), value, scriptContext, skipPrototypeCheck);
- }
- else
- {
- Assert((flags & Data) == Data && (flags & Writable) == None);
- if ((propertyOperationFlags & PropertyOperationFlags::PropertyOperation_ThrowIfNotExtensible) == PropertyOperationFlags::PropertyOperation_ThrowIfNotExtensible)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_NonExtensibleObject);
- }
- JavascriptError::ThrowCantAssign(propertyOperationFlags, scriptContext, index);
- JavascriptError::ThrowCantAssignIfStrictMode(propertyOperationFlags, scriptContext);
- return FALSE;
- }
- }
- else if (!JavascriptOperators::IsObject(receiver))
- {
- JavascriptError::ThrowCantAssignIfStrictMode(propertyOperationFlags, scriptContext);
- return FALSE;
- }
- return (RecyclableObject::FromVar(receiver))->SetItem(index, value, propertyOperationFlags);
- }
- BOOL JavascriptOperators::DeleteItem(RecyclableObject* object, uint32 index, PropertyOperationFlags propertyOperationFlags)
- {
- return object->DeleteItem(index, propertyOperationFlags);
- }
- BOOL JavascriptOperators::DeleteItem(RecyclableObject* object, uint64 index, PropertyOperationFlags propertyOperationFlags)
- {
- PropertyRecord const * propertyRecord = nullptr;
- JavascriptOperators::GetPropertyIdForInt(index, object->GetScriptContext(), &propertyRecord);
- return JavascriptOperators::DeleteProperty(object, propertyRecord->GetPropertyId(), propertyOperationFlags);
- }
- BOOL JavascriptOperators::OP_HasItem(Var instance, Var index, ScriptContext* scriptContext)
- {
- RecyclableObject* object = TaggedNumber::Is(instance) ?
- scriptContext->GetLibrary()->GetNumberPrototype() :
- RecyclableObject::FromVar(instance);
- uint32 indexVal;
- PropertyRecord const * propertyRecord = nullptr;
- IndexType indexType = GetIndexType(index, scriptContext, &indexVal, &propertyRecord, false);
- if (indexType == IndexType_Number)
- {
- return HasItem(object, indexVal);
- }
- else
- {
- Assert(indexType == IndexType_PropertyId);
- if (propertyRecord == nullptr && !JavascriptOperators::CanShortcutOnUnknownPropertyName(object))
- {
- indexType = GetIndexTypeFromPrimitive(index, scriptContext, &indexVal, &propertyRecord, true);
- Assert(indexType == IndexType_PropertyId);
- Assert(propertyRecord != nullptr);
- }
- if (propertyRecord != nullptr)
- {
- return HasProperty(object, propertyRecord->GetPropertyId());
- }
- else
- {
- #if DBG
- JavascriptString* indexStr = JavascriptConversion::ToString(index, scriptContext);
- PropertyRecord const * debugPropertyRecord;
- scriptContext->GetOrAddPropertyRecord(indexStr, &debugPropertyRecord);
- AssertMsg(!JavascriptOperators::HasProperty(object, debugPropertyRecord->GetPropertyId()), "how did this property come? See OS Bug 2727708 if you see this come from the web");
- #endif
- return FALSE;
- }
- }
- }
- #if ENABLE_PROFILE_INFO
- void JavascriptOperators::UpdateNativeArrayProfileInfoToCreateVarArray(Var instance, const bool expectingNativeFloatArray, const bool expectingVarArray)
- {
- Assert(instance);
- Assert(expectingNativeFloatArray ^ expectingVarArray);
- JavascriptNativeArray * nativeArr = JavascriptOperators::TryFromVar<JavascriptNativeArray>(instance);
- if (!nativeArr)
- {
- return;
- }
- ArrayCallSiteInfo *const arrayCallSiteInfo = nativeArr->GetArrayCallSiteInfo();
- if (!arrayCallSiteInfo)
- {
- return;
- }
- if (expectingNativeFloatArray)
- {
- // Profile data is expecting a native float array. Ensure that at the array's creation site, that a native int array
- // is not created, such that the profiled array type would be correct.
- arrayCallSiteInfo->SetIsNotNativeIntArray();
- }
- else
- {
- // Profile data is expecting a var array. Ensure that at the array's creation site, that a native array is not
- // created, such that the profiled array type would be correct.
- Assert(expectingVarArray);
- arrayCallSiteInfo->SetIsNotNativeArray();
- }
- }
- bool JavascriptOperators::SetElementMayHaveImplicitCalls(ScriptContext *const scriptContext)
- {
- return
- scriptContext->optimizationOverrides.GetArraySetElementFastPathVtable() ==
- ScriptContextOptimizationOverrideInfo::InvalidVtable;
- }
- #endif
- RecyclableObject *JavascriptOperators::GetCallableObjectOrThrow(const Var callee, ScriptContext *const scriptContext)
- {
- Assert(callee);
- Assert(scriptContext);
- if (TaggedNumber::Is(callee))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedFunction /* TODO-ERROR: get arg name - aFunc */);
- }
- return RecyclableObject::UnsafeFromVar(callee);
- }
- Var JavascriptOperators::OP_GetElementI_JIT(Var instance, Var index, ScriptContext *scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_GetElementI);
- #if ENABLE_NATIVE_CODEGEN
- Assert(Js::JavascriptStackWalker::ValidateTopJitFrame(scriptContext));
- #endif
- return OP_GetElementI(instance, index, scriptContext);
- JIT_HELPER_END(Op_GetElementI);
- }
- Var JavascriptOperators::OP_GetElementI_UInt32(Var instance, uint32 index, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_GetElementI_UInt32);
- #if FLOATVAR
- return OP_GetElementI_JIT(instance, Js::JavascriptNumber::ToVar(index, scriptContext), scriptContext);
- #else
- char buffer[sizeof(Js::JavascriptNumber)];
- return OP_GetElementI_JIT(instance, Js::JavascriptNumber::ToVarInPlace(index, scriptContext,
- (Js::JavascriptNumber *)buffer), scriptContext);
- #endif
- JIT_HELPER_END(Op_GetElementI_UInt32);
- }
- Var JavascriptOperators::OP_GetElementI_Int32(Var instance, int32 index, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_GetElementI_Int32);
- #if FLOATVAR
- return OP_GetElementI_JIT(instance, Js::JavascriptNumber::ToVar(index, scriptContext), scriptContext);
- #else
- char buffer[sizeof(Js::JavascriptNumber)];
- return OP_GetElementI_JIT(instance, Js::JavascriptNumber::ToVarInPlace(index, scriptContext,
- (Js::JavascriptNumber *)buffer), scriptContext);
- #endif
- JIT_HELPER_END(Op_GetElementI_Int32);
- }
- BOOL JavascriptOperators::GetItemFromArrayPrototype(JavascriptArray * arr, int32 indexInt, Var * result, ScriptContext * scriptContext)
- {
- // try get from Array prototype
- RecyclableObject* prototype = arr->GetPrototype();
- if (JavascriptOperators::GetTypeId(prototype) != TypeIds_Array) //This can be TypeIds_ES5Array (or any other object changed through __proto__).
- {
- return false;
- }
- JavascriptArray* arrayPrototype = JavascriptArray::UnsafeFromVar(prototype); //Prototype must be Array.prototype (unless changed through __proto__)
- if (arrayPrototype->GetLength() && arrayPrototype->GetItem(arrayPrototype, (uint32)indexInt, result, scriptContext))
- {
- return true;
- }
- prototype = arrayPrototype->GetPrototype(); //Its prototype must be Object.prototype (unless changed through __proto__)
- if (prototype->GetScriptContext()->GetLibrary()->GetObjectPrototype() != prototype)
- {
- return false;
- }
- if (DynamicObject::FromVar(prototype)->HasNonEmptyObjectArray())
- {
- if (prototype->GetItem(arr, (uint32)indexInt, result, scriptContext))
- {
- return true;
- }
- }
- *result = scriptContext->GetMissingItemResult();
- return true;
- }
- Var JavascriptOperators::GetElementIIntIndex(_In_ Var instance, _In_ Var index, _In_ ScriptContext* scriptContext)
- {
- Assert(TaggedInt::Is(index));
- switch (JavascriptOperators::GetTypeId(instance))
- {
- case TypeIds_Array: //fast path for array
- {
- Var result;
- if (OP_GetElementI_ArrayFastPath(JavascriptArray::UnsafeFromVar(instance), TaggedInt::ToInt32(index), &result, scriptContext))
- {
- return result;
- }
- break;
- }
- case TypeIds_NativeIntArray:
- {
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(instance);
- #endif
- Var result;
- if (OP_GetElementI_ArrayFastPath(JavascriptNativeIntArray::UnsafeFromVar(instance), TaggedInt::ToInt32(index), &result, scriptContext))
- {
- return result;
- }
- break;
- }
- case TypeIds_NativeFloatArray:
- {
- Var result;
- if (OP_GetElementI_ArrayFastPath(JavascriptNativeFloatArray::UnsafeFromVar(instance), TaggedInt::ToInt32(index), &result, scriptContext))
- {
- return result;
- }
- break;
- }
- case TypeIds_String: // fast path for string
- {
- charcount_t indexInt = TaggedInt::ToUInt32(index);
- JavascriptString* string = JavascriptString::UnsafeFromVar(instance);
- Var result;
- if (JavascriptConversion::PropertyQueryFlagsToBoolean(string->JavascriptString::GetItemQuery(instance, indexInt, &result, scriptContext)))
- {
- return result;
- }
- break;
- }
- case TypeIds_Int8Array:
- {
- // The typed array will deal with all possible values for the index
- int32 indexInt = TaggedInt::ToInt32(index);
- if (VirtualTableInfo<Int8VirtualArray>::HasVirtualTable(instance))
- {
- Int8VirtualArray* int8Array = Int8VirtualArray::UnsafeFromVar(instance);
- if (indexInt >= 0)
- {
- return int8Array->DirectGetItem(indexInt);
- }
- }
- else if (VirtualTableInfo<Int8Array>::HasVirtualTable(instance))
- {
- Int8Array* int8Array = Int8Array::UnsafeFromVar(instance);
- if (indexInt >= 0)
- {
- return int8Array->DirectGetItem(indexInt);
- }
- }
- break;
- }
- case TypeIds_Uint8Array:
- {
- // The typed array will deal with all possible values for the index
- int32 indexInt = TaggedInt::ToInt32(index);
- if (VirtualTableInfo<Uint8VirtualArray>::HasVirtualTable(instance))
- {
- Uint8VirtualArray* uint8Array = Uint8VirtualArray::UnsafeFromVar(instance);
- if (indexInt >= 0)
- {
- return uint8Array->DirectGetItem(indexInt);
- }
- }
- else if (VirtualTableInfo<Uint8Array>::HasVirtualTable(instance))
- {
- Uint8Array* uint8Array = Uint8Array::UnsafeFromVar(instance);
- if (indexInt >= 0)
- {
- return uint8Array->DirectGetItem(indexInt);
- }
- }
- break;
- }
- case TypeIds_Uint8ClampedArray:
- {
- // The typed array will deal with all possible values for the index
- int32 indexInt = TaggedInt::ToInt32(index);
- if (VirtualTableInfo<Uint8ClampedVirtualArray>::HasVirtualTable(instance))
- {
- Uint8ClampedVirtualArray* uint8ClampedArray = Uint8ClampedVirtualArray::UnsafeFromVar(instance);
- if (indexInt >= 0)
- {
- return uint8ClampedArray->DirectGetItem(indexInt);
- }
- }
- else if (VirtualTableInfo<Uint8ClampedArray>::HasVirtualTable(instance))
- {
- Uint8ClampedArray* uint8ClampedArray = Uint8ClampedArray::UnsafeFromVar(instance);
- if (indexInt >= 0)
- {
- return uint8ClampedArray->DirectGetItem(indexInt);
- }
- }
- break;
- }
- case TypeIds_Int16Array:
- {
- // The type array will deal with all possible values for the index
- int32 indexInt = TaggedInt::ToInt32(index);
- if (VirtualTableInfo<Int16VirtualArray>::HasVirtualTable(instance))
- {
- Int16VirtualArray* int16Array = Int16VirtualArray::UnsafeFromVar(instance);
- if (indexInt >= 0)
- {
- return int16Array->DirectGetItem(indexInt);
- }
- }
- else if (VirtualTableInfo<Int16Array>::HasVirtualTable(instance))
- {
- Int16Array* int16Array = Int16Array::UnsafeFromVar(instance);
- if (indexInt >= 0)
- {
- return int16Array->DirectGetItem(indexInt);
- }
- }
- break;
- }
- case TypeIds_Uint16Array:
- {
- // The type array will deal with all possible values for the index
- int32 indexInt = TaggedInt::ToInt32(index);
- if (VirtualTableInfo<Uint16VirtualArray>::HasVirtualTable(instance))
- {
- Uint16VirtualArray* uint16Array = Uint16VirtualArray::UnsafeFromVar(instance);
- if (indexInt >= 0)
- {
- return uint16Array->DirectGetItem(indexInt);
- }
- }
- else if (VirtualTableInfo<Uint16Array>::HasVirtualTable(instance))
- {
- Uint16Array* uint16Array = Uint16Array::UnsafeFromVar(instance);
- if (indexInt >= 0)
- {
- return uint16Array->DirectGetItem(indexInt);
- }
- }
- break;
- }
- case TypeIds_Int32Array:
- {
- // The type array will deal with all possible values for the index
- int32 indexInt = TaggedInt::ToInt32(index);
- if (VirtualTableInfo<Int32VirtualArray>::HasVirtualTable(instance))
- {
- Int32VirtualArray* int32Array = Int32VirtualArray::UnsafeFromVar(instance);
- if (indexInt >= 0)
- {
- return int32Array->DirectGetItem(indexInt);
- }
- }
- else if (VirtualTableInfo<Int32Array>::HasVirtualTable(instance))
- {
- Int32Array* int32Array = Int32Array::UnsafeFromVar(instance);
- if (indexInt >= 0)
- {
- return int32Array->DirectGetItem(indexInt);
- }
- }
- break;
- }
- case TypeIds_Uint32Array:
- {
- // The type array will deal with all possible values for the index
- int32 indexInt = TaggedInt::ToInt32(index);
- if (VirtualTableInfo<Uint32VirtualArray>::HasVirtualTable(instance))
- {
- Uint32VirtualArray* uint32Array = Uint32VirtualArray::UnsafeFromVar(instance);
- if (indexInt >= 0)
- {
- return uint32Array->DirectGetItem(indexInt);
- }
- }
- else if (VirtualTableInfo<Uint32Array>::HasVirtualTable(instance))
- {
- Uint32Array* uint32Array = Uint32Array::UnsafeFromVar(instance);
- if (indexInt >= 0)
- {
- return uint32Array->DirectGetItem(indexInt);
- }
- }
- break;
- }
- case TypeIds_Float32Array:
- {
- // The type array will deal with all possible values for the index
- int32 indexInt = TaggedInt::ToInt32(index);
- if (VirtualTableInfo<Float32VirtualArray>::HasVirtualTable(instance))
- {
- Float32VirtualArray* float32Array = Float32VirtualArray::UnsafeFromVar(instance);
- if (indexInt >= 0)
- {
- return float32Array->DirectGetItem(indexInt);
- }
- }
- else if (VirtualTableInfo<Float32Array>::HasVirtualTable(instance))
- {
- Float32Array* float32Array = Float32Array::UnsafeFromVar(instance);
- if (indexInt >= 0)
- {
- return float32Array->DirectGetItem(indexInt);
- }
- }
- break;
- }
- case TypeIds_Float64Array:
- {
- // The type array will deal with all possible values for the index
- int32 indexInt = TaggedInt::ToInt32(index);
- if (VirtualTableInfo<Float64VirtualArray>::HasVirtualTable(instance))
- {
- Float64VirtualArray* float64Array = Float64VirtualArray::UnsafeFromVar(instance);
- if (indexInt >= 0)
- {
- return float64Array->DirectGetItem(indexInt);
- }
- }
- else if (VirtualTableInfo<Float64Array>::HasVirtualTable(instance))
- {
- Float64Array* float64Array = Float64Array::UnsafeFromVar(instance);
- if (indexInt >= 0)
- {
- return float64Array->DirectGetItem(indexInt);
- }
- }
- break;
- }
- default:
- break;
- }
- return JavascriptOperators::GetElementIHelper(instance, index, instance, scriptContext);
- }
- template <typename T>
- BOOL JavascriptOperators::OP_GetElementI_ArrayFastPath(T * arr, int indexInt, Var * result, ScriptContext * scriptContext)
- {
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(arr);
- #endif
- if (indexInt >= 0)
- {
- if (!CrossSite::IsCrossSiteObjectTyped(arr))
- {
- if (arr->T::DirectGetVarItemAt((uint32)indexInt, result, scriptContext))
- {
- return true;
- }
- }
- else
- {
- if (arr->GetItem(arr, (uint32)indexInt, result, scriptContext))
- {
- return true;
- }
- }
- return GetItemFromArrayPrototype(arr, indexInt, result, scriptContext);
- }
- return false;
- }
- Var JavascriptOperators::OP_GetElementI(Var instance, Var index, ScriptContext* scriptContext)
- {
- if (TaggedInt::Is(index))
- {
- return GetElementIIntIndex(instance, index, scriptContext);
- }
- if (JavascriptNumber::Is_NoTaggedIntCheck(index))
- {
- uint32 uint32Index = JavascriptConversion::ToUInt32(index, scriptContext);
- if ((double)uint32Index == JavascriptNumber::GetValue(index) && !TaggedInt::IsOverflow(uint32Index))
- {
- index = TaggedInt::ToVarUnchecked(uint32Index);
- return GetElementIIntIndex(instance, index, scriptContext);
- }
- }
- else if (RecyclableObject::Is(instance))
- {
- RecyclableObject* cacheOwner;
- PropertyRecordUsageCache* propertyRecordUsageCache;
- if (GetPropertyRecordUsageCache(index, scriptContext, &propertyRecordUsageCache, &cacheOwner))
- {
- return GetElementIWithCache<false /* ReturnOperationInfo */>(instance, cacheOwner, propertyRecordUsageCache, scriptContext, nullptr);
- }
- }
- return JavascriptOperators::GetElementIHelper(instance, index, instance, scriptContext);
- }
- _Success_(return) bool JavascriptOperators::GetPropertyRecordUsageCache(Var index, ScriptContext* scriptContext, _Outptr_ PropertyRecordUsageCache** propertyRecordUsageCache, _Outptr_ RecyclableObject** cacheOwner)
- {
- JavascriptString* string = JavascriptOperators::TryFromVar<JavascriptString>(index);
- if (string)
- {
- PropertyString * propertyString = nullptr;
- if (VirtualTableInfo<Js::PropertyString>::HasVirtualTable(string))
- {
- propertyString = (PropertyString*)string;
- }
- else if (VirtualTableInfo<Js::LiteralStringWithPropertyStringPtr>::HasVirtualTable(string))
- {
- LiteralStringWithPropertyStringPtr * strWithPtr = (LiteralStringWithPropertyStringPtr *)string;
- if (!strWithPtr->HasPropertyRecord())
- {
- PropertyRecord const * propertyRecord;
- strWithPtr->GetPropertyRecord(&propertyRecord); // lookup-cache propertyRecord
- }
- else
- {
- propertyString = strWithPtr->GetOrAddPropertyString();
- // this is the second time this property string is used
- // we already had created the propertyRecord..
- // now create the propertyString!
- }
- }
- if (propertyString != nullptr)
- {
- *propertyRecordUsageCache = propertyString->GetPropertyRecordUsageCache();
- *cacheOwner = propertyString;
- return true;
- }
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- if (PHASE_TRACE1(PropertyCachePhase))
- {
- Output::Print(_u("PropertyCache: GetElem No property string for '%s'\n"), string->GetString());
- }
- #endif
- #if DBG_DUMP
- scriptContext->forinNoCache++;
- #endif
- }
- JavascriptSymbol* symbol = JavascriptOperators::TryFromVar<JavascriptSymbol>(index);
- if (symbol)
- {
- *propertyRecordUsageCache = symbol->GetPropertyRecordUsageCache();
- *cacheOwner = symbol;
- return true;
- }
- return false;
- }
- bool JavascriptOperators::SetElementIOnTaggedNumber(
- _In_ Var receiver,
- _In_ RecyclableObject* object,
- _In_ Var index,
- _In_ Var value,
- _In_ ScriptContext* requestContext,
- _In_ PropertyOperationFlags propertyOperationFlags)
- {
- Assert(TaggedNumber::Is(receiver));
- uint32 indexVal = 0;
- PropertyRecord const * propertyRecord = nullptr;
- IndexType indexType = GetIndexType(index, requestContext, &indexVal, &propertyRecord, true);
- if (indexType == IndexType_Number)
- {
- return JavascriptOperators::SetItemOnTaggedNumber(receiver, object, indexVal, value, requestContext, propertyOperationFlags);
- }
- else
- {
- return JavascriptOperators::SetPropertyOnTaggedNumber(receiver, object, propertyRecord->GetPropertyId(), value, requestContext, propertyOperationFlags);
- }
- }
- template <bool ReturnOperationInfo>
- bool JavascriptOperators::SetElementIWithCache(
- _In_ Var receiver,
- _In_ RecyclableObject* object,
- _In_ RecyclableObject* index,
- _In_ Var value,
- _In_ PropertyRecordUsageCache* propertyRecordUsageCache,
- _In_ ScriptContext* scriptContext,
- _In_ PropertyOperationFlags flags,
- _Inout_opt_ PropertyCacheOperationInfo* operationInfo)
- {
- if (TaggedNumber::Is(receiver))
- {
- return JavascriptOperators::SetElementIOnTaggedNumber(receiver, object, index, value, scriptContext, flags);
- }
- PropertyRecord const * propertyRecord = propertyRecordUsageCache->GetPropertyRecord();
- if (propertyRecord->IsNumeric())
- {
- return JavascriptOperators::SetItem(receiver, object, propertyRecord->GetNumericValue(), value, scriptContext, flags);
- }
- PropertyValueInfo info;
- if (receiver == object)
- {
- if (propertyRecordUsageCache->TrySetPropertyFromCache<ReturnOperationInfo>(object, value, scriptContext, flags, &info, index, operationInfo))
- {
- return true;
- }
- }
- PropertyId propId = propertyRecord->GetPropertyId();
- if (propId == PropertyIds::NaN || propId == PropertyIds::Infinity)
- {
- // As we no longer convert o[x] into o.x for NaN and Infinity, we need to follow SetProperty convention for these,
- // which would check for read-only properties, strict mode, etc.
- // Note that "-Infinity" does not qualify as property name, so we don't have to take care of it.
- return JavascriptOperators::SetProperty(receiver, object, propId, value, scriptContext, flags);
- }
- return JavascriptOperators::SetPropertyWPCache(receiver, object, propId, value, scriptContext, flags, &info);
- }
- template bool JavascriptOperators::SetElementIWithCache<false>(Var receiver, RecyclableObject* object, RecyclableObject* index, Var value, PropertyRecordUsageCache* propertyRecordUsageCache, ScriptContext* scriptContext, PropertyOperationFlags flags, PropertyCacheOperationInfo* operationInfo);
- template bool JavascriptOperators::SetElementIWithCache<true>(Var receiver, RecyclableObject* object, RecyclableObject* index, Var value, PropertyRecordUsageCache* propertyRecordUsageCache, ScriptContext* scriptContext, PropertyOperationFlags flags, PropertyCacheOperationInfo* operationInfo);
- template <bool ReturnOperationInfo>
- Var JavascriptOperators::GetElementIWithCache(
- _In_ Var instance,
- _In_ RecyclableObject* index,
- _In_ PropertyRecordUsageCache* propertyRecordUsageCache,
- _In_ ScriptContext* scriptContext,
- _Inout_opt_ PropertyCacheOperationInfo* operationInfo)
- {
- RecyclableObject* object = nullptr;
- if (!JavascriptOperators::GetPropertyObjectForGetElementI(instance, index, scriptContext, &object))
- {
- return scriptContext->GetLibrary()->GetUndefined();
- }
- PropertyRecord const * propertyRecord = propertyRecordUsageCache->GetPropertyRecord();
- Var value;
- if (propertyRecord->IsNumeric())
- {
- if (JavascriptOperators::GetItem(instance, object, propertyRecord->GetNumericValue(), &value, scriptContext))
- {
- return value;
- }
- }
- else
- {
- PropertyValueInfo info;
- if (propertyRecordUsageCache->TryGetPropertyFromCache<false /* OwnPropertyOnly */, false /* OutputExistence */, ReturnOperationInfo>(instance, object, &value, scriptContext, &info, index, operationInfo))
- {
- return value;
- }
- if (JavascriptOperators::GetPropertyWPCache<false /* OutputExistence */>(instance, object, propertyRecord->GetPropertyId(), &value, scriptContext, &info))
- {
- return value;
- }
- }
- return scriptContext->GetLibrary()->GetUndefined();
- }
- template Var JavascriptOperators::GetElementIWithCache<false>(Var instance, RecyclableObject* index, PropertyRecordUsageCache* propertyRecordUsageCache, ScriptContext* scriptContext, PropertyCacheOperationInfo* operationInfo);
- template Var JavascriptOperators::GetElementIWithCache<true>(Var instance, RecyclableObject* index, PropertyRecordUsageCache* propertyRecordUsageCache, ScriptContext* scriptContext, PropertyCacheOperationInfo* operationInfo);
- Var JavascriptOperators::GetElementIHelper(Var instance, Var index, Var receiver, ScriptContext* scriptContext)
- {
- RecyclableObject* object = nullptr;
- if (!JavascriptOperators::GetPropertyObjectForGetElementI(instance, index, scriptContext, &object))
- {
- return scriptContext->GetLibrary()->GetUndefined();
- }
- uint32 indexVal;
- PropertyRecord const * propertyRecord = nullptr;
- JavascriptString * propertyNameString = nullptr;
- Var value = nullptr;
- IndexType indexType = GetIndexType(index, scriptContext, &indexVal, &propertyRecord, &propertyNameString, false, true);
- if (indexType == IndexType_Number)
- {
- if (JavascriptOperators::GetItem(receiver, object, indexVal, &value, scriptContext))
- {
- return value;
- }
- }
- else if (indexType == IndexType_JavascriptString)
- {
- PropertyValueInfo info;
- if (JavascriptOperators::GetPropertyWPCache<false /* OutputExistence */>(receiver, object, propertyNameString, &value, scriptContext, &info))
- {
- return value;
- }
- }
- else
- {
- Assert(indexType == IndexType_PropertyId);
- if (propertyRecord == nullptr && !JavascriptOperators::CanShortcutOnUnknownPropertyName(object))
- {
- indexType = GetIndexTypeFromPrimitive(index, scriptContext, &indexVal, &propertyRecord, &propertyNameString, true, true);
- Assert(indexType == IndexType_PropertyId);
- Assert(propertyRecord != nullptr);
- }
- if (propertyRecord != nullptr)
- {
- PropertyValueInfo info;
- if (JavascriptOperators::GetPropertyWPCache<false /* OutputExistence */>(receiver, object, propertyRecord->GetPropertyId(), &value, scriptContext, &info))
- {
- return value;
- }
- }
- #if DBG
- else
- {
- JavascriptString* indexStr = JavascriptConversion::ToString(index, scriptContext);
- PropertyRecord const * debugPropertyRecord;
- scriptContext->GetOrAddPropertyRecord(indexStr, &debugPropertyRecord);
- AssertMsg(!JavascriptOperators::GetProperty(receiver, object, debugPropertyRecord->GetPropertyId(), &value, scriptContext), "how did this property come? See OS Bug 2727708 if you see this come from the web");
- }
- #endif
- }
- return scriptContext->GetMissingItemResult();
- }
- int32 JavascriptOperators::OP_GetNativeIntElementI(Var instance, Var index)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(Op_GetNativeIntElementI);
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(instance);
- #endif
- if (TaggedInt::Is(index))
- {
- int32 indexInt = TaggedInt::ToInt32(index);
- if (indexInt < 0)
- {
- return JavascriptNativeIntArray::MissingItem;
- }
- JavascriptArray * arr = JavascriptArray::FromVar(instance);
- int32 result;
- if (arr->DirectGetItemAt((uint32)indexInt, &result))
- {
- return result;
- }
- }
- else if (JavascriptNumber::Is_NoTaggedIntCheck(index))
- {
- int32 indexInt;
- bool isInt32;
- double dIndex = JavascriptNumber::GetValue(index);
- if (JavascriptNumber::TryGetInt32OrUInt32Value(dIndex, &indexInt, &isInt32))
- {
- if (isInt32 && indexInt < 0)
- {
- return JavascriptNativeIntArray::MissingItem;
- }
- JavascriptArray * arr = JavascriptArray::FromVar(instance);
- int32 result;
- if (arr->DirectGetItemAt((uint32)indexInt, &result))
- {
- return result;
- }
- }
- }
- else
- {
- AssertMsg(false, "Non-numerical index in this helper?");
- }
- return JavascriptNativeIntArray::MissingItem;
- JIT_HELPER_END(Op_GetNativeIntElementI);
- }
- int32 JavascriptOperators::OP_GetNativeIntElementI_UInt32(Var instance, uint32 index, ScriptContext* scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(Op_GetNativeIntElementI_UInt32);
- JIT_HELPER_SAME_ATTRIBUTES(Op_GetNativeIntElementI_UInt32, Op_GetNativeIntElementI);
- #if FLOATVAR
- return OP_GetNativeIntElementI(instance, Js::JavascriptNumber::ToVar(index, scriptContext));
- #else
- char buffer[sizeof(Js::JavascriptNumber)];
- return OP_GetNativeIntElementI(instance, Js::JavascriptNumber::ToVarInPlace(index, scriptContext,
- (Js::JavascriptNumber *)buffer));
- #endif
- JIT_HELPER_END(Op_GetNativeIntElementI_UInt32);
- }
- int32 JavascriptOperators::OP_GetNativeIntElementI_Int32(Var instance, int32 index, ScriptContext* scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(Op_GetNativeIntElementI_Int32);
- JIT_HELPER_SAME_ATTRIBUTES(Op_GetNativeIntElementI_Int32, Op_GetNativeIntElementI);
- #if FLOATVAR
- return OP_GetNativeIntElementI(instance, Js::JavascriptNumber::ToVar(index, scriptContext));
- #else
- char buffer[sizeof(Js::JavascriptNumber)];
- return OP_GetNativeIntElementI(instance, Js::JavascriptNumber::ToVarInPlace(index, scriptContext,
- (Js::JavascriptNumber *)buffer));
- #endif
- JIT_HELPER_END(Op_GetNativeIntElementI_Int32);
- }
- double JavascriptOperators::OP_GetNativeFloatElementI(Var instance, Var index)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(Op_GetNativeFloatElementI);
- double result = 0;
- if (TaggedInt::Is(index))
- {
- int32 indexInt = TaggedInt::ToInt32(index);
- if (indexInt < 0)
- {
- result = JavascriptNativeFloatArray::MissingItem;
- }
- else
- {
- JavascriptArray * arr = JavascriptArray::FromVar(instance);
- if (!arr->DirectGetItemAt((uint32)indexInt, &result))
- {
- result = JavascriptNativeFloatArray::MissingItem;
- }
- }
- }
- else if (JavascriptNumber::Is_NoTaggedIntCheck(index))
- {
- int32 indexInt;
- bool isInt32;
- double dIndex = JavascriptNumber::GetValue(index);
- if (JavascriptNumber::TryGetInt32OrUInt32Value(dIndex, &indexInt, &isInt32))
- {
- if (isInt32 && indexInt < 0)
- {
- result = JavascriptNativeFloatArray::MissingItem;
- }
- else
- {
- JavascriptArray * arr = JavascriptArray::FromVar(instance);
- if (!arr->DirectGetItemAt((uint32)indexInt, &result))
- {
- result = JavascriptNativeFloatArray::MissingItem;
- }
- }
- }
- }
- else
- {
- AssertMsg(false, "Non-numerical index in this helper?");
- }
- return result;
- JIT_HELPER_END(Op_GetNativeFloatElementI);
- }
- double JavascriptOperators::OP_GetNativeFloatElementI_UInt32(Var instance, uint32 index, ScriptContext* scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(Op_GetNativeFloatElementI_UInt32);
- JIT_HELPER_SAME_ATTRIBUTES(Op_GetNativeFloatElementI_UInt32, Op_GetNativeFloatElementI);
- #if FLOATVAR
- return OP_GetNativeFloatElementI(instance, Js::JavascriptNumber::ToVar(index, scriptContext));
- #else
- char buffer[sizeof(Js::JavascriptNumber)];
- return OP_GetNativeFloatElementI(instance, Js::JavascriptNumber::ToVarInPlace(index, scriptContext,
- (Js::JavascriptNumber *)buffer));
- #endif
- JIT_HELPER_END(Op_GetNativeFloatElementI_UInt32);
- }
- double JavascriptOperators::OP_GetNativeFloatElementI_Int32(Var instance, int32 index, ScriptContext* scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(Op_GetNativeFloatElementI_Int32);
- JIT_HELPER_SAME_ATTRIBUTES(Op_GetNativeFloatElementI_Int32, Op_GetNativeFloatElementI);
- #if FLOATVAR
- return OP_GetNativeFloatElementI(instance, Js::JavascriptNumber::ToVar(index, scriptContext));
- #else
- char buffer[sizeof(Js::JavascriptNumber)];
- return OP_GetNativeFloatElementI(instance, Js::JavascriptNumber::ToVarInPlace(index, scriptContext,
- (Js::JavascriptNumber *)buffer));
- #endif
- JIT_HELPER_END(Op_GetNativeFloatElementI_Int32);
- }
- Var JavascriptOperators::OP_GetMethodElement_UInt32(Var instance, uint32 index, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_GetMethodElement_UInt32);
- JIT_HELPER_SAME_ATTRIBUTES(Op_GetMethodElement_UInt32, Op_GetMethodElement);
- #if FLOATVAR
- return OP_GetMethodElement(instance, Js::JavascriptNumber::ToVar(index, scriptContext), scriptContext);
- #else
- char buffer[sizeof(Js::JavascriptNumber)];
- return OP_GetMethodElement(instance, Js::JavascriptNumber::ToVarInPlace(index, scriptContext,
- (Js::JavascriptNumber *)buffer), scriptContext);
- #endif
- JIT_HELPER_END(Op_GetMethodElement_UInt32);
- }
- Var JavascriptOperators::OP_GetMethodElement_Int32(Var instance, int32 index, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_GetMethodElement_Int32);
- JIT_HELPER_SAME_ATTRIBUTES(Op_GetMethodElement_Int32, Op_GetMethodElement);
- #if FLOATVAR
- return OP_GetElementI(instance, Js::JavascriptNumber::ToVar(index, scriptContext), scriptContext);
- #else
- char buffer[sizeof(Js::JavascriptNumber)];
- return OP_GetMethodElement(instance, Js::JavascriptNumber::ToVarInPlace(index, scriptContext,
- (Js::JavascriptNumber *)buffer), scriptContext);
- #endif
- JIT_HELPER_END(Op_GetMethodElement_Int32);
- }
- Var JavascriptOperators::OP_GetMethodElement(Var instance, Var index, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_GetMethodElement);
- RecyclableObject* object = nullptr;
- if (FALSE == JavascriptOperators::GetPropertyObject(instance, scriptContext, &object))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_CannotGet_NullOrUndefined, GetPropertyDisplayNameForError(index, scriptContext));
- }
- ThreadContext* threadContext = scriptContext->GetThreadContext();
- ImplicitCallFlags savedImplicitCallFlags = threadContext->GetImplicitCallFlags();
- threadContext->ClearImplicitCallFlags();
- uint32 indexVal;
- PropertyRecord const * propertyRecord = nullptr;
- Var value = NULL;
- BOOL hasProperty = FALSE;
- IndexType indexType = GetIndexType(index, scriptContext, &indexVal, &propertyRecord, false);
- if (indexType == IndexType_Number)
- {
- hasProperty = JavascriptOperators::GetItemReference(instance, object, indexVal, &value, scriptContext);
- }
- else
- {
- Assert(indexType == IndexType_PropertyId);
- if (propertyRecord == nullptr && !JavascriptOperators::CanShortcutOnUnknownPropertyName(object))
- {
- indexType = GetIndexTypeFromPrimitive(index, scriptContext, &indexVal, &propertyRecord, true);
- Assert(indexType == IndexType_PropertyId);
- Assert(propertyRecord != nullptr);
- }
- if (propertyRecord != nullptr)
- {
- hasProperty = JavascriptOperators::GetPropertyReference(instance, object, propertyRecord->GetPropertyId(), &value, scriptContext, NULL);
- }
- #if DBG
- else
- {
- JavascriptString* indexStr = JavascriptConversion::ToString(index, scriptContext);
- PropertyRecord const * debugPropertyRecord;
- scriptContext->GetOrAddPropertyRecord(indexStr, &debugPropertyRecord);
- AssertMsg(!JavascriptOperators::GetPropertyReference(instance, object, debugPropertyRecord->GetPropertyId(), &value, scriptContext, NULL),
- "how did this property come? See OS Bug 2727708 if you see this come from the web");
- }
- #endif
- }
- if (!hasProperty)
- {
- JavascriptString* varName = nullptr;
- if (indexType == IndexType_PropertyId && propertyRecord != nullptr && propertyRecord->IsSymbol())
- {
- varName = JavascriptSymbol::ToString(propertyRecord, scriptContext);
- }
- else
- {
- varName = JavascriptConversion::ToString(index, scriptContext);
- }
- // ES5 11.2.3 #2: We evaluate the call target but don't throw yet if target member is missing. We need to evaluate argList
- // first (#3). Postpone throwing error to invoke time.
- value = ThrowErrorObject::CreateThrowTypeErrorObject(scriptContext, VBSERR_OLENoPropOrMethod, varName);
- }
- else if(!JavascriptConversion::IsCallable(value))
- {
- // ES5 11.2.3 #2: We evaluate the call target but don't throw yet if target member is missing. We need to evaluate argList
- // first (#3). Postpone throwing error to invoke time.
- JavascriptString* varName = JavascriptConversion::ToString(index, scriptContext);
- value = ThrowErrorObject::CreateThrowTypeErrorObject(scriptContext, JSERR_Property_NeedFunction, varName);
- }
- threadContext->CheckAndResetImplicitCallAccessorFlag();
- threadContext->AddImplicitCallFlags(savedImplicitCallFlags);
- return value;
- JIT_HELPER_END(Op_GetMethodElement);
- }
- BOOL JavascriptOperators::OP_SetElementI_UInt32(Var instance, uint32 index, Var value, ScriptContext* scriptContext, PropertyOperationFlags flags)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_SetElementI_UInt32);
- JIT_HELPER_SAME_ATTRIBUTES(Op_SetElementI_UInt32, Op_SetElementI);
- #if FLOATVAR
- return OP_SetElementI_JIT(instance, Js::JavascriptNumber::ToVar(index, scriptContext), value, scriptContext, flags);
- #else
- char buffer[sizeof(Js::JavascriptNumber)];
- return OP_SetElementI_JIT(instance, Js::JavascriptNumber::ToVarInPlace(index, scriptContext,
- (Js::JavascriptNumber *)buffer), value, scriptContext, flags);
- #endif
- JIT_HELPER_END(Op_SetElementI_UInt32);
- }
- BOOL JavascriptOperators::OP_SetElementI_Int32(Var instance, int32 index, Var value, ScriptContext* scriptContext, PropertyOperationFlags flags)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_SetElementI_Int32);
- JIT_HELPER_SAME_ATTRIBUTES(Op_SetElementI_Int32, Op_SetElementI);
- #if FLOATVAR
- return OP_SetElementI_JIT(instance, Js::JavascriptNumber::ToVar(index, scriptContext), value, scriptContext, flags);
- #else
- char buffer[sizeof(Js::JavascriptNumber)];
- return OP_SetElementI_JIT(instance, Js::JavascriptNumber::ToVarInPlace(index, scriptContext,
- (Js::JavascriptNumber *)buffer), value, scriptContext, flags);
- #endif
- JIT_HELPER_END(Op_SetElementI_Int32);
- }
- BOOL JavascriptOperators::OP_SetElementI_JIT(Var instance, Var index, Var value, ScriptContext* scriptContext, PropertyOperationFlags flags)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_SetElementI);
- if (TaggedNumber::Is(instance))
- {
- return OP_SetElementI(instance, index, value, scriptContext, flags);
- }
- INT_PTR vt = VirtualTableInfoBase::GetVirtualTable(instance);
- OP_SetElementI(instance, index, value, scriptContext, flags);
- return vt != VirtualTableInfoBase::GetVirtualTable(instance);
- JIT_HELPER_END(Op_SetElementI);
- }
- BOOL JavascriptOperators::OP_SetElementI(Var instance, Var index, Var value, ScriptContext* scriptContext, PropertyOperationFlags flags)
- {
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(instance);
- #endif
- TypeId instanceType = JavascriptOperators::GetTypeId(instance);
- bool isTypedArray = (instanceType >= TypeIds_Int8Array && instanceType <= TypeIds_Float64Array);
- if (isTypedArray)
- {
- if (TaggedInt::Is(index) || JavascriptNumber::Is_NoTaggedIntCheck(index) || JavascriptString::Is(index))
- {
- BOOL returnValue = FALSE;
- bool isNumericIndex = false;
- // CrossSite types will go down the slow path.
- switch (instanceType)
- {
- case TypeIds_Int8Array:
- {
- // The typed array will deal with all possible values for the index
- if (VirtualTableInfo<Int8VirtualArray>::HasVirtualTable(instance))
- {
- Int8VirtualArray* int8Array = Int8VirtualArray::UnsafeFromVar(instance);
- returnValue = int8Array->ValidateIndexAndDirectSetItem(index, value, &isNumericIndex);
- }
- else if( VirtualTableInfo<Int8Array>::HasVirtualTable(instance))
- {
- Int8Array* int8Array = Int8Array::UnsafeFromVar(instance);
- returnValue = int8Array->ValidateIndexAndDirectSetItem(index, value, &isNumericIndex);
- }
- break;
- }
- case TypeIds_Uint8Array:
- {
- // The typed array will deal with all possible values for the index
- if (VirtualTableInfo<Uint8VirtualArray>::HasVirtualTable(instance))
- {
- Uint8VirtualArray* uint8Array = Uint8VirtualArray::UnsafeFromVar(instance);
- returnValue = uint8Array->ValidateIndexAndDirectSetItem(index, value, &isNumericIndex);
- }
- else if (VirtualTableInfo<Uint8Array>::HasVirtualTable(instance))
- {
- Uint8Array* uint8Array = Uint8Array::UnsafeFromVar(instance);
- returnValue = uint8Array->ValidateIndexAndDirectSetItem(index, value, &isNumericIndex);
- }
- break;
- }
- case TypeIds_Uint8ClampedArray:
- {
- // The typed array will deal with all possible values for the index
- if (VirtualTableInfo<Uint8ClampedVirtualArray>::HasVirtualTable(instance))
- {
- Uint8ClampedVirtualArray* uint8ClampedArray = Uint8ClampedVirtualArray::UnsafeFromVar(instance);
- returnValue = uint8ClampedArray->ValidateIndexAndDirectSetItem(index, value, &isNumericIndex);
- }
- else if(VirtualTableInfo<Uint8ClampedArray>::HasVirtualTable(instance))
- {
- Uint8ClampedArray* uint8ClampedArray = Uint8ClampedArray::UnsafeFromVar(instance);
- returnValue = uint8ClampedArray->ValidateIndexAndDirectSetItem(index, value, &isNumericIndex);
- }
- break;
- }
- case TypeIds_Int16Array:
- {
- // The type array will deal with all possible values for the index
- if (VirtualTableInfo<Int16VirtualArray>::HasVirtualTable(instance))
- {
- Int16VirtualArray* int16Array = Int16VirtualArray::UnsafeFromVar(instance);
- returnValue = int16Array->ValidateIndexAndDirectSetItem(index, value, &isNumericIndex);
- }
- else if (VirtualTableInfo<Int16Array>::HasVirtualTable(instance))
- {
- Int16Array* int16Array = Int16Array::UnsafeFromVar(instance);
- returnValue = int16Array->ValidateIndexAndDirectSetItem(index, value, &isNumericIndex);
- }
- break;
- }
- case TypeIds_Uint16Array:
- {
- // The type array will deal with all possible values for the index
- if (VirtualTableInfo<Uint16VirtualArray>::HasVirtualTable(instance))
- {
- Uint16VirtualArray* uint16Array = Uint16VirtualArray::UnsafeFromVar(instance);
- returnValue = uint16Array->ValidateIndexAndDirectSetItem(index, value, &isNumericIndex);
- }
- else if (VirtualTableInfo<Uint16Array>::HasVirtualTable(instance))
- {
- Uint16Array* uint16Array = Uint16Array::UnsafeFromVar(instance);
- returnValue = uint16Array->ValidateIndexAndDirectSetItem(index, value, &isNumericIndex);
- }
- break;
- }
- case TypeIds_Int32Array:
- {
- // The type array will deal with all possible values for the index
- if (VirtualTableInfo<Int32VirtualArray>::HasVirtualTable(instance))
- {
- Int32VirtualArray* int32Array = Int32VirtualArray::UnsafeFromVar(instance);
- returnValue = int32Array->ValidateIndexAndDirectSetItem(index, value, &isNumericIndex);
- }
- else if(VirtualTableInfo<Int32Array>::HasVirtualTable(instance))
- {
- Int32Array* int32Array = Int32Array::UnsafeFromVar(instance);
- returnValue = int32Array->ValidateIndexAndDirectSetItem(index, value, &isNumericIndex);
- }
- break;
- }
- case TypeIds_Uint32Array:
- {
- // The type array will deal with all possible values for the index
- if (VirtualTableInfo<Uint32VirtualArray>::HasVirtualTable(instance))
- {
- Uint32VirtualArray* uint32Array = Uint32VirtualArray::UnsafeFromVar(instance);
- returnValue = uint32Array->ValidateIndexAndDirectSetItem(index, value, &isNumericIndex);
- }
- else if (VirtualTableInfo<Uint32Array>::HasVirtualTable(instance))
- {
- Uint32Array* uint32Array = Uint32Array::UnsafeFromVar(instance);
- returnValue = uint32Array->ValidateIndexAndDirectSetItem(index, value, &isNumericIndex);
- }
- break;
- }
- case TypeIds_Float32Array:
- {
- // The type array will deal with all possible values for the index
- if (VirtualTableInfo<Float32VirtualArray>::HasVirtualTable(instance))
- {
- Float32VirtualArray* float32Array = Float32VirtualArray::UnsafeFromVar(instance);
- returnValue = float32Array->ValidateIndexAndDirectSetItem(index, value, &isNumericIndex);
- }
- else if (VirtualTableInfo<Float32Array>::HasVirtualTable(instance))
- {
- Float32Array* float32Array = Float32Array::UnsafeFromVar(instance);
- returnValue = float32Array->ValidateIndexAndDirectSetItem(index, value, &isNumericIndex);
- }
- break;
- }
- case TypeIds_Float64Array:
- {
- // The type array will deal with all possible values for the index
- if (VirtualTableInfo<Float64VirtualArray>::HasVirtualTable(instance))
- {
- Float64VirtualArray* float64Array = Float64VirtualArray::UnsafeFromVar(instance);
- returnValue = float64Array->ValidateIndexAndDirectSetItem(index, value, &isNumericIndex);
- }
- else if (VirtualTableInfo<Float64Array>::HasVirtualTable(instance))
- {
- Float64Array* float64Array = Float64Array::UnsafeFromVar(instance);
- returnValue = float64Array->ValidateIndexAndDirectSetItem(index, value, &isNumericIndex);
- }
- break;
- }
- }
- // if this was numeric index, return operation status else
- // Return the result of calling the default ordinary object [[Set]] internal method (9.1.8) on O passing P, V, and Receiver as arguments.
- if (isNumericIndex)
- return returnValue;
- }
- }
- else
- {
- if (TaggedInt::Is(index))
- {
- TaggedIntIndex:
- switch (instanceType)
- {
- case TypeIds_NativeIntArray:
- case TypeIds_NativeFloatArray:
- case TypeIds_Array: // fast path for array
- {
- int indexInt = TaggedInt::ToInt32(index);
- if (indexInt >= 0 && scriptContext->optimizationOverrides.IsEnabledArraySetElementFastPath())
- {
- JavascriptArray::UnsafeFromVar(instance)->SetItem((uint32)indexInt, value, flags);
- return TRUE;
- }
- break;
- }
- }
- }
- else if (JavascriptNumber::Is_NoTaggedIntCheck(index))
- {
- double dIndexValue = JavascriptNumber::GetValue(index);
- uint32 uint32Index = JavascriptConversion::ToUInt32(index, scriptContext);
- if ((double)uint32Index == dIndexValue && !TaggedInt::IsOverflow(uint32Index))
- {
- index = TaggedInt::ToVarUnchecked(uint32Index);
- goto TaggedIntIndex;
- }
- }
- }
- RecyclableObject* object = nullptr;
- if (!GetPropertyObjectForSetElementI(instance, index, scriptContext, &object))
- {
- return FALSE;
- }
- return JavascriptOperators::SetElementIHelper(instance, object, index, value, scriptContext, flags);
- }
- BOOL JavascriptOperators::SetElementIHelper(Var receiver, RecyclableObject* object, Var index, Var value, ScriptContext* scriptContext, PropertyOperationFlags flags)
- {
- IndexType indexType;
- uint32 indexVal = 0;
- PropertyRecord const * propertyRecord = nullptr;
- JavascriptString * propertyNameString = nullptr;
- PropertyValueInfo propertyValueInfo;
- RecyclableObject* cacheOwner;
- PropertyRecordUsageCache* propertyRecordUsageCache;
- if (JavascriptOperators::GetPropertyRecordUsageCache(index, scriptContext, &propertyRecordUsageCache, &cacheOwner))
- {
- return JavascriptOperators::SetElementIWithCache<false>(receiver, object, cacheOwner, value, propertyRecordUsageCache, scriptContext, flags, nullptr);
- }
- if (TaggedNumber::Is(receiver))
- {
- return JavascriptOperators::SetElementIOnTaggedNumber(receiver, object, index, value, scriptContext, flags);
- }
- #if DBG_DUMP
- scriptContext->forinNoCache += (!TaggedInt::Is(index) && JavascriptString::Is(index));
- #endif
- indexType = GetIndexType(index, scriptContext, &indexVal, &propertyRecord, &propertyNameString, false, true);
- if (scriptContext->GetThreadContext()->IsDisableImplicitCall() &&
- scriptContext->GetThreadContext()->GetImplicitCallFlags() != ImplicitCall_None)
- {
- // We hit an implicit call trying to convert the index, and implicit calls are disabled, so
- // quit before we try to store the element.
- return FALSE;
- }
- if (indexType == IndexType_Number)
- {
- SetElementIHelper_INDEX_TYPE_IS_NUMBER:
- return JavascriptOperators::SetItem(receiver, object, indexVal, value, scriptContext, flags);
- }
- else if (indexType == IndexType_JavascriptString)
- {
- Assert(propertyNameString);
- // At this point, we know that the propertyNameString is neither PropertyString
- // or LiteralStringWithPropertyStringPtr.. Get PropertyRecord!
- // we will get it anyways otherwise. (Also, 1:1 string comparison for Builtin types will be expensive.)
- if (propertyRecord == nullptr)
- {
- scriptContext->GetOrAddPropertyRecord(propertyNameString, &propertyRecord);
- if (propertyRecord->IsNumeric())
- {
- indexVal = propertyRecord->GetNumericValue();
- goto SetElementIHelper_INDEX_TYPE_IS_NUMBER;
- }
- }
- }
- Assert(indexType == IndexType_PropertyId || indexType == IndexType_JavascriptString);
- Assert(propertyRecord);
- return JavascriptOperators::SetProperty(receiver, object, propertyRecord->GetPropertyId(), value, scriptContext, flags);
- }
- BOOL JavascriptOperators::OP_SetNativeIntElementI(
- Var instance,
- Var aElementIndex,
- int32 iValue,
- ScriptContext* scriptContext,
- PropertyOperationFlags flags)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_SetNativeIntElementI);
- INT_PTR vt = (INT_PTR)nullptr;
- vt = VirtualTableInfoBase::GetVirtualTable(instance);
- if (TaggedInt::Is(aElementIndex))
- {
- int32 indexInt = TaggedInt::ToInt32(aElementIndex);
- if (indexInt >= 0 && scriptContext->optimizationOverrides.IsEnabledArraySetElementFastPath())
- {
- JavascriptNativeIntArray *arr = JavascriptNativeIntArray::FromVar(instance);
- if (!(arr->TryGrowHeadSegmentAndSetItem<int32, JavascriptNativeIntArray>((uint32)indexInt, iValue)))
- {
- arr->SetItem(indexInt, iValue);
- }
- return vt != VirtualTableInfoBase::GetVirtualTable(instance);
- }
- }
- JavascriptOperators::OP_SetElementI(instance, aElementIndex, JavascriptNumber::ToVar(iValue, scriptContext), scriptContext, flags);
- return vt != VirtualTableInfoBase::GetVirtualTable(instance);
- JIT_HELPER_END(Op_SetNativeIntElementI);
- }
- BOOL JavascriptOperators::OP_SetNativeIntElementI_UInt32(
- Var instance,
- uint32 aElementIndex,
- int32 iValue,
- ScriptContext* scriptContext,
- PropertyOperationFlags flags)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_SetNativeIntElementI_UInt32);
- JIT_HELPER_SAME_ATTRIBUTES(Op_SetNativeIntElementI_UInt32, Op_SetNativeIntElementI);
- #if FLOATVAR
- return OP_SetNativeIntElementI(instance, Js::JavascriptNumber::ToVar(aElementIndex, scriptContext), iValue, scriptContext, flags);
- #else
- char buffer[sizeof(Js::JavascriptNumber)];
- return OP_SetNativeIntElementI(instance, Js::JavascriptNumber::ToVarInPlace(aElementIndex, scriptContext,
- (Js::JavascriptNumber *)buffer), iValue, scriptContext, flags);
- #endif
- JIT_HELPER_END(Op_SetNativeIntElementI_UInt32);
- }
- BOOL JavascriptOperators::OP_SetNativeIntElementI_Int32(
- Var instance,
- int aElementIndex,
- int32 iValue,
- ScriptContext* scriptContext,
- PropertyOperationFlags flags)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_SetNativeIntElementI_Int32);
- JIT_HELPER_SAME_ATTRIBUTES(Op_SetNativeIntElementI_Int32, Op_SetNativeIntElementI);
- #if FLOATVAR
- return OP_SetNativeIntElementI(instance, Js::JavascriptNumber::ToVar(aElementIndex, scriptContext), iValue, scriptContext, flags);
- #else
- char buffer[sizeof(Js::JavascriptNumber)];
- return OP_SetNativeIntElementI(instance, Js::JavascriptNumber::ToVarInPlace(aElementIndex, scriptContext,
- (Js::JavascriptNumber *)buffer), iValue, scriptContext, flags);
- #endif
- JIT_HELPER_END(Op_SetNativeIntElementI_Int32);
- }
- BOOL JavascriptOperators::OP_SetNativeFloatElementI(
- Var instance,
- Var aElementIndex,
- ScriptContext* scriptContext,
- PropertyOperationFlags flags,
- double dValue)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_SetNativeFloatElementI);
- INT_PTR vt = (INT_PTR)nullptr;
- vt = VirtualTableInfoBase::GetVirtualTable(instance);
- if (TaggedInt::Is(aElementIndex))
- {
- int32 indexInt = TaggedInt::ToInt32(aElementIndex);
- if (indexInt >= 0 && scriptContext->optimizationOverrides.IsEnabledArraySetElementFastPath())
- {
- JavascriptNativeFloatArray *arr = JavascriptNativeFloatArray::FromVar(instance);
- if (!(arr->TryGrowHeadSegmentAndSetItem<double, JavascriptNativeFloatArray>((uint32)indexInt, dValue)))
- {
- arr->SetItem(indexInt, dValue);
- }
- return vt != VirtualTableInfoBase::GetVirtualTable(instance);
- }
- }
- JavascriptOperators::OP_SetElementI(instance, aElementIndex, JavascriptNumber::ToVarWithCheck(dValue, scriptContext), scriptContext, flags);
- return vt != VirtualTableInfoBase::GetVirtualTable(instance);
- JIT_HELPER_END(Op_SetNativeFloatElementI);
- }
- BOOL JavascriptOperators::OP_SetNativeFloatElementI_UInt32(
- Var instance,
- uint32 aElementIndex,
- ScriptContext* scriptContext,
- PropertyOperationFlags flags,
- double dValue)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_SetNativeFloatElementI_UInt32);
- JIT_HELPER_SAME_ATTRIBUTES(Op_SetNativeFloatElementI_UInt32, Op_SetNativeFloatElementI);
- #if FLOATVAR
- return OP_SetNativeFloatElementI(instance, JavascriptNumber::ToVar(aElementIndex, scriptContext), scriptContext, flags, dValue);
- #else
- char buffer[sizeof(Js::JavascriptNumber)];
- return OP_SetNativeFloatElementI(instance, JavascriptNumber::ToVarInPlace(aElementIndex, scriptContext,
- (Js::JavascriptNumber *)buffer), scriptContext, flags, dValue);
- #endif
- JIT_HELPER_END(Op_SetNativeFloatElementI_UInt32);
- }
- BOOL JavascriptOperators::OP_SetNativeFloatElementI_Int32(
- Var instance,
- int aElementIndex,
- ScriptContext* scriptContext,
- PropertyOperationFlags flags,
- double dValue)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_SetNativeFloatElementI_Int32);
- JIT_HELPER_SAME_ATTRIBUTES(Op_SetNativeFloatElementI_Int32, Op_SetNativeFloatElementI);
- #if FLOATVAR
- return OP_SetNativeFloatElementI(instance, JavascriptNumber::ToVar(aElementIndex, scriptContext), scriptContext, flags, dValue);
- #else
- char buffer[sizeof(Js::JavascriptNumber)];
- return OP_SetNativeFloatElementI(instance, JavascriptNumber::ToVarInPlace(aElementIndex, scriptContext,
- (Js::JavascriptNumber *)buffer), scriptContext, flags, dValue);
- #endif
- JIT_HELPER_END(Op_SetNativeFloatElementI_Int32);
- }
- BOOL JavascriptOperators::OP_Memcopy(Var dstInstance, int32 dstStart, Var srcInstance, int32 srcStart, int32 length, ScriptContext* scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_HEADER(Op_Memcopy, reentrancylock, scriptContext->GetThreadContext());
- if (length <= 0)
- {
- return false;
- }
- TypeId instanceType = JavascriptOperators::GetTypeId(srcInstance);
- if (instanceType != JavascriptOperators::GetTypeId(dstInstance))
- {
- return false;
- }
- if (srcStart != dstStart)
- {
- return false;
- }
- BOOL returnValue = false;
- #define MEMCOPY_TYPED_ARRAY(type, conversion) type ## ::FromVar(dstInstance)->DirectSetItemAtRange( type ## ::FromVar(srcInstance), srcStart, dstStart, length, JavascriptConversion:: ## conversion)
- switch (instanceType)
- {
- case TypeIds_Int8Array:
- {
- returnValue = MEMCOPY_TYPED_ARRAY(Int8Array, ToInt8);
- break;
- }
- case TypeIds_Uint8Array:
- {
- returnValue = MEMCOPY_TYPED_ARRAY(Uint8Array, ToUInt8);
- break;
- }
- case TypeIds_Uint8ClampedArray:
- {
- returnValue = MEMCOPY_TYPED_ARRAY(Uint8ClampedArray, ToUInt8Clamped);
- break;
- }
- case TypeIds_Int16Array:
- {
- returnValue = MEMCOPY_TYPED_ARRAY(Int16Array, ToInt16);
- break;
- }
- case TypeIds_Uint16Array:
- {
- returnValue = MEMCOPY_TYPED_ARRAY(Uint16Array, ToUInt16);
- break;
- }
- case TypeIds_Int32Array:
- {
- returnValue = MEMCOPY_TYPED_ARRAY(Int32Array, ToInt32);
- break;
- }
- case TypeIds_Uint32Array:
- {
- returnValue = MEMCOPY_TYPED_ARRAY(Uint32Array, ToUInt32);
- break;
- }
- case TypeIds_Float32Array:
- {
- returnValue = MEMCOPY_TYPED_ARRAY(Float32Array, ToFloat);
- break;
- }
- case TypeIds_Float64Array:
- {
- returnValue = MEMCOPY_TYPED_ARRAY(Float64Array, ToNumber);
- break;
- }
- case TypeIds_Array:
- case TypeIds_NativeFloatArray:
- case TypeIds_NativeIntArray:
- {
- if (dstStart < 0 || srcStart < 0)
- {
- // This is not supported, Bailout
- break;
- }
- // Upper bounds check for source array
- JavascriptArray* srcArray = JavascriptArray::UnsafeFromVar(srcInstance);
- JavascriptArray* dstArray = JavascriptArray::FromVar(dstInstance);
- if (scriptContext->optimizationOverrides.IsEnabledArraySetElementFastPath())
- {
- INT_PTR vt = VirtualTableInfoBase::GetVirtualTable(dstInstance);
- if (instanceType == TypeIds_Array)
- {
- returnValue = dstArray->DirectSetItemAtRangeFromArray<Var>(dstStart, length, srcArray, srcStart);
- }
- else if (instanceType == TypeIds_NativeIntArray)
- {
- returnValue = dstArray->DirectSetItemAtRangeFromArray<int32>(dstStart, length, srcArray, srcStart);
- }
- else
- {
- returnValue = dstArray->DirectSetItemAtRangeFromArray<double>(dstStart, length, srcArray, srcStart);
- }
- returnValue &= vt == VirtualTableInfoBase::GetVirtualTable(dstInstance);
- }
- break;
- }
- default:
- AssertMsg(false, "We don't support this type for memcopy yet.");
- break;
- }
- #undef MEMCOPY_TYPED_ARRAY
- return returnValue;
- JIT_HELPER_END(Op_Memcopy);
- }
- template<typename T, T(*func)(Var, ScriptContext*)> bool MemsetConversion(Var value, ScriptContext* scriptContext, T* result)
- {
- ImplicitCallFlags flags = scriptContext->GetThreadContext()->TryWithDisabledImplicitCall([&]
- {
- *result = func(value, scriptContext);
- });
- return (flags & (~ImplicitCall_None)) == 0;
- }
- BOOL JavascriptOperators::OP_Memset(Var instance, int32 start, Var value, int32 length, ScriptContext* scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_HEADER(Op_Memset, reentrancylock, scriptContext->GetThreadContext());
- if (length <= 0)
- {
- return false;
- }
- TypeId instanceType = JavascriptOperators::GetTypeId(instance);
- BOOL returnValue = false;
- // The typed array will deal with all possible values for the index
- #define MEMSET_TYPED_ARRAY_CASE(type, conversion) \
- case TypeIds_##type: \
- { \
- type## ::TypedArrayType typedValue = 0; \
- if (!MemsetConversion<type## ::TypedArrayType, JavascriptConversion:: ##conversion>(value, scriptContext, &typedValue)) return false; \
- returnValue = type## ::FromVar(instance)->DirectSetItemAtRange(start, length, typedValue); \
- break; \
- }
- switch (instanceType)
- {
- MEMSET_TYPED_ARRAY_CASE(Int8Array, ToInt8)
- MEMSET_TYPED_ARRAY_CASE(Uint8Array, ToUInt8)
- MEMSET_TYPED_ARRAY_CASE(Uint8ClampedArray, ToUInt8Clamped)
- MEMSET_TYPED_ARRAY_CASE(Int16Array, ToInt16)
- MEMSET_TYPED_ARRAY_CASE(Uint16Array, ToUInt16)
- MEMSET_TYPED_ARRAY_CASE(Int32Array, ToInt32)
- MEMSET_TYPED_ARRAY_CASE(Uint32Array, ToUInt32)
- MEMSET_TYPED_ARRAY_CASE(Float32Array, ToFloat)
- MEMSET_TYPED_ARRAY_CASE(Float64Array, ToNumber)
- case TypeIds_NativeFloatArray:
- case TypeIds_NativeIntArray:
- case TypeIds_Array:
- {
- if (start < 0)
- {
- for (start; start < 0 && length > 0; ++start, --length)
- {
- if (!OP_SetElementI(instance, JavascriptNumber::ToVar(start, scriptContext), value, scriptContext))
- {
- return false;
- }
- }
- }
- if (scriptContext->optimizationOverrides.IsEnabledArraySetElementFastPath())
- {
- INT_PTR vt = VirtualTableInfoBase::GetVirtualTable(instance);
- if (instanceType == TypeIds_Array)
- {
- returnValue = JavascriptArray::UnsafeFromVar(instance)->DirectSetItemAtRange<Var>(start, length, value);
- }
- else if (instanceType == TypeIds_NativeIntArray)
- {
- // Only accept tagged int. Also covers case for MissingItem
- if (!TaggedInt::Is(value))
- {
- return false;
- }
- int32 intValue = 0;
- if (!MemsetConversion<int32, JavascriptConversion::ToInt32>(value, scriptContext, &intValue))
- {
- return false;
- }
- returnValue = JavascriptArray::UnsafeFromVar(instance)->DirectSetItemAtRange<int32>(start, length, intValue);
- }
- else
- {
- // For native float arrays, the jit doesn't check the type of the source so we have to do it here
- if (!JavascriptNumber::Is(value) && !TaggedNumber::Is(value))
- {
- return false;
- }
- double doubleValue = 0;
- if (!MemsetConversion<double, JavascriptConversion::ToNumber>(value, scriptContext, &doubleValue))
- {
- return false;
- }
- // Special case for missing item
- if (SparseArraySegment<double>::IsMissingItem(&doubleValue))
- {
- return false;
- }
- returnValue = JavascriptArray::UnsafeFromVar(instance)->DirectSetItemAtRange<double>(start, length, doubleValue);
- }
- returnValue &= vt == VirtualTableInfoBase::GetVirtualTable(instance);
- }
- break;
- }
- default:
- AssertMsg(false, "We don't support this type for memset yet.");
- break;
- }
- #undef MEMSET_TYPED_ARRAY
- return returnValue;
- JIT_HELPER_END(Op_Memset);
- }
- Var JavascriptOperators::OP_DeleteElementI_UInt32(Var instance, uint32 index, ScriptContext* scriptContext, PropertyOperationFlags propertyOperationFlags)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_DeleteElementI_UInt32);
- JIT_HELPER_SAME_ATTRIBUTES(Op_DeleteElementI_UInt32, Op_DeleteElementI);
- #if FLOATVAR
- return OP_DeleteElementI(instance, Js::JavascriptNumber::ToVar(index, scriptContext), scriptContext, propertyOperationFlags);
- #else
- char buffer[sizeof(Js::JavascriptNumber)];
- return OP_DeleteElementI(instance, Js::JavascriptNumber::ToVarInPlace(index, scriptContext,
- (Js::JavascriptNumber *)buffer), scriptContext, propertyOperationFlags);
- #endif
- JIT_HELPER_END(Op_DeleteElementI_UInt32);
- }
- Var JavascriptOperators::OP_DeleteElementI_Int32(Var instance, int32 index, ScriptContext* scriptContext, PropertyOperationFlags propertyOperationFlags)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_DeleteElementI_Int32);
- JIT_HELPER_SAME_ATTRIBUTES(Op_DeleteElementI_Int32, Op_DeleteElementI);
- #if FLOATVAR
- return OP_DeleteElementI(instance, Js::JavascriptNumber::ToVar(index, scriptContext), scriptContext, propertyOperationFlags);
- #else
- char buffer[sizeof(Js::JavascriptNumber)];
- return OP_DeleteElementI(instance, Js::JavascriptNumber::ToVarInPlace(index, scriptContext,
- (Js::JavascriptNumber *)buffer), scriptContext, propertyOperationFlags);
- #endif
- JIT_HELPER_END(Op_DeleteElementI_Int32);
- }
- Var JavascriptOperators::OP_DeleteElementI(Var instance, Var index, ScriptContext* scriptContext, PropertyOperationFlags propertyOperationFlags)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_DeleteElementI);
- if(TaggedNumber::Is(instance))
- {
- return scriptContext->GetLibrary()->GetTrue();
- }
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(instance);
- #endif
- RecyclableObject* object = RecyclableObject::FromVar(instance);
- if (JavascriptOperators::IsUndefinedOrNull(object))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_CannotDelete_NullOrUndefined, GetPropertyDisplayNameForError(index, scriptContext));
- }
- uint32 indexVal;
- PropertyRecord const * propertyRecord = nullptr;
- JavascriptString * propertyNameString = nullptr;
- BOOL result = TRUE;
- IndexType indexType = GetIndexType(index, scriptContext, &indexVal, &propertyRecord, &propertyNameString, false, true);
- if (indexType == IndexType_Number)
- {
- result = JavascriptOperators::DeleteItem(object, indexVal, propertyOperationFlags);
- }
- else if (indexType == IndexType_JavascriptString)
- {
- result = JavascriptOperators::DeleteProperty(object, propertyNameString, propertyOperationFlags);
- }
- else
- {
- Assert(indexType == IndexType_PropertyId);
- if (propertyRecord == nullptr && !JavascriptOperators::CanShortcutOnUnknownPropertyName(object))
- {
- indexType = GetIndexTypeFromPrimitive(index, scriptContext, &indexVal, &propertyRecord, true);
- Assert(indexType == IndexType_PropertyId);
- Assert(propertyRecord != nullptr);
- }
- if (propertyRecord != nullptr)
- {
- result = JavascriptOperators::DeleteProperty(object, propertyRecord->GetPropertyId(), propertyOperationFlags);
- }
- #if DBG
- else
- {
- JavascriptString* indexStr = JavascriptConversion::ToString(index, scriptContext);
- PropertyRecord const * debugPropertyRecord;
- scriptContext->GetOrAddPropertyRecord(indexStr, &debugPropertyRecord);
- AssertMsg(JavascriptOperators::DeleteProperty(object, debugPropertyRecord->GetPropertyId(), propertyOperationFlags), "delete should have been true. See OS Bug 2727708 if you see this come from the web");
- }
- #endif
- }
- Assert(result || !(propertyOperationFlags & (PropertyOperation_StrictMode | PropertyOperation_ThrowOnDeleteIfNotConfig)));
- return scriptContext->GetLibrary()->CreateBoolean(result);
- JIT_HELPER_END(Op_DeleteElementI);
- }
- Var JavascriptOperators::OP_GetLength(Var instance, ScriptContext* scriptContext)
- {
- return JavascriptOperators::OP_GetProperty(instance, PropertyIds::length, scriptContext);
- }
- Var JavascriptOperators::GetThisFromModuleRoot(Var thisVar)
- {
- RootObjectBase * rootObject = static_cast<RootObjectBase*>(thisVar);
- RecyclableObject* hostObject = rootObject->GetHostObject();
- //
- // if the module root has the host object, use that as "this"
- //
- if (hostObject)
- {
- thisVar = hostObject->GetHostDispatchVar();
- }
- return thisVar;
- }
- inline void JavascriptOperators::TryLoadRoot(Var& thisVar, TypeId typeId, int moduleID, ScriptContextInfo* scriptContext)
- {
- bool loadRoot = false;
- if (JavascriptOperators::IsUndefinedOrNullType(typeId) || typeId == TypeIds_ActivationObject)
- {
- loadRoot = true;
- }
- else if (typeId == TypeIds_HostDispatch)
- {
- TypeId remoteTypeId = TypeIds_Limit;
- if (RecyclableObject::FromVar(thisVar)->GetRemoteTypeId(&remoteTypeId))
- {
- if (remoteTypeId == TypeIds_Null || remoteTypeId == TypeIds_Undefined || remoteTypeId == TypeIds_ActivationObject)
- {
- loadRoot = true;
- }
- }
- }
- if (loadRoot)
- {
- if (moduleID == 0)
- {
- thisVar = (Js::Var)scriptContext->GetGlobalObjectThisAddr();
- }
- else
- {
- // TODO: OOP JIT, create a copy of module roots in server side
- Js::ModuleRoot * moduleRoot = JavascriptOperators::GetModuleRoot(moduleID, (ScriptContext*)scriptContext);
- if (moduleRoot == nullptr)
- {
- Assert(false);
- thisVar = (Js::Var)scriptContext->GetUndefinedAddr();
- }
- else
- {
- thisVar = GetThisFromModuleRoot(moduleRoot);
- }
- }
- }
- }
- Var JavascriptOperators::OP_GetThis(Var thisVar, int moduleID, ScriptContextInfo* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(LdThis);
- //
- // if "this" is null or undefined
- // Pass the global object
- // Else
- // Pass ToObject(this)
- //
- TypeId typeId = JavascriptOperators::GetTypeId(thisVar);
- Assert(!JavascriptOperators::IsThisSelf(typeId));
- return JavascriptOperators::GetThisHelper(thisVar, typeId, moduleID, scriptContext);
- JIT_HELPER_END(LdThis);
- }
- Var JavascriptOperators::OP_GetThisNoFastPath(Var thisVar, int moduleID, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(LdThisNoFastPath);
- TypeId typeId = JavascriptOperators::GetTypeId(thisVar);
- if (JavascriptOperators::IsThisSelf(typeId))
- {
- Assert(typeId != TypeIds_GlobalObject || ((Js::GlobalObject*)thisVar)->ToThis() == thisVar);
- Assert(typeId != TypeIds_ModuleRoot || JavascriptOperators::GetThisFromModuleRoot(thisVar) == thisVar);
- return thisVar;
- }
- return JavascriptOperators::GetThisHelper(thisVar, typeId, moduleID, scriptContext);
- JIT_HELPER_END(LdThisNoFastPath);
- }
- bool JavascriptOperators::IsThisSelf(TypeId typeId)
- {
- return (JavascriptOperators::IsObjectType(typeId) && ! JavascriptOperators::IsSpecialObjectType(typeId));
- }
- Var JavascriptOperators::GetThisHelper(Var thisVar, TypeId typeId, int moduleID, ScriptContextInfo *scriptContext)
- {
- if (! JavascriptOperators::IsObjectType(typeId) && ! JavascriptOperators::IsUndefinedOrNullType(typeId))
- {
- #if ENABLE_NATIVE_CODEGEN
- Assert(!JITManager::GetJITManager()->IsJITServer());
- #endif
- #if !FLOATVAR
- // We allowed stack number to be used as the "this" for getter and setter activation of
- // n.x and n[prop], where n is the Javascript Number
- return JavascriptOperators::ToObject(
- JavascriptNumber::BoxStackNumber(thisVar, (ScriptContext*)scriptContext), (ScriptContext*)scriptContext);
- #else
- return JavascriptOperators::ToObject(thisVar, (ScriptContext*)scriptContext);
- #endif
- }
- else
- {
- TryLoadRoot(thisVar, typeId, moduleID, scriptContext);
- return thisVar;
- }
- }
- Var JavascriptOperators::OP_StrictGetThis(Var thisVar, ScriptContext* scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_HEADER(StrictLdThis, reentrancylock, scriptContext->GetThreadContext());
- TypeId typeId = JavascriptOperators::GetTypeId(thisVar);
- if (typeId == TypeIds_ActivationObject)
- {
- return scriptContext->GetLibrary()->GetUndefined();
- }
- return thisVar;
- JIT_HELPER_END(StrictLdThis);
- }
- BOOL JavascriptOperators::GetRemoteTypeId(Var aValue, __out TypeId* typeId)
- {
- *typeId = TypeIds_Limit;
- if (GetTypeId(aValue) != TypeIds_HostDispatch)
- {
- return FALSE;
- }
- return RecyclableObject::FromVar(aValue)->GetRemoteTypeId(typeId);
- }
- BOOL JavascriptOperators::IsJsNativeType(TypeId type)
- {
- switch(type)
- {
- case TypeIds_Object:
- case TypeIds_Function:
- case TypeIds_Array:
- case TypeIds_NativeIntArray:
- #if ENABLE_COPYONACCESS_ARRAY
- case TypeIds_CopyOnAccessNativeIntArray:
- #endif
- case TypeIds_NativeFloatArray:
- case TypeIds_ES5Array:
- case TypeIds_Date:
- case TypeIds_WinRTDate:
- case TypeIds_RegEx:
- case TypeIds_Error:
- case TypeIds_BooleanObject:
- case TypeIds_NumberObject:
- case TypeIds_StringObject:
- case TypeIds_Symbol:
- case TypeIds_SymbolObject:
- //case TypeIds_GlobalObject:
- //case TypeIds_ModuleRoot:
- //case TypeIds_HostObject:
- case TypeIds_Arguments:
- case TypeIds_ActivationObject:
- case TypeIds_Map:
- case TypeIds_Set:
- case TypeIds_WeakMap:
- case TypeIds_WeakSet:
- case TypeIds_ArrayIterator:
- case TypeIds_MapIterator:
- case TypeIds_SetIterator:
- case TypeIds_StringIterator:
- case TypeIds_Generator:
- case TypeIds_Promise:
- case TypeIds_Proxy:
- return true;
- default:
- return false;
- }
- }
- BOOL JavascriptOperators::IsJsNativeObject(Var instance)
- {
- return IsJsNativeType(GetTypeId(instance));
- }
- BOOL JavascriptOperators::IsJsNativeObject(_In_ RecyclableObject* instance)
- {
- return IsJsNativeType(GetTypeId(instance));
- }
- bool JavascriptOperators::CanShortcutOnUnknownPropertyName(RecyclableObject *instance)
- {
- if (!CanShortcutInstanceOnUnknownPropertyName(instance))
- {
- return false;
- }
- return CanShortcutPrototypeChainOnUnknownPropertyName(instance->GetPrototype());
- }
- bool JavascriptOperators::CanShortcutInstanceOnUnknownPropertyName(RecyclableObject *instance)
- {
- if (PHASE_OFF1(Js::OptUnknownElementNamePhase))
- {
- return false;
- }
- TypeId typeId = instance->GetTypeId();
- if (typeId == TypeIds_Proxy || typeId == TypeIds_HostDispatch)
- {
- return false;
- }
- if (DynamicType::Is(typeId) &&
- static_cast<DynamicObject*>(instance)->GetTypeHandler()->IsStringTypeHandler())
- {
- return false;
- }
- if (instance->IsExternal())
- {
- return false;
- }
- if (!(instance->HasDeferredTypeHandler()))
- {
- JavascriptFunction * function = JavascriptOperators::TryFromVar<JavascriptFunction>(instance);
- return function && function->IsExternalFunction();
- }
- return false;
- }
- bool JavascriptOperators::CanShortcutPrototypeChainOnUnknownPropertyName(RecyclableObject *prototype)
- {
- Assert(prototype);
- for (; !JavascriptOperators::IsNull(prototype); prototype = prototype->GetPrototype())
- {
- if (!CanShortcutInstanceOnUnknownPropertyName(prototype))
- {
- return false;
- }
- }
- return true;
- }
- RecyclableObject* JavascriptOperators::GetPrototype(RecyclableObject* instance)
- {
- if (JavascriptOperators::GetTypeId(instance) == TypeIds_Null)
- {
- return instance;
- }
- return instance->GetPrototype();
- }
- RecyclableObject* JavascriptOperators::OP_GetPrototype(Var instance, ScriptContext* scriptContext)
- {
- if (TaggedNumber::Is(instance))
- {
- return scriptContext->GetLibrary()->GetNumberPrototype();
- }
- else
- {
- RecyclableObject* object = RecyclableObject::FromVar(instance);
- if (JavascriptOperators::IsNull(object))
- {
- return object;
- }
- return JavascriptOperators::GetPrototype(object);
- }
- }
- BOOL JavascriptOperators::OP_BrFncEqApply(Var instance, ScriptContext *scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_HEADER(Op_OP_BrFncEqApply, reentrancylock, scriptContext->GetThreadContext());
- // JavascriptFunction && !HostDispatch
- if (JavascriptOperators::GetTypeId(instance) == TypeIds_Function)
- {
- FunctionProxy *bod= ((JavascriptFunction*)instance)->GetFunctionProxy();
- if (bod != nullptr)
- {
- return bod->GetDirectEntryPoint(bod->GetDefaultEntryPointInfo()) == &Js::JavascriptFunction::EntryApply;
- }
- else
- {
- FunctionInfo* info = ((JavascriptFunction *)instance)->GetFunctionInfo();
- if (info != nullptr)
- {
- return &Js::JavascriptFunction::EntryApply == info->GetOriginalEntryPoint();
- }
- else
- {
- return false;
- }
- }
- }
- return false;
- JIT_HELPER_END(Op_OP_BrFncEqApply);
- }
- BOOL JavascriptOperators::OP_BrFncNeqApply(Var instance, ScriptContext *scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_HEADER(Op_OP_BrFncNeqApply, reentrancylock, scriptContext->GetThreadContext());
- // JavascriptFunction and !HostDispatch
- if (JavascriptOperators::GetTypeId(instance) == TypeIds_Function)
- {
- FunctionProxy *bod = ((JavascriptFunction *)instance)->GetFunctionProxy();
- if (bod != nullptr)
- {
- return bod->GetDirectEntryPoint(bod->GetDefaultEntryPointInfo()) != &Js::JavascriptFunction::EntryApply;
- }
- else
- {
- FunctionInfo* info = ((JavascriptFunction *)instance)->GetFunctionInfo();
- if (info != nullptr)
- {
- return &Js::JavascriptFunction::EntryApply != info->GetOriginalEntryPoint();
- }
- else
- {
- return true;
- }
- }
- }
- return true;
- JIT_HELPER_END(Op_OP_BrFncNeqApply);
- }
- BOOL JavascriptOperators::OP_BrHasSideEffects(int se, ScriptContext* scriptContext)
- {
- return (scriptContext->optimizationOverrides.GetSideEffects() & se) != SideEffects_None;
- }
- BOOL JavascriptOperators::OP_BrNotHasSideEffects(int se, ScriptContext* scriptContext)
- {
- return (scriptContext->optimizationOverrides.GetSideEffects() & se) == SideEffects_None;
- }
- // returns NULL if there is no more elements to enumerate.
- Var JavascriptOperators::OP_BrOnEmpty(ForInObjectEnumerator * aEnumerator)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_OP_BrOnEmpty);
- PropertyId id;
- return aEnumerator->MoveAndGetNext(id);
- JIT_HELPER_END(Op_OP_BrOnEmpty);
- }
- void JavascriptOperators::OP_InitForInEnumerator(Var enumerable, ForInObjectEnumerator * enumerator, ScriptContext* scriptContext, EnumeratorCache * forInCache)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_OP_InitForInEnumerator);
- RecyclableObject* enumerableObject;
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(enumerable);
- #endif
- if (!GetPropertyObject(enumerable, scriptContext, &enumerableObject))
- {
- enumerableObject = nullptr;
- }
- enumerator->Initialize(enumerableObject, scriptContext, false, forInCache);
- JIT_HELPER_END(Op_OP_InitForInEnumerator);
- }
- Js::Var JavascriptOperators::OP_CmEq_A(Var a, Var b, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(OP_CmEq_A);
- return JavascriptBoolean::ToVar(JavascriptOperators::Equal(a, b, scriptContext), scriptContext);
- JIT_HELPER_END(OP_CmEq_A);
- }
- Var JavascriptOperators::OP_CmNeq_A(Var a, Var b, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(OP_CmNeq_A);
- return JavascriptBoolean::ToVar(JavascriptOperators::NotEqual(a,b,scriptContext), scriptContext);
- JIT_HELPER_END(OP_CmNeq_A);
- }
- Var JavascriptOperators::OP_CmSrEq_A(Var a, Var b, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(OP_CmSrEq_A);
- return JavascriptBoolean::ToVar(JavascriptOperators::StrictEqual(a, b, scriptContext), scriptContext);
- JIT_HELPER_END(OP_CmSrEq_A);
- }
- Var JavascriptOperators::OP_CmSrEq_String(Var a, JavascriptString* b, ScriptContext *scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(OP_CmSrEq_String);
- return JavascriptBoolean::ToVar(JavascriptOperators::StrictEqualString(a, b), scriptContext);
- JIT_HELPER_END(OP_CmSrEq_String);
- }
- Var JavascriptOperators::OP_CmSrEq_EmptyString(Var a, ScriptContext *scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(OP_CmSrEq_EmptyString);
- return JavascriptBoolean::ToVar(JavascriptOperators::StrictEqualEmptyString(a), scriptContext);
- JIT_HELPER_END(OP_CmSrEq_EmptyString);
- }
- Var JavascriptOperators::OP_CmSrNeq_A(Var a, Var b, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(OP_CmSrNeq_A);
- return JavascriptBoolean::ToVar(JavascriptOperators::NotStrictEqual(a, b, scriptContext), scriptContext);
- JIT_HELPER_END(OP_CmSrNeq_A);
- }
- Var JavascriptOperators::OP_CmLt_A(Var a, Var b, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(OP_CmLt_A);
- return JavascriptBoolean::ToVar(JavascriptOperators::Less(a, b, scriptContext), scriptContext);
- JIT_HELPER_END(OP_CmLt_A);
- }
- Var JavascriptOperators::OP_CmLe_A(Var a, Var b, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(OP_CmLe_A);
- return JavascriptBoolean::ToVar(JavascriptOperators::LessEqual(a, b, scriptContext), scriptContext);
- JIT_HELPER_END(OP_CmLe_A);
- }
- Var JavascriptOperators::OP_CmGt_A(Var a, Var b, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(OP_CmGt_A);
- return JavascriptBoolean::ToVar(JavascriptOperators::Greater(a, b, scriptContext), scriptContext);
- JIT_HELPER_END(OP_CmGt_A);
- }
- Var JavascriptOperators::OP_CmGe_A(Var a, Var b, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(OP_CmGe_A);
- return JavascriptBoolean::ToVar(JavascriptOperators::GreaterEqual(a, b, scriptContext), scriptContext);
- JIT_HELPER_END(OP_CmGe_A);
- }
- DetachedStateBase* JavascriptOperators::DetachVarAndGetState(Var var)
- {
- switch (GetTypeId(var))
- {
- case TypeIds_ArrayBuffer:
- return Js::ArrayBuffer::FromVar(var)->DetachAndGetState();
- default:
- if (!Js::RecyclableObject::FromVar(var)->IsExternal())
- {
- AssertMsg(false, "We should explicitly have a case statement for each non-external object that can be detached.");
- }
- return nullptr;
- }
- }
- bool JavascriptOperators::IsObjectDetached(Var var)
- {
- switch (GetTypeId(var))
- {
- case TypeIds_ArrayBuffer:
- return Js::ArrayBuffer::FromVar(var)->IsDetached();
- default:
- return false;
- }
- }
- Var JavascriptOperators::NewVarFromDetachedState(DetachedStateBase* state, JavascriptLibrary *library)
- {
- AssertOrFailFastMsg(state->GetTypeId() == TypeIds_ArrayBuffer, "We should only re-activate detached ArrayBuffer");
- return Js::ArrayBuffer::NewFromDetachedState(state, library);
- }
- DynamicType *
- JavascriptOperators::EnsureObjectLiteralType(ScriptContext* scriptContext, const Js::PropertyIdArray *propIds, Field(DynamicType*)* literalType)
- {
- JIT_HELPER_NOT_REENTRANT_HEADER(EnsureObjectLiteralType, reentrancylock, scriptContext->GetThreadContext());
- DynamicType * newType = *literalType;
- if (newType != nullptr)
- {
- if (!newType->GetIsShared())
- {
- newType->ShareType();
- }
- }
- else
- {
- DynamicType* objectType =
- FunctionBody::DoObjectHeaderInliningForObjectLiteral(propIds)
- ? scriptContext->GetLibrary()->GetObjectHeaderInlinedLiteralType((uint16)propIds->count)
- : scriptContext->GetLibrary()->GetObjectLiteralType(
- static_cast<PropertyIndex>(
- min(propIds->count, static_cast<uint32>(MaxPreInitializedObjectTypeInlineSlotCount))));
- newType = PathTypeHandlerBase::CreateTypeForNewScObject(scriptContext, objectType, propIds, false);
- *literalType = newType;
- }
- Assert(scriptContext);
- Assert(GetLiteralInlineSlotCapacity(propIds) == newType->GetTypeHandler()->GetInlineSlotCapacity());
- Assert(newType->GetTypeHandler()->GetSlotCapacity() >= 0);
- Assert(GetLiteralSlotCapacity(propIds) == (uint)newType->GetTypeHandler()->GetSlotCapacity());
- return newType;
- JIT_HELPER_END(EnsureObjectLiteralType);
- }
- Var JavascriptOperators::NewScObjectLiteral(ScriptContext* scriptContext, const Js::PropertyIdArray *propIds, Field(DynamicType*)* literalType)
- {
- Assert(propIds->count != 0);
- Assert(!propIds->hadDuplicates); // duplicates are removed by parser
- #ifdef PROFILE_OBJECT_LITERALS
- // Empty objects not counted in the object literal counts
- scriptContext->objectLiteralInstanceCount++;
- if (propIds->count > scriptContext->objectLiteralMaxLength)
- {
- scriptContext->objectLiteralMaxLength = propIds->count;
- }
- #endif
- DynamicType* newType = EnsureObjectLiteralType(scriptContext, propIds, literalType);
- DynamicObject* instance = DynamicObject::New(scriptContext->GetRecycler(), newType);
- if (!newType->GetIsShared())
- {
- #if ENABLE_FIXED_FIELDS
- newType->GetTypeHandler()->SetSingletonInstanceIfNeeded(instance);
- #endif
- }
- #ifdef PROFILE_OBJECT_LITERALS
- else
- {
- scriptContext->objectLiteralCacheCount++;
- }
- #endif
- JS_ETW(EventWriteJSCRIPT_RECYCLER_ALLOCATE_OBJECT(instance));
- // can't auto-proxy here as object literal is not exactly "new" object and cannot be intercepted as proxy.
- return instance;
- }
- uint JavascriptOperators::GetLiteralSlotCapacity(Js::PropertyIdArray const * propIds)
- {
- const uint inlineSlotCapacity = GetLiteralInlineSlotCapacity(propIds);
- return DynamicTypeHandler::RoundUpSlotCapacity(propIds->count, static_cast<PropertyIndex>(inlineSlotCapacity));
- }
- uint JavascriptOperators::GetLiteralInlineSlotCapacity(
- Js::PropertyIdArray const * propIds)
- {
- if (propIds->hadDuplicates)
- {
- return 0;
- }
- return
- FunctionBody::DoObjectHeaderInliningForObjectLiteral(propIds)
- ? DynamicTypeHandler::RoundUpObjectHeaderInlinedInlineSlotCapacity(static_cast<PropertyIndex>(propIds->count))
- : DynamicTypeHandler::RoundUpInlineSlotCapacity(
- static_cast<PropertyIndex>(
- min(propIds->count, static_cast<uint32>(MaxPreInitializedObjectTypeInlineSlotCount))));
- }
- Var JavascriptOperators::OP_InitCachedScope(Var varFunc, const Js::PropertyIdArray *propIds, Field(DynamicType*)* literalType, bool formalsAreLetDecls, ScriptContext *scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_HEADER(OP_InitCachedScope, reentrancylock, scriptContext->GetThreadContext());
- bool isGAFunction = JavascriptFunction::Is(varFunc);
- Assert(isGAFunction);
- if (isGAFunction)
- {
- JavascriptFunction *function = JavascriptFunction::FromVar(varFunc);
- isGAFunction = JavascriptGeneratorFunction::Test(function) || JavascriptAsyncFunction::Test(function);
- }
- ScriptFunction *func = isGAFunction ?
- JavascriptGeneratorFunction::FromVar(varFunc)->GetGeneratorVirtualScriptFunction() :
- ScriptFunction::FromVar(varFunc);
- #ifdef PROFILE_OBJECT_LITERALS
- // Empty objects not counted in the object literal counts
- scriptContext->objectLiteralInstanceCount++;
- if (propIds->count > scriptContext->objectLiteralMaxLength)
- {
- scriptContext->objectLiteralMaxLength = propIds->count;
- }
- #endif
- PropertyId cachedFuncCount = ActivationObjectEx::GetCachedFuncCount(propIds);
- PropertyId firstFuncSlot = ActivationObjectEx::GetFirstFuncSlot(propIds);
- PropertyId firstVarSlot = ActivationObjectEx::GetFirstVarSlot(propIds);
- PropertyId lastFuncSlot = Constants::NoProperty;
- if (firstFuncSlot != Constants::NoProperty)
- {
- if (firstVarSlot == Constants::NoProperty || firstVarSlot < firstFuncSlot)
- {
- lastFuncSlot = propIds->count - 1;
- }
- else
- {
- lastFuncSlot = firstVarSlot - 1;
- }
- }
- DynamicType *type = *literalType;
- if (type != nullptr)
- {
- #ifdef PROFILE_OBJECT_LITERALS
- scriptContext->objectLiteralCacheCount++;
- #endif
- }
- else
- {
- type = scriptContext->GetLibrary()->GetActivationObjectType();
- if (formalsAreLetDecls)
- {
- uint formalsSlotLimit = (firstFuncSlot != Constants::NoProperty) ? (uint)firstFuncSlot :
- (firstVarSlot != Constants::NoProperty) ? (uint)firstVarSlot :
- propIds->count;
- type = PathTypeHandlerBase::CreateNewScopeObject(scriptContext, type, propIds, PropertyLet, formalsSlotLimit);
- }
- else
- {
- type = PathTypeHandlerBase::CreateNewScopeObject(scriptContext, type, propIds);
- }
- *literalType = type;
- }
- Var undef = scriptContext->GetLibrary()->GetUndefined();
- ActivationObjectEx *scopeObjEx = func->GetCachedScope();
- if (scopeObjEx && scopeObjEx->IsCommitted())
- {
- scopeObjEx->ReplaceType(type);
- scopeObjEx->SetCommit(false);
- #if DBG
- for (uint i = firstVarSlot; i < propIds->count; i++)
- {
- AssertMsg(scopeObjEx->GetSlot(i) == undef, "Var attached to cached scope");
- }
- #endif
- }
- else
- {
- ActivationObjectEx *tmp = RecyclerNewPlus(scriptContext->GetRecycler(), (cachedFuncCount == 0 ? 0 : cachedFuncCount - 1) * sizeof(FuncCacheEntry), ActivationObjectEx, type, func, cachedFuncCount, firstFuncSlot, lastFuncSlot);
- if (!scopeObjEx)
- {
- func->SetCachedScope(tmp);
- }
- scopeObjEx = tmp;
- for (uint i = firstVarSlot; i < propIds->count; i++)
- {
- scopeObjEx->SetSlot(SetSlotArguments(propIds->elements[i], i, undef));
- }
- }
- return scopeObjEx;
- JIT_HELPER_END(OP_InitCachedScope);
- }
- void JavascriptOperators::OP_InvalidateCachedScope(void* varEnv, int32 envIndex)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(OP_InvalidateCachedScope);
- FrameDisplay *disp = (FrameDisplay*)varEnv;
- Var item = disp->GetItem(envIndex);
- if (item != nullptr)
- {
- Assert(ActivationObjectEx::Is(item));
- RecyclableObject *objScope = RecyclableObject::FromVar(item);
- objScope->InvalidateCachedScope();
- }
- JIT_HELPER_END(OP_InvalidateCachedScope);
- }
- void JavascriptOperators::OP_InitCachedFuncs(Var varScope, FrameDisplay *pDisplay, const FuncInfoArray *info, ScriptContext *scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_HEADER(OP_InitCachedFuncs, reentrancylock, scriptContext->GetThreadContext());
- ActivationObjectEx *scopeObj = ActivationObjectEx::FromVar(varScope);
- Assert(scopeObj->GetTypeHandler()->GetInlineSlotCapacity() == 0);
- uint funcCount = info->count;
- if (funcCount == 0)
- {
- // Degenerate case: no nested funcs at all
- return;
- }
- if (scopeObj->HasCachedFuncs())
- {
- for (uint i = 0; i < funcCount; i++)
- {
- const FuncCacheEntry *entry = scopeObj->GetFuncCacheEntry(i);
- ScriptFunction *func = entry->func;
- FunctionProxy * proxy = func->GetFunctionProxy();
- // Reset the function's type to the default type with no properties
- // Use the cached type on the function proxy rather than the type in the func cache entry
- // CONSIDER: Stop caching the function types in the scope object
- func->ReplaceType(proxy->EnsureDeferredPrototypeType());
- func->ResetConstructorCacheToDefault();
- uint scopeSlot = info->elements[i].scopeSlot;
- if (scopeSlot != Constants::NoProperty)
- {
- // CONSIDER: Store property IDs in FuncInfoArray in debug builds so we can properly assert in SetAuxSlot
- scopeObj->SetAuxSlot(SetSlotArguments(Constants::NoProperty, scopeSlot, entry->func));
- }
- }
- return;
- }
- // No cached functions, so create them and cache them.
- JavascriptFunction *funcParent = scopeObj->GetParentFunc();
- for (uint i = 0; i < funcCount; i++)
- {
- const FuncInfoEntry *entry = &info->elements[i];
- uint nestedIndex = entry->nestedIndex;
- uint scopeSlot = entry->scopeSlot;
- FunctionProxy * proxy = funcParent->GetFunctionBody()->GetNestedFunctionProxy(nestedIndex);
- ScriptFunction *func = scriptContext->GetLibrary()->CreateScriptFunction(proxy);
- func->SetEnvironment(pDisplay);
- JS_ETW(EventWriteJSCRIPT_RECYCLER_ALLOCATE_FUNCTION(func, EtwTrace::GetFunctionId(proxy)));
- scopeObj->SetCachedFunc(i, func);
- if (scopeSlot != Constants::NoProperty)
- {
- // CONSIDER: Store property IDs in FuncInfoArray in debug builds so we can properly assert in SetAuxSlot
- scopeObj->SetAuxSlot(SetSlotArguments(Constants::NoProperty, scopeSlot, func));
- }
- }
- JIT_HELPER_END(OP_InitCachedFuncs);
- }
- Var JavascriptOperators::AddVarsToArraySegment(SparseArraySegment<Var> * segment, const Js::VarArray *vars)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(ArraySegmentVars);
- uint32 count = vars->count;
- Assert(segment->left == 0);
- Assert(count <= segment->size);
- if(count > segment->length)
- {
- segment->length = count;
- segment->CheckLengthvsSize();
- }
- CopyArray(segment->elements, segment->length, vars->elements, count);
- return segment;
- JIT_HELPER_END(ArraySegmentVars);
- }
- void JavascriptOperators::AddIntsToArraySegment(SparseArraySegment<int32> * segment, const Js::AuxArray<int32> *ints)
- {
- uint32 count = ints->count;
- Assert(segment->left == 0);
- Assert(count <= segment->size);
- if(count > segment->length)
- {
- segment->length = count;
- segment->CheckLengthvsSize();
- }
- js_memcpy_s(segment->elements, sizeof(int32) * segment->length, ints->elements, sizeof(int32) * count);
- }
- void JavascriptOperators::AddFloatsToArraySegment(SparseArraySegment<double> * segment, const Js::AuxArray<double> *doubles)
- {
- uint32 count = doubles->count;
- Assert(segment->left == 0);
- Assert(count <= segment->size);
- if(count > segment->length)
- {
- segment->length = count;
- segment->CheckLengthvsSize();
- }
- js_memcpy_s(segment->elements, sizeof(double) * segment->length, doubles->elements, sizeof(double) * count);
- }
- RecyclableObject * JavascriptOperators::GetPrototypeObject(RecyclableObject * constructorFunction, ScriptContext * scriptContext)
- {
- Var prototypeProperty = JavascriptOperators::GetProperty(constructorFunction, PropertyIds::prototype, scriptContext);
- RecyclableObject* prototypeObject;
- PrototypeObject(prototypeProperty, constructorFunction, scriptContext, &prototypeObject);
- return prototypeObject;
- }
- RecyclableObject * JavascriptOperators::GetPrototypeObjectForConstructorCache(RecyclableObject * constructor, ScriptContext* requestContext, bool& canBeCached)
- {
- PropertyValueInfo info;
- Var prototypeValue;
- RecyclableObject* prototypeObject;
- canBeCached = false;
- // Do a local property lookup. Since a function's prototype property is a non-configurable data property, we don't need to worry
- // about the prototype being an accessor property, whose getter returns different values every time it's called.
- if (constructor->GetProperty(constructor, PropertyIds::prototype, &prototypeValue, &info, requestContext))
- {
- if (!JavascriptOperators::PrototypeObject(prototypeValue, constructor, requestContext, &prototypeObject))
- {
- // The value returned by the property lookup is not a valid prototype object, default to object prototype.
- Assert(prototypeObject == constructor->GetLibrary()->GetObjectPrototype());
- }
- // For these scenarios, we do not want to populate the cache.
- if (constructor->GetScriptContext() != requestContext || info.GetInstance() != constructor)
- {
- return prototypeObject;
- }
- }
- else
- {
- // It's ok to cache Object.prototype, because Object.prototype cannot be overwritten.
- prototypeObject = constructor->GetLibrary()->GetObjectPrototype();
- }
- canBeCached = true;
- return prototypeObject;
- }
- bool JavascriptOperators::PrototypeObject(Var prototypeProperty, RecyclableObject * constructorFunction, ScriptContext * scriptContext, RecyclableObject** prototypeObject)
- {
- TypeId prototypeType = JavascriptOperators::GetTypeId(prototypeProperty);
- if (JavascriptOperators::IsObjectType(prototypeType))
- {
- *prototypeObject = RecyclableObject::FromVar(prototypeProperty);
- return true;
- }
- *prototypeObject = constructorFunction->GetLibrary()->GetObjectPrototype();
- return false;
- }
- FunctionInfo* JavascriptOperators::GetConstructorFunctionInfo(Var instance, ScriptContext * scriptContext)
- {
- TypeId typeId = JavascriptOperators::GetTypeId(instance);
- if (typeId == TypeIds_Function)
- {
- JavascriptFunction * function = JavascriptFunction::UnsafeFromVar(instance);
- return function->GetFunctionInfo();
- }
- if (typeId != TypeIds_HostDispatch && typeId != TypeIds_Proxy)
- {
- if (typeId == TypeIds_Null)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedObject);
- }
- JavascriptError::ThrowTypeError(scriptContext, VBSERR_ActionNotSupported);
- }
- return nullptr;
- }
- Var JavascriptOperators::NewJavascriptObjectNoArg(ScriptContext* requestContext)
- {
- JIT_HELPER_NOT_REENTRANT_HEADER(NewJavascriptObjectNoArg, reentrancylock, requestContext->GetThreadContext());
- DynamicObject * newObject = requestContext->GetLibrary()->CreateObject(true);
- JS_ETW(EventWriteJSCRIPT_RECYCLER_ALLOCATE_OBJECT(newObject));
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- if (Js::Configuration::Global.flags.IsEnabled(Js::autoProxyFlag))
- {
- newObject = DynamicObject::FromVar(JavascriptProxy::AutoProxyWrapper(newObject));
- }
- #endif
- return newObject;
- JIT_HELPER_END(NewJavascriptObjectNoArg);
- }
- Var JavascriptOperators::NewJavascriptArrayNoArg(ScriptContext* requestContext)
- {
- JIT_HELPER_NOT_REENTRANT_HEADER(NewJavascriptArrayNoArg, reentrancylock, requestContext->GetThreadContext());
- JavascriptArray * newArray = requestContext->GetLibrary()->CreateArray();
- JS_ETW(EventWriteJSCRIPT_RECYCLER_ALLOCATE_OBJECT(newArray));
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- if (Js::Configuration::Global.flags.IsEnabled(Js::autoProxyFlag))
- {
- newArray = static_cast<JavascriptArray*>(JavascriptProxy::AutoProxyWrapper(newArray));
- }
- #endif
- return newArray;
- JIT_HELPER_END(NewJavascriptArrayNoArg);
- }
- Var JavascriptOperators::NewScObjectNoArgNoCtorFull(Var instance, ScriptContext* requestContext)
- {
- // This helper can be reentrant because although we don't call the Constructor, we might have to parse it if bytecode is missing
- // In which case, we would leave script. When we leave script we DisposeObjects which can dispose of Edge objects that could
- // have a javascript onDispose handler and call that handler.
- JIT_HELPER_REENTRANT_HEADER(NewScObjectNoArgNoCtorFull);
- return NewScObjectNoArgNoCtorCommon(instance, requestContext, true);
- JIT_HELPER_END(NewScObjectNoArgNoCtorFull);
- }
- Var JavascriptOperators::NewScObjectNoArgNoCtor(Var instance, ScriptContext* requestContext)
- {
- // This helper can be reentrant because although we don't call the Constructor, we might have to parse it if bytecode is missing
- // In which case, we would leave script. When we leave script we DisposeObjects which can dispose of Edge objects that could
- // have a javascript onDispose handler and call that handler.
- JIT_HELPER_REENTRANT_HEADER(NewScObjectNoArgNoCtor);
- return NewScObjectNoArgNoCtorCommon(instance, requestContext, false);
- JIT_HELPER_END(NewScObjectNoArgNoCtor);
- }
- Var JavascriptOperators::NewScObjectNoArgNoCtorCommon(Var instance, ScriptContext* requestContext, bool isBaseClassConstructorNewScObject)
- {
- RecyclableObject * object = RecyclableObject::FromVar(instance);
- FunctionInfo* functionInfo = JavascriptOperators::GetConstructorFunctionInfo(instance, requestContext);
- Assert(functionInfo != &JavascriptObject::EntryInfo::NewInstance); // built-ins are not inlined
- Assert(functionInfo != &JavascriptArray::EntryInfo::NewInstance); // built-ins are not inlined
- return functionInfo != nullptr ?
- JavascriptOperators::NewScObjectCommon(object, functionInfo, requestContext, isBaseClassConstructorNewScObject) :
- JavascriptOperators::NewScObjectHostDispatchOrProxy(object, requestContext);
- }
- Var JavascriptOperators::NewScObjectNoArg(Var instance, ScriptContext * requestContext)
- {
- JIT_HELPER_REENTRANT_HEADER(NewScObjectNoArg);
- JavascriptProxy * proxy = JavascriptOperators::TryFromVar<JavascriptProxy>(instance);
- if (proxy)
- {
- Arguments args(CallInfo(CallFlags_New, 1), &instance);
- return requestContext->GetThreadContext()->ExecuteImplicitCall(proxy, Js::ImplicitCall_Accessor, [=]()->Js::Var
- {
- return proxy->ConstructorTrap(args, requestContext, 0);
- });
- }
- FunctionInfo* functionInfo = JavascriptOperators::GetConstructorFunctionInfo(instance, requestContext);
- RecyclableObject * object = RecyclableObject::FromVar(instance);
- if (functionInfo == &JavascriptObject::EntryInfo::NewInstance)
- {
- // Fast path for new Object()
- Assert((functionInfo->GetAttributes() & FunctionInfo::ErrorOnNew) == 0);
- JavascriptLibrary* library = object->GetLibrary();
- DynamicObject * newObject = library->CreateObject(true);
- JS_ETW(EventWriteJSCRIPT_RECYCLER_ALLOCATE_OBJECT(newObject));
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- if (Js::Configuration::Global.flags.IsEnabled(Js::autoProxyFlag))
- {
- newObject = DynamicObject::FromVar(JavascriptProxy::AutoProxyWrapper(newObject));
- }
- #endif
- #if DBG
- DynamicType* newObjectType = newObject->GetDynamicType();
- Assert(newObjectType->GetIsShared());
- JavascriptFunction* constructor = JavascriptFunction::FromVar(instance);
- Assert(!constructor->GetConstructorCache()->NeedsUpdateAfterCtor());
- #endif
- ScriptContext * scriptContext = library->GetScriptContext();
- if (scriptContext != requestContext)
- {
- CrossSite::MarshalDynamicObjectAndPrototype(requestContext, newObject);
- }
- return newObject;
- }
- else if (functionInfo == &JavascriptArray::EntryInfo::NewInstance)
- {
- Assert((functionInfo->GetAttributes() & FunctionInfo::ErrorOnNew) == 0);
- JavascriptLibrary* library = object->GetLibrary();
- JavascriptArray * newArray = library->CreateArray();
- JS_ETW(EventWriteJSCRIPT_RECYCLER_ALLOCATE_OBJECT(newArray));
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- if (Js::Configuration::Global.flags.IsEnabled(Js::autoProxyFlag))
- {
- newArray = static_cast<JavascriptArray*>(JavascriptProxy::AutoProxyWrapper(newArray));
- }
- #endif
- #if DBG
- DynamicType* newArrayType = newArray->GetDynamicType();
- Assert(newArrayType->GetIsShared());
- JavascriptFunction* constructor = JavascriptFunction::FromVar(instance);
- Assert(!constructor->GetConstructorCache()->NeedsUpdateAfterCtor());
- #endif
- ScriptContext * scriptContext = library->GetScriptContext();
- if (scriptContext != requestContext)
- {
- CrossSite::MarshalDynamicObjectAndPrototype(requestContext, newArray);
- }
- return newArray;
- }
- Var newObject = functionInfo != nullptr ?
- JavascriptOperators::NewScObjectCommon(object, functionInfo, requestContext) :
- JavascriptOperators::NewScObjectHostDispatchOrProxy(object, requestContext);
- ThreadContext * threadContext = object->GetScriptContext()->GetThreadContext();
- Var returnVar = threadContext->ExecuteImplicitCall(object, Js::ImplicitCall_Accessor, [=]()->Js::Var
- {
- return CALL_FUNCTION(threadContext, object, CallInfo(CallFlags_New, 1), newObject);
- });
- if (JavascriptOperators::IsObject(returnVar))
- {
- newObject = returnVar;
- }
- ConstructorCache * constructorCache = nullptr;
- JavascriptFunction *function = JavascriptOperators::TryFromVar<JavascriptFunction>(instance);
- if (function)
- {
- constructorCache = function->GetConstructorCache();
- }
- if (constructorCache != nullptr && constructorCache->NeedsUpdateAfterCtor())
- {
- JavascriptOperators::UpdateNewScObjectCache(object, newObject, requestContext);
- }
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- if (Js::Configuration::Global.flags.IsEnabled(Js::autoProxyFlag))
- {
- DynamicObject* newDynamicObject = DynamicObject::FromVar(JavascriptProxy::AutoProxyWrapper(newObject));
- // this might come from a different scriptcontext.
- newObject = CrossSite::MarshalVar(requestContext, newDynamicObject, newDynamicObject->GetScriptContext());
- }
- #endif
- return newObject;
- JIT_HELPER_END(NewScObjectNoArg);
- }
- Var JavascriptOperators::NewScObjectNoCtorFull(Var instance, ScriptContext* requestContext)
- {
- // This helper can be reentrant because although we don't call the Constructor, we might have to parse it if bytecode is missing
- // In which case, we would leave script. When we leave script we DisposeObjects which can dispose of Edge objects that could
- // have a javascript onDispose handler and call that handler.
- JIT_HELPER_REENTRANT_HEADER(NewScObjectNoCtorFull);
- return NewScObjectNoCtorCommon(instance, requestContext, true);
- JIT_HELPER_END(NewScObjectNoCtorFull);
- }
- Var JavascriptOperators::NewScObjectNoCtor(Var instance, ScriptContext * requestContext)
- {
- // This helper can be reentrant because although we don't call the Constructor, we might have to parse it if bytecode is missing
- // In which case, we would leave script. When we leave script we DisposeObjects which can dispose of Edge objects that could
- // have a javascript onDispose handler and call that handler.
- JIT_HELPER_REENTRANT_HEADER(NewScObjectNoCtor);
- // We can still call into NewScObjectNoCtor variations in JIT code for performance; however for proxy we don't
- // really need the new object as the trap will handle the "this" pointer separately. pass back nullptr to ensure
- // failure in invalid case.
- return (JavascriptProxy::Is(instance)) ? nullptr : NewScObjectNoCtorCommon(instance, requestContext, false);
- JIT_HELPER_END(NewScObjectNoCtor);
- }
- Var JavascriptOperators::NewScObjectNoCtorCommon(Var instance, ScriptContext* requestContext, bool isBaseClassConstructorNewScObject)
- {
- FunctionInfo* functionInfo = JavascriptOperators::GetConstructorFunctionInfo(instance, requestContext);
- if (functionInfo)
- {
- return JavascriptOperators::NewScObjectCommon(RecyclableObject::UnsafeFromVar(instance), functionInfo, requestContext, isBaseClassConstructorNewScObject);
- }
- else
- {
- return JavascriptOperators::NewScObjectHostDispatchOrProxy(RecyclableObject::FromVar(instance), requestContext);
- }
- }
- Var JavascriptOperators::NewScObjectHostDispatchOrProxy(RecyclableObject * function, ScriptContext * requestContext)
- {
- ScriptContext* functionScriptContext = function->GetScriptContext();
- RecyclableObject * prototype = JavascriptOperators::GetPrototypeObject(function, functionScriptContext);
- prototype = RecyclableObject::FromVar(CrossSite::MarshalVar(requestContext, prototype, functionScriptContext));
- Var object = requestContext->GetLibrary()->CreateObject(prototype);
- JS_ETW(EventWriteJSCRIPT_RECYCLER_ALLOCATE_OBJECT(object));
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- if (Js::Configuration::Global.flags.IsEnabled(Js::autoProxyFlag))
- {
- object = DynamicObject::FromVar(JavascriptProxy::AutoProxyWrapper(object));
- }
- #endif
- return object;
- }
- Var JavascriptOperators::NewScObjectCommon(RecyclableObject * function, FunctionInfo* functionInfo, ScriptContext * requestContext, bool isBaseClassConstructorNewScObject)
- {
- // CONSIDER: Allow for the cache to be repopulated if the type got collected, and a new one got populated with
- // the same number of inlined slots. This requires that the JIT-ed code actually load the type from the cache
- // (instead of hard-coding it), but it can (and must) keep the hard-coded number of inline slots.
- // CONSIDER: Consider also not pinning the type in the cache. This can be done by using a registration based
- // weak reference (we need to control the memory address), which we don't yet have, or by allocating the cache from
- // the inline cache arena to allow it to be zeroed, but retain a recycler-allocated portion to hold on to the size of
- // inlined slots.
- JavascriptFunction* constructor = JavascriptFunction::UnsafeFromVar(function);
- if (functionInfo->IsClassConstructor() && !isBaseClassConstructorNewScObject)
- {
- // If we are calling new on a class constructor, the contract is that we pass new.target as the 'this' argument.
- // function is the constructor on which we called new - which is new.target.
- // If we are trying to construct the object for a base class constructor as part of a super call, we should not
- // store new.target in the 'this' argument.
- return function;
- }
- ConstructorCache* constructorCache = constructor->GetConstructorCache();
- AssertMsg(constructorCache->GetScriptContext() == nullptr || constructorCache->GetScriptContext() == constructor->GetScriptContext(),
- "Why did we populate a constructor cache with a mismatched script context?");
- Assert(constructorCache != nullptr);
- DynamicType* type = constructorCache->GetGuardValueAsType();
- if (type != nullptr && constructorCache->GetScriptContext() == requestContext)
- {
- #if DBG
- bool cachedProtoCanBeCached;
- Assert(type->GetPrototype() == JavascriptOperators::GetPrototypeObjectForConstructorCache(constructor, requestContext, cachedProtoCanBeCached));
- Assert(cachedProtoCanBeCached);
- Assert(type->GetIsShared());
- #endif
- #if DBG_DUMP
- TraceUseConstructorCache(constructorCache, constructor, true);
- #endif
- Var object = DynamicObject::New(requestContext->GetRecycler(), type);
- JS_ETW(EventWriteJSCRIPT_RECYCLER_ALLOCATE_OBJECT(object));
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- if (Js::Configuration::Global.flags.IsEnabled(Js::autoProxyFlag))
- {
- object = DynamicObject::FromVar(JavascriptProxy::AutoProxyWrapper(object));
- }
- #endif
- return object;
- }
- if (constructorCache->SkipDefaultNewObject())
- {
- Assert(!constructorCache->NeedsUpdateAfterCtor());
- #if DBG_DUMP
- TraceUseConstructorCache(constructorCache, constructor, true);
- #endif
- if (isBaseClassConstructorNewScObject)
- {
- return JavascriptOperators::CreateFromConstructor(function, requestContext);
- }
- return nullptr;
- }
- #if DBG_DUMP
- TraceUseConstructorCache(constructorCache, constructor, false);
- #endif
- ScriptContext* constructorScriptContext = function->GetScriptContext();
- Assert(!constructorScriptContext->GetThreadContext()->IsDisableImplicitException());
- // we shouldn't try to call the constructor if it's closed already.
- constructorScriptContext->VerifyAlive(TRUE, requestContext);
- FunctionInfo::Attributes attributes = functionInfo->GetAttributes();
- if (attributes & FunctionInfo::ErrorOnNew)
- {
- JavascriptError::ThrowTypeError(requestContext, JSERR_ErrorOnNew);
- }
- // Slow path
- FunctionProxy * ctorProxy = constructor->GetFunctionProxy();
- FunctionBody * functionBody = ctorProxy != nullptr ? ctorProxy->EnsureDeserialized()->Parse() : nullptr;
- if (attributes & FunctionInfo::SkipDefaultNewObject)
- {
- // The constructor doesn't use the default new object.
- #pragma prefast(suppress:6236, "DevDiv bug 830883. False positive when PHASE_OFF is #defined as '(false)'.")
- if (!PHASE_OFF1(ConstructorCachePhase) && (functionBody == nullptr || !PHASE_OFF(ConstructorCachePhase, functionBody)))
- {
- constructorCache = constructor->EnsureValidConstructorCache();
- constructorCache->PopulateForSkipDefaultNewObject(constructorScriptContext);
- #if DBG_DUMP
- if ((functionBody != nullptr && PHASE_TRACE(Js::ConstructorCachePhase, functionBody)) || (functionBody == nullptr && PHASE_TRACE1(Js::ConstructorCachePhase)))
- {
- const char16* ctorName = functionBody != nullptr ? functionBody->GetDisplayName() : _u("<unknown>");
- char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
- Output::Print(_u("CtorCache: populated cache (0x%p) for ctor %s (%s): "), constructorCache, ctorName,
- functionBody ? functionBody->GetDebugNumberSet(debugStringBuffer) : _u("(null)"));
- constructorCache->Dump();
- Output::Print(_u("\n"));
- Output::Flush();
- }
- #endif
- }
- Assert(!constructorCache->NeedsUpdateAfterCtor());
- return nullptr;
- }
- // CONSIDER: Create some form of PatchGetProtoObjForCtorCache, which actually caches the prototype object in the constructor cache.
- // Make sure that it does NOT populate the guard field. On the slow path (the only path for cross-context calls) we can do a faster lookup
- // after we fail the guard check. When invalidating the cache for proto change, make sure we zap the prototype field of the cache in
- // addition to the guard value.
- bool prototypeCanBeCached;
- RecyclableObject* prototype = JavascriptOperators::GetPrototypeObjectForConstructorCache(
- function, constructorScriptContext, prototypeCanBeCached);
- prototype = RecyclableObject::FromVar(CrossSite::MarshalVar(requestContext,
- prototype, constructorScriptContext));
- DynamicObject* newObject = requestContext->GetLibrary()->CreateObject(prototype, 8);
- JS_ETW(EventWriteJSCRIPT_RECYCLER_ALLOCATE_OBJECT(newObject));
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- if (Js::Configuration::Global.flags.IsEnabled(Js::autoProxyFlag))
- {
- newObject = DynamicObject::FromVar(JavascriptProxy::AutoProxyWrapper(newObject));
- }
- #endif
- Assert(newObject->GetTypeHandler()->GetPropertyCount() == 0);
- if (prototypeCanBeCached && functionBody != nullptr && requestContext == constructorScriptContext &&
- !Js::JavascriptProxy::Is(newObject) &&
- !PHASE_OFF1(ConstructorCachePhase) && !PHASE_OFF(ConstructorCachePhase, functionBody))
- {
- DynamicType* newObjectType = newObject->GetDynamicType();
- // Initial type (without any properties) should always be shared up-front. This allows us to populate the cache right away.
- Assert(newObjectType->GetIsShared());
- // Populate the cache here and set the updateAfterCtor flag. This way, if the ctor is called recursively the
- // recursive calls will hit the cache and use the initial type. On the unwind path, we will update the cache
- // after the innermost ctor and clear the flag. After subsequent ctors we won't attempt an update anymore.
- // As long as the updateAfterCtor flag is set it is safe to update the cache, because it would not have been
- // hard-coded in the JIT-ed code.
- constructorCache = constructor->EnsureValidConstructorCache();
- constructorCache->Populate(newObjectType, constructorScriptContext, functionBody->GetHasNoExplicitReturnValue(), true);
- Assert(constructorCache->IsConsistent());
- #if DBG_DUMP
- if ((functionBody != nullptr && PHASE_TRACE(Js::ConstructorCachePhase, functionBody)) || (functionBody == nullptr && PHASE_TRACE1(Js::ConstructorCachePhase)))
- {
- const char16* ctorName = functionBody != nullptr ? functionBody->GetDisplayName() : _u("<unknown>");
- char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
- Output::Print(_u("CtorCache: populated cache (0x%p) for ctor %s (%s): "), constructorCache, ctorName,
- functionBody ? functionBody->GetDebugNumberSet(debugStringBuffer) : _u("(null)"));
- constructorCache->Dump();
- Output::Print(_u("\n"));
- Output::Flush();
- }
- #endif
- }
- else
- {
- #if DBG_DUMP
- if ((functionBody != nullptr && PHASE_TRACE(Js::ConstructorCachePhase, functionBody)) || (functionBody == nullptr && PHASE_TRACE1(Js::ConstructorCachePhase)))
- {
- const char16* ctorName = functionBody != nullptr ? functionBody->GetDisplayName() : _u("<unknown>");
- char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
- Output::Print(_u("CtorCache: did not populate cache (0x%p) for ctor %s (%s), because %s: prototype = 0x%p, functionBody = 0x%p, ctor context = 0x%p, request context = 0x%p"),
- constructorCache, ctorName, functionBody ? functionBody->GetDebugNumberSet(debugStringBuffer) : _u("(null)"),
- !prototypeCanBeCached ? _u("prototype cannot be cached") :
- functionBody == nullptr ? _u("function has no body") :
- requestContext != constructorScriptContext ? _u("of cross-context call") : _u("constructor cache phase is off"),
- prototype, functionBody, constructorScriptContext, requestContext);
- Output::Print(_u("\n"));
- Output::Flush();
- }
- #endif
- }
- return newObject;
- }
- void JavascriptOperators::UpdateNewScObjectCache(Var function, Var instance, ScriptContext* requestContext)
- {
- JIT_HELPER_NOT_REENTRANT_HEADER(UpdateNewScObjectCache, reentrancylock, requestContext->GetThreadContext());
- JavascriptFunction* constructor = JavascriptFunction::FromVar(function);
- if(constructor->GetScriptContext() != requestContext)
- {
- // The cache is populated only when the constructor function's context is the same as the calling context. However,
- // the cached type is not finalized yet and may not be until multiple calls to the constructor have been made (see
- // flag ConstructorCallsRequiredToFinalizeCachedType). A subsequent call to the constructor may be made from a
- // different context, so ignore those cross-context calls and wait for the constructor to be called from its own
- // context again to finalize the cached type.
- return;
- }
- // Review : What happens if the cache got invalidated between NewScObject and here?
- // Should we allocate new? Should we mark it as polymorphic?
- ConstructorCache* constructorCache = constructor->GetConstructorCache();
- Assert(constructorCache->IsConsistent());
- Assert(!ConstructorCache::IsDefault(constructorCache));
- AssertMsg(constructorCache->GetScriptContext() == constructor->GetScriptContext(), "Why did we populate a constructor cache with a mismatched script context?");
- AssertMsg(constructorCache->IsPopulated(), "Why are we updating a constructor cache that hasn't been populated?");
- // The presence of the updateAfterCtor flag guarantees that this cache hasn't been used in JIT-ed fast path. Even, if the
- // cache is invalidated, this flag is not changed.
- AssertMsg(constructorCache->NeedsUpdateAfterCtor(), "Why are we updating a constructor cache that doesn't need to be updated?");
- const bool finalizeCachedType =
- constructorCache->CallCount() >= CONFIG_FLAG(ConstructorCallsRequiredToFinalizeCachedType);
- if(!finalizeCachedType)
- {
- constructorCache->IncCallCount();
- }
- else
- {
- constructorCache->ClearUpdateAfterCtor();
- }
- FunctionBody* constructorBody = constructor->GetFunctionBody();
- AssertMsg(constructorBody != nullptr, "Constructor function doesn't have a function body.");
- Assert(RecyclableObject::Is(instance));
- // The cache might have been invalidated between NewScObjectCommon and UpdateNewScObjectCache. This could occur, for example, if
- // the constructor updates its own prototype property. If that happens we don't want to re-populate it here. A new cache will
- // be created when the constructor is called again.
- if (constructorCache->IsInvalidated())
- {
- #if DBG_DUMP
- TraceUpdateConstructorCache(constructorCache, constructorBody, false, _u("because cache is invalidated"));
- #endif
- return;
- }
- Assert(constructorCache->GetGuardValueAsType() != nullptr);
- if (DynamicType::Is(RecyclableObject::FromVar(instance)->GetTypeId()))
- {
- DynamicObject *object = DynamicObject::UnsafeFromVar(instance);
- DynamicType* type = object->GetDynamicType();
- DynamicTypeHandler* typeHandler = type->GetTypeHandler();
- if (constructorBody->GetHasOnlyThisStmts())
- {
- if (typeHandler->IsSharable())
- {
- #if DBG
- bool cachedProtoCanBeCached = false;
- Assert(type->GetPrototype() == JavascriptOperators::GetPrototypeObjectForConstructorCache(constructor, requestContext, cachedProtoCanBeCached));
- Assert(cachedProtoCanBeCached);
- Assert(type->GetScriptContext() == constructorCache->GetScriptContext());
- Assert(type->GetPrototype() == constructorCache->GetType()->GetPrototype());
- #endif
- typeHandler->SetMayBecomeShared();
- // CONSIDER: Remove only this for delayed type sharing.
- type->ShareType();
- #if ENABLE_PROFILE_INFO
- DynamicProfileInfo* profileInfo = constructorBody->HasDynamicProfileInfo() ? constructorBody->GetAnyDynamicProfileInfo() : nullptr;
- if ((profileInfo != nullptr && profileInfo->GetImplicitCallFlags() <= ImplicitCall_None) ||
- CheckIfPrototypeChainHasOnlyWritableDataProperties(type->GetPrototype()))
- {
- Assert(typeHandler->GetPropertyCount() < Js::PropertyIndexRanges<PropertyIndex>::MaxValue);
- for (PropertyIndex pi = 0; pi < typeHandler->GetPropertyCount(); pi++)
- {
- requestContext->RegisterConstructorCache(typeHandler->GetPropertyId(requestContext, pi), constructorCache);
- }
- Assert(constructorBody->GetUtf8SourceInfo()->GetIsLibraryCode() || !constructor->GetScriptContext()->IsScriptContextInDebugMode());
- if (constructorCache->TryUpdateAfterConstructor(type, constructor->GetScriptContext()))
- {
- #if DBG_DUMP
- TraceUpdateConstructorCache(constructorCache, constructorBody, true, _u(""));
- #endif
- }
- else
- {
- #if DBG_DUMP
- TraceUpdateConstructorCache(constructorCache, constructorBody, false, _u("because number of slots > MaxCachedSlotCount"));
- #endif
- }
- }
- #if DBG_DUMP
- else
- {
- if (profileInfo &&
- ((profileInfo->GetImplicitCallFlags() & ~(Js::ImplicitCall_External | Js::ImplicitCall_Accessor)) == 0) &&
- profileInfo != nullptr && CheckIfPrototypeChainHasOnlyWritableDataProperties(type->GetPrototype()) &&
- Js::Configuration::Global.flags.Trace.IsEnabled(Js::HostOptPhase))
- {
- const char16* ctorName = constructorBody->GetDisplayName();
- char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
- Output::Print(_u("CtorCache: %s cache (0x%p) for ctor %s (#%u) did not update because external call"),
- constructorCache, constructorBody, ctorName, constructorBody ? constructorBody->GetDebugNumberSet(debugStringBuffer) : _u("(null)"));
- Output::Print(_u("\n"));
- Output::Flush();
- }
- }
- #endif
- #endif
- }
- else
- {
- // Dynamic type created is not sharable.
- // So in future don't try to check for "this assignment optimization".
- constructorBody->SetHasOnlyThisStmts(false);
- #if DBG_DUMP
- TraceUpdateConstructorCache(constructorCache, constructorBody, false, _u("because final type is not shareable"));
- #endif
- }
- }
- else
- {
- #if DBG_DUMP
- TraceUpdateConstructorCache(constructorCache, constructorBody, false, _u("because ctor has not only this statements"));
- #endif
- }
- }
- else
- {
- // Even though this constructor apparently returned something other than the default object we created,
- // it still makes sense to cache the parameters of the default object, since we must create it every time, anyway.
- #if DBG_DUMP
- TraceUpdateConstructorCache(constructorCache, constructorBody, false, _u("because ctor return a non-object value"));
- #endif
- return;
- }
- // Whatever the constructor returned, if we're caching a type we want to be sure we shrink its inline slot capacity.
- if (finalizeCachedType && constructorCache->IsEnabled())
- {
- DynamicType* cachedType = constructorCache->NeedsTypeUpdate() ? constructorCache->GetPendingType() : constructorCache->GetType();
- DynamicTypeHandler* cachedTypeHandler = cachedType->GetTypeHandler();
- // Consider: We could delay inline slot capacity shrinking until the second time this constructor is invoked. In some cases
- // this might permit more properties to remain inlined if the objects grow after constructor. This would require flagging
- // the cache as special (already possible) and forcing the shrinking during work item creation if we happen to JIT this
- // constructor while the cache is in this special state.
- if (cachedTypeHandler->GetInlineSlotCapacity())
- {
- #if DBG_DUMP
- int inlineSlotCapacityBeforeShrink = cachedTypeHandler->GetInlineSlotCapacity();
- #endif
- // Note that after the cache has been updated and might have been used in the JIT-ed code, it is no longer legal to
- // shrink the inline slot capacity of the type. That's because we allocate memory for a fixed number of inlined properties
- // and if that number changed on the type, this update wouldn't get reflected in JIT-ed code and we would allocate objects
- // of a wrong size. This could conceivably happen if the original object got collected, and with it some of the successor
- // types also. If then another constructor has the same prototype and needs to populate its own cache, it would attempt to
- // shrink inlined slots again. If all surviving type handlers have smaller inline slot capacity, we would shrink it further.
- // To address this problem the type handler has a bit indicating its inline slots have been shrunk already. If that bit is
- // set ShrinkSlotAndInlineSlotCapacity does nothing.
- cachedTypeHandler->ShrinkSlotAndInlineSlotCapacity();
- constructorCache->UpdateInlineSlotCount();
- #if DBG_DUMP
- Assert(inlineSlotCapacityBeforeShrink >= cachedTypeHandler->GetInlineSlotCapacity());
- if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::InlineSlotsPhase))
- {
- if (inlineSlotCapacityBeforeShrink != cachedTypeHandler->GetInlineSlotCapacity())
- {
- char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
- Output::Print(_u("Inline slot capacity shrunk: Function:%04s Before:%d After:%d\n"),
- constructorBody->GetDebugNumberSet(debugStringBuffer), inlineSlotCapacityBeforeShrink, cachedTypeHandler->GetInlineSlotCapacity());
- }
- }
- #endif
- }
- }
- JIT_HELPER_END(UpdateNewScObjectCache);
- }
- void JavascriptOperators::TraceUseConstructorCache(const ConstructorCache* ctorCache, const JavascriptFunction* ctor, bool isHit)
- {
- #if DBG_DUMP
- // We are under debug, so we can incur the extra check here.
- FunctionProxy* ctorBody = ctor->GetFunctionProxy();
- if (ctorBody != nullptr && !ctorBody->GetScriptContext()->IsClosed())
- {
- ctorBody = ctorBody->EnsureDeserialized();
- }
- if ((ctorBody != nullptr && PHASE_TRACE(Js::ConstructorCachePhase, ctorBody)) || (ctorBody == nullptr && PHASE_TRACE1(Js::ConstructorCachePhase)))
- {
- const char16* ctorName = ctorBody != nullptr ? ctorBody->GetDisplayName() : _u("<unknown>");
- char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
- Output::Print(_u("CtorCache: %s cache (0x%p) for ctor %s (%s): "), isHit ? _u("hit") : _u("missed"), ctorCache, ctorName,
- ctorBody ? ctorBody->GetDebugNumberSet(debugStringBuffer) : _u("(null)"));
- ctorCache->Dump();
- Output::Print(_u("\n"));
- Output::Flush();
- }
- #endif
- }
- void JavascriptOperators::TraceUpdateConstructorCache(const ConstructorCache* ctorCache, const FunctionBody* ctorBody, bool updated, const char16* reason)
- {
- #if DBG_DUMP
- if (PHASE_TRACE(Js::ConstructorCachePhase, ctorBody))
- {
- const char16* ctorName = ctorBody->GetDisplayName();
- char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
- Output::Print(_u("CtorCache: %s cache (0x%p) for ctor %s (%s)%s %s: "),
- updated ? _u("updated") : _u("did not update"), ctorBody, ctorName,
- ctorBody ? const_cast<Js::FunctionBody *>(ctorBody)->GetDebugNumberSet(debugStringBuffer) : _u("(null)"),
- updated ? _u("") : _u(", because") , reason);
- ctorCache->Dump();
- Output::Print(_u("\n"));
- Output::Flush();
- }
- #endif
- }
- Var JavascriptOperators::NewScObject(const Var callee, const Arguments args, ScriptContext *const scriptContext, const Js::AuxArray<uint32> *spreadIndices)
- {
- Assert(callee);
- Assert(args.Info.Count != 0);
- Assert(scriptContext);
- // Always save and restore implicit call flags when calling out
- // REVIEW: Can we avoid it if we don't collect dynamic profile info?
- ThreadContext *const threadContext = scriptContext->GetThreadContext();
- const ImplicitCallFlags savedImplicitCallFlags = threadContext->GetImplicitCallFlags();
- const Var newVarInstance = JavascriptFunction::CallAsConstructor(callee, /* overridingNewTarget = */nullptr, args, scriptContext, spreadIndices);
- threadContext->SetImplicitCallFlags(savedImplicitCallFlags);
- return newVarInstance;
- }
- Js::GlobalObject * JavascriptOperators::OP_LdRoot(ScriptContext* scriptContext)
- {
- return scriptContext->GetGlobalObject();
- }
- Js::ModuleRoot * JavascriptOperators::GetModuleRoot(int moduleID, ScriptContext* scriptContext)
- {
- Assert(moduleID != kmodGlobal);
- JavascriptLibrary* library = scriptContext->GetLibrary();
- HostObjectBase *hostObject = library->GetGlobalObject()->GetHostObject();
- if (hostObject)
- {
- Js::ModuleRoot * moduleRoot = hostObject->GetModuleRoot(moduleID);
- Assert(!CrossSite::NeedMarshalVar(moduleRoot, scriptContext));
- return moduleRoot;
- }
- HostScriptContext *hostScriptContext = scriptContext->GetHostScriptContext();
- if (hostScriptContext)
- {
- Js::ModuleRoot * moduleRoot = hostScriptContext->GetModuleRoot(moduleID);
- Assert(!CrossSite::NeedMarshalVar(moduleRoot, scriptContext));
- return moduleRoot;
- }
- Assert(FALSE);
- return nullptr;
- }
- Var JavascriptOperators::OP_LoadModuleRoot(int moduleID, ScriptContext* scriptContext)
- {
- Js::ModuleRoot * moduleRoot = GetModuleRoot(moduleID, scriptContext);
- if (moduleRoot)
- {
- return moduleRoot;
- }
- Assert(false);
- return scriptContext->GetLibrary()->GetUndefined();
- }
- Var JavascriptOperators::OP_LdNull(ScriptContext* scriptContext)
- {
- return scriptContext->GetLibrary()->GetNull();
- }
- Var JavascriptOperators::OP_LdUndef(ScriptContext* scriptContext)
- {
- return scriptContext->GetLibrary()->GetUndefined();
- }
- Var JavascriptOperators::OP_LdNaN(ScriptContext* scriptContext)
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- Var JavascriptOperators::OP_LdChakraLib(ScriptContext* scriptContext)
- {
- return scriptContext->GetLibrary()->GetChakraLib();
- }
- Var JavascriptOperators::OP_LdInfinity(ScriptContext* scriptContext)
- {
- return scriptContext->GetLibrary()->GetPositiveInfinite();
- }
- void JavascriptOperators::BuildHandlerScope(Var argThis, RecyclableObject * hostObject, FrameDisplay * pDisplay, ScriptContext * scriptContext)
- {
- Assert(argThis != nullptr);
- pDisplay->SetItem(0, TaggedNumber::Is(argThis) ? scriptContext->GetLibrary()->CreateNumberObject(argThis) : argThis);
- uint16 i = 1;
- Var aChild = argThis;
- uint16 length = pDisplay->GetLength();
- // Now add any parent scopes
- // We need to support the namespace parent lookup in both fastDOM on and off scenario.
- while (aChild != NULL)
- {
- Var aParent = hostObject->GetNamespaceParent(aChild);
- if (aParent == nullptr)
- {
- break;
- }
- aParent = CrossSite::MarshalVar(scriptContext, aParent);
- if (i == length)
- {
- length = UInt16Math::Add(length, 8);
- FrameDisplay * tmp = RecyclerNewPlus(scriptContext->GetRecycler(), length * sizeof(void*), FrameDisplay, length);
- js_memcpy_s((char*)tmp + tmp->GetOffsetOfScopes(), tmp->GetLength() * sizeof(void *), (char*)pDisplay + pDisplay->GetOffsetOfScopes(), pDisplay->GetLength() * sizeof(void*));
- pDisplay = tmp;
- }
- pDisplay->SetItem(i, aParent);
- aChild = aParent;
- i++;
- }
- Assert(i <= pDisplay->GetLength());
- pDisplay->SetLength(i);
- }
- FrameDisplay * JavascriptOperators::OP_LdHandlerScope(Var argThis, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(ScrObj_LdHandlerScope);
- // The idea here is to build a stack of nested scopes in the form of a JS array.
- //
- // The scope stack for an event handler looks like this:
- //
- // implicit "this"
- // implicit namespace parent scopes
- // Put the implicit "this"
- if (argThis != NULL)
- {
- RecyclableObject* hostObject = scriptContext->GetGlobalObject()->GetHostObject();
- if (hostObject == nullptr)
- {
- hostObject = scriptContext->GetGlobalObject()->GetDirectHostObject();
- }
- if (hostObject != nullptr)
- {
- uint16 length = 7;
- FrameDisplay *pDisplay =
- RecyclerNewPlus(scriptContext->GetRecycler(), length * sizeof(void*), FrameDisplay, length);
- BuildHandlerScope(argThis, hostObject, pDisplay, scriptContext);
- return pDisplay;
- }
- }
- return const_cast<FrameDisplay *>(&Js::NullFrameDisplay);
- JIT_HELPER_END(ScrObj_LdHandlerScope);
- }
- FrameDisplay* JavascriptOperators::OP_LdFrameDisplay(void *argHead, void *argEnv, ScriptContext* scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_HEADER(ScrObj_LdFrameDisplay, reentrancylock, scriptContext->GetThreadContext());
- // Build a display of nested frame objects.
- // argHead is the current scope; argEnv is either the lone trailing scope or an array of scopes
- // which we append to the new display.
- // Note that there are cases in which a function with no local frame must construct a display to pass
- // to the function(s) nested within it. In such a case, argHead will be a null object, and it's not
- // strictly necessary to include it. But such cases are rare and not perf critical, so it's not
- // worth the extra complexity to notify the nested functions that they can "skip" this slot in the
- // frame display when they're loading scopes nested outside it.
- FrameDisplay *pDisplay = nullptr;
- FrameDisplay *envDisplay = (FrameDisplay*)argEnv;
- uint16 length = UInt16Math::Add(envDisplay->GetLength(), 1);
- pDisplay = RecyclerNewPlus(scriptContext->GetRecycler(), length * sizeof(void*), FrameDisplay, length);
- for (uint16 j = 0; j < length - 1; j++)
- {
- pDisplay->SetItem(j + 1, envDisplay->GetItem(j));
- }
- pDisplay->SetItem(0, argHead);
- return pDisplay;
- JIT_HELPER_END(ScrObj_LdFrameDisplay);
- }
- FrameDisplay* JavascriptOperators::OP_LdFrameDisplayNoParent(void *argHead, ScriptContext* scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(ScrObj_LdFrameDisplayNoParent);
- return OP_LdFrameDisplay(argHead, (void*)&NullFrameDisplay, scriptContext);
- JIT_HELPER_END(ScrObj_LdFrameDisplayNoParent);
- }
- FrameDisplay* JavascriptOperators::OP_LdStrictFrameDisplay(void *argHead, void *argEnv, ScriptContext* scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(ScrObj_LdStrictFrameDisplay);
- FrameDisplay * pDisplay = OP_LdFrameDisplay(argHead, argEnv, scriptContext);
- pDisplay->SetStrictMode(true);
- return pDisplay;
- JIT_HELPER_END(ScrObj_LdStrictFrameDisplay);
- }
- FrameDisplay* JavascriptOperators::OP_LdStrictFrameDisplayNoParent(void *argHead, ScriptContext* scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(ScrObj_LdStrictFrameDisplayNoParent);
- return OP_LdStrictFrameDisplay(argHead, (void*)&StrictNullFrameDisplay, scriptContext);
- JIT_HELPER_END(ScrObj_LdStrictFrameDisplayNoParent);
- }
- FrameDisplay* JavascriptOperators::OP_LdInnerFrameDisplay(void *argHead, void *argEnv, ScriptContext* scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(ScrObj_LdInnerFrameDisplay);
- CheckInnerFrameDisplayArgument(argHead);
- return OP_LdFrameDisplay(argHead, argEnv, scriptContext);
- JIT_HELPER_END(ScrObj_LdInnerFrameDisplay);
- }
- FrameDisplay* JavascriptOperators::OP_LdInnerFrameDisplayNoParent(void *argHead, ScriptContext* scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(ScrObj_LdInnerFrameDisplayNoParent);
- CheckInnerFrameDisplayArgument(argHead);
- return OP_LdFrameDisplayNoParent(argHead, scriptContext);
- JIT_HELPER_END(ScrObj_LdInnerFrameDisplayNoParent);
- }
- FrameDisplay* JavascriptOperators::OP_LdStrictInnerFrameDisplay(void *argHead, void *argEnv, ScriptContext* scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(ScrObj_LdStrictInnerFrameDisplay);
- CheckInnerFrameDisplayArgument(argHead);
- return OP_LdStrictFrameDisplay(argHead, argEnv, scriptContext);
- JIT_HELPER_END(ScrObj_LdStrictInnerFrameDisplay);
- }
- FrameDisplay* JavascriptOperators::OP_LdStrictInnerFrameDisplayNoParent(void *argHead, ScriptContext* scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(ScrObj_LdStrictInnerFrameDisplayNoParent);
- CheckInnerFrameDisplayArgument(argHead);
- return OP_LdStrictFrameDisplayNoParent(argHead, scriptContext);
- JIT_HELPER_END(ScrObj_LdStrictInnerFrameDisplayNoParent);
- }
- void JavascriptOperators::CheckInnerFrameDisplayArgument(void *argHead)
- {
- if (ThreadContext::IsOnStack(argHead))
- {
- AssertMsg(false, "Illegal byte code: stack object as with scope");
- Js::Throw::FatalInternalError();
- }
- if (!RecyclableObject::Is(argHead))
- {
- AssertMsg(false, "Illegal byte code: non-object as with scope");
- Js::Throw::FatalInternalError();
- }
- }
- Js::PropertyId JavascriptOperators::GetPropertyId(Var propertyName, ScriptContext* scriptContext)
- {
- PropertyRecord const * propertyRecord = nullptr;
- JavascriptSymbol * symbol = JavascriptOperators::TryFromVar<Js::JavascriptSymbol>(propertyName);
- if (symbol)
- {
- propertyRecord = symbol->GetValue();
- }
- else
- {
- JavascriptSymbolObject * symbolObject = JavascriptOperators::TryFromVar<JavascriptSymbolObject>(propertyName);
- if (symbolObject)
- {
- propertyRecord = symbolObject->GetValue();
- }
- else
- {
- JavascriptString * indexStr = JavascriptConversion::ToString(propertyName, scriptContext);
- scriptContext->GetOrAddPropertyRecord(indexStr, &propertyRecord);
- }
- }
- return propertyRecord->GetPropertyId();
- }
- void JavascriptOperators::OP_InitSetter(Var object, PropertyId propertyId, Var setter)
- {
- AssertMsg(!TaggedNumber::Is(object), "SetMember on a non-object?");
- RecyclableObject* recylableObject = RecyclableObject::FromVar(object);
- JIT_HELPER_NOT_REENTRANT_HEADER(OP_InitSetter, reentrancylock, recylableObject->GetScriptContext()->GetThreadContext());
- recylableObject->SetAccessors(propertyId, nullptr, setter);
- JIT_HELPER_END(OP_InitSetter);
- }
- void JavascriptOperators::OP_InitClassMemberSet(Var object, PropertyId propertyId, Var setter)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(Op_InitClassMemberSet);
- JIT_HELPER_SAME_ATTRIBUTES(Op_InitClassMemberSet, OP_InitSetter);
- JavascriptOperators::OP_InitSetter(object, propertyId, setter);
- RecyclableObject::FromVar(object)->SetAttributes(propertyId, PropertyClassMemberDefaults);
- JIT_HELPER_END(Op_InitClassMemberSet);
- }
- Js::PropertyId JavascriptOperators::OP_InitElemSetter(Var object, Var elementName, Var setter, ScriptContext* scriptContext, PropertyOperationFlags flags)
- {
- JIT_HELPER_REENTRANT_HEADER(OP_InitElemSetter);
- AssertMsg(!TaggedNumber::Is(object), "SetMember on a non-object?");
- PropertyId propertyId = JavascriptOperators::GetPropertyId(elementName, scriptContext);
- RecyclableObject::FromVar(object)->SetAccessors(propertyId, nullptr, setter);
- return propertyId;
- JIT_HELPER_END(OP_InitElemSetter);
- }
- Field(Var)* JavascriptOperators::OP_GetModuleExportSlotArrayAddress(uint moduleIndex, uint slotIndex, ScriptContextInfo* scriptContext)
- {
- return scriptContext->GetModuleExportSlotArrayAddress(moduleIndex, slotIndex);
- }
- Field(Var)* JavascriptOperators::OP_GetModuleExportSlotAddress(uint moduleIndex, uint slotIndex, ScriptContext* scriptContext)
- {
- Field(Var)* moduleRecordSlots = OP_GetModuleExportSlotArrayAddress(moduleIndex, slotIndex, scriptContext);
- Assert(moduleRecordSlots != nullptr);
- return &moduleRecordSlots[slotIndex];
- }
- Var JavascriptOperators::OP_LdModuleSlot(uint moduleIndex, uint slotIndex, ScriptContext* scriptContext)
- {
- Field(Var)* addr = OP_GetModuleExportSlotAddress(moduleIndex, slotIndex, scriptContext);
- Assert(addr != nullptr);
- return *addr;
- }
- void JavascriptOperators::OP_StModuleSlot(uint moduleIndex, uint slotIndex, Var value, ScriptContext* scriptContext)
- {
- Assert(value != nullptr);
- Field(Var)* addr = OP_GetModuleExportSlotAddress(moduleIndex, slotIndex, scriptContext);
- Assert(addr != nullptr);
- *addr = value;
- }
- void JavascriptOperators::OP_InitClassMemberSetComputedName(Var object, Var elementName, Var value, ScriptContext* scriptContext, PropertyOperationFlags flags)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_InitClassMemberSetComputedName);
- Js::PropertyId propertyId = JavascriptOperators::OP_InitElemSetter(object, elementName, value, scriptContext);
- RecyclableObject* instance = RecyclableObject::FromVar(object);
- // instance will be a function if it is the class constructor (otherwise it would be an object)
- if (JavascriptFunction::Is(instance) && Js::PropertyIds::prototype == propertyId)
- {
- // It is a TypeError to have a static member with a computed name that evaluates to 'prototype'
- JavascriptError::ThrowTypeError(scriptContext, JSERR_ClassStaticMethodCannotBePrototype);
- }
- instance->SetAttributes(propertyId, PropertyClassMemberDefaults);
- JIT_HELPER_END(Op_InitClassMemberSetComputedName);
- }
- BOOL JavascriptOperators::IsClassConstructor(Var instance)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(Op_IsClassConstructor);
- JavascriptFunction * function = JavascriptOperators::TryFromVar<JavascriptFunction>(instance);
- return function && (function->GetFunctionInfo()->IsClassConstructor() || (!function->IsScriptFunction() && !function->IsExternalFunction()));
- JIT_HELPER_END(Op_IsClassConstructor);
- }
- BOOL JavascriptOperators::IsClassMethod(Var instance)
- {
- JavascriptFunction * function = JavascriptOperators::TryFromVar<JavascriptFunction>(instance);
- return function && function->GetFunctionInfo()->IsClassMethod();
- }
- BOOL JavascriptOperators::IsBaseConstructorKind(Var instance)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(Op_IsBaseConstructorKind);
- JavascriptFunction * function = JavascriptOperators::TryFromVar<JavascriptFunction>(instance);
- return function && (function->GetFunctionInfo()->GetBaseConstructorKind());
- JIT_HELPER_END(Op_IsBaseConstructorKind);
- }
- void JavascriptOperators::OP_InitGetter(Var object, PropertyId propertyId, Var getter)
- {
- AssertMsg(!TaggedNumber::Is(object), "GetMember on a non-object?");
- RecyclableObject* recylableObject = RecyclableObject::FromVar(object);
- JIT_HELPER_NOT_REENTRANT_HEADER(OP_InitGetter, reentrancylock, recylableObject->GetScriptContext()->GetThreadContext());
- recylableObject->SetAccessors(propertyId, getter, nullptr);
- JIT_HELPER_END(OP_InitGetter);
- }
- void JavascriptOperators::OP_InitClassMemberGet(Var object, PropertyId propertyId, Var getter)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(Op_InitClassMemberGet);
- JIT_HELPER_SAME_ATTRIBUTES(Op_InitClassMemberGet, OP_InitGetter);
- JavascriptOperators::OP_InitGetter(object, propertyId, getter);
- RecyclableObject::FromVar(object)->SetAttributes(propertyId, PropertyClassMemberDefaults);
- JIT_HELPER_END(Op_InitClassMemberGet);
- }
- Js::PropertyId JavascriptOperators::OP_InitElemGetter(Var object, Var elementName, Var getter, ScriptContext* scriptContext, PropertyOperationFlags flags)
- {
- JIT_HELPER_REENTRANT_HEADER(OP_InitElemGetter);
- AssertMsg(!TaggedNumber::Is(object), "GetMember on a non-object?");
- PropertyId propertyId = JavascriptOperators::GetPropertyId(elementName, scriptContext);
- RecyclableObject::FromVar(object)->SetAccessors(propertyId, getter, nullptr);
- return propertyId;
- JIT_HELPER_END(OP_InitElemGetter);
- }
- void JavascriptOperators::OP_InitClassMemberGetComputedName(Var object, Var elementName, Var value, ScriptContext* scriptContext, PropertyOperationFlags flags)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_InitClassMemberGetComputedName);
- Js::PropertyId propertyId = JavascriptOperators::OP_InitElemGetter(object, elementName, value, scriptContext);
- RecyclableObject* instance = RecyclableObject::FromVar(object);
- // instance will be a function if it is the class constructor (otherwise it would be an object)
- if (JavascriptFunction::Is(instance) && Js::PropertyIds::prototype == propertyId)
- {
- // It is a TypeError to have a static member with a computed name that evaluates to 'prototype'
- JavascriptError::ThrowTypeError(scriptContext, JSERR_ClassStaticMethodCannotBePrototype);
- }
- instance->SetAttributes(propertyId, PropertyClassMemberDefaults);
- JIT_HELPER_END(Op_InitClassMemberGetComputedName);
- }
- void JavascriptOperators::OP_InitComputedProperty(Var object, Var elementName, Var value, ScriptContext* scriptContext, PropertyOperationFlags flags)
- {
- JIT_HELPER_REENTRANT_HEADER(OP_InitComputedProperty);
- PropertyId propertyId = JavascriptOperators::GetPropertyId(elementName, scriptContext);
- RecyclableObject::FromVar(object)->InitProperty(propertyId, value, flags);
- JIT_HELPER_END(OP_InitComputedProperty);
- }
- void JavascriptOperators::OP_InitClassMemberComputedName(Var object, Var elementName, Var value, ScriptContext* scriptContext, PropertyOperationFlags flags)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_InitClassMemberComputedName);
- PropertyId propertyId = JavascriptOperators::GetPropertyId(elementName, scriptContext);
- RecyclableObject* instance = RecyclableObject::FromVar(object);
- // instance will be a function if it is the class constructor (otherwise it would be an object)
- if (JavascriptFunction::Is(instance) && Js::PropertyIds::prototype == propertyId)
- {
- // It is a TypeError to have a static member with a computed name that evaluates to 'prototype'
- JavascriptError::ThrowTypeError(scriptContext, JSERR_ClassStaticMethodCannotBePrototype);
- }
- instance->SetPropertyWithAttributes(propertyId, value, PropertyClassMemberDefaults, NULL, flags);
- JIT_HELPER_END(Op_InitClassMemberComputedName);
- }
- //
- // Used by object literal {..., __proto__: ..., }.
- //
- void JavascriptOperators::OP_InitProto(Var instance, PropertyId propertyId, Var value)
- {
- AssertMsg(RecyclableObject::Is(instance), "__proto__ member on a non-object?");
- Assert(propertyId == PropertyIds::__proto__);
- RecyclableObject* object = RecyclableObject::FromVar(instance);
- ScriptContext* scriptContext = object->GetScriptContext();
- JIT_HELPER_NOT_REENTRANT_HEADER(OP_InitProto, reentrancylock, scriptContext->GetThreadContext());
- // B.3.1 __proto___ Property Names in Object Initializers
- //6.If propKey is the string value "__proto__" and if isComputedPropertyName(propKey) is false, then
- // a.If Type(v) is either Object or Null, then
- // i.Return the result of calling the [[SetInheritance]] internal method of object with argument propValue.
- // b.Return NormalCompletion(empty).
- if (JavascriptOperators::IsObjectOrNull(value))
- {
- JavascriptObject::ChangePrototype(object, RecyclableObject::FromVar(value), /*validate*/false, scriptContext);
- }
- JIT_HELPER_END(OP_InitProto);
- }
- Var JavascriptOperators::ConvertToUnmappedArguments(HeapArgumentsObject *argumentsObject,
- uint32 paramCount,
- Var *paramAddr,
- DynamicObject* frameObject,
- Js::PropertyIdArray *propIds,
- uint32 formalsCount,
- ScriptContext* scriptContext)
- {
- Var *paramIter = paramAddr;
- uint32 i = 0;
- for (paramIter = paramAddr + i; i < paramCount; i++, paramIter++)
- {
- JavascriptOperators::SetItem(argumentsObject, argumentsObject, i, *paramIter, scriptContext, PropertyOperation_None, /* skipPrototypeCheck = */ TRUE);
- }
- argumentsObject = argumentsObject->ConvertToUnmappedArgumentsObject();
- // Now as the unmapping is done we need to fill those frame object with Undecl
- for (i = 0; i < formalsCount; i++)
- {
- frameObject->SetSlot(SetSlotArguments(propIds != nullptr ? propIds->elements[i] : Js::Constants::NoProperty, i, scriptContext->GetLibrary()->GetUndeclBlockVar()));
- }
- return argumentsObject;
- }
- Var JavascriptOperators::LoadHeapArguments(JavascriptFunction *funcCallee, uint32 actualsCount, Var *paramAddr, Var frameObj, Var vArray, ScriptContext* scriptContext, bool nonSimpleParamList)
- {
- JIT_HELPER_NOT_REENTRANT_HEADER(Op_LoadHeapArguments, reentrancylock, scriptContext->GetThreadContext());
- AssertMsg(actualsCount != (unsigned int)-1, "Loading the arguments object in the global function?");
- // Create and initialize the Arguments object.
- uint32 formalsCount = 0;
- Js::PropertyIdArray *propIds = nullptr;
- if (vArray != scriptContext->GetLibrary()->GetNull())
- {
- propIds = (Js::PropertyIdArray *)vArray;
- formalsCount = propIds->count;
- Assert(formalsCount != 0 && propIds != nullptr);
- }
- HeapArgumentsObject *argsObj = JavascriptOperators::CreateHeapArguments(funcCallee, actualsCount, formalsCount, frameObj, scriptContext);
- return FillScopeObject(funcCallee, actualsCount, formalsCount, frameObj, paramAddr, propIds, argsObj, scriptContext, nonSimpleParamList, false);
- JIT_HELPER_END(Op_LoadHeapArguments);
- }
- Var JavascriptOperators::LoadHeapArgsCached(JavascriptFunction *funcCallee, uint32 actualsCount, uint32 formalsCount, Var *paramAddr, Var frameObj, ScriptContext* scriptContext, bool nonSimpleParamList)
- {
- JIT_HELPER_NOT_REENTRANT_HEADER(Op_LoadHeapArgsCached, reentrancylock, scriptContext->GetThreadContext());
- // Disregard the "this" param.
- AssertMsg(actualsCount != (uint32)-1 && formalsCount != (uint32)-1,
- "Loading the arguments object in the global function?");
- HeapArgumentsObject *argsObj = JavascriptOperators::CreateHeapArguments(funcCallee, actualsCount, formalsCount, frameObj, scriptContext);
- return FillScopeObject(funcCallee, actualsCount, formalsCount, frameObj, paramAddr, nullptr, argsObj, scriptContext, nonSimpleParamList, true);
- JIT_HELPER_END(Op_LoadHeapArgsCached);
- }
- Var JavascriptOperators::FillScopeObject(JavascriptFunction *funcCallee, uint32 actualsCount, uint32 formalsCount, Var frameObj, Var * paramAddr,
- Js::PropertyIdArray *propIds, HeapArgumentsObject * argsObj, ScriptContext * scriptContext, bool nonSimpleParamList, bool useCachedScope)
- {
- Assert(formalsCount == 0 || frameObj != nullptr);
- // Transfer formal arguments (that were actually passed) from their ArgIn slots to the local frame object.
- uint32 i;
- Var *tmpAddr = paramAddr;
- if (formalsCount != 0)
- {
- DynamicObject* frameObject = nullptr;
- if (useCachedScope)
- {
- frameObject = DynamicObject::FromVar(frameObj);
- __analysis_assume((uint32)frameObject->GetDynamicType()->GetTypeHandler()->GetSlotCapacity() >= formalsCount);
- }
- else
- {
- frameObject = (DynamicObject*)frameObj;
- // No fixed fields for formal parameters of the arguments object. Also, mark all fields as initialized up-front, because
- // we will set them directly using SetSlot below, so the type handler will not have a chance to mark them as initialized later.
- // CONSIDER : When we delay type sharing until the second instance is created, pass an argument indicating we want the types
- // and handlers created here to be marked as shared up-front. This is to ensure we don't get any fixed fields and that the handler
- // is ready for storing values directly to slots.
- DynamicType* newType = PathTypeHandlerBase::CreateNewScopeObject(scriptContext, frameObject->GetDynamicType(), propIds, nonSimpleParamList ? PropertyLetDefaults : PropertyNone);
- int oldSlotCapacity = frameObject->GetDynamicType()->GetTypeHandler()->GetSlotCapacity();
- int newSlotCapacity = newType->GetTypeHandler()->GetSlotCapacity();
- __analysis_assume((uint32)newSlotCapacity >= formalsCount);
- frameObject->EnsureSlots(oldSlotCapacity, newSlotCapacity, scriptContext, newType->GetTypeHandler());
- frameObject->ReplaceType(newType);
- }
- if (argsObj && nonSimpleParamList)
- {
- return ConvertToUnmappedArguments(argsObj, actualsCount, paramAddr, frameObject, propIds, formalsCount, scriptContext);
- }
- for (i = 0; i < formalsCount && i < actualsCount; i++, tmpAddr++)
- {
- frameObject->SetSlot(SetSlotArguments(propIds != nullptr? propIds->elements[i] : Constants::NoProperty, i, *tmpAddr));
- }
- if (i < formalsCount)
- {
- // The formals that weren't passed still need to be put in the frame object so that
- // their names will be found. Initialize them to "undefined".
- for (; i < formalsCount; i++)
- {
- frameObject->SetSlot(SetSlotArguments(propIds != nullptr? propIds->elements[i] : Constants::NoProperty, i, scriptContext->GetLibrary()->GetUndefined()));
- }
- }
- }
- if (argsObj != nullptr)
- {
- // Transfer the unnamed actual arguments, if any, to the Arguments object itself.
- for (i = formalsCount, tmpAddr = paramAddr + i; i < actualsCount; i++, tmpAddr++)
- {
- // ES5 10.6.11: use [[DefineOwnProperty]] semantics (instead of [[Put]]):
- // do not check whether property is non-writable/etc in the prototype.
- // ES3 semantics is same.
- JavascriptOperators::SetItem(argsObj, argsObj, i, *tmpAddr, scriptContext, PropertyOperation_None, /* skipPrototypeCheck = */ TRUE);
- }
- if (funcCallee->IsStrictMode())
- {
- // If the formals are let decls, then we just overwrote the frame object slots with
- // Undecl sentinels, and we can use the original arguments that were passed to the HeapArgumentsObject.
- return argsObj->ConvertToUnmappedArgumentsObject(!nonSimpleParamList);
- }
- }
- return argsObj;
- }
- HeapArgumentsObject *JavascriptOperators::CreateHeapArguments(JavascriptFunction *funcCallee, uint32 actualsCount, uint32 formalsCount, Var frameObj, ScriptContext* scriptContext)
- {
- JavascriptLibrary *library = scriptContext->GetLibrary();
- HeapArgumentsObject *argsObj = library->CreateHeapArguments(frameObj, formalsCount, !!funcCallee->IsStrictMode());
- #if DBG
- DynamicTypeHandler* typeHandler = argsObj->GetTypeHandler();
- #endif
- //
- // Set the number of arguments of Arguments Object
- //
- argsObj->SetNumberOfArguments(actualsCount);
- JavascriptOperators::SetProperty(argsObj, argsObj, PropertyIds::length, JavascriptNumber::ToVar(actualsCount, scriptContext), scriptContext);
- JavascriptOperators::SetProperty(argsObj, argsObj, PropertyIds::_symbolIterator, library->EnsureArrayPrototypeValuesFunction(), scriptContext);
- if (funcCallee->IsStrictMode())
- {
- JavascriptFunction* restrictedPropertyAccessor = library->GetThrowTypeErrorRestrictedPropertyAccessorFunction();
- argsObj->SetAccessors(PropertyIds::callee, restrictedPropertyAccessor, restrictedPropertyAccessor, PropertyOperation_NonFixedValue);
- }
- else
- {
- JavascriptOperators::SetProperty(argsObj, argsObj, PropertyIds::callee,
- StackScriptFunction::EnsureBoxed(BOX_PARAM(funcCallee, nullptr, _u("callee"))), scriptContext);
- }
- AssertMsg(argsObj->GetTypeHandler() == typeHandler || scriptContext->IsScriptContextInDebugMode(), "type handler should not transition because we initialized it correctly");
- return argsObj;
- }
- Var JavascriptOperators::OP_NewScopeObject(ScriptContext* scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(OP_NewScopeObject);
- return scriptContext->GetLibrary()->CreateActivationObject();
- JIT_HELPER_END(OP_NewScopeObject);
- }
- Var JavascriptOperators::OP_NewScopeObjectWithFormals(ScriptContext* scriptContext, FunctionBody * calleeBody, bool nonSimpleParamList)
- {
- JIT_HELPER_NOT_REENTRANT_HEADER(OP_NewScopeObjectWithFormals, reentrancylock, scriptContext->GetThreadContext());
- Js::ActivationObject * frameObject = (ActivationObject*)OP_NewScopeObject(scriptContext);
- // No fixed fields for formal parameters of the arguments object. Also, mark all fields as initialized up-front, because
- // we will set them directly using SetSlot below, so the type handler will not have a chance to mark them as initialized later.
- // CONSIDER : When we delay type sharing until the second instance is created, pass an argument indicating we want the types
- // and handlers created here to be marked as shared up-front. This is to ensure we don't get any fixed fields and that the handler
- // is ready for storing values directly to slots.
- DynamicType* newType = PathTypeHandlerBase::CreateNewScopeObject(scriptContext, frameObject->GetDynamicType(), calleeBody->GetFormalsPropIdArray(), nonSimpleParamList ? PropertyLetDefaults : PropertyNone);
- int oldSlotCapacity = frameObject->GetDynamicType()->GetTypeHandler()->GetSlotCapacity();
- int newSlotCapacity = newType->GetTypeHandler()->GetSlotCapacity();
- frameObject->EnsureSlots(oldSlotCapacity, newSlotCapacity, scriptContext, newType->GetTypeHandler());
- frameObject->ReplaceType(newType);
- return frameObject;
- JIT_HELPER_END(OP_NewScopeObjectWithFormals);
- }
- Field(Var)* JavascriptOperators::OP_NewScopeSlots(unsigned int size, ScriptContext *scriptContext, Var scope)
- {
- JIT_HELPER_NOT_REENTRANT_HEADER(OP_NewScopeSlots, reentrancylock, scriptContext->GetThreadContext());
- Assert(size > ScopeSlots::FirstSlotIndex); // Should never see empty slot array
- Field(Var)* slotArray = RecyclerNewArray(scriptContext->GetRecycler(), Field(Var), size); // last initialized slot contains reference to array of propertyIds, correspondent to objects in previous slots
- uint count = size - ScopeSlots::FirstSlotIndex;
- ScopeSlots slots(slotArray);
- slots.SetCount(count);
- AssertMsg(!FunctionBody::Is(scope), "Scope should only be FunctionInfo or DebuggerScope, not FunctionBody");
- slots.SetScopeMetadata(scope);
- Var undef = scriptContext->GetLibrary()->GetUndefined();
- for (unsigned int i = 0; i < count; i++)
- {
- slots.Set(i, undef);
- }
- return slotArray;
- JIT_HELPER_END(OP_NewScopeSlots);
- }
- Field(Var)* JavascriptOperators::OP_NewScopeSlotsWithoutPropIds(unsigned int count, int scopeIndex, ScriptContext *scriptContext, FunctionBody *functionBody)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(OP_NewScopeSlotsWithoutPropIds);
- DebuggerScope* scope = reinterpret_cast<DebuggerScope*>(Constants::FunctionBodyUnavailable);
- if (scopeIndex != DebuggerScope::InvalidScopeIndex)
- {
- AssertMsg(functionBody->GetScopeObjectChain(), "A scope chain should always be created when there are new scope slots for blocks.");
- scope = functionBody->GetScopeObjectChain()->pScopeChain->Item(scopeIndex);
- }
- return OP_NewScopeSlots(count, scriptContext, scope);
- JIT_HELPER_END(OP_NewScopeSlotsWithoutPropIds);
- }
- Field(Var)* JavascriptOperators::OP_CloneScopeSlots(Field(Var) *slotArray, ScriptContext *scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_HEADER(OP_CloneInnerScopeSlots, reentrancylock, scriptContext->GetThreadContext());
- ScopeSlots slots(slotArray);
- uint size = ScopeSlots::FirstSlotIndex + static_cast<uint>(slots.GetCount());
- Field(Var)* slotArrayClone = RecyclerNewArray(scriptContext->GetRecycler(), Field(Var), size);
- CopyArray(slotArrayClone, size, slotArray, size);
- return slotArrayClone;
- JIT_HELPER_END(OP_CloneInnerScopeSlots);
- }
- Var JavascriptOperators::OP_NewPseudoScope(ScriptContext *scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(OP_NewPseudoScope);
- return scriptContext->GetLibrary()->CreatePseudoActivationObject();
- JIT_HELPER_END(OP_NewPseudoScope);
- }
- Var JavascriptOperators::OP_NewBlockScope(ScriptContext *scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(OP_NewBlockScope);
- return scriptContext->GetLibrary()->CreateBlockActivationObject();
- JIT_HELPER_END(OP_NewBlockScope);
- }
- Var JavascriptOperators::OP_CloneBlockScope(BlockActivationObject *blockScope, ScriptContext *scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_HEADER(OP_CloneBlockScope, reentrancylock, scriptContext->GetThreadContext());
- return blockScope->Clone(scriptContext);
- JIT_HELPER_END(OP_CloneBlockScope);
- }
- Var JavascriptOperators::OP_IsInst(Var instance, Var aClass, ScriptContext* scriptContext, IsInstInlineCache* inlineCache)
- {
- JIT_HELPER_REENTRANT_HEADER(ScrObj_OP_IsInst);
- if (!RecyclableObject::Is(aClass))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_Operand_Invalid_NeedFunction, _u("instanceof"));
- }
- RecyclableObject* constructor = RecyclableObject::FromVar(aClass);
- if (scriptContext->GetConfig()->IsES6HasInstanceEnabled())
- {
- Var instOfHandler = JavascriptOperators::GetPropertyNoCache(constructor,
- PropertyIds::_symbolHasInstance, scriptContext);
- if (JavascriptOperators::IsUndefinedObject(instOfHandler)
- || instOfHandler == scriptContext->GetBuiltInLibraryFunction(JavascriptFunction::EntryInfo::SymbolHasInstance.GetOriginalEntryPoint()))
- {
- return JavascriptBoolean::ToVar(constructor->HasInstance(instance, scriptContext, inlineCache), scriptContext);
- }
- else
- {
- if (!JavascriptConversion::IsCallable(instOfHandler))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_NeedFunction, _u("Symbol[Symbol.hasInstance]"));
- }
- ThreadContext * threadContext = scriptContext->GetThreadContext();
- RecyclableObject *instFunc = RecyclableObject::FromVar(instOfHandler);
- Var result = threadContext->ExecuteImplicitCall(instFunc, ImplicitCall_Accessor, [=]()->Js::Var
- {
- return CALL_FUNCTION(scriptContext->GetThreadContext(), instFunc, CallInfo(CallFlags_Value, 2), constructor, instance);
- });
- return JavascriptBoolean::ToVar(JavascriptConversion::ToBoolean(result, scriptContext) ? TRUE : FALSE, scriptContext);
- }
- }
- else
- {
- return JavascriptBoolean::ToVar(constructor->HasInstance(instance, scriptContext, inlineCache), scriptContext);
- }
- JIT_HELPER_END(ScrObj_OP_IsInst);
- }
- void JavascriptOperators::OP_InitClass(Var constructor, Var extends, ScriptContext * scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(OP_InitClass);
- if (JavascriptOperators::GetTypeId(constructor) != Js::TypeId::TypeIds_Function)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_Operand_Invalid_NeedFunction, _u("class"));
- }
- RecyclableObject * ctor = RecyclableObject::FromVar(constructor);
- if (extends)
- {
- switch (JavascriptOperators::GetTypeId(extends))
- {
- case Js::TypeId::TypeIds_Null:
- {
- Var ctorProto = JavascriptOperators::GetProperty(constructor, ctor, Js::PropertyIds::prototype, scriptContext);
- RecyclableObject * ctorProtoObj = RecyclableObject::FromVar(ctorProto);
- ctorProtoObj->SetPrototype(RecyclableObject::FromVar(extends));
- ctorProtoObj->EnsureProperty(Js::PropertyIds::constructor);
- ctorProtoObj->SetEnumerable(Js::PropertyIds::constructor, FALSE);
- break;
- }
- default:
- {
- if (!RecyclableObject::Is(extends))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_ErrorOnNew);
- }
- RecyclableObject * extendsObj = RecyclableObject::FromVar(extends);
- if (!JavascriptOperators::IsConstructor(extendsObj))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_ErrorOnNew);
- }
- if (!extendsObj->HasProperty(Js::PropertyIds::prototype))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_InvalidPrototype);
- }
- Var extendsProto = JavascriptOperators::GetPropertyNoCache(extends, extendsObj, Js::PropertyIds::prototype, scriptContext);
- uint extendsProtoTypeId = JavascriptOperators::GetTypeId(extendsProto);
- if (extendsProtoTypeId <= Js::TypeId::TypeIds_LastJavascriptPrimitiveType && extendsProtoTypeId != Js::TypeId::TypeIds_Null)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_InvalidPrototype);
- }
- Var ctorProto = JavascriptOperators::GetPropertyNoCache(constructor, ctor, Js::PropertyIds::prototype, scriptContext);
- RecyclableObject * ctorProtoObj = RecyclableObject::FromVar(ctorProto);
- ctorProtoObj->SetPrototype(RecyclableObject::FromVar(extendsProto));
- ctorProtoObj->EnsureProperty(Js::PropertyIds::constructor);
- ctorProtoObj->SetEnumerable(Js::PropertyIds::constructor, FALSE);
- Var protoCtor = JavascriptOperators::GetPropertyNoCache(ctorProto, ctorProtoObj, Js::PropertyIds::constructor, scriptContext);
- RecyclableObject * protoCtorObj = RecyclableObject::FromVar(protoCtor);
- protoCtorObj->SetPrototype(extendsObj);
- break;
- }
- }
- }
- Var proto = JavascriptOperators::GetProperty(constructor, ctor, Js::PropertyIds::prototype, scriptContext);
- JavascriptOperators::OP_SetHomeObj(constructor, proto);
- JIT_HELPER_END(OP_InitClass);
- }
- void JavascriptOperators::OP_LoadUndefinedToElement(Var instance, PropertyId propertyId)
- {
- JIT_HELPER_NOT_REENTRANT_HEADER(Op_LdElemUndef, reentrancylock, RecyclableObject::FromVar(instance)->GetScriptContext()->GetThreadContext());
- AssertMsg(!TaggedNumber::Is(instance), "Invalid scope/root object");
- JavascriptOperators::EnsureProperty(instance, propertyId);
- JIT_HELPER_END(Op_LdElemUndef);
- }
- void JavascriptOperators::OP_LoadUndefinedToElementScoped(FrameDisplay *pScope, PropertyId propertyId, Var defaultInstance, ScriptContext* scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_HEADER(Op_LdElemUndefScoped, reentrancylock, scriptContext->GetThreadContext());
- int i;
- int length = pScope->GetLength();
- Var argInstance;
- for (i = 0; i < length; i++)
- {
- argInstance = pScope->GetItem(i);
- if (JavascriptOperators::EnsureProperty(argInstance, propertyId))
- {
- return;
- }
- }
- if (!JavascriptOperators::HasOwnPropertyNoHostObject(defaultInstance, propertyId))
- {
- // CONSIDER : Consider adding pre-initialization support to activation objects.
- JavascriptOperators::OP_InitPropertyScoped(pScope, propertyId, scriptContext->GetLibrary()->GetUndefined(), defaultInstance, scriptContext);
- }
- JIT_HELPER_END(Op_LdElemUndefScoped);
- }
- void JavascriptOperators::OP_LoadUndefinedToElementDynamic(Var instance, PropertyId propertyId, ScriptContext *scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_HEADER(Op_LdElemUndefDynamic, reentrancylock, scriptContext->GetThreadContext());
- if (!JavascriptOperators::HasOwnPropertyNoHostObject(instance, propertyId))
- {
- RecyclableObject::FromVar(instance)->InitPropertyScoped(propertyId, scriptContext->GetLibrary()->GetUndefined());
- }
- JIT_HELPER_END(Op_LdElemUndefDynamic);
- }
- BOOL JavascriptOperators::EnsureProperty(Var instance, PropertyId propertyId)
- {
- RecyclableObject *obj = RecyclableObject::FromVar(instance);
- return (obj && obj->EnsureProperty(propertyId));
- }
- void JavascriptOperators::OP_EnsureNoRootProperty(Var instance, PropertyId propertyId)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(Op_EnsureNoRootProperty);
- Assert(RootObjectBase::Is(instance));
- RootObjectBase *obj = RootObjectBase::FromVar(instance);
- obj->EnsureNoProperty(propertyId);
- JIT_HELPER_END(Op_EnsureNoRootProperty);
- }
- void JavascriptOperators::OP_EnsureNoRootRedeclProperty(Var instance, PropertyId propertyId)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(Op_EnsureNoRootRedeclProperty);
- Assert(RootObjectBase::Is(instance));
- RecyclableObject *obj = RecyclableObject::FromVar(instance);
- obj->EnsureNoRedeclProperty(propertyId);
- JIT_HELPER_END(Op_EnsureNoRootRedeclProperty);
- }
- void JavascriptOperators::OP_ScopedEnsureNoRedeclProperty(FrameDisplay *pDisplay, PropertyId propertyId, Var defaultInstance)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(Op_EnsureNoRedeclPropertyScoped);
- int i;
- int length = pDisplay->GetLength();
- RecyclableObject *object;
- for (i = 0; i < length; i++)
- {
- object = RecyclableObject::FromVar(pDisplay->GetItem(i));
- if (object->EnsureNoRedeclProperty(propertyId))
- {
- return;
- }
- }
- object = RecyclableObject::FromVar(defaultInstance);
- object->EnsureNoRedeclProperty(propertyId);
- JIT_HELPER_END(Op_EnsureNoRedeclPropertyScoped);
- }
- Var JavascriptOperators::IsIn(Var argProperty, Var instance, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_IsIn);
- // Note that the fact that we haven't seen a given name before doesn't mean that the instance doesn't
- if (!IsObject(instance))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_Operand_Invalid_NeedObject, _u("in"));
- }
- RecyclableObject* object = RecyclableObject::FromVar(instance);
- BOOL result;
- PropertyRecord const * propertyRecord = nullptr;
- uint32 index;
- IndexType indexType;
- // Fast path for JavascriptSymbols and PropertyStrings
- RecyclableObject* cacheOwner;
- PropertyRecordUsageCache* propertyRecordUsageCache;
- if (GetPropertyRecordUsageCache(argProperty, scriptContext, &propertyRecordUsageCache, &cacheOwner))
- {
- Var value;
- propertyRecord = propertyRecordUsageCache->GetPropertyRecord();
- if (!propertyRecord->IsNumeric())
- {
- PropertyValueInfo info;
- if (propertyRecordUsageCache->TryGetPropertyFromCache<false /* OwnPropertyOnly */, true /* OutputExistence */, false /* ReturnOperationInfo */>(instance, object, &value, scriptContext, &info, cacheOwner, nullptr))
- {
- Assert(JavascriptBoolean::Is(value));
- return value;
- }
- result = JavascriptOperators::GetPropertyWPCache<true /* OutputExistence */>(instance, object, propertyRecordUsageCache->GetPropertyRecord()->GetPropertyId(), &value, scriptContext, &info);
- Assert(value == JavascriptBoolean::ToVar(result, scriptContext));
- return value;
- }
- // We don't cache numeric property lookups, so fall through to the IndexType_Number case
- index = propertyRecord->GetNumericValue();
- indexType = IndexType_Number;
- }
- else
- {
- indexType = GetIndexType(argProperty, scriptContext, &index, &propertyRecord, true);
- }
- if (indexType == IndexType_Number)
- {
- result = JavascriptOperators::HasItem(object, index);
- }
- else
- {
- result = JavascriptOperators::HasProperty(object, propertyRecord->GetPropertyId());
- #ifdef TELEMETRY_JSO
- {
- Assert(indexType != Js::IndexType_JavascriptString);
- if (indexType == Js::IndexType_PropertyId)
- {
- scriptContext->GetTelemetry().GetOpcodeTelemetry().IsIn(instance, propertyId, result != 0);
- }
- }
- #endif
- }
- return JavascriptBoolean::ToVar(result, scriptContext);
- JIT_HELPER_END(Op_IsIn);
- }
- template <bool IsFromFullJit, class TInlineCache>
- inline Var JavascriptOperators::PatchGetValue(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_PatchGetValue);
- return PatchGetValueWithThisPtr<IsFromFullJit, TInlineCache>(functionBody, inlineCache, inlineCacheIndex, instance, propertyId, instance);
- JIT_HELPER_END(Op_PatchGetValue);
- }
- JIT_HELPER_TEMPLATE(Op_PatchGetValue, Op_PatchGetValuePolymorphic)
- template <bool IsFromFullJit, class TInlineCache>
- __forceinline Var JavascriptOperators::PatchGetValueWithThisPtr(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var thisInstance)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_PatchGetValueWithThisPtr);
- ScriptContext *const scriptContext = functionBody->GetScriptContext();
- Assert(Js::JavascriptStackWalker::ValidateTopJitFrame(scriptContext));
- RecyclableObject* object = nullptr;
- if (FALSE == JavascriptOperators::GetPropertyObject(instance, scriptContext, &object))
- {
- if (scriptContext->GetThreadContext()->RecordImplicitException())
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_CannotGet_NullOrUndefined,
- scriptContext->GetPropertyName(propertyId)->GetBuffer());
- }
- else
- {
- return scriptContext->GetLibrary()->GetUndefined();
- }
- }
- PropertyValueInfo info;
- PropertyValueInfo::SetCacheInfo(&info, functionBody, inlineCache, inlineCacheIndex, !IsFromFullJit);
- Var value;
- if (CacheOperators::TryGetProperty<true, true, true, true, true, true, !TInlineCache::IsPolymorphic, TInlineCache::IsPolymorphic, false, false>(
- instance, false, object, propertyId, &value, scriptContext, nullptr, &info))
- {
- return value;
- }
- #if DBG_DUMP
- if (PHASE_VERBOSE_TRACE1(Js::InlineCachePhase))
- {
- CacheOperators::TraceCache(inlineCache, _u("PatchGetValue"), propertyId, scriptContext, object);
- }
- #endif
- return JavascriptOperators::GetProperty(thisInstance, object, propertyId, scriptContext, &info);
- JIT_HELPER_END(Op_PatchGetValueWithThisPtr);
- }
- JIT_HELPER_TEMPLATE(Op_PatchGetValueWithThisPtr, Op_PatchGetValuePolymorphicWithThisPtr)
- template Var JavascriptOperators::PatchGetValue<false, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId);
- template Var JavascriptOperators::PatchGetValue<true, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId);
- template Var JavascriptOperators::PatchGetValue<false, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId);
- template Var JavascriptOperators::PatchGetValue<true, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId);
- template Var JavascriptOperators::PatchGetValueWithThisPtr<false, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var thisInstance);
- template Var JavascriptOperators::PatchGetValueWithThisPtr<true, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var thisInstance);
- template Var JavascriptOperators::PatchGetValueWithThisPtr<false, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var thisInstance);
- template Var JavascriptOperators::PatchGetValueWithThisPtr<true, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var thisInstance);
- template <bool IsFromFullJit, class TInlineCache>
- Var JavascriptOperators::PatchGetValueForTypeOf(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_PatchGetValueForTypeOf);
- ScriptContext *const scriptContext = functionBody->GetScriptContext();
- Assert(Js::JavascriptStackWalker::ValidateTopJitFrame(scriptContext));
- RecyclableObject* object = nullptr;
- if (FALSE == JavascriptOperators::GetPropertyObject(instance, scriptContext, &object))
- {
- if (scriptContext->GetThreadContext()->RecordImplicitException())
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_CannotGet_NullOrUndefined,
- scriptContext->GetPropertyName(propertyId)->GetBuffer());
- }
- else
- {
- return scriptContext->GetLibrary()->GetUndefined();
- }
- }
- PropertyValueInfo info;
- PropertyValueInfo::SetCacheInfo(&info, functionBody, inlineCache, inlineCacheIndex, !IsFromFullJit);
- Var value;
- if (CacheOperators::TryGetProperty<true, true, true, true, true, true, !TInlineCache::IsPolymorphic, TInlineCache::IsPolymorphic, false, false>(
- instance, false, object, propertyId, &value, scriptContext, nullptr, &info))
- {
- return value;
- }
- #if DBG_DUMP
- if (PHASE_VERBOSE_TRACE1(Js::InlineCachePhase))
- {
- CacheOperators::TraceCache(inlineCache, _u("PatchGetValueForTypeOf"), propertyId, scriptContext, object);
- }
- #endif
- Var prop = nullptr;
- BEGIN_TYPEOF_ERROR_HANDLER(scriptContext);
- prop = JavascriptOperators::GetProperty(instance, object, propertyId, scriptContext, &info);
- END_TYPEOF_ERROR_HANDLER(scriptContext, prop);
- return prop;
- JIT_HELPER_END(Op_PatchGetValueForTypeOf);
- }
- JIT_HELPER_TEMPLATE(Op_PatchGetValueForTypeOf, Op_PatchGetValuePolymorphicForTypeOf)
- template Var JavascriptOperators::PatchGetValueForTypeOf<false, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId);
- template Var JavascriptOperators::PatchGetValueForTypeOf<true, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId);
- template Var JavascriptOperators::PatchGetValueForTypeOf<false, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId);
- template Var JavascriptOperators::PatchGetValueForTypeOf<true, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId);
- Var JavascriptOperators::PatchGetValueUsingSpecifiedInlineCache(InlineCache * inlineCache, Var instance, RecyclableObject * object, PropertyId propertyId, ScriptContext* scriptContext)
- {
- PropertyValueInfo info;
- PropertyValueInfo::SetCacheInfo(&info, inlineCache);
- Var value;
- if (CacheOperators::TryGetProperty<true, true, true, true, false, true, !InlineCache::IsPolymorphic, InlineCache::IsPolymorphic, false, false>(
- instance, false, object, propertyId, &value, scriptContext, nullptr, &info))
- {
- return value;
- }
- #if DBG_DUMP
- if (PHASE_VERBOSE_TRACE1(Js::InlineCachePhase))
- {
- CacheOperators::TraceCache(inlineCache, _u("PatchGetValue"), propertyId, scriptContext, object);
- }
- #endif
- return JavascriptOperators::GetProperty(instance, object, propertyId, scriptContext, &info);
- }
- Var JavascriptOperators::PatchGetValueNoFastPath(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId)
- {
- return PatchGetValueWithThisPtrNoFastPath(functionBody, inlineCache, inlineCacheIndex, instance, propertyId, instance);
- }
- Var JavascriptOperators::PatchGetValueWithThisPtrNoFastPath(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var thisInstance)
- {
- ScriptContext *const scriptContext = functionBody->GetScriptContext();
- RecyclableObject* object = nullptr;
- if (FALSE == JavascriptOperators::GetPropertyObject(instance, scriptContext, &object))
- {
- if (scriptContext->GetThreadContext()->RecordImplicitException())
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_CannotGet_NullOrUndefined,
- scriptContext->GetPropertyName(propertyId)->GetBuffer());
- }
- else
- {
- return scriptContext->GetLibrary()->GetUndefined();
- }
- }
- PropertyValueInfo info;
- PropertyValueInfo::SetCacheInfo(&info, functionBody, inlineCache, inlineCacheIndex, true);
- return JavascriptOperators::GetProperty(thisInstance, object, propertyId, scriptContext, &info);
- }
- template <bool IsFromFullJit, class TInlineCache>
- inline Var JavascriptOperators::PatchGetRootValue(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, DynamicObject * object, PropertyId propertyId)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_PatchGetRootValue);
- AssertMsg(RootObjectBase::Is(object), "Root must be a global object!");
- ScriptContext *const scriptContext = functionBody->GetScriptContext();
- PropertyValueInfo info;
- PropertyValueInfo::SetCacheInfo(&info, functionBody, inlineCache, inlineCacheIndex, !IsFromFullJit);
- Var value;
- if (CacheOperators::TryGetProperty<true, true, true, false, true, false, !TInlineCache::IsPolymorphic, TInlineCache::IsPolymorphic, false, false>(
- object, true, object, propertyId, &value, scriptContext, nullptr, &info))
- {
- return value;
- }
- #if DBG_DUMP
- if (PHASE_VERBOSE_TRACE1(Js::InlineCachePhase))
- {
- CacheOperators::TraceCache(inlineCache, _u("PatchGetRootValue"), propertyId, scriptContext, object);
- }
- #endif
- return JavascriptOperators::OP_GetRootProperty(object, propertyId, &info, scriptContext);
- JIT_HELPER_END(Op_PatchGetRootValue);
- }
- JIT_HELPER_TEMPLATE(Op_PatchGetRootValue, Op_PatchGetRootValuePolymorphic)
- template Var JavascriptOperators::PatchGetRootValue<false, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, DynamicObject * object, PropertyId propertyId);
- template Var JavascriptOperators::PatchGetRootValue<true, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, DynamicObject * object, PropertyId propertyId);
- template Var JavascriptOperators::PatchGetRootValue<false, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, DynamicObject * object, PropertyId propertyId);
- template Var JavascriptOperators::PatchGetRootValue<true, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, DynamicObject * object, PropertyId propertyId);
- template <bool IsFromFullJit, class TInlineCache>
- Var JavascriptOperators::PatchGetRootValueForTypeOf(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, DynamicObject * object, PropertyId propertyId)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_PatchGetRootValueForTypeOf);
- AssertMsg(RootObjectBase::Is(object), "Root must be a global object!");
- ScriptContext *const scriptContext = functionBody->GetScriptContext();
- PropertyValueInfo info;
- PropertyValueInfo::SetCacheInfo(&info, functionBody, inlineCache, inlineCacheIndex, !IsFromFullJit);
- Var value = nullptr;
- if (CacheOperators::TryGetProperty<true, true, true, false, true, false, !TInlineCache::IsPolymorphic, TInlineCache::IsPolymorphic, false, false>(
- object, true, object, propertyId, &value, scriptContext, nullptr, &info))
- {
- return value;
- }
- #if DBG_DUMP
- if (PHASE_VERBOSE_TRACE1(Js::InlineCachePhase))
- {
- CacheOperators::TraceCache(inlineCache, _u("PatchGetRootValueForTypeOf"), propertyId, scriptContext, object);
- }
- #endif
- value = nullptr;
- BEGIN_TYPEOF_ERROR_HANDLER(scriptContext);
- if (JavascriptOperators::GetRootProperty(RecyclableObject::FromVar(object), propertyId, &value, scriptContext, &info))
- {
- if (scriptContext->IsUndeclBlockVar(value))
- {
- JavascriptError::ThrowReferenceError(scriptContext, JSERR_UseBeforeDeclaration);
- }
- return value;
- }
- END_TYPEOF_ERROR_HANDLER(scriptContext, value);
- value = scriptContext->GetLibrary()->GetUndefined();
- return value;
- JIT_HELPER_END(Op_PatchGetRootValueForTypeOf);
- }
- JIT_HELPER_TEMPLATE(Op_PatchGetRootValueForTypeOf, Op_PatchGetRootValuePolymorphicForTypeOf)
- template Var JavascriptOperators::PatchGetRootValueForTypeOf<false, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, DynamicObject * object, PropertyId propertyId);
- template Var JavascriptOperators::PatchGetRootValueForTypeOf<true, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, DynamicObject * object, PropertyId propertyId);
- template Var JavascriptOperators::PatchGetRootValueForTypeOf<false, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, DynamicObject * object, PropertyId propertyId);
- template Var JavascriptOperators::PatchGetRootValueForTypeOf<true, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, DynamicObject * object, PropertyId propertyId);
- Var JavascriptOperators::PatchGetRootValueNoFastPath_Var(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId)
- {
- return
- PatchGetRootValueNoFastPath(
- functionBody,
- inlineCache,
- inlineCacheIndex,
- DynamicObject::FromVar(instance),
- propertyId);
- }
- Var JavascriptOperators::PatchGetRootValueNoFastPath(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, DynamicObject* object, PropertyId propertyId)
- {
- AssertMsg(RootObjectBase::Is(object), "Root must be a global object!");
- ScriptContext *const scriptContext = functionBody->GetScriptContext();
- PropertyValueInfo info;
- PropertyValueInfo::SetCacheInfo(&info, functionBody, inlineCache, inlineCacheIndex, true);
- return JavascriptOperators::OP_GetRootProperty(object, propertyId, &info, scriptContext);
- }
- template <bool IsFromFullJit, class TInlineCache>
- inline Var JavascriptOperators::PatchGetPropertyScoped(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, FrameDisplay *pDisplay, PropertyId propertyId, Var defaultInstance)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_PatchGetPropertyScoped);
- // Get the property, using a scope stack rather than an individual instance.
- // Walk the stack until we find an instance that has the property.
- ScriptContext *const scriptContext = functionBody->GetScriptContext();
- uint16 length = pDisplay->GetLength();
- PropertyValueInfo info;
- PropertyValueInfo::SetCacheInfo(&info, functionBody, inlineCache, inlineCacheIndex, !IsFromFullJit);
- for (uint16 i = 0; i < length; i++)
- {
- RecyclableObject* object = RecyclableObject::UnsafeFromVar(pDisplay->GetItem(i));
- Var value;
- if (CacheOperators::TryGetProperty<true, true, true, false, true, true, !TInlineCache::IsPolymorphic, TInlineCache::IsPolymorphic, false, false>(
- object, false, object, propertyId, &value, scriptContext, nullptr, &info))
- {
- return value;
- }
- #if DBG_DUMP
- if (PHASE_VERBOSE_TRACE1(Js::InlineCachePhase))
- {
- CacheOperators::TraceCache(inlineCache, _u("PatchGetPropertyScoped"), propertyId, scriptContext, object);
- }
- #endif
- if (JavascriptOperators::GetProperty(object, propertyId, &value, scriptContext, &info))
- {
- if (scriptContext->IsUndeclBlockVar(value) && propertyId != PropertyIds::_this)
- {
- JavascriptError::ThrowReferenceError(scriptContext, JSERR_UseBeforeDeclaration);
- }
- return value;
- }
- }
- // There is no root decl for 'this', we should instead load the global 'this' value.
- if (propertyId == PropertyIds::_this)
- {
- Var varNull = OP_LdNull(scriptContext);
- return JavascriptOperators::OP_GetThis(varNull, functionBody->GetModuleID(), scriptContext);
- }
- else if (propertyId == PropertyIds::_super)
- {
- JavascriptError::ThrowReferenceError(scriptContext, JSERR_BadSuperReference);
- }
- // No one in the scope stack has the property, so get it from the default instance provided by the caller.
- Var value = JavascriptOperators::PatchGetRootValue<IsFromFullJit>(functionBody, inlineCache, inlineCacheIndex, DynamicObject::FromVar(defaultInstance), propertyId);
- if (scriptContext->IsUndeclBlockVar(value))
- {
- JavascriptError::ThrowReferenceError(scriptContext, JSERR_UseBeforeDeclaration);
- }
- return value;
- JIT_HELPER_END(Op_PatchGetPropertyScoped);
- }
- template Var JavascriptOperators::PatchGetPropertyScoped<false, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, FrameDisplay *pDisplay, PropertyId propertyId, Var defaultInstance);
- template Var JavascriptOperators::PatchGetPropertyScoped<true, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, FrameDisplay *pDisplay, PropertyId propertyId, Var defaultInstance);
- template Var JavascriptOperators::PatchGetPropertyScoped<false, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, FrameDisplay *pDisplay, PropertyId propertyId, Var defaultInstance);
- template Var JavascriptOperators::PatchGetPropertyScoped<true, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, FrameDisplay *pDisplay, PropertyId propertyId, Var defaultInstance);
- template <bool IsFromFullJit, class TInlineCache>
- Var JavascriptOperators::PatchGetPropertyForTypeOfScoped(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, FrameDisplay *pDisplay, PropertyId propertyId, Var defaultInstance)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_PatchGetPropertyForTypeOfScoped);
- Var value = nullptr;
- ScriptContext *scriptContext = functionBody->GetScriptContext();
- BEGIN_TYPEOF_ERROR_HANDLER(scriptContext);
- value = JavascriptOperators::PatchGetPropertyScoped<IsFromFullJit, TInlineCache>(functionBody, inlineCache, inlineCacheIndex, pDisplay, propertyId, defaultInstance);
- END_TYPEOF_ERROR_HANDLER(scriptContext, value)
- return value;
- JIT_HELPER_END(Op_PatchGetPropertyForTypeOfScoped);
- }
- template Var JavascriptOperators::PatchGetPropertyForTypeOfScoped<false, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, FrameDisplay *pDisplay, PropertyId propertyId, Var defaultInstance);
- template Var JavascriptOperators::PatchGetPropertyForTypeOfScoped<true, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, FrameDisplay *pDisplay, PropertyId propertyId, Var defaultInstance);
- template Var JavascriptOperators::PatchGetPropertyForTypeOfScoped<false, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, FrameDisplay *pDisplay, PropertyId propertyId, Var defaultInstance);
- template Var JavascriptOperators::PatchGetPropertyForTypeOfScoped<true, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, FrameDisplay *pDisplay, PropertyId propertyId, Var defaultInstance);
- template <bool IsFromFullJit, class TInlineCache>
- inline Var JavascriptOperators::PatchGetMethod(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_PatchGetMethod);
- Assert(inlineCache != nullptr);
- ScriptContext *const scriptContext = functionBody->GetScriptContext();
- RecyclableObject* object = nullptr;
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(instance);
- #endif
- if (FALSE == JavascriptOperators::GetPropertyObject(instance, scriptContext, &object))
- {
- // Don't error if we disabled implicit calls
- if (scriptContext->GetThreadContext()->RecordImplicitException())
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_CannotGet_NullOrUndefined,
- scriptContext->GetPropertyName(propertyId)->GetBuffer());
- }
- else
- {
- #ifdef TELEMETRY_JSO
- if (TELEMETRY_PROPERTY_OPCODE_FILTER(propertyId))
- {
- // `successful` will be true as PatchGetMethod throws an exception if not found.
- scriptContext->GetTelemetry().GetOpcodeTelemetry().GetMethodProperty(object, propertyId, value, /*successful:*/false);
- }
- #endif
- return scriptContext->GetLibrary()->GetUndefined();
- }
- }
- PropertyValueInfo info;
- PropertyValueInfo::SetCacheInfo(&info, functionBody, inlineCache, inlineCacheIndex, !IsFromFullJit);
- Var value;
- if (CacheOperators::TryGetProperty<true, true, true, false, true, true, !TInlineCache::IsPolymorphic, TInlineCache::IsPolymorphic, false, false>(
- instance, false, object, propertyId, &value, scriptContext, nullptr, &info))
- {
- return value;
- }
- #if DBG_DUMP
- if (PHASE_VERBOSE_TRACE1(Js::InlineCachePhase))
- {
- CacheOperators::TraceCache(inlineCache, _u("PatchGetMethod"), propertyId, scriptContext, object);
- }
- #endif
- value = Js::JavascriptOperators::PatchGetMethodFromObject(instance, object, propertyId, &info, scriptContext, false);
- #ifdef TELEMETRY_JSO
- if (TELEMETRY_PROPERTY_OPCODE_FILTER(propertyId))
- {
- // `successful` will be true as PatchGetMethod throws an exception if not found.
- scriptContext->GetTelemetry().GetOpcodeTelemetry().GetMethodProperty(object, propertyId, value, /*successful:*/true);
- }
- #endif
- return value;
- JIT_HELPER_END(Op_PatchGetMethod);
- }
- JIT_HELPER_TEMPLATE(Op_PatchGetMethod, Op_PatchGetMethodPolymorphic)
- template Var JavascriptOperators::PatchGetMethod<false, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId);
- template Var JavascriptOperators::PatchGetMethod<true, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId);
- template Var JavascriptOperators::PatchGetMethod<false, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId);
- template Var JavascriptOperators::PatchGetMethod<true, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId);
- template <bool IsFromFullJit, class TInlineCache>
- inline Var JavascriptOperators::PatchGetRootMethod(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, DynamicObject* object, PropertyId propertyId)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_PatchGetRootMethod);
- Assert(inlineCache != nullptr);
- AssertMsg(RootObjectBase::Is(object), "Root must be a global object!");
- ScriptContext *const scriptContext = functionBody->GetScriptContext();
- PropertyValueInfo info;
- PropertyValueInfo::SetCacheInfo(&info, functionBody, inlineCache, inlineCacheIndex, !IsFromFullJit);
- Var value;
- if (CacheOperators::TryGetProperty<true, true, true, false, true, false, !TInlineCache::IsPolymorphic, TInlineCache::IsPolymorphic, false, false>(
- object, true, object, propertyId, &value, scriptContext, nullptr, &info))
- {
- return value;
- }
- #if DBG_DUMP
- if (PHASE_VERBOSE_TRACE1(Js::InlineCachePhase))
- {
- CacheOperators::TraceCache(inlineCache, _u("PatchGetRootMethod"), propertyId, scriptContext, object);
- }
- #endif
- value = Js::JavascriptOperators::PatchGetMethodFromObject(object, object, propertyId, &info, scriptContext, true);
- #ifdef TELEMETRY_JSO
- if (TELEMETRY_PROPERTY_OPCODE_FILTER(propertyId))
- {
- // `successful` will be true as PatchGetMethod throws an exception if not found.
- scriptContext->GetTelemetry().GetOpcodeTelemetry().GetMethodProperty(object, propertyId, value, /*successful:*/ true);
- }
- #endif
- return value;
- JIT_HELPER_END(Op_PatchGetRootMethod);
- }
- JIT_HELPER_TEMPLATE(Op_PatchGetRootMethod, Op_PatchGetRootMethodPolymorphic)
- template Var JavascriptOperators::PatchGetRootMethod<false, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, DynamicObject* object, PropertyId propertyId);
- template Var JavascriptOperators::PatchGetRootMethod<true, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, DynamicObject* object, PropertyId propertyId);
- template Var JavascriptOperators::PatchGetRootMethod<false, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, DynamicObject* object, PropertyId propertyId);
- template Var JavascriptOperators::PatchGetRootMethod<true, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, DynamicObject* object, PropertyId propertyId);
- template <bool IsFromFullJit, class TInlineCache>
- inline Var JavascriptOperators::PatchScopedGetMethod(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_ScopedGetMethod);
- Assert(inlineCache != nullptr);
- ScriptContext *const scriptContext = functionBody->GetScriptContext();
- RecyclableObject* object = nullptr;
- if (FALSE == JavascriptOperators::GetPropertyObject(instance, scriptContext, &object))
- {
- // Don't error if we disabled implicit calls
- if (scriptContext->GetThreadContext()->RecordImplicitException())
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_CannotGet_NullOrUndefined,
- scriptContext->GetPropertyName(propertyId)->GetBuffer());
- }
- else
- {
- return scriptContext->GetLibrary()->GetUndefined();
- }
- }
- PropertyValueInfo info;
- PropertyValueInfo::SetCacheInfo(&info, functionBody, inlineCache, inlineCacheIndex, !IsFromFullJit);
- const bool isRoot = RootObjectBase::Is(object);
- Var value;
- if (CacheOperators::TryGetProperty<true, true, true, false, true, false, !TInlineCache::IsPolymorphic, TInlineCache::IsPolymorphic, false, false>(
- instance, isRoot, object, propertyId, &value, scriptContext, nullptr, &info))
- {
- return value;
- }
- #if DBG_DUMP
- if (PHASE_VERBOSE_TRACE1(Js::InlineCachePhase))
- {
- CacheOperators::TraceCache(inlineCache, _u("PatchGetMethod"), propertyId, scriptContext, object);
- }
- #endif
- return Js::JavascriptOperators::PatchGetMethodFromObject(instance, object, propertyId, &info, scriptContext, isRoot);
- JIT_HELPER_END(Op_ScopedGetMethod);
- }
- JIT_HELPER_TEMPLATE(Op_ScopedGetMethod, Op_ScopedGetMethodPolymorphic)
- template Var JavascriptOperators::PatchScopedGetMethod<false, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId);
- template Var JavascriptOperators::PatchScopedGetMethod<true, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId);
- template Var JavascriptOperators::PatchScopedGetMethod<false, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId);
- template Var JavascriptOperators::PatchScopedGetMethod<true, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId);
- Var JavascriptOperators::PatchGetMethodNoFastPath(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId)
- {
- ScriptContext *const scriptContext = functionBody->GetScriptContext();
- RecyclableObject* object = nullptr;
- if (FALSE == JavascriptOperators::GetPropertyObject(instance, scriptContext, &object))
- {
- // Don't error if we disabled implicit calls
- if (scriptContext->GetThreadContext()->RecordImplicitException())
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_CannotGet_NullOrUndefined,
- scriptContext->GetPropertyName(propertyId)->GetBuffer());
- }
- else
- {
- return scriptContext->GetLibrary()->GetUndefined();
- }
- }
- PropertyValueInfo info;
- PropertyValueInfo::SetCacheInfo(&info, functionBody, inlineCache, inlineCacheIndex, true);
- return Js::JavascriptOperators::PatchGetMethodFromObject(instance, object, propertyId, &info, scriptContext, false);
- }
- Var JavascriptOperators::PatchGetRootMethodNoFastPath_Var(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId)
- {
- return
- PatchGetRootMethodNoFastPath(
- functionBody,
- inlineCache,
- inlineCacheIndex,
- DynamicObject::FromVar(instance),
- propertyId);
- }
- Var JavascriptOperators::PatchGetRootMethodNoFastPath(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, DynamicObject* object, PropertyId propertyId)
- {
- AssertMsg(RootObjectBase::Is(object), "Root must be a global object!");
- PropertyValueInfo info;
- PropertyValueInfo::SetCacheInfo(&info, functionBody, inlineCache, inlineCacheIndex, true);
- return Js::JavascriptOperators::PatchGetMethodFromObject(object, object, propertyId, &info, functionBody->GetScriptContext(), true);
- }
- Var JavascriptOperators::PatchGetMethodFromObject(Var instance, RecyclableObject* propertyObject, PropertyId propertyId, PropertyValueInfo * info, ScriptContext* scriptContext, bool isRootLd)
- {
- Assert(IsPropertyObject(propertyObject));
- Var value = nullptr;
- BOOL foundValue = FALSE;
- if (isRootLd)
- {
- RootObjectBase* rootObject = RootObjectBase::FromVar(instance);
- foundValue = JavascriptOperators::GetRootPropertyReference(rootObject, propertyId, &value, scriptContext, info);
- }
- else
- {
- foundValue = JavascriptOperators::GetPropertyReference(instance, propertyObject, propertyId, &value, scriptContext, info);
- }
- if (!foundValue)
- {
- // Don't error if we disabled implicit calls
- if (scriptContext->GetThreadContext()->RecordImplicitException())
- {
- const char16* propertyName = scriptContext->GetPropertyName(propertyId)->GetBuffer();
- value = scriptContext->GetLibrary()->GetUndefined();
- JavascriptFunction * caller = NULL;
- if (JavascriptStackWalker::GetCaller(&caller, scriptContext))
- {
- FunctionBody * callerBody = caller->GetFunctionBody();
- if (callerBody && callerBody->GetUtf8SourceInfo()->GetIsXDomain())
- {
- propertyName = NULL;
- }
- }
- // Prior to version 12 we had mistakenly immediately thrown an error for property reference method calls
- // (i.e. <expr>.foo() form) when the target object is the global object. The spec says that a GetValue
- // on a reference should throw if the reference is unresolved, of which a property reference can never be,
- // however it can be unresolved in the case of an identifier expression, e.g. foo() with no qualification.
- // Such a case would come down to the global object if foo was undefined, hence the check for root object,
- // except that it should have been a check for isRootLd to be correct.
- //
- // // (at global scope)
- // foo(x());
- //
- // should throw an error before evaluating x() if foo is not defined, but
- //
- // // (at global scope)
- // this.foo(x());
- //
- // should evaluate x() before throwing an error if foo is not a property on the global object.
- // Maintain old behavior prior to version 12.
- bool isPropertyReference = !isRootLd;
- if (!isPropertyReference)
- {
- JavascriptError::ThrowReferenceError(scriptContext, JSERR_UndefVariable, propertyName);
- }
- else
- {
- // ES5 11.2.3 #2: We evaluate the call target but don't throw yet if target member is missing. We need to evaluate argList
- // first (#3). Postpone throwing error to invoke time.
- value = ThrowErrorObject::CreateThrowTypeErrorObject(scriptContext, VBSERR_OLENoPropOrMethod, propertyName);
- }
- }
- }
- return value;
- }
- template <bool IsFromFullJit, class TInlineCache>
- inline void JavascriptOperators::PatchPutValue(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_PatchPutValue);
- JIT_HELPER_SAME_ATTRIBUTES(Op_PatchPutValue, Op_PatchPutValueWithThisPtr);
- return PatchPutValueWithThisPtr<IsFromFullJit, TInlineCache>(functionBody, inlineCache, inlineCacheIndex, instance, propertyId, newValue, instance, flags);
- JIT_HELPER_END(Op_PatchPutValue);
- }
- JIT_HELPER_TEMPLATE(Op_PatchPutValue, Op_PatchPutValuePolymorphic)
- template <bool IsFromFullJit, class TInlineCache>
- inline void JavascriptOperators::PatchPutValueWithThisPtr(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, Var thisInstance, PropertyOperationFlags flags)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_PatchPutValueWithThisPtr);
- ScriptContext *const scriptContext = functionBody->GetScriptContext();
- if (TaggedNumber::Is(instance))
- {
- JavascriptOperators::SetPropertyOnTaggedNumber(instance, nullptr, propertyId, newValue, scriptContext, flags);
- return;
- }
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(instance);
- #endif
- RecyclableObject* object = RecyclableObject::FromVar(instance);
- PropertyValueInfo info;
- PropertyValueInfo::SetCacheInfo(&info, functionBody, inlineCache, inlineCacheIndex, !IsFromFullJit);
- if (CacheOperators::TrySetProperty<true, true, true, true, true, !TInlineCache::IsPolymorphic, TInlineCache::IsPolymorphic, false>(
- object, false, propertyId, newValue, scriptContext, flags, nullptr, &info))
- {
- return;
- }
- #if DBG_DUMP
- if (PHASE_VERBOSE_TRACE1(Js::InlineCachePhase))
- {
- CacheOperators::TraceCache(inlineCache, _u("PatchPutValue"), propertyId, scriptContext, object);
- }
- #endif
- ImplicitCallFlags prevImplicitCallFlags = ImplicitCall_None;
- ImplicitCallFlags currImplicitCallFlags = ImplicitCall_None;
- bool hasThisOnlyStatements = functionBody->GetHasOnlyThisStmts();
- if (hasThisOnlyStatements)
- {
- prevImplicitCallFlags = CacheAndClearImplicitBit(scriptContext);
- }
- if (!JavascriptOperators::OP_SetProperty(object, propertyId, newValue, scriptContext, &info, flags, thisInstance))
- {
- // Add implicit call flags, to bail out if field copy prop may propagate the wrong value.
- scriptContext->GetThreadContext()->AddImplicitCallFlags(ImplicitCall_NoOpSet);
- }
- if (hasThisOnlyStatements)
- {
- currImplicitCallFlags = CheckAndUpdateFunctionBodyWithImplicitFlag(functionBody);
- RestoreImplicitFlag(scriptContext, prevImplicitCallFlags, currImplicitCallFlags);
- }
- JIT_HELPER_END(Op_PatchPutValueWithThisPtr);
- }
- JIT_HELPER_TEMPLATE(Op_PatchPutValueWithThisPtr, Op_PatchPutValueWithThisPtrPolymorphic)
- template void JavascriptOperators::PatchPutValue<false, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags);
- template void JavascriptOperators::PatchPutValue<true, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags);
- template void JavascriptOperators::PatchPutValue<false, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags);
- template void JavascriptOperators::PatchPutValue<true, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags);
- template <bool IsFromFullJit, class TInlineCache>
- inline void JavascriptOperators::PatchPutRootValue(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_PatchPutRootValue);
- ScriptContext *const scriptContext = functionBody->GetScriptContext();
- RecyclableObject* object = RecyclableObject::FromVar(instance);
- PropertyValueInfo info;
- PropertyValueInfo::SetCacheInfo(&info, functionBody, inlineCache, inlineCacheIndex, !IsFromFullJit);
- if (CacheOperators::TrySetProperty<true, true, true, true, false, !TInlineCache::IsPolymorphic, TInlineCache::IsPolymorphic, false>(
- object, true, propertyId, newValue, scriptContext, flags, nullptr, &info))
- {
- return;
- }
- #if DBG_DUMP
- if (PHASE_VERBOSE_TRACE1(Js::InlineCachePhase))
- {
- CacheOperators::TraceCache(inlineCache, _u("PatchPutRootValue"), propertyId, scriptContext, object);
- }
- #endif
- ImplicitCallFlags prevImplicitCallFlags = ImplicitCall_None;
- ImplicitCallFlags currImplicitCallFlags = ImplicitCall_None;
- bool hasThisOnlyStatements = functionBody->GetHasOnlyThisStmts();
- if (hasThisOnlyStatements)
- {
- prevImplicitCallFlags = CacheAndClearImplicitBit(scriptContext);
- }
- if (!JavascriptOperators::SetRootProperty(object, propertyId, newValue, &info, scriptContext, flags))
- {
- // Add implicit call flags, to bail out if field copy prop may propagate the wrong value.
- scriptContext->GetThreadContext()->AddImplicitCallFlags(ImplicitCall_NoOpSet);
- }
- if (hasThisOnlyStatements)
- {
- currImplicitCallFlags = CheckAndUpdateFunctionBodyWithImplicitFlag(functionBody);
- RestoreImplicitFlag(scriptContext, prevImplicitCallFlags, currImplicitCallFlags);
- }
- JIT_HELPER_END(Op_PatchPutRootValue);
- }
- JIT_HELPER_TEMPLATE(Op_PatchPutRootValue, Op_PatchPutRootValuePolymorphic)
- template void JavascriptOperators::PatchPutRootValue<false, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags);
- template void JavascriptOperators::PatchPutRootValue<true, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags);
- template void JavascriptOperators::PatchPutRootValue<false, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags);
- template void JavascriptOperators::PatchPutRootValue<true, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags);
- template <bool IsFromFullJit, class TInlineCache>
- inline void JavascriptOperators::PatchPutValueNoLocalFastPath(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_PatchPutValueNoLocalFastPath);
- JIT_HELPER_SAME_ATTRIBUTES(Op_PatchPutValueNoLocalFastPath, Op_PatchPutValueWithThisPtrNoLocalFastPath);
- PatchPutValueWithThisPtrNoLocalFastPath<IsFromFullJit, TInlineCache>(functionBody, inlineCache, inlineCacheIndex, instance, propertyId, newValue, instance, flags);
- JIT_HELPER_END(Op_PatchPutValueNoLocalFastPath);
- }
- JIT_HELPER_TEMPLATE(Op_PatchPutValueNoLocalFastPath, Op_PatchPutValueNoLocalFastPathPolymorphic)
- template void JavascriptOperators::PatchPutValueNoLocalFastPath<false, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags);
- template void JavascriptOperators::PatchPutValueNoLocalFastPath<true, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags);
- template void JavascriptOperators::PatchPutValueNoLocalFastPath<false, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags);
- template void JavascriptOperators::PatchPutValueNoLocalFastPath<true, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags);
- template <bool IsFromFullJit, class TInlineCache>
- inline void JavascriptOperators::PatchPutValueWithThisPtrNoLocalFastPath(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, Var thisInstance, PropertyOperationFlags flags)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_PatchPutValueWithThisPtrNoLocalFastPath);
- ScriptContext *const scriptContext = functionBody->GetScriptContext();
- if (TaggedNumber::Is(instance))
- {
- JavascriptOperators::SetPropertyOnTaggedNumber(instance,
- nullptr,
- propertyId,
- newValue,
- scriptContext,
- flags);
- return;
- }
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(instance);
- #endif
- RecyclableObject *object = RecyclableObject::UnsafeFromVar(instance);
- PropertyValueInfo info;
- PropertyValueInfo::SetCacheInfo(&info, functionBody, inlineCache, inlineCacheIndex, !IsFromFullJit);
- if (CacheOperators::TrySetProperty<!TInlineCache::IsPolymorphic, true, true, true, true, !TInlineCache::IsPolymorphic, TInlineCache::IsPolymorphic, false>(
- object, false, propertyId, newValue, scriptContext, flags, nullptr, &info))
- {
- return;
- }
- #if DBG_DUMP
- if (PHASE_VERBOSE_TRACE1(Js::InlineCachePhase))
- {
- CacheOperators::TraceCache(inlineCache, _u("PatchPutValueNoLocalFastPath"), propertyId, scriptContext, object);
- }
- #endif
- ImplicitCallFlags prevImplicitCallFlags = ImplicitCall_None;
- ImplicitCallFlags currImplicitCallFlags = ImplicitCall_None;
- bool hasThisOnlyStatements = functionBody->GetHasOnlyThisStmts();
- if (hasThisOnlyStatements)
- {
- prevImplicitCallFlags = CacheAndClearImplicitBit(scriptContext);
- }
- if (!JavascriptOperators::OP_SetProperty(instance, propertyId, newValue, scriptContext, &info, flags, thisInstance))
- {
- // Add implicit call flags, to bail out if field copy prop may propagate the wrong value.
- scriptContext->GetThreadContext()->AddImplicitCallFlags(ImplicitCall_NoOpSet);
- }
- if (hasThisOnlyStatements)
- {
- currImplicitCallFlags = CheckAndUpdateFunctionBodyWithImplicitFlag(functionBody);
- RestoreImplicitFlag(scriptContext, prevImplicitCallFlags, currImplicitCallFlags);
- }
- JIT_HELPER_END(Op_PatchPutValueWithThisPtrNoLocalFastPath);
- }
- JIT_HELPER_TEMPLATE(Op_PatchPutValueWithThisPtrNoLocalFastPath, Op_PatchPutValueWithThisPtrNoLocalFastPathPolymorphic)
- template void JavascriptOperators::PatchPutValueWithThisPtrNoLocalFastPath<false, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, Var thisInstance, PropertyOperationFlags flags);
- template void JavascriptOperators::PatchPutValueWithThisPtrNoLocalFastPath<true, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, Var thisInstance, PropertyOperationFlags flags);
- template void JavascriptOperators::PatchPutValueWithThisPtrNoLocalFastPath<false, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, Var thisInstance, PropertyOperationFlags flags);
- template void JavascriptOperators::PatchPutValueWithThisPtrNoLocalFastPath<true, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, Var thisInstance, PropertyOperationFlags flags);
- template <bool IsFromFullJit, class TInlineCache>
- inline void JavascriptOperators::PatchPutRootValueNoLocalFastPath(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_PatchPutRootValueNoLocalFastPath);
- ScriptContext *const scriptContext = functionBody->GetScriptContext();
- RecyclableObject *object = RecyclableObject::FromVar(instance);
- PropertyValueInfo info;
- PropertyValueInfo::SetCacheInfo(&info, functionBody, inlineCache, inlineCacheIndex, !IsFromFullJit);
- if (CacheOperators::TrySetProperty<!TInlineCache::IsPolymorphic, true, true, true, false, !TInlineCache::IsPolymorphic, TInlineCache::IsPolymorphic, false>(
- object, true, propertyId, newValue, scriptContext, flags, nullptr, &info))
- {
- return;
- }
- #if DBG_DUMP
- if (PHASE_VERBOSE_TRACE1(Js::InlineCachePhase))
- {
- CacheOperators::TraceCache(inlineCache, _u("PatchPutRootValueNoLocalFastPath"), propertyId, scriptContext, object);
- }
- #endif
- ImplicitCallFlags prevImplicitCallFlags = ImplicitCall_None;
- ImplicitCallFlags currImplicitCallFlags = ImplicitCall_None;
- bool hasThisOnlyStatements = functionBody->GetHasOnlyThisStmts();
- if (hasThisOnlyStatements)
- {
- prevImplicitCallFlags = CacheAndClearImplicitBit(scriptContext);
- }
- if (!JavascriptOperators::SetRootProperty(object, propertyId, newValue, &info, scriptContext, flags))
- {
- // Add implicit call flags, to bail out if field copy prop may propagate the wrong value.
- scriptContext->GetThreadContext()->AddImplicitCallFlags(ImplicitCall_NoOpSet);
- }
- if (hasThisOnlyStatements)
- {
- currImplicitCallFlags = CheckAndUpdateFunctionBodyWithImplicitFlag(functionBody);
- RestoreImplicitFlag(scriptContext, prevImplicitCallFlags, currImplicitCallFlags);
- }
- JIT_HELPER_END(Op_PatchPutRootValueNoLocalFastPath);
- }
- JIT_HELPER_TEMPLATE(Op_PatchPutRootValueNoLocalFastPath, Op_PatchPutRootValueNoLocalFastPathPolymorphic)
- template void JavascriptOperators::PatchPutRootValueNoLocalFastPath<false, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags);
- template void JavascriptOperators::PatchPutRootValueNoLocalFastPath<true, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags);
- template void JavascriptOperators::PatchPutRootValueNoLocalFastPath<false, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags);
- template void JavascriptOperators::PatchPutRootValueNoLocalFastPath<true, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags);
- void JavascriptOperators::PatchPutValueNoFastPath(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags)
- {
- PatchPutValueWithThisPtrNoFastPath(functionBody, inlineCache, inlineCacheIndex, instance, propertyId, newValue, instance, flags);
- }
- void JavascriptOperators::PatchPutValueWithThisPtrNoFastPath(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, Var thisInstance, PropertyOperationFlags flags)
- {
- ScriptContext *const scriptContext = functionBody->GetScriptContext();
- if (TaggedNumber::Is(instance))
- {
- JavascriptOperators::SetPropertyOnTaggedNumber(instance, nullptr, propertyId, newValue, scriptContext, flags);
- return;
- }
- RecyclableObject* object = RecyclableObject::FromVar(instance);
- PropertyValueInfo info;
- PropertyValueInfo::SetCacheInfo(&info, functionBody, inlineCache, inlineCacheIndex, true);
- if (!JavascriptOperators::OP_SetProperty(object, propertyId, newValue, scriptContext, &info, flags, thisInstance))
- {
- // Add implicit call flags, to bail out if field copy prop may propagate the wrong value.
- scriptContext->GetThreadContext()->AddImplicitCallFlags(ImplicitCall_NoOpSet);
- }
- }
- void JavascriptOperators::PatchPutRootValueNoFastPath(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags)
- {
- ScriptContext *const scriptContext = functionBody->GetScriptContext();
- RecyclableObject* object = RecyclableObject::FromVar(instance);
- PropertyValueInfo info;
- PropertyValueInfo::SetCacheInfo(&info, functionBody, inlineCache, inlineCacheIndex, true);
- if (!JavascriptOperators::SetRootProperty(object, propertyId, newValue, &info, scriptContext, flags))
- {
- // Add implicit call flags, to bail out if field copy prop may propagate the wrong value.
- scriptContext->GetThreadContext()->AddImplicitCallFlags(ImplicitCall_NoOpSet);
- }
- }
- template <bool IsFromFullJit, class TInlineCache>
- inline void JavascriptOperators::PatchInitValue(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, RecyclableObject* object, PropertyId propertyId, Var newValue)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_PatchInitValue);
- ScriptContext *const scriptContext = functionBody->GetScriptContext();
- const PropertyOperationFlags flags = newValue == NULL ? PropertyOperation_SpecialValue : PropertyOperation_None;
- PropertyValueInfo info;
- PropertyValueInfo::SetCacheInfo(&info, functionBody, inlineCache, inlineCacheIndex, !IsFromFullJit);
- if (CacheOperators::TrySetProperty<true, true, false, true, true, !TInlineCache::IsPolymorphic, TInlineCache::IsPolymorphic, false>(
- object, false, propertyId, newValue, scriptContext, flags, nullptr, &info))
- {
- return;
- }
- #if DBG_DUMP
- if (PHASE_VERBOSE_TRACE1(Js::InlineCachePhase))
- {
- CacheOperators::TraceCache(inlineCache, _u("PatchInitValue"), propertyId, scriptContext, object);
- }
- #endif
- Type *typeWithoutProperty = object->GetType();
- if (functionBody->IsEval())
- {
- if (object->InitPropertyInEval(propertyId, newValue, flags, &info))
- {
- CacheOperators::CachePropertyWrite(object, false, typeWithoutProperty, propertyId, &info, scriptContext);
- return;
- }
- }
- // Ideally the lowerer would emit a call to the right flavor of PatchInitValue, so that we can ensure that we only
- // ever initialize to NULL in the right cases. But the backend uses the StFld opcode for initialization, and it
- // would be cumbersome to thread the different helper calls all the way down
- if (object->InitProperty(propertyId, newValue, flags, &info))
- {
- CacheOperators::CachePropertyWrite(object, false, typeWithoutProperty, propertyId, &info, scriptContext);
- }
- JIT_HELPER_END(Op_PatchInitValue);
- }
- JIT_HELPER_TEMPLATE(Op_PatchInitValue, Op_PatchInitValuePolymorphic)
- template void JavascriptOperators::PatchInitValue<false, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, RecyclableObject* object, PropertyId propertyId, Var newValue);
- template void JavascriptOperators::PatchInitValue<true, InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, RecyclableObject* object, PropertyId propertyId, Var newValue);
- template void JavascriptOperators::PatchInitValue<false, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, RecyclableObject* object, PropertyId propertyId, Var newValue);
- template void JavascriptOperators::PatchInitValue<true, PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, RecyclableObject* object, PropertyId propertyId, Var newValue);
- void JavascriptOperators::PatchInitValueNoFastPath(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, RecyclableObject* object, PropertyId propertyId, Var newValue)
- {
- PropertyValueInfo info;
- PropertyValueInfo::SetCacheInfo(&info, functionBody, inlineCache, inlineCacheIndex, true);
- Type *typeWithoutProperty = object->GetType();
- if (functionBody->IsEval())
- {
- if (object->InitPropertyInEval(propertyId, newValue, PropertyOperation_None, &info))
- {
- CacheOperators::CachePropertyWrite(object, false, typeWithoutProperty, propertyId, &info, functionBody->GetScriptContext());
- return;
- }
- }
- if (object->InitProperty(propertyId, newValue, PropertyOperation_None, &info))
- {
- CacheOperators::CachePropertyWrite(object, false, typeWithoutProperty, propertyId, &info, functionBody->GetScriptContext());
- }
- }
- void JavascriptOperators::GetPropertyIdForInt(uint64 value, ScriptContext* scriptContext, PropertyRecord const ** propertyRecord)
- {
- char16 buffer[20];
- ::_ui64tow_s(value, buffer, sizeof(buffer)/sizeof(char16), 10);
- scriptContext->GetOrAddPropertyRecord(buffer, JavascriptString::GetBufferLength(buffer), propertyRecord);
- }
- void JavascriptOperators::GetPropertyIdForInt(uint32 value, ScriptContext* scriptContext, PropertyRecord const ** propertyRecord)
- {
- GetPropertyIdForInt(static_cast<uint64>(value), scriptContext, propertyRecord);
- }
- Var JavascriptOperators::FromPropertyDescriptor(const PropertyDescriptor& descriptor, ScriptContext* scriptContext)
- {
- DynamicObject* object = scriptContext->GetLibrary()->CreateObject();
- // ES5 Section 8.10.4 specifies the order for adding these properties.
- if (descriptor.IsDataDescriptor())
- {
- if (descriptor.ValueSpecified())
- {
- JavascriptOperators::InitProperty(object, PropertyIds::value, descriptor.GetValue());
- }
- JavascriptOperators::InitProperty(object, PropertyIds::writable, JavascriptBoolean::ToVar(descriptor.IsWritable(),scriptContext));
- }
- else if (descriptor.IsAccessorDescriptor())
- {
- JavascriptOperators::InitProperty(object, PropertyIds::get, JavascriptOperators::CanonicalizeAccessor(descriptor.GetGetter(), scriptContext));
- JavascriptOperators::InitProperty(object, PropertyIds::set, JavascriptOperators::CanonicalizeAccessor(descriptor.GetSetter(), scriptContext));
- }
- if (descriptor.EnumerableSpecified())
- {
- JavascriptOperators::InitProperty(object, PropertyIds::enumerable, JavascriptBoolean::ToVar(descriptor.IsEnumerable(), scriptContext));
- }
- if (descriptor.ConfigurableSpecified())
- {
- JavascriptOperators::InitProperty(object, PropertyIds::configurable, JavascriptBoolean::ToVar(descriptor.IsConfigurable(), scriptContext));
- }
- return object;
- }
- // ES5 8.12.9 [[DefineOwnProperty]].
- // Return value:
- // - TRUE = success.
- // - FALSE (can throw depending on throwOnError parameter) = unsuccessful.
- BOOL JavascriptOperators::DefineOwnPropertyDescriptor(RecyclableObject* obj, PropertyId propId, const PropertyDescriptor& descriptor, bool throwOnError, ScriptContext* scriptContext)
- {
- Assert(obj);
- Assert(scriptContext);
- if (JavascriptProxy::Is(obj))
- {
- return JavascriptProxy::DefineOwnPropertyDescriptor(obj, propId, descriptor, throwOnError, scriptContext);
- }
- PropertyDescriptor currentDescriptor;
- BOOL isCurrentDescriptorDefined = JavascriptOperators::GetOwnPropertyDescriptor(obj, propId, scriptContext, ¤tDescriptor);
- bool isExtensible = !!obj->IsExtensible();
- return ValidateAndApplyPropertyDescriptor<true>(obj, propId, descriptor, isCurrentDescriptorDefined ? ¤tDescriptor : nullptr, isExtensible, throwOnError, scriptContext);
- }
- BOOL JavascriptOperators::IsCompatiblePropertyDescriptor(const PropertyDescriptor& descriptor, PropertyDescriptor* currentDescriptor, bool isExtensible, bool throwOnError, ScriptContext* scriptContext)
- {
- return ValidateAndApplyPropertyDescriptor<false>(nullptr, Constants::NoProperty, descriptor, currentDescriptor, isExtensible, throwOnError, scriptContext);
- }
- template<bool needToSetProperty>
- BOOL JavascriptOperators::ValidateAndApplyPropertyDescriptor(RecyclableObject* obj, PropertyId propId, const PropertyDescriptor& descriptor,
- PropertyDescriptor* currentDescriptor, bool isExtensible, bool throwOnError, ScriptContext* scriptContext)
- {
- Var defaultDataValue = scriptContext->GetLibrary()->GetUndefined();
- Var defaultAccessorValue = scriptContext->GetLibrary()->GetDefaultAccessorFunction();
- if (currentDescriptor == nullptr)
- {
- if (!isExtensible) // ES5 8.12.9.3.
- {
- return Reject(throwOnError, scriptContext, JSERR_DefineProperty_NotExtensible, propId);
- }
- else // ES5 8.12.9.4.
- {
- if (needToSetProperty)
- {
- if (descriptor.IsGenericDescriptor() || descriptor.IsDataDescriptor())
- {
- // ES5 8.12.9.4a: Create an own data property named P of object O whose [[Value]], [[Writable]],
- // [[Enumerable]] and [[Configurable]] attribute values are described by Desc.
- // If the value of an attribute field of Desc is absent, the attribute of the newly created property
- // is set to its default value.
- PropertyDescriptor filledDescriptor = FillMissingPropertyDescriptorFields<false>(descriptor, scriptContext);
- BOOL tempResult = obj->SetPropertyWithAttributes(propId, filledDescriptor.GetValue(), filledDescriptor.GetAttributes(), nullptr);
- if (!obj->IsExternal() && !tempResult)
- {
- Assert(TypedArrayBase::Is(obj)); // typed array returns false when canonical numeric index is not integer or out of range
- return FALSE;
- }
- }
- else
- {
- // ES5 8.12.9.4b: Create an own accessor property named P of object O whose [[Get]], [[Set]], [[Enumerable]]
- // and [[Configurable]] attribute values are described by Desc. If the value of an attribute field of Desc is absent,
- // the attribute of the newly created property is set to its default value.
- Assert(descriptor.IsAccessorDescriptor());
- PropertyDescriptor filledDescriptor = FillMissingPropertyDescriptorFields<true>(descriptor, scriptContext);
- BOOL isSetAccessorsSuccess = obj->SetAccessors(propId, filledDescriptor.GetGetter(), filledDescriptor.GetSetter());
- // It is valid for some objects to not-support getters and setters, specifically, for projection of an ABI method
- // (CustomExternalObject => MapWithStringKey) which SetAccessors returns VBSErr_ActionNotSupported.
- // But for non-external objects SetAccessors should succeed.
- Assert(isSetAccessorsSuccess || obj->IsExternal());
- // If SetAccessors failed, the property wasn't created, so no need to change the attributes.
- if (isSetAccessorsSuccess)
- {
- JavascriptOperators::SetAttributes(obj, propId, filledDescriptor, true); // use 'force' as default attributes in type system are different from ES5.
- }
- }
- }
- return TRUE;
- }
- }
- // ES5 8.12.9.5: Return true, if every field in Desc is absent.
- if (!descriptor.ConfigurableSpecified() && !descriptor.EnumerableSpecified() && !descriptor.WritableSpecified() &&
- !descriptor.ValueSpecified() && !descriptor.GetterSpecified() && !descriptor.SetterSpecified())
- {
- return TRUE;
- }
- // ES5 8.12.9.6: Return true, if every field in Desc also occurs in current and the value of every field in Desc is the same value
- // as the corresponding field in current when compared using the SameValue algorithm (9.12).
- PropertyDescriptor filledDescriptor = descriptor.IsAccessorDescriptor() ? FillMissingPropertyDescriptorFields<true>(descriptor, scriptContext)
- : FillMissingPropertyDescriptorFields<false>(descriptor, scriptContext);
- if (JavascriptOperators::AreSamePropertyDescriptors(&filledDescriptor, currentDescriptor, scriptContext))
- {
- return TRUE;
- }
- if (!currentDescriptor->IsConfigurable()) // ES5 8.12.9.7.
- {
- if (descriptor.ConfigurableSpecified() && descriptor.IsConfigurable())
- {
- return Reject(throwOnError, scriptContext, JSERR_DefineProperty_NotConfigurable, propId);
- }
- if (descriptor.EnumerableSpecified() && descriptor.IsEnumerable() != currentDescriptor->IsEnumerable())
- {
- return Reject(throwOnError, scriptContext, JSERR_DefineProperty_NotConfigurable, propId);
- }
- }
- // Whether to merge attributes from tempDescriptor into descriptor to keep original values
- // of some attributes from the object/use tempDescriptor for SetAttributes, or just use descriptor.
- // This is optimization to avoid 2 calls to SetAttributes.
- bool mergeDescriptors = false;
- // Whether to call SetAttributes with 'force' flag which forces setting all attributes
- // rather than only specified or which have true values.
- // This is to make sure that the object has correct attributes, as default values in the object are not for ES5.
- bool forceSetAttributes = false;
- PropertyDescriptor tempDescriptor;
- // ES5 8.12.9.8: If IsGenericDescriptor(Desc) is true, then no further validation is required.
- if (!descriptor.IsGenericDescriptor())
- {
- if (currentDescriptor->IsDataDescriptor() != descriptor.IsDataDescriptor())
- {
- // ES5 8.12.9.9: Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) have different results...
- if (!currentDescriptor->IsConfigurable())
- {
- return Reject(throwOnError, scriptContext, JSERR_DefineProperty_NotConfigurable, propId);
- }
- if (needToSetProperty)
- {
- if (currentDescriptor->IsDataDescriptor())
- {
- // ES5 8.12.9.9.b: Convert the property named P of object O from a data property to an accessor property.
- // Preserve the existing values of the converted property's [[Configurable]] and [[Enumerable]] attributes
- // and set the rest of the property's attributes to their default values.
- PropertyAttributes preserveFromObject = currentDescriptor->GetAttributes() & (PropertyConfigurable | PropertyEnumerable);
- BOOL isSetAccessorsSuccess = obj->SetAccessors(propId, defaultAccessorValue, defaultAccessorValue);
- // It is valid for some objects to not-support getters and setters, specifically, for projection of an ABI method
- // (CustomExternalObject => MapWithStringKey) which SetAccessors returns VBSErr_ActionNotSupported.
- // But for non-external objects SetAccessors should succeed.
- Assert(isSetAccessorsSuccess || obj->IsExternal());
- if (isSetAccessorsSuccess)
- {
- tempDescriptor.SetAttributes(preserveFromObject, PropertyConfigurable | PropertyEnumerable);
- forceSetAttributes = true; // use SetAttrbiutes with 'force' as default attributes in type system are different from ES5.
- mergeDescriptors = true;
- }
- }
- else
- {
- // ES5 8.12.9.9.c: Convert the property named P of object O from an accessor property to a data property.
- // Preserve the existing values of the converted property's [[Configurable]] and [[Enumerable]] attributes
- // and set the rest of the property's attributes to their default values.
- // Note: avoid using SetProperty/SetPropertyWithAttributes here because they has undesired side-effects:
- // it calls previous setter and in some cases of attribute values throws.
- // To walk around, call DeleteProperty and then AddProperty.
- PropertyAttributes preserveFromObject = currentDescriptor->GetAttributes() & (PropertyConfigurable | PropertyEnumerable);
- tempDescriptor.SetAttributes(preserveFromObject, PropertyConfigurable | PropertyEnumerable);
- tempDescriptor.MergeFrom(descriptor); // Update only fields specified in 'descriptor'.
- Var descriptorValue = descriptor.ValueSpecified() ? descriptor.GetValue() : defaultDataValue;
- // Note: HostDispath'es implementation of DeleteProperty currently throws E_NOTIMPL.
- obj->DeleteProperty(propId, PropertyOperation_None);
- BOOL tempResult = obj->SetPropertyWithAttributes(propId, descriptorValue, tempDescriptor.GetAttributes(), NULL, PropertyOperation_Force);
- Assert(tempResult);
- // At this time we already set value and attributes to desired values,
- // thus we can skip step ES5 8.12.9.12 and simply return true.
- return TRUE;
- }
- }
- }
- else if (currentDescriptor->IsDataDescriptor() && descriptor.IsDataDescriptor())
- {
- // ES5 8.12.9.10: Else, if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true...
- if (!currentDescriptor->IsConfigurable())
- {
- if (!currentDescriptor->IsWritable())
- {
- if (descriptor.WritableSpecified() && descriptor.IsWritable()) // ES5 8.12.9.10.a.i
- {
- return Reject(throwOnError, scriptContext, JSERR_DefineProperty_NotConfigurable, propId);
- }
- else if (descriptor.ValueSpecified() &&
- !JavascriptConversion::SameValue(descriptor.GetValue(), currentDescriptor->GetValue())) // ES5 8.12.9.10.a.ii
- {
- return Reject(throwOnError, scriptContext, JSERR_DefineProperty_NotWritable, propId);
- }
- }
- }
- // ES5 8.12.9.10.b: else, the [[Configurable]] field of current is true, so any change is acceptable.
- }
- else
- {
- // ES5 8.12.9.11: Else, IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) are both true, so...
- Assert(currentDescriptor->IsAccessorDescriptor() && descriptor.IsAccessorDescriptor());
- if (!currentDescriptor->IsConfigurable())
- {
- if ((descriptor.SetterSpecified() &&
- !JavascriptConversion::SameValue(
- JavascriptOperators::CanonicalizeAccessor(descriptor.GetSetter(), scriptContext),
- JavascriptOperators::CanonicalizeAccessor(currentDescriptor->GetSetter(), scriptContext))) ||
- (descriptor.GetterSpecified() &&
- !JavascriptConversion::SameValue(
- JavascriptOperators::CanonicalizeAccessor(descriptor.GetGetter(), scriptContext),
- JavascriptOperators::CanonicalizeAccessor(currentDescriptor->GetGetter(), scriptContext))))
- {
- return Reject(throwOnError, scriptContext, JSERR_DefineProperty_NotConfigurable, propId);
- }
- }
- }
- // This part is only for non-generic descriptors:
- // ES5 8.12.9.12: For each attribute field of Desc that is present,
- // set the correspondingly named attribute of the property named P of object O to the value of the field.
- if (descriptor.IsDataDescriptor())
- {
- if (descriptor.ValueSpecified() && needToSetProperty)
- {
- // Set just the value by passing the current attributes of the property.
- // If the property's attributes are also changing (perhaps becoming non-writable),
- // this will be taken care of in the call to JavascriptOperators::SetAttributes below.
- // Built-in Function.prototype properties 'length', 'arguments', and 'caller' are special cases.
- BOOL tempResult = obj->SetPropertyWithAttributes(propId, descriptor.GetValue(), currentDescriptor->GetAttributes(), nullptr);
- AssertMsg(tempResult || JavascriptFunction::IsBuiltinProperty(obj, propId), "If you hit this assert, most likely there is something wrong with the object/type.");
- }
- }
- else if (descriptor.IsAccessorDescriptor() && needToSetProperty)
- {
- Assert(descriptor.GetterSpecified() || descriptor.SetterSpecified());
- Var oldGetter = defaultAccessorValue, oldSetter = defaultAccessorValue;
- if (!descriptor.GetterSpecified() || !descriptor.SetterSpecified())
- {
- // Unless both getter and setter are specified, make sure we don't overwrite old accessor.
- #pragma prefast(suppress:6031, "We defaulted oldGetter and oldSetter already, so ignoring the return value here is safe")
- obj->GetAccessors(propId, &oldGetter, &oldSetter, scriptContext);
- }
- Var getter = descriptor.GetterSpecified() ? descriptor.GetGetter() : oldGetter;
- Var setter = descriptor.SetterSpecified() ? descriptor.GetSetter() : oldSetter;
- obj->SetAccessors(propId, getter, setter);
- }
- } // if (!descriptor.IsGenericDescriptor())
- // Continue for all descriptors including generic:
- // ES5 8.12.9.12: For each attribute field of Desc that is present,
- // set the correspondingly named attribute of the property named P of object O to the value of the field.
- if (needToSetProperty)
- {
- if (mergeDescriptors)
- {
- tempDescriptor.MergeFrom(descriptor);
- JavascriptOperators::SetAttributes(obj, propId, tempDescriptor, forceSetAttributes);
- }
- else
- {
- JavascriptOperators::SetAttributes(obj, propId, descriptor, forceSetAttributes);
- }
- }
- return TRUE;
- }
- template <bool isAccessor>
- PropertyDescriptor JavascriptOperators::FillMissingPropertyDescriptorFields(PropertyDescriptor descriptor, ScriptContext* scriptContext)
- {
- PropertyDescriptor newDescriptor;
- const PropertyDescriptor* defaultDescriptor = scriptContext->GetLibrary()->GetDefaultPropertyDescriptor();
- if (isAccessor)
- {
- newDescriptor.SetGetter(descriptor.GetterSpecified() ? descriptor.GetGetter() : defaultDescriptor->GetGetter());
- newDescriptor.SetSetter(descriptor.SetterSpecified() ? descriptor.GetSetter() : defaultDescriptor->GetSetter());
- }
- else
- {
- newDescriptor.SetValue(descriptor.ValueSpecified() ? descriptor.GetValue() : defaultDescriptor->GetValue());
- newDescriptor.SetWritable(descriptor.WritableSpecified() ? descriptor.IsWritable() : defaultDescriptor->IsWritable());
- }
- newDescriptor.SetConfigurable(descriptor.ConfigurableSpecified() ? descriptor.IsConfigurable() : defaultDescriptor->IsConfigurable());
- newDescriptor.SetEnumerable(descriptor.EnumerableSpecified() ? descriptor.IsEnumerable() : defaultDescriptor->IsEnumerable());
- return newDescriptor;
- }
- // ES5: 15.4.5.1
- BOOL JavascriptOperators::DefineOwnPropertyForArray(JavascriptArray* arr, PropertyId propId, const PropertyDescriptor& descriptor, bool throwOnError, ScriptContext* scriptContext)
- {
- if (propId == PropertyIds::length)
- {
- if (!descriptor.ValueSpecified())
- {
- return DefineOwnPropertyDescriptor(arr, PropertyIds::length, descriptor, throwOnError, scriptContext);
- }
- PropertyDescriptor newLenDesc = descriptor;
- uint32 newLen = ES5Array::ToLengthValue(descriptor.GetValue(), scriptContext);
- newLenDesc.SetValue(JavascriptNumber::ToVar(newLen, scriptContext));
- uint32 oldLen = arr->GetLength();
- if (newLen >= oldLen)
- {
- return DefineOwnPropertyDescriptor(arr, PropertyIds::length, newLenDesc, throwOnError, scriptContext);
- }
- BOOL oldLenWritable = arr->IsWritable(PropertyIds::length);
- if (!oldLenWritable)
- {
- return Reject(throwOnError, scriptContext, JSERR_DefineProperty_NotWritable, propId);
- }
- bool newWritable = (!newLenDesc.WritableSpecified() || newLenDesc.IsWritable());
- if (!newWritable)
- {
- // Need to defer setting writable to false in case any elements cannot be deleted
- newLenDesc.SetWritable(true);
- }
- BOOL succeeded = DefineOwnPropertyDescriptor(arr, PropertyIds::length, newLenDesc, throwOnError, scriptContext);
- //
- // Our SetProperty(length) is also responsible to trim elements. When succeeded is
- //
- // false:
- // * length attributes rejected
- // * elements not touched
- // true:
- // * length attributes are set successfully
- // * elements trimming may be either completed or incompleted, length value is correct
- //
- // * Strict mode TODO: Currently SetProperty(length) does not throw. If that throws, we need
- // to update here to set correct newWritable even on exception.
- //
- if (!succeeded)
- {
- return false;
- }
- if (!newWritable) // Now set requested newWritable.
- {
- PropertyDescriptor newWritableDesc;
- newWritableDesc.SetWritable(false);
- DefineOwnPropertyDescriptor(arr, PropertyIds::length, newWritableDesc, false, scriptContext);
- }
- if (arr->GetLength() > newLen) // Delete incompleted
- {
- // Since SetProperty(length) not throwing, we'll reject here
- return Reject(throwOnError, scriptContext, JSERR_DefineProperty_Default, propId);
- }
- return true;
- }
- uint32 index;
- if (scriptContext->IsNumericPropertyId(propId, &index))
- {
- if (index >= arr->GetLength() && !arr->IsWritable(PropertyIds::length))
- {
- return Reject(throwOnError, scriptContext, JSERR_DefineProperty_LengthNotWritable, propId);
- }
- BOOL succeeded = DefineOwnPropertyDescriptor(arr, propId, descriptor, false, scriptContext);
- if (!succeeded)
- {
- return Reject(throwOnError, scriptContext, JSERR_DefineProperty_Default, propId);
- }
- // Out SetItem takes care of growing "length". we are done.
- return true;
- }
- return DefineOwnPropertyDescriptor(arr, propId, descriptor, throwOnError, scriptContext);
- }
- BOOL JavascriptOperators::SetPropertyDescriptor(RecyclableObject* object, PropertyId propId, const PropertyDescriptor& descriptor)
- {
- if (descriptor.ValueSpecified())
- {
- ScriptContext* requestContext = object->GetScriptContext(); // Real requestContext?
- JavascriptOperators::SetProperty(object, object, propId, descriptor.GetValue(), requestContext);
- }
- else if (descriptor.GetterSpecified() || descriptor.SetterSpecified())
- {
- JavascriptOperators::SetAccessors(object, propId, descriptor.GetGetter(), descriptor.GetSetter());
- }
- if (descriptor.EnumerableSpecified())
- {
- object->SetEnumerable(propId, descriptor.IsEnumerable());
- }
- if (descriptor.ConfigurableSpecified())
- {
- object->SetConfigurable(propId, descriptor.IsConfigurable());
- }
- if (descriptor.WritableSpecified())
- {
- object->SetWritable(propId, descriptor.IsWritable());
- }
- return true;
- }
- BOOL JavascriptOperators::ToPropertyDescriptorForProxyObjects(Var propertySpec, PropertyDescriptor* descriptor, ScriptContext* scriptContext)
- {
- if (!JavascriptOperators::IsObject(propertySpec))
- {
- return FALSE;
- }
- Var value;
- RecyclableObject* propertySpecObj = RecyclableObject::FromVar(propertySpec);
- if (JavascriptOperators::HasProperty(propertySpecObj, PropertyIds::enumerable) == TRUE)
- {
- if (JavascriptOperators::GetProperty(propertySpecObj, PropertyIds::enumerable, &value, scriptContext))
- {
- descriptor->SetEnumerable(JavascriptConversion::ToBoolean(value, scriptContext) ? true : false);
- }
- else
- {
- // The proxy said we have the property, so we try to read the property and get the default value.
- descriptor->SetEnumerable(false);
- }
- }
- if (JavascriptOperators::HasProperty(propertySpecObj, PropertyIds::configurable) == TRUE)
- {
- if (JavascriptOperators::GetProperty(propertySpecObj, PropertyIds::configurable, &value, scriptContext))
- {
- descriptor->SetConfigurable(JavascriptConversion::ToBoolean(value, scriptContext) ? true : false);
- }
- else
- {
- // The proxy said we have the property, so we try to read the property and get the default value.
- descriptor->SetConfigurable(false);
- }
- }
- if (JavascriptOperators::HasProperty(propertySpecObj, PropertyIds::value) == TRUE)
- {
- if (JavascriptOperators::GetProperty(propertySpecObj, PropertyIds::value, &value, scriptContext))
- {
- descriptor->SetValue(value);
- }
- else
- {
- // The proxy said we have the property, so we try to read the property and get the default value.
- descriptor->SetValue(scriptContext->GetLibrary()->GetUndefined());
- }
- }
- if (JavascriptOperators::HasProperty(propertySpecObj, PropertyIds::writable) == TRUE)
- {
- if (JavascriptOperators::GetProperty(propertySpecObj, PropertyIds::writable, &value, scriptContext))
- {
- descriptor->SetWritable(JavascriptConversion::ToBoolean(value, scriptContext) ? true : false);
- }
- else
- {
- // The proxy said we have the property, so we try to read the property and get the default value.
- descriptor->SetWritable(false);
- }
- }
- if (JavascriptOperators::HasProperty(propertySpecObj, PropertyIds::get) == TRUE)
- {
- if (JavascriptOperators::GetProperty(propertySpecObj, PropertyIds::get, &value, scriptContext))
- {
- if (JavascriptOperators::GetTypeId(value) != TypeIds_Undefined && (false == JavascriptConversion::IsCallable(value)))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_NeedFunction, scriptContext->GetPropertyName(PropertyIds::get)->GetBuffer());
- }
- descriptor->SetGetter(value);
- }
- else
- {
- // The proxy said we have the property, so we try to read the property and get the default value.
- descriptor->SetGetter(scriptContext->GetLibrary()->GetUndefined());
- }
- }
- if (JavascriptOperators::HasProperty(propertySpecObj, PropertyIds::set) == TRUE)
- {
- if (JavascriptOperators::GetProperty(propertySpecObj, PropertyIds::set, &value, scriptContext))
- {
- if (JavascriptOperators::GetTypeId(value) != TypeIds_Undefined && (false == JavascriptConversion::IsCallable(value)))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_NeedFunction, scriptContext->GetPropertyName(PropertyIds::set)->GetBuffer());
- }
- descriptor->SetSetter(value);
- }
- else
- {
- // The proxy said we have the property, so we try to read the property and get the default value.
- descriptor->SetSetter(scriptContext->GetLibrary()->GetUndefined());
- }
- }
- return TRUE;
- }
- BOOL JavascriptOperators::ToPropertyDescriptorForGenericObjects(Var propertySpec, PropertyDescriptor* descriptor, ScriptContext* scriptContext)
- {
- if (!JavascriptOperators::IsObject(propertySpec))
- {
- return FALSE;
- }
- Var value;
- RecyclableObject* propertySpecObj = RecyclableObject::FromVar(propertySpec);
- if (JavascriptOperators::GetPropertyNoCache(propertySpecObj, PropertyIds::enumerable, &value, scriptContext))
- {
- descriptor->SetEnumerable(JavascriptConversion::ToBoolean(value, scriptContext) ? true : false);
- }
- if (JavascriptOperators::GetPropertyNoCache(propertySpecObj, PropertyIds::configurable, &value, scriptContext))
- {
- descriptor->SetConfigurable(JavascriptConversion::ToBoolean(value, scriptContext) ? true : false);
- }
- if (JavascriptOperators::GetPropertyNoCache(propertySpecObj, PropertyIds::value, &value, scriptContext))
- {
- descriptor->SetValue(value);
- }
- if (JavascriptOperators::GetPropertyNoCache(propertySpecObj, PropertyIds::writable, &value, scriptContext))
- {
- descriptor->SetWritable(JavascriptConversion::ToBoolean(value, scriptContext) ? true : false);
- }
- if (JavascriptOperators::GetPropertyNoCache(propertySpecObj, PropertyIds::get, &value, scriptContext))
- {
- if (JavascriptOperators::GetTypeId(value) != TypeIds_Undefined && (false == JavascriptConversion::IsCallable(value)))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_NeedFunction, scriptContext->GetPropertyName(PropertyIds::get)->GetBuffer());
- }
- descriptor->SetGetter(value);
- }
- if (JavascriptOperators::GetPropertyNoCache(propertySpecObj, PropertyIds::set, &value, scriptContext))
- {
- if (JavascriptOperators::GetTypeId(value) != TypeIds_Undefined && (false == JavascriptConversion::IsCallable(value)))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_NeedFunction, scriptContext->GetPropertyName(PropertyIds::set)->GetBuffer());
- }
- descriptor->SetSetter(value);
- }
- return TRUE;
- }
- BOOL JavascriptOperators::ToPropertyDescriptor(Var propertySpec, PropertyDescriptor* descriptor, ScriptContext* scriptContext)
- {
- if (JavascriptProxy::Is(propertySpec) || (
- RecyclableObject::Is(propertySpec) &&
- JavascriptOperators::CheckIfPrototypeChainContainsProxyObject(RecyclableObject::FromVar(propertySpec)->GetPrototype())))
- {
- if (ToPropertyDescriptorForProxyObjects(propertySpec, descriptor, scriptContext) == FALSE)
- {
- return FALSE;
- }
- }
- else
- {
- if (ToPropertyDescriptorForGenericObjects(propertySpec, descriptor, scriptContext) == FALSE)
- {
- return FALSE;
- }
- }
- if (descriptor->GetterSpecified() || descriptor->SetterSpecified())
- {
- if (descriptor->ValueSpecified())
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_CannotHaveAccessorsAndValue);
- }
- if (descriptor->WritableSpecified())
- {
- int32 hCode = descriptor->IsWritable() ? JSERR_InvalidAttributeTrue : JSERR_InvalidAttributeFalse;
- JavascriptError::ThrowTypeError(scriptContext, hCode, _u("writable"));
- }
- }
- descriptor->SetOriginal(propertySpec);
- return TRUE;
- }
- void JavascriptOperators::CompletePropertyDescriptor(PropertyDescriptor* resultDescriptor, PropertyDescriptor* likeDescriptor, ScriptContext* requestContext)
- {
- const PropertyDescriptor* likePropertyDescriptor = likeDescriptor;
- // 1. Assert: LikeDesc is either a Property Descriptor or undefined.
- // 2. ReturnIfAbrupt(Desc).
- // 3. Assert : Desc is a Property Descriptor
- // 4. If LikeDesc is undefined, then set LikeDesc to Record{ [[Value]]: undefined, [[Writable]] : false, [[Get]] : undefined, [[Set]] : undefined, [[Enumerable]] : false, [[Configurable]] : false }.
- if (likePropertyDescriptor == nullptr)
- {
- likePropertyDescriptor = requestContext->GetLibrary()->GetDefaultPropertyDescriptor();
- }
- // 5. If either IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then
- if (resultDescriptor->IsDataDescriptor() || resultDescriptor->IsGenericDescriptor())
- {
- // a.If Desc does not have a[[Value]] field, then set Desc.[[Value]] to LikeDesc.[[Value]].
- // b.If Desc does not have a[[Writable]] field, then set Desc.[[Writable]] to LikeDesc.[[Writable]].
- if (!resultDescriptor->ValueSpecified())
- {
- resultDescriptor->SetValue(likePropertyDescriptor->GetValue());
- }
- if (!resultDescriptor->WritableSpecified())
- {
- resultDescriptor->SetWritable(likePropertyDescriptor->IsWritable());
- }
- }
- else
- {
- // 6. Else,
- // a.If Desc does not have a[[Get]] field, then set Desc.[[Get]] to LikeDesc.[[Get]].
- // b.If Desc does not have a[[Set]] field, then set Desc.[[Set]] to LikeDesc.[[Set]].
- if (!resultDescriptor->GetterSpecified())
- {
- resultDescriptor->SetGetter(likePropertyDescriptor->GetGetter());
- }
- if (!resultDescriptor->SetterSpecified())
- {
- resultDescriptor->SetSetter(likePropertyDescriptor->GetSetter());
- }
- }
- // 7. If Desc does not have an[[Enumerable]] field, then set Desc.[[Enumerable]] to LikeDesc.[[Enumerable]].
- // 8. If Desc does not have a[[Configurable]] field, then set Desc.[[Configurable]] to LikeDesc.[[Configurable]].
- // 9. Return Desc.
- if (!resultDescriptor->EnumerableSpecified())
- {
- resultDescriptor->SetEnumerable(likePropertyDescriptor->IsEnumerable());
- }
- if (!resultDescriptor->ConfigurableSpecified())
- {
- resultDescriptor->SetConfigurable(likePropertyDescriptor->IsConfigurable());
- }
- }
- // Conformance to: ES5 8.6.1.
- // Set attributes on the object as provided by property descriptor.
- // If force parameter is true, we force SetAttributes call even if none of the attributes are defined by the descriptor.
- // NOTE: does not set [[Get]], [Set]], [[Value]]
- void JavascriptOperators::SetAttributes(RecyclableObject* object, PropertyId propId, const PropertyDescriptor& descriptor, bool force)
- {
- Assert(object);
- BOOL isWritable = FALSE;
- if (descriptor.IsDataDescriptor())
- {
- isWritable = descriptor.WritableSpecified() ? descriptor.IsWritable() : FALSE;
- }
- else if (descriptor.IsAccessorDescriptor())
- {
- // The reason is that JavascriptOperators::OP_SetProperty checks for RecyclableObject::FromVar(instance)->IsWritableOrAccessor(propertyId),
- // which should in fact check for 'is writable or accessor' but since there is no GetAttributes, we can't do that efficiently.
- isWritable = TRUE;
- }
- // CONSIDER: call object->SetAttributes which is much more efficient as that's 1 call instead of 3.
- // Can't do that now as object->SetAttributes doesn't provide a way which attributes to modify and which not.
- if (force || descriptor.ConfigurableSpecified())
- {
- object->SetConfigurable(propId, descriptor.ConfigurableSpecified() ? descriptor.IsConfigurable() : FALSE);
- }
- if (force || descriptor.EnumerableSpecified())
- {
- object->SetEnumerable(propId, descriptor.EnumerableSpecified() ? descriptor.IsEnumerable() : FALSE);
- }
- if (force || descriptor.WritableSpecified() || isWritable)
- {
- object->SetWritable(propId, isWritable);
- }
- }
- void JavascriptOperators::OP_ClearAttributes(Var instance, PropertyId propertyId)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(OP_ClearAttributes);
- Assert(instance);
- if (RecyclableObject::Is(instance))
- {
- RecyclableObject* obj = RecyclableObject::FromVar(instance);
- obj->SetAttributes(propertyId, PropertyNone);
- }
- JIT_HELPER_END(OP_ClearAttributes);
- }
- BOOL JavascriptOperators::Reject(bool throwOnError, ScriptContext* scriptContext, int32 errorCode, PropertyId propertyId)
- {
- Assert(scriptContext);
- if (throwOnError)
- {
- JavascriptError::ThrowTypeError(scriptContext, errorCode, scriptContext->GetThreadContext()->GetPropertyName(propertyId)->GetBuffer());
- }
- return FALSE;
- }
- bool JavascriptOperators::AreSamePropertyDescriptors(const PropertyDescriptor* x, const PropertyDescriptor* y, ScriptContext* scriptContext)
- {
- Assert(scriptContext);
- if (x->ConfigurableSpecified() != y->ConfigurableSpecified() || x->IsConfigurable() != y->IsConfigurable() ||
- x->EnumerableSpecified() != y->EnumerableSpecified() || x->IsEnumerable() != y->IsEnumerable())
- {
- return false;
- }
- if (x->IsDataDescriptor())
- {
- if (!y->IsDataDescriptor() || x->WritableSpecified() != y->WritableSpecified() || x->IsWritable() != y->IsWritable())
- {
- return false;
- }
- if (x->ValueSpecified())
- {
- if (!y->ValueSpecified() || !JavascriptConversion::SameValue(x->GetValue(), y->GetValue()))
- {
- return false;
- }
- }
- }
- else if (x->IsAccessorDescriptor())
- {
- if (!y->IsAccessorDescriptor())
- {
- return false;
- }
- if (x->GetterSpecified())
- {
- if (!y->GetterSpecified() || !JavascriptConversion::SameValue(
- JavascriptOperators::CanonicalizeAccessor(x->GetGetter(), scriptContext),
- JavascriptOperators::CanonicalizeAccessor(y->GetGetter(), scriptContext)))
- {
- return false;
- }
- }
- if (x->SetterSpecified())
- {
- if (!y->SetterSpecified() || !JavascriptConversion::SameValue(
- JavascriptOperators::CanonicalizeAccessor(x->GetSetter(), scriptContext),
- JavascriptOperators::CanonicalizeAccessor(y->GetSetter(), scriptContext)))
- {
- return false;
- }
- }
- }
- return true;
- }
- // Check if an accessor is undefined (null or defaultAccessor)
- bool JavascriptOperators::IsUndefinedAccessor(Var accessor, ScriptContext* scriptContext)
- {
- return nullptr == accessor || scriptContext->GetLibrary()->GetDefaultAccessorFunction() == accessor;
- }
- // Converts default accessor to undefined.
- // Can be used when comparing accessors.
- Var JavascriptOperators::CanonicalizeAccessor(Var accessor, ScriptContext* scriptContext)
- {
- Assert(scriptContext);
- if (IsUndefinedAccessor(accessor, scriptContext))
- {
- return scriptContext->GetLibrary()->GetUndefined();
- }
- return accessor;
- }
- Var JavascriptOperators::DefaultAccessor(RecyclableObject* function, CallInfo callInfo, ...)
- {
- return function->GetLibrary()->GetUndefined();
- }
- void FrameDisplay::SetItem(uint index, void* item)
- {
- AssertMsg(index < this->length, "Invalid frame display access");
- scopes[index] = item;
- }
- void *FrameDisplay::GetItem(uint index)
- {
- AssertMsg(index < this->length, "Invalid frame display access");
- return scopes[index];
- }
- // Grab the "this" pointer, mapping a root object to its associated host object.
- Var JavascriptOperators::RootToThisObject(const Var object, ScriptContext* scriptContext)
- {
- Js::Var thisVar = object;
- TypeId typeId = Js::JavascriptOperators::GetTypeId(thisVar);
- switch (typeId)
- {
- case Js::TypeIds_GlobalObject:
- return ((Js::GlobalObject*)thisVar)->ToThis();
- case Js::TypeIds_ModuleRoot:
- return Js::JavascriptOperators::GetThisFromModuleRoot(thisVar);
- default:
- if (typeId == scriptContext->GetDirectHostTypeId())
- {
- return ((RecyclableObject*)thisVar)->GetLibrary()->GetGlobalObject()->ToThis();
- }
- }
- return thisVar;
- }
- Var JavascriptOperators::CallGetter(RecyclableObject * const function, Var const object, ScriptContext * requestContext)
- {
- #if ENABLE_TTD
- if(function->GetScriptContext()->ShouldSuppressGetterInvocationForDebuggerEvaluation())
- {
- return requestContext->GetLibrary()->GetUndefined();
- }
- #endif
- ScriptContext * scriptContext = function->GetScriptContext();
- ThreadContext * threadContext = scriptContext->GetThreadContext();
- return threadContext->ExecuteImplicitCall(function, ImplicitCall_Accessor, [=]() -> Js::Var
- {
- // Stack object should have a pre-op bail on implicit call. We shouldn't see them here.
- // Stack numbers are ok, as we will call ToObject to wrap it in a number object anyway
- // See JavascriptOperators::GetThisHelper
- Assert(JavascriptOperators::GetTypeId(object) == TypeIds_Integer ||
- JavascriptOperators::GetTypeId(object) == TypeIds_Number ||
- threadContext->HasNoSideEffect(function) ||
- !ThreadContext::IsOnStack(object));
- // Verify that the scriptcontext is alive before firing getter/setter
- if (!scriptContext->VerifyAlive(!function->IsExternal(), requestContext))
- {
- return nullptr;
- }
- CallFlags flags = CallFlags_Value;
- Var thisVar = RootToThisObject(object, scriptContext);
- RecyclableObject* marshalledFunction = RecyclableObject::UnsafeFromVar(
- CrossSite::MarshalVar(requestContext, function, scriptContext));
- Var result = CALL_ENTRYPOINT(threadContext, marshalledFunction->GetEntryPoint(), function, CallInfo(flags, 1), thisVar);
- result = CrossSite::MarshalVar(requestContext, result);
- return result;
- });
- }
- void JavascriptOperators::CallSetter(RecyclableObject * const function, Var const object, Var const value, ScriptContext * requestContext)
- {
- ScriptContext * scriptContext = function->GetScriptContext();
- ThreadContext * threadContext = scriptContext->GetThreadContext();
- threadContext->ExecuteImplicitCall(function, ImplicitCall_Accessor, [=]() -> Js::Var
- {
- // Stack object should have a pre-op bail on implicit call. We shouldn't see them here.
- // Stack numbers are ok, as we will call ToObject to wrap it in a number object anyway
- // See JavascriptOperators::GetThisHelper
- Assert(JavascriptOperators::GetTypeId(object) == TypeIds_Integer ||
- JavascriptOperators::GetTypeId(object) == TypeIds_Number || !ThreadContext::IsOnStack(object));
- // Verify that the scriptcontext is alive before firing getter/setter
- if (!scriptContext->VerifyAlive(!function->IsExternal(), requestContext))
- {
- return nullptr;
- }
- CallFlags flags = CallFlags_Value;
- Var putValue = value;
- // CONSIDER: Have requestContext everywhere, even in the setProperty related codepath.
- if (requestContext)
- {
- putValue = CrossSite::MarshalVar(requestContext, value);
- }
- Var thisVar = RootToThisObject(object, scriptContext);
- RecyclableObject* marshalledFunction = function;
- if (requestContext)
- {
- marshalledFunction = RecyclableObject::UnsafeFromVar(CrossSite::MarshalVar(requestContext, function, function->GetScriptContext()));
- }
- Var result = CALL_ENTRYPOINT(threadContext, marshalledFunction->GetEntryPoint(), function, CallInfo(flags, 2), thisVar, putValue);
- Assert(result);
- return nullptr;
- });
- }
- void * JavascriptOperators::AllocMemForVarArray(size_t size, Recycler* recycler)
- {
- TRACK_ALLOC_INFO(recycler, Js::Var, Recycler, 0, (size_t)(size / sizeof(Js::Var)));
- return recycler->AllocZero(size);
- }
- #if !FLOATVAR
- void * JavascriptOperators::AllocUninitializedNumber(Js::RecyclerJavascriptNumberAllocator * allocator)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(AllocUninitializedNumber);
- TRACK_ALLOC_INFO(allocator->GetRecycler(), Js::JavascriptNumber, Recycler, 0, (size_t)-1);
- return allocator->Alloc(sizeof(Js::JavascriptNumber));
- JIT_HELPER_END(AllocUninitializedNumber);
- }
- #endif
- void JavascriptOperators::ScriptAbort()
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(ScriptAbort);
- throw ScriptAbortException();
- JIT_HELPER_END(ScriptAbort);
- }
- JavascriptString * JavascriptOperators::Concat3(Var aLeft, Var aCenter, Var aRight, ScriptContext * scriptContext)
- {
- // Make sure we do the conversion in order from left to right
- JavascriptString * strLeft = JavascriptConversion::ToPrimitiveString(aLeft, scriptContext);
- JavascriptString * strCenter = JavascriptConversion::ToPrimitiveString(aCenter, scriptContext);
- JavascriptString * strRight = JavascriptConversion::ToPrimitiveString(aRight, scriptContext);
- return JavascriptString::Concat3(strLeft, strCenter, strRight);
- }
- JavascriptString *
- JavascriptOperators::NewConcatStrMulti(Var a1, Var a2, uint count, ScriptContext * scriptContext)
- {
- // Make sure we do the conversion in order
- JavascriptString * str1 = JavascriptConversion::ToPrimitiveString(a1, scriptContext);
- JavascriptString * str2 = JavascriptConversion::ToPrimitiveString(a2, scriptContext);
- return ConcatStringMulti::New(count, str1, str2, scriptContext);
- }
- void
- JavascriptOperators::SetConcatStrMultiItem(Var concatStr, Var str, uint index, ScriptContext * scriptContext)
- {
- ConcatStringMulti::FromVar(concatStr)->SetItem(index,
- JavascriptConversion::ToPrimitiveString(str, scriptContext));
- }
- void
- JavascriptOperators::SetConcatStrMultiItem2(Var concatStr, Var str1, Var str2, uint index, ScriptContext * scriptContext)
- {
- ConcatStringMulti * cs = ConcatStringMulti::FromVar(concatStr);
- cs->SetItem(index, JavascriptConversion::ToPrimitiveString(str1, scriptContext));
- cs->SetItem(index + 1, JavascriptConversion::ToPrimitiveString(str2, scriptContext));
- }
- void JavascriptOperators::OP_SetComputedNameVar(Var method, Var computedNameVar)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(SetComputedNameVar);
- ScriptFunctionBase *scriptFunction = ScriptFunctionBase::FromVar(method);
- scriptFunction->SetComputedNameVar(computedNameVar);
- JIT_HELPER_END(SetComputedNameVar);
- }
- void JavascriptOperators::OP_SetHomeObj(Var method, Var homeObj)
- {
- ScriptFunctionBase *scriptFunction = ScriptFunctionBase::FromVar(method);
- scriptFunction->SetHomeObj(homeObj);
- }
- Var JavascriptOperators::OP_LdHomeObj(Var scriptFunction, ScriptContext * scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_HEADER(LdHomeObj, reentrancylock, scriptContext->GetThreadContext());
- // Ensure this is not a stack ScriptFunction
- if (!ScriptFunction::Is(scriptFunction) || ThreadContext::IsOnStack(scriptFunction))
- {
- return scriptContext->GetLibrary()->GetUndefined();
- }
- ScriptFunction *instance = ScriptFunction::UnsafeFromVar(scriptFunction);
- // We keep a reference to the current class rather than its super prototype
- // since the prototype could change.
- Var homeObj = instance->GetHomeObj();
- return (homeObj != nullptr) ? homeObj : scriptContext->GetLibrary()->GetUndefined();
- JIT_HELPER_END(LdHomeObj);
- }
- Var JavascriptOperators::OP_LdHomeObjProto(Var homeObj, ScriptContext* scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_HEADER(LdHomeObjProto, reentrancylock, scriptContext->GetThreadContext());
- if (homeObj == nullptr || !RecyclableObject::Is(homeObj))
- {
- return scriptContext->GetLibrary()->GetUndefined();
- }
- RecyclableObject *thisObjPrototype = RecyclableObject::FromVar(homeObj);
- TypeId typeId = thisObjPrototype->GetTypeId();
- if (typeId == TypeIds_Null || typeId == TypeIds_Undefined)
- {
- JavascriptError::ThrowReferenceError(scriptContext, JSERR_BadSuperReference);
- }
- Assert(thisObjPrototype != nullptr);
- RecyclableObject *superBase = thisObjPrototype->GetPrototype();
- if (superBase == nullptr || !RecyclableObject::Is(superBase))
- {
- return scriptContext->GetLibrary()->GetUndefined();
- }
- return superBase;
- JIT_HELPER_END(LdHomeObjProto);
- }
- Var JavascriptOperators::OP_LdFuncObj(Var scriptFunction, ScriptContext * scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(LdFuncObj);
- // use self as value of [[FunctionObject]] - this is true only for constructors
- Assert(RecyclableObject::Is(scriptFunction));
- return scriptFunction;
- JIT_HELPER_END(LdFuncObj);
- }
- Var JavascriptOperators::OP_LdFuncObjProto(Var funcObj, ScriptContext* scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_HEADER(LdFuncObjProto, reentrancylock, scriptContext->GetThreadContext());
- RecyclableObject *superCtor = RecyclableObject::FromVar(funcObj)->GetPrototype();
- if (superCtor == nullptr || !IsConstructor(superCtor))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_NotAConstructor);
- }
- return superCtor;
- JIT_HELPER_END(LdFuncObjProto);
- }
- Var JavascriptOperators::OP_ImportCall(__in JavascriptFunction *function, __in Var specifier, __in ScriptContext* scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(ImportCall);
- ModuleRecordBase *moduleRecordBase = nullptr;
- SourceTextModuleRecord *moduleRecord = nullptr;
- FunctionBody* parentFuncBody = function->GetFunctionBody();
- JavascriptString *specifierString = nullptr;
- try
- {
- specifierString = JavascriptConversion::ToString(specifier, scriptContext);
- }
- catch (const JavascriptException &err)
- {
- Var errorObject = err.GetAndClear()->GetThrownObject(scriptContext);
- AssertMsg(errorObject != nullptr, "OP_ImportCall: null error object thrown by ToString(specifier)");
- if (errorObject != nullptr)
- {
- return SourceTextModuleRecord::ResolveOrRejectDynamicImportPromise(false, errorObject, scriptContext);
- }
- Throw::InternalError();
- }
- DWORD_PTR dwReferencingSourceContext = parentFuncBody->GetHostSourceContext();
- if (!parentFuncBody->IsES6ModuleCode() && dwReferencingSourceContext == Js::Constants::NoHostSourceContext)
- {
- // import() called from eval
- if (parentFuncBody->GetUtf8SourceInfo()->GetCallerUtf8SourceInfo() == nullptr)
- {
- JavascriptError *error = scriptContext->GetLibrary()->CreateError();
- JavascriptError::SetErrorMessageProperties(error, E_FAIL, _u("Unable to locate active script or module that calls import()"), scriptContext);
- return SourceTextModuleRecord::ResolveOrRejectDynamicImportPromise(false, error, scriptContext);
- }
- dwReferencingSourceContext = parentFuncBody->GetUtf8SourceInfo()->GetCallerUtf8SourceInfo()->GetSourceContextInfo()->dwHostSourceContext;
- if (dwReferencingSourceContext == Js::Constants::NoHostSourceContext)
- {
- // Walk the call stack if caller function is neither module code nor having host source context
- JavascriptFunction* caller = nullptr;
- Js::JavascriptStackWalker walker(scriptContext);
- walker.GetCaller(&caller);
- do
- {
- if (walker.GetCaller(&caller) && caller != nullptr && caller->IsScriptFunction())
- {
- parentFuncBody = caller->GetFunctionBody();
- dwReferencingSourceContext = parentFuncBody->GetHostSourceContext();
- }
- else
- {
- JavascriptError *error = scriptContext->GetLibrary()->CreateError();
- JavascriptError::SetErrorMessageProperties(error, E_FAIL, _u("Unable to locate active script or module that calls import()"), scriptContext);
- return SourceTextModuleRecord::ResolveOrRejectDynamicImportPromise(false, error, scriptContext);
- }
- } while (!parentFuncBody->IsES6ModuleCode() && dwReferencingSourceContext == Js::Constants::NoHostSourceContext);
- }
- }
- LPCOLESTR moduleName = specifierString->GetSz();
- HRESULT hr = 0;
- if (parentFuncBody->IsES6ModuleCode())
- {
- SourceTextModuleRecord *referenceModuleRecord = parentFuncBody->GetScriptContext()->GetLibrary()->GetModuleRecord(parentFuncBody->GetModuleID());
- BEGIN_LEAVE_SCRIPT(scriptContext);
- BEGIN_TRANSLATE_TO_HRESULT(static_cast<ExceptionType>(ExceptionType_OutOfMemory | ExceptionType_StackOverflow));
- hr = scriptContext->GetHostScriptContext()->FetchImportedModule(referenceModuleRecord, moduleName, &moduleRecordBase);
- END_TRANSLATE_EXCEPTION_TO_HRESULT(hr);
- END_LEAVE_SCRIPT(scriptContext);
- }
- else
- {
- Assert(dwReferencingSourceContext != Js::Constants::NoHostSourceContext);
- BEGIN_LEAVE_SCRIPT(scriptContext);
- BEGIN_TRANSLATE_TO_HRESULT(static_cast<ExceptionType>(ExceptionType_OutOfMemory | ExceptionType_StackOverflow));
- hr = scriptContext->GetHostScriptContext()->FetchImportedModuleFromScript(dwReferencingSourceContext, moduleName, &moduleRecordBase);
- END_TRANSLATE_EXCEPTION_TO_HRESULT(hr);
- END_LEAVE_SCRIPT(scriptContext);
- }
- if (FAILED(hr))
- {
- // We cannot just use the buffer in the specifier string - need to make a copy here.
- size_t length = wcslen(moduleName);
- char16* allocatedString = RecyclerNewArrayLeaf(scriptContext->GetRecycler(), char16, length + 1);
- wmemcpy_s(allocatedString, length + 1, moduleName, length);
- allocatedString[length] = _u('\0');
- Js::JavascriptError *error = scriptContext->GetLibrary()->CreateURIError();
- JavascriptError::SetErrorMessageProperties(error, hr, allocatedString, scriptContext);
- return SourceTextModuleRecord::ResolveOrRejectDynamicImportPromise(false, error, scriptContext);
- }
- moduleRecord = SourceTextModuleRecord::FromHost(moduleRecordBase);
- if (moduleRecord->GetErrorObject() != nullptr)
- {
- return SourceTextModuleRecord::ResolveOrRejectDynamicImportPromise(false, moduleRecord->GetErrorObject(), scriptContext, moduleRecord);
- }
- else if (moduleRecord->WasEvaluated())
- {
- return SourceTextModuleRecord::ResolveOrRejectDynamicImportPromise(true, moduleRecord->GetNamespace(), scriptContext, moduleRecord);
- }
- return moduleRecord->PostProcessDynamicModuleImport();
- JIT_HELPER_END(ImportCall);
- }
- Var JavascriptOperators::OP_ResumeYield(ResumeYieldData* yieldData, RecyclableObject* iterator)
- {
- JIT_HELPER_REENTRANT_HEADER(ResumeYield);
- bool isNext = yieldData->exceptionObj == nullptr;
- bool isThrow = !isNext && !yieldData->exceptionObj->IsGeneratorReturnException();
- if (iterator != nullptr) // yield*
- {
- ScriptContext* scriptContext = iterator->GetScriptContext();
- PropertyId propertyId = isNext ? PropertyIds::next : isThrow ? PropertyIds::throw_ : PropertyIds::return_;
- Var prop = JavascriptOperators::GetProperty(iterator, propertyId, scriptContext);
- if (!isNext && JavascriptOperators::IsUndefinedOrNull(prop))
- {
- if (isThrow)
- {
- // 5.b.iii.2
- // NOTE: If iterator does not have a throw method, this throw is going to terminate the yield* loop.
- // But first we need to give iterator a chance to clean up.
- prop = JavascriptOperators::GetProperty(iterator, PropertyIds::return_, scriptContext);
- if (!JavascriptOperators::IsUndefinedOrNull(prop))
- {
- if (!JavascriptConversion::IsCallable(prop))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_NeedFunction, _u("return"));
- }
- Var result = nullptr;
- RecyclableObject* method = RecyclableObject::FromVar(prop);
- BEGIN_SAFE_REENTRANT_CALL(scriptContext->GetThreadContext())
- {
- Var args[] = { iterator, yieldData->data };
- CallInfo callInfo(CallFlags_Value, _countof(args));
- result = JavascriptFunction::CallFunction<true>(method, method->GetEntryPoint(), Arguments(callInfo, args));
- }
- END_SAFE_REENTRANT_CALL
- if (!JavascriptOperators::IsObject(result))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedObject);
- }
- }
- // 5.b.iii.3
- // NOTE: The next step throws a TypeError to indicate that there was a yield* protocol violation:
- // iterator does not have a throw method.
- JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_NeedFunction, _u("throw"));
- }
- // Do not use ThrowExceptionObject for return() API exceptions since these exceptions are not real exceptions
- JavascriptExceptionOperators::DoThrow(yieldData->exceptionObj, scriptContext);
- }
- if (!JavascriptConversion::IsCallable(prop))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_NeedFunction, isNext ? _u("next") : isThrow ? _u("throw") : _u("return"));
- }
- RecyclableObject* method = RecyclableObject::FromVar(prop);
- Var result = scriptContext->GetThreadContext()->ExecuteImplicitCall(method, Js::ImplicitCall_Accessor, [=]()->Js::Var
- {
- Var args[] = { iterator, yieldData->data };
- CallInfo callInfo(CallFlags_Value, _countof(args));
- return JavascriptFunction::CallFunction<true>(method, method->GetEntryPoint(), Arguments(callInfo, args));
- });
- if (!JavascriptOperators::IsObject(result))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedObject);
- }
- if (isThrow || isNext)
- {
- // 5.b.ii.2
- // NOTE: Exceptions from the inner iterator throw method are propagated.
- // Normal completions from an inner throw method are processed similarly to an inner next.
- return result;
- }
- RecyclableObject* obj = RecyclableObject::FromVar(result);
- Var done = JavascriptOperators::GetProperty(obj, PropertyIds::done, scriptContext);
- if (done == iterator->GetLibrary()->GetTrue())
- {
- Var value = JavascriptOperators::GetProperty(obj, PropertyIds::value, scriptContext);
- yieldData->exceptionObj->SetThrownObject(value);
- // Do not use ThrowExceptionObject for return() API exceptions since these exceptions are not real exceptions
- JavascriptExceptionOperators::DoThrow(yieldData->exceptionObj, scriptContext);
- }
- return result;
- }
- // CONSIDER: Fast path this early out return path in JITed code before helper call to avoid the helper call overhead in the common case e.g. next() calls.
- if (isNext)
- {
- return yieldData->data;
- }
- if (isThrow)
- {
- // Use ThrowExceptionObject() to get debugger support for breaking on throw
- JavascriptExceptionOperators::ThrowExceptionObject(yieldData->exceptionObj, yieldData->exceptionObj->GetScriptContext(), true);
- }
- // CONSIDER: Using an exception to carry the return value and force finally code to execute is a bit of a janky
- // solution since we have to override the value here in the case of yield* expressions. It works but is there
- // a more elegant way?
- //
- // Instead what if ResumeYield was a "set Dst then optionally branch" opcode, that could also throw? Then we could
- // avoid using a special exception entirely with byte code something like this:
- //
- // ;; Ry is the yieldData
- //
- // ResumeYield Rx Ry $returnPathLabel
- // ... code like normal
- // $returnPathLabel:
- // Ld_A R0 Rx
- // Br $exitFinallyAndReturn
- //
- // This would probably give better performance for the common case of calling next() on generators since we wouldn't
- // have to wrap the call to the generator code in a try catch.
- // Do not use ThrowExceptionObject for return() API exceptions since these exceptions are not real exceptions
- JavascriptExceptionOperators::DoThrow(yieldData->exceptionObj, yieldData->exceptionObj->GetScriptContext());
- JIT_HELPER_END(ResumeYield);
- }
- Js::Var
- JavascriptOperators::BoxStackInstance(Js::Var instance, ScriptContext * scriptContext, bool allowStackFunction, bool deepCopy)
- {
- if (!ThreadContext::IsOnStack(instance) || (allowStackFunction && !TaggedNumber::Is(instance) && (*(int*)instance & 1)))
- {
- return instance;
- }
- TypeId typeId = JavascriptOperators::GetTypeId(instance);
- switch (typeId)
- {
- case Js::TypeIds_Number:
- #if !FLOATVAR
- return JavascriptNumber::BoxStackInstance(instance, scriptContext);
- #endif
- // fall-through
- case Js::TypeIds_Integer:
- return instance;
- case Js::TypeIds_RegEx:
- return JavascriptRegExp::BoxStackInstance(JavascriptRegExp::FromVar(instance), deepCopy);
- case Js::TypeIds_Object:
- return DynamicObject::BoxStackInstance(DynamicObject::FromVar(instance), deepCopy);
- case Js::TypeIds_Array:
- return JavascriptArray::BoxStackInstance(JavascriptArray::UnsafeFromVar(instance), deepCopy);
- case Js::TypeIds_NativeIntArray:
- return JavascriptNativeIntArray::BoxStackInstance(JavascriptNativeIntArray::UnsafeFromVar(instance), deepCopy);
- case Js::TypeIds_NativeFloatArray:
- return JavascriptNativeFloatArray::BoxStackInstance(JavascriptNativeFloatArray::UnsafeFromVar(instance), deepCopy);
- case Js::TypeIds_Function:
- Assert(allowStackFunction);
- // Stack functions are deal with not mar mark them, but by nested function escape analysis
- // in the front end. No need to box here.
- return instance;
- #if ENABLE_COPYONACCESS_ARRAY
- case Js::TypeIds_CopyOnAccessNativeIntArray:
- Assert(false);
- // fall-through
- #endif
- default:
- Assert(false);
- return instance;
- };
- }
- ImplicitCallFlags
- JavascriptOperators::CacheAndClearImplicitBit(ScriptContext* scriptContext)
- {
- ImplicitCallFlags prevImplicitCallFlags = scriptContext->GetThreadContext()->GetImplicitCallFlags();
- scriptContext->GetThreadContext()->ClearImplicitCallFlags();
- return prevImplicitCallFlags;
- }
- ImplicitCallFlags
- JavascriptOperators::CheckAndUpdateFunctionBodyWithImplicitFlag(FunctionBody* functionBody)
- {
- ScriptContext* scriptContext = functionBody->GetScriptContext();
- ImplicitCallFlags currImplicitCallFlags = scriptContext->GetThreadContext()->GetImplicitCallFlags();
- if ((currImplicitCallFlags > ImplicitCall_None))
- {
- functionBody->SetHasOnlyThisStmts(false);
- }
- return currImplicitCallFlags;
- }
- void
- JavascriptOperators::RestoreImplicitFlag(ScriptContext* scriptContext, ImplicitCallFlags prevImplicitCallFlags, ImplicitCallFlags currImplicitCallFlags)
- {
- scriptContext->GetThreadContext()->SetImplicitCallFlags((ImplicitCallFlags)(prevImplicitCallFlags | currImplicitCallFlags));
- }
- FunctionProxy*
- JavascriptOperators::GetDeferredDeserializedFunctionProxy(JavascriptFunction* func)
- {
- FunctionProxy* proxy = func->GetFunctionProxy();
- Assert(proxy->GetFunctionInfo()->GetFunctionProxy() != proxy);
- return proxy;
- }
- template <>
- Js::Var JavascriptOperators::GetElementAtIndex(Js::JavascriptArray* arrayObject, UINT index, Js::ScriptContext* scriptContext)
- {
- Js::Var result;
- if (Js::JavascriptOperators::OP_GetElementI_ArrayFastPath(arrayObject, index, &result, scriptContext))
- {
- return result;
- }
- return scriptContext->GetMissingItemResult();
- }
- template<>
- Js::Var JavascriptOperators::GetElementAtIndex(Js::JavascriptNativeIntArray* arrayObject, UINT index, Js::ScriptContext* scriptContext)
- {
- Js::Var result;
- if (Js::JavascriptOperators::OP_GetElementI_ArrayFastPath(arrayObject, index, &result, scriptContext))
- {
- return result;
- }
- return scriptContext->GetMissingItemResult();
- }
- template<>
- Js::Var JavascriptOperators::GetElementAtIndex(Js::JavascriptNativeFloatArray* arrayObject, UINT index, Js::ScriptContext* scriptContext)
- {
- Js::Var result;
- if (Js::JavascriptOperators::OP_GetElementI_ArrayFastPath(arrayObject, index, &result, scriptContext))
- {
- return result;
- }
- return scriptContext->GetMissingItemResult();
- }
- template<>
- Js::Var JavascriptOperators::GetElementAtIndex(Js::Var* arrayObject, UINT index, Js::ScriptContext* scriptContext)
- {
- return Js::JavascriptOperators::OP_GetElementI_Int32(*arrayObject, index, scriptContext);
- }
- template<typename T>
- void JavascriptOperators::ObjectToNativeArray(T* arrayObject,
- JsNativeValueType valueType,
- __in UINT length,
- __in UINT elementSize,
- __out_bcount(length*elementSize) byte* buffer,
- Js::ScriptContext* scriptContext)
- {
- Var element;
- uint64 allocSize = UInt32Math::Mul(length, elementSize);
- // TODO:further fast path the call for things like IntArray convert to int, floatarray convert to float etc.
- // such that we don't need boxing.
- switch (valueType)
- {
- case JsInt8Type:
- AnalysisAssert(elementSize == sizeof(int8));
- for (UINT i = 0; i < length; i++)
- {
- element = GetElementAtIndex(arrayObject, i, scriptContext);
- AnalysisAssert((i + 1) * sizeof(int8) <= allocSize);
- #pragma prefast(suppress:22102)
- ((int8*)buffer)[i] = Js::JavascriptConversion::ToInt8(element, scriptContext);
- }
- break;
- case JsUint8Type:
- AnalysisAssert(elementSize == sizeof(uint8));
- for (UINT i = 0; i < length; i++)
- {
- element = GetElementAtIndex(arrayObject, i, scriptContext);
- AnalysisAssert((i + 1) * sizeof(uint8) <= allocSize);
- ((uint8*)buffer)[i] = Js::JavascriptConversion::ToUInt8(element, scriptContext);
- }
- break;
- case JsInt16Type:
- AnalysisAssert(elementSize == sizeof(int16));
- for (UINT i = 0; i < length; i++)
- {
- element = GetElementAtIndex(arrayObject, i, scriptContext);
- AnalysisAssert((i + 1) * sizeof(int16) <= allocSize);
- ((int16*)buffer)[i] = Js::JavascriptConversion::ToInt16(element, scriptContext);
- }
- break;
- case JsUint16Type:
- AnalysisAssert(elementSize == sizeof(uint16));
- for (UINT i = 0; i < length; i++)
- {
- element = GetElementAtIndex(arrayObject, i, scriptContext);
- AnalysisAssert((i + 1) * sizeof(uint16) <= allocSize);
- ((uint16*)buffer)[i] = Js::JavascriptConversion::ToUInt16(element, scriptContext);
- }
- break;
- case JsInt32Type:
- AnalysisAssert(elementSize == sizeof(int32));
- for (UINT i = 0; i < length; i++)
- {
- element = GetElementAtIndex(arrayObject, i, scriptContext);
- AnalysisAssert((i + 1) * sizeof(int32) <= allocSize);
- ((int32*)buffer)[i] = Js::JavascriptConversion::ToInt32(element, scriptContext);
- }
- break;
- case JsUint32Type:
- AnalysisAssert(elementSize == sizeof(uint32));
- for (UINT i = 0; i < length; i++)
- {
- element = GetElementAtIndex(arrayObject, i, scriptContext);
- AnalysisAssert((i + 1) * sizeof(uint32) <= allocSize);
- ((uint32*)buffer)[i] = Js::JavascriptConversion::ToUInt32(element, scriptContext);
- }
- break;
- case JsInt64Type:
- AnalysisAssert(elementSize == sizeof(int64));
- for (UINT i = 0; i < length; i++)
- {
- element = GetElementAtIndex(arrayObject, i, scriptContext);
- AnalysisAssert((i + 1) * sizeof(int64) <= allocSize);
- ((int64*)buffer)[i] = Js::JavascriptConversion::ToInt64(element, scriptContext);
- }
- break;
- case JsUint64Type:
- AnalysisAssert(elementSize == sizeof(uint64));
- for (UINT i = 0; i < length; i++)
- {
- element = GetElementAtIndex(arrayObject, i, scriptContext);
- AnalysisAssert((i + 1) * sizeof(uint64) <= allocSize);
- ((uint64*)buffer)[i] = Js::JavascriptConversion::ToUInt64(element, scriptContext);
- }
- break;
- case JsFloatType:
- AnalysisAssert(elementSize == sizeof(float));
- for (UINT i = 0; i < length; i++)
- {
- element = GetElementAtIndex(arrayObject, i, scriptContext);
- AnalysisAssert((i + 1) * sizeof(float) <= allocSize);
- ((float*)buffer)[i] = Js::JavascriptConversion::ToFloat(element, scriptContext);
- }
- break;
- case JsDoubleType:
- AnalysisAssert(elementSize == sizeof(double));
- for (UINT i = 0; i < length; i++)
- {
- element = GetElementAtIndex(arrayObject, i, scriptContext);
- AnalysisAssert((i + 1) * sizeof(double) <= allocSize);
- ((double*)buffer)[i] = Js::JavascriptConversion::ToNumber(element, scriptContext);
- }
- break;
- case JsNativeStringType:
- AnalysisAssert(elementSize == sizeof(JsNativeString));
- for (UINT i = 0; i < length; i++)
- {
- element = GetElementAtIndex(arrayObject, i, scriptContext);
- AnalysisAssert((i + 1) * sizeof(JsNativeString) <= allocSize);
- Js::JavascriptString* string = Js::JavascriptConversion::ToString(element, scriptContext);
- (((JsNativeString*)buffer)[i]).str = string->GetSz();
- (((JsNativeString*)buffer)[i]).length = string->GetLength();
- }
- break;
- default:
- Assert(FALSE);
- }
- }
- void JavascriptOperators::VarToNativeArray(Var arrayObject,
- JsNativeValueType valueType,
- __in UINT length,
- __in UINT elementSize,
- __out_bcount(length*elementSize) byte* buffer,
- Js::ScriptContext* scriptContext)
- {
- Js::DynamicObject* dynamicObject = DynamicObject::FromVar(arrayObject);
- if (dynamicObject->IsCrossSiteObject() || Js::TaggedInt::IsOverflow(length))
- {
- Js::JavascriptOperators::ObjectToNativeArray(&arrayObject, valueType, length, elementSize, buffer, scriptContext);
- }
- else
- {
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(arrayObject);
- #endif
- switch (Js::JavascriptOperators::GetTypeId(arrayObject))
- {
- case TypeIds_Array:
- Js::JavascriptOperators::ObjectToNativeArray(Js::JavascriptArray::UnsafeFromVar(arrayObject), valueType, length, elementSize, buffer, scriptContext);
- break;
- case TypeIds_NativeFloatArray:
- Js::JavascriptOperators::ObjectToNativeArray(Js::JavascriptNativeFloatArray::UnsafeFromVar(arrayObject), valueType, length, elementSize, buffer, scriptContext);
- break;
- case TypeIds_NativeIntArray:
- Js::JavascriptOperators::ObjectToNativeArray(Js::JavascriptNativeIntArray::UnsafeFromVar(arrayObject), valueType, length, elementSize, buffer, scriptContext);
- break;
- // We can have more specialized template if needed.
- default:
- Js::JavascriptOperators::ObjectToNativeArray(&arrayObject, valueType, length, elementSize, buffer, scriptContext);
- }
- }
- }
- // SpeciesConstructor abstract operation as described in ES6.0 Section 7.3.20
- RecyclableObject* JavascriptOperators::SpeciesConstructor(_In_ RecyclableObject* object, _In_ JavascriptFunction* defaultConstructor, _In_ ScriptContext* scriptContext)
- {
- //1.Assert: Type(O) is Object.
- Assert(JavascriptOperators::IsObject(object));
- //2.Let C be Get(O, "constructor").
- //3.ReturnIfAbrupt(C).
- Var constructor = JavascriptOperators::GetProperty(object, PropertyIds::constructor, scriptContext);
- if (scriptContext->GetConfig()->IsES6SpeciesEnabled())
- {
- //4.If C is undefined, return defaultConstructor.
- if (JavascriptOperators::IsUndefinedObject(constructor))
- {
- return defaultConstructor;
- }
- //5.If Type(C) is not Object, throw a TypeError exception.
- if (!JavascriptOperators::IsObject(constructor))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedObject, _u("[constructor]"));
- }
- //6.Let S be Get(C, @@species).
- //7.ReturnIfAbrupt(S).
- Var species = nullptr;
- if (!JavascriptOperators::GetProperty(RecyclableObject::FromVar(constructor),
- PropertyIds::_symbolSpecies, &species, scriptContext)
- || JavascriptOperators::IsUndefinedOrNull(species))
- {
- //8.If S is either undefined or null, return defaultConstructor.
- return defaultConstructor;
- }
- constructor = species;
- }
- //9.If IsConstructor(S) is true, return S.
- RecyclableObject* constructorObj = JavascriptOperators::TryFromVar<RecyclableObject>(constructor);
- if (constructorObj && JavascriptOperators::IsConstructor(constructorObj))
- {
- return constructorObj;
- }
- //10.Throw a TypeError exception.
- JavascriptError::ThrowTypeError(scriptContext, JSERR_NotAConstructor, _u("constructor[Symbol.species]"));
- }
- BOOL JavascriptOperators::GreaterEqual(Var aLeft, Var aRight, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_GreaterEqual);
- if (TaggedInt::Is(aLeft))
- {
- if (TaggedInt::Is(aRight))
- {
- // Works whether it is TaggedInt31 or TaggedInt32
- return ::Math::PointerCastToIntegralTruncate<int>(aLeft) >= ::Math::PointerCastToIntegralTruncate<int>(aRight);
- }
- if (JavascriptNumber::Is_NoTaggedIntCheck(aRight))
- {
- return TaggedInt::ToDouble(aLeft) >= JavascriptNumber::GetValue(aRight);
- }
- }
- else if (TaggedInt::Is(aRight))
- {
- if (JavascriptNumber::Is_NoTaggedIntCheck(aLeft))
- {
- return JavascriptNumber::GetValue(aLeft) >= TaggedInt::ToDouble(aRight);
- }
- }
- else
- {
- if (JavascriptNumber::Is_NoTaggedIntCheck(aLeft) && JavascriptNumber::Is_NoTaggedIntCheck(aRight))
- {
- return JavascriptNumber::GetValue(aLeft) >= JavascriptNumber::GetValue(aRight);
- }
- }
- return !RelationalComparisonHelper(aLeft, aRight, scriptContext, true, true);
- JIT_HELPER_END(Op_GreaterEqual);
- }
- BOOL JavascriptOperators::LessEqual(Var aLeft, Var aRight, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_LessEqual);
- if (TaggedInt::Is(aLeft))
- {
- if (TaggedInt::Is(aRight))
- {
- // Works whether it is TaggedInt31 or TaggedInt32
- return ::Math::PointerCastToIntegralTruncate<int>(aLeft) <= ::Math::PointerCastToIntegralTruncate<int>(aRight);
- }
- if (JavascriptNumber::Is_NoTaggedIntCheck(aRight))
- {
- return TaggedInt::ToDouble(aLeft) <= JavascriptNumber::GetValue(aRight);
- }
- }
- else if (TaggedInt::Is(aRight))
- {
- if (JavascriptNumber::Is_NoTaggedIntCheck(aLeft))
- {
- return JavascriptNumber::GetValue(aLeft) <= TaggedInt::ToDouble(aRight);
- }
- }
- else
- {
- if (JavascriptNumber::Is_NoTaggedIntCheck(aLeft) && JavascriptNumber::Is_NoTaggedIntCheck(aRight))
- {
- return JavascriptNumber::GetValue(aLeft) <= JavascriptNumber::GetValue(aRight);
- }
- }
- return !RelationalComparisonHelper(aRight, aLeft, scriptContext, false, true);
- JIT_HELPER_END(Op_LessEqual);
- }
- BOOL JavascriptOperators::NotEqual(Var aLeft, Var aRight, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_NotEqual);
- JIT_HELPER_SAME_ATTRIBUTES(Op_NotEqual, Op_Equal);
- //
- // TODO: Change to use Abstract Equality Comparison Algorithm (ES3.0: S11.9.3):
- // - Evaluate left, then right, operands to preserve correct evaluation order.
- // - Call algorithm, potentially reversing arguments.
- //
- return !Equal(aLeft, aRight, scriptContext);
- JIT_HELPER_END(Op_NotEqual);
- }
- // NotStrictEqual() returns whether the two vars have strict equality, as
- // described in (ES3.0: S11.9.5, S11.9.6).
- BOOL JavascriptOperators::NotStrictEqual(Var aLeft, Var aRight, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_NotStrictEqual);
- JIT_HELPER_SAME_ATTRIBUTES(Op_NotStrictEqual, Op_StrictEqual);
- return !StrictEqual(aLeft, aRight, scriptContext);
- JIT_HELPER_END(Op_NotStrictEqual);
- }
- bool JavascriptOperators::CheckIfObjectAndPrototypeChainHasOnlyWritableDataProperties(_In_ RecyclableObject* object)
- {
- return object->GetLibrary()->GetTypesWithOnlyWritablePropertyProtoChainCache()->Check(object);
- }
- bool JavascriptOperators::CheckIfPrototypeChainHasOnlyWritableDataProperties(_In_ RecyclableObject* prototype)
- {
- return prototype->GetLibrary()->GetTypesWithOnlyWritablePropertyProtoChainCache()->CheckProtoChain(prototype);
- }
- bool JavascriptOperators::CheckIfObjectAndProtoChainHasNoSpecialProperties(_In_ RecyclableObject* object)
- {
- return object->GetLibrary()->GetTypesWithNoSpecialPropertyProtoChainCache()->Check(object);
- }
- // Checks to see if the specified object (which should be a prototype object)
- // contains a proxy anywhere in the prototype chain.
- bool JavascriptOperators::CheckIfPrototypeChainContainsProxyObject(RecyclableObject* prototype)
- {
- if (prototype == nullptr)
- {
- return false;
- }
- Assert(JavascriptOperators::IsObjectOrNull(prototype));
- while (prototype->GetTypeId() != TypeIds_Null)
- {
- if (prototype->GetTypeId() == TypeIds_Proxy)
- {
- return true;
- }
- prototype = prototype->GetPrototype();
- }
- return false;
- }
- BOOL JavascriptOperators::Equal(Var aLeft, Var aRight, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_Equal);
- JIT_HELPER_SAME_ATTRIBUTES(Op_Equal, Op_Equal_Full);
- if (aLeft == aRight)
- {
- if (TaggedInt::Is(aLeft) || JavascriptObject::Is(aLeft))
- {
- return true;
- }
- else
- {
- return Equal_Full(aLeft, aRight, scriptContext);
- }
- }
- if (JavascriptString::Is(aLeft) && JavascriptString::Is(aRight))
- {
- JavascriptString* left = (JavascriptString*)aLeft;
- JavascriptString* right = (JavascriptString*)aRight;
- if (left->GetLength() == right->GetLength())
- {
- if (left->UnsafeGetBuffer() != NULL && right->UnsafeGetBuffer() != NULL)
- {
- if (left->GetLength() == 1)
- {
- return left->UnsafeGetBuffer()[0] == right->UnsafeGetBuffer()[0];
- }
- return memcmp(left->UnsafeGetBuffer(), right->UnsafeGetBuffer(), left->GetLength() * sizeof(left->UnsafeGetBuffer()[0])) == 0;
- }
- // fall through to Equal_Full
- }
- else
- {
- return false;
- }
- }
- return Equal_Full(aLeft, aRight, scriptContext);
- JIT_HELPER_END(Op_Equal);
- }
- BOOL JavascriptOperators::Greater(Var aLeft, Var aRight, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_Greater);
- if (TaggedInt::Is(aLeft))
- {
- if (TaggedInt::Is(aRight))
- {
- // Works whether it is TaggedInt31 or TaggedInt32
- return ::Math::PointerCastToIntegralTruncate<int>(aLeft) > ::Math::PointerCastToIntegralTruncate<int>(aRight);
- }
- if (JavascriptNumber::Is_NoTaggedIntCheck(aRight))
- {
- return TaggedInt::ToDouble(aLeft) > JavascriptNumber::GetValue(aRight);
- }
- }
- else if (TaggedInt::Is(aRight))
- {
- if (JavascriptNumber::Is_NoTaggedIntCheck(aLeft))
- {
- return JavascriptNumber::GetValue(aLeft) > TaggedInt::ToDouble(aRight);
- }
- }
- else
- {
- if (JavascriptNumber::Is_NoTaggedIntCheck(aLeft) && JavascriptNumber::Is_NoTaggedIntCheck(aRight))
- {
- return JavascriptNumber::GetValue(aLeft) > JavascriptNumber::GetValue(aRight);
- }
- }
- return Greater_Full(aLeft, aRight, scriptContext);
- JIT_HELPER_END(Op_Greater);
- }
- BOOL JavascriptOperators::Less(Var aLeft, Var aRight, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_Less);
- if (TaggedInt::Is(aLeft))
- {
- if (TaggedInt::Is(aRight))
- {
- // Works whether it is TaggedInt31 or TaggedInt32
- return ::Math::PointerCastToIntegralTruncate<int>(aLeft) < ::Math::PointerCastToIntegralTruncate<int>(aRight);
- }
- if (JavascriptNumber::Is_NoTaggedIntCheck(aRight))
- {
- return TaggedInt::ToDouble(aLeft) < JavascriptNumber::GetValue(aRight);
- }
- }
- else if (TaggedInt::Is(aRight))
- {
- if (JavascriptNumber::Is_NoTaggedIntCheck(aLeft))
- {
- return JavascriptNumber::GetValue(aLeft) < TaggedInt::ToDouble(aRight);
- }
- }
- else
- {
- if (JavascriptNumber::Is_NoTaggedIntCheck(aLeft) && JavascriptNumber::Is_NoTaggedIntCheck(aRight))
- {
- return JavascriptNumber::GetValue(aLeft) < JavascriptNumber::GetValue(aRight);
- }
- }
- return Less_Full(aLeft, aRight, scriptContext);
- JIT_HELPER_END(Op_Less);
- }
- RecyclableObject* JavascriptOperators::ToObject(Var aRight, ScriptContext* scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_HEADER(Op_ConvObject, reentrancylock, scriptContext->GetThreadContext());
- RecyclableObject* object = nullptr;
- if (FALSE == JavascriptConversion::ToObject(aRight, scriptContext, &object))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedObject /* TODO-ERROR: get arg name - aValue */);
- }
- return object;
- JIT_HELPER_END(Op_ConvObject);
- }
- Var JavascriptOperators::ToWithObject(Var aRight, ScriptContext* scriptContext)
- {
- JIT_HELPER_NOT_REENTRANT_HEADER(Op_NewWithObject, reentrancylock, scriptContext->GetThreadContext());
- RecyclableObject* object = RecyclableObject::FromVar(aRight);
- WithScopeObject* withWrapper = RecyclerNew(scriptContext->GetRecycler(), WithScopeObject, object, scriptContext->GetLibrary()->GetWithType());
- return withWrapper;
- JIT_HELPER_END(Op_NewWithObject);
- }
- Var JavascriptOperators::ToNumber(Var aRight, ScriptContext* scriptContext)
- {
- JIT_HELPER_REENTRANT_HEADER(Op_ConvNumber_Full);
- if (TaggedInt::Is(aRight) || (JavascriptNumber::Is_NoTaggedIntCheck(aRight)))
- {
- return aRight;
- }
- return JavascriptNumber::ToVarNoCheck(JavascriptConversion::ToNumber_Full(aRight, scriptContext), scriptContext);
- JIT_HELPER_END(Op_ConvNumber_Full);
- }
- BOOL JavascriptOperators::IsObject(_In_ RecyclableObject* instance)
- {
- return GetTypeId(instance) > TypeIds_LastJavascriptPrimitiveType;
- }
- BOOL JavascriptOperators::IsObject(_In_ Var instance)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(Op_IsObject);
- return GetTypeId(instance) > TypeIds_LastJavascriptPrimitiveType;
- JIT_HELPER_END(Op_IsObject);
- }
- BOOL JavascriptOperators::IsObjectType(TypeId typeId)
- {
- return typeId > TypeIds_LastJavascriptPrimitiveType;
- }
- BOOL JavascriptOperators::IsExposedType(TypeId typeId)
- {
- return typeId <= TypeIds_LastTrueJavascriptObjectType && typeId != TypeIds_HostDispatch;
- }
- BOOL JavascriptOperators::IsObjectOrNull(Var instance)
- {
- TypeId typeId = GetTypeId(instance);
- return IsObjectType(typeId) || typeId == TypeIds_Null;
- }
- BOOL JavascriptOperators::IsUndefined(_In_ RecyclableObject* instance)
- {
- return JavascriptOperators::GetTypeId(instance) == TypeIds_Undefined;
- }
- BOOL JavascriptOperators::IsUndefined(Var instance)
- {
- return JavascriptOperators::GetTypeId(instance) == TypeIds_Undefined;
- }
- BOOL JavascriptOperators::IsUndefinedOrNullType(TypeId typeId)
- {
- return typeId <= TypeIds_UndefinedOrNull;
- }
- BOOL JavascriptOperators::IsUndefinedOrNull(Var instance)
- {
- return IsUndefinedOrNullType(JavascriptOperators::GetTypeId(instance));
- }
- BOOL JavascriptOperators::IsUndefinedOrNull(RecyclableObject* instance)
- {
- return JavascriptOperators::IsUndefinedOrNullType(instance->GetTypeId());
- }
- BOOL JavascriptOperators::IsUndefinedOrNull(Var instance, ScriptContext* scriptContext)
- {
- JavascriptLibrary* library = scriptContext->GetLibrary();
- return IsUndefinedObject(instance, library) || IsNull(instance, library);
- }
- BOOL JavascriptOperators::IsUndefinedOrNull(Var instance, JavascriptLibrary* library)
- {
- return IsUndefinedObject(instance, library) || IsNull(instance, library);
- }
- BOOL JavascriptOperators::IsNull(Var instance)
- {
- return JavascriptOperators::GetTypeId(instance) == TypeIds_Null;
- }
- BOOL JavascriptOperators::IsNull(Var instance, ScriptContext* scriptContext)
- {
- return JavascriptOperators::IsNull(instance, scriptContext->GetLibrary());
- }
- BOOL JavascriptOperators::IsNull(Var instance, JavascriptLibrary* library)
- {
- Assert(!RecyclableObject::Is(instance) ? TRUE : ((RecyclableObject*)instance)->GetScriptContext()->GetLibrary() == library );
- return library->GetNull() == instance;
- }
- BOOL JavascriptOperators::IsNull(RecyclableObject* instance)
- {
- return instance->GetType()->GetTypeId() == TypeIds_Null;
- }
- BOOL JavascriptOperators::IsSpecialObjectType(TypeId typeId)
- {
- return typeId > TypeIds_LastTrueJavascriptObjectType;
- }
- BOOL JavascriptOperators::IsUndefinedObject(Var instance)
- {
- return JavascriptOperators::GetTypeId(instance) == TypeIds_Undefined;
- }
- BOOL JavascriptOperators::IsUndefinedObject(RecyclableObject* instance)
- {
- return instance->GetType()->GetTypeId() == TypeIds_Undefined;
- }
- BOOL JavascriptOperators::IsUndefinedObject(Var instance, RecyclableObject *libraryUndefined)
- {
- Assert(JavascriptOperators::IsUndefinedObject(libraryUndefined));
- AssertMsg((instance == libraryUndefined)
- == JavascriptOperators::IsUndefinedObject(instance), "Wrong ScriptContext?");
- return instance == libraryUndefined;
- }
- BOOL JavascriptOperators::IsUndefinedObject(Var instance, ScriptContext *scriptContext)
- {
- return JavascriptOperators::IsUndefinedObject(instance, scriptContext->GetLibrary());
- }
- BOOL JavascriptOperators::IsUndefinedObject(Var instance, JavascriptLibrary* library)
- {
- Assert(!RecyclableObject::Is(instance) ? TRUE : ((RecyclableObject*)instance)->GetScriptContext()->GetLibrary() == library );
- return JavascriptOperators::IsUndefinedObject(instance, library->GetUndefined());
- }
- BOOL JavascriptOperators::IsAnyNumberValue(Var instance)
- {
- TypeId typeId = GetTypeId(instance);
- return TypeIds_FirstNumberType <= typeId && typeId <= TypeIds_LastNumberType;
- }
- // GetIterator as described in ES6.0 (draft 22) Section 7.4.1
- RecyclableObject* JavascriptOperators::GetIterator(Var iterable, ScriptContext* scriptContext, bool optional)
- {
- RecyclableObject* iterableObj = JavascriptOperators::ToObject(iterable, scriptContext);
- return JavascriptOperators::GetIterator(iterableObj, scriptContext, optional);
- }
- RecyclableObject* JavascriptOperators::GetIteratorFunction(Var iterable, ScriptContext* scriptContext, bool optional)
- {
- RecyclableObject* iterableObj = JavascriptOperators::ToObject(iterable, scriptContext);
- return JavascriptOperators::GetIteratorFunction(iterableObj, scriptContext, optional);
- }
- RecyclableObject* JavascriptOperators::GetIteratorFunction(RecyclableObject* instance, ScriptContext * scriptContext, bool optional)
- {
- Var func = JavascriptOperators::GetPropertyNoCache(instance, PropertyIds::_symbolIterator, scriptContext);
- if (optional && JavascriptOperators::IsUndefinedOrNull(func))
- {
- return nullptr;
- }
- if (!JavascriptConversion::IsCallable(func))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_NeedFunction);
- }
- RecyclableObject* function = RecyclableObject::FromVar(func);
- return function;
- }
- RecyclableObject* JavascriptOperators::GetIterator(RecyclableObject* instance, ScriptContext * scriptContext, bool optional)
- {
- RecyclableObject* function = GetIteratorFunction(instance, scriptContext, optional);
- if (function == nullptr)
- {
- Assert(optional);
- return nullptr;
- }
- Var iterator = scriptContext->GetThreadContext()->ExecuteImplicitCall(function, Js::ImplicitCall_Accessor, [=]()->Js::Var
- {
- return CALL_FUNCTION(scriptContext->GetThreadContext(), function, CallInfo(Js::CallFlags_Value, 1), instance);
- });
- if (!JavascriptOperators::IsObject(iterator))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedObject);
- }
- return RecyclableObject::FromVar(iterator);
- }
- void JavascriptOperators::IteratorClose(RecyclableObject* iterator, ScriptContext* scriptContext)
- {
- try
- {
- Var func = JavascriptOperators::GetProperty(iterator, PropertyIds::return_, scriptContext);
- if (JavascriptConversion::IsCallable(func))
- {
- RecyclableObject* callable = RecyclableObject::FromVar(func);
- scriptContext->GetThreadContext()->ExecuteImplicitCall(callable, ImplicitCall_Accessor, [=]()->Var
- {
- Js::Var args[] = { iterator };
- Js::CallInfo callInfo(Js::CallFlags_Value, _countof(args));
- return JavascriptFunction::CallFunction<true>(callable, callable->GetEntryPoint(), Js::Arguments(callInfo, args));
- });
- }
- }
- catch (const JavascriptException& err)
- {
- err.GetAndClear(); // discard exception object
- // We have arrived in this function due to AbruptCompletion (which is an exception), so we don't need to
- // propagate the exception of calling return function
- }
- }
- // IteratorNext as described in ES6.0 (draft 22) Section 7.4.2
- RecyclableObject* JavascriptOperators::IteratorNext(RecyclableObject* iterator, ScriptContext* scriptContext, Var value)
- {
- Var func = JavascriptOperators::GetPropertyNoCache(iterator, PropertyIds::next, scriptContext);
- ThreadContext *threadContext = scriptContext->GetThreadContext();
- if (!JavascriptConversion::IsCallable(func))
- {
- if (!threadContext->RecordImplicitException())
- {
- return scriptContext->GetLibrary()->GetUndefined();
- }
- JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedFunction);
- }
- RecyclableObject* callable = RecyclableObject::FromVar(func);
- Var result = threadContext->ExecuteImplicitCall(callable, ImplicitCall_Accessor, [=]() -> Var
- {
- Js::Var args[] = { iterator, value };
- Js::CallInfo callInfo(Js::CallFlags_Value, _countof(args) + (value == nullptr ? -1 : 0));
- return JavascriptFunction::CallFunction<true>(callable, callable->GetEntryPoint(), Arguments(callInfo, args));
- });
- if (!JavascriptOperators::IsObject(result))
- {
- if (!threadContext->RecordImplicitException())
- {
- return scriptContext->GetLibrary()->GetUndefined();
- }
- JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedObject);
- }
- return RecyclableObject::FromVar(result);
- }
- // IteratorComplete as described in ES6.0 (draft 22) Section 7.4.3
- bool JavascriptOperators::IteratorComplete(RecyclableObject* iterResult, ScriptContext* scriptContext)
- {
- Var done = JavascriptOperators::GetPropertyNoCache(iterResult, Js::PropertyIds::done, scriptContext);
- return JavascriptConversion::ToBool(done, scriptContext);
- }
- // IteratorValue as described in ES6.0 (draft 22) Section 7.4.4
- Var JavascriptOperators::IteratorValue(RecyclableObject* iterResult, ScriptContext* scriptContext)
- {
- return JavascriptOperators::GetPropertyNoCache(iterResult, Js::PropertyIds::value, scriptContext);
- }
- // IteratorStep as described in ES6.0 (draft 22) Section 7.4.5
- bool JavascriptOperators::IteratorStep(RecyclableObject* iterator, ScriptContext* scriptContext, RecyclableObject** result)
- {
- Assert(result);
- *result = JavascriptOperators::IteratorNext(iterator, scriptContext);
- return !JavascriptOperators::IteratorComplete(*result, scriptContext);
- }
- bool JavascriptOperators::IteratorStepAndValue(RecyclableObject* iterator, ScriptContext* scriptContext, Var* resultValue)
- {
- // CONSIDER: Fast-pathing for iterators that are built-ins?
- RecyclableObject* result = JavascriptOperators::IteratorNext(iterator, scriptContext);
- if (!JavascriptOperators::IteratorComplete(result, scriptContext))
- {
- *resultValue = JavascriptOperators::IteratorValue(result, scriptContext);
- return true;
- }
- return false;
- }
- RecyclableObject* JavascriptOperators::CreateFromConstructor(RecyclableObject* constructor, ScriptContext* scriptContext)
- {
- // Create a regular object and set the internal proto from the constructor
- return JavascriptOperators::OrdinaryCreateFromConstructor(constructor, scriptContext->GetLibrary()->CreateObject(), nullptr, scriptContext);
- }
- RecyclableObject* JavascriptOperators::OrdinaryCreateFromConstructor(RecyclableObject* constructor, RecyclableObject* obj, DynamicObject* intrinsicProto, ScriptContext* scriptContext)
- {
- // There isn't a good way for us to add internal properties to objects in Chakra.
- // Thus, caller should take care to create obj with the correct internal properties.
- Var proto = JavascriptOperators::GetPropertyNoCache(constructor, Js::PropertyIds::prototype, scriptContext);
- // If constructor.prototype is an object, we should use that as the [[Prototype]] for our obj.
- // Else, we set the [[Prototype]] internal slot of obj to %intrinsicProto% - which should be the default.
- if (JavascriptOperators::IsObjectType(JavascriptOperators::GetTypeId(proto)) &&
- DynamicObject::FromVar(proto) != intrinsicProto)
- {
- JavascriptObject::ChangePrototype(obj, RecyclableObject::FromVar(proto), /*validate*/true, scriptContext);
- }
- return obj;
- }
- Var JavascriptOperators::GetProperty(RecyclableObject* instance, PropertyId propertyId, ScriptContext* requestContext, PropertyValueInfo* info)
- {
- return JavascriptOperators::GetProperty(instance, instance, propertyId, requestContext, info);
- }
- BOOL JavascriptOperators::GetProperty(RecyclableObject* instance, PropertyId propertyId, Var* value, ScriptContext* requestContext, PropertyValueInfo* info)
- {
- return JavascriptOperators::GetProperty(instance, instance, propertyId, value, requestContext, info);
- }
- Var JavascriptOperators::GetProperty(Var instance, RecyclableObject* propertyObject, PropertyId propertyId, ScriptContext* requestContext, PropertyValueInfo* info)
- {
- Var value;
- if (JavascriptOperators::GetProperty(instance, propertyObject, propertyId, &value, requestContext, info))
- {
- return value;
- }
- return requestContext->GetMissingPropertyResult();
- }
- Var JavascriptOperators::GetPropertyNoCache(RecyclableObject* instance, PropertyId propertyId, ScriptContext* requestContext)
- {
- return JavascriptOperators::GetPropertyNoCache(instance, instance, propertyId, requestContext);
- }
- Var JavascriptOperators::GetPropertyNoCache(Var instance, RecyclableObject* propertyObject, PropertyId propertyId, ScriptContext* requestContext)
- {
- Var value;
- JavascriptOperators::GetProperty_InternalSimple(instance, propertyObject, propertyId, &value, requestContext);
- return value;
- }
- BOOL JavascriptOperators::GetPropertyNoCache(RecyclableObject* instance, PropertyId propertyId, Var* value, ScriptContext* requestContext)
- {
- return JavascriptOperators::GetPropertyNoCache(instance, instance, propertyId, value, requestContext);
- }
- BOOL JavascriptOperators::GetPropertyNoCache(Var instance, RecyclableObject* propertyObject, PropertyId propertyId, Var* value, ScriptContext* requestContext)
- {
- return JavascriptOperators::GetProperty_InternalSimple(instance, propertyObject, propertyId, value, requestContext);
- }
- Var JavascriptOperators::GetRootProperty(RecyclableObject* instance, PropertyId propertyId, ScriptContext* requestContext, PropertyValueInfo* info)
- {
- Var value;
- if (JavascriptOperators::GetRootProperty(instance, propertyId, &value, requestContext, info))
- {
- return value;
- }
- return requestContext->GetMissingPropertyResult();
- }
- BOOL JavascriptOperators::GetPropertyReference(RecyclableObject *instance, PropertyId propertyId, Var* value, ScriptContext* requestContext, PropertyValueInfo* info)
- {
- return JavascriptOperators::GetPropertyReference(instance, instance, propertyId, value, requestContext, info);
- }
- Var JavascriptOperators::GetItem(RecyclableObject* instance, uint32 index, ScriptContext* requestContext)
- {
- Var value;
- if (GetItem(instance, index, &value, requestContext))
- {
- return value;
- }
- return requestContext->GetMissingItemResult();
- }
- Var JavascriptOperators::GetItem(RecyclableObject* instance, uint64 index, ScriptContext* requestContext)
- {
- Var value;
- if (GetItem(instance, index, &value, requestContext))
- {
- return value;
- }
- return requestContext->GetMissingItemResult();
- }
- BOOL JavascriptOperators::GetItem(RecyclableObject* instance, uint64 index, Var* value, ScriptContext* requestContext)
- {
- if (index < JavascriptArray::InvalidIndex)
- {
- // In case index fits in uint32, we can avoid the (slower) big-index path
- return GetItem(instance, static_cast<uint32>(index), value, requestContext);
- }
- PropertyRecord const * propertyRecord = nullptr;
- JavascriptOperators::GetPropertyIdForInt(index, requestContext, &propertyRecord);
- return JavascriptOperators::GetProperty(instance, propertyRecord->GetPropertyId(), value, requestContext);
- }
- BOOL JavascriptOperators::GetItem(RecyclableObject* instance, uint32 index, Var* value, ScriptContext* requestContext)
- {
- return JavascriptOperators::GetItem(instance, instance, index, value, requestContext);
- }
- BOOL JavascriptOperators::GetItemReference(RecyclableObject* instance, uint32 index, Var* value, ScriptContext* requestContext)
- {
- return GetItemReference(instance, instance, index, value, requestContext);
- }
- BOOL JavascriptOperators::CheckPrototypesForAccessorOrNonWritableProperty(RecyclableObject* instance, PropertyId propertyId, Var* setterValue, DescriptorFlags* flags, PropertyValueInfo* info, ScriptContext* scriptContext)
- {
- if (propertyId == Js::PropertyIds::__proto__)
- {
- return CheckPrototypesForAccessorOrNonWritablePropertyCore<PropertyId, false, false>(instance, propertyId, setterValue, flags, info, scriptContext);
- }
- else
- {
- return CheckPrototypesForAccessorOrNonWritablePropertyCore<PropertyId, true, false>(instance, propertyId, setterValue, flags, info, scriptContext);
- }
- }
- BOOL JavascriptOperators::CheckPrototypesForAccessorOrNonWritableRootProperty(RecyclableObject* instance, PropertyId propertyId, Var* setterValue, DescriptorFlags* flags, PropertyValueInfo* info, ScriptContext* scriptContext)
- {
- if (propertyId == Js::PropertyIds::__proto__)
- {
- return CheckPrototypesForAccessorOrNonWritablePropertyCore<PropertyId, false, true>(instance, propertyId, setterValue, flags, info, scriptContext);
- }
- else
- {
- return CheckPrototypesForAccessorOrNonWritablePropertyCore<PropertyId, true, true>(instance, propertyId, setterValue, flags, info, scriptContext);
- }
- }
- BOOL JavascriptOperators::CheckPrototypesForAccessorOrNonWritableProperty(RecyclableObject* instance, JavascriptString* propertyNameString, Var* setterValue, DescriptorFlags* flags, PropertyValueInfo* info, ScriptContext* scriptContext)
- {
- Js::PropertyRecord const * localPropertyRecord;
- propertyNameString->GetPropertyRecord(&localPropertyRecord);
- PropertyId propertyId = localPropertyRecord->GetPropertyId();
- return CheckPrototypesForAccessorOrNonWritableProperty(instance, propertyId, setterValue, flags, info, scriptContext);
- }
- BOOL JavascriptOperators::SetProperty(Var instance, RecyclableObject* object, PropertyId propertyId, Var newValue, ScriptContext* requestContext, PropertyOperationFlags propertyOperationFlags)
- {
- PropertyValueInfo info;
- return JavascriptOperators::SetProperty(instance, object, propertyId, newValue, &info, requestContext, propertyOperationFlags);
- }
- BOOL JavascriptOperators::TryConvertToUInt32(const char16* str, int length, uint32* intVal)
- {
- return NumberUtilities::TryConvertToUInt32(str, length, intVal);
- }
- template <typename TPropertyKey>
- DescriptorFlags JavascriptOperators::GetRootSetter(RecyclableObject* instance, TPropertyKey propertyKey, Var *setterValue, PropertyValueInfo* info, ScriptContext* requestContext)
- {
- // This is provided only so that CheckPrototypesForAccessorOrNonWritablePropertyCore will compile.
- // It will never be called.
- Throw::FatalInternalError();
- }
- template <>
- inline DescriptorFlags JavascriptOperators::GetRootSetter(RecyclableObject* instance, PropertyId propertyId, Var *setterValue, PropertyValueInfo* info, ScriptContext* requestContext)
- {
- AssertMsg(JavascriptOperators::GetTypeId(instance) == TypeIds_GlobalObject
- || JavascriptOperators::GetTypeId(instance) == TypeIds_ModuleRoot,
- "Root must be a global object!");
- RootObjectBase* rootObject = static_cast<RootObjectBase*>(instance);
- return rootObject->GetRootSetter(propertyId, setterValue, info, requestContext);
- }
- // Helper to fetch @@species from a constructor object
- Var JavascriptOperators::GetSpecies(RecyclableObject* constructor, ScriptContext* scriptContext)
- {
- if (scriptContext->GetConfig()->IsES6SpeciesEnabled())
- {
- Var species = nullptr;
- // Let S be Get(C, @@species)
- if (JavascriptOperators::GetProperty(constructor, PropertyIds::_symbolSpecies, &species, scriptContext)
- && !JavascriptOperators::IsUndefinedOrNull(species))
- {
- // If S is neither undefined nor null, let C be S
- return species;
- }
- }
- return constructor;
- }
|