| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #include "RuntimeLibraryPch.h"
- #include "Types/PathTypeHandler.h"
- #include "Types/SpreadArgument.h"
- namespace Js
- {
- // Make sure EmptySegment points to read-only memory.
- // Can't do this the easy way because SparseArraySegment has a constructor...
- static const char EmptySegmentData[sizeof(SparseArraySegmentBase)] = {0};
- const SparseArraySegmentBase *JavascriptArray::EmptySegment = (SparseArraySegmentBase *)&EmptySegmentData;
- #if defined(_M_X64_OR_ARM64)
- const Var JavascriptArray::MissingItem = (Var)0x8000000280000002;
- #else
- const Var JavascriptArray::MissingItem = (Var)0x80000002;
- #endif
- const int32 JavascriptNativeIntArray::MissingItem = 0x80000002;
- static const uint64 FloatMissingItemPattern = 0x8000000280000002ull;
- const double JavascriptNativeFloatArray::MissingItem = *(double*)&FloatMissingItemPattern;
- // Allocate enough space for 4 inline property slots and 16 inline element slots
- const size_t JavascriptArray::StackAllocationSize = DetermineAllocationSize<JavascriptArray, 4>(16);
- const size_t JavascriptNativeIntArray::StackAllocationSize = DetermineAllocationSize<JavascriptNativeIntArray, 4>(16);
- const size_t JavascriptNativeFloatArray::StackAllocationSize = DetermineAllocationSize<JavascriptNativeFloatArray, 4>(16);
- SegmentBTree::SegmentBTree()
- : segmentCount(0),
- segments(NULL),
- keys(NULL),
- children(NULL)
- {
- }
- uint32 SegmentBTree::GetLazyCrossOverLimit()
- {
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- if (Js::Configuration::Global.flags.DisableArrayBTree)
- {
- return Js::JavascriptArray::InvalidIndex;
- }
- else if (Js::Configuration::Global.flags.ForceArrayBTree)
- {
- return ARRAY_CROSSOVER_FOR_VALIDATE;
- }
- #endif
- #ifdef VALIDATE_ARRAY
- if (Js::Configuration::Global.flags.ArrayValidate)
- {
- return ARRAY_CROSSOVER_FOR_VALIDATE;
- }
- #endif
- return SegmentBTree::MinDegree * 3;
- }
- BOOL SegmentBTree::IsLeaf() const
- {
- return children == NULL;
- }
- BOOL SegmentBTree::IsFullNode() const
- {
- return segmentCount == MaxKeys;
- }
- void SegmentBTree::InternalFind(SegmentBTree* node, uint32 itemIndex, SparseArraySegmentBase*& prev, SparseArraySegmentBase*& matchOrNext)
- {
- uint32 i = 0;
- for(; i < node->segmentCount; i++)
- {
- Assert(node->keys[i] == node->segments[i]->left);
- if (itemIndex < node->keys[i])
- {
- break;
- }
- }
- // i indicates the 1st segment in the node past any matching segment.
- // the i'th child is the children to the 'left' of the i'th segment.
- // If itemIndex matches segment i-1 (note that left is always a match even when length == 0)
- bool matches = i > 0 && (itemIndex == node->keys[i-1] || itemIndex < node->keys[i-1] + node->segments[i-1]->length);
- if (matches)
- {
- // Find prev segment
- if (node->IsLeaf())
- {
- if (i > 1)
- {
- // Previous is either sibling or set in a parent
- prev = node->segments[i-2];
- }
- }
- else
- {
- // prev is the right most leaf in children[i-1] tree
- SegmentBTree* child = &node->children[i - 1];
- while (!child->IsLeaf())
- {
- child = &child->children[child->segmentCount];
- }
- prev = child->segments[child->segmentCount - 1];
- }
- // Return the matching segment
- matchOrNext = node->segments[i-1];
- }
- else // itemIndex in between segment i-1 and i
- {
- if (i > 0)
- {
- // Store in previous in case a match or next is the first segment in a child.
- prev = node->segments[i-1];
- }
- if (node->IsLeaf())
- {
- matchOrNext = (i == 0 ? node->segments[0] : prev->next);
- }
- else
- {
- InternalFind(node->children + i, itemIndex, prev, matchOrNext);
- }
- }
- }
- void SegmentBTreeRoot::Find(uint32 itemIndex, SparseArraySegmentBase*& prev, SparseArraySegmentBase*& matchOrNext)
- {
- prev = matchOrNext = NULL;
- InternalFind(this, itemIndex, prev, matchOrNext);
- Assert(prev == NULL || (prev->next == matchOrNext));// If prev exists it is immediately before matchOrNext in the list of arraysegments
- Assert(prev == NULL || (prev->left < itemIndex && prev->left + prev->length <= itemIndex)); // prev should never be a match (left is a match if length == 0)
- Assert(matchOrNext == NULL || (matchOrNext->left >= itemIndex || matchOrNext->left + matchOrNext->length > itemIndex));
- }
- void SegmentBTreeRoot::Add(Recycler* recycler, SparseArraySegmentBase* newSeg)
- {
- if (IsFullNode())
- {
- SegmentBTree * children = AllocatorNewArrayZ(Recycler, recycler, SegmentBTree, MaxDegree);
- children[0] = *this;
- // Even though the segments point to a GC pointer, the main array should keep a references
- // as well. So just make it a leaf allocation
- this->segmentCount = 0;
- this->segments = AllocatorNewArrayLeafZ(Recycler, recycler, SparseArraySegmentBase*, MaxKeys);
- this->keys = AllocatorNewArrayLeafZ(Recycler,recycler,uint32,MaxKeys);
- this->children = children;
- // This split is the only way the tree gets deeper
- SplitChild(recycler, this, 0, &children[0]);
- }
- InsertNonFullNode(recycler, this, newSeg);
- }
- void SegmentBTree::SwapSegment(uint32 originalKey, SparseArraySegmentBase* oldSeg, SparseArraySegmentBase* newSeg)
- {
- // Find old segment
- uint32 itemIndex = originalKey;
- uint32 i = 0;
- for(; i < segmentCount; i++)
- {
- Assert(keys[i] == segments[i]->left || (oldSeg == newSeg && newSeg == segments[i]));
- if (itemIndex < keys[i])
- {
- break;
- }
- }
- // i is 1 past any match
- if (i > 0)
- {
- if (oldSeg == segments[i-1])
- {
- segments[i-1] = newSeg;
- keys[i-1] = newSeg->left;
- return;
- }
- }
- Assert(!IsLeaf());
- children[i].SwapSegment(originalKey, oldSeg, newSeg);
- }
- void SegmentBTree::SplitChild(Recycler* recycler, SegmentBTree* parent, uint32 iChild, SegmentBTree* child)
- {
- // Split child in two, move it's median key up to parent, and put the result of the split
- // on either side of the key moved up into parent
- Assert(child != NULL);
- Assert(parent != NULL);
- Assert(!parent->IsFullNode());
- Assert(child->IsFullNode());
- SegmentBTree newNode;
- newNode.segmentCount = MinKeys;
- // Even though the segments point to a GC pointer, the main array should keep a references
- // as well. So just make it a leaf allocation
- newNode.segments = AllocatorNewArrayLeafZ(Recycler, recycler, SparseArraySegmentBase*, MaxKeys);
- newNode.keys = AllocatorNewArrayLeafZ(Recycler,recycler,uint32,MaxKeys);
- // Move the keys above the median into the new node
- for(uint32 i = 0; i < MinKeys; i++)
- {
- newNode.segments[i] = child->segments[i+MinDegree];
- newNode.keys[i] = child->keys[i+MinDegree];
- // Do not leave false positive references around in the b-tree
- child->segments[i+MinDegree] = NULL;
- }
- // If children exist move those as well.
- if (!child->IsLeaf())
- {
- newNode.children = AllocatorNewArrayZ(Recycler, recycler, SegmentBTree, MaxDegree);
- for(uint32 j = 0; j < MinDegree; j++)
- {
- newNode.children[j] = child->children[j+MinDegree];
- // Do not leave false positive references around in the b-tree
- child->children[j+MinDegree].segments = NULL;
- child->children[j+MinDegree].children = NULL;
- }
- }
- child->segmentCount = MinKeys;
- // Make room for the new child in parent
- for(uint32 j = parent->segmentCount; j > iChild; j--)
- {
- parent->children[j+1] = parent->children[j];
- }
- // Copy the contents of the new node into the correct place in the parent's child array
- parent->children[iChild+1] = newNode;
- // Move the keys to make room for the median key
- for(uint32 k = parent->segmentCount; k > iChild; k--)
- {
- parent->segments[k] = parent->segments[k-1];
- parent->keys[k] = parent->keys[k-1];
- }
- // Move the median key into the proper place in the parent node
- parent->segments[iChild] = child->segments[MinKeys];
- parent->keys[iChild] = child->keys[MinKeys];
- // Do not leave false positive references around in the b-tree
- child->segments[MinKeys] = NULL;
- parent->segmentCount++;
- }
- void SegmentBTree::InsertNonFullNode(Recycler* recycler, SegmentBTree* node, SparseArraySegmentBase* newSeg)
- {
- Assert(!node->IsFullNode());
- AnalysisAssert(node->segmentCount < MaxKeys); // Same as !node->IsFullNode()
- Assert(newSeg != NULL);
- if (node->IsLeaf())
- {
- // Move the keys
- uint32 i = node->segmentCount - 1;
- while( (i != -1) && (newSeg->left < node->keys[i]))
- {
- node->segments[i+1] = node->segments[i];
- node->keys[i+1] = node->keys[i];
- i--;
- }
- if (!node->segments)
- {
- // Even though the segments point to a GC pointer, the main array should keep a references
- // as well. So just make it a leaf allocation
- node->segments = AllocatorNewArrayLeafZ(Recycler, recycler, SparseArraySegmentBase*, MaxKeys);
- node->keys = AllocatorNewArrayLeafZ(Recycler, recycler, uint32, MaxKeys);
- }
- node->segments[i + 1] = newSeg;
- node->keys[i + 1] = newSeg->left;
- node->segmentCount++;
- }
- else
- {
- // find the correct child node
- uint32 i = node->segmentCount-1;
- while((i != -1) && (newSeg->left < node->keys[i]))
- {
- i--;
- }
- i++;
- // Make room if full
- if(node->children[i].IsFullNode())
- {
- // This split doesn't make the tree any deeper as node already has children.
- SplitChild(recycler, node, i, node->children+i);
- Assert(node->keys[i] == node->segments[i]->left);
- if (newSeg->left > node->keys[i])
- {
- i++;
- }
- }
- InsertNonFullNode(recycler, node->children+i, newSeg);
- }
- }
- inline void ThrowTypeErrorOnFailureHelper::ThrowTypeErrorOnFailure(BOOL operationSucceeded)
- {
- if (IsThrowTypeError(operationSucceeded))
- {
- ThrowTypeErrorOnFailure();
- }
- }
- inline void ThrowTypeErrorOnFailureHelper::ThrowTypeErrorOnFailure()
- {
- JavascriptError::ThrowTypeError(m_scriptContext, VBSERR_ActionNotSupported, m_functionName);
- }
- inline BOOL ThrowTypeErrorOnFailureHelper::IsThrowTypeError(BOOL operationSucceeded)
- {
- return !operationSucceeded;
- }
- // Make sure EmptySegment points to read-only memory.
- // Can't do this the easy way because SparseArraySegment has a constructor...
- JavascriptArray::JavascriptArray(DynamicType * type)
- : ArrayObject(type, false, 0)
- {
- Assert(type->GetTypeId() == TypeIds_Array || type->GetTypeId() == TypeIds_NativeIntArray || type->GetTypeId() == TypeIds_NativeFloatArray || ((type->GetTypeId() == TypeIds_ES5Array || type->GetTypeId() == TypeIds_Object) && type->GetPrototype() == GetScriptContext()->GetLibrary()->GetArrayPrototype()));
- Assert(EmptySegment->length == 0 && EmptySegment->size == 0 && EmptySegment->next == NULL);
- InitArrayFlags(DynamicObjectFlags::InitialArrayValue);
- SetHeadAndLastUsedSegment(const_cast<SparseArraySegmentBase *>(EmptySegment));
- }
- JavascriptArray::JavascriptArray(uint32 length, DynamicType * type)
- : ArrayObject(type, false, length)
- {
- Assert(JavascriptArray::Is(type->GetTypeId()));
- Assert(EmptySegment->length == 0 && EmptySegment->size == 0 && EmptySegment->next == NULL);
- InitArrayFlags(DynamicObjectFlags::InitialArrayValue);
- SetHeadAndLastUsedSegment(const_cast<SparseArraySegmentBase *>(EmptySegment));
- }
- JavascriptArray::JavascriptArray(uint32 length, uint32 size, DynamicType * type)
- : ArrayObject(type, false, length)
- {
- Assert(type->GetTypeId() == TypeIds_Array);
- InitArrayFlags(DynamicObjectFlags::InitialArrayValue);
- Recycler* recycler = GetRecycler();
- SetHeadAndLastUsedSegment(SparseArraySegment<Var>::AllocateSegment(recycler, 0, 0, size, nullptr));
- }
- JavascriptArray::JavascriptArray(DynamicType * type, uint32 size)
- : ArrayObject(type, false)
- {
- InitArrayFlags(DynamicObjectFlags::InitialArrayValue);
- SetHeadAndLastUsedSegment(DetermineInlineHeadSegmentPointer<JavascriptArray, 0, false>(this));
- head->size = size;
- Var fill = Js::JavascriptArray::MissingItem;
- for (uint i = 0; i < size; i++)
- {
- ((SparseArraySegment<Var>*)head)->elements[i] = fill;
- }
- }
- JavascriptNativeIntArray::JavascriptNativeIntArray(uint32 length, uint32 size, DynamicType * type)
- : JavascriptNativeArray(type)
- {
- Assert(type->GetTypeId() == TypeIds_NativeIntArray);
- this->length = length;
- Recycler* recycler = GetRecycler();
- SetHeadAndLastUsedSegment(SparseArraySegment<int32>::AllocateSegment(recycler, 0, 0, size, nullptr));
- }
- JavascriptNativeIntArray::JavascriptNativeIntArray(DynamicType * type, uint32 size)
- : JavascriptNativeArray(type)
- {
- SetHeadAndLastUsedSegment(DetermineInlineHeadSegmentPointer<JavascriptNativeIntArray, 0, false>(this));
- head->size = size;
- ((SparseArraySegment<int32>*)head)->FillSegmentBuffer(0, size);
- }
- JavascriptNativeFloatArray::JavascriptNativeFloatArray(uint32 length, uint32 size, DynamicType * type)
- : JavascriptNativeArray(type)
- {
- Assert(type->GetTypeId() == TypeIds_NativeFloatArray);
- this->length = length;
- Recycler* recycler = GetRecycler();
- SetHeadAndLastUsedSegment(SparseArraySegment<double>::AllocateSegment(recycler, 0, 0, size, nullptr));
- }
- JavascriptNativeFloatArray::JavascriptNativeFloatArray(DynamicType * type, uint32 size)
- : JavascriptNativeArray(type)
- {
- SetHeadAndLastUsedSegment(DetermineInlineHeadSegmentPointer<JavascriptNativeFloatArray, 0, false>(this));
- head->size = size;
- ((SparseArraySegment<double>*)head)->FillSegmentBuffer(0, size);
- }
- bool JavascriptArray::Is(Var aValue)
- {
- TypeId typeId = JavascriptOperators::GetTypeId(aValue);
- return JavascriptArray::Is(typeId);
- }
- bool JavascriptArray::Is(TypeId typeId)
- {
- return typeId >= TypeIds_ArrayFirst && typeId <= TypeIds_ArrayLast;
- }
- bool JavascriptArray::IsVarArray(Var aValue)
- {
- TypeId typeId = JavascriptOperators::GetTypeId(aValue);
- return JavascriptArray::IsVarArray(typeId);
- }
- bool JavascriptArray::IsVarArray(TypeId typeId)
- {
- return typeId == TypeIds_Array;
- }
- JavascriptArray* JavascriptArray::FromVar(Var aValue)
- {
- AssertMsg(Is(aValue), "Ensure var is actually a 'JavascriptArray'");
- return static_cast<JavascriptArray *>(RecyclableObject::FromVar(aValue));
- }
- // Get JavascriptArray* from a Var, which is either a JavascriptArray* or ESArray*.
- JavascriptArray* JavascriptArray::FromAnyArray(Var aValue)
- {
- AssertMsg(Is(aValue) || ES5Array::Is(aValue), "Ensure var is actually a 'JavascriptArray' or 'ES5Array'");
- return static_cast<JavascriptArray *>(RecyclableObject::FromVar(aValue));
- }
- // Check if a Var is a direct-accessible (fast path) JavascriptArray.
- bool JavascriptArray::IsDirectAccessArray(Var aValue)
- {
- return RecyclableObject::Is(aValue) &&
- (VirtualTableInfo<JavascriptArray>::HasVirtualTable(aValue) ||
- VirtualTableInfo<JavascriptNativeIntArray>::HasVirtualTable(aValue) ||
- VirtualTableInfo<JavascriptNativeFloatArray>::HasVirtualTable(aValue));
- }
- DynamicObjectFlags JavascriptArray::GetFlags() const
- {
- return GetArrayFlags();
- }
- DynamicObjectFlags JavascriptArray::GetFlags_Unchecked() const // do not use except in extreme circumstances
- {
- return GetArrayFlags_Unchecked();
- }
- void JavascriptArray::SetFlags(const DynamicObjectFlags flags)
- {
- SetArrayFlags(flags);
- }
- DynamicType * JavascriptArray::GetInitialType(ScriptContext * scriptContext)
- {
- return scriptContext->GetLibrary()->GetArrayType();
- }
- JavascriptArray *JavascriptArray::GetArrayForArrayOrObjectWithArray(const Var var)
- {
- bool isObjectWithArray;
- TypeId arrayTypeId;
- return GetArrayForArrayOrObjectWithArray(var, &isObjectWithArray, &arrayTypeId);
- }
- JavascriptArray *JavascriptArray::GetArrayForArrayOrObjectWithArray(
- const Var var,
- bool *const isObjectWithArrayRef,
- TypeId *const arrayTypeIdRef)
- {
- // This is a helper function used by jitted code. The array checks done here match the array checks done by jitted code
- // (see Lowerer::GenerateArrayTest) to minimize bailouts.
- Assert(var);
- Assert(isObjectWithArrayRef);
- Assert(arrayTypeIdRef);
- *isObjectWithArrayRef = false;
- *arrayTypeIdRef = TypeIds_Undefined;
- if(!RecyclableObject::Is(var))
- {
- return nullptr;
- }
- JavascriptArray *array = nullptr;
- INT_PTR vtable = VirtualTableInfoBase::GetVirtualTable(var);
- if(vtable == VirtualTableInfo<DynamicObject>::Address)
- {
- ArrayObject* objectArray = DynamicObject::FromVar(var)->GetObjectArray();
- array = (objectArray && Is(objectArray)) ? FromVar(objectArray) : nullptr;
- if(!array)
- {
- return nullptr;
- }
- *isObjectWithArrayRef = true;
- vtable = VirtualTableInfoBase::GetVirtualTable(array);
- }
- if(vtable == VirtualTableInfo<JavascriptArray>::Address)
- {
- *arrayTypeIdRef = TypeIds_Array;
- }
- else if(vtable == VirtualTableInfo<JavascriptNativeIntArray>::Address)
- {
- *arrayTypeIdRef = TypeIds_NativeIntArray;
- }
- else if(vtable == VirtualTableInfo<JavascriptNativeFloatArray>::Address)
- {
- *arrayTypeIdRef = TypeIds_NativeFloatArray;
- }
- else
- {
- return nullptr;
- }
- if(!array)
- {
- array = FromVar(var);
- }
- return array;
- }
- const SparseArraySegmentBase *JavascriptArray::Jit_GetArrayHeadSegmentForArrayOrObjectWithArray(const Var var)
- {
- // This is a helper function used by jitted code
- JavascriptArray *const array = GetArrayForArrayOrObjectWithArray(var);
- return array ? array->head : nullptr;
- }
- uint32 JavascriptArray::Jit_GetArrayHeadSegmentLength(const SparseArraySegmentBase *const headSegment)
- {
- // This is a helper function used by jitted code
- return headSegment ? headSegment->length : 0;
- }
- bool JavascriptArray::Jit_OperationInvalidatedArrayHeadSegment(
- const SparseArraySegmentBase *const headSegmentBeforeOperation,
- const uint32 headSegmentLengthBeforeOperation,
- const Var varAfterOperation)
- {
- // This is a helper function used by jitted code
- Assert(varAfterOperation);
- if(!headSegmentBeforeOperation)
- {
- return false;
- }
- const SparseArraySegmentBase *const headSegmentAfterOperation =
- Jit_GetArrayHeadSegmentForArrayOrObjectWithArray(varAfterOperation);
- return
- headSegmentAfterOperation != headSegmentBeforeOperation ||
- headSegmentAfterOperation->length != headSegmentLengthBeforeOperation;
- }
- uint32 JavascriptArray::Jit_GetArrayLength(const Var var)
- {
- // This is a helper function used by jitted code
- bool isObjectWithArray;
- TypeId arrayTypeId;
- JavascriptArray *const array = GetArrayForArrayOrObjectWithArray(var, &isObjectWithArray, &arrayTypeId);
- return array && !isObjectWithArray ? array->GetLength() : 0;
- }
- bool JavascriptArray::Jit_OperationInvalidatedArrayLength(const uint32 lengthBeforeOperation, const Var varAfterOperation)
- {
- // This is a helper function used by jitted code
- return Jit_GetArrayLength(varAfterOperation) != lengthBeforeOperation;
- }
- DynamicObjectFlags JavascriptArray::Jit_GetArrayFlagsForArrayOrObjectWithArray(const Var var)
- {
- // This is a helper function used by jitted code
- JavascriptArray *const array = GetArrayForArrayOrObjectWithArray(var);
- return array && array->UsesObjectArrayOrFlagsAsFlags() ? array->GetFlags() : DynamicObjectFlags::None;
- }
- bool JavascriptArray::Jit_OperationCreatedFirstMissingValue(
- const DynamicObjectFlags flagsBeforeOperation,
- const Var varAfterOperation)
- {
- // This is a helper function used by jitted code
- Assert(varAfterOperation);
- return
- !!(flagsBeforeOperation & DynamicObjectFlags::HasNoMissingValues) &&
- !(Jit_GetArrayFlagsForArrayOrObjectWithArray(varAfterOperation) & DynamicObjectFlags::HasNoMissingValues);
- }
- bool JavascriptArray::HasNoMissingValues() const
- {
- return !!(GetFlags() & DynamicObjectFlags::HasNoMissingValues);
- }
- bool JavascriptArray::HasNoMissingValues_Unchecked() const // do not use except in extreme circumstances
- {
- return !!(GetFlags_Unchecked() & DynamicObjectFlags::HasNoMissingValues);
- }
- void JavascriptArray::SetHasNoMissingValues(const bool hasNoMissingValues)
- {
- SetFlags(
- hasNoMissingValues
- ? GetFlags() | DynamicObjectFlags::HasNoMissingValues
- : GetFlags() & ~DynamicObjectFlags::HasNoMissingValues);
- }
- template<class T>
- bool JavascriptArray::IsMissingHeadSegmentItemImpl(const uint32 index) const
- {
- Assert(index < head->length);
- return SparseArraySegment<T>::IsMissingItem(&static_cast<SparseArraySegment<T> *>(head)->elements[index]);
- }
- bool JavascriptArray::IsMissingHeadSegmentItem(const uint32 index) const
- {
- return IsMissingHeadSegmentItemImpl<Var>(index);
- }
- #if ENABLE_COPYONACCESS_ARRAY
- void JavascriptCopyOnAccessNativeIntArray::ConvertCopyOnAccessSegment()
- {
- Assert(this->GetScriptContext()->GetLibrary()->cacheForCopyOnAccessArraySegments->IsValidIndex(::Math::PointerCastToIntegral<uint32>(this->GetHead())));
- SparseArraySegment<int32> *seg = this->GetScriptContext()->GetLibrary()->cacheForCopyOnAccessArraySegments->GetSegmentByIndex(::Math::PointerCastToIntegral<byte>(this->GetHead()));
- SparseArraySegment<int32> *newSeg = SparseArraySegment<int32>::AllocateLiteralHeadSegment(this->GetRecycler(), seg->length);
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- if (Js::Configuration::Global.flags.TestTrace.IsEnabled(Js::CopyOnAccessArrayPhase))
- {
- Output::Print(_u("Convert copy-on-access array: index(%d) length(%d)\n"), this->GetHead(), seg->length);
- Output::Flush();
- }
- #endif
- newSeg->CopySegment(this->GetRecycler(), newSeg, 0, seg, 0, seg->length);
- this->SetHeadAndLastUsedSegment(newSeg);
- VirtualTableInfo<JavascriptNativeIntArray>::SetVirtualTable(this);
- this->type = JavascriptNativeIntArray::GetInitialType(this->GetScriptContext());
- ArrayCallSiteInfo *arrayInfo = this->GetArrayCallSiteInfo();
- if (arrayInfo && !arrayInfo->isNotCopyOnAccessArray)
- {
- arrayInfo->isNotCopyOnAccessArray = 1;
- }
- }
- uint32 JavascriptCopyOnAccessNativeIntArray::GetNextIndex(uint32 index) const
- {
- if (this->length == 0 || (index != Js::JavascriptArray::InvalidIndex && index >= this->length))
- {
- return Js::JavascriptArray::InvalidIndex;
- }
- else if (index == Js::JavascriptArray::InvalidIndex)
- {
- return 0;
- }
- else
- {
- return index + 1;
- }
- }
- BOOL JavascriptCopyOnAccessNativeIntArray::DirectGetItemAt(uint32 index, int* outVal)
- {
- Assert(this->GetScriptContext()->GetLibrary()->cacheForCopyOnAccessArraySegments->IsValidIndex(::Math::PointerCastToIntegral<uint32>(this->GetHead())));
- SparseArraySegment<int32> *seg = this->GetScriptContext()->GetLibrary()->cacheForCopyOnAccessArraySegments->GetSegmentByIndex(::Math::PointerCastToIntegral<byte>(this->GetHead()));
- if (this->length == 0 || index == Js::JavascriptArray::InvalidIndex || index >= this->length)
- {
- return FALSE;
- }
- else
- {
- *outVal = seg->elements[index];
- return TRUE;
- }
- }
- #endif
- bool JavascriptNativeIntArray::IsMissingHeadSegmentItem(const uint32 index) const
- {
- return IsMissingHeadSegmentItemImpl<int32>(index);
- }
- bool JavascriptNativeFloatArray::IsMissingHeadSegmentItem(const uint32 index) const
- {
- return IsMissingHeadSegmentItemImpl<double>(index);
- }
- /* static */
- bool JavascriptArray::HasInlineHeadSegment(uint32 length)
- {
- return length <= SparseArraySegmentBase::INLINE_CHUNK_SIZE;
- }
- Var JavascriptArray::OP_NewScArray(uint32 elementCount, ScriptContext* scriptContext)
- {
- // Called only to create array literals: size is known.
- return scriptContext->GetLibrary()->CreateArrayLiteral(elementCount);
- }
- Var JavascriptArray::OP_NewScArrayWithElements(uint32 elementCount, Var *elements, ScriptContext* scriptContext)
- {
- // Called only to create array literals: size is known.
- JavascriptArray *arr = scriptContext->GetLibrary()->CreateArrayLiteral(elementCount);
- SparseArraySegment<Var> *head = (SparseArraySegment<Var>*)arr->head;
- Assert(elementCount <= head->length);
- js_memcpy_s(head->elements, sizeof(Var) * head->length, elements, sizeof(Var) * elementCount);
- #ifdef VALIDATE_ARRAY
- arr->ValidateArray();
- #endif
- return arr;
- }
- Var JavascriptArray::OP_NewScArrayWithMissingValues(uint32 elementCount, ScriptContext* scriptContext)
- {
- // Called only to create array literals: size is known.
- JavascriptArray *const array = static_cast<JavascriptArray *>(OP_NewScArray(elementCount, scriptContext));
- array->SetHasNoMissingValues(false);
- SparseArraySegment<Var> *head = (SparseArraySegment<Var>*)array->head;
- head->FillSegmentBuffer(0, elementCount);
- return array;
- }
- #if ENABLE_PROFILE_INFO
- Var JavascriptArray::ProfiledNewScArray(uint32 elementCount, ScriptContext *scriptContext, ArrayCallSiteInfo *arrayInfo, RecyclerWeakReference<FunctionBody> *weakFuncRef)
- {
- if (arrayInfo->IsNativeIntArray())
- {
- JavascriptNativeIntArray *arr = scriptContext->GetLibrary()->CreateNativeIntArrayLiteral(elementCount);
- arr->SetArrayProfileInfo(weakFuncRef, arrayInfo);
- return arr;
- }
- if (arrayInfo->IsNativeFloatArray())
- {
- JavascriptNativeFloatArray *arr = scriptContext->GetLibrary()->CreateNativeFloatArrayLiteral(elementCount);
- arr->SetArrayProfileInfo(weakFuncRef, arrayInfo);
- return arr;
- }
- JavascriptArray *arr = scriptContext->GetLibrary()->CreateArrayLiteral(elementCount);
- return arr;
- }
- #endif
- Var JavascriptArray::OP_NewScIntArray(AuxArray<int32> *ints, ScriptContext* scriptContext)
- {
- uint32 count = ints->count;
- JavascriptArray *arr = scriptContext->GetLibrary()->CreateArrayLiteral(count);
- SparseArraySegment<Var> *head = (SparseArraySegment<Var>*)arr->head;
- Assert(count > 0 && count == head->length);
- for (uint i = 0; i < count; i++)
- {
- head->elements[i] = JavascriptNumber::ToVar(ints->elements[i], scriptContext);
- }
- return arr;
- }
- #if ENABLE_PROFILE_INFO
- Var JavascriptArray::ProfiledNewScIntArray(AuxArray<int32> *ints, ScriptContext* scriptContext, ArrayCallSiteInfo *arrayInfo, RecyclerWeakReference<FunctionBody> *weakFuncRef)
- {
- // Called only to create array literals: size is known.
- uint32 count = ints->count;
- if (arrayInfo->IsNativeIntArray())
- {
- JavascriptNativeIntArray *arr;
- FunctionBody *functionBody = weakFuncRef->Get();
- JavascriptLibrary *lib = scriptContext->GetLibrary();
- #if ENABLE_COPYONACCESS_ARRAY
- if (JavascriptLibrary::IsCopyOnAccessArrayCallSite(lib, arrayInfo, count))
- {
- Assert(lib->cacheForCopyOnAccessArraySegments);
- arr = scriptContext->GetLibrary()->CreateCopyOnAccessNativeIntArrayLiteral(arrayInfo, functionBody, ints);
- }
- else
- #endif
- {
- arr = scriptContext->GetLibrary()->CreateNativeIntArrayLiteral(count);
- SparseArraySegment<int32> *head = static_cast<SparseArraySegment<int32>*>(arr->head);
- Assert(count > 0 && count == head->length);
- js_memcpy_s(head->elements, sizeof(int32)* head->length, ints->elements, sizeof(int32)* count);
- }
- arr->SetArrayProfileInfo(weakFuncRef, arrayInfo);
- return arr;
- }
- if (arrayInfo->IsNativeFloatArray())
- {
- JavascriptNativeFloatArray *arr = scriptContext->GetLibrary()->CreateNativeFloatArrayLiteral(count);
- SparseArraySegment<double> *head = (SparseArraySegment<double>*)arr->head;
- Assert(count > 0 && count == head->length);
- for (uint i = 0; i < count; i++)
- {
- head->elements[i] = (double)ints->elements[i];
- }
- arr->SetArrayProfileInfo(weakFuncRef, arrayInfo);
- return arr;
- }
- return OP_NewScIntArray(ints, scriptContext);
- }
- #endif
- Var JavascriptArray::OP_NewScFltArray(AuxArray<double> *doubles, ScriptContext* scriptContext)
- {
- uint32 count = doubles->count;
- JavascriptArray *arr = scriptContext->GetLibrary()->CreateArrayLiteral(count);
- SparseArraySegment<Var> *head = (SparseArraySegment<Var>*)arr->head;
- Assert(count > 0 && count == head->length);
- for (uint i = 0; i < count; i++)
- {
- double dval = doubles->elements[i];
- int32 ival;
- if (JavascriptNumber::TryGetInt32Value(dval, &ival) && !TaggedInt::IsOverflow(ival))
- {
- head->elements[i] = TaggedInt::ToVarUnchecked(ival);
- }
- else
- {
- head->elements[i] = JavascriptNumber::ToVarNoCheck(dval, scriptContext);
- }
- }
- return arr;
- }
- #if ENABLE_PROFILE_INFO
- Var JavascriptArray::ProfiledNewScFltArray(AuxArray<double> *doubles, ScriptContext* scriptContext, ArrayCallSiteInfo *arrayInfo, RecyclerWeakReference<FunctionBody> *weakFuncRef)
- {
- // Called only to create array literals: size is known.
- if (arrayInfo->IsNativeFloatArray())
- {
- arrayInfo->SetIsNotNativeIntArray();
- uint32 count = doubles->count;
- JavascriptNativeFloatArray *arr = scriptContext->GetLibrary()->CreateNativeFloatArrayLiteral(count);
- SparseArraySegment<double> *head = (SparseArraySegment<double>*)arr->head;
- Assert(count > 0 && count == head->length);
- js_memcpy_s(head->elements, sizeof(double) * head->length, doubles->elements, sizeof(double) * count);
- arr->SetArrayProfileInfo(weakFuncRef, arrayInfo);
- return arr;
- }
- return OP_NewScFltArray(doubles, scriptContext);
- }
- Var JavascriptArray::ProfiledNewInstance(RecyclableObject* function, CallInfo callInfo, ...)
- {
- ARGUMENTS(args, callInfo);
- Assert(JavascriptFunction::Is(function) &&
- JavascriptFunction::FromVar(function)->GetFunctionInfo() == &JavascriptArray::EntryInfo::NewInstance);
- Assert(callInfo.Count >= 2);
- ArrayCallSiteInfo *arrayInfo = (ArrayCallSiteInfo*)args[0];
- JavascriptArray* pNew = nullptr;
- if (callInfo.Count == 2)
- {
- // Exactly one argument, which is the array length if it's a uint32.
- Var firstArgument = args[1];
- int elementCount;
- if (TaggedInt::Is(firstArgument))
- {
- elementCount = TaggedInt::ToInt32(firstArgument);
- if (elementCount < 0)
- {
- JavascriptError::ThrowRangeError(function->GetScriptContext(), JSERR_ArrayLengthConstructIncorrect);
- }
- if (arrayInfo && arrayInfo->IsNativeArray())
- {
- if (arrayInfo->IsNativeIntArray())
- {
- pNew = function->GetLibrary()->CreateNativeIntArray(elementCount);
- }
- else
- {
- pNew = function->GetLibrary()->CreateNativeFloatArray(elementCount);
- }
- }
- else
- {
- pNew = function->GetLibrary()->CreateArray(elementCount);
- }
- }
- else if (JavascriptNumber::Is_NoTaggedIntCheck(firstArgument))
- {
- // Non-tagged-int number: make sure the double value is really a uint32.
- double value = JavascriptNumber::GetValue(firstArgument);
- uint32 uvalue = JavascriptConversion::ToUInt32(value);
- if (value != uvalue)
- {
- JavascriptError::ThrowRangeError(function->GetScriptContext(), JSERR_ArrayLengthConstructIncorrect);
- }
- if (arrayInfo && arrayInfo->IsNativeArray())
- {
- if (arrayInfo->IsNativeIntArray())
- {
- pNew = function->GetLibrary()->CreateNativeIntArray(uvalue);
- }
- else
- {
- pNew = function->GetLibrary()->CreateNativeFloatArray(uvalue);
- }
- }
- else
- {
- pNew = function->GetLibrary()->CreateArray(uvalue);
- }
- }
- else
- {
- //
- // First element is not int/double
- // create an array of length 1.
- // Set first element as the passed Var
- //
- pNew = function->GetLibrary()->CreateArray(1);
- pNew->DirectSetItemAt<Var>(0, firstArgument);
- }
- }
- else
- {
- // Called with a list of initial element values.
- // Create an array of the appropriate length and walk the list.
- if (arrayInfo && arrayInfo->IsNativeArray())
- {
- if (arrayInfo->IsNativeIntArray())
- {
- pNew = function->GetLibrary()->CreateNativeIntArray(callInfo.Count - 1);
- }
- else
- {
- pNew = function->GetLibrary()->CreateNativeFloatArray(callInfo.Count - 1);
- }
- }
- else
- {
- pNew = function->GetLibrary()->CreateArray(callInfo.Count - 1);
- }
- pNew->FillFromArgs(callInfo.Count - 1, 0, args.Values, arrayInfo);
- }
- #ifdef VALIDATE_ARRAY
- pNew->ValidateArray();
- #endif
- return pNew;
- }
- #endif
- Var JavascriptArray::NewInstance(RecyclableObject* function, CallInfo callInfo, ...)
- {
- ARGUMENTS(args, callInfo);
- return NewInstance(function, args);
- }
- Var JavascriptArray::NewInstance(RecyclableObject* function, Arguments args)
- {
- // Call to new Array(), possibly under another name.
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- // SkipDefaultNewObject function flag should have prevented the default object
- // being created, except when call true a host dispatch.
- const CallInfo &callInfo = args.Info;
- Var newTarget = callInfo.Flags & CallFlags_NewTarget ? args.Values[args.Info.Count] : args[0];
- bool isCtorSuperCall = (callInfo.Flags & CallFlags_New) && newTarget != nullptr && !JavascriptOperators::IsUndefined(newTarget);
- Assert( isCtorSuperCall || !(callInfo.Flags & CallFlags_New) || args[0] == nullptr
- || JavascriptOperators::GetTypeId(args[0]) == TypeIds_HostDispatch);
- ScriptContext* scriptContext = function->GetScriptContext();
- JavascriptArray* pNew = nullptr;
- if (callInfo.Count < 2)
- {
- if (pNew == nullptr)
- {
- // No arguments passed to Array(), so create with the default size (0).
- pNew = CreateArrayFromConstructor(function, 0, scriptContext);
- }
- else
- {
- pNew->SetLength((uint32)0);
- }
- return isCtorSuperCall ?
- JavascriptOperators::OrdinaryCreateFromConstructor(RecyclableObject::FromVar(newTarget), pNew, nullptr, scriptContext) :
- pNew;
- }
- if (callInfo.Count == 2)
- {
- // Exactly one argument, which is the array length if it's a uint32.
- Var firstArgument = args[1];
- int elementCount;
- if (TaggedInt::Is(firstArgument))
- {
- elementCount = TaggedInt::ToInt32(firstArgument);
- if (elementCount < 0)
- {
- JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthConstructIncorrect);
- }
- if (pNew == nullptr)
- {
- pNew = CreateArrayFromConstructor(function, elementCount, scriptContext);
- }
- else
- {
- pNew->SetLength(elementCount);
- }
- }
- else if (JavascriptNumber::Is_NoTaggedIntCheck(firstArgument))
- {
- // Non-tagged-int number: make sure the double value is really a uint32.
- double value = JavascriptNumber::GetValue(firstArgument);
- uint32 uvalue = JavascriptConversion::ToUInt32(value);
- if (value != uvalue)
- {
- JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthConstructIncorrect);
- }
- if (pNew == nullptr)
- {
- pNew = CreateArrayFromConstructor(function, uvalue, scriptContext);
- }
- else
- {
- pNew->SetLength(uvalue);
- }
- }
- else
- {
- //
- // First element is not int/double
- // create an array of length 1.
- // Set first element as the passed Var
- //
- if (pNew == nullptr)
- {
- pNew = CreateArrayFromConstructor(function, 1, scriptContext);
- }
- JavascriptOperators::SetItem(pNew, pNew, 0u, firstArgument, scriptContext, PropertyOperation_ThrowIfNotExtensible);
- // If we were passed an uninitialized JavascriptArray as the this argument,
- // we need to set the length. We must do this _after_ setting the first
- // element as the array may have side effects such as a setter for property
- // named '0' which would make the previous length of the array observable.
- // If we weren't passed a JavascriptArray as the this argument, this is no-op.
- pNew->SetLength(1);
- }
- }
- else
- {
- // Called with a list of initial element values.
- // Create an array of the appropriate length and walk the list.
- if (pNew == nullptr)
- {
- pNew = CreateArrayFromConstructor(function, callInfo.Count - 1, scriptContext);
- }
- else
- {
- // If we were passed an uninitialized JavascriptArray as the this argument,
- // we need to set the length. We should do this _after_ setting the
- // elements as the array may have side effects such as a setter for property
- // named '0' which would make the previous length of the array observable.
- // Note: We don't support this case now as the DirectSetItemAt calls in FillFromArgs
- // will not call the setter. Need to refactor that method.
- pNew->SetLength(callInfo.Count - 1);
- }
- pNew->JavascriptArray::FillFromArgs(callInfo.Count - 1, 0, args.Values);
- }
- #ifdef VALIDATE_ARRAY
- pNew->ValidateArray();
- #endif
- return isCtorSuperCall ?
- JavascriptOperators::OrdinaryCreateFromConstructor(RecyclableObject::FromVar(newTarget), pNew, nullptr, scriptContext) :
- pNew;
- }
- JavascriptArray* JavascriptArray::CreateArrayFromConstructor(RecyclableObject* constructor, uint32 length, ScriptContext* scriptContext)
- {
- JavascriptLibrary* library = constructor->GetLibrary();
- // Create the Array object we'll return - this is the only way to create an object which is an exotic Array object.
- // Note: We need to use the library from the ScriptContext of the constructor, not the currently executing function.
- // This is for the case where a built-in @@create method from a different JavascriptLibrary is installed on
- // constructor.
- JavascriptArray* arr = library->CreateArray(length);
- return arr;
- }
- #if ENABLE_PROFILE_INFO
- Var JavascriptArray::ProfiledNewInstanceNoArg(RecyclableObject *function, ScriptContext *scriptContext, ArrayCallSiteInfo *arrayInfo, RecyclerWeakReference<FunctionBody> *weakFuncRef)
- {
- Assert(JavascriptFunction::Is(function) &&
- JavascriptFunction::FromVar(function)->GetFunctionInfo() == &JavascriptArray::EntryInfo::NewInstance);
- if (arrayInfo->IsNativeIntArray())
- {
- JavascriptNativeIntArray *arr = scriptContext->GetLibrary()->CreateNativeIntArray();
- arr->SetArrayProfileInfo(weakFuncRef, arrayInfo);
- return arr;
- }
- if (arrayInfo->IsNativeFloatArray())
- {
- JavascriptNativeFloatArray *arr = scriptContext->GetLibrary()->CreateNativeFloatArray();
- arr->SetArrayProfileInfo(weakFuncRef, arrayInfo);
- return arr;
- }
- return scriptContext->GetLibrary()->CreateArray();
- }
- #endif
- Var JavascriptNativeIntArray::NewInstance(RecyclableObject* function, CallInfo callInfo, ...)
- {
- ARGUMENTS(args, callInfo);
- return NewInstance(function, args);
- }
- Var JavascriptNativeIntArray::NewInstance(RecyclableObject* function, Arguments args)
- {
- Assert(!PHASE_OFF1(NativeArrayPhase));
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- const CallInfo &callInfo = args.Info;
- if (callInfo.Count < 2)
- {
- // No arguments passed to Array(), so create with the default size (0).
- return function->GetLibrary()->CreateNativeIntArray();
- }
- JavascriptArray* pNew = nullptr;
- if (callInfo.Count == 2)
- {
- // Exactly one argument, which is the array length if it's a uint32.
- Var firstArgument = args[1];
- int elementCount;
- if (TaggedInt::Is(firstArgument))
- {
- elementCount = TaggedInt::ToInt32(firstArgument);
- if (elementCount < 0)
- {
- JavascriptError::ThrowRangeError(
- function->GetScriptContext(), JSERR_ArrayLengthConstructIncorrect);
- }
- pNew = function->GetLibrary()->CreateNativeIntArray(elementCount);
- }
- else if (JavascriptNumber::Is_NoTaggedIntCheck(firstArgument))
- {
- // Non-tagged-int number: make sure the double value is really a uint32.
- double value = JavascriptNumber::GetValue(firstArgument);
- uint32 uvalue = JavascriptConversion::ToUInt32(value);
- if (value != uvalue)
- {
- JavascriptError::ThrowRangeError(
- function->GetScriptContext(), JSERR_ArrayLengthConstructIncorrect);
- }
- pNew = function->GetLibrary()->CreateNativeIntArray(uvalue);
- }
- else
- {
- //
- // First element is not int/double
- // create an array of length 1.
- // Set first element as the passed Var
- //
- pNew = function->GetLibrary()->CreateArray(1);
- pNew->DirectSetItemAt<Var>(0, firstArgument);
- }
- }
- else
- {
- // Called with a list of initial element values.
- // Create an array of the appropriate length and walk the list.
- JavascriptNativeIntArray *arr = function->GetLibrary()->CreateNativeIntArray(callInfo.Count - 1);
- pNew = arr->FillFromArgs(callInfo.Count - 1, 0, args.Values);
- }
- #ifdef VALIDATE_ARRAY
- pNew->ValidateArray();
- #endif
- return pNew;
- }
- Var JavascriptNativeFloatArray::NewInstance(RecyclableObject* function, CallInfo callInfo, ...)
- {
- ARGUMENTS(args, callInfo);
- return NewInstance(function, args);
- }
- Var JavascriptNativeFloatArray::NewInstance(RecyclableObject* function, Arguments args)
- {
- Assert(!PHASE_OFF1(NativeArrayPhase));
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- const CallInfo &callInfo = args.Info;
- if (callInfo.Count < 2)
- {
- // No arguments passed to Array(), so create with the default size (0).
- return function->GetLibrary()->CreateNativeFloatArray();
- }
- JavascriptArray* pNew = nullptr;
- if (callInfo.Count == 2)
- {
- // Exactly one argument, which is the array length if it's a uint32.
- Var firstArgument = args[1];
- int elementCount;
- if (TaggedInt::Is(firstArgument))
- {
- elementCount = TaggedInt::ToInt32(firstArgument);
- if (elementCount < 0)
- {
- JavascriptError::ThrowRangeError(
- function->GetScriptContext(), JSERR_ArrayLengthConstructIncorrect);
- }
- pNew = function->GetLibrary()->CreateNativeFloatArray(elementCount);
- }
- else if (JavascriptNumber::Is_NoTaggedIntCheck(firstArgument))
- {
- // Non-tagged-int number: make sure the double value is really a uint32.
- double value = JavascriptNumber::GetValue(firstArgument);
- uint32 uvalue = JavascriptConversion::ToUInt32(value);
- if (value != uvalue)
- {
- JavascriptError::ThrowRangeError(
- function->GetScriptContext(), JSERR_ArrayLengthConstructIncorrect);
- }
- pNew = function->GetLibrary()->CreateNativeFloatArray(uvalue);
- }
- else
- {
- //
- // First element is not int/double
- // create an array of length 1.
- // Set first element as the passed Var
- //
- pNew = function->GetLibrary()->CreateArray(1);
- pNew->DirectSetItemAt<Var>(0, firstArgument);
- }
- }
- else
- {
- // Called with a list of initial element values.
- // Create an array of the appropriate length and walk the list.
- JavascriptNativeFloatArray *arr = function->GetLibrary()->CreateNativeFloatArray(callInfo.Count - 1);
- pNew = arr->FillFromArgs(callInfo.Count - 1, 0, args.Values);
- }
- #ifdef VALIDATE_ARRAY
- pNew->ValidateArray();
- #endif
- return pNew;
- }
- #if ENABLE_PROFILE_INFO
- JavascriptArray * JavascriptNativeIntArray::FillFromArgs(uint length, uint start, Var *args, ArrayCallSiteInfo *arrayInfo, bool dontCreateNewArray)
- #else
- JavascriptArray * JavascriptNativeIntArray::FillFromArgs(uint length, uint start, Var *args, bool dontCreateNewArray)
- #endif
- {
- uint i;
- for (i = start; i < length; i++)
- {
- Var item = args[i + 1];
- bool isTaggedInt = TaggedInt::Is(item);
- bool isTaggedIntMissingValue = false;
- #ifdef _M_AMD64
- if (isTaggedInt)
- {
- int32 iValue = TaggedInt::ToInt32(item);
- isTaggedIntMissingValue = Js::SparseArraySegment<int32>::IsMissingItem(&iValue);
- }
- #endif
- if (isTaggedInt && !isTaggedIntMissingValue)
- {
- // This is taggedInt case and we verified that item is not missing value in AMD64.
- this->DirectSetItemAt(i, TaggedInt::ToInt32(item));
- }
- else if (!isTaggedIntMissingValue && JavascriptNumber::Is_NoTaggedIntCheck(item))
- {
- double dvalue = JavascriptNumber::GetValue(item);
- int32 ivalue;
- if (JavascriptNumber::TryGetInt32Value(dvalue, &ivalue) && !Js::SparseArraySegment<int32>::IsMissingItem(&ivalue))
- {
- this->DirectSetItemAt(i, ivalue);
- }
- else
- {
- #if ENABLE_PROFILE_INFO
- if (arrayInfo)
- {
- arrayInfo->SetIsNotNativeIntArray();
- }
- #endif
- if (HasInlineHeadSegment(length) && i < this->head->length && !dontCreateNewArray)
- {
- // Avoid shrinking the number of elements in the head segment. We can still create a new
- // array here, so go ahead.
- JavascriptNativeFloatArray *fArr =
- this->GetScriptContext()->GetLibrary()->CreateNativeFloatArrayLiteral(length);
- return fArr->JavascriptNativeFloatArray::FillFromArgs(length, 0, args);
- }
- JavascriptNativeFloatArray *fArr = JavascriptNativeIntArray::ToNativeFloatArray(this);
- fArr->DirectSetItemAt(i, dvalue);
- #if ENABLE_PROFILE_INFO
- return fArr->JavascriptNativeFloatArray::FillFromArgs(length, i + 1, args, arrayInfo, dontCreateNewArray);
- #else
- return fArr->JavascriptNativeFloatArray::FillFromArgs(length, i + 1, args, dontCreateNewArray);
- #endif
- }
- }
- else
- {
- #if ENABLE_PROFILE_INFO
- if (arrayInfo)
- {
- arrayInfo->SetIsNotNativeArray();
- }
- #endif
- #pragma prefast(suppress:6237, "The right hand side condition does not have any side effects.")
- if (sizeof(int32) < sizeof(Var) && HasInlineHeadSegment(length) && i < this->head->length && !dontCreateNewArray)
- {
- // Avoid shrinking the number of elements in the head segment. We can still create a new
- // array here, so go ahead.
- JavascriptArray *arr = this->GetScriptContext()->GetLibrary()->CreateArrayLiteral(length);
- return arr->JavascriptArray::FillFromArgs(length, 0, args);
- }
- JavascriptArray *arr = JavascriptNativeIntArray::ToVarArray(this);
- #if ENABLE_PROFILE_INFO
- return arr->JavascriptArray::FillFromArgs(length, i, args, nullptr, dontCreateNewArray);
- #else
- return arr->JavascriptArray::FillFromArgs(length, i, args, dontCreateNewArray);
- #endif
- }
- }
- return this;
- }
- #if ENABLE_PROFILE_INFO
- JavascriptArray * JavascriptNativeFloatArray::FillFromArgs(uint length, uint start, Var *args, ArrayCallSiteInfo *arrayInfo, bool dontCreateNewArray)
- #else
- JavascriptArray * JavascriptNativeFloatArray::FillFromArgs(uint length, uint start, Var *args, bool dontCreateNewArray)
- #endif
- {
- uint i;
- for (i = start; i < length; i++)
- {
- Var item = args[i + 1];
- if (TaggedInt::Is(item))
- {
- this->DirectSetItemAt(i, TaggedInt::ToDouble(item));
- }
- else if (JavascriptNumber::Is_NoTaggedIntCheck(item))
- {
- this->DirectSetItemAt(i, JavascriptNumber::GetValue(item));
- }
- else
- {
- JavascriptArray *arr = JavascriptNativeFloatArray::ToVarArray(this);
- #if ENABLE_PROFILE_INFO
- if (arrayInfo)
- {
- arrayInfo->SetIsNotNativeArray();
- }
- return arr->JavascriptArray::FillFromArgs(length, i, args, nullptr, dontCreateNewArray);
- #else
- return arr->JavascriptArray::FillFromArgs(length, i, args, dontCreateNewArray);
- #endif
- }
- }
- return this;
- }
- #if ENABLE_PROFILE_INFO
- JavascriptArray * JavascriptArray::FillFromArgs(uint length, uint start, Var *args, ArrayCallSiteInfo *arrayInfo, bool dontCreateNewArray)
- #else
- JavascriptArray * JavascriptArray::FillFromArgs(uint length, uint start, Var *args, bool dontCreateNewArray)
- #endif
- {
- uint32 i;
- for (i = start; i < length; i++)
- {
- Var item = args[i + 1];
- this->DirectSetItemAt(i, item);
- }
- return this;
- }
- DynamicType * JavascriptNativeIntArray::GetInitialType(ScriptContext * scriptContext)
- {
- return scriptContext->GetLibrary()->GetNativeIntArrayType();
- }
- #if ENABLE_COPYONACCESS_ARRAY
- DynamicType * JavascriptCopyOnAccessNativeIntArray::GetInitialType(ScriptContext * scriptContext)
- {
- return scriptContext->GetLibrary()->GetCopyOnAccessNativeIntArrayType();
- }
- #endif
- JavascriptNativeFloatArray *JavascriptNativeIntArray::ToNativeFloatArray(JavascriptNativeIntArray *intArray)
- {
- #if ENABLE_PROFILE_INFO
- ArrayCallSiteInfo *arrayInfo = intArray->GetArrayCallSiteInfo();
- if (arrayInfo)
- {
- #if DBG
- Js::JavascriptStackWalker walker(intArray->GetScriptContext());
- Js::JavascriptFunction* caller = NULL;
- bool foundScriptCaller = false;
- while(walker.GetCaller(&caller))
- {
- if(caller != NULL && Js::ScriptFunction::Is(caller))
- {
- foundScriptCaller = true;
- break;
- }
- }
- if(foundScriptCaller)
- {
- Assert(caller);
- Assert(caller->GetFunctionBody());
- if(PHASE_TRACE(Js::NativeArrayConversionPhase, caller->GetFunctionBody()))
- {
- Output::Print(_u("Conversion: Int array to Float array ArrayCreationFunctionNumber:%2d CallSiteNumber:%2d \n"), arrayInfo->functionNumber, arrayInfo->callSiteNumber);
- Output::Flush();
- }
- }
- else
- {
- if(PHASE_TRACE1(Js::NativeArrayConversionPhase))
- {
- Output::Print(_u("Conversion: Int array to Float array across ScriptContexts"));
- Output::Flush();
- }
- }
- #else
- if(PHASE_TRACE1(Js::NativeArrayConversionPhase))
- {
- Output::Print(_u("Conversion: Int array to Float array"));
- Output::Flush();
- }
- #endif
- arrayInfo->SetIsNotNativeIntArray();
- }
- #endif
- // Grow the segments
- ScriptContext *scriptContext = intArray->GetScriptContext();
- Recycler *recycler = scriptContext->GetRecycler();
- SparseArraySegmentBase *seg, *nextSeg, *prevSeg = nullptr;
- for (seg = intArray->head; seg; seg = nextSeg)
- {
- nextSeg = seg->next;
- uint32 size = seg->size;
- if (size == 0)
- {
- continue;
- }
- uint32 left = seg->left;
- uint32 length = seg->length;
- int i;
- int32 ival;
- // The old segment will have size/2 and length capped by the new size.
- seg->size >>= 1;
- if (seg == intArray->head || seg->length > (seg->size >>= 1))
- {
- // Some live elements are being pushed out of this segment, so allocate a new one.
- SparseArraySegment<double> *newSeg =
- SparseArraySegment<double>::AllocateSegment(recycler, left, length, nextSeg);
- Assert(newSeg != nullptr);
- Assert((prevSeg == nullptr) == (seg == intArray->head));
- newSeg->next = nextSeg;
- intArray->LinkSegments((SparseArraySegment<double>*)prevSeg, newSeg);
- if (intArray->GetLastUsedSegment() == seg)
- {
- intArray->SetLastUsedSegment(newSeg);
- }
- prevSeg = newSeg;
- SegmentBTree * segmentMap = intArray->GetSegmentMap();
- if (segmentMap)
- {
- segmentMap->SwapSegment(left, seg, newSeg);
- }
- // Fill the new segment with the overflow.
- for (i = 0; (uint)i < newSeg->length; i++)
- {
- ival = ((SparseArraySegment<int32>*)seg)->elements[i /*+ seg->length*/];
- if (ival == JavascriptNativeIntArray::MissingItem)
- {
- continue;
- }
- newSeg->elements[i] = (double)ival;
- }
- }
- else
- {
- // Now convert the contents that will remain in the old segment.
- for (i = seg->length - 1; i >= 0; i--)
- {
- ival = ((SparseArraySegment<int32>*)seg)->elements[i];
- if (ival == JavascriptNativeIntArray::MissingItem)
- {
- ((SparseArraySegment<double>*)seg)->elements[i] = (double)JavascriptNativeFloatArray::MissingItem;
- }
- else
- {
- ((SparseArraySegment<double>*)seg)->elements[i] = (double)ival;
- }
- }
- prevSeg = seg;
- }
- }
- if (intArray->GetType() == scriptContext->GetLibrary()->GetNativeIntArrayType())
- {
- intArray->type = scriptContext->GetLibrary()->GetNativeFloatArrayType();
- }
- else
- {
- if (intArray->GetDynamicType()->GetIsLocked())
- {
- DynamicTypeHandler *typeHandler = intArray->GetDynamicType()->GetTypeHandler();
- if (typeHandler->IsPathTypeHandler())
- {
- // We can't allow a type with the new type ID to be promoted to the old type.
- // So go to a dictionary type handler, which will orphan the new type.
- // This should be a corner case, so the inability to share the new type is unlikely to matter.
- // If it does matter, try building a path from the new type's built-in root.
- static_cast<PathTypeHandlerBase*>(typeHandler)->ResetTypeHandler(intArray);
- }
- else
- {
- intArray->ChangeType();
- }
- }
- intArray->GetType()->SetTypeId(TypeIds_NativeFloatArray);
- }
- if (CrossSite::IsCrossSiteObjectTyped(intArray))
- {
- Assert(VirtualTableInfo<CrossSiteObject<JavascriptNativeIntArray>>::HasVirtualTable(intArray));
- VirtualTableInfo<CrossSiteObject<JavascriptNativeFloatArray>>::SetVirtualTable(intArray);
- }
- else
- {
- Assert(VirtualTableInfo<JavascriptNativeIntArray>::HasVirtualTable(intArray));
- VirtualTableInfo<JavascriptNativeFloatArray>::SetVirtualTable(intArray);
- }
- return (JavascriptNativeFloatArray*)intArray;
- }
- /*
- * JavascriptArray::ChangeArrayTypeToNativeArray<double>
- * - Converts the Var Array's type to NativeFloat.
- * - Sets the VirtualTable to "JavascriptNativeFloatArray"
- */
- template<>
- void JavascriptArray::ChangeArrayTypeToNativeArray<double>(JavascriptArray * varArray, ScriptContext * scriptContext)
- {
- AssertMsg(!JavascriptNativeArray::Is(varArray), "Ensure that the incoming Array is a Var array");
- if (varArray->GetType() == scriptContext->GetLibrary()->GetArrayType())
- {
- varArray->type = scriptContext->GetLibrary()->GetNativeFloatArrayType();
- }
- else
- {
- if (varArray->GetDynamicType()->GetIsLocked())
- {
- DynamicTypeHandler *typeHandler = varArray->GetDynamicType()->GetTypeHandler();
- if (typeHandler->IsPathTypeHandler())
- {
- // We can't allow a type with the new type ID to be promoted to the old type.
- // So go to a dictionary type handler, which will orphan the new type.
- // This should be a corner case, so the inability to share the new type is unlikely to matter.
- // If it does matter, try building a path from the new type's built-in root.
- static_cast<PathTypeHandlerBase*>(typeHandler)->ResetTypeHandler(varArray);
- }
- else
- {
- varArray->ChangeType();
- }
- }
- varArray->GetType()->SetTypeId(TypeIds_NativeFloatArray);
- }
- if (CrossSite::IsCrossSiteObjectTyped(varArray))
- {
- Assert(VirtualTableInfo<CrossSiteObject<JavascriptArray>>::HasVirtualTable(varArray));
- VirtualTableInfo<CrossSiteObject<JavascriptNativeFloatArray>>::SetVirtualTable(varArray);
- }
- else
- {
- Assert(VirtualTableInfo<JavascriptArray>::HasVirtualTable(varArray));
- VirtualTableInfo<JavascriptNativeFloatArray>::SetVirtualTable(varArray);
- }
- }
- /*
- * JavascriptArray::ChangeArrayTypeToNativeArray<int32>
- * - Converts the Var Array's type to NativeInt.
- * - Sets the VirtualTable to "JavascriptNativeIntArray"
- */
- template<>
- void JavascriptArray::ChangeArrayTypeToNativeArray<int32>(JavascriptArray * varArray, ScriptContext * scriptContext)
- {
- AssertMsg(!JavascriptNativeArray::Is(varArray), "Ensure that the incoming Array is a Var array");
- if (varArray->GetType() == scriptContext->GetLibrary()->GetArrayType())
- {
- varArray->type = scriptContext->GetLibrary()->GetNativeIntArrayType();
- }
- else
- {
- if (varArray->GetDynamicType()->GetIsLocked())
- {
- DynamicTypeHandler *typeHandler = varArray->GetDynamicType()->GetTypeHandler();
- if (typeHandler->IsPathTypeHandler())
- {
- // We can't allow a type with the new type ID to be promoted to the old type.
- // So go to a dictionary type handler, which will orphan the new type.
- // This should be a corner case, so the inability to share the new type is unlikely to matter.
- // If it does matter, try building a path from the new type's built-in root.
- static_cast<PathTypeHandlerBase*>(typeHandler)->ResetTypeHandler(varArray);
- }
- else
- {
- varArray->ChangeType();
- }
- }
- varArray->GetType()->SetTypeId(TypeIds_NativeIntArray);
- }
- if (CrossSite::IsCrossSiteObjectTyped(varArray))
- {
- Assert(VirtualTableInfo<CrossSiteObject<JavascriptArray>>::HasVirtualTable(varArray));
- VirtualTableInfo<CrossSiteObject<JavascriptNativeIntArray>>::SetVirtualTable(varArray);
- }
- else
- {
- Assert(VirtualTableInfo<JavascriptArray>::HasVirtualTable(varArray));
- VirtualTableInfo<JavascriptNativeIntArray>::SetVirtualTable(varArray);
- }
- }
- template<>
- int32 JavascriptArray::GetNativeValue<int32>(Js::Var ival, ScriptContext * scriptContext)
- {
- return JavascriptConversion::ToInt32(ival, scriptContext);
- }
- template <>
- double JavascriptArray::GetNativeValue<double>(Var ival, ScriptContext * scriptContext)
- {
- return JavascriptConversion::ToNumber(ival, scriptContext);
- }
- /*
- * JavascriptArray::ConvertToNativeArrayInPlace
- * In place conversion of all Var elements to Native Int/Double elements in an array.
- * We do not update the DynamicProfileInfo of the array here.
- */
- template<typename NativeArrayType, typename T>
- NativeArrayType *JavascriptArray::ConvertToNativeArrayInPlace(JavascriptArray *varArray)
- {
- AssertMsg(!JavascriptNativeArray::Is(varArray), "Ensure that the incoming Array is a Var array");
- ScriptContext *scriptContext = varArray->GetScriptContext();
- SparseArraySegmentBase *seg, *nextSeg, *prevSeg = nullptr;
- for (seg = varArray->head; seg; seg = nextSeg)
- {
- nextSeg = seg->next;
- uint32 size = seg->size;
- if (size == 0)
- {
- continue;
- }
- int i;
- Var ival;
- uint32 growFactor = sizeof(Var) / sizeof(T);
- AssertMsg(growFactor == 1, "We support only in place conversion of Var array to Native Array");
- // Now convert the contents that will remain in the old segment.
- for (i = seg->length - 1; i >= 0; i--)
- {
- ival = ((SparseArraySegment<Var>*)seg)->elements[i];
- if (ival == JavascriptArray::MissingItem)
- {
- ((SparseArraySegment<T>*)seg)->elements[i] = NativeArrayType::MissingItem;
- }
- else
- {
- ((SparseArraySegment<T>*)seg)->elements[i] = GetNativeValue<T>(ival, scriptContext);
- }
- }
- prevSeg = seg;
- }
- // Update the type of the Array
- ChangeArrayTypeToNativeArray<T>(varArray, scriptContext);
- return (NativeArrayType*)varArray;
- }
- JavascriptArray *JavascriptNativeIntArray::ConvertToVarArray(JavascriptNativeIntArray *intArray)
- {
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(intArray);
- #endif
- ScriptContext *scriptContext = intArray->GetScriptContext();
- Recycler *recycler = scriptContext->GetRecycler();
- SparseArraySegmentBase *seg, *nextSeg, *prevSeg = nullptr;
- for (seg = intArray->head; seg; seg = nextSeg)
- {
- nextSeg = seg->next;
- uint32 size = seg->size;
- if (size == 0)
- {
- continue;
- }
- uint32 left = seg->left;
- uint32 length = seg->length;
- int i;
- int32 ival;
- // Shrink?
- uint32 growFactor = sizeof(Var) / sizeof(int32);
- if ((growFactor != 1 && (seg == intArray->head || seg->length > (seg->size /= growFactor))) ||
- (seg->next == nullptr && SparseArraySegmentBase::IsLeafSegment(seg, recycler)))
- {
- // Some live elements are being pushed out of this segment, so allocate a new one.
- // And/or the old segment is not scanned by the recycler, so we need a new one to hold vars.
- SparseArraySegment<Var> *newSeg =
- SparseArraySegment<Var>::AllocateSegment(recycler, left, length, nextSeg);
- AnalysisAssert(newSeg);
- Assert((prevSeg == nullptr) == (seg == intArray->head));
- newSeg->next = nextSeg;
- intArray->LinkSegments((SparseArraySegment<Var>*)prevSeg, newSeg);
- if (intArray->GetLastUsedSegment() == seg)
- {
- intArray->SetLastUsedSegment(newSeg);
- }
- prevSeg = newSeg;
- SegmentBTree * segmentMap = intArray->GetSegmentMap();
- if (segmentMap)
- {
- segmentMap->SwapSegment(left, seg, newSeg);
- }
- // Fill the new segment with the overflow.
- for (i = 0; (uint)i < newSeg->length; i++)
- {
- ival = ((SparseArraySegment<int32>*)seg)->elements[i];
- if (ival == JavascriptNativeIntArray::MissingItem)
- {
- continue;
- }
- newSeg->elements[i] = JavascriptNumber::ToVar(ival, scriptContext);
- }
- }
- else
- {
- // Now convert the contents that will remain in the old segment.
- // Walk backward in case we're growing the element size.
- for (i = seg->length - 1; i >= 0; i--)
- {
- ival = ((SparseArraySegment<int32>*)seg)->elements[i];
- if (ival == JavascriptNativeIntArray::MissingItem)
- {
- ((SparseArraySegment<Var>*)seg)->elements[i] = (Var)JavascriptArray::MissingItem;
- }
- else
- {
- ((SparseArraySegment<Var>*)seg)->elements[i] = JavascriptNumber::ToVar(ival, scriptContext);
- }
- }
- prevSeg = seg;
- }
- }
- if (intArray->GetType() == scriptContext->GetLibrary()->GetNativeIntArrayType())
- {
- intArray->type = scriptContext->GetLibrary()->GetArrayType();
- }
- else
- {
- if (intArray->GetDynamicType()->GetIsLocked())
- {
- DynamicTypeHandler *typeHandler = intArray->GetDynamicType()->GetTypeHandler();
- if (typeHandler->IsPathTypeHandler())
- {
- // We can't allow a type with the new type ID to be promoted to the old type.
- // So go to a dictionary type handler, which will orphan the new type.
- // This should be a corner case, so the inability to share the new type is unlikely to matter.
- // If it does matter, try building a path from the new type's built-in root.
- static_cast<PathTypeHandlerBase*>(typeHandler)->ResetTypeHandler(intArray);
- }
- else
- {
- intArray->ChangeType();
- }
- }
- intArray->GetType()->SetTypeId(TypeIds_Array);
- }
- if (CrossSite::IsCrossSiteObjectTyped(intArray))
- {
- Assert(VirtualTableInfo<CrossSiteObject<JavascriptNativeIntArray>>::HasVirtualTable(intArray));
- VirtualTableInfo<CrossSiteObject<JavascriptArray>>::SetVirtualTable(intArray);
- }
- else
- {
- Assert(VirtualTableInfo<JavascriptNativeIntArray>::HasVirtualTable(intArray));
- VirtualTableInfo<JavascriptArray>::SetVirtualTable(intArray);
- }
- return intArray;
- }
- JavascriptArray *JavascriptNativeIntArray::ToVarArray(JavascriptNativeIntArray *intArray)
- {
- #if ENABLE_PROFILE_INFO
- ArrayCallSiteInfo *arrayInfo = intArray->GetArrayCallSiteInfo();
- if (arrayInfo)
- {
- #if DBG
- Js::JavascriptStackWalker walker(intArray->GetScriptContext());
- Js::JavascriptFunction* caller = NULL;
- bool foundScriptCaller = false;
- while(walker.GetCaller(&caller))
- {
- if(caller != NULL && Js::ScriptFunction::Is(caller))
- {
- foundScriptCaller = true;
- break;
- }
- }
- if(foundScriptCaller)
- {
- Assert(caller);
- Assert(caller->GetFunctionBody());
- if(PHASE_TRACE(Js::NativeArrayConversionPhase, caller->GetFunctionBody()))
- {
- Output::Print(_u("Conversion: Int array to Var array ArrayCreationFunctionNumber:%2d CallSiteNumber:%2d \n"), arrayInfo->functionNumber, arrayInfo->callSiteNumber);
- Output::Flush();
- }
- }
- else
- {
- if(PHASE_TRACE1(Js::NativeArrayConversionPhase))
- {
- Output::Print(_u("Conversion: Int array to Var array across ScriptContexts"));
- Output::Flush();
- }
- }
- #else
- if(PHASE_TRACE1(Js::NativeArrayConversionPhase))
- {
- Output::Print(_u("Conversion: Int array to Var array"));
- Output::Flush();
- }
- #endif
- arrayInfo->SetIsNotNativeArray();
- }
- #endif
- intArray->ClearArrayCallSiteIndex();
- return ConvertToVarArray(intArray);
- }
- DynamicType * JavascriptNativeFloatArray::GetInitialType(ScriptContext * scriptContext)
- {
- return scriptContext->GetLibrary()->GetNativeFloatArrayType();
- }
- /*
- * JavascriptNativeFloatArray::ConvertToVarArray
- * This function only converts all Float elements to Var elements in an array.
- * DynamicProfileInfo of the array is not updated in this function.
- */
- JavascriptArray *JavascriptNativeFloatArray::ConvertToVarArray(JavascriptNativeFloatArray *fArray)
- {
- // We can't be growing the size of the element.
- Assert(sizeof(double) >= sizeof(Var));
- uint32 shrinkFactor = sizeof(double) / sizeof(Var);
- ScriptContext *scriptContext = fArray->GetScriptContext();
- Recycler *recycler = scriptContext->GetRecycler();
- SparseArraySegmentBase *seg, *nextSeg, *prevSeg = nullptr;
- for (seg = fArray->head; seg; seg = nextSeg)
- {
- nextSeg = seg->next;
- if (seg->size == 0)
- {
- continue;
- }
- uint32 left = seg->left;
- uint32 length = seg->length;
- SparseArraySegment<Var> *newSeg;
- if (seg->next == nullptr && SparseArraySegmentBase::IsLeafSegment(seg, recycler))
- {
- // The old segment is not scanned by the recycler, so we need a new one to hold vars.
- newSeg =
- SparseArraySegment<Var>::AllocateSegment(recycler, left, length, nextSeg);
- Assert((prevSeg == nullptr) == (seg == fArray->head));
- newSeg->next = nextSeg;
- fArray->LinkSegments((SparseArraySegment<Var>*)prevSeg, newSeg);
- if (fArray->GetLastUsedSegment() == seg)
- {
- fArray->SetLastUsedSegment(newSeg);
- }
- prevSeg = newSeg;
- SegmentBTree * segmentMap = fArray->GetSegmentMap();
- if (segmentMap)
- {
- segmentMap->SwapSegment(left, seg, newSeg);
- }
- }
- else
- {
- newSeg = (SparseArraySegment<Var>*)seg;
- prevSeg = seg;
- if (shrinkFactor != 1)
- {
- uint32 newSize = seg->size * shrinkFactor;
- uint32 limit;
- if (seg->next)
- {
- limit = seg->next->left;
- }
- else
- {
- limit = JavascriptArray::MaxArrayLength;
- }
- seg->size = min(newSize, limit - seg->left);
- }
- }
- uint32 i;
- for (i = 0; i < seg->length; i++)
- {
- if (SparseArraySegment<double>::IsMissingItem(&((SparseArraySegment<double>*)seg)->elements[i]))
- {
- if (seg == newSeg)
- {
- newSeg->elements[i] = (Var)JavascriptArray::MissingItem;
- }
- Assert(newSeg->elements[i] == (Var)JavascriptArray::MissingItem);
- }
- else if (*(uint64*)&(((SparseArraySegment<double>*)seg)->elements[i]) == 0ull)
- {
- newSeg->elements[i] = TaggedInt::ToVarUnchecked(0);
- }
- else
- {
- int32 ival;
- double dval = ((SparseArraySegment<double>*)seg)->elements[i];
- if (JavascriptNumber::TryGetInt32Value(dval, &ival) && !TaggedInt::IsOverflow(ival))
- {
- newSeg->elements[i] = TaggedInt::ToVarUnchecked(ival);
- }
- else
- {
- newSeg->elements[i] = JavascriptNumber::ToVarWithCheck(dval, scriptContext);
- }
- }
- }
- if (seg == newSeg && shrinkFactor != 1)
- {
- // Fill the remaining slots.
- newSeg->FillSegmentBuffer(i, seg->size);
- }
- }
- if (fArray->GetType() == scriptContext->GetLibrary()->GetNativeFloatArrayType())
- {
- fArray->type = scriptContext->GetLibrary()->GetArrayType();
- }
- else
- {
- if (fArray->GetDynamicType()->GetIsLocked())
- {
- DynamicTypeHandler *typeHandler = fArray->GetDynamicType()->GetTypeHandler();
- if (typeHandler->IsPathTypeHandler())
- {
- // We can't allow a type with the new type ID to be promoted to the old type.
- // So go to a dictionary type handler, which will orphan the new type.
- // This should be a corner case, so the inability to share the new type is unlikely to matter.
- // If it does matter, try building a path from the new type's built-in root.
- static_cast<PathTypeHandlerBase*>(typeHandler)->ResetTypeHandler(fArray);
- }
- else
- {
- fArray->ChangeType();
- }
- }
- fArray->GetType()->SetTypeId(TypeIds_Array);
- }
- if (CrossSite::IsCrossSiteObjectTyped(fArray))
- {
- Assert(VirtualTableInfo<CrossSiteObject<JavascriptNativeFloatArray>>::HasVirtualTable(fArray));
- VirtualTableInfo<CrossSiteObject<JavascriptArray>>::SetVirtualTable(fArray);
- }
- else
- {
- Assert(VirtualTableInfo<JavascriptNativeFloatArray>::HasVirtualTable(fArray));
- VirtualTableInfo<JavascriptArray>::SetVirtualTable(fArray);
- }
- return fArray;
- }
- JavascriptArray *JavascriptNativeFloatArray::ToVarArray(JavascriptNativeFloatArray *fArray)
- {
- #if ENABLE_PROFILE_INFO
- ArrayCallSiteInfo *arrayInfo = fArray->GetArrayCallSiteInfo();
- if (arrayInfo)
- {
- #if DBG
- Js::JavascriptStackWalker walker(fArray->GetScriptContext());
- Js::JavascriptFunction* caller = NULL;
- bool foundScriptCaller = false;
- while(walker.GetCaller(&caller))
- {
- if(caller != NULL && Js::ScriptFunction::Is(caller))
- {
- foundScriptCaller = true;
- break;
- }
- }
- if(foundScriptCaller)
- {
- Assert(caller);
- Assert(caller->GetFunctionBody());
- if(PHASE_TRACE(Js::NativeArrayConversionPhase, caller->GetFunctionBody()))
- {
- Output::Print(_u("Conversion: Float array to Var array ArrayCreationFunctionNumber:%2d CallSiteNumber:%2d \n"), arrayInfo->functionNumber, arrayInfo->callSiteNumber);
- Output::Flush();
- }
- }
- else
- {
- if(PHASE_TRACE1(Js::NativeArrayConversionPhase))
- {
- Output::Print(_u("Conversion: Float array to Var array across ScriptContexts"));
- Output::Flush();
- }
- }
- #else
- if(PHASE_TRACE1(Js::NativeArrayConversionPhase))
- {
- Output::Print(_u("Conversion: Float array to Var array"));
- Output::Flush();
- }
- #endif
- if(fArray->GetScriptContext()->IsScriptContextInNonDebugMode())
- {
- Assert(!arrayInfo->IsNativeIntArray());
- }
- arrayInfo->SetIsNotNativeArray();
- }
- #endif
- fArray->ClearArrayCallSiteIndex();
- return ConvertToVarArray(fArray);
- }
- // Convert Var to index in the Array.
- // Note: Spec calls out a few rules for these parameters:
- // 1. if (arg > length) { return length; }
- // clamp to length, not length-1
- // 2. if (arg < 0) { return max(0, length + arg); }
- // treat negative arg as index from the end of the array (with -1 mapping to length-1)
- // Effectively, this function will return a value between 0 and length, inclusive.
- int64 JavascriptArray::GetIndexFromVar(Js::Var arg, int64 length, ScriptContext* scriptContext)
- {
- int64 index;
- if (TaggedInt::Is(arg))
- {
- int intValue = TaggedInt::ToInt32(arg);
- if (intValue < 0)
- {
- index = max<int64>(0, length + intValue);
- }
- else
- {
- index = intValue;
- }
- if (index > length)
- {
- index = length;
- }
- }
- else
- {
- double doubleValue = JavascriptConversion::ToInteger(arg, scriptContext);
- // Handle the Number.POSITIVE_INFINITY case
- if (doubleValue > length)
- {
- return length;
- }
- index = NumberUtilities::TryToInt64(doubleValue);
- if (index < 0)
- {
- index = max<int64>(0, index + length);
- }
- }
- return index;
- }
- TypeId JavascriptArray::OP_SetNativeIntElementC(JavascriptNativeIntArray *arr, uint32 index, Var value, ScriptContext *scriptContext)
- {
- int32 iValue;
- double dValue;
- TypeId typeId = arr->TrySetNativeIntArrayItem(value, &iValue, &dValue);
- if (typeId == TypeIds_NativeIntArray)
- {
- arr->SetArrayLiteralItem(index, iValue);
- }
- else if (typeId == TypeIds_NativeFloatArray)
- {
- arr->SetArrayLiteralItem(index, dValue);
- }
- else
- {
- arr->SetArrayLiteralItem(index, value);
- }
- return typeId;
- }
- TypeId JavascriptArray::OP_SetNativeFloatElementC(JavascriptNativeFloatArray *arr, uint32 index, Var value, ScriptContext *scriptContext)
- {
- double dValue;
- TypeId typeId = arr->TrySetNativeFloatArrayItem(value, &dValue);
- if (typeId == TypeIds_NativeFloatArray)
- {
- arr->SetArrayLiteralItem(index, dValue);
- }
- else
- {
- arr->SetArrayLiteralItem(index, value);
- }
- return typeId;
- }
- template<typename T>
- void JavascriptArray::SetArrayLiteralItem(uint32 index, T value)
- {
- SparseArraySegment<T> * segment = (SparseArraySegment<T>*)this->head;
- Assert(segment->left == 0);
- Assert(index < segment->length);
- segment->elements[index] = value;
- }
- void JavascriptNativeIntArray::SetIsPrototype()
- {
- // Force the array to be non-native to simplify inspection, filling from proto, etc.
- ToVarArray(this);
- __super::SetIsPrototype();
- }
- void JavascriptNativeFloatArray::SetIsPrototype()
- {
- // Force the array to be non-native to simplify inspection, filling from proto, etc.
- ToVarArray(this);
- __super::SetIsPrototype();
- }
- #if ENABLE_PROFILE_INFO
- ArrayCallSiteInfo *JavascriptNativeArray::GetArrayCallSiteInfo()
- {
- RecyclerWeakReference<FunctionBody> *weakRef = this->weakRefToFuncBody;
- if (weakRef)
- {
- FunctionBody *functionBody = weakRef->Get();
- if (functionBody)
- {
- if (functionBody->HasDynamicProfileInfo())
- {
- Js::ProfileId profileId = this->GetArrayCallSiteIndex();
- if (profileId < functionBody->GetProfiledArrayCallSiteCount())
- {
- return functionBody->GetAnyDynamicProfileInfo()->GetArrayCallSiteInfo(functionBody, profileId);
- }
- }
- }
- else
- {
- this->ClearArrayCallSiteIndex();
- }
- }
- return nullptr;
- }
- void JavascriptNativeArray::SetArrayProfileInfo(RecyclerWeakReference<FunctionBody> *weakRef, ArrayCallSiteInfo *arrayInfo)
- {
- Assert(weakRef);
- FunctionBody *functionBody = weakRef->Get();
- if (functionBody && functionBody->HasDynamicProfileInfo())
- {
- ArrayCallSiteInfo *baseInfo = functionBody->GetAnyDynamicProfileInfo()->GetArrayCallSiteInfo(functionBody, 0);
- Js::ProfileId index = (Js::ProfileId)(arrayInfo - baseInfo);
- Assert(index < functionBody->GetProfiledArrayCallSiteCount());
- SetArrayCallSite(index, weakRef);
- }
- }
- void JavascriptNativeArray::CopyArrayProfileInfo(Js::JavascriptNativeArray* baseArray)
- {
- if (baseArray->weakRefToFuncBody)
- {
- if (baseArray->weakRefToFuncBody->Get())
- {
- SetArrayCallSite(baseArray->GetArrayCallSiteIndex(), baseArray->weakRefToFuncBody);
- }
- else
- {
- baseArray->ClearArrayCallSiteIndex();
- }
- }
- }
- #endif
- Var JavascriptNativeArray::FindMinOrMax(Js::ScriptContext * scriptContext, bool findMax)
- {
- if (JavascriptNativeIntArray::Is(this))
- {
- return this->FindMinOrMax<int32, false>(scriptContext, findMax);
- }
- else
- {
- return this->FindMinOrMax<double, true>(scriptContext, findMax);
- }
- }
- template <typename T, bool checkNaNAndNegZero>
- Var JavascriptNativeArray::FindMinOrMax(Js::ScriptContext * scriptContext, bool findMax)
- {
- AssertMsg(this->HasNoMissingValues(), "Fastpath is only for arrays with one segment and no missing values");
- uint len = this->GetLength();
- Js::SparseArraySegment<T>* headSegment = ((Js::SparseArraySegment<T>*)this->GetHead());
- uint headSegLen = headSegment->length;
- Assert(headSegLen == len);
- if (headSegment->next == nullptr)
- {
- T currentRes = headSegment->elements[0];
- for (uint i = 0; i < headSegLen; i++)
- {
- T compare = headSegment->elements[i];
- if (checkNaNAndNegZero && JavascriptNumber::IsNan(double(compare)))
- {
- return scriptContext->GetLibrary()->GetNaN();
- }
- if (findMax ? currentRes < compare : currentRes > compare ||
- (checkNaNAndNegZero && compare == 0 && Js::JavascriptNumber::IsNegZero(double(currentRes))))
- {
- currentRes = compare;
- }
- }
- return Js::JavascriptNumber::ToVarNoCheck(currentRes, scriptContext);
- }
- else
- {
- AssertMsg(false, "FindMinOrMax currently supports native arrays with only one segment");
- Throw::FatalInternalError();
- }
- }
- SparseArraySegmentBase * JavascriptArray::GetLastUsedSegment() const
- {
- return (HasSegmentMap() ? segmentUnion.segmentBTreeRoot->lastUsedSegment : segmentUnion.lastUsedSegment);
- }
- void JavascriptArray::SetHeadAndLastUsedSegment(SparseArraySegmentBase * segment)
- {
- Assert(!HasSegmentMap());
- this->head = this->segmentUnion.lastUsedSegment = segment;
- }
- void JavascriptArray::SetLastUsedSegment(SparseArraySegmentBase * segment)
- {
- if (HasSegmentMap())
- {
- this->segmentUnion.segmentBTreeRoot->lastUsedSegment = segment;
- }
- else
- {
- this->segmentUnion.lastUsedSegment = segment;
- }
- }
- bool JavascriptArray::HasSegmentMap() const
- {
- return !!(GetFlags() & DynamicObjectFlags::HasSegmentMap);
- }
- SegmentBTreeRoot * JavascriptArray::GetSegmentMap() const
- {
- return (HasSegmentMap() ? segmentUnion.segmentBTreeRoot : nullptr);
- }
- void JavascriptArray::SetSegmentMap(SegmentBTreeRoot * segmentMap)
- {
- Assert(!HasSegmentMap());
- SparseArraySegmentBase * lastUsedSeg = this->segmentUnion.lastUsedSegment;
- SetFlags(GetFlags() | DynamicObjectFlags::HasSegmentMap);
- segmentUnion.segmentBTreeRoot = segmentMap;
- segmentMap->lastUsedSegment = lastUsedSeg;
- }
- void JavascriptArray::ClearSegmentMap()
- {
- if (HasSegmentMap())
- {
- SetFlags(GetFlags() & ~DynamicObjectFlags::HasSegmentMap);
- SparseArraySegmentBase * lastUsedSeg = segmentUnion.segmentBTreeRoot->lastUsedSegment;
- segmentUnion.segmentBTreeRoot = nullptr;
- segmentUnion.lastUsedSegment = lastUsedSeg;
- }
- }
- SegmentBTreeRoot * JavascriptArray::BuildSegmentMap()
- {
- Recycler* recycler = GetRecycler();
- SegmentBTreeRoot* tmpSegmentMap = AllocatorNewStruct(Recycler, recycler, SegmentBTreeRoot);
- ForEachSegment([recycler, tmpSegmentMap](SparseArraySegmentBase * current)
- {
- tmpSegmentMap->Add(recycler, current);
- return false;
- });
- // There could be OOM during building segment map. Save to array only after its successful completion.
- SetSegmentMap(tmpSegmentMap);
- return tmpSegmentMap;
- }
- void JavascriptArray::TryAddToSegmentMap(Recycler* recycler, SparseArraySegmentBase* seg)
- {
- SegmentBTreeRoot * savedSegmentMap = GetSegmentMap();
- if (savedSegmentMap)
- {
- //
- // We could OOM and throw when adding to segmentMap, resulting in a corrupted segmentMap on this
- // array. Set segmentMap to null temporarily to protect from this. It will be restored correctly
- // if adding segment succeeds.
- //
- ClearSegmentMap();
- savedSegmentMap->Add(recycler, seg);
- SetSegmentMap(savedSegmentMap);
- }
- }
- void JavascriptArray::InvalidateLastUsedSegment()
- {
- this->SetLastUsedSegment(this->head);
- }
- DescriptorFlags JavascriptArray::GetSetter(PropertyId propertyId, Var *setterValue, PropertyValueInfo* info, ScriptContext* requestContext)
- {
- DescriptorFlags flags;
- if (GetSetterBuiltIns(propertyId, info, &flags))
- {
- return flags;
- }
- return __super::GetSetter(propertyId, setterValue, info, requestContext);
- }
- DescriptorFlags JavascriptArray::GetSetter(JavascriptString* propertyNameString, Var *setterValue, PropertyValueInfo* info, ScriptContext* requestContext)
- {
- DescriptorFlags flags;
- PropertyRecord const* propertyRecord;
- this->GetScriptContext()->FindPropertyRecord(propertyNameString, &propertyRecord);
- if (propertyRecord != nullptr && GetSetterBuiltIns(propertyRecord->GetPropertyId(), info, &flags))
- {
- return flags;
- }
- return __super::GetSetter(propertyNameString, setterValue, info, requestContext);
- }
- bool JavascriptArray::GetSetterBuiltIns(PropertyId propertyId, PropertyValueInfo* info, DescriptorFlags* descriptorFlags)
- {
- if (propertyId == PropertyIds::length)
- {
- PropertyValueInfo::SetNoCache(info, this);
- *descriptorFlags = WritableData;
- return true;
- }
- return false;
- }
- SparseArraySegmentBase * JavascriptArray::GetBeginLookupSegment(uint32 index, const bool useSegmentMap) const
- {
- SparseArraySegmentBase *seg = nullptr;
- SparseArraySegmentBase * lastUsedSeg = this->GetLastUsedSegment();
- if (lastUsedSeg != nullptr && lastUsedSeg->left <= index)
- {
- seg = lastUsedSeg;
- if(index - lastUsedSeg->left < lastUsedSeg->size)
- {
- return seg;
- }
- }
- SegmentBTreeRoot * segmentMap = GetSegmentMap();
- if(!useSegmentMap || !segmentMap)
- {
- return seg ? seg : this->head;
- }
- if(seg)
- {
- // If indexes are being accessed sequentially, check the segment after the last-used segment before checking the
- // segment map, as it is likely to hit
- SparseArraySegmentBase *const nextSeg = seg->next;
- if(nextSeg)
- {
- if(index < nextSeg->left)
- {
- return seg;
- }
- else if(index - nextSeg->left < nextSeg->size)
- {
- return nextSeg;
- }
- }
- }
- SparseArraySegmentBase *matchOrNextSeg;
- segmentMap->Find(index, seg, matchOrNextSeg);
- return seg ? seg : matchOrNextSeg;
- }
- uint32 JavascriptArray::GetNextIndex(uint32 index) const
- {
- if (JavascriptNativeIntArray::Is((Var)this))
- {
- return this->GetNextIndexHelper<int32>(index);
- }
- else if (JavascriptNativeFloatArray::Is((Var)this))
- {
- return this->GetNextIndexHelper<double>(index);
- }
- return this->GetNextIndexHelper<Var>(index);
- }
- template<typename T>
- uint32 JavascriptArray::GetNextIndexHelper(uint32 index) const
- {
- AssertMsg(this->head, "array head should never be null");
- uint candidateIndex;
- if (index == JavascriptArray::InvalidIndex)
- {
- candidateIndex = head->left;
- }
- else
- {
- candidateIndex = index + 1;
- }
- SparseArraySegment<T>* current = (SparseArraySegment<T>*)this->GetBeginLookupSegment(candidateIndex);
- while (current != nullptr)
- {
- if ((current->left <= candidateIndex) && ((candidateIndex - current->left) < current->length))
- {
- for (uint i = candidateIndex - current->left; i < current->length; i++)
- {
- if (!SparseArraySegment<T>::IsMissingItem(¤t->elements[i]))
- {
- return i + current->left;
- }
- }
- }
- current = (SparseArraySegment<T>*)current->next;
- if (current != NULL)
- {
- if (candidateIndex < current->left)
- {
- candidateIndex = current->left;
- }
- }
- }
- return JavascriptArray::InvalidIndex;
- }
- // If new length > length, we just reset the length
- // If new length < length, we need to remove the rest of the elements and segment
- void JavascriptArray::SetLength(uint32 newLength)
- {
- if (newLength == length)
- return;
- if (head == EmptySegment)
- {
- // Do nothing to the segment.
- }
- else if (newLength == 0)
- {
- this->ClearElements(head, 0);
- head->length = 0;
- head->next = nullptr;
- SetHasNoMissingValues();
- ClearSegmentMap();
- this->InvalidateLastUsedSegment();
- }
- else if (newLength < length)
- {
- // _ _ 2 3 _ _ 6 7 _ _
- // SetLength(0)
- // 0 <= left -> set *prev = null
- // SetLength(2)
- // 2 <= left -> set *prev = null
- // SetLength(3)
- // 3 !<= left; 3 <= right -> truncate to length - 1
- // SetLength(5)
- // 5 <=
- SparseArraySegmentBase* next = GetBeginLookupSegment(newLength - 1); // head, or next.left < newLength
- SparseArraySegmentBase** prev = &head;
- while(next != nullptr)
- {
- if (newLength <= next->left)
- {
- ClearSegmentMap(); // truncate segments, null out segmentMap
- *prev = nullptr;
- break;
- }
- else if (newLength <= (next->left + next->length))
- {
- if (next->next)
- {
- ClearSegmentMap(); // Will truncate segments, null out segmentMap
- }
- uint32 newSegmentLength = newLength - next->left;
- this->ClearElements(next, newSegmentLength);
- next->next = nullptr;
- next->length = newSegmentLength;
- break;
- }
- else
- {
- prev = &next->next;
- next = next->next;
- }
- }
- this->InvalidateLastUsedSegment();
- }
- this->length = newLength;
- #ifdef VALIDATE_ARRAY
- ValidateArray();
- #endif
- }
- BOOL JavascriptArray::SetLength(Var newLength)
- {
- ScriptContext *scriptContext;
- if(TaggedInt::Is(newLength))
- {
- int32 lenValue = TaggedInt::ToInt32(newLength);
- if (lenValue < 0)
- {
- scriptContext = GetScriptContext();
- if (scriptContext->GetThreadContext()->RecordImplicitException())
- {
- JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthAssignIncorrect);
- }
- }
- else
- {
- this->SetLength(lenValue);
- }
- return TRUE;
- }
- scriptContext = GetScriptContext();
- uint32 uintValue = JavascriptConversion::ToUInt32(newLength, scriptContext);
- double dblValue = JavascriptConversion::ToNumber(newLength, scriptContext);
- if (dblValue == uintValue)
- {
- this->SetLength(uintValue);
- }
- else
- {
- ThreadContext* threadContext = scriptContext->GetThreadContext();
- ImplicitCallFlags flags = threadContext->GetImplicitCallFlags();
- if (flags != ImplicitCall_None && threadContext->IsDisableImplicitCall())
- {
- // We couldn't execute the implicit call(s) needed to convert the newLength to an integer.
- // Do nothing and let the jitted code bail out.
- return TRUE;
- }
- if (threadContext->RecordImplicitException())
- {
- JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthAssignIncorrect);
- }
- }
- return TRUE;
- }
- void JavascriptArray::ClearElements(SparseArraySegmentBase *seg, uint32 newSegmentLength)
- {
- SparseArraySegment<Var>::ClearElements(((SparseArraySegment<Var>*)seg)->elements + newSegmentLength, seg->length - newSegmentLength);
- }
- void JavascriptNativeIntArray::ClearElements(SparseArraySegmentBase *seg, uint32 newSegmentLength)
- {
- SparseArraySegment<int32>::ClearElements(((SparseArraySegment<int32>*)seg)->elements + newSegmentLength, seg->length - newSegmentLength);
- }
- void JavascriptNativeFloatArray::ClearElements(SparseArraySegmentBase *seg, uint32 newSegmentLength)
- {
- SparseArraySegment<double>::ClearElements(((SparseArraySegment<double>*)seg)->elements + newSegmentLength, seg->length - newSegmentLength);
- }
- Var JavascriptArray::DirectGetItem(uint32 index)
- {
- SparseArraySegment<Var> *seg = (SparseArraySegment<Var>*)this->GetLastUsedSegment();
- uint32 offset = index - seg->left;
- if (index >= seg->left && offset < seg->length)
- {
- if (!SparseArraySegment<Var>::IsMissingItem(&seg->elements[offset]))
- {
- return seg->elements[offset];
- }
- }
- Var element;
- if (DirectGetItemAtFull(index, &element))
- {
- return element;
- }
- return GetType()->GetLibrary()->GetUndefined();
- }
- Var JavascriptNativeIntArray::DirectGetItem(uint32 index)
- {
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(this);
- #endif
- SparseArraySegment<int32> *seg = (SparseArraySegment<int32>*)this->GetLastUsedSegment();
- uint32 offset = index - seg->left;
- if (index >= seg->left && offset < seg->length)
- {
- if (!SparseArraySegment<int32>::IsMissingItem(&seg->elements[offset]))
- {
- return JavascriptNumber::ToVar(seg->elements[offset], GetScriptContext());
- }
- }
- Var element;
- if (DirectGetItemAtFull(index, &element))
- {
- return element;
- }
- return GetType()->GetLibrary()->GetUndefined();
- }
- Var JavascriptNativeFloatArray::DirectGetItem(uint32 index)
- {
- SparseArraySegment<double> *seg = (SparseArraySegment<double>*)this->GetLastUsedSegment();
- uint32 offset = index - seg->left;
- if (index >= seg->left && offset < seg->length)
- {
- if (!SparseArraySegment<double>::IsMissingItem(&seg->elements[offset]))
- {
- return JavascriptNumber::ToVarWithCheck(seg->elements[offset], GetScriptContext());
- }
- }
- Var element;
- if (DirectGetItemAtFull(index, &element))
- {
- return element;
- }
- return GetType()->GetLibrary()->GetUndefined();
- }
- Var JavascriptArray::DirectGetItem(JavascriptString *propName, ScriptContext* scriptContext)
- {
- PropertyRecord const * propertyRecord;
- scriptContext->GetOrAddPropertyRecord(propName->GetString(), propName->GetLength(), &propertyRecord);
- return JavascriptOperators::GetProperty(this, propertyRecord->GetPropertyId(), scriptContext, NULL);
- }
- BOOL JavascriptArray::DirectGetItemAtFull(uint32 index, Var* outVal)
- {
- if (this->DirectGetItemAt(index, outVal))
- {
- return TRUE;
- }
- ScriptContext* requestContext = type->GetScriptContext();
- return JavascriptOperators::GetItem(this, this->GetPrototype(), index, (Var*)outVal, requestContext);
- }
- //
- // Link prev and current. If prev is NULL, make current the head segment.
- //
- void JavascriptArray::LinkSegmentsCommon(SparseArraySegmentBase* prev, SparseArraySegmentBase* current)
- {
- if (prev)
- {
- prev->next = current;
- }
- else
- {
- Assert(current);
- head = current;
- }
- }
- template<typename T>
- BOOL JavascriptArray::DirectDeleteItemAt(uint32 itemIndex)
- {
- if (itemIndex >= length)
- {
- return true;
- }
- SparseArraySegment<T>* next = (SparseArraySegment<T>*)GetBeginLookupSegment(itemIndex);
- while(next != nullptr && next->left <= itemIndex)
- {
- uint32 limit = next->left + next->length;
- if (itemIndex < limit)
- {
- next->SetElement(GetRecycler(), itemIndex, SparseArraySegment<T>::GetMissingItem());
- if(itemIndex - next->left == next->length - 1)
- {
- --next->length;
- }
- else if(next == head)
- {
- SetHasNoMissingValues(false);
- }
- break;
- }
- next = (SparseArraySegment<T>*)next->next;
- }
- #ifdef VALIDATE_ARRAY
- ValidateArray();
- #endif
- return true;
- }
- template <> Var JavascriptArray::ConvertToIndex(BigIndex idxDest, ScriptContext* scriptContext)
- {
- return idxDest.ToNumber(scriptContext);
- }
- template <> uint32 JavascriptArray::ConvertToIndex(BigIndex idxDest, ScriptContext* scriptContext)
- {
- // Note this is only for setting Array length which is a uint32
- return idxDest.IsSmallIndex() ? idxDest.GetSmallIndex() : UINT_MAX;
- }
- template <> Var JavascriptArray::ConvertToIndex(uint32 idxDest, ScriptContext* scriptContext)
- {
- return JavascriptNumber::ToVar(idxDest, scriptContext);
- }
- BOOL JavascriptArray::SetArrayLikeObjects(RecyclableObject* pDestObj, uint32 idxDest, Var aItem)
- {
- return pDestObj->SetItem(idxDest, aItem, Js::PropertyOperation_ThrowIfNotExtensible);
- }
- BOOL JavascriptArray::SetArrayLikeObjects(RecyclableObject* pDestObj, BigIndex idxDest, Var aItem)
- {
- ScriptContext* scriptContext = pDestObj->GetScriptContext();
- if (idxDest.IsSmallIndex())
- {
- return pDestObj->SetItem(idxDest.GetSmallIndex(), aItem, Js::PropertyOperation_ThrowIfNotExtensible);
- }
- PropertyRecord const * propertyRecord;
- JavascriptOperators::GetPropertyIdForInt(idxDest.GetBigIndex(), scriptContext, &propertyRecord);
- return pDestObj->SetProperty(propertyRecord->GetPropertyId(), aItem, PropertyOperation_ThrowIfNotExtensible, nullptr);
- }
- template<typename T>
- void JavascriptArray::ConcatArgs(RecyclableObject* pDestObj, TypeId* remoteTypeIds, Js::Arguments& args, ScriptContext* scriptContext, uint start, BigIndex startIdxDest, BOOL FirstPromotedItemIsSpreadable, BigIndex FirstPromotedItemLength)
- {
- // This never gets called.
- Throw::InternalError();
- }
- //
- // Helper for EntryConcat. Concat args or elements of arg arrays into dest array.
- //
- template<typename T>
- void JavascriptArray::ConcatArgs(RecyclableObject* pDestObj, TypeId* remoteTypeIds, Js::Arguments& args, ScriptContext* scriptContext, uint start, uint startIdxDest, BOOL firstPromotedItemIsSpreadable, BigIndex firstPromotedItemLength)
- {
- JavascriptArray* pDestArray = nullptr;
- if (JavascriptArray::Is(pDestObj))
- {
- pDestArray = JavascriptArray::FromVar(pDestObj);
- }
- T idxDest = startIdxDest;
- for (uint idxArg = start; idxArg < args.Info.Count; idxArg++)
- {
- Var aItem = args[idxArg];
- BOOL spreadable = false;
- if (scriptContext->GetConfig()->IsES6IsConcatSpreadableEnabled())
- {
- // firstPromotedItemIsSpreadable is ONLY used to resume after a type promotion from uint32 to uint64
- // we do this because calls to IsConcatSpreadable are observable (a big deal for proxies) and we don't
- // want to do the work a second time as soon as we record the length we clear the flag.
- spreadable = firstPromotedItemIsSpreadable || JavascriptOperators::IsConcatSpreadable(aItem);
- if (!spreadable)
- {
- JavascriptArray::SetConcatItem<T>(aItem, idxArg, pDestArray, pDestObj, idxDest, scriptContext);
- ++idxDest;
- continue;
- }
- }
- if (pDestArray && JavascriptArray::IsDirectAccessArray(aItem) && JavascriptArray::IsDirectAccessArray(pDestArray)) // Fast path
- {
- if (JavascriptNativeIntArray::Is(aItem))
- {
- JavascriptNativeIntArray *pItemArray = JavascriptNativeIntArray::FromVar(aItem);
- CopyNativeIntArrayElementsToVar(pDestArray, idxDest, pItemArray);
- idxDest = idxDest + pItemArray->length;
- }
- else if (JavascriptNativeFloatArray::Is(aItem))
- {
- JavascriptNativeFloatArray *pItemArray = JavascriptNativeFloatArray::FromVar(aItem);
- CopyNativeFloatArrayElementsToVar(pDestArray, idxDest, pItemArray);
- idxDest = idxDest + pItemArray->length;
- }
- else
- {
- JavascriptArray* pItemArray = JavascriptArray::FromVar(aItem);
- CopyArrayElements(pDestArray, idxDest, pItemArray);
- idxDest = idxDest + pItemArray->length;
- }
- }
- else
- {
- // Flatten if other array or remote array (marked with TypeIds_Array)
- if (DynamicObject::IsAnyArray(aItem) || remoteTypeIds[idxArg] == TypeIds_Array || spreadable)
- {
- //CONSIDER: enumerating remote array instead of walking all indices
- BigIndex length;
- if (firstPromotedItemIsSpreadable)
- {
- firstPromotedItemIsSpreadable = false;
- length = firstPromotedItemLength;
- }
- else if (scriptContext->GetConfig()->IsES6ToLengthEnabled())
- {
- // we can cast to uin64 without fear of converting negative numbers to large positive ones
- // from int64 because ToLength makes negative lengths 0
- length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(aItem, scriptContext), scriptContext);
- }
- else
- {
- length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(aItem, scriptContext), scriptContext);
- }
- if (PromoteToBigIndex(length,idxDest))
- {
- // This is a special case for spreadable objects. We do not pre-calculate the length
- // in EntryConcat like we do with Arrays because a getProperty on an object Length
- // is observable. The result is we have to check for overflows separately for
- // spreadable objects and promote to a bigger index type when we find them.
- ConcatArgs<BigIndex>(pDestArray, remoteTypeIds, args, scriptContext, idxArg, idxDest, /*firstPromotedItemIsSpreadable*/true, length);
- return;
- }
- RecyclableObject* itemObject = RecyclableObject::FromVar(aItem);
- Var subItem;
- uint32 lengthToUin32Max = length.IsSmallIndex() ? length.GetSmallIndex() : MaxArrayLength;
- for (uint32 idxSubItem = 0u; idxSubItem < lengthToUin32Max; ++idxSubItem)
- {
- if (JavascriptOperators::HasItem(itemObject, idxSubItem))
- {
- JavascriptOperators::GetItem(itemObject, idxSubItem, &subItem, scriptContext);
- if (pDestArray)
- {
- pDestArray->DirectSetItemAt(idxDest, subItem);
- }
- else
- {
- SetArrayLikeObjects(pDestObj, idxDest, subItem);
- }
- }
- ++idxDest;
- }
- for (BigIndex idxSubItem = MaxArrayLength; idxSubItem < length; ++idxSubItem)
- {
- PropertyRecord const * propertyRecord;
- JavascriptOperators::GetPropertyIdForInt(idxSubItem.GetBigIndex(), scriptContext, &propertyRecord);
- if (JavascriptOperators::HasProperty(itemObject,propertyRecord->GetPropertyId()))
- {
- subItem = JavascriptOperators::GetProperty(itemObject, propertyRecord->GetPropertyId(), scriptContext);
- if (pDestArray)
- {
- pDestArray->DirectSetItemAt(idxDest, subItem);
- }
- else
- {
- SetArrayLikeObjects(pDestObj, idxDest, subItem);
- }
- }
- ++idxDest;
- }
- }
- else // concat 1 item
- {
- JavascriptArray::SetConcatItem<T>(aItem, idxArg, pDestArray, pDestObj, idxDest, scriptContext);
- ++idxDest;
- }
- }
- }
- if (!pDestArray)
- {
- pDestObj->SetProperty(PropertyIds::length, ConvertToIndex<T, Var>(idxDest, scriptContext), Js::PropertyOperation_None, nullptr);
- }
- else if (pDestArray->GetLength() != ConvertToIndex<T, uint32>(idxDest, scriptContext))
- {
- pDestArray->SetLength(ConvertToIndex<T, uint32>(idxDest, scriptContext));
- }
- }
- bool JavascriptArray::PromoteToBigIndex(BigIndex lhs, BigIndex rhs)
- {
- return false; // already a big index
- }
- bool JavascriptArray::PromoteToBigIndex(BigIndex lhs, uint32 rhs)
- {
- ::Math::RecordOverflowPolicy destLengthOverflow;
- if (lhs.IsSmallIndex())
- {
- UInt32Math::Add(lhs.GetSmallIndex(), rhs, destLengthOverflow);
- return destLengthOverflow.HasOverflowed();
- }
- return true;
- }
- void JavascriptArray::ConcatIntArgs(JavascriptNativeIntArray* pDestArray, TypeId *remoteTypeIds, Js::Arguments& args, ScriptContext* scriptContext)
- {
- uint idxDest = 0u;
- for (uint idxArg = 0; idxArg < args.Info.Count; idxArg++)
- {
- Var aItem = args[idxArg];
- if (scriptContext->GetConfig()->IsES6IsConcatSpreadableEnabled() && !JavascriptOperators::IsConcatSpreadable(aItem))
- {
- pDestArray->SetItem(idxDest, aItem, PropertyOperation_ThrowIfNotExtensible);
- idxDest = idxDest + 1;
- if (!JavascriptNativeIntArray::Is(pDestArray)) // SetItem could convert pDestArray to a var array if aItem is not an integer if so fall back
- {
- ConcatArgs<uint>(pDestArray, remoteTypeIds, args, scriptContext, idxArg + 1, idxDest);
- return;
- }
- continue;
- }
- if (JavascriptNativeIntArray::Is(aItem)) // Fast path
- {
- JavascriptNativeIntArray* pItemArray = JavascriptNativeIntArray::FromVar(aItem);
- bool converted = CopyNativeIntArrayElements(pDestArray, idxDest, pItemArray);
- idxDest = idxDest + pItemArray->length;
- if (converted)
- {
- // Copying the last array forced a conversion, so switch over to the var version
- // to finish.
- ConcatArgs<uint>(pDestArray, remoteTypeIds, args, scriptContext, idxArg + 1, idxDest);
- return;
- }
- }
- else
- {
- Assert(!JavascriptArray::IsAnyArray(aItem) && remoteTypeIds[idxArg] != TypeIds_Array);
- if (TaggedInt::Is(aItem))
- {
- pDestArray->DirectSetItemAt(idxDest, TaggedInt::ToInt32(aItem));
- }
- else
- {
- #if DBG
- int32 int32Value;
- Assert(
- JavascriptNumber::TryGetInt32Value(JavascriptNumber::GetValue(aItem), &int32Value) &&
- !SparseArraySegment<int32>::IsMissingItem(&int32Value));
- #endif
- pDestArray->DirectSetItemAt(idxDest, static_cast<int32>(JavascriptNumber::GetValue(aItem)));
- }
- ++idxDest;
- }
- }
- if (pDestArray->GetLength() != idxDest)
- {
- pDestArray->SetLength(idxDest);
- }
- }
- void JavascriptArray::ConcatFloatArgs(JavascriptNativeFloatArray* pDestArray, TypeId *remoteTypeIds, Js::Arguments& args, ScriptContext* scriptContext)
- {
- uint idxDest = 0u;
- for (uint idxArg = 0; idxArg < args.Info.Count; idxArg++)
- {
- Var aItem = args[idxArg];
- if (scriptContext->GetConfig()->IsES6IsConcatSpreadableEnabled() && !JavascriptOperators::IsConcatSpreadable(aItem))
- {
- pDestArray->SetItem(idxDest, aItem, PropertyOperation_ThrowIfNotExtensible);
- idxDest = idxDest + 1;
- if (!JavascriptNativeFloatArray::Is(pDestArray)) // SetItem could convert pDestArray to a var array if aItem is not an integer if so fall back
- {
- ConcatArgs<uint>(pDestArray, remoteTypeIds, args, scriptContext, idxArg + 1, idxDest);
- return;
- }
- continue;
- }
- bool converted;
- if (JavascriptArray::IsAnyArray(aItem))
- {
- if (JavascriptNativeIntArray::Is(aItem)) // Fast path
- {
- JavascriptNativeIntArray *pIntArray = JavascriptNativeIntArray::FromVar(aItem);
- converted = CopyNativeIntArrayElementsToFloat(pDestArray, idxDest, pIntArray);
- idxDest = idxDest + pIntArray->length;
- }
- else
- {
- JavascriptNativeFloatArray* pItemArray = JavascriptNativeFloatArray::FromVar(aItem);
- converted = CopyNativeFloatArrayElements(pDestArray, idxDest, pItemArray);
- idxDest = idxDest + pItemArray->length;
- }
- if (converted)
- {
- // Copying the last array forced a conversion, so switch over to the var version
- // to finish.
- ConcatArgs<uint>(pDestArray, remoteTypeIds, args, scriptContext, idxArg + 1, idxDest);
- return;
- }
- }
- else
- {
- Assert(!JavascriptArray::IsAnyArray(aItem) && remoteTypeIds[idxArg] != TypeIds_Array);
- if (TaggedInt::Is(aItem))
- {
- pDestArray->DirectSetItemAt(idxDest, (double)TaggedInt::ToInt32(aItem));
- }
- else
- {
- Assert(JavascriptNumber::Is(aItem));
- pDestArray->DirectSetItemAt(idxDest, JavascriptNumber::GetValue(aItem));
- }
- ++idxDest;
- }
- }
- if (pDestArray->GetLength() != idxDest)
- {
- pDestArray->SetLength(idxDest);
- }
- }
- bool JavascriptArray::BoxConcatItem(Var aItem, uint idxArg, ScriptContext *scriptContext)
- {
- return idxArg == 0 && !JavascriptOperators::IsObject(aItem);
- }
- Var JavascriptArray::EntryConcat(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- if (args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.concat"));
- }
- //
- // Compute the destination ScriptArray size:
- // - Each item, flattening only one level if a ScriptArray.
- //
- uint32 cDestLength = 0;
- JavascriptArray * pDestArray = NULL;
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault + (args.Info.Count * sizeof(TypeId*)));
- TypeId* remoteTypeIds = (TypeId*)_alloca(args.Info.Count * sizeof(TypeId*));
- bool isInt = true;
- bool isFloat = true;
- ::Math::RecordOverflowPolicy destLengthOverflow;
- for (uint idxArg = 0; idxArg < args.Info.Count; idxArg++)
- {
- Var aItem = args[idxArg];
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(aItem);
- #endif
- if (DynamicObject::IsAnyArray(aItem)) // Get JavascriptArray or ES5Array length
- {
- JavascriptArray * pItemArray = JavascriptArray::FromAnyArray(aItem);
- if (isFloat)
- {
- if (!JavascriptNativeIntArray::Is(pItemArray))
- {
- isInt = false;
- if (!JavascriptNativeFloatArray::Is(pItemArray))
- {
- isFloat = false;
- }
- }
- }
- cDestLength = UInt32Math::Add(cDestLength, pItemArray->GetLength(), destLengthOverflow);
- }
- else // Get remote array or object length
- {
- // We already checked for types derived from JavascriptArray. These are types that should behave like array
- // i.e. proxy to array and remote array.
- if (JavascriptOperators::IsArray(aItem))
- {
- // Don't try to preserve nativeness of remote arrays. The extra complexity is probably not
- // worth it.
- isInt = false;
- isFloat = false;
- if (!JavascriptProxy::Is(aItem))
- {
- if (scriptContext->GetConfig()->IsES6ToLengthEnabled())
- {
- int64 len = JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(aItem, scriptContext), scriptContext);
- // clipping to MaxArrayLength will overflow when added to cDestLength which we catch below
- cDestLength = UInt32Math::Add(cDestLength, len < MaxArrayLength ? (uint32)len : MaxArrayLength, destLengthOverflow);
- }
- else
- {
- uint len = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(aItem, scriptContext), scriptContext);
- cDestLength = UInt32Math::Add(cDestLength, len, destLengthOverflow);
- }
- }
- remoteTypeIds[idxArg] = TypeIds_Array; // Mark remote array, no matter remote JavascriptArray or ES5Array.
- }
- else
- {
- if (isFloat)
- {
- if (BoxConcatItem(aItem, idxArg, scriptContext))
- {
- // A primitive will be boxed, so we have to create a var array for the result.
- isInt = false;
- isFloat = false;
- }
- else if (!TaggedInt::Is(aItem))
- {
- if (!JavascriptNumber::Is(aItem))
- {
- isInt = false;
- isFloat = false;
- }
- else if (isInt)
- {
- int32 int32Value;
- if(!JavascriptNumber::TryGetInt32Value(JavascriptNumber::GetValue(aItem), &int32Value) ||
- SparseArraySegment<int32>::IsMissingItem(&int32Value))
- {
- isInt = false;
- }
- }
- }
- else if(isInt)
- {
- int32 int32Value = TaggedInt::ToInt32(aItem);
- if(SparseArraySegment<int32>::IsMissingItem(&int32Value))
- {
- isInt = false;
- }
- }
- }
- remoteTypeIds[idxArg] = TypeIds_Limit;
- cDestLength = UInt32Math::Add(cDestLength, 1, destLengthOverflow);
- }
- }
- }
- if (destLengthOverflow.HasOverflowed())
- {
- cDestLength = MaxArrayLength;
- isInt = false;
- isFloat = false;
- }
- //
- // Create the destination array
- //
- RecyclableObject* pDestObj = nullptr;
- bool isArray = false;
- pDestObj = ArraySpeciesCreate(args[0], 0, scriptContext);
- if (pDestObj)
- {
- isInt = JavascriptNativeIntArray::Is(pDestObj);
- isFloat = !isInt && JavascriptNativeFloatArray::Is(pDestObj); // if we know it is an int short the condition to avoid a function call
- isArray = isInt || isFloat || JavascriptArray::Is(pDestObj);
- }
- if (pDestObj == nullptr || isArray)
- {
- if (isInt)
- {
- JavascriptNativeIntArray *pIntArray = isArray ? JavascriptNativeIntArray::FromVar(pDestObj) : scriptContext->GetLibrary()->CreateNativeIntArray(cDestLength);
- pIntArray->EnsureHead<int32>();
- ConcatIntArgs(pIntArray, remoteTypeIds, args, scriptContext);
- pDestArray = pIntArray;
- }
- else if (isFloat)
- {
- JavascriptNativeFloatArray *pFArray = isArray ? JavascriptNativeFloatArray::FromVar(pDestObj) : scriptContext->GetLibrary()->CreateNativeFloatArray(cDestLength);
- pFArray->EnsureHead<double>();
- ConcatFloatArgs(pFArray, remoteTypeIds, args, scriptContext);
- pDestArray = pFArray;
- }
- else
- {
- pDestArray = isArray ? JavascriptArray::FromVar(pDestObj) : scriptContext->GetLibrary()->CreateArray(cDestLength);
- // if the constructor has changed then we no longer specialize for ints and floats
- pDestArray->EnsureHead<Var>();
- ConcatArgsCallingHelper(pDestArray, remoteTypeIds, args, scriptContext, destLengthOverflow);
- }
- //
- // Return the new array instance.
- //
- #ifdef VALIDATE_ARRAY
- pDestArray->ValidateArray();
- #endif
- return pDestArray;
- }
- Assert(pDestObj);
- ConcatArgsCallingHelper(pDestObj, remoteTypeIds, args, scriptContext, destLengthOverflow);
- return pDestObj;
- }
- void JavascriptArray::ConcatArgsCallingHelper(RecyclableObject* pDestObj, TypeId* remoteTypeIds, Js::Arguments& args, ScriptContext* scriptContext, ::Math::RecordOverflowPolicy &destLengthOverflow)
- {
- if (destLengthOverflow.HasOverflowed())
- {
- ConcatArgs<BigIndex>(pDestObj, remoteTypeIds, args, scriptContext);
- }
- else
- {
- // Use faster uint32 version if no overflow
- ConcatArgs<uint32>(pDestObj, remoteTypeIds, args, scriptContext);
- }
- }
- template<typename T>
- /* static */ void JavascriptArray::SetConcatItem(Var aItem, uint idxArg, JavascriptArray* pDestArray, RecyclableObject* pDestObj, T idxDest, ScriptContext *scriptContext)
- {
- if (BoxConcatItem(aItem, idxArg, scriptContext))
- {
- // bug# 725784: ES5: not calling ToObject in Step 1 of 15.4.4.4
- RecyclableObject* pObj = nullptr;
- if (FALSE == JavascriptConversion::ToObject(aItem, scriptContext, &pObj))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.concat"));
- }
- if (pDestArray)
- {
- pDestArray->DirectSetItemAt(idxDest, pObj);
- }
- else
- {
- SetArrayLikeObjects(pDestObj, idxDest, pObj);
- }
- }
- else
- {
- if (pDestArray)
- {
- pDestArray->DirectSetItemAt(idxDest, aItem);
- }
- else
- {
- SetArrayLikeObjects(pDestObj, idxDest, aItem);
- }
- }
- }
- uint32 JavascriptArray::GetFromIndex(Var arg, uint32 length, ScriptContext *scriptContext)
- {
- uint32 fromIndex;
- if (TaggedInt::Is(arg))
- {
- int intValue = TaggedInt::ToInt32(arg);
- if (intValue >= 0)
- {
- fromIndex = intValue;
- }
- else
- {
- // (intValue + length) may exceed 2^31 or may be < 0, so promote to int64
- fromIndex = (uint32)max(0i64, (int64)(length) + intValue);
- }
- }
- else
- {
- double value = JavascriptConversion::ToInteger(arg, scriptContext);
- if (value > length)
- {
- return (uint32)-1;
- }
- else if (value >= 0)
- {
- fromIndex = (uint32)value;
- }
- else
- {
- fromIndex = (uint32)max((double)0, value + length);
- }
- }
- return fromIndex;
- }
- uint64 JavascriptArray::GetFromIndex(Var arg, uint64 length, ScriptContext *scriptContext)
- {
- uint64 fromIndex;
- if (TaggedInt::Is(arg))
- {
- int64 intValue = TaggedInt::ToInt64(arg);
- if (intValue >= 0)
- {
- fromIndex = intValue;
- }
- else
- {
- fromIndex = max((int64)0, (int64)(intValue + length));
- }
- }
- else
- {
- double value = JavascriptConversion::ToInteger(arg, scriptContext);
- if (value > length)
- {
- return (uint64)-1;
- }
- else if (value >= 0)
- {
- fromIndex = (uint64)value;
- }
- else
- {
- fromIndex = (uint64)max((double)0, value + length);
- }
- }
- return fromIndex;
- }
- int64 JavascriptArray::GetFromLastIndex(Var arg, int64 length, ScriptContext *scriptContext)
- {
- int64 fromIndex;
- if (TaggedInt::Is(arg))
- {
- int intValue = TaggedInt::ToInt32(arg);
- if (intValue >= 0)
- {
- fromIndex = min<int64>(intValue, length - 1);
- }
- else if ((uint32)-intValue > length)
- {
- return length;
- }
- else
- {
- fromIndex = intValue + length;
- }
- }
- else
- {
- double value = JavascriptConversion::ToInteger(arg, scriptContext);
- if (value >= 0)
- {
- fromIndex = (int64)min(value, (double)(length - 1));
- }
- else if (value + length < 0)
- {
- return length;
- }
- else
- {
- fromIndex = (int64)(value + length);
- }
- }
- return fromIndex;
- }
- // includesAlgorithm specifies to follow ES7 Array.prototype.includes semantics instead of Array.prototype.indexOf
- // Differences
- // 1. Returns boolean true or false value instead of the search hit index
- // 2. Follows SameValueZero algorithm instead of StrictEquals
- // 3. Missing values are scanned if the search value is undefined
- template <bool includesAlgorithm>
- Var JavascriptArray::IndexOfHelper(Arguments const & args, ScriptContext *scriptContext)
- {
- RecyclableObject* obj = nullptr;
- JavascriptArray* pArr = nullptr;
- BigIndex length;
- Var trueValue = scriptContext->GetLibrary()->GetTrue();
- Var falseValue = scriptContext->GetLibrary()->GetFalse();
- if (JavascriptArray::Is(args[0]))
- {
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(args[0]);
- #endif
- pArr = JavascriptArray::FromVar(args[0]);
- obj = pArr;
- }
- else
- {
- if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.indexOf"));
- }
- }
- // In ES6-mode, we always load the length property from the object instead of using the internal slot.
- // Even for arrays, this is now observable via proxies.
- // If source object is not an array, we fall back to this behavior anyway.
- if (scriptContext->GetConfig()->IsES6TypedArrayExtensionsEnabled() || pArr == nullptr)
- {
- if (scriptContext->GetConfig()->IsES6ToLengthEnabled())
- {
- length = (uint64)JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext);
- }
- else
- {
- length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext);
- }
- }
- else
- {
- length = pArr->length;
- }
- if (pArr)
- {
- Var search;
- uint32 fromIndex;
- uint32 len = length.IsUint32Max() ? MaxArrayLength : length.GetSmallIndex();
- if (!GetParamForIndexOf(len, args, search, fromIndex, scriptContext))
- {
- return includesAlgorithm ? falseValue : TaggedInt::ToVarUnchecked(-1);
- }
- int32 index = pArr->HeadSegmentIndexOfHelper(search, fromIndex, len, includesAlgorithm, scriptContext);
- // If we found the search value in the head segment, or if we determined there is no need to search other segments,
- // we stop right here.
- if (index != -1 || fromIndex == -1)
- {
- if (includesAlgorithm)
- {
- //Array.prototype.includes
- return (index == -1)? falseValue : trueValue;
- }
- else
- {
- //Array.prototype.indexOf
- return JavascriptNumber::ToVar(index, scriptContext);
- }
- }
- // If we really must search other segments, let's do it now. We'll have to search the slow way (dealing with holes, etc.).
- switch (pArr->GetTypeId())
- {
- case Js::TypeIds_Array:
- return TemplatedIndexOfHelper<includesAlgorithm>(pArr, search, fromIndex, len, scriptContext);
- case Js::TypeIds_NativeIntArray:
- return TemplatedIndexOfHelper<includesAlgorithm>(JavascriptNativeIntArray::FromVar(pArr), search, fromIndex, len, scriptContext);
- case Js::TypeIds_NativeFloatArray:
- return TemplatedIndexOfHelper<includesAlgorithm>(JavascriptNativeFloatArray::FromVar(pArr), search, fromIndex, len, scriptContext);
- default:
- AssertMsg(FALSE, "invalid array typeid");
- return TemplatedIndexOfHelper<includesAlgorithm>(pArr, search, fromIndex, len, scriptContext);
- }
- }
- // source object is not a JavascriptArray but source could be a TypedArray
- if (TypedArrayBase::Is(obj))
- {
- if (length.IsSmallIndex() || length.IsUint32Max())
- {
- Var search;
- uint32 fromIndex;
- uint32 len = length.IsUint32Max() ? MaxArrayLength : length.GetSmallIndex();
- if (!GetParamForIndexOf(len, args, search, fromIndex, scriptContext))
- {
- return includesAlgorithm ? falseValue : TaggedInt::ToVarUnchecked(-1);
- }
- return TemplatedIndexOfHelper<includesAlgorithm>(TypedArrayBase::FromVar(obj), search, fromIndex, length.GetSmallIndex(), scriptContext);
- }
- }
- if (length.IsSmallIndex())
- {
- Var search;
- uint32 fromIndex;
- if (!GetParamForIndexOf(length.GetSmallIndex(), args, search, fromIndex, scriptContext))
- {
- return includesAlgorithm ? falseValue : TaggedInt::ToVarUnchecked(-1);
- }
- return TemplatedIndexOfHelper<includesAlgorithm>(obj, search, fromIndex, length.GetSmallIndex(), scriptContext);
- }
- else
- {
- Var search;
- uint64 fromIndex;
- if (!GetParamForIndexOf(length.GetBigIndex(), args, search, fromIndex, scriptContext))
- {
- return includesAlgorithm ? falseValue : TaggedInt::ToVarUnchecked(-1);
- }
- return TemplatedIndexOfHelper<includesAlgorithm>(obj, search, fromIndex, length.GetBigIndex(), scriptContext);
- }
- }
- // Array.prototype.indexOf as defined in ES6.0 (final) Section 22.1.3.11
- Var JavascriptArray::EntryIndexOf(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(ArrayIndexOfCount);
- Var returnValue = IndexOfHelper<false>(args, scriptContext);
- //IndexOfHelper code is reused for array.prototype.includes as well. Let us assert here we didn't get a true or false instead of index
- Assert(returnValue != scriptContext->GetLibrary()->GetTrue() && returnValue != scriptContext->GetLibrary()->GetFalse());
- return returnValue;
- }
- Var JavascriptArray::EntryIncludes(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(ArrayIncludesCount);
- Var returnValue = IndexOfHelper<true>(args, scriptContext);
- Assert(returnValue == scriptContext->GetLibrary()->GetTrue() || returnValue == scriptContext->GetLibrary()->GetFalse());
- return returnValue;
- }
- template<typename T>
- BOOL JavascriptArray::GetParamForIndexOf(T length, Arguments const& args, Var& search, T& fromIndex, ScriptContext * scriptContext)
- {
- if (length == 0)
- {
- return false;
- }
- if (args.Info.Count > 2)
- {
- fromIndex = GetFromIndex(args[2], length, scriptContext);
- if (fromIndex >= length)
- {
- return false;
- }
- search = args[1];
- }
- else
- {
- fromIndex = 0;
- search = args.Info.Count > 1 ? args[1] : scriptContext->GetLibrary()->GetUndefined();
- }
- return true;
- }
- template <>
- BOOL JavascriptArray::TemplatedGetItem(RecyclableObject * obj, uint32 index, Var * element, ScriptContext * scriptContext)
- {
- // Note: Sometime cross site array go down this path to get the marshalling
- Assert(!VirtualTableInfo<JavascriptArray>::HasVirtualTable(obj)
- && !VirtualTableInfo<JavascriptNativeIntArray>::HasVirtualTable(obj)
- && !VirtualTableInfo<JavascriptNativeFloatArray>::HasVirtualTable(obj));
- if (!JavascriptOperators::HasItem(obj, index))
- {
- return FALSE;
- }
- return JavascriptOperators::GetItem(obj, index, element, scriptContext);
- }
- template <>
- BOOL JavascriptArray::TemplatedGetItem(RecyclableObject * obj, uint64 index, Var * element, ScriptContext * scriptContext)
- {
- // Note: Sometime cross site array go down this path to get the marshalling
- Assert(!VirtualTableInfo<JavascriptArray>::HasVirtualTable(obj)
- && !VirtualTableInfo<JavascriptNativeIntArray>::HasVirtualTable(obj)
- && !VirtualTableInfo<JavascriptNativeFloatArray>::HasVirtualTable(obj));
- PropertyRecord const * propertyRecord;
- JavascriptOperators::GetPropertyIdForInt(index, scriptContext, &propertyRecord);
- if (!JavascriptOperators::HasProperty(obj, propertyRecord->GetPropertyId()))
- {
- return FALSE;
- }
- *element = JavascriptOperators::GetProperty(obj, propertyRecord->GetPropertyId(), scriptContext);
- return *element != scriptContext->GetLibrary()->GetUndefined();
- }
- template <>
- BOOL JavascriptArray::TemplatedGetItem(JavascriptArray *pArr, uint32 index, Var * element, ScriptContext * scriptContext)
- {
- Assert(VirtualTableInfo<JavascriptArray>::HasVirtualTable(pArr)
- || VirtualTableInfo<CrossSiteObject<JavascriptArray>>::HasVirtualTable(pArr));
- return pArr->JavascriptArray::DirectGetItemAtFull(index, element);
- }
- template <>
- BOOL JavascriptArray::TemplatedGetItem(JavascriptArray *pArr, uint64 index, Var * element, ScriptContext * scriptContext)
- {
- // This should never get called.
- Assert(false);
- Throw::InternalError();
- }
- template <>
- BOOL JavascriptArray::TemplatedGetItem(JavascriptNativeIntArray *pArr, uint32 index, Var * element, ScriptContext * scriptContext)
- {
- Assert(VirtualTableInfo<JavascriptNativeIntArray>::HasVirtualTable(pArr)
- || VirtualTableInfo<CrossSiteObject<JavascriptNativeIntArray>>::HasVirtualTable(pArr));
- return pArr->JavascriptNativeIntArray::DirectGetItemAtFull(index, element);
- }
- template <>
- BOOL JavascriptArray::TemplatedGetItem(JavascriptNativeIntArray *pArr, uint64 index, Var * element, ScriptContext * scriptContext)
- {
- // This should never get called.
- Assert(false);
- Throw::InternalError();
- }
- template <>
- BOOL JavascriptArray::TemplatedGetItem(JavascriptNativeFloatArray *pArr, uint32 index, Var * element, ScriptContext * scriptContext)
- {
- Assert(VirtualTableInfo<JavascriptNativeFloatArray>::HasVirtualTable(pArr)
- || VirtualTableInfo<CrossSiteObject<JavascriptNativeFloatArray>>::HasVirtualTable(pArr));
- return pArr->JavascriptNativeFloatArray::DirectGetItemAtFull(index, element);
- }
- template <>
- BOOL JavascriptArray::TemplatedGetItem(JavascriptNativeFloatArray *pArr, uint64 index, Var * element, ScriptContext * scriptContext)
- {
- // This should never get called.
- Assert(false);
- Throw::InternalError();
- }
- template <>
- BOOL JavascriptArray::TemplatedGetItem(TypedArrayBase * typedArrayBase, uint32 index, Var * element, ScriptContext * scriptContext)
- {
- // We need to do explicit check for items since length value may not actually match the actual TypedArray length.
- // User could add a length property to a TypedArray instance which lies and returns a different value from the underlying length.
- // Since this method can be called via Array.prototype.indexOf with .apply or .call passing a TypedArray as this parameter
- // we don't know whether or not length == typedArrayBase->GetLength().
- if (!typedArrayBase->HasItem(index))
- {
- return false;
- }
- *element = typedArrayBase->DirectGetItem(index);
- return true;
- }
- template <>
- BOOL JavascriptArray::TemplatedGetItem(TypedArrayBase * typedArrayBase, uint64 index, Var * element, ScriptContext * scriptContext)
- {
- // This should never get called.
- Assert(false);
- Throw::InternalError();
- }
- template <bool includesAlgorithm, typename T, typename P>
- Var JavascriptArray::TemplatedIndexOfHelper(T * pArr, Var search, P fromIndex, P toIndex, ScriptContext * scriptContext)
- {
- Var element = nullptr;
- bool isSearchTaggedInt = TaggedInt::Is(search);
- bool doUndefinedSearch = includesAlgorithm && JavascriptOperators::GetTypeId(search) == TypeIds_Undefined;
- Var trueValue = scriptContext->GetLibrary()->GetTrue();
- Var falseValue = scriptContext->GetLibrary()->GetFalse();
- //Consider: enumerating instead of walking all indices
- for (P i = fromIndex; i < toIndex; i++)
- {
- if (!TemplatedGetItem(pArr, i, &element, scriptContext))
- {
- if (doUndefinedSearch)
- {
- return trueValue;
- }
- continue;
- }
- if (isSearchTaggedInt && TaggedInt::Is(element))
- {
- if (element == search)
- {
- return includesAlgorithm? trueValue : JavascriptNumber::ToVar(i, scriptContext);
- }
- continue;
- }
- if (includesAlgorithm)
- {
- //Array.prototype.includes
- if (JavascriptConversion::SameValueZero(element, search))
- {
- return trueValue;
- }
- }
- else
- {
- //Array.prototype.indexOf
- if (JavascriptOperators::StrictEqual(element, search, scriptContext))
- {
- return JavascriptNumber::ToVar(i, scriptContext);
- }
- }
- }
- return includesAlgorithm ? falseValue : TaggedInt::ToVarUnchecked(-1);
- }
- int32 JavascriptArray::HeadSegmentIndexOfHelper(Var search, uint32 &fromIndex, uint32 toIndex, bool includesAlgorithm, ScriptContext * scriptContext)
- {
- Assert(Is(GetTypeId()) && !JavascriptNativeArray::Is(GetTypeId()));
- if (!HasNoMissingValues() || fromIndex >= GetHead()->length)
- {
- return -1;
- }
- bool isSearchTaggedInt = TaggedInt::Is(search);
- // We need to cast head segment to SparseArraySegment<Var> to have access to GetElement (onSparseArraySegment<T>). Because there are separate overloads of this
- // virtual method on JavascriptNativeIntArray and JavascriptNativeFloatArray, we know this version of this method will only be called for true JavascriptArray, and not for
- // either of the derived native arrays, so the elements of each segment used here must be Vars. Hence, the cast is safe.
- SparseArraySegment<Var>* head = static_cast<SparseArraySegment<Var>*>(GetHead());
- uint32 toIndexTrimmed = toIndex <= head->length ? toIndex : head->length;
- for (uint32 i = fromIndex; i < toIndexTrimmed; i++)
- {
- Var element = head->GetElement(i);
- if (isSearchTaggedInt && TaggedInt::Is(element))
- {
- if (search == element)
- {
- return i;
- }
- }
- else if (includesAlgorithm && JavascriptConversion::SameValueZero(element, search))
- {
- //Array.prototype.includes
- return i;
- }
- else if (JavascriptOperators::StrictEqual(element, search, scriptContext))
- {
- //Array.prototype.indexOf
- return i;
- }
- }
- // Element not found in the head segment. Keep looking only if the range of indices extends past
- // the head segment.
- fromIndex = toIndex > GetHead()->length ? GetHead()->length : -1;
- return -1;
- }
- int32 JavascriptNativeIntArray::HeadSegmentIndexOfHelper(Var search, uint32 &fromIndex, uint32 toIndex, bool includesAlgorithm, ScriptContext * scriptContext)
- {
- // We proceed largely in the same manner as in JavascriptArray's version of this method (see comments there for more information),
- // except when we can further optimize thanks to the knowledge that all elements in the array are int32's. This allows for two additional optimizations:
- // 1. Only tagged ints or JavascriptNumbers that can be represented as int32 can be strict equal to some element in the array (all int32). Thus, if
- // the search value is some other kind of Var, we can return -1 without ever iterating over the elements.
- // 2. If the search value is a number that can be represented as int32, then we inspect the elements, but we don't need to perform the full strict equality algorithm.
- // Instead we can use simple C++ equality (which in case of such values is equivalent to strict equality in JavaScript).
- if (!HasNoMissingValues() || fromIndex >= GetHead()->length)
- {
- return -1;
- }
- bool isSearchTaggedInt = TaggedInt::Is(search);
- if (!isSearchTaggedInt && !JavascriptNumber::Is_NoTaggedIntCheck(search))
- {
- // The value can't be in the array, but it could be in a prototype, and we can only guarantee that
- // the head segment has no gaps.
- fromIndex = toIndex > GetHead()->length ? GetHead()->length : -1;
- return -1;
- }
- int32 searchAsInt32;
- if (isSearchTaggedInt)
- {
- searchAsInt32 = TaggedInt::ToInt32(search);
- }
- else if (!JavascriptNumber::TryGetInt32Value<true>(JavascriptNumber::GetValue(search), &searchAsInt32))
- {
- // The value can't be in the array, but it could be in a prototype, and we can only guarantee that
- // the head segment has no gaps.
- fromIndex = toIndex > GetHead()->length ? GetHead()->length : -1;
- return -1;
- }
- // We need to cast head segment to SparseArraySegment<int32> to have access to GetElement (onSparseArraySegment<T>). Because there are separate overloads of this
- // virtual method on JavascriptNativeIntArray and JavascriptNativeFloatArray, we know this version of this method will only be called for true JavascriptNativeIntArray, and not for
- // the other two, so the elements of each segment used here must be int32's. Hence, the cast is safe.
- SparseArraySegment<int32> * head = static_cast<SparseArraySegment<int32>*>(GetHead());
- uint32 toIndexTrimmed = toIndex <= head->length ? toIndex : head->length;
- for (uint32 i = fromIndex; i < toIndexTrimmed; i++)
- {
- int32 element = head->GetElement(i);
- if (searchAsInt32 == element)
- {
- return i;
- }
- }
- // Element not found in the head segment. Keep looking only if the range of indices extends past
- // the head segment.
- fromIndex = toIndex > GetHead()->length ? GetHead()->length : -1;
- return -1;
- }
- int32 JavascriptNativeFloatArray::HeadSegmentIndexOfHelper(Var search, uint32 &fromIndex, uint32 toIndex, bool includesAlgorithm, ScriptContext * scriptContext)
- {
- // We proceed largely in the same manner as in JavascriptArray's version of this method (see comments there for more information),
- // except when we can further optimize thanks to the knowledge that all elements in the array are doubles. This allows for two additional optimizations:
- // 1. Only tagged ints or JavascriptNumbers can be strict equal to some element in the array (all doubles). Thus, if
- // the search value is some other kind of Var, we can return -1 without ever iterating over the elements.
- // 2. If the search value is a number, then we inspect the elements, but we don't need to perform the full strict equality algorithm.
- // Instead we can use simple C++ equality (which in case of such values is equivalent to strict equality in JavaScript).
- if (!HasNoMissingValues() || fromIndex >= GetHead()->length)
- {
- return -1;
- }
- bool isSearchTaggedInt = TaggedInt::Is(search);
- if (!isSearchTaggedInt && !JavascriptNumber::Is_NoTaggedIntCheck(search))
- {
- // The value can't be in the array, but it could be in a prototype, and we can only guarantee that
- // the head segment has no gaps.
- fromIndex = toIndex > GetHead()->length ? GetHead()->length : -1;
- return -1;
- }
- double searchAsDouble = isSearchTaggedInt ? TaggedInt::ToDouble(search) : JavascriptNumber::GetValue(search);
- // We need to cast head segment to SparseArraySegment<double> to have access to GetElement (SparseArraySegment). We know the
- // segment's elements are all Vars so the cast is safe. It would have been more convenient here if JavascriptArray
- // used SparseArraySegment<Var>, instead of SparseArraySegmentBase.
- SparseArraySegment<double> * head = static_cast<SparseArraySegment<double>*>(GetHead());
- uint32 toIndexTrimmed = toIndex <= head->length ? toIndex : head->length;
- bool matchNaN = includesAlgorithm && JavascriptNumber::IsNan(searchAsDouble);
- for (uint32 i = fromIndex; i < toIndexTrimmed; i++)
- {
- double element = head->GetElement(i);
- if (element == searchAsDouble)
- {
- return i;
- }
- //NaN != NaN we expect to match for NaN in Array.prototype.includes algorithm
- if (matchNaN && JavascriptNumber::IsNan(element))
- {
- return i;
- }
- }
- fromIndex = toIndex > GetHead()->length ? GetHead()->length : -1;
- return -1;
- }
- Var JavascriptArray::EntryJoin(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- if (args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.join"));
- }
- JavascriptString* separator;
- if (args.Info.Count >= 2)
- {
- TypeId typeId = JavascriptOperators::GetTypeId(args[1]);
- //ES5 15.4.4.5 If separator is undefined, let separator be the single-character String ",".
- if (TypeIds_Undefined != typeId)
- {
- separator = JavascriptConversion::ToString(args[1], scriptContext);
- }
- else
- {
- separator = scriptContext->GetLibrary()->GetCommaDisplayString();
- }
- }
- else
- {
- separator = scriptContext->GetLibrary()->GetCommaDisplayString();
- }
- return JoinHelper(args[0], separator, scriptContext);
- }
- JavascriptString* JavascriptArray::JoinToString(Var value, ScriptContext* scriptContext)
- {
- TypeId typeId = JavascriptOperators::GetTypeId(value);
- if (typeId == TypeIds_Null || typeId == TypeIds_Undefined)
- {
- return scriptContext->GetLibrary()->GetEmptyString();
- }
- else
- {
- return JavascriptConversion::ToString(value, scriptContext);
- }
- }
- JavascriptString* JavascriptArray::JoinHelper(Var thisArg, JavascriptString* separator, ScriptContext* scriptContext)
- {
- bool isArray = JavascriptArray::Is(thisArg) && (scriptContext == JavascriptArray::FromVar(thisArg)->GetScriptContext());
- bool isProxy = JavascriptProxy::Is(thisArg) && (scriptContext == JavascriptProxy::FromVar(thisArg)->GetScriptContext());
- Var target = NULL;
- bool isTargetObjectPushed = false;
- // if we are visiting a proxy object, track that we have visited the target object as well so the next time w
- // call the join helper for the target of this proxy, we will return above.
- if (isProxy)
- {
- JavascriptProxy* proxy = JavascriptProxy::FromVar(thisArg);
- Assert(proxy);
- target = proxy->GetTarget();
- if (target != nullptr)
- {
- // If we end up joining same array, instead of going in infinite loop, return the empty string
- if (scriptContext->CheckObject(target))
- {
- return scriptContext->GetLibrary()->GetEmptyString();
- }
- else
- {
- scriptContext->PushObject(target);
- isTargetObjectPushed = true;
- }
- }
- }
- // If we end up joining same array, instead of going in infinite loop, return the empty string
- else if (scriptContext->CheckObject(thisArg))
- {
- return scriptContext->GetLibrary()->GetEmptyString();
- }
- if (!isTargetObjectPushed)
- {
- scriptContext->PushObject(thisArg);
- }
- JavascriptString* res = nullptr;
- TryFinally([&]()
- {
- if (isArray)
- {
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray(thisArg);
- #endif
- JavascriptArray * arr = JavascriptArray::FromVar(thisArg);
- switch (arr->GetTypeId())
- {
- case Js::TypeIds_Array:
- res = JoinArrayHelper(arr, separator, scriptContext);
- break;
- case Js::TypeIds_NativeIntArray:
- res = JoinArrayHelper(JavascriptNativeIntArray::FromVar(arr), separator, scriptContext);
- break;
- case Js::TypeIds_NativeFloatArray:
- res = JoinArrayHelper(JavascriptNativeFloatArray::FromVar(arr), separator, scriptContext);
- break;
- }
- }
- else if (RecyclableObject::Is(thisArg))
- {
- res = JoinOtherHelper(RecyclableObject::FromVar(thisArg), separator, scriptContext);
- }
- else
- {
- res = JoinOtherHelper(scriptContext->GetLibrary()->CreateNumberObject(thisArg), separator, scriptContext);
- }
- },
- [&](bool/*hasException*/)
- {
- Var top = scriptContext->PopObject();
- if (JavascriptProxy::Is(thisArg))
- {
- AssertMsg(top == target, "Unmatched operation stack");
- }
- else
- {
- AssertMsg(top == thisArg, "Unmatched operation stack");
- }
- });
- if (res == nullptr)
- {
- res = scriptContext->GetLibrary()->GetEmptyString();
- }
- return res;
- }
- static const charcount_t Join_MaxEstimatedAppendCount = static_cast<charcount_t>((64 << 20) / sizeof(void *)); // 64 MB worth of pointers
- template <typename T>
- JavascriptString* JavascriptArray::JoinArrayHelper(T * arr, JavascriptString* separator, ScriptContext* scriptContext)
- {
- Assert(VirtualTableInfo<T>::HasVirtualTable(arr) || VirtualTableInfo<CrossSiteObject<T>>::HasVirtualTable(arr));
- const uint32 arrLength = arr->length;
- switch(arrLength)
- {
- default:
- {
- CaseDefault:
- bool hasSeparator = (separator->GetLength() != 0);
- const charcount_t estimatedAppendCount =
- min(
- Join_MaxEstimatedAppendCount,
- static_cast<charcount_t>(arrLength + (hasSeparator ? arrLength - 1 : 0)));
- CompoundString *const cs =
- CompoundString::NewWithPointerCapacity(estimatedAppendCount, scriptContext->GetLibrary());
- Var item;
- if (TemplatedGetItem(arr, 0u, &item, scriptContext))
- {
- cs->Append(JavascriptArray::JoinToString(item, scriptContext));
- }
- for (uint32 i = 1; i < arrLength; i++)
- {
- if (hasSeparator)
- {
- cs->Append(separator);
- }
- if (TemplatedGetItem(arr, i, &item, scriptContext))
- {
- cs->Append(JavascriptArray::JoinToString(item, scriptContext));
- }
- }
- return cs;
- }
- case 2:
- {
- bool hasSeparator = (separator->GetLength() != 0);
- if(hasSeparator)
- {
- goto CaseDefault;
- }
- JavascriptString *res = nullptr;
- Var item;
- if (TemplatedGetItem(arr, 0u, &item, scriptContext))
- {
- res = JavascriptArray::JoinToString(item, scriptContext);
- }
- if (TemplatedGetItem(arr, 1u, &item, scriptContext))
- {
- JavascriptString *const itemString = JavascriptArray::JoinToString(item, scriptContext);
- return res ? ConcatString::New(res, itemString) : itemString;
- }
- if(res)
- {
- return res;
- }
- goto Case0;
- }
- case 1:
- {
- Var item;
- if (TemplatedGetItem(arr, 0u, &item, scriptContext))
- {
- return JavascriptArray::JoinToString(item, scriptContext);
- }
- // fall through
- }
- case 0:
- Case0:
- return scriptContext->GetLibrary()->GetEmptyString();
- }
- }
- JavascriptString* JavascriptArray::JoinOtherHelper(RecyclableObject* object, JavascriptString* separator, ScriptContext* scriptContext)
- {
- // In ES6-mode, we always load the length property from the object instead of using the internal slot.
- // Even for arrays, this is now observable via proxies.
- // If source object is not an array, we fall back to this behavior anyway.
- Var lenValue = JavascriptOperators::OP_GetLength(object, scriptContext);
- int64 cSrcLength = JavascriptConversion::ToLength(lenValue, scriptContext);
- switch (cSrcLength)
- {
- default:
- {
- CaseDefault:
- bool hasSeparator = (separator->GetLength() != 0);
- const charcount_t estimatedAppendCount =
- min(
- Join_MaxEstimatedAppendCount,
- static_cast<charcount_t>(cSrcLength + (hasSeparator ? cSrcLength - 1 : 0)));
- CompoundString *const cs =
- CompoundString::NewWithPointerCapacity(estimatedAppendCount, scriptContext->GetLibrary());
- Var value;
- if (JavascriptOperators::GetItem(object, 0u, &value, scriptContext))
- {
- cs->Append(JavascriptArray::JoinToString(value, scriptContext));
- }
- for (uint32 i = 1; i < cSrcLength; i++)
- {
- if (hasSeparator)
- {
- cs->Append(separator);
- }
- Var value;
- if (JavascriptOperators::GetItem(object, i, &value, scriptContext))
- {
- cs->Append(JavascriptArray::JoinToString(value, scriptContext));
- }
- }
- return cs;
- }
- case 2:
- {
- bool hasSeparator = (separator->GetLength() != 0);
- if(hasSeparator)
- {
- goto CaseDefault;
- }
- JavascriptString *res = nullptr;
- Var value;
- if (JavascriptOperators::GetItem(object, 0u, &value, scriptContext))
- {
- res = JavascriptArray::JoinToString(value, scriptContext);
- }
- if (JavascriptOperators::GetItem(object, 1u, &value, scriptContext))
- {
- JavascriptString *const valueString = JavascriptArray::JoinToString(value, scriptContext);
- return res ? ConcatString::New(res, valueString) : valueString;
- }
- if(res)
- {
- return res;
- }
- goto Case0;
- }
- case 1:
- {
- Var value;
- if (JavascriptOperators::GetItem(object, 0u, &value, scriptContext))
- {
- return JavascriptArray::JoinToString(value, scriptContext);
- }
- // fall through
- }
- case 0:
- Case0:
- return scriptContext->GetLibrary()->GetEmptyString();
- }
- }
- Var JavascriptArray::EntryLastIndexOf(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(ArrayLastIndexOfCount);
- Assert(!(callInfo.Flags & CallFlags_New));
- int64 length;
- JavascriptArray * pArr = nullptr;
- RecyclableObject* obj = nullptr;
- if (JavascriptArray::Is(args[0]))
- {
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(args[0]);
- #endif
- pArr = JavascriptArray::FromVar(args[0]);
- obj = pArr;
- length = pArr->length;
- }
- else
- {
- if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.lastIndexOf"));
- }
- Var lenValue = JavascriptOperators::OP_GetLength(obj, scriptContext);
- length = JavascriptConversion::ToLength(lenValue, scriptContext);
- }
- Var search;
- int64 fromIndex;
- if (!GetParamForLastIndexOf(length, args, search, fromIndex, scriptContext))
- {
- return TaggedInt::ToVarUnchecked(-1);
- }
- if (pArr)
- {
- switch (pArr->GetTypeId())
- {
- case Js::TypeIds_Array:
- return LastIndexOfHelper(pArr, search, fromIndex, scriptContext);
- case Js::TypeIds_NativeIntArray:
- return LastIndexOfHelper(JavascriptNativeIntArray::FromVar(pArr), search, fromIndex, scriptContext);
- case Js::TypeIds_NativeFloatArray:
- return LastIndexOfHelper(JavascriptNativeFloatArray::FromVar(pArr), search, fromIndex, scriptContext);
- default:
- AssertMsg(FALSE, "invalid array typeid");
- return LastIndexOfHelper(pArr, search, fromIndex, scriptContext);
- }
- }
- // source object is not a JavascriptArray but source could be a TypedArray
- if (TypedArrayBase::Is(obj))
- {
- return LastIndexOfHelper(TypedArrayBase::FromVar(obj), search, fromIndex, scriptContext);
- }
- return LastIndexOfHelper(obj, search, fromIndex, scriptContext);
- }
- // Array.prototype.lastIndexOf as described in ES6.0 (draft 22) Section 22.1.3.14
- BOOL JavascriptArray::GetParamForLastIndexOf(int64 length, Arguments const & args, Var& search, int64& fromIndex, ScriptContext * scriptContext)
- {
- if (length == 0)
- {
- return false;
- }
- if (args.Info.Count > 2)
- {
- fromIndex = GetFromLastIndex(args[2], length, scriptContext);
- if (fromIndex >= length)
- {
- return false;
- }
- search = args[1];
- }
- else
- {
- search = args.Info.Count > 1 ? args[1] : scriptContext->GetLibrary()->GetUndefined();
- fromIndex = length - 1;
- }
- return true;
- }
- template <typename T>
- Var JavascriptArray::LastIndexOfHelper(T* pArr, Var search, int64 fromIndex, ScriptContext * scriptContext)
- {
- Var element = nullptr;
- bool isSearchTaggedInt = TaggedInt::Is(search);
- // First handle the indices > 2^32
- while (fromIndex >= MaxArrayLength)
- {
- Var index = JavascriptNumber::ToVar(fromIndex, scriptContext);
- if (JavascriptOperators::OP_HasItem(pArr, index, scriptContext))
- {
- element = JavascriptOperators::OP_GetElementI(pArr, index, scriptContext);
- if (isSearchTaggedInt && TaggedInt::Is(element))
- {
- if (element == search)
- {
- return index;
- }
- fromIndex--;
- continue;
- }
- if (JavascriptOperators::StrictEqual(element, search, scriptContext))
- {
- return index;
- }
- }
- fromIndex--;
- }
- Assert(fromIndex < MaxArrayLength);
- // fromIndex now has to be < MaxArrayLength so casting to uint32 is safe
- uint32 end = static_cast<uint32>(fromIndex);
- for (uint32 i = 0; i <= end; i++)
- {
- uint32 index = end - i;
- if (!TemplatedGetItem(pArr, index, &element, scriptContext))
- {
- continue;
- }
- if (isSearchTaggedInt && TaggedInt::Is(element))
- {
- if (element == search)
- {
- return JavascriptNumber::ToVar(index, scriptContext);
- }
- continue;
- }
- if (JavascriptOperators::StrictEqual(element, search, scriptContext))
- {
- return JavascriptNumber::ToVar(index, scriptContext);
- }
- }
- return TaggedInt::ToVarUnchecked(-1);
- }
- /*
- * PopWithNoDst
- * - For pop calls that do not return a value, we only need to decrement the length of the array.
- */
- void JavascriptNativeArray::PopWithNoDst(Var nativeArray)
- {
- Assert(JavascriptNativeArray::Is(nativeArray));
- JavascriptArray * arr = JavascriptArray::FromVar(nativeArray);
- // we will bailout on length 0
- Assert(arr->GetLength() != 0);
- uint32 index = arr->GetLength() - 1;
- arr->SetLength(index);
- }
- /*
- * JavascriptNativeIntArray::Pop
- * - Returns int32 value from the array.
- * - Returns missing item when the element is not available in the array object.
- * - It doesn't walk up the prototype chain.
- * - Length is decremented only if it pops an int32 element, in all other cases - we bail out from the jitted code.
- * - This api cannot cause any implicit call and hence do not need implicit call bailout test around this api
- */
- int32 JavascriptNativeIntArray::Pop(ScriptContext * scriptContext, Var object)
- {
- Assert(JavascriptNativeIntArray::Is(object));
- JavascriptNativeIntArray * arr = JavascriptNativeIntArray::FromVar(object);
- Assert(arr->GetLength() != 0);
- uint32 index = arr->length - 1;
- int32 element = Js::JavascriptOperators::OP_GetNativeIntElementI_UInt32(object, index, scriptContext);
- //If it is a missing item, then don't update the length - Pre-op Bail out will happen.
- if(!SparseArraySegment<int32>::IsMissingItem(&element))
- {
- arr->SetLength(index);
- }
- return element;
- }
- /*
- * JavascriptNativeFloatArray::Pop
- * - Returns double value from the array.
- * - Returns missing item when the element is not available in the array object.
- * - It doesn't walk up the prototype chain.
- * - Length is decremented only if it pops a double element, in all other cases - we bail out from the jitted code.
- * - This api cannot cause any implicit call and hence do not need implicit call bailout test around this api
- */
- double JavascriptNativeFloatArray::Pop(ScriptContext * scriptContext, Var object)
- {
- Assert(JavascriptNativeFloatArray::Is(object));
- JavascriptNativeFloatArray * arr = JavascriptNativeFloatArray::FromVar(object);
- Assert(arr->GetLength() != 0);
- uint32 index = arr->length - 1;
- double element = Js::JavascriptOperators::OP_GetNativeFloatElementI_UInt32(object, index, scriptContext);
- // If it is a missing item then don't update the length - Pre-op Bail out will happen.
- if(!SparseArraySegment<double>::IsMissingItem(&element))
- {
- arr->SetLength(index);
- }
- return element;
- }
- /*
- * JavascriptArray::Pop
- * - Calls the generic Pop API, which can find elements from the prototype chain, when it is not available in the array object.
- * - This API may cause implicit calls. Handles Array and non-array objects
- */
- Var JavascriptArray::Pop(ScriptContext * scriptContext, Var object)
- {
- if (JavascriptArray::Is(object))
- {
- return EntryPopJavascriptArray(scriptContext, object);
- }
- else
- {
- return EntryPopNonJavascriptArray(scriptContext, object);
- }
- }
- Var JavascriptArray::EntryPopJavascriptArray(ScriptContext * scriptContext, Var object)
- {
- JavascriptArray * arr = JavascriptArray::FromVar(object);
- uint32 length = arr->length;
- if (length == 0)
- {
- // If length is 0, return 'undefined'
- return scriptContext->GetLibrary()->GetUndefined();
- }
- uint32 index = length - 1;
- Var element;
- if (!arr->DirectGetItemAtFull(index, &element))
- {
- element = scriptContext->GetLibrary()->GetUndefined();
- }
- else
- {
- element = CrossSite::MarshalVar(scriptContext, element);
- }
- arr->SetLength(index); // SetLength will clear element at index
- #ifdef VALIDATE_ARRAY
- arr->ValidateArray();
- #endif
- return element;
- }
- Var JavascriptArray::EntryPopNonJavascriptArray(ScriptContext * scriptContext, Var object)
- {
- RecyclableObject* dynamicObject = nullptr;
- if (FALSE == JavascriptConversion::ToObject(object, scriptContext, &dynamicObject))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.pop"));
- }
- BigIndex length;
- if (scriptContext->GetConfig()->IsES6ToLengthEnabled())
- {
- length = (uint64)JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(dynamicObject, scriptContext), scriptContext);
- }
- else
- {
- length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(dynamicObject, scriptContext), scriptContext);
- }
- ThrowTypeErrorOnFailureHelper h(scriptContext, _u("Array.prototype.pop"));
- if (length == 0u)
- {
- // Set length = 0
- h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(dynamicObject, dynamicObject, PropertyIds::length, TaggedInt::ToVarUnchecked(0), scriptContext, PropertyOperation_ThrowIfNotExtensible));
- return scriptContext->GetLibrary()->GetUndefined();
- }
- BigIndex index = length;
- --index;
- Var element;
- if (index.IsSmallIndex())
- {
- if (!JavascriptOperators::GetItem(dynamicObject, index.GetSmallIndex(), &element, scriptContext))
- {
- element = scriptContext->GetLibrary()->GetUndefined();
- }
- h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(dynamicObject, index.GetSmallIndex(), PropertyOperation_ThrowIfNotExtensible));
- // Set the new length
- h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(dynamicObject, dynamicObject, PropertyIds::length, JavascriptNumber::ToVar(index.GetSmallIndex(), scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible));
- }
- else
- {
- if (!JavascriptOperators::GetItem(dynamicObject, index.GetBigIndex(), &element, scriptContext))
- {
- element = scriptContext->GetLibrary()->GetUndefined();
- }
- h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(dynamicObject, index.GetBigIndex(), PropertyOperation_ThrowIfNotExtensible));
- // Set the new length
- h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(dynamicObject, dynamicObject, PropertyIds::length, JavascriptNumber::ToVar(index.GetBigIndex(), scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible));
- }
- return element;
- }
- Var JavascriptArray::EntryPop(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- if (args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.pop"));
- }
- if (JavascriptArray::Is(args[0]))
- {
- return EntryPopJavascriptArray(scriptContext, args.Values[0]);
- }
- else
- {
- return EntryPopNonJavascriptArray(scriptContext, args.Values[0]);
- }
- }
- /*
- * JavascriptNativeIntArray::Push
- * Pushes Int element in a native Int Array.
- * We call the generic Push, if the array is not native Int or we have a really big array.
- */
- Var JavascriptNativeIntArray::Push(ScriptContext * scriptContext, Var array, int value)
- {
- // Handle non crossSite native int arrays here length within MaxArrayLength.
- // JavascriptArray::Push will handle other cases.
- if (JavascriptNativeIntArray::IsNonCrossSite(array))
- {
- JavascriptNativeIntArray * nativeIntArray = JavascriptNativeIntArray::FromVar(array);
- Assert(!nativeIntArray->IsCrossSiteObject());
- uint32 n = nativeIntArray->length;
- if(n < JavascriptArray::MaxArrayLength)
- {
- nativeIntArray->SetItem(n, value);
- n++;
- AssertMsg(n == nativeIntArray->length, "Wrong update to the length of the native Int array");
- return JavascriptNumber::ToVar(n, scriptContext);
- }
- }
- return JavascriptArray::Push(scriptContext, array, JavascriptNumber::ToVar(value, scriptContext));
- }
- /*
- * JavascriptNativeFloatArray::Push
- * Pushes Float element in a native Int Array.
- * We call the generic Push, if the array is not native Float or we have a really big array.
- */
- Var JavascriptNativeFloatArray::Push(ScriptContext * scriptContext, Var * array, double value)
- {
- // Handle non crossSite native int arrays here length within MaxArrayLength.
- // JavascriptArray::Push will handle other cases.
- if(JavascriptNativeFloatArray::IsNonCrossSite(array))
- {
- JavascriptNativeFloatArray * nativeFloatArray = JavascriptNativeFloatArray::FromVar(array);
- Assert(!nativeFloatArray->IsCrossSiteObject());
- uint32 n = nativeFloatArray->length;
- if(n < JavascriptArray::MaxArrayLength)
- {
- nativeFloatArray->SetItem(n, value);
- n++;
- AssertMsg(n == nativeFloatArray->length, "Wrong update to the length of the native Float array");
- return JavascriptNumber::ToVar(n, scriptContext);
- }
- }
- return JavascriptArray::Push(scriptContext, array, JavascriptNumber::ToVarNoCheck(value, scriptContext));
- }
- /*
- * JavascriptArray::Push
- * Pushes Var element in a Var Array.
- */
- Var JavascriptArray::Push(ScriptContext * scriptContext, Var object, Var value)
- {
- Var args[2];
- args[0] = object;
- args[1] = value;
- if (JavascriptArray::Is(object))
- {
- return EntryPushJavascriptArray(scriptContext, args, 2);
- }
- else
- {
- return EntryPushNonJavascriptArray(scriptContext, args, 2);
- }
- }
- /*
- * EntryPushNonJavascriptArray
- * - Handles Entry push calls, when Objects are not javascript arrays
- */
- Var JavascriptArray::EntryPushNonJavascriptArray(ScriptContext * scriptContext, Var * args, uint argCount)
- {
- RecyclableObject* obj = nullptr;
- if (FALSE == JavascriptConversion::ToObject(args[0], scriptContext, &obj))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.push"));
- }
- Var length = JavascriptOperators::OP_GetLength(obj, scriptContext);
- if(JavascriptOperators::GetTypeId(length) == TypeIds_Undefined && scriptContext->GetThreadContext()->IsDisableImplicitCall() &&
- scriptContext->GetThreadContext()->GetImplicitCallFlags() != Js::ImplicitCall_None)
- {
- return length;
- }
- ThrowTypeErrorOnFailureHelper h(scriptContext, _u("Array.prototype.push"));
- BigIndex n;
- if (scriptContext->GetConfig()->IsES6ToLengthEnabled())
- {
- n = (uint64) JavascriptConversion::ToLength(length, scriptContext);
- }
- else
- {
- n = JavascriptConversion::ToUInt32(length, scriptContext);
- }
- // First handle "small" indices.
- uint index;
- for (index=1; index < argCount && n < JavascriptArray::MaxArrayLength; ++index, ++n)
- {
- if (h.IsThrowTypeError(JavascriptOperators::SetItem(obj, obj, n.GetSmallIndex(), args[index], scriptContext, PropertyOperation_ThrowIfNotExtensible)))
- {
- if (scriptContext->GetThreadContext()->RecordImplicitException())
- {
- h.ThrowTypeErrorOnFailure();
- }
- else
- {
- return nullptr;
- }
- }
- }
- // Use BigIndex if we need to push indices >= MaxArrayLength
- if (index < argCount)
- {
- BigIndex big = n;
- for (; index < argCount; ++index, ++big)
- {
- if (h.IsThrowTypeError(big.SetItem(obj, args[index], PropertyOperation_ThrowIfNotExtensible)))
- {
- if(scriptContext->GetThreadContext()->RecordImplicitException())
- {
- h.ThrowTypeErrorOnFailure();
- }
- else
- {
- return nullptr;
- }
- }
- }
- // Set the new length; for objects it is all right for this to be >= MaxArrayLength
- if (h.IsThrowTypeError(JavascriptOperators::SetProperty(obj, obj, PropertyIds::length, big.ToNumber(scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible)))
- {
- if(scriptContext->GetThreadContext()->RecordImplicitException())
- {
- h.ThrowTypeErrorOnFailure();
- }
- else
- {
- return nullptr;
- }
- }
- return big.ToNumber(scriptContext);
- }
- else
- {
- // Set the new length
- Var lengthAsNUmberVar = JavascriptNumber::ToVar(n.IsSmallIndex() ? n.GetSmallIndex() : n.GetBigIndex(), scriptContext);
- if (h.IsThrowTypeError(JavascriptOperators::SetProperty(obj, obj, PropertyIds::length, lengthAsNUmberVar, scriptContext, PropertyOperation_ThrowIfNotExtensible)))
- {
- if(scriptContext->GetThreadContext()->RecordImplicitException())
- {
- h.ThrowTypeErrorOnFailure();
- }
- else
- {
- return nullptr;
- }
- }
- return lengthAsNUmberVar;
- }
- }
- /*
- * JavascriptArray::EntryPushJavascriptArray
- * Pushes Var element in a Var Array.
- * Returns the length of the array.
- */
- Var JavascriptArray::EntryPushJavascriptArray(ScriptContext * scriptContext, Var * args, uint argCount)
- {
- JavascriptArray * arr = JavascriptArray::FromAnyArray(args[0]);
- uint n = arr->length;
- ThrowTypeErrorOnFailureHelper h(scriptContext, _u("Array.prototype.push"));
- // Fast Path for one push for small indexes
- if (argCount == 2 && n < JavascriptArray::MaxArrayLength)
- {
- // Set Item is overridden by CrossSiteObject, so no need to check for IsCrossSiteObject()
- h.ThrowTypeErrorOnFailure(arr->SetItem(n, args[1], PropertyOperation_None));
- return JavascriptNumber::ToVar(n + 1, scriptContext);
- }
- // Fast Path for multiple push for small indexes
- if (JavascriptArray::MaxArrayLength - argCount + 1 > n && JavascriptArray::IsVarArray(arr) && scriptContext == arr->GetScriptContext())
- {
- uint index;
- for (index = 1; index < argCount; ++index, ++n)
- {
- Assert(n != JavascriptArray::MaxArrayLength);
- // Set Item is overridden by CrossSiteObject, so no need to check for IsCrossSiteObject()
- arr->JavascriptArray::DirectSetItemAt(n, args[index]);
- }
- return JavascriptNumber::ToVar(n, scriptContext);
- }
- return EntryPushJavascriptArrayNoFastPath(scriptContext, args, argCount);
- }
- Var JavascriptArray::EntryPushJavascriptArrayNoFastPath(ScriptContext * scriptContext, Var * args, uint argCount)
- {
- JavascriptArray * arr = JavascriptArray::FromAnyArray(args[0]);
- uint n = arr->length;
- ThrowTypeErrorOnFailureHelper h(scriptContext, _u("Array.prototype.push"));
- // First handle "small" indices.
- uint index;
- for (index = 1; index < argCount && n < JavascriptArray::MaxArrayLength; ++index, ++n)
- {
- // Set Item is overridden by CrossSiteObject, so no need to check for IsCrossSiteObject()
- h.ThrowTypeErrorOnFailure(arr->SetItem(n, args[index], PropertyOperation_None));
- }
- // Use BigIndex if we need to push indices >= MaxArrayLength
- if (index < argCount)
- {
- // Not supporting native array with BigIndex.
- arr = EnsureNonNativeArray(arr);
- Assert(n == JavascriptArray::MaxArrayLength);
- for (BigIndex big = n; index < argCount; ++index, ++big)
- {
- h.ThrowTypeErrorOnFailure(big.SetItem(arr, args[index]));
- }
- #ifdef VALIDATE_ARRAY
- arr->ValidateArray();
- #endif
- // This is where we should set the length, but for arrays it cannot be >= MaxArrayLength
- JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthAssignIncorrect);
- }
- #ifdef VALIDATE_ARRAY
- arr->ValidateArray();
- #endif
- return JavascriptNumber::ToVar(n, scriptContext);
- }
- /*
- * JavascriptArray::EntryPush
- * Handles Push calls(Script Function)
- */
- Var JavascriptArray::EntryPush(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- if (args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.push"));
- }
- if (JavascriptArray::Is(args[0]))
- {
- return EntryPushJavascriptArray(scriptContext, args.Values, args.Info.Count);
- }
- else
- {
- return EntryPushNonJavascriptArray(scriptContext, args.Values, args.Info.Count);
- }
- }
- Var JavascriptArray::EntryReverse(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- if (args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.reverse"));
- }
- BigIndex length = 0u;
- JavascriptArray* pArr = nullptr;
- RecyclableObject* obj = nullptr;
- if (JavascriptArray::Is(args[0]))
- {
- pArr = JavascriptArray::FromVar(args[0]);
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(pArr);
- #endif
- obj = pArr;
- }
- else
- {
- if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.reverse"));
- }
- }
- // In ES6-mode, we always load the length property from the object instead of using the internal slot.
- // Even for arrays, this is now observable via proxies.
- // If source object is not an array, we fall back to this behavior anyway.
- if (scriptContext->GetConfig()->IsES6TypedArrayExtensionsEnabled() || pArr == nullptr)
- {
- if (scriptContext->GetConfig()->IsES6ToLengthEnabled())
- {
- length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext);
- }
- else
- {
- length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext);
- }
- }
- else
- {
- length = pArr->length;
- }
- if (length.IsSmallIndex())
- {
- return JavascriptArray::ReverseHelper(pArr, nullptr, obj, length.GetSmallIndex(), scriptContext);
- }
- Assert(pArr == nullptr || length.IsUint32Max()); // if pArr is not null lets make sure length is safe to cast, which will only happen if length is a uint32max
- return JavascriptArray::ReverseHelper(pArr, nullptr, obj, length.GetBigIndex(), scriptContext);
- }
- // Array.prototype.reverse as described in ES6.0 (draft 22) Section 22.1.3.20
- template <typename T>
- Var JavascriptArray::ReverseHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, ScriptContext* scriptContext)
- {
- T middle = length / 2;
- Var lowerValue = nullptr, upperValue = nullptr;
- T lowerExists, upperExists;
- const char16* methodName;
- bool isTypedArrayEntryPoint = typedArrayBase != nullptr;
- if (isTypedArrayEntryPoint)
- {
- methodName = _u("[TypedArray].prototype.reverse");
- }
- else
- {
- methodName = _u("Array.prototype.reverse");
- }
- // If we came from Array.prototype.map and source object is not a JavascriptArray, source could be a TypedArray
- if (!isTypedArrayEntryPoint && pArr == nullptr && TypedArrayBase::Is(obj))
- {
- typedArrayBase = TypedArrayBase::FromVar(obj);
- }
- ThrowTypeErrorOnFailureHelper h(scriptContext, methodName);
- if (pArr)
- {
- Recycler * recycler = scriptContext->GetRecycler();
- if (length <= 1)
- {
- return pArr;
- }
- if (pArr->IsFillFromPrototypes())
- {
- // For odd-length arrays, the middle element is unchanged,
- // so we cannot fill it from the prototypes.
- if (length % 2 == 0)
- {
- pArr->FillFromPrototypes(0, (uint32)length);
- }
- else
- {
- middle = length / 2;
- pArr->FillFromPrototypes(0, (uint32)middle);
- pArr->FillFromPrototypes(1 + (uint32)middle, (uint32)length);
- }
- }
- if (pArr->HasNoMissingValues() && pArr->head && pArr->head->next)
- {
- // This function currently does not track missing values in the head segment if there are multiple segments
- pArr->SetHasNoMissingValues(false);
- }
- SparseArraySegmentBase* seg = pArr->head;
- SparseArraySegmentBase *prevSeg = nullptr;
- SparseArraySegmentBase *nextSeg = nullptr;
- SparseArraySegmentBase *pinPrevSeg = nullptr;
- bool isIntArray = false;
- bool isFloatArray = false;
- if (JavascriptNativeIntArray::Is(pArr))
- {
- isIntArray = true;
- }
- else if (JavascriptNativeFloatArray::Is(pArr))
- {
- isFloatArray = true;
- }
- while (seg)
- {
- nextSeg = seg->next;
- // If seg.length == 0, it is possible that (seg.left + seg.length == prev.left + prev.length),
- // resulting in 2 segments sharing the same "left".
- if (seg->length > 0)
- {
- if (isIntArray)
- {
- ((SparseArraySegment<int32>*)seg)->ReverseSegment(recycler);
- }
- else if (isFloatArray)
- {
- ((SparseArraySegment<double>*)seg)->ReverseSegment(recycler);
- }
- else
- {
- ((SparseArraySegment<Var>*)seg)->ReverseSegment(recycler);
- }
- seg->left = ((uint32)length) - (seg->left + seg->length);
- seg->next = prevSeg;
- // Make sure size doesn't overlap with next segment.
- // An easy fix is to just truncate the size...
- seg->EnsureSizeInBound();
- // If the last segment is a leaf, then we may be losing our last scanned pointer to its previous
- // segment. Hold onto it with pinPrevSeg until we reallocate below.
- pinPrevSeg = prevSeg;
- prevSeg = seg;
- }
- seg = nextSeg;
- }
- pArr->head = prevSeg;
- // Just dump the segment map on reverse
- pArr->ClearSegmentMap();
- if (isIntArray)
- {
- if (pArr->head && pArr->head->next && SparseArraySegmentBase::IsLeafSegment(pArr->head, recycler))
- {
- pArr->ReallocNonLeafSegment((SparseArraySegment<int32>*)pArr->head, pArr->head->next);
- }
- pArr->EnsureHeadStartsFromZero<int32>(recycler);
- }
- else if (isFloatArray)
- {
- if (pArr->head && pArr->head->next && SparseArraySegmentBase::IsLeafSegment(pArr->head, recycler))
- {
- pArr->ReallocNonLeafSegment((SparseArraySegment<double>*)pArr->head, pArr->head->next);
- }
- pArr->EnsureHeadStartsFromZero<double>(recycler);
- }
- else
- {
- pArr->EnsureHeadStartsFromZero<Var>(recycler);
- }
- pArr->InvalidateLastUsedSegment(); // lastUsedSegment might be 0-length and discarded above
- #ifdef VALIDATE_ARRAY
- pArr->ValidateArray();
- #endif
- }
- else if (typedArrayBase)
- {
- Assert(length <= JavascriptArray::MaxArrayLength);
- if (typedArrayBase->GetLength() == length)
- {
- // If typedArrayBase->length == length then we know that the TypedArray will have all items < length
- // and we won't have to check that the elements exist or not.
- for (uint32 lower = 0; lower < (uint32)middle; lower++)
- {
- uint32 upper = (uint32)length - lower - 1;
- lowerValue = typedArrayBase->DirectGetItem(lower);
- upperValue = typedArrayBase->DirectGetItem(upper);
- // We still have to call HasItem even though we know the TypedArray has both lower and upper because
- // there may be a proxy handler trapping HasProperty.
- lowerExists = typedArrayBase->HasItem(lower);
- upperExists = typedArrayBase->HasItem(upper);
- h.ThrowTypeErrorOnFailure(typedArrayBase->DirectSetItem(lower, upperValue, false));
- h.ThrowTypeErrorOnFailure(typedArrayBase->DirectSetItem(upper, lowerValue, false));
- }
- }
- else
- {
- for (uint32 lower = 0; lower < middle; lower++)
- {
- uint32 upper = (uint32)length - lower - 1;
- lowerValue = typedArrayBase->DirectGetItem(lower);
- upperValue = typedArrayBase->DirectGetItem(upper);
- lowerExists = typedArrayBase->HasItem(lower);
- upperExists = typedArrayBase->HasItem(upper);
- if (lowerExists)
- {
- if (upperExists)
- {
- h.ThrowTypeErrorOnFailure(typedArrayBase->DirectSetItem(lower, upperValue, false));
- h.ThrowTypeErrorOnFailure(typedArrayBase->DirectSetItem(upper, lowerValue, false));
- }
- else
- {
- // This will always fail for a TypedArray if lower < length
- h.ThrowTypeErrorOnFailure(typedArrayBase->DeleteItem(lower, PropertyOperation_ThrowIfNotExtensible));
- h.ThrowTypeErrorOnFailure(typedArrayBase->DirectSetItem(upper, lowerValue, false));
- }
- }
- else
- {
- if (upperExists)
- {
- h.ThrowTypeErrorOnFailure(typedArrayBase->DirectSetItem(lower, upperValue, false));
- // This will always fail for a TypedArray if upper < length
- h.ThrowTypeErrorOnFailure(typedArrayBase->DeleteItem(upper, PropertyOperation_ThrowIfNotExtensible));
- }
- }
- }
- }
- }
- else
- {
- for (T lower = 0; lower < middle; lower++)
- {
- T upper = length - lower - 1;
- lowerExists = JavascriptOperators::HasItem(obj, lower);
- if (lowerExists)
- {
- BOOL getResult = JavascriptOperators::GetItem(obj, lower, &lowerValue, scriptContext);
- Assert(getResult);
- }
- upperExists = JavascriptOperators::HasItem(obj, upper);
- if (upperExists)
- {
- BOOL getResult = JavascriptOperators::GetItem(obj, upper, &upperValue, scriptContext);
- Assert(getResult);
- }
- if (lowerExists)
- {
- if (upperExists)
- {
- h.ThrowTypeErrorOnFailure(JavascriptOperators::SetItem(obj, obj, lower, upperValue, scriptContext, PropertyOperation_ThrowIfNotExtensible));
- h.ThrowTypeErrorOnFailure(JavascriptOperators::SetItem(obj, obj, upper, lowerValue, scriptContext, PropertyOperation_ThrowIfNotExtensible));
- }
- else
- {
- h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(obj, lower, PropertyOperation_ThrowIfNotExtensible));
- h.ThrowTypeErrorOnFailure(JavascriptOperators::SetItem(obj, obj, upper, lowerValue, scriptContext, PropertyOperation_ThrowIfNotExtensible));
- }
- }
- else
- {
- if (upperExists)
- {
- h.ThrowTypeErrorOnFailure(JavascriptOperators::SetItem(obj, obj, lower, upperValue, scriptContext, PropertyOperation_ThrowIfNotExtensible));
- h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(obj, upper, PropertyOperation_ThrowIfNotExtensible));
- }
- }
- }
- }
- return obj;
- }
- template<typename T>
- void JavascriptArray::ShiftHelper(JavascriptArray* pArr, ScriptContext * scriptContext)
- {
- Recycler * recycler = scriptContext->GetRecycler();
- SparseArraySegment<T>* next = (SparseArraySegment<T>*)pArr->head->next;
- while (next)
- {
- next->left--;
- next = (SparseArraySegment<T>*)next->next;
- }
- // head and next might overlap as the next segment left is decremented
- next = (SparseArraySegment<T>*)pArr->head->next;
- if (next && (pArr->head->size > next->left))
- {
- AssertMsg(pArr->head->left == 0, "Array always points to a head starting at index 0");
- AssertMsg(pArr->head->size == next->left + 1, "Shift next->left overlaps current segment by more than 1 element");
- SparseArraySegment<T> *head = (SparseArraySegment<T>*)pArr->head;
- // Merge the two adjacent segments
- if (next->length != 0)
- {
- uint32 offset = head->size - 1;
- // There is room for one unshifted element in head segment.
- // Hence it's enough if we grow the head segment by next->length - 1
- if (next->next)
- {
- // If we have a next->next, we can't grow pass the left of that
- // If the array had a segment map before, the next->next might just be right after next as well.
- // So we just need to grow to the end of the next segment
- // TODO: merge that segment too?
- Assert(next->next->left >= head->size);
- uint32 maxGrowSize = next->next->left - head->size;
- if (maxGrowSize != 0)
- {
- head = head->GrowByMinMax(recycler, next->length - 1, maxGrowSize); //-1 is to account for unshift
- }
- else
- {
- // The next segment is only of length one, so we already have space in the header to copy that
- Assert(next->length == 1);
- }
- }
- else
- {
- head = head->GrowByMin(recycler, next->length - 1); //-1 is to account for unshift
- }
- memmove(head->elements + offset, next->elements, next->length * sizeof(T));
- head->length = offset + next->length;
- pArr->head = head;
- }
- head->next = next->next;
- pArr->InvalidateLastUsedSegment();
- }
- #ifdef VALIDATE_ARRAY
- pArr->ValidateArray();
- #endif
- }
- Var JavascriptArray::EntryShift(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- Var res = scriptContext->GetLibrary()->GetUndefined();
- if (args.Info.Count == 0)
- {
- return res;
- }
- if (JavascriptArray::Is(args[0]))
- {
- JavascriptArray * pArr = JavascriptArray::FromVar(args[0]);
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(pArr);
- #endif
- if (pArr->length == 0)
- {
- return res;
- }
- if(pArr->IsFillFromPrototypes())
- {
- pArr->FillFromPrototypes(0, pArr->length); // We need find all missing value from [[proto]] object
- }
- if(pArr->HasNoMissingValues() && pArr->head && pArr->head->next)
- {
- // This function currently does not track missing values in the head segment if there are multiple segments
- pArr->SetHasNoMissingValues(false);
- }
- pArr->length--;
- pArr->ClearSegmentMap(); // Dump segmentMap on shift (before any allocation)
- Recycler * recycler = scriptContext->GetRecycler();
- bool isIntArray = false;
- bool isFloatArray = false;
- if(JavascriptNativeIntArray::Is(pArr))
- {
- isIntArray = true;
- }
- else if(JavascriptNativeFloatArray::Is(pArr))
- {
- isFloatArray = true;
- }
- if (pArr->head->length != 0)
- {
- if(isIntArray)
- {
- int32 nativeResult = ((SparseArraySegment<int32>*)pArr->head)->GetElement(0);
- if(SparseArraySegment<int32>::IsMissingItem(&nativeResult))
- {
- res = scriptContext->GetLibrary()->GetUndefined();
- }
- else
- {
- res = Js::JavascriptNumber::ToVar(nativeResult, scriptContext);
- }
- ((SparseArraySegment<int32>*)pArr->head)->RemoveElement(recycler, 0);
- }
- else if (isFloatArray)
- {
- double nativeResult = ((SparseArraySegment<double>*)pArr->head)->GetElement(0);
- if(SparseArraySegment<double>::IsMissingItem(&nativeResult))
- {
- res = scriptContext->GetLibrary()->GetUndefined();
- }
- else
- {
- res = Js::JavascriptNumber::ToVarNoCheck(nativeResult, scriptContext);
- }
- ((SparseArraySegment<double>*)pArr->head)->RemoveElement(recycler, 0);
- }
- else
- {
- res = ((SparseArraySegment<Var>*)pArr->head)->GetElement(0);
- if(SparseArraySegment<Var>::IsMissingItem(&res))
- {
- res = scriptContext->GetLibrary()->GetUndefined();
- }
- else
- {
- res = CrossSite::MarshalVar(scriptContext, res);
- }
- ((SparseArraySegment<Var>*)pArr->head)->RemoveElement(recycler, 0);
- }
- }
- if(isIntArray)
- {
- ShiftHelper<int32>(pArr, scriptContext);
- }
- else if (isFloatArray)
- {
- ShiftHelper<double>(pArr, scriptContext);
- }
- else
- {
- ShiftHelper<Var>(pArr, scriptContext);
- }
- }
- else
- {
- RecyclableObject* dynamicObject = nullptr;
- if (FALSE == JavascriptConversion::ToObject(args[0], scriptContext, &dynamicObject))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.shift"));
- }
- ThrowTypeErrorOnFailureHelper h(scriptContext, _u("Array.prototype.shift"));
- BigIndex length = 0u;
- if (scriptContext->GetConfig()->IsES6ToLengthEnabled())
- {
- length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(dynamicObject, scriptContext), scriptContext);
- }
- else
- {
- length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(dynamicObject, scriptContext), scriptContext);
- }
- if (length == 0u)
- {
- // If length is 0, return 'undefined'
- h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(dynamicObject, dynamicObject, PropertyIds::length, TaggedInt::ToVarUnchecked(0), scriptContext, PropertyOperation_ThrowIfNotExtensible));
- return scriptContext->GetLibrary()->GetUndefined();
- }
- if (!JavascriptOperators::GetItem(dynamicObject, 0u, &res, scriptContext))
- {
- res = scriptContext->GetLibrary()->GetUndefined();
- }
- --length;
- uint32 lengthToUin32Max = length.IsSmallIndex() ? length.GetSmallIndex() : MaxArrayLength;
- for (uint32 i = 0u; i < lengthToUin32Max; i++)
- {
- Var element;
- if (JavascriptOperators::HasItem(dynamicObject, i + 1))
- {
- BOOL getResult = JavascriptOperators::GetItem(dynamicObject, i + 1, &element, scriptContext);
- Assert(getResult);
- h.ThrowTypeErrorOnFailure(JavascriptOperators::SetItem(dynamicObject, dynamicObject, i, element, scriptContext, PropertyOperation_ThrowIfNotExtensible, /*skipPrototypeCheck*/ true));
- }
- else
- {
- h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(dynamicObject, i, PropertyOperation_ThrowIfNotExtensible));
- }
- }
- for (uint64 i = MaxArrayLength; length > i; i++)
- {
- Var element;
- if (JavascriptOperators::HasItem(dynamicObject, i + 1))
- {
- BOOL getResult = JavascriptOperators::GetItem(dynamicObject, i + 1, &element, scriptContext);
- Assert(getResult);
- h.ThrowTypeErrorOnFailure(JavascriptOperators::SetItem(dynamicObject, dynamicObject, i, element, scriptContext, PropertyOperation_ThrowIfNotExtensible));
- }
- else
- {
- h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(dynamicObject, i, PropertyOperation_ThrowIfNotExtensible));
- }
- }
- if (length.IsSmallIndex())
- {
- h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(dynamicObject, length.GetSmallIndex(), PropertyOperation_ThrowIfNotExtensible));
- h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(dynamicObject, dynamicObject, PropertyIds::length, JavascriptNumber::ToVar(length.GetSmallIndex(), scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible));
- }
- else
- {
- h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(dynamicObject, length.GetBigIndex(), PropertyOperation_ThrowIfNotExtensible));
- h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(dynamicObject, dynamicObject, PropertyIds::length, JavascriptNumber::ToVar(length.GetBigIndex(), scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible));
- }
- }
- return res;
- }
- Js::JavascriptArray* JavascriptArray::CreateNewArrayHelper(uint32 len, bool isIntArray, bool isFloatArray, Js::JavascriptArray* baseArray, ScriptContext* scriptContext)
- {
- if (isIntArray)
- {
- Js::JavascriptNativeIntArray *pnewArr = scriptContext->GetLibrary()->CreateNativeIntArray(len);
- pnewArr->EnsureHead<int32>();
- #if ENABLE_PROFILE_INFO
- pnewArr->CopyArrayProfileInfo(Js::JavascriptNativeIntArray::FromVar(baseArray));
- #endif
- return pnewArr;
- }
- else if (isFloatArray)
- {
- Js::JavascriptNativeFloatArray *pnewArr = scriptContext->GetLibrary()->CreateNativeFloatArray(len);
- pnewArr->EnsureHead<double>();
- #if ENABLE_PROFILE_INFO
- pnewArr->CopyArrayProfileInfo(Js::JavascriptNativeFloatArray::FromVar(baseArray));
- #endif
- return pnewArr;
- }
- else
- {
- JavascriptArray *pnewArr = pnewArr = scriptContext->GetLibrary()->CreateArray(len);
- pnewArr->EnsureHead<Var>();
- return pnewArr;
- }
- }
- template<typename T>
- void JavascriptArray::SliceHelper(JavascriptArray* pArr, JavascriptArray* pnewArr, uint32 start, uint32 newLen)
- {
- SparseArraySegment<T>* headSeg = (SparseArraySegment<T>*)pArr->head;
- SparseArraySegment<T>* pnewHeadSeg = (SparseArraySegment<T>*)pnewArr->head;
- // Fill the newly created sliced array
- js_memcpy_s(pnewHeadSeg->elements, sizeof(T) * newLen, headSeg->elements + start, sizeof(T) * newLen);
- pnewHeadSeg->length = newLen;
- Assert(pnewHeadSeg->length <= pnewHeadSeg->size);
- // Prototype lookup for missing elements
- if (!pArr->HasNoMissingValues())
- {
- for (uint32 i = 0; i < newLen; i++)
- {
- if (SparseArraySegment<T>::IsMissingItem(&headSeg->elements[i+start]))
- {
- Var element;
- pnewArr->SetHasNoMissingValues(false);
- if (pArr->DirectGetItemAtFull(i + start, &element))
- {
- pnewArr->SetItem(i, element, PropertyOperation_None);
- }
- }
- }
- }
- #ifdef DBG
- else
- {
- for (uint32 i = 0; i < newLen; i++)
- {
- AssertMsg(!SparseArraySegment<T>::IsMissingItem(&headSeg->elements[i+start]), "Array marked incorrectly as having missing value");
- }
- }
- #endif
- }
- // If the creating profile data has changed, convert it to the type of array indicated
- // in the profile
- void JavascriptArray::GetArrayTypeAndConvert(bool* isIntArray, bool* isFloatArray)
- {
- if (JavascriptNativeIntArray::Is(this))
- {
- #if ENABLE_PROFILE_INFO
- JavascriptNativeIntArray* nativeIntArray = JavascriptNativeIntArray::FromVar(this);
- ArrayCallSiteInfo* info = nativeIntArray->GetArrayCallSiteInfo();
- if(!info || info->IsNativeIntArray())
- {
- *isIntArray = true;
- }
- else if(info->IsNativeFloatArray())
- {
- JavascriptNativeIntArray::ToNativeFloatArray(nativeIntArray);
- *isFloatArray = true;
- }
- else
- {
- JavascriptNativeIntArray::ToVarArray(nativeIntArray);
- }
- #else
- *isIntArray = true;
- #endif
- }
- else if (JavascriptNativeFloatArray::Is(this))
- {
- #if ENABLE_PROFILE_INFO
- JavascriptNativeFloatArray* nativeFloatArray = JavascriptNativeFloatArray::FromVar(this);
- ArrayCallSiteInfo* info = nativeFloatArray->GetArrayCallSiteInfo();
- if(info && !info->IsNativeArray())
- {
- JavascriptNativeFloatArray::ToVarArray(nativeFloatArray);
- }
- else
- {
- *isFloatArray = true;
- }
- #else
- *isFloatArray = true;
- #endif
- }
- }
- Var JavascriptArray::EntrySlice(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- Var res = scriptContext->GetLibrary()->GetUndefined();
- if (args.Info.Count == 0)
- {
- return res;
- }
- BigIndex length;
- JavascriptArray* pArr = nullptr;
- RecyclableObject* obj = nullptr;
- if (JavascriptArray::Is(args[0]) && scriptContext == JavascriptArray::FromVar(args[0])->GetScriptContext())
- {
- pArr = JavascriptArray::FromVar(args[0]);
- obj = pArr;
- }
- else
- {
- if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.slice"));
- }
- }
- // In ES6-mode, we always load the length property from the object instead of using the internal slot.
- // Even for arrays, this is now observable via proxies.
- // If source object is not an array, we fall back to this behavior anyway.
- if (scriptContext->GetConfig()->IsES6TypedArrayExtensionsEnabled() || pArr == nullptr)
- {
- Var lenValue = JavascriptOperators::OP_GetLength(obj, scriptContext);
- if (scriptContext->GetConfig()->IsES6ToLengthEnabled())
- {
- length = (uint64) JavascriptConversion::ToLength(lenValue, scriptContext);
- }
- else
- {
- length = JavascriptConversion::ToUInt32(lenValue, scriptContext);
- }
- }
- else
- {
- length = pArr->length;
- }
- if (length.IsSmallIndex())
- {
- return JavascriptArray::SliceHelper(pArr, nullptr, obj, length.GetSmallIndex(), args, scriptContext);
- }
- Assert(pArr == nullptr || length.IsUint32Max());
- return JavascriptArray::SliceHelper(pArr, nullptr, obj, length.GetBigIndex(), args, scriptContext);
- }
- // Array.prototype.slice as described in ES6.0 (draft 22) Section 22.1.3.22
- template <typename T>
- Var JavascriptArray::SliceHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, Arguments& args, ScriptContext* scriptContext)
- {
- JavascriptLibrary* library = scriptContext->GetLibrary();
- JavascriptArray* newArr = nullptr;
- RecyclableObject* newObj = nullptr;
- bool isIntArray = false;
- bool isFloatArray = false;
- bool isTypedArrayEntryPoint = typedArrayBase != nullptr;
- T startT = 0;
- T newLenT = length;
- T endT = length;
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(pArr);
- #endif
- if (args.Info.Count > 1)
- {
- startT = GetFromIndex(args[1], length, scriptContext);
- if (startT > length)
- {
- startT = length;
- }
- if (args.Info.Count > 2)
- {
- if (JavascriptOperators::GetTypeId(args[2]) == TypeIds_Undefined)
- {
- endT = length;
- }
- else
- {
- endT = GetFromIndex(args[2], length, scriptContext);
- if (endT > length)
- {
- endT = length;
- }
- }
- }
- newLenT = endT > startT ? endT - startT : 0;
- }
- if (TypedArrayBase::IsDetachedTypedArray(obj))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_DetachedTypedArray, _u("Array.prototype.slice"));
- }
- // If we came from Array.prototype.slice and source object is not a JavascriptArray, source could be a TypedArray
- if (!isTypedArrayEntryPoint && pArr == nullptr && TypedArrayBase::Is(obj))
- {
- typedArrayBase = TypedArrayBase::FromVar(obj);
- }
- // If the entry point is %TypedArray%.prototype.slice or the source object is an Array exotic object we should try to load the constructor property
- // and use it to construct the return object.
- if (isTypedArrayEntryPoint)
- {
- Var constructor = JavascriptOperators::SpeciesConstructor(typedArrayBase, TypedArrayBase::GetDefaultConstructor(args[0], scriptContext), scriptContext);
- // If we have an array source object, we need to make sure to do the right thing if it's a native array.
- // The helpers below which do the element copying require the source and destination arrays to have the same native type.
- if (pArr && constructor == library->GetArrayConstructor())
- {
- if (newLenT > JavascriptArray::MaxArrayLength)
- {
- JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthConstructIncorrect);
- }
- // If the constructor function is the built-in Array constructor, we can be smart and create the right type of native array.
- pArr->GetArrayTypeAndConvert(&isIntArray, &isFloatArray);
- newArr = CreateNewArrayHelper(static_cast<uint32>(newLenT), isIntArray, isFloatArray, pArr, scriptContext);
- newObj = newArr;
- }
- else if (JavascriptOperators::IsConstructor(constructor) && JavascriptLibrary::IsTypedArrayConstructor(constructor, scriptContext))
- {
- if (pArr)
- {
- // If the constructor function is any other function, it can return anything so we have to call it.
- // Roll the source array into a non-native array if it was one.
- pArr = EnsureNonNativeArray(pArr);
- }
- Js::Var constructorArgs[] = { constructor, JavascriptNumber::ToVar(newLenT, scriptContext) };
- Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs));
- newObj = RecyclableObject::FromVar(JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext));
- }
- else
- {
- // We only need to throw a TypeError when the constructor property is not an actual constructor if %TypedArray%.prototype.slice was called
- JavascriptError::ThrowTypeError(scriptContext, JSERR_InvalidTypedArray_Constructor, _u("[TypedArray].prototype.slice"));
- }
- }
- else if (pArr != nullptr)
- {
- newObj = ArraySpeciesCreate(pArr, newLenT, scriptContext, &isIntArray, &isFloatArray);
- }
- // skip the typed array and "pure" array case, we still need to handle special arrays like es5array, remote array, and proxy of array.
- else
- {
- newObj = ArraySpeciesCreate(obj, newLenT, scriptContext);
- }
- // If we didn't create a new object above we will create a new array here.
- // This is the pre-ES6 behavior or the case of calling Array.prototype.slice with a constructor argument that is not a constructor function.
- if (newObj == nullptr)
- {
- if (pArr)
- {
- pArr->GetArrayTypeAndConvert(&isIntArray, &isFloatArray);
- }
- if (newLenT > JavascriptArray::MaxArrayLength)
- {
- JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthConstructIncorrect);
- }
- newArr = CreateNewArrayHelper(static_cast<uint32>(newLenT), isIntArray, isFloatArray, pArr, scriptContext);
- newObj = newArr;
- }
- else
- {
- // If the new object we created is an array, remember that as it will save us time setting properties in the object below
- if (JavascriptArray::Is(newObj))
- {
- newArr = JavascriptArray::FromVar(newObj);
- }
- }
- uint32 start = (uint32) startT;
- uint32 newLen = (uint32) newLenT;
- // We at least have to have newObj as a valid object
- Assert(newObj);
- // Bail out early if the new object will have zero length.
- if (newLen == 0)
- {
- return newObj;
- }
- if (pArr)
- {
- // If we constructed a new Array object, we have some nice helpers here
- if (newArr)
- {
- if (JavascriptArray::IsDirectAccessArray(newArr))
- {
- if (((start + newLen) <= pArr->head->length) && newLen <= newArr->head->size) //Fast Path
- {
- if (isIntArray)
- {
- SliceHelper<int32>(pArr, newArr, start, newLen);
- }
- else if (isFloatArray)
- {
- SliceHelper<double>(pArr, newArr, start, newLen);
- }
- else
- {
- SliceHelper<Var>(pArr, newArr, start, newLen);
- }
- }
- else
- {
- if (isIntArray)
- {
- CopyNativeIntArrayElements(JavascriptNativeIntArray::FromVar(newArr), 0, JavascriptNativeIntArray::FromVar(pArr), start, start + newLen);
- }
- else if (isFloatArray)
- {
- CopyNativeFloatArrayElements(JavascriptNativeFloatArray::FromVar(newArr), 0, JavascriptNativeFloatArray::FromVar(pArr), start, start + newLen);
- }
- else
- {
- CopyArrayElements(newArr, 0u, pArr, start, start + newLen);
- }
- }
- }
- else
- {
- AssertMsg(CONFIG_FLAG(ForceES5Array), "newArr can only be ES5Array when it is forced");
- Var element;
- for (uint32 i = 0; i < newLen; i++)
- {
- if (!pArr->DirectGetItemAtFull(i + start, &element))
- {
- continue;
- }
- newArr->DirectSetItemAt(i, element);
- }
- }
- }
- else
- {
- // The constructed object isn't an array, we'll need to use normal object manipulation
- Var element;
- for (uint32 i = 0; i < newLen; i++)
- {
- if (!pArr->DirectGetItemAtFull(i + start, &element))
- {
- continue;
- }
- JavascriptArray::SetArrayLikeObjects(newObj, i, element);
- }
- }
- }
- else if (typedArrayBase)
- {
- // Source is a TypedArray, we must have created the return object via a call to constructor, but newObj may not be a TypedArray (or an array either)
- TypedArrayBase* newTypedArray = nullptr;
- if (TypedArrayBase::Is(newObj))
- {
- newTypedArray = TypedArrayBase::FromVar(newObj);
- }
- Var element;
- for (uint32 i = 0; i < newLen; i++)
- {
- // We only need to call HasItem in the case that we are called from Array.prototype.slice
- if (!isTypedArrayEntryPoint && !typedArrayBase->HasItem(i + start))
- {
- continue;
- }
- element = typedArrayBase->DirectGetItem(i + start);
- // The object we got back from the constructor might not be a TypedArray. In fact, it could be any object.
- if (newTypedArray)
- {
- newTypedArray->DirectSetItem(i, element, false);
- }
- else if (newArr)
- {
- newArr->DirectSetItemAt(i, element);
- }
- else
- {
- JavascriptOperators::OP_SetElementI_UInt32(newObj, i, element, scriptContext, PropertyOperation_ThrowIfNotExtensible);
- }
- }
- }
- else
- {
- Var element;
- for (uint32 i = 0; i < newLen; i++)
- {
- if (!JavascriptOperators::HasItem(obj, i+start))
- {
- continue;
- }
- BOOL getResult = JavascriptOperators::GetItem(obj, i + start, &element, scriptContext);
- Assert(getResult);
- if (newArr != nullptr)
- {
- newArr->DirectSetItemAt(i, element);
- }
- else
- {
- JavascriptOperators::OP_SetElementI_UInt32(newObj, i, element, scriptContext, PropertyOperation_ThrowIfNotExtensible);
- }
- }
- }
- if (!isTypedArrayEntryPoint)
- {
- JavascriptOperators::SetProperty(newObj, newObj, Js::PropertyIds::length, JavascriptNumber::ToVar(newLen, scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible);
- }
- #ifdef VALIDATE_ARRAY
- if (JavascriptArray::Is(newObj))
- {
- JavascriptArray::FromVar(newObj)->ValidateArray();
- }
- #endif
- return newObj;
- }
- struct CompareVarsInfo
- {
- ScriptContext* scriptContext;
- RecyclableObject* compFn;
- };
- int __cdecl compareVars(void* cvInfoV, const void* aRef, const void* bRef)
- {
- CompareVarsInfo* cvInfo=(CompareVarsInfo*)cvInfoV;
- ScriptContext* requestContext=cvInfo->scriptContext;
- RecyclableObject* compFn=cvInfo->compFn;
- AssertMsg(*(Var*)aRef, "No null expected in sort");
- AssertMsg(*(Var*)bRef, "No null expected in sort");
- if (compFn != nullptr)
- {
- ScriptContext* scriptContext = compFn->GetScriptContext();
- // The correct flag value is CallFlags_Value but we pass CallFlags_None in compat modes
- CallFlags flags = CallFlags_Value;
- Var undefined = scriptContext->GetLibrary()->GetUndefined();
- Var retVal;
- if (requestContext != scriptContext)
- {
- Var leftVar = CrossSite::MarshalVar(scriptContext, *(Var*)aRef);
- Var rightVar = CrossSite::MarshalVar(scriptContext, *(Var*)bRef);
- retVal = compFn->GetEntryPoint()(compFn, CallInfo(flags, 3), undefined, leftVar, rightVar);
- }
- else
- {
- retVal = compFn->GetEntryPoint()(compFn, CallInfo(flags, 3), undefined, *(Var*)aRef, *(Var*)bRef);
- }
- if (TaggedInt::Is(retVal))
- {
- return TaggedInt::ToInt32(retVal);
- }
- double dblResult;
- if (JavascriptNumber::Is_NoTaggedIntCheck(retVal))
- {
- dblResult = JavascriptNumber::GetValue(retVal);
- }
- else
- {
- dblResult = JavascriptConversion::ToNumber_Full(retVal, scriptContext);
- }
- if (dblResult < 0)
- {
- return -1;
- }
- return (dblResult > 0) ? 1 : 0;
- }
- else
- {
- JavascriptString* pStr1 = JavascriptConversion::ToString(*(Var*)aRef, requestContext);
- JavascriptString* pStr2 = JavascriptConversion::ToString(*(Var*)bRef, requestContext);
- return JavascriptString::strcmp(pStr1, pStr2);
- }
- }
- static void hybridSort(__inout_ecount(length) Var *elements, uint32 length, CompareVarsInfo* compareInfo)
- {
- // The cost of memory moves starts to be more expensive than additional comparer calls (given a simple comparer)
- // for arrays of more than 512 elements.
- if (length > 512)
- {
- qsort_s(elements, length, sizeof(Var), compareVars, compareInfo);
- return;
- }
- for (int i = 1; i < (int)length; i++)
- {
- if (compareVars(compareInfo, elements + i, elements + i - 1) < 0) {
- // binary search for the left-most element greater than value:
- int first = 0;
- int last = i - 1;
- while (first <= last)
- {
- int middle = (first + last) / 2;
- if (compareVars(compareInfo, elements + i, elements + middle) < 0)
- {
- last = middle - 1;
- }
- else
- {
- first = middle + 1;
- }
- }
- // insert value right before first:
- Var value = elements[i];
- memmove(elements + first + 1, elements + first, (i - first) * sizeof(Var));
- elements[first] = value;
- }
- }
- }
- void JavascriptArray::Sort(RecyclableObject* compFn)
- {
- if (length <= 1)
- {
- return;
- }
- this->EnsureHead<Var>();
- ScriptContext* scriptContext = this->GetScriptContext();
- Recycler* recycler = scriptContext->GetRecycler();
- CompareVarsInfo cvInfo;
- cvInfo.scriptContext = scriptContext;
- cvInfo.compFn = compFn;
- Assert(head != nullptr);
- // Just dump the segment map on sort
- ClearSegmentMap();
- uint32 countUndefined = 0;
- SparseArraySegment<Var>* startSeg = (SparseArraySegment<Var>*)head;
- // Sort may have side effects on the array. Setting a dummy head so that original array is not affected
- uint32 saveLength = length;
- // that if compare function tries to modify the array it won't AV.
- head = const_cast<SparseArraySegmentBase*>(EmptySegment);
- SetFlags(DynamicObjectFlags::None);
- this->InvalidateLastUsedSegment();
- length = 0;
- TryFinally([&]()
- {
- //The array is a continuous array if there is only one segment
- if (startSeg->next == nullptr) // Single segment fast path
- {
- if (compFn != nullptr)
- {
- countUndefined = startSeg->RemoveUndefined(scriptContext);
- #ifdef VALIDATE_ARRAY
- ValidateSegment(startSeg);
- #endif
- hybridSort(startSeg->elements, startSeg->length, &cvInfo);
- }
- else
- {
- countUndefined = sort(startSeg->elements, &startSeg->length, scriptContext);
- }
- head = startSeg;
- }
- else
- {
- SparseArraySegment<Var>* allElements = SparseArraySegment<Var>::AllocateSegment(recycler, 0, 0, nullptr);
- SparseArraySegment<Var>* next = startSeg;
- uint32 nextIndex = 0;
- // copy all the elements to single segment
- while (next)
- {
- countUndefined += next->RemoveUndefined(scriptContext);
- if (next->length != 0)
- {
- allElements = SparseArraySegment<Var>::CopySegment(recycler, allElements, nextIndex, next, next->left, next->length);
- }
- next = (SparseArraySegment<Var>*)next->next;
- nextIndex = allElements->length;
- #ifdef VALIDATE_ARRAY
- ValidateSegment(allElements);
- #endif
- }
- if (compFn != nullptr)
- {
- hybridSort(allElements->elements, allElements->length, &cvInfo);
- }
- else
- {
- sort(allElements->elements, &allElements->length, scriptContext);
- }
- head = allElements;
- head->next = nullptr;
- }
- },
- [&](bool hasException)
- {
- length = saveLength;
- ClearSegmentMap(); // Dump the segmentMap again in case user compare function rebuilds it
- if (hasException)
- {
- head = startSeg;
- this->InvalidateLastUsedSegment();
- }
- });
- #if DEBUG
- {
- uint32 countNull = 0;
- uint32 index = head->length - 1;
- while (countNull < head->length)
- {
- if (((SparseArraySegment<Var>*)head)->elements[index] != NULL)
- {
- break;
- }
- index--;
- countNull++;
- }
- AssertMsg(countNull == 0, "No null expected at the end");
- }
- #endif
- if (countUndefined != 0)
- {
- // fill undefined at the end
- uint32 newLength = head->length + countUndefined;
- if (newLength > head->size)
- {
- head = ((SparseArraySegment<Var>*)head)->GrowByMin(recycler, newLength - head->size);
- }
- Var undefined = scriptContext->GetLibrary()->GetUndefined();
- for (uint32 i = head->length; i < newLength; i++)
- {
- ((SparseArraySegment<Var>*)head)->elements[i] = undefined;
- }
- head->length = newLength;
- }
- SetHasNoMissingValues();
- this->InvalidateLastUsedSegment();
- #ifdef VALIDATE_ARRAY
- ValidateArray();
- #endif
- return;
- }
- uint32 JavascriptArray::sort(__inout_ecount(*len) Var *orig, uint32 *len, ScriptContext *scriptContext)
- {
- uint32 count = 0, countUndefined = 0;
- Element *elements = RecyclerNewArrayZ(scriptContext->GetRecycler(), Element, *len);
- RecyclableObject *undefined = scriptContext->GetLibrary()->GetUndefined();
- //
- // Create the Elements array
- //
- for (uint32 i = 0; i < *len; ++i)
- {
- if (!SparseArraySegment<Var>::IsMissingItem(&orig[i]))
- {
- if (!JavascriptOperators::IsUndefinedObject(orig[i], undefined))
- {
- elements[count].Value = orig[i];
- elements[count].StringValue = JavascriptConversion::ToString(orig[i], scriptContext);
- count++;
- }
- else
- {
- countUndefined++;
- }
- orig[i] = SparseArraySegment<Var>::GetMissingItem();
- }
- }
- if (count == 0)
- {
- *len = 0; // set the length to zero
- return countUndefined;
- }
- SortElements(elements, 0, count - 1);
- for (uint32 i = 0; i < count; ++i)
- {
- orig[i] = elements[i].Value;
- }
- *len = count; // set the correct length
- return countUndefined;
- }
- int __cdecl JavascriptArray::CompareElements(void* context, const void* elem1, const void* elem2)
- {
- const Element* element1 = static_cast<const Element*>(elem1);
- const Element* element2 = static_cast<const Element*>(elem2);
- Assert(element1 != NULL);
- Assert(element2 != NULL);
- return JavascriptString::strcmp(element1->StringValue, element2->StringValue);
- }
- void JavascriptArray::SortElements(Element* elements, uint32 left, uint32 right)
- {
- qsort_s(elements, right - left + 1, sizeof(Element), CompareElements, this);
- }
- Var JavascriptArray::EntrySort(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Array.prototype.sort"));
- Assert(!(callInfo.Flags & CallFlags_New));
- AssertMsg(args.Info.Count >= 1, "Should have at least one argument");
- RecyclableObject* compFn = NULL;
- if (args.Info.Count > 1)
- {
- if (JavascriptConversion::IsCallable(args[1]))
- {
- compFn = RecyclableObject::FromVar(args[1]);
- }
- else
- {
- TypeId typeId = JavascriptOperators::GetTypeId(args[1]);
- // Use default comparer:
- // - In ES5 mode if the argument is undefined.
- bool useDefaultComparer = typeId == TypeIds_Undefined;
- if (!useDefaultComparer)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedInternalObject, _u("Array.prototype.sort"));
- }
- }
- }
- if (JavascriptArray::Is(args[0]))
- {
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(args[0]);
- #endif
- JavascriptArray *arr = JavascriptArray::FromVar(args[0]);
- if (arr->length <= 1)
- {
- return args[0];
- }
- if(arr->IsFillFromPrototypes())
- {
- arr->FillFromPrototypes(0, arr->length); // We need find all missing value from [[proto]] object
- }
- // Maintain nativity of the array only for the following cases (To favor inplace conversions - keeps the conversion cost less):
- // - int cases for X86 and
- // - FloatArray for AMD64
- // We convert the entire array back and forth once here O(n), rather than doing the costly conversion down the call stack which is O(nlogn)
- #if defined(_M_X64_OR_ARM64)
- if(compFn && JavascriptNativeFloatArray::Is(arr))
- {
- arr = JavascriptNativeFloatArray::ConvertToVarArray((JavascriptNativeFloatArray*)arr);
- arr->Sort(compFn);
- arr = arr->ConvertToNativeArrayInPlace<JavascriptNativeFloatArray, double>(arr);
- }
- else
- {
- EnsureNonNativeArray(arr);
- arr->Sort(compFn);
- }
- #else
- if(compFn && JavascriptNativeIntArray::Is(arr))
- {
- //EnsureNonNativeArray(arr);
- arr = JavascriptNativeIntArray::ConvertToVarArray((JavascriptNativeIntArray*)arr);
- arr->Sort(compFn);
- arr = arr->ConvertToNativeArrayInPlace<JavascriptNativeIntArray, int32>(arr);
- }
- else
- {
- EnsureNonNativeArray(arr);
- arr->Sort(compFn);
- }
- #endif
- }
- else
- {
- RecyclableObject* pObj = nullptr;
- if (FALSE == JavascriptConversion::ToObject(args[0], scriptContext, &pObj))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.sort"));
- }
- uint32 len = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(pObj, scriptContext), scriptContext);
- JavascriptArray* sortArray = scriptContext->GetLibrary()->CreateArray(len);
- sortArray->EnsureHead<Var>();
- ThrowTypeErrorOnFailureHelper h(scriptContext, _u("Array.prototype.sort"));
- BEGIN_TEMP_ALLOCATOR(tempAlloc, scriptContext, _u("Runtime"))
- {
- JsUtil::List<uint32, ArenaAllocator>* indexList = JsUtil::List<uint32, ArenaAllocator>::New(tempAlloc);
- for (uint32 i = 0; i < len; i++)
- {
- Var item;
- if (JavascriptOperators::GetItem(pObj, i, &item, scriptContext))
- {
- indexList->Add(i);
- sortArray->DirectSetItemAt(i, item);
- }
- }
- if (indexList->Count() > 0)
- {
- if (sortArray->length > 1)
- {
- sortArray->FillFromPrototypes(0, sortArray->length); // We need find all missing value from [[proto]] object
- }
- sortArray->Sort(compFn);
- uint32 removeIndex = sortArray->head->length;
- for (uint32 i = 0; i < removeIndex; i++)
- {
- AssertMsg(!SparseArraySegment<Var>::IsMissingItem(&((SparseArraySegment<Var>*)sortArray->head)->elements[i]), "No gaps expected in sorted array");
- h.ThrowTypeErrorOnFailure(JavascriptOperators::SetItem(pObj, pObj, i, ((SparseArraySegment<Var>*)sortArray->head)->elements[i], scriptContext));
- }
- for (int i = 0; i < indexList->Count(); i++)
- {
- uint32 value = indexList->Item(i);
- if (value >= removeIndex)
- {
- h.ThrowTypeErrorOnFailure((JavascriptOperators::DeleteItem(pObj, value)));
- }
- }
- }
- }
- END_TEMP_ALLOCATOR(tempAlloc, scriptContext);
- }
- return args[0];
- }
- Var JavascriptArray::EntrySplice(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Recycler *recycler = scriptContext->GetRecycler();
- Assert(!(callInfo.Flags & CallFlags_New));
- AssertMsg(args.Info.Count >= 1, "Should have at least one argument");
- bool isArr = false;
- JavascriptArray* pArr = 0;
- RecyclableObject* pObj = 0;
- RecyclableObject* newObj = nullptr;
- uint32 start = 0;
- uint32 deleteLen = 0;
- uint32 len = 0;
- if (JavascriptArray::Is(args[0]) && scriptContext == JavascriptArray::FromVar(args[0])->GetScriptContext())
- {
- isArr = true;
- pArr = JavascriptArray::FromVar(args[0]);
- pObj = pArr;
- len = pArr->length;
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(args[0]);
- #endif
- }
- else
- {
- if (FALSE == JavascriptConversion::ToObject(args[0], scriptContext, &pObj))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.splice"));
- }
- if (scriptContext->GetConfig()->IsES6ToLengthEnabled())
- {
- int64 len64 = JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(pObj, scriptContext), scriptContext);
- len = len64 > UINT_MAX ? UINT_MAX : (uint)len64;
- }
- else
- {
- len = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(pObj, scriptContext), scriptContext);
- }
- }
- switch (args.Info.Count)
- {
- case 1:
- start = len;
- deleteLen = 0;
- break;
- case 2:
- start = min(GetFromIndex(args[1], len, scriptContext), len);
- deleteLen = len - start;
- break;
- default:
- start = GetFromIndex(args[1], len, scriptContext);
- if (start > len)
- {
- start = len;
- }
- // When start >= len, we know we won't be deleting any items and don't really need to evaluate the second argument.
- // However, ECMA 262 15.4.4.12 requires that it be evaluated, anyway. If the argument is an object with a valueOf
- // with a side effect, this evaluation is observable. Hence, we must evaluate.
- if (TaggedInt::Is(args[2]))
- {
- int intDeleteLen = TaggedInt::ToInt32(args[2]);
- if (intDeleteLen < 0)
- {
- deleteLen = 0;
- }
- else
- {
- deleteLen = intDeleteLen;
- }
- }
- else
- {
- double dblDeleteLen = JavascriptConversion::ToInteger(args[2], scriptContext);
- if (dblDeleteLen > len)
- {
- deleteLen = (uint32)-1;
- }
- else if (dblDeleteLen <= 0)
- {
- deleteLen = 0;
- }
- else
- {
- deleteLen = (uint32)dblDeleteLen;
- }
- }
- deleteLen = min(len - start, deleteLen);
- break;
- }
- Var* insertArgs = args.Info.Count > 3 ? &args.Values[3] : nullptr;
- uint32 insertLen = args.Info.Count > 3 ? args.Info.Count - 3 : 0;
- ::Math::RecordOverflowPolicy newLenOverflow;
- uint32 newLen = UInt32Math::Add(len - deleteLen, insertLen, newLenOverflow); // new length of the array after splice
- if (isArr)
- {
- // If we have missing values then convert to not native array for now
- // In future, we could support this scenario.
- if (deleteLen == insertLen)
- {
- pArr->FillFromPrototypes(start, start + deleteLen);
- }
- else if (len)
- {
- pArr->FillFromPrototypes(start, len);
- }
- //
- // If newLen overflowed, pre-process to prevent pushing sparse array segments or elements out of
- // max array length, which would result in tons of index overflow and difficult to fix.
- //
- if (newLenOverflow.HasOverflowed())
- {
- pArr = EnsureNonNativeArray(pArr);
- BigIndex dstIndex = MaxArrayLength;
- uint32 maxInsertLen = MaxArrayLength - start;
- if (insertLen > maxInsertLen)
- {
- // Copy overflowing insertArgs to properties
- for (uint32 i = maxInsertLen; i < insertLen; i++)
- {
- pArr->DirectSetItemAt(dstIndex, insertArgs[i]);
- ++dstIndex;
- }
- insertLen = maxInsertLen; // update
- // Truncate elements on the right to properties
- if (start + deleteLen < len)
- {
- pArr->TruncateToProperties(dstIndex, start + deleteLen);
- }
- }
- else
- {
- // Truncate would-overflow elements to properties
- pArr->TruncateToProperties(dstIndex, MaxArrayLength - insertLen + deleteLen);
- }
- len = pArr->length; // update
- newLen = len - deleteLen + insertLen;
- Assert(newLen == MaxArrayLength);
- }
- if (insertArgs)
- {
- pArr = EnsureNonNativeArray(pArr);
- }
- bool isIntArray = false;
- bool isFloatArray = false;
- JavascriptArray *newArr = nullptr;
- // Just dump the segment map on splice (before any possible allocation and throw)
- pArr->ClearSegmentMap();
- // If the source object is an Array exotic object (Array.isArray) we should try to load the constructor property
- // and use it to construct the return object.
- newObj = ArraySpeciesCreate(pArr, deleteLen, scriptContext);
- if (newObj != nullptr)
- {
- pArr = EnsureNonNativeArray(pArr);
- // If the new object we created is an array, remember that as it will save us time setting properties in the object below
- if (JavascriptArray::Is(newObj))
- {
- newArr = JavascriptArray::FromVar(newObj);
- }
- }
- else
- // This is the ES5 case, pArr['constructor'] doesn't exist, or pArr['constructor'] is the builtin Array constructor
- {
- pArr->GetArrayTypeAndConvert(&isIntArray, &isFloatArray);
- newArr = CreateNewArrayHelper(deleteLen, isIntArray, isFloatArray, pArr, scriptContext);
- }
- // If return object is a JavascriptArray, we can use all the array splice helpers
- if (newArr)
- {
- // Array has a single segment (need not start at 0) and splice start lies in the range
- // of that segment we optimize splice - Fast path.
- if (pArr->IsSingleSegmentArray() && pArr->head->HasIndex(start))
- {
- if (isIntArray)
- {
- ArraySegmentSpliceHelper<int32>(newArr, (SparseArraySegment<int32>*)pArr->head, (SparseArraySegment<int32>**)&pArr->head, start, deleteLen, insertArgs, insertLen, recycler);
- }
- else if (isFloatArray)
- {
- ArraySegmentSpliceHelper<double>(newArr, (SparseArraySegment<double>*)pArr->head, (SparseArraySegment<double>**)&pArr->head, start, deleteLen, insertArgs, insertLen, recycler);
- }
- else
- {
- ArraySegmentSpliceHelper<Var>(newArr, (SparseArraySegment<Var>*)pArr->head, (SparseArraySegment<Var>**)&pArr->head, start, deleteLen, insertArgs, insertLen, recycler);
- }
- // Since the start index is within the bounds of the original array's head segment, it will not acquire any new
- // missing values. If the original array had missing values in the head segment, some of them may have been
- // copied into the array that will be returned; otherwise, the array that is returned will also not have any
- // missing values.
- newArr->SetHasNoMissingValues(pArr->HasNoMissingValues());
- }
- else
- {
- if (isIntArray)
- {
- ArraySpliceHelper<int32>(newArr, pArr, start, deleteLen, insertArgs, insertLen, scriptContext);
- }
- else if (isFloatArray)
- {
- ArraySpliceHelper<double>(newArr, pArr, start, deleteLen, insertArgs, insertLen, scriptContext);
- }
- else
- {
- ArraySpliceHelper<Var>(newArr, pArr, start, deleteLen, insertArgs, insertLen, scriptContext);
- }
- // This function currently does not track missing values in the head segment if there are multiple segments
- pArr->SetHasNoMissingValues(false);
- newArr->SetHasNoMissingValues(false);
- }
- if (isIntArray)
- {
- pArr->EnsureHeadStartsFromZero<int32>(recycler);
- newArr->EnsureHeadStartsFromZero<int32>(recycler);
- }
- else if (isFloatArray)
- {
- pArr->EnsureHeadStartsFromZero<double>(recycler);
- newArr->EnsureHeadStartsFromZero<double>(recycler);
- }
- else
- {
- pArr->EnsureHeadStartsFromZero<Var>(recycler);
- newArr->EnsureHeadStartsFromZero<Var>(recycler);
- }
- pArr->InvalidateLastUsedSegment();
- // it is possible for valueOf accessors for the start or deleteLen
- // arguments to modify the size of the array. Since the resulting size of the array
- // is based on the cached value of length, this might lead to us having to trim
- // excess array segments at the end of the splice operation, which SetLength() will do.
- // However, this is also slower than performing the simple length assignment, so we only
- // do it if we can detect the array length changing.
- if(pArr->length != len)
- {
- pArr->SetLength(newLen);
- }
- else
- {
- pArr->length = newLen;
- }
- newArr->InvalidateLastUsedSegment();
- #ifdef VALIDATE_ARRAY
- newArr->ValidateArray();
- pArr->ValidateArray();
- #endif
- if (newLenOverflow.HasOverflowed())
- {
- // ES5 15.4.4.12 16: If new len overflowed, SetLength throws
- JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthAssignIncorrect);
- }
- return newArr;
- }
- }
- if (newLenOverflow.HasOverflowed())
- {
- return ObjectSpliceHelper<BigIndex>(pObj, len, start, deleteLen, insertArgs, insertLen, scriptContext, newObj);
- }
- else // Use uint32 version if no overflow
- {
- return ObjectSpliceHelper<uint32>(pObj, len, start, deleteLen, insertArgs, insertLen, scriptContext, newObj);
- }
- }
- inline BOOL JavascriptArray::IsSingleSegmentArray() const
- {
- return nullptr == head->next;
- }
- template<typename T>
- void JavascriptArray::ArraySegmentSpliceHelper(JavascriptArray *pnewArr, SparseArraySegment<T> *seg, SparseArraySegment<T> **prev,
- uint32 start, uint32 deleteLen, Var* insertArgs, uint32 insertLen, Recycler *recycler)
- {
- // book keeping variables
- uint32 relativeStart = start - seg->left; // This will be different from start when head->left is non zero -
- //(Missing elements at the beginning)
- uint32 headDeleteLen = min(start + deleteLen , seg->left + seg->length) - start; // actual number of elements to delete in
- // head if deleteLen overflows the length of head
- uint32 newHeadLen = seg->length - headDeleteLen + insertLen; // new length of the head after splice
- // Save the deleted elements
- if (headDeleteLen != 0)
- {
- pnewArr->InvalidateLastUsedSegment();
- pnewArr->head = SparseArraySegment<T>::CopySegment(recycler, (SparseArraySegment<T>*)pnewArr->head, 0, seg, start, headDeleteLen);
- }
- if (newHeadLen != 0)
- {
- if (seg->size < newHeadLen)
- {
- if (seg->next)
- {
- // If we have "next", require that we haven't adjusted next segments left yet.
- seg = seg->GrowByMinMax(recycler, newHeadLen - seg->size, seg->next->left - deleteLen + insertLen - seg->left - seg->size);
- }
- else
- {
- seg = seg->GrowByMin(recycler, newHeadLen - seg->size);
- }
- #ifdef VALIDATE_ARRAY
- ValidateSegment(seg);
- #endif
- }
- // Move the elements if necessary
- if (headDeleteLen != insertLen)
- {
- uint32 noElementsToMove = seg->length - (relativeStart + headDeleteLen);
- memmove(seg->elements + relativeStart + insertLen,
- seg->elements + relativeStart + headDeleteLen,
- sizeof(T) * noElementsToMove);
- if (newHeadLen < seg->length) // truncate if necessary
- {
- seg->Truncate(seg->left + newHeadLen); // set end elements to null so that when we introduce null elements we are safe
- }
- seg->length = newHeadLen;
- }
- // Copy the new elements
- if (insertLen > 0)
- {
- Assert(!VirtualTableInfo<JavascriptNativeIntArray>::HasVirtualTable(pnewArr) &&
- !VirtualTableInfo<JavascriptNativeFloatArray>::HasVirtualTable(pnewArr));
- // inserted elements starts at argument 3 of splice(start, deleteNumber, insertelem1, insertelem2, insertelem3, ...);
- js_memcpy_s(seg->elements + relativeStart, sizeof(Var) * insertLen, insertArgs, sizeof(Var) * insertLen);
- }
- *prev = seg;
- }
- else
- {
- *prev = (SparseArraySegment<T>*)seg->next;
- }
- }
- template<typename T>
- void JavascriptArray::ArraySpliceHelper(JavascriptArray* pnewArr, JavascriptArray* pArr, uint32 start, uint32 deleteLen, Var* insertArgs, uint32 insertLen, ScriptContext *scriptContext)
- {
- // Skip pnewArr->EnsureHead(): we don't use existing segment at all.
- Recycler *recycler = scriptContext->GetRecycler();
- SparseArraySegmentBase** prevSeg = &pArr->head; // holds the next pointer of previous
- SparseArraySegmentBase** prevPrevSeg = &pArr->head; // this holds the previous pointer to prevSeg dirty trick.
- SparseArraySegmentBase* savePrev = nullptr;
- Assert(pArr->head); // We should never have a null head.
- pArr->EnsureHead<T>();
- SparseArraySegment<T>* startSeg = (SparseArraySegment<T>*)pArr->head;
- const uint32 limit = start + deleteLen;
- uint32 rightLimit;
- if (UInt32Math::Add(startSeg->left, startSeg->size, &rightLimit))
- {
- rightLimit = JavascriptArray::MaxArrayLength;
- }
- // Find out the segment to start delete
- while (startSeg && (rightLimit <= start))
- {
- savePrev = startSeg;
- prevPrevSeg = prevSeg;
- prevSeg = &startSeg->next;
- startSeg = (SparseArraySegment<T>*)startSeg->next;
- if (startSeg)
- {
- if (UInt32Math::Add(startSeg->left, startSeg->size, &rightLimit))
- {
- rightLimit = JavascriptArray::MaxArrayLength;
- }
- }
- }
- // handle inlined segment
- SparseArraySegmentBase* inlineHeadSegment = nullptr;
- bool hasInlineSegment = false;
- // The following if else set is used to determine whether a shallow or hard copy is needed
- if (JavascriptNativeArray::Is(pArr))
- {
- if (JavascriptNativeFloatArray::Is(pArr))
- {
- inlineHeadSegment = DetermineInlineHeadSegmentPointer<JavascriptNativeFloatArray, 0, true>((JavascriptNativeFloatArray*)pArr);
- }
- else if (JavascriptNativeIntArray::Is(pArr))
- {
- inlineHeadSegment = DetermineInlineHeadSegmentPointer<JavascriptNativeIntArray, 0, true>((JavascriptNativeIntArray*)pArr);
- }
- Assert(inlineHeadSegment);
- hasInlineSegment = (startSeg == (SparseArraySegment<T>*)inlineHeadSegment);
- }
- else
- {
- // This will result in false positives. It is used because DetermineInlineHeadSegmentPointer
- // does not handle Arrays that change type e.g. from JavascriptNativeIntArray to JavascriptArray
- // This conversion in particular is problematic because JavascriptNativeIntArray is larger than JavascriptArray
- // so the returned head segment ptr never equals pArr->head. So we will default to using this and deal with
- // false positives. It is better than always doing a hard copy.
- hasInlineSegment = HasInlineHeadSegment(pArr->head->length);
- }
- if (startSeg)
- {
- // Delete Phase
- if (startSeg->left <= start && (startSeg->left + startSeg->length) >= limit)
- {
- // All splice happens in one segment.
- SparseArraySegmentBase *nextSeg = startSeg->next;
- // Splice the segment first, which might OOM throw but the array would be intact.
- JavascriptArray::ArraySegmentSpliceHelper(pnewArr, (SparseArraySegment<T>*)startSeg, (SparseArraySegment<T>**)prevSeg, start, deleteLen, insertArgs, insertLen, recycler);
- while (nextSeg)
- {
- // adjust next segments left
- nextSeg->left = nextSeg->left - deleteLen + insertLen;
- if (nextSeg->next == nullptr)
- {
- nextSeg->EnsureSizeInBound();
- }
- nextSeg = nextSeg->next;
- }
- if (*prevSeg)
- {
- (*prevSeg)->EnsureSizeInBound();
- }
- return;
- }
- else
- {
- SparseArraySegment<T>* newHeadSeg = nullptr; // pnewArr->head is null
- SparseArraySegmentBase** prevNewHeadSeg = &(pnewArr->head);
- // delete till deleteLen and reuse segments for new array if it is possible.
- // 3 steps -
- //1. delete 1st segment (which may be partial delete)
- // 2. delete next n complete segments
- // 3. delete last segment (which again may be partial delete)
- // Step (1) -- WOOB 1116297: When left >= start, step (1) is skipped, resulting in pNewArr->head->left != 0. We need to touch up pNewArr.
- if (startSeg->left < start)
- {
- if (start < startSeg->left + startSeg->length)
- {
- uint32 headDeleteLen = startSeg->left + startSeg->length - start;
- if (startSeg->next)
- {
- // We know the new segment will have a next segment, so allocate it as non-leaf.
- newHeadSeg = SparseArraySegment<T>::template AllocateSegmentImpl<false>(recycler, 0, headDeleteLen, headDeleteLen, nullptr);
- }
- else
- {
- newHeadSeg = SparseArraySegment<T>::AllocateSegment(recycler, 0, headDeleteLen, headDeleteLen, nullptr);
- }
- newHeadSeg = SparseArraySegment<T>::CopySegment(recycler, newHeadSeg, 0, startSeg, start, headDeleteLen);
- newHeadSeg->next = nullptr;
- *prevNewHeadSeg = newHeadSeg;
- prevNewHeadSeg = &newHeadSeg->next;
- startSeg->Truncate(start);
- }
- savePrev = startSeg;
- prevPrevSeg = prevSeg;
- prevSeg = &startSeg->next;
- startSeg = (SparseArraySegment<T>*)startSeg->next;
- }
- // Step (2) first we should do a hard copy if we have an inline head Segment
- else if (hasInlineSegment && nullptr != startSeg)
- {
- // start should be in between left and left + length
- if (startSeg->left <= start && start < startSeg->left + startSeg->length)
- {
- uint32 headDeleteLen = startSeg->left + startSeg->length - start;
- if (startSeg->next)
- {
- // We know the new segment will have a next segment, so allocate it as non-leaf.
- newHeadSeg = SparseArraySegment<T>::template AllocateSegmentImpl<false>(recycler, 0, headDeleteLen, headDeleteLen, nullptr);
- }
- else
- {
- newHeadSeg = SparseArraySegment<T>::AllocateSegment(recycler, 0, headDeleteLen, headDeleteLen, nullptr);
- }
- newHeadSeg = SparseArraySegment<T>::CopySegment(recycler, newHeadSeg, 0, startSeg, start, headDeleteLen);
- *prevNewHeadSeg = newHeadSeg;
- prevNewHeadSeg = &newHeadSeg->next;
- // Remove the entire segment from the original array
- *prevSeg = startSeg->next;
- startSeg = (SparseArraySegment<T>*)startSeg->next;
- }
- // if we have an inline head segment with 0 elements, remove it
- else if (startSeg->left == 0 && startSeg->length == 0)
- {
- Assert(startSeg->size != 0);
- *prevSeg = startSeg->next;
- startSeg = (SparseArraySegment<T>*)startSeg->next;
- }
- }
- // Step (2) proper
- SparseArraySegmentBase *temp = nullptr;
- while (startSeg && (startSeg->left + startSeg->length) <= limit)
- {
- temp = startSeg->next;
- // move that entire segment to new array
- startSeg->left = startSeg->left - start;
- startSeg->next = nullptr;
- *prevNewHeadSeg = startSeg;
- prevNewHeadSeg = &startSeg->next;
- // Remove the entire segment from the original array
- *prevSeg = temp;
- startSeg = (SparseArraySegment<T>*)temp;
- }
- // Step(2) above could delete the original head segment entirely, causing current head not
- // starting from 0. Then if any of the following throw, we have a corrupted array. Need
- // protection here.
- bool dummyHeadNodeInserted = false;
- if (!savePrev && (!startSeg || startSeg->left != 0))
- {
- Assert(pArr->head == startSeg);
- pArr->EnsureHeadStartsFromZero<T>(recycler);
- Assert(pArr->head && pArr->head->next == startSeg);
- savePrev = pArr->head;
- prevPrevSeg = prevSeg;
- prevSeg = &pArr->head->next;
- dummyHeadNodeInserted = true;
- }
- // Step (3)
- if (startSeg && (startSeg->left < limit))
- {
- // copy the first part of the last segment to be deleted to new array
- uint32 headDeleteLen = start + deleteLen - startSeg->left ;
- newHeadSeg = SparseArraySegment<T>::AllocateSegment(recycler, startSeg->left - start, headDeleteLen, (SparseArraySegmentBase *)nullptr);
- newHeadSeg = SparseArraySegment<T>::CopySegment(recycler, newHeadSeg, startSeg->left - start, startSeg, startSeg->left, headDeleteLen);
- newHeadSeg->next = nullptr;
- *prevNewHeadSeg = newHeadSeg;
- prevNewHeadSeg = &newHeadSeg->next;
- // move the last segment
- memmove(startSeg->elements, startSeg->elements + headDeleteLen, sizeof(T) * (startSeg->length - headDeleteLen));
- startSeg->left = startSeg->left + headDeleteLen; // We are moving the left ahead to point to the right index
- startSeg->length = startSeg->length - headDeleteLen;
- startSeg->Truncate(startSeg->left + startSeg->length);
- startSeg->EnsureSizeInBound(); // Just truncated, size might exceed next.left
- }
- if (startSeg && ((startSeg->left - deleteLen + insertLen) == 0) && dummyHeadNodeInserted)
- {
- Assert(start + insertLen == 0);
- // Remove the dummy head node to preserve array consistency.
- pArr->head = startSeg;
- savePrev = nullptr;
- prevSeg = &pArr->head;
- }
- while (startSeg)
- {
- startSeg->left = startSeg->left - deleteLen + insertLen ;
- if (startSeg->next == nullptr)
- {
- startSeg->EnsureSizeInBound();
- }
- startSeg = (SparseArraySegment<T>*)startSeg->next;
- }
- }
- }
- // The size of pnewArr head allocated in above step 1 might exceed next.left concatenated in step 2/3.
- pnewArr->head->EnsureSizeInBound();
- if (savePrev)
- {
- savePrev->EnsureSizeInBound();
- }
- // insert elements
- if (insertLen > 0)
- {
- Assert(!JavascriptNativeIntArray::Is(pArr) && !JavascriptNativeFloatArray::Is(pArr));
- // InsertPhase
- SparseArraySegment<T> *segInsert = nullptr;
- // see if we are just about the right of the previous segment
- Assert(!savePrev || savePrev->left <= start);
- if (savePrev && (start - savePrev->left < savePrev->size))
- {
- segInsert = (SparseArraySegment<T>*)savePrev;
- uint32 spaceLeft = segInsert->size - (start - segInsert->left);
- if(spaceLeft < insertLen)
- {
- if (!segInsert->next)
- {
- segInsert = segInsert->GrowByMin(recycler, insertLen - spaceLeft);
- }
- else
- {
- segInsert = segInsert->GrowByMinMax(recycler, insertLen - spaceLeft, segInsert->next->left - segInsert->left - segInsert->size);
- }
- }
- *prevPrevSeg = segInsert;
- segInsert->length = start + insertLen - segInsert->left;
- }
- else
- {
- segInsert = SparseArraySegment<T>::AllocateSegment(recycler, start, insertLen, *prevSeg);
- segInsert->next = *prevSeg;
- *prevSeg = segInsert;
- savePrev = segInsert;
- }
- uint32 relativeStart = start - segInsert->left;
- // inserted elements starts at argument 3 of splice(start, deleteNumber, insertelem1, insertelem2, insertelem3, ...);
- js_memcpy_s(segInsert->elements + relativeStart, sizeof(T) * insertLen, insertArgs, sizeof(T) * insertLen);
- }
- }
- template<typename indexT>
- RecyclableObject* JavascriptArray::ObjectSpliceHelper(RecyclableObject* pObj, uint32 len, uint32 start,
- uint32 deleteLen, Var* insertArgs, uint32 insertLen, ScriptContext *scriptContext, RecyclableObject* pNewObj)
- {
- JavascriptArray *pnewArr = nullptr;
- if (pNewObj == nullptr)
- {
- pNewObj = ArraySpeciesCreate(pObj, deleteLen, scriptContext);
- if (pNewObj == nullptr || !JavascriptArray::Is(pNewObj))
- {
- pnewArr = scriptContext->GetLibrary()->CreateArray(deleteLen);
- pnewArr->EnsureHead<Var>();
- pNewObj = pnewArr;
- }
- }
- if (JavascriptArray::Is(pNewObj))
- {
- pnewArr = JavascriptArray::FromVar(pNewObj);
- }
- // copy elements to delete to new array
- if (deleteLen > 0)
- {
- for (uint32 i = 0; i < deleteLen; i++)
- {
- Var element;
- if (JavascriptOperators::HasItem(pObj, start+i))
- {
- BOOL getResult = JavascriptOperators::GetItem(pObj, start + i, &element, scriptContext);
- Assert(getResult);
- if (pnewArr)
- {
- pnewArr->DirectSetItemAt(i, element);
- }
- else
- {
- JavascriptArray::SetArrayLikeObjects(pNewObj, i, element);
- }
- }
- }
- }
- ThrowTypeErrorOnFailureHelper h(scriptContext, _u("Array.prototype.splice"));
- // If the return object is not an array, we'll need to set the 'length' property
- if (pnewArr == nullptr)
- {
- h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(pNewObj, pNewObj, PropertyIds::length, JavascriptNumber::ToVar(deleteLen, scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible));
- }
- // Now we need reserve room if it is necessary
- if (insertLen > deleteLen) // Might overflow max array length
- {
- // Unshift [start + deleteLen, len) to start + insertLen
- Unshift<indexT>(pObj, start + insertLen, start + deleteLen, len, scriptContext);
- }
- else if (insertLen < deleteLen) // Won't overflow max array length
- {
- uint32 j = 0;
- for (uint32 i = start + deleteLen; i < len; i++)
- {
- Var element;
- if (JavascriptOperators::HasItem(pObj, i))
- {
- BOOL getResult = JavascriptOperators::GetItem(pObj, i, &element, scriptContext);
- Assert(getResult);
- h.ThrowTypeErrorOnFailure(JavascriptOperators::SetItem(pObj, pObj, start + insertLen + j, element, scriptContext, PropertyOperation_ThrowIfNotExtensible));
- }
- else
- {
- h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(pObj, start + insertLen + j, PropertyOperation_ThrowIfNotExtensible));
- }
- j++;
- }
- // Clean up the rest
- for (uint32 i = len; i > len - deleteLen + insertLen; i--)
- {
- h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(pObj, i - 1, PropertyOperation_ThrowIfNotExtensible));
- }
- }
- if (insertLen > 0)
- {
- indexT dstIndex = start; // insert index might overflow max array length
- for (uint i = 0; i < insertLen; i++)
- {
- h.ThrowTypeErrorOnFailure(IndexTrace<indexT>::SetItem(pObj, dstIndex, insertArgs[i], PropertyOperation_ThrowIfNotExtensible));
- ++dstIndex;
- }
- }
- // Set up new length
- indexT newLen = indexT(len - deleteLen) + insertLen;
- h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(pObj, pObj, PropertyIds::length, IndexTrace<indexT>::ToNumber(newLen, scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible));
- #ifdef VALIDATE_ARRAY
- if (pnewArr)
- {
- pnewArr->ValidateArray();
- }
- #endif
- return pNewObj;
- }
- Var JavascriptArray::EntryToLocaleString(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- if (args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedObject, _u("Array.prototype.toLocaleString"));
- }
- if (JavascriptArray::IsDirectAccessArray(args[0]))
- {
- JavascriptArray* arr = JavascriptArray::FromVar(args[0]);
- return ToLocaleString(arr, scriptContext);
- }
- else
- {
- RecyclableObject* obj = nullptr;
- if (FALSE == JavascriptConversion::ToObject(args[0], scriptContext, &obj))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.toLocaleString"));
- }
- return ToLocaleString(obj, scriptContext);
- }
- }
- //
- // Unshift object elements [start, end) to toIndex, asserting toIndex > start.
- //
- template<typename T, typename P>
- void JavascriptArray::Unshift(RecyclableObject* obj, const T& toIndex, uint32 start, P end, ScriptContext* scriptContext)
- {
- typedef IndexTrace<T> index_trace;
- ThrowTypeErrorOnFailureHelper h(scriptContext, _u("Array.prototype.unshift"));
- if (start < end)
- {
- T newEnd = (end - start - 1);// newEnd - 1
- T dst = toIndex + newEnd;
- uint32 i = 0;
- if (end > UINT32_MAX)
- {
- uint64 i64 = end;
- for (; i64 > UINT32_MAX; i64--)
- {
- Var element;
- if (JavascriptOperators::HasItem(obj, i64 - 1))
- {
- BOOL getResult = JavascriptOperators::GetItem(obj, i64 - 1, &element, scriptContext);
- Assert(getResult);
- h.ThrowTypeErrorOnFailure(index_trace::SetItem(obj, dst, element, PropertyOperation_ThrowIfNotExtensible));
- }
- else
- {
- h.ThrowTypeErrorOnFailure(index_trace::DeleteItem(obj, dst, PropertyOperation_ThrowIfNotExtensible));
- }
- --dst;
- }
- i = UINT32_MAX;
- }
- else
- {
- i = (uint32) end;
- }
- for (; i > start; i--)
- {
- Var element;
- if (JavascriptOperators::HasItem(obj, i-1))
- {
- BOOL getResult = JavascriptOperators::GetItem(obj, i - 1, &element, scriptContext);
- Assert(getResult);
- h.ThrowTypeErrorOnFailure(index_trace::SetItem(obj, dst, element, PropertyOperation_ThrowIfNotExtensible));
- }
- else
- {
- h.ThrowTypeErrorOnFailure(index_trace::DeleteItem(obj, dst, PropertyOperation_ThrowIfNotExtensible));
- }
- --dst;
- }
- }
- }
- template<typename T>
- void JavascriptArray::GrowArrayHeadHelperForUnshift(JavascriptArray* pArr, uint32 unshiftElements, ScriptContext * scriptContext)
- {
- SparseArraySegmentBase* nextToHeadSeg = pArr->head->next;
- Recycler* recycler = scriptContext->GetRecycler();
- if (nextToHeadSeg == nullptr)
- {
- pArr->EnsureHead<T>();
- pArr->head = ((SparseArraySegment<T>*)pArr->head)->GrowByMin(recycler, unshiftElements);
- }
- else
- {
- pArr->head = ((SparseArraySegment<T>*)pArr->head)->GrowByMinMax(recycler, unshiftElements, ((nextToHeadSeg->left + unshiftElements) - pArr->head->left - pArr->head->size));
- }
- }
- template<typename T>
- void JavascriptArray::UnshiftHelper(JavascriptArray* pArr, uint32 unshiftElements, Js::Var * elements)
- {
- SparseArraySegment<T>* head = (SparseArraySegment<T>*)pArr->head;
- // Make enough room in the head segment to insert new elements at the front
- memmove(head->elements + unshiftElements, head->elements, sizeof(T) * pArr->head->length);
- uint32 oldHeadLength = head->length;
- head->length += unshiftElements;
- /* Set head segment as the last used segment */
- pArr->InvalidateLastUsedSegment();
- bool hasNoMissingValues = pArr->HasNoMissingValues();
- /* Set HasNoMissingValues to false -> Since we shifted elements right, we might have missing values after the memmove */
- if(unshiftElements > oldHeadLength)
- {
- pArr->SetHasNoMissingValues(false);
- }
- #if ENABLE_PROFILE_INFO
- pArr->FillFromArgs(unshiftElements, 0, elements, nullptr, true/*dontCreateNewArray*/);
- #else
- pArr->FillFromArgs(unshiftElements, 0, elements, true/*dontCreateNewArray*/);
- #endif
- // Setting back to the old value
- pArr->SetHasNoMissingValues(hasNoMissingValues);
- }
- Var JavascriptArray::EntryUnshift(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- Var res = scriptContext->GetLibrary()->GetUndefined();
- if (args.Info.Count == 0)
- {
- return res;
- }
- if (JavascriptArray::Is(args[0]))
- {
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(args[0]);
- #endif
- JavascriptArray * pArr = JavascriptArray::FromVar(args[0]);
- uint32 unshiftElements = args.Info.Count - 1;
- if (unshiftElements > 0)
- {
- if (pArr->IsFillFromPrototypes())
- {
- pArr->FillFromPrototypes(0, pArr->length); // We need find all missing value from [[proto]] object
- }
- // Pre-process: truncate overflowing elements to properties
- bool newLenOverflowed = false;
- uint32 maxLen = MaxArrayLength - unshiftElements;
- if (pArr->length > maxLen)
- {
- newLenOverflowed = true;
- // Ensure the array is non-native when overflow happens
- EnsureNonNativeArray(pArr);
- pArr->TruncateToProperties(MaxArrayLength, maxLen);
- Assert(pArr->length + unshiftElements == MaxArrayLength);
- }
- pArr->ClearSegmentMap(); // Dump segmentMap on unshift (before any possible allocation and throw)
- Assert(pArr->length <= MaxArrayLength - unshiftElements);
- SparseArraySegmentBase* renumberSeg = pArr->head->next;
- bool isIntArray = false;
- bool isFloatArray = false;
- if (JavascriptNativeIntArray::Is(pArr))
- {
- isIntArray = true;
- }
- else if (JavascriptNativeFloatArray::Is(pArr))
- {
- isFloatArray = true;
- }
- // If we need to grow head segment and there is already a next segment, then allocate the new head segment upfront
- // If there is OOM in array allocation, then array consistency is maintained.
- if (pArr->head->size < pArr->head->length + unshiftElements)
- {
- if (isIntArray)
- {
- GrowArrayHeadHelperForUnshift<int32>(pArr, unshiftElements, scriptContext);
- }
- else if (isFloatArray)
- {
- GrowArrayHeadHelperForUnshift<double>(pArr, unshiftElements, scriptContext);
- }
- else
- {
- GrowArrayHeadHelperForUnshift<Var>(pArr, unshiftElements, scriptContext);
- }
- }
- while (renumberSeg)
- {
- renumberSeg->left += unshiftElements;
- if (renumberSeg->next == nullptr)
- {
- // last segment can shift its left + size beyond MaxArrayLength, so truncate if so
- renumberSeg->EnsureSizeInBound();
- }
- renumberSeg = renumberSeg->next;
- }
- if (isIntArray)
- {
- UnshiftHelper<int32>(pArr, unshiftElements, args.Values);
- }
- else if (isFloatArray)
- {
- UnshiftHelper<double>(pArr, unshiftElements, args.Values);
- }
- else
- {
- UnshiftHelper<Var>(pArr, unshiftElements, args.Values);
- }
- pArr->InvalidateLastUsedSegment();
- pArr->length += unshiftElements;
- #ifdef VALIDATE_ARRAY
- pArr->ValidateArray();
- #endif
- if (newLenOverflowed) // ES5: throw if new "length" exceeds max array length
- {
- JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthAssignIncorrect);
- }
- }
- res = JavascriptNumber::ToVar(pArr->length, scriptContext);
- }
- else
- {
- RecyclableObject* dynamicObject = nullptr;
- if (FALSE == JavascriptConversion::ToObject(args[0], scriptContext, &dynamicObject))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.unshift"));
- }
- BigIndex length;
- if (scriptContext->GetConfig()->IsES6ToLengthEnabled())
- {
- length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(dynamicObject, scriptContext), scriptContext);
- }
- else
- {
- length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(dynamicObject, scriptContext), scriptContext);
- }
- uint32 unshiftElements = args.Info.Count - 1;
- if (unshiftElements > 0)
- {
- uint32 MaxSpaceUint32 = MaxArrayLength - unshiftElements;
- // Note: end will always be a smallIndex either it is less than length in which case it is MaxSpaceUint32
- // or MaxSpaceUint32 is greater than length meaning length is a uint32 number
- BigIndex end = length > MaxSpaceUint32 ? MaxSpaceUint32 : length;
- if (end < length)
- {
- // Unshift [end, length) to MaxArrayLength
- // MaxArrayLength + (length - MaxSpaceUint32 - 1) = length + unshiftElements -1
- if (length.IsSmallIndex())
- {
- Unshift<BigIndex>(dynamicObject, MaxArrayLength, end.GetSmallIndex(), length.GetSmallIndex(), scriptContext);
- }
- else
- {
- Unshift<BigIndex, uint64>(dynamicObject, MaxArrayLength, end.GetSmallIndex(), length.GetBigIndex(), scriptContext);
- }
- }
- // Unshift [0, end) to unshiftElements
- // unshiftElements + (MaxSpaceUint32 - 0 - 1) = MaxArrayLength -1 therefore this unshift covers up to MaxArrayLength - 1
- Unshift<uint32>(dynamicObject, unshiftElements, 0, end.GetSmallIndex(), scriptContext);
- for (uint32 i = 0; i < unshiftElements; i++)
- {
- JavascriptOperators::SetItem(dynamicObject, dynamicObject, i, args[i + 1], scriptContext, PropertyOperation_ThrowIfNotExtensible, true);
- }
- }
- ThrowTypeErrorOnFailureHelper h(scriptContext, _u("Array.prototype.unshift"));
- //ES6 - update 'length' even if unshiftElements == 0;
- BigIndex newLen = length + unshiftElements;
- res = JavascriptNumber::ToVar(newLen.IsSmallIndex() ? newLen.GetSmallIndex() : newLen.GetBigIndex(), scriptContext);
- h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(dynamicObject, dynamicObject, PropertyIds::length, res, scriptContext, PropertyOperation_ThrowIfNotExtensible));
- }
- return res;
- }
- Var JavascriptArray::EntryToString(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- if (args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedObject);
- }
- // ES5 15.4.4.2: call join, or built-in Object.prototype.toString
- RecyclableObject* obj = nullptr;
- if (FALSE == JavascriptConversion::ToObject(args[0], scriptContext, &obj))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.toString"));
- }
- // In ES5 we could be calling a user defined join, even on array. We must [[Get]] join at runtime.
- Var join = JavascriptOperators::GetProperty(obj, PropertyIds::join, scriptContext);
- if (JavascriptConversion::IsCallable(join))
- {
- RecyclableObject* func = RecyclableObject::FromVar(join);
- // We need to record implicit call here, because marked the Array.toString as no side effect,
- // but if we call user code here which may have side effect
- ThreadContext * threadContext = scriptContext->GetThreadContext();
- Var result = threadContext->ExecuteImplicitCall(func, ImplicitCall_ToPrimitive, [=]() -> Js::Var
- {
- // Stack object should have a pre-op bail on implicit call. We shouldn't see them here.
- Assert(!ThreadContext::IsOnStack(obj));
- // The correct flag value is CallFlags_Value but we pass CallFlags_None in compat modes
- CallFlags flags = CallFlags_Value;
- return func->GetEntryPoint()(func, CallInfo(flags, 1), obj);
- });
- if(!result)
- {
- // There was an implicit call and implicit calls are disabled. This would typically cause a bailout.
- Assert(threadContext->IsDisableImplicitCall());
- result = scriptContext->GetLibrary()->GetNull();
- }
- return result;
- }
- else
- {
- // call built-in Object.prototype.toString
- return JavascriptObject::EntryToString(function, 1, obj);
- }
- }
- #if DEBUG
- BOOL JavascriptArray::GetIndex(const char16* propName, ulong *pIndex)
- {
- ulong lu, luDig;
- long cch = (long)wcslen(propName);
- char16* pch = const_cast<char16 *>(propName);
- lu = *pch - '0';
- if (lu > 9)
- return FALSE;
- if (0 == lu)
- {
- *pIndex = 0;
- return 1 == cch;
- }
- while ((luDig = *++pch - '0') < 10)
- {
- // If we overflow 32 bits, ignore the item
- if (lu > 0x19999999)
- return FALSE;
- lu *= 10;
- if(lu > (ULONG_MAX - luDig))
- return FALSE;
- lu += luDig;
- }
- if (pch - propName != cch)
- return FALSE;
- if (lu == JavascriptArray::InvalidIndex)
- {
- // 0xFFFFFFFF is not treated as an array index so that the length can be
- // capped at 32 bits.
- return FALSE;
- }
- *pIndex = lu;
- return TRUE;
- }
- #endif
- JavascriptString* JavascriptArray::GetLocaleSeparator(ScriptContext* scriptContext)
- {
- #ifdef ENABLE_GLOBALIZATION
- LCID lcid = GetUserDefaultLCID();
- int count = 0;
- char16 szSeparator[6];
- // According to the document for GetLocaleInfo this is a sufficient buffer size.
- count = GetLocaleInfoW(lcid, LOCALE_SLIST, szSeparator, 5);
- if( !count)
- {
- AssertMsg(FALSE, "GetLocaleInfo failed");
- return scriptContext->GetLibrary()->GetCommaSpaceDisplayString();
- }
- else
- {
- // Append ' ' if necessary
- if( count < 2 || szSeparator[count-2] != ' ')
- {
- szSeparator[count-1] = ' ';
- szSeparator[count] = '\0';
- }
- return JavascriptString::NewCopyBuffer(szSeparator, count, scriptContext);
- }
- #else
- // xplat-todo: Support locale-specific seperator
- return scriptContext->GetLibrary()->GetCommaSpaceDisplayString();
- #endif
- }
- template <typename T>
- JavascriptString* JavascriptArray::ToLocaleString(T* arr, ScriptContext* scriptContext)
- {
- uint32 length = ItemTrace<T>::GetLength(arr, scriptContext);
- if (length == 0 || scriptContext->CheckObject(arr))
- {
- return scriptContext->GetLibrary()->GetEmptyString();
- }
- JavascriptString* res = scriptContext->GetLibrary()->GetEmptyString();
- bool pushedObject = false;
- TryFinally([&]()
- {
- scriptContext->PushObject(arr);
- pushedObject = true;
- Var element;
- if (ItemTrace<T>::GetItem(arr, 0, &element, scriptContext))
- {
- res = JavascriptArray::ToLocaleStringHelper(element, scriptContext);
- }
- if (length > 1)
- {
- JavascriptString* separator = GetLocaleSeparator(scriptContext);
- for (uint32 i = 1; i < length; i++)
- {
- res = JavascriptString::Concat(res, separator);
- if (ItemTrace<T>::GetItem(arr, i, &element, scriptContext))
- {
- res = JavascriptString::Concat(res, JavascriptArray::ToLocaleStringHelper(element, scriptContext));
- }
- }
- }
- },
- [&](bool/*hasException*/)
- {
- if (pushedObject)
- {
- Var top = scriptContext->PopObject();
- AssertMsg(top == arr, "Unmatched operation stack");
- }
- });
- if (res == nullptr)
- {
- res = scriptContext->GetLibrary()->GetEmptyString();
- }
- return res;
- }
- Var JavascriptArray::EntryIsArray(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(ArrayisArrayCount);
- if (args.Info.Count < 2)
- {
- return scriptContext->GetLibrary()->GetFalse();
- }
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(args[1]);
- #endif
- if (JavascriptOperators::IsArray(args[1]))
- {
- return scriptContext->GetLibrary()->GetTrue();
- }
- return scriptContext->GetLibrary()->GetFalse();
- }
- ///----------------------------------------------------------------------------
- /// Find() calls the given predicate callback on each element of the array, in
- /// order, and returns the first element that makes the predicate return true,
- /// as described in (ES6.0: S22.1.3.8).
- ///----------------------------------------------------------------------------
- Var JavascriptArray::EntryFind(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- if (args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.find"));
- }
- int64 length;
- JavascriptArray * pArr = nullptr;
- RecyclableObject* obj = nullptr;
- if (JavascriptArray::Is(args[0]) && !JavascriptArray::FromVar(args[0])->IsCrossSiteObject())
- {
- pArr = JavascriptArray::FromVar(args[0]);
- obj = pArr;
- length = pArr->length;
- }
- else
- {
- if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.find"));
- }
- // In ES6-mode, we always load the length property from the object instead of using the internal slot.
- // Even for arrays, this is now observable via proxies.
- // If source object is not an array, we fall back to this behavior anyway.
- Var lenValue = JavascriptOperators::OP_GetLength(obj, scriptContext);
- length = JavascriptConversion::ToLength(lenValue, scriptContext);
- }
- return JavascriptArray::FindHelper<false>(pArr, nullptr, obj, length, args, scriptContext);
- }
- template <bool findIndex>
- Var JavascriptArray::FindHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, int64 length, Arguments& args, ScriptContext* scriptContext)
- {
- if (args.Info.Count < 2 || !JavascriptConversion::IsCallable(args[1]))
- {
- // typedArrayBase is only non-null if and only if we came here via the TypedArray entrypoint
- if (typedArrayBase != nullptr)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, findIndex ? _u("[TypedArray].prototype.findIndex") : _u("[TypedArray].prototype.find"));
- }
- else
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, findIndex ? _u("Array.prototype.findIndex") : _u("Array.prototype.find"));
- }
- }
- RecyclableObject* callBackFn = RecyclableObject::FromVar(args[1]);
- Var thisArg;
- if (args.Info.Count > 2)
- {
- thisArg = args[2];
- }
- else
- {
- thisArg = scriptContext->GetLibrary()->GetUndefined();
- }
- // If we came from Array.prototype.find/findIndex and source object is not a JavascriptArray, source could be a TypedArray
- if (typedArrayBase == nullptr && pArr == nullptr && TypedArrayBase::Is(obj))
- {
- typedArrayBase = TypedArrayBase::FromVar(obj);
- }
- // The correct flag value is CallFlags_Value but we pass CallFlags_None in compat modes
- CallFlags flags = CallFlags_Value;
- Var element = nullptr;
- Var testResult = nullptr;
- Var undefined = scriptContext->GetLibrary()->GetUndefined();
- if (pArr)
- {
- for (uint32 k = 0; k < length; k++)
- {
- element = undefined;
- pArr->DirectGetItemAtFull(k, &element);
- Var index = JavascriptNumber::ToVar(k, scriptContext);
- testResult = callBackFn->GetEntryPoint()(callBackFn, CallInfo(flags, 4), thisArg,
- element,
- index,
- pArr);
- if (JavascriptConversion::ToBoolean(testResult, scriptContext))
- {
- return findIndex ? index : element;
- }
- }
- }
- else if (typedArrayBase)
- {
- for (uint32 k = 0; k < length; k++)
- {
- element = typedArrayBase->DirectGetItem(k);
- Var index = JavascriptNumber::ToVar(k, scriptContext);
- testResult = callBackFn->GetEntryPoint()(callBackFn, CallInfo(flags, 4), thisArg,
- element,
- index,
- typedArrayBase);
- if (JavascriptConversion::ToBoolean(testResult, scriptContext))
- {
- return findIndex ? index : element;
- }
- }
- }
- else
- {
- for (uint32 k = 0; k < length; k++)
- {
- element = undefined;
- JavascriptOperators::GetItem(obj, k, &element, scriptContext);
- Var index = JavascriptNumber::ToVar(k, scriptContext);
- testResult = callBackFn->GetEntryPoint()(callBackFn, CallInfo(flags, 4), thisArg,
- element,
- index,
- obj);
- if (JavascriptConversion::ToBoolean(testResult, scriptContext))
- {
- return findIndex ? index : element;
- }
- }
- }
- return findIndex ? JavascriptNumber::ToVar(-1, scriptContext) : scriptContext->GetLibrary()->GetUndefined();
- }
- ///----------------------------------------------------------------------------
- /// FindIndex() calls the given predicate callback on each element of the
- /// array, in order, and returns the index of the first element that makes the
- /// predicate return true, as described in (ES6.0: S22.1.3.9).
- ///----------------------------------------------------------------------------
- Var JavascriptArray::EntryFindIndex(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- if (args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.findIndex"));
- }
- int64 length;
- JavascriptArray * pArr = nullptr;
- RecyclableObject* obj = nullptr;
- if (JavascriptArray::Is(args[0]) && !JavascriptArray::FromVar(args[0])->IsCrossSiteObject())
- {
- pArr = JavascriptArray::FromVar(args[0]);
- obj = pArr;
- length = pArr->length;
- }
- else
- {
- if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.findIndex"));
- }
- // In ES6-mode, we always load the length property from the object instead of using the internal slot.
- // Even for arrays, this is now observable via proxies.
- // If source object is not an array, we fall back to this behavior anyway.
- Var lenValue = JavascriptOperators::OP_GetLength(obj, scriptContext);
- length = JavascriptConversion::ToLength(lenValue, scriptContext);
- }
- return JavascriptArray::FindHelper<true>(pArr, nullptr, obj, length, args, scriptContext);
- }
- ///----------------------------------------------------------------------------
- /// Entries() returns a new ArrayIterator object configured to return key-
- /// value pairs matching the elements of the this array/array-like object,
- /// as described in (ES6.0: S22.1.3.4).
- ///----------------------------------------------------------------------------
- Var JavascriptArray::EntryEntries(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- if (args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.entries"));
- }
- RecyclableObject* thisObj = nullptr;
- if (FALSE == JavascriptConversion::ToObject(args[0], scriptContext, &thisObj))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.entries"));
- }
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(thisObj);
- #endif
- return scriptContext->GetLibrary()->CreateArrayIterator(thisObj, JavascriptArrayIteratorKind::KeyAndValue);
- }
- ///----------------------------------------------------------------------------
- /// Keys() returns a new ArrayIterator object configured to return the keys
- /// of the this array/array-like object, as described in (ES6.0: S22.1.3.13).
- ///----------------------------------------------------------------------------
- Var JavascriptArray::EntryKeys(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- if (args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.keys"));
- }
- RecyclableObject* thisObj = nullptr;
- if (FALSE == JavascriptConversion::ToObject(args[0], scriptContext, &thisObj))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.keys"));
- }
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(thisObj);
- #endif
- return scriptContext->GetLibrary()->CreateArrayIterator(thisObj, JavascriptArrayIteratorKind::Key);
- }
- ///----------------------------------------------------------------------------
- /// Values() returns a new ArrayIterator object configured to return the values
- /// of the this array/array-like object, as described in (ES6.0: S22.1.3.29).
- ///----------------------------------------------------------------------------
- Var JavascriptArray::EntryValues(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- if (args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.values"));
- }
- RecyclableObject* thisObj = nullptr;
- if (FALSE == JavascriptConversion::ToObject(args[0], scriptContext, &thisObj))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.values"));
- }
- return scriptContext->GetLibrary()->CreateArrayIterator(thisObj, JavascriptArrayIteratorKind::Value);
- }
- Var JavascriptArray::EntryEvery(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Array.prototype.every"));
- Assert(!(callInfo.Flags & CallFlags_New));
- CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(ArrayEveryCount);
- if (args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.every"));
- }
- BigIndex length;
- JavascriptArray* pArr = nullptr;
- RecyclableObject* obj = nullptr;
- if (JavascriptArray::Is(args[0]) && !JavascriptArray::FromVar(args[0])->IsCrossSiteObject())
- {
- pArr = JavascriptArray::FromVar(args[0]);
- obj = pArr;
- }
- else
- {
- if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.every"));
- }
- }
- // In ES6-mode, we always load the length property from the object instead of using the internal slot.
- // Even for arrays, this is now observable via proxies.
- // If source object is not an array, we fall back to this behavior anyway.
- if (scriptContext->GetConfig()->IsES6TypedArrayExtensionsEnabled() || pArr == nullptr)
- {
- if (scriptContext->GetConfig()->IsES6ToLengthEnabled())
- {
- length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext);
- }
- else
- {
- length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext);
- }
- }
- else
- {
- length = pArr->length;
- }
- if (length.IsSmallIndex())
- {
- return JavascriptArray::EveryHelper(pArr, nullptr, obj, length.GetSmallIndex(), args, scriptContext);
- }
- Assert(pArr == nullptr || length.IsUint32Max()); // if pArr is not null lets make sure length is safe to cast, which will only happen if length is a uint32max
- return JavascriptArray::EveryHelper(pArr, nullptr, obj, length.GetBigIndex(), args, scriptContext);
- }
- // Array.prototype.every as described by ES6.0 (draft 22) Section 22.1.3.5
- template <typename T>
- Var JavascriptArray::EveryHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, Arguments& args, ScriptContext* scriptContext)
- {
- if (args.Info.Count < 2 || !JavascriptConversion::IsCallable(args[1]))
- {
- // typedArrayBase is only non-null if and only if we came here via the TypedArray entrypoint
- if (typedArrayBase != nullptr)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("[TypedArray].prototype.every"));
- }
- else
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("Array.prototype.every"));
- }
- }
- RecyclableObject* callBackFn = RecyclableObject::FromVar(args[1]);
- Var thisArg = nullptr;
- if (args.Info.Count > 2)
- {
- thisArg = args[2];
- }
- else
- {
- thisArg = scriptContext->GetLibrary()->GetUndefined();
- }
- // If we came from Array.prototype.map and source object is not a JavascriptArray, source could be a TypedArray
- if (typedArrayBase == nullptr && pArr == nullptr && TypedArrayBase::Is(obj))
- {
- typedArrayBase = TypedArrayBase::FromVar(obj);
- }
- Var element = nullptr;
- Var testResult = nullptr;
- // The correct flag value is CallFlags_Value but we pass CallFlags_None in compat modes
- CallFlags flags = CallFlags_Value;
- if (pArr)
- {
- for (uint32 k = 0; k < length; k++)
- {
- if (!pArr->DirectGetItemAtFull(k, &element))
- {
- continue;
- }
- testResult = callBackFn->GetEntryPoint()(callBackFn, CallInfo(flags, 4), thisArg,
- element,
- JavascriptNumber::ToVar(k, scriptContext),
- pArr);
- if (!JavascriptConversion::ToBoolean(testResult, scriptContext))
- {
- return scriptContext->GetLibrary()->GetFalse();
- }
- }
- }
- else if (typedArrayBase)
- {
- Assert(length <= UINT_MAX);
- for (uint32 k = 0; k < length; k++)
- {
- if (!typedArrayBase->HasItem(k))
- {
- continue;
- }
- element = typedArrayBase->DirectGetItem(k);
- testResult = callBackFn->GetEntryPoint()(callBackFn, CallInfo(flags, 4), thisArg,
- element,
- JavascriptNumber::ToVar(k, scriptContext),
- typedArrayBase);
- if (!JavascriptConversion::ToBoolean(testResult, scriptContext))
- {
- return scriptContext->GetLibrary()->GetFalse();
- }
- }
- }
- else
- {
- for (T k = 0; k < length; k++)
- {
- // According to es6 spec, we need to call Has first before calling Get
- if (!JavascriptOperators::HasItem(obj, k))
- {
- continue;
- }
- BOOL getResult = JavascriptOperators::GetItem(obj, k, &element, scriptContext);
- Assert(getResult);
- testResult = callBackFn->GetEntryPoint()(callBackFn, CallInfo(flags, 4), thisArg,
- element,
- JavascriptNumber::ToVar(k, scriptContext),
- obj);
- if (!JavascriptConversion::ToBoolean(testResult, scriptContext))
- {
- return scriptContext->GetLibrary()->GetFalse();
- }
- }
- }
- return scriptContext->GetLibrary()->GetTrue();
- }
- Var JavascriptArray::EntrySome(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Array.prototype.some"));
- CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(ArraySomeCount);
- Assert(!(callInfo.Flags & CallFlags_New));
- if (args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.some"));
- }
- BigIndex length;
- JavascriptArray* pArr = nullptr;
- RecyclableObject* obj = nullptr;
- if (JavascriptArray::Is(args[0]) && !JavascriptArray::FromVar(args[0])->IsCrossSiteObject())
- {
- pArr = JavascriptArray::FromVar(args[0]);
- obj = pArr;
- }
- else
- {
- if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.some"));
- }
- }
- // In ES6-mode, we always load the length property from the object instead of using the internal slot.
- // Even for arrays, this is now observable via proxies.
- // If source object is not an array, we fall back to this behavior anyway.
- if (scriptContext->GetConfig()->IsES6TypedArrayExtensionsEnabled() || pArr == nullptr)
- {
- if (scriptContext->GetConfig()->IsES6ToLengthEnabled())
- {
- length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext);
- }
- else
- {
- length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext);
- }
- }
- else
- {
- length = pArr->length;
- }
- if (length.IsSmallIndex())
- {
- return JavascriptArray::SomeHelper(pArr, nullptr, obj, length.GetSmallIndex(), args, scriptContext);
- }
- Assert(pArr == nullptr || length.IsUint32Max()); // if pArr is not null lets make sure length is safe to cast, which will only happen if length is a uint32max
- return JavascriptArray::SomeHelper(pArr, nullptr, obj, length.GetBigIndex(), args, scriptContext);
- }
- // Array.prototype.some as described in ES6.0 (draft 22) Section 22.1.3.23
- template <typename T>
- Var JavascriptArray::SomeHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, Arguments& args, ScriptContext* scriptContext)
- {
- if (args.Info.Count < 2 || !JavascriptConversion::IsCallable(args[1]))
- {
- // We are in the TypedArray version of this API if and only if typedArrayBase != nullptr
- if (typedArrayBase != nullptr)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("[TypedArray].prototype.some"));
- }
- else
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("Array.prototype.some"));
- }
- }
- RecyclableObject* callBackFn = RecyclableObject::FromVar(args[1]);
- Var thisArg = nullptr;
- if (args.Info.Count > 2)
- {
- thisArg = args[2];
- }
- else
- {
- thisArg = scriptContext->GetLibrary()->GetUndefined();
- }
- // If we came from Array.prototype.some and source object is not a JavascriptArray, source could be a TypedArray
- if (typedArrayBase == nullptr && pArr == nullptr && TypedArrayBase::Is(obj))
- {
- typedArrayBase = TypedArrayBase::FromVar(obj);
- }
- // The correct flag value is CallFlags_Value but we pass CallFlags_None in compat modes
- CallFlags flags = CallFlags_Value;
- Var element = nullptr;
- Var testResult = nullptr;
- if (pArr)
- {
- for (uint32 k = 0; k < length; k++)
- {
- if (!pArr->DirectGetItemAtFull(k, &element))
- {
- continue;
- }
- testResult = callBackFn->GetEntryPoint()(callBackFn, CallInfo(flags, 4), thisArg,
- element,
- JavascriptNumber::ToVar(k, scriptContext),
- pArr);
- if (JavascriptConversion::ToBoolean(testResult, scriptContext))
- {
- return scriptContext->GetLibrary()->GetTrue();
- }
- }
- }
- else if (typedArrayBase)
- {
- Assert(length <= UINT_MAX);
- for (uint32 k = 0; k < length; k++)
- {
- // If k < typedArrayBase->length, we know that HasItem will return true.
- // But we still have to call it in case there's a proxy trap or in the case that we are calling
- // Array.prototype.some with a TypedArray that has a different length instance property.
- if (!typedArrayBase->HasItem(k))
- {
- continue;
- }
- element = typedArrayBase->DirectGetItem(k);
- testResult = callBackFn->GetEntryPoint()(callBackFn, CallInfo(flags, 4), thisArg,
- element,
- JavascriptNumber::ToVar(k, scriptContext),
- typedArrayBase);
- if (JavascriptConversion::ToBoolean(testResult, scriptContext))
- {
- return scriptContext->GetLibrary()->GetTrue();
- }
- }
- }
- else
- {
- for (T k = 0; k < length; k++)
- {
- if (!JavascriptOperators::HasItem(obj, k))
- {
- continue;
- }
- BOOL getResult = JavascriptOperators::GetItem(obj, k, &element, scriptContext);
- Assert(getResult);
- testResult = callBackFn->GetEntryPoint()(callBackFn, CallInfo(flags, 4), thisArg,
- element,
- JavascriptNumber::ToVar(k, scriptContext),
- obj);
- if (JavascriptConversion::ToBoolean(testResult, scriptContext))
- {
- return scriptContext->GetLibrary()->GetTrue();
- }
- }
- }
- return scriptContext->GetLibrary()->GetFalse();
- }
- Var JavascriptArray::EntryForEach(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Array.prototype.forEach"));
- CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(ArrayForEachCount)
- Assert(!(callInfo.Flags & CallFlags_New));
- if (args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.forEach"));
- }
- BigIndex length;
- JavascriptArray* pArr = nullptr;
- RecyclableObject* dynamicObject = nullptr;
- RecyclableObject* callBackFn = nullptr;
- Var thisArg = nullptr;
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(args[0]);
- #endif
- if (JavascriptArray::Is(args[0]) && scriptContext == JavascriptArray::FromVar(args[0])->GetScriptContext())
- {
- pArr = JavascriptArray::FromVar(args[0]);
- dynamicObject = pArr;
- }
- else
- {
- if (FALSE == JavascriptConversion::ToObject(args[0], scriptContext, &dynamicObject))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.forEach"));
- }
- if (JavascriptArray::Is(dynamicObject) && scriptContext == JavascriptArray::FromVar(dynamicObject)->GetScriptContext())
- {
- pArr = JavascriptArray::FromVar(dynamicObject);
- }
- }
- // In ES6-mode, we always load the length property from the object instead of using the internal slot.
- // Even for arrays, this is now observable via proxies.
- // If source object is not an array, we fall back to this behavior anyway.
- if (scriptContext->GetConfig()->IsES6TypedArrayExtensionsEnabled() || pArr == nullptr)
- {
- if (scriptContext->GetConfig()->IsES6ToLengthEnabled())
- {
- length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(dynamicObject, scriptContext), scriptContext);
- }
- else
- {
- length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(dynamicObject, scriptContext), scriptContext);
- }
- }
- else
- {
- length = pArr->length;
- }
- if (args.Info.Count < 2 || !JavascriptConversion::IsCallable(args[1]))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("Array.prototype.forEach"));
- }
- callBackFn = RecyclableObject::FromVar(args[1]);
- if (args.Info.Count > 2)
- {
- thisArg = args[2];
- }
- else
- {
- thisArg = scriptContext->GetLibrary()->GetUndefined();
- }
- // The correct flag value is CallFlags_Value but we pass CallFlags_None in compat modes
- CallFlags flags = CallFlags_Value;
- auto fn32 = [dynamicObject, callBackFn, flags, thisArg, scriptContext](uint32 k, Var element)
- {
- callBackFn->GetEntryPoint()(callBackFn, CallInfo(flags, 4), thisArg,
- element,
- JavascriptNumber::ToVar(k, scriptContext),
- dynamicObject);
- };
- auto fn64 = [dynamicObject, callBackFn, flags, thisArg, scriptContext](uint64 k, Var element)
- {
- callBackFn->GetEntryPoint()(callBackFn, CallInfo(flags, 4), thisArg,
- element,
- JavascriptNumber::ToVar(k, scriptContext),
- dynamicObject);
- };
- if (pArr)
- {
- Assert(pArr == dynamicObject);
- pArr->ForEachItemInRange<true>(0, length.IsUint32Max() ? MaxArrayLength : length.GetSmallIndex(), scriptContext, fn32);
- }
- else
- {
- if (length.IsSmallIndex())
- {
- TemplatedForEachItemInRange<true>(dynamicObject, 0u, length.GetSmallIndex(), scriptContext, fn32);
- }
- else
- {
- TemplatedForEachItemInRange<true>(dynamicObject, 0ui64, length.GetBigIndex(), scriptContext, fn64);
- }
- }
- return scriptContext->GetLibrary()->GetUndefined();
- }
- Var JavascriptArray::EntryCopyWithin(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- RecyclableObject* obj = nullptr;
- JavascriptArray* pArr = nullptr;
- int64 length;
- if (JavascriptArray::Is(args[0]) && !JavascriptArray::FromVar(args[0])->IsCrossSiteObject())
- {
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(args[0]);
- #endif
- pArr = JavascriptArray::FromVar(args[0]);
- obj = pArr;
- length = pArr->length;
- }
- else
- {
- if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.copyWithin"));
- }
- // In ES6-mode, we always load the length property from the object instead of using the internal slot.
- // Even for arrays, this is now observable via proxies.
- // If source object is not an array, we fall back to this behavior anyway.
- Var lenValue = JavascriptOperators::OP_GetLength(obj, scriptContext);
- length = JavascriptConversion::ToLength(lenValue, scriptContext);
- }
- return JavascriptArray::CopyWithinHelper(pArr, nullptr, obj, length, args, scriptContext);
- }
- // Array.prototype.copyWithin as defined in ES6.0 (draft 22) Section 22.1.3.3
- Var JavascriptArray::CopyWithinHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, int64 length, Arguments& args, ScriptContext* scriptContext)
- {
- Assert(args.Info.Count > 0);
- JavascriptLibrary* library = scriptContext->GetLibrary();
- int64 fromVal = 0;
- int64 toVal = 0;
- int64 finalVal = length;
- // If we came from Array.prototype.copyWithin and source object is not a JavascriptArray, source could be a TypedArray
- if (typedArrayBase == nullptr && pArr == nullptr && TypedArrayBase::Is(obj))
- {
- typedArrayBase = TypedArrayBase::FromVar(obj);
- }
- if (args.Info.Count > 1)
- {
- toVal = JavascriptArray::GetIndexFromVar(args[1], length, scriptContext);
- if (args.Info.Count > 2)
- {
- fromVal = JavascriptArray::GetIndexFromVar(args[2], length, scriptContext);
- if (args.Info.Count > 3 && args[3] != library->GetUndefined())
- {
- finalVal = JavascriptArray::GetIndexFromVar(args[3], length, scriptContext);
- }
- }
- }
- // If count would be negative or zero, we won't do anything so go ahead and return early.
- if (finalVal <= fromVal || length <= toVal)
- {
- return obj;
- }
- // Make sure we won't underflow during the count calculation
- Assert(finalVal > fromVal && length > toVal);
- int64 count = min(finalVal - fromVal, length - toVal);
- // We shouldn't have made it here if the count was going to be zero
- Assert(count > 0);
- int direction;
- if (fromVal < toVal && toVal < (fromVal + count))
- {
- direction = -1;
- fromVal += count - 1;
- toVal += count - 1;
- }
- else
- {
- direction = 1;
- }
- // If we are going to copy elements from or to indices > 2^32-1 we'll execute this (slightly slower path)
- // It's possible to optimize here so that we use the normal code below except for the > 2^32-1 indices
- if ((direction == -1 && (fromVal >= MaxArrayLength || toVal >= MaxArrayLength))
- || (((fromVal + count) > MaxArrayLength) || ((toVal + count) > MaxArrayLength)))
- {
- while (count > 0)
- {
- Var index = JavascriptNumber::ToVar(fromVal, scriptContext);
- if (JavascriptOperators::OP_HasItem(obj, index, scriptContext))
- {
- Var val = JavascriptOperators::OP_GetElementI(obj, index, scriptContext);
- JavascriptOperators::OP_SetElementI(obj, JavascriptNumber::ToVar(toVal, scriptContext), val, scriptContext, PropertyOperation_ThrowIfNotExtensible);
- }
- else
- {
- JavascriptOperators::OP_DeleteElementI(obj, JavascriptNumber::ToVar(toVal, scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible);
- }
- fromVal += direction;
- toVal += direction;
- count--;
- }
- }
- else
- {
- Assert(fromVal < MaxArrayLength);
- Assert(toVal < MaxArrayLength);
- Assert(direction == -1 || (fromVal + count < MaxArrayLength && toVal + count < MaxArrayLength));
- uint32 fromIndex = static_cast<uint32>(fromVal);
- uint32 toIndex = static_cast<uint32>(toVal);
- while (count > 0)
- {
- if (obj->HasItem(fromIndex))
- {
- if (typedArrayBase)
- {
- Var val = typedArrayBase->DirectGetItem(fromIndex);
- typedArrayBase->DirectSetItem(toIndex, val, false);
- }
- else if (pArr)
- {
- Var val = pArr->DirectGetItem(fromIndex);
- pArr->SetItem(toIndex, val, Js::PropertyOperation_ThrowIfNotExtensible);
- }
- else
- {
- Var val = JavascriptOperators::OP_GetElementI_UInt32(obj, fromIndex, scriptContext);
- JavascriptOperators::OP_SetElementI_UInt32(obj, toIndex, val, scriptContext, PropertyOperation_ThrowIfNotExtensible);
- }
- }
- else
- {
- obj->DeleteItem(toIndex, PropertyOperation_ThrowIfNotExtensible);
- }
- fromIndex += direction;
- toIndex += direction;
- count--;
- }
- }
- return obj;
- }
- Var JavascriptArray::EntryFill(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- RecyclableObject* obj = nullptr;
- JavascriptArray* pArr = nullptr;
- int64 length;
- if (JavascriptArray::Is(args[0]) && !JavascriptArray::FromVar(args[0])->IsCrossSiteObject())
- {
- pArr = JavascriptArray::FromVar(args[0]);
- obj = pArr;
- length = pArr->length;
- }
- else
- {
- if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.fill"));
- }
- // In ES6-mode, we always load the length property from the object instead of using the internal slot.
- // Even for arrays, this is now observable via proxies.
- // If source object is not an array, we fall back to this behavior anyway.
- Var lenValue = JavascriptOperators::OP_GetLength(obj, scriptContext);
- length = JavascriptConversion::ToLength(lenValue, scriptContext);
- }
- return JavascriptArray::FillHelper(pArr, nullptr, obj, length, args, scriptContext);
- }
- // Array.prototype.fill as defined in ES6.0 (draft 22) Section 22.1.3.6
- Var JavascriptArray::FillHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, int64 length, Arguments& args, ScriptContext* scriptContext)
- {
- Assert(args.Info.Count > 0);
- JavascriptLibrary* library = scriptContext->GetLibrary();
- // If we came from Array.prototype.fill and source object is not a JavascriptArray, source could be a TypedArray
- if (typedArrayBase == nullptr && pArr == nullptr && TypedArrayBase::Is(obj))
- {
- typedArrayBase = TypedArrayBase::FromVar(obj);
- }
- Var fillValue;
- if (args.Info.Count > 1)
- {
- fillValue = args[1];
- }
- else
- {
- fillValue = library->GetUndefined();
- }
- int64 k = 0;
- int64 finalVal = length;
- if (args.Info.Count > 2)
- {
- k = JavascriptArray::GetIndexFromVar(args[2], length, scriptContext);
- if (args.Info.Count > 3 && !JavascriptOperators::IsUndefinedObject(args[3]))
- {
- finalVal = JavascriptArray::GetIndexFromVar(args[3], length, scriptContext);
- }
- }
- if (k < MaxArrayLength)
- {
- int64 end = min<int64>(finalVal, MaxArrayLength);
- uint32 u32k = static_cast<uint32>(k);
- while (u32k < end)
- {
- if (typedArrayBase)
- {
- typedArrayBase->DirectSetItem(u32k, fillValue, false);
- }
- else if (pArr)
- {
- pArr->SetItem(u32k, fillValue, PropertyOperation_ThrowIfNotExtensible);
- }
- else
- {
- JavascriptOperators::OP_SetElementI_UInt32(obj, u32k, fillValue, scriptContext, Js::PropertyOperation_ThrowIfNotExtensible);
- }
- u32k++;
- }
- BigIndex dstIndex = MaxArrayLength;
- for (int64 i = end; i < finalVal; ++i)
- {
- if (pArr)
- {
- pArr->DirectSetItemAt(dstIndex, fillValue);
- ++dstIndex;
- }
- else
- {
- JavascriptOperators::OP_SetElementI(obj, JavascriptNumber::ToVar(i, scriptContext), fillValue, scriptContext, Js::PropertyOperation_ThrowIfNotExtensible);
- }
- }
- }
- else
- {
- BigIndex dstIndex = static_cast<uint64>(k);
- for (int64 i = k; i < finalVal; i++)
- {
- if (pArr)
- {
- pArr->DirectSetItemAt(dstIndex, fillValue);
- ++dstIndex;
- }
- else
- {
- JavascriptOperators::OP_SetElementI(obj, JavascriptNumber::ToVar(i, scriptContext), fillValue, scriptContext, Js::PropertyOperation_ThrowIfNotExtensible);
- }
- }
- }
- return obj;
- }
- // Array.prototype.map as defined by ES6.0 (Final) 22.1.3.15
- Var JavascriptArray::EntryMap(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Array.prototype.map"));
- CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(ArrayMapCount);
- Assert(!(callInfo.Flags & CallFlags_New));
- if (args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.map"));
- }
- BigIndex length;
- JavascriptArray* pArr = nullptr;
- RecyclableObject* obj = nullptr;
- if (JavascriptArray::Is(args[0]) && !JavascriptArray::FromVar(args[0])->IsCrossSiteObject())
- {
- pArr = JavascriptArray::FromVar(args[0]);
- obj = pArr;
- }
- else
- {
- if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.map"));
- }
- }
- // In ES6-mode, we always load the length property from the object instead of using the internal slot.
- // Even for arrays, this is now observable via proxies.
- // If source object is not an array, we fall back to this behavior anyway.
- if (scriptContext->GetConfig()->IsES6TypedArrayExtensionsEnabled() || pArr == nullptr)
- {
- length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext);
- }
- else
- {
- length = pArr->length;
- }
- if (length.IsSmallIndex())
- {
- return JavascriptArray::MapHelper(pArr, nullptr, obj, length.GetSmallIndex(), args, scriptContext);
- }
- Assert(pArr == nullptr || length.IsUint32Max()); // if pArr is not null lets make sure length is safe to cast, which will only happen if length is a uint32max
- return JavascriptArray::MapHelper(pArr, nullptr, obj, length.GetBigIndex(), args, scriptContext);
- }
- template<typename T>
- Var JavascriptArray::MapHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, Arguments& args, ScriptContext* scriptContext)
- {
- RecyclableObject* newObj = nullptr;
- JavascriptArray* newArr = nullptr;
- bool isTypedArrayEntryPoint = typedArrayBase != nullptr;
- if (args.Info.Count < 2 || !JavascriptConversion::IsCallable(args[1]))
- {
- if (isTypedArrayEntryPoint)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("[TypedArray].prototype.map"));
- }
- else
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("Array.prototype.map"));
- }
- }
- RecyclableObject* callBackFn = RecyclableObject::FromVar(args[1]);
- Var thisArg;
- if (args.Info.Count > 2)
- {
- thisArg = args[2];
- }
- else
- {
- thisArg = scriptContext->GetLibrary()->GetUndefined();
- }
- // If we came from Array.prototype.map and source object is not a JavascriptArray, source could be a TypedArray
- if (!isTypedArrayEntryPoint && pArr == nullptr && TypedArrayBase::Is(obj))
- {
- typedArrayBase = TypedArrayBase::FromVar(obj);
- }
- // If the entry point is %TypedArray%.prototype.map or the source object is an Array exotic object we should try to load the constructor property
- // and use it to construct the return object.
- if (isTypedArrayEntryPoint)
- {
- Var constructor = JavascriptOperators::SpeciesConstructor(
- typedArrayBase, TypedArrayBase::GetDefaultConstructor(args[0], scriptContext), scriptContext);
- if (JavascriptOperators::IsConstructor(constructor))
- {
- Js::Var constructorArgs[] = { constructor, JavascriptNumber::ToVar(length, scriptContext) };
- Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs));
- newObj = RecyclableObject::FromVar(JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext));
- }
- else if (isTypedArrayEntryPoint)
- {
- // We only need to throw a TypeError when the constructor property is not an actual constructor if %TypedArray%.prototype.map was called
- JavascriptError::ThrowTypeError(scriptContext, JSERR_NotAConstructor, _u("[TypedArray].prototype.map"));
- }
- }
- // skip the typed array and "pure" array case, we still need to handle special arrays like es5array, remote array, and proxy of array.
- else if (pArr == nullptr || scriptContext->GetConfig()->IsES6SpeciesEnabled())
- {
- newObj = ArraySpeciesCreate(obj, length, scriptContext);
- }
- if (newObj == nullptr)
- {
- if (length > UINT_MAX)
- {
- JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthConstructIncorrect);
- }
- newArr = scriptContext->GetLibrary()->CreateArray(static_cast<uint32>(length));
- newArr->EnsureHead<Var>();
- newObj = newArr;
- }
- else
- {
- // If the new object we created is an array, remember that as it will save us time setting properties in the object below
- if (JavascriptArray::Is(newObj))
- {
- newArr = JavascriptArray::FromVar(newObj);
- }
- }
- Var element = nullptr;
- Var mappedValue = nullptr;
- // The correct flag value is CallFlags_Value but we pass CallFlags_None in compat modes
- CallFlags callBackFnflags = CallFlags_Value;
- CallInfo callBackFnInfo = CallInfo(callBackFnflags, 4);
- // We at least have to have newObj as a valid object
- Assert(newObj);
- if (pArr != nullptr)
- {
- // If source is a JavascriptArray, newObj may or may not be an array based on what was in source's constructor property
- for (uint32 k = 0; k < length; k++)
- {
- if (!pArr->DirectGetItemAtFull(k, &element))
- {
- continue;
- }
- mappedValue = callBackFn->GetEntryPoint()(callBackFn, callBackFnInfo, thisArg,
- element,
- JavascriptNumber::ToVar(k, scriptContext),
- pArr);
- // If newArr is a valid pointer, then we constructed an array to return. Otherwise we need to do generic object operations
- if (newArr)
- {
- newArr->DirectSetItemAt(k, mappedValue);
- }
- else
- {
- JavascriptArray::SetArrayLikeObjects(RecyclableObject::FromVar(newObj), k, mappedValue);
- }
- }
- }
- else if (typedArrayBase != nullptr)
- {
- // Source is a TypedArray, we may have tried to call a constructor, but newObj may not be a TypedArray (or an array either)
- TypedArrayBase* newTypedArray = nullptr;
- if (TypedArrayBase::Is(newObj))
- {
- newTypedArray = TypedArrayBase::FromVar(newObj);
- }
- for (uint32 k = 0; k < length; k++)
- {
- // We can't rely on the length value being equal to typedArrayBase->GetLength() because user code may lie and
- // attach any length property to a TypedArray instance and pass it as this parameter when .calling
- // Array.prototype.map.
- if (!typedArrayBase->HasItem(k))
- {
- // We know that if HasItem returns false, all the future calls to HasItem will return false as well since
- // we visit the items in order. We could return early here except that we have to continue calling HasItem
- // on all the subsequent items according to the spec.
- continue;
- }
- element = typedArrayBase->DirectGetItem(k);
- mappedValue = callBackFn->GetEntryPoint()(callBackFn, callBackFnInfo, thisArg,
- element,
- JavascriptNumber::ToVar(k, scriptContext),
- obj);
- // If newObj is a TypedArray, set the mappedValue directly, otherwise see if it's an array and finally fall back to
- // the normal Set path.
- if (newTypedArray)
- {
- newTypedArray->DirectSetItem(k, mappedValue, false);
- }
- else if (newArr)
- {
- newArr->DirectSetItemAt(k, mappedValue);
- }
- else
- {
- JavascriptArray::SetArrayLikeObjects(RecyclableObject::FromVar(newObj), k, mappedValue);
- }
- }
- }
- else
- {
- for (uint32 k = 0; k < length; k++)
- {
- if (!JavascriptOperators::HasItem(obj, k))
- {
- continue;
- }
- BOOL getResult = JavascriptOperators::GetItem(obj, k, &element, scriptContext);
- Assert(getResult);
- mappedValue = callBackFn->GetEntryPoint()(callBackFn, callBackFnInfo, thisArg,
- element,
- JavascriptNumber::ToVar(k, scriptContext),
- obj);
- if (newArr)
- {
- newArr->DirectSetItemAt(k, mappedValue);
- }
- else
- {
- JavascriptArray::SetArrayLikeObjects(RecyclableObject::FromVar(newObj), k, mappedValue);
- }
- }
- }
- #ifdef VALIDATE_ARRAY
- if (JavascriptArray::Is(newObj))
- {
- newArr->ValidateArray();
- }
- #endif
- return newObj;
- }
- Var JavascriptArray::EntryFilter(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Array.prototype.filter"));
- CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(ArrayFilterCount);
- Assert(!(callInfo.Flags & CallFlags_New));
- if (args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.filter"));
- }
- RecyclableObject* newObj = nullptr;
- JavascriptArray* newArr = nullptr;
- BigIndex length;
- JavascriptArray* pArr = nullptr;
- RecyclableObject* dynamicObject = nullptr;
- RecyclableObject* callBackFn = nullptr;
- Var thisArg = nullptr;
- if (JavascriptArray::Is(args[0]) && !JavascriptArray::FromVar(args[0])->IsCrossSiteObject())
- {
- pArr = JavascriptArray::FromVar(args[0]);
- dynamicObject = pArr;
- }
- else
- {
- if (FALSE == JavascriptConversion::ToObject(args[0], scriptContext, &dynamicObject))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.filter"));
- }
- }
- // In ES6-mode, we always load the length property from the object instead of using the internal slot.
- // Even for arrays, this is now observable via proxies.
- // If source object is not an array, we fall back to this behavior anyway.
- if (scriptContext->GetConfig()->IsES6TypedArrayExtensionsEnabled() || pArr == nullptr)
- {
- if (scriptContext->GetConfig()->IsES6ToLengthEnabled())
- {
- length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(dynamicObject, scriptContext), scriptContext);
- }
- else
- {
- length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(dynamicObject, scriptContext), scriptContext);
- }
- }
- else
- {
- length = pArr->length;
- }
- if (args.Info.Count < 2 || !JavascriptConversion::IsCallable(args[1]))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("Array.prototype.filter"));
- }
- callBackFn = RecyclableObject::FromVar(args[1]);
- if (args.Info.Count > 2)
- {
- thisArg = args[2];
- }
- else
- {
- thisArg = scriptContext->GetLibrary()->GetUndefined();
- }
- // If the source object is an Array exotic object we should try to load the constructor property and use it to construct the return object.
- newObj = ArraySpeciesCreate(dynamicObject, 0, scriptContext);
- if (newObj == nullptr)
- {
- newArr = scriptContext->GetLibrary()->CreateArray(0);
- newArr->EnsureHead<Var>();
- newObj = newArr;
- }
- else
- {
- // If the new object we created is an array, remember that as it will save us time setting properties in the object below
- if (JavascriptArray::Is(newObj))
- {
- newArr = JavascriptArray::FromVar(newObj);
- }
- }
- // The correct flag value is CallFlags_Value but we pass CallFlags_None in compat modes
- CallFlags flags = CallFlags_Value;
- Var element = nullptr;
- Var selected = nullptr;
- BigIndex i = 0u;
- if (pArr)
- {
- uint32 arrayLength = length.IsUint32Max() ? MaxArrayLength : length.GetSmallIndex();
- // If source was an array object, the return object might be any random object
- for (uint32 k = 0; k < arrayLength; k++)
- {
- if (!pArr->DirectGetItemAtFull(k, &element))
- {
- continue;
- }
- selected = callBackFn->GetEntryPoint()(callBackFn, CallInfo(flags, 4), thisArg,
- element,
- JavascriptNumber::ToVar(k, scriptContext),
- pArr);
- if (JavascriptConversion::ToBoolean(selected, scriptContext))
- {
- // Try to fast path if the return object is an array
- if (newArr)
- {
- newArr->DirectSetItemAt(i, element);
- }
- else
- {
- JavascriptArray::SetArrayLikeObjects(RecyclableObject::FromVar(newObj), i, element);
- }
- ++i;
- }
- }
- }
- else
- {
- for (BigIndex k = 0u; k < length; ++k)
- {
- if (!JavascriptOperators::HasItem(dynamicObject, k.IsSmallIndex() ? k.GetSmallIndex() : k.GetBigIndex()))
- {
- continue;
- }
- BOOL getResult = JavascriptOperators::GetItem(dynamicObject, k.IsSmallIndex() ? k.GetSmallIndex() : k.GetBigIndex(), &element, scriptContext);
- Assert(getResult);
- selected = callBackFn->GetEntryPoint()(callBackFn, CallInfo(flags, 4), thisArg,
- element,
- JavascriptNumber::ToVar(k.IsSmallIndex() ? k.GetSmallIndex() : k.GetBigIndex(), scriptContext),
- dynamicObject);
- if (JavascriptConversion::ToBoolean(selected, scriptContext))
- {
- if (newArr)
- {
- newArr->DirectSetItemAt(i, element);
- }
- else
- {
- JavascriptArray::SetArrayLikeObjects(RecyclableObject::FromVar(newObj), i, element);
- }
- ++i;
- }
- }
- }
- #ifdef VALIDATE_ARRAY
- if (newArr)
- {
- newArr->ValidateArray();
- }
- #endif
- return newObj;
- }
- Var JavascriptArray::EntryReduce(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Array.prototype.reduce"));
- CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(ArrayReduceCount);
- Assert(!(callInfo.Flags & CallFlags_New));
- if (args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.reduce"));
- }
- BigIndex length;
- JavascriptArray * pArr = nullptr;
- RecyclableObject* obj = nullptr;
- if (JavascriptArray::Is(args[0]) && !JavascriptArray::FromVar(args[0])->IsCrossSiteObject())
- {
- pArr = JavascriptArray::FromVar(args[0]);
- obj = pArr;
- length = pArr->length;
- }
- else
- {
- if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.reduce"));
- }
- if (scriptContext->GetConfig()->IsES6ToLengthEnabled())
- {
- length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext);
- }
- else
- {
- length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext);
- }
- }
- if (length.IsSmallIndex())
- {
- return JavascriptArray::ReduceHelper(pArr, nullptr, obj, length.GetSmallIndex(), args, scriptContext);
- }
- return JavascriptArray::ReduceHelper(pArr, nullptr, obj, length.GetBigIndex(), args, scriptContext);
- }
- // Array.prototype.reduce as described in ES6.0 (draft 22) Section 22.1.3.18
- template <typename T>
- Var JavascriptArray::ReduceHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, Arguments& args, ScriptContext* scriptContext)
- {
- if (args.Info.Count < 2 || !JavascriptConversion::IsCallable(args[1]))
- {
- if (typedArrayBase != nullptr)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("[TypedArray].prototype.reduce"));
- }
- else
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("Array.prototype.reduce"));
- }
- }
- // If we came from Array.prototype.reduce and source object is not a JavascriptArray, source could be a TypedArray
- if (typedArrayBase == nullptr && pArr == nullptr && TypedArrayBase::Is(obj))
- {
- typedArrayBase = TypedArrayBase::FromVar(obj);
- }
- T k = 0;
- RecyclableObject* callBackFn = RecyclableObject::FromVar(args[1]);
- Var accumulator = nullptr;
- Var element = nullptr;
- if (args.Info.Count > 2)
- {
- accumulator = args[2];
- }
- else
- {
- if (length == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, VBSERR_ActionNotSupported);
- }
- bool bPresent = false;
- if (pArr)
- {
- for (; k < length && bPresent == false; k++)
- {
- if (!pArr->DirectGetItemAtFull((uint32)k, &element))
- {
- continue;
- }
- bPresent = true;
- accumulator = element;
- }
- }
- else if (typedArrayBase)
- {
- Assert(length <= UINT_MAX);
- for (; k < length && bPresent == false; k++)
- {
- if (!typedArrayBase->HasItem((uint32)k))
- {
- continue;
- }
- element = typedArrayBase->DirectGetItem((uint32)k);
- bPresent = true;
- accumulator = element;
- }
- }
- else
- {
- for (; k < length && bPresent == false; k++)
- {
- if (!JavascriptOperators::HasItem(obj, k))
- {
- continue;
- }
- BOOL getResult = JavascriptOperators::GetItem(obj, k, &accumulator, scriptContext);
- Assert(getResult);
- bPresent = true;
- }
- }
- if (bPresent == false)
- {
- JavascriptError::ThrowTypeError(scriptContext, VBSERR_ActionNotSupported);
- }
- }
- Assert(accumulator);
- Var undefinedValue = scriptContext->GetLibrary()->GetUndefined();
- // The correct flag value is CallFlags_Value but we pass CallFlags_None in compat modes
- CallFlags flags = CallFlags_Value;
- if (pArr)
- {
- for (; k < length; k++)
- {
- if (!pArr->DirectGetItemAtFull((uint32)k, &element))
- {
- continue;
- }
- accumulator = callBackFn->GetEntryPoint()(callBackFn, CallInfo(flags, 5), undefinedValue,
- accumulator,
- element,
- JavascriptNumber::ToVar(k, scriptContext),
- pArr);
- }
- }
- else if (typedArrayBase)
- {
- Assert(length <= UINT_MAX);
- for (; k < length; k++)
- {
- if (!typedArrayBase->HasItem((uint32)k))
- {
- continue;
- }
- element = typedArrayBase->DirectGetItem((uint32)k);
- accumulator = callBackFn->GetEntryPoint()(callBackFn, CallInfo(flags, 5), undefinedValue,
- accumulator,
- element,
- JavascriptNumber::ToVar(k, scriptContext),
- typedArrayBase);
- }
- }
- else
- {
- for (; k < length; k++)
- {
- if (!JavascriptOperators::HasItem(obj, k))
- {
- continue;
- }
- BOOL getResult = JavascriptOperators::GetItem(obj, k, &element, scriptContext);
- Assert(getResult);
- accumulator = callBackFn->GetEntryPoint()(callBackFn, CallInfo(flags, 5), undefinedValue,
- accumulator,
- element,
- JavascriptNumber::ToVar(k, scriptContext),
- obj);
- }
- }
- return accumulator;
- }
- Var JavascriptArray::EntryReduceRight(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Array.prototype.reduceRight"));
- CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(ArrayReduceRightCount);
- Assert(!(callInfo.Flags & CallFlags_New));
- if (args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.reduceRight"));
- }
- BigIndex length;
- JavascriptArray * pArr = nullptr;
- RecyclableObject* obj = nullptr;
- if (JavascriptArray::Is(args[0]) && !JavascriptArray::FromVar(args[0])->IsCrossSiteObject())
- {
- pArr = JavascriptArray::FromVar(args[0]);
- obj = pArr;
- }
- else
- {
- if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.reduceRight"));
- }
- }
- // In ES6-mode, we always load the length property from the object instead of using the internal slot.
- // Even for arrays, this is now observable via proxies.
- // If source object is not an array, we fall back to this behavior anyway.
- if (scriptContext->GetConfig()->IsES6TypedArrayExtensionsEnabled() || pArr == nullptr)
- {
- if (scriptContext->GetConfig()->IsES6ToLengthEnabled())
- {
- length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext);
- }
- else
- {
- length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext);
- }
- }
- else
- {
- length = pArr->length;
- }
- if (length.IsSmallIndex())
- {
- return JavascriptArray::ReduceRightHelper(pArr, nullptr, obj, length.GetSmallIndex(), args, scriptContext);
- }
- return JavascriptArray::ReduceRightHelper(pArr, nullptr, obj, length.GetBigIndex(), args, scriptContext);
- }
- // Array.prototype.reduceRight as described in ES6.0 (draft 22) Section 22.1.3.19
- template <typename T>
- Var JavascriptArray::ReduceRightHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, Arguments& args, ScriptContext* scriptContext)
- {
- if (args.Info.Count < 2 || !JavascriptConversion::IsCallable(args[1]))
- {
- if (typedArrayBase != nullptr)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("[TypedArray].prototype.reduceRight"));
- }
- else
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("Array.prototype.reduceRight"));
- }
- }
- // If we came from Array.prototype.reduceRight and source object is not a JavascriptArray, source could be a TypedArray
- if (typedArrayBase == nullptr && pArr == nullptr && TypedArrayBase::Is(obj))
- {
- typedArrayBase = TypedArrayBase::FromVar(obj);
- }
- RecyclableObject* callBackFn = RecyclableObject::FromVar(args[1]);
- Var accumulator = nullptr;
- Var element = nullptr;
- T k = 0;
- T index = 0;
- if (args.Info.Count > 2)
- {
- accumulator = args[2];
- }
- else
- {
- if (length == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, VBSERR_ActionNotSupported);
- }
- bool bPresent = false;
- if (pArr)
- {
- for (; k < length && bPresent == false; k++)
- {
- index = length - k - 1;
- if (!pArr->DirectGetItemAtFull((uint32)index, &element))
- {
- continue;
- }
- bPresent = true;
- accumulator = element;
- }
- }
- else if (typedArrayBase)
- {
- Assert(length <= UINT_MAX);
- for (; k < length && bPresent == false; k++)
- {
- index = length - k - 1;
- if (!typedArrayBase->HasItem((uint32)index))
- {
- continue;
- }
- element = typedArrayBase->DirectGetItem((uint32)index);
- bPresent = true;
- accumulator = element;
- }
- }
- else
- {
- for (; k < length && bPresent == false; k++)
- {
- index = length - k - 1;
- if (!JavascriptOperators::HasItem(obj, index))
- {
- continue;
- }
- BOOL getResult = JavascriptOperators::GetItem(obj, index, &accumulator, scriptContext);
- Assert(getResult);
- bPresent = true;
- }
- }
- if (bPresent == false)
- {
- JavascriptError::ThrowTypeError(scriptContext, VBSERR_ActionNotSupported);
- }
- }
- // The correct flag value is CallFlags_Value but we pass CallFlags_None in compat modes
- CallFlags flags = CallFlags_Value;
- Var undefinedValue = scriptContext->GetLibrary()->GetUndefined();
- if (pArr)
- {
- for (; k < length; k++)
- {
- index = length - k - 1;
- if (!pArr->DirectGetItemAtFull((uint32)index, &element))
- {
- continue;
- }
- accumulator = callBackFn->GetEntryPoint()(callBackFn, CallInfo(flags, 5), undefinedValue,
- accumulator,
- element,
- JavascriptNumber::ToVar(index, scriptContext),
- pArr);
- }
- }
- else if (typedArrayBase)
- {
- Assert(length <= UINT_MAX);
- for (; k < length; k++)
- {
- index = length - k - 1;
- if (!typedArrayBase->HasItem((uint32) index))
- {
- continue;
- }
- element = typedArrayBase->DirectGetItem((uint32)index);
- accumulator = callBackFn->GetEntryPoint()(callBackFn, CallInfo(flags, 5), undefinedValue,
- accumulator,
- element,
- JavascriptNumber::ToVar(index, scriptContext),
- typedArrayBase);
- }
- }
- else
- {
- for (; k < length; k++)
- {
- index = length - k - 1;
- if (!JavascriptOperators::HasItem(obj, index))
- {
- continue;
- }
- BOOL getResult = JavascriptOperators::GetItem(obj, index, &element, scriptContext);
- Assert(getResult);
- accumulator = callBackFn->GetEntryPoint()(callBackFn, CallInfo(flags, 5), undefinedValue,
- accumulator,
- element,
- JavascriptNumber::ToVar(index, scriptContext),
- obj);
- }
- }
- return accumulator;
- }
- Var JavascriptArray::EntryFrom(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Array.from"));
- Assert(!(callInfo.Flags & CallFlags_New));
- JavascriptLibrary* library = scriptContext->GetLibrary();
- RecyclableObject* constructor = nullptr;
- if (JavascriptOperators::IsConstructor(args[0]))
- {
- constructor = RecyclableObject::FromVar(args[0]);
- }
- RecyclableObject* items = nullptr;
- if (args.Info.Count < 2 || !JavascriptConversion::ToObject(args[1], scriptContext, &items))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedObject, _u("Array.from"));
- }
- JavascriptArray* itemsArr = nullptr;
- if (JavascriptArray::Is(items))
- {
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(items);
- #endif
- itemsArr = JavascriptArray::FromVar(items);
- }
- bool mapping = false;
- JavascriptFunction* mapFn = nullptr;
- Var mapFnThisArg = nullptr;
- if (args.Info.Count >= 3 && !JavascriptOperators::IsUndefinedObject(args[2]))
- {
- if (!JavascriptFunction::Is(args[2]))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("Array.from"));
- }
- mapFn = JavascriptFunction::FromVar(args[2]);
- if (args.Info.Count >= 4)
- {
- mapFnThisArg = args[3];
- }
- else
- {
- mapFnThisArg = library->GetUndefined();
- }
- mapping = true;
- }
- RecyclableObject* newObj = nullptr;
- JavascriptArray* newArr = nullptr;
- RecyclableObject* iterator = JavascriptOperators::GetIterator(items, scriptContext, true /* optional */);
- if (iterator != nullptr)
- {
- if (constructor)
- {
- Js::Var constructorArgs[] = { constructor };
- Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs));
- newObj = RecyclableObject::FromVar(JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext));
- if (JavascriptArray::Is(newObj))
- {
- newArr = JavascriptArray::FromVar(newObj);
- }
- }
- else
- {
- newArr = scriptContext->GetLibrary()->CreateArray(0);
- newArr->EnsureHead<Var>();
- newObj = newArr;
- }
- Var nextValue;
- uint32 k = 0;
- while (JavascriptOperators::IteratorStepAndValue(iterator, scriptContext, &nextValue))
- {
- if (mapping)
- {
- Assert(mapFn != nullptr);
- Assert(mapFnThisArg != nullptr);
- Js::Var mapFnArgs[] = { mapFnThisArg, nextValue, JavascriptNumber::ToVar(k, scriptContext) };
- Js::CallInfo mapFnCallInfo(Js::CallFlags_Value, _countof(mapFnArgs));
- nextValue = mapFn->CallFunction(Js::Arguments(mapFnCallInfo, mapFnArgs));
- }
- if (newArr)
- {
- newArr->DirectSetItemAt(k, nextValue);
- }
- else
- {
- JavascriptArray::SetArrayLikeObjects(RecyclableObject::FromVar(newObj), k, nextValue);
- }
- k++;
- }
- JavascriptOperators::SetProperty(newObj, newObj, Js::PropertyIds::length, JavascriptNumber::ToVar(k, scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible);
- }
- else
- {
- Var lenValue = JavascriptOperators::OP_GetLength(items, scriptContext);
- int64 len = JavascriptConversion::ToLength(lenValue, scriptContext);
- if (constructor)
- {
- Js::Var constructorArgs[] = { constructor, JavascriptNumber::ToVar(len, scriptContext) };
- Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs));
- newObj = RecyclableObject::FromVar(JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext));
- if (JavascriptArray::Is(newObj))
- {
- newArr = JavascriptArray::FromVar(newObj);
- }
- }
- else
- {
- // Abstract operation ArrayCreate throws RangeError if length argument is > 2^32 -1
- if (len > MaxArrayLength)
- {
- JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthAssignIncorrect, _u("Array.from"));
- }
- // Static cast len should be valid (len < 2^32) or we would throw above
- newArr = scriptContext->GetLibrary()->CreateArray(static_cast<uint32>(len));
- newArr->EnsureHead<Var>();
- newObj = newArr;
- }
- uint32 k = 0;
- for ( ; k < len; k++)
- {
- Var kValue;
- if (itemsArr)
- {
- kValue = itemsArr->DirectGetItem(k);
- }
- else
- {
- kValue = JavascriptOperators::OP_GetElementI_UInt32(items, k, scriptContext);
- }
- if (mapping)
- {
- Assert(mapFn != nullptr);
- Assert(mapFnThisArg != nullptr);
- Js::Var mapFnArgs[] = { mapFnThisArg, kValue, JavascriptNumber::ToVar(k, scriptContext) };
- Js::CallInfo mapFnCallInfo(Js::CallFlags_Value, _countof(mapFnArgs));
- kValue = mapFn->CallFunction(Js::Arguments(mapFnCallInfo, mapFnArgs));
- }
- if (newArr)
- {
- newArr->DirectSetItemAt(k, kValue);
- }
- else
- {
- JavascriptArray::SetArrayLikeObjects(RecyclableObject::FromVar(newObj), k, kValue);
- }
- }
- JavascriptOperators::SetProperty(newObj, newObj, Js::PropertyIds::length, JavascriptNumber::ToVar(len, scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible);
- }
- return newObj;
- }
- Var JavascriptArray::EntryOf(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- if (args.Info.Count == 0)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.of"));
- }
- return JavascriptArray::OfHelper(false, args, scriptContext);
- }
- Var JavascriptArray::EntryGetterSymbolSpecies(RecyclableObject* function, CallInfo callInfo, ...)
- {
- ARGUMENTS(args, callInfo);
- Assert(args.Info.Count > 0);
- return args[0];
- }
- // Array.of and %TypedArray%.of as described in ES6.0 (draft 22) Section 22.1.2.2 and 22.2.2.2
- Var JavascriptArray::OfHelper(bool isTypedArrayEntryPoint, Arguments& args, ScriptContext* scriptContext)
- {
- Assert(args.Info.Count > 0);
- // args.Info.Count cannot equal zero or we would have thrown above so no chance of underflowing
- uint32 len = args.Info.Count - 1;
- Var newObj = nullptr;
- JavascriptArray* newArr = nullptr;
- TypedArrayBase* newTypedArray = nullptr;
- if (JavascriptOperators::IsConstructor(args[0]))
- {
- RecyclableObject* constructor = RecyclableObject::FromVar(args[0]);
- Js::Var constructorArgs[] = { constructor, JavascriptNumber::ToVar(len, scriptContext) };
- Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs));
- newObj = JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext);
- // If the new object we created is an array, remember that as it will save us time setting properties in the object below
- if (JavascriptArray::Is(newObj))
- {
- newArr = JavascriptArray::FromVar(newObj);
- }
- else if (TypedArrayBase::Is(newObj))
- {
- newTypedArray = TypedArrayBase::FromVar(newObj);
- }
- }
- else
- {
- // We only throw when the constructor property is not a constructor function in the TypedArray version
- if (isTypedArrayEntryPoint)
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedFunction, _u("[TypedArray].of"));
- }
- newArr = scriptContext->GetLibrary()->CreateArray(len);
- newArr->EnsureHead<Var>();
- newObj = newArr;
- }
- // At least we have a new object of some kind
- Assert(newObj);
- if (newArr)
- {
- for (uint32 k = 0; k < len; k++)
- {
- Var kValue = args[k + 1];
- newArr->DirectSetItemAt(k, kValue);
- }
- }
- else if (newTypedArray)
- {
- for (uint32 k = 0; k < len; k++)
- {
- Var kValue = args[k + 1];
- newTypedArray->DirectSetItem(k, kValue, false);
- }
- }
- else
- {
- for (uint32 k = 0; k < len; k++)
- {
- Var kValue = args[k + 1];
- JavascriptArray::SetArrayLikeObjects(RecyclableObject::FromVar(newObj), k, kValue);
- }
- }
- if (!isTypedArrayEntryPoint)
- {
- // Set length if we are in the Array version of the function
- JavascriptOperators::OP_SetProperty(newObj, Js::PropertyIds::length, JavascriptNumber::ToVar(len, scriptContext), scriptContext, nullptr, PropertyOperation_ThrowIfNotExtensible);
- }
- return newObj;
- }
- JavascriptString* JavascriptArray::ToLocaleStringHelper(Var value, ScriptContext* scriptContext)
- {
- TypeId typeId = JavascriptOperators::GetTypeId(value);
- if (typeId == TypeIds_Null || typeId == TypeIds_Undefined)
- {
- return scriptContext->GetLibrary()->GetEmptyString();
- }
- else
- {
- return JavascriptConversion::ToLocaleString(value, scriptContext);
- }
- }
- inline BOOL JavascriptArray::IsFullArray() const
- {
- if (head && head->length == length)
- {
- AssertMsg(head->next == 0 && head->left == 0, "Invalid Array");
- return true;
- }
- return (0 == length);
- }
- /*
- * IsFillFromPrototypes
- * - Check the array has no missing values and only head segment.
- * - Also ensure if the lengths match.
- */
- bool JavascriptArray::IsFillFromPrototypes()
- {
- return !(this->head->next == nullptr && this->HasNoMissingValues() && this->length == this->head->length);
- }
- // Fill all missing value in the array and fill it from prototype between startIndex and limitIndex
- // typically startIndex = 0 and limitIndex = length. From start of the array till end of the array.
- void JavascriptArray::FillFromPrototypes(uint32 startIndex, uint32 limitIndex)
- {
- if (startIndex >= limitIndex)
- {
- return;
- }
- RecyclableObject* prototype = this->GetPrototype();
- // Fill all missing values by walking through prototype
- while (JavascriptOperators::GetTypeId(prototype) != TypeIds_Null)
- {
- ForEachOwnMissingArrayIndexOfObject(this, nullptr, prototype, startIndex, limitIndex,0, [this](uint32 index, Var value) {
- this->SetItem(index, value, PropertyOperation_None);
- });
- prototype = prototype->GetPrototype();
- }
- #ifdef VALIDATE_ARRAY
- ValidateArray();
- #endif
- }
- //
- // JavascriptArray requires head->left == 0 for fast path Get.
- //
- template<typename T>
- void JavascriptArray::EnsureHeadStartsFromZero(Recycler * recycler)
- {
- if (head == nullptr || head->left != 0)
- {
- // This is used to fix up altered arrays.
- // any SegmentMap would be invalid at this point.
- ClearSegmentMap();
- //
- // We could OOM and throw when allocating new empty head, resulting in a corrupted array. Need
- // some protection here. Save the head and switch this array to EmptySegment. Will be restored
- // correctly if allocating new segment succeeds.
- //
- SparseArraySegment<T>* savedHead = (SparseArraySegment<T>*)this->head;
- SparseArraySegment<T>* savedLastUsedSegment = (SparseArraySegment<T>*)this->GetLastUsedSegment();
- SetHeadAndLastUsedSegment(const_cast<SparseArraySegmentBase*>(EmptySegment));
- SparseArraySegment<T> *newSeg = SparseArraySegment<T>::AllocateSegment(recycler, 0, 0, savedHead);
- newSeg->next = savedHead;
- this->head = newSeg;
- SetHasNoMissingValues();
- this->SetLastUsedSegment(savedLastUsedSegment);
- }
- }
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- void JavascriptArray::CheckForceES5Array()
- {
- if (Configuration::Global.flags.ForceES5Array)
- {
- // There's a bad interaction with the jitted code for native array creation here.
- // ForceES5Array doesn't interact well with native arrays
- if (PHASE_OFF1(NativeArrayPhase))
- {
- GetTypeHandler()->ConvertToTypeWithItemAttributes(this);
- }
- }
- }
- #endif
- template <typename Fn>
- void JavascriptArray::ForEachOwnArrayIndexOfObject(RecyclableObject* obj, uint32 startIndex, uint32 limitIndex, Fn fn)
- {
- Assert(DynamicObject::IsAnyArray(obj) || JavascriptOperators::IsObject(obj));
- JavascriptArray* arr = nullptr;
- if (DynamicObject::IsAnyArray(obj))
- {
- arr = JavascriptArray::FromAnyArray(obj);
- }
- else if (DynamicType::Is(obj->GetTypeId()))
- {
- DynamicObject* dynobj = DynamicObject::FromVar(obj);
- arr = dynobj->GetObjectArray();
- }
- if (arr != nullptr)
- {
- if (JavascriptArray::Is(arr))
- {
- ArrayElementEnumerator e(arr, startIndex, limitIndex);
- while(e.MoveNext<Var>())
- {
- fn(e.GetIndex(), e.GetItem<Var>());
- }
- }
- else
- {
- ScriptContext* scriptContext = obj->GetScriptContext();
- Assert(ES5Array::Is(arr));
- ES5Array* es5Array = ES5Array::FromVar(arr);
- ES5ArrayIndexEnumerator<true> e(es5Array);
- while (e.MoveNext())
- {
- uint32 index = e.GetIndex();
- if (index < startIndex) continue;
- else if (index >= limitIndex) break;
- Var value;
- BOOL success = JavascriptOperators::GetOwnItem(es5Array, index, &value, scriptContext);
- Assert(success);
- fn(index, value);
- }
- }
- }
- }
- template <typename T, typename Fn>
- void JavascriptArray::ForEachOwnMissingArrayIndexOfObject(JavascriptArray *baseArray, JavascriptArray *destArray, RecyclableObject* obj, uint32 startIndex, uint32 limitIndex, T destIndex, Fn fn)
- {
- Assert(DynamicObject::IsAnyArray(obj) || JavascriptOperators::IsObject(obj));
- Var oldValue;
- JavascriptArray* arr = nullptr;
- if (DynamicObject::IsAnyArray(obj))
- {
- arr = JavascriptArray::FromAnyArray(obj);
- }
- else if (DynamicType::Is(obj->GetTypeId()))
- {
- DynamicObject* dynobj = DynamicObject::FromVar(obj);
- ArrayObject* objectArray = dynobj->GetObjectArray();
- arr = (objectArray && JavascriptArray::IsAnyArray(objectArray)) ? JavascriptArray::FromAnyArray(objectArray) : nullptr;
- }
- if (arr != nullptr)
- {
- if (JavascriptArray::Is(arr))
- {
- ArrayElementEnumerator e(arr, startIndex, limitIndex);
- while(e.MoveNext<Var>())
- {
- uint32 index = e.GetIndex();
- if (!baseArray->DirectGetVarItemAt(index, &oldValue, baseArray->GetScriptContext()))
- {
- T n = destIndex + (index - startIndex);
- if (destArray == nullptr || !destArray->DirectGetItemAt(n, &oldValue))
- {
- fn(index, e.GetItem<Var>());
- }
- }
- }
- }
- else
- {
- ScriptContext* scriptContext = obj->GetScriptContext();
- Assert(ES5Array::Is(arr));
- ES5Array* es5Array = ES5Array::FromVar(arr);
- ES5ArrayIndexEnumerator<true> e(es5Array);
- while (e.MoveNext())
- {
- uint32 index = e.GetIndex();
- if (index < startIndex) continue;
- else if (index >= limitIndex) break;
- if (!baseArray->DirectGetVarItemAt(index, &oldValue, baseArray->GetScriptContext()))
- {
- T n = destIndex + (index - startIndex);
- if (destArray == nullptr || !destArray->DirectGetItemAt(n, &oldValue))
- {
- Var value;
- BOOL success = JavascriptOperators::GetOwnItem(es5Array, index, &value, scriptContext);
- Assert(success);
- fn(index, value);
- }
- }
- }
- }
- }
- }
- //
- // ArrayElementEnumerator to enumerate array elements (not including elements from prototypes).
- //
- JavascriptArray::ArrayElementEnumerator::ArrayElementEnumerator(JavascriptArray* arr, uint32 start, uint32 end)
- : start(start), end(min(end, arr->length))
- {
- Init(arr);
- }
- //
- // Initialize this enumerator and prepare for the first MoveNext.
- //
- void JavascriptArray::ArrayElementEnumerator::Init(JavascriptArray* arr)
- {
- // Find start segment
- seg = (arr ? arr->GetBeginLookupSegment(start) : nullptr);
- while (seg && (seg->left + seg->length <= start))
- {
- seg = seg->next;
- }
- // Set start index and endIndex
- if (seg)
- {
- if (seg->left >= end)
- {
- seg = nullptr;
- }
- else
- {
- // set index to be at target index - 1, so MoveNext will move to target
- index = max(seg->left, start) - seg->left - 1;
- endIndex = min(end - seg->left, seg->length);
- }
- }
- }
- //
- // Move to the next element if available.
- //
- template<typename T>
- inline bool JavascriptArray::ArrayElementEnumerator::MoveNext()
- {
- while (seg)
- {
- // Look for next non-null item in current segment
- while (++index < endIndex)
- {
- if (!SparseArraySegment<T>::IsMissingItem(&((SparseArraySegment<T>*)seg)->elements[index]))
- {
- return true;
- }
- }
- // Move to next segment
- seg = seg->next;
- if (seg)
- {
- if (seg->left >= end)
- {
- seg = nullptr;
- break;
- }
- else
- {
- index = static_cast<uint32>(-1);
- endIndex = min(end - seg->left, seg->length);
- }
- }
- }
- return false;
- }
- //
- // Get current array element index.
- //
- uint32 JavascriptArray::ArrayElementEnumerator::GetIndex() const
- {
- Assert(seg && index < seg->length && index < endIndex);
- return seg->left + index;
- }
- //
- // Get current array element value.
- //
- template<typename T>
- T JavascriptArray::ArrayElementEnumerator::GetItem() const
- {
- Assert(seg && index < seg->length && index < endIndex &&
- !SparseArraySegment<T>::IsMissingItem(&((SparseArraySegment<T>*)seg)->elements[index]));
- return ((SparseArraySegment<T>*)seg)->elements[index];
- }
- //
- // Construct a BigIndex initialized to a given uint32 (small index).
- //
- JavascriptArray::BigIndex::BigIndex(uint32 initIndex)
- : index(initIndex), bigIndex(InvalidIndex)
- {
- //ok if initIndex == InvalidIndex
- }
- //
- // Construct a BigIndex initialized to a given uint64 (large or small index).
- //
- JavascriptArray::BigIndex::BigIndex(uint64 initIndex)
- : index(InvalidIndex), bigIndex(initIndex)
- {
- if (bigIndex < InvalidIndex) // if it's actually small index
- {
- index = static_cast<uint32>(bigIndex);
- bigIndex = InvalidIndex;
- }
- }
- bool JavascriptArray::BigIndex::IsUint32Max() const
- {
- return index == InvalidIndex && bigIndex == InvalidIndex;
- }
- bool JavascriptArray::BigIndex::IsSmallIndex() const
- {
- return index < InvalidIndex;
- }
- uint32 JavascriptArray::BigIndex::GetSmallIndex() const
- {
- Assert(IsSmallIndex());
- return index;
- }
- uint64 JavascriptArray::BigIndex::GetBigIndex() const
- {
- Assert(!IsSmallIndex());
- return bigIndex;
- }
- //
- // Convert this index value to a JS number
- //
- Var JavascriptArray::BigIndex::ToNumber(ScriptContext* scriptContext) const
- {
- if (IsSmallIndex())
- {
- return small_index::ToNumber(index, scriptContext);
- }
- else
- {
- return JavascriptNumber::ToVar(bigIndex, scriptContext);
- }
- }
- //
- // Increment this index by 1.
- //
- const JavascriptArray::BigIndex& JavascriptArray::BigIndex::operator++()
- {
- if (IsSmallIndex())
- {
- ++index;
- // If index reaches InvalidIndex, we will start to use bigIndex which is initially InvalidIndex.
- }
- else
- {
- bigIndex = bigIndex + 1;
- }
- return *this;
- }
- //
- // Decrement this index by 1.
- //
- const JavascriptArray::BigIndex& JavascriptArray::BigIndex::operator--()
- {
- if (IsSmallIndex())
- {
- --index;
- }
- else
- {
- Assert(index == InvalidIndex && bigIndex >= InvalidIndex);
- --bigIndex;
- if (bigIndex < InvalidIndex)
- {
- index = InvalidIndex - 1;
- bigIndex = InvalidIndex;
- }
- }
- return *this;
- }
- JavascriptArray::BigIndex JavascriptArray::BigIndex::operator+(const BigIndex& delta) const
- {
- if (delta.IsSmallIndex())
- {
- return operator+(delta.GetSmallIndex());
- }
- if (IsSmallIndex())
- {
- return index + delta.GetBigIndex();
- }
- return bigIndex + delta.GetBigIndex();
- }
- //
- // Get a new BigIndex representing this + delta.
- //
- JavascriptArray::BigIndex JavascriptArray::BigIndex::operator+(uint32 delta) const
- {
- if (IsSmallIndex())
- {
- uint32 newIndex;
- if (UInt32Math::Add(index, delta, &newIndex))
- {
- return static_cast<uint64>(index) + static_cast<uint64>(delta);
- }
- else
- {
- return newIndex; // ok if newIndex == InvalidIndex
- }
- }
- else
- {
- return bigIndex + static_cast<uint64>(delta);
- }
- }
- bool JavascriptArray::BigIndex::operator==(const BigIndex& rhs) const
- {
- if (rhs.IsSmallIndex() && this->IsSmallIndex())
- {
- return this->GetSmallIndex() == rhs.GetSmallIndex();
- }
- else if (rhs.IsSmallIndex() && !this->IsSmallIndex())
- {
- // if lhs is big promote rhs
- return this->GetBigIndex() == (uint64) rhs.GetSmallIndex();
- }
- else if (!rhs.IsSmallIndex() && this->IsSmallIndex())
- {
- // if rhs is big promote lhs
- return ((uint64)this->GetSmallIndex()) == rhs.GetBigIndex();
- }
- return this->GetBigIndex() == rhs.GetBigIndex();
- }
- bool JavascriptArray::BigIndex::operator> (const BigIndex& rhs) const
- {
- if (rhs.IsSmallIndex() && this->IsSmallIndex())
- {
- return this->GetSmallIndex() > rhs.GetSmallIndex();
- }
- else if (rhs.IsSmallIndex() && !this->IsSmallIndex())
- {
- // if lhs is big promote rhs
- return this->GetBigIndex() > (uint64)rhs.GetSmallIndex();
- }
- else if (!rhs.IsSmallIndex() && this->IsSmallIndex())
- {
- // if rhs is big promote lhs
- return ((uint64)this->GetSmallIndex()) > rhs.GetBigIndex();
- }
- return this->GetBigIndex() > rhs.GetBigIndex();
- }
- bool JavascriptArray::BigIndex::operator< (const BigIndex& rhs) const
- {
- if (rhs.IsSmallIndex() && this->IsSmallIndex())
- {
- return this->GetSmallIndex() < rhs.GetSmallIndex();
- }
- else if (rhs.IsSmallIndex() && !this->IsSmallIndex())
- {
- // if lhs is big promote rhs
- return this->GetBigIndex() < (uint64)rhs.GetSmallIndex();
- }
- else if (!rhs.IsSmallIndex() && this->IsSmallIndex())
- {
- // if rhs is big promote lhs
- return ((uint64)this->GetSmallIndex()) < rhs.GetBigIndex();
- }
- return this->GetBigIndex() < rhs.GetBigIndex();
- }
- bool JavascriptArray::BigIndex::operator<=(const BigIndex& rhs) const
- {
- if (rhs.IsSmallIndex() && this->IsSmallIndex())
- {
- return this->GetSmallIndex() <= rhs.GetSmallIndex();
- }
- else if (rhs.IsSmallIndex() && !this->IsSmallIndex())
- {
- // if lhs is big promote rhs
- return this->GetBigIndex() <= (uint64)rhs.GetSmallIndex();
- }
- else if (!rhs.IsSmallIndex() && !this->IsSmallIndex())
- {
- // if rhs is big promote lhs
- return ((uint64)this->GetSmallIndex()) <= rhs.GetBigIndex();
- }
- return this->GetBigIndex() <= rhs.GetBigIndex();
- }
- bool JavascriptArray::BigIndex::operator>=(const BigIndex& rhs) const
- {
- if (rhs.IsSmallIndex() && this->IsSmallIndex())
- {
- return this->GetSmallIndex() >= rhs.GetSmallIndex();
- }
- else if (rhs.IsSmallIndex() && !this->IsSmallIndex())
- {
- // if lhs is big promote rhs
- return this->GetBigIndex() >= (uint64)rhs.GetSmallIndex();
- }
- else if (!rhs.IsSmallIndex() && this->IsSmallIndex())
- {
- // if rhs is big promote lhs
- return ((uint64)this->GetSmallIndex()) >= rhs.GetBigIndex();
- }
- return this->GetBigIndex() >= rhs.GetBigIndex();
- }
- BOOL JavascriptArray::BigIndex::GetItem(JavascriptArray* arr, Var* outVal) const
- {
- if (IsSmallIndex())
- {
- return small_index::GetItem(arr, index, outVal);
- }
- else
- {
- ScriptContext* scriptContext = arr->GetScriptContext();
- PropertyRecord const * propertyRecord;
- JavascriptOperators::GetPropertyIdForInt(bigIndex, scriptContext, &propertyRecord);
- return arr->GetProperty(arr, propertyRecord->GetPropertyId(), outVal, NULL, scriptContext);
- }
- }
- BOOL JavascriptArray::BigIndex::SetItem(JavascriptArray* arr, Var newValue) const
- {
- if (IsSmallIndex())
- {
- return small_index::SetItem(arr, index, newValue);
- }
- else
- {
- ScriptContext* scriptContext = arr->GetScriptContext();
- PropertyRecord const * propertyRecord;
- JavascriptOperators::GetPropertyIdForInt(bigIndex, scriptContext, &propertyRecord);
- return arr->SetProperty(propertyRecord->GetPropertyId(), newValue, PropertyOperation_None, NULL);
- }
- }
- void JavascriptArray::BigIndex::SetItemIfNotExist(JavascriptArray* arr, Var newValue) const
- {
- if (IsSmallIndex())
- {
- small_index::SetItemIfNotExist(arr, index, newValue);
- }
- else
- {
- ScriptContext* scriptContext = arr->GetScriptContext();
- PropertyRecord const * propertyRecord;
- JavascriptOperators::GetPropertyIdForInt(bigIndex, scriptContext, &propertyRecord);
- Var oldValue;
- PropertyId propertyId = propertyRecord->GetPropertyId();
- if (!arr->GetProperty(arr, propertyId, &oldValue, NULL, scriptContext))
- {
- arr->SetProperty(propertyId, newValue, PropertyOperation_None, NULL);
- }
- }
- }
- BOOL JavascriptArray::BigIndex::DeleteItem(JavascriptArray* arr) const
- {
- if (IsSmallIndex())
- {
- return small_index::DeleteItem(arr, index);
- }
- else
- {
- ScriptContext* scriptContext = arr->GetScriptContext();
- PropertyRecord const * propertyRecord;
- JavascriptOperators::GetPropertyIdForInt(bigIndex, scriptContext, &propertyRecord);
- return arr->DeleteProperty(propertyRecord->GetPropertyId(), PropertyOperation_None);
- }
- }
- BOOL JavascriptArray::BigIndex::SetItem(RecyclableObject* obj, Var newValue, PropertyOperationFlags flags) const
- {
- if (IsSmallIndex())
- {
- return small_index::SetItem(obj, index, newValue, flags);
- }
- else
- {
- ScriptContext* scriptContext = obj->GetScriptContext();
- PropertyRecord const * propertyRecord;
- JavascriptOperators::GetPropertyIdForInt(bigIndex, scriptContext, &propertyRecord);
- return JavascriptOperators::SetProperty(obj, obj, propertyRecord->GetPropertyId(), newValue, scriptContext, flags);
- }
- }
- BOOL JavascriptArray::BigIndex::DeleteItem(RecyclableObject* obj, PropertyOperationFlags flags) const
- {
- if (IsSmallIndex())
- {
- return small_index::DeleteItem(obj, index, flags);
- }
- else
- {
- PropertyRecord const * propertyRecord;
- JavascriptOperators::GetPropertyIdForInt(bigIndex, obj->GetScriptContext(), &propertyRecord);
- return JavascriptOperators::DeleteProperty(obj, propertyRecord->GetPropertyId(), flags);
- }
- }
- //
- // Truncate the array at start and clone the truncated span as properties starting at dstIndex (asserting dstIndex >= MaxArrayLength).
- //
- void JavascriptArray::TruncateToProperties(const BigIndex& dstIndex, uint32 start)
- {
- Assert(!dstIndex.IsSmallIndex());
- typedef IndexTrace<BigIndex> index_trace;
- BigIndex dst = dstIndex;
- uint32 i = start;
- ArrayElementEnumerator e(this, start);
- while(e.MoveNext<Var>())
- {
- // delete all items not enumerated
- while (i < e.GetIndex())
- {
- index_trace::DeleteItem(this, dst);
- ++i;
- ++dst;
- }
- // Copy over the item
- index_trace::SetItem(this, dst, e.GetItem<Var>());
- ++i;
- ++dst;
- }
- // Delete the rest till length
- while (i < this->length)
- {
- index_trace::DeleteItem(this, dst);
- ++i;
- ++dst;
- }
- // Elements moved, truncate the array at start
- SetLength(start);
- }
- //
- // Copy a srcArray elements (including elements from prototypes) to a dstArray starting from an index.
- //
- template<typename T>
- void JavascriptArray::InternalCopyArrayElements(JavascriptArray* dstArray, const T& dstIndex, JavascriptArray* srcArray, uint32 start, uint32 end)
- {
- Assert(start < end && end <= srcArray->length);
- uint32 count = 0;
- // iterate on the array itself
- ArrayElementEnumerator e(srcArray, start, end);
- while(e.MoveNext<Var>())
- {
- T n = dstIndex + (e.GetIndex() - start);
- dstArray->DirectSetItemAt(n, e.GetItem<Var>());
- count++;
- }
- // iterate on the array's prototypes only if not all elements found
- if (start + count != end)
- {
- InternalFillFromPrototype(dstArray, dstIndex, srcArray, start, end, count);
- }
- }
- //
- // Copy a srcArray elements (including elements from prototypes) to a dstArray starting from an index. If the index grows larger than
- // "array index", it will automatically turn to SetProperty using the index as property name.
- //
- void JavascriptArray::CopyArrayElements(JavascriptArray* dstArray, const BigIndex& dstIndex, JavascriptArray* srcArray, uint32 start, uint32 end)
- {
- end = min(end, srcArray->length);
- if (start < end)
- {
- uint32 len = end - start;
- if (dstIndex.IsSmallIndex() && (len < MaxArrayLength - dstIndex.GetSmallIndex()))
- {
- // Won't overflow, use faster small_index version
- InternalCopyArrayElements(dstArray, dstIndex.GetSmallIndex(), srcArray, start, end);
- }
- else
- {
- InternalCopyArrayElements(dstArray, dstIndex, srcArray, start, end);
- }
- }
- }
- //
- // Faster small_index overload of CopyArrayElements, asserting the uint32 dstIndex won't overflow.
- //
- void JavascriptArray::CopyArrayElements(JavascriptArray* dstArray, uint32 dstIndex, JavascriptArray* srcArray, uint32 start, uint32 end)
- {
- end = min(end, srcArray->length);
- if (start < end)
- {
- Assert(end - start <= MaxArrayLength - dstIndex);
- InternalCopyArrayElements(dstArray, dstIndex, srcArray, start, end);
- }
- }
- template <typename T>
- void JavascriptArray::CopyAnyArrayElementsToVar(JavascriptArray* dstArray, T dstIndex, JavascriptArray* srcArray, uint32 start, uint32 end)
- {
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(srcArray);
- #endif
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(dstArray);
- #endif
- if (JavascriptNativeIntArray::Is(srcArray))
- {
- CopyNativeIntArrayElementsToVar(dstArray, dstIndex, JavascriptNativeIntArray::FromVar(srcArray), start, end);
- }
- else if (JavascriptNativeFloatArray::Is(srcArray))
- {
- CopyNativeFloatArrayElementsToVar(dstArray, dstIndex, JavascriptNativeFloatArray::FromVar(srcArray), start, end);
- }
- else
- {
- CopyArrayElements(dstArray, dstIndex, srcArray, start, end);
- }
- }
- void JavascriptArray::CopyNativeIntArrayElementsToVar(JavascriptArray* dstArray, const BigIndex& dstIndex, JavascriptNativeIntArray* srcArray, uint32 start, uint32 end)
- {
- end = min(end, srcArray->length);
- if (start < end)
- {
- uint32 len = end - start;
- if (dstIndex.IsSmallIndex() && (len < MaxArrayLength - dstIndex.GetSmallIndex()))
- {
- // Won't overflow, use faster small_index version
- InternalCopyNativeIntArrayElements(dstArray, dstIndex.GetSmallIndex(), srcArray, start, end);
- }
- else
- {
- InternalCopyNativeIntArrayElements(dstArray, dstIndex, srcArray, start, end);
- }
- }
- }
- //
- // Faster small_index overload of CopyArrayElements, asserting the uint32 dstIndex won't overflow.
- //
- void JavascriptArray::CopyNativeIntArrayElementsToVar(JavascriptArray* dstArray, uint32 dstIndex, JavascriptNativeIntArray* srcArray, uint32 start, uint32 end)
- {
- end = min(end, srcArray->length);
- if (start < end)
- {
- Assert(end - start <= MaxArrayLength - dstIndex);
- InternalCopyNativeIntArrayElements(dstArray, dstIndex, srcArray, start, end);
- }
- }
- bool JavascriptArray::CopyNativeIntArrayElements(JavascriptNativeIntArray* dstArray, uint32 dstIndex, JavascriptNativeIntArray* srcArray, uint32 start, uint32 end)
- {
- end = min(end, srcArray->length);
- if (start >= end)
- {
- return false;
- }
- Assert(end - start <= MaxArrayLength - dstIndex);
- Assert(start < end && end <= srcArray->length);
- uint32 count = 0;
- // iterate on the array itself
- ArrayElementEnumerator e(srcArray, start, end);
- while(e.MoveNext<int32>())
- {
- uint n = dstIndex + (e.GetIndex() - start);
- dstArray->DirectSetItemAt(n, e.GetItem<int32>());
- count++;
- }
- // iterate on the array's prototypes only if not all elements found
- if (start + count != end)
- {
- JavascriptArray *varArray = JavascriptNativeIntArray::ToVarArray(dstArray);
- InternalFillFromPrototype(varArray, dstIndex, srcArray, start, end, count);
- return true;
- }
- return false;
- }
- bool JavascriptArray::CopyNativeIntArrayElementsToFloat(JavascriptNativeFloatArray* dstArray, uint32 dstIndex, JavascriptNativeIntArray* srcArray, uint32 start, uint32 end)
- {
- end = min(end, srcArray->length);
- if (start >= end)
- {
- return false;
- }
- Assert(end - start <= MaxArrayLength - dstIndex);
- Assert(start < end && end <= srcArray->length);
- uint32 count = 0;
- // iterate on the array itself
- ArrayElementEnumerator e(srcArray, start, end);
- while(e.MoveNext<int32>())
- {
- uint n = dstIndex + (e.GetIndex() - start);
- dstArray->DirectSetItemAt(n, (double)e.GetItem<int32>());
- count++;
- }
- // iterate on the array's prototypes only if not all elements found
- if (start + count != end)
- {
- JavascriptArray *varArray = JavascriptNativeFloatArray::ToVarArray(dstArray);
- InternalFillFromPrototype(varArray, dstIndex, srcArray, start, end, count);
- return true;
- }
- return false;
- }
- void JavascriptArray::CopyNativeFloatArrayElementsToVar(JavascriptArray* dstArray, const BigIndex& dstIndex, JavascriptNativeFloatArray* srcArray, uint32 start, uint32 end)
- {
- end = min(end, srcArray->length);
- if (start < end)
- {
- uint32 len = end - start;
- if (dstIndex.IsSmallIndex() && (len < MaxArrayLength - dstIndex.GetSmallIndex()))
- {
- // Won't overflow, use faster small_index version
- InternalCopyNativeFloatArrayElements(dstArray, dstIndex.GetSmallIndex(), srcArray, start, end);
- }
- else
- {
- InternalCopyNativeFloatArrayElements(dstArray, dstIndex, srcArray, start, end);
- }
- }
- }
- //
- // Faster small_index overload of CopyArrayElements, asserting the uint32 dstIndex won't overflow.
- //
- void JavascriptArray::CopyNativeFloatArrayElementsToVar(JavascriptArray* dstArray, uint32 dstIndex, JavascriptNativeFloatArray* srcArray, uint32 start, uint32 end)
- {
- end = min(end, srcArray->length);
- if (start < end)
- {
- Assert(end - start <= MaxArrayLength - dstIndex);
- InternalCopyNativeFloatArrayElements(dstArray, dstIndex, srcArray, start, end);
- }
- }
- bool JavascriptArray::CopyNativeFloatArrayElements(JavascriptNativeFloatArray* dstArray, uint32 dstIndex, JavascriptNativeFloatArray* srcArray, uint32 start, uint32 end)
- {
- end = min(end, srcArray->length);
- if (start >= end)
- {
- return false;
- }
- Assert(end - start <= MaxArrayLength - dstIndex);
- Assert(start < end && end <= srcArray->length);
- uint32 count = 0;
- // iterate on the array itself
- ArrayElementEnumerator e(srcArray, start, end);
- while(e.MoveNext<double>())
- {
- uint n = dstIndex + (e.GetIndex() - start);
- dstArray->DirectSetItemAt(n, e.GetItem<double>());
- count++;
- }
- // iterate on the array's prototypes only if not all elements found
- if (start + count != end)
- {
- JavascriptArray *varArray = JavascriptNativeFloatArray::ToVarArray(dstArray);
- InternalFillFromPrototype(varArray, dstIndex, srcArray, start, end, count);
- return true;
- }
- return false;
- }
- JavascriptArray *JavascriptArray::EnsureNonNativeArray(JavascriptArray *arr)
- {
- if (JavascriptNativeIntArray::Is(arr))
- {
- arr = JavascriptNativeIntArray::ToVarArray((JavascriptNativeIntArray*)arr);
- }
- else if (JavascriptNativeFloatArray::Is(arr))
- {
- arr = JavascriptNativeFloatArray::ToVarArray((JavascriptNativeFloatArray*)arr);
- }
- return arr;
- }
- BOOL JavascriptNativeIntArray::DirectGetItemAtFull(uint32 index, Var* outVal)
- {
- ScriptContext* requestContext = type->GetScriptContext();
- if (JavascriptNativeIntArray::GetItem(this, index, outVal, requestContext))
- {
- return TRUE;
- }
- return JavascriptOperators::GetItem(this, this->GetPrototype(), index, outVal, requestContext);
- }
- BOOL JavascriptNativeFloatArray::DirectGetItemAtFull(uint32 index, Var* outVal)
- {
- ScriptContext* requestContext = type->GetScriptContext();
- if (JavascriptNativeFloatArray::GetItem(this, index, outVal, requestContext))
- {
- return TRUE;
- }
- return JavascriptOperators::GetItem(this, this->GetPrototype(), index, outVal, requestContext);
- }
- template<typename T>
- void JavascriptArray::InternalCopyNativeIntArrayElements(JavascriptArray* dstArray, const T& dstIndex, JavascriptNativeIntArray* srcArray, uint32 start, uint32 end)
- {
- Assert(start < end && end <= srcArray->length);
- uint32 count = 0;
- // iterate on the array itself
- ScriptContext *scriptContext = dstArray->GetScriptContext();
- ArrayElementEnumerator e(srcArray, start, end);
- while(e.MoveNext<int32>())
- {
- T n = dstIndex + (e.GetIndex() - start);
- dstArray->DirectSetItemAt(n, JavascriptNumber::ToVar(e.GetItem<int32>(), scriptContext));
- count++;
- }
- // iterate on the array's prototypes only if not all elements found
- if (start + count != end)
- {
- InternalFillFromPrototype(dstArray, dstIndex, srcArray, start, end, count);
- }
- }
- template<typename T>
- void JavascriptArray::InternalCopyNativeFloatArrayElements(JavascriptArray* dstArray, const T& dstIndex, JavascriptNativeFloatArray* srcArray, uint32 start, uint32 end)
- {
- Assert(start < end && end <= srcArray->length);
- uint32 count = 0;
- // iterate on the array itself
- ScriptContext *scriptContext = dstArray->GetScriptContext();
- ArrayElementEnumerator e(srcArray, start, end);
- while(e.MoveNext<double>())
- {
- T n = dstIndex + (e.GetIndex() - start);
- dstArray->DirectSetItemAt(n, JavascriptNumber::ToVarWithCheck(e.GetItem<double>(), scriptContext));
- count++;
- }
- // iterate on the array's prototypes only if not all elements found
- if (start + count != end)
- {
- InternalFillFromPrototype(dstArray, dstIndex, srcArray, start, end, count);
- }
- }
- template<typename T>
- void JavascriptArray::InternalFillFromPrototype(JavascriptArray *dstArray, const T& dstIndex, JavascriptArray *srcArray, uint32 start, uint32 end, uint32 count)
- {
- RecyclableObject* prototype = srcArray->GetPrototype();
- while (start + count != end && JavascriptOperators::GetTypeId(prototype) != TypeIds_Null)
- {
- ForEachOwnMissingArrayIndexOfObject(srcArray, dstArray, prototype, start, end, dstIndex, [&](uint32 index, Var value) {
- T n = dstIndex + (index - start);
- dstArray->DirectSetItemAt(n, value);
- count++;
- });
- prototype = prototype->GetPrototype();
- }
- }
- Var JavascriptArray::SpreadArrayArgs(Var arrayToSpread, const Js::AuxArray<uint32> *spreadIndices, ScriptContext *scriptContext)
- {
- // At this stage we have an array literal with some arguments to be spread.
- // First we need to calculate the real size of the final literal.
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(arrayToSpread);
- #endif
- JavascriptArray *array = FromVar(arrayToSpread);
- uint32 actualLength = array->GetLength();
- for (unsigned i = 0; i < spreadIndices->count; ++i)
- {
- actualLength = UInt32Math::Add(actualLength - 1, GetSpreadArgLen(array->DirectGetItem(spreadIndices->elements[i]), scriptContext));
- }
- JavascriptArray *result = FromVar(OP_NewScArrayWithMissingValues(actualLength, scriptContext));
- // Now we copy each element and expand the spread parameters inline.
- for (unsigned i = 0, spreadArrIndex = 0, resultIndex = 0; i < array->GetLength() && resultIndex < actualLength; ++i)
- {
- uint32 spreadIndex = spreadIndices->elements[spreadArrIndex]; // The index of the next element to be spread.
- // An array needs a slow copy if it is a cross-site object or we have missing values that need to be set to undefined.
- auto needArraySlowCopy = [&](Var instance) {
- if (JavascriptArray::Is(instance))
- {
- JavascriptArray *arr = JavascriptArray::FromVar(instance);
- return arr->IsCrossSiteObject() || arr->IsFillFromPrototypes();
- }
- return false;
- };
- // Designed to have interchangeable arguments with CopyAnyArrayElementsToVar.
- auto slowCopy = [&scriptContext, &needArraySlowCopy](JavascriptArray *dstArray, unsigned dstIndex, Var srcArray, uint32 start, uint32 end) {
- Assert(needArraySlowCopy(srcArray) || ArgumentsObject::Is(srcArray) || TypedArrayBase::Is(srcArray) || JavascriptString::Is(srcArray));
- RecyclableObject *propertyObject;
- if (!JavascriptOperators::GetPropertyObject(srcArray, scriptContext, &propertyObject))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_InvalidSpreadArgument);
- }
- for (uint32 j = start; j < end; j++)
- {
- Var element;
- if (!JavascriptOperators::GetItem(srcArray, propertyObject, j, &element, scriptContext))
- {
- // Copy across missing values as undefined as per 12.2.5.2 SpreadElement : ... AssignmentExpression 5f.
- element = scriptContext->GetLibrary()->GetUndefined();
- }
- dstArray->DirectSetItemAt(dstIndex++, element);
- }
- };
- if (i < spreadIndex)
- {
- // Any non-spread elements can be copied in bulk.
- if (needArraySlowCopy(array))
- {
- slowCopy(result, resultIndex, (Var)array, i, spreadIndex);
- }
- else
- {
- CopyAnyArrayElementsToVar(result, resultIndex, array, i, spreadIndex);
- }
- resultIndex += spreadIndex - i;
- i = spreadIndex - 1;
- continue;
- }
- else if (i > spreadIndex)
- {
- // Any non-spread elements terminating the array can also be copied in bulk.
- Assert(spreadArrIndex == spreadIndices->count - 1);
- if (needArraySlowCopy(array))
- {
- slowCopy(result, resultIndex, array, i, array->GetLength());
- }
- else
- {
- CopyAnyArrayElementsToVar(result, resultIndex, array, i, array->GetLength());
- }
- break;
- }
- else
- {
- Var instance = array->DirectGetItem(i);
- if (SpreadArgument::Is(instance))
- {
- SpreadArgument* spreadArgument = SpreadArgument::FromVar(instance);
- uint32 len = spreadArgument->GetArgumentSpreadCount();
- const Var* spreadItems = spreadArgument->GetArgumentSpread();
- for (uint32 j = 0; j < len; j++)
- {
- result->DirectSetItemAt(resultIndex++, spreadItems[j]);
- }
- }
- else
- {
- AssertMsg(JavascriptArray::Is(instance) || TypedArrayBase::Is(instance), "Only SpreadArgument, TypedArray, and JavascriptArray should be listed as spread arguments");
- // We first try to interpret the spread parameter as a JavascriptArray.
- JavascriptArray *arr = nullptr;
- if (JavascriptArray::Is(instance))
- {
- arr = JavascriptArray::FromVar(instance);
- }
- if (arr != nullptr)
- {
- if (arr->GetLength() > 0)
- {
- if (needArraySlowCopy(arr))
- {
- slowCopy(result, resultIndex, arr, 0, arr->GetLength());
- }
- else
- {
- CopyAnyArrayElementsToVar(result, resultIndex, arr, 0, arr->GetLength());
- }
- resultIndex += arr->GetLength();
- }
- }
- else
- {
- uint32 len = GetSpreadArgLen(instance, scriptContext);
- slowCopy(result, resultIndex, instance, 0, len);
- resultIndex += len;
- }
- }
- if (spreadArrIndex < spreadIndices->count - 1)
- {
- spreadArrIndex++;
- }
- }
- }
- return result;
- }
- uint32 JavascriptArray::GetSpreadArgLen(Var spreadArg, ScriptContext *scriptContext)
- {
- // A spread argument can be anything that returns a 'length' property, even if that
- // property is null or undefined.
- spreadArg = CrossSite::MarshalVar(scriptContext, spreadArg);
- if (JavascriptArray::Is(spreadArg))
- {
- JavascriptArray *arr = JavascriptArray::FromVar(spreadArg);
- return arr->GetLength();
- }
- if (TypedArrayBase::Is(spreadArg))
- {
- TypedArrayBase *tarr = TypedArrayBase::FromVar(spreadArg);
- return tarr->GetLength();
- }
- if (SpreadArgument::Is(spreadArg))
- {
- SpreadArgument *spreadFunctionArgs = SpreadArgument::FromVar(spreadArg);
- return spreadFunctionArgs->GetArgumentSpreadCount();
- }
- AssertMsg(false, "LdCustomSpreadIteratorList should have converted the arg to one of the above types");
- Throw::FatalInternalError();
- }
- #ifdef VALIDATE_ARRAY
- class ArraySegmentsVisitor
- {
- private:
- SparseArraySegmentBase* seg;
- public:
- ArraySegmentsVisitor(SparseArraySegmentBase* head)
- : seg(head)
- {
- }
- void operator()(SparseArraySegmentBase* s)
- {
- Assert(seg == s);
- if (seg)
- {
- seg = seg->next;
- }
- }
- };
- void JavascriptArray::ValidateArrayCommon()
- {
- SparseArraySegmentBase * lastUsedSegment = this->GetLastUsedSegment();
- AssertMsg(this != nullptr && head && lastUsedSegment, "Array should not be null");
- AssertMsg(head->left == 0, "Array always should have a segment starting at zero");
- // Simple segments validation
- bool foundLastUsedSegment = false;
- SparseArraySegmentBase *seg = head;
- while(seg != nullptr)
- {
- if (seg == lastUsedSegment)
- {
- foundLastUsedSegment = true;
- }
- AssertMsg(seg->length <= seg->size , "Length greater than size not possible");
- SparseArraySegmentBase* next = seg->next;
- if (next != nullptr)
- {
- AssertMsg(seg->left < next->left, "Segment is adjacent to or overlaps with next segment");
- AssertMsg(seg->size <= (next->left - seg->left), "Segment is adjacent to or overlaps with next segment");
- AssertMsg(!SparseArraySegmentBase::IsLeafSegment(seg, this->GetScriptContext()->GetRecycler()), "Leaf segment with a next pointer");
- }
- else
- {
- AssertMsg(seg->length <= MaxArrayLength - seg->left, "Segment index range overflow");
- AssertMsg(seg->left + seg->length <= this->length, "Segment index range exceeds array length");
- }
- seg = next;
- }
- AssertMsg(foundLastUsedSegment || HasSegmentMap(), "Corrupt lastUsedSegment in array header");
- // Validate segmentMap if present
- if (HasSegmentMap())
- {
- ArraySegmentsVisitor visitor(head);
- GetSegmentMap()->Walk(visitor);
- }
- }
- void JavascriptArray::ValidateArray()
- {
- if (!Js::Configuration::Global.flags.ArrayValidate)
- {
- return;
- }
- ValidateArrayCommon();
- // Detailed segments validation
- JavascriptArray::ValidateVarSegment((SparseArraySegment<Var>*)head);
- }
- void JavascriptNativeIntArray::ValidateArray()
- {
- if (!Js::Configuration::Global.flags.ArrayValidate)
- {
- #if DBG
- SparseArraySegmentBase *seg = head;
- while (seg)
- {
- if (seg->next != nullptr)
- {
- AssertMsg(!SparseArraySegmentBase::IsLeafSegment(seg, this->GetScriptContext()->GetRecycler()), "Leaf segment with a next pointer");
- }
- seg = seg->next;
- }
- #endif
- return;
- }
- ValidateArrayCommon();
- // Detailed segments validation
- JavascriptArray::ValidateSegment<int32>((SparseArraySegment<int32>*)head);
- }
- void JavascriptNativeFloatArray::ValidateArray()
- {
- if (!Js::Configuration::Global.flags.ArrayValidate)
- {
- #if DBG
- SparseArraySegmentBase *seg = head;
- while (seg)
- {
- if (seg->next != nullptr)
- {
- AssertMsg(!SparseArraySegmentBase::IsLeafSegment(seg, this->GetScriptContext()->GetRecycler()), "Leaf segment with a next pointer");
- }
- seg = seg->next;
- }
- #endif
- return;
- }
- ValidateArrayCommon();
- // Detailed segments validation
- JavascriptArray::ValidateSegment<double>((SparseArraySegment<double>*)head);
- }
- void JavascriptArray::ValidateVarSegment(SparseArraySegment<Var>* seg)
- {
- if (!Js::Configuration::Global.flags.ArrayValidate)
- {
- return;
- }
- int32 inspect;
- double inspectDouble;
- while (seg)
- {
- uint32 i = 0;
- for (i = 0; i < seg->length; i++)
- {
- if (SparseArraySegment<Var>::IsMissingItem(&seg->elements[i]))
- {
- continue;
- }
- if (TaggedInt::Is(seg->elements[i]))
- {
- inspect = TaggedInt::ToInt32(seg->elements[i]);
- }
- else if (JavascriptNumber::Is_NoTaggedIntCheck(seg->elements[i]))
- {
- inspectDouble = JavascriptNumber::GetValue(seg->elements[i]);
- }
- else
- {
- AssertMsg(RecyclableObject::Is(seg->elements[i]), "Invalid entry in segment");
- }
- }
- ValidateSegment(seg);
- seg = (SparseArraySegment<Var>*)seg->next;
- }
- }
- template<typename T>
- void JavascriptArray::ValidateSegment(SparseArraySegment<T>* seg)
- {
- if (!Js::Configuration::Global.flags.ArrayValidate)
- {
- return;
- }
- while (seg)
- {
- uint32 i = seg->length;
- while (i < seg->size)
- {
- AssertMsg(SparseArraySegment<T>::IsMissingItem(&seg->elements[i]), "Non missing value the end of the segment");
- i++;
- }
- seg = (SparseArraySegment<T>*)seg->next;
- }
- }
- #endif
- template <typename T>
- void JavascriptArray::InitBoxedInlineHeadSegment(SparseArraySegment<T> * dst, SparseArraySegment<T> * src)
- {
- // Don't copy the segment map, we will build it again
- SetFlags(GetFlags() & ~DynamicObjectFlags::HasSegmentMap);
- SetHeadAndLastUsedSegment(dst);
- dst->left = src->left;
- dst->length = src->length;
- dst->size = src->size;
- dst->next = src->next;
- js_memcpy_s(dst->elements, sizeof(T) * dst->size, src->elements, sizeof(T) * src->size);
- }
- JavascriptArray::JavascriptArray(JavascriptArray * instance, bool boxHead)
- : ArrayObject(instance)
- {
- if (boxHead)
- {
- InitBoxedInlineHeadSegment(DetermineInlineHeadSegmentPointer<JavascriptArray, 0, true>(this), (SparseArraySegment<Var>*)instance->head);
- }
- else
- {
- SetFlags(GetFlags() & ~DynamicObjectFlags::HasSegmentMap);
- head = instance->head;
- SetLastUsedSegment(instance->GetLastUsedSegment());
- }
- }
- template <typename T>
- T * JavascriptArray::BoxStackInstance(T * instance)
- {
- Assert(ThreadContext::IsOnStack(instance));
- // On the stack, the we reserved a pointer before the object as to store the boxed value
- T ** boxedInstanceRef = ((T **)instance) - 1;
- T * boxedInstance = *boxedInstanceRef;
- if (boxedInstance)
- {
- return boxedInstance;
- }
- const size_t inlineSlotsSize = instance->GetTypeHandler()->GetInlineSlotsSize();
- if (ThreadContext::IsOnStack(instance->head))
- {
- boxedInstance = RecyclerNewPlusZ(instance->GetRecycler(),
- inlineSlotsSize + sizeof(Js::SparseArraySegmentBase) + instance->head->size * sizeof(typename T::TElement),
- T, instance, true);
- }
- else if(inlineSlotsSize)
- {
- boxedInstance = RecyclerNewPlusZ(instance->GetRecycler(), inlineSlotsSize, T, instance, false);
- }
- else
- {
- boxedInstance = RecyclerNew(instance->GetRecycler(), T, instance, false);
- }
- *boxedInstanceRef = boxedInstance;
- return boxedInstance;
- }
- JavascriptArray *
- JavascriptArray::BoxStackInstance(JavascriptArray * instance)
- {
- return BoxStackInstance<JavascriptArray>(instance);
- }
- JavascriptNativeArray::JavascriptNativeArray(JavascriptNativeArray * instance) :
- JavascriptArray(instance, false),
- weakRefToFuncBody(instance->weakRefToFuncBody)
- {
- }
- JavascriptNativeIntArray::JavascriptNativeIntArray(JavascriptNativeIntArray * instance, bool boxHead) :
- JavascriptNativeArray(instance)
- {
- if (boxHead)
- {
- InitBoxedInlineHeadSegment(DetermineInlineHeadSegmentPointer<JavascriptNativeIntArray, 0, true>(this), (SparseArraySegment<int>*)instance->head);
- }
- else
- {
- // Base class ctor should have copied these
- Assert(head == instance->head);
- Assert(segmentUnion.lastUsedSegment == instance->GetLastUsedSegment());
- }
- }
- JavascriptNativeIntArray *
- JavascriptNativeIntArray::BoxStackInstance(JavascriptNativeIntArray * instance)
- {
- return JavascriptArray::BoxStackInstance<JavascriptNativeIntArray>(instance);
- }
- JavascriptNativeFloatArray::JavascriptNativeFloatArray(JavascriptNativeFloatArray * instance, bool boxHead) :
- JavascriptNativeArray(instance)
- {
- if (boxHead)
- {
- InitBoxedInlineHeadSegment(DetermineInlineHeadSegmentPointer<JavascriptNativeFloatArray, 0, true>(this), (SparseArraySegment<double>*)instance->head);
- }
- else
- {
- // Base class ctor should have copied these
- Assert(head == instance->head);
- Assert(segmentUnion.lastUsedSegment == instance->GetLastUsedSegment());
- }
- }
- JavascriptNativeFloatArray *
- JavascriptNativeFloatArray::BoxStackInstance(JavascriptNativeFloatArray * instance)
- {
- return JavascriptArray::BoxStackInstance<JavascriptNativeFloatArray>(instance);
- }
- template<typename T>
- RecyclableObject*
- JavascriptArray::ArraySpeciesCreate(Var originalArray, T length, ScriptContext* scriptContext, bool* pIsIntArray, bool* pIsFloatArray)
- {
- if (originalArray == nullptr || !scriptContext->GetConfig()->IsES6SpeciesEnabled())
- {
- return nullptr;
- }
- if (JavascriptArray::Is(originalArray)
- && !DynamicObject::FromVar(originalArray)->GetDynamicType()->GetTypeHandler()->GetIsNotPathTypeHandlerOrHasUserDefinedCtor()
- && DynamicObject::FromVar(originalArray)->GetPrototype() == scriptContext->GetLibrary()->GetArrayPrototype()
- && !scriptContext->GetLibrary()->GetArrayObjectHasUserDefinedSpecies())
- {
- return nullptr;
- }
- Var constructor = scriptContext->GetLibrary()->GetUndefined();
- if (JavascriptOperators::IsArray(originalArray))
- {
- if (!JavascriptOperators::GetProperty(RecyclableObject::FromVar(originalArray), PropertyIds::constructor, &constructor, scriptContext))
- {
- return nullptr;
- }
- if (JavascriptOperators::IsConstructor(constructor))
- {
- ScriptContext* constructorScriptContext = RecyclableObject::FromVar(constructor)->GetScriptContext();
- if (constructorScriptContext != scriptContext)
- {
- if (constructorScriptContext->GetLibrary()->GetArrayConstructor() == constructor)
- {
- constructor = scriptContext->GetLibrary()->GetUndefined();
- }
- }
- }
- if (JavascriptOperators::IsObject(constructor))
- {
- if (!JavascriptOperators::GetProperty((RecyclableObject*)constructor, PropertyIds::_symbolSpecies, &constructor, scriptContext))
- {
- return nullptr;
- }
- if (constructor == scriptContext->GetLibrary()->GetNull())
- {
- constructor = scriptContext->GetLibrary()->GetUndefined();
- }
- }
- }
- if (constructor == scriptContext->GetLibrary()->GetUndefined() || constructor == scriptContext->GetLibrary()->GetArrayConstructor())
- {
- if (length > UINT_MAX)
- {
- JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthConstructIncorrect);
- }
- if (nullptr == pIsIntArray)
- {
- return scriptContext->GetLibrary()->CreateArray(static_cast<uint32>(length));
- }
- else
- {
- // If the constructor function is the built-in Array constructor, we can be smart and create the right type of native array.
- JavascriptArray* pArr = JavascriptArray::FromVar(originalArray);
- pArr->GetArrayTypeAndConvert(pIsIntArray, pIsFloatArray);
- return CreateNewArrayHelper(static_cast<uint32>(length), *pIsIntArray, *pIsFloatArray, pArr, scriptContext);
- }
- }
- if (!JavascriptOperators::IsConstructor(constructor))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_NotAConstructor, _u("constructor[Symbol.species]"));
- }
- Js::Var constructorArgs[] = { constructor, JavascriptNumber::ToVar(length, scriptContext) };
- Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs));
- return RecyclableObject::FromVar(JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext));
- }
- /*static*/
- PropertyId const JavascriptArray::specialPropertyIds[] =
- {
- PropertyIds::length
- };
- BOOL JavascriptArray::DeleteProperty(PropertyId propertyId, PropertyOperationFlags flags)
- {
- if (propertyId == PropertyIds::length)
- {
- return false;
- }
- return DynamicObject::DeleteProperty(propertyId, flags);
- }
- BOOL JavascriptArray::HasProperty(PropertyId propertyId)
- {
- if (propertyId == PropertyIds::length)
- {
- return true;
- }
- ScriptContext* scriptContext = GetScriptContext();
- uint32 index;
- if (scriptContext->IsNumericPropertyId(propertyId, &index))
- {
- return this->HasItem(index);
- }
- return DynamicObject::HasProperty(propertyId);
- }
- BOOL JavascriptArray::IsEnumerable(PropertyId propertyId)
- {
- if (propertyId == PropertyIds::length)
- {
- return false;
- }
- return DynamicObject::IsEnumerable(propertyId);
- }
- BOOL JavascriptArray::IsConfigurable(PropertyId propertyId)
- {
- if (propertyId == PropertyIds::length)
- {
- return false;
- }
- return DynamicObject::IsConfigurable(propertyId);
- }
- //
- // Evolve typeHandlers explicitly so that simple typeHandlers can skip array
- // handling and only check instance objectArray for numeric propertyIds.
- //
- BOOL JavascriptArray::SetEnumerable(PropertyId propertyId, BOOL value)
- {
- if (propertyId == PropertyIds::length)
- {
- Assert(!value); // Can't change array length enumerable
- return true;
- }
- ScriptContext* scriptContext = this->GetScriptContext();
- uint32 index;
- if (scriptContext->IsNumericPropertyId(propertyId, &index))
- {
- return GetTypeHandler()->ConvertToTypeWithItemAttributes(this)
- ->SetEnumerable(this, propertyId, value);
- }
- return __super::SetEnumerable(propertyId, value);
- }
- //
- // Evolve typeHandlers explicitly so that simple typeHandlers can skip array
- // handling and only check instance objectArray for numeric propertyIds.
- //
- BOOL JavascriptArray::SetWritable(PropertyId propertyId, BOOL value)
- {
- ScriptContext* scriptContext = this->GetScriptContext();
- uint32 index;
- bool setLengthNonWritable = (propertyId == PropertyIds::length && !value);
- if (setLengthNonWritable || scriptContext->IsNumericPropertyId(propertyId, &index))
- {
- return GetTypeHandler()->ConvertToTypeWithItemAttributes(this)
- ->SetWritable(this, propertyId, value);
- }
- return __super::SetWritable(propertyId, value);
- }
- //
- // Evolve typeHandlers explicitly so that simple typeHandlers can skip array
- // handling and only check instance objectArray for numeric propertyIds.
- //
- BOOL JavascriptArray::SetConfigurable(PropertyId propertyId, BOOL value)
- {
- if (propertyId == PropertyIds::length)
- {
- Assert(!value); // Can't change array length configurable
- return true;
- }
- ScriptContext* scriptContext = this->GetScriptContext();
- uint32 index;
- if (scriptContext->IsNumericPropertyId(propertyId, &index))
- {
- return GetTypeHandler()->ConvertToTypeWithItemAttributes(this)
- ->SetConfigurable(this, propertyId, value);
- }
- return __super::SetConfigurable(propertyId, value);
- }
- //
- // Evolve typeHandlers explicitly so that simple typeHandlers can skip array
- // handling and only check instance objectArray for numeric propertyIds.
- //
- BOOL JavascriptArray::SetAttributes(PropertyId propertyId, PropertyAttributes attributes)
- {
- ScriptContext* scriptContext = this->GetScriptContext();
- // SetAttributes on "length" is not expected. DefineOwnProperty uses SetWritable. If this is
- // changed, we need to handle it here.
- Assert(propertyId != PropertyIds::length);
- uint32 index;
- if (scriptContext->IsNumericPropertyId(propertyId, &index))
- {
- return GetTypeHandler()->ConvertToTypeWithItemAttributes(this)
- ->SetItemAttributes(this, index, attributes);
- }
- return __super::SetAttributes(propertyId, attributes);
- }
- //
- // Evolve typeHandlers explicitly so that simple typeHandlers can skip array
- // handling and only check instance objectArray for numeric propertyIds.
- //
- BOOL JavascriptArray::SetAccessors(PropertyId propertyId, Var getter, Var setter, PropertyOperationFlags flags)
- {
- ScriptContext* scriptContext = this->GetScriptContext();
- uint32 index;
- if (scriptContext->IsNumericPropertyId(propertyId, &index))
- {
- return GetTypeHandler()->ConvertToTypeWithItemAttributes(this)
- ->SetItemAccessors(this, index, getter, setter);
- }
- return __super::SetAccessors(propertyId, getter, setter, flags);
- }
- //
- // Evolve typeHandlers explicitly so that simple typeHandlers can skip array
- // handling and only check instance objectArray for numeric propertyIds.
- //
- BOOL JavascriptArray::SetItemWithAttributes(uint32 index, Var value, PropertyAttributes attributes)
- {
- return GetTypeHandler()->ConvertToTypeWithItemAttributes(this)
- ->SetItemWithAttributes(this, index, value, attributes);
- }
- //
- // Evolve typeHandlers explicitly so that simple typeHandlers can skip array
- // handling and only check instance objectArray for numeric propertyIds.
- //
- BOOL JavascriptArray::SetItemAttributes(uint32 index, PropertyAttributes attributes)
- {
- return GetTypeHandler()->ConvertToTypeWithItemAttributes(this)
- ->SetItemAttributes(this, index, attributes);
- }
- //
- // Evolve typeHandlers explicitly so that simple typeHandlers can skip array
- // handling and only check instance objectArray for numeric propertyIds.
- //
- BOOL JavascriptArray::SetItemAccessors(uint32 index, Var getter, Var setter)
- {
- return GetTypeHandler()->ConvertToTypeWithItemAttributes(this)
- ->SetItemAccessors(this, index, getter, setter);
- }
- // Check if this objectArray isFrozen.
- BOOL JavascriptArray::IsObjectArrayFrozen()
- {
- // If this is still a JavascriptArray, it's not frozen.
- return false;
- }
- BOOL JavascriptArray::GetEnumerator(Var originalInstance, BOOL enumNonEnumerable, Var* enumerator, ScriptContext* requestContext, bool preferSnapshotSemantics, bool enumSymbols)
- {
- // JavascriptArray does not support accessors, discard originalInstance.
- return JavascriptArray::GetEnumerator(enumNonEnumerable, enumerator, requestContext, preferSnapshotSemantics, enumSymbols);
- }
- BOOL JavascriptArray::GetNonIndexEnumerator(Var* enumerator, ScriptContext* requestContext)
- {
- *enumerator = RecyclerNew(GetScriptContext()->GetRecycler(), JavascriptArrayNonIndexSnapshotEnumerator, this, requestContext, false);
- return true;
- }
- BOOL JavascriptArray::IsItemEnumerable(uint32 index)
- {
- return true;
- }
- //
- // Evolve typeHandlers explicitly so that simple typeHandlers can skip array
- // handling and only check instance objectArray for numeric propertyIds.
- //
- BOOL JavascriptArray::PreventExtensions()
- {
- return GetTypeHandler()->ConvertToTypeWithItemAttributes(this)->PreventExtensions(this);
- }
- //
- // Evolve typeHandlers explicitly so that simple typeHandlers can skip array
- // handling and only check instance objectArray for numeric propertyIds.
- //
- BOOL JavascriptArray::Seal()
- {
- return GetTypeHandler()->ConvertToTypeWithItemAttributes(this)->Seal(this);
- }
- //
- // Evolve typeHandlers explicitly so that simple typeHandlers can skip array
- // handling and only check instance objectArray for numeric propertyIds.
- //
- BOOL JavascriptArray::Freeze()
- {
- return GetTypeHandler()->ConvertToTypeWithItemAttributes(this)->Freeze(this);
- }
- BOOL JavascriptArray::GetSpecialPropertyName(uint32 index, Var *propertyName, ScriptContext * requestContext)
- {
- if (index == 0)
- {
- *propertyName = requestContext->GetPropertyString(PropertyIds::length);
- return true;
- }
- return false;
- }
- // Returns the number of special non-enumerable properties this type has.
- uint JavascriptArray::GetSpecialPropertyCount() const
- {
- return _countof(specialPropertyIds);
- }
- // Returns the list of special non-enumerable properties for the type.
- PropertyId const * JavascriptArray::GetSpecialPropertyIds() const
- {
- return specialPropertyIds;
- }
- BOOL JavascriptArray::GetPropertyReference(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
- {
- return JavascriptArray::GetProperty(originalInstance, propertyId, value, info, requestContext);
- }
- BOOL JavascriptArray::GetProperty(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
- {
- if (GetPropertyBuiltIns(propertyId, value))
- {
- return true;
- }
- ScriptContext* scriptContext = GetScriptContext();
- uint32 index;
- if (scriptContext->IsNumericPropertyId(propertyId, &index))
- {
- return this->GetItem(this, index, value, scriptContext);
- }
- return DynamicObject::GetProperty(originalInstance, propertyId, value, info, requestContext);
- }
- BOOL JavascriptArray::GetProperty(Var originalInstance, JavascriptString* propertyNameString, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
- {
- AssertMsg(!PropertyRecord::IsPropertyNameNumeric(propertyNameString->GetString(), propertyNameString->GetLength()),
- "Numeric property names should have been converted to uint or PropertyRecord*");
- PropertyRecord const* propertyRecord;
- this->GetScriptContext()->FindPropertyRecord(propertyNameString, &propertyRecord);
- if (propertyRecord != nullptr && GetPropertyBuiltIns(propertyRecord->GetPropertyId(), value))
- {
- return true;
- }
- return DynamicObject::GetProperty(originalInstance, propertyNameString, value, info, requestContext);
- }
- BOOL JavascriptArray::GetPropertyBuiltIns(PropertyId propertyId, Var* value)
- {
- //
- // length being accessed. Return array length
- //
- if (propertyId == PropertyIds::length)
- {
- *value = JavascriptNumber::ToVar(this->GetLength(), GetScriptContext());
- return true;
- }
- return false;
- }
- BOOL JavascriptArray::HasItem(uint32 index)
- {
- Var value;
- return this->DirectGetItemAt<Var>(index, &value);
- }
- BOOL JavascriptArray::GetItem(Var originalInstance, uint32 index, Var* value, ScriptContext* requestContext)
- {
- return this->DirectGetItemAt<Var>(index, value);
- }
- BOOL JavascriptArray::GetItemReference(Var originalInstance, uint32 index, Var* value, ScriptContext* requestContext)
- {
- return this->DirectGetItemAt<Var>(index, value);
- }
- BOOL JavascriptArray::DirectGetVarItemAt(uint32 index, Var *value, ScriptContext *requestContext)
- {
- return this->DirectGetItemAt<Var>(index, value);
- }
- BOOL JavascriptNativeIntArray::HasItem(uint32 index)
- {
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(this);
- #endif
- int32 value;
- return this->DirectGetItemAt<int32>(index, &value);
- }
- BOOL JavascriptNativeFloatArray::HasItem(uint32 index)
- {
- double dvalue;
- return this->DirectGetItemAt<double>(index, &dvalue);
- }
- BOOL JavascriptNativeIntArray::GetItem(Var originalInstance, uint32 index, Var* value, ScriptContext* requestContext)
- {
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(this);
- #endif
- return JavascriptNativeIntArray::DirectGetVarItemAt(index, value, requestContext);
- }
- BOOL JavascriptNativeIntArray::DirectGetVarItemAt(uint32 index, Var *value, ScriptContext *requestContext)
- {
- int32 intvalue;
- if (!this->DirectGetItemAt<int32>(index, &intvalue))
- {
- return FALSE;
- }
- *value = JavascriptNumber::ToVar(intvalue, requestContext);
- return TRUE;
- }
- BOOL JavascriptNativeIntArray::GetItemReference(Var originalInstance, uint32 index, Var* value, ScriptContext* requestContext)
- {
- return JavascriptNativeIntArray::GetItem(originalInstance, index, value, requestContext);
- }
- BOOL JavascriptNativeFloatArray::GetItem(Var originalInstance, uint32 index, Var* value, ScriptContext* requestContext)
- {
- return JavascriptNativeFloatArray::DirectGetVarItemAt(index, value, requestContext);
- }
- BOOL JavascriptNativeFloatArray::DirectGetVarItemAt(uint32 index, Var *value, ScriptContext *requestContext)
- {
- double dvalue;
- int32 ivalue;
- if (!this->DirectGetItemAt<double>(index, &dvalue))
- {
- return FALSE;
- }
- if (*(uint64*)&dvalue == 0ull)
- {
- *value = TaggedInt::ToVarUnchecked(0);
- }
- else if (JavascriptNumber::TryGetInt32Value(dvalue, &ivalue) && !TaggedInt::IsOverflow(ivalue))
- {
- *value = TaggedInt::ToVarUnchecked(ivalue);
- }
- else
- {
- *value = JavascriptNumber::ToVarWithCheck(dvalue, requestContext);
- }
- return TRUE;
- }
- BOOL JavascriptNativeFloatArray::GetItemReference(Var originalInstance, uint32 index, Var* value, ScriptContext* requestContext)
- {
- return JavascriptNativeFloatArray::GetItem(originalInstance, index, value, requestContext);
- }
- BOOL JavascriptArray::SetProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
- {
- uint32 indexValue;
- if (propertyId == PropertyIds::length)
- {
- return this->SetLength(value);
- }
- else if (GetScriptContext()->IsNumericPropertyId(propertyId, &indexValue))
- {
- // Call this or subclass method
- return SetItem(indexValue, value, flags);
- }
- else
- {
- return DynamicObject::SetProperty(propertyId, value, flags, info);
- }
- }
- BOOL JavascriptArray::SetProperty(JavascriptString* propertyNameString, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
- {
- AssertMsg(!PropertyRecord::IsPropertyNameNumeric(propertyNameString->GetString(), propertyNameString->GetLength()),
- "Numeric property names should have been converted to uint or PropertyRecord*");
- PropertyRecord const* propertyRecord;
- this->GetScriptContext()->FindPropertyRecord(propertyNameString, &propertyRecord);
- if (propertyRecord != nullptr && propertyRecord->GetPropertyId() == PropertyIds::length)
- {
- return this->SetLength(value);
- }
- return DynamicObject::SetProperty(propertyNameString, value, flags, info);
- }
- BOOL JavascriptArray::SetPropertyWithAttributes(PropertyId propertyId, Var value, PropertyAttributes attributes, PropertyValueInfo* info, PropertyOperationFlags flags, SideEffects possibleSideEffects)
- {
- ScriptContext* scriptContext = GetScriptContext();
- if (propertyId == PropertyIds::length)
- {
- Assert(attributes == PropertyWritable);
- Assert(IsWritable(propertyId) && !IsConfigurable(propertyId) && !IsEnumerable(propertyId));
- return this->SetLength(value);
- }
- uint32 index;
- if (scriptContext->IsNumericPropertyId(propertyId, &index))
- {
- // Call this or subclass method
- return SetItemWithAttributes(index, value, attributes);
- }
- return __super::SetPropertyWithAttributes(propertyId, value, attributes, info, flags, possibleSideEffects);
- }
- BOOL JavascriptArray::SetItem(uint32 index, Var value, PropertyOperationFlags flags)
- {
- this->DirectSetItemAt(index, value);
- return true;
- }
- BOOL JavascriptNativeIntArray::SetItem(uint32 index, Var value, PropertyOperationFlags flags)
- {
- int32 iValue;
- double dValue;
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(this);
- #endif
- TypeId typeId = this->TrySetNativeIntArrayItem(value, &iValue, &dValue);
- if (typeId == TypeIds_NativeIntArray)
- {
- this->SetItem(index, iValue);
- }
- else if (typeId == TypeIds_NativeFloatArray)
- {
- reinterpret_cast<JavascriptNativeFloatArray*>(this)->DirectSetItemAt<double>(index, dValue);
- }
- else
- {
- this->DirectSetItemAt<Var>(index, value);
- }
- return TRUE;
- }
- TypeId JavascriptNativeIntArray::TrySetNativeIntArrayItem(Var value, int32 *iValue, double *dValue)
- {
- if (TaggedInt::Is(value))
- {
- int32 i = TaggedInt::ToInt32(value);
- if (i != JavascriptNativeIntArray::MissingItem)
- {
- *iValue = i;
- return TypeIds_NativeIntArray;
- }
- }
- if (JavascriptNumber::Is_NoTaggedIntCheck(value))
- {
- bool isInt32;
- int32 i;
- double d = JavascriptNumber::GetValue(value);
- if (JavascriptNumber::TryGetInt32OrUInt32Value(d, &i, &isInt32))
- {
- if (isInt32 && i != JavascriptNativeIntArray::MissingItem)
- {
- *iValue = i;
- return TypeIds_NativeIntArray;
- }
- }
- else
- {
- *dValue = d;
- JavascriptNativeIntArray::ToNativeFloatArray(this);
- return TypeIds_NativeFloatArray;
- }
- }
- JavascriptNativeIntArray::ToVarArray(this);
- return TypeIds_Array;
- }
- BOOL JavascriptNativeIntArray::SetItem(uint32 index, int32 iValue)
- {
- if (iValue == JavascriptNativeIntArray::MissingItem)
- {
- JavascriptArray *varArr = JavascriptNativeIntArray::ToVarArray(this);
- varArr->DirectSetItemAt(index, JavascriptNumber::ToVar(iValue, GetScriptContext()));
- return TRUE;
- }
- this->DirectSetItemAt(index, iValue);
- return TRUE;
- }
- BOOL JavascriptNativeFloatArray::SetItem(uint32 index, Var value, PropertyOperationFlags flags)
- {
- double dValue;
- TypeId typeId = this->TrySetNativeFloatArrayItem(value, &dValue);
- if (typeId == TypeIds_NativeFloatArray)
- {
- this->SetItem(index, dValue);
- }
- else
- {
- this->DirectSetItemAt(index, value);
- }
- return TRUE;
- }
- TypeId JavascriptNativeFloatArray::TrySetNativeFloatArrayItem(Var value, double *dValue)
- {
- if (TaggedInt::Is(value))
- {
- *dValue = (double)TaggedInt::ToInt32(value);
- return TypeIds_NativeFloatArray;
- }
- else if (JavascriptNumber::Is_NoTaggedIntCheck(value))
- {
- *dValue = JavascriptNumber::GetValue(value);
- return TypeIds_NativeFloatArray;
- }
- JavascriptNativeFloatArray::ToVarArray(this);
- return TypeIds_Array;
- }
- BOOL JavascriptNativeFloatArray::SetItem(uint32 index, double dValue)
- {
- if (*(uint64*)&dValue == *(uint64*)&JavascriptNativeFloatArray::MissingItem)
- {
- JavascriptArray *varArr = JavascriptNativeFloatArray::ToVarArray(this);
- varArr->DirectSetItemAt(index, JavascriptNumber::ToVarNoCheck(dValue, GetScriptContext()));
- return TRUE;
- }
- this->DirectSetItemAt<double>(index, dValue);
- return TRUE;
- }
- BOOL JavascriptArray::DeleteItem(uint32 index, PropertyOperationFlags flags)
- {
- return this->DirectDeleteItemAt<Var>(index);
- }
- BOOL JavascriptNativeIntArray::DeleteItem(uint32 index, PropertyOperationFlags flags)
- {
- return this->DirectDeleteItemAt<int32>(index);
- }
- BOOL JavascriptNativeFloatArray::DeleteItem(uint32 index, PropertyOperationFlags flags)
- {
- return this->DirectDeleteItemAt<double>(index);
- }
- BOOL JavascriptArray::GetEnumerator(BOOL enumNonEnumerable, Var* enumerator, ScriptContext * requestContext, bool preferSnapshotSemantics, bool enumSymbols)
- {
- if (preferSnapshotSemantics)
- {
- *enumerator = RecyclerNew(GetRecycler(), JavascriptArraySnapshotEnumerator, this, requestContext, enumNonEnumerable, enumSymbols);
- }
- else
- {
- *enumerator = RecyclerNew(GetRecycler(), JavascriptArrayEnumerator, this, requestContext, enumNonEnumerable, enumSymbols);
- }
- return true;
- }
- BOOL JavascriptArray::GetDiagValueString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext)
- {
- stringBuilder->Append(_u('['));
- if (this->length < 10)
- {
- BEGIN_JS_RUNTIME_CALL(requestContext);
- {
- ENTER_PINNED_SCOPE(JavascriptString, valueStr);
- valueStr = JavascriptArray::JoinHelper(this, GetLibrary()->GetCommaDisplayString(), requestContext);
- stringBuilder->Append(valueStr->GetString(), valueStr->GetLength());
- LEAVE_PINNED_SCOPE();
- }
- END_JS_RUNTIME_CALL(requestContext);
- }
- else
- {
- stringBuilder->AppendCppLiteral(_u("..."));
- }
- stringBuilder->Append(_u(']'));
- return TRUE;
- }
- BOOL JavascriptArray::GetDiagTypeString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext)
- {
- stringBuilder->AppendCppLiteral(_u("Object, (Array)"));
- return TRUE;
- }
- bool JavascriptNativeArray::Is(Var aValue)
- {
- TypeId typeId = JavascriptOperators::GetTypeId(aValue);
- return JavascriptNativeArray::Is(typeId);
- }
- bool JavascriptNativeArray::Is(TypeId typeId)
- {
- return JavascriptNativeIntArray::Is(typeId) || JavascriptNativeFloatArray::Is(typeId);
- }
- JavascriptNativeArray* JavascriptNativeArray::FromVar(Var aValue)
- {
- AssertMsg(Is(aValue), "Ensure var is actually a 'JavascriptNativeArray'");
- return static_cast<JavascriptNativeArray *>(RecyclableObject::FromVar(aValue));
- }
- bool JavascriptNativeIntArray::Is(Var aValue)
- {
- TypeId typeId = JavascriptOperators::GetTypeId(aValue);
- return JavascriptNativeIntArray::Is(typeId);
- }
- #if ENABLE_COPYONACCESS_ARRAY
- bool JavascriptCopyOnAccessNativeIntArray::Is(Var aValue)
- {
- TypeId typeId = JavascriptOperators::GetTypeId(aValue);
- return JavascriptCopyOnAccessNativeIntArray::Is(typeId);
- }
- #endif
- bool JavascriptNativeIntArray::Is(TypeId typeId)
- {
- return typeId == TypeIds_NativeIntArray;
- }
- #if ENABLE_COPYONACCESS_ARRAY
- bool JavascriptCopyOnAccessNativeIntArray::Is(TypeId typeId)
- {
- return typeId == TypeIds_CopyOnAccessNativeIntArray;
- }
- #endif
- bool JavascriptNativeIntArray::IsNonCrossSite(Var aValue)
- {
- bool ret = !TaggedInt::Is(aValue) && VirtualTableInfo<JavascriptNativeIntArray>::HasVirtualTable(aValue);
- Assert(ret == (JavascriptNativeIntArray::Is(aValue) && !JavascriptNativeIntArray::FromVar(aValue)->IsCrossSiteObject()));
- return ret;
- }
- JavascriptNativeIntArray* JavascriptNativeIntArray::FromVar(Var aValue)
- {
- AssertMsg(Is(aValue), "Ensure var is actually a 'JavascriptNativeIntArray'");
- return static_cast<JavascriptNativeIntArray *>(RecyclableObject::FromVar(aValue));
- }
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptCopyOnAccessNativeIntArray* JavascriptCopyOnAccessNativeIntArray::FromVar(Var aValue)
- {
- AssertMsg(Is(aValue), "Ensure var is actually a 'JavascriptCopyOnAccessNativeIntArray'");
- return static_cast<JavascriptCopyOnAccessNativeIntArray *>(RecyclableObject::FromVar(aValue));
- }
- #endif
- bool JavascriptNativeFloatArray::Is(Var aValue)
- {
- TypeId typeId = JavascriptOperators::GetTypeId(aValue);
- return JavascriptNativeFloatArray::Is(typeId);
- }
- bool JavascriptNativeFloatArray::Is(TypeId typeId)
- {
- return typeId == TypeIds_NativeFloatArray;
- }
- bool JavascriptNativeFloatArray::IsNonCrossSite(Var aValue)
- {
- bool ret = !TaggedInt::Is(aValue) && VirtualTableInfo<JavascriptNativeFloatArray>::HasVirtualTable(aValue);
- Assert(ret == (JavascriptNativeFloatArray::Is(aValue) && !JavascriptNativeFloatArray::FromVar(aValue)->IsCrossSiteObject()));
- return ret;
- }
- JavascriptNativeFloatArray* JavascriptNativeFloatArray::FromVar(Var aValue)
- {
- AssertMsg(Is(aValue), "Ensure var is actually a 'JavascriptNativeFloatArray'");
- return static_cast<JavascriptNativeFloatArray *>(RecyclableObject::FromVar(aValue));
- }
- template int Js::JavascriptArray::GetParamForIndexOf<unsigned int>(unsigned int, Js::Arguments const&, void*&, unsigned int&, Js::ScriptContext*);
- template bool Js::JavascriptArray::ArrayElementEnumerator::MoveNext<void*>();
- template void Js::JavascriptArray::SetArrayLiteralItem<void*>(unsigned int, void*);
- template void* Js::JavascriptArray::TemplatedIndexOfHelper<false, Js::TypedArrayBase, unsigned int>(Js::TypedArrayBase*, void*, unsigned int, unsigned int, Js::ScriptContext*);
- template void* Js::JavascriptArray::TemplatedIndexOfHelper<true, Js::TypedArrayBase, unsigned int>(Js::TypedArrayBase*, void*, unsigned int, unsigned int, Js::ScriptContext*);
- } //namespace Js
|