| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575 |
- //-------------------------------------------------------------------------------------------------------
- // 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
- 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(indexVar, JavascriptHint::HintString, 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)
- {
- 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);
- ret = JavascriptFunction::CallFunction<false>(funcPtr, entryPoint, args);
- }
- break;
- }
- return ret;
- }
- #ifdef _M_IX86
- // Alias for overloaded JavascriptNumber::ToVar so it can be called unambiguously from native code
- Var JavascriptOperators::Int32ToVar(int32 value, ScriptContext* scriptContext)
- {
- return JavascriptNumber::ToVar(value, scriptContext);
- }
- // Alias for overloaded JavascriptNumber::ToVar so it can be called unambiguously from native code
- Var JavascriptOperators::Int32ToVarInPlace(int32 value, ScriptContext* scriptContext, JavascriptNumber* result)
- {
- return JavascriptNumber::ToVarInPlace(value, scriptContext, result);
- }
- // Alias for overloaded JavascriptNumber::ToVar so it can be called unambiguously from native code
- Var JavascriptOperators::UInt32ToVar(uint32 value, ScriptContext* scriptContext)
- {
- return JavascriptNumber::ToVar(value, scriptContext);
- }
- // Alias for overloaded JavascriptNumber::ToVar so it can be called unambiguously from native code
- Var JavascriptOperators::UInt32ToVarInPlace(uint32 value, ScriptContext* scriptContext, JavascriptNumber* result)
- {
- return JavascriptNumber::ToVarInPlace(value, scriptContext, result);
- }
- #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)
- {
- if (TaggedInt::Is(aRight) || JavascriptNumber::Is_NoTaggedIntCheck(aRight))
- {
- return aRight;
- }
- return JavascriptNumber::ToVarInPlace(JavascriptConversion::ToNumber(aRight, scriptContext), scriptContext, result);
- }
- Var JavascriptOperators::Typeof(Var var, ScriptContext* scriptContext)
- {
- #ifdef ENABLE_SIMDJS
- if (SIMDUtils::IsSimdType(var) && scriptContext->GetConfig()->IsSimdjsEnabled())
- {
- switch ((JavascriptOperators::GetTypeId(var)))
- {
- case TypeIds_SIMDFloat32x4:
- return scriptContext->GetLibrary()->GetSIMDFloat32x4DisplayString();
- //case TypeIds_SIMDFloat64x2: //Type under review by the spec.
- // return scriptContext->GetLibrary()->GetSIMDFloat64x2DisplayString();
- case TypeIds_SIMDInt32x4:
- return scriptContext->GetLibrary()->GetSIMDInt32x4DisplayString();
- case TypeIds_SIMDInt16x8:
- return scriptContext->GetLibrary()->GetSIMDInt16x8DisplayString();
- case TypeIds_SIMDInt8x16:
- return scriptContext->GetLibrary()->GetSIMDInt8x16DisplayString();
- case TypeIds_SIMDUint32x4:
- return scriptContext->GetLibrary()->GetSIMDUint32x4DisplayString();
- case TypeIds_SIMDUint16x8:
- return scriptContext->GetLibrary()->GetSIMDUint16x8DisplayString();
- case TypeIds_SIMDUint8x16:
- return scriptContext->GetLibrary()->GetSIMDUint8x16DisplayString();
- case TypeIds_SIMDBool32x4:
- return scriptContext->GetLibrary()->GetSIMDBool32x4DisplayString();
- case TypeIds_SIMDBool16x8:
- return scriptContext->GetLibrary()->GetSIMDBool16x8DisplayString();
- case TypeIds_SIMDBool8x16:
- return scriptContext->GetLibrary()->GetSIMDBool8x16DisplayString();
- default:
- Assert(UNREACHED);
- }
- }
- #endif
- //All remaining types.
- 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);
- }
- }
- }
- 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)
- {
- 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
- }
- Var JavascriptOperators::TypeofElem_Int32(Var instance, int32 index, ScriptContext* scriptContext)
- {
- 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
- }
- 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)
- {
- 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();
- }
- }
- //
- // Delete the given Var
- //
- Var JavascriptOperators::Delete(Var var, ScriptContext* scriptContext)
- {
- return scriptContext->GetLibrary()->GetTrue();
- }
- BOOL JavascriptOperators::Equal_Full(Var aLeft, Var aRight, ScriptContext* requestContext)
- {
- //
- // 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;
- }
- }
- #ifdef ENABLE_SIMDJS
- else if (SIMDUtils::IsSimdType(aLeft) && SIMDUtils::IsSimdType(aRight))
- {
- return StrictEqualSIMD(aLeft, aRight, requestContext);
- }
- #endif
- if (RecyclableObject::UnsafeFromVar(aLeft)->Equals(aRight, &result, requestContext))
- {
- return result;
- }
- else
- {
- return false;
- }
- }
- BOOL JavascriptOperators::Greater_Full(Var aLeft,Var aRight,ScriptContext* scriptContext)
- {
- return RelationalComparisonHelper(aRight, aLeft, scriptContext, false, false);
- }
- 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;
- #ifdef ENABLE_SIMDJS
- if (SIMDUtils::IsSimdType(aLeft) || SIMDUtils::IsSimdType(aRight))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_SIMDConversion, _u("SIMD type"));
- }
- #endif
- 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(aRight, JavascriptHint::HintNumber, 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(aLeft, JavascriptHint::HintNumber, scriptContext);
- aRight = JavascriptConversion::ToPrimitive(aRight, JavascriptHint::HintNumber, scriptContext);
- }
- else
- {
- aRight = JavascriptConversion::ToPrimitive(aRight, JavascriptHint::HintNumber, scriptContext);
- aLeft = JavascriptConversion::ToPrimitive(aLeft, JavascriptHint::HintNumber, 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;
- }
- #ifdef ENABLE_SIMDJS
- BOOL JavascriptOperators::StrictEqualSIMD(Var aLeft, Var aRight, ScriptContext* scriptContext)
- {
- TypeId leftTid = JavascriptOperators::GetTypeId(aLeft);
- TypeId rightTid = JavascriptOperators::GetTypeId(aRight);
- bool result = false;
- if (leftTid != rightTid)
- {
- return result;
- }
- SIMDValue leftSimd;
- SIMDValue rightSimd;
- switch (leftTid)
- {
- case TypeIds_SIMDBool8x16:
- leftSimd = JavascriptSIMDBool8x16::FromVar(aLeft)->GetValue();
- rightSimd = JavascriptSIMDBool8x16::FromVar(aRight)->GetValue();
- return (leftSimd == rightSimd);
- case TypeIds_SIMDBool16x8:
- leftSimd = JavascriptSIMDBool16x8::FromVar(aLeft)->GetValue();
- rightSimd = JavascriptSIMDBool16x8::FromVar(aRight)->GetValue();
- return (leftSimd == rightSimd);
- case TypeIds_SIMDBool32x4:
- leftSimd = JavascriptSIMDBool32x4::FromVar(aLeft)->GetValue();
- rightSimd = JavascriptSIMDBool32x4::FromVar(aRight)->GetValue();
- return (leftSimd == rightSimd);
- case TypeIds_SIMDInt8x16:
- leftSimd = JavascriptSIMDInt8x16::FromVar(aLeft)->GetValue();
- rightSimd = JavascriptSIMDInt8x16::FromVar(aRight)->GetValue();
- return (leftSimd == rightSimd);
- case TypeIds_SIMDInt16x8:
- leftSimd = JavascriptSIMDInt16x8::FromVar(aLeft)->GetValue();
- rightSimd = JavascriptSIMDInt16x8::FromVar(aRight)->GetValue();
- return (leftSimd == rightSimd);
- case TypeIds_SIMDInt32x4:
- leftSimd = JavascriptSIMDInt32x4::FromVar(aLeft)->GetValue();
- rightSimd = JavascriptSIMDInt32x4::FromVar(aRight)->GetValue();
- return (leftSimd == rightSimd);
- case TypeIds_SIMDUint8x16:
- leftSimd = JavascriptSIMDUint8x16::FromVar(aLeft)->GetValue();
- rightSimd = JavascriptSIMDUint8x16::FromVar(aRight)->GetValue();
- return (leftSimd == rightSimd);
- case TypeIds_SIMDUint16x8:
- leftSimd = JavascriptSIMDUint16x8::FromVar(aLeft)->GetValue();
- rightSimd = JavascriptSIMDUint16x8::FromVar(aRight)->GetValue();
- return (leftSimd == rightSimd);
- case TypeIds_SIMDUint32x4:
- leftSimd = JavascriptSIMDUint32x4::FromVar(aLeft)->GetValue();
- rightSimd = JavascriptSIMDUint32x4::FromVar(aRight)->GetValue();
- return (leftSimd == rightSimd);
- case TypeIds_SIMDFloat32x4:
- leftSimd = JavascriptSIMDFloat32x4::FromVar(aLeft)->GetValue();
- rightSimd = JavascriptSIMDFloat32x4::FromVar(aRight)->GetValue();
- result = true;
- for (int i = 0; i < 4; ++i)
- {
- Var laneVarLeft = JavascriptNumber::ToVarWithCheck(leftSimd.f32[i], scriptContext);
- Var laneVarRight = JavascriptNumber::ToVarWithCheck(rightSimd.f32[i], scriptContext);
- result = result && JavascriptOperators::Equal(laneVarLeft, laneVarRight, scriptContext);
- }
- return result;
- default:
- Assert(UNREACHED);
- }
- return result;
- }
- #endif
- BOOL JavascriptOperators::StrictEqualString(Var aLeft, Var aRight)
- {
- Assert(JavascriptOperators::GetTypeId(aRight) == TypeIds_String);
- if (JavascriptOperators::GetTypeId(aLeft) != TypeIds_String)
- return false;
- return JavascriptString::Equals(aLeft, aRight);
- }
- BOOL JavascriptOperators::StrictEqualEmptyString(Var aLeft)
- {
- JavascriptString * string = JavascriptOperators::TryFromVar<JavascriptString>(aLeft);
- if (!string)
- {
- return false;
- }
- Assert(string);
- return string->GetLength() == 0;
- }
- BOOL JavascriptOperators::StrictEqual(Var aLeft, Var aRight, ScriptContext* requestContext)
- {
- 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(aLeft, 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);
- case TypeIds_Symbol:
- switch (rightType)
- {
- case TypeIds_Symbol:
- {
- const PropertyRecord* leftValue = JavascriptSymbol::UnsafeFromVar(aLeft)->GetValue();
- const PropertyRecord* rightValue = JavascriptSymbol::UnsafeFromVar(aRight)->GetValue();
- return leftValue == rightValue;
- }
- }
- return false;
- 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;
- #ifdef ENABLE_SIMDJS
- case TypeIds_SIMDBool8x16:
- case TypeIds_SIMDInt8x16:
- case TypeIds_SIMDUint8x16:
- case TypeIds_SIMDBool16x8:
- case TypeIds_SIMDInt16x8:
- case TypeIds_SIMDUint16x8:
- case TypeIds_SIMDBool32x4:
- case TypeIds_SIMDInt32x4:
- case TypeIds_SIMDUint32x4:
- case TypeIds_SIMDFloat32x4:
- case TypeIds_SIMDFloat64x2:
- return StrictEqualSIMD(aLeft, aRight, requestContext);
- break;
- #endif
- }
- 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;
- }
- 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)
- {
- #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());
- }
- 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);
- 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)
- {
- RecyclableObject* object = TaggedNumber::Is(instance) ?
- scriptContext->GetLibrary()->GetNumberPrototype() :
- RecyclableObject::FromVar(instance);
- BOOL result = HasProperty(object, propertyId);
- return result;
- }
- 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);
- }
- // Only cache missing property lookups for non-root field loads on objects that have PathTypeHandlers and DictionaryTypeHandlers, because only these types have the right behavior
- // when the missing property is later added: path types guarantee a type change, and DictionaryTypeHandlerBase::AddProperty explicitly invalidates all prototype caches for the
- // property. (We don't support other types only because we haven't needed to yet; we do not anticipate any difficulties in adding the cache-invalidation logic there if that changes.)
- if (!PHASE_OFF1(MissingPropertyCachePhase) && !isRoot && DynamicObject::Is(instance) &&
- (DynamicObject::FromVar(instance)->GetDynamicType()->GetTypeHandler()->IsPathTypeHandler() || DynamicObject::FromVar(instance)->GetDynamicType()->GetTypeHandler()->IsDictionaryTypeHandler()))
- {
- #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(propertyObject, requestContext->GetLibrary()->GetMissingPropertyHolder(), isRoot, propertyId, true, info, requestContext);
- }
- #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, /*successful: */false);
- }
- }
- #endif
- *value = requestContext->GetMissingPropertyResult();
- return FALSE;
- }
- }
- template<typename PropertyKeyType>
- BOOL JavascriptOperators::GetPropertyWPCache(Var instance, RecyclableObject* propertyObject, PropertyKeyType propertyKey, Var* value, ScriptContext* requestContext, _Inout_ PropertyValueInfo * info)
- {
- RecyclableObject* object = propertyObject;
- while (!JavascriptOperators::IsNull(object))
- {
- PropertyQueryFlags result = object->GetPropertyQuery(instance, propertyKey, value, info, requestContext);
- if (result != PropertyQueryFlags::Property_NotFound)
- {
- if (value && !WithScopeObject::Is(object) && info->GetPropertyString())
- {
- PropertyId propertyId = info->GetPropertyString()->GetPropertyId();
- CacheOperators::CachePropertyRead(instance, object, false, propertyId, false, info, requestContext);
- }
- return JavascriptConversion::PropertyQueryFlagsToBoolean(result);
- }
- if (object->SkipsPrototype())
- {
- break;
- }
- object = JavascriptOperators::GetPrototypeNoTrap(object);
- }
- if (!PHASE_OFF1(MissingPropertyCachePhase) && info->GetPropertyString() && DynamicObject::Is(instance) && ((DynamicObject*)instance)->GetDynamicType()->GetTypeHandler()->IsPathTypeHandler())
- {
- PropertyValueInfo::Set(info, requestContext->GetLibrary()->GetMissingPropertyHolder(), 0);
- CacheOperators::CachePropertyRead(instance, requestContext->GetLibrary()->GetMissingPropertyHolder(), false, info->GetPropertyString()->GetPropertyId(), true, info, requestContext);
- }
- *value = requestContext->GetMissingPropertyResult();
- return FALSE;
- }
- 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)
- {
- 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;
- }
- 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))
- {
- 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)
- {
- return RecyclableObject::FromVar(aValue)->GetThisObjectOrUnWrap();
- }
- Var JavascriptOperators::OP_GetInstanceScoped(FrameDisplay *pScope, PropertyId propertyId, Var rootObject, Var* thisVar, ScriptContext* scriptContext)
- {
- // 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();
- }
- 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, /*successful: */false);
- }
- }
- #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)
- {
- scriptContext->InvalidateProtoCaches(propertyId);
- }
- // 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->GetPropertyString())
- {
- CacheOperators::CachePropertyWrite(RecyclableObject::FromVar(receiver), false, object->GetType(), info->GetPropertyString()->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->GetPropertyString())
- {
- 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->GetPropertyString() && info->GetFlags() != InlineCacheSetterFlag && !object->IsExternal())
- {
- CacheOperators::CachePropertyWrite(RecyclableObject::FromVar(receiver), false, typeWithoutProperty, info->GetPropertyString()->GetPropertyId(), info, requestContext);
- if (info->GetInstance() == receiverObject)
- {
- PropertyValueInfo::SetCacheInfo(info, info->GetPropertyString(), info->GetPropertyString()->GetLdElemInlineCache(), info->AllowResizingPolymorphicInlineCache());
- CacheOperators::CachePropertyRead(object, receiverObject, false, info->GetPropertyString()->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
- {
- 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.
- if ( (instanceType == TypeIds_NativeIntArray || instanceType == TypeIds_NativeFloatArray) || (instanceType >= TypeIds_Int8Array && instanceType <= TypeIds_Uint64Array) )
- {
- 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 JavascriptOperators::GetAccessors(RecyclableObject* instance, PropertyId propertyId, ScriptContext* requestContext, Var* getter, 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(thisInstance);
- 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(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);
- instance->SetProperty(propertyId, newValue, PropertyOperation_None, NULL);
- instance->SetWritable(propertyId, FALSE);
- instance->SetConfigurable(propertyId, FALSE);
- return TRUE;
- }
- BOOL JavascriptOperators::OP_InitClassMember(Var obj, PropertyId propertyId, Var newValue)
- {
- RecyclableObject* instance = RecyclableObject::FromVar(obj);
- PropertyOperationFlags flags = PropertyOperation_None;
- PropertyAttributes attributes = PropertyClassMemberDefaults;
- instance->SetPropertyWithAttributes(propertyId, newValue, attributes, NULL, flags);
- return TRUE;
- }
- BOOL JavascriptOperators::OP_InitLetProperty(Var obj, PropertyId propertyId, Var newValue)
- {
- 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;
- }
- BOOL JavascriptOperators::OP_InitConstProperty(Var obj, PropertyId propertyId, Var newValue)
- {
- RecyclableObject* instance = RecyclableObject::FromVar(obj);
- 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;
- }
- BOOL JavascriptOperators::OP_InitUndeclRootLetProperty(Var obj, PropertyId propertyId)
- {
- RecyclableObject* instance = RecyclableObject::FromVar(obj);
- PropertyOperationFlags flags = static_cast<PropertyOperationFlags>(PropertyOperation_SpecialValue | PropertyOperation_AllowUndecl);
- PropertyAttributes attributes = PropertyLetDefaults | PropertyLetConstGlobal;
- instance->SetPropertyWithAttributes(propertyId, instance->GetLibrary()->GetUndeclBlockVar(), attributes, NULL, flags);
- return TRUE;
- }
- BOOL JavascriptOperators::OP_InitUndeclRootConstProperty(Var obj, PropertyId propertyId)
- {
- RecyclableObject* instance = RecyclableObject::FromVar(obj);
- PropertyOperationFlags flags = static_cast<PropertyOperationFlags>(PropertyOperation_SpecialValue | PropertyOperation_AllowUndecl);
- PropertyAttributes attributes = PropertyConstDefaults | PropertyLetConstGlobal;
- instance->SetPropertyWithAttributes(propertyId, instance->GetLibrary()->GetUndeclBlockVar(), attributes, NULL, flags);
- return TRUE;
- }
- 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));
- PropertyOperationFlags flags = static_cast<PropertyOperationFlags>(PropertyOperation_SpecialValue | PropertyOperation_AllowUndecl);
- PropertyAttributes attributes = PropertyLetDefaults;
- instance->SetPropertyWithAttributes(propertyId, instance->GetLibrary()->GetUndeclBlockVar(), attributes, NULL, flags);
- return TRUE;
- }
- 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));
- PropertyOperationFlags flags = static_cast<PropertyOperationFlags>(PropertyOperation_SpecialValue | PropertyOperation_AllowUndecl);
- PropertyAttributes attributes = PropertyConstDefaults;
- instance->SetPropertyWithAttributes(propertyId, instance->GetLibrary()->GetUndeclBlockVar(), attributes, NULL, flags);
- return TRUE;
- }
- 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)
- {
- 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));
- }
- Var JavascriptOperators::OP_DeleteRootProperty(Var instance, PropertyId propertyId, ScriptContext* scriptContext, PropertyOperationFlags propertyOperationFlags)
- {
- AssertMsg(RootObjectBase::Is(instance), "Root must be a global object!");
- RootObjectBase* rootObject = static_cast<RootObjectBase*>(instance);
- return scriptContext->GetLibrary()->CreateBoolean(
- rootObject->DeleteRootProperty(propertyId, propertyOperationFlags));
- }
- 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)
- {
- // 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));
- }
- }
- 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)
- {
- 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);
- }
- 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)
- {
- 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);
- }
- Var JavascriptOperators::OP_TypeofPropertyScoped(FrameDisplay *pScope, PropertyId propertyId, Var defaultInstance, ScriptContext* scriptContext)
- {
- 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);
- }
- 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)
- {
- #if ENABLE_NATIVE_CODEGEN
- Assert(Js::JavascriptStackWalker::ValidateTopJitFrame(scriptContext));
- #endif
- return OP_GetElementI(instance, index, scriptContext);
- }
- Var JavascriptOperators::OP_GetElementI_UInt32(Var instance, uint32 index, ScriptContext* scriptContext)
- {
- #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
- }
- Var JavascriptOperators::OP_GetElementI_Int32(Var instance, int32 index, ScriptContext* scriptContext)
- {
- #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
- }
- 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;
- }
- 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)
- {
- JavascriptString *temp = nullptr;
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(instance);
- #endif
- if (TaggedInt::Is(index))
- {
- TaggedIntIndex:
- 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:
- {
- 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;
- }
- }
- else if (JavascriptNumber::Is_NoTaggedIntCheck(index))
- {
- uint32 uint32Index = JavascriptConversion::ToUInt32(index, scriptContext);
- if ((double)uint32Index == JavascriptNumber::GetValue(index) && !TaggedInt::IsOverflow(uint32Index))
- {
- index = TaggedInt::ToVarUnchecked(uint32Index);
- goto TaggedIntIndex;
- }
- }
- else
- {
- temp = JavascriptOperators::TryFromVar<JavascriptString>(index);
- if (temp && RecyclableObject::Is(instance)) // fastpath for PropertyStrings
- {
- PropertyString * propertyString = nullptr;
- if (VirtualTableInfo<Js::PropertyString>::HasVirtualTable(temp))
- {
- propertyString = (PropertyString*)temp;
- }
- else if (VirtualTableInfo<Js::LiteralStringWithPropertyStringPtr>::HasVirtualTable(temp))
- {
- LiteralStringWithPropertyStringPtr * strWithPtr = (LiteralStringWithPropertyStringPtr *)temp;
- 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)
- {
- RecyclableObject* object = nullptr;
- if (FALSE == JavascriptOperators::GetPropertyObject(instance, scriptContext, &object))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_CannotGet_NullOrUndefined,
- JavascriptString::FromVar(index)->GetSz());
- }
- PropertyRecord const * propertyRecord;
- propertyString->GetPropertyRecord(&propertyRecord);
- Var value;
- if (propertyRecord->IsNumeric())
- {
- if (JavascriptOperators::GetItem(instance, object, propertyRecord->GetNumericValue(), &value, scriptContext))
- {
- return value;
- }
- }
- else
- {
- PropertyValueInfo info;
- if (propertyString->TryGetPropertyFromCache<false /* OwnPropertyOnly */>(instance, object, &value, scriptContext, &info))
- {
- return value;
- }
- if (JavascriptOperators::GetPropertyWPCache(instance, object, propertyRecord->GetPropertyId(), &value, scriptContext, &info))
- {
- return value;
- }
- }
- return scriptContext->GetLibrary()->GetUndefined();
- }
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- if (PHASE_TRACE1(PropertyStringCachePhase))
- {
- Output::Print(_u("PropertyCache: GetElem No property string for '%s'\n"), temp->GetString());
- }
- #endif
- #if DBG_DUMP
- scriptContext->forinNoCache++;
- #endif
- }
- }
- return JavascriptOperators::GetElementIHelper(instance, index, instance, scriptContext);
- }
- Var JavascriptOperators::GetElementIHelper(Var instance, Var index, Var receiver, ScriptContext* scriptContext)
- {
- RecyclableObject* object = nullptr;
- if (FALSE == JavascriptOperators::GetPropertyObject(instance, scriptContext, &object))
- {
- if (scriptContext->GetThreadContext()->RecordImplicitException())
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_CannotGet_NullOrUndefined, GetPropertyDisplayNameForError(index, scriptContext));
- }
- else
- {
- 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(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(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)
- {
- #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;
- }
- int32 JavascriptOperators::OP_GetNativeIntElementI_UInt32(Var instance, uint32 index, ScriptContext* scriptContext)
- {
- #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
- }
- int32 JavascriptOperators::OP_GetNativeIntElementI_Int32(Var instance, int32 index, ScriptContext* scriptContext)
- {
- #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
- }
- double JavascriptOperators::OP_GetNativeFloatElementI(Var instance, Var index)
- {
- 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;
- }
- double JavascriptOperators::OP_GetNativeFloatElementI_UInt32(Var instance, uint32 index, ScriptContext* scriptContext)
- {
- #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
- }
- double JavascriptOperators::OP_GetNativeFloatElementI_Int32(Var instance, int32 index, ScriptContext* scriptContext)
- {
- #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
- }
- Var JavascriptOperators::OP_GetMethodElement_UInt32(Var instance, uint32 index, ScriptContext* scriptContext)
- {
- #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
- }
- Var JavascriptOperators::OP_GetMethodElement_Int32(Var instance, int32 index, ScriptContext* scriptContext)
- {
- #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
- }
- Var JavascriptOperators::OP_GetMethodElement(Var instance, Var index, ScriptContext* scriptContext)
- {
- 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;
- }
- BOOL JavascriptOperators::OP_SetElementI_UInt32(Var instance, uint32 index, Var value, ScriptContext* scriptContext, PropertyOperationFlags flags)
- {
- #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
- }
- BOOL JavascriptOperators::OP_SetElementI_Int32(Var instance, int32 index, Var value, ScriptContext* scriptContext, PropertyOperationFlags flags)
- {
- #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
- }
- BOOL JavascriptOperators::OP_SetElementI_JIT(Var instance, Var index, Var value, ScriptContext* scriptContext, PropertyOperationFlags flags)
- {
- 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);
- }
- 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;
- BOOL isNullOrUndefined = !GetPropertyObject(instance, scriptContext, &object);
- Assert(object == instance || TaggedNumber::Is(instance));
- if (isNullOrUndefined)
- {
- if (!scriptContext->GetThreadContext()->RecordImplicitException())
- {
- return FALSE;
- }
- JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_CannotSet_NullOrUndefined, GetPropertyDisplayNameForError(index, scriptContext));
- }
- return JavascriptOperators::SetElementIHelper(instance, object, index, value, scriptContext, flags);
- }
- BOOL JavascriptOperators::SetElementIHelper(Var receiver, RecyclableObject* object, Var index, Var value, ScriptContext* scriptContext, PropertyOperationFlags flags)
- {
- Js::IndexType indexType;
- uint32 indexVal = 0;
- PropertyRecord const * propertyRecord = nullptr;
- JavascriptString * propertyNameString = nullptr;
- PropertyValueInfo propertyValueInfo;
- if (TaggedNumber::Is(receiver))
- {
- indexType = GetIndexType(index, scriptContext, &indexVal, &propertyRecord, true);
- if (indexType == IndexType_Number)
- {
- return JavascriptOperators::SetItemOnTaggedNumber(receiver, object, indexVal, value, scriptContext, flags);
- }
- else
- {
- return JavascriptOperators::SetPropertyOnTaggedNumber(receiver, object, propertyRecord->GetPropertyId(), value, scriptContext, flags);
- }
- }
- // fastpath for PropertyStrings only if receiver == object
- PropertyString * propertyString = PropertyString::TryFromVar(index);
- if (propertyString == nullptr)
- {
- LiteralStringWithPropertyStringPtr * strWithPtr = LiteralStringWithPropertyStringPtr::TryFromVar(index);
- if (strWithPtr != nullptr)
- {
- propertyString = strWithPtr->GetPropertyString(); // do not force create the PropertyString,
- // if it wasn't there, it won't be efficient for now.
- strWithPtr->GetPropertyRecord(&propertyRecord, true /* dontLookupFromDictionary */);
- if (propertyRecord == nullptr)
- {
- strWithPtr->GetPropertyRecord(&propertyRecord); // lookup-cache propertyRecord
- // later this call, there will be a lookup anyways!
- }
- else if (propertyString == nullptr)
- {
- propertyString = strWithPtr->GetOrAddPropertyString(); // this is the second time this property is here
- // we already had created the propertyRecord..
- // now create the propertyString!
- }
- }
- }
- else
- {
- propertyString->GetPropertyRecord(&propertyRecord);
- }
- if (propertyRecord != nullptr)
- {
- if (propertyRecord->IsNumeric())
- {
- indexType = IndexType_Number;
- indexVal = propertyRecord->GetNumericValue();
- }
- else
- {
- if (propertyString != nullptr && receiver == object)
- {
- Assert(propertyString->GetScriptContext() == scriptContext);
- if (propertyString->TrySetPropertyFromCache(object, value, scriptContext, flags, &propertyValueInfo))
- {
- return true;
- }
- }
- indexType = IndexType_PropertyId;
- }
- }
- else
- {
- #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);
- 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 SetPropertyWPCache(receiver, object, propId, value, scriptContext, flags, &propertyValueInfo);
- }
- BOOL JavascriptOperators::OP_SetNativeIntElementI(
- Var instance,
- Var aElementIndex,
- int32 iValue,
- ScriptContext* scriptContext,
- PropertyOperationFlags flags)
- {
- 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 TRUE;
- }
- }
- return JavascriptOperators::OP_SetElementI(instance, aElementIndex, JavascriptNumber::ToVar(iValue, scriptContext), scriptContext, flags);
- }
- BOOL JavascriptOperators::OP_SetNativeIntElementI_UInt32(
- Var instance,
- uint32 aElementIndex,
- int32 iValue,
- ScriptContext* scriptContext,
- PropertyOperationFlags flags)
- {
- #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
- }
- BOOL JavascriptOperators::OP_SetNativeIntElementI_Int32(
- Var instance,
- int aElementIndex,
- int32 iValue,
- ScriptContext* scriptContext,
- PropertyOperationFlags flags)
- {
- #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
- }
- BOOL JavascriptOperators::OP_SetNativeFloatElementI(
- Var instance,
- Var aElementIndex,
- ScriptContext* scriptContext,
- PropertyOperationFlags flags,
- double dValue)
- {
- 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 TRUE;
- }
- }
- return JavascriptOperators::OP_SetElementI(instance, aElementIndex, JavascriptNumber::ToVarWithCheck(dValue, scriptContext), scriptContext, flags);
- }
- BOOL JavascriptOperators::OP_SetNativeFloatElementI_UInt32(
- Var instance, uint32
- aElementIndex,
- ScriptContext* scriptContext,
- PropertyOperationFlags flags,
- double dValue)
- {
- #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
- }
- BOOL JavascriptOperators::OP_SetNativeFloatElementI_Int32(
- Var instance,
- int aElementIndex,
- ScriptContext* scriptContext,
- PropertyOperationFlags flags,
- double dValue)
- {
- #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
- }
- BOOL JavascriptOperators::OP_Memcopy(Var dstInstance, int32 dstStart, Var srcInstance, int32 srcStart, int32 length, ScriptContext* scriptContext)
- {
- 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;
- }
- 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)
- {
- 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;
- }
- Var JavascriptOperators::OP_DeleteElementI_UInt32(Var instance, uint32 index, ScriptContext* scriptContext, PropertyOperationFlags propertyOperationFlags)
- {
- #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
- }
- Var JavascriptOperators::OP_DeleteElementI_Int32(Var instance, int32 index, ScriptContext* scriptContext, PropertyOperationFlags propertyOperationFlags)
- {
- #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
- }
- Var JavascriptOperators::OP_DeleteElementI(Var instance, Var index, ScriptContext* scriptContext, PropertyOperationFlags propertyOperationFlags)
- {
- 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
- }
- return scriptContext->GetLibrary()->CreateBoolean(result);
- }
- 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)
- {
- //
- // 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);
- }
- Var JavascriptOperators::OP_GetThisNoFastPath(Var thisVar, int moduleID, ScriptContext* scriptContext)
- {
- 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);
- }
- 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)
- {
- TypeId typeId = JavascriptOperators::GetTypeId(thisVar);
- if (typeId == TypeIds_ActivationObject)
- {
- return scriptContext->GetLibrary()->GetUndefined();
- }
- return thisVar;
- }
- 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::IsJsNativeObject(Var aValue)
- {
- switch(GetTypeId(aValue))
- {
- 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:
- #ifdef ENABLE_SIMDJS
- case TypeIds_SIMDObject:
- #endif
- 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::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)
- {
- // 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;
- }
- BOOL JavascriptOperators::OP_BrFncNeqApply(Var instance, ScriptContext *scriptContext)
- {
- // 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;
- }
- 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)
- {
- PropertyId id;
- return aEnumerator->MoveAndGetNext(id);
- }
- void JavascriptOperators::OP_InitForInEnumerator(Var enumerable, ForInObjectEnumerator * enumerator, ScriptContext* scriptContext, ForInCache * forInCache)
- {
- RecyclableObject* enumerableObject;
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(enumerable);
- #endif
- if (!GetPropertyObject(enumerable, scriptContext, &enumerableObject))
- {
- enumerableObject = nullptr;
- }
- enumerator->Initialize(enumerableObject, scriptContext, false, forInCache);
- }
- Js::Var JavascriptOperators::OP_CmEq_A(Var a, Var b, ScriptContext* scriptContext)
- {
- return JavascriptBoolean::ToVar(JavascriptOperators::Equal(a, b, scriptContext), scriptContext);
- }
- Var JavascriptOperators::OP_CmNeq_A(Var a, Var b, ScriptContext* scriptContext)
- {
- return JavascriptBoolean::ToVar(JavascriptOperators::NotEqual(a,b,scriptContext), scriptContext);
- }
- Var JavascriptOperators::OP_CmSrEq_A(Var a, Var b, ScriptContext* scriptContext)
- {
- return JavascriptBoolean::ToVar(JavascriptOperators::StrictEqual(a, b, scriptContext), scriptContext);
- }
- Var JavascriptOperators::OP_CmSrEq_String(Var a, Var b, ScriptContext *scriptContext)
- {
- return JavascriptBoolean::ToVar(JavascriptOperators::StrictEqualString(a, b), scriptContext);
- }
- Var JavascriptOperators::OP_CmSrEq_EmptyString(Var a, ScriptContext *scriptContext)
- {
- return JavascriptBoolean::ToVar(JavascriptOperators::StrictEqualEmptyString(a), scriptContext);
- }
- Var JavascriptOperators::OP_CmSrNeq_A(Var a, Var b, ScriptContext* scriptContext)
- {
- return JavascriptBoolean::ToVar(JavascriptOperators::NotStrictEqual(a, b, scriptContext), scriptContext);
- }
- Var JavascriptOperators::OP_CmLt_A(Var a, Var b, ScriptContext* scriptContext)
- {
- return JavascriptBoolean::ToVar(JavascriptOperators::Less(a, b, scriptContext), scriptContext);
- }
- Var JavascriptOperators::OP_CmLe_A(Var a, Var b, ScriptContext* scriptContext)
- {
- return JavascriptBoolean::ToVar(JavascriptOperators::LessEqual(a, b, scriptContext), scriptContext);
- }
- Var JavascriptOperators::OP_CmGt_A(Var a, Var b, ScriptContext* scriptContext)
- {
- return JavascriptBoolean::ToVar(JavascriptOperators::Greater(a, b, scriptContext), scriptContext);
- }
- Var JavascriptOperators::OP_CmGe_A(Var a, Var b, ScriptContext* scriptContext)
- {
- return JavascriptBoolean::ToVar(JavascriptOperators::GreaterEqual(a, b, scriptContext), scriptContext);
- }
- 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)
- {
- 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;
- }
- 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)
- {
- 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;
- }
- void JavascriptOperators::OP_InvalidateCachedScope(void* varEnv, int32 envIndex)
- {
- FrameDisplay *disp = (FrameDisplay*)varEnv;
- RecyclableObject *objScope = RecyclableObject::FromVar(disp->GetItem(envIndex));
- objScope->InvalidateCachedScope();
- }
- void JavascriptOperators::OP_InitCachedFuncs(Var varScope, FrameDisplay *pDisplay, const FuncInfoArray *info, ScriptContext *scriptContext)
- {
- 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));
- }
- }
- }
- Var JavascriptOperators::AddVarsToArraySegment(SparseArraySegment<Var> * segment, const Js::VarArray *vars)
- {
- 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;
- }
- 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)
- {
- 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;
- }
- Var JavascriptOperators::NewJavascriptArrayNoArg(ScriptContext* requestContext)
- {
- 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;
- }
- Var JavascriptOperators::NewScObjectNoArgNoCtorFull(Var instance, ScriptContext* requestContext)
- {
- return NewScObjectNoArgNoCtorCommon(instance, requestContext, true);
- }
- Var JavascriptOperators::NewScObjectNoArgNoCtor(Var instance, ScriptContext* requestContext)
- {
- return NewScObjectNoArgNoCtorCommon(instance, requestContext, false);
- }
- 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)
- {
- JavascriptProxy * proxy = JavascriptOperators::TryFromVar<JavascriptProxy>(instance);
- if (proxy)
- {
- Arguments args(CallInfo(CallFlags_New, 1), &instance);
- 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);
- Var returnVar = CALL_FUNCTION(object->GetScriptContext()->GetThreadContext(), 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;
- }
- Var JavascriptOperators::NewScObjectNoCtorFull(Var instance, ScriptContext* requestContext)
- {
- return NewScObjectNoCtorCommon(instance, requestContext, true);
- }
- Var JavascriptOperators::NewScObjectNoCtor(Var instance, ScriptContext * requestContext)
- {
- // 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);
- }
- 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)
- {
- 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
- }
- }
- }
- 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)
- {
- // 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);
- }
- FrameDisplay* JavascriptOperators::OP_LdFrameDisplay(void *argHead, void *argEnv, ScriptContext* scriptContext)
- {
- // 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;
- }
- FrameDisplay* JavascriptOperators::OP_LdFrameDisplayNoParent(void *argHead, ScriptContext* scriptContext)
- {
- return OP_LdFrameDisplay(argHead, (void*)&NullFrameDisplay, scriptContext);
- }
- FrameDisplay* JavascriptOperators::OP_LdStrictFrameDisplay(void *argHead, void *argEnv, ScriptContext* scriptContext)
- {
- FrameDisplay * pDisplay = OP_LdFrameDisplay(argHead, argEnv, scriptContext);
- pDisplay->SetStrictMode(true);
- return pDisplay;
- }
- FrameDisplay* JavascriptOperators::OP_LdStrictFrameDisplayNoParent(void *argHead, ScriptContext* scriptContext)
- {
- return OP_LdStrictFrameDisplay(argHead, (void*)&StrictNullFrameDisplay, scriptContext);
- }
- FrameDisplay* JavascriptOperators::OP_LdInnerFrameDisplay(void *argHead, void *argEnv, ScriptContext* scriptContext)
- {
- CheckInnerFrameDisplayArgument(argHead);
- return OP_LdFrameDisplay(argHead, argEnv, scriptContext);
- }
- FrameDisplay* JavascriptOperators::OP_LdInnerFrameDisplayNoParent(void *argHead, ScriptContext* scriptContext)
- {
- CheckInnerFrameDisplayArgument(argHead);
- return OP_LdFrameDisplayNoParent(argHead, scriptContext);
- }
- FrameDisplay* JavascriptOperators::OP_LdStrictInnerFrameDisplay(void *argHead, void *argEnv, ScriptContext* scriptContext)
- {
- CheckInnerFrameDisplayArgument(argHead);
- return OP_LdStrictFrameDisplay(argHead, argEnv, scriptContext);
- }
- FrameDisplay* JavascriptOperators::OP_LdStrictInnerFrameDisplayNoParent(void *argHead, ScriptContext* scriptContext)
- {
- CheckInnerFrameDisplayArgument(argHead);
- return OP_LdStrictFrameDisplayNoParent(argHead, scriptContext);
- }
- 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::FromVar(object)->SetAccessors(propertyId, nullptr, setter);
- }
- void JavascriptOperators::OP_InitClassMemberSet(Var object, PropertyId propertyId, Var setter)
- {
- JavascriptOperators::OP_InitSetter(object, propertyId, setter);
- RecyclableObject::FromVar(object)->SetAttributes(propertyId, PropertyClassMemberDefaults);
- }
- Js::PropertyId JavascriptOperators::OP_InitElemSetter(Var object, Var elementName, Var setter, ScriptContext* scriptContext, PropertyOperationFlags flags)
- {
- AssertMsg(!TaggedNumber::Is(object), "SetMember on a non-object?");
- PropertyId propertyId = JavascriptOperators::GetPropertyId(elementName, scriptContext);
- RecyclableObject::FromVar(object)->SetAccessors(propertyId, nullptr, setter);
- return propertyId;
- }
- 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)
- {
- 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);
- }
- BOOL JavascriptOperators::IsClassConstructor(Var instance)
- {
- JavascriptFunction * function = JavascriptOperators::TryFromVar<JavascriptFunction>(instance);
- return function && (function->GetFunctionInfo()->IsClassConstructor() || (!function->IsScriptFunction() && !function->IsExternalFunction()));
- }
- BOOL JavascriptOperators::IsClassMethod(Var instance)
- {
- JavascriptFunction * function = JavascriptOperators::TryFromVar<JavascriptFunction>(instance);
- return function && function->GetFunctionInfo()->IsClassMethod();
- }
- BOOL JavascriptOperators::IsBaseConstructorKind(Var instance)
- {
- JavascriptFunction * function = JavascriptOperators::TryFromVar<JavascriptFunction>(instance);
- return function && (function->GetFunctionInfo()->GetBaseConstructorKind());
- }
- void JavascriptOperators::OP_InitGetter(Var object, PropertyId propertyId, Var getter)
- {
- AssertMsg(!TaggedNumber::Is(object), "GetMember on a non-object?");
- RecyclableObject::FromVar(object)->SetAccessors(propertyId, getter, nullptr);
- }
- void JavascriptOperators::OP_InitClassMemberGet(Var object, PropertyId propertyId, Var getter)
- {
- JavascriptOperators::OP_InitGetter(object, propertyId, getter);
- RecyclableObject::FromVar(object)->SetAttributes(propertyId, PropertyClassMemberDefaults);
- }
- Js::PropertyId JavascriptOperators::OP_InitElemGetter(Var object, Var elementName, Var getter, ScriptContext* scriptContext, PropertyOperationFlags flags)
- {
- AssertMsg(!TaggedNumber::Is(object), "GetMember on a non-object?");
- PropertyId propertyId = JavascriptOperators::GetPropertyId(elementName, scriptContext);
- RecyclableObject::FromVar(object)->SetAccessors(propertyId, getter, nullptr);
- return propertyId;
- }
- void JavascriptOperators::OP_InitClassMemberGetComputedName(Var object, Var elementName, Var value, ScriptContext* scriptContext, PropertyOperationFlags flags)
- {
- 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);
- }
- void JavascriptOperators::OP_InitComputedProperty(Var object, Var elementName, Var value, ScriptContext* scriptContext, PropertyOperationFlags flags)
- {
- PropertyId propertyId = JavascriptOperators::GetPropertyId(elementName, scriptContext);
- RecyclableObject::FromVar(object)->InitProperty(propertyId, value, flags);
- }
- void JavascriptOperators::OP_InitClassMemberComputedName(Var object, Var elementName, Var value, ScriptContext* scriptContext, PropertyOperationFlags flags)
- {
- 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);
- }
- //
- // 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();
- // 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);
- }
- }
- 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)
- {
- 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);
- }
- Var JavascriptOperators::LoadHeapArgsCached(JavascriptFunction *funcCallee, uint32 actualsCount, uint32 formalsCount, Var *paramAddr, Var frameObj, ScriptContext* scriptContext, bool nonSimpleParamList)
- {
- // 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);
- }
- 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(frameObj);
- // 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());
- //
- // 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);
- }
- return argsObj;
- }
- Var JavascriptOperators::OP_NewScopeObject(ScriptContext* scriptContext)
- {
- return scriptContext->GetLibrary()->CreateActivationObject();
- }
- Var JavascriptOperators::OP_NewScopeObjectWithFormals(ScriptContext* scriptContext, FunctionBody * calleeBody, bool nonSimpleParamList)
- {
- 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;
- }
- Field(Var)* JavascriptOperators::OP_NewScopeSlots(unsigned int size, ScriptContext *scriptContext, Var scope)
- {
- 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;
- }
- Field(Var)* JavascriptOperators::OP_NewScopeSlotsWithoutPropIds(unsigned int count, int scopeIndex, ScriptContext *scriptContext, FunctionBody *functionBody)
- {
- 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);
- }
- Field(Var)* JavascriptOperators::OP_CloneScopeSlots(Field(Var) *slotArray, ScriptContext *scriptContext)
- {
- 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;
- }
- Var JavascriptOperators::OP_NewPseudoScope(ScriptContext *scriptContext)
- {
- return scriptContext->GetLibrary()->CreatePseudoActivationObject();
- }
- Var JavascriptOperators::OP_NewBlockScope(ScriptContext *scriptContext)
- {
- return scriptContext->GetLibrary()->CreateBlockActivationObject();
- }
- Var JavascriptOperators::OP_CloneBlockScope(BlockActivationObject *blockScope, ScriptContext *scriptContext)
- {
- return blockScope->Clone(scriptContext);
- }
- Var JavascriptOperators::OP_IsInst(Var instance, Var aClass, ScriptContext* scriptContext, IsInstInlineCache* inlineCache)
- {
- 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);
- }
- }
- void JavascriptOperators::OP_InitClass(Var constructor, Var extends, ScriptContext * scriptContext)
- {
- if (JavascriptOperators::GetTypeId(constructor) != Js::TypeId::TypeIds_Function)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_Operand_Invalid_NeedFunction, _u("class"));
- }
- RecyclableObject * ctor = RecyclableObject::FromVar(constructor);
- // This is a circular reference to the constructor, it associate the constructor with the class and also allows us to check if a
- // function is a constructor by comparing the homeObj to the this pointer. see ScriptFunction::IsClassConstructor() for implementation
- JavascriptOperators::OP_SetHomeObj(constructor, 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_InvalidPrototype, _u("extends"));
- }
- 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;
- }
- }
- }
- }
- void JavascriptOperators::OP_LoadUndefinedToElement(Var instance, PropertyId propertyId)
- {
- AssertMsg(!TaggedNumber::Is(instance), "Invalid scope/root object");
- JavascriptOperators::EnsureProperty(instance, propertyId);
- }
- void JavascriptOperators::OP_LoadUndefinedToElementScoped(FrameDisplay *pScope, PropertyId propertyId, Var defaultInstance, ScriptContext* scriptContext)
- {
- 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);
- }
- }
- void JavascriptOperators::OP_LoadUndefinedToElementDynamic(Var instance, PropertyId propertyId, ScriptContext *scriptContext)
- {
- if (!JavascriptOperators::HasOwnPropertyNoHostObject(instance, propertyId))
- {
- RecyclableObject::FromVar(instance)->InitPropertyScoped(propertyId, scriptContext->GetLibrary()->GetUndefined());
- }
- }
- 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)
- {
- Assert(RootObjectBase::Is(instance));
- RootObjectBase *obj = RootObjectBase::FromVar(instance);
- obj->EnsureNoProperty(propertyId);
- }
- void JavascriptOperators::OP_EnsureNoRootRedeclProperty(Var instance, PropertyId propertyId)
- {
- Assert(RootObjectBase::Is(instance));
- RecyclableObject *obj = RecyclableObject::FromVar(instance);
- obj->EnsureNoRedeclProperty(propertyId);
- }
- void JavascriptOperators::OP_ScopedEnsureNoRedeclProperty(FrameDisplay *pDisplay, PropertyId propertyId, Var defaultInstance)
- {
- 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);
- }
- Var JavascriptOperators::IsIn(Var argProperty, Var instance, ScriptContext* scriptContext)
- {
- // 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"));
- }
- PropertyRecord const * propertyRecord = nullptr;
- uint32 index;
- IndexType indexType = GetIndexType(argProperty, scriptContext, &index, &propertyRecord, true);
- RecyclableObject* object = RecyclableObject::FromVar(instance);
- BOOL result;
- if (indexType == Js::IndexType_Number)
- {
- result = JavascriptOperators::HasItem(object, index);
- }
- else
- {
- PropertyId propertyId = propertyRecord->GetPropertyId();
- result = JavascriptOperators::HasProperty(object, propertyId);
- #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);
- }
- template <bool IsFromFullJit, class TInlineCache>
- inline Var JavascriptOperators::PatchGetValue(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId)
- {
- return PatchGetValueWithThisPtr<IsFromFullJit, TInlineCache>(functionBody, inlineCache, inlineCacheIndex, instance, propertyId, instance);
- }
- template <bool IsFromFullJit, class TInlineCache>
- __forceinline Var JavascriptOperators::PatchGetValueWithThisPtr(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var thisInstance)
- {
- 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>(
- 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);
- }
- 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)
- {
- 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>(
- 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;
- }
- 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>(
- 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)
- {
- 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>(
- 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);
- }
- 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)
- {
- 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>(
- 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;
- }
- 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)
- {
- // 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>(
- 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;
- }
- 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)
- {
- 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;
- }
- 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)
- {
- 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>(
- 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;
- }
- 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)
- {
- 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>(
- 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;
- }
- 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)
- {
- 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>(
- 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);
- }
- 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)
- {
- return PatchPutValueWithThisPtr<IsFromFullJit, TInlineCache>(functionBody, inlineCache, inlineCacheIndex, instance, propertyId, newValue, instance, flags);
- }
- 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)
- {
- 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);
- }
- }
- 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)
- {
- 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);
- }
- }
- 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)
- {
- PatchPutValueWithThisPtrNoLocalFastPath<IsFromFullJit, TInlineCache>(functionBody, inlineCache, inlineCacheIndex, instance, propertyId, newValue, instance, flags);
- }
- 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)
- {
- 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);
- }
- }
- 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)
- {
- 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);
- }
- }
- 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)
- {
- 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);
- }
- }
- 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.
- 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)
- {
- Assert(instance);
- if (RecyclableObject::Is(instance))
- {
- RecyclableObject* obj = RecyclableObject::FromVar(instance);
- obj->SetAttributes(propertyId, PropertyNone);
- }
- }
- void JavascriptOperators::OP_Freeze(Var instance)
- {
- Assert(instance);
- if (RecyclableObject::Is(instance))
- {
- RecyclableObject* obj = RecyclableObject::FromVar(instance);
- obj->Freeze();
- }
- }
- 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);
- }
- void * JavascriptOperators::AllocUninitializedNumber(Js::RecyclerJavascriptNumberAllocator * allocator)
- {
- TRACK_ALLOC_INFO(allocator->GetRecycler(), Js::JavascriptNumber, Recycler, 0, (size_t)-1);
- return allocator->Alloc(sizeof(Js::JavascriptNumber));
- }
- void JavascriptOperators::ScriptAbort()
- {
- throw ScriptAbortException();
- }
- 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)
- {
- ScriptFunctionBase *scriptFunction = ScriptFunctionBase::FromVar(method);
- scriptFunction->SetComputedNameVar(computedNameVar);
- }
- void JavascriptOperators::OP_SetHomeObj(Var method, Var homeObj)
- {
- ScriptFunctionBase *scriptFunction = ScriptFunctionBase::FromVar(method);
- scriptFunction->SetHomeObj(homeObj);
- }
- Var JavascriptOperators::OP_LdHomeObj(Var scriptFunction, ScriptContext * scriptContext)
- {
- // 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();
- }
- Var JavascriptOperators::OP_LdHomeObjProto(Var homeObj, ScriptContext* scriptContext)
- {
- 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;
- }
- Var JavascriptOperators::OP_LdFuncObj(Var scriptFunction, ScriptContext * scriptContext)
- {
- // use self as value of [[FunctionObject]] - this is true only for constructors
- Assert(RecyclableObject::Is(scriptFunction));
- return scriptFunction;
- }
- Var JavascriptOperators::OP_LdFuncObjProto(Var funcObj, ScriptContext* scriptContext)
- {
- RecyclableObject *superCtor = RecyclableObject::FromVar(funcObj)->GetPrototype();
- if (superCtor == nullptr || !IsConstructor(superCtor))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_NotAConstructor);
- }
- return superCtor;
- }
- Var JavascriptOperators::OP_ImportCall(__in JavascriptFunction *function, __in Var specifier, __in ScriptContext* scriptContext)
- {
- 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();
- }
- Var JavascriptOperators::OP_ResumeYield(ResumeYieldData* yieldData, RecyclableObject* iterator)
- {
- 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"));
- }
- RecyclableObject* method = RecyclableObject::FromVar(prop);
- Var args[] = { iterator, yieldData->data };
- CallInfo callInfo(CallFlags_Value, _countof(args));
- Var result = JavascriptFunction::CallFunction<true>(method, method->GetEntryPoint(), Arguments(callInfo, args));
- 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 args[] = { iterator, yieldData->data };
- CallInfo callInfo(CallFlags_Value, _countof(args));
- Var result = 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());
- }
- 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));
- case Js::TypeIds_Object:
- return DynamicObject::BoxStackInstance(DynamicObject::FromVar(instance));
- 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)
- {
- 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);
- }
- BOOL JavascriptOperators::LessEqual(Var aLeft, Var aRight, ScriptContext* scriptContext)
- {
- 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);
- }
- BOOL JavascriptOperators::NotEqual(Var aLeft, Var aRight, ScriptContext* scriptContext)
- {
- //
- // 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);
- }
- // 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)
- {
- return !StrictEqual(aLeft, aRight, scriptContext);
- }
- bool JavascriptOperators::CheckIfObjectAndPrototypeChainHasOnlyWritableDataProperties(RecyclableObject* object)
- {
- Assert(object);
- if (object->GetType()->HasSpecialPrototype())
- {
- TypeId typeId = object->GetTypeId();
- if (typeId == TypeIds_Null)
- {
- return true;
- }
- if (typeId == TypeIds_Proxy)
- {
- return false;
- }
- }
- if (!object->HasOnlyWritableDataProperties())
- {
- return false;
- }
- return CheckIfPrototypeChainHasOnlyWritableDataProperties(object->GetPrototype());
- }
- bool JavascriptOperators::CheckIfPrototypeChainHasOnlyWritableDataProperties(RecyclableObject* prototype)
- {
- Assert(prototype);
- if (prototype->GetType()->AreThisAndPrototypesEnsuredToHaveOnlyWritableDataProperties())
- {
- Assert(DoCheckIfPrototypeChainHasOnlyWritableDataProperties(prototype));
- return true;
- }
- return DoCheckIfPrototypeChainHasOnlyWritableDataProperties(prototype);
- }
- // Does a quick check to see if the specified object (which should be a prototype object) and all objects in its prototype
- // chain have only writable data properties (i.e. no accessors or non-writable properties).
- bool JavascriptOperators::DoCheckIfPrototypeChainHasOnlyWritableDataProperties(RecyclableObject* prototype)
- {
- Assert(prototype);
- Type *const originalType = prototype->GetType();
- ScriptContext *const scriptContext = prototype->GetScriptContext();
- bool onlyOneScriptContext = true;
- TypeId typeId;
- for (; (typeId = prototype->GetTypeId()) != TypeIds_Null; prototype = prototype->GetPrototype())
- {
- if (typeId == TypeIds_Proxy)
- {
- return false;
- }
- if (!prototype->HasOnlyWritableDataProperties())
- {
- return false;
- }
- if (prototype->GetScriptContext() != scriptContext)
- {
- onlyOneScriptContext = false;
- }
- }
- if (onlyOneScriptContext)
- {
- // See JavascriptLibrary::typesEnsuredToHaveOnlyWritableDataPropertiesInItAndPrototypeChain for a description of
- // this cache. Technically, we could register all prototypes in the chain but this is good enough for now.
- originalType->SetAreThisAndPrototypesEnsuredToHaveOnlyWritableDataProperties(true);
- }
- return true;
- }
- // 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)
- {
- 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);
- }
- BOOL JavascriptOperators::Greater(Var aLeft, Var aRight, ScriptContext* scriptContext)
- {
- 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);
- }
- BOOL JavascriptOperators::Less(Var aLeft, Var aRight, ScriptContext* scriptContext)
- {
- 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);
- }
- RecyclableObject* JavascriptOperators::ToObject(Var aRight, ScriptContext* scriptContext)
- {
- RecyclableObject* object = nullptr;
- if (FALSE == JavascriptConversion::ToObject(aRight, scriptContext, &object))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedObject /* TODO-ERROR: get arg name - aValue */);
- }
- return object;
- }
- Var JavascriptOperators::ToWithObject(Var aRight, ScriptContext* scriptContext)
- {
- RecyclableObject* object = RecyclableObject::FromVar(aRight);
- WithScopeObject* withWrapper = RecyclerNew(scriptContext->GetRecycler(), WithScopeObject, object, scriptContext->GetLibrary()->GetWithType());
- return withWrapper;
- }
- Var JavascriptOperators::ToNumber(Var aRight, ScriptContext* scriptContext)
- {
- if (TaggedInt::Is(aRight) || (JavascriptNumber::Is_NoTaggedIntCheck(aRight)))
- {
- return aRight;
- }
- return JavascriptNumber::ToVarNoCheck(JavascriptConversion::ToNumber_Full(aRight, scriptContext), scriptContext);
- }
- BOOL JavascriptOperators::IsObject(_In_ RecyclableObject* instance)
- {
- return GetTypeId(instance) > TypeIds_LastJavascriptPrimitiveType;
- }
- BOOL JavascriptOperators::IsObject(_In_ Var instance)
- {
- return GetTypeId(instance) > TypeIds_LastJavascriptPrimitiveType;
- }
- 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 = 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);
- Js::Var args[] = { iterator };
- Js::CallInfo callInfo(Js::CallFlags_Value, _countof(args));
- 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;
- }
- } // namespace Js
|