JavascriptArray.cpp 460 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "RuntimeLibraryPch.h"
  6. #include "Types/PathTypeHandler.h"
  7. #include "Types/SpreadArgument.h"
  8. namespace Js
  9. {
  10. // Make sure EmptySegment points to read-only memory.
  11. // Can't do this the easy way because SparseArraySegment has a constructor...
  12. static const char EmptySegmentData[sizeof(SparseArraySegmentBase)] = {0};
  13. const SparseArraySegmentBase *JavascriptArray::EmptySegment = (SparseArraySegmentBase *)&EmptySegmentData;
  14. #if defined(_M_X64_OR_ARM64)
  15. const Var JavascriptArray::MissingItem = (Var)0x8000000280000002;
  16. #else
  17. const Var JavascriptArray::MissingItem = (Var)0x80000002;
  18. #endif
  19. const int32 JavascriptNativeIntArray::MissingItem = 0x80000002;
  20. static const uint64 FloatMissingItemPattern = 0x8000000280000002ull;
  21. const double JavascriptNativeFloatArray::MissingItem = *(double*)&FloatMissingItemPattern;
  22. // Allocate enough space for 4 inline property slots and 16 inline element slots
  23. const size_t JavascriptArray::StackAllocationSize = DetermineAllocationSize<JavascriptArray, 4>(16);
  24. const size_t JavascriptNativeIntArray::StackAllocationSize = DetermineAllocationSize<JavascriptNativeIntArray, 4>(16);
  25. const size_t JavascriptNativeFloatArray::StackAllocationSize = DetermineAllocationSize<JavascriptNativeFloatArray, 4>(16);
  26. SegmentBTree::SegmentBTree()
  27. : segmentCount(0),
  28. segments(NULL),
  29. keys(NULL),
  30. children(NULL)
  31. {
  32. }
  33. uint32 SegmentBTree::GetLazyCrossOverLimit()
  34. {
  35. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  36. if (Js::Configuration::Global.flags.DisableArrayBTree)
  37. {
  38. return Js::JavascriptArray::InvalidIndex;
  39. }
  40. else if (Js::Configuration::Global.flags.ForceArrayBTree)
  41. {
  42. return ARRAY_CROSSOVER_FOR_VALIDATE;
  43. }
  44. #endif
  45. #ifdef VALIDATE_ARRAY
  46. if (Js::Configuration::Global.flags.ArrayValidate)
  47. {
  48. return ARRAY_CROSSOVER_FOR_VALIDATE;
  49. }
  50. #endif
  51. return SegmentBTree::MinDegree * 3;
  52. }
  53. BOOL SegmentBTree::IsLeaf() const
  54. {
  55. return children == NULL;
  56. }
  57. BOOL SegmentBTree::IsFullNode() const
  58. {
  59. return segmentCount == MaxKeys;
  60. }
  61. void SegmentBTree::InternalFind(SegmentBTree* node, uint32 itemIndex, SparseArraySegmentBase*& prev, SparseArraySegmentBase*& matchOrNext)
  62. {
  63. uint32 i = 0;
  64. for(; i < node->segmentCount; i++)
  65. {
  66. Assert(node->keys[i] == node->segments[i]->left);
  67. if (itemIndex < node->keys[i])
  68. {
  69. break;
  70. }
  71. }
  72. // i indicates the 1st segment in the node past any matching segment.
  73. // the i'th child is the children to the 'left' of the i'th segment.
  74. // If itemIndex matches segment i-1 (note that left is always a match even when length == 0)
  75. bool matches = i > 0 && (itemIndex == node->keys[i-1] || itemIndex < node->keys[i-1] + node->segments[i-1]->length);
  76. if (matches)
  77. {
  78. // Find prev segment
  79. if (node->IsLeaf())
  80. {
  81. if (i > 1)
  82. {
  83. // Previous is either sibling or set in a parent
  84. prev = node->segments[i-2];
  85. }
  86. }
  87. else
  88. {
  89. // prev is the right most leaf in children[i-1] tree
  90. SegmentBTree* child = &node->children[i - 1];
  91. while (!child->IsLeaf())
  92. {
  93. child = &child->children[child->segmentCount];
  94. }
  95. prev = child->segments[child->segmentCount - 1];
  96. }
  97. // Return the matching segment
  98. matchOrNext = node->segments[i-1];
  99. }
  100. else // itemIndex in between segment i-1 and i
  101. {
  102. if (i > 0)
  103. {
  104. // Store in previous in case a match or next is the first segment in a child.
  105. prev = node->segments[i-1];
  106. }
  107. if (node->IsLeaf())
  108. {
  109. matchOrNext = (i == 0 ? node->segments[0] : prev->next);
  110. }
  111. else
  112. {
  113. InternalFind(node->children + i, itemIndex, prev, matchOrNext);
  114. }
  115. }
  116. }
  117. void SegmentBTreeRoot::Find(uint32 itemIndex, SparseArraySegmentBase*& prev, SparseArraySegmentBase*& matchOrNext)
  118. {
  119. prev = matchOrNext = NULL;
  120. InternalFind(this, itemIndex, prev, matchOrNext);
  121. Assert(prev == NULL || (prev->next == matchOrNext));// If prev exists it is immediately before matchOrNext in the list of arraysegments
  122. Assert(prev == NULL || (prev->left < itemIndex && prev->left + prev->length <= itemIndex)); // prev should never be a match (left is a match if length == 0)
  123. Assert(matchOrNext == NULL || (matchOrNext->left >= itemIndex || matchOrNext->left + matchOrNext->length > itemIndex));
  124. }
  125. void SegmentBTreeRoot::Add(Recycler* recycler, SparseArraySegmentBase* newSeg)
  126. {
  127. if (IsFullNode())
  128. {
  129. SegmentBTree * children = AllocatorNewArrayZ(Recycler, recycler, SegmentBTree, MaxDegree);
  130. children[0] = *this;
  131. // Even though the segments point to a GC pointer, the main array should keep a references
  132. // as well. So just make it a leaf allocation
  133. this->segmentCount = 0;
  134. this->segments = AllocatorNewArrayLeafZ(Recycler, recycler, SparseArraySegmentBase*, MaxKeys);
  135. this->keys = AllocatorNewArrayLeafZ(Recycler,recycler,uint32,MaxKeys);
  136. this->children = children;
  137. // This split is the only way the tree gets deeper
  138. SplitChild(recycler, this, 0, &children[0]);
  139. }
  140. InsertNonFullNode(recycler, this, newSeg);
  141. }
  142. void SegmentBTree::SwapSegment(uint32 originalKey, SparseArraySegmentBase* oldSeg, SparseArraySegmentBase* newSeg)
  143. {
  144. // Find old segment
  145. uint32 itemIndex = originalKey;
  146. uint32 i = 0;
  147. for(; i < segmentCount; i++)
  148. {
  149. Assert(keys[i] == segments[i]->left || (oldSeg == newSeg && newSeg == segments[i]));
  150. if (itemIndex < keys[i])
  151. {
  152. break;
  153. }
  154. }
  155. // i is 1 past any match
  156. if (i > 0)
  157. {
  158. if (oldSeg == segments[i-1])
  159. {
  160. segments[i-1] = newSeg;
  161. keys[i-1] = newSeg->left;
  162. return;
  163. }
  164. }
  165. Assert(!IsLeaf());
  166. children[i].SwapSegment(originalKey, oldSeg, newSeg);
  167. }
  168. void SegmentBTree::SplitChild(Recycler* recycler, SegmentBTree* parent, uint32 iChild, SegmentBTree* child)
  169. {
  170. // Split child in two, move it's median key up to parent, and put the result of the split
  171. // on either side of the key moved up into parent
  172. Assert(child != NULL);
  173. Assert(parent != NULL);
  174. Assert(!parent->IsFullNode());
  175. Assert(child->IsFullNode());
  176. SegmentBTree newNode;
  177. newNode.segmentCount = MinKeys;
  178. // Even though the segments point to a GC pointer, the main array should keep a references
  179. // as well. So just make it a leaf allocation
  180. newNode.segments = AllocatorNewArrayLeafZ(Recycler, recycler, SparseArraySegmentBase*, MaxKeys);
  181. newNode.keys = AllocatorNewArrayLeafZ(Recycler,recycler,uint32,MaxKeys);
  182. // Move the keys above the median into the new node
  183. for(uint32 i = 0; i < MinKeys; i++)
  184. {
  185. newNode.segments[i] = child->segments[i+MinDegree];
  186. newNode.keys[i] = child->keys[i+MinDegree];
  187. // Do not leave false positive references around in the b-tree
  188. child->segments[i+MinDegree] = NULL;
  189. }
  190. // If children exist move those as well.
  191. if (!child->IsLeaf())
  192. {
  193. newNode.children = AllocatorNewArrayZ(Recycler, recycler, SegmentBTree, MaxDegree);
  194. for(uint32 j = 0; j < MinDegree; j++)
  195. {
  196. newNode.children[j] = child->children[j+MinDegree];
  197. // Do not leave false positive references around in the b-tree
  198. child->children[j+MinDegree].segments = NULL;
  199. child->children[j+MinDegree].children = NULL;
  200. }
  201. }
  202. child->segmentCount = MinKeys;
  203. // Make room for the new child in parent
  204. for(uint32 j = parent->segmentCount; j > iChild; j--)
  205. {
  206. parent->children[j+1] = parent->children[j];
  207. }
  208. // Copy the contents of the new node into the correct place in the parent's child array
  209. parent->children[iChild+1] = newNode;
  210. // Move the keys to make room for the median key
  211. for(uint32 k = parent->segmentCount; k > iChild; k--)
  212. {
  213. parent->segments[k] = parent->segments[k-1];
  214. parent->keys[k] = parent->keys[k-1];
  215. }
  216. // Move the median key into the proper place in the parent node
  217. parent->segments[iChild] = child->segments[MinKeys];
  218. parent->keys[iChild] = child->keys[MinKeys];
  219. // Do not leave false positive references around in the b-tree
  220. child->segments[MinKeys] = NULL;
  221. parent->segmentCount++;
  222. }
  223. void SegmentBTree::InsertNonFullNode(Recycler* recycler, SegmentBTree* node, SparseArraySegmentBase* newSeg)
  224. {
  225. Assert(!node->IsFullNode());
  226. AnalysisAssert(node->segmentCount < MaxKeys); // Same as !node->IsFullNode()
  227. Assert(newSeg != NULL);
  228. if (node->IsLeaf())
  229. {
  230. // Move the keys
  231. uint32 i = node->segmentCount - 1;
  232. while( (i != -1) && (newSeg->left < node->keys[i]))
  233. {
  234. node->segments[i+1] = node->segments[i];
  235. node->keys[i+1] = node->keys[i];
  236. i--;
  237. }
  238. if (!node->segments)
  239. {
  240. // Even though the segments point to a GC pointer, the main array should keep a references
  241. // as well. So just make it a leaf allocation
  242. node->segments = AllocatorNewArrayLeafZ(Recycler, recycler, SparseArraySegmentBase*, MaxKeys);
  243. node->keys = AllocatorNewArrayLeafZ(Recycler, recycler, uint32, MaxKeys);
  244. }
  245. node->segments[i + 1] = newSeg;
  246. node->keys[i + 1] = newSeg->left;
  247. node->segmentCount++;
  248. }
  249. else
  250. {
  251. // find the correct child node
  252. uint32 i = node->segmentCount-1;
  253. while((i != -1) && (newSeg->left < node->keys[i]))
  254. {
  255. i--;
  256. }
  257. i++;
  258. // Make room if full
  259. if(node->children[i].IsFullNode())
  260. {
  261. // This split doesn't make the tree any deeper as node already has children.
  262. SplitChild(recycler, node, i, node->children+i);
  263. Assert(node->keys[i] == node->segments[i]->left);
  264. if (newSeg->left > node->keys[i])
  265. {
  266. i++;
  267. }
  268. }
  269. InsertNonFullNode(recycler, node->children+i, newSeg);
  270. }
  271. }
  272. inline void ThrowTypeErrorOnFailureHelper::ThrowTypeErrorOnFailure(BOOL operationSucceeded)
  273. {
  274. if (IsThrowTypeError(operationSucceeded))
  275. {
  276. ThrowTypeErrorOnFailure();
  277. }
  278. }
  279. inline void ThrowTypeErrorOnFailureHelper::ThrowTypeErrorOnFailure()
  280. {
  281. JavascriptError::ThrowTypeError(m_scriptContext, VBSERR_ActionNotSupported, m_functionName);
  282. }
  283. inline BOOL ThrowTypeErrorOnFailureHelper::IsThrowTypeError(BOOL operationSucceeded)
  284. {
  285. return !operationSucceeded;
  286. }
  287. // Make sure EmptySegment points to read-only memory.
  288. // Can't do this the easy way because SparseArraySegment has a constructor...
  289. JavascriptArray::JavascriptArray(DynamicType * type)
  290. : ArrayObject(type, false, 0)
  291. {
  292. 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()));
  293. Assert(EmptySegment->length == 0 && EmptySegment->size == 0 && EmptySegment->next == NULL);
  294. InitArrayFlags(DynamicObjectFlags::InitialArrayValue);
  295. SetHeadAndLastUsedSegment(const_cast<SparseArraySegmentBase *>(EmptySegment));
  296. }
  297. JavascriptArray::JavascriptArray(uint32 length, DynamicType * type)
  298. : ArrayObject(type, false, length)
  299. {
  300. Assert(JavascriptArray::Is(type->GetTypeId()));
  301. Assert(EmptySegment->length == 0 && EmptySegment->size == 0 && EmptySegment->next == NULL);
  302. InitArrayFlags(DynamicObjectFlags::InitialArrayValue);
  303. SetHeadAndLastUsedSegment(const_cast<SparseArraySegmentBase *>(EmptySegment));
  304. }
  305. JavascriptArray::JavascriptArray(uint32 length, uint32 size, DynamicType * type)
  306. : ArrayObject(type, false, length)
  307. {
  308. Assert(type->GetTypeId() == TypeIds_Array);
  309. InitArrayFlags(DynamicObjectFlags::InitialArrayValue);
  310. Recycler* recycler = GetRecycler();
  311. SetHeadAndLastUsedSegment(SparseArraySegment<Var>::AllocateSegment(recycler, 0, 0, size, nullptr));
  312. }
  313. JavascriptArray::JavascriptArray(DynamicType * type, uint32 size)
  314. : ArrayObject(type, false)
  315. {
  316. InitArrayFlags(DynamicObjectFlags::InitialArrayValue);
  317. SetHeadAndLastUsedSegment(DetermineInlineHeadSegmentPointer<JavascriptArray, 0, false>(this));
  318. head->size = size;
  319. Var fill = Js::JavascriptArray::MissingItem;
  320. for (uint i = 0; i < size; i++)
  321. {
  322. ((SparseArraySegment<Var>*)head)->elements[i] = fill;
  323. }
  324. }
  325. JavascriptNativeIntArray::JavascriptNativeIntArray(uint32 length, uint32 size, DynamicType * type)
  326. : JavascriptNativeArray(type)
  327. {
  328. Assert(type->GetTypeId() == TypeIds_NativeIntArray);
  329. this->length = length;
  330. Recycler* recycler = GetRecycler();
  331. SetHeadAndLastUsedSegment(SparseArraySegment<int32>::AllocateSegment(recycler, 0, 0, size, nullptr));
  332. }
  333. JavascriptNativeIntArray::JavascriptNativeIntArray(DynamicType * type, uint32 size)
  334. : JavascriptNativeArray(type)
  335. {
  336. SetHeadAndLastUsedSegment(DetermineInlineHeadSegmentPointer<JavascriptNativeIntArray, 0, false>(this));
  337. head->size = size;
  338. ((SparseArraySegment<int32>*)head)->FillSegmentBuffer(0, size);
  339. }
  340. JavascriptNativeFloatArray::JavascriptNativeFloatArray(uint32 length, uint32 size, DynamicType * type)
  341. : JavascriptNativeArray(type)
  342. {
  343. Assert(type->GetTypeId() == TypeIds_NativeFloatArray);
  344. this->length = length;
  345. Recycler* recycler = GetRecycler();
  346. SetHeadAndLastUsedSegment(SparseArraySegment<double>::AllocateSegment(recycler, 0, 0, size, nullptr));
  347. }
  348. JavascriptNativeFloatArray::JavascriptNativeFloatArray(DynamicType * type, uint32 size)
  349. : JavascriptNativeArray(type)
  350. {
  351. SetHeadAndLastUsedSegment(DetermineInlineHeadSegmentPointer<JavascriptNativeFloatArray, 0, false>(this));
  352. head->size = size;
  353. ((SparseArraySegment<double>*)head)->FillSegmentBuffer(0, size);
  354. }
  355. bool JavascriptArray::Is(Var aValue)
  356. {
  357. TypeId typeId = JavascriptOperators::GetTypeId(aValue);
  358. return JavascriptArray::Is(typeId);
  359. }
  360. bool JavascriptArray::Is(TypeId typeId)
  361. {
  362. return typeId >= TypeIds_ArrayFirst && typeId <= TypeIds_ArrayLast;
  363. }
  364. bool JavascriptArray::IsVarArray(Var aValue)
  365. {
  366. TypeId typeId = JavascriptOperators::GetTypeId(aValue);
  367. return JavascriptArray::IsVarArray(typeId);
  368. }
  369. bool JavascriptArray::IsVarArray(TypeId typeId)
  370. {
  371. return typeId == TypeIds_Array;
  372. }
  373. JavascriptArray* JavascriptArray::FromVar(Var aValue)
  374. {
  375. AssertMsg(Is(aValue), "Ensure var is actually a 'JavascriptArray'");
  376. return static_cast<JavascriptArray *>(RecyclableObject::FromVar(aValue));
  377. }
  378. // Get JavascriptArray* from a Var, which is either a JavascriptArray* or ESArray*.
  379. JavascriptArray* JavascriptArray::FromAnyArray(Var aValue)
  380. {
  381. AssertMsg(Is(aValue) || ES5Array::Is(aValue), "Ensure var is actually a 'JavascriptArray' or 'ES5Array'");
  382. return static_cast<JavascriptArray *>(RecyclableObject::FromVar(aValue));
  383. }
  384. // Check if a Var is a direct-accessible (fast path) JavascriptArray.
  385. bool JavascriptArray::IsDirectAccessArray(Var aValue)
  386. {
  387. return RecyclableObject::Is(aValue) &&
  388. (VirtualTableInfo<JavascriptArray>::HasVirtualTable(aValue) ||
  389. VirtualTableInfo<JavascriptNativeIntArray>::HasVirtualTable(aValue) ||
  390. VirtualTableInfo<JavascriptNativeFloatArray>::HasVirtualTable(aValue));
  391. }
  392. DynamicObjectFlags JavascriptArray::GetFlags() const
  393. {
  394. return GetArrayFlags();
  395. }
  396. DynamicObjectFlags JavascriptArray::GetFlags_Unchecked() const // do not use except in extreme circumstances
  397. {
  398. return GetArrayFlags_Unchecked();
  399. }
  400. void JavascriptArray::SetFlags(const DynamicObjectFlags flags)
  401. {
  402. SetArrayFlags(flags);
  403. }
  404. DynamicType * JavascriptArray::GetInitialType(ScriptContext * scriptContext)
  405. {
  406. return scriptContext->GetLibrary()->GetArrayType();
  407. }
  408. JavascriptArray *JavascriptArray::GetArrayForArrayOrObjectWithArray(const Var var)
  409. {
  410. bool isObjectWithArray;
  411. TypeId arrayTypeId;
  412. return GetArrayForArrayOrObjectWithArray(var, &isObjectWithArray, &arrayTypeId);
  413. }
  414. JavascriptArray *JavascriptArray::GetArrayForArrayOrObjectWithArray(
  415. const Var var,
  416. bool *const isObjectWithArrayRef,
  417. TypeId *const arrayTypeIdRef)
  418. {
  419. // This is a helper function used by jitted code. The array checks done here match the array checks done by jitted code
  420. // (see Lowerer::GenerateArrayTest) to minimize bailouts.
  421. Assert(var);
  422. Assert(isObjectWithArrayRef);
  423. Assert(arrayTypeIdRef);
  424. *isObjectWithArrayRef = false;
  425. *arrayTypeIdRef = TypeIds_Undefined;
  426. if(!RecyclableObject::Is(var))
  427. {
  428. return nullptr;
  429. }
  430. JavascriptArray *array = nullptr;
  431. INT_PTR vtable = VirtualTableInfoBase::GetVirtualTable(var);
  432. if(vtable == VirtualTableInfo<DynamicObject>::Address)
  433. {
  434. ArrayObject* objectArray = DynamicObject::FromVar(var)->GetObjectArray();
  435. array = (objectArray && Is(objectArray)) ? FromVar(objectArray) : nullptr;
  436. if(!array)
  437. {
  438. return nullptr;
  439. }
  440. *isObjectWithArrayRef = true;
  441. vtable = VirtualTableInfoBase::GetVirtualTable(array);
  442. }
  443. if(vtable == VirtualTableInfo<JavascriptArray>::Address)
  444. {
  445. *arrayTypeIdRef = TypeIds_Array;
  446. }
  447. else if(vtable == VirtualTableInfo<JavascriptNativeIntArray>::Address)
  448. {
  449. *arrayTypeIdRef = TypeIds_NativeIntArray;
  450. }
  451. else if(vtable == VirtualTableInfo<JavascriptNativeFloatArray>::Address)
  452. {
  453. *arrayTypeIdRef = TypeIds_NativeFloatArray;
  454. }
  455. else
  456. {
  457. return nullptr;
  458. }
  459. if(!array)
  460. {
  461. array = FromVar(var);
  462. }
  463. return array;
  464. }
  465. const SparseArraySegmentBase *JavascriptArray::Jit_GetArrayHeadSegmentForArrayOrObjectWithArray(const Var var)
  466. {
  467. // This is a helper function used by jitted code
  468. JavascriptArray *const array = GetArrayForArrayOrObjectWithArray(var);
  469. return array ? array->head : nullptr;
  470. }
  471. uint32 JavascriptArray::Jit_GetArrayHeadSegmentLength(const SparseArraySegmentBase *const headSegment)
  472. {
  473. // This is a helper function used by jitted code
  474. return headSegment ? headSegment->length : 0;
  475. }
  476. bool JavascriptArray::Jit_OperationInvalidatedArrayHeadSegment(
  477. const SparseArraySegmentBase *const headSegmentBeforeOperation,
  478. const uint32 headSegmentLengthBeforeOperation,
  479. const Var varAfterOperation)
  480. {
  481. // This is a helper function used by jitted code
  482. Assert(varAfterOperation);
  483. if(!headSegmentBeforeOperation)
  484. {
  485. return false;
  486. }
  487. const SparseArraySegmentBase *const headSegmentAfterOperation =
  488. Jit_GetArrayHeadSegmentForArrayOrObjectWithArray(varAfterOperation);
  489. return
  490. headSegmentAfterOperation != headSegmentBeforeOperation ||
  491. headSegmentAfterOperation->length != headSegmentLengthBeforeOperation;
  492. }
  493. uint32 JavascriptArray::Jit_GetArrayLength(const Var var)
  494. {
  495. // This is a helper function used by jitted code
  496. bool isObjectWithArray;
  497. TypeId arrayTypeId;
  498. JavascriptArray *const array = GetArrayForArrayOrObjectWithArray(var, &isObjectWithArray, &arrayTypeId);
  499. return array && !isObjectWithArray ? array->GetLength() : 0;
  500. }
  501. bool JavascriptArray::Jit_OperationInvalidatedArrayLength(const uint32 lengthBeforeOperation, const Var varAfterOperation)
  502. {
  503. // This is a helper function used by jitted code
  504. return Jit_GetArrayLength(varAfterOperation) != lengthBeforeOperation;
  505. }
  506. DynamicObjectFlags JavascriptArray::Jit_GetArrayFlagsForArrayOrObjectWithArray(const Var var)
  507. {
  508. // This is a helper function used by jitted code
  509. JavascriptArray *const array = GetArrayForArrayOrObjectWithArray(var);
  510. return array && array->UsesObjectArrayOrFlagsAsFlags() ? array->GetFlags() : DynamicObjectFlags::None;
  511. }
  512. bool JavascriptArray::Jit_OperationCreatedFirstMissingValue(
  513. const DynamicObjectFlags flagsBeforeOperation,
  514. const Var varAfterOperation)
  515. {
  516. // This is a helper function used by jitted code
  517. Assert(varAfterOperation);
  518. return
  519. !!(flagsBeforeOperation & DynamicObjectFlags::HasNoMissingValues) &&
  520. !(Jit_GetArrayFlagsForArrayOrObjectWithArray(varAfterOperation) & DynamicObjectFlags::HasNoMissingValues);
  521. }
  522. bool JavascriptArray::HasNoMissingValues() const
  523. {
  524. return !!(GetFlags() & DynamicObjectFlags::HasNoMissingValues);
  525. }
  526. bool JavascriptArray::HasNoMissingValues_Unchecked() const // do not use except in extreme circumstances
  527. {
  528. return !!(GetFlags_Unchecked() & DynamicObjectFlags::HasNoMissingValues);
  529. }
  530. void JavascriptArray::SetHasNoMissingValues(const bool hasNoMissingValues)
  531. {
  532. SetFlags(
  533. hasNoMissingValues
  534. ? GetFlags() | DynamicObjectFlags::HasNoMissingValues
  535. : GetFlags() & ~DynamicObjectFlags::HasNoMissingValues);
  536. }
  537. template<class T>
  538. bool JavascriptArray::IsMissingHeadSegmentItemImpl(const uint32 index) const
  539. {
  540. Assert(index < head->length);
  541. return SparseArraySegment<T>::IsMissingItem(&static_cast<SparseArraySegment<T> *>(head)->elements[index]);
  542. }
  543. bool JavascriptArray::IsMissingHeadSegmentItem(const uint32 index) const
  544. {
  545. return IsMissingHeadSegmentItemImpl<Var>(index);
  546. }
  547. #if ENABLE_COPYONACCESS_ARRAY
  548. void JavascriptCopyOnAccessNativeIntArray::ConvertCopyOnAccessSegment()
  549. {
  550. Assert(this->GetScriptContext()->GetLibrary()->cacheForCopyOnAccessArraySegments->IsValidIndex(::Math::PointerCastToIntegral<uint32>(this->GetHead())));
  551. SparseArraySegment<int32> *seg = this->GetScriptContext()->GetLibrary()->cacheForCopyOnAccessArraySegments->GetSegmentByIndex(::Math::PointerCastToIntegral<byte>(this->GetHead()));
  552. SparseArraySegment<int32> *newSeg = SparseArraySegment<int32>::AllocateLiteralHeadSegment(this->GetRecycler(), seg->length);
  553. #if ENABLE_DEBUG_CONFIG_OPTIONS
  554. if (Js::Configuration::Global.flags.TestTrace.IsEnabled(Js::CopyOnAccessArrayPhase))
  555. {
  556. Output::Print(_u("Convert copy-on-access array: index(%d) length(%d)\n"), this->GetHead(), seg->length);
  557. Output::Flush();
  558. }
  559. #endif
  560. newSeg->CopySegment(this->GetRecycler(), newSeg, 0, seg, 0, seg->length);
  561. this->SetHeadAndLastUsedSegment(newSeg);
  562. VirtualTableInfo<JavascriptNativeIntArray>::SetVirtualTable(this);
  563. this->type = JavascriptNativeIntArray::GetInitialType(this->GetScriptContext());
  564. ArrayCallSiteInfo *arrayInfo = this->GetArrayCallSiteInfo();
  565. if (arrayInfo && !arrayInfo->isNotCopyOnAccessArray)
  566. {
  567. arrayInfo->isNotCopyOnAccessArray = 1;
  568. }
  569. }
  570. uint32 JavascriptCopyOnAccessNativeIntArray::GetNextIndex(uint32 index) const
  571. {
  572. if (this->length == 0 || (index != Js::JavascriptArray::InvalidIndex && index >= this->length))
  573. {
  574. return Js::JavascriptArray::InvalidIndex;
  575. }
  576. else if (index == Js::JavascriptArray::InvalidIndex)
  577. {
  578. return 0;
  579. }
  580. else
  581. {
  582. return index + 1;
  583. }
  584. }
  585. BOOL JavascriptCopyOnAccessNativeIntArray::DirectGetItemAt(uint32 index, int* outVal)
  586. {
  587. Assert(this->GetScriptContext()->GetLibrary()->cacheForCopyOnAccessArraySegments->IsValidIndex(::Math::PointerCastToIntegral<uint32>(this->GetHead())));
  588. SparseArraySegment<int32> *seg = this->GetScriptContext()->GetLibrary()->cacheForCopyOnAccessArraySegments->GetSegmentByIndex(::Math::PointerCastToIntegral<byte>(this->GetHead()));
  589. if (this->length == 0 || index == Js::JavascriptArray::InvalidIndex || index >= this->length)
  590. {
  591. return FALSE;
  592. }
  593. else
  594. {
  595. *outVal = seg->elements[index];
  596. return TRUE;
  597. }
  598. }
  599. #endif
  600. bool JavascriptNativeIntArray::IsMissingHeadSegmentItem(const uint32 index) const
  601. {
  602. return IsMissingHeadSegmentItemImpl<int32>(index);
  603. }
  604. bool JavascriptNativeFloatArray::IsMissingHeadSegmentItem(const uint32 index) const
  605. {
  606. return IsMissingHeadSegmentItemImpl<double>(index);
  607. }
  608. /* static */
  609. bool JavascriptArray::HasInlineHeadSegment(uint32 length)
  610. {
  611. return length <= SparseArraySegmentBase::INLINE_CHUNK_SIZE;
  612. }
  613. Var JavascriptArray::OP_NewScArray(uint32 elementCount, ScriptContext* scriptContext)
  614. {
  615. // Called only to create array literals: size is known.
  616. return scriptContext->GetLibrary()->CreateArrayLiteral(elementCount);
  617. }
  618. Var JavascriptArray::OP_NewScArrayWithElements(uint32 elementCount, Var *elements, ScriptContext* scriptContext)
  619. {
  620. // Called only to create array literals: size is known.
  621. JavascriptArray *arr = scriptContext->GetLibrary()->CreateArrayLiteral(elementCount);
  622. SparseArraySegment<Var> *head = (SparseArraySegment<Var>*)arr->head;
  623. Assert(elementCount <= head->length);
  624. js_memcpy_s(head->elements, sizeof(Var) * head->length, elements, sizeof(Var) * elementCount);
  625. #ifdef VALIDATE_ARRAY
  626. arr->ValidateArray();
  627. #endif
  628. return arr;
  629. }
  630. Var JavascriptArray::OP_NewScArrayWithMissingValues(uint32 elementCount, ScriptContext* scriptContext)
  631. {
  632. // Called only to create array literals: size is known.
  633. JavascriptArray *const array = static_cast<JavascriptArray *>(OP_NewScArray(elementCount, scriptContext));
  634. array->SetHasNoMissingValues(false);
  635. SparseArraySegment<Var> *head = (SparseArraySegment<Var>*)array->head;
  636. head->FillSegmentBuffer(0, elementCount);
  637. return array;
  638. }
  639. #if ENABLE_PROFILE_INFO
  640. Var JavascriptArray::ProfiledNewScArray(uint32 elementCount, ScriptContext *scriptContext, ArrayCallSiteInfo *arrayInfo, RecyclerWeakReference<FunctionBody> *weakFuncRef)
  641. {
  642. if (arrayInfo->IsNativeIntArray())
  643. {
  644. JavascriptNativeIntArray *arr = scriptContext->GetLibrary()->CreateNativeIntArrayLiteral(elementCount);
  645. arr->SetArrayProfileInfo(weakFuncRef, arrayInfo);
  646. return arr;
  647. }
  648. if (arrayInfo->IsNativeFloatArray())
  649. {
  650. JavascriptNativeFloatArray *arr = scriptContext->GetLibrary()->CreateNativeFloatArrayLiteral(elementCount);
  651. arr->SetArrayProfileInfo(weakFuncRef, arrayInfo);
  652. return arr;
  653. }
  654. JavascriptArray *arr = scriptContext->GetLibrary()->CreateArrayLiteral(elementCount);
  655. return arr;
  656. }
  657. #endif
  658. Var JavascriptArray::OP_NewScIntArray(AuxArray<int32> *ints, ScriptContext* scriptContext)
  659. {
  660. uint32 count = ints->count;
  661. JavascriptArray *arr = scriptContext->GetLibrary()->CreateArrayLiteral(count);
  662. SparseArraySegment<Var> *head = (SparseArraySegment<Var>*)arr->head;
  663. Assert(count > 0 && count == head->length);
  664. for (uint i = 0; i < count; i++)
  665. {
  666. head->elements[i] = JavascriptNumber::ToVar(ints->elements[i], scriptContext);
  667. }
  668. return arr;
  669. }
  670. #if ENABLE_PROFILE_INFO
  671. Var JavascriptArray::ProfiledNewScIntArray(AuxArray<int32> *ints, ScriptContext* scriptContext, ArrayCallSiteInfo *arrayInfo, RecyclerWeakReference<FunctionBody> *weakFuncRef)
  672. {
  673. // Called only to create array literals: size is known.
  674. uint32 count = ints->count;
  675. if (arrayInfo->IsNativeIntArray())
  676. {
  677. JavascriptNativeIntArray *arr;
  678. FunctionBody *functionBody = weakFuncRef->Get();
  679. JavascriptLibrary *lib = scriptContext->GetLibrary();
  680. #if ENABLE_COPYONACCESS_ARRAY
  681. if (JavascriptLibrary::IsCopyOnAccessArrayCallSite(lib, arrayInfo, count))
  682. {
  683. Assert(lib->cacheForCopyOnAccessArraySegments);
  684. arr = scriptContext->GetLibrary()->CreateCopyOnAccessNativeIntArrayLiteral(arrayInfo, functionBody, ints);
  685. }
  686. else
  687. #endif
  688. {
  689. arr = scriptContext->GetLibrary()->CreateNativeIntArrayLiteral(count);
  690. SparseArraySegment<int32> *head = static_cast<SparseArraySegment<int32>*>(arr->head);
  691. Assert(count > 0 && count == head->length);
  692. js_memcpy_s(head->elements, sizeof(int32)* head->length, ints->elements, sizeof(int32)* count);
  693. }
  694. arr->SetArrayProfileInfo(weakFuncRef, arrayInfo);
  695. return arr;
  696. }
  697. if (arrayInfo->IsNativeFloatArray())
  698. {
  699. JavascriptNativeFloatArray *arr = scriptContext->GetLibrary()->CreateNativeFloatArrayLiteral(count);
  700. SparseArraySegment<double> *head = (SparseArraySegment<double>*)arr->head;
  701. Assert(count > 0 && count == head->length);
  702. for (uint i = 0; i < count; i++)
  703. {
  704. head->elements[i] = (double)ints->elements[i];
  705. }
  706. arr->SetArrayProfileInfo(weakFuncRef, arrayInfo);
  707. return arr;
  708. }
  709. return OP_NewScIntArray(ints, scriptContext);
  710. }
  711. #endif
  712. Var JavascriptArray::OP_NewScFltArray(AuxArray<double> *doubles, ScriptContext* scriptContext)
  713. {
  714. uint32 count = doubles->count;
  715. JavascriptArray *arr = scriptContext->GetLibrary()->CreateArrayLiteral(count);
  716. SparseArraySegment<Var> *head = (SparseArraySegment<Var>*)arr->head;
  717. Assert(count > 0 && count == head->length);
  718. for (uint i = 0; i < count; i++)
  719. {
  720. double dval = doubles->elements[i];
  721. int32 ival;
  722. if (JavascriptNumber::TryGetInt32Value(dval, &ival) && !TaggedInt::IsOverflow(ival))
  723. {
  724. head->elements[i] = TaggedInt::ToVarUnchecked(ival);
  725. }
  726. else
  727. {
  728. head->elements[i] = JavascriptNumber::ToVarNoCheck(dval, scriptContext);
  729. }
  730. }
  731. return arr;
  732. }
  733. #if ENABLE_PROFILE_INFO
  734. Var JavascriptArray::ProfiledNewScFltArray(AuxArray<double> *doubles, ScriptContext* scriptContext, ArrayCallSiteInfo *arrayInfo, RecyclerWeakReference<FunctionBody> *weakFuncRef)
  735. {
  736. // Called only to create array literals: size is known.
  737. if (arrayInfo->IsNativeFloatArray())
  738. {
  739. arrayInfo->SetIsNotNativeIntArray();
  740. uint32 count = doubles->count;
  741. JavascriptNativeFloatArray *arr = scriptContext->GetLibrary()->CreateNativeFloatArrayLiteral(count);
  742. SparseArraySegment<double> *head = (SparseArraySegment<double>*)arr->head;
  743. Assert(count > 0 && count == head->length);
  744. js_memcpy_s(head->elements, sizeof(double) * head->length, doubles->elements, sizeof(double) * count);
  745. arr->SetArrayProfileInfo(weakFuncRef, arrayInfo);
  746. return arr;
  747. }
  748. return OP_NewScFltArray(doubles, scriptContext);
  749. }
  750. Var JavascriptArray::ProfiledNewInstance(RecyclableObject* function, CallInfo callInfo, ...)
  751. {
  752. ARGUMENTS(args, callInfo);
  753. Assert(JavascriptFunction::Is(function) &&
  754. JavascriptFunction::FromVar(function)->GetFunctionInfo() == &JavascriptArray::EntryInfo::NewInstance);
  755. Assert(callInfo.Count >= 2);
  756. ArrayCallSiteInfo *arrayInfo = (ArrayCallSiteInfo*)args[0];
  757. JavascriptArray* pNew = nullptr;
  758. if (callInfo.Count == 2)
  759. {
  760. // Exactly one argument, which is the array length if it's a uint32.
  761. Var firstArgument = args[1];
  762. int elementCount;
  763. if (TaggedInt::Is(firstArgument))
  764. {
  765. elementCount = TaggedInt::ToInt32(firstArgument);
  766. if (elementCount < 0)
  767. {
  768. JavascriptError::ThrowRangeError(function->GetScriptContext(), JSERR_ArrayLengthConstructIncorrect);
  769. }
  770. if (arrayInfo && arrayInfo->IsNativeArray())
  771. {
  772. if (arrayInfo->IsNativeIntArray())
  773. {
  774. pNew = function->GetLibrary()->CreateNativeIntArray(elementCount);
  775. }
  776. else
  777. {
  778. pNew = function->GetLibrary()->CreateNativeFloatArray(elementCount);
  779. }
  780. }
  781. else
  782. {
  783. pNew = function->GetLibrary()->CreateArray(elementCount);
  784. }
  785. }
  786. else if (JavascriptNumber::Is_NoTaggedIntCheck(firstArgument))
  787. {
  788. // Non-tagged-int number: make sure the double value is really a uint32.
  789. double value = JavascriptNumber::GetValue(firstArgument);
  790. uint32 uvalue = JavascriptConversion::ToUInt32(value);
  791. if (value != uvalue)
  792. {
  793. JavascriptError::ThrowRangeError(function->GetScriptContext(), JSERR_ArrayLengthConstructIncorrect);
  794. }
  795. if (arrayInfo && arrayInfo->IsNativeArray())
  796. {
  797. if (arrayInfo->IsNativeIntArray())
  798. {
  799. pNew = function->GetLibrary()->CreateNativeIntArray(uvalue);
  800. }
  801. else
  802. {
  803. pNew = function->GetLibrary()->CreateNativeFloatArray(uvalue);
  804. }
  805. }
  806. else
  807. {
  808. pNew = function->GetLibrary()->CreateArray(uvalue);
  809. }
  810. }
  811. else
  812. {
  813. //
  814. // First element is not int/double
  815. // create an array of length 1.
  816. // Set first element as the passed Var
  817. //
  818. pNew = function->GetLibrary()->CreateArray(1);
  819. pNew->DirectSetItemAt<Var>(0, firstArgument);
  820. }
  821. }
  822. else
  823. {
  824. // Called with a list of initial element values.
  825. // Create an array of the appropriate length and walk the list.
  826. if (arrayInfo && arrayInfo->IsNativeArray())
  827. {
  828. if (arrayInfo->IsNativeIntArray())
  829. {
  830. pNew = function->GetLibrary()->CreateNativeIntArray(callInfo.Count - 1);
  831. }
  832. else
  833. {
  834. pNew = function->GetLibrary()->CreateNativeFloatArray(callInfo.Count - 1);
  835. }
  836. }
  837. else
  838. {
  839. pNew = function->GetLibrary()->CreateArray(callInfo.Count - 1);
  840. }
  841. pNew->FillFromArgs(callInfo.Count - 1, 0, args.Values, arrayInfo);
  842. }
  843. #ifdef VALIDATE_ARRAY
  844. pNew->ValidateArray();
  845. #endif
  846. return pNew;
  847. }
  848. #endif
  849. Var JavascriptArray::NewInstance(RecyclableObject* function, CallInfo callInfo, ...)
  850. {
  851. ARGUMENTS(args, callInfo);
  852. return NewInstance(function, args);
  853. }
  854. Var JavascriptArray::NewInstance(RecyclableObject* function, Arguments args)
  855. {
  856. // Call to new Array(), possibly under another name.
  857. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  858. // SkipDefaultNewObject function flag should have prevented the default object
  859. // being created, except when call true a host dispatch.
  860. const CallInfo &callInfo = args.Info;
  861. Var newTarget = callInfo.Flags & CallFlags_NewTarget ? args.Values[args.Info.Count] : args[0];
  862. bool isCtorSuperCall = (callInfo.Flags & CallFlags_New) && newTarget != nullptr && !JavascriptOperators::IsUndefined(newTarget);
  863. Assert( isCtorSuperCall || !(callInfo.Flags & CallFlags_New) || args[0] == nullptr
  864. || JavascriptOperators::GetTypeId(args[0]) == TypeIds_HostDispatch);
  865. ScriptContext* scriptContext = function->GetScriptContext();
  866. JavascriptArray* pNew = nullptr;
  867. if (callInfo.Count < 2)
  868. {
  869. if (pNew == nullptr)
  870. {
  871. // No arguments passed to Array(), so create with the default size (0).
  872. pNew = CreateArrayFromConstructor(function, 0, scriptContext);
  873. }
  874. else
  875. {
  876. pNew->SetLength((uint32)0);
  877. }
  878. return isCtorSuperCall ?
  879. JavascriptOperators::OrdinaryCreateFromConstructor(RecyclableObject::FromVar(newTarget), pNew, nullptr, scriptContext) :
  880. pNew;
  881. }
  882. if (callInfo.Count == 2)
  883. {
  884. // Exactly one argument, which is the array length if it's a uint32.
  885. Var firstArgument = args[1];
  886. int elementCount;
  887. if (TaggedInt::Is(firstArgument))
  888. {
  889. elementCount = TaggedInt::ToInt32(firstArgument);
  890. if (elementCount < 0)
  891. {
  892. JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthConstructIncorrect);
  893. }
  894. if (pNew == nullptr)
  895. {
  896. pNew = CreateArrayFromConstructor(function, elementCount, scriptContext);
  897. }
  898. else
  899. {
  900. pNew->SetLength(elementCount);
  901. }
  902. }
  903. else if (JavascriptNumber::Is_NoTaggedIntCheck(firstArgument))
  904. {
  905. // Non-tagged-int number: make sure the double value is really a uint32.
  906. double value = JavascriptNumber::GetValue(firstArgument);
  907. uint32 uvalue = JavascriptConversion::ToUInt32(value);
  908. if (value != uvalue)
  909. {
  910. JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthConstructIncorrect);
  911. }
  912. if (pNew == nullptr)
  913. {
  914. pNew = CreateArrayFromConstructor(function, uvalue, scriptContext);
  915. }
  916. else
  917. {
  918. pNew->SetLength(uvalue);
  919. }
  920. }
  921. else
  922. {
  923. //
  924. // First element is not int/double
  925. // create an array of length 1.
  926. // Set first element as the passed Var
  927. //
  928. if (pNew == nullptr)
  929. {
  930. pNew = CreateArrayFromConstructor(function, 1, scriptContext);
  931. }
  932. JavascriptOperators::SetItem(pNew, pNew, 0u, firstArgument, scriptContext, PropertyOperation_ThrowIfNotExtensible);
  933. // If we were passed an uninitialized JavascriptArray as the this argument,
  934. // we need to set the length. We must do this _after_ setting the first
  935. // element as the array may have side effects such as a setter for property
  936. // named '0' which would make the previous length of the array observable.
  937. // If we weren't passed a JavascriptArray as the this argument, this is no-op.
  938. pNew->SetLength(1);
  939. }
  940. }
  941. else
  942. {
  943. // Called with a list of initial element values.
  944. // Create an array of the appropriate length and walk the list.
  945. if (pNew == nullptr)
  946. {
  947. pNew = CreateArrayFromConstructor(function, callInfo.Count - 1, scriptContext);
  948. }
  949. else
  950. {
  951. // If we were passed an uninitialized JavascriptArray as the this argument,
  952. // we need to set the length. We should do this _after_ setting the
  953. // elements as the array may have side effects such as a setter for property
  954. // named '0' which would make the previous length of the array observable.
  955. // Note: We don't support this case now as the DirectSetItemAt calls in FillFromArgs
  956. // will not call the setter. Need to refactor that method.
  957. pNew->SetLength(callInfo.Count - 1);
  958. }
  959. pNew->JavascriptArray::FillFromArgs(callInfo.Count - 1, 0, args.Values);
  960. }
  961. #ifdef VALIDATE_ARRAY
  962. pNew->ValidateArray();
  963. #endif
  964. return isCtorSuperCall ?
  965. JavascriptOperators::OrdinaryCreateFromConstructor(RecyclableObject::FromVar(newTarget), pNew, nullptr, scriptContext) :
  966. pNew;
  967. }
  968. JavascriptArray* JavascriptArray::CreateArrayFromConstructor(RecyclableObject* constructor, uint32 length, ScriptContext* scriptContext)
  969. {
  970. JavascriptLibrary* library = constructor->GetLibrary();
  971. // Create the Array object we'll return - this is the only way to create an object which is an exotic Array object.
  972. // Note: We need to use the library from the ScriptContext of the constructor, not the currently executing function.
  973. // This is for the case where a built-in @@create method from a different JavascriptLibrary is installed on
  974. // constructor.
  975. JavascriptArray* arr = library->CreateArray(length);
  976. return arr;
  977. }
  978. #if ENABLE_PROFILE_INFO
  979. Var JavascriptArray::ProfiledNewInstanceNoArg(RecyclableObject *function, ScriptContext *scriptContext, ArrayCallSiteInfo *arrayInfo, RecyclerWeakReference<FunctionBody> *weakFuncRef)
  980. {
  981. Assert(JavascriptFunction::Is(function) &&
  982. JavascriptFunction::FromVar(function)->GetFunctionInfo() == &JavascriptArray::EntryInfo::NewInstance);
  983. if (arrayInfo->IsNativeIntArray())
  984. {
  985. JavascriptNativeIntArray *arr = scriptContext->GetLibrary()->CreateNativeIntArray();
  986. arr->SetArrayProfileInfo(weakFuncRef, arrayInfo);
  987. return arr;
  988. }
  989. if (arrayInfo->IsNativeFloatArray())
  990. {
  991. JavascriptNativeFloatArray *arr = scriptContext->GetLibrary()->CreateNativeFloatArray();
  992. arr->SetArrayProfileInfo(weakFuncRef, arrayInfo);
  993. return arr;
  994. }
  995. return scriptContext->GetLibrary()->CreateArray();
  996. }
  997. #endif
  998. Var JavascriptNativeIntArray::NewInstance(RecyclableObject* function, CallInfo callInfo, ...)
  999. {
  1000. ARGUMENTS(args, callInfo);
  1001. return NewInstance(function, args);
  1002. }
  1003. Var JavascriptNativeIntArray::NewInstance(RecyclableObject* function, Arguments args)
  1004. {
  1005. Assert(!PHASE_OFF1(NativeArrayPhase));
  1006. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  1007. const CallInfo &callInfo = args.Info;
  1008. if (callInfo.Count < 2)
  1009. {
  1010. // No arguments passed to Array(), so create with the default size (0).
  1011. return function->GetLibrary()->CreateNativeIntArray();
  1012. }
  1013. JavascriptArray* pNew = nullptr;
  1014. if (callInfo.Count == 2)
  1015. {
  1016. // Exactly one argument, which is the array length if it's a uint32.
  1017. Var firstArgument = args[1];
  1018. int elementCount;
  1019. if (TaggedInt::Is(firstArgument))
  1020. {
  1021. elementCount = TaggedInt::ToInt32(firstArgument);
  1022. if (elementCount < 0)
  1023. {
  1024. JavascriptError::ThrowRangeError(
  1025. function->GetScriptContext(), JSERR_ArrayLengthConstructIncorrect);
  1026. }
  1027. pNew = function->GetLibrary()->CreateNativeIntArray(elementCount);
  1028. }
  1029. else if (JavascriptNumber::Is_NoTaggedIntCheck(firstArgument))
  1030. {
  1031. // Non-tagged-int number: make sure the double value is really a uint32.
  1032. double value = JavascriptNumber::GetValue(firstArgument);
  1033. uint32 uvalue = JavascriptConversion::ToUInt32(value);
  1034. if (value != uvalue)
  1035. {
  1036. JavascriptError::ThrowRangeError(
  1037. function->GetScriptContext(), JSERR_ArrayLengthConstructIncorrect);
  1038. }
  1039. pNew = function->GetLibrary()->CreateNativeIntArray(uvalue);
  1040. }
  1041. else
  1042. {
  1043. //
  1044. // First element is not int/double
  1045. // create an array of length 1.
  1046. // Set first element as the passed Var
  1047. //
  1048. pNew = function->GetLibrary()->CreateArray(1);
  1049. pNew->DirectSetItemAt<Var>(0, firstArgument);
  1050. }
  1051. }
  1052. else
  1053. {
  1054. // Called with a list of initial element values.
  1055. // Create an array of the appropriate length and walk the list.
  1056. JavascriptNativeIntArray *arr = function->GetLibrary()->CreateNativeIntArray(callInfo.Count - 1);
  1057. pNew = arr->FillFromArgs(callInfo.Count - 1, 0, args.Values);
  1058. }
  1059. #ifdef VALIDATE_ARRAY
  1060. pNew->ValidateArray();
  1061. #endif
  1062. return pNew;
  1063. }
  1064. Var JavascriptNativeFloatArray::NewInstance(RecyclableObject* function, CallInfo callInfo, ...)
  1065. {
  1066. ARGUMENTS(args, callInfo);
  1067. return NewInstance(function, args);
  1068. }
  1069. Var JavascriptNativeFloatArray::NewInstance(RecyclableObject* function, Arguments args)
  1070. {
  1071. Assert(!PHASE_OFF1(NativeArrayPhase));
  1072. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  1073. const CallInfo &callInfo = args.Info;
  1074. if (callInfo.Count < 2)
  1075. {
  1076. // No arguments passed to Array(), so create with the default size (0).
  1077. return function->GetLibrary()->CreateNativeFloatArray();
  1078. }
  1079. JavascriptArray* pNew = nullptr;
  1080. if (callInfo.Count == 2)
  1081. {
  1082. // Exactly one argument, which is the array length if it's a uint32.
  1083. Var firstArgument = args[1];
  1084. int elementCount;
  1085. if (TaggedInt::Is(firstArgument))
  1086. {
  1087. elementCount = TaggedInt::ToInt32(firstArgument);
  1088. if (elementCount < 0)
  1089. {
  1090. JavascriptError::ThrowRangeError(
  1091. function->GetScriptContext(), JSERR_ArrayLengthConstructIncorrect);
  1092. }
  1093. pNew = function->GetLibrary()->CreateNativeFloatArray(elementCount);
  1094. }
  1095. else if (JavascriptNumber::Is_NoTaggedIntCheck(firstArgument))
  1096. {
  1097. // Non-tagged-int number: make sure the double value is really a uint32.
  1098. double value = JavascriptNumber::GetValue(firstArgument);
  1099. uint32 uvalue = JavascriptConversion::ToUInt32(value);
  1100. if (value != uvalue)
  1101. {
  1102. JavascriptError::ThrowRangeError(
  1103. function->GetScriptContext(), JSERR_ArrayLengthConstructIncorrect);
  1104. }
  1105. pNew = function->GetLibrary()->CreateNativeFloatArray(uvalue);
  1106. }
  1107. else
  1108. {
  1109. //
  1110. // First element is not int/double
  1111. // create an array of length 1.
  1112. // Set first element as the passed Var
  1113. //
  1114. pNew = function->GetLibrary()->CreateArray(1);
  1115. pNew->DirectSetItemAt<Var>(0, firstArgument);
  1116. }
  1117. }
  1118. else
  1119. {
  1120. // Called with a list of initial element values.
  1121. // Create an array of the appropriate length and walk the list.
  1122. JavascriptNativeFloatArray *arr = function->GetLibrary()->CreateNativeFloatArray(callInfo.Count - 1);
  1123. pNew = arr->FillFromArgs(callInfo.Count - 1, 0, args.Values);
  1124. }
  1125. #ifdef VALIDATE_ARRAY
  1126. pNew->ValidateArray();
  1127. #endif
  1128. return pNew;
  1129. }
  1130. #if ENABLE_PROFILE_INFO
  1131. JavascriptArray * JavascriptNativeIntArray::FillFromArgs(uint length, uint start, Var *args, ArrayCallSiteInfo *arrayInfo, bool dontCreateNewArray)
  1132. #else
  1133. JavascriptArray * JavascriptNativeIntArray::FillFromArgs(uint length, uint start, Var *args, bool dontCreateNewArray)
  1134. #endif
  1135. {
  1136. uint i;
  1137. for (i = start; i < length; i++)
  1138. {
  1139. Var item = args[i + 1];
  1140. bool isTaggedInt = TaggedInt::Is(item);
  1141. bool isTaggedIntMissingValue = false;
  1142. #ifdef _M_AMD64
  1143. if (isTaggedInt)
  1144. {
  1145. int32 iValue = TaggedInt::ToInt32(item);
  1146. isTaggedIntMissingValue = Js::SparseArraySegment<int32>::IsMissingItem(&iValue);
  1147. }
  1148. #endif
  1149. if (isTaggedInt && !isTaggedIntMissingValue)
  1150. {
  1151. // This is taggedInt case and we verified that item is not missing value in AMD64.
  1152. this->DirectSetItemAt(i, TaggedInt::ToInt32(item));
  1153. }
  1154. else if (!isTaggedIntMissingValue && JavascriptNumber::Is_NoTaggedIntCheck(item))
  1155. {
  1156. double dvalue = JavascriptNumber::GetValue(item);
  1157. int32 ivalue;
  1158. if (JavascriptNumber::TryGetInt32Value(dvalue, &ivalue) && !Js::SparseArraySegment<int32>::IsMissingItem(&ivalue))
  1159. {
  1160. this->DirectSetItemAt(i, ivalue);
  1161. }
  1162. else
  1163. {
  1164. #if ENABLE_PROFILE_INFO
  1165. if (arrayInfo)
  1166. {
  1167. arrayInfo->SetIsNotNativeIntArray();
  1168. }
  1169. #endif
  1170. if (HasInlineHeadSegment(length) && i < this->head->length && !dontCreateNewArray)
  1171. {
  1172. // Avoid shrinking the number of elements in the head segment. We can still create a new
  1173. // array here, so go ahead.
  1174. JavascriptNativeFloatArray *fArr =
  1175. this->GetScriptContext()->GetLibrary()->CreateNativeFloatArrayLiteral(length);
  1176. return fArr->JavascriptNativeFloatArray::FillFromArgs(length, 0, args);
  1177. }
  1178. JavascriptNativeFloatArray *fArr = JavascriptNativeIntArray::ToNativeFloatArray(this);
  1179. fArr->DirectSetItemAt(i, dvalue);
  1180. #if ENABLE_PROFILE_INFO
  1181. return fArr->JavascriptNativeFloatArray::FillFromArgs(length, i + 1, args, arrayInfo, dontCreateNewArray);
  1182. #else
  1183. return fArr->JavascriptNativeFloatArray::FillFromArgs(length, i + 1, args, dontCreateNewArray);
  1184. #endif
  1185. }
  1186. }
  1187. else
  1188. {
  1189. #if ENABLE_PROFILE_INFO
  1190. if (arrayInfo)
  1191. {
  1192. arrayInfo->SetIsNotNativeArray();
  1193. }
  1194. #endif
  1195. #pragma prefast(suppress:6237, "The right hand side condition does not have any side effects.")
  1196. if (sizeof(int32) < sizeof(Var) && HasInlineHeadSegment(length) && i < this->head->length && !dontCreateNewArray)
  1197. {
  1198. // Avoid shrinking the number of elements in the head segment. We can still create a new
  1199. // array here, so go ahead.
  1200. JavascriptArray *arr = this->GetScriptContext()->GetLibrary()->CreateArrayLiteral(length);
  1201. return arr->JavascriptArray::FillFromArgs(length, 0, args);
  1202. }
  1203. JavascriptArray *arr = JavascriptNativeIntArray::ToVarArray(this);
  1204. #if ENABLE_PROFILE_INFO
  1205. return arr->JavascriptArray::FillFromArgs(length, i, args, nullptr, dontCreateNewArray);
  1206. #else
  1207. return arr->JavascriptArray::FillFromArgs(length, i, args, dontCreateNewArray);
  1208. #endif
  1209. }
  1210. }
  1211. return this;
  1212. }
  1213. #if ENABLE_PROFILE_INFO
  1214. JavascriptArray * JavascriptNativeFloatArray::FillFromArgs(uint length, uint start, Var *args, ArrayCallSiteInfo *arrayInfo, bool dontCreateNewArray)
  1215. #else
  1216. JavascriptArray * JavascriptNativeFloatArray::FillFromArgs(uint length, uint start, Var *args, bool dontCreateNewArray)
  1217. #endif
  1218. {
  1219. uint i;
  1220. for (i = start; i < length; i++)
  1221. {
  1222. Var item = args[i + 1];
  1223. if (TaggedInt::Is(item))
  1224. {
  1225. this->DirectSetItemAt(i, TaggedInt::ToDouble(item));
  1226. }
  1227. else if (JavascriptNumber::Is_NoTaggedIntCheck(item))
  1228. {
  1229. this->DirectSetItemAt(i, JavascriptNumber::GetValue(item));
  1230. }
  1231. else
  1232. {
  1233. JavascriptArray *arr = JavascriptNativeFloatArray::ToVarArray(this);
  1234. #if ENABLE_PROFILE_INFO
  1235. if (arrayInfo)
  1236. {
  1237. arrayInfo->SetIsNotNativeArray();
  1238. }
  1239. return arr->JavascriptArray::FillFromArgs(length, i, args, nullptr, dontCreateNewArray);
  1240. #else
  1241. return arr->JavascriptArray::FillFromArgs(length, i, args, dontCreateNewArray);
  1242. #endif
  1243. }
  1244. }
  1245. return this;
  1246. }
  1247. #if ENABLE_PROFILE_INFO
  1248. JavascriptArray * JavascriptArray::FillFromArgs(uint length, uint start, Var *args, ArrayCallSiteInfo *arrayInfo, bool dontCreateNewArray)
  1249. #else
  1250. JavascriptArray * JavascriptArray::FillFromArgs(uint length, uint start, Var *args, bool dontCreateNewArray)
  1251. #endif
  1252. {
  1253. uint32 i;
  1254. for (i = start; i < length; i++)
  1255. {
  1256. Var item = args[i + 1];
  1257. this->DirectSetItemAt(i, item);
  1258. }
  1259. return this;
  1260. }
  1261. DynamicType * JavascriptNativeIntArray::GetInitialType(ScriptContext * scriptContext)
  1262. {
  1263. return scriptContext->GetLibrary()->GetNativeIntArrayType();
  1264. }
  1265. #if ENABLE_COPYONACCESS_ARRAY
  1266. DynamicType * JavascriptCopyOnAccessNativeIntArray::GetInitialType(ScriptContext * scriptContext)
  1267. {
  1268. return scriptContext->GetLibrary()->GetCopyOnAccessNativeIntArrayType();
  1269. }
  1270. #endif
  1271. JavascriptNativeFloatArray *JavascriptNativeIntArray::ToNativeFloatArray(JavascriptNativeIntArray *intArray)
  1272. {
  1273. #if ENABLE_PROFILE_INFO
  1274. ArrayCallSiteInfo *arrayInfo = intArray->GetArrayCallSiteInfo();
  1275. if (arrayInfo)
  1276. {
  1277. #if DBG
  1278. Js::JavascriptStackWalker walker(intArray->GetScriptContext());
  1279. Js::JavascriptFunction* caller = NULL;
  1280. bool foundScriptCaller = false;
  1281. while(walker.GetCaller(&caller))
  1282. {
  1283. if(caller != NULL && Js::ScriptFunction::Is(caller))
  1284. {
  1285. foundScriptCaller = true;
  1286. break;
  1287. }
  1288. }
  1289. if(foundScriptCaller)
  1290. {
  1291. Assert(caller);
  1292. Assert(caller->GetFunctionBody());
  1293. if(PHASE_TRACE(Js::NativeArrayConversionPhase, caller->GetFunctionBody()))
  1294. {
  1295. Output::Print(_u("Conversion: Int array to Float array ArrayCreationFunctionNumber:%2d CallSiteNumber:%2d \n"), arrayInfo->functionNumber, arrayInfo->callSiteNumber);
  1296. Output::Flush();
  1297. }
  1298. }
  1299. else
  1300. {
  1301. if(PHASE_TRACE1(Js::NativeArrayConversionPhase))
  1302. {
  1303. Output::Print(_u("Conversion: Int array to Float array across ScriptContexts"));
  1304. Output::Flush();
  1305. }
  1306. }
  1307. #else
  1308. if(PHASE_TRACE1(Js::NativeArrayConversionPhase))
  1309. {
  1310. Output::Print(_u("Conversion: Int array to Float array"));
  1311. Output::Flush();
  1312. }
  1313. #endif
  1314. arrayInfo->SetIsNotNativeIntArray();
  1315. }
  1316. #endif
  1317. // Grow the segments
  1318. ScriptContext *scriptContext = intArray->GetScriptContext();
  1319. Recycler *recycler = scriptContext->GetRecycler();
  1320. SparseArraySegmentBase *seg, *nextSeg, *prevSeg = nullptr;
  1321. for (seg = intArray->head; seg; seg = nextSeg)
  1322. {
  1323. nextSeg = seg->next;
  1324. uint32 size = seg->size;
  1325. if (size == 0)
  1326. {
  1327. continue;
  1328. }
  1329. uint32 left = seg->left;
  1330. uint32 length = seg->length;
  1331. int i;
  1332. int32 ival;
  1333. // The old segment will have size/2 and length capped by the new size.
  1334. seg->size >>= 1;
  1335. if (seg == intArray->head || seg->length > (seg->size >>= 1))
  1336. {
  1337. // Some live elements are being pushed out of this segment, so allocate a new one.
  1338. SparseArraySegment<double> *newSeg =
  1339. SparseArraySegment<double>::AllocateSegment(recycler, left, length, nextSeg);
  1340. Assert(newSeg != nullptr);
  1341. Assert((prevSeg == nullptr) == (seg == intArray->head));
  1342. newSeg->next = nextSeg;
  1343. intArray->LinkSegments((SparseArraySegment<double>*)prevSeg, newSeg);
  1344. if (intArray->GetLastUsedSegment() == seg)
  1345. {
  1346. intArray->SetLastUsedSegment(newSeg);
  1347. }
  1348. prevSeg = newSeg;
  1349. SegmentBTree * segmentMap = intArray->GetSegmentMap();
  1350. if (segmentMap)
  1351. {
  1352. segmentMap->SwapSegment(left, seg, newSeg);
  1353. }
  1354. // Fill the new segment with the overflow.
  1355. for (i = 0; (uint)i < newSeg->length; i++)
  1356. {
  1357. ival = ((SparseArraySegment<int32>*)seg)->elements[i /*+ seg->length*/];
  1358. if (ival == JavascriptNativeIntArray::MissingItem)
  1359. {
  1360. continue;
  1361. }
  1362. newSeg->elements[i] = (double)ival;
  1363. }
  1364. }
  1365. else
  1366. {
  1367. // Now convert the contents that will remain in the old segment.
  1368. for (i = seg->length - 1; i >= 0; i--)
  1369. {
  1370. ival = ((SparseArraySegment<int32>*)seg)->elements[i];
  1371. if (ival == JavascriptNativeIntArray::MissingItem)
  1372. {
  1373. ((SparseArraySegment<double>*)seg)->elements[i] = (double)JavascriptNativeFloatArray::MissingItem;
  1374. }
  1375. else
  1376. {
  1377. ((SparseArraySegment<double>*)seg)->elements[i] = (double)ival;
  1378. }
  1379. }
  1380. prevSeg = seg;
  1381. }
  1382. }
  1383. if (intArray->GetType() == scriptContext->GetLibrary()->GetNativeIntArrayType())
  1384. {
  1385. intArray->type = scriptContext->GetLibrary()->GetNativeFloatArrayType();
  1386. }
  1387. else
  1388. {
  1389. if (intArray->GetDynamicType()->GetIsLocked())
  1390. {
  1391. DynamicTypeHandler *typeHandler = intArray->GetDynamicType()->GetTypeHandler();
  1392. if (typeHandler->IsPathTypeHandler())
  1393. {
  1394. // We can't allow a type with the new type ID to be promoted to the old type.
  1395. // So go to a dictionary type handler, which will orphan the new type.
  1396. // This should be a corner case, so the inability to share the new type is unlikely to matter.
  1397. // If it does matter, try building a path from the new type's built-in root.
  1398. static_cast<PathTypeHandlerBase*>(typeHandler)->ResetTypeHandler(intArray);
  1399. }
  1400. else
  1401. {
  1402. intArray->ChangeType();
  1403. }
  1404. }
  1405. intArray->GetType()->SetTypeId(TypeIds_NativeFloatArray);
  1406. }
  1407. if (CrossSite::IsCrossSiteObjectTyped(intArray))
  1408. {
  1409. Assert(VirtualTableInfo<CrossSiteObject<JavascriptNativeIntArray>>::HasVirtualTable(intArray));
  1410. VirtualTableInfo<CrossSiteObject<JavascriptNativeFloatArray>>::SetVirtualTable(intArray);
  1411. }
  1412. else
  1413. {
  1414. Assert(VirtualTableInfo<JavascriptNativeIntArray>::HasVirtualTable(intArray));
  1415. VirtualTableInfo<JavascriptNativeFloatArray>::SetVirtualTable(intArray);
  1416. }
  1417. return (JavascriptNativeFloatArray*)intArray;
  1418. }
  1419. /*
  1420. * JavascriptArray::ChangeArrayTypeToNativeArray<double>
  1421. * - Converts the Var Array's type to NativeFloat.
  1422. * - Sets the VirtualTable to "JavascriptNativeFloatArray"
  1423. */
  1424. template<>
  1425. void JavascriptArray::ChangeArrayTypeToNativeArray<double>(JavascriptArray * varArray, ScriptContext * scriptContext)
  1426. {
  1427. AssertMsg(!JavascriptNativeArray::Is(varArray), "Ensure that the incoming Array is a Var array");
  1428. if (varArray->GetType() == scriptContext->GetLibrary()->GetArrayType())
  1429. {
  1430. varArray->type = scriptContext->GetLibrary()->GetNativeFloatArrayType();
  1431. }
  1432. else
  1433. {
  1434. if (varArray->GetDynamicType()->GetIsLocked())
  1435. {
  1436. DynamicTypeHandler *typeHandler = varArray->GetDynamicType()->GetTypeHandler();
  1437. if (typeHandler->IsPathTypeHandler())
  1438. {
  1439. // We can't allow a type with the new type ID to be promoted to the old type.
  1440. // So go to a dictionary type handler, which will orphan the new type.
  1441. // This should be a corner case, so the inability to share the new type is unlikely to matter.
  1442. // If it does matter, try building a path from the new type's built-in root.
  1443. static_cast<PathTypeHandlerBase*>(typeHandler)->ResetTypeHandler(varArray);
  1444. }
  1445. else
  1446. {
  1447. varArray->ChangeType();
  1448. }
  1449. }
  1450. varArray->GetType()->SetTypeId(TypeIds_NativeFloatArray);
  1451. }
  1452. if (CrossSite::IsCrossSiteObjectTyped(varArray))
  1453. {
  1454. Assert(VirtualTableInfo<CrossSiteObject<JavascriptArray>>::HasVirtualTable(varArray));
  1455. VirtualTableInfo<CrossSiteObject<JavascriptNativeFloatArray>>::SetVirtualTable(varArray);
  1456. }
  1457. else
  1458. {
  1459. Assert(VirtualTableInfo<JavascriptArray>::HasVirtualTable(varArray));
  1460. VirtualTableInfo<JavascriptNativeFloatArray>::SetVirtualTable(varArray);
  1461. }
  1462. }
  1463. /*
  1464. * JavascriptArray::ChangeArrayTypeToNativeArray<int32>
  1465. * - Converts the Var Array's type to NativeInt.
  1466. * - Sets the VirtualTable to "JavascriptNativeIntArray"
  1467. */
  1468. template<>
  1469. void JavascriptArray::ChangeArrayTypeToNativeArray<int32>(JavascriptArray * varArray, ScriptContext * scriptContext)
  1470. {
  1471. AssertMsg(!JavascriptNativeArray::Is(varArray), "Ensure that the incoming Array is a Var array");
  1472. if (varArray->GetType() == scriptContext->GetLibrary()->GetArrayType())
  1473. {
  1474. varArray->type = scriptContext->GetLibrary()->GetNativeIntArrayType();
  1475. }
  1476. else
  1477. {
  1478. if (varArray->GetDynamicType()->GetIsLocked())
  1479. {
  1480. DynamicTypeHandler *typeHandler = varArray->GetDynamicType()->GetTypeHandler();
  1481. if (typeHandler->IsPathTypeHandler())
  1482. {
  1483. // We can't allow a type with the new type ID to be promoted to the old type.
  1484. // So go to a dictionary type handler, which will orphan the new type.
  1485. // This should be a corner case, so the inability to share the new type is unlikely to matter.
  1486. // If it does matter, try building a path from the new type's built-in root.
  1487. static_cast<PathTypeHandlerBase*>(typeHandler)->ResetTypeHandler(varArray);
  1488. }
  1489. else
  1490. {
  1491. varArray->ChangeType();
  1492. }
  1493. }
  1494. varArray->GetType()->SetTypeId(TypeIds_NativeIntArray);
  1495. }
  1496. if (CrossSite::IsCrossSiteObjectTyped(varArray))
  1497. {
  1498. Assert(VirtualTableInfo<CrossSiteObject<JavascriptArray>>::HasVirtualTable(varArray));
  1499. VirtualTableInfo<CrossSiteObject<JavascriptNativeIntArray>>::SetVirtualTable(varArray);
  1500. }
  1501. else
  1502. {
  1503. Assert(VirtualTableInfo<JavascriptArray>::HasVirtualTable(varArray));
  1504. VirtualTableInfo<JavascriptNativeIntArray>::SetVirtualTable(varArray);
  1505. }
  1506. }
  1507. template<>
  1508. int32 JavascriptArray::GetNativeValue<int32>(Js::Var ival, ScriptContext * scriptContext)
  1509. {
  1510. return JavascriptConversion::ToInt32(ival, scriptContext);
  1511. }
  1512. template <>
  1513. double JavascriptArray::GetNativeValue<double>(Var ival, ScriptContext * scriptContext)
  1514. {
  1515. return JavascriptConversion::ToNumber(ival, scriptContext);
  1516. }
  1517. /*
  1518. * JavascriptArray::ConvertToNativeArrayInPlace
  1519. * In place conversion of all Var elements to Native Int/Double elements in an array.
  1520. * We do not update the DynamicProfileInfo of the array here.
  1521. */
  1522. template<typename NativeArrayType, typename T>
  1523. NativeArrayType *JavascriptArray::ConvertToNativeArrayInPlace(JavascriptArray *varArray)
  1524. {
  1525. AssertMsg(!JavascriptNativeArray::Is(varArray), "Ensure that the incoming Array is a Var array");
  1526. ScriptContext *scriptContext = varArray->GetScriptContext();
  1527. SparseArraySegmentBase *seg, *nextSeg, *prevSeg = nullptr;
  1528. for (seg = varArray->head; seg; seg = nextSeg)
  1529. {
  1530. nextSeg = seg->next;
  1531. uint32 size = seg->size;
  1532. if (size == 0)
  1533. {
  1534. continue;
  1535. }
  1536. int i;
  1537. Var ival;
  1538. uint32 growFactor = sizeof(Var) / sizeof(T);
  1539. AssertMsg(growFactor == 1, "We support only in place conversion of Var array to Native Array");
  1540. // Now convert the contents that will remain in the old segment.
  1541. for (i = seg->length - 1; i >= 0; i--)
  1542. {
  1543. ival = ((SparseArraySegment<Var>*)seg)->elements[i];
  1544. if (ival == JavascriptArray::MissingItem)
  1545. {
  1546. ((SparseArraySegment<T>*)seg)->elements[i] = NativeArrayType::MissingItem;
  1547. }
  1548. else
  1549. {
  1550. ((SparseArraySegment<T>*)seg)->elements[i] = GetNativeValue<T>(ival, scriptContext);
  1551. }
  1552. }
  1553. prevSeg = seg;
  1554. }
  1555. // Update the type of the Array
  1556. ChangeArrayTypeToNativeArray<T>(varArray, scriptContext);
  1557. return (NativeArrayType*)varArray;
  1558. }
  1559. JavascriptArray *JavascriptNativeIntArray::ConvertToVarArray(JavascriptNativeIntArray *intArray)
  1560. {
  1561. #if ENABLE_COPYONACCESS_ARRAY
  1562. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(intArray);
  1563. #endif
  1564. ScriptContext *scriptContext = intArray->GetScriptContext();
  1565. Recycler *recycler = scriptContext->GetRecycler();
  1566. SparseArraySegmentBase *seg, *nextSeg, *prevSeg = nullptr;
  1567. for (seg = intArray->head; seg; seg = nextSeg)
  1568. {
  1569. nextSeg = seg->next;
  1570. uint32 size = seg->size;
  1571. if (size == 0)
  1572. {
  1573. continue;
  1574. }
  1575. uint32 left = seg->left;
  1576. uint32 length = seg->length;
  1577. int i;
  1578. int32 ival;
  1579. // Shrink?
  1580. uint32 growFactor = sizeof(Var) / sizeof(int32);
  1581. if ((growFactor != 1 && (seg == intArray->head || seg->length > (seg->size /= growFactor))) ||
  1582. (seg->next == nullptr && SparseArraySegmentBase::IsLeafSegment(seg, recycler)))
  1583. {
  1584. // Some live elements are being pushed out of this segment, so allocate a new one.
  1585. // And/or the old segment is not scanned by the recycler, so we need a new one to hold vars.
  1586. SparseArraySegment<Var> *newSeg =
  1587. SparseArraySegment<Var>::AllocateSegment(recycler, left, length, nextSeg);
  1588. AnalysisAssert(newSeg);
  1589. Assert((prevSeg == nullptr) == (seg == intArray->head));
  1590. newSeg->next = nextSeg;
  1591. intArray->LinkSegments((SparseArraySegment<Var>*)prevSeg, newSeg);
  1592. if (intArray->GetLastUsedSegment() == seg)
  1593. {
  1594. intArray->SetLastUsedSegment(newSeg);
  1595. }
  1596. prevSeg = newSeg;
  1597. SegmentBTree * segmentMap = intArray->GetSegmentMap();
  1598. if (segmentMap)
  1599. {
  1600. segmentMap->SwapSegment(left, seg, newSeg);
  1601. }
  1602. // Fill the new segment with the overflow.
  1603. for (i = 0; (uint)i < newSeg->length; i++)
  1604. {
  1605. ival = ((SparseArraySegment<int32>*)seg)->elements[i];
  1606. if (ival == JavascriptNativeIntArray::MissingItem)
  1607. {
  1608. continue;
  1609. }
  1610. newSeg->elements[i] = JavascriptNumber::ToVar(ival, scriptContext);
  1611. }
  1612. }
  1613. else
  1614. {
  1615. // Now convert the contents that will remain in the old segment.
  1616. // Walk backward in case we're growing the element size.
  1617. for (i = seg->length - 1; i >= 0; i--)
  1618. {
  1619. ival = ((SparseArraySegment<int32>*)seg)->elements[i];
  1620. if (ival == JavascriptNativeIntArray::MissingItem)
  1621. {
  1622. ((SparseArraySegment<Var>*)seg)->elements[i] = (Var)JavascriptArray::MissingItem;
  1623. }
  1624. else
  1625. {
  1626. ((SparseArraySegment<Var>*)seg)->elements[i] = JavascriptNumber::ToVar(ival, scriptContext);
  1627. }
  1628. }
  1629. prevSeg = seg;
  1630. }
  1631. }
  1632. if (intArray->GetType() == scriptContext->GetLibrary()->GetNativeIntArrayType())
  1633. {
  1634. intArray->type = scriptContext->GetLibrary()->GetArrayType();
  1635. }
  1636. else
  1637. {
  1638. if (intArray->GetDynamicType()->GetIsLocked())
  1639. {
  1640. DynamicTypeHandler *typeHandler = intArray->GetDynamicType()->GetTypeHandler();
  1641. if (typeHandler->IsPathTypeHandler())
  1642. {
  1643. // We can't allow a type with the new type ID to be promoted to the old type.
  1644. // So go to a dictionary type handler, which will orphan the new type.
  1645. // This should be a corner case, so the inability to share the new type is unlikely to matter.
  1646. // If it does matter, try building a path from the new type's built-in root.
  1647. static_cast<PathTypeHandlerBase*>(typeHandler)->ResetTypeHandler(intArray);
  1648. }
  1649. else
  1650. {
  1651. intArray->ChangeType();
  1652. }
  1653. }
  1654. intArray->GetType()->SetTypeId(TypeIds_Array);
  1655. }
  1656. if (CrossSite::IsCrossSiteObjectTyped(intArray))
  1657. {
  1658. Assert(VirtualTableInfo<CrossSiteObject<JavascriptNativeIntArray>>::HasVirtualTable(intArray));
  1659. VirtualTableInfo<CrossSiteObject<JavascriptArray>>::SetVirtualTable(intArray);
  1660. }
  1661. else
  1662. {
  1663. Assert(VirtualTableInfo<JavascriptNativeIntArray>::HasVirtualTable(intArray));
  1664. VirtualTableInfo<JavascriptArray>::SetVirtualTable(intArray);
  1665. }
  1666. return intArray;
  1667. }
  1668. JavascriptArray *JavascriptNativeIntArray::ToVarArray(JavascriptNativeIntArray *intArray)
  1669. {
  1670. #if ENABLE_PROFILE_INFO
  1671. ArrayCallSiteInfo *arrayInfo = intArray->GetArrayCallSiteInfo();
  1672. if (arrayInfo)
  1673. {
  1674. #if DBG
  1675. Js::JavascriptStackWalker walker(intArray->GetScriptContext());
  1676. Js::JavascriptFunction* caller = NULL;
  1677. bool foundScriptCaller = false;
  1678. while(walker.GetCaller(&caller))
  1679. {
  1680. if(caller != NULL && Js::ScriptFunction::Is(caller))
  1681. {
  1682. foundScriptCaller = true;
  1683. break;
  1684. }
  1685. }
  1686. if(foundScriptCaller)
  1687. {
  1688. Assert(caller);
  1689. Assert(caller->GetFunctionBody());
  1690. if(PHASE_TRACE(Js::NativeArrayConversionPhase, caller->GetFunctionBody()))
  1691. {
  1692. Output::Print(_u("Conversion: Int array to Var array ArrayCreationFunctionNumber:%2d CallSiteNumber:%2d \n"), arrayInfo->functionNumber, arrayInfo->callSiteNumber);
  1693. Output::Flush();
  1694. }
  1695. }
  1696. else
  1697. {
  1698. if(PHASE_TRACE1(Js::NativeArrayConversionPhase))
  1699. {
  1700. Output::Print(_u("Conversion: Int array to Var array across ScriptContexts"));
  1701. Output::Flush();
  1702. }
  1703. }
  1704. #else
  1705. if(PHASE_TRACE1(Js::NativeArrayConversionPhase))
  1706. {
  1707. Output::Print(_u("Conversion: Int array to Var array"));
  1708. Output::Flush();
  1709. }
  1710. #endif
  1711. arrayInfo->SetIsNotNativeArray();
  1712. }
  1713. #endif
  1714. intArray->ClearArrayCallSiteIndex();
  1715. return ConvertToVarArray(intArray);
  1716. }
  1717. DynamicType * JavascriptNativeFloatArray::GetInitialType(ScriptContext * scriptContext)
  1718. {
  1719. return scriptContext->GetLibrary()->GetNativeFloatArrayType();
  1720. }
  1721. /*
  1722. * JavascriptNativeFloatArray::ConvertToVarArray
  1723. * This function only converts all Float elements to Var elements in an array.
  1724. * DynamicProfileInfo of the array is not updated in this function.
  1725. */
  1726. JavascriptArray *JavascriptNativeFloatArray::ConvertToVarArray(JavascriptNativeFloatArray *fArray)
  1727. {
  1728. // We can't be growing the size of the element.
  1729. Assert(sizeof(double) >= sizeof(Var));
  1730. uint32 shrinkFactor = sizeof(double) / sizeof(Var);
  1731. ScriptContext *scriptContext = fArray->GetScriptContext();
  1732. Recycler *recycler = scriptContext->GetRecycler();
  1733. SparseArraySegmentBase *seg, *nextSeg, *prevSeg = nullptr;
  1734. for (seg = fArray->head; seg; seg = nextSeg)
  1735. {
  1736. nextSeg = seg->next;
  1737. if (seg->size == 0)
  1738. {
  1739. continue;
  1740. }
  1741. uint32 left = seg->left;
  1742. uint32 length = seg->length;
  1743. SparseArraySegment<Var> *newSeg;
  1744. if (seg->next == nullptr && SparseArraySegmentBase::IsLeafSegment(seg, recycler))
  1745. {
  1746. // The old segment is not scanned by the recycler, so we need a new one to hold vars.
  1747. newSeg =
  1748. SparseArraySegment<Var>::AllocateSegment(recycler, left, length, nextSeg);
  1749. Assert((prevSeg == nullptr) == (seg == fArray->head));
  1750. newSeg->next = nextSeg;
  1751. fArray->LinkSegments((SparseArraySegment<Var>*)prevSeg, newSeg);
  1752. if (fArray->GetLastUsedSegment() == seg)
  1753. {
  1754. fArray->SetLastUsedSegment(newSeg);
  1755. }
  1756. prevSeg = newSeg;
  1757. SegmentBTree * segmentMap = fArray->GetSegmentMap();
  1758. if (segmentMap)
  1759. {
  1760. segmentMap->SwapSegment(left, seg, newSeg);
  1761. }
  1762. }
  1763. else
  1764. {
  1765. newSeg = (SparseArraySegment<Var>*)seg;
  1766. prevSeg = seg;
  1767. if (shrinkFactor != 1)
  1768. {
  1769. uint32 newSize = seg->size * shrinkFactor;
  1770. uint32 limit;
  1771. if (seg->next)
  1772. {
  1773. limit = seg->next->left;
  1774. }
  1775. else
  1776. {
  1777. limit = JavascriptArray::MaxArrayLength;
  1778. }
  1779. seg->size = min(newSize, limit - seg->left);
  1780. }
  1781. }
  1782. uint32 i;
  1783. for (i = 0; i < seg->length; i++)
  1784. {
  1785. if (SparseArraySegment<double>::IsMissingItem(&((SparseArraySegment<double>*)seg)->elements[i]))
  1786. {
  1787. if (seg == newSeg)
  1788. {
  1789. newSeg->elements[i] = (Var)JavascriptArray::MissingItem;
  1790. }
  1791. Assert(newSeg->elements[i] == (Var)JavascriptArray::MissingItem);
  1792. }
  1793. else if (*(uint64*)&(((SparseArraySegment<double>*)seg)->elements[i]) == 0ull)
  1794. {
  1795. newSeg->elements[i] = TaggedInt::ToVarUnchecked(0);
  1796. }
  1797. else
  1798. {
  1799. int32 ival;
  1800. double dval = ((SparseArraySegment<double>*)seg)->elements[i];
  1801. if (JavascriptNumber::TryGetInt32Value(dval, &ival) && !TaggedInt::IsOverflow(ival))
  1802. {
  1803. newSeg->elements[i] = TaggedInt::ToVarUnchecked(ival);
  1804. }
  1805. else
  1806. {
  1807. newSeg->elements[i] = JavascriptNumber::ToVarWithCheck(dval, scriptContext);
  1808. }
  1809. }
  1810. }
  1811. if (seg == newSeg && shrinkFactor != 1)
  1812. {
  1813. // Fill the remaining slots.
  1814. newSeg->FillSegmentBuffer(i, seg->size);
  1815. }
  1816. }
  1817. if (fArray->GetType() == scriptContext->GetLibrary()->GetNativeFloatArrayType())
  1818. {
  1819. fArray->type = scriptContext->GetLibrary()->GetArrayType();
  1820. }
  1821. else
  1822. {
  1823. if (fArray->GetDynamicType()->GetIsLocked())
  1824. {
  1825. DynamicTypeHandler *typeHandler = fArray->GetDynamicType()->GetTypeHandler();
  1826. if (typeHandler->IsPathTypeHandler())
  1827. {
  1828. // We can't allow a type with the new type ID to be promoted to the old type.
  1829. // So go to a dictionary type handler, which will orphan the new type.
  1830. // This should be a corner case, so the inability to share the new type is unlikely to matter.
  1831. // If it does matter, try building a path from the new type's built-in root.
  1832. static_cast<PathTypeHandlerBase*>(typeHandler)->ResetTypeHandler(fArray);
  1833. }
  1834. else
  1835. {
  1836. fArray->ChangeType();
  1837. }
  1838. }
  1839. fArray->GetType()->SetTypeId(TypeIds_Array);
  1840. }
  1841. if (CrossSite::IsCrossSiteObjectTyped(fArray))
  1842. {
  1843. Assert(VirtualTableInfo<CrossSiteObject<JavascriptNativeFloatArray>>::HasVirtualTable(fArray));
  1844. VirtualTableInfo<CrossSiteObject<JavascriptArray>>::SetVirtualTable(fArray);
  1845. }
  1846. else
  1847. {
  1848. Assert(VirtualTableInfo<JavascriptNativeFloatArray>::HasVirtualTable(fArray));
  1849. VirtualTableInfo<JavascriptArray>::SetVirtualTable(fArray);
  1850. }
  1851. return fArray;
  1852. }
  1853. JavascriptArray *JavascriptNativeFloatArray::ToVarArray(JavascriptNativeFloatArray *fArray)
  1854. {
  1855. #if ENABLE_PROFILE_INFO
  1856. ArrayCallSiteInfo *arrayInfo = fArray->GetArrayCallSiteInfo();
  1857. if (arrayInfo)
  1858. {
  1859. #if DBG
  1860. Js::JavascriptStackWalker walker(fArray->GetScriptContext());
  1861. Js::JavascriptFunction* caller = NULL;
  1862. bool foundScriptCaller = false;
  1863. while(walker.GetCaller(&caller))
  1864. {
  1865. if(caller != NULL && Js::ScriptFunction::Is(caller))
  1866. {
  1867. foundScriptCaller = true;
  1868. break;
  1869. }
  1870. }
  1871. if(foundScriptCaller)
  1872. {
  1873. Assert(caller);
  1874. Assert(caller->GetFunctionBody());
  1875. if(PHASE_TRACE(Js::NativeArrayConversionPhase, caller->GetFunctionBody()))
  1876. {
  1877. Output::Print(_u("Conversion: Float array to Var array ArrayCreationFunctionNumber:%2d CallSiteNumber:%2d \n"), arrayInfo->functionNumber, arrayInfo->callSiteNumber);
  1878. Output::Flush();
  1879. }
  1880. }
  1881. else
  1882. {
  1883. if(PHASE_TRACE1(Js::NativeArrayConversionPhase))
  1884. {
  1885. Output::Print(_u("Conversion: Float array to Var array across ScriptContexts"));
  1886. Output::Flush();
  1887. }
  1888. }
  1889. #else
  1890. if(PHASE_TRACE1(Js::NativeArrayConversionPhase))
  1891. {
  1892. Output::Print(_u("Conversion: Float array to Var array"));
  1893. Output::Flush();
  1894. }
  1895. #endif
  1896. if(fArray->GetScriptContext()->IsScriptContextInNonDebugMode())
  1897. {
  1898. Assert(!arrayInfo->IsNativeIntArray());
  1899. }
  1900. arrayInfo->SetIsNotNativeArray();
  1901. }
  1902. #endif
  1903. fArray->ClearArrayCallSiteIndex();
  1904. return ConvertToVarArray(fArray);
  1905. }
  1906. // Convert Var to index in the Array.
  1907. // Note: Spec calls out a few rules for these parameters:
  1908. // 1. if (arg > length) { return length; }
  1909. // clamp to length, not length-1
  1910. // 2. if (arg < 0) { return max(0, length + arg); }
  1911. // treat negative arg as index from the end of the array (with -1 mapping to length-1)
  1912. // Effectively, this function will return a value between 0 and length, inclusive.
  1913. int64 JavascriptArray::GetIndexFromVar(Js::Var arg, int64 length, ScriptContext* scriptContext)
  1914. {
  1915. int64 index;
  1916. if (TaggedInt::Is(arg))
  1917. {
  1918. int intValue = TaggedInt::ToInt32(arg);
  1919. if (intValue < 0)
  1920. {
  1921. index = max<int64>(0, length + intValue);
  1922. }
  1923. else
  1924. {
  1925. index = intValue;
  1926. }
  1927. if (index > length)
  1928. {
  1929. index = length;
  1930. }
  1931. }
  1932. else
  1933. {
  1934. double doubleValue = JavascriptConversion::ToInteger(arg, scriptContext);
  1935. // Handle the Number.POSITIVE_INFINITY case
  1936. if (doubleValue > length)
  1937. {
  1938. return length;
  1939. }
  1940. index = NumberUtilities::TryToInt64(doubleValue);
  1941. if (index < 0)
  1942. {
  1943. index = max<int64>(0, index + length);
  1944. }
  1945. }
  1946. return index;
  1947. }
  1948. TypeId JavascriptArray::OP_SetNativeIntElementC(JavascriptNativeIntArray *arr, uint32 index, Var value, ScriptContext *scriptContext)
  1949. {
  1950. int32 iValue;
  1951. double dValue;
  1952. TypeId typeId = arr->TrySetNativeIntArrayItem(value, &iValue, &dValue);
  1953. if (typeId == TypeIds_NativeIntArray)
  1954. {
  1955. arr->SetArrayLiteralItem(index, iValue);
  1956. }
  1957. else if (typeId == TypeIds_NativeFloatArray)
  1958. {
  1959. arr->SetArrayLiteralItem(index, dValue);
  1960. }
  1961. else
  1962. {
  1963. arr->SetArrayLiteralItem(index, value);
  1964. }
  1965. return typeId;
  1966. }
  1967. TypeId JavascriptArray::OP_SetNativeFloatElementC(JavascriptNativeFloatArray *arr, uint32 index, Var value, ScriptContext *scriptContext)
  1968. {
  1969. double dValue;
  1970. TypeId typeId = arr->TrySetNativeFloatArrayItem(value, &dValue);
  1971. if (typeId == TypeIds_NativeFloatArray)
  1972. {
  1973. arr->SetArrayLiteralItem(index, dValue);
  1974. }
  1975. else
  1976. {
  1977. arr->SetArrayLiteralItem(index, value);
  1978. }
  1979. return typeId;
  1980. }
  1981. template<typename T>
  1982. void JavascriptArray::SetArrayLiteralItem(uint32 index, T value)
  1983. {
  1984. SparseArraySegment<T> * segment = (SparseArraySegment<T>*)this->head;
  1985. Assert(segment->left == 0);
  1986. Assert(index < segment->length);
  1987. segment->elements[index] = value;
  1988. }
  1989. void JavascriptNativeIntArray::SetIsPrototype()
  1990. {
  1991. // Force the array to be non-native to simplify inspection, filling from proto, etc.
  1992. ToVarArray(this);
  1993. __super::SetIsPrototype();
  1994. }
  1995. void JavascriptNativeFloatArray::SetIsPrototype()
  1996. {
  1997. // Force the array to be non-native to simplify inspection, filling from proto, etc.
  1998. ToVarArray(this);
  1999. __super::SetIsPrototype();
  2000. }
  2001. #if ENABLE_PROFILE_INFO
  2002. ArrayCallSiteInfo *JavascriptNativeArray::GetArrayCallSiteInfo()
  2003. {
  2004. RecyclerWeakReference<FunctionBody> *weakRef = this->weakRefToFuncBody;
  2005. if (weakRef)
  2006. {
  2007. FunctionBody *functionBody = weakRef->Get();
  2008. if (functionBody)
  2009. {
  2010. if (functionBody->HasDynamicProfileInfo())
  2011. {
  2012. Js::ProfileId profileId = this->GetArrayCallSiteIndex();
  2013. if (profileId < functionBody->GetProfiledArrayCallSiteCount())
  2014. {
  2015. return functionBody->GetAnyDynamicProfileInfo()->GetArrayCallSiteInfo(functionBody, profileId);
  2016. }
  2017. }
  2018. }
  2019. else
  2020. {
  2021. this->ClearArrayCallSiteIndex();
  2022. }
  2023. }
  2024. return nullptr;
  2025. }
  2026. void JavascriptNativeArray::SetArrayProfileInfo(RecyclerWeakReference<FunctionBody> *weakRef, ArrayCallSiteInfo *arrayInfo)
  2027. {
  2028. Assert(weakRef);
  2029. FunctionBody *functionBody = weakRef->Get();
  2030. if (functionBody && functionBody->HasDynamicProfileInfo())
  2031. {
  2032. ArrayCallSiteInfo *baseInfo = functionBody->GetAnyDynamicProfileInfo()->GetArrayCallSiteInfo(functionBody, 0);
  2033. Js::ProfileId index = (Js::ProfileId)(arrayInfo - baseInfo);
  2034. Assert(index < functionBody->GetProfiledArrayCallSiteCount());
  2035. SetArrayCallSite(index, weakRef);
  2036. }
  2037. }
  2038. void JavascriptNativeArray::CopyArrayProfileInfo(Js::JavascriptNativeArray* baseArray)
  2039. {
  2040. if (baseArray->weakRefToFuncBody)
  2041. {
  2042. if (baseArray->weakRefToFuncBody->Get())
  2043. {
  2044. SetArrayCallSite(baseArray->GetArrayCallSiteIndex(), baseArray->weakRefToFuncBody);
  2045. }
  2046. else
  2047. {
  2048. baseArray->ClearArrayCallSiteIndex();
  2049. }
  2050. }
  2051. }
  2052. #endif
  2053. Var JavascriptNativeArray::FindMinOrMax(Js::ScriptContext * scriptContext, bool findMax)
  2054. {
  2055. if (JavascriptNativeIntArray::Is(this))
  2056. {
  2057. return this->FindMinOrMax<int32, false>(scriptContext, findMax);
  2058. }
  2059. else
  2060. {
  2061. return this->FindMinOrMax<double, true>(scriptContext, findMax);
  2062. }
  2063. }
  2064. template <typename T, bool checkNaNAndNegZero>
  2065. Var JavascriptNativeArray::FindMinOrMax(Js::ScriptContext * scriptContext, bool findMax)
  2066. {
  2067. AssertMsg(this->HasNoMissingValues(), "Fastpath is only for arrays with one segment and no missing values");
  2068. uint len = this->GetLength();
  2069. Js::SparseArraySegment<T>* headSegment = ((Js::SparseArraySegment<T>*)this->GetHead());
  2070. uint headSegLen = headSegment->length;
  2071. Assert(headSegLen == len);
  2072. if (headSegment->next == nullptr)
  2073. {
  2074. T currentRes = headSegment->elements[0];
  2075. for (uint i = 0; i < headSegLen; i++)
  2076. {
  2077. T compare = headSegment->elements[i];
  2078. if (checkNaNAndNegZero && JavascriptNumber::IsNan(double(compare)))
  2079. {
  2080. return scriptContext->GetLibrary()->GetNaN();
  2081. }
  2082. if (findMax ? currentRes < compare : currentRes > compare ||
  2083. (checkNaNAndNegZero && compare == 0 && Js::JavascriptNumber::IsNegZero(double(currentRes))))
  2084. {
  2085. currentRes = compare;
  2086. }
  2087. }
  2088. return Js::JavascriptNumber::ToVarNoCheck(currentRes, scriptContext);
  2089. }
  2090. else
  2091. {
  2092. AssertMsg(false, "FindMinOrMax currently supports native arrays with only one segment");
  2093. Throw::FatalInternalError();
  2094. }
  2095. }
  2096. SparseArraySegmentBase * JavascriptArray::GetLastUsedSegment() const
  2097. {
  2098. return (HasSegmentMap() ? segmentUnion.segmentBTreeRoot->lastUsedSegment : segmentUnion.lastUsedSegment);
  2099. }
  2100. void JavascriptArray::SetHeadAndLastUsedSegment(SparseArraySegmentBase * segment)
  2101. {
  2102. Assert(!HasSegmentMap());
  2103. this->head = this->segmentUnion.lastUsedSegment = segment;
  2104. }
  2105. void JavascriptArray::SetLastUsedSegment(SparseArraySegmentBase * segment)
  2106. {
  2107. if (HasSegmentMap())
  2108. {
  2109. this->segmentUnion.segmentBTreeRoot->lastUsedSegment = segment;
  2110. }
  2111. else
  2112. {
  2113. this->segmentUnion.lastUsedSegment = segment;
  2114. }
  2115. }
  2116. bool JavascriptArray::HasSegmentMap() const
  2117. {
  2118. return !!(GetFlags() & DynamicObjectFlags::HasSegmentMap);
  2119. }
  2120. SegmentBTreeRoot * JavascriptArray::GetSegmentMap() const
  2121. {
  2122. return (HasSegmentMap() ? segmentUnion.segmentBTreeRoot : nullptr);
  2123. }
  2124. void JavascriptArray::SetSegmentMap(SegmentBTreeRoot * segmentMap)
  2125. {
  2126. Assert(!HasSegmentMap());
  2127. SparseArraySegmentBase * lastUsedSeg = this->segmentUnion.lastUsedSegment;
  2128. SetFlags(GetFlags() | DynamicObjectFlags::HasSegmentMap);
  2129. segmentUnion.segmentBTreeRoot = segmentMap;
  2130. segmentMap->lastUsedSegment = lastUsedSeg;
  2131. }
  2132. void JavascriptArray::ClearSegmentMap()
  2133. {
  2134. if (HasSegmentMap())
  2135. {
  2136. SetFlags(GetFlags() & ~DynamicObjectFlags::HasSegmentMap);
  2137. SparseArraySegmentBase * lastUsedSeg = segmentUnion.segmentBTreeRoot->lastUsedSegment;
  2138. segmentUnion.segmentBTreeRoot = nullptr;
  2139. segmentUnion.lastUsedSegment = lastUsedSeg;
  2140. }
  2141. }
  2142. SegmentBTreeRoot * JavascriptArray::BuildSegmentMap()
  2143. {
  2144. Recycler* recycler = GetRecycler();
  2145. SegmentBTreeRoot* tmpSegmentMap = AllocatorNewStruct(Recycler, recycler, SegmentBTreeRoot);
  2146. ForEachSegment([recycler, tmpSegmentMap](SparseArraySegmentBase * current)
  2147. {
  2148. tmpSegmentMap->Add(recycler, current);
  2149. return false;
  2150. });
  2151. // There could be OOM during building segment map. Save to array only after its successful completion.
  2152. SetSegmentMap(tmpSegmentMap);
  2153. return tmpSegmentMap;
  2154. }
  2155. void JavascriptArray::TryAddToSegmentMap(Recycler* recycler, SparseArraySegmentBase* seg)
  2156. {
  2157. SegmentBTreeRoot * savedSegmentMap = GetSegmentMap();
  2158. if (savedSegmentMap)
  2159. {
  2160. //
  2161. // We could OOM and throw when adding to segmentMap, resulting in a corrupted segmentMap on this
  2162. // array. Set segmentMap to null temporarily to protect from this. It will be restored correctly
  2163. // if adding segment succeeds.
  2164. //
  2165. ClearSegmentMap();
  2166. savedSegmentMap->Add(recycler, seg);
  2167. SetSegmentMap(savedSegmentMap);
  2168. }
  2169. }
  2170. void JavascriptArray::InvalidateLastUsedSegment()
  2171. {
  2172. this->SetLastUsedSegment(this->head);
  2173. }
  2174. DescriptorFlags JavascriptArray::GetSetter(PropertyId propertyId, Var *setterValue, PropertyValueInfo* info, ScriptContext* requestContext)
  2175. {
  2176. DescriptorFlags flags;
  2177. if (GetSetterBuiltIns(propertyId, info, &flags))
  2178. {
  2179. return flags;
  2180. }
  2181. return __super::GetSetter(propertyId, setterValue, info, requestContext);
  2182. }
  2183. DescriptorFlags JavascriptArray::GetSetter(JavascriptString* propertyNameString, Var *setterValue, PropertyValueInfo* info, ScriptContext* requestContext)
  2184. {
  2185. DescriptorFlags flags;
  2186. PropertyRecord const* propertyRecord;
  2187. this->GetScriptContext()->FindPropertyRecord(propertyNameString, &propertyRecord);
  2188. if (propertyRecord != nullptr && GetSetterBuiltIns(propertyRecord->GetPropertyId(), info, &flags))
  2189. {
  2190. return flags;
  2191. }
  2192. return __super::GetSetter(propertyNameString, setterValue, info, requestContext);
  2193. }
  2194. bool JavascriptArray::GetSetterBuiltIns(PropertyId propertyId, PropertyValueInfo* info, DescriptorFlags* descriptorFlags)
  2195. {
  2196. if (propertyId == PropertyIds::length)
  2197. {
  2198. PropertyValueInfo::SetNoCache(info, this);
  2199. *descriptorFlags = WritableData;
  2200. return true;
  2201. }
  2202. return false;
  2203. }
  2204. SparseArraySegmentBase * JavascriptArray::GetBeginLookupSegment(uint32 index, const bool useSegmentMap) const
  2205. {
  2206. SparseArraySegmentBase *seg = nullptr;
  2207. SparseArraySegmentBase * lastUsedSeg = this->GetLastUsedSegment();
  2208. if (lastUsedSeg != nullptr && lastUsedSeg->left <= index)
  2209. {
  2210. seg = lastUsedSeg;
  2211. if(index - lastUsedSeg->left < lastUsedSeg->size)
  2212. {
  2213. return seg;
  2214. }
  2215. }
  2216. SegmentBTreeRoot * segmentMap = GetSegmentMap();
  2217. if(!useSegmentMap || !segmentMap)
  2218. {
  2219. return seg ? seg : this->head;
  2220. }
  2221. if(seg)
  2222. {
  2223. // If indexes are being accessed sequentially, check the segment after the last-used segment before checking the
  2224. // segment map, as it is likely to hit
  2225. SparseArraySegmentBase *const nextSeg = seg->next;
  2226. if(nextSeg)
  2227. {
  2228. if(index < nextSeg->left)
  2229. {
  2230. return seg;
  2231. }
  2232. else if(index - nextSeg->left < nextSeg->size)
  2233. {
  2234. return nextSeg;
  2235. }
  2236. }
  2237. }
  2238. SparseArraySegmentBase *matchOrNextSeg;
  2239. segmentMap->Find(index, seg, matchOrNextSeg);
  2240. return seg ? seg : matchOrNextSeg;
  2241. }
  2242. uint32 JavascriptArray::GetNextIndex(uint32 index) const
  2243. {
  2244. if (JavascriptNativeIntArray::Is((Var)this))
  2245. {
  2246. return this->GetNextIndexHelper<int32>(index);
  2247. }
  2248. else if (JavascriptNativeFloatArray::Is((Var)this))
  2249. {
  2250. return this->GetNextIndexHelper<double>(index);
  2251. }
  2252. return this->GetNextIndexHelper<Var>(index);
  2253. }
  2254. template<typename T>
  2255. uint32 JavascriptArray::GetNextIndexHelper(uint32 index) const
  2256. {
  2257. AssertMsg(this->head, "array head should never be null");
  2258. uint candidateIndex;
  2259. if (index == JavascriptArray::InvalidIndex)
  2260. {
  2261. candidateIndex = head->left;
  2262. }
  2263. else
  2264. {
  2265. candidateIndex = index + 1;
  2266. }
  2267. SparseArraySegment<T>* current = (SparseArraySegment<T>*)this->GetBeginLookupSegment(candidateIndex);
  2268. while (current != nullptr)
  2269. {
  2270. if ((current->left <= candidateIndex) && ((candidateIndex - current->left) < current->length))
  2271. {
  2272. for (uint i = candidateIndex - current->left; i < current->length; i++)
  2273. {
  2274. if (!SparseArraySegment<T>::IsMissingItem(&current->elements[i]))
  2275. {
  2276. return i + current->left;
  2277. }
  2278. }
  2279. }
  2280. current = (SparseArraySegment<T>*)current->next;
  2281. if (current != NULL)
  2282. {
  2283. if (candidateIndex < current->left)
  2284. {
  2285. candidateIndex = current->left;
  2286. }
  2287. }
  2288. }
  2289. return JavascriptArray::InvalidIndex;
  2290. }
  2291. // If new length > length, we just reset the length
  2292. // If new length < length, we need to remove the rest of the elements and segment
  2293. void JavascriptArray::SetLength(uint32 newLength)
  2294. {
  2295. if (newLength == length)
  2296. return;
  2297. if (head == EmptySegment)
  2298. {
  2299. // Do nothing to the segment.
  2300. }
  2301. else if (newLength == 0)
  2302. {
  2303. this->ClearElements(head, 0);
  2304. head->length = 0;
  2305. head->next = nullptr;
  2306. SetHasNoMissingValues();
  2307. ClearSegmentMap();
  2308. this->InvalidateLastUsedSegment();
  2309. }
  2310. else if (newLength < length)
  2311. {
  2312. // _ _ 2 3 _ _ 6 7 _ _
  2313. // SetLength(0)
  2314. // 0 <= left -> set *prev = null
  2315. // SetLength(2)
  2316. // 2 <= left -> set *prev = null
  2317. // SetLength(3)
  2318. // 3 !<= left; 3 <= right -> truncate to length - 1
  2319. // SetLength(5)
  2320. // 5 <=
  2321. SparseArraySegmentBase* next = GetBeginLookupSegment(newLength - 1); // head, or next.left < newLength
  2322. SparseArraySegmentBase** prev = &head;
  2323. while(next != nullptr)
  2324. {
  2325. if (newLength <= next->left)
  2326. {
  2327. ClearSegmentMap(); // truncate segments, null out segmentMap
  2328. *prev = nullptr;
  2329. break;
  2330. }
  2331. else if (newLength <= (next->left + next->length))
  2332. {
  2333. if (next->next)
  2334. {
  2335. ClearSegmentMap(); // Will truncate segments, null out segmentMap
  2336. }
  2337. uint32 newSegmentLength = newLength - next->left;
  2338. this->ClearElements(next, newSegmentLength);
  2339. next->next = nullptr;
  2340. next->length = newSegmentLength;
  2341. break;
  2342. }
  2343. else
  2344. {
  2345. prev = &next->next;
  2346. next = next->next;
  2347. }
  2348. }
  2349. this->InvalidateLastUsedSegment();
  2350. }
  2351. this->length = newLength;
  2352. #ifdef VALIDATE_ARRAY
  2353. ValidateArray();
  2354. #endif
  2355. }
  2356. BOOL JavascriptArray::SetLength(Var newLength)
  2357. {
  2358. ScriptContext *scriptContext;
  2359. if(TaggedInt::Is(newLength))
  2360. {
  2361. int32 lenValue = TaggedInt::ToInt32(newLength);
  2362. if (lenValue < 0)
  2363. {
  2364. scriptContext = GetScriptContext();
  2365. if (scriptContext->GetThreadContext()->RecordImplicitException())
  2366. {
  2367. JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthAssignIncorrect);
  2368. }
  2369. }
  2370. else
  2371. {
  2372. this->SetLength(lenValue);
  2373. }
  2374. return TRUE;
  2375. }
  2376. scriptContext = GetScriptContext();
  2377. uint32 uintValue = JavascriptConversion::ToUInt32(newLength, scriptContext);
  2378. double dblValue = JavascriptConversion::ToNumber(newLength, scriptContext);
  2379. if (dblValue == uintValue)
  2380. {
  2381. this->SetLength(uintValue);
  2382. }
  2383. else
  2384. {
  2385. ThreadContext* threadContext = scriptContext->GetThreadContext();
  2386. ImplicitCallFlags flags = threadContext->GetImplicitCallFlags();
  2387. if (flags != ImplicitCall_None && threadContext->IsDisableImplicitCall())
  2388. {
  2389. // We couldn't execute the implicit call(s) needed to convert the newLength to an integer.
  2390. // Do nothing and let the jitted code bail out.
  2391. return TRUE;
  2392. }
  2393. if (threadContext->RecordImplicitException())
  2394. {
  2395. JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthAssignIncorrect);
  2396. }
  2397. }
  2398. return TRUE;
  2399. }
  2400. void JavascriptArray::ClearElements(SparseArraySegmentBase *seg, uint32 newSegmentLength)
  2401. {
  2402. SparseArraySegment<Var>::ClearElements(((SparseArraySegment<Var>*)seg)->elements + newSegmentLength, seg->length - newSegmentLength);
  2403. }
  2404. void JavascriptNativeIntArray::ClearElements(SparseArraySegmentBase *seg, uint32 newSegmentLength)
  2405. {
  2406. SparseArraySegment<int32>::ClearElements(((SparseArraySegment<int32>*)seg)->elements + newSegmentLength, seg->length - newSegmentLength);
  2407. }
  2408. void JavascriptNativeFloatArray::ClearElements(SparseArraySegmentBase *seg, uint32 newSegmentLength)
  2409. {
  2410. SparseArraySegment<double>::ClearElements(((SparseArraySegment<double>*)seg)->elements + newSegmentLength, seg->length - newSegmentLength);
  2411. }
  2412. Var JavascriptArray::DirectGetItem(uint32 index)
  2413. {
  2414. SparseArraySegment<Var> *seg = (SparseArraySegment<Var>*)this->GetLastUsedSegment();
  2415. uint32 offset = index - seg->left;
  2416. if (index >= seg->left && offset < seg->length)
  2417. {
  2418. if (!SparseArraySegment<Var>::IsMissingItem(&seg->elements[offset]))
  2419. {
  2420. return seg->elements[offset];
  2421. }
  2422. }
  2423. Var element;
  2424. if (DirectGetItemAtFull(index, &element))
  2425. {
  2426. return element;
  2427. }
  2428. return GetType()->GetLibrary()->GetUndefined();
  2429. }
  2430. Var JavascriptNativeIntArray::DirectGetItem(uint32 index)
  2431. {
  2432. #if ENABLE_COPYONACCESS_ARRAY
  2433. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(this);
  2434. #endif
  2435. SparseArraySegment<int32> *seg = (SparseArraySegment<int32>*)this->GetLastUsedSegment();
  2436. uint32 offset = index - seg->left;
  2437. if (index >= seg->left && offset < seg->length)
  2438. {
  2439. if (!SparseArraySegment<int32>::IsMissingItem(&seg->elements[offset]))
  2440. {
  2441. return JavascriptNumber::ToVar(seg->elements[offset], GetScriptContext());
  2442. }
  2443. }
  2444. Var element;
  2445. if (DirectGetItemAtFull(index, &element))
  2446. {
  2447. return element;
  2448. }
  2449. return GetType()->GetLibrary()->GetUndefined();
  2450. }
  2451. Var JavascriptNativeFloatArray::DirectGetItem(uint32 index)
  2452. {
  2453. SparseArraySegment<double> *seg = (SparseArraySegment<double>*)this->GetLastUsedSegment();
  2454. uint32 offset = index - seg->left;
  2455. if (index >= seg->left && offset < seg->length)
  2456. {
  2457. if (!SparseArraySegment<double>::IsMissingItem(&seg->elements[offset]))
  2458. {
  2459. return JavascriptNumber::ToVarWithCheck(seg->elements[offset], GetScriptContext());
  2460. }
  2461. }
  2462. Var element;
  2463. if (DirectGetItemAtFull(index, &element))
  2464. {
  2465. return element;
  2466. }
  2467. return GetType()->GetLibrary()->GetUndefined();
  2468. }
  2469. Var JavascriptArray::DirectGetItem(JavascriptString *propName, ScriptContext* scriptContext)
  2470. {
  2471. PropertyRecord const * propertyRecord;
  2472. scriptContext->GetOrAddPropertyRecord(propName->GetString(), propName->GetLength(), &propertyRecord);
  2473. return JavascriptOperators::GetProperty(this, propertyRecord->GetPropertyId(), scriptContext, NULL);
  2474. }
  2475. BOOL JavascriptArray::DirectGetItemAtFull(uint32 index, Var* outVal)
  2476. {
  2477. if (this->DirectGetItemAt(index, outVal))
  2478. {
  2479. return TRUE;
  2480. }
  2481. ScriptContext* requestContext = type->GetScriptContext();
  2482. return JavascriptOperators::GetItem(this, this->GetPrototype(), index, outVal, requestContext);
  2483. }
  2484. //
  2485. // Link prev and current. If prev is NULL, make current the head segment.
  2486. //
  2487. void JavascriptArray::LinkSegmentsCommon(SparseArraySegmentBase* prev, SparseArraySegmentBase* current)
  2488. {
  2489. if (prev)
  2490. {
  2491. prev->next = current;
  2492. }
  2493. else
  2494. {
  2495. Assert(current);
  2496. head = current;
  2497. }
  2498. }
  2499. template<typename T>
  2500. BOOL JavascriptArray::DirectDeleteItemAt(uint32 itemIndex)
  2501. {
  2502. if (itemIndex >= length)
  2503. {
  2504. return true;
  2505. }
  2506. SparseArraySegment<T>* next = (SparseArraySegment<T>*)GetBeginLookupSegment(itemIndex);
  2507. while(next != nullptr && next->left <= itemIndex)
  2508. {
  2509. uint32 limit = next->left + next->length;
  2510. if (itemIndex < limit)
  2511. {
  2512. next->SetElement(GetRecycler(), itemIndex, SparseArraySegment<T>::GetMissingItem());
  2513. if(itemIndex - next->left == next->length - 1)
  2514. {
  2515. --next->length;
  2516. }
  2517. else if(next == head)
  2518. {
  2519. SetHasNoMissingValues(false);
  2520. }
  2521. break;
  2522. }
  2523. next = (SparseArraySegment<T>*)next->next;
  2524. }
  2525. #ifdef VALIDATE_ARRAY
  2526. ValidateArray();
  2527. #endif
  2528. return true;
  2529. }
  2530. template <> Var JavascriptArray::ConvertToIndex(BigIndex idxDest, ScriptContext* scriptContext)
  2531. {
  2532. return idxDest.ToNumber(scriptContext);
  2533. }
  2534. template <> uint32 JavascriptArray::ConvertToIndex(BigIndex idxDest, ScriptContext* scriptContext)
  2535. {
  2536. // Note this is only for setting Array length which is a uint32
  2537. return idxDest.IsSmallIndex() ? idxDest.GetSmallIndex() : UINT_MAX;
  2538. }
  2539. template <> Var JavascriptArray::ConvertToIndex(uint32 idxDest, ScriptContext* scriptContext)
  2540. {
  2541. return JavascriptNumber::ToVar(idxDest, scriptContext);
  2542. }
  2543. BOOL JavascriptArray::SetArrayLikeObjects(RecyclableObject* pDestObj, uint32 idxDest, Var aItem)
  2544. {
  2545. return pDestObj->SetItem(idxDest, aItem, Js::PropertyOperation_ThrowIfNotExtensible);
  2546. }
  2547. BOOL JavascriptArray::SetArrayLikeObjects(RecyclableObject* pDestObj, BigIndex idxDest, Var aItem)
  2548. {
  2549. ScriptContext* scriptContext = pDestObj->GetScriptContext();
  2550. if (idxDest.IsSmallIndex())
  2551. {
  2552. return pDestObj->SetItem(idxDest.GetSmallIndex(), aItem, Js::PropertyOperation_ThrowIfNotExtensible);
  2553. }
  2554. PropertyRecord const * propertyRecord;
  2555. JavascriptOperators::GetPropertyIdForInt(idxDest.GetBigIndex(), scriptContext, &propertyRecord);
  2556. return pDestObj->SetProperty(propertyRecord->GetPropertyId(), aItem, PropertyOperation_ThrowIfNotExtensible, nullptr);
  2557. }
  2558. template<typename T>
  2559. void JavascriptArray::ConcatArgs(RecyclableObject* pDestObj, TypeId* remoteTypeIds, Js::Arguments& args, ScriptContext* scriptContext, uint start, BigIndex startIdxDest, BOOL FirstPromotedItemIsSpreadable, BigIndex FirstPromotedItemLength)
  2560. {
  2561. // This never gets called.
  2562. Throw::InternalError();
  2563. }
  2564. //
  2565. // Helper for EntryConcat. Concat args or elements of arg arrays into dest array.
  2566. //
  2567. template<typename T>
  2568. void JavascriptArray::ConcatArgs(RecyclableObject* pDestObj, TypeId* remoteTypeIds, Js::Arguments& args, ScriptContext* scriptContext, uint start, uint startIdxDest, BOOL firstPromotedItemIsSpreadable, BigIndex firstPromotedItemLength)
  2569. {
  2570. JavascriptArray* pDestArray = nullptr;
  2571. if (JavascriptArray::Is(pDestObj))
  2572. {
  2573. pDestArray = JavascriptArray::FromVar(pDestObj);
  2574. }
  2575. T idxDest = startIdxDest;
  2576. for (uint idxArg = start; idxArg < args.Info.Count; idxArg++)
  2577. {
  2578. Var aItem = args[idxArg];
  2579. BOOL spreadable = false;
  2580. if (scriptContext->GetConfig()->IsES6IsConcatSpreadableEnabled())
  2581. {
  2582. // firstPromotedItemIsSpreadable is ONLY used to resume after a type promotion from uint32 to uint64
  2583. // we do this because calls to IsConcatSpreadable are observable (a big deal for proxies) and we don't
  2584. // want to do the work a second time as soon as we record the length we clear the flag.
  2585. spreadable = firstPromotedItemIsSpreadable || JavascriptOperators::IsConcatSpreadable(aItem);
  2586. if (!spreadable)
  2587. {
  2588. JavascriptArray::SetConcatItem<T>(aItem, idxArg, pDestArray, pDestObj, idxDest, scriptContext);
  2589. ++idxDest;
  2590. continue;
  2591. }
  2592. }
  2593. if (pDestArray && JavascriptArray::IsDirectAccessArray(aItem) && JavascriptArray::IsDirectAccessArray(pDestArray)) // Fast path
  2594. {
  2595. if (JavascriptNativeIntArray::Is(aItem))
  2596. {
  2597. JavascriptNativeIntArray *pItemArray = JavascriptNativeIntArray::FromVar(aItem);
  2598. CopyNativeIntArrayElementsToVar(pDestArray, idxDest, pItemArray);
  2599. idxDest = idxDest + pItemArray->length;
  2600. }
  2601. else if (JavascriptNativeFloatArray::Is(aItem))
  2602. {
  2603. JavascriptNativeFloatArray *pItemArray = JavascriptNativeFloatArray::FromVar(aItem);
  2604. CopyNativeFloatArrayElementsToVar(pDestArray, idxDest, pItemArray);
  2605. idxDest = idxDest + pItemArray->length;
  2606. }
  2607. else
  2608. {
  2609. JavascriptArray* pItemArray = JavascriptArray::FromVar(aItem);
  2610. CopyArrayElements(pDestArray, idxDest, pItemArray);
  2611. idxDest = idxDest + pItemArray->length;
  2612. }
  2613. }
  2614. else
  2615. {
  2616. // Flatten if other array or remote array (marked with TypeIds_Array)
  2617. if (DynamicObject::IsAnyArray(aItem) || remoteTypeIds[idxArg] == TypeIds_Array || spreadable)
  2618. {
  2619. //CONSIDER: enumerating remote array instead of walking all indices
  2620. BigIndex length;
  2621. if (firstPromotedItemIsSpreadable)
  2622. {
  2623. firstPromotedItemIsSpreadable = false;
  2624. length = firstPromotedItemLength;
  2625. }
  2626. else if (scriptContext->GetConfig()->IsES6ToLengthEnabled())
  2627. {
  2628. // we can cast to uin64 without fear of converting negative numbers to large positive ones
  2629. // from int64 because ToLength makes negative lengths 0
  2630. length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(aItem, scriptContext), scriptContext);
  2631. }
  2632. else
  2633. {
  2634. length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(aItem, scriptContext), scriptContext);
  2635. }
  2636. if (PromoteToBigIndex(length,idxDest))
  2637. {
  2638. // This is a special case for spreadable objects. We do not pre-calculate the length
  2639. // in EntryConcat like we do with Arrays because a getProperty on an object Length
  2640. // is observable. The result is we have to check for overflows separately for
  2641. // spreadable objects and promote to a bigger index type when we find them.
  2642. ConcatArgs<BigIndex>(pDestArray, remoteTypeIds, args, scriptContext, idxArg, idxDest, /*firstPromotedItemIsSpreadable*/true, length);
  2643. return;
  2644. }
  2645. if (length + idxDest > FiftyThirdPowerOfTwoMinusOne) // 2^53-1: from ECMA 22.1.3.1 Array.prototype.concat(...arguments)
  2646. {
  2647. JavascriptError::ThrowTypeError(scriptContext, JSERR_IllegalArraySizeAndLength);
  2648. }
  2649. RecyclableObject* itemObject = RecyclableObject::FromVar(aItem);
  2650. Var subItem;
  2651. uint32 lengthToUin32Max = length.IsSmallIndex() ? length.GetSmallIndex() : MaxArrayLength;
  2652. for (uint32 idxSubItem = 0u; idxSubItem < lengthToUin32Max; ++idxSubItem)
  2653. {
  2654. if (JavascriptOperators::HasItem(itemObject, idxSubItem))
  2655. {
  2656. subItem = JavascriptOperators::GetItem(itemObject, idxSubItem, scriptContext);
  2657. if (pDestArray)
  2658. {
  2659. pDestArray->DirectSetItemAt(idxDest, subItem);
  2660. }
  2661. else
  2662. {
  2663. SetArrayLikeObjects(pDestObj, idxDest, subItem);
  2664. }
  2665. }
  2666. ++idxDest;
  2667. }
  2668. for (BigIndex idxSubItem = MaxArrayLength; idxSubItem < length; ++idxSubItem)
  2669. {
  2670. PropertyRecord const * propertyRecord;
  2671. JavascriptOperators::GetPropertyIdForInt(idxSubItem.GetBigIndex(), scriptContext, &propertyRecord);
  2672. if (JavascriptOperators::HasProperty(itemObject,propertyRecord->GetPropertyId()))
  2673. {
  2674. subItem = JavascriptOperators::GetProperty(itemObject, propertyRecord->GetPropertyId(), scriptContext);
  2675. if (pDestArray)
  2676. {
  2677. pDestArray->DirectSetItemAt(idxDest, subItem);
  2678. }
  2679. else
  2680. {
  2681. SetArrayLikeObjects(pDestObj, idxDest, subItem);
  2682. }
  2683. }
  2684. ++idxDest;
  2685. }
  2686. }
  2687. else // concat 1 item
  2688. {
  2689. JavascriptArray::SetConcatItem<T>(aItem, idxArg, pDestArray, pDestObj, idxDest, scriptContext);
  2690. ++idxDest;
  2691. }
  2692. }
  2693. }
  2694. if (!pDestArray)
  2695. {
  2696. pDestObj->SetProperty(PropertyIds::length, ConvertToIndex<T, Var>(idxDest, scriptContext), Js::PropertyOperation_None, nullptr);
  2697. }
  2698. else if (pDestArray->GetLength() != ConvertToIndex<T, uint32>(idxDest, scriptContext))
  2699. {
  2700. pDestArray->SetLength(ConvertToIndex<T, uint32>(idxDest, scriptContext));
  2701. }
  2702. }
  2703. bool JavascriptArray::PromoteToBigIndex(BigIndex lhs, BigIndex rhs)
  2704. {
  2705. return false; // already a big index
  2706. }
  2707. bool JavascriptArray::PromoteToBigIndex(BigIndex lhs, uint32 rhs)
  2708. {
  2709. ::Math::RecordOverflowPolicy destLengthOverflow;
  2710. if (lhs.IsSmallIndex())
  2711. {
  2712. UInt32Math::Add(lhs.GetSmallIndex(), rhs, destLengthOverflow);
  2713. return destLengthOverflow.HasOverflowed();
  2714. }
  2715. return true;
  2716. }
  2717. JavascriptArray* JavascriptArray::ConcatIntArgs(JavascriptNativeIntArray* pDestArray, TypeId *remoteTypeIds, Js::Arguments& args, ScriptContext* scriptContext)
  2718. {
  2719. uint idxDest = 0u;
  2720. for (uint idxArg = 0; idxArg < args.Info.Count; idxArg++)
  2721. {
  2722. Var aItem = args[idxArg];
  2723. if (scriptContext->GetConfig()->IsES6IsConcatSpreadableEnabled() && !JavascriptOperators::IsConcatSpreadable(aItem))
  2724. {
  2725. pDestArray->SetItem(idxDest, aItem, PropertyOperation_ThrowIfNotExtensible);
  2726. idxDest = idxDest + 1;
  2727. if (!JavascriptNativeIntArray::Is(pDestArray)) // SetItem could convert pDestArray to a var array if aItem is not an integer if so fall back
  2728. {
  2729. ConcatArgs<uint>(pDestArray, remoteTypeIds, args, scriptContext, idxArg + 1, idxDest);
  2730. return pDestArray;
  2731. }
  2732. continue;
  2733. }
  2734. if (JavascriptNativeIntArray::Is(aItem)) // Fast path
  2735. {
  2736. JavascriptNativeIntArray* pItemArray = JavascriptNativeIntArray::FromVar(aItem);
  2737. bool converted = CopyNativeIntArrayElements(pDestArray, idxDest, pItemArray);
  2738. idxDest = idxDest + pItemArray->length;
  2739. if (converted)
  2740. {
  2741. // Copying the last array forced a conversion, so switch over to the var version
  2742. // to finish.
  2743. ConcatArgs<uint>(pDestArray, remoteTypeIds, args, scriptContext, idxArg + 1, idxDest);
  2744. return pDestArray;
  2745. }
  2746. }
  2747. else if (!JavascriptArray::IsAnyArray(aItem) && remoteTypeIds[idxArg] != TypeIds_Array)
  2748. {
  2749. if (TaggedInt::Is(aItem))
  2750. {
  2751. pDestArray->DirectSetItemAt(idxDest, TaggedInt::ToInt32(aItem));
  2752. }
  2753. else
  2754. {
  2755. #if DBG
  2756. int32 int32Value;
  2757. Assert(
  2758. JavascriptNumber::TryGetInt32Value(JavascriptNumber::GetValue(aItem), &int32Value) &&
  2759. !SparseArraySegment<int32>::IsMissingItem(&int32Value));
  2760. #endif
  2761. pDestArray->DirectSetItemAt(idxDest, static_cast<int32>(JavascriptNumber::GetValue(aItem)));
  2762. }
  2763. ++idxDest;
  2764. }
  2765. else
  2766. {
  2767. JavascriptArray *pVarDestArray = JavascriptNativeIntArray::ConvertToVarArray(pDestArray);
  2768. ConcatArgs<uint>(pVarDestArray, remoteTypeIds, args, scriptContext, idxArg, idxDest);
  2769. return pVarDestArray;
  2770. }
  2771. }
  2772. if (pDestArray->GetLength() != idxDest)
  2773. {
  2774. pDestArray->SetLength(idxDest);
  2775. }
  2776. return pDestArray;
  2777. }
  2778. JavascriptArray* JavascriptArray::ConcatFloatArgs(JavascriptNativeFloatArray* pDestArray, TypeId *remoteTypeIds, Js::Arguments& args, ScriptContext* scriptContext)
  2779. {
  2780. uint idxDest = 0u;
  2781. for (uint idxArg = 0; idxArg < args.Info.Count; idxArg++)
  2782. {
  2783. Var aItem = args[idxArg];
  2784. if (scriptContext->GetConfig()->IsES6IsConcatSpreadableEnabled() && !JavascriptOperators::IsConcatSpreadable(aItem))
  2785. {
  2786. pDestArray->SetItem(idxDest, aItem, PropertyOperation_ThrowIfNotExtensible);
  2787. idxDest = idxDest + 1;
  2788. if (!JavascriptNativeFloatArray::Is(pDestArray)) // SetItem could convert pDestArray to a var array if aItem is not an integer if so fall back
  2789. {
  2790. ConcatArgs<uint>(pDestArray, remoteTypeIds, args, scriptContext, idxArg + 1, idxDest);
  2791. return pDestArray;
  2792. }
  2793. continue;
  2794. }
  2795. bool converted;
  2796. if (JavascriptArray::IsAnyArray(aItem))
  2797. {
  2798. if (JavascriptNativeIntArray::Is(aItem)) // Fast path
  2799. {
  2800. JavascriptNativeIntArray *pIntArray = JavascriptNativeIntArray::FromVar(aItem);
  2801. converted = CopyNativeIntArrayElementsToFloat(pDestArray, idxDest, pIntArray);
  2802. idxDest = idxDest + pIntArray->length;
  2803. }
  2804. else if (JavascriptNativeFloatArray::Is(aItem))
  2805. {
  2806. JavascriptNativeFloatArray* pItemArray = JavascriptNativeFloatArray::FromVar(aItem);
  2807. converted = CopyNativeFloatArrayElements(pDestArray, idxDest, pItemArray);
  2808. idxDest = idxDest + pItemArray->length;
  2809. }
  2810. else
  2811. {
  2812. JavascriptArray *pVarDestArray = JavascriptNativeFloatArray::ConvertToVarArray(pDestArray);
  2813. ConcatArgs<uint>(pVarDestArray, remoteTypeIds, args, scriptContext, idxArg, idxDest);
  2814. return pVarDestArray;
  2815. }
  2816. if (converted)
  2817. {
  2818. // Copying the last array forced a conversion, so switch over to the var version
  2819. // to finish.
  2820. ConcatArgs<uint>(pDestArray, remoteTypeIds, args, scriptContext, idxArg + 1, idxDest);
  2821. return pDestArray;
  2822. }
  2823. }
  2824. else
  2825. {
  2826. Assert(!JavascriptArray::IsAnyArray(aItem) && remoteTypeIds[idxArg] != TypeIds_Array);
  2827. if (TaggedInt::Is(aItem))
  2828. {
  2829. pDestArray->DirectSetItemAt(idxDest, (double)TaggedInt::ToInt32(aItem));
  2830. }
  2831. else
  2832. {
  2833. Assert(JavascriptNumber::Is(aItem));
  2834. pDestArray->DirectSetItemAt(idxDest, JavascriptNumber::GetValue(aItem));
  2835. }
  2836. ++idxDest;
  2837. }
  2838. }
  2839. if (pDestArray->GetLength() != idxDest)
  2840. {
  2841. pDestArray->SetLength(idxDest);
  2842. }
  2843. return pDestArray;
  2844. }
  2845. bool JavascriptArray::BoxConcatItem(Var aItem, uint idxArg, ScriptContext *scriptContext)
  2846. {
  2847. return idxArg == 0 && !JavascriptOperators::IsObject(aItem);
  2848. }
  2849. Var JavascriptArray::EntryConcat(RecyclableObject* function, CallInfo callInfo, ...)
  2850. {
  2851. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  2852. ARGUMENTS(args, callInfo);
  2853. ScriptContext* scriptContext = function->GetScriptContext();
  2854. Assert(!(callInfo.Flags & CallFlags_New));
  2855. if (args.Info.Count == 0)
  2856. {
  2857. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.concat"));
  2858. }
  2859. //
  2860. // Compute the destination ScriptArray size:
  2861. // - Each item, flattening only one level if a ScriptArray.
  2862. //
  2863. uint32 cDestLength = 0;
  2864. JavascriptArray * pDestArray = NULL;
  2865. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault + (args.Info.Count * sizeof(TypeId*)));
  2866. TypeId* remoteTypeIds = (TypeId*)_alloca(args.Info.Count * sizeof(TypeId*));
  2867. bool isInt = true;
  2868. bool isFloat = true;
  2869. ::Math::RecordOverflowPolicy destLengthOverflow;
  2870. for (uint idxArg = 0; idxArg < args.Info.Count; idxArg++)
  2871. {
  2872. Var aItem = args[idxArg];
  2873. #if ENABLE_COPYONACCESS_ARRAY
  2874. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(aItem);
  2875. #endif
  2876. if (DynamicObject::IsAnyArray(aItem)) // Get JavascriptArray or ES5Array length
  2877. {
  2878. JavascriptArray * pItemArray = JavascriptArray::FromAnyArray(aItem);
  2879. if (isFloat)
  2880. {
  2881. if (!JavascriptNativeIntArray::Is(pItemArray))
  2882. {
  2883. isInt = false;
  2884. if (!JavascriptNativeFloatArray::Is(pItemArray))
  2885. {
  2886. isFloat = false;
  2887. }
  2888. }
  2889. }
  2890. cDestLength = UInt32Math::Add(cDestLength, pItemArray->GetLength(), destLengthOverflow);
  2891. }
  2892. else // Get remote array or object length
  2893. {
  2894. // We already checked for types derived from JavascriptArray. These are types that should behave like array
  2895. // i.e. proxy to array and remote array.
  2896. if (JavascriptOperators::IsArray(aItem))
  2897. {
  2898. // Don't try to preserve nativeness of remote arrays. The extra complexity is probably not
  2899. // worth it.
  2900. isInt = false;
  2901. isFloat = false;
  2902. if (!JavascriptProxy::Is(aItem))
  2903. {
  2904. if (scriptContext->GetConfig()->IsES6ToLengthEnabled())
  2905. {
  2906. int64 len = JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(aItem, scriptContext), scriptContext);
  2907. // clipping to MaxArrayLength will overflow when added to cDestLength which we catch below
  2908. cDestLength = UInt32Math::Add(cDestLength, len < MaxArrayLength ? (uint32)len : MaxArrayLength, destLengthOverflow);
  2909. }
  2910. else
  2911. {
  2912. uint len = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(aItem, scriptContext), scriptContext);
  2913. cDestLength = UInt32Math::Add(cDestLength, len, destLengthOverflow);
  2914. }
  2915. }
  2916. remoteTypeIds[idxArg] = TypeIds_Array; // Mark remote array, no matter remote JavascriptArray or ES5Array.
  2917. }
  2918. else
  2919. {
  2920. if (isFloat)
  2921. {
  2922. if (BoxConcatItem(aItem, idxArg, scriptContext))
  2923. {
  2924. // A primitive will be boxed, so we have to create a var array for the result.
  2925. isInt = false;
  2926. isFloat = false;
  2927. }
  2928. else if (!TaggedInt::Is(aItem))
  2929. {
  2930. if (!JavascriptNumber::Is(aItem))
  2931. {
  2932. isInt = false;
  2933. isFloat = false;
  2934. }
  2935. else if (isInt)
  2936. {
  2937. int32 int32Value;
  2938. if(!JavascriptNumber::TryGetInt32Value(JavascriptNumber::GetValue(aItem), &int32Value) ||
  2939. SparseArraySegment<int32>::IsMissingItem(&int32Value))
  2940. {
  2941. isInt = false;
  2942. }
  2943. }
  2944. }
  2945. else if(isInt)
  2946. {
  2947. int32 int32Value = TaggedInt::ToInt32(aItem);
  2948. if(SparseArraySegment<int32>::IsMissingItem(&int32Value))
  2949. {
  2950. isInt = false;
  2951. }
  2952. }
  2953. }
  2954. remoteTypeIds[idxArg] = TypeIds_Limit;
  2955. cDestLength = UInt32Math::Add(cDestLength, 1, destLengthOverflow);
  2956. }
  2957. }
  2958. }
  2959. if (destLengthOverflow.HasOverflowed())
  2960. {
  2961. cDestLength = MaxArrayLength;
  2962. isInt = false;
  2963. isFloat = false;
  2964. }
  2965. //
  2966. // Create the destination array
  2967. //
  2968. RecyclableObject* pDestObj = nullptr;
  2969. bool isArray = false;
  2970. pDestObj = ArraySpeciesCreate(args[0], 0, scriptContext);
  2971. if (pDestObj)
  2972. {
  2973. isInt = JavascriptNativeIntArray::Is(pDestObj);
  2974. isFloat = !isInt && JavascriptNativeFloatArray::Is(pDestObj); // if we know it is an int short the condition to avoid a function call
  2975. isArray = isInt || isFloat || JavascriptArray::Is(pDestObj);
  2976. }
  2977. if (pDestObj == nullptr || isArray)
  2978. {
  2979. if (isInt)
  2980. {
  2981. JavascriptNativeIntArray *pIntArray = isArray ? JavascriptNativeIntArray::FromVar(pDestObj) : scriptContext->GetLibrary()->CreateNativeIntArray(cDestLength);
  2982. pIntArray->EnsureHead<int32>();
  2983. pDestArray = ConcatIntArgs(pIntArray, remoteTypeIds, args, scriptContext);
  2984. }
  2985. else if (isFloat)
  2986. {
  2987. JavascriptNativeFloatArray *pFArray = isArray ? JavascriptNativeFloatArray::FromVar(pDestObj) : scriptContext->GetLibrary()->CreateNativeFloatArray(cDestLength);
  2988. pFArray->EnsureHead<double>();
  2989. pDestArray = ConcatFloatArgs(pFArray, remoteTypeIds, args, scriptContext);
  2990. }
  2991. else
  2992. {
  2993. pDestArray = isArray ? JavascriptArray::FromVar(pDestObj) : scriptContext->GetLibrary()->CreateArray(cDestLength);
  2994. // if the constructor has changed then we no longer specialize for ints and floats
  2995. pDestArray->EnsureHead<Var>();
  2996. ConcatArgsCallingHelper(pDestArray, remoteTypeIds, args, scriptContext, destLengthOverflow);
  2997. }
  2998. //
  2999. // Return the new array instance.
  3000. //
  3001. #ifdef VALIDATE_ARRAY
  3002. pDestArray->ValidateArray();
  3003. #endif
  3004. return pDestArray;
  3005. }
  3006. Assert(pDestObj);
  3007. ConcatArgsCallingHelper(pDestObj, remoteTypeIds, args, scriptContext, destLengthOverflow);
  3008. return pDestObj;
  3009. }
  3010. void JavascriptArray::ConcatArgsCallingHelper(RecyclableObject* pDestObj, TypeId* remoteTypeIds, Js::Arguments& args, ScriptContext* scriptContext, ::Math::RecordOverflowPolicy &destLengthOverflow)
  3011. {
  3012. if (destLengthOverflow.HasOverflowed())
  3013. {
  3014. ConcatArgs<BigIndex>(pDestObj, remoteTypeIds, args, scriptContext);
  3015. }
  3016. else
  3017. {
  3018. // Use faster uint32 version if no overflow
  3019. ConcatArgs<uint32>(pDestObj, remoteTypeIds, args, scriptContext);
  3020. }
  3021. }
  3022. template<typename T>
  3023. /* static */ void JavascriptArray::SetConcatItem(Var aItem, uint idxArg, JavascriptArray* pDestArray, RecyclableObject* pDestObj, T idxDest, ScriptContext *scriptContext)
  3024. {
  3025. if (BoxConcatItem(aItem, idxArg, scriptContext))
  3026. {
  3027. // bug# 725784: ES5: not calling ToObject in Step 1 of 15.4.4.4
  3028. RecyclableObject* pObj = nullptr;
  3029. if (FALSE == JavascriptConversion::ToObject(aItem, scriptContext, &pObj))
  3030. {
  3031. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.concat"));
  3032. }
  3033. if (pDestArray)
  3034. {
  3035. pDestArray->DirectSetItemAt(idxDest, pObj);
  3036. }
  3037. else
  3038. {
  3039. SetArrayLikeObjects(pDestObj, idxDest, pObj);
  3040. }
  3041. }
  3042. else
  3043. {
  3044. if (pDestArray)
  3045. {
  3046. pDestArray->DirectSetItemAt(idxDest, aItem);
  3047. }
  3048. else
  3049. {
  3050. SetArrayLikeObjects(pDestObj, idxDest, aItem);
  3051. }
  3052. }
  3053. }
  3054. uint32 JavascriptArray::GetFromIndex(Var arg, uint32 length, ScriptContext *scriptContext)
  3055. {
  3056. uint32 fromIndex;
  3057. if (TaggedInt::Is(arg))
  3058. {
  3059. int intValue = TaggedInt::ToInt32(arg);
  3060. if (intValue >= 0)
  3061. {
  3062. fromIndex = intValue;
  3063. }
  3064. else
  3065. {
  3066. // (intValue + length) may exceed 2^31 or may be < 0, so promote to int64
  3067. fromIndex = (uint32)max(0i64, (int64)(length) + intValue);
  3068. }
  3069. }
  3070. else
  3071. {
  3072. double value = JavascriptConversion::ToInteger(arg, scriptContext);
  3073. if (value > length)
  3074. {
  3075. return (uint32)-1;
  3076. }
  3077. else if (value >= 0)
  3078. {
  3079. fromIndex = (uint32)value;
  3080. }
  3081. else
  3082. {
  3083. fromIndex = (uint32)max((double)0, value + length);
  3084. }
  3085. }
  3086. return fromIndex;
  3087. }
  3088. uint64 JavascriptArray::GetFromIndex(Var arg, uint64 length, ScriptContext *scriptContext)
  3089. {
  3090. uint64 fromIndex;
  3091. if (TaggedInt::Is(arg))
  3092. {
  3093. int64 intValue = TaggedInt::ToInt64(arg);
  3094. if (intValue >= 0)
  3095. {
  3096. fromIndex = intValue;
  3097. }
  3098. else
  3099. {
  3100. fromIndex = max((int64)0, (int64)(intValue + length));
  3101. }
  3102. }
  3103. else
  3104. {
  3105. double value = JavascriptConversion::ToInteger(arg, scriptContext);
  3106. if (value > length)
  3107. {
  3108. return (uint64)-1;
  3109. }
  3110. else if (value >= 0)
  3111. {
  3112. fromIndex = (uint64)value;
  3113. }
  3114. else
  3115. {
  3116. fromIndex = (uint64)max((double)0, value + length);
  3117. }
  3118. }
  3119. return fromIndex;
  3120. }
  3121. int64 JavascriptArray::GetFromLastIndex(Var arg, int64 length, ScriptContext *scriptContext)
  3122. {
  3123. int64 fromIndex;
  3124. if (TaggedInt::Is(arg))
  3125. {
  3126. int intValue = TaggedInt::ToInt32(arg);
  3127. if (intValue >= 0)
  3128. {
  3129. fromIndex = min<int64>(intValue, length - 1);
  3130. }
  3131. else if ((uint32)-intValue > length)
  3132. {
  3133. return length;
  3134. }
  3135. else
  3136. {
  3137. fromIndex = intValue + length;
  3138. }
  3139. }
  3140. else
  3141. {
  3142. double value = JavascriptConversion::ToInteger(arg, scriptContext);
  3143. if (value >= 0)
  3144. {
  3145. fromIndex = (int64)min(value, (double)(length - 1));
  3146. }
  3147. else if (value + length < 0)
  3148. {
  3149. return length;
  3150. }
  3151. else
  3152. {
  3153. fromIndex = (int64)(value + length);
  3154. }
  3155. }
  3156. return fromIndex;
  3157. }
  3158. // includesAlgorithm specifies to follow ES7 Array.prototype.includes semantics instead of Array.prototype.indexOf
  3159. // Differences
  3160. // 1. Returns boolean true or false value instead of the search hit index
  3161. // 2. Follows SameValueZero algorithm instead of StrictEquals
  3162. // 3. Missing values are scanned if the search value is undefined
  3163. template <bool includesAlgorithm>
  3164. Var JavascriptArray::IndexOfHelper(Arguments const & args, ScriptContext *scriptContext)
  3165. {
  3166. RecyclableObject* obj = nullptr;
  3167. JavascriptArray* pArr = nullptr;
  3168. BigIndex length;
  3169. Var trueValue = scriptContext->GetLibrary()->GetTrue();
  3170. Var falseValue = scriptContext->GetLibrary()->GetFalse();
  3171. if (JavascriptArray::Is(args[0]))
  3172. {
  3173. #if ENABLE_COPYONACCESS_ARRAY
  3174. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(args[0]);
  3175. #endif
  3176. pArr = JavascriptArray::FromVar(args[0]);
  3177. obj = pArr;
  3178. }
  3179. else
  3180. {
  3181. if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj))
  3182. {
  3183. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.indexOf"));
  3184. }
  3185. }
  3186. // In ES6-mode, we always load the length property from the object instead of using the internal slot.
  3187. // Even for arrays, this is now observable via proxies.
  3188. // If source object is not an array, we fall back to this behavior anyway.
  3189. if (scriptContext->GetConfig()->IsES6TypedArrayExtensionsEnabled() || pArr == nullptr)
  3190. {
  3191. if (scriptContext->GetConfig()->IsES6ToLengthEnabled())
  3192. {
  3193. length = (uint64)JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext);
  3194. }
  3195. else
  3196. {
  3197. length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext);
  3198. }
  3199. }
  3200. else
  3201. {
  3202. length = pArr->length;
  3203. }
  3204. if (pArr)
  3205. {
  3206. Var search;
  3207. uint32 fromIndex;
  3208. uint32 len = length.IsUint32Max() ? MaxArrayLength : length.GetSmallIndex();
  3209. if (!GetParamForIndexOf(len, args, search, fromIndex, scriptContext))
  3210. {
  3211. return includesAlgorithm ? falseValue : TaggedInt::ToVarUnchecked(-1);
  3212. }
  3213. int32 index = pArr->HeadSegmentIndexOfHelper(search, fromIndex, len, includesAlgorithm, scriptContext);
  3214. // If we found the search value in the head segment, or if we determined there is no need to search other segments,
  3215. // we stop right here.
  3216. if (index != -1 || fromIndex == -1)
  3217. {
  3218. if (includesAlgorithm)
  3219. {
  3220. //Array.prototype.includes
  3221. return (index == -1)? falseValue : trueValue;
  3222. }
  3223. else
  3224. {
  3225. //Array.prototype.indexOf
  3226. return JavascriptNumber::ToVar(index, scriptContext);
  3227. }
  3228. }
  3229. // If we really must search other segments, let's do it now. We'll have to search the slow way (dealing with holes, etc.).
  3230. switch (pArr->GetTypeId())
  3231. {
  3232. case Js::TypeIds_Array:
  3233. return TemplatedIndexOfHelper<includesAlgorithm>(pArr, search, fromIndex, len, scriptContext);
  3234. case Js::TypeIds_NativeIntArray:
  3235. return TemplatedIndexOfHelper<includesAlgorithm>(JavascriptNativeIntArray::FromVar(pArr), search, fromIndex, len, scriptContext);
  3236. case Js::TypeIds_NativeFloatArray:
  3237. return TemplatedIndexOfHelper<includesAlgorithm>(JavascriptNativeFloatArray::FromVar(pArr), search, fromIndex, len, scriptContext);
  3238. default:
  3239. AssertMsg(FALSE, "invalid array typeid");
  3240. return TemplatedIndexOfHelper<includesAlgorithm>(pArr, search, fromIndex, len, scriptContext);
  3241. }
  3242. }
  3243. // source object is not a JavascriptArray but source could be a TypedArray
  3244. if (TypedArrayBase::Is(obj))
  3245. {
  3246. if (length.IsSmallIndex() || length.IsUint32Max())
  3247. {
  3248. Var search;
  3249. uint32 fromIndex;
  3250. uint32 len = length.IsUint32Max() ? MaxArrayLength : length.GetSmallIndex();
  3251. if (!GetParamForIndexOf(len, args, search, fromIndex, scriptContext))
  3252. {
  3253. return includesAlgorithm ? falseValue : TaggedInt::ToVarUnchecked(-1);
  3254. }
  3255. return TemplatedIndexOfHelper<includesAlgorithm>(TypedArrayBase::FromVar(obj), search, fromIndex, length.GetSmallIndex(), scriptContext);
  3256. }
  3257. }
  3258. if (length.IsSmallIndex())
  3259. {
  3260. Var search;
  3261. uint32 fromIndex;
  3262. if (!GetParamForIndexOf(length.GetSmallIndex(), args, search, fromIndex, scriptContext))
  3263. {
  3264. return includesAlgorithm ? falseValue : TaggedInt::ToVarUnchecked(-1);
  3265. }
  3266. return TemplatedIndexOfHelper<includesAlgorithm>(obj, search, fromIndex, length.GetSmallIndex(), scriptContext);
  3267. }
  3268. else
  3269. {
  3270. Var search;
  3271. uint64 fromIndex;
  3272. if (!GetParamForIndexOf(length.GetBigIndex(), args, search, fromIndex, scriptContext))
  3273. {
  3274. return includesAlgorithm ? falseValue : TaggedInt::ToVarUnchecked(-1);
  3275. }
  3276. return TemplatedIndexOfHelper<includesAlgorithm>(obj, search, fromIndex, length.GetBigIndex(), scriptContext);
  3277. }
  3278. }
  3279. // Array.prototype.indexOf as defined in ES6.0 (final) Section 22.1.3.11
  3280. Var JavascriptArray::EntryIndexOf(RecyclableObject* function, CallInfo callInfo, ...)
  3281. {
  3282. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  3283. ARGUMENTS(args, callInfo);
  3284. ScriptContext* scriptContext = function->GetScriptContext();
  3285. Assert(!(callInfo.Flags & CallFlags_New));
  3286. CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(ArrayIndexOfCount);
  3287. Var returnValue = IndexOfHelper<false>(args, scriptContext);
  3288. //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
  3289. Assert(returnValue != scriptContext->GetLibrary()->GetTrue() && returnValue != scriptContext->GetLibrary()->GetFalse());
  3290. return returnValue;
  3291. }
  3292. Var JavascriptArray::EntryIncludes(RecyclableObject* function, CallInfo callInfo, ...)
  3293. {
  3294. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  3295. ARGUMENTS(args, callInfo);
  3296. ScriptContext* scriptContext = function->GetScriptContext();
  3297. Assert(!(callInfo.Flags & CallFlags_New));
  3298. CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(ArrayIncludesCount);
  3299. Var returnValue = IndexOfHelper<true>(args, scriptContext);
  3300. Assert(returnValue == scriptContext->GetLibrary()->GetTrue() || returnValue == scriptContext->GetLibrary()->GetFalse());
  3301. return returnValue;
  3302. }
  3303. template<typename T>
  3304. BOOL JavascriptArray::GetParamForIndexOf(T length, Arguments const& args, Var& search, T& fromIndex, ScriptContext * scriptContext)
  3305. {
  3306. if (length == 0)
  3307. {
  3308. return false;
  3309. }
  3310. if (args.Info.Count > 2)
  3311. {
  3312. fromIndex = GetFromIndex(args[2], length, scriptContext);
  3313. if (fromIndex >= length)
  3314. {
  3315. return false;
  3316. }
  3317. search = args[1];
  3318. }
  3319. else
  3320. {
  3321. fromIndex = 0;
  3322. search = args.Info.Count > 1 ? args[1] : scriptContext->GetLibrary()->GetUndefined();
  3323. }
  3324. return true;
  3325. }
  3326. template <>
  3327. BOOL JavascriptArray::TemplatedGetItem(RecyclableObject * obj, uint32 index, Var * element, ScriptContext * scriptContext)
  3328. {
  3329. // Note: Sometime cross site array go down this path to get the marshalling
  3330. Assert(!VirtualTableInfo<JavascriptArray>::HasVirtualTable(obj)
  3331. && !VirtualTableInfo<JavascriptNativeIntArray>::HasVirtualTable(obj)
  3332. && !VirtualTableInfo<JavascriptNativeFloatArray>::HasVirtualTable(obj));
  3333. if (!JavascriptOperators::HasItem(obj, index))
  3334. {
  3335. return FALSE;
  3336. }
  3337. return JavascriptOperators::GetItem(obj, index, element, scriptContext);
  3338. }
  3339. template <>
  3340. BOOL JavascriptArray::TemplatedGetItem(RecyclableObject * obj, uint64 index, Var * element, ScriptContext * scriptContext)
  3341. {
  3342. // Note: Sometime cross site array go down this path to get the marshalling
  3343. Assert(!VirtualTableInfo<JavascriptArray>::HasVirtualTable(obj)
  3344. && !VirtualTableInfo<JavascriptNativeIntArray>::HasVirtualTable(obj)
  3345. && !VirtualTableInfo<JavascriptNativeFloatArray>::HasVirtualTable(obj));
  3346. PropertyRecord const * propertyRecord;
  3347. JavascriptOperators::GetPropertyIdForInt(index, scriptContext, &propertyRecord);
  3348. if (!JavascriptOperators::HasProperty(obj, propertyRecord->GetPropertyId()))
  3349. {
  3350. return FALSE;
  3351. }
  3352. *element = JavascriptOperators::GetProperty(obj, propertyRecord->GetPropertyId(), scriptContext);
  3353. return *element != scriptContext->GetLibrary()->GetUndefined();
  3354. }
  3355. template <>
  3356. BOOL JavascriptArray::TemplatedGetItem(JavascriptArray *pArr, uint32 index, Var * element, ScriptContext * scriptContext)
  3357. {
  3358. Assert(VirtualTableInfo<JavascriptArray>::HasVirtualTable(pArr)
  3359. || VirtualTableInfo<CrossSiteObject<JavascriptArray>>::HasVirtualTable(pArr));
  3360. return pArr->JavascriptArray::DirectGetItemAtFull(index, element);
  3361. }
  3362. template <>
  3363. BOOL JavascriptArray::TemplatedGetItem(JavascriptArray *pArr, uint64 index, Var * element, ScriptContext * scriptContext)
  3364. {
  3365. // This should never get called.
  3366. Assert(false);
  3367. Throw::InternalError();
  3368. }
  3369. template <>
  3370. BOOL JavascriptArray::TemplatedGetItem(JavascriptNativeIntArray *pArr, uint32 index, Var * element, ScriptContext * scriptContext)
  3371. {
  3372. Assert(VirtualTableInfo<JavascriptNativeIntArray>::HasVirtualTable(pArr)
  3373. || VirtualTableInfo<CrossSiteObject<JavascriptNativeIntArray>>::HasVirtualTable(pArr));
  3374. return pArr->JavascriptNativeIntArray::DirectGetItemAtFull(index, element);
  3375. }
  3376. template <>
  3377. BOOL JavascriptArray::TemplatedGetItem(JavascriptNativeIntArray *pArr, uint64 index, Var * element, ScriptContext * scriptContext)
  3378. {
  3379. // This should never get called.
  3380. Assert(false);
  3381. Throw::InternalError();
  3382. }
  3383. template <>
  3384. BOOL JavascriptArray::TemplatedGetItem(JavascriptNativeFloatArray *pArr, uint32 index, Var * element, ScriptContext * scriptContext)
  3385. {
  3386. Assert(VirtualTableInfo<JavascriptNativeFloatArray>::HasVirtualTable(pArr)
  3387. || VirtualTableInfo<CrossSiteObject<JavascriptNativeFloatArray>>::HasVirtualTable(pArr));
  3388. return pArr->JavascriptNativeFloatArray::DirectGetItemAtFull(index, element);
  3389. }
  3390. template <>
  3391. BOOL JavascriptArray::TemplatedGetItem(JavascriptNativeFloatArray *pArr, uint64 index, Var * element, ScriptContext * scriptContext)
  3392. {
  3393. // This should never get called.
  3394. Assert(false);
  3395. Throw::InternalError();
  3396. }
  3397. template <>
  3398. BOOL JavascriptArray::TemplatedGetItem(TypedArrayBase * typedArrayBase, uint32 index, Var * element, ScriptContext * scriptContext)
  3399. {
  3400. // We need to do explicit check for items since length value may not actually match the actual TypedArray length.
  3401. // User could add a length property to a TypedArray instance which lies and returns a different value from the underlying length.
  3402. // Since this method can be called via Array.prototype.indexOf with .apply or .call passing a TypedArray as this parameter
  3403. // we don't know whether or not length == typedArrayBase->GetLength().
  3404. if (!typedArrayBase->HasItem(index))
  3405. {
  3406. return false;
  3407. }
  3408. *element = typedArrayBase->DirectGetItem(index);
  3409. return true;
  3410. }
  3411. template <>
  3412. BOOL JavascriptArray::TemplatedGetItem(TypedArrayBase * typedArrayBase, uint64 index, Var * element, ScriptContext * scriptContext)
  3413. {
  3414. // This should never get called.
  3415. Assert(false);
  3416. Throw::InternalError();
  3417. }
  3418. template <bool includesAlgorithm, typename T, typename P>
  3419. Var JavascriptArray::TemplatedIndexOfHelper(T * pArr, Var search, P fromIndex, P toIndex, ScriptContext * scriptContext)
  3420. {
  3421. Var element = nullptr;
  3422. bool isSearchTaggedInt = TaggedInt::Is(search);
  3423. bool doUndefinedSearch = includesAlgorithm && JavascriptOperators::GetTypeId(search) == TypeIds_Undefined;
  3424. Var trueValue = scriptContext->GetLibrary()->GetTrue();
  3425. Var falseValue = scriptContext->GetLibrary()->GetFalse();
  3426. //Consider: enumerating instead of walking all indices
  3427. for (P i = fromIndex; i < toIndex; i++)
  3428. {
  3429. if (!TemplatedGetItem(pArr, i, &element, scriptContext))
  3430. {
  3431. if (doUndefinedSearch)
  3432. {
  3433. return trueValue;
  3434. }
  3435. continue;
  3436. }
  3437. if (isSearchTaggedInt && TaggedInt::Is(element))
  3438. {
  3439. if (element == search)
  3440. {
  3441. return includesAlgorithm? trueValue : JavascriptNumber::ToVar(i, scriptContext);
  3442. }
  3443. continue;
  3444. }
  3445. if (includesAlgorithm)
  3446. {
  3447. //Array.prototype.includes
  3448. if (JavascriptConversion::SameValueZero(element, search))
  3449. {
  3450. return trueValue;
  3451. }
  3452. }
  3453. else
  3454. {
  3455. //Array.prototype.indexOf
  3456. if (JavascriptOperators::StrictEqual(element, search, scriptContext))
  3457. {
  3458. return JavascriptNumber::ToVar(i, scriptContext);
  3459. }
  3460. }
  3461. }
  3462. return includesAlgorithm ? falseValue : TaggedInt::ToVarUnchecked(-1);
  3463. }
  3464. int32 JavascriptArray::HeadSegmentIndexOfHelper(Var search, uint32 &fromIndex, uint32 toIndex, bool includesAlgorithm, ScriptContext * scriptContext)
  3465. {
  3466. Assert(Is(GetTypeId()) && !JavascriptNativeArray::Is(GetTypeId()));
  3467. if (!HasNoMissingValues() || fromIndex >= GetHead()->length)
  3468. {
  3469. return -1;
  3470. }
  3471. bool isSearchTaggedInt = TaggedInt::Is(search);
  3472. // We need to cast head segment to SparseArraySegment<Var> to have access to GetElement (onSparseArraySegment<T>). Because there are separate overloads of this
  3473. // virtual method on JavascriptNativeIntArray and JavascriptNativeFloatArray, we know this version of this method will only be called for true JavascriptArray, and not for
  3474. // either of the derived native arrays, so the elements of each segment used here must be Vars. Hence, the cast is safe.
  3475. SparseArraySegment<Var>* head = static_cast<SparseArraySegment<Var>*>(GetHead());
  3476. uint32 toIndexTrimmed = toIndex <= head->length ? toIndex : head->length;
  3477. for (uint32 i = fromIndex; i < toIndexTrimmed; i++)
  3478. {
  3479. Var element = head->GetElement(i);
  3480. if (isSearchTaggedInt && TaggedInt::Is(element))
  3481. {
  3482. if (search == element)
  3483. {
  3484. return i;
  3485. }
  3486. }
  3487. else if (includesAlgorithm && JavascriptConversion::SameValueZero(element, search))
  3488. {
  3489. //Array.prototype.includes
  3490. return i;
  3491. }
  3492. else if (JavascriptOperators::StrictEqual(element, search, scriptContext))
  3493. {
  3494. //Array.prototype.indexOf
  3495. return i;
  3496. }
  3497. }
  3498. // Element not found in the head segment. Keep looking only if the range of indices extends past
  3499. // the head segment.
  3500. fromIndex = toIndex > GetHead()->length ? GetHead()->length : -1;
  3501. return -1;
  3502. }
  3503. int32 JavascriptNativeIntArray::HeadSegmentIndexOfHelper(Var search, uint32 &fromIndex, uint32 toIndex, bool includesAlgorithm, ScriptContext * scriptContext)
  3504. {
  3505. // We proceed largely in the same manner as in JavascriptArray's version of this method (see comments there for more information),
  3506. // 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:
  3507. // 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
  3508. // the search value is some other kind of Var, we can return -1 without ever iterating over the elements.
  3509. // 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.
  3510. // Instead we can use simple C++ equality (which in case of such values is equivalent to strict equality in JavaScript).
  3511. if (!HasNoMissingValues() || fromIndex >= GetHead()->length)
  3512. {
  3513. return -1;
  3514. }
  3515. bool isSearchTaggedInt = TaggedInt::Is(search);
  3516. if (!isSearchTaggedInt && !JavascriptNumber::Is_NoTaggedIntCheck(search))
  3517. {
  3518. // The value can't be in the array, but it could be in a prototype, and we can only guarantee that
  3519. // the head segment has no gaps.
  3520. fromIndex = toIndex > GetHead()->length ? GetHead()->length : -1;
  3521. return -1;
  3522. }
  3523. int32 searchAsInt32;
  3524. if (isSearchTaggedInt)
  3525. {
  3526. searchAsInt32 = TaggedInt::ToInt32(search);
  3527. }
  3528. else if (!JavascriptNumber::TryGetInt32Value<true>(JavascriptNumber::GetValue(search), &searchAsInt32))
  3529. {
  3530. // The value can't be in the array, but it could be in a prototype, and we can only guarantee that
  3531. // the head segment has no gaps.
  3532. fromIndex = toIndex > GetHead()->length ? GetHead()->length : -1;
  3533. return -1;
  3534. }
  3535. // We need to cast head segment to SparseArraySegment<int32> to have access to GetElement (onSparseArraySegment<T>). Because there are separate overloads of this
  3536. // virtual method on JavascriptNativeIntArray and JavascriptNativeFloatArray, we know this version of this method will only be called for true JavascriptNativeIntArray, and not for
  3537. // the other two, so the elements of each segment used here must be int32's. Hence, the cast is safe.
  3538. SparseArraySegment<int32> * head = static_cast<SparseArraySegment<int32>*>(GetHead());
  3539. uint32 toIndexTrimmed = toIndex <= head->length ? toIndex : head->length;
  3540. for (uint32 i = fromIndex; i < toIndexTrimmed; i++)
  3541. {
  3542. int32 element = head->GetElement(i);
  3543. if (searchAsInt32 == element)
  3544. {
  3545. return i;
  3546. }
  3547. }
  3548. // Element not found in the head segment. Keep looking only if the range of indices extends past
  3549. // the head segment.
  3550. fromIndex = toIndex > GetHead()->length ? GetHead()->length : -1;
  3551. return -1;
  3552. }
  3553. int32 JavascriptNativeFloatArray::HeadSegmentIndexOfHelper(Var search, uint32 &fromIndex, uint32 toIndex, bool includesAlgorithm, ScriptContext * scriptContext)
  3554. {
  3555. // We proceed largely in the same manner as in JavascriptArray's version of this method (see comments there for more information),
  3556. // except when we can further optimize thanks to the knowledge that all elements in the array are doubles. This allows for two additional optimizations:
  3557. // 1. Only tagged ints or JavascriptNumbers can be strict equal to some element in the array (all doubles). Thus, if
  3558. // the search value is some other kind of Var, we can return -1 without ever iterating over the elements.
  3559. // 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.
  3560. // Instead we can use simple C++ equality (which in case of such values is equivalent to strict equality in JavaScript).
  3561. if (!HasNoMissingValues() || fromIndex >= GetHead()->length)
  3562. {
  3563. return -1;
  3564. }
  3565. bool isSearchTaggedInt = TaggedInt::Is(search);
  3566. if (!isSearchTaggedInt && !JavascriptNumber::Is_NoTaggedIntCheck(search))
  3567. {
  3568. // The value can't be in the array, but it could be in a prototype, and we can only guarantee that
  3569. // the head segment has no gaps.
  3570. fromIndex = toIndex > GetHead()->length ? GetHead()->length : -1;
  3571. return -1;
  3572. }
  3573. double searchAsDouble = isSearchTaggedInt ? TaggedInt::ToDouble(search) : JavascriptNumber::GetValue(search);
  3574. // We need to cast head segment to SparseArraySegment<double> to have access to GetElement (SparseArraySegment). We know the
  3575. // segment's elements are all Vars so the cast is safe. It would have been more convenient here if JavascriptArray
  3576. // used SparseArraySegment<Var>, instead of SparseArraySegmentBase.
  3577. SparseArraySegment<double> * head = static_cast<SparseArraySegment<double>*>(GetHead());
  3578. uint32 toIndexTrimmed = toIndex <= head->length ? toIndex : head->length;
  3579. bool matchNaN = includesAlgorithm && JavascriptNumber::IsNan(searchAsDouble);
  3580. for (uint32 i = fromIndex; i < toIndexTrimmed; i++)
  3581. {
  3582. double element = head->GetElement(i);
  3583. if (element == searchAsDouble)
  3584. {
  3585. return i;
  3586. }
  3587. //NaN != NaN we expect to match for NaN in Array.prototype.includes algorithm
  3588. if (matchNaN && JavascriptNumber::IsNan(element))
  3589. {
  3590. return i;
  3591. }
  3592. }
  3593. fromIndex = toIndex > GetHead()->length ? GetHead()->length : -1;
  3594. return -1;
  3595. }
  3596. Var JavascriptArray::EntryJoin(RecyclableObject* function, CallInfo callInfo, ...)
  3597. {
  3598. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  3599. ARGUMENTS(args, callInfo);
  3600. ScriptContext* scriptContext = function->GetScriptContext();
  3601. Assert(!(callInfo.Flags & CallFlags_New));
  3602. if (args.Info.Count == 0)
  3603. {
  3604. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.join"));
  3605. }
  3606. JavascriptString* separator;
  3607. if (args.Info.Count >= 2)
  3608. {
  3609. TypeId typeId = JavascriptOperators::GetTypeId(args[1]);
  3610. //ES5 15.4.4.5 If separator is undefined, let separator be the single-character String ",".
  3611. if (TypeIds_Undefined != typeId)
  3612. {
  3613. separator = JavascriptConversion::ToString(args[1], scriptContext);
  3614. }
  3615. else
  3616. {
  3617. separator = scriptContext->GetLibrary()->GetCommaDisplayString();
  3618. }
  3619. }
  3620. else
  3621. {
  3622. separator = scriptContext->GetLibrary()->GetCommaDisplayString();
  3623. }
  3624. return JoinHelper(args[0], separator, scriptContext);
  3625. }
  3626. JavascriptString* JavascriptArray::JoinToString(Var value, ScriptContext* scriptContext)
  3627. {
  3628. TypeId typeId = JavascriptOperators::GetTypeId(value);
  3629. if (typeId == TypeIds_Null || typeId == TypeIds_Undefined)
  3630. {
  3631. return scriptContext->GetLibrary()->GetEmptyString();
  3632. }
  3633. else
  3634. {
  3635. return JavascriptConversion::ToString(value, scriptContext);
  3636. }
  3637. }
  3638. JavascriptString* JavascriptArray::JoinHelper(Var thisArg, JavascriptString* separator, ScriptContext* scriptContext)
  3639. {
  3640. bool isArray = JavascriptArray::Is(thisArg) && (scriptContext == JavascriptArray::FromVar(thisArg)->GetScriptContext());
  3641. bool isProxy = JavascriptProxy::Is(thisArg) && (scriptContext == JavascriptProxy::FromVar(thisArg)->GetScriptContext());
  3642. Var target = NULL;
  3643. bool isTargetObjectPushed = false;
  3644. // if we are visiting a proxy object, track that we have visited the target object as well so the next time w
  3645. // call the join helper for the target of this proxy, we will return above.
  3646. if (isProxy)
  3647. {
  3648. JavascriptProxy* proxy = JavascriptProxy::FromVar(thisArg);
  3649. Assert(proxy);
  3650. target = proxy->GetTarget();
  3651. if (target != nullptr)
  3652. {
  3653. // If we end up joining same array, instead of going in infinite loop, return the empty string
  3654. if (scriptContext->CheckObject(target))
  3655. {
  3656. return scriptContext->GetLibrary()->GetEmptyString();
  3657. }
  3658. else
  3659. {
  3660. scriptContext->PushObject(target);
  3661. isTargetObjectPushed = true;
  3662. }
  3663. }
  3664. }
  3665. // If we end up joining same array, instead of going in infinite loop, return the empty string
  3666. else if (scriptContext->CheckObject(thisArg))
  3667. {
  3668. return scriptContext->GetLibrary()->GetEmptyString();
  3669. }
  3670. if (!isTargetObjectPushed)
  3671. {
  3672. scriptContext->PushObject(thisArg);
  3673. }
  3674. JavascriptString* res = nullptr;
  3675. TryFinally([&]()
  3676. {
  3677. if (isArray)
  3678. {
  3679. #if ENABLE_COPYONACCESS_ARRAY
  3680. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray(thisArg);
  3681. #endif
  3682. JavascriptArray * arr = JavascriptArray::FromVar(thisArg);
  3683. switch (arr->GetTypeId())
  3684. {
  3685. case Js::TypeIds_Array:
  3686. res = JoinArrayHelper(arr, separator, scriptContext);
  3687. break;
  3688. case Js::TypeIds_NativeIntArray:
  3689. res = JoinArrayHelper(JavascriptNativeIntArray::FromVar(arr), separator, scriptContext);
  3690. break;
  3691. case Js::TypeIds_NativeFloatArray:
  3692. res = JoinArrayHelper(JavascriptNativeFloatArray::FromVar(arr), separator, scriptContext);
  3693. break;
  3694. }
  3695. }
  3696. else if (RecyclableObject::Is(thisArg))
  3697. {
  3698. res = JoinOtherHelper(RecyclableObject::FromVar(thisArg), separator, scriptContext);
  3699. }
  3700. else
  3701. {
  3702. res = JoinOtherHelper(scriptContext->GetLibrary()->CreateNumberObject(thisArg), separator, scriptContext);
  3703. }
  3704. },
  3705. [&](bool/*hasException*/)
  3706. {
  3707. Var top = scriptContext->PopObject();
  3708. if (JavascriptProxy::Is(thisArg))
  3709. {
  3710. AssertMsg(top == target, "Unmatched operation stack");
  3711. }
  3712. else
  3713. {
  3714. AssertMsg(top == thisArg, "Unmatched operation stack");
  3715. }
  3716. });
  3717. if (res == nullptr)
  3718. {
  3719. res = scriptContext->GetLibrary()->GetEmptyString();
  3720. }
  3721. return res;
  3722. }
  3723. static const charcount_t Join_MaxEstimatedAppendCount = static_cast<charcount_t>((64 << 20) / sizeof(void *)); // 64 MB worth of pointers
  3724. template <typename T>
  3725. JavascriptString* JavascriptArray::JoinArrayHelper(T * arr, JavascriptString* separator, ScriptContext* scriptContext)
  3726. {
  3727. Assert(VirtualTableInfo<T>::HasVirtualTable(arr) || VirtualTableInfo<CrossSiteObject<T>>::HasVirtualTable(arr));
  3728. const uint32 arrLength = arr->length;
  3729. switch(arrLength)
  3730. {
  3731. default:
  3732. {
  3733. CaseDefault:
  3734. bool hasSeparator = (separator->GetLength() != 0);
  3735. const charcount_t estimatedAppendCount =
  3736. min(
  3737. Join_MaxEstimatedAppendCount,
  3738. static_cast<charcount_t>(arrLength + (hasSeparator ? arrLength - 1 : 0)));
  3739. CompoundString *const cs =
  3740. CompoundString::NewWithPointerCapacity(estimatedAppendCount, scriptContext->GetLibrary());
  3741. Var item;
  3742. if (TemplatedGetItem(arr, 0u, &item, scriptContext))
  3743. {
  3744. cs->Append(JavascriptArray::JoinToString(item, scriptContext));
  3745. }
  3746. for (uint32 i = 1; i < arrLength; i++)
  3747. {
  3748. if (hasSeparator)
  3749. {
  3750. cs->Append(separator);
  3751. }
  3752. if (TemplatedGetItem(arr, i, &item, scriptContext))
  3753. {
  3754. cs->Append(JavascriptArray::JoinToString(item, scriptContext));
  3755. }
  3756. }
  3757. return cs;
  3758. }
  3759. case 2:
  3760. {
  3761. bool hasSeparator = (separator->GetLength() != 0);
  3762. if(hasSeparator)
  3763. {
  3764. goto CaseDefault;
  3765. }
  3766. JavascriptString *res = nullptr;
  3767. Var item;
  3768. if (TemplatedGetItem(arr, 0u, &item, scriptContext))
  3769. {
  3770. res = JavascriptArray::JoinToString(item, scriptContext);
  3771. }
  3772. if (TemplatedGetItem(arr, 1u, &item, scriptContext))
  3773. {
  3774. JavascriptString *const itemString = JavascriptArray::JoinToString(item, scriptContext);
  3775. return res ? ConcatString::New(res, itemString) : itemString;
  3776. }
  3777. if(res)
  3778. {
  3779. return res;
  3780. }
  3781. goto Case0;
  3782. }
  3783. case 1:
  3784. {
  3785. Var item;
  3786. if (TemplatedGetItem(arr, 0u, &item, scriptContext))
  3787. {
  3788. return JavascriptArray::JoinToString(item, scriptContext);
  3789. }
  3790. // fall through
  3791. }
  3792. case 0:
  3793. Case0:
  3794. return scriptContext->GetLibrary()->GetEmptyString();
  3795. }
  3796. }
  3797. JavascriptString* JavascriptArray::JoinOtherHelper(RecyclableObject* object, JavascriptString* separator, ScriptContext* scriptContext)
  3798. {
  3799. // In ES6-mode, we always load the length property from the object instead of using the internal slot.
  3800. // Even for arrays, this is now observable via proxies.
  3801. // If source object is not an array, we fall back to this behavior anyway.
  3802. Var lenValue = JavascriptOperators::OP_GetLength(object, scriptContext);
  3803. int64 cSrcLength = JavascriptConversion::ToLength(lenValue, scriptContext);
  3804. switch (cSrcLength)
  3805. {
  3806. default:
  3807. {
  3808. CaseDefault:
  3809. bool hasSeparator = (separator->GetLength() != 0);
  3810. const charcount_t estimatedAppendCount =
  3811. min(
  3812. Join_MaxEstimatedAppendCount,
  3813. static_cast<charcount_t>(cSrcLength + (hasSeparator ? cSrcLength - 1 : 0)));
  3814. CompoundString *const cs =
  3815. CompoundString::NewWithPointerCapacity(estimatedAppendCount, scriptContext->GetLibrary());
  3816. Var value;
  3817. if (JavascriptOperators::GetItem(object, 0u, &value, scriptContext))
  3818. {
  3819. cs->Append(JavascriptArray::JoinToString(value, scriptContext));
  3820. }
  3821. for (uint32 i = 1; i < cSrcLength; i++)
  3822. {
  3823. if (hasSeparator)
  3824. {
  3825. cs->Append(separator);
  3826. }
  3827. if (JavascriptOperators::GetItem(object, i, &value, scriptContext))
  3828. {
  3829. cs->Append(JavascriptArray::JoinToString(value, scriptContext));
  3830. }
  3831. }
  3832. return cs;
  3833. }
  3834. case 2:
  3835. {
  3836. bool hasSeparator = (separator->GetLength() != 0);
  3837. if(hasSeparator)
  3838. {
  3839. goto CaseDefault;
  3840. }
  3841. JavascriptString *res = nullptr;
  3842. Var value;
  3843. if (JavascriptOperators::GetItem(object, 0u, &value, scriptContext))
  3844. {
  3845. res = JavascriptArray::JoinToString(value, scriptContext);
  3846. }
  3847. if (JavascriptOperators::GetItem(object, 1u, &value, scriptContext))
  3848. {
  3849. JavascriptString *const valueString = JavascriptArray::JoinToString(value, scriptContext);
  3850. return res ? ConcatString::New(res, valueString) : valueString;
  3851. }
  3852. if(res)
  3853. {
  3854. return res;
  3855. }
  3856. goto Case0;
  3857. }
  3858. case 1:
  3859. {
  3860. Var value;
  3861. if (JavascriptOperators::GetItem(object, 0u, &value, scriptContext))
  3862. {
  3863. return JavascriptArray::JoinToString(value, scriptContext);
  3864. }
  3865. // fall through
  3866. }
  3867. case 0:
  3868. Case0:
  3869. return scriptContext->GetLibrary()->GetEmptyString();
  3870. }
  3871. }
  3872. Var JavascriptArray::EntryLastIndexOf(RecyclableObject* function, CallInfo callInfo, ...)
  3873. {
  3874. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  3875. ARGUMENTS(args, callInfo);
  3876. ScriptContext* scriptContext = function->GetScriptContext();
  3877. CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(ArrayLastIndexOfCount);
  3878. Assert(!(callInfo.Flags & CallFlags_New));
  3879. int64 length;
  3880. JavascriptArray * pArr = nullptr;
  3881. RecyclableObject* obj = nullptr;
  3882. if (JavascriptArray::Is(args[0]))
  3883. {
  3884. #if ENABLE_COPYONACCESS_ARRAY
  3885. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(args[0]);
  3886. #endif
  3887. pArr = JavascriptArray::FromVar(args[0]);
  3888. obj = pArr;
  3889. length = pArr->length;
  3890. }
  3891. else
  3892. {
  3893. if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj))
  3894. {
  3895. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.lastIndexOf"));
  3896. }
  3897. Var lenValue = JavascriptOperators::OP_GetLength(obj, scriptContext);
  3898. length = JavascriptConversion::ToLength(lenValue, scriptContext);
  3899. }
  3900. Var search;
  3901. int64 fromIndex;
  3902. if (!GetParamForLastIndexOf(length, args, search, fromIndex, scriptContext))
  3903. {
  3904. return TaggedInt::ToVarUnchecked(-1);
  3905. }
  3906. if (pArr)
  3907. {
  3908. switch (pArr->GetTypeId())
  3909. {
  3910. case Js::TypeIds_Array:
  3911. return LastIndexOfHelper(pArr, search, fromIndex, scriptContext);
  3912. case Js::TypeIds_NativeIntArray:
  3913. return LastIndexOfHelper(JavascriptNativeIntArray::FromVar(pArr), search, fromIndex, scriptContext);
  3914. case Js::TypeIds_NativeFloatArray:
  3915. return LastIndexOfHelper(JavascriptNativeFloatArray::FromVar(pArr), search, fromIndex, scriptContext);
  3916. default:
  3917. AssertMsg(FALSE, "invalid array typeid");
  3918. return LastIndexOfHelper(pArr, search, fromIndex, scriptContext);
  3919. }
  3920. }
  3921. // source object is not a JavascriptArray but source could be a TypedArray
  3922. if (TypedArrayBase::Is(obj))
  3923. {
  3924. return LastIndexOfHelper(TypedArrayBase::FromVar(obj), search, fromIndex, scriptContext);
  3925. }
  3926. return LastIndexOfHelper(obj, search, fromIndex, scriptContext);
  3927. }
  3928. // Array.prototype.lastIndexOf as described in ES6.0 (draft 22) Section 22.1.3.14
  3929. BOOL JavascriptArray::GetParamForLastIndexOf(int64 length, Arguments const & args, Var& search, int64& fromIndex, ScriptContext * scriptContext)
  3930. {
  3931. if (length == 0)
  3932. {
  3933. return false;
  3934. }
  3935. if (args.Info.Count > 2)
  3936. {
  3937. fromIndex = GetFromLastIndex(args[2], length, scriptContext);
  3938. if (fromIndex >= length)
  3939. {
  3940. return false;
  3941. }
  3942. search = args[1];
  3943. }
  3944. else
  3945. {
  3946. search = args.Info.Count > 1 ? args[1] : scriptContext->GetLibrary()->GetUndefined();
  3947. fromIndex = length - 1;
  3948. }
  3949. return true;
  3950. }
  3951. template <typename T>
  3952. Var JavascriptArray::LastIndexOfHelper(T* pArr, Var search, int64 fromIndex, ScriptContext * scriptContext)
  3953. {
  3954. Var element = nullptr;
  3955. bool isSearchTaggedInt = TaggedInt::Is(search);
  3956. // First handle the indices > 2^32
  3957. while (fromIndex >= MaxArrayLength)
  3958. {
  3959. Var index = JavascriptNumber::ToVar(fromIndex, scriptContext);
  3960. if (JavascriptOperators::OP_HasItem(pArr, index, scriptContext))
  3961. {
  3962. element = JavascriptOperators::OP_GetElementI(pArr, index, scriptContext);
  3963. if (isSearchTaggedInt && TaggedInt::Is(element))
  3964. {
  3965. if (element == search)
  3966. {
  3967. return index;
  3968. }
  3969. fromIndex--;
  3970. continue;
  3971. }
  3972. if (JavascriptOperators::StrictEqual(element, search, scriptContext))
  3973. {
  3974. return index;
  3975. }
  3976. }
  3977. fromIndex--;
  3978. }
  3979. Assert(fromIndex < MaxArrayLength);
  3980. // fromIndex now has to be < MaxArrayLength so casting to uint32 is safe
  3981. uint32 end = static_cast<uint32>(fromIndex);
  3982. for (uint32 i = 0; i <= end; i++)
  3983. {
  3984. uint32 index = end - i;
  3985. if (!TemplatedGetItem(pArr, index, &element, scriptContext))
  3986. {
  3987. continue;
  3988. }
  3989. if (isSearchTaggedInt && TaggedInt::Is(element))
  3990. {
  3991. if (element == search)
  3992. {
  3993. return JavascriptNumber::ToVar(index, scriptContext);
  3994. }
  3995. continue;
  3996. }
  3997. if (JavascriptOperators::StrictEqual(element, search, scriptContext))
  3998. {
  3999. return JavascriptNumber::ToVar(index, scriptContext);
  4000. }
  4001. }
  4002. return TaggedInt::ToVarUnchecked(-1);
  4003. }
  4004. /*
  4005. * PopWithNoDst
  4006. * - For pop calls that do not return a value, we only need to decrement the length of the array.
  4007. */
  4008. void JavascriptNativeArray::PopWithNoDst(Var nativeArray)
  4009. {
  4010. Assert(JavascriptNativeArray::Is(nativeArray));
  4011. JavascriptArray * arr = JavascriptArray::FromVar(nativeArray);
  4012. // we will bailout on length 0
  4013. Assert(arr->GetLength() != 0);
  4014. uint32 index = arr->GetLength() - 1;
  4015. arr->SetLength(index);
  4016. }
  4017. /*
  4018. * JavascriptNativeIntArray::Pop
  4019. * - Returns int32 value from the array.
  4020. * - Returns missing item when the element is not available in the array object.
  4021. * - It doesn't walk up the prototype chain.
  4022. * - Length is decremented only if it pops an int32 element, in all other cases - we bail out from the jitted code.
  4023. * - This api cannot cause any implicit call and hence do not need implicit call bailout test around this api
  4024. */
  4025. int32 JavascriptNativeIntArray::Pop(ScriptContext * scriptContext, Var object)
  4026. {
  4027. Assert(JavascriptNativeIntArray::Is(object));
  4028. JavascriptNativeIntArray * arr = JavascriptNativeIntArray::FromVar(object);
  4029. Assert(arr->GetLength() != 0);
  4030. uint32 index = arr->length - 1;
  4031. int32 element = Js::JavascriptOperators::OP_GetNativeIntElementI_UInt32(object, index, scriptContext);
  4032. //If it is a missing item, then don't update the length - Pre-op Bail out will happen.
  4033. if(!SparseArraySegment<int32>::IsMissingItem(&element))
  4034. {
  4035. arr->SetLength(index);
  4036. }
  4037. return element;
  4038. }
  4039. /*
  4040. * JavascriptNativeFloatArray::Pop
  4041. * - Returns double value from the array.
  4042. * - Returns missing item when the element is not available in the array object.
  4043. * - It doesn't walk up the prototype chain.
  4044. * - Length is decremented only if it pops a double element, in all other cases - we bail out from the jitted code.
  4045. * - This api cannot cause any implicit call and hence do not need implicit call bailout test around this api
  4046. */
  4047. double JavascriptNativeFloatArray::Pop(ScriptContext * scriptContext, Var object)
  4048. {
  4049. Assert(JavascriptNativeFloatArray::Is(object));
  4050. JavascriptNativeFloatArray * arr = JavascriptNativeFloatArray::FromVar(object);
  4051. Assert(arr->GetLength() != 0);
  4052. uint32 index = arr->length - 1;
  4053. double element = Js::JavascriptOperators::OP_GetNativeFloatElementI_UInt32(object, index, scriptContext);
  4054. // If it is a missing item then don't update the length - Pre-op Bail out will happen.
  4055. if(!SparseArraySegment<double>::IsMissingItem(&element))
  4056. {
  4057. arr->SetLength(index);
  4058. }
  4059. return element;
  4060. }
  4061. /*
  4062. * JavascriptArray::Pop
  4063. * - Calls the generic Pop API, which can find elements from the prototype chain, when it is not available in the array object.
  4064. * - This API may cause implicit calls. Handles Array and non-array objects
  4065. */
  4066. Var JavascriptArray::Pop(ScriptContext * scriptContext, Var object)
  4067. {
  4068. if (JavascriptArray::Is(object))
  4069. {
  4070. return EntryPopJavascriptArray(scriptContext, object);
  4071. }
  4072. else
  4073. {
  4074. return EntryPopNonJavascriptArray(scriptContext, object);
  4075. }
  4076. }
  4077. Var JavascriptArray::EntryPopJavascriptArray(ScriptContext * scriptContext, Var object)
  4078. {
  4079. JavascriptArray * arr = JavascriptArray::FromVar(object);
  4080. uint32 length = arr->length;
  4081. if (length == 0)
  4082. {
  4083. // If length is 0, return 'undefined'
  4084. return scriptContext->GetLibrary()->GetUndefined();
  4085. }
  4086. uint32 index = length - 1;
  4087. Var element;
  4088. if (!arr->DirectGetItemAtFull(index, &element))
  4089. {
  4090. element = scriptContext->GetLibrary()->GetUndefined();
  4091. }
  4092. else
  4093. {
  4094. element = CrossSite::MarshalVar(scriptContext, element);
  4095. }
  4096. arr->SetLength(index); // SetLength will clear element at index
  4097. #ifdef VALIDATE_ARRAY
  4098. arr->ValidateArray();
  4099. #endif
  4100. return element;
  4101. }
  4102. Var JavascriptArray::EntryPopNonJavascriptArray(ScriptContext * scriptContext, Var object)
  4103. {
  4104. RecyclableObject* dynamicObject = nullptr;
  4105. if (FALSE == JavascriptConversion::ToObject(object, scriptContext, &dynamicObject))
  4106. {
  4107. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.pop"));
  4108. }
  4109. BigIndex length;
  4110. if (scriptContext->GetConfig()->IsES6ToLengthEnabled())
  4111. {
  4112. length = (uint64)JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(dynamicObject, scriptContext), scriptContext);
  4113. }
  4114. else
  4115. {
  4116. length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(dynamicObject, scriptContext), scriptContext);
  4117. }
  4118. ThrowTypeErrorOnFailureHelper h(scriptContext, _u("Array.prototype.pop"));
  4119. if (length == 0u)
  4120. {
  4121. // Set length = 0
  4122. h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(dynamicObject, dynamicObject, PropertyIds::length, TaggedInt::ToVarUnchecked(0), scriptContext, PropertyOperation_ThrowIfNotExtensible));
  4123. return scriptContext->GetLibrary()->GetUndefined();
  4124. }
  4125. BigIndex index = length;
  4126. --index;
  4127. Var element;
  4128. if (index.IsSmallIndex())
  4129. {
  4130. if (!JavascriptOperators::GetItem(dynamicObject, index.GetSmallIndex(), &element, scriptContext))
  4131. {
  4132. element = scriptContext->GetLibrary()->GetUndefined();
  4133. }
  4134. h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(dynamicObject, index.GetSmallIndex(), PropertyOperation_ThrowIfNotExtensible));
  4135. // Set the new length
  4136. h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(dynamicObject, dynamicObject, PropertyIds::length, JavascriptNumber::ToVar(index.GetSmallIndex(), scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible));
  4137. }
  4138. else
  4139. {
  4140. if (!JavascriptOperators::GetItem(dynamicObject, index.GetBigIndex(), &element, scriptContext))
  4141. {
  4142. element = scriptContext->GetLibrary()->GetUndefined();
  4143. }
  4144. h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(dynamicObject, index.GetBigIndex(), PropertyOperation_ThrowIfNotExtensible));
  4145. // Set the new length
  4146. h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(dynamicObject, dynamicObject, PropertyIds::length, JavascriptNumber::ToVar(index.GetBigIndex(), scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible));
  4147. }
  4148. return element;
  4149. }
  4150. Var JavascriptArray::EntryPop(RecyclableObject* function, CallInfo callInfo, ...)
  4151. {
  4152. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  4153. ARGUMENTS(args, callInfo);
  4154. ScriptContext* scriptContext = function->GetScriptContext();
  4155. Assert(!(callInfo.Flags & CallFlags_New));
  4156. if (args.Info.Count == 0)
  4157. {
  4158. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.pop"));
  4159. }
  4160. if (JavascriptArray::Is(args[0]))
  4161. {
  4162. return EntryPopJavascriptArray(scriptContext, args.Values[0]);
  4163. }
  4164. else
  4165. {
  4166. return EntryPopNonJavascriptArray(scriptContext, args.Values[0]);
  4167. }
  4168. }
  4169. /*
  4170. * JavascriptNativeIntArray::Push
  4171. * Pushes Int element in a native Int Array.
  4172. * We call the generic Push, if the array is not native Int or we have a really big array.
  4173. */
  4174. Var JavascriptNativeIntArray::Push(ScriptContext * scriptContext, Var array, int value)
  4175. {
  4176. // Handle non crossSite native int arrays here length within MaxArrayLength.
  4177. // JavascriptArray::Push will handle other cases.
  4178. if (JavascriptNativeIntArray::IsNonCrossSite(array))
  4179. {
  4180. JavascriptNativeIntArray * nativeIntArray = JavascriptNativeIntArray::FromVar(array);
  4181. Assert(!nativeIntArray->IsCrossSiteObject());
  4182. uint32 n = nativeIntArray->length;
  4183. if(n < JavascriptArray::MaxArrayLength)
  4184. {
  4185. nativeIntArray->SetItem(n, value);
  4186. n++;
  4187. AssertMsg(n == nativeIntArray->length, "Wrong update to the length of the native Int array");
  4188. return JavascriptNumber::ToVar(n, scriptContext);
  4189. }
  4190. }
  4191. return JavascriptArray::Push(scriptContext, array, JavascriptNumber::ToVar(value, scriptContext));
  4192. }
  4193. /*
  4194. * JavascriptNativeFloatArray::Push
  4195. * Pushes Float element in a native Int Array.
  4196. * We call the generic Push, if the array is not native Float or we have a really big array.
  4197. */
  4198. Var JavascriptNativeFloatArray::Push(ScriptContext * scriptContext, Var * array, double value)
  4199. {
  4200. // Handle non crossSite native int arrays here length within MaxArrayLength.
  4201. // JavascriptArray::Push will handle other cases.
  4202. if(JavascriptNativeFloatArray::IsNonCrossSite(array))
  4203. {
  4204. JavascriptNativeFloatArray * nativeFloatArray = JavascriptNativeFloatArray::FromVar(array);
  4205. Assert(!nativeFloatArray->IsCrossSiteObject());
  4206. uint32 n = nativeFloatArray->length;
  4207. if(n < JavascriptArray::MaxArrayLength)
  4208. {
  4209. nativeFloatArray->SetItem(n, value);
  4210. n++;
  4211. AssertMsg(n == nativeFloatArray->length, "Wrong update to the length of the native Float array");
  4212. return JavascriptNumber::ToVar(n, scriptContext);
  4213. }
  4214. }
  4215. return JavascriptArray::Push(scriptContext, array, JavascriptNumber::ToVarNoCheck(value, scriptContext));
  4216. }
  4217. /*
  4218. * JavascriptArray::Push
  4219. * Pushes Var element in a Var Array.
  4220. */
  4221. Var JavascriptArray::Push(ScriptContext * scriptContext, Var object, Var value)
  4222. {
  4223. Var args[2];
  4224. args[0] = object;
  4225. args[1] = value;
  4226. if (JavascriptArray::Is(object))
  4227. {
  4228. return EntryPushJavascriptArray(scriptContext, args, 2);
  4229. }
  4230. else
  4231. {
  4232. return EntryPushNonJavascriptArray(scriptContext, args, 2);
  4233. }
  4234. }
  4235. /*
  4236. * EntryPushNonJavascriptArray
  4237. * - Handles Entry push calls, when Objects are not javascript arrays
  4238. */
  4239. Var JavascriptArray::EntryPushNonJavascriptArray(ScriptContext * scriptContext, Var * args, uint argCount)
  4240. {
  4241. RecyclableObject* obj = nullptr;
  4242. if (FALSE == JavascriptConversion::ToObject(args[0], scriptContext, &obj))
  4243. {
  4244. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.push"));
  4245. }
  4246. Var length = JavascriptOperators::OP_GetLength(obj, scriptContext);
  4247. if(JavascriptOperators::GetTypeId(length) == TypeIds_Undefined && scriptContext->GetThreadContext()->IsDisableImplicitCall() &&
  4248. scriptContext->GetThreadContext()->GetImplicitCallFlags() != Js::ImplicitCall_None)
  4249. {
  4250. return length;
  4251. }
  4252. ThrowTypeErrorOnFailureHelper h(scriptContext, _u("Array.prototype.push"));
  4253. BigIndex n;
  4254. if (scriptContext->GetConfig()->IsES6ToLengthEnabled())
  4255. {
  4256. n = (uint64) JavascriptConversion::ToLength(length, scriptContext);
  4257. }
  4258. else
  4259. {
  4260. n = JavascriptConversion::ToUInt32(length, scriptContext);
  4261. }
  4262. // First handle "small" indices.
  4263. uint index;
  4264. for (index=1; index < argCount && n < JavascriptArray::MaxArrayLength; ++index, ++n)
  4265. {
  4266. if (h.IsThrowTypeError(JavascriptOperators::SetItem(obj, obj, n.GetSmallIndex(), args[index], scriptContext, PropertyOperation_ThrowIfNotExtensible)))
  4267. {
  4268. if (scriptContext->GetThreadContext()->RecordImplicitException())
  4269. {
  4270. h.ThrowTypeErrorOnFailure();
  4271. }
  4272. else
  4273. {
  4274. return nullptr;
  4275. }
  4276. }
  4277. }
  4278. // Use BigIndex if we need to push indices >= MaxArrayLength
  4279. if (index < argCount)
  4280. {
  4281. BigIndex big = n;
  4282. for (; index < argCount; ++index, ++big)
  4283. {
  4284. if (h.IsThrowTypeError(big.SetItem(obj, args[index], PropertyOperation_ThrowIfNotExtensible)))
  4285. {
  4286. if(scriptContext->GetThreadContext()->RecordImplicitException())
  4287. {
  4288. h.ThrowTypeErrorOnFailure();
  4289. }
  4290. else
  4291. {
  4292. return nullptr;
  4293. }
  4294. }
  4295. }
  4296. // Set the new length; for objects it is all right for this to be >= MaxArrayLength
  4297. if (h.IsThrowTypeError(JavascriptOperators::SetProperty(obj, obj, PropertyIds::length, big.ToNumber(scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible)))
  4298. {
  4299. if(scriptContext->GetThreadContext()->RecordImplicitException())
  4300. {
  4301. h.ThrowTypeErrorOnFailure();
  4302. }
  4303. else
  4304. {
  4305. return nullptr;
  4306. }
  4307. }
  4308. return big.ToNumber(scriptContext);
  4309. }
  4310. else
  4311. {
  4312. // Set the new length
  4313. Var lengthAsNUmberVar = JavascriptNumber::ToVar(n.IsSmallIndex() ? n.GetSmallIndex() : n.GetBigIndex(), scriptContext);
  4314. if (h.IsThrowTypeError(JavascriptOperators::SetProperty(obj, obj, PropertyIds::length, lengthAsNUmberVar, scriptContext, PropertyOperation_ThrowIfNotExtensible)))
  4315. {
  4316. if(scriptContext->GetThreadContext()->RecordImplicitException())
  4317. {
  4318. h.ThrowTypeErrorOnFailure();
  4319. }
  4320. else
  4321. {
  4322. return nullptr;
  4323. }
  4324. }
  4325. return lengthAsNUmberVar;
  4326. }
  4327. }
  4328. /*
  4329. * JavascriptArray::EntryPushJavascriptArray
  4330. * Pushes Var element in a Var Array.
  4331. * Returns the length of the array.
  4332. */
  4333. Var JavascriptArray::EntryPushJavascriptArray(ScriptContext * scriptContext, Var * args, uint argCount)
  4334. {
  4335. JavascriptArray * arr = JavascriptArray::FromAnyArray(args[0]);
  4336. uint n = arr->length;
  4337. ThrowTypeErrorOnFailureHelper h(scriptContext, _u("Array.prototype.push"));
  4338. // Fast Path for one push for small indexes
  4339. if (argCount == 2 && n < JavascriptArray::MaxArrayLength)
  4340. {
  4341. // Set Item is overridden by CrossSiteObject, so no need to check for IsCrossSiteObject()
  4342. h.ThrowTypeErrorOnFailure(arr->SetItem(n, args[1], PropertyOperation_None));
  4343. return JavascriptNumber::ToVar(n + 1, scriptContext);
  4344. }
  4345. // Fast Path for multiple push for small indexes
  4346. if (JavascriptArray::MaxArrayLength - argCount + 1 > n && JavascriptArray::IsVarArray(arr) && scriptContext == arr->GetScriptContext())
  4347. {
  4348. uint index;
  4349. for (index = 1; index < argCount; ++index, ++n)
  4350. {
  4351. Assert(n != JavascriptArray::MaxArrayLength);
  4352. // Set Item is overridden by CrossSiteObject, so no need to check for IsCrossSiteObject()
  4353. arr->JavascriptArray::DirectSetItemAt(n, args[index]);
  4354. }
  4355. return JavascriptNumber::ToVar(n, scriptContext);
  4356. }
  4357. return EntryPushJavascriptArrayNoFastPath(scriptContext, args, argCount);
  4358. }
  4359. Var JavascriptArray::EntryPushJavascriptArrayNoFastPath(ScriptContext * scriptContext, Var * args, uint argCount)
  4360. {
  4361. JavascriptArray * arr = JavascriptArray::FromAnyArray(args[0]);
  4362. uint n = arr->length;
  4363. ThrowTypeErrorOnFailureHelper h(scriptContext, _u("Array.prototype.push"));
  4364. // First handle "small" indices.
  4365. uint index;
  4366. for (index = 1; index < argCount && n < JavascriptArray::MaxArrayLength; ++index, ++n)
  4367. {
  4368. // Set Item is overridden by CrossSiteObject, so no need to check for IsCrossSiteObject()
  4369. h.ThrowTypeErrorOnFailure(arr->SetItem(n, args[index], PropertyOperation_None));
  4370. }
  4371. // Use BigIndex if we need to push indices >= MaxArrayLength
  4372. if (index < argCount)
  4373. {
  4374. // Not supporting native array with BigIndex.
  4375. arr = EnsureNonNativeArray(arr);
  4376. Assert(n == JavascriptArray::MaxArrayLength);
  4377. for (BigIndex big = n; index < argCount; ++index, ++big)
  4378. {
  4379. h.ThrowTypeErrorOnFailure(big.SetItem(arr, args[index]));
  4380. }
  4381. #ifdef VALIDATE_ARRAY
  4382. arr->ValidateArray();
  4383. #endif
  4384. // This is where we should set the length, but for arrays it cannot be >= MaxArrayLength
  4385. JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthAssignIncorrect);
  4386. }
  4387. #ifdef VALIDATE_ARRAY
  4388. arr->ValidateArray();
  4389. #endif
  4390. return JavascriptNumber::ToVar(n, scriptContext);
  4391. }
  4392. /*
  4393. * JavascriptArray::EntryPush
  4394. * Handles Push calls(Script Function)
  4395. */
  4396. Var JavascriptArray::EntryPush(RecyclableObject* function, CallInfo callInfo, ...)
  4397. {
  4398. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  4399. ARGUMENTS(args, callInfo);
  4400. ScriptContext* scriptContext = function->GetScriptContext();
  4401. Assert(!(callInfo.Flags & CallFlags_New));
  4402. if (args.Info.Count == 0)
  4403. {
  4404. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.push"));
  4405. }
  4406. if (JavascriptArray::Is(args[0]))
  4407. {
  4408. return EntryPushJavascriptArray(scriptContext, args.Values, args.Info.Count);
  4409. }
  4410. else
  4411. {
  4412. return EntryPushNonJavascriptArray(scriptContext, args.Values, args.Info.Count);
  4413. }
  4414. }
  4415. Var JavascriptArray::EntryReverse(RecyclableObject* function, CallInfo callInfo, ...)
  4416. {
  4417. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  4418. ARGUMENTS(args, callInfo);
  4419. ScriptContext* scriptContext = function->GetScriptContext();
  4420. Assert(!(callInfo.Flags & CallFlags_New));
  4421. if (args.Info.Count == 0)
  4422. {
  4423. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.reverse"));
  4424. }
  4425. BigIndex length = 0u;
  4426. JavascriptArray* pArr = nullptr;
  4427. RecyclableObject* obj = nullptr;
  4428. if (JavascriptArray::Is(args[0]))
  4429. {
  4430. pArr = JavascriptArray::FromVar(args[0]);
  4431. #if ENABLE_COPYONACCESS_ARRAY
  4432. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(pArr);
  4433. #endif
  4434. obj = pArr;
  4435. }
  4436. else
  4437. {
  4438. if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj))
  4439. {
  4440. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.reverse"));
  4441. }
  4442. }
  4443. // In ES6-mode, we always load the length property from the object instead of using the internal slot.
  4444. // Even for arrays, this is now observable via proxies.
  4445. // If source object is not an array, we fall back to this behavior anyway.
  4446. if (scriptContext->GetConfig()->IsES6TypedArrayExtensionsEnabled() || pArr == nullptr)
  4447. {
  4448. if (scriptContext->GetConfig()->IsES6ToLengthEnabled())
  4449. {
  4450. length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext);
  4451. }
  4452. else
  4453. {
  4454. length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext);
  4455. }
  4456. }
  4457. else
  4458. {
  4459. length = pArr->length;
  4460. }
  4461. if (length.IsSmallIndex())
  4462. {
  4463. return JavascriptArray::ReverseHelper(pArr, nullptr, obj, length.GetSmallIndex(), scriptContext);
  4464. }
  4465. 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
  4466. return JavascriptArray::ReverseHelper(pArr, nullptr, obj, length.GetBigIndex(), scriptContext);
  4467. }
  4468. // Array.prototype.reverse as described in ES6.0 (draft 22) Section 22.1.3.20
  4469. template <typename T>
  4470. Var JavascriptArray::ReverseHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, ScriptContext* scriptContext)
  4471. {
  4472. T middle = length / 2;
  4473. Var lowerValue = nullptr, upperValue = nullptr;
  4474. T lowerExists, upperExists;
  4475. const char16* methodName;
  4476. bool isTypedArrayEntryPoint = typedArrayBase != nullptr;
  4477. if (isTypedArrayEntryPoint)
  4478. {
  4479. methodName = _u("[TypedArray].prototype.reverse");
  4480. }
  4481. else
  4482. {
  4483. methodName = _u("Array.prototype.reverse");
  4484. }
  4485. // If we came from Array.prototype.map and source object is not a JavascriptArray, source could be a TypedArray
  4486. if (!isTypedArrayEntryPoint && pArr == nullptr && TypedArrayBase::Is(obj))
  4487. {
  4488. typedArrayBase = TypedArrayBase::FromVar(obj);
  4489. }
  4490. ThrowTypeErrorOnFailureHelper h(scriptContext, methodName);
  4491. if (pArr)
  4492. {
  4493. Recycler * recycler = scriptContext->GetRecycler();
  4494. if (length <= 1)
  4495. {
  4496. return pArr;
  4497. }
  4498. if (pArr->IsFillFromPrototypes())
  4499. {
  4500. // For odd-length arrays, the middle element is unchanged,
  4501. // so we cannot fill it from the prototypes.
  4502. if (length % 2 == 0)
  4503. {
  4504. pArr->FillFromPrototypes(0, (uint32)length);
  4505. }
  4506. else
  4507. {
  4508. middle = length / 2;
  4509. pArr->FillFromPrototypes(0, (uint32)middle);
  4510. pArr->FillFromPrototypes(1 + (uint32)middle, (uint32)length);
  4511. }
  4512. }
  4513. if (pArr->HasNoMissingValues() && pArr->head && pArr->head->next)
  4514. {
  4515. // This function currently does not track missing values in the head segment if there are multiple segments
  4516. pArr->SetHasNoMissingValues(false);
  4517. }
  4518. SparseArraySegmentBase* seg = pArr->head;
  4519. SparseArraySegmentBase *prevSeg = nullptr;
  4520. SparseArraySegmentBase *nextSeg = nullptr;
  4521. SparseArraySegmentBase *pinPrevSeg = nullptr;
  4522. bool isIntArray = false;
  4523. bool isFloatArray = false;
  4524. if (JavascriptNativeIntArray::Is(pArr))
  4525. {
  4526. isIntArray = true;
  4527. }
  4528. else if (JavascriptNativeFloatArray::Is(pArr))
  4529. {
  4530. isFloatArray = true;
  4531. }
  4532. while (seg)
  4533. {
  4534. nextSeg = seg->next;
  4535. // If seg.length == 0, it is possible that (seg.left + seg.length == prev.left + prev.length),
  4536. // resulting in 2 segments sharing the same "left".
  4537. if (seg->length > 0)
  4538. {
  4539. if (isIntArray)
  4540. {
  4541. ((SparseArraySegment<int32>*)seg)->ReverseSegment(recycler);
  4542. }
  4543. else if (isFloatArray)
  4544. {
  4545. ((SparseArraySegment<double>*)seg)->ReverseSegment(recycler);
  4546. }
  4547. else
  4548. {
  4549. ((SparseArraySegment<Var>*)seg)->ReverseSegment(recycler);
  4550. }
  4551. seg->left = ((uint32)length) - (seg->left + seg->length);
  4552. seg->next = prevSeg;
  4553. // Make sure size doesn't overlap with next segment.
  4554. // An easy fix is to just truncate the size...
  4555. seg->EnsureSizeInBound();
  4556. // If the last segment is a leaf, then we may be losing our last scanned pointer to its previous
  4557. // segment. Hold onto it with pinPrevSeg until we reallocate below.
  4558. pinPrevSeg = prevSeg;
  4559. prevSeg = seg;
  4560. }
  4561. seg = nextSeg;
  4562. }
  4563. pArr->head = prevSeg;
  4564. // Just dump the segment map on reverse
  4565. pArr->ClearSegmentMap();
  4566. if (isIntArray)
  4567. {
  4568. if (pArr->head && pArr->head->next && SparseArraySegmentBase::IsLeafSegment(pArr->head, recycler))
  4569. {
  4570. pArr->ReallocNonLeafSegment((SparseArraySegment<int32>*)pArr->head, pArr->head->next);
  4571. }
  4572. pArr->EnsureHeadStartsFromZero<int32>(recycler);
  4573. }
  4574. else if (isFloatArray)
  4575. {
  4576. if (pArr->head && pArr->head->next && SparseArraySegmentBase::IsLeafSegment(pArr->head, recycler))
  4577. {
  4578. pArr->ReallocNonLeafSegment((SparseArraySegment<double>*)pArr->head, pArr->head->next);
  4579. }
  4580. pArr->EnsureHeadStartsFromZero<double>(recycler);
  4581. }
  4582. else
  4583. {
  4584. pArr->EnsureHeadStartsFromZero<Var>(recycler);
  4585. }
  4586. pArr->InvalidateLastUsedSegment(); // lastUsedSegment might be 0-length and discarded above
  4587. #ifdef VALIDATE_ARRAY
  4588. pArr->ValidateArray();
  4589. #endif
  4590. }
  4591. else if (typedArrayBase)
  4592. {
  4593. Assert(length <= JavascriptArray::MaxArrayLength);
  4594. if (typedArrayBase->GetLength() == length)
  4595. {
  4596. // If typedArrayBase->length == length then we know that the TypedArray will have all items < length
  4597. // and we won't have to check that the elements exist or not.
  4598. for (uint32 lower = 0; lower < (uint32)middle; lower++)
  4599. {
  4600. uint32 upper = (uint32)length - lower - 1;
  4601. lowerValue = typedArrayBase->DirectGetItem(lower);
  4602. upperValue = typedArrayBase->DirectGetItem(upper);
  4603. // We still have to call HasItem even though we know the TypedArray has both lower and upper because
  4604. // there may be a proxy handler trapping HasProperty.
  4605. lowerExists = typedArrayBase->HasItem(lower);
  4606. upperExists = typedArrayBase->HasItem(upper);
  4607. h.ThrowTypeErrorOnFailure(typedArrayBase->DirectSetItem(lower, upperValue));
  4608. h.ThrowTypeErrorOnFailure(typedArrayBase->DirectSetItem(upper, lowerValue));
  4609. }
  4610. }
  4611. else
  4612. {
  4613. for (uint32 lower = 0; lower < middle; lower++)
  4614. {
  4615. uint32 upper = (uint32)length - lower - 1;
  4616. lowerValue = typedArrayBase->DirectGetItem(lower);
  4617. upperValue = typedArrayBase->DirectGetItem(upper);
  4618. lowerExists = typedArrayBase->HasItem(lower);
  4619. upperExists = typedArrayBase->HasItem(upper);
  4620. if (lowerExists)
  4621. {
  4622. if (upperExists)
  4623. {
  4624. h.ThrowTypeErrorOnFailure(typedArrayBase->DirectSetItem(lower, upperValue));
  4625. h.ThrowTypeErrorOnFailure(typedArrayBase->DirectSetItem(upper, lowerValue));
  4626. }
  4627. else
  4628. {
  4629. // This will always fail for a TypedArray if lower < length
  4630. h.ThrowTypeErrorOnFailure(typedArrayBase->DeleteItem(lower, PropertyOperation_ThrowIfNotExtensible));
  4631. h.ThrowTypeErrorOnFailure(typedArrayBase->DirectSetItem(upper, lowerValue));
  4632. }
  4633. }
  4634. else
  4635. {
  4636. if (upperExists)
  4637. {
  4638. h.ThrowTypeErrorOnFailure(typedArrayBase->DirectSetItem(lower, upperValue));
  4639. // This will always fail for a TypedArray if upper < length
  4640. h.ThrowTypeErrorOnFailure(typedArrayBase->DeleteItem(upper, PropertyOperation_ThrowIfNotExtensible));
  4641. }
  4642. }
  4643. }
  4644. }
  4645. }
  4646. else
  4647. {
  4648. for (T lower = 0; lower < middle; lower++)
  4649. {
  4650. T upper = length - lower - 1;
  4651. lowerExists = JavascriptOperators::HasItem(obj, lower) &&
  4652. JavascriptOperators::GetItem(obj, lower, &lowerValue, scriptContext);
  4653. upperExists = JavascriptOperators::HasItem(obj, upper) &&
  4654. JavascriptOperators::GetItem(obj, upper, &upperValue, scriptContext);
  4655. if (lowerExists)
  4656. {
  4657. if (upperExists)
  4658. {
  4659. h.ThrowTypeErrorOnFailure(JavascriptOperators::SetItem(obj, obj, lower, upperValue, scriptContext, PropertyOperation_ThrowIfNotExtensible));
  4660. h.ThrowTypeErrorOnFailure(JavascriptOperators::SetItem(obj, obj, upper, lowerValue, scriptContext, PropertyOperation_ThrowIfNotExtensible));
  4661. }
  4662. else
  4663. {
  4664. h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(obj, lower, PropertyOperation_ThrowIfNotExtensible));
  4665. h.ThrowTypeErrorOnFailure(JavascriptOperators::SetItem(obj, obj, upper, lowerValue, scriptContext, PropertyOperation_ThrowIfNotExtensible));
  4666. }
  4667. }
  4668. else
  4669. {
  4670. if (upperExists)
  4671. {
  4672. h.ThrowTypeErrorOnFailure(JavascriptOperators::SetItem(obj, obj, lower, upperValue, scriptContext, PropertyOperation_ThrowIfNotExtensible));
  4673. h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(obj, upper, PropertyOperation_ThrowIfNotExtensible));
  4674. }
  4675. }
  4676. }
  4677. }
  4678. return obj;
  4679. }
  4680. template<typename T>
  4681. void JavascriptArray::ShiftHelper(JavascriptArray* pArr, ScriptContext * scriptContext)
  4682. {
  4683. Recycler * recycler = scriptContext->GetRecycler();
  4684. SparseArraySegment<T>* next = (SparseArraySegment<T>*)pArr->head->next;
  4685. while (next)
  4686. {
  4687. next->left--;
  4688. next = (SparseArraySegment<T>*)next->next;
  4689. }
  4690. // head and next might overlap as the next segment left is decremented
  4691. next = (SparseArraySegment<T>*)pArr->head->next;
  4692. if (next && (pArr->head->size > next->left))
  4693. {
  4694. AssertMsg(pArr->head->left == 0, "Array always points to a head starting at index 0");
  4695. AssertMsg(pArr->head->size == next->left + 1, "Shift next->left overlaps current segment by more than 1 element");
  4696. SparseArraySegment<T> *head = (SparseArraySegment<T>*)pArr->head;
  4697. // Merge the two adjacent segments
  4698. if (next->length != 0)
  4699. {
  4700. uint32 offset = head->size - 1;
  4701. // There is room for one unshifted element in head segment.
  4702. // Hence it's enough if we grow the head segment by next->length - 1
  4703. if (next->next)
  4704. {
  4705. // If we have a next->next, we can't grow pass the left of that
  4706. // If the array had a segment map before, the next->next might just be right after next as well.
  4707. // So we just need to grow to the end of the next segment
  4708. // TODO: merge that segment too?
  4709. Assert(next->next->left >= head->size);
  4710. uint32 maxGrowSize = next->next->left - head->size;
  4711. if (maxGrowSize != 0)
  4712. {
  4713. head = head->GrowByMinMax(recycler, next->length - 1, maxGrowSize); //-1 is to account for unshift
  4714. }
  4715. else
  4716. {
  4717. // The next segment is only of length one, so we already have space in the header to copy that
  4718. Assert(next->length == 1);
  4719. }
  4720. }
  4721. else
  4722. {
  4723. head = head->GrowByMin(recycler, next->length - 1); //-1 is to account for unshift
  4724. }
  4725. memmove(head->elements + offset, next->elements, next->length * sizeof(T));
  4726. head->length = offset + next->length;
  4727. pArr->head = head;
  4728. }
  4729. head->next = next->next;
  4730. pArr->InvalidateLastUsedSegment();
  4731. }
  4732. #ifdef VALIDATE_ARRAY
  4733. pArr->ValidateArray();
  4734. #endif
  4735. }
  4736. Var JavascriptArray::EntryShift(RecyclableObject* function, CallInfo callInfo, ...)
  4737. {
  4738. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  4739. ARGUMENTS(args, callInfo);
  4740. ScriptContext* scriptContext = function->GetScriptContext();
  4741. Assert(!(callInfo.Flags & CallFlags_New));
  4742. Var res = scriptContext->GetLibrary()->GetUndefined();
  4743. if (args.Info.Count == 0)
  4744. {
  4745. return res;
  4746. }
  4747. if (JavascriptArray::Is(args[0]))
  4748. {
  4749. JavascriptArray * pArr = JavascriptArray::FromVar(args[0]);
  4750. #if ENABLE_COPYONACCESS_ARRAY
  4751. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(pArr);
  4752. #endif
  4753. if (pArr->length == 0)
  4754. {
  4755. return res;
  4756. }
  4757. if(pArr->IsFillFromPrototypes())
  4758. {
  4759. pArr->FillFromPrototypes(0, pArr->length); // We need find all missing value from [[proto]] object
  4760. }
  4761. if(pArr->HasNoMissingValues() && pArr->head && pArr->head->next)
  4762. {
  4763. // This function currently does not track missing values in the head segment if there are multiple segments
  4764. pArr->SetHasNoMissingValues(false);
  4765. }
  4766. pArr->length--;
  4767. pArr->ClearSegmentMap(); // Dump segmentMap on shift (before any allocation)
  4768. Recycler * recycler = scriptContext->GetRecycler();
  4769. bool isIntArray = false;
  4770. bool isFloatArray = false;
  4771. if(JavascriptNativeIntArray::Is(pArr))
  4772. {
  4773. isIntArray = true;
  4774. }
  4775. else if(JavascriptNativeFloatArray::Is(pArr))
  4776. {
  4777. isFloatArray = true;
  4778. }
  4779. if (pArr->head->length != 0)
  4780. {
  4781. if(isIntArray)
  4782. {
  4783. int32 nativeResult = ((SparseArraySegment<int32>*)pArr->head)->GetElement(0);
  4784. if(SparseArraySegment<int32>::IsMissingItem(&nativeResult))
  4785. {
  4786. res = scriptContext->GetLibrary()->GetUndefined();
  4787. }
  4788. else
  4789. {
  4790. res = Js::JavascriptNumber::ToVar(nativeResult, scriptContext);
  4791. }
  4792. ((SparseArraySegment<int32>*)pArr->head)->RemoveElement(recycler, 0);
  4793. }
  4794. else if (isFloatArray)
  4795. {
  4796. double nativeResult = ((SparseArraySegment<double>*)pArr->head)->GetElement(0);
  4797. if(SparseArraySegment<double>::IsMissingItem(&nativeResult))
  4798. {
  4799. res = scriptContext->GetLibrary()->GetUndefined();
  4800. }
  4801. else
  4802. {
  4803. res = Js::JavascriptNumber::ToVarNoCheck(nativeResult, scriptContext);
  4804. }
  4805. ((SparseArraySegment<double>*)pArr->head)->RemoveElement(recycler, 0);
  4806. }
  4807. else
  4808. {
  4809. res = ((SparseArraySegment<Var>*)pArr->head)->GetElement(0);
  4810. if(SparseArraySegment<Var>::IsMissingItem(&res))
  4811. {
  4812. res = scriptContext->GetLibrary()->GetUndefined();
  4813. }
  4814. else
  4815. {
  4816. res = CrossSite::MarshalVar(scriptContext, res);
  4817. }
  4818. ((SparseArraySegment<Var>*)pArr->head)->RemoveElement(recycler, 0);
  4819. }
  4820. }
  4821. if(isIntArray)
  4822. {
  4823. ShiftHelper<int32>(pArr, scriptContext);
  4824. }
  4825. else if (isFloatArray)
  4826. {
  4827. ShiftHelper<double>(pArr, scriptContext);
  4828. }
  4829. else
  4830. {
  4831. ShiftHelper<Var>(pArr, scriptContext);
  4832. }
  4833. }
  4834. else
  4835. {
  4836. RecyclableObject* dynamicObject = nullptr;
  4837. if (FALSE == JavascriptConversion::ToObject(args[0], scriptContext, &dynamicObject))
  4838. {
  4839. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.shift"));
  4840. }
  4841. ThrowTypeErrorOnFailureHelper h(scriptContext, _u("Array.prototype.shift"));
  4842. BigIndex length = 0u;
  4843. if (scriptContext->GetConfig()->IsES6ToLengthEnabled())
  4844. {
  4845. length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(dynamicObject, scriptContext), scriptContext);
  4846. }
  4847. else
  4848. {
  4849. length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(dynamicObject, scriptContext), scriptContext);
  4850. }
  4851. if (length == 0u)
  4852. {
  4853. // If length is 0, return 'undefined'
  4854. h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(dynamicObject, dynamicObject, PropertyIds::length, TaggedInt::ToVarUnchecked(0), scriptContext, PropertyOperation_ThrowIfNotExtensible));
  4855. return scriptContext->GetLibrary()->GetUndefined();
  4856. }
  4857. if (!JavascriptOperators::GetItem(dynamicObject, 0u, &res, scriptContext))
  4858. {
  4859. res = scriptContext->GetLibrary()->GetUndefined();
  4860. }
  4861. --length;
  4862. uint32 lengthToUin32Max = length.IsSmallIndex() ? length.GetSmallIndex() : MaxArrayLength;
  4863. for (uint32 i = 0u; i < lengthToUin32Max; i++)
  4864. {
  4865. if (JavascriptOperators::HasItem(dynamicObject, i + 1))
  4866. {
  4867. Var element = JavascriptOperators::GetItem(dynamicObject, i + 1, scriptContext);
  4868. h.ThrowTypeErrorOnFailure(JavascriptOperators::SetItem(dynamicObject, dynamicObject, i, element, scriptContext, PropertyOperation_ThrowIfNotExtensible, /*skipPrototypeCheck*/ true));
  4869. }
  4870. else
  4871. {
  4872. h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(dynamicObject, i, PropertyOperation_ThrowIfNotExtensible));
  4873. }
  4874. }
  4875. for (uint64 i = MaxArrayLength; length > i; i++)
  4876. {
  4877. if (JavascriptOperators::HasItem(dynamicObject, i + 1))
  4878. {
  4879. Var element = JavascriptOperators::GetItem(dynamicObject, i + 1, scriptContext);
  4880. h.ThrowTypeErrorOnFailure(JavascriptOperators::SetItem(dynamicObject, dynamicObject, i, element, scriptContext, PropertyOperation_ThrowIfNotExtensible));
  4881. }
  4882. else
  4883. {
  4884. h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(dynamicObject, i, PropertyOperation_ThrowIfNotExtensible));
  4885. }
  4886. }
  4887. if (length.IsSmallIndex())
  4888. {
  4889. h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(dynamicObject, length.GetSmallIndex(), PropertyOperation_ThrowIfNotExtensible));
  4890. h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(dynamicObject, dynamicObject, PropertyIds::length, JavascriptNumber::ToVar(length.GetSmallIndex(), scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible));
  4891. }
  4892. else
  4893. {
  4894. h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(dynamicObject, length.GetBigIndex(), PropertyOperation_ThrowIfNotExtensible));
  4895. h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(dynamicObject, dynamicObject, PropertyIds::length, JavascriptNumber::ToVar(length.GetBigIndex(), scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible));
  4896. }
  4897. }
  4898. return res;
  4899. }
  4900. Js::JavascriptArray* JavascriptArray::CreateNewArrayHelper(uint32 len, bool isIntArray, bool isFloatArray, Js::JavascriptArray* baseArray, ScriptContext* scriptContext)
  4901. {
  4902. if (isIntArray)
  4903. {
  4904. Js::JavascriptNativeIntArray *pnewArr = scriptContext->GetLibrary()->CreateNativeIntArray(len);
  4905. pnewArr->EnsureHead<int32>();
  4906. #if ENABLE_PROFILE_INFO
  4907. pnewArr->CopyArrayProfileInfo(Js::JavascriptNativeIntArray::FromVar(baseArray));
  4908. #endif
  4909. return pnewArr;
  4910. }
  4911. else if (isFloatArray)
  4912. {
  4913. Js::JavascriptNativeFloatArray *pnewArr = scriptContext->GetLibrary()->CreateNativeFloatArray(len);
  4914. pnewArr->EnsureHead<double>();
  4915. #if ENABLE_PROFILE_INFO
  4916. pnewArr->CopyArrayProfileInfo(Js::JavascriptNativeFloatArray::FromVar(baseArray));
  4917. #endif
  4918. return pnewArr;
  4919. }
  4920. else
  4921. {
  4922. JavascriptArray *pnewArr = pnewArr = scriptContext->GetLibrary()->CreateArray(len);
  4923. pnewArr->EnsureHead<Var>();
  4924. return pnewArr;
  4925. }
  4926. }
  4927. template<typename T>
  4928. void JavascriptArray::SliceHelper(JavascriptArray* pArr, JavascriptArray* pnewArr, uint32 start, uint32 newLen)
  4929. {
  4930. SparseArraySegment<T>* headSeg = (SparseArraySegment<T>*)pArr->head;
  4931. SparseArraySegment<T>* pnewHeadSeg = (SparseArraySegment<T>*)pnewArr->head;
  4932. // Fill the newly created sliced array
  4933. js_memcpy_s(pnewHeadSeg->elements, sizeof(T) * newLen, headSeg->elements + start, sizeof(T) * newLen);
  4934. pnewHeadSeg->length = newLen;
  4935. Assert(pnewHeadSeg->length <= pnewHeadSeg->size);
  4936. // Prototype lookup for missing elements
  4937. if (!pArr->HasNoMissingValues())
  4938. {
  4939. for (uint32 i = 0; i < newLen; i++)
  4940. {
  4941. if (SparseArraySegment<T>::IsMissingItem(&headSeg->elements[i+start]))
  4942. {
  4943. Var element;
  4944. pnewArr->SetHasNoMissingValues(false);
  4945. if (pArr->DirectGetItemAtFull(i + start, &element))
  4946. {
  4947. pnewArr->SetItem(i, element, PropertyOperation_None);
  4948. }
  4949. }
  4950. }
  4951. }
  4952. #ifdef DBG
  4953. else
  4954. {
  4955. for (uint32 i = 0; i < newLen; i++)
  4956. {
  4957. AssertMsg(!SparseArraySegment<T>::IsMissingItem(&headSeg->elements[i+start]), "Array marked incorrectly as having missing value");
  4958. }
  4959. }
  4960. #endif
  4961. }
  4962. // If the creating profile data has changed, convert it to the type of array indicated
  4963. // in the profile
  4964. void JavascriptArray::GetArrayTypeAndConvert(bool* isIntArray, bool* isFloatArray)
  4965. {
  4966. if (JavascriptNativeIntArray::Is(this))
  4967. {
  4968. #if ENABLE_PROFILE_INFO
  4969. JavascriptNativeIntArray* nativeIntArray = JavascriptNativeIntArray::FromVar(this);
  4970. ArrayCallSiteInfo* info = nativeIntArray->GetArrayCallSiteInfo();
  4971. if(!info || info->IsNativeIntArray())
  4972. {
  4973. *isIntArray = true;
  4974. }
  4975. else if(info->IsNativeFloatArray())
  4976. {
  4977. JavascriptNativeIntArray::ToNativeFloatArray(nativeIntArray);
  4978. *isFloatArray = true;
  4979. }
  4980. else
  4981. {
  4982. JavascriptNativeIntArray::ToVarArray(nativeIntArray);
  4983. }
  4984. #else
  4985. *isIntArray = true;
  4986. #endif
  4987. }
  4988. else if (JavascriptNativeFloatArray::Is(this))
  4989. {
  4990. #if ENABLE_PROFILE_INFO
  4991. JavascriptNativeFloatArray* nativeFloatArray = JavascriptNativeFloatArray::FromVar(this);
  4992. ArrayCallSiteInfo* info = nativeFloatArray->GetArrayCallSiteInfo();
  4993. if(info && !info->IsNativeArray())
  4994. {
  4995. JavascriptNativeFloatArray::ToVarArray(nativeFloatArray);
  4996. }
  4997. else
  4998. {
  4999. *isFloatArray = true;
  5000. }
  5001. #else
  5002. *isFloatArray = true;
  5003. #endif
  5004. }
  5005. }
  5006. Var JavascriptArray::EntrySlice(RecyclableObject* function, CallInfo callInfo, ...)
  5007. {
  5008. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  5009. ARGUMENTS(args, callInfo);
  5010. ScriptContext* scriptContext = function->GetScriptContext();
  5011. Assert(!(callInfo.Flags & CallFlags_New));
  5012. Var res = scriptContext->GetLibrary()->GetUndefined();
  5013. if (args.Info.Count == 0)
  5014. {
  5015. return res;
  5016. }
  5017. BigIndex length;
  5018. JavascriptArray* pArr = nullptr;
  5019. RecyclableObject* obj = nullptr;
  5020. if (JavascriptArray::Is(args[0]) && scriptContext == JavascriptArray::FromVar(args[0])->GetScriptContext())
  5021. {
  5022. pArr = JavascriptArray::FromVar(args[0]);
  5023. obj = pArr;
  5024. }
  5025. else
  5026. {
  5027. if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj))
  5028. {
  5029. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.slice"));
  5030. }
  5031. }
  5032. // In ES6-mode, we always load the length property from the object instead of using the internal slot.
  5033. // Even for arrays, this is now observable via proxies.
  5034. // If source object is not an array, we fall back to this behavior anyway.
  5035. if (scriptContext->GetConfig()->IsES6TypedArrayExtensionsEnabled() || pArr == nullptr)
  5036. {
  5037. Var lenValue = JavascriptOperators::OP_GetLength(obj, scriptContext);
  5038. if (scriptContext->GetConfig()->IsES6ToLengthEnabled())
  5039. {
  5040. length = (uint64) JavascriptConversion::ToLength(lenValue, scriptContext);
  5041. }
  5042. else
  5043. {
  5044. length = JavascriptConversion::ToUInt32(lenValue, scriptContext);
  5045. }
  5046. }
  5047. else
  5048. {
  5049. length = pArr->length;
  5050. }
  5051. if (length.IsSmallIndex())
  5052. {
  5053. return JavascriptArray::SliceHelper(pArr, nullptr, obj, length.GetSmallIndex(), args, scriptContext);
  5054. }
  5055. Assert(pArr == nullptr || length.IsUint32Max());
  5056. return JavascriptArray::SliceHelper(pArr, nullptr, obj, length.GetBigIndex(), args, scriptContext);
  5057. }
  5058. // Array.prototype.slice as described in ES6.0 (draft 22) Section 22.1.3.22
  5059. template <typename T>
  5060. Var JavascriptArray::SliceHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, Arguments& args, ScriptContext* scriptContext)
  5061. {
  5062. JavascriptLibrary* library = scriptContext->GetLibrary();
  5063. JavascriptArray* newArr = nullptr;
  5064. RecyclableObject* newObj = nullptr;
  5065. bool isIntArray = false;
  5066. bool isFloatArray = false;
  5067. bool isTypedArrayEntryPoint = typedArrayBase != nullptr;
  5068. T startT = 0;
  5069. T newLenT = length;
  5070. T endT = length;
  5071. #if ENABLE_COPYONACCESS_ARRAY
  5072. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(pArr);
  5073. #endif
  5074. if (args.Info.Count > 1)
  5075. {
  5076. startT = GetFromIndex(args[1], length, scriptContext);
  5077. if (startT > length)
  5078. {
  5079. startT = length;
  5080. }
  5081. if (args.Info.Count > 2)
  5082. {
  5083. if (JavascriptOperators::GetTypeId(args[2]) == TypeIds_Undefined)
  5084. {
  5085. endT = length;
  5086. }
  5087. else
  5088. {
  5089. endT = GetFromIndex(args[2], length, scriptContext);
  5090. if (endT > length)
  5091. {
  5092. endT = length;
  5093. }
  5094. }
  5095. }
  5096. newLenT = endT > startT ? endT - startT : 0;
  5097. }
  5098. if (TypedArrayBase::IsDetachedTypedArray(obj))
  5099. {
  5100. JavascriptError::ThrowTypeError(scriptContext, JSERR_DetachedTypedArray, _u("Array.prototype.slice"));
  5101. }
  5102. // If we came from Array.prototype.slice and source object is not a JavascriptArray, source could be a TypedArray
  5103. if (!isTypedArrayEntryPoint && pArr == nullptr && TypedArrayBase::Is(obj))
  5104. {
  5105. typedArrayBase = TypedArrayBase::FromVar(obj);
  5106. }
  5107. // 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
  5108. // and use it to construct the return object.
  5109. if (isTypedArrayEntryPoint)
  5110. {
  5111. Var constructor = JavascriptOperators::SpeciesConstructor(typedArrayBase, TypedArrayBase::GetDefaultConstructor(args[0], scriptContext), scriptContext);
  5112. // If we have an array source object, we need to make sure to do the right thing if it's a native array.
  5113. // The helpers below which do the element copying require the source and destination arrays to have the same native type.
  5114. if (pArr && constructor == library->GetArrayConstructor())
  5115. {
  5116. if (newLenT > JavascriptArray::MaxArrayLength)
  5117. {
  5118. JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthConstructIncorrect);
  5119. }
  5120. // If the constructor function is the built-in Array constructor, we can be smart and create the right type of native array.
  5121. pArr->GetArrayTypeAndConvert(&isIntArray, &isFloatArray);
  5122. newArr = CreateNewArrayHelper(static_cast<uint32>(newLenT), isIntArray, isFloatArray, pArr, scriptContext);
  5123. newObj = newArr;
  5124. }
  5125. else if (JavascriptOperators::IsConstructor(constructor))
  5126. {
  5127. if (pArr)
  5128. {
  5129. // If the constructor function is any other function, it can return anything so we have to call it.
  5130. // Roll the source array into a non-native array if it was one.
  5131. pArr = EnsureNonNativeArray(pArr);
  5132. }
  5133. Js::Var constructorArgs[] = { constructor, JavascriptNumber::ToVar(newLenT, scriptContext) };
  5134. Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs));
  5135. newObj = RecyclableObject::FromVar(TypedArrayBase::TypedArrayCreate(constructor, &Js::Arguments(constructorCallInfo, constructorArgs), (uint32)newLenT, scriptContext));
  5136. }
  5137. else
  5138. {
  5139. // We only need to throw a TypeError when the constructor property is not an actual constructor if %TypedArray%.prototype.slice was called
  5140. JavascriptError::ThrowTypeError(scriptContext, JSERR_InvalidTypedArray_Constructor, _u("[TypedArray].prototype.slice"));
  5141. }
  5142. }
  5143. else if (pArr != nullptr)
  5144. {
  5145. newObj = ArraySpeciesCreate(pArr, newLenT, scriptContext, &isIntArray, &isFloatArray);
  5146. }
  5147. // skip the typed array and "pure" array case, we still need to handle special arrays like es5array, remote array, and proxy of array.
  5148. else
  5149. {
  5150. newObj = ArraySpeciesCreate(obj, newLenT, scriptContext);
  5151. }
  5152. // If we didn't create a new object above we will create a new array here.
  5153. // This is the pre-ES6 behavior or the case of calling Array.prototype.slice with a constructor argument that is not a constructor function.
  5154. if (newObj == nullptr)
  5155. {
  5156. if (pArr)
  5157. {
  5158. pArr->GetArrayTypeAndConvert(&isIntArray, &isFloatArray);
  5159. }
  5160. if (newLenT > JavascriptArray::MaxArrayLength)
  5161. {
  5162. JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthConstructIncorrect);
  5163. }
  5164. newArr = CreateNewArrayHelper(static_cast<uint32>(newLenT), isIntArray, isFloatArray, pArr, scriptContext);
  5165. newObj = newArr;
  5166. }
  5167. else
  5168. {
  5169. // If the new object we created is an array, remember that as it will save us time setting properties in the object below
  5170. if (JavascriptArray::Is(newObj))
  5171. {
  5172. newArr = JavascriptArray::FromVar(newObj);
  5173. }
  5174. }
  5175. uint32 start = (uint32) startT;
  5176. uint32 newLen = (uint32) newLenT;
  5177. // We at least have to have newObj as a valid object
  5178. Assert(newObj);
  5179. // Bail out early if the new object will have zero length.
  5180. if (newLen == 0)
  5181. {
  5182. return newObj;
  5183. }
  5184. if (pArr)
  5185. {
  5186. // If we constructed a new Array object, we have some nice helpers here
  5187. if (newArr)
  5188. {
  5189. if (JavascriptArray::IsDirectAccessArray(newArr))
  5190. {
  5191. if (((start + newLen) <= pArr->head->length) && newLen <= newArr->head->size) //Fast Path
  5192. {
  5193. if (isIntArray)
  5194. {
  5195. SliceHelper<int32>(pArr, newArr, start, newLen);
  5196. }
  5197. else if (isFloatArray)
  5198. {
  5199. SliceHelper<double>(pArr, newArr, start, newLen);
  5200. }
  5201. else
  5202. {
  5203. SliceHelper<Var>(pArr, newArr, start, newLen);
  5204. }
  5205. }
  5206. else
  5207. {
  5208. if (isIntArray)
  5209. {
  5210. CopyNativeIntArrayElements(JavascriptNativeIntArray::FromVar(newArr), 0, JavascriptNativeIntArray::FromVar(pArr), start, start + newLen);
  5211. }
  5212. else if (isFloatArray)
  5213. {
  5214. CopyNativeFloatArrayElements(JavascriptNativeFloatArray::FromVar(newArr), 0, JavascriptNativeFloatArray::FromVar(pArr), start, start + newLen);
  5215. }
  5216. else
  5217. {
  5218. CopyArrayElements(newArr, 0u, pArr, start, start + newLen);
  5219. }
  5220. }
  5221. }
  5222. else
  5223. {
  5224. AssertMsg(CONFIG_FLAG(ForceES5Array), "newArr can only be ES5Array when it is forced");
  5225. Var element;
  5226. for (uint32 i = 0; i < newLen; i++)
  5227. {
  5228. if (!pArr->DirectGetItemAtFull(i + start, &element))
  5229. {
  5230. continue;
  5231. }
  5232. newArr->DirectSetItemAt(i, element);
  5233. }
  5234. }
  5235. }
  5236. else
  5237. {
  5238. // The constructed object isn't an array, we'll need to use normal object manipulation
  5239. Var element;
  5240. for (uint32 i = 0; i < newLen; i++)
  5241. {
  5242. if (!pArr->DirectGetItemAtFull(i + start, &element))
  5243. {
  5244. continue;
  5245. }
  5246. JavascriptArray::SetArrayLikeObjects(newObj, i, element);
  5247. }
  5248. }
  5249. }
  5250. else if (typedArrayBase)
  5251. {
  5252. // 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)
  5253. TypedArrayBase* newTypedArray = nullptr;
  5254. if (TypedArrayBase::Is(newObj))
  5255. {
  5256. newTypedArray = TypedArrayBase::FromVar(newObj);
  5257. }
  5258. Var element;
  5259. for (uint32 i = 0; i < newLen; i++)
  5260. {
  5261. // We only need to call HasItem in the case that we are called from Array.prototype.slice
  5262. if (!isTypedArrayEntryPoint && !typedArrayBase->HasItem(i + start))
  5263. {
  5264. continue;
  5265. }
  5266. element = typedArrayBase->DirectGetItem(i + start);
  5267. // The object we got back from the constructor might not be a TypedArray. In fact, it could be any object.
  5268. if (newTypedArray)
  5269. {
  5270. newTypedArray->DirectSetItem(i, element);
  5271. }
  5272. else if (newArr)
  5273. {
  5274. newArr->DirectSetItemAt(i, element);
  5275. }
  5276. else
  5277. {
  5278. JavascriptOperators::OP_SetElementI_UInt32(newObj, i, element, scriptContext, PropertyOperation_ThrowIfNotExtensible);
  5279. }
  5280. }
  5281. }
  5282. else
  5283. {
  5284. for (uint32 i = 0; i < newLen; i++)
  5285. {
  5286. if (JavascriptOperators::HasItem(obj, i + start))
  5287. {
  5288. Var element = JavascriptOperators::GetItem(obj, i + start, scriptContext);
  5289. if (newArr != nullptr)
  5290. {
  5291. newArr->DirectSetItemAt(i, element);
  5292. }
  5293. else
  5294. {
  5295. JavascriptOperators::OP_SetElementI_UInt32(newObj, i, element, scriptContext, PropertyOperation_ThrowIfNotExtensible);
  5296. }
  5297. }
  5298. }
  5299. }
  5300. if (!isTypedArrayEntryPoint)
  5301. {
  5302. JavascriptOperators::SetProperty(newObj, newObj, Js::PropertyIds::length, JavascriptNumber::ToVar(newLen, scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible);
  5303. }
  5304. #ifdef VALIDATE_ARRAY
  5305. if (JavascriptArray::Is(newObj))
  5306. {
  5307. JavascriptArray::FromVar(newObj)->ValidateArray();
  5308. }
  5309. #endif
  5310. return newObj;
  5311. }
  5312. struct CompareVarsInfo
  5313. {
  5314. ScriptContext* scriptContext;
  5315. RecyclableObject* compFn;
  5316. };
  5317. int __cdecl compareVars(void* cvInfoV, const void* aRef, const void* bRef)
  5318. {
  5319. CompareVarsInfo* cvInfo=(CompareVarsInfo*)cvInfoV;
  5320. ScriptContext* requestContext=cvInfo->scriptContext;
  5321. RecyclableObject* compFn=cvInfo->compFn;
  5322. AssertMsg(*(Var*)aRef, "No null expected in sort");
  5323. AssertMsg(*(Var*)bRef, "No null expected in sort");
  5324. if (compFn != nullptr)
  5325. {
  5326. ScriptContext* scriptContext = compFn->GetScriptContext();
  5327. // The correct flag value is CallFlags_Value but we pass CallFlags_None in compat modes
  5328. CallFlags flags = CallFlags_Value;
  5329. Var undefined = scriptContext->GetLibrary()->GetUndefined();
  5330. Var retVal;
  5331. if (requestContext != scriptContext)
  5332. {
  5333. Var leftVar = CrossSite::MarshalVar(scriptContext, *(Var*)aRef);
  5334. Var rightVar = CrossSite::MarshalVar(scriptContext, *(Var*)bRef);
  5335. retVal = CALL_FUNCTION(compFn, CallInfo(flags, 3), undefined, leftVar, rightVar);
  5336. }
  5337. else
  5338. {
  5339. retVal = CALL_FUNCTION(compFn, CallInfo(flags, 3), undefined, *(Var*)aRef, *(Var*)bRef);
  5340. }
  5341. if (TaggedInt::Is(retVal))
  5342. {
  5343. return TaggedInt::ToInt32(retVal);
  5344. }
  5345. double dblResult;
  5346. if (JavascriptNumber::Is_NoTaggedIntCheck(retVal))
  5347. {
  5348. dblResult = JavascriptNumber::GetValue(retVal);
  5349. }
  5350. else
  5351. {
  5352. dblResult = JavascriptConversion::ToNumber_Full(retVal, scriptContext);
  5353. }
  5354. if (dblResult < 0)
  5355. {
  5356. return -1;
  5357. }
  5358. return (dblResult > 0) ? 1 : 0;
  5359. }
  5360. else
  5361. {
  5362. JavascriptString* pStr1 = JavascriptConversion::ToString(*(Var*)aRef, requestContext);
  5363. JavascriptString* pStr2 = JavascriptConversion::ToString(*(Var*)bRef, requestContext);
  5364. return JavascriptString::strcmp(pStr1, pStr2);
  5365. }
  5366. }
  5367. static void hybridSort(__inout_ecount(length) Var *elements, uint32 length, CompareVarsInfo* compareInfo)
  5368. {
  5369. // The cost of memory moves starts to be more expensive than additional comparer calls (given a simple comparer)
  5370. // for arrays of more than 512 elements.
  5371. if (length > 512)
  5372. {
  5373. qsort_s(elements, length, sizeof(Var), compareVars, compareInfo);
  5374. return;
  5375. }
  5376. for (int i = 1; i < (int)length; i++)
  5377. {
  5378. if (compareVars(compareInfo, elements + i, elements + i - 1) < 0) {
  5379. // binary search for the left-most element greater than value:
  5380. int first = 0;
  5381. int last = i - 1;
  5382. while (first <= last)
  5383. {
  5384. int middle = (first + last) / 2;
  5385. if (compareVars(compareInfo, elements + i, elements + middle) < 0)
  5386. {
  5387. last = middle - 1;
  5388. }
  5389. else
  5390. {
  5391. first = middle + 1;
  5392. }
  5393. }
  5394. // insert value right before first:
  5395. Var value = elements[i];
  5396. memmove(elements + first + 1, elements + first, (i - first) * sizeof(Var));
  5397. elements[first] = value;
  5398. }
  5399. }
  5400. }
  5401. void JavascriptArray::Sort(RecyclableObject* compFn)
  5402. {
  5403. if (length <= 1)
  5404. {
  5405. return;
  5406. }
  5407. this->EnsureHead<Var>();
  5408. ScriptContext* scriptContext = this->GetScriptContext();
  5409. Recycler* recycler = scriptContext->GetRecycler();
  5410. CompareVarsInfo cvInfo;
  5411. cvInfo.scriptContext = scriptContext;
  5412. cvInfo.compFn = compFn;
  5413. Assert(head != nullptr);
  5414. // Just dump the segment map on sort
  5415. ClearSegmentMap();
  5416. uint32 countUndefined = 0;
  5417. SparseArraySegment<Var>* startSeg = (SparseArraySegment<Var>*)head;
  5418. // Sort may have side effects on the array. Setting a dummy head so that original array is not affected
  5419. uint32 saveLength = length;
  5420. // that if compare function tries to modify the array it won't AV.
  5421. head = const_cast<SparseArraySegmentBase*>(EmptySegment);
  5422. SetFlags(DynamicObjectFlags::None);
  5423. this->InvalidateLastUsedSegment();
  5424. length = 0;
  5425. TryFinally([&]()
  5426. {
  5427. //The array is a continuous array if there is only one segment
  5428. if (startSeg->next == nullptr) // Single segment fast path
  5429. {
  5430. if (compFn != nullptr)
  5431. {
  5432. countUndefined = startSeg->RemoveUndefined(scriptContext);
  5433. #ifdef VALIDATE_ARRAY
  5434. ValidateSegment(startSeg);
  5435. #endif
  5436. hybridSort(startSeg->elements, startSeg->length, &cvInfo);
  5437. }
  5438. else
  5439. {
  5440. countUndefined = sort(startSeg->elements, &startSeg->length, scriptContext);
  5441. }
  5442. head = startSeg;
  5443. }
  5444. else
  5445. {
  5446. SparseArraySegment<Var>* allElements = SparseArraySegment<Var>::AllocateSegment(recycler, 0, 0, nullptr);
  5447. SparseArraySegment<Var>* next = startSeg;
  5448. uint32 nextIndex = 0;
  5449. // copy all the elements to single segment
  5450. while (next)
  5451. {
  5452. countUndefined += next->RemoveUndefined(scriptContext);
  5453. if (next->length != 0)
  5454. {
  5455. allElements = SparseArraySegment<Var>::CopySegment(recycler, allElements, nextIndex, next, next->left, next->length);
  5456. }
  5457. next = (SparseArraySegment<Var>*)next->next;
  5458. nextIndex = allElements->length;
  5459. #ifdef VALIDATE_ARRAY
  5460. ValidateSegment(allElements);
  5461. #endif
  5462. }
  5463. if (compFn != nullptr)
  5464. {
  5465. hybridSort(allElements->elements, allElements->length, &cvInfo);
  5466. }
  5467. else
  5468. {
  5469. sort(allElements->elements, &allElements->length, scriptContext);
  5470. }
  5471. head = allElements;
  5472. head->next = nullptr;
  5473. }
  5474. },
  5475. [&](bool hasException)
  5476. {
  5477. length = saveLength;
  5478. ClearSegmentMap(); // Dump the segmentMap again in case user compare function rebuilds it
  5479. if (hasException)
  5480. {
  5481. head = startSeg;
  5482. this->InvalidateLastUsedSegment();
  5483. }
  5484. });
  5485. #if DEBUG
  5486. {
  5487. uint32 countNull = 0;
  5488. uint32 index = head->length - 1;
  5489. while (countNull < head->length)
  5490. {
  5491. if (((SparseArraySegment<Var>*)head)->elements[index] != NULL)
  5492. {
  5493. break;
  5494. }
  5495. index--;
  5496. countNull++;
  5497. }
  5498. AssertMsg(countNull == 0, "No null expected at the end");
  5499. }
  5500. #endif
  5501. if (countUndefined != 0)
  5502. {
  5503. // fill undefined at the end
  5504. uint32 newLength = head->length + countUndefined;
  5505. if (newLength > head->size)
  5506. {
  5507. head = ((SparseArraySegment<Var>*)head)->GrowByMin(recycler, newLength - head->size);
  5508. }
  5509. Var undefined = scriptContext->GetLibrary()->GetUndefined();
  5510. for (uint32 i = head->length; i < newLength; i++)
  5511. {
  5512. ((SparseArraySegment<Var>*)head)->elements[i] = undefined;
  5513. }
  5514. head->length = newLength;
  5515. }
  5516. SetHasNoMissingValues();
  5517. this->InvalidateLastUsedSegment();
  5518. #ifdef VALIDATE_ARRAY
  5519. ValidateArray();
  5520. #endif
  5521. return;
  5522. }
  5523. uint32 JavascriptArray::sort(__inout_ecount(*len) Var *orig, uint32 *len, ScriptContext *scriptContext)
  5524. {
  5525. uint32 count = 0, countUndefined = 0;
  5526. Element *elements = RecyclerNewArrayZ(scriptContext->GetRecycler(), Element, *len);
  5527. RecyclableObject *undefined = scriptContext->GetLibrary()->GetUndefined();
  5528. //
  5529. // Create the Elements array
  5530. //
  5531. for (uint32 i = 0; i < *len; ++i)
  5532. {
  5533. if (!SparseArraySegment<Var>::IsMissingItem(&orig[i]))
  5534. {
  5535. if (!JavascriptOperators::IsUndefinedObject(orig[i], undefined))
  5536. {
  5537. elements[count].Value = orig[i];
  5538. elements[count].StringValue = JavascriptConversion::ToString(orig[i], scriptContext);
  5539. count++;
  5540. }
  5541. else
  5542. {
  5543. countUndefined++;
  5544. }
  5545. orig[i] = SparseArraySegment<Var>::GetMissingItem();
  5546. }
  5547. }
  5548. if (count == 0)
  5549. {
  5550. *len = 0; // set the length to zero
  5551. return countUndefined;
  5552. }
  5553. SortElements(elements, 0, count - 1);
  5554. for (uint32 i = 0; i < count; ++i)
  5555. {
  5556. orig[i] = elements[i].Value;
  5557. }
  5558. *len = count; // set the correct length
  5559. return countUndefined;
  5560. }
  5561. int __cdecl JavascriptArray::CompareElements(void* context, const void* elem1, const void* elem2)
  5562. {
  5563. const Element* element1 = static_cast<const Element*>(elem1);
  5564. const Element* element2 = static_cast<const Element*>(elem2);
  5565. Assert(element1 != NULL);
  5566. Assert(element2 != NULL);
  5567. return JavascriptString::strcmp(element1->StringValue, element2->StringValue);
  5568. }
  5569. void JavascriptArray::SortElements(Element* elements, uint32 left, uint32 right)
  5570. {
  5571. qsort_s(elements, right - left + 1, sizeof(Element), CompareElements, this);
  5572. }
  5573. Var JavascriptArray::EntrySort(RecyclableObject* function, CallInfo callInfo, ...)
  5574. {
  5575. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  5576. ARGUMENTS(args, callInfo);
  5577. ScriptContext* scriptContext = function->GetScriptContext();
  5578. AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Array.prototype.sort"));
  5579. Assert(!(callInfo.Flags & CallFlags_New));
  5580. AssertMsg(args.Info.Count >= 1, "Should have at least one argument");
  5581. RecyclableObject* compFn = NULL;
  5582. if (args.Info.Count > 1)
  5583. {
  5584. if (JavascriptConversion::IsCallable(args[1]))
  5585. {
  5586. compFn = RecyclableObject::FromVar(args[1]);
  5587. }
  5588. else
  5589. {
  5590. TypeId typeId = JavascriptOperators::GetTypeId(args[1]);
  5591. // Use default comparer:
  5592. // - In ES5 mode if the argument is undefined.
  5593. bool useDefaultComparer = typeId == TypeIds_Undefined;
  5594. if (!useDefaultComparer)
  5595. {
  5596. JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedInternalObject, _u("Array.prototype.sort"));
  5597. }
  5598. }
  5599. }
  5600. if (JavascriptArray::Is(args[0]))
  5601. {
  5602. #if ENABLE_COPYONACCESS_ARRAY
  5603. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(args[0]);
  5604. #endif
  5605. JavascriptArray *arr = JavascriptArray::FromVar(args[0]);
  5606. if (arr->length <= 1)
  5607. {
  5608. return args[0];
  5609. }
  5610. if(arr->IsFillFromPrototypes())
  5611. {
  5612. arr->FillFromPrototypes(0, arr->length); // We need find all missing value from [[proto]] object
  5613. }
  5614. // Maintain nativity of the array only for the following cases (To favor inplace conversions - keeps the conversion cost less):
  5615. // - int cases for X86 and
  5616. // - FloatArray for AMD64
  5617. // 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)
  5618. #if defined(_M_X64_OR_ARM64)
  5619. if(compFn && JavascriptNativeFloatArray::Is(arr))
  5620. {
  5621. arr = JavascriptNativeFloatArray::ConvertToVarArray((JavascriptNativeFloatArray*)arr);
  5622. arr->Sort(compFn);
  5623. arr = arr->ConvertToNativeArrayInPlace<JavascriptNativeFloatArray, double>(arr);
  5624. }
  5625. else
  5626. {
  5627. EnsureNonNativeArray(arr);
  5628. arr->Sort(compFn);
  5629. }
  5630. #else
  5631. if(compFn && JavascriptNativeIntArray::Is(arr))
  5632. {
  5633. //EnsureNonNativeArray(arr);
  5634. arr = JavascriptNativeIntArray::ConvertToVarArray((JavascriptNativeIntArray*)arr);
  5635. arr->Sort(compFn);
  5636. arr = arr->ConvertToNativeArrayInPlace<JavascriptNativeIntArray, int32>(arr);
  5637. }
  5638. else
  5639. {
  5640. EnsureNonNativeArray(arr);
  5641. arr->Sort(compFn);
  5642. }
  5643. #endif
  5644. }
  5645. else
  5646. {
  5647. RecyclableObject* pObj = nullptr;
  5648. if (FALSE == JavascriptConversion::ToObject(args[0], scriptContext, &pObj))
  5649. {
  5650. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.sort"));
  5651. }
  5652. uint32 len = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(pObj, scriptContext), scriptContext);
  5653. JavascriptArray* sortArray = scriptContext->GetLibrary()->CreateArray(len);
  5654. sortArray->EnsureHead<Var>();
  5655. ThrowTypeErrorOnFailureHelper h(scriptContext, _u("Array.prototype.sort"));
  5656. BEGIN_TEMP_ALLOCATOR(tempAlloc, scriptContext, _u("Runtime"))
  5657. {
  5658. JsUtil::List<uint32, ArenaAllocator>* indexList = JsUtil::List<uint32, ArenaAllocator>::New(tempAlloc);
  5659. for (uint32 i = 0; i < len; i++)
  5660. {
  5661. Var item;
  5662. if (JavascriptOperators::GetItem(pObj, i, &item, scriptContext))
  5663. {
  5664. indexList->Add(i);
  5665. sortArray->DirectSetItemAt(i, item);
  5666. }
  5667. }
  5668. if (indexList->Count() > 0)
  5669. {
  5670. if (sortArray->length > 1)
  5671. {
  5672. sortArray->FillFromPrototypes(0, sortArray->length); // We need find all missing value from [[proto]] object
  5673. }
  5674. sortArray->Sort(compFn);
  5675. uint32 removeIndex = sortArray->head->length;
  5676. for (uint32 i = 0; i < removeIndex; i++)
  5677. {
  5678. AssertMsg(!SparseArraySegment<Var>::IsMissingItem(&((SparseArraySegment<Var>*)sortArray->head)->elements[i]), "No gaps expected in sorted array");
  5679. h.ThrowTypeErrorOnFailure(JavascriptOperators::SetItem(pObj, pObj, i, ((SparseArraySegment<Var>*)sortArray->head)->elements[i], scriptContext));
  5680. }
  5681. for (int i = 0; i < indexList->Count(); i++)
  5682. {
  5683. uint32 value = indexList->Item(i);
  5684. if (value >= removeIndex)
  5685. {
  5686. h.ThrowTypeErrorOnFailure((JavascriptOperators::DeleteItem(pObj, value)));
  5687. }
  5688. }
  5689. }
  5690. }
  5691. END_TEMP_ALLOCATOR(tempAlloc, scriptContext);
  5692. }
  5693. return args[0];
  5694. }
  5695. Var JavascriptArray::EntrySplice(RecyclableObject* function, CallInfo callInfo, ...)
  5696. {
  5697. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  5698. ARGUMENTS(args, callInfo);
  5699. ScriptContext* scriptContext = function->GetScriptContext();
  5700. Recycler *recycler = scriptContext->GetRecycler();
  5701. Assert(!(callInfo.Flags & CallFlags_New));
  5702. AssertMsg(args.Info.Count >= 1, "Should have at least one argument");
  5703. bool isArr = false;
  5704. JavascriptArray* pArr = 0;
  5705. RecyclableObject* pObj = 0;
  5706. RecyclableObject* newObj = nullptr;
  5707. uint32 start = 0;
  5708. uint32 deleteLen = 0;
  5709. uint32 len = 0;
  5710. if (JavascriptArray::Is(args[0]) && scriptContext == JavascriptArray::FromVar(args[0])->GetScriptContext())
  5711. {
  5712. isArr = true;
  5713. pArr = JavascriptArray::FromVar(args[0]);
  5714. pObj = pArr;
  5715. len = pArr->length;
  5716. #if ENABLE_COPYONACCESS_ARRAY
  5717. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(args[0]);
  5718. #endif
  5719. }
  5720. else
  5721. {
  5722. if (FALSE == JavascriptConversion::ToObject(args[0], scriptContext, &pObj))
  5723. {
  5724. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.splice"));
  5725. }
  5726. if (scriptContext->GetConfig()->IsES6ToLengthEnabled())
  5727. {
  5728. int64 len64 = JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(pObj, scriptContext), scriptContext);
  5729. len = len64 > UINT_MAX ? UINT_MAX : (uint)len64;
  5730. }
  5731. else
  5732. {
  5733. len = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(pObj, scriptContext), scriptContext);
  5734. }
  5735. }
  5736. switch (args.Info.Count)
  5737. {
  5738. case 1:
  5739. start = len;
  5740. deleteLen = 0;
  5741. break;
  5742. case 2:
  5743. start = min(GetFromIndex(args[1], len, scriptContext), len);
  5744. deleteLen = len - start;
  5745. break;
  5746. default:
  5747. start = GetFromIndex(args[1], len, scriptContext);
  5748. if (start > len)
  5749. {
  5750. start = len;
  5751. }
  5752. // When start >= len, we know we won't be deleting any items and don't really need to evaluate the second argument.
  5753. // However, ECMA 262 15.4.4.12 requires that it be evaluated, anyway. If the argument is an object with a valueOf
  5754. // with a side effect, this evaluation is observable. Hence, we must evaluate.
  5755. if (TaggedInt::Is(args[2]))
  5756. {
  5757. int intDeleteLen = TaggedInt::ToInt32(args[2]);
  5758. if (intDeleteLen < 0)
  5759. {
  5760. deleteLen = 0;
  5761. }
  5762. else
  5763. {
  5764. deleteLen = intDeleteLen;
  5765. }
  5766. }
  5767. else
  5768. {
  5769. double dblDeleteLen = JavascriptConversion::ToInteger(args[2], scriptContext);
  5770. if (dblDeleteLen > len)
  5771. {
  5772. deleteLen = (uint32)-1;
  5773. }
  5774. else if (dblDeleteLen <= 0)
  5775. {
  5776. deleteLen = 0;
  5777. }
  5778. else
  5779. {
  5780. deleteLen = (uint32)dblDeleteLen;
  5781. }
  5782. }
  5783. deleteLen = min(len - start, deleteLen);
  5784. break;
  5785. }
  5786. Var* insertArgs = args.Info.Count > 3 ? &args.Values[3] : nullptr;
  5787. uint32 insertLen = args.Info.Count > 3 ? args.Info.Count - 3 : 0;
  5788. ::Math::RecordOverflowPolicy newLenOverflow;
  5789. uint32 newLen = UInt32Math::Add(len - deleteLen, insertLen, newLenOverflow); // new length of the array after splice
  5790. if (isArr)
  5791. {
  5792. // If we have missing values then convert to not native array for now
  5793. // In future, we could support this scenario.
  5794. if (deleteLen == insertLen)
  5795. {
  5796. pArr->FillFromPrototypes(start, start + deleteLen);
  5797. }
  5798. else if (len)
  5799. {
  5800. pArr->FillFromPrototypes(start, len);
  5801. }
  5802. //
  5803. // If newLen overflowed, pre-process to prevent pushing sparse array segments or elements out of
  5804. // max array length, which would result in tons of index overflow and difficult to fix.
  5805. //
  5806. if (newLenOverflow.HasOverflowed())
  5807. {
  5808. pArr = EnsureNonNativeArray(pArr);
  5809. BigIndex dstIndex = MaxArrayLength;
  5810. uint32 maxInsertLen = MaxArrayLength - start;
  5811. if (insertLen > maxInsertLen)
  5812. {
  5813. // Copy overflowing insertArgs to properties
  5814. for (uint32 i = maxInsertLen; i < insertLen; i++)
  5815. {
  5816. pArr->DirectSetItemAt(dstIndex, insertArgs[i]);
  5817. ++dstIndex;
  5818. }
  5819. insertLen = maxInsertLen; // update
  5820. // Truncate elements on the right to properties
  5821. if (start + deleteLen < len)
  5822. {
  5823. pArr->TruncateToProperties(dstIndex, start + deleteLen);
  5824. }
  5825. }
  5826. else
  5827. {
  5828. // Truncate would-overflow elements to properties
  5829. pArr->TruncateToProperties(dstIndex, MaxArrayLength - insertLen + deleteLen);
  5830. }
  5831. len = pArr->length; // update
  5832. newLen = len - deleteLen + insertLen;
  5833. Assert(newLen == MaxArrayLength);
  5834. }
  5835. if (insertArgs)
  5836. {
  5837. pArr = EnsureNonNativeArray(pArr);
  5838. }
  5839. bool isIntArray = false;
  5840. bool isFloatArray = false;
  5841. JavascriptArray *newArr = nullptr;
  5842. // Just dump the segment map on splice (before any possible allocation and throw)
  5843. pArr->ClearSegmentMap();
  5844. // If the source object is an Array exotic object (Array.isArray) we should try to load the constructor property
  5845. // and use it to construct the return object.
  5846. newObj = ArraySpeciesCreate(pArr, deleteLen, scriptContext);
  5847. if (newObj != nullptr)
  5848. {
  5849. pArr = EnsureNonNativeArray(pArr);
  5850. // If the new object we created is an array, remember that as it will save us time setting properties in the object below
  5851. if (JavascriptArray::Is(newObj))
  5852. {
  5853. newArr = JavascriptArray::FromVar(newObj);
  5854. }
  5855. }
  5856. else
  5857. // This is the ES5 case, pArr['constructor'] doesn't exist, or pArr['constructor'] is the builtin Array constructor
  5858. {
  5859. pArr->GetArrayTypeAndConvert(&isIntArray, &isFloatArray);
  5860. newArr = CreateNewArrayHelper(deleteLen, isIntArray, isFloatArray, pArr, scriptContext);
  5861. }
  5862. // If return object is a JavascriptArray, we can use all the array splice helpers
  5863. if (newArr)
  5864. {
  5865. // Array has a single segment (need not start at 0) and splice start lies in the range
  5866. // of that segment we optimize splice - Fast path.
  5867. if (pArr->IsSingleSegmentArray() && pArr->head->HasIndex(start))
  5868. {
  5869. if (isIntArray)
  5870. {
  5871. ArraySegmentSpliceHelper<int32>(newArr, (SparseArraySegment<int32>*)pArr->head, (SparseArraySegment<int32>**)&pArr->head, start, deleteLen, insertArgs, insertLen, recycler);
  5872. }
  5873. else if (isFloatArray)
  5874. {
  5875. ArraySegmentSpliceHelper<double>(newArr, (SparseArraySegment<double>*)pArr->head, (SparseArraySegment<double>**)&pArr->head, start, deleteLen, insertArgs, insertLen, recycler);
  5876. }
  5877. else
  5878. {
  5879. ArraySegmentSpliceHelper<Var>(newArr, (SparseArraySegment<Var>*)pArr->head, (SparseArraySegment<Var>**)&pArr->head, start, deleteLen, insertArgs, insertLen, recycler);
  5880. }
  5881. // Since the start index is within the bounds of the original array's head segment, it will not acquire any new
  5882. // missing values. If the original array had missing values in the head segment, some of them may have been
  5883. // copied into the array that will be returned; otherwise, the array that is returned will also not have any
  5884. // missing values.
  5885. newArr->SetHasNoMissingValues(pArr->HasNoMissingValues());
  5886. }
  5887. else
  5888. {
  5889. if (isIntArray)
  5890. {
  5891. ArraySpliceHelper<int32>(newArr, pArr, start, deleteLen, insertArgs, insertLen, scriptContext);
  5892. }
  5893. else if (isFloatArray)
  5894. {
  5895. ArraySpliceHelper<double>(newArr, pArr, start, deleteLen, insertArgs, insertLen, scriptContext);
  5896. }
  5897. else
  5898. {
  5899. ArraySpliceHelper<Var>(newArr, pArr, start, deleteLen, insertArgs, insertLen, scriptContext);
  5900. }
  5901. // This function currently does not track missing values in the head segment if there are multiple segments
  5902. pArr->SetHasNoMissingValues(false);
  5903. newArr->SetHasNoMissingValues(false);
  5904. }
  5905. if (isIntArray)
  5906. {
  5907. pArr->EnsureHeadStartsFromZero<int32>(recycler);
  5908. newArr->EnsureHeadStartsFromZero<int32>(recycler);
  5909. }
  5910. else if (isFloatArray)
  5911. {
  5912. pArr->EnsureHeadStartsFromZero<double>(recycler);
  5913. newArr->EnsureHeadStartsFromZero<double>(recycler);
  5914. }
  5915. else
  5916. {
  5917. pArr->EnsureHeadStartsFromZero<Var>(recycler);
  5918. newArr->EnsureHeadStartsFromZero<Var>(recycler);
  5919. }
  5920. pArr->InvalidateLastUsedSegment();
  5921. // it is possible for valueOf accessors for the start or deleteLen
  5922. // arguments to modify the size of the array. Since the resulting size of the array
  5923. // is based on the cached value of length, this might lead to us having to trim
  5924. // excess array segments at the end of the splice operation, which SetLength() will do.
  5925. // However, this is also slower than performing the simple length assignment, so we only
  5926. // do it if we can detect the array length changing.
  5927. if(pArr->length != len)
  5928. {
  5929. pArr->SetLength(newLen);
  5930. }
  5931. else
  5932. {
  5933. pArr->length = newLen;
  5934. }
  5935. newArr->InvalidateLastUsedSegment();
  5936. #ifdef VALIDATE_ARRAY
  5937. newArr->ValidateArray();
  5938. pArr->ValidateArray();
  5939. #endif
  5940. if (newLenOverflow.HasOverflowed())
  5941. {
  5942. // ES5 15.4.4.12 16: If new len overflowed, SetLength throws
  5943. JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthAssignIncorrect);
  5944. }
  5945. return newArr;
  5946. }
  5947. }
  5948. if (newLenOverflow.HasOverflowed())
  5949. {
  5950. return ObjectSpliceHelper<BigIndex>(pObj, len, start, deleteLen, insertArgs, insertLen, scriptContext, newObj);
  5951. }
  5952. else // Use uint32 version if no overflow
  5953. {
  5954. return ObjectSpliceHelper<uint32>(pObj, len, start, deleteLen, insertArgs, insertLen, scriptContext, newObj);
  5955. }
  5956. }
  5957. inline BOOL JavascriptArray::IsSingleSegmentArray() const
  5958. {
  5959. return nullptr == head->next;
  5960. }
  5961. template<typename T>
  5962. void JavascriptArray::ArraySegmentSpliceHelper(JavascriptArray *pnewArr, SparseArraySegment<T> *seg, SparseArraySegment<T> **prev,
  5963. uint32 start, uint32 deleteLen, Var* insertArgs, uint32 insertLen, Recycler *recycler)
  5964. {
  5965. // book keeping variables
  5966. uint32 relativeStart = start - seg->left; // This will be different from start when head->left is non zero -
  5967. //(Missing elements at the beginning)
  5968. uint32 headDeleteLen = min(start + deleteLen , seg->left + seg->length) - start; // actual number of elements to delete in
  5969. // head if deleteLen overflows the length of head
  5970. uint32 newHeadLen = seg->length - headDeleteLen + insertLen; // new length of the head after splice
  5971. // Save the deleted elements
  5972. if (headDeleteLen != 0)
  5973. {
  5974. pnewArr->InvalidateLastUsedSegment();
  5975. pnewArr->head = SparseArraySegment<T>::CopySegment(recycler, (SparseArraySegment<T>*)pnewArr->head, 0, seg, start, headDeleteLen);
  5976. }
  5977. if (newHeadLen != 0)
  5978. {
  5979. if (seg->size < newHeadLen)
  5980. {
  5981. if (seg->next)
  5982. {
  5983. // If we have "next", require that we haven't adjusted next segments left yet.
  5984. seg = seg->GrowByMinMax(recycler, newHeadLen - seg->size, seg->next->left - deleteLen + insertLen - seg->left - seg->size);
  5985. }
  5986. else
  5987. {
  5988. seg = seg->GrowByMin(recycler, newHeadLen - seg->size);
  5989. }
  5990. #ifdef VALIDATE_ARRAY
  5991. ValidateSegment(seg);
  5992. #endif
  5993. }
  5994. // Move the elements if necessary
  5995. if (headDeleteLen != insertLen)
  5996. {
  5997. uint32 noElementsToMove = seg->length - (relativeStart + headDeleteLen);
  5998. memmove(seg->elements + relativeStart + insertLen,
  5999. seg->elements + relativeStart + headDeleteLen,
  6000. sizeof(T) * noElementsToMove);
  6001. if (newHeadLen < seg->length) // truncate if necessary
  6002. {
  6003. seg->Truncate(seg->left + newHeadLen); // set end elements to null so that when we introduce null elements we are safe
  6004. }
  6005. seg->length = newHeadLen;
  6006. }
  6007. // Copy the new elements
  6008. if (insertLen > 0)
  6009. {
  6010. Assert(!VirtualTableInfo<JavascriptNativeIntArray>::HasVirtualTable(pnewArr) &&
  6011. !VirtualTableInfo<JavascriptNativeFloatArray>::HasVirtualTable(pnewArr));
  6012. // inserted elements starts at argument 3 of splice(start, deleteNumber, insertelem1, insertelem2, insertelem3, ...);
  6013. js_memcpy_s(seg->elements + relativeStart, sizeof(Var) * insertLen, insertArgs, sizeof(Var) * insertLen);
  6014. }
  6015. *prev = seg;
  6016. }
  6017. else
  6018. {
  6019. *prev = (SparseArraySegment<T>*)seg->next;
  6020. }
  6021. }
  6022. template<typename T>
  6023. void JavascriptArray::ArraySpliceHelper(JavascriptArray* pnewArr, JavascriptArray* pArr, uint32 start, uint32 deleteLen, Var* insertArgs, uint32 insertLen, ScriptContext *scriptContext)
  6024. {
  6025. // Skip pnewArr->EnsureHead(): we don't use existing segment at all.
  6026. Recycler *recycler = scriptContext->GetRecycler();
  6027. SparseArraySegmentBase** prevSeg = &pArr->head; // holds the next pointer of previous
  6028. SparseArraySegmentBase** prevPrevSeg = &pArr->head; // this holds the previous pointer to prevSeg dirty trick.
  6029. SparseArraySegmentBase* savePrev = nullptr;
  6030. Assert(pArr->head); // We should never have a null head.
  6031. pArr->EnsureHead<T>();
  6032. SparseArraySegment<T>* startSeg = (SparseArraySegment<T>*)pArr->head;
  6033. const uint32 limit = start + deleteLen;
  6034. uint32 rightLimit;
  6035. if (UInt32Math::Add(startSeg->left, startSeg->size, &rightLimit))
  6036. {
  6037. rightLimit = JavascriptArray::MaxArrayLength;
  6038. }
  6039. // Find out the segment to start delete
  6040. while (startSeg && (rightLimit <= start))
  6041. {
  6042. savePrev = startSeg;
  6043. prevPrevSeg = prevSeg;
  6044. prevSeg = &startSeg->next;
  6045. startSeg = (SparseArraySegment<T>*)startSeg->next;
  6046. if (startSeg)
  6047. {
  6048. if (UInt32Math::Add(startSeg->left, startSeg->size, &rightLimit))
  6049. {
  6050. rightLimit = JavascriptArray::MaxArrayLength;
  6051. }
  6052. }
  6053. }
  6054. // handle inlined segment
  6055. SparseArraySegmentBase* inlineHeadSegment = nullptr;
  6056. bool hasInlineSegment = false;
  6057. // The following if else set is used to determine whether a shallow or hard copy is needed
  6058. if (JavascriptNativeArray::Is(pArr))
  6059. {
  6060. if (JavascriptNativeFloatArray::Is(pArr))
  6061. {
  6062. inlineHeadSegment = DetermineInlineHeadSegmentPointer<JavascriptNativeFloatArray, 0, true>((JavascriptNativeFloatArray*)pArr);
  6063. }
  6064. else if (JavascriptNativeIntArray::Is(pArr))
  6065. {
  6066. inlineHeadSegment = DetermineInlineHeadSegmentPointer<JavascriptNativeIntArray, 0, true>((JavascriptNativeIntArray*)pArr);
  6067. }
  6068. Assert(inlineHeadSegment);
  6069. hasInlineSegment = (startSeg == (SparseArraySegment<T>*)inlineHeadSegment);
  6070. }
  6071. else
  6072. {
  6073. // This will result in false positives. It is used because DetermineInlineHeadSegmentPointer
  6074. // does not handle Arrays that change type e.g. from JavascriptNativeIntArray to JavascriptArray
  6075. // This conversion in particular is problematic because JavascriptNativeIntArray is larger than JavascriptArray
  6076. // so the returned head segment ptr never equals pArr->head. So we will default to using this and deal with
  6077. // false positives. It is better than always doing a hard copy.
  6078. hasInlineSegment = HasInlineHeadSegment(pArr->head->length);
  6079. }
  6080. if (startSeg)
  6081. {
  6082. // Delete Phase
  6083. if (startSeg->left <= start && (startSeg->left + startSeg->length) >= limit)
  6084. {
  6085. // All splice happens in one segment.
  6086. SparseArraySegmentBase *nextSeg = startSeg->next;
  6087. // Splice the segment first, which might OOM throw but the array would be intact.
  6088. JavascriptArray::ArraySegmentSpliceHelper(pnewArr, (SparseArraySegment<T>*)startSeg, (SparseArraySegment<T>**)prevSeg, start, deleteLen, insertArgs, insertLen, recycler);
  6089. while (nextSeg)
  6090. {
  6091. // adjust next segments left
  6092. nextSeg->left = nextSeg->left - deleteLen + insertLen;
  6093. if (nextSeg->next == nullptr)
  6094. {
  6095. nextSeg->EnsureSizeInBound();
  6096. }
  6097. nextSeg = nextSeg->next;
  6098. }
  6099. if (*prevSeg)
  6100. {
  6101. (*prevSeg)->EnsureSizeInBound();
  6102. }
  6103. return;
  6104. }
  6105. else
  6106. {
  6107. SparseArraySegment<T>* newHeadSeg = nullptr; // pnewArr->head is null
  6108. SparseArraySegmentBase** prevNewHeadSeg = &(pnewArr->head);
  6109. // delete till deleteLen and reuse segments for new array if it is possible.
  6110. // 3 steps -
  6111. //1. delete 1st segment (which may be partial delete)
  6112. // 2. delete next n complete segments
  6113. // 3. delete last segment (which again may be partial delete)
  6114. // Step (1) -- WOOB 1116297: When left >= start, step (1) is skipped, resulting in pNewArr->head->left != 0. We need to touch up pNewArr.
  6115. if (startSeg->left < start)
  6116. {
  6117. if (start < startSeg->left + startSeg->length)
  6118. {
  6119. uint32 headDeleteLen = startSeg->left + startSeg->length - start;
  6120. if (startSeg->next)
  6121. {
  6122. // We know the new segment will have a next segment, so allocate it as non-leaf.
  6123. newHeadSeg = SparseArraySegment<T>::template AllocateSegmentImpl<false>(recycler, 0, headDeleteLen, headDeleteLen, nullptr);
  6124. }
  6125. else
  6126. {
  6127. newHeadSeg = SparseArraySegment<T>::AllocateSegment(recycler, 0, headDeleteLen, headDeleteLen, nullptr);
  6128. }
  6129. newHeadSeg = SparseArraySegment<T>::CopySegment(recycler, newHeadSeg, 0, startSeg, start, headDeleteLen);
  6130. newHeadSeg->next = nullptr;
  6131. *prevNewHeadSeg = newHeadSeg;
  6132. prevNewHeadSeg = &newHeadSeg->next;
  6133. startSeg->Truncate(start);
  6134. }
  6135. savePrev = startSeg;
  6136. prevPrevSeg = prevSeg;
  6137. prevSeg = &startSeg->next;
  6138. startSeg = (SparseArraySegment<T>*)startSeg->next;
  6139. }
  6140. // Step (2) first we should do a hard copy if we have an inline head Segment
  6141. else if (hasInlineSegment && nullptr != startSeg)
  6142. {
  6143. // start should be in between left and left + length
  6144. if (startSeg->left <= start && start < startSeg->left + startSeg->length)
  6145. {
  6146. uint32 headDeleteLen = startSeg->left + startSeg->length - start;
  6147. if (startSeg->next)
  6148. {
  6149. // We know the new segment will have a next segment, so allocate it as non-leaf.
  6150. newHeadSeg = SparseArraySegment<T>::template AllocateSegmentImpl<false>(recycler, 0, headDeleteLen, headDeleteLen, nullptr);
  6151. }
  6152. else
  6153. {
  6154. newHeadSeg = SparseArraySegment<T>::AllocateSegment(recycler, 0, headDeleteLen, headDeleteLen, nullptr);
  6155. }
  6156. newHeadSeg = SparseArraySegment<T>::CopySegment(recycler, newHeadSeg, 0, startSeg, start, headDeleteLen);
  6157. *prevNewHeadSeg = newHeadSeg;
  6158. prevNewHeadSeg = &newHeadSeg->next;
  6159. // Remove the entire segment from the original array
  6160. *prevSeg = startSeg->next;
  6161. startSeg = (SparseArraySegment<T>*)startSeg->next;
  6162. }
  6163. // if we have an inline head segment with 0 elements, remove it
  6164. else if (startSeg->left == 0 && startSeg->length == 0)
  6165. {
  6166. Assert(startSeg->size != 0);
  6167. *prevSeg = startSeg->next;
  6168. startSeg = (SparseArraySegment<T>*)startSeg->next;
  6169. }
  6170. }
  6171. // Step (2) proper
  6172. SparseArraySegmentBase *temp = nullptr;
  6173. while (startSeg && (startSeg->left + startSeg->length) <= limit)
  6174. {
  6175. temp = startSeg->next;
  6176. // move that entire segment to new array
  6177. startSeg->left = startSeg->left - start;
  6178. startSeg->next = nullptr;
  6179. *prevNewHeadSeg = startSeg;
  6180. prevNewHeadSeg = &startSeg->next;
  6181. // Remove the entire segment from the original array
  6182. *prevSeg = temp;
  6183. startSeg = (SparseArraySegment<T>*)temp;
  6184. }
  6185. // Step(2) above could delete the original head segment entirely, causing current head not
  6186. // starting from 0. Then if any of the following throw, we have a corrupted array. Need
  6187. // protection here.
  6188. bool dummyHeadNodeInserted = false;
  6189. if (!savePrev && (!startSeg || startSeg->left != 0))
  6190. {
  6191. Assert(pArr->head == startSeg);
  6192. pArr->EnsureHeadStartsFromZero<T>(recycler);
  6193. Assert(pArr->head && pArr->head->next == startSeg);
  6194. savePrev = pArr->head;
  6195. prevPrevSeg = prevSeg;
  6196. prevSeg = &pArr->head->next;
  6197. dummyHeadNodeInserted = true;
  6198. }
  6199. // Step (3)
  6200. if (startSeg && (startSeg->left < limit))
  6201. {
  6202. // copy the first part of the last segment to be deleted to new array
  6203. uint32 headDeleteLen = start + deleteLen - startSeg->left ;
  6204. newHeadSeg = SparseArraySegment<T>::AllocateSegment(recycler, startSeg->left - start, headDeleteLen, (SparseArraySegmentBase *)nullptr);
  6205. newHeadSeg = SparseArraySegment<T>::CopySegment(recycler, newHeadSeg, startSeg->left - start, startSeg, startSeg->left, headDeleteLen);
  6206. newHeadSeg->next = nullptr;
  6207. *prevNewHeadSeg = newHeadSeg;
  6208. prevNewHeadSeg = &newHeadSeg->next;
  6209. // move the last segment
  6210. memmove(startSeg->elements, startSeg->elements + headDeleteLen, sizeof(T) * (startSeg->length - headDeleteLen));
  6211. startSeg->left = startSeg->left + headDeleteLen; // We are moving the left ahead to point to the right index
  6212. startSeg->length = startSeg->length - headDeleteLen;
  6213. startSeg->Truncate(startSeg->left + startSeg->length);
  6214. startSeg->EnsureSizeInBound(); // Just truncated, size might exceed next.left
  6215. }
  6216. if (startSeg && ((startSeg->left - deleteLen + insertLen) == 0) && dummyHeadNodeInserted)
  6217. {
  6218. Assert(start + insertLen == 0);
  6219. // Remove the dummy head node to preserve array consistency.
  6220. pArr->head = startSeg;
  6221. savePrev = nullptr;
  6222. prevSeg = &pArr->head;
  6223. }
  6224. while (startSeg)
  6225. {
  6226. startSeg->left = startSeg->left - deleteLen + insertLen ;
  6227. if (startSeg->next == nullptr)
  6228. {
  6229. startSeg->EnsureSizeInBound();
  6230. }
  6231. startSeg = (SparseArraySegment<T>*)startSeg->next;
  6232. }
  6233. }
  6234. }
  6235. // The size of pnewArr head allocated in above step 1 might exceed next.left concatenated in step 2/3.
  6236. pnewArr->head->EnsureSizeInBound();
  6237. if (savePrev)
  6238. {
  6239. savePrev->EnsureSizeInBound();
  6240. }
  6241. // insert elements
  6242. if (insertLen > 0)
  6243. {
  6244. Assert(!JavascriptNativeIntArray::Is(pArr) && !JavascriptNativeFloatArray::Is(pArr));
  6245. // InsertPhase
  6246. SparseArraySegment<T> *segInsert = nullptr;
  6247. // see if we are just about the right of the previous segment
  6248. Assert(!savePrev || savePrev->left <= start);
  6249. if (savePrev && (start - savePrev->left < savePrev->size))
  6250. {
  6251. segInsert = (SparseArraySegment<T>*)savePrev;
  6252. uint32 spaceLeft = segInsert->size - (start - segInsert->left);
  6253. if(spaceLeft < insertLen)
  6254. {
  6255. if (!segInsert->next)
  6256. {
  6257. segInsert = segInsert->GrowByMin(recycler, insertLen - spaceLeft);
  6258. }
  6259. else
  6260. {
  6261. segInsert = segInsert->GrowByMinMax(recycler, insertLen - spaceLeft, segInsert->next->left - segInsert->left - segInsert->size);
  6262. }
  6263. }
  6264. *prevPrevSeg = segInsert;
  6265. segInsert->length = start + insertLen - segInsert->left;
  6266. }
  6267. else
  6268. {
  6269. segInsert = SparseArraySegment<T>::AllocateSegment(recycler, start, insertLen, *prevSeg);
  6270. segInsert->next = *prevSeg;
  6271. *prevSeg = segInsert;
  6272. savePrev = segInsert;
  6273. }
  6274. uint32 relativeStart = start - segInsert->left;
  6275. // inserted elements starts at argument 3 of splice(start, deleteNumber, insertelem1, insertelem2, insertelem3, ...);
  6276. js_memcpy_s(segInsert->elements + relativeStart, sizeof(T) * insertLen, insertArgs, sizeof(T) * insertLen);
  6277. }
  6278. }
  6279. template<typename indexT>
  6280. RecyclableObject* JavascriptArray::ObjectSpliceHelper(RecyclableObject* pObj, uint32 len, uint32 start,
  6281. uint32 deleteLen, Var* insertArgs, uint32 insertLen, ScriptContext *scriptContext, RecyclableObject* pNewObj)
  6282. {
  6283. JavascriptArray *pnewArr = nullptr;
  6284. if (pNewObj == nullptr)
  6285. {
  6286. pNewObj = ArraySpeciesCreate(pObj, deleteLen, scriptContext);
  6287. if (pNewObj == nullptr || !JavascriptArray::Is(pNewObj))
  6288. {
  6289. pnewArr = scriptContext->GetLibrary()->CreateArray(deleteLen);
  6290. pnewArr->EnsureHead<Var>();
  6291. pNewObj = pnewArr;
  6292. }
  6293. }
  6294. if (JavascriptArray::Is(pNewObj))
  6295. {
  6296. pnewArr = JavascriptArray::FromVar(pNewObj);
  6297. }
  6298. // copy elements to delete to new array
  6299. if (deleteLen > 0)
  6300. {
  6301. for (uint32 i = 0; i < deleteLen; i++)
  6302. {
  6303. if (JavascriptOperators::HasItem(pObj, start+i))
  6304. {
  6305. Var element = JavascriptOperators::GetItem(pObj, start + i, scriptContext);
  6306. if (pnewArr)
  6307. {
  6308. pnewArr->DirectSetItemAt(i, element);
  6309. }
  6310. else
  6311. {
  6312. JavascriptArray::SetArrayLikeObjects(pNewObj, i, element);
  6313. }
  6314. }
  6315. }
  6316. }
  6317. ThrowTypeErrorOnFailureHelper h(scriptContext, _u("Array.prototype.splice"));
  6318. // If the return object is not an array, we'll need to set the 'length' property
  6319. if (pnewArr == nullptr)
  6320. {
  6321. h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(pNewObj, pNewObj, PropertyIds::length, JavascriptNumber::ToVar(deleteLen, scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible));
  6322. }
  6323. // Now we need reserve room if it is necessary
  6324. if (insertLen > deleteLen) // Might overflow max array length
  6325. {
  6326. // Unshift [start + deleteLen, len) to start + insertLen
  6327. Unshift<indexT>(pObj, start + insertLen, start + deleteLen, len, scriptContext);
  6328. }
  6329. else if (insertLen < deleteLen) // Won't overflow max array length
  6330. {
  6331. uint32 j = 0;
  6332. for (uint32 i = start + deleteLen; i < len; i++)
  6333. {
  6334. if (JavascriptOperators::HasItem(pObj, i))
  6335. {
  6336. Var element = JavascriptOperators::GetItem(pObj, i, scriptContext);
  6337. h.ThrowTypeErrorOnFailure(JavascriptOperators::SetItem(pObj, pObj, start + insertLen + j, element, scriptContext, PropertyOperation_ThrowIfNotExtensible));
  6338. }
  6339. else
  6340. {
  6341. h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(pObj, start + insertLen + j, PropertyOperation_ThrowIfNotExtensible));
  6342. }
  6343. j++;
  6344. }
  6345. // Clean up the rest
  6346. for (uint32 i = len; i > len - deleteLen + insertLen; i--)
  6347. {
  6348. h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(pObj, i - 1, PropertyOperation_ThrowIfNotExtensible));
  6349. }
  6350. }
  6351. if (insertLen > 0)
  6352. {
  6353. indexT dstIndex = start; // insert index might overflow max array length
  6354. for (uint i = 0; i < insertLen; i++)
  6355. {
  6356. h.ThrowTypeErrorOnFailure(IndexTrace<indexT>::SetItem(pObj, dstIndex, insertArgs[i], PropertyOperation_ThrowIfNotExtensible));
  6357. ++dstIndex;
  6358. }
  6359. }
  6360. // Set up new length
  6361. indexT newLen = indexT(len - deleteLen) + insertLen;
  6362. h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(pObj, pObj, PropertyIds::length, IndexTrace<indexT>::ToNumber(newLen, scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible));
  6363. #ifdef VALIDATE_ARRAY
  6364. if (pnewArr)
  6365. {
  6366. pnewArr->ValidateArray();
  6367. }
  6368. #endif
  6369. return pNewObj;
  6370. }
  6371. Var JavascriptArray::EntryToLocaleString(RecyclableObject* function, CallInfo callInfo, ...)
  6372. {
  6373. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  6374. ARGUMENTS(args, callInfo);
  6375. ScriptContext* scriptContext = function->GetScriptContext();
  6376. Assert(!(callInfo.Flags & CallFlags_New));
  6377. if (args.Info.Count == 0)
  6378. {
  6379. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedObject, _u("Array.prototype.toLocaleString"));
  6380. }
  6381. if (JavascriptArray::IsDirectAccessArray(args[0]))
  6382. {
  6383. JavascriptArray* arr = JavascriptArray::FromVar(args[0]);
  6384. return ToLocaleString(arr, scriptContext);
  6385. }
  6386. else
  6387. {
  6388. RecyclableObject* obj = nullptr;
  6389. if (FALSE == JavascriptConversion::ToObject(args[0], scriptContext, &obj))
  6390. {
  6391. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.toLocaleString"));
  6392. }
  6393. return ToLocaleString(obj, scriptContext);
  6394. }
  6395. }
  6396. //
  6397. // Unshift object elements [start, end) to toIndex, asserting toIndex > start.
  6398. //
  6399. template<typename T, typename P>
  6400. void JavascriptArray::Unshift(RecyclableObject* obj, const T& toIndex, uint32 start, P end, ScriptContext* scriptContext)
  6401. {
  6402. typedef IndexTrace<T> index_trace;
  6403. ThrowTypeErrorOnFailureHelper h(scriptContext, _u("Array.prototype.unshift"));
  6404. if (start < end)
  6405. {
  6406. T newEnd = (end - start - 1);// newEnd - 1
  6407. T dst = toIndex + newEnd;
  6408. uint32 i = 0;
  6409. if (end > UINT32_MAX)
  6410. {
  6411. uint64 i64 = end;
  6412. for (; i64 > UINT32_MAX; i64--)
  6413. {
  6414. if (JavascriptOperators::HasItem(obj, i64 - 1))
  6415. {
  6416. Var element = JavascriptOperators::GetItem(obj, i64 - 1, scriptContext);
  6417. h.ThrowTypeErrorOnFailure(index_trace::SetItem(obj, dst, element, PropertyOperation_ThrowIfNotExtensible));
  6418. }
  6419. else
  6420. {
  6421. h.ThrowTypeErrorOnFailure(index_trace::DeleteItem(obj, dst, PropertyOperation_ThrowIfNotExtensible));
  6422. }
  6423. --dst;
  6424. }
  6425. i = UINT32_MAX;
  6426. }
  6427. else
  6428. {
  6429. i = (uint32) end;
  6430. }
  6431. for (; i > start; i--)
  6432. {
  6433. if (JavascriptOperators::HasItem(obj, i-1))
  6434. {
  6435. Var element = JavascriptOperators::GetItem(obj, i - 1, scriptContext);
  6436. h.ThrowTypeErrorOnFailure(index_trace::SetItem(obj, dst, element, PropertyOperation_ThrowIfNotExtensible));
  6437. }
  6438. else
  6439. {
  6440. h.ThrowTypeErrorOnFailure(index_trace::DeleteItem(obj, dst, PropertyOperation_ThrowIfNotExtensible));
  6441. }
  6442. --dst;
  6443. }
  6444. }
  6445. }
  6446. template<typename T>
  6447. void JavascriptArray::GrowArrayHeadHelperForUnshift(JavascriptArray* pArr, uint32 unshiftElements, ScriptContext * scriptContext)
  6448. {
  6449. SparseArraySegmentBase* nextToHeadSeg = pArr->head->next;
  6450. Recycler* recycler = scriptContext->GetRecycler();
  6451. if (nextToHeadSeg == nullptr)
  6452. {
  6453. pArr->EnsureHead<T>();
  6454. pArr->head = ((SparseArraySegment<T>*)pArr->head)->GrowByMin(recycler, unshiftElements);
  6455. }
  6456. else
  6457. {
  6458. pArr->head = ((SparseArraySegment<T>*)pArr->head)->GrowByMinMax(recycler, unshiftElements, ((nextToHeadSeg->left + unshiftElements) - pArr->head->left - pArr->head->size));
  6459. }
  6460. }
  6461. template<typename T>
  6462. void JavascriptArray::UnshiftHelper(JavascriptArray* pArr, uint32 unshiftElements, Js::Var * elements)
  6463. {
  6464. SparseArraySegment<T>* head = (SparseArraySegment<T>*)pArr->head;
  6465. // Make enough room in the head segment to insert new elements at the front
  6466. memmove(head->elements + unshiftElements, head->elements, sizeof(T) * pArr->head->length);
  6467. uint32 oldHeadLength = head->length;
  6468. head->length += unshiftElements;
  6469. /* Set head segment as the last used segment */
  6470. pArr->InvalidateLastUsedSegment();
  6471. bool hasNoMissingValues = pArr->HasNoMissingValues();
  6472. /* Set HasNoMissingValues to false -> Since we shifted elements right, we might have missing values after the memmove */
  6473. if(unshiftElements > oldHeadLength)
  6474. {
  6475. pArr->SetHasNoMissingValues(false);
  6476. }
  6477. #if ENABLE_PROFILE_INFO
  6478. pArr->FillFromArgs(unshiftElements, 0, elements, nullptr, true/*dontCreateNewArray*/);
  6479. #else
  6480. pArr->FillFromArgs(unshiftElements, 0, elements, true/*dontCreateNewArray*/);
  6481. #endif
  6482. // Setting back to the old value
  6483. pArr->SetHasNoMissingValues(hasNoMissingValues);
  6484. }
  6485. Var JavascriptArray::EntryUnshift(RecyclableObject* function, CallInfo callInfo, ...)
  6486. {
  6487. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  6488. ARGUMENTS(args, callInfo);
  6489. ScriptContext* scriptContext = function->GetScriptContext();
  6490. Assert(!(callInfo.Flags & CallFlags_New));
  6491. Var res = scriptContext->GetLibrary()->GetUndefined();
  6492. if (args.Info.Count == 0)
  6493. {
  6494. return res;
  6495. }
  6496. if (JavascriptArray::Is(args[0]))
  6497. {
  6498. #if ENABLE_COPYONACCESS_ARRAY
  6499. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(args[0]);
  6500. #endif
  6501. JavascriptArray * pArr = JavascriptArray::FromVar(args[0]);
  6502. uint32 unshiftElements = args.Info.Count - 1;
  6503. if (unshiftElements > 0)
  6504. {
  6505. if (pArr->IsFillFromPrototypes())
  6506. {
  6507. pArr->FillFromPrototypes(0, pArr->length); // We need find all missing value from [[proto]] object
  6508. }
  6509. // Pre-process: truncate overflowing elements to properties
  6510. bool newLenOverflowed = false;
  6511. uint32 maxLen = MaxArrayLength - unshiftElements;
  6512. if (pArr->length > maxLen)
  6513. {
  6514. newLenOverflowed = true;
  6515. // Ensure the array is non-native when overflow happens
  6516. EnsureNonNativeArray(pArr);
  6517. pArr->TruncateToProperties(MaxArrayLength, maxLen);
  6518. Assert(pArr->length + unshiftElements == MaxArrayLength);
  6519. }
  6520. pArr->ClearSegmentMap(); // Dump segmentMap on unshift (before any possible allocation and throw)
  6521. Assert(pArr->length <= MaxArrayLength - unshiftElements);
  6522. SparseArraySegmentBase* renumberSeg = pArr->head->next;
  6523. bool isIntArray = false;
  6524. bool isFloatArray = false;
  6525. if (JavascriptNativeIntArray::Is(pArr))
  6526. {
  6527. isIntArray = true;
  6528. }
  6529. else if (JavascriptNativeFloatArray::Is(pArr))
  6530. {
  6531. isFloatArray = true;
  6532. }
  6533. // If we need to grow head segment and there is already a next segment, then allocate the new head segment upfront
  6534. // If there is OOM in array allocation, then array consistency is maintained.
  6535. if (pArr->head->size < pArr->head->length + unshiftElements)
  6536. {
  6537. if (isIntArray)
  6538. {
  6539. GrowArrayHeadHelperForUnshift<int32>(pArr, unshiftElements, scriptContext);
  6540. }
  6541. else if (isFloatArray)
  6542. {
  6543. GrowArrayHeadHelperForUnshift<double>(pArr, unshiftElements, scriptContext);
  6544. }
  6545. else
  6546. {
  6547. GrowArrayHeadHelperForUnshift<Var>(pArr, unshiftElements, scriptContext);
  6548. }
  6549. }
  6550. while (renumberSeg)
  6551. {
  6552. renumberSeg->left += unshiftElements;
  6553. if (renumberSeg->next == nullptr)
  6554. {
  6555. // last segment can shift its left + size beyond MaxArrayLength, so truncate if so
  6556. renumberSeg->EnsureSizeInBound();
  6557. }
  6558. renumberSeg = renumberSeg->next;
  6559. }
  6560. if (isIntArray)
  6561. {
  6562. UnshiftHelper<int32>(pArr, unshiftElements, args.Values);
  6563. }
  6564. else if (isFloatArray)
  6565. {
  6566. UnshiftHelper<double>(pArr, unshiftElements, args.Values);
  6567. }
  6568. else
  6569. {
  6570. UnshiftHelper<Var>(pArr, unshiftElements, args.Values);
  6571. }
  6572. pArr->InvalidateLastUsedSegment();
  6573. pArr->length += unshiftElements;
  6574. #ifdef VALIDATE_ARRAY
  6575. pArr->ValidateArray();
  6576. #endif
  6577. if (newLenOverflowed) // ES5: throw if new "length" exceeds max array length
  6578. {
  6579. JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthAssignIncorrect);
  6580. }
  6581. }
  6582. res = JavascriptNumber::ToVar(pArr->length, scriptContext);
  6583. }
  6584. else
  6585. {
  6586. RecyclableObject* dynamicObject = nullptr;
  6587. if (FALSE == JavascriptConversion::ToObject(args[0], scriptContext, &dynamicObject))
  6588. {
  6589. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.unshift"));
  6590. }
  6591. BigIndex length;
  6592. if (scriptContext->GetConfig()->IsES6ToLengthEnabled())
  6593. {
  6594. length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(dynamicObject, scriptContext), scriptContext);
  6595. }
  6596. else
  6597. {
  6598. length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(dynamicObject, scriptContext), scriptContext);
  6599. }
  6600. uint32 unshiftElements = args.Info.Count - 1;
  6601. if (unshiftElements > 0)
  6602. {
  6603. uint32 MaxSpaceUint32 = MaxArrayLength - unshiftElements;
  6604. // Note: end will always be a smallIndex either it is less than length in which case it is MaxSpaceUint32
  6605. // or MaxSpaceUint32 is greater than length meaning length is a uint32 number
  6606. BigIndex end = length > MaxSpaceUint32 ? MaxSpaceUint32 : length;
  6607. if (end < length)
  6608. {
  6609. // Unshift [end, length) to MaxArrayLength
  6610. // MaxArrayLength + (length - MaxSpaceUint32 - 1) = length + unshiftElements -1
  6611. if (length.IsSmallIndex())
  6612. {
  6613. Unshift<BigIndex>(dynamicObject, MaxArrayLength, end.GetSmallIndex(), length.GetSmallIndex(), scriptContext);
  6614. }
  6615. else
  6616. {
  6617. Unshift<BigIndex, uint64>(dynamicObject, MaxArrayLength, end.GetSmallIndex(), length.GetBigIndex(), scriptContext);
  6618. }
  6619. }
  6620. // Unshift [0, end) to unshiftElements
  6621. // unshiftElements + (MaxSpaceUint32 - 0 - 1) = MaxArrayLength -1 therefore this unshift covers up to MaxArrayLength - 1
  6622. Unshift<uint32>(dynamicObject, unshiftElements, 0, end.GetSmallIndex(), scriptContext);
  6623. for (uint32 i = 0; i < unshiftElements; i++)
  6624. {
  6625. JavascriptOperators::SetItem(dynamicObject, dynamicObject, i, args[i + 1], scriptContext, PropertyOperation_ThrowIfNotExtensible, true);
  6626. }
  6627. }
  6628. ThrowTypeErrorOnFailureHelper h(scriptContext, _u("Array.prototype.unshift"));
  6629. //ES6 - update 'length' even if unshiftElements == 0;
  6630. BigIndex newLen = length + unshiftElements;
  6631. res = JavascriptNumber::ToVar(newLen.IsSmallIndex() ? newLen.GetSmallIndex() : newLen.GetBigIndex(), scriptContext);
  6632. h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(dynamicObject, dynamicObject, PropertyIds::length, res, scriptContext, PropertyOperation_ThrowIfNotExtensible));
  6633. }
  6634. return res;
  6635. }
  6636. Var JavascriptArray::EntryToString(RecyclableObject* function, CallInfo callInfo, ...)
  6637. {
  6638. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  6639. ARGUMENTS(args, callInfo);
  6640. ScriptContext* scriptContext = function->GetScriptContext();
  6641. Assert(!(callInfo.Flags & CallFlags_New));
  6642. if (args.Info.Count == 0)
  6643. {
  6644. JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedObject);
  6645. }
  6646. // ES5 15.4.4.2: call join, or built-in Object.prototype.toString
  6647. RecyclableObject* obj = nullptr;
  6648. if (FALSE == JavascriptConversion::ToObject(args[0], scriptContext, &obj))
  6649. {
  6650. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.toString"));
  6651. }
  6652. // In ES5 we could be calling a user defined join, even on array. We must [[Get]] join at runtime.
  6653. Var join = JavascriptOperators::GetProperty(obj, PropertyIds::join, scriptContext);
  6654. if (JavascriptConversion::IsCallable(join))
  6655. {
  6656. RecyclableObject* func = RecyclableObject::FromVar(join);
  6657. // We need to record implicit call here, because marked the Array.toString as no side effect,
  6658. // but if we call user code here which may have side effect
  6659. ThreadContext * threadContext = scriptContext->GetThreadContext();
  6660. Var result = threadContext->ExecuteImplicitCall(func, ImplicitCall_ToPrimitive, [=]() -> Js::Var
  6661. {
  6662. // Stack object should have a pre-op bail on implicit call. We shouldn't see them here.
  6663. Assert(!ThreadContext::IsOnStack(obj));
  6664. // The correct flag value is CallFlags_Value but we pass CallFlags_None in compat modes
  6665. CallFlags flags = CallFlags_Value;
  6666. return CALL_FUNCTION(func, CallInfo(flags, 1), obj);
  6667. });
  6668. if(!result)
  6669. {
  6670. // There was an implicit call and implicit calls are disabled. This would typically cause a bailout.
  6671. Assert(threadContext->IsDisableImplicitCall());
  6672. result = scriptContext->GetLibrary()->GetNull();
  6673. }
  6674. return result;
  6675. }
  6676. else
  6677. {
  6678. // call built-in Object.prototype.toString
  6679. return CALL_ENTRYPOINT(JavascriptObject::EntryToString, function, CallInfo(1), obj);
  6680. }
  6681. }
  6682. #if DEBUG
  6683. BOOL JavascriptArray::GetIndex(const char16* propName, uint32 *pIndex)
  6684. {
  6685. uint32 lu, luDig;
  6686. int32 cch = (int32)wcslen(propName);
  6687. char16* pch = const_cast<char16 *>(propName);
  6688. lu = *pch - '0';
  6689. if (lu > 9)
  6690. return FALSE;
  6691. if (0 == lu)
  6692. {
  6693. *pIndex = 0;
  6694. return 1 == cch;
  6695. }
  6696. while ((luDig = *++pch - '0') < 10)
  6697. {
  6698. // If we overflow 32 bits, ignore the item
  6699. if (lu > 0x19999999)
  6700. return FALSE;
  6701. lu *= 10;
  6702. if(lu > (ULONG_MAX - luDig))
  6703. return FALSE;
  6704. lu += luDig;
  6705. }
  6706. if (pch - propName != cch)
  6707. return FALSE;
  6708. if (lu == JavascriptArray::InvalidIndex)
  6709. {
  6710. // 0xFFFFFFFF is not treated as an array index so that the length can be
  6711. // capped at 32 bits.
  6712. return FALSE;
  6713. }
  6714. *pIndex = lu;
  6715. return TRUE;
  6716. }
  6717. #endif
  6718. JavascriptString* JavascriptArray::GetLocaleSeparator(ScriptContext* scriptContext)
  6719. {
  6720. #ifdef ENABLE_GLOBALIZATION
  6721. LCID lcid = GetUserDefaultLCID();
  6722. int count = 0;
  6723. char16 szSeparator[6];
  6724. // According to the document for GetLocaleInfo this is a sufficient buffer size.
  6725. count = GetLocaleInfoW(lcid, LOCALE_SLIST, szSeparator, 5);
  6726. if( !count)
  6727. {
  6728. AssertMsg(FALSE, "GetLocaleInfo failed");
  6729. return scriptContext->GetLibrary()->GetCommaSpaceDisplayString();
  6730. }
  6731. else
  6732. {
  6733. // Append ' ' if necessary
  6734. if( count < 2 || szSeparator[count-2] != ' ')
  6735. {
  6736. szSeparator[count-1] = ' ';
  6737. szSeparator[count] = '\0';
  6738. }
  6739. return JavascriptString::NewCopyBuffer(szSeparator, count, scriptContext);
  6740. }
  6741. #else
  6742. // xplat-todo: Support locale-specific seperator
  6743. return scriptContext->GetLibrary()->GetCommaSpaceDisplayString();
  6744. #endif
  6745. }
  6746. template <typename T>
  6747. JavascriptString* JavascriptArray::ToLocaleString(T* arr, ScriptContext* scriptContext)
  6748. {
  6749. uint32 length = ItemTrace<T>::GetLength(arr, scriptContext);
  6750. if (length == 0 || scriptContext->CheckObject(arr))
  6751. {
  6752. return scriptContext->GetLibrary()->GetEmptyString();
  6753. }
  6754. JavascriptString* res = scriptContext->GetLibrary()->GetEmptyString();
  6755. bool pushedObject = false;
  6756. TryFinally([&]()
  6757. {
  6758. scriptContext->PushObject(arr);
  6759. pushedObject = true;
  6760. Var element;
  6761. if (ItemTrace<T>::GetItem(arr, 0, &element, scriptContext))
  6762. {
  6763. res = JavascriptArray::ToLocaleStringHelper(element, scriptContext);
  6764. }
  6765. if (length > 1)
  6766. {
  6767. JavascriptString* separator = GetLocaleSeparator(scriptContext);
  6768. for (uint32 i = 1; i < length; i++)
  6769. {
  6770. res = JavascriptString::Concat(res, separator);
  6771. if (ItemTrace<T>::GetItem(arr, i, &element, scriptContext))
  6772. {
  6773. res = JavascriptString::Concat(res, JavascriptArray::ToLocaleStringHelper(element, scriptContext));
  6774. }
  6775. }
  6776. }
  6777. },
  6778. [&](bool/*hasException*/)
  6779. {
  6780. if (pushedObject)
  6781. {
  6782. Var top = scriptContext->PopObject();
  6783. AssertMsg(top == arr, "Unmatched operation stack");
  6784. }
  6785. });
  6786. if (res == nullptr)
  6787. {
  6788. res = scriptContext->GetLibrary()->GetEmptyString();
  6789. }
  6790. return res;
  6791. }
  6792. Var JavascriptArray::EntryIsArray(RecyclableObject* function, CallInfo callInfo, ...)
  6793. {
  6794. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  6795. ARGUMENTS(args, callInfo);
  6796. ScriptContext* scriptContext = function->GetScriptContext();
  6797. Assert(!(callInfo.Flags & CallFlags_New));
  6798. CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(ArrayisArrayCount);
  6799. if (args.Info.Count < 2)
  6800. {
  6801. return scriptContext->GetLibrary()->GetFalse();
  6802. }
  6803. #if ENABLE_COPYONACCESS_ARRAY
  6804. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(args[1]);
  6805. #endif
  6806. if (JavascriptOperators::IsArray(args[1]))
  6807. {
  6808. return scriptContext->GetLibrary()->GetTrue();
  6809. }
  6810. return scriptContext->GetLibrary()->GetFalse();
  6811. }
  6812. ///----------------------------------------------------------------------------
  6813. /// Find() calls the given predicate callback on each element of the array, in
  6814. /// order, and returns the first element that makes the predicate return true,
  6815. /// as described in (ES6.0: S22.1.3.8).
  6816. ///----------------------------------------------------------------------------
  6817. Var JavascriptArray::EntryFind(RecyclableObject* function, CallInfo callInfo, ...)
  6818. {
  6819. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  6820. ARGUMENTS(args, callInfo);
  6821. ScriptContext* scriptContext = function->GetScriptContext();
  6822. Assert(!(callInfo.Flags & CallFlags_New));
  6823. if (args.Info.Count == 0)
  6824. {
  6825. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.find"));
  6826. }
  6827. int64 length;
  6828. JavascriptArray * pArr = nullptr;
  6829. RecyclableObject* obj = nullptr;
  6830. if (JavascriptArray::Is(args[0]) && !JavascriptArray::FromVar(args[0])->IsCrossSiteObject())
  6831. {
  6832. pArr = JavascriptArray::FromVar(args[0]);
  6833. obj = pArr;
  6834. length = pArr->length;
  6835. }
  6836. else
  6837. {
  6838. if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj))
  6839. {
  6840. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.find"));
  6841. }
  6842. // In ES6-mode, we always load the length property from the object instead of using the internal slot.
  6843. // Even for arrays, this is now observable via proxies.
  6844. // If source object is not an array, we fall back to this behavior anyway.
  6845. Var lenValue = JavascriptOperators::OP_GetLength(obj, scriptContext);
  6846. length = JavascriptConversion::ToLength(lenValue, scriptContext);
  6847. }
  6848. return JavascriptArray::FindHelper<false>(pArr, nullptr, obj, length, args, scriptContext);
  6849. }
  6850. template <bool findIndex>
  6851. Var JavascriptArray::FindHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, int64 length, Arguments& args, ScriptContext* scriptContext)
  6852. {
  6853. if (args.Info.Count < 2 || !JavascriptConversion::IsCallable(args[1]))
  6854. {
  6855. // typedArrayBase is only non-null if and only if we came here via the TypedArray entrypoint
  6856. if (typedArrayBase != nullptr)
  6857. {
  6858. JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, findIndex ? _u("[TypedArray].prototype.findIndex") : _u("[TypedArray].prototype.find"));
  6859. }
  6860. else
  6861. {
  6862. JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, findIndex ? _u("Array.prototype.findIndex") : _u("Array.prototype.find"));
  6863. }
  6864. }
  6865. RecyclableObject* callBackFn = RecyclableObject::FromVar(args[1]);
  6866. Var thisArg;
  6867. if (args.Info.Count > 2)
  6868. {
  6869. thisArg = args[2];
  6870. }
  6871. else
  6872. {
  6873. thisArg = scriptContext->GetLibrary()->GetUndefined();
  6874. }
  6875. // If we came from Array.prototype.find/findIndex and source object is not a JavascriptArray, source could be a TypedArray
  6876. if (typedArrayBase == nullptr && pArr == nullptr && TypedArrayBase::Is(obj))
  6877. {
  6878. typedArrayBase = TypedArrayBase::FromVar(obj);
  6879. }
  6880. // The correct flag value is CallFlags_Value but we pass CallFlags_None in compat modes
  6881. CallFlags flags = CallFlags_Value;
  6882. Var element = nullptr;
  6883. Var testResult = nullptr;
  6884. if (pArr)
  6885. {
  6886. Var undefined = scriptContext->GetLibrary()->GetUndefined();
  6887. for (uint32 k = 0; k < length; k++)
  6888. {
  6889. element = undefined;
  6890. pArr->DirectGetItemAtFull(k, &element);
  6891. Var index = JavascriptNumber::ToVar(k, scriptContext);
  6892. testResult = CALL_FUNCTION(callBackFn, CallInfo(flags, 4), thisArg,
  6893. element,
  6894. index,
  6895. pArr);
  6896. if (JavascriptConversion::ToBoolean(testResult, scriptContext))
  6897. {
  6898. return findIndex ? index : element;
  6899. }
  6900. }
  6901. }
  6902. else if (typedArrayBase)
  6903. {
  6904. for (uint32 k = 0; k < length; k++)
  6905. {
  6906. element = typedArrayBase->DirectGetItem(k);
  6907. Var index = JavascriptNumber::ToVar(k, scriptContext);
  6908. testResult = CALL_FUNCTION(callBackFn, CallInfo(flags, 4), thisArg,
  6909. element,
  6910. index,
  6911. typedArrayBase);
  6912. if (JavascriptConversion::ToBoolean(testResult, scriptContext))
  6913. {
  6914. return findIndex ? index : element;
  6915. }
  6916. }
  6917. }
  6918. else
  6919. {
  6920. for (uint32 k = 0; k < length; k++)
  6921. {
  6922. element = JavascriptOperators::GetItem(obj, k, scriptContext);
  6923. Var index = JavascriptNumber::ToVar(k, scriptContext);
  6924. testResult = CALL_FUNCTION(callBackFn, CallInfo(flags, 4), thisArg,
  6925. element,
  6926. index,
  6927. obj);
  6928. if (JavascriptConversion::ToBoolean(testResult, scriptContext))
  6929. {
  6930. return findIndex ? index : element;
  6931. }
  6932. }
  6933. }
  6934. return findIndex ? JavascriptNumber::ToVar(-1, scriptContext) : scriptContext->GetLibrary()->GetUndefined();
  6935. }
  6936. ///----------------------------------------------------------------------------
  6937. /// FindIndex() calls the given predicate callback on each element of the
  6938. /// array, in order, and returns the index of the first element that makes the
  6939. /// predicate return true, as described in (ES6.0: S22.1.3.9).
  6940. ///----------------------------------------------------------------------------
  6941. Var JavascriptArray::EntryFindIndex(RecyclableObject* function, CallInfo callInfo, ...)
  6942. {
  6943. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  6944. ARGUMENTS(args, callInfo);
  6945. ScriptContext* scriptContext = function->GetScriptContext();
  6946. Assert(!(callInfo.Flags & CallFlags_New));
  6947. if (args.Info.Count == 0)
  6948. {
  6949. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.findIndex"));
  6950. }
  6951. int64 length;
  6952. JavascriptArray * pArr = nullptr;
  6953. RecyclableObject* obj = nullptr;
  6954. if (JavascriptArray::Is(args[0]) && !JavascriptArray::FromVar(args[0])->IsCrossSiteObject())
  6955. {
  6956. pArr = JavascriptArray::FromVar(args[0]);
  6957. obj = pArr;
  6958. length = pArr->length;
  6959. }
  6960. else
  6961. {
  6962. if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj))
  6963. {
  6964. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.findIndex"));
  6965. }
  6966. // In ES6-mode, we always load the length property from the object instead of using the internal slot.
  6967. // Even for arrays, this is now observable via proxies.
  6968. // If source object is not an array, we fall back to this behavior anyway.
  6969. Var lenValue = JavascriptOperators::OP_GetLength(obj, scriptContext);
  6970. length = JavascriptConversion::ToLength(lenValue, scriptContext);
  6971. }
  6972. return JavascriptArray::FindHelper<true>(pArr, nullptr, obj, length, args, scriptContext);
  6973. }
  6974. ///----------------------------------------------------------------------------
  6975. /// Entries() returns a new ArrayIterator object configured to return key-
  6976. /// value pairs matching the elements of the this array/array-like object,
  6977. /// as described in (ES6.0: S22.1.3.4).
  6978. ///----------------------------------------------------------------------------
  6979. Var JavascriptArray::EntryEntries(RecyclableObject* function, CallInfo callInfo, ...)
  6980. {
  6981. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  6982. ARGUMENTS(args, callInfo);
  6983. ScriptContext* scriptContext = function->GetScriptContext();
  6984. Assert(!(callInfo.Flags & CallFlags_New));
  6985. if (args.Info.Count == 0)
  6986. {
  6987. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.entries"));
  6988. }
  6989. RecyclableObject* thisObj = nullptr;
  6990. if (FALSE == JavascriptConversion::ToObject(args[0], scriptContext, &thisObj))
  6991. {
  6992. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.entries"));
  6993. }
  6994. #if ENABLE_COPYONACCESS_ARRAY
  6995. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(thisObj);
  6996. #endif
  6997. return scriptContext->GetLibrary()->CreateArrayIterator(thisObj, JavascriptArrayIteratorKind::KeyAndValue);
  6998. }
  6999. ///----------------------------------------------------------------------------
  7000. /// Keys() returns a new ArrayIterator object configured to return the keys
  7001. /// of the this array/array-like object, as described in (ES6.0: S22.1.3.13).
  7002. ///----------------------------------------------------------------------------
  7003. Var JavascriptArray::EntryKeys(RecyclableObject* function, CallInfo callInfo, ...)
  7004. {
  7005. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  7006. ARGUMENTS(args, callInfo);
  7007. ScriptContext* scriptContext = function->GetScriptContext();
  7008. Assert(!(callInfo.Flags & CallFlags_New));
  7009. if (args.Info.Count == 0)
  7010. {
  7011. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.keys"));
  7012. }
  7013. RecyclableObject* thisObj = nullptr;
  7014. if (FALSE == JavascriptConversion::ToObject(args[0], scriptContext, &thisObj))
  7015. {
  7016. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.keys"));
  7017. }
  7018. #if ENABLE_COPYONACCESS_ARRAY
  7019. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(thisObj);
  7020. #endif
  7021. return scriptContext->GetLibrary()->CreateArrayIterator(thisObj, JavascriptArrayIteratorKind::Key);
  7022. }
  7023. ///----------------------------------------------------------------------------
  7024. /// Values() returns a new ArrayIterator object configured to return the values
  7025. /// of the this array/array-like object, as described in (ES6.0: S22.1.3.29).
  7026. ///----------------------------------------------------------------------------
  7027. Var JavascriptArray::EntryValues(RecyclableObject* function, CallInfo callInfo, ...)
  7028. {
  7029. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  7030. ARGUMENTS(args, callInfo);
  7031. ScriptContext* scriptContext = function->GetScriptContext();
  7032. Assert(!(callInfo.Flags & CallFlags_New));
  7033. if (args.Info.Count == 0)
  7034. {
  7035. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.values"));
  7036. }
  7037. RecyclableObject* thisObj = nullptr;
  7038. if (FALSE == JavascriptConversion::ToObject(args[0], scriptContext, &thisObj))
  7039. {
  7040. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.values"));
  7041. }
  7042. return scriptContext->GetLibrary()->CreateArrayIterator(thisObj, JavascriptArrayIteratorKind::Value);
  7043. }
  7044. Var JavascriptArray::EntryEvery(RecyclableObject* function, CallInfo callInfo, ...)
  7045. {
  7046. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  7047. ARGUMENTS(args, callInfo);
  7048. ScriptContext* scriptContext = function->GetScriptContext();
  7049. AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Array.prototype.every"));
  7050. Assert(!(callInfo.Flags & CallFlags_New));
  7051. CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(ArrayEveryCount);
  7052. if (args.Info.Count == 0)
  7053. {
  7054. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.every"));
  7055. }
  7056. BigIndex length;
  7057. JavascriptArray* pArr = nullptr;
  7058. RecyclableObject* obj = nullptr;
  7059. if (JavascriptArray::Is(args[0]) && !JavascriptArray::FromVar(args[0])->IsCrossSiteObject())
  7060. {
  7061. pArr = JavascriptArray::FromVar(args[0]);
  7062. obj = pArr;
  7063. }
  7064. else
  7065. {
  7066. if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj))
  7067. {
  7068. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.every"));
  7069. }
  7070. }
  7071. // In ES6-mode, we always load the length property from the object instead of using the internal slot.
  7072. // Even for arrays, this is now observable via proxies.
  7073. // If source object is not an array, we fall back to this behavior anyway.
  7074. if (scriptContext->GetConfig()->IsES6TypedArrayExtensionsEnabled() || pArr == nullptr)
  7075. {
  7076. if (scriptContext->GetConfig()->IsES6ToLengthEnabled())
  7077. {
  7078. length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext);
  7079. }
  7080. else
  7081. {
  7082. length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext);
  7083. }
  7084. }
  7085. else
  7086. {
  7087. length = pArr->length;
  7088. }
  7089. if (length.IsSmallIndex())
  7090. {
  7091. return JavascriptArray::EveryHelper(pArr, nullptr, obj, length.GetSmallIndex(), args, scriptContext);
  7092. }
  7093. 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
  7094. return JavascriptArray::EveryHelper(pArr, nullptr, obj, length.GetBigIndex(), args, scriptContext);
  7095. }
  7096. // Array.prototype.every as described by ES6.0 (draft 22) Section 22.1.3.5
  7097. template <typename T>
  7098. Var JavascriptArray::EveryHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, Arguments& args, ScriptContext* scriptContext)
  7099. {
  7100. if (args.Info.Count < 2 || !JavascriptConversion::IsCallable(args[1]))
  7101. {
  7102. // typedArrayBase is only non-null if and only if we came here via the TypedArray entrypoint
  7103. if (typedArrayBase != nullptr)
  7104. {
  7105. JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("[TypedArray].prototype.every"));
  7106. }
  7107. else
  7108. {
  7109. JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("Array.prototype.every"));
  7110. }
  7111. }
  7112. RecyclableObject* callBackFn = RecyclableObject::FromVar(args[1]);
  7113. Var thisArg = nullptr;
  7114. if (args.Info.Count > 2)
  7115. {
  7116. thisArg = args[2];
  7117. }
  7118. else
  7119. {
  7120. thisArg = scriptContext->GetLibrary()->GetUndefined();
  7121. }
  7122. // If we came from Array.prototype.map and source object is not a JavascriptArray, source could be a TypedArray
  7123. if (typedArrayBase == nullptr && pArr == nullptr && TypedArrayBase::Is(obj))
  7124. {
  7125. typedArrayBase = TypedArrayBase::FromVar(obj);
  7126. }
  7127. Var element = nullptr;
  7128. Var testResult = nullptr;
  7129. // The correct flag value is CallFlags_Value but we pass CallFlags_None in compat modes
  7130. CallFlags flags = CallFlags_Value;
  7131. if (pArr)
  7132. {
  7133. for (uint32 k = 0; k < length; k++)
  7134. {
  7135. if (!pArr->DirectGetItemAtFull(k, &element))
  7136. {
  7137. continue;
  7138. }
  7139. testResult = CALL_FUNCTION(callBackFn, CallInfo(flags, 4), thisArg,
  7140. element,
  7141. JavascriptNumber::ToVar(k, scriptContext),
  7142. pArr);
  7143. if (!JavascriptConversion::ToBoolean(testResult, scriptContext))
  7144. {
  7145. return scriptContext->GetLibrary()->GetFalse();
  7146. }
  7147. }
  7148. }
  7149. else if (typedArrayBase)
  7150. {
  7151. Assert(length <= UINT_MAX);
  7152. for (uint32 k = 0; k < length; k++)
  7153. {
  7154. if (!typedArrayBase->HasItem(k))
  7155. {
  7156. continue;
  7157. }
  7158. element = typedArrayBase->DirectGetItem(k);
  7159. testResult = CALL_FUNCTION(callBackFn, CallInfo(flags, 4), thisArg,
  7160. element,
  7161. JavascriptNumber::ToVar(k, scriptContext),
  7162. typedArrayBase);
  7163. if (!JavascriptConversion::ToBoolean(testResult, scriptContext))
  7164. {
  7165. return scriptContext->GetLibrary()->GetFalse();
  7166. }
  7167. }
  7168. }
  7169. else
  7170. {
  7171. for (T k = 0; k < length; k++)
  7172. {
  7173. // According to es6 spec, we need to call Has first before calling Get
  7174. if (JavascriptOperators::HasItem(obj, k))
  7175. {
  7176. element = JavascriptOperators::GetItem(obj, k, scriptContext);
  7177. testResult = CALL_FUNCTION(callBackFn, CallInfo(flags, 4), thisArg,
  7178. element,
  7179. JavascriptNumber::ToVar(k, scriptContext),
  7180. obj);
  7181. if (!JavascriptConversion::ToBoolean(testResult, scriptContext))
  7182. {
  7183. return scriptContext->GetLibrary()->GetFalse();
  7184. }
  7185. }
  7186. }
  7187. }
  7188. return scriptContext->GetLibrary()->GetTrue();
  7189. }
  7190. Var JavascriptArray::EntrySome(RecyclableObject* function, CallInfo callInfo, ...)
  7191. {
  7192. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  7193. ARGUMENTS(args, callInfo);
  7194. ScriptContext* scriptContext = function->GetScriptContext();
  7195. AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Array.prototype.some"));
  7196. CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(ArraySomeCount);
  7197. Assert(!(callInfo.Flags & CallFlags_New));
  7198. if (args.Info.Count == 0)
  7199. {
  7200. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.some"));
  7201. }
  7202. BigIndex length;
  7203. JavascriptArray* pArr = nullptr;
  7204. RecyclableObject* obj = nullptr;
  7205. if (JavascriptArray::Is(args[0]) && !JavascriptArray::FromVar(args[0])->IsCrossSiteObject())
  7206. {
  7207. pArr = JavascriptArray::FromVar(args[0]);
  7208. obj = pArr;
  7209. }
  7210. else
  7211. {
  7212. if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj))
  7213. {
  7214. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.some"));
  7215. }
  7216. }
  7217. // In ES6-mode, we always load the length property from the object instead of using the internal slot.
  7218. // Even for arrays, this is now observable via proxies.
  7219. // If source object is not an array, we fall back to this behavior anyway.
  7220. if (scriptContext->GetConfig()->IsES6TypedArrayExtensionsEnabled() || pArr == nullptr)
  7221. {
  7222. if (scriptContext->GetConfig()->IsES6ToLengthEnabled())
  7223. {
  7224. length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext);
  7225. }
  7226. else
  7227. {
  7228. length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext);
  7229. }
  7230. }
  7231. else
  7232. {
  7233. length = pArr->length;
  7234. }
  7235. if (length.IsSmallIndex())
  7236. {
  7237. return JavascriptArray::SomeHelper(pArr, nullptr, obj, length.GetSmallIndex(), args, scriptContext);
  7238. }
  7239. 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
  7240. return JavascriptArray::SomeHelper(pArr, nullptr, obj, length.GetBigIndex(), args, scriptContext);
  7241. }
  7242. // Array.prototype.some as described in ES6.0 (draft 22) Section 22.1.3.23
  7243. template <typename T>
  7244. Var JavascriptArray::SomeHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, Arguments& args, ScriptContext* scriptContext)
  7245. {
  7246. if (args.Info.Count < 2 || !JavascriptConversion::IsCallable(args[1]))
  7247. {
  7248. // We are in the TypedArray version of this API if and only if typedArrayBase != nullptr
  7249. if (typedArrayBase != nullptr)
  7250. {
  7251. JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("[TypedArray].prototype.some"));
  7252. }
  7253. else
  7254. {
  7255. JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("Array.prototype.some"));
  7256. }
  7257. }
  7258. RecyclableObject* callBackFn = RecyclableObject::FromVar(args[1]);
  7259. Var thisArg = nullptr;
  7260. if (args.Info.Count > 2)
  7261. {
  7262. thisArg = args[2];
  7263. }
  7264. else
  7265. {
  7266. thisArg = scriptContext->GetLibrary()->GetUndefined();
  7267. }
  7268. // If we came from Array.prototype.some and source object is not a JavascriptArray, source could be a TypedArray
  7269. if (typedArrayBase == nullptr && pArr == nullptr && TypedArrayBase::Is(obj))
  7270. {
  7271. typedArrayBase = TypedArrayBase::FromVar(obj);
  7272. }
  7273. // The correct flag value is CallFlags_Value but we pass CallFlags_None in compat modes
  7274. CallFlags flags = CallFlags_Value;
  7275. Var element = nullptr;
  7276. Var testResult = nullptr;
  7277. if (pArr)
  7278. {
  7279. for (uint32 k = 0; k < length; k++)
  7280. {
  7281. if (!pArr->DirectGetItemAtFull(k, &element))
  7282. {
  7283. continue;
  7284. }
  7285. testResult = CALL_FUNCTION(callBackFn, CallInfo(flags, 4), thisArg,
  7286. element,
  7287. JavascriptNumber::ToVar(k, scriptContext),
  7288. pArr);
  7289. if (JavascriptConversion::ToBoolean(testResult, scriptContext))
  7290. {
  7291. return scriptContext->GetLibrary()->GetTrue();
  7292. }
  7293. }
  7294. }
  7295. else if (typedArrayBase)
  7296. {
  7297. Assert(length <= UINT_MAX);
  7298. for (uint32 k = 0; k < length; k++)
  7299. {
  7300. // If k < typedArrayBase->length, we know that HasItem will return true.
  7301. // But we still have to call it in case there's a proxy trap or in the case that we are calling
  7302. // Array.prototype.some with a TypedArray that has a different length instance property.
  7303. if (!typedArrayBase->HasItem(k))
  7304. {
  7305. continue;
  7306. }
  7307. element = typedArrayBase->DirectGetItem(k);
  7308. testResult = CALL_FUNCTION(callBackFn, CallInfo(flags, 4), thisArg,
  7309. element,
  7310. JavascriptNumber::ToVar(k, scriptContext),
  7311. typedArrayBase);
  7312. if (JavascriptConversion::ToBoolean(testResult, scriptContext))
  7313. {
  7314. return scriptContext->GetLibrary()->GetTrue();
  7315. }
  7316. }
  7317. }
  7318. else
  7319. {
  7320. for (T k = 0; k < length; k++)
  7321. {
  7322. if (JavascriptOperators::HasItem(obj, k))
  7323. {
  7324. element = JavascriptOperators::GetItem(obj, k, scriptContext);
  7325. testResult = CALL_FUNCTION(callBackFn, CallInfo(flags, 4), thisArg,
  7326. element,
  7327. JavascriptNumber::ToVar(k, scriptContext),
  7328. obj);
  7329. if (JavascriptConversion::ToBoolean(testResult, scriptContext))
  7330. {
  7331. return scriptContext->GetLibrary()->GetTrue();
  7332. }
  7333. }
  7334. }
  7335. }
  7336. return scriptContext->GetLibrary()->GetFalse();
  7337. }
  7338. Var JavascriptArray::EntryForEach(RecyclableObject* function, CallInfo callInfo, ...)
  7339. {
  7340. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  7341. ARGUMENTS(args, callInfo);
  7342. ScriptContext* scriptContext = function->GetScriptContext();
  7343. AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Array.prototype.forEach"));
  7344. CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(ArrayForEachCount)
  7345. Assert(!(callInfo.Flags & CallFlags_New));
  7346. if (args.Info.Count == 0)
  7347. {
  7348. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.forEach"));
  7349. }
  7350. BigIndex length;
  7351. JavascriptArray* pArr = nullptr;
  7352. RecyclableObject* dynamicObject = nullptr;
  7353. RecyclableObject* callBackFn = nullptr;
  7354. Var thisArg = nullptr;
  7355. #if ENABLE_COPYONACCESS_ARRAY
  7356. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(args[0]);
  7357. #endif
  7358. if (JavascriptArray::Is(args[0]) && scriptContext == JavascriptArray::FromVar(args[0])->GetScriptContext())
  7359. {
  7360. pArr = JavascriptArray::FromVar(args[0]);
  7361. dynamicObject = pArr;
  7362. }
  7363. else
  7364. {
  7365. if (FALSE == JavascriptConversion::ToObject(args[0], scriptContext, &dynamicObject))
  7366. {
  7367. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.forEach"));
  7368. }
  7369. if (JavascriptArray::Is(dynamicObject) && scriptContext == JavascriptArray::FromVar(dynamicObject)->GetScriptContext())
  7370. {
  7371. pArr = JavascriptArray::FromVar(dynamicObject);
  7372. }
  7373. }
  7374. // In ES6-mode, we always load the length property from the object instead of using the internal slot.
  7375. // Even for arrays, this is now observable via proxies.
  7376. // If source object is not an array, we fall back to this behavior anyway.
  7377. if (scriptContext->GetConfig()->IsES6TypedArrayExtensionsEnabled() || pArr == nullptr)
  7378. {
  7379. if (scriptContext->GetConfig()->IsES6ToLengthEnabled())
  7380. {
  7381. length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(dynamicObject, scriptContext), scriptContext);
  7382. }
  7383. else
  7384. {
  7385. length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(dynamicObject, scriptContext), scriptContext);
  7386. }
  7387. }
  7388. else
  7389. {
  7390. length = pArr->length;
  7391. }
  7392. if (args.Info.Count < 2 || !JavascriptConversion::IsCallable(args[1]))
  7393. {
  7394. JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("Array.prototype.forEach"));
  7395. }
  7396. callBackFn = RecyclableObject::FromVar(args[1]);
  7397. if (args.Info.Count > 2)
  7398. {
  7399. thisArg = args[2];
  7400. }
  7401. else
  7402. {
  7403. thisArg = scriptContext->GetLibrary()->GetUndefined();
  7404. }
  7405. // The correct flag value is CallFlags_Value but we pass CallFlags_None in compat modes
  7406. CallFlags flags = CallFlags_Value;
  7407. auto fn32 = [dynamicObject, callBackFn, flags, thisArg, scriptContext](uint32 k, Var element)
  7408. {
  7409. CALL_FUNCTION(callBackFn, CallInfo(flags, 4), thisArg,
  7410. element,
  7411. JavascriptNumber::ToVar(k, scriptContext),
  7412. dynamicObject);
  7413. };
  7414. auto fn64 = [dynamicObject, callBackFn, flags, thisArg, scriptContext](uint64 k, Var element)
  7415. {
  7416. CALL_FUNCTION(callBackFn, CallInfo(flags, 4), thisArg,
  7417. element,
  7418. JavascriptNumber::ToVar(k, scriptContext),
  7419. dynamicObject);
  7420. };
  7421. if (pArr)
  7422. {
  7423. Assert(pArr == dynamicObject);
  7424. pArr->ForEachItemInRange<true>(0, length.IsUint32Max() ? MaxArrayLength : length.GetSmallIndex(), scriptContext, fn32);
  7425. }
  7426. else
  7427. {
  7428. if (length.IsSmallIndex())
  7429. {
  7430. TemplatedForEachItemInRange<true>(dynamicObject, 0u, length.GetSmallIndex(), scriptContext, fn32);
  7431. }
  7432. else
  7433. {
  7434. TemplatedForEachItemInRange<true>(dynamicObject, 0ui64, length.GetBigIndex(), scriptContext, fn64);
  7435. }
  7436. }
  7437. return scriptContext->GetLibrary()->GetUndefined();
  7438. }
  7439. Var JavascriptArray::EntryCopyWithin(RecyclableObject* function, CallInfo callInfo, ...)
  7440. {
  7441. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  7442. ARGUMENTS(args, callInfo);
  7443. ScriptContext* scriptContext = function->GetScriptContext();
  7444. Assert(!(callInfo.Flags & CallFlags_New));
  7445. RecyclableObject* obj = nullptr;
  7446. JavascriptArray* pArr = nullptr;
  7447. int64 length;
  7448. if (JavascriptArray::Is(args[0]) && !JavascriptArray::FromVar(args[0])->IsCrossSiteObject())
  7449. {
  7450. #if ENABLE_COPYONACCESS_ARRAY
  7451. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(args[0]);
  7452. #endif
  7453. pArr = JavascriptArray::FromVar(args[0]);
  7454. obj = pArr;
  7455. length = pArr->length;
  7456. }
  7457. else
  7458. {
  7459. if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj))
  7460. {
  7461. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.copyWithin"));
  7462. }
  7463. // In ES6-mode, we always load the length property from the object instead of using the internal slot.
  7464. // Even for arrays, this is now observable via proxies.
  7465. // If source object is not an array, we fall back to this behavior anyway.
  7466. Var lenValue = JavascriptOperators::OP_GetLength(obj, scriptContext);
  7467. length = JavascriptConversion::ToLength(lenValue, scriptContext);
  7468. }
  7469. return JavascriptArray::CopyWithinHelper(pArr, nullptr, obj, length, args, scriptContext);
  7470. }
  7471. // Array.prototype.copyWithin as defined in ES6.0 (draft 22) Section 22.1.3.3
  7472. Var JavascriptArray::CopyWithinHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, int64 length, Arguments& args, ScriptContext* scriptContext)
  7473. {
  7474. Assert(args.Info.Count > 0);
  7475. JavascriptLibrary* library = scriptContext->GetLibrary();
  7476. int64 fromVal = 0;
  7477. int64 toVal = 0;
  7478. int64 finalVal = length;
  7479. // If we came from Array.prototype.copyWithin and source object is not a JavascriptArray, source could be a TypedArray
  7480. if (typedArrayBase == nullptr && pArr == nullptr && TypedArrayBase::Is(obj))
  7481. {
  7482. typedArrayBase = TypedArrayBase::FromVar(obj);
  7483. }
  7484. if (args.Info.Count > 1)
  7485. {
  7486. toVal = JavascriptArray::GetIndexFromVar(args[1], length, scriptContext);
  7487. if (args.Info.Count > 2)
  7488. {
  7489. fromVal = JavascriptArray::GetIndexFromVar(args[2], length, scriptContext);
  7490. if (args.Info.Count > 3 && args[3] != library->GetUndefined())
  7491. {
  7492. finalVal = JavascriptArray::GetIndexFromVar(args[3], length, scriptContext);
  7493. }
  7494. }
  7495. }
  7496. // If count would be negative or zero, we won't do anything so go ahead and return early.
  7497. if (finalVal <= fromVal || length <= toVal)
  7498. {
  7499. return obj;
  7500. }
  7501. // Make sure we won't underflow during the count calculation
  7502. Assert(finalVal > fromVal && length > toVal);
  7503. int64 count = min(finalVal - fromVal, length - toVal);
  7504. // We shouldn't have made it here if the count was going to be zero
  7505. Assert(count > 0);
  7506. int direction;
  7507. if (fromVal < toVal && toVal < (fromVal + count))
  7508. {
  7509. direction = -1;
  7510. fromVal += count - 1;
  7511. toVal += count - 1;
  7512. }
  7513. else
  7514. {
  7515. direction = 1;
  7516. }
  7517. // If we are going to copy elements from or to indices > 2^32-1 we'll execute this (slightly slower path)
  7518. // It's possible to optimize here so that we use the normal code below except for the > 2^32-1 indices
  7519. if ((direction == -1 && (fromVal >= MaxArrayLength || toVal >= MaxArrayLength))
  7520. || (((fromVal + count) > MaxArrayLength) || ((toVal + count) > MaxArrayLength)))
  7521. {
  7522. while (count > 0)
  7523. {
  7524. Var index = JavascriptNumber::ToVar(fromVal, scriptContext);
  7525. if (JavascriptOperators::OP_HasItem(obj, index, scriptContext))
  7526. {
  7527. Var val = JavascriptOperators::OP_GetElementI(obj, index, scriptContext);
  7528. JavascriptOperators::OP_SetElementI(obj, JavascriptNumber::ToVar(toVal, scriptContext), val, scriptContext, PropertyOperation_ThrowIfNotExtensible);
  7529. }
  7530. else
  7531. {
  7532. JavascriptOperators::OP_DeleteElementI(obj, JavascriptNumber::ToVar(toVal, scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible);
  7533. }
  7534. fromVal += direction;
  7535. toVal += direction;
  7536. count--;
  7537. }
  7538. }
  7539. else
  7540. {
  7541. Assert(fromVal < MaxArrayLength);
  7542. Assert(toVal < MaxArrayLength);
  7543. Assert(direction == -1 || (fromVal + count < MaxArrayLength && toVal + count < MaxArrayLength));
  7544. uint32 fromIndex = static_cast<uint32>(fromVal);
  7545. uint32 toIndex = static_cast<uint32>(toVal);
  7546. while (count > 0)
  7547. {
  7548. if (obj->HasItem(fromIndex))
  7549. {
  7550. if (typedArrayBase)
  7551. {
  7552. Var val = typedArrayBase->DirectGetItem(fromIndex);
  7553. typedArrayBase->DirectSetItem(toIndex, val);
  7554. }
  7555. else if (pArr)
  7556. {
  7557. Var val = pArr->DirectGetItem(fromIndex);
  7558. pArr->SetItem(toIndex, val, Js::PropertyOperation_ThrowIfNotExtensible);
  7559. }
  7560. else
  7561. {
  7562. Var val = JavascriptOperators::OP_GetElementI_UInt32(obj, fromIndex, scriptContext);
  7563. JavascriptOperators::OP_SetElementI_UInt32(obj, toIndex, val, scriptContext, PropertyOperation_ThrowIfNotExtensible);
  7564. }
  7565. }
  7566. else
  7567. {
  7568. obj->DeleteItem(toIndex, PropertyOperation_ThrowIfNotExtensible);
  7569. }
  7570. fromIndex += direction;
  7571. toIndex += direction;
  7572. count--;
  7573. }
  7574. }
  7575. return obj;
  7576. }
  7577. Var JavascriptArray::EntryFill(RecyclableObject* function, CallInfo callInfo, ...)
  7578. {
  7579. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  7580. ARGUMENTS(args, callInfo);
  7581. ScriptContext* scriptContext = function->GetScriptContext();
  7582. Assert(!(callInfo.Flags & CallFlags_New));
  7583. RecyclableObject* obj = nullptr;
  7584. JavascriptArray* pArr = nullptr;
  7585. int64 length;
  7586. if (JavascriptArray::Is(args[0]) && !JavascriptArray::FromVar(args[0])->IsCrossSiteObject())
  7587. {
  7588. pArr = JavascriptArray::FromVar(args[0]);
  7589. obj = pArr;
  7590. length = pArr->length;
  7591. }
  7592. else
  7593. {
  7594. if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj))
  7595. {
  7596. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.fill"));
  7597. }
  7598. // In ES6-mode, we always load the length property from the object instead of using the internal slot.
  7599. // Even for arrays, this is now observable via proxies.
  7600. // If source object is not an array, we fall back to this behavior anyway.
  7601. Var lenValue = JavascriptOperators::OP_GetLength(obj, scriptContext);
  7602. length = JavascriptConversion::ToLength(lenValue, scriptContext);
  7603. }
  7604. return JavascriptArray::FillHelper(pArr, nullptr, obj, length, args, scriptContext);
  7605. }
  7606. // Array.prototype.fill as defined in ES6.0 (draft 22) Section 22.1.3.6
  7607. Var JavascriptArray::FillHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, int64 length, Arguments& args, ScriptContext* scriptContext)
  7608. {
  7609. Assert(args.Info.Count > 0);
  7610. JavascriptLibrary* library = scriptContext->GetLibrary();
  7611. // If we came from Array.prototype.fill and source object is not a JavascriptArray, source could be a TypedArray
  7612. if (typedArrayBase == nullptr && pArr == nullptr && TypedArrayBase::Is(obj))
  7613. {
  7614. typedArrayBase = TypedArrayBase::FromVar(obj);
  7615. }
  7616. Var fillValue;
  7617. if (args.Info.Count > 1)
  7618. {
  7619. fillValue = args[1];
  7620. }
  7621. else
  7622. {
  7623. fillValue = library->GetUndefined();
  7624. }
  7625. int64 k = 0;
  7626. int64 finalVal = length;
  7627. if (args.Info.Count > 2)
  7628. {
  7629. k = JavascriptArray::GetIndexFromVar(args[2], length, scriptContext);
  7630. if (args.Info.Count > 3 && !JavascriptOperators::IsUndefinedObject(args[3]))
  7631. {
  7632. finalVal = JavascriptArray::GetIndexFromVar(args[3], length, scriptContext);
  7633. }
  7634. }
  7635. if (k < MaxArrayLength)
  7636. {
  7637. int64 end = min<int64>(finalVal, MaxArrayLength);
  7638. uint32 u32k = static_cast<uint32>(k);
  7639. while (u32k < end)
  7640. {
  7641. if (typedArrayBase)
  7642. {
  7643. typedArrayBase->DirectSetItem(u32k, fillValue);
  7644. }
  7645. else if (pArr)
  7646. {
  7647. pArr->SetItem(u32k, fillValue, PropertyOperation_ThrowIfNotExtensible);
  7648. }
  7649. else
  7650. {
  7651. JavascriptOperators::OP_SetElementI_UInt32(obj, u32k, fillValue, scriptContext, Js::PropertyOperation_ThrowIfNotExtensible);
  7652. }
  7653. u32k++;
  7654. }
  7655. BigIndex dstIndex = MaxArrayLength;
  7656. for (int64 i = end; i < finalVal; ++i)
  7657. {
  7658. if (pArr)
  7659. {
  7660. pArr->DirectSetItemAt(dstIndex, fillValue);
  7661. ++dstIndex;
  7662. }
  7663. else
  7664. {
  7665. JavascriptOperators::OP_SetElementI(obj, JavascriptNumber::ToVar(i, scriptContext), fillValue, scriptContext, Js::PropertyOperation_ThrowIfNotExtensible);
  7666. }
  7667. }
  7668. }
  7669. else
  7670. {
  7671. BigIndex dstIndex = static_cast<uint64>(k);
  7672. for (int64 i = k; i < finalVal; i++)
  7673. {
  7674. if (pArr)
  7675. {
  7676. pArr->DirectSetItemAt(dstIndex, fillValue);
  7677. ++dstIndex;
  7678. }
  7679. else
  7680. {
  7681. JavascriptOperators::OP_SetElementI(obj, JavascriptNumber::ToVar(i, scriptContext), fillValue, scriptContext, Js::PropertyOperation_ThrowIfNotExtensible);
  7682. }
  7683. }
  7684. }
  7685. return obj;
  7686. }
  7687. // Array.prototype.map as defined by ES6.0 (Final) 22.1.3.15
  7688. Var JavascriptArray::EntryMap(RecyclableObject* function, CallInfo callInfo, ...)
  7689. {
  7690. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  7691. ARGUMENTS(args, callInfo);
  7692. ScriptContext* scriptContext = function->GetScriptContext();
  7693. AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Array.prototype.map"));
  7694. CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(ArrayMapCount);
  7695. Assert(!(callInfo.Flags & CallFlags_New));
  7696. if (args.Info.Count == 0)
  7697. {
  7698. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.map"));
  7699. }
  7700. BigIndex length;
  7701. JavascriptArray* pArr = nullptr;
  7702. RecyclableObject* obj = nullptr;
  7703. if (JavascriptArray::Is(args[0]) && !JavascriptArray::FromVar(args[0])->IsCrossSiteObject())
  7704. {
  7705. pArr = JavascriptArray::FromVar(args[0]);
  7706. obj = pArr;
  7707. }
  7708. else
  7709. {
  7710. if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj))
  7711. {
  7712. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.map"));
  7713. }
  7714. }
  7715. // In ES6-mode, we always load the length property from the object instead of using the internal slot.
  7716. // Even for arrays, this is now observable via proxies.
  7717. // If source object is not an array, we fall back to this behavior anyway.
  7718. if (scriptContext->GetConfig()->IsES6TypedArrayExtensionsEnabled() || pArr == nullptr)
  7719. {
  7720. length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext);
  7721. }
  7722. else
  7723. {
  7724. length = pArr->length;
  7725. }
  7726. if (length.IsSmallIndex())
  7727. {
  7728. return JavascriptArray::MapHelper(pArr, nullptr, obj, length.GetSmallIndex(), args, scriptContext);
  7729. }
  7730. 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
  7731. return JavascriptArray::MapHelper(pArr, nullptr, obj, length.GetBigIndex(), args, scriptContext);
  7732. }
  7733. template<typename T>
  7734. Var JavascriptArray::MapHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, Arguments& args, ScriptContext* scriptContext)
  7735. {
  7736. RecyclableObject* newObj = nullptr;
  7737. JavascriptArray* newArr = nullptr;
  7738. bool isTypedArrayEntryPoint = typedArrayBase != nullptr;
  7739. if (args.Info.Count < 2 || !JavascriptConversion::IsCallable(args[1]))
  7740. {
  7741. if (isTypedArrayEntryPoint)
  7742. {
  7743. JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("[TypedArray].prototype.map"));
  7744. }
  7745. else
  7746. {
  7747. JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("Array.prototype.map"));
  7748. }
  7749. }
  7750. RecyclableObject* callBackFn = RecyclableObject::FromVar(args[1]);
  7751. Var thisArg;
  7752. if (args.Info.Count > 2)
  7753. {
  7754. thisArg = args[2];
  7755. }
  7756. else
  7757. {
  7758. thisArg = scriptContext->GetLibrary()->GetUndefined();
  7759. }
  7760. // If we came from Array.prototype.map and source object is not a JavascriptArray, source could be a TypedArray
  7761. if (!isTypedArrayEntryPoint && pArr == nullptr && TypedArrayBase::Is(obj))
  7762. {
  7763. typedArrayBase = TypedArrayBase::FromVar(obj);
  7764. }
  7765. // 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
  7766. // and use it to construct the return object.
  7767. if (isTypedArrayEntryPoint)
  7768. {
  7769. Var constructor = JavascriptOperators::SpeciesConstructor(
  7770. typedArrayBase, TypedArrayBase::GetDefaultConstructor(args[0], scriptContext), scriptContext);
  7771. if (JavascriptOperators::IsConstructor(constructor))
  7772. {
  7773. Js::Var constructorArgs[] = { constructor, JavascriptNumber::ToVar(length, scriptContext) };
  7774. Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs));
  7775. newObj = RecyclableObject::FromVar(TypedArrayBase::TypedArrayCreate(constructor, &Js::Arguments(constructorCallInfo, constructorArgs), (uint32)length, scriptContext));
  7776. }
  7777. else if (isTypedArrayEntryPoint)
  7778. {
  7779. // We only need to throw a TypeError when the constructor property is not an actual constructor if %TypedArray%.prototype.map was called
  7780. JavascriptError::ThrowTypeError(scriptContext, JSERR_NotAConstructor, _u("[TypedArray].prototype.map"));
  7781. }
  7782. }
  7783. // skip the typed array and "pure" array case, we still need to handle special arrays like es5array, remote array, and proxy of array.
  7784. else if (pArr == nullptr || scriptContext->GetConfig()->IsES6SpeciesEnabled())
  7785. {
  7786. newObj = ArraySpeciesCreate(obj, length, scriptContext);
  7787. }
  7788. if (newObj == nullptr)
  7789. {
  7790. if (length > UINT_MAX)
  7791. {
  7792. JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthConstructIncorrect);
  7793. }
  7794. newArr = scriptContext->GetLibrary()->CreateArray(static_cast<uint32>(length));
  7795. newArr->EnsureHead<Var>();
  7796. newObj = newArr;
  7797. }
  7798. else
  7799. {
  7800. // If the new object we created is an array, remember that as it will save us time setting properties in the object below
  7801. if (JavascriptArray::Is(newObj))
  7802. {
  7803. newArr = JavascriptArray::FromVar(newObj);
  7804. }
  7805. }
  7806. Var element = nullptr;
  7807. Var mappedValue = nullptr;
  7808. // The correct flag value is CallFlags_Value but we pass CallFlags_None in compat modes
  7809. CallFlags callBackFnflags = CallFlags_Value;
  7810. CallInfo callBackFnInfo = CallInfo(callBackFnflags, 4);
  7811. // We at least have to have newObj as a valid object
  7812. Assert(newObj);
  7813. if (pArr != nullptr)
  7814. {
  7815. // If source is a JavascriptArray, newObj may or may not be an array based on what was in source's constructor property
  7816. for (uint32 k = 0; k < length; k++)
  7817. {
  7818. if (!pArr->DirectGetItemAtFull(k, &element))
  7819. {
  7820. continue;
  7821. }
  7822. mappedValue = CALL_FUNCTION(callBackFn, callBackFnInfo, thisArg,
  7823. element,
  7824. JavascriptNumber::ToVar(k, scriptContext),
  7825. pArr);
  7826. // If newArr is a valid pointer, then we constructed an array to return. Otherwise we need to do generic object operations
  7827. if (newArr)
  7828. {
  7829. newArr->DirectSetItemAt(k, mappedValue);
  7830. }
  7831. else
  7832. {
  7833. JavascriptArray::SetArrayLikeObjects(RecyclableObject::FromVar(newObj), k, mappedValue);
  7834. }
  7835. }
  7836. }
  7837. else if (typedArrayBase != nullptr)
  7838. {
  7839. // Source is a TypedArray, we may have tried to call a constructor, but newObj may not be a TypedArray (or an array either)
  7840. TypedArrayBase* newTypedArray = nullptr;
  7841. if (TypedArrayBase::Is(newObj))
  7842. {
  7843. newTypedArray = TypedArrayBase::FromVar(newObj);
  7844. }
  7845. for (uint32 k = 0; k < length; k++)
  7846. {
  7847. // We can't rely on the length value being equal to typedArrayBase->GetLength() because user code may lie and
  7848. // attach any length property to a TypedArray instance and pass it as this parameter when .calling
  7849. // Array.prototype.map.
  7850. if (!typedArrayBase->HasItem(k))
  7851. {
  7852. // We know that if HasItem returns false, all the future calls to HasItem will return false as well since
  7853. // we visit the items in order. We could return early here except that we have to continue calling HasItem
  7854. // on all the subsequent items according to the spec.
  7855. continue;
  7856. }
  7857. element = typedArrayBase->DirectGetItem(k);
  7858. mappedValue = CALL_FUNCTION(callBackFn, callBackFnInfo, thisArg,
  7859. element,
  7860. JavascriptNumber::ToVar(k, scriptContext),
  7861. obj);
  7862. // If newObj is a TypedArray, set the mappedValue directly, otherwise see if it's an array and finally fall back to
  7863. // the normal Set path.
  7864. if (newTypedArray)
  7865. {
  7866. newTypedArray->DirectSetItem(k, mappedValue);
  7867. }
  7868. else if (newArr)
  7869. {
  7870. newArr->DirectSetItemAt(k, mappedValue);
  7871. }
  7872. else
  7873. {
  7874. JavascriptArray::SetArrayLikeObjects(RecyclableObject::FromVar(newObj), k, mappedValue);
  7875. }
  7876. }
  7877. }
  7878. else
  7879. {
  7880. for (uint32 k = 0; k < length; k++)
  7881. {
  7882. if (JavascriptOperators::HasItem(obj, k))
  7883. {
  7884. element = JavascriptOperators::GetItem(obj, k, scriptContext);
  7885. mappedValue = CALL_FUNCTION(callBackFn, callBackFnInfo, thisArg,
  7886. element,
  7887. JavascriptNumber::ToVar(k, scriptContext),
  7888. obj);
  7889. if (newArr)
  7890. {
  7891. newArr->DirectSetItemAt(k, mappedValue);
  7892. }
  7893. else
  7894. {
  7895. JavascriptArray::SetArrayLikeObjects(RecyclableObject::FromVar(newObj), k, mappedValue);
  7896. }
  7897. }
  7898. }
  7899. }
  7900. #ifdef VALIDATE_ARRAY
  7901. if (JavascriptArray::Is(newObj))
  7902. {
  7903. newArr->ValidateArray();
  7904. }
  7905. #endif
  7906. return newObj;
  7907. }
  7908. Var JavascriptArray::EntryFilter(RecyclableObject* function, CallInfo callInfo, ...)
  7909. {
  7910. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  7911. ARGUMENTS(args, callInfo);
  7912. ScriptContext* scriptContext = function->GetScriptContext();
  7913. AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Array.prototype.filter"));
  7914. CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(ArrayFilterCount);
  7915. Assert(!(callInfo.Flags & CallFlags_New));
  7916. if (args.Info.Count == 0)
  7917. {
  7918. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.filter"));
  7919. }
  7920. BigIndex length;
  7921. JavascriptArray* pArr = nullptr;
  7922. RecyclableObject* dynamicObject = nullptr;
  7923. if (JavascriptArray::Is(args[0]) && !JavascriptArray::FromVar(args[0])->IsCrossSiteObject())
  7924. {
  7925. pArr = JavascriptArray::FromVar(args[0]);
  7926. dynamicObject = pArr;
  7927. }
  7928. else
  7929. {
  7930. if (FALSE == JavascriptConversion::ToObject(args[0], scriptContext, &dynamicObject))
  7931. {
  7932. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.filter"));
  7933. }
  7934. }
  7935. // In ES6-mode, we always load the length property from the object instead of using the internal slot.
  7936. // Even for arrays, this is now observable via proxies.
  7937. // If source object is not an array, we fall back to this behavior anyway.
  7938. if (scriptContext->GetConfig()->IsES6TypedArrayExtensionsEnabled() || pArr == nullptr)
  7939. {
  7940. if (scriptContext->GetConfig()->IsES6ToLengthEnabled())
  7941. {
  7942. length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(dynamicObject, scriptContext), scriptContext);
  7943. }
  7944. else
  7945. {
  7946. length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(dynamicObject, scriptContext), scriptContext);
  7947. }
  7948. }
  7949. else
  7950. {
  7951. length = pArr->length;
  7952. }
  7953. if (length.IsSmallIndex())
  7954. {
  7955. return JavascriptArray::FilterHelper(pArr, dynamicObject, length.GetSmallIndex(), args, scriptContext);
  7956. }
  7957. return JavascriptArray::FilterHelper(pArr, dynamicObject, length.GetBigIndex(), args, scriptContext);
  7958. }
  7959. template <typename T>
  7960. Var JavascriptArray::FilterHelper(JavascriptArray* pArr, RecyclableObject* obj, T length, Arguments& args, ScriptContext* scriptContext)
  7961. {
  7962. if (args.Info.Count < 2 || !JavascriptConversion::IsCallable(args[1]))
  7963. {
  7964. JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("Array.prototype.filter"));
  7965. }
  7966. RecyclableObject* callBackFn = RecyclableObject::FromVar(args[1]);
  7967. Var thisArg = nullptr;
  7968. if (args.Info.Count > 2)
  7969. {
  7970. thisArg = args[2];
  7971. }
  7972. else
  7973. {
  7974. thisArg = scriptContext->GetLibrary()->GetUndefined();
  7975. }
  7976. // 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.
  7977. RecyclableObject* newObj = ArraySpeciesCreate(obj, 0, scriptContext);
  7978. JavascriptArray* newArr = nullptr;
  7979. if (newObj == nullptr)
  7980. {
  7981. newArr = scriptContext->GetLibrary()->CreateArray(0);
  7982. newArr->EnsureHead<Var>();
  7983. newObj = newArr;
  7984. }
  7985. else
  7986. {
  7987. // If the new object we created is an array, remember that as it will save us time setting properties in the object below
  7988. if (JavascriptArray::Is(newObj))
  7989. {
  7990. newArr = JavascriptArray::FromVar(newObj);
  7991. }
  7992. }
  7993. Var element = nullptr;
  7994. Var selected = nullptr;
  7995. if (pArr)
  7996. {
  7997. Assert(length <= MaxArrayLength);
  7998. uint32 i = 0;
  7999. for (uint32 k = 0; k < length; k++)
  8000. {
  8001. if (!pArr->DirectGetItemAtFull(k, &element))
  8002. {
  8003. continue;
  8004. }
  8005. selected = callBackFn->GetEntryPoint()(callBackFn, CallInfo(CallFlags_Value, 4), thisArg,
  8006. element,
  8007. JavascriptNumber::ToVar(k, scriptContext),
  8008. pArr);
  8009. if (JavascriptConversion::ToBoolean(selected, scriptContext))
  8010. {
  8011. // Try to fast path if the return object is an array
  8012. if (newArr)
  8013. {
  8014. newArr->DirectSetItemAt(i, element);
  8015. }
  8016. else
  8017. {
  8018. JavascriptArray::SetArrayLikeObjects(newObj, i, element);
  8019. }
  8020. ++i;
  8021. }
  8022. }
  8023. }
  8024. else
  8025. {
  8026. BigIndex i = 0u;
  8027. for (T k = 0; k < length; k++)
  8028. {
  8029. if (JavascriptOperators::HasItem(obj, k))
  8030. {
  8031. element = JavascriptOperators::GetItem(obj, k, scriptContext);
  8032. selected = callBackFn->GetEntryPoint()(callBackFn, CallInfo(CallFlags_Value, 4), thisArg,
  8033. element,
  8034. JavascriptNumber::ToVar(k, scriptContext),
  8035. obj);
  8036. if (JavascriptConversion::ToBoolean(selected, scriptContext))
  8037. {
  8038. if (newArr)
  8039. {
  8040. newArr->DirectSetItemAt(i, element);
  8041. }
  8042. else
  8043. {
  8044. JavascriptArray::SetArrayLikeObjects(newObj, i, element);
  8045. }
  8046. ++i;
  8047. }
  8048. }
  8049. }
  8050. }
  8051. #ifdef VALIDATE_ARRAY
  8052. if (newArr)
  8053. {
  8054. newArr->ValidateArray();
  8055. }
  8056. #endif
  8057. return newObj;
  8058. }
  8059. Var JavascriptArray::EntryReduce(RecyclableObject* function, CallInfo callInfo, ...)
  8060. {
  8061. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  8062. ARGUMENTS(args, callInfo);
  8063. ScriptContext* scriptContext = function->GetScriptContext();
  8064. AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Array.prototype.reduce"));
  8065. CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(ArrayReduceCount);
  8066. Assert(!(callInfo.Flags & CallFlags_New));
  8067. if (args.Info.Count == 0)
  8068. {
  8069. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.reduce"));
  8070. }
  8071. BigIndex length;
  8072. JavascriptArray * pArr = nullptr;
  8073. RecyclableObject* obj = nullptr;
  8074. if (JavascriptArray::Is(args[0]) && !JavascriptArray::FromVar(args[0])->IsCrossSiteObject())
  8075. {
  8076. pArr = JavascriptArray::FromVar(args[0]);
  8077. obj = pArr;
  8078. length = pArr->length;
  8079. }
  8080. else
  8081. {
  8082. if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj))
  8083. {
  8084. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.reduce"));
  8085. }
  8086. if (scriptContext->GetConfig()->IsES6ToLengthEnabled())
  8087. {
  8088. length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext);
  8089. }
  8090. else
  8091. {
  8092. length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext);
  8093. }
  8094. }
  8095. if (length.IsSmallIndex())
  8096. {
  8097. return JavascriptArray::ReduceHelper(pArr, nullptr, obj, length.GetSmallIndex(), args, scriptContext);
  8098. }
  8099. return JavascriptArray::ReduceHelper(pArr, nullptr, obj, length.GetBigIndex(), args, scriptContext);
  8100. }
  8101. // Array.prototype.reduce as described in ES6.0 (draft 22) Section 22.1.3.18
  8102. template <typename T>
  8103. Var JavascriptArray::ReduceHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, Arguments& args, ScriptContext* scriptContext)
  8104. {
  8105. if (args.Info.Count < 2 || !JavascriptConversion::IsCallable(args[1]))
  8106. {
  8107. if (typedArrayBase != nullptr)
  8108. {
  8109. JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("[TypedArray].prototype.reduce"));
  8110. }
  8111. else
  8112. {
  8113. JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("Array.prototype.reduce"));
  8114. }
  8115. }
  8116. // If we came from Array.prototype.reduce and source object is not a JavascriptArray, source could be a TypedArray
  8117. if (typedArrayBase == nullptr && pArr == nullptr && TypedArrayBase::Is(obj))
  8118. {
  8119. typedArrayBase = TypedArrayBase::FromVar(obj);
  8120. }
  8121. T k = 0;
  8122. RecyclableObject* callBackFn = RecyclableObject::FromVar(args[1]);
  8123. Var accumulator = nullptr;
  8124. Var element = nullptr;
  8125. if (args.Info.Count > 2)
  8126. {
  8127. accumulator = args[2];
  8128. }
  8129. else
  8130. {
  8131. if (length == 0)
  8132. {
  8133. JavascriptError::ThrowTypeError(scriptContext, VBSERR_ActionNotSupported);
  8134. }
  8135. bool bPresent = false;
  8136. if (pArr)
  8137. {
  8138. for (; k < length && bPresent == false; k++)
  8139. {
  8140. if (!pArr->DirectGetItemAtFull((uint32)k, &element))
  8141. {
  8142. continue;
  8143. }
  8144. bPresent = true;
  8145. accumulator = element;
  8146. }
  8147. }
  8148. else if (typedArrayBase)
  8149. {
  8150. Assert(length <= UINT_MAX);
  8151. for (; k < length && bPresent == false; k++)
  8152. {
  8153. if (!typedArrayBase->HasItem((uint32)k))
  8154. {
  8155. continue;
  8156. }
  8157. element = typedArrayBase->DirectGetItem((uint32)k);
  8158. bPresent = true;
  8159. accumulator = element;
  8160. }
  8161. }
  8162. else
  8163. {
  8164. for (; k < length && bPresent == false; k++)
  8165. {
  8166. if (JavascriptOperators::HasItem(obj, k))
  8167. {
  8168. accumulator = JavascriptOperators::GetItem(obj, k, scriptContext);
  8169. bPresent = true;
  8170. }
  8171. }
  8172. }
  8173. if (bPresent == false)
  8174. {
  8175. JavascriptError::ThrowTypeError(scriptContext, VBSERR_ActionNotSupported);
  8176. }
  8177. }
  8178. Assert(accumulator);
  8179. Var undefinedValue = scriptContext->GetLibrary()->GetUndefined();
  8180. // The correct flag value is CallFlags_Value but we pass CallFlags_None in compat modes
  8181. CallFlags flags = CallFlags_Value;
  8182. if (pArr)
  8183. {
  8184. for (; k < length; k++)
  8185. {
  8186. if (!pArr->DirectGetItemAtFull((uint32)k, &element))
  8187. {
  8188. continue;
  8189. }
  8190. accumulator = CALL_FUNCTION(callBackFn, CallInfo(flags, 5), undefinedValue,
  8191. accumulator,
  8192. element,
  8193. JavascriptNumber::ToVar(k, scriptContext),
  8194. pArr);
  8195. }
  8196. }
  8197. else if (typedArrayBase)
  8198. {
  8199. Assert(length <= UINT_MAX);
  8200. for (; k < length; k++)
  8201. {
  8202. if (!typedArrayBase->HasItem((uint32)k))
  8203. {
  8204. continue;
  8205. }
  8206. element = typedArrayBase->DirectGetItem((uint32)k);
  8207. accumulator = CALL_FUNCTION(callBackFn, CallInfo(flags, 5), undefinedValue,
  8208. accumulator,
  8209. element,
  8210. JavascriptNumber::ToVar(k, scriptContext),
  8211. typedArrayBase);
  8212. }
  8213. }
  8214. else
  8215. {
  8216. for (; k < length; k++)
  8217. {
  8218. if (JavascriptOperators::HasItem(obj, k))
  8219. {
  8220. element = JavascriptOperators::GetItem(obj, k, scriptContext);
  8221. accumulator = CALL_FUNCTION(callBackFn, CallInfo(flags, 5), undefinedValue,
  8222. accumulator,
  8223. element,
  8224. JavascriptNumber::ToVar(k, scriptContext),
  8225. obj);
  8226. }
  8227. }
  8228. }
  8229. return accumulator;
  8230. }
  8231. Var JavascriptArray::EntryReduceRight(RecyclableObject* function, CallInfo callInfo, ...)
  8232. {
  8233. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  8234. ARGUMENTS(args, callInfo);
  8235. ScriptContext* scriptContext = function->GetScriptContext();
  8236. AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Array.prototype.reduceRight"));
  8237. CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(ArrayReduceRightCount);
  8238. Assert(!(callInfo.Flags & CallFlags_New));
  8239. if (args.Info.Count == 0)
  8240. {
  8241. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.reduceRight"));
  8242. }
  8243. BigIndex length;
  8244. JavascriptArray * pArr = nullptr;
  8245. RecyclableObject* obj = nullptr;
  8246. if (JavascriptArray::Is(args[0]) && !JavascriptArray::FromVar(args[0])->IsCrossSiteObject())
  8247. {
  8248. pArr = JavascriptArray::FromVar(args[0]);
  8249. obj = pArr;
  8250. }
  8251. else
  8252. {
  8253. if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj))
  8254. {
  8255. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.reduceRight"));
  8256. }
  8257. }
  8258. // In ES6-mode, we always load the length property from the object instead of using the internal slot.
  8259. // Even for arrays, this is now observable via proxies.
  8260. // If source object is not an array, we fall back to this behavior anyway.
  8261. if (scriptContext->GetConfig()->IsES6TypedArrayExtensionsEnabled() || pArr == nullptr)
  8262. {
  8263. if (scriptContext->GetConfig()->IsES6ToLengthEnabled())
  8264. {
  8265. length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext);
  8266. }
  8267. else
  8268. {
  8269. length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext);
  8270. }
  8271. }
  8272. else
  8273. {
  8274. length = pArr->length;
  8275. }
  8276. if (length.IsSmallIndex())
  8277. {
  8278. return JavascriptArray::ReduceRightHelper(pArr, nullptr, obj, length.GetSmallIndex(), args, scriptContext);
  8279. }
  8280. return JavascriptArray::ReduceRightHelper(pArr, nullptr, obj, length.GetBigIndex(), args, scriptContext);
  8281. }
  8282. // Array.prototype.reduceRight as described in ES6.0 (draft 22) Section 22.1.3.19
  8283. template <typename T>
  8284. Var JavascriptArray::ReduceRightHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, Arguments& args, ScriptContext* scriptContext)
  8285. {
  8286. if (args.Info.Count < 2 || !JavascriptConversion::IsCallable(args[1]))
  8287. {
  8288. if (typedArrayBase != nullptr)
  8289. {
  8290. JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("[TypedArray].prototype.reduceRight"));
  8291. }
  8292. else
  8293. {
  8294. JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("Array.prototype.reduceRight"));
  8295. }
  8296. }
  8297. // If we came from Array.prototype.reduceRight and source object is not a JavascriptArray, source could be a TypedArray
  8298. if (typedArrayBase == nullptr && pArr == nullptr && TypedArrayBase::Is(obj))
  8299. {
  8300. typedArrayBase = TypedArrayBase::FromVar(obj);
  8301. }
  8302. RecyclableObject* callBackFn = RecyclableObject::FromVar(args[1]);
  8303. Var accumulator = nullptr;
  8304. Var element = nullptr;
  8305. T k = 0;
  8306. T index = 0;
  8307. if (args.Info.Count > 2)
  8308. {
  8309. accumulator = args[2];
  8310. }
  8311. else
  8312. {
  8313. if (length == 0)
  8314. {
  8315. JavascriptError::ThrowTypeError(scriptContext, VBSERR_ActionNotSupported);
  8316. }
  8317. bool bPresent = false;
  8318. if (pArr)
  8319. {
  8320. for (; k < length && bPresent == false; k++)
  8321. {
  8322. index = length - k - 1;
  8323. if (!pArr->DirectGetItemAtFull((uint32)index, &element))
  8324. {
  8325. continue;
  8326. }
  8327. bPresent = true;
  8328. accumulator = element;
  8329. }
  8330. }
  8331. else if (typedArrayBase)
  8332. {
  8333. Assert(length <= UINT_MAX);
  8334. for (; k < length && bPresent == false; k++)
  8335. {
  8336. index = length - k - 1;
  8337. if (!typedArrayBase->HasItem((uint32)index))
  8338. {
  8339. continue;
  8340. }
  8341. element = typedArrayBase->DirectGetItem((uint32)index);
  8342. bPresent = true;
  8343. accumulator = element;
  8344. }
  8345. }
  8346. else
  8347. {
  8348. for (; k < length && bPresent == false; k++)
  8349. {
  8350. index = length - k - 1;
  8351. if (JavascriptOperators::HasItem(obj, index))
  8352. {
  8353. accumulator = JavascriptOperators::GetItem(obj, index, scriptContext);
  8354. bPresent = true;
  8355. }
  8356. }
  8357. }
  8358. if (bPresent == false)
  8359. {
  8360. JavascriptError::ThrowTypeError(scriptContext, VBSERR_ActionNotSupported);
  8361. }
  8362. }
  8363. // The correct flag value is CallFlags_Value but we pass CallFlags_None in compat modes
  8364. CallFlags flags = CallFlags_Value;
  8365. Var undefinedValue = scriptContext->GetLibrary()->GetUndefined();
  8366. if (pArr)
  8367. {
  8368. for (; k < length; k++)
  8369. {
  8370. index = length - k - 1;
  8371. if (!pArr->DirectGetItemAtFull((uint32)index, &element))
  8372. {
  8373. continue;
  8374. }
  8375. accumulator = CALL_FUNCTION(callBackFn, CallInfo(flags, 5), undefinedValue,
  8376. accumulator,
  8377. element,
  8378. JavascriptNumber::ToVar(index, scriptContext),
  8379. pArr);
  8380. }
  8381. }
  8382. else if (typedArrayBase)
  8383. {
  8384. Assert(length <= UINT_MAX);
  8385. for (; k < length; k++)
  8386. {
  8387. index = length - k - 1;
  8388. if (!typedArrayBase->HasItem((uint32) index))
  8389. {
  8390. continue;
  8391. }
  8392. element = typedArrayBase->DirectGetItem((uint32)index);
  8393. accumulator = CALL_FUNCTION(callBackFn, CallInfo(flags, 5), undefinedValue,
  8394. accumulator,
  8395. element,
  8396. JavascriptNumber::ToVar(index, scriptContext),
  8397. typedArrayBase);
  8398. }
  8399. }
  8400. else
  8401. {
  8402. for (; k < length; k++)
  8403. {
  8404. index = length - k - 1;
  8405. if (JavascriptOperators::HasItem(obj, index))
  8406. {
  8407. element = JavascriptOperators::GetItem(obj, index, scriptContext);
  8408. accumulator = CALL_FUNCTION(callBackFn, CallInfo(flags, 5), undefinedValue,
  8409. accumulator,
  8410. element,
  8411. JavascriptNumber::ToVar(index, scriptContext),
  8412. obj);
  8413. }
  8414. }
  8415. }
  8416. return accumulator;
  8417. }
  8418. Var JavascriptArray::EntryFrom(RecyclableObject* function, CallInfo callInfo, ...)
  8419. {
  8420. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  8421. ARGUMENTS(args, callInfo);
  8422. ScriptContext* scriptContext = function->GetScriptContext();
  8423. AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Array.from"));
  8424. Assert(!(callInfo.Flags & CallFlags_New));
  8425. JavascriptLibrary* library = scriptContext->GetLibrary();
  8426. RecyclableObject* constructor = nullptr;
  8427. if (JavascriptOperators::IsConstructor(args[0]))
  8428. {
  8429. constructor = RecyclableObject::FromVar(args[0]);
  8430. }
  8431. RecyclableObject* items = nullptr;
  8432. if (args.Info.Count < 2 || !JavascriptConversion::ToObject(args[1], scriptContext, &items))
  8433. {
  8434. JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedObject, _u("Array.from"));
  8435. }
  8436. JavascriptArray* itemsArr = nullptr;
  8437. if (JavascriptArray::Is(items))
  8438. {
  8439. #if ENABLE_COPYONACCESS_ARRAY
  8440. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(items);
  8441. #endif
  8442. itemsArr = JavascriptArray::FromVar(items);
  8443. }
  8444. bool mapping = false;
  8445. JavascriptFunction* mapFn = nullptr;
  8446. Var mapFnThisArg = nullptr;
  8447. if (args.Info.Count >= 3 && !JavascriptOperators::IsUndefinedObject(args[2]))
  8448. {
  8449. if (!JavascriptFunction::Is(args[2]))
  8450. {
  8451. JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("Array.from"));
  8452. }
  8453. mapFn = JavascriptFunction::FromVar(args[2]);
  8454. if (args.Info.Count >= 4)
  8455. {
  8456. mapFnThisArg = args[3];
  8457. }
  8458. else
  8459. {
  8460. mapFnThisArg = library->GetUndefined();
  8461. }
  8462. mapping = true;
  8463. }
  8464. RecyclableObject* newObj = nullptr;
  8465. JavascriptArray* newArr = nullptr;
  8466. RecyclableObject* iterator = JavascriptOperators::GetIterator(items, scriptContext, true /* optional */);
  8467. if (iterator != nullptr)
  8468. {
  8469. if (constructor)
  8470. {
  8471. Js::Var constructorArgs[] = { constructor };
  8472. Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs));
  8473. newObj = RecyclableObject::FromVar(JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext));
  8474. if (JavascriptArray::Is(newObj))
  8475. {
  8476. newArr = JavascriptArray::FromVar(newObj);
  8477. }
  8478. }
  8479. else
  8480. {
  8481. newArr = scriptContext->GetLibrary()->CreateArray(0);
  8482. newArr->EnsureHead<Var>();
  8483. newObj = newArr;
  8484. }
  8485. uint32 k = 0;
  8486. JavascriptOperators::DoIteratorStepAndValue(iterator, scriptContext, [&](Var nextValue) {
  8487. if (mapping)
  8488. {
  8489. Assert(mapFn != nullptr);
  8490. Assert(mapFnThisArg != nullptr);
  8491. Js::Var mapFnArgs[] = { mapFnThisArg, nextValue, JavascriptNumber::ToVar(k, scriptContext) };
  8492. Js::CallInfo mapFnCallInfo(Js::CallFlags_Value, _countof(mapFnArgs));
  8493. nextValue = mapFn->CallFunction(Js::Arguments(mapFnCallInfo, mapFnArgs));
  8494. }
  8495. if (newArr)
  8496. {
  8497. newArr->DirectSetItemAt(k, nextValue);
  8498. }
  8499. else
  8500. {
  8501. JavascriptArray::SetArrayLikeObjects(RecyclableObject::FromVar(newObj), k, nextValue);
  8502. }
  8503. k++;
  8504. });
  8505. JavascriptOperators::SetProperty(newObj, newObj, Js::PropertyIds::length, JavascriptNumber::ToVar(k, scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible);
  8506. }
  8507. else
  8508. {
  8509. Var lenValue = JavascriptOperators::OP_GetLength(items, scriptContext);
  8510. int64 len = JavascriptConversion::ToLength(lenValue, scriptContext);
  8511. if (constructor)
  8512. {
  8513. Js::Var constructorArgs[] = { constructor, JavascriptNumber::ToVar(len, scriptContext) };
  8514. Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs));
  8515. newObj = RecyclableObject::FromVar(JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext));
  8516. if (JavascriptArray::Is(newObj))
  8517. {
  8518. newArr = JavascriptArray::FromVar(newObj);
  8519. }
  8520. }
  8521. else
  8522. {
  8523. // Abstract operation ArrayCreate throws RangeError if length argument is > 2^32 -1
  8524. if (len > MaxArrayLength)
  8525. {
  8526. JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthAssignIncorrect, _u("Array.from"));
  8527. }
  8528. // Static cast len should be valid (len < 2^32) or we would throw above
  8529. newArr = scriptContext->GetLibrary()->CreateArray(static_cast<uint32>(len));
  8530. newArr->EnsureHead<Var>();
  8531. newObj = newArr;
  8532. }
  8533. uint32 k = 0;
  8534. for ( ; k < len; k++)
  8535. {
  8536. Var kValue;
  8537. if (itemsArr)
  8538. {
  8539. kValue = itemsArr->DirectGetItem(k);
  8540. }
  8541. else
  8542. {
  8543. kValue = JavascriptOperators::OP_GetElementI_UInt32(items, k, scriptContext);
  8544. }
  8545. if (mapping)
  8546. {
  8547. Assert(mapFn != nullptr);
  8548. Assert(mapFnThisArg != nullptr);
  8549. Js::Var mapFnArgs[] = { mapFnThisArg, kValue, JavascriptNumber::ToVar(k, scriptContext) };
  8550. Js::CallInfo mapFnCallInfo(Js::CallFlags_Value, _countof(mapFnArgs));
  8551. kValue = mapFn->CallFunction(Js::Arguments(mapFnCallInfo, mapFnArgs));
  8552. }
  8553. if (newArr)
  8554. {
  8555. newArr->DirectSetItemAt(k, kValue);
  8556. }
  8557. else
  8558. {
  8559. JavascriptArray::SetArrayLikeObjects(RecyclableObject::FromVar(newObj), k, kValue);
  8560. }
  8561. }
  8562. JavascriptOperators::SetProperty(newObj, newObj, Js::PropertyIds::length, JavascriptNumber::ToVar(len, scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible);
  8563. }
  8564. return newObj;
  8565. }
  8566. Var JavascriptArray::EntryOf(RecyclableObject* function, CallInfo callInfo, ...)
  8567. {
  8568. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  8569. ARGUMENTS(args, callInfo);
  8570. ScriptContext* scriptContext = function->GetScriptContext();
  8571. Assert(!(callInfo.Flags & CallFlags_New));
  8572. if (args.Info.Count == 0)
  8573. {
  8574. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.of"));
  8575. }
  8576. return JavascriptArray::OfHelper(false, args, scriptContext);
  8577. }
  8578. Var JavascriptArray::EntryGetterSymbolSpecies(RecyclableObject* function, CallInfo callInfo, ...)
  8579. {
  8580. ARGUMENTS(args, callInfo);
  8581. Assert(args.Info.Count > 0);
  8582. return args[0];
  8583. }
  8584. // Array.of and %TypedArray%.of as described in ES6.0 (draft 22) Section 22.1.2.2 and 22.2.2.2
  8585. Var JavascriptArray::OfHelper(bool isTypedArrayEntryPoint, Arguments& args, ScriptContext* scriptContext)
  8586. {
  8587. Assert(args.Info.Count > 0);
  8588. // args.Info.Count cannot equal zero or we would have thrown above so no chance of underflowing
  8589. uint32 len = args.Info.Count - 1;
  8590. Var newObj = nullptr;
  8591. JavascriptArray* newArr = nullptr;
  8592. TypedArrayBase* newTypedArray = nullptr;
  8593. if (JavascriptOperators::IsConstructor(args[0]))
  8594. {
  8595. RecyclableObject* constructor = RecyclableObject::FromVar(args[0]);
  8596. Js::Var constructorArgs[] = { constructor, JavascriptNumber::ToVar(len, scriptContext) };
  8597. Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs));
  8598. newObj = isTypedArrayEntryPoint ?
  8599. TypedArrayBase::TypedArrayCreate(constructor, &Js::Arguments(constructorCallInfo, constructorArgs), len, scriptContext) :
  8600. JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext);
  8601. // If the new object we created is an array, remember that as it will save us time setting properties in the object below
  8602. if (JavascriptArray::Is(newObj))
  8603. {
  8604. newArr = JavascriptArray::FromVar(newObj);
  8605. }
  8606. else if (TypedArrayBase::Is(newObj))
  8607. {
  8608. newTypedArray = TypedArrayBase::FromVar(newObj);
  8609. }
  8610. }
  8611. else
  8612. {
  8613. // We only throw when the constructor property is not a constructor function in the TypedArray version
  8614. if (isTypedArrayEntryPoint)
  8615. {
  8616. JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedFunction, _u("[TypedArray].of"));
  8617. }
  8618. newArr = scriptContext->GetLibrary()->CreateArray(len);
  8619. newArr->EnsureHead<Var>();
  8620. newObj = newArr;
  8621. }
  8622. // At least we have a new object of some kind
  8623. Assert(newObj);
  8624. if (newArr)
  8625. {
  8626. for (uint32 k = 0; k < len; k++)
  8627. {
  8628. Var kValue = args[k + 1];
  8629. newArr->DirectSetItemAt(k, kValue);
  8630. }
  8631. }
  8632. else if (newTypedArray)
  8633. {
  8634. for (uint32 k = 0; k < len; k++)
  8635. {
  8636. Var kValue = args[k + 1];
  8637. newTypedArray->DirectSetItem(k, kValue);
  8638. }
  8639. }
  8640. else
  8641. {
  8642. for (uint32 k = 0; k < len; k++)
  8643. {
  8644. Var kValue = args[k + 1];
  8645. JavascriptArray::SetArrayLikeObjects(RecyclableObject::FromVar(newObj), k, kValue);
  8646. }
  8647. }
  8648. if (!isTypedArrayEntryPoint)
  8649. {
  8650. // Set length if we are in the Array version of the function
  8651. JavascriptOperators::OP_SetProperty(newObj, Js::PropertyIds::length, JavascriptNumber::ToVar(len, scriptContext), scriptContext, nullptr, PropertyOperation_ThrowIfNotExtensible);
  8652. }
  8653. return newObj;
  8654. }
  8655. JavascriptString* JavascriptArray::ToLocaleStringHelper(Var value, ScriptContext* scriptContext)
  8656. {
  8657. TypeId typeId = JavascriptOperators::GetTypeId(value);
  8658. if (typeId == TypeIds_Null || typeId == TypeIds_Undefined)
  8659. {
  8660. return scriptContext->GetLibrary()->GetEmptyString();
  8661. }
  8662. else
  8663. {
  8664. return JavascriptConversion::ToLocaleString(value, scriptContext);
  8665. }
  8666. }
  8667. inline BOOL JavascriptArray::IsFullArray() const
  8668. {
  8669. if (head && head->length == length)
  8670. {
  8671. AssertMsg(head->next == 0 && head->left == 0, "Invalid Array");
  8672. return true;
  8673. }
  8674. return (0 == length);
  8675. }
  8676. /*
  8677. * IsFillFromPrototypes
  8678. * - Check the array has no missing values and only head segment.
  8679. * - Also ensure if the lengths match.
  8680. */
  8681. bool JavascriptArray::IsFillFromPrototypes()
  8682. {
  8683. return !(this->head->next == nullptr && this->HasNoMissingValues() && this->length == this->head->length);
  8684. }
  8685. // Fill all missing value in the array and fill it from prototype between startIndex and limitIndex
  8686. // typically startIndex = 0 and limitIndex = length. From start of the array till end of the array.
  8687. void JavascriptArray::FillFromPrototypes(uint32 startIndex, uint32 limitIndex)
  8688. {
  8689. if (startIndex >= limitIndex)
  8690. {
  8691. return;
  8692. }
  8693. RecyclableObject* prototype = this->GetPrototype();
  8694. // Fill all missing values by walking through prototype
  8695. while (JavascriptOperators::GetTypeId(prototype) != TypeIds_Null)
  8696. {
  8697. ForEachOwnMissingArrayIndexOfObject(this, nullptr, prototype, startIndex, limitIndex,0, [this](uint32 index, Var value) {
  8698. this->SetItem(index, value, PropertyOperation_None);
  8699. });
  8700. prototype = prototype->GetPrototype();
  8701. }
  8702. #ifdef VALIDATE_ARRAY
  8703. ValidateArray();
  8704. #endif
  8705. }
  8706. //
  8707. // JavascriptArray requires head->left == 0 for fast path Get.
  8708. //
  8709. template<typename T>
  8710. void JavascriptArray::EnsureHeadStartsFromZero(Recycler * recycler)
  8711. {
  8712. if (head == nullptr || head->left != 0)
  8713. {
  8714. // This is used to fix up altered arrays.
  8715. // any SegmentMap would be invalid at this point.
  8716. ClearSegmentMap();
  8717. //
  8718. // We could OOM and throw when allocating new empty head, resulting in a corrupted array. Need
  8719. // some protection here. Save the head and switch this array to EmptySegment. Will be restored
  8720. // correctly if allocating new segment succeeds.
  8721. //
  8722. SparseArraySegment<T>* savedHead = (SparseArraySegment<T>*)this->head;
  8723. SparseArraySegment<T>* savedLastUsedSegment = (SparseArraySegment<T>*)this->GetLastUsedSegment();
  8724. SetHeadAndLastUsedSegment(const_cast<SparseArraySegmentBase*>(EmptySegment));
  8725. SparseArraySegment<T> *newSeg = SparseArraySegment<T>::AllocateSegment(recycler, 0, 0, savedHead);
  8726. newSeg->next = savedHead;
  8727. this->head = newSeg;
  8728. SetHasNoMissingValues();
  8729. this->SetLastUsedSegment(savedLastUsedSegment);
  8730. }
  8731. }
  8732. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  8733. void JavascriptArray::CheckForceES5Array()
  8734. {
  8735. if (Configuration::Global.flags.ForceES5Array)
  8736. {
  8737. // There's a bad interaction with the jitted code for native array creation here.
  8738. // ForceES5Array doesn't interact well with native arrays
  8739. if (PHASE_OFF1(NativeArrayPhase))
  8740. {
  8741. GetTypeHandler()->ConvertToTypeWithItemAttributes(this);
  8742. }
  8743. }
  8744. }
  8745. #endif
  8746. template <typename Fn>
  8747. void JavascriptArray::ForEachOwnArrayIndexOfObject(RecyclableObject* obj, uint32 startIndex, uint32 limitIndex, Fn fn)
  8748. {
  8749. Assert(DynamicObject::IsAnyArray(obj) || JavascriptOperators::IsObject(obj));
  8750. JavascriptArray* arr = nullptr;
  8751. if (DynamicObject::IsAnyArray(obj))
  8752. {
  8753. arr = JavascriptArray::FromAnyArray(obj);
  8754. }
  8755. else if (DynamicType::Is(obj->GetTypeId()))
  8756. {
  8757. DynamicObject* dynobj = DynamicObject::FromVar(obj);
  8758. arr = dynobj->GetObjectArray();
  8759. }
  8760. if (arr != nullptr)
  8761. {
  8762. if (JavascriptArray::Is(arr))
  8763. {
  8764. ArrayElementEnumerator e(arr, startIndex, limitIndex);
  8765. while(e.MoveNext<Var>())
  8766. {
  8767. fn(e.GetIndex(), e.GetItem<Var>());
  8768. }
  8769. }
  8770. else
  8771. {
  8772. ScriptContext* scriptContext = obj->GetScriptContext();
  8773. Assert(ES5Array::Is(arr));
  8774. ES5Array* es5Array = ES5Array::FromVar(arr);
  8775. ES5ArrayIndexEnumerator<true> e(es5Array);
  8776. while (e.MoveNext())
  8777. {
  8778. uint32 index = e.GetIndex();
  8779. if (index < startIndex) continue;
  8780. else if (index >= limitIndex) break;
  8781. Var value;
  8782. BOOL success = JavascriptOperators::GetOwnItem(es5Array, index, &value, scriptContext);
  8783. Assert(success);
  8784. fn(index, value);
  8785. }
  8786. }
  8787. }
  8788. }
  8789. template <typename T, typename Fn>
  8790. void JavascriptArray::ForEachOwnMissingArrayIndexOfObject(JavascriptArray *baseArray, JavascriptArray *destArray, RecyclableObject* obj, uint32 startIndex, uint32 limitIndex, T destIndex, Fn fn)
  8791. {
  8792. Assert(DynamicObject::IsAnyArray(obj) || JavascriptOperators::IsObject(obj));
  8793. Var oldValue;
  8794. JavascriptArray* arr = nullptr;
  8795. if (DynamicObject::IsAnyArray(obj))
  8796. {
  8797. arr = JavascriptArray::FromAnyArray(obj);
  8798. }
  8799. else if (DynamicType::Is(obj->GetTypeId()))
  8800. {
  8801. DynamicObject* dynobj = DynamicObject::FromVar(obj);
  8802. ArrayObject* objectArray = dynobj->GetObjectArray();
  8803. arr = (objectArray && JavascriptArray::IsAnyArray(objectArray)) ? JavascriptArray::FromAnyArray(objectArray) : nullptr;
  8804. }
  8805. if (arr != nullptr)
  8806. {
  8807. if (JavascriptArray::Is(arr))
  8808. {
  8809. ArrayElementEnumerator e(arr, startIndex, limitIndex);
  8810. while(e.MoveNext<Var>())
  8811. {
  8812. uint32 index = e.GetIndex();
  8813. if (!baseArray->DirectGetVarItemAt(index, &oldValue, baseArray->GetScriptContext()))
  8814. {
  8815. T n = destIndex + (index - startIndex);
  8816. if (destArray == nullptr || !destArray->DirectGetItemAt(n, &oldValue))
  8817. {
  8818. fn(index, e.GetItem<Var>());
  8819. }
  8820. }
  8821. }
  8822. }
  8823. else
  8824. {
  8825. ScriptContext* scriptContext = obj->GetScriptContext();
  8826. Assert(ES5Array::Is(arr));
  8827. ES5Array* es5Array = ES5Array::FromVar(arr);
  8828. ES5ArrayIndexEnumerator<true> e(es5Array);
  8829. while (e.MoveNext())
  8830. {
  8831. uint32 index = e.GetIndex();
  8832. if (index < startIndex) continue;
  8833. else if (index >= limitIndex) break;
  8834. if (!baseArray->DirectGetVarItemAt(index, &oldValue, baseArray->GetScriptContext()))
  8835. {
  8836. T n = destIndex + (index - startIndex);
  8837. if (destArray == nullptr || !destArray->DirectGetItemAt(n, &oldValue))
  8838. {
  8839. Var value;
  8840. BOOL success = JavascriptOperators::GetOwnItem(es5Array, index, &value, scriptContext);
  8841. Assert(success);
  8842. fn(index, value);
  8843. }
  8844. }
  8845. }
  8846. }
  8847. }
  8848. }
  8849. //
  8850. // ArrayElementEnumerator to enumerate array elements (not including elements from prototypes).
  8851. //
  8852. JavascriptArray::ArrayElementEnumerator::ArrayElementEnumerator(JavascriptArray* arr, uint32 start, uint32 end)
  8853. : start(start), end(min(end, arr->length))
  8854. {
  8855. Init(arr);
  8856. }
  8857. //
  8858. // Initialize this enumerator and prepare for the first MoveNext.
  8859. //
  8860. void JavascriptArray::ArrayElementEnumerator::Init(JavascriptArray* arr)
  8861. {
  8862. // Find start segment
  8863. seg = (arr ? arr->GetBeginLookupSegment(start) : nullptr);
  8864. while (seg && (seg->left + seg->length <= start))
  8865. {
  8866. seg = seg->next;
  8867. }
  8868. // Set start index and endIndex
  8869. if (seg)
  8870. {
  8871. if (seg->left >= end)
  8872. {
  8873. seg = nullptr;
  8874. }
  8875. else
  8876. {
  8877. // set index to be at target index - 1, so MoveNext will move to target
  8878. index = max(seg->left, start) - seg->left - 1;
  8879. endIndex = min(end - seg->left, seg->length);
  8880. }
  8881. }
  8882. }
  8883. //
  8884. // Move to the next element if available.
  8885. //
  8886. template<typename T>
  8887. inline bool JavascriptArray::ArrayElementEnumerator::MoveNext()
  8888. {
  8889. while (seg)
  8890. {
  8891. // Look for next non-null item in current segment
  8892. while (++index < endIndex)
  8893. {
  8894. if (!SparseArraySegment<T>::IsMissingItem(&((SparseArraySegment<T>*)seg)->elements[index]))
  8895. {
  8896. return true;
  8897. }
  8898. }
  8899. // Move to next segment
  8900. seg = seg->next;
  8901. if (seg)
  8902. {
  8903. if (seg->left >= end)
  8904. {
  8905. seg = nullptr;
  8906. break;
  8907. }
  8908. else
  8909. {
  8910. index = static_cast<uint32>(-1);
  8911. endIndex = min(end - seg->left, seg->length);
  8912. }
  8913. }
  8914. }
  8915. return false;
  8916. }
  8917. //
  8918. // Get current array element index.
  8919. //
  8920. uint32 JavascriptArray::ArrayElementEnumerator::GetIndex() const
  8921. {
  8922. Assert(seg && index < seg->length && index < endIndex);
  8923. return seg->left + index;
  8924. }
  8925. //
  8926. // Get current array element value.
  8927. //
  8928. template<typename T>
  8929. T JavascriptArray::ArrayElementEnumerator::GetItem() const
  8930. {
  8931. Assert(seg && index < seg->length && index < endIndex &&
  8932. !SparseArraySegment<T>::IsMissingItem(&((SparseArraySegment<T>*)seg)->elements[index]));
  8933. return ((SparseArraySegment<T>*)seg)->elements[index];
  8934. }
  8935. //
  8936. // Construct a BigIndex initialized to a given uint32 (small index).
  8937. //
  8938. JavascriptArray::BigIndex::BigIndex(uint32 initIndex)
  8939. : index(initIndex), bigIndex(InvalidIndex)
  8940. {
  8941. //ok if initIndex == InvalidIndex
  8942. }
  8943. //
  8944. // Construct a BigIndex initialized to a given uint64 (large or small index).
  8945. //
  8946. JavascriptArray::BigIndex::BigIndex(uint64 initIndex)
  8947. : index(InvalidIndex), bigIndex(initIndex)
  8948. {
  8949. if (bigIndex < InvalidIndex) // if it's actually small index
  8950. {
  8951. index = static_cast<uint32>(bigIndex);
  8952. bigIndex = InvalidIndex;
  8953. }
  8954. }
  8955. bool JavascriptArray::BigIndex::IsUint32Max() const
  8956. {
  8957. return index == InvalidIndex && bigIndex == InvalidIndex;
  8958. }
  8959. bool JavascriptArray::BigIndex::IsSmallIndex() const
  8960. {
  8961. return index < InvalidIndex;
  8962. }
  8963. uint32 JavascriptArray::BigIndex::GetSmallIndex() const
  8964. {
  8965. Assert(IsSmallIndex());
  8966. return index;
  8967. }
  8968. uint64 JavascriptArray::BigIndex::GetBigIndex() const
  8969. {
  8970. Assert(!IsSmallIndex());
  8971. return bigIndex;
  8972. }
  8973. //
  8974. // Convert this index value to a JS number
  8975. //
  8976. Var JavascriptArray::BigIndex::ToNumber(ScriptContext* scriptContext) const
  8977. {
  8978. if (IsSmallIndex())
  8979. {
  8980. return small_index::ToNumber(index, scriptContext);
  8981. }
  8982. else
  8983. {
  8984. return JavascriptNumber::ToVar(bigIndex, scriptContext);
  8985. }
  8986. }
  8987. //
  8988. // Increment this index by 1.
  8989. //
  8990. const JavascriptArray::BigIndex& JavascriptArray::BigIndex::operator++()
  8991. {
  8992. if (IsSmallIndex())
  8993. {
  8994. ++index;
  8995. // If index reaches InvalidIndex, we will start to use bigIndex which is initially InvalidIndex.
  8996. }
  8997. else
  8998. {
  8999. bigIndex = bigIndex + 1;
  9000. }
  9001. return *this;
  9002. }
  9003. //
  9004. // Decrement this index by 1.
  9005. //
  9006. const JavascriptArray::BigIndex& JavascriptArray::BigIndex::operator--()
  9007. {
  9008. if (IsSmallIndex())
  9009. {
  9010. --index;
  9011. }
  9012. else
  9013. {
  9014. Assert(index == InvalidIndex && bigIndex >= InvalidIndex);
  9015. --bigIndex;
  9016. if (bigIndex < InvalidIndex)
  9017. {
  9018. index = InvalidIndex - 1;
  9019. bigIndex = InvalidIndex;
  9020. }
  9021. }
  9022. return *this;
  9023. }
  9024. JavascriptArray::BigIndex JavascriptArray::BigIndex::operator+(const BigIndex& delta) const
  9025. {
  9026. if (delta.IsSmallIndex())
  9027. {
  9028. return operator+(delta.GetSmallIndex());
  9029. }
  9030. if (IsSmallIndex())
  9031. {
  9032. return index + delta.GetBigIndex();
  9033. }
  9034. return bigIndex + delta.GetBigIndex();
  9035. }
  9036. //
  9037. // Get a new BigIndex representing this + delta.
  9038. //
  9039. JavascriptArray::BigIndex JavascriptArray::BigIndex::operator+(uint32 delta) const
  9040. {
  9041. if (IsSmallIndex())
  9042. {
  9043. uint32 newIndex;
  9044. if (UInt32Math::Add(index, delta, &newIndex))
  9045. {
  9046. return static_cast<uint64>(index) + static_cast<uint64>(delta);
  9047. }
  9048. else
  9049. {
  9050. return newIndex; // ok if newIndex == InvalidIndex
  9051. }
  9052. }
  9053. else
  9054. {
  9055. return bigIndex + static_cast<uint64>(delta);
  9056. }
  9057. }
  9058. bool JavascriptArray::BigIndex::operator==(const BigIndex& rhs) const
  9059. {
  9060. if (rhs.IsSmallIndex() && this->IsSmallIndex())
  9061. {
  9062. return this->GetSmallIndex() == rhs.GetSmallIndex();
  9063. }
  9064. else if (rhs.IsSmallIndex() && !this->IsSmallIndex())
  9065. {
  9066. // if lhs is big promote rhs
  9067. return this->GetBigIndex() == (uint64) rhs.GetSmallIndex();
  9068. }
  9069. else if (!rhs.IsSmallIndex() && this->IsSmallIndex())
  9070. {
  9071. // if rhs is big promote lhs
  9072. return ((uint64)this->GetSmallIndex()) == rhs.GetBigIndex();
  9073. }
  9074. return this->GetBigIndex() == rhs.GetBigIndex();
  9075. }
  9076. bool JavascriptArray::BigIndex::operator> (const BigIndex& rhs) const
  9077. {
  9078. if (rhs.IsSmallIndex() && this->IsSmallIndex())
  9079. {
  9080. return this->GetSmallIndex() > rhs.GetSmallIndex();
  9081. }
  9082. else if (rhs.IsSmallIndex() && !this->IsSmallIndex())
  9083. {
  9084. // if lhs is big promote rhs
  9085. return this->GetBigIndex() > (uint64)rhs.GetSmallIndex();
  9086. }
  9087. else if (!rhs.IsSmallIndex() && this->IsSmallIndex())
  9088. {
  9089. // if rhs is big promote lhs
  9090. return ((uint64)this->GetSmallIndex()) > rhs.GetBigIndex();
  9091. }
  9092. return this->GetBigIndex() > rhs.GetBigIndex();
  9093. }
  9094. bool JavascriptArray::BigIndex::operator< (const BigIndex& rhs) const
  9095. {
  9096. if (rhs.IsSmallIndex() && this->IsSmallIndex())
  9097. {
  9098. return this->GetSmallIndex() < rhs.GetSmallIndex();
  9099. }
  9100. else if (rhs.IsSmallIndex() && !this->IsSmallIndex())
  9101. {
  9102. // if lhs is big promote rhs
  9103. return this->GetBigIndex() < (uint64)rhs.GetSmallIndex();
  9104. }
  9105. else if (!rhs.IsSmallIndex() && this->IsSmallIndex())
  9106. {
  9107. // if rhs is big promote lhs
  9108. return ((uint64)this->GetSmallIndex()) < rhs.GetBigIndex();
  9109. }
  9110. return this->GetBigIndex() < rhs.GetBigIndex();
  9111. }
  9112. bool JavascriptArray::BigIndex::operator<=(const BigIndex& rhs) const
  9113. {
  9114. if (rhs.IsSmallIndex() && this->IsSmallIndex())
  9115. {
  9116. return this->GetSmallIndex() <= rhs.GetSmallIndex();
  9117. }
  9118. else if (rhs.IsSmallIndex() && !this->IsSmallIndex())
  9119. {
  9120. // if lhs is big promote rhs
  9121. return this->GetBigIndex() <= (uint64)rhs.GetSmallIndex();
  9122. }
  9123. else if (!rhs.IsSmallIndex() && !this->IsSmallIndex())
  9124. {
  9125. // if rhs is big promote lhs
  9126. return ((uint64)this->GetSmallIndex()) <= rhs.GetBigIndex();
  9127. }
  9128. return this->GetBigIndex() <= rhs.GetBigIndex();
  9129. }
  9130. bool JavascriptArray::BigIndex::operator>=(const BigIndex& rhs) const
  9131. {
  9132. if (rhs.IsSmallIndex() && this->IsSmallIndex())
  9133. {
  9134. return this->GetSmallIndex() >= rhs.GetSmallIndex();
  9135. }
  9136. else if (rhs.IsSmallIndex() && !this->IsSmallIndex())
  9137. {
  9138. // if lhs is big promote rhs
  9139. return this->GetBigIndex() >= (uint64)rhs.GetSmallIndex();
  9140. }
  9141. else if (!rhs.IsSmallIndex() && this->IsSmallIndex())
  9142. {
  9143. // if rhs is big promote lhs
  9144. return ((uint64)this->GetSmallIndex()) >= rhs.GetBigIndex();
  9145. }
  9146. return this->GetBigIndex() >= rhs.GetBigIndex();
  9147. }
  9148. BOOL JavascriptArray::BigIndex::GetItem(JavascriptArray* arr, Var* outVal) const
  9149. {
  9150. if (IsSmallIndex())
  9151. {
  9152. return small_index::GetItem(arr, index, outVal);
  9153. }
  9154. else
  9155. {
  9156. ScriptContext* scriptContext = arr->GetScriptContext();
  9157. PropertyRecord const * propertyRecord;
  9158. JavascriptOperators::GetPropertyIdForInt(bigIndex, scriptContext, &propertyRecord);
  9159. return arr->GetProperty(arr, propertyRecord->GetPropertyId(), outVal, NULL, scriptContext);
  9160. }
  9161. }
  9162. BOOL JavascriptArray::BigIndex::SetItem(JavascriptArray* arr, Var newValue) const
  9163. {
  9164. if (IsSmallIndex())
  9165. {
  9166. return small_index::SetItem(arr, index, newValue);
  9167. }
  9168. else
  9169. {
  9170. ScriptContext* scriptContext = arr->GetScriptContext();
  9171. PropertyRecord const * propertyRecord;
  9172. JavascriptOperators::GetPropertyIdForInt(bigIndex, scriptContext, &propertyRecord);
  9173. return arr->SetProperty(propertyRecord->GetPropertyId(), newValue, PropertyOperation_None, NULL);
  9174. }
  9175. }
  9176. void JavascriptArray::BigIndex::SetItemIfNotExist(JavascriptArray* arr, Var newValue) const
  9177. {
  9178. if (IsSmallIndex())
  9179. {
  9180. small_index::SetItemIfNotExist(arr, index, newValue);
  9181. }
  9182. else
  9183. {
  9184. ScriptContext* scriptContext = arr->GetScriptContext();
  9185. PropertyRecord const * propertyRecord;
  9186. JavascriptOperators::GetPropertyIdForInt(bigIndex, scriptContext, &propertyRecord);
  9187. Var oldValue;
  9188. PropertyId propertyId = propertyRecord->GetPropertyId();
  9189. if (!arr->GetProperty(arr, propertyId, &oldValue, NULL, scriptContext))
  9190. {
  9191. arr->SetProperty(propertyId, newValue, PropertyOperation_None, NULL);
  9192. }
  9193. }
  9194. }
  9195. BOOL JavascriptArray::BigIndex::DeleteItem(JavascriptArray* arr) const
  9196. {
  9197. if (IsSmallIndex())
  9198. {
  9199. return small_index::DeleteItem(arr, index);
  9200. }
  9201. else
  9202. {
  9203. ScriptContext* scriptContext = arr->GetScriptContext();
  9204. PropertyRecord const * propertyRecord;
  9205. JavascriptOperators::GetPropertyIdForInt(bigIndex, scriptContext, &propertyRecord);
  9206. return arr->DeleteProperty(propertyRecord->GetPropertyId(), PropertyOperation_None);
  9207. }
  9208. }
  9209. BOOL JavascriptArray::BigIndex::SetItem(RecyclableObject* obj, Var newValue, PropertyOperationFlags flags) const
  9210. {
  9211. if (IsSmallIndex())
  9212. {
  9213. return small_index::SetItem(obj, index, newValue, flags);
  9214. }
  9215. else
  9216. {
  9217. ScriptContext* scriptContext = obj->GetScriptContext();
  9218. PropertyRecord const * propertyRecord;
  9219. JavascriptOperators::GetPropertyIdForInt(bigIndex, scriptContext, &propertyRecord);
  9220. return JavascriptOperators::SetProperty(obj, obj, propertyRecord->GetPropertyId(), newValue, scriptContext, flags);
  9221. }
  9222. }
  9223. BOOL JavascriptArray::BigIndex::DeleteItem(RecyclableObject* obj, PropertyOperationFlags flags) const
  9224. {
  9225. if (IsSmallIndex())
  9226. {
  9227. return small_index::DeleteItem(obj, index, flags);
  9228. }
  9229. else
  9230. {
  9231. PropertyRecord const * propertyRecord;
  9232. JavascriptOperators::GetPropertyIdForInt(bigIndex, obj->GetScriptContext(), &propertyRecord);
  9233. return JavascriptOperators::DeleteProperty(obj, propertyRecord->GetPropertyId(), flags);
  9234. }
  9235. }
  9236. //
  9237. // Truncate the array at start and clone the truncated span as properties starting at dstIndex (asserting dstIndex >= MaxArrayLength).
  9238. //
  9239. void JavascriptArray::TruncateToProperties(const BigIndex& dstIndex, uint32 start)
  9240. {
  9241. Assert(!dstIndex.IsSmallIndex());
  9242. typedef IndexTrace<BigIndex> index_trace;
  9243. BigIndex dst = dstIndex;
  9244. uint32 i = start;
  9245. ArrayElementEnumerator e(this, start);
  9246. while(e.MoveNext<Var>())
  9247. {
  9248. // delete all items not enumerated
  9249. while (i < e.GetIndex())
  9250. {
  9251. index_trace::DeleteItem(this, dst);
  9252. ++i;
  9253. ++dst;
  9254. }
  9255. // Copy over the item
  9256. index_trace::SetItem(this, dst, e.GetItem<Var>());
  9257. ++i;
  9258. ++dst;
  9259. }
  9260. // Delete the rest till length
  9261. while (i < this->length)
  9262. {
  9263. index_trace::DeleteItem(this, dst);
  9264. ++i;
  9265. ++dst;
  9266. }
  9267. // Elements moved, truncate the array at start
  9268. SetLength(start);
  9269. }
  9270. //
  9271. // Copy a srcArray elements (including elements from prototypes) to a dstArray starting from an index.
  9272. //
  9273. template<typename T>
  9274. void JavascriptArray::InternalCopyArrayElements(JavascriptArray* dstArray, const T& dstIndex, JavascriptArray* srcArray, uint32 start, uint32 end)
  9275. {
  9276. Assert(start < end && end <= srcArray->length);
  9277. uint32 count = 0;
  9278. // iterate on the array itself
  9279. ArrayElementEnumerator e(srcArray, start, end);
  9280. while(e.MoveNext<Var>())
  9281. {
  9282. T n = dstIndex + (e.GetIndex() - start);
  9283. dstArray->DirectSetItemAt(n, e.GetItem<Var>());
  9284. count++;
  9285. }
  9286. // iterate on the array's prototypes only if not all elements found
  9287. if (start + count != end)
  9288. {
  9289. InternalFillFromPrototype(dstArray, dstIndex, srcArray, start, end, count);
  9290. }
  9291. }
  9292. //
  9293. // Copy a srcArray elements (including elements from prototypes) to a dstArray starting from an index. If the index grows larger than
  9294. // "array index", it will automatically turn to SetProperty using the index as property name.
  9295. //
  9296. void JavascriptArray::CopyArrayElements(JavascriptArray* dstArray, const BigIndex& dstIndex, JavascriptArray* srcArray, uint32 start, uint32 end)
  9297. {
  9298. end = min(end, srcArray->length);
  9299. if (start < end)
  9300. {
  9301. uint32 len = end - start;
  9302. if (dstIndex.IsSmallIndex() && (len < MaxArrayLength - dstIndex.GetSmallIndex()))
  9303. {
  9304. // Won't overflow, use faster small_index version
  9305. InternalCopyArrayElements(dstArray, dstIndex.GetSmallIndex(), srcArray, start, end);
  9306. }
  9307. else
  9308. {
  9309. InternalCopyArrayElements(dstArray, dstIndex, srcArray, start, end);
  9310. }
  9311. }
  9312. }
  9313. //
  9314. // Faster small_index overload of CopyArrayElements, asserting the uint32 dstIndex won't overflow.
  9315. //
  9316. void JavascriptArray::CopyArrayElements(JavascriptArray* dstArray, uint32 dstIndex, JavascriptArray* srcArray, uint32 start, uint32 end)
  9317. {
  9318. end = min(end, srcArray->length);
  9319. if (start < end)
  9320. {
  9321. Assert(end - start <= MaxArrayLength - dstIndex);
  9322. InternalCopyArrayElements(dstArray, dstIndex, srcArray, start, end);
  9323. }
  9324. }
  9325. template <typename T>
  9326. void JavascriptArray::CopyAnyArrayElementsToVar(JavascriptArray* dstArray, T dstIndex, JavascriptArray* srcArray, uint32 start, uint32 end)
  9327. {
  9328. #if ENABLE_COPYONACCESS_ARRAY
  9329. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(srcArray);
  9330. #endif
  9331. #if ENABLE_COPYONACCESS_ARRAY
  9332. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(dstArray);
  9333. #endif
  9334. if (JavascriptNativeIntArray::Is(srcArray))
  9335. {
  9336. CopyNativeIntArrayElementsToVar(dstArray, dstIndex, JavascriptNativeIntArray::FromVar(srcArray), start, end);
  9337. }
  9338. else if (JavascriptNativeFloatArray::Is(srcArray))
  9339. {
  9340. CopyNativeFloatArrayElementsToVar(dstArray, dstIndex, JavascriptNativeFloatArray::FromVar(srcArray), start, end);
  9341. }
  9342. else
  9343. {
  9344. CopyArrayElements(dstArray, dstIndex, srcArray, start, end);
  9345. }
  9346. }
  9347. void JavascriptArray::CopyNativeIntArrayElementsToVar(JavascriptArray* dstArray, const BigIndex& dstIndex, JavascriptNativeIntArray* srcArray, uint32 start, uint32 end)
  9348. {
  9349. end = min(end, srcArray->length);
  9350. if (start < end)
  9351. {
  9352. uint32 len = end - start;
  9353. if (dstIndex.IsSmallIndex() && (len < MaxArrayLength - dstIndex.GetSmallIndex()))
  9354. {
  9355. // Won't overflow, use faster small_index version
  9356. InternalCopyNativeIntArrayElements(dstArray, dstIndex.GetSmallIndex(), srcArray, start, end);
  9357. }
  9358. else
  9359. {
  9360. InternalCopyNativeIntArrayElements(dstArray, dstIndex, srcArray, start, end);
  9361. }
  9362. }
  9363. }
  9364. //
  9365. // Faster small_index overload of CopyArrayElements, asserting the uint32 dstIndex won't overflow.
  9366. //
  9367. void JavascriptArray::CopyNativeIntArrayElementsToVar(JavascriptArray* dstArray, uint32 dstIndex, JavascriptNativeIntArray* srcArray, uint32 start, uint32 end)
  9368. {
  9369. end = min(end, srcArray->length);
  9370. if (start < end)
  9371. {
  9372. Assert(end - start <= MaxArrayLength - dstIndex);
  9373. InternalCopyNativeIntArrayElements(dstArray, dstIndex, srcArray, start, end);
  9374. }
  9375. }
  9376. bool JavascriptArray::CopyNativeIntArrayElements(JavascriptNativeIntArray* dstArray, uint32 dstIndex, JavascriptNativeIntArray* srcArray, uint32 start, uint32 end)
  9377. {
  9378. end = min(end, srcArray->length);
  9379. if (start >= end)
  9380. {
  9381. return false;
  9382. }
  9383. Assert(end - start <= MaxArrayLength - dstIndex);
  9384. Assert(start < end && end <= srcArray->length);
  9385. uint32 count = 0;
  9386. // iterate on the array itself
  9387. ArrayElementEnumerator e(srcArray, start, end);
  9388. while(e.MoveNext<int32>())
  9389. {
  9390. uint n = dstIndex + (e.GetIndex() - start);
  9391. dstArray->DirectSetItemAt(n, e.GetItem<int32>());
  9392. count++;
  9393. }
  9394. // iterate on the array's prototypes only if not all elements found
  9395. if (start + count != end)
  9396. {
  9397. JavascriptArray *varArray = JavascriptNativeIntArray::ToVarArray(dstArray);
  9398. InternalFillFromPrototype(varArray, dstIndex, srcArray, start, end, count);
  9399. return true;
  9400. }
  9401. return false;
  9402. }
  9403. bool JavascriptArray::CopyNativeIntArrayElementsToFloat(JavascriptNativeFloatArray* dstArray, uint32 dstIndex, JavascriptNativeIntArray* srcArray, uint32 start, uint32 end)
  9404. {
  9405. end = min(end, srcArray->length);
  9406. if (start >= end)
  9407. {
  9408. return false;
  9409. }
  9410. Assert(end - start <= MaxArrayLength - dstIndex);
  9411. Assert(start < end && end <= srcArray->length);
  9412. uint32 count = 0;
  9413. // iterate on the array itself
  9414. ArrayElementEnumerator e(srcArray, start, end);
  9415. while(e.MoveNext<int32>())
  9416. {
  9417. uint n = dstIndex + (e.GetIndex() - start);
  9418. dstArray->DirectSetItemAt(n, (double)e.GetItem<int32>());
  9419. count++;
  9420. }
  9421. // iterate on the array's prototypes only if not all elements found
  9422. if (start + count != end)
  9423. {
  9424. JavascriptArray *varArray = JavascriptNativeFloatArray::ToVarArray(dstArray);
  9425. InternalFillFromPrototype(varArray, dstIndex, srcArray, start, end, count);
  9426. return true;
  9427. }
  9428. return false;
  9429. }
  9430. void JavascriptArray::CopyNativeFloatArrayElementsToVar(JavascriptArray* dstArray, const BigIndex& dstIndex, JavascriptNativeFloatArray* srcArray, uint32 start, uint32 end)
  9431. {
  9432. end = min(end, srcArray->length);
  9433. if (start < end)
  9434. {
  9435. uint32 len = end - start;
  9436. if (dstIndex.IsSmallIndex() && (len < MaxArrayLength - dstIndex.GetSmallIndex()))
  9437. {
  9438. // Won't overflow, use faster small_index version
  9439. InternalCopyNativeFloatArrayElements(dstArray, dstIndex.GetSmallIndex(), srcArray, start, end);
  9440. }
  9441. else
  9442. {
  9443. InternalCopyNativeFloatArrayElements(dstArray, dstIndex, srcArray, start, end);
  9444. }
  9445. }
  9446. }
  9447. //
  9448. // Faster small_index overload of CopyArrayElements, asserting the uint32 dstIndex won't overflow.
  9449. //
  9450. void JavascriptArray::CopyNativeFloatArrayElementsToVar(JavascriptArray* dstArray, uint32 dstIndex, JavascriptNativeFloatArray* srcArray, uint32 start, uint32 end)
  9451. {
  9452. end = min(end, srcArray->length);
  9453. if (start < end)
  9454. {
  9455. Assert(end - start <= MaxArrayLength - dstIndex);
  9456. InternalCopyNativeFloatArrayElements(dstArray, dstIndex, srcArray, start, end);
  9457. }
  9458. }
  9459. bool JavascriptArray::CopyNativeFloatArrayElements(JavascriptNativeFloatArray* dstArray, uint32 dstIndex, JavascriptNativeFloatArray* srcArray, uint32 start, uint32 end)
  9460. {
  9461. end = min(end, srcArray->length);
  9462. if (start >= end)
  9463. {
  9464. return false;
  9465. }
  9466. Assert(end - start <= MaxArrayLength - dstIndex);
  9467. Assert(start < end && end <= srcArray->length);
  9468. uint32 count = 0;
  9469. // iterate on the array itself
  9470. ArrayElementEnumerator e(srcArray, start, end);
  9471. while(e.MoveNext<double>())
  9472. {
  9473. uint n = dstIndex + (e.GetIndex() - start);
  9474. dstArray->DirectSetItemAt(n, e.GetItem<double>());
  9475. count++;
  9476. }
  9477. // iterate on the array's prototypes only if not all elements found
  9478. if (start + count != end)
  9479. {
  9480. JavascriptArray *varArray = JavascriptNativeFloatArray::ToVarArray(dstArray);
  9481. InternalFillFromPrototype(varArray, dstIndex, srcArray, start, end, count);
  9482. return true;
  9483. }
  9484. return false;
  9485. }
  9486. JavascriptArray *JavascriptArray::EnsureNonNativeArray(JavascriptArray *arr)
  9487. {
  9488. if (JavascriptNativeIntArray::Is(arr))
  9489. {
  9490. arr = JavascriptNativeIntArray::ToVarArray((JavascriptNativeIntArray*)arr);
  9491. }
  9492. else if (JavascriptNativeFloatArray::Is(arr))
  9493. {
  9494. arr = JavascriptNativeFloatArray::ToVarArray((JavascriptNativeFloatArray*)arr);
  9495. }
  9496. return arr;
  9497. }
  9498. BOOL JavascriptNativeIntArray::DirectGetItemAtFull(uint32 index, Var* outVal)
  9499. {
  9500. ScriptContext* requestContext = type->GetScriptContext();
  9501. if (JavascriptNativeIntArray::GetItem(this, index, outVal, requestContext))
  9502. {
  9503. return TRUE;
  9504. }
  9505. return JavascriptOperators::GetItem(this, this->GetPrototype(), index, outVal, requestContext);
  9506. }
  9507. BOOL JavascriptNativeFloatArray::DirectGetItemAtFull(uint32 index, Var* outVal)
  9508. {
  9509. ScriptContext* requestContext = type->GetScriptContext();
  9510. if (JavascriptNativeFloatArray::GetItem(this, index, outVal, requestContext))
  9511. {
  9512. return TRUE;
  9513. }
  9514. return JavascriptOperators::GetItem(this, this->GetPrototype(), index, outVal, requestContext);
  9515. }
  9516. template<typename T>
  9517. void JavascriptArray::InternalCopyNativeIntArrayElements(JavascriptArray* dstArray, const T& dstIndex, JavascriptNativeIntArray* srcArray, uint32 start, uint32 end)
  9518. {
  9519. Assert(start < end && end <= srcArray->length);
  9520. uint32 count = 0;
  9521. // iterate on the array itself
  9522. ScriptContext *scriptContext = dstArray->GetScriptContext();
  9523. ArrayElementEnumerator e(srcArray, start, end);
  9524. while(e.MoveNext<int32>())
  9525. {
  9526. T n = dstIndex + (e.GetIndex() - start);
  9527. dstArray->DirectSetItemAt(n, JavascriptNumber::ToVar(e.GetItem<int32>(), scriptContext));
  9528. count++;
  9529. }
  9530. // iterate on the array's prototypes only if not all elements found
  9531. if (start + count != end)
  9532. {
  9533. InternalFillFromPrototype(dstArray, dstIndex, srcArray, start, end, count);
  9534. }
  9535. }
  9536. template<typename T>
  9537. void JavascriptArray::InternalCopyNativeFloatArrayElements(JavascriptArray* dstArray, const T& dstIndex, JavascriptNativeFloatArray* srcArray, uint32 start, uint32 end)
  9538. {
  9539. Assert(start < end && end <= srcArray->length);
  9540. uint32 count = 0;
  9541. // iterate on the array itself
  9542. ScriptContext *scriptContext = dstArray->GetScriptContext();
  9543. ArrayElementEnumerator e(srcArray, start, end);
  9544. while(e.MoveNext<double>())
  9545. {
  9546. T n = dstIndex + (e.GetIndex() - start);
  9547. dstArray->DirectSetItemAt(n, JavascriptNumber::ToVarWithCheck(e.GetItem<double>(), scriptContext));
  9548. count++;
  9549. }
  9550. // iterate on the array's prototypes only if not all elements found
  9551. if (start + count != end)
  9552. {
  9553. InternalFillFromPrototype(dstArray, dstIndex, srcArray, start, end, count);
  9554. }
  9555. }
  9556. template<typename T>
  9557. void JavascriptArray::InternalFillFromPrototype(JavascriptArray *dstArray, const T& dstIndex, JavascriptArray *srcArray, uint32 start, uint32 end, uint32 count)
  9558. {
  9559. RecyclableObject* prototype = srcArray->GetPrototype();
  9560. while (start + count != end && JavascriptOperators::GetTypeId(prototype) != TypeIds_Null)
  9561. {
  9562. ForEachOwnMissingArrayIndexOfObject(srcArray, dstArray, prototype, start, end, dstIndex, [&](uint32 index, Var value) {
  9563. T n = dstIndex + (index - start);
  9564. dstArray->DirectSetItemAt(n, value);
  9565. count++;
  9566. });
  9567. prototype = prototype->GetPrototype();
  9568. }
  9569. }
  9570. Var JavascriptArray::SpreadArrayArgs(Var arrayToSpread, const Js::AuxArray<uint32> *spreadIndices, ScriptContext *scriptContext)
  9571. {
  9572. // At this stage we have an array literal with some arguments to be spread.
  9573. // First we need to calculate the real size of the final literal.
  9574. #if ENABLE_COPYONACCESS_ARRAY
  9575. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(arrayToSpread);
  9576. #endif
  9577. JavascriptArray *array = FromVar(arrayToSpread);
  9578. uint32 actualLength = array->GetLength();
  9579. for (unsigned i = 0; i < spreadIndices->count; ++i)
  9580. {
  9581. actualLength = UInt32Math::Add(actualLength - 1, GetSpreadArgLen(array->DirectGetItem(spreadIndices->elements[i]), scriptContext));
  9582. }
  9583. JavascriptArray *result = FromVar(OP_NewScArrayWithMissingValues(actualLength, scriptContext));
  9584. // Now we copy each element and expand the spread parameters inline.
  9585. for (unsigned i = 0, spreadArrIndex = 0, resultIndex = 0; i < array->GetLength() && resultIndex < actualLength; ++i)
  9586. {
  9587. uint32 spreadIndex = spreadIndices->elements[spreadArrIndex]; // The index of the next element to be spread.
  9588. // 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.
  9589. auto needArraySlowCopy = [&](Var instance) {
  9590. if (JavascriptArray::Is(instance))
  9591. {
  9592. JavascriptArray *arr = JavascriptArray::FromVar(instance);
  9593. return arr->IsCrossSiteObject() || arr->IsFillFromPrototypes();
  9594. }
  9595. return false;
  9596. };
  9597. // Designed to have interchangeable arguments with CopyAnyArrayElementsToVar.
  9598. auto slowCopy = [&scriptContext, &needArraySlowCopy](JavascriptArray *dstArray, unsigned dstIndex, Var srcArray, uint32 start, uint32 end) {
  9599. Assert(needArraySlowCopy(srcArray) || ArgumentsObject::Is(srcArray) || TypedArrayBase::Is(srcArray) || JavascriptString::Is(srcArray));
  9600. RecyclableObject *propertyObject;
  9601. if (!JavascriptOperators::GetPropertyObject(srcArray, scriptContext, &propertyObject))
  9602. {
  9603. JavascriptError::ThrowTypeError(scriptContext, JSERR_InvalidSpreadArgument);
  9604. }
  9605. for (uint32 j = start; j < end; j++)
  9606. {
  9607. Var element;
  9608. if (!JavascriptOperators::GetItem(srcArray, propertyObject, j, &element, scriptContext))
  9609. {
  9610. // Copy across missing values as undefined as per 12.2.5.2 SpreadElement : ... AssignmentExpression 5f.
  9611. element = scriptContext->GetLibrary()->GetUndefined();
  9612. }
  9613. dstArray->DirectSetItemAt(dstIndex++, element);
  9614. }
  9615. };
  9616. if (i < spreadIndex)
  9617. {
  9618. // Any non-spread elements can be copied in bulk.
  9619. if (needArraySlowCopy(array))
  9620. {
  9621. slowCopy(result, resultIndex, (Var)array, i, spreadIndex);
  9622. }
  9623. else
  9624. {
  9625. CopyAnyArrayElementsToVar(result, resultIndex, array, i, spreadIndex);
  9626. }
  9627. resultIndex += spreadIndex - i;
  9628. i = spreadIndex - 1;
  9629. continue;
  9630. }
  9631. else if (i > spreadIndex)
  9632. {
  9633. // Any non-spread elements terminating the array can also be copied in bulk.
  9634. Assert(spreadArrIndex == spreadIndices->count - 1);
  9635. if (needArraySlowCopy(array))
  9636. {
  9637. slowCopy(result, resultIndex, array, i, array->GetLength());
  9638. }
  9639. else
  9640. {
  9641. CopyAnyArrayElementsToVar(result, resultIndex, array, i, array->GetLength());
  9642. }
  9643. break;
  9644. }
  9645. else
  9646. {
  9647. Var instance = array->DirectGetItem(i);
  9648. if (SpreadArgument::Is(instance))
  9649. {
  9650. SpreadArgument* spreadArgument = SpreadArgument::FromVar(instance);
  9651. uint32 len = spreadArgument->GetArgumentSpreadCount();
  9652. const Var* spreadItems = spreadArgument->GetArgumentSpread();
  9653. for (uint32 j = 0; j < len; j++)
  9654. {
  9655. result->DirectSetItemAt(resultIndex++, spreadItems[j]);
  9656. }
  9657. }
  9658. else
  9659. {
  9660. AssertMsg(JavascriptArray::Is(instance) || TypedArrayBase::Is(instance), "Only SpreadArgument, TypedArray, and JavascriptArray should be listed as spread arguments");
  9661. // We first try to interpret the spread parameter as a JavascriptArray.
  9662. JavascriptArray *arr = nullptr;
  9663. if (JavascriptArray::Is(instance))
  9664. {
  9665. arr = JavascriptArray::FromVar(instance);
  9666. }
  9667. if (arr != nullptr)
  9668. {
  9669. if (arr->GetLength() > 0)
  9670. {
  9671. if (needArraySlowCopy(arr))
  9672. {
  9673. slowCopy(result, resultIndex, arr, 0, arr->GetLength());
  9674. }
  9675. else
  9676. {
  9677. CopyAnyArrayElementsToVar(result, resultIndex, arr, 0, arr->GetLength());
  9678. }
  9679. resultIndex += arr->GetLength();
  9680. }
  9681. }
  9682. else
  9683. {
  9684. uint32 len = GetSpreadArgLen(instance, scriptContext);
  9685. slowCopy(result, resultIndex, instance, 0, len);
  9686. resultIndex += len;
  9687. }
  9688. }
  9689. if (spreadArrIndex < spreadIndices->count - 1)
  9690. {
  9691. spreadArrIndex++;
  9692. }
  9693. }
  9694. }
  9695. return result;
  9696. }
  9697. uint32 JavascriptArray::GetSpreadArgLen(Var spreadArg, ScriptContext *scriptContext)
  9698. {
  9699. // A spread argument can be anything that returns a 'length' property, even if that
  9700. // property is null or undefined.
  9701. spreadArg = CrossSite::MarshalVar(scriptContext, spreadArg);
  9702. if (JavascriptArray::Is(spreadArg))
  9703. {
  9704. JavascriptArray *arr = JavascriptArray::FromVar(spreadArg);
  9705. return arr->GetLength();
  9706. }
  9707. if (TypedArrayBase::Is(spreadArg))
  9708. {
  9709. TypedArrayBase *tarr = TypedArrayBase::FromVar(spreadArg);
  9710. return tarr->GetLength();
  9711. }
  9712. if (SpreadArgument::Is(spreadArg))
  9713. {
  9714. SpreadArgument *spreadFunctionArgs = SpreadArgument::FromVar(spreadArg);
  9715. return spreadFunctionArgs->GetArgumentSpreadCount();
  9716. }
  9717. AssertMsg(false, "LdCustomSpreadIteratorList should have converted the arg to one of the above types");
  9718. Throw::FatalInternalError();
  9719. }
  9720. #ifdef VALIDATE_ARRAY
  9721. class ArraySegmentsVisitor
  9722. {
  9723. private:
  9724. SparseArraySegmentBase* seg;
  9725. public:
  9726. ArraySegmentsVisitor(SparseArraySegmentBase* head)
  9727. : seg(head)
  9728. {
  9729. }
  9730. void operator()(SparseArraySegmentBase* s)
  9731. {
  9732. Assert(seg == s);
  9733. if (seg)
  9734. {
  9735. seg = seg->next;
  9736. }
  9737. }
  9738. };
  9739. void JavascriptArray::ValidateArrayCommon()
  9740. {
  9741. SparseArraySegmentBase * lastUsedSegment = this->GetLastUsedSegment();
  9742. AssertMsg(this != nullptr && head && lastUsedSegment, "Array should not be null");
  9743. AssertMsg(head->left == 0, "Array always should have a segment starting at zero");
  9744. // Simple segments validation
  9745. bool foundLastUsedSegment = false;
  9746. SparseArraySegmentBase *seg = head;
  9747. while(seg != nullptr)
  9748. {
  9749. if (seg == lastUsedSegment)
  9750. {
  9751. foundLastUsedSegment = true;
  9752. }
  9753. AssertMsg(seg->length <= seg->size , "Length greater than size not possible");
  9754. SparseArraySegmentBase* next = seg->next;
  9755. if (next != nullptr)
  9756. {
  9757. AssertMsg(seg->left < next->left, "Segment is adjacent to or overlaps with next segment");
  9758. AssertMsg(seg->size <= (next->left - seg->left), "Segment is adjacent to or overlaps with next segment");
  9759. AssertMsg(!SparseArraySegmentBase::IsLeafSegment(seg, this->GetScriptContext()->GetRecycler()), "Leaf segment with a next pointer");
  9760. }
  9761. else
  9762. {
  9763. AssertMsg(seg->length <= MaxArrayLength - seg->left, "Segment index range overflow");
  9764. AssertMsg(seg->left + seg->length <= this->length, "Segment index range exceeds array length");
  9765. }
  9766. seg = next;
  9767. }
  9768. AssertMsg(foundLastUsedSegment || HasSegmentMap(), "Corrupt lastUsedSegment in array header");
  9769. // Validate segmentMap if present
  9770. if (HasSegmentMap())
  9771. {
  9772. ArraySegmentsVisitor visitor(head);
  9773. GetSegmentMap()->Walk(visitor);
  9774. }
  9775. }
  9776. void JavascriptArray::ValidateArray()
  9777. {
  9778. if (!Js::Configuration::Global.flags.ArrayValidate)
  9779. {
  9780. return;
  9781. }
  9782. ValidateArrayCommon();
  9783. // Detailed segments validation
  9784. JavascriptArray::ValidateVarSegment((SparseArraySegment<Var>*)head);
  9785. }
  9786. void JavascriptNativeIntArray::ValidateArray()
  9787. {
  9788. if (!Js::Configuration::Global.flags.ArrayValidate)
  9789. {
  9790. #if DBG
  9791. SparseArraySegmentBase *seg = head;
  9792. while (seg)
  9793. {
  9794. if (seg->next != nullptr)
  9795. {
  9796. AssertMsg(!SparseArraySegmentBase::IsLeafSegment(seg, this->GetScriptContext()->GetRecycler()), "Leaf segment with a next pointer");
  9797. }
  9798. seg = seg->next;
  9799. }
  9800. #endif
  9801. return;
  9802. }
  9803. ValidateArrayCommon();
  9804. // Detailed segments validation
  9805. JavascriptArray::ValidateSegment<int32>((SparseArraySegment<int32>*)head);
  9806. }
  9807. void JavascriptNativeFloatArray::ValidateArray()
  9808. {
  9809. if (!Js::Configuration::Global.flags.ArrayValidate)
  9810. {
  9811. #if DBG
  9812. SparseArraySegmentBase *seg = head;
  9813. while (seg)
  9814. {
  9815. if (seg->next != nullptr)
  9816. {
  9817. AssertMsg(!SparseArraySegmentBase::IsLeafSegment(seg, this->GetScriptContext()->GetRecycler()), "Leaf segment with a next pointer");
  9818. }
  9819. seg = seg->next;
  9820. }
  9821. #endif
  9822. return;
  9823. }
  9824. ValidateArrayCommon();
  9825. // Detailed segments validation
  9826. JavascriptArray::ValidateSegment<double>((SparseArraySegment<double>*)head);
  9827. }
  9828. void JavascriptArray::ValidateVarSegment(SparseArraySegment<Var>* seg)
  9829. {
  9830. if (!Js::Configuration::Global.flags.ArrayValidate)
  9831. {
  9832. return;
  9833. }
  9834. int32 inspect;
  9835. double inspectDouble;
  9836. while (seg)
  9837. {
  9838. uint32 i = 0;
  9839. for (i = 0; i < seg->length; i++)
  9840. {
  9841. if (SparseArraySegment<Var>::IsMissingItem(&seg->elements[i]))
  9842. {
  9843. continue;
  9844. }
  9845. if (TaggedInt::Is(seg->elements[i]))
  9846. {
  9847. inspect = TaggedInt::ToInt32(seg->elements[i]);
  9848. }
  9849. else if (JavascriptNumber::Is_NoTaggedIntCheck(seg->elements[i]))
  9850. {
  9851. inspectDouble = JavascriptNumber::GetValue(seg->elements[i]);
  9852. }
  9853. else
  9854. {
  9855. AssertMsg(RecyclableObject::Is(seg->elements[i]), "Invalid entry in segment");
  9856. }
  9857. }
  9858. ValidateSegment(seg);
  9859. seg = (SparseArraySegment<Var>*)seg->next;
  9860. }
  9861. }
  9862. template<typename T>
  9863. void JavascriptArray::ValidateSegment(SparseArraySegment<T>* seg)
  9864. {
  9865. if (!Js::Configuration::Global.flags.ArrayValidate)
  9866. {
  9867. return;
  9868. }
  9869. while (seg)
  9870. {
  9871. uint32 i = seg->length;
  9872. while (i < seg->size)
  9873. {
  9874. AssertMsg(SparseArraySegment<T>::IsMissingItem(&seg->elements[i]), "Non missing value the end of the segment");
  9875. i++;
  9876. }
  9877. seg = (SparseArraySegment<T>*)seg->next;
  9878. }
  9879. }
  9880. #endif
  9881. template <typename T>
  9882. void JavascriptArray::InitBoxedInlineHeadSegment(SparseArraySegment<T> * dst, SparseArraySegment<T> * src)
  9883. {
  9884. // Don't copy the segment map, we will build it again
  9885. SetFlags(GetFlags() & ~DynamicObjectFlags::HasSegmentMap);
  9886. SetHeadAndLastUsedSegment(dst);
  9887. dst->left = src->left;
  9888. dst->length = src->length;
  9889. dst->size = src->size;
  9890. dst->next = src->next;
  9891. js_memcpy_s(dst->elements, sizeof(T) * dst->size, src->elements, sizeof(T) * src->size);
  9892. }
  9893. JavascriptArray::JavascriptArray(JavascriptArray * instance, bool boxHead)
  9894. : ArrayObject(instance)
  9895. {
  9896. if (boxHead)
  9897. {
  9898. InitBoxedInlineHeadSegment(DetermineInlineHeadSegmentPointer<JavascriptArray, 0, true>(this), (SparseArraySegment<Var>*)instance->head);
  9899. }
  9900. else
  9901. {
  9902. SetFlags(GetFlags() & ~DynamicObjectFlags::HasSegmentMap);
  9903. head = instance->head;
  9904. SetLastUsedSegment(instance->GetLastUsedSegment());
  9905. }
  9906. }
  9907. template <typename T>
  9908. T * JavascriptArray::BoxStackInstance(T * instance)
  9909. {
  9910. Assert(ThreadContext::IsOnStack(instance));
  9911. // On the stack, the we reserved a pointer before the object as to store the boxed value
  9912. T ** boxedInstanceRef = ((T **)instance) - 1;
  9913. T * boxedInstance = *boxedInstanceRef;
  9914. if (boxedInstance)
  9915. {
  9916. return boxedInstance;
  9917. }
  9918. const size_t inlineSlotsSize = instance->GetTypeHandler()->GetInlineSlotsSize();
  9919. if (ThreadContext::IsOnStack(instance->head))
  9920. {
  9921. boxedInstance = RecyclerNewPlusZ(instance->GetRecycler(),
  9922. inlineSlotsSize + sizeof(Js::SparseArraySegmentBase) + instance->head->size * sizeof(typename T::TElement),
  9923. T, instance, true);
  9924. }
  9925. else if(inlineSlotsSize)
  9926. {
  9927. boxedInstance = RecyclerNewPlusZ(instance->GetRecycler(), inlineSlotsSize, T, instance, false);
  9928. }
  9929. else
  9930. {
  9931. boxedInstance = RecyclerNew(instance->GetRecycler(), T, instance, false);
  9932. }
  9933. *boxedInstanceRef = boxedInstance;
  9934. return boxedInstance;
  9935. }
  9936. JavascriptArray *
  9937. JavascriptArray::BoxStackInstance(JavascriptArray * instance)
  9938. {
  9939. return BoxStackInstance<JavascriptArray>(instance);
  9940. }
  9941. #if ENABLE_TTD
  9942. void JavascriptArray::MarkVisitKindSpecificPtrs(TTD::SnapshotExtractor* extractor)
  9943. {
  9944. AssertMsg(this->GetTypeId() == Js::TypeIds_Array || this->GetTypeId() == Js::TypeIds_ES5Array, "Should only be used on basic arrays (or called as super from ES5Array.");
  9945. ScriptContext* ctx = this->GetScriptContext();
  9946. uint32 index = Js::JavascriptArray::InvalidIndex;
  9947. while(true)
  9948. {
  9949. index = this->GetNextIndex(index);
  9950. if(index == Js::JavascriptArray::InvalidIndex) // End of array
  9951. {
  9952. break;
  9953. }
  9954. Js::Var aval = nullptr;
  9955. if(this->DirectGetVarItemAt(index, &aval, ctx))
  9956. {
  9957. extractor->MarkVisitVar(aval);
  9958. }
  9959. }
  9960. }
  9961. void JavascriptArray::ProcessCorePaths()
  9962. {
  9963. AssertMsg(this->GetTypeId() == Js::TypeIds_Array, "Should only be used on basic arrays.");
  9964. ScriptContext* ctx = this->GetScriptContext();
  9965. uint32 index = Js::JavascriptArray::InvalidIndex;
  9966. while(true)
  9967. {
  9968. index = this->GetNextIndex(index);
  9969. if(index == Js::JavascriptArray::InvalidIndex) // End of array
  9970. {
  9971. break;
  9972. }
  9973. Js::Var aval = nullptr;
  9974. if(this->DirectGetVarItemAt(index, &aval, ctx))
  9975. {
  9976. TTD::UtilSupport::TTAutoString pathExt;
  9977. ctx->TTDWellKnownInfo->BuildArrayIndexBuffer(index, pathExt);
  9978. ctx->TTDWellKnownInfo->EnqueueNewPathVarAsNeeded(this, aval, pathExt.GetStrValue());
  9979. }
  9980. }
  9981. }
  9982. TTD::NSSnapObjects::SnapObjectType JavascriptArray::GetSnapTag_TTD() const
  9983. {
  9984. return TTD::NSSnapObjects::SnapObjectType::SnapArrayObject;
  9985. }
  9986. void JavascriptArray::ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc)
  9987. {
  9988. AssertMsg(this->GetTypeId() == Js::TypeIds_Array, "Should only be used on basic Js arrays.");
  9989. TTD::NSSnapObjects::SnapArrayInfo<TTD::TTDVar>* sai = TTD::NSSnapObjects::ExtractArrayValues<TTD::TTDVar>(this, alloc);
  9990. TTD::NSSnapObjects::StdExtractSetKindSpecificInfo<TTD::NSSnapObjects::SnapArrayInfo<TTD::TTDVar>*, TTD::NSSnapObjects::SnapObjectType::SnapArrayObject>(objData, sai);
  9991. }
  9992. #endif
  9993. JavascriptNativeArray::JavascriptNativeArray(JavascriptNativeArray * instance) :
  9994. JavascriptArray(instance, false),
  9995. weakRefToFuncBody(instance->weakRefToFuncBody)
  9996. {
  9997. }
  9998. JavascriptNativeIntArray::JavascriptNativeIntArray(JavascriptNativeIntArray * instance, bool boxHead) :
  9999. JavascriptNativeArray(instance)
  10000. {
  10001. if (boxHead)
  10002. {
  10003. InitBoxedInlineHeadSegment(DetermineInlineHeadSegmentPointer<JavascriptNativeIntArray, 0, true>(this), (SparseArraySegment<int>*)instance->head);
  10004. }
  10005. else
  10006. {
  10007. // Base class ctor should have copied these
  10008. Assert(head == instance->head);
  10009. Assert(segmentUnion.lastUsedSegment == instance->GetLastUsedSegment());
  10010. }
  10011. }
  10012. JavascriptNativeIntArray *
  10013. JavascriptNativeIntArray::BoxStackInstance(JavascriptNativeIntArray * instance)
  10014. {
  10015. return JavascriptArray::BoxStackInstance<JavascriptNativeIntArray>(instance);
  10016. }
  10017. #if ENABLE_TTD
  10018. TTD::NSSnapObjects::SnapObjectType JavascriptNativeIntArray::GetSnapTag_TTD() const
  10019. {
  10020. return TTD::NSSnapObjects::SnapObjectType::SnapNativeIntArrayObject;
  10021. }
  10022. void JavascriptNativeIntArray::ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc)
  10023. {
  10024. AssertMsg(this->GetTypeId() == Js::TypeIds_NativeIntArray, "Should only be used on native int arrays.");
  10025. #if ENABLE_COPYONACCESS_ARRAY
  10026. AssertMsg(this->GetTypeId() != Js::TypeIds_CopyOnAccessNativeIntArray, "Need to handle this case seperately.");
  10027. #endif
  10028. TTD::NSSnapObjects::SnapArrayInfo<int32>* sai = TTD::NSSnapObjects::ExtractArrayValues<int32>(this, alloc);
  10029. TTD::NSSnapObjects::StdExtractSetKindSpecificInfo<TTD::NSSnapObjects::SnapArrayInfo<int32>*, TTD::NSSnapObjects::SnapObjectType::SnapNativeIntArrayObject>(objData, sai);
  10030. }
  10031. #endif
  10032. JavascriptNativeFloatArray::JavascriptNativeFloatArray(JavascriptNativeFloatArray * instance, bool boxHead) :
  10033. JavascriptNativeArray(instance)
  10034. {
  10035. if (boxHead)
  10036. {
  10037. InitBoxedInlineHeadSegment(DetermineInlineHeadSegmentPointer<JavascriptNativeFloatArray, 0, true>(this), (SparseArraySegment<double>*)instance->head);
  10038. }
  10039. else
  10040. {
  10041. // Base class ctor should have copied these
  10042. Assert(head == instance->head);
  10043. Assert(segmentUnion.lastUsedSegment == instance->GetLastUsedSegment());
  10044. }
  10045. }
  10046. JavascriptNativeFloatArray *
  10047. JavascriptNativeFloatArray::BoxStackInstance(JavascriptNativeFloatArray * instance)
  10048. {
  10049. return JavascriptArray::BoxStackInstance<JavascriptNativeFloatArray>(instance);
  10050. }
  10051. #if ENABLE_TTD
  10052. TTD::NSSnapObjects::SnapObjectType JavascriptNativeFloatArray::GetSnapTag_TTD() const
  10053. {
  10054. return TTD::NSSnapObjects::SnapObjectType::SnapNativeFloatArrayObject;
  10055. }
  10056. void JavascriptNativeFloatArray::ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc)
  10057. {
  10058. AssertMsg(this->GetTypeId() == Js::TypeIds_NativeFloatArray, "Should only be used on native float arrays.");
  10059. TTD::NSSnapObjects::SnapArrayInfo<double>* sai = TTD::NSSnapObjects::ExtractArrayValues<double>(this, alloc);
  10060. TTD::NSSnapObjects::StdExtractSetKindSpecificInfo<TTD::NSSnapObjects::SnapArrayInfo<double>*, TTD::NSSnapObjects::SnapObjectType::SnapNativeFloatArrayObject>(objData, sai);
  10061. }
  10062. #endif
  10063. template<typename T>
  10064. RecyclableObject*
  10065. JavascriptArray::ArraySpeciesCreate(Var originalArray, T length, ScriptContext* scriptContext, bool* pIsIntArray, bool* pIsFloatArray)
  10066. {
  10067. if (originalArray == nullptr || !scriptContext->GetConfig()->IsES6SpeciesEnabled())
  10068. {
  10069. return nullptr;
  10070. }
  10071. if (JavascriptArray::Is(originalArray)
  10072. && !DynamicObject::FromVar(originalArray)->GetDynamicType()->GetTypeHandler()->GetIsNotPathTypeHandlerOrHasUserDefinedCtor()
  10073. && DynamicObject::FromVar(originalArray)->GetPrototype() == scriptContext->GetLibrary()->GetArrayPrototype()
  10074. && !scriptContext->GetLibrary()->GetArrayObjectHasUserDefinedSpecies())
  10075. {
  10076. return nullptr;
  10077. }
  10078. Var constructor = scriptContext->GetLibrary()->GetUndefined();
  10079. if (JavascriptOperators::IsArray(originalArray))
  10080. {
  10081. if (!JavascriptOperators::GetProperty(RecyclableObject::FromVar(originalArray), PropertyIds::constructor, &constructor, scriptContext))
  10082. {
  10083. return nullptr;
  10084. }
  10085. if (JavascriptOperators::IsConstructor(constructor))
  10086. {
  10087. ScriptContext* constructorScriptContext = RecyclableObject::FromVar(constructor)->GetScriptContext();
  10088. if (constructorScriptContext != scriptContext)
  10089. {
  10090. if (constructorScriptContext->GetLibrary()->GetArrayConstructor() == constructor)
  10091. {
  10092. constructor = scriptContext->GetLibrary()->GetUndefined();
  10093. }
  10094. }
  10095. }
  10096. if (JavascriptOperators::IsObject(constructor))
  10097. {
  10098. if (!JavascriptOperators::GetProperty((RecyclableObject*)constructor, PropertyIds::_symbolSpecies, &constructor, scriptContext))
  10099. {
  10100. return nullptr;
  10101. }
  10102. if (constructor == scriptContext->GetLibrary()->GetNull())
  10103. {
  10104. constructor = scriptContext->GetLibrary()->GetUndefined();
  10105. }
  10106. }
  10107. }
  10108. if (constructor == scriptContext->GetLibrary()->GetUndefined() || constructor == scriptContext->GetLibrary()->GetArrayConstructor())
  10109. {
  10110. if (length > UINT_MAX)
  10111. {
  10112. JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthConstructIncorrect);
  10113. }
  10114. if (nullptr == pIsIntArray)
  10115. {
  10116. return scriptContext->GetLibrary()->CreateArray(static_cast<uint32>(length));
  10117. }
  10118. else
  10119. {
  10120. // If the constructor function is the built-in Array constructor, we can be smart and create the right type of native array.
  10121. JavascriptArray* pArr = JavascriptArray::FromVar(originalArray);
  10122. pArr->GetArrayTypeAndConvert(pIsIntArray, pIsFloatArray);
  10123. return CreateNewArrayHelper(static_cast<uint32>(length), *pIsIntArray, *pIsFloatArray, pArr, scriptContext);
  10124. }
  10125. }
  10126. if (!JavascriptOperators::IsConstructor(constructor))
  10127. {
  10128. JavascriptError::ThrowTypeError(scriptContext, JSERR_NotAConstructor, _u("constructor[Symbol.species]"));
  10129. }
  10130. Js::Var constructorArgs[] = { constructor, JavascriptNumber::ToVar(length, scriptContext) };
  10131. Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs));
  10132. return RecyclableObject::FromVar(JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext));
  10133. }
  10134. /*static*/
  10135. PropertyId const JavascriptArray::specialPropertyIds[] =
  10136. {
  10137. PropertyIds::length
  10138. };
  10139. BOOL JavascriptArray::DeleteProperty(PropertyId propertyId, PropertyOperationFlags flags)
  10140. {
  10141. if (propertyId == PropertyIds::length)
  10142. {
  10143. return false;
  10144. }
  10145. return DynamicObject::DeleteProperty(propertyId, flags);
  10146. }
  10147. BOOL JavascriptArray::HasProperty(PropertyId propertyId)
  10148. {
  10149. if (propertyId == PropertyIds::length)
  10150. {
  10151. return true;
  10152. }
  10153. ScriptContext* scriptContext = GetScriptContext();
  10154. uint32 index;
  10155. if (scriptContext->IsNumericPropertyId(propertyId, &index))
  10156. {
  10157. return this->HasItem(index);
  10158. }
  10159. return DynamicObject::HasProperty(propertyId);
  10160. }
  10161. BOOL JavascriptArray::IsEnumerable(PropertyId propertyId)
  10162. {
  10163. if (propertyId == PropertyIds::length)
  10164. {
  10165. return false;
  10166. }
  10167. return DynamicObject::IsEnumerable(propertyId);
  10168. }
  10169. BOOL JavascriptArray::IsConfigurable(PropertyId propertyId)
  10170. {
  10171. if (propertyId == PropertyIds::length)
  10172. {
  10173. return false;
  10174. }
  10175. return DynamicObject::IsConfigurable(propertyId);
  10176. }
  10177. //
  10178. // Evolve typeHandlers explicitly so that simple typeHandlers can skip array
  10179. // handling and only check instance objectArray for numeric propertyIds.
  10180. //
  10181. BOOL JavascriptArray::SetEnumerable(PropertyId propertyId, BOOL value)
  10182. {
  10183. if (propertyId == PropertyIds::length)
  10184. {
  10185. Assert(!value); // Can't change array length enumerable
  10186. return true;
  10187. }
  10188. ScriptContext* scriptContext = this->GetScriptContext();
  10189. uint32 index;
  10190. if (scriptContext->IsNumericPropertyId(propertyId, &index))
  10191. {
  10192. return GetTypeHandler()->ConvertToTypeWithItemAttributes(this)
  10193. ->SetEnumerable(this, propertyId, value);
  10194. }
  10195. return __super::SetEnumerable(propertyId, value);
  10196. }
  10197. //
  10198. // Evolve typeHandlers explicitly so that simple typeHandlers can skip array
  10199. // handling and only check instance objectArray for numeric propertyIds.
  10200. //
  10201. BOOL JavascriptArray::SetWritable(PropertyId propertyId, BOOL value)
  10202. {
  10203. ScriptContext* scriptContext = this->GetScriptContext();
  10204. uint32 index;
  10205. bool setLengthNonWritable = (propertyId == PropertyIds::length && !value);
  10206. if (setLengthNonWritable || scriptContext->IsNumericPropertyId(propertyId, &index))
  10207. {
  10208. return GetTypeHandler()->ConvertToTypeWithItemAttributes(this)
  10209. ->SetWritable(this, propertyId, value);
  10210. }
  10211. return __super::SetWritable(propertyId, value);
  10212. }
  10213. //
  10214. // Evolve typeHandlers explicitly so that simple typeHandlers can skip array
  10215. // handling and only check instance objectArray for numeric propertyIds.
  10216. //
  10217. BOOL JavascriptArray::SetConfigurable(PropertyId propertyId, BOOL value)
  10218. {
  10219. if (propertyId == PropertyIds::length)
  10220. {
  10221. Assert(!value); // Can't change array length configurable
  10222. return true;
  10223. }
  10224. ScriptContext* scriptContext = this->GetScriptContext();
  10225. uint32 index;
  10226. if (scriptContext->IsNumericPropertyId(propertyId, &index))
  10227. {
  10228. return GetTypeHandler()->ConvertToTypeWithItemAttributes(this)
  10229. ->SetConfigurable(this, propertyId, value);
  10230. }
  10231. return __super::SetConfigurable(propertyId, value);
  10232. }
  10233. //
  10234. // Evolve typeHandlers explicitly so that simple typeHandlers can skip array
  10235. // handling and only check instance objectArray for numeric propertyIds.
  10236. //
  10237. BOOL JavascriptArray::SetAttributes(PropertyId propertyId, PropertyAttributes attributes)
  10238. {
  10239. ScriptContext* scriptContext = this->GetScriptContext();
  10240. // SetAttributes on "length" is not expected. DefineOwnProperty uses SetWritable. If this is
  10241. // changed, we need to handle it here.
  10242. Assert(propertyId != PropertyIds::length);
  10243. uint32 index;
  10244. if (scriptContext->IsNumericPropertyId(propertyId, &index))
  10245. {
  10246. return GetTypeHandler()->ConvertToTypeWithItemAttributes(this)
  10247. ->SetItemAttributes(this, index, attributes);
  10248. }
  10249. return __super::SetAttributes(propertyId, attributes);
  10250. }
  10251. //
  10252. // Evolve typeHandlers explicitly so that simple typeHandlers can skip array
  10253. // handling and only check instance objectArray for numeric propertyIds.
  10254. //
  10255. BOOL JavascriptArray::SetAccessors(PropertyId propertyId, Var getter, Var setter, PropertyOperationFlags flags)
  10256. {
  10257. ScriptContext* scriptContext = this->GetScriptContext();
  10258. uint32 index;
  10259. if (scriptContext->IsNumericPropertyId(propertyId, &index))
  10260. {
  10261. return GetTypeHandler()->ConvertToTypeWithItemAttributes(this)
  10262. ->SetItemAccessors(this, index, getter, setter);
  10263. }
  10264. return __super::SetAccessors(propertyId, getter, setter, flags);
  10265. }
  10266. //
  10267. // Evolve typeHandlers explicitly so that simple typeHandlers can skip array
  10268. // handling and only check instance objectArray for numeric propertyIds.
  10269. //
  10270. BOOL JavascriptArray::SetItemWithAttributes(uint32 index, Var value, PropertyAttributes attributes)
  10271. {
  10272. return GetTypeHandler()->ConvertToTypeWithItemAttributes(this)
  10273. ->SetItemWithAttributes(this, index, value, attributes);
  10274. }
  10275. //
  10276. // Evolve typeHandlers explicitly so that simple typeHandlers can skip array
  10277. // handling and only check instance objectArray for numeric propertyIds.
  10278. //
  10279. BOOL JavascriptArray::SetItemAttributes(uint32 index, PropertyAttributes attributes)
  10280. {
  10281. return GetTypeHandler()->ConvertToTypeWithItemAttributes(this)
  10282. ->SetItemAttributes(this, index, attributes);
  10283. }
  10284. //
  10285. // Evolve typeHandlers explicitly so that simple typeHandlers can skip array
  10286. // handling and only check instance objectArray for numeric propertyIds.
  10287. //
  10288. BOOL JavascriptArray::SetItemAccessors(uint32 index, Var getter, Var setter)
  10289. {
  10290. return GetTypeHandler()->ConvertToTypeWithItemAttributes(this)
  10291. ->SetItemAccessors(this, index, getter, setter);
  10292. }
  10293. // Check if this objectArray isFrozen.
  10294. BOOL JavascriptArray::IsObjectArrayFrozen()
  10295. {
  10296. // If this is still a JavascriptArray, it's not frozen.
  10297. return false;
  10298. }
  10299. BOOL JavascriptArray::GetEnumerator(Var originalInstance, BOOL enumNonEnumerable, Var* enumerator, ScriptContext* requestContext, bool preferSnapshotSemantics, bool enumSymbols)
  10300. {
  10301. // JavascriptArray does not support accessors, discard originalInstance.
  10302. return JavascriptArray::GetEnumerator(enumNonEnumerable, enumerator, requestContext, preferSnapshotSemantics, enumSymbols);
  10303. }
  10304. BOOL JavascriptArray::GetNonIndexEnumerator(Var* enumerator, ScriptContext* requestContext)
  10305. {
  10306. *enumerator = RecyclerNew(GetScriptContext()->GetRecycler(), JavascriptArrayNonIndexSnapshotEnumerator, this, requestContext, false);
  10307. return true;
  10308. }
  10309. BOOL JavascriptArray::IsItemEnumerable(uint32 index)
  10310. {
  10311. return true;
  10312. }
  10313. //
  10314. // Evolve typeHandlers explicitly so that simple typeHandlers can skip array
  10315. // handling and only check instance objectArray for numeric propertyIds.
  10316. //
  10317. BOOL JavascriptArray::PreventExtensions()
  10318. {
  10319. return GetTypeHandler()->ConvertToTypeWithItemAttributes(this)->PreventExtensions(this);
  10320. }
  10321. //
  10322. // Evolve typeHandlers explicitly so that simple typeHandlers can skip array
  10323. // handling and only check instance objectArray for numeric propertyIds.
  10324. //
  10325. BOOL JavascriptArray::Seal()
  10326. {
  10327. return GetTypeHandler()->ConvertToTypeWithItemAttributes(this)->Seal(this);
  10328. }
  10329. //
  10330. // Evolve typeHandlers explicitly so that simple typeHandlers can skip array
  10331. // handling and only check instance objectArray for numeric propertyIds.
  10332. //
  10333. BOOL JavascriptArray::Freeze()
  10334. {
  10335. return GetTypeHandler()->ConvertToTypeWithItemAttributes(this)->Freeze(this);
  10336. }
  10337. BOOL JavascriptArray::GetSpecialPropertyName(uint32 index, Var *propertyName, ScriptContext * requestContext)
  10338. {
  10339. if (index == 0)
  10340. {
  10341. *propertyName = requestContext->GetPropertyString(PropertyIds::length);
  10342. return true;
  10343. }
  10344. return false;
  10345. }
  10346. // Returns the number of special non-enumerable properties this type has.
  10347. uint JavascriptArray::GetSpecialPropertyCount() const
  10348. {
  10349. return _countof(specialPropertyIds);
  10350. }
  10351. // Returns the list of special non-enumerable properties for the type.
  10352. PropertyId const * JavascriptArray::GetSpecialPropertyIds() const
  10353. {
  10354. return specialPropertyIds;
  10355. }
  10356. BOOL JavascriptArray::GetPropertyReference(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
  10357. {
  10358. return JavascriptArray::GetProperty(originalInstance, propertyId, value, info, requestContext);
  10359. }
  10360. BOOL JavascriptArray::GetProperty(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
  10361. {
  10362. if (GetPropertyBuiltIns(propertyId, value))
  10363. {
  10364. return true;
  10365. }
  10366. ScriptContext* scriptContext = GetScriptContext();
  10367. uint32 index;
  10368. if (scriptContext->IsNumericPropertyId(propertyId, &index))
  10369. {
  10370. return this->GetItem(this, index, value, scriptContext);
  10371. }
  10372. return DynamicObject::GetProperty(originalInstance, propertyId, value, info, requestContext);
  10373. }
  10374. BOOL JavascriptArray::GetProperty(Var originalInstance, JavascriptString* propertyNameString, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
  10375. {
  10376. AssertMsg(!PropertyRecord::IsPropertyNameNumeric(propertyNameString->GetString(), propertyNameString->GetLength()),
  10377. "Numeric property names should have been converted to uint or PropertyRecord*");
  10378. PropertyRecord const* propertyRecord;
  10379. this->GetScriptContext()->FindPropertyRecord(propertyNameString, &propertyRecord);
  10380. if (propertyRecord != nullptr && GetPropertyBuiltIns(propertyRecord->GetPropertyId(), value))
  10381. {
  10382. return true;
  10383. }
  10384. return DynamicObject::GetProperty(originalInstance, propertyNameString, value, info, requestContext);
  10385. }
  10386. BOOL JavascriptArray::GetPropertyBuiltIns(PropertyId propertyId, Var* value)
  10387. {
  10388. //
  10389. // length being accessed. Return array length
  10390. //
  10391. if (propertyId == PropertyIds::length)
  10392. {
  10393. *value = JavascriptNumber::ToVar(this->GetLength(), GetScriptContext());
  10394. return true;
  10395. }
  10396. return false;
  10397. }
  10398. BOOL JavascriptArray::HasItem(uint32 index)
  10399. {
  10400. Var value;
  10401. return this->DirectGetItemAt<Var>(index, &value);
  10402. }
  10403. BOOL JavascriptArray::GetItem(Var originalInstance, uint32 index, Var* value, ScriptContext* requestContext)
  10404. {
  10405. return this->DirectGetItemAt<Var>(index, value);
  10406. }
  10407. BOOL JavascriptArray::GetItemReference(Var originalInstance, uint32 index, Var* value, ScriptContext* requestContext)
  10408. {
  10409. return this->DirectGetItemAt<Var>(index, value);
  10410. }
  10411. BOOL JavascriptArray::DirectGetVarItemAt(uint32 index, Var *value, ScriptContext *requestContext)
  10412. {
  10413. return this->DirectGetItemAt<Var>(index, value);
  10414. }
  10415. BOOL JavascriptNativeIntArray::HasItem(uint32 index)
  10416. {
  10417. #if ENABLE_COPYONACCESS_ARRAY
  10418. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(this);
  10419. #endif
  10420. int32 value;
  10421. return this->DirectGetItemAt<int32>(index, &value);
  10422. }
  10423. BOOL JavascriptNativeFloatArray::HasItem(uint32 index)
  10424. {
  10425. double dvalue;
  10426. return this->DirectGetItemAt<double>(index, &dvalue);
  10427. }
  10428. BOOL JavascriptNativeIntArray::GetItem(Var originalInstance, uint32 index, Var* value, ScriptContext* requestContext)
  10429. {
  10430. #if ENABLE_COPYONACCESS_ARRAY
  10431. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(this);
  10432. #endif
  10433. return JavascriptNativeIntArray::DirectGetVarItemAt(index, value, requestContext);
  10434. }
  10435. BOOL JavascriptNativeIntArray::DirectGetVarItemAt(uint32 index, Var *value, ScriptContext *requestContext)
  10436. {
  10437. int32 intvalue;
  10438. if (!this->DirectGetItemAt<int32>(index, &intvalue))
  10439. {
  10440. return FALSE;
  10441. }
  10442. *value = JavascriptNumber::ToVar(intvalue, requestContext);
  10443. return TRUE;
  10444. }
  10445. BOOL JavascriptNativeIntArray::GetItemReference(Var originalInstance, uint32 index, Var* value, ScriptContext* requestContext)
  10446. {
  10447. return JavascriptNativeIntArray::GetItem(originalInstance, index, value, requestContext);
  10448. }
  10449. BOOL JavascriptNativeFloatArray::GetItem(Var originalInstance, uint32 index, Var* value, ScriptContext* requestContext)
  10450. {
  10451. return JavascriptNativeFloatArray::DirectGetVarItemAt(index, value, requestContext);
  10452. }
  10453. BOOL JavascriptNativeFloatArray::DirectGetVarItemAt(uint32 index, Var *value, ScriptContext *requestContext)
  10454. {
  10455. double dvalue;
  10456. int32 ivalue;
  10457. if (!this->DirectGetItemAt<double>(index, &dvalue))
  10458. {
  10459. return FALSE;
  10460. }
  10461. if (*(uint64*)&dvalue == 0ull)
  10462. {
  10463. *value = TaggedInt::ToVarUnchecked(0);
  10464. }
  10465. else if (JavascriptNumber::TryGetInt32Value(dvalue, &ivalue) && !TaggedInt::IsOverflow(ivalue))
  10466. {
  10467. *value = TaggedInt::ToVarUnchecked(ivalue);
  10468. }
  10469. else
  10470. {
  10471. *value = JavascriptNumber::ToVarWithCheck(dvalue, requestContext);
  10472. }
  10473. return TRUE;
  10474. }
  10475. BOOL JavascriptNativeFloatArray::GetItemReference(Var originalInstance, uint32 index, Var* value, ScriptContext* requestContext)
  10476. {
  10477. return JavascriptNativeFloatArray::GetItem(originalInstance, index, value, requestContext);
  10478. }
  10479. BOOL JavascriptArray::SetProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
  10480. {
  10481. uint32 indexValue;
  10482. if (propertyId == PropertyIds::length)
  10483. {
  10484. return this->SetLength(value);
  10485. }
  10486. else if (GetScriptContext()->IsNumericPropertyId(propertyId, &indexValue))
  10487. {
  10488. // Call this or subclass method
  10489. return SetItem(indexValue, value, flags);
  10490. }
  10491. else
  10492. {
  10493. return DynamicObject::SetProperty(propertyId, value, flags, info);
  10494. }
  10495. }
  10496. BOOL JavascriptArray::SetProperty(JavascriptString* propertyNameString, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
  10497. {
  10498. AssertMsg(!PropertyRecord::IsPropertyNameNumeric(propertyNameString->GetString(), propertyNameString->GetLength()),
  10499. "Numeric property names should have been converted to uint or PropertyRecord*");
  10500. PropertyRecord const* propertyRecord;
  10501. this->GetScriptContext()->FindPropertyRecord(propertyNameString, &propertyRecord);
  10502. if (propertyRecord != nullptr && propertyRecord->GetPropertyId() == PropertyIds::length)
  10503. {
  10504. return this->SetLength(value);
  10505. }
  10506. return DynamicObject::SetProperty(propertyNameString, value, flags, info);
  10507. }
  10508. BOOL JavascriptArray::SetPropertyWithAttributes(PropertyId propertyId, Var value, PropertyAttributes attributes, PropertyValueInfo* info, PropertyOperationFlags flags, SideEffects possibleSideEffects)
  10509. {
  10510. ScriptContext* scriptContext = GetScriptContext();
  10511. if (propertyId == PropertyIds::length)
  10512. {
  10513. Assert(attributes == PropertyWritable);
  10514. Assert(IsWritable(propertyId) && !IsConfigurable(propertyId) && !IsEnumerable(propertyId));
  10515. return this->SetLength(value);
  10516. }
  10517. uint32 index;
  10518. if (scriptContext->IsNumericPropertyId(propertyId, &index))
  10519. {
  10520. // Call this or subclass method
  10521. return SetItemWithAttributes(index, value, attributes);
  10522. }
  10523. return __super::SetPropertyWithAttributes(propertyId, value, attributes, info, flags, possibleSideEffects);
  10524. }
  10525. BOOL JavascriptArray::SetItem(uint32 index, Var value, PropertyOperationFlags flags)
  10526. {
  10527. this->DirectSetItemAt(index, value);
  10528. return true;
  10529. }
  10530. BOOL JavascriptNativeIntArray::SetItem(uint32 index, Var value, PropertyOperationFlags flags)
  10531. {
  10532. int32 iValue;
  10533. double dValue;
  10534. #if ENABLE_COPYONACCESS_ARRAY
  10535. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(this);
  10536. #endif
  10537. TypeId typeId = this->TrySetNativeIntArrayItem(value, &iValue, &dValue);
  10538. if (typeId == TypeIds_NativeIntArray)
  10539. {
  10540. this->SetItem(index, iValue);
  10541. }
  10542. else if (typeId == TypeIds_NativeFloatArray)
  10543. {
  10544. reinterpret_cast<JavascriptNativeFloatArray*>(this)->DirectSetItemAt<double>(index, dValue);
  10545. }
  10546. else
  10547. {
  10548. this->DirectSetItemAt<Var>(index, value);
  10549. }
  10550. return TRUE;
  10551. }
  10552. TypeId JavascriptNativeIntArray::TrySetNativeIntArrayItem(Var value, int32 *iValue, double *dValue)
  10553. {
  10554. if (TaggedInt::Is(value))
  10555. {
  10556. int32 i = TaggedInt::ToInt32(value);
  10557. if (i != JavascriptNativeIntArray::MissingItem)
  10558. {
  10559. *iValue = i;
  10560. return TypeIds_NativeIntArray;
  10561. }
  10562. }
  10563. if (JavascriptNumber::Is_NoTaggedIntCheck(value))
  10564. {
  10565. bool isInt32;
  10566. int32 i;
  10567. double d = JavascriptNumber::GetValue(value);
  10568. if (JavascriptNumber::TryGetInt32OrUInt32Value(d, &i, &isInt32))
  10569. {
  10570. if (isInt32 && i != JavascriptNativeIntArray::MissingItem)
  10571. {
  10572. *iValue = i;
  10573. return TypeIds_NativeIntArray;
  10574. }
  10575. }
  10576. else
  10577. {
  10578. *dValue = d;
  10579. JavascriptNativeIntArray::ToNativeFloatArray(this);
  10580. return TypeIds_NativeFloatArray;
  10581. }
  10582. }
  10583. JavascriptNativeIntArray::ToVarArray(this);
  10584. return TypeIds_Array;
  10585. }
  10586. BOOL JavascriptNativeIntArray::SetItem(uint32 index, int32 iValue)
  10587. {
  10588. if (iValue == JavascriptNativeIntArray::MissingItem)
  10589. {
  10590. JavascriptArray *varArr = JavascriptNativeIntArray::ToVarArray(this);
  10591. varArr->DirectSetItemAt(index, JavascriptNumber::ToVar(iValue, GetScriptContext()));
  10592. return TRUE;
  10593. }
  10594. this->DirectSetItemAt(index, iValue);
  10595. return TRUE;
  10596. }
  10597. BOOL JavascriptNativeFloatArray::SetItem(uint32 index, Var value, PropertyOperationFlags flags)
  10598. {
  10599. double dValue;
  10600. TypeId typeId = this->TrySetNativeFloatArrayItem(value, &dValue);
  10601. if (typeId == TypeIds_NativeFloatArray)
  10602. {
  10603. this->SetItem(index, dValue);
  10604. }
  10605. else
  10606. {
  10607. this->DirectSetItemAt(index, value);
  10608. }
  10609. return TRUE;
  10610. }
  10611. TypeId JavascriptNativeFloatArray::TrySetNativeFloatArrayItem(Var value, double *dValue)
  10612. {
  10613. if (TaggedInt::Is(value))
  10614. {
  10615. *dValue = (double)TaggedInt::ToInt32(value);
  10616. return TypeIds_NativeFloatArray;
  10617. }
  10618. else if (JavascriptNumber::Is_NoTaggedIntCheck(value))
  10619. {
  10620. *dValue = JavascriptNumber::GetValue(value);
  10621. return TypeIds_NativeFloatArray;
  10622. }
  10623. JavascriptNativeFloatArray::ToVarArray(this);
  10624. return TypeIds_Array;
  10625. }
  10626. BOOL JavascriptNativeFloatArray::SetItem(uint32 index, double dValue)
  10627. {
  10628. if (*(uint64*)&dValue == *(uint64*)&JavascriptNativeFloatArray::MissingItem)
  10629. {
  10630. JavascriptArray *varArr = JavascriptNativeFloatArray::ToVarArray(this);
  10631. varArr->DirectSetItemAt(index, JavascriptNumber::ToVarNoCheck(dValue, GetScriptContext()));
  10632. return TRUE;
  10633. }
  10634. this->DirectSetItemAt<double>(index, dValue);
  10635. return TRUE;
  10636. }
  10637. BOOL JavascriptArray::DeleteItem(uint32 index, PropertyOperationFlags flags)
  10638. {
  10639. return this->DirectDeleteItemAt<Var>(index);
  10640. }
  10641. BOOL JavascriptNativeIntArray::DeleteItem(uint32 index, PropertyOperationFlags flags)
  10642. {
  10643. return this->DirectDeleteItemAt<int32>(index);
  10644. }
  10645. BOOL JavascriptNativeFloatArray::DeleteItem(uint32 index, PropertyOperationFlags flags)
  10646. {
  10647. return this->DirectDeleteItemAt<double>(index);
  10648. }
  10649. BOOL JavascriptArray::GetEnumerator(BOOL enumNonEnumerable, Var* enumerator, ScriptContext * requestContext, bool preferSnapshotSemantics, bool enumSymbols)
  10650. {
  10651. if (preferSnapshotSemantics)
  10652. {
  10653. *enumerator = RecyclerNew(GetRecycler(), JavascriptArraySnapshotEnumerator, this, requestContext, enumNonEnumerable, enumSymbols);
  10654. }
  10655. else
  10656. {
  10657. *enumerator = RecyclerNew(GetRecycler(), JavascriptArrayEnumerator, this, requestContext, enumNonEnumerable, enumSymbols);
  10658. }
  10659. return true;
  10660. }
  10661. BOOL JavascriptArray::GetDiagValueString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext)
  10662. {
  10663. stringBuilder->Append(_u('['));
  10664. if (this->length < 10)
  10665. {
  10666. auto funcPtr = [&]()
  10667. {
  10668. ENTER_PINNED_SCOPE(JavascriptString, valueStr);
  10669. valueStr = JavascriptArray::JoinHelper(this, GetLibrary()->GetCommaDisplayString(), requestContext);
  10670. stringBuilder->Append(valueStr->GetString(), valueStr->GetLength());
  10671. LEAVE_PINNED_SCOPE();
  10672. };
  10673. if (!requestContext->GetThreadContext()->IsScriptActive())
  10674. {
  10675. BEGIN_JS_RUNTIME_CALL(requestContext);
  10676. {
  10677. funcPtr();
  10678. }
  10679. END_JS_RUNTIME_CALL(requestContext);
  10680. }
  10681. else
  10682. {
  10683. funcPtr();
  10684. }
  10685. }
  10686. else
  10687. {
  10688. stringBuilder->AppendCppLiteral(_u("..."));
  10689. }
  10690. stringBuilder->Append(_u(']'));
  10691. return TRUE;
  10692. }
  10693. BOOL JavascriptArray::GetDiagTypeString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext)
  10694. {
  10695. stringBuilder->AppendCppLiteral(_u("Object, (Array)"));
  10696. return TRUE;
  10697. }
  10698. bool JavascriptNativeArray::Is(Var aValue)
  10699. {
  10700. TypeId typeId = JavascriptOperators::GetTypeId(aValue);
  10701. return JavascriptNativeArray::Is(typeId);
  10702. }
  10703. bool JavascriptNativeArray::Is(TypeId typeId)
  10704. {
  10705. return JavascriptNativeIntArray::Is(typeId) || JavascriptNativeFloatArray::Is(typeId);
  10706. }
  10707. JavascriptNativeArray* JavascriptNativeArray::FromVar(Var aValue)
  10708. {
  10709. AssertMsg(Is(aValue), "Ensure var is actually a 'JavascriptNativeArray'");
  10710. return static_cast<JavascriptNativeArray *>(RecyclableObject::FromVar(aValue));
  10711. }
  10712. bool JavascriptNativeIntArray::Is(Var aValue)
  10713. {
  10714. TypeId typeId = JavascriptOperators::GetTypeId(aValue);
  10715. return JavascriptNativeIntArray::Is(typeId);
  10716. }
  10717. #if ENABLE_COPYONACCESS_ARRAY
  10718. bool JavascriptCopyOnAccessNativeIntArray::Is(Var aValue)
  10719. {
  10720. TypeId typeId = JavascriptOperators::GetTypeId(aValue);
  10721. return JavascriptCopyOnAccessNativeIntArray::Is(typeId);
  10722. }
  10723. #endif
  10724. bool JavascriptNativeIntArray::Is(TypeId typeId)
  10725. {
  10726. return typeId == TypeIds_NativeIntArray;
  10727. }
  10728. #if ENABLE_COPYONACCESS_ARRAY
  10729. bool JavascriptCopyOnAccessNativeIntArray::Is(TypeId typeId)
  10730. {
  10731. return typeId == TypeIds_CopyOnAccessNativeIntArray;
  10732. }
  10733. #endif
  10734. bool JavascriptNativeIntArray::IsNonCrossSite(Var aValue)
  10735. {
  10736. bool ret = !TaggedInt::Is(aValue) && VirtualTableInfo<JavascriptNativeIntArray>::HasVirtualTable(aValue);
  10737. Assert(ret == (JavascriptNativeIntArray::Is(aValue) && !JavascriptNativeIntArray::FromVar(aValue)->IsCrossSiteObject()));
  10738. return ret;
  10739. }
  10740. JavascriptNativeIntArray* JavascriptNativeIntArray::FromVar(Var aValue)
  10741. {
  10742. AssertMsg(Is(aValue), "Ensure var is actually a 'JavascriptNativeIntArray'");
  10743. return static_cast<JavascriptNativeIntArray *>(RecyclableObject::FromVar(aValue));
  10744. }
  10745. #if ENABLE_COPYONACCESS_ARRAY
  10746. JavascriptCopyOnAccessNativeIntArray* JavascriptCopyOnAccessNativeIntArray::FromVar(Var aValue)
  10747. {
  10748. AssertMsg(Is(aValue), "Ensure var is actually a 'JavascriptCopyOnAccessNativeIntArray'");
  10749. return static_cast<JavascriptCopyOnAccessNativeIntArray *>(RecyclableObject::FromVar(aValue));
  10750. }
  10751. #endif
  10752. bool JavascriptNativeFloatArray::Is(Var aValue)
  10753. {
  10754. TypeId typeId = JavascriptOperators::GetTypeId(aValue);
  10755. return JavascriptNativeFloatArray::Is(typeId);
  10756. }
  10757. bool JavascriptNativeFloatArray::Is(TypeId typeId)
  10758. {
  10759. return typeId == TypeIds_NativeFloatArray;
  10760. }
  10761. bool JavascriptNativeFloatArray::IsNonCrossSite(Var aValue)
  10762. {
  10763. bool ret = !TaggedInt::Is(aValue) && VirtualTableInfo<JavascriptNativeFloatArray>::HasVirtualTable(aValue);
  10764. Assert(ret == (JavascriptNativeFloatArray::Is(aValue) && !JavascriptNativeFloatArray::FromVar(aValue)->IsCrossSiteObject()));
  10765. return ret;
  10766. }
  10767. JavascriptNativeFloatArray* JavascriptNativeFloatArray::FromVar(Var aValue)
  10768. {
  10769. AssertMsg(Is(aValue), "Ensure var is actually a 'JavascriptNativeFloatArray'");
  10770. return static_cast<JavascriptNativeFloatArray *>(RecyclableObject::FromVar(aValue));
  10771. }
  10772. template int Js::JavascriptArray::GetParamForIndexOf<unsigned int>(unsigned int, Js::Arguments const&, void*&, unsigned int&, Js::ScriptContext*);
  10773. template bool Js::JavascriptArray::ArrayElementEnumerator::MoveNext<void*>();
  10774. template void Js::JavascriptArray::SetArrayLiteralItem<void*>(unsigned int, void*);
  10775. template void* Js::JavascriptArray::TemplatedIndexOfHelper<false, Js::TypedArrayBase, unsigned int>(Js::TypedArrayBase*, void*, unsigned int, unsigned int, Js::ScriptContext*);
  10776. template void* Js::JavascriptArray::TemplatedIndexOfHelper<true, Js::TypedArrayBase, unsigned int>(Js::TypedArrayBase*, void*, unsigned int, unsigned int, Js::ScriptContext*);
  10777. } //namespace Js