| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262132631326413265132661326713268132691327013271132721327313274132751327613277132781327913280132811328213283132841328513286132871328813289132901329113292132931329413295132961329713298132991330013301133021330313304133051330613307133081330913310133111331213313133141331513316133171331813319133201332113322133231332413325133261332713328133291333013331133321333313334133351333613337133381333913340133411334213343133441334513346133471334813349133501335113352133531335413355133561335713358133591336013361133621336313364133651336613367133681336913370133711337213373133741337513376133771337813379133801338113382133831338413385133861338713388133891339013391133921339313394133951339613397133981339913400134011340213403134041340513406134071340813409134101341113412134131341413415134161341713418134191342013421134221342313424134251342613427134281342913430134311343213433134341343513436134371343813439134401344113442134431344413445134461344713448134491345013451134521345313454134551345613457134581345913460134611346213463134641346513466134671346813469134701347113472134731347413475134761347713478134791348013481134821348313484134851348613487134881348913490134911349213493134941349513496134971349813499135001350113502135031350413505135061350713508135091351013511135121351313514135151351613517135181351913520135211352213523135241352513526135271352813529135301353113532135331353413535135361353713538135391354013541135421354313544135451354613547135481354913550135511355213553135541355513556135571355813559135601356113562135631356413565135661356713568135691357013571135721357313574135751357613577135781357913580135811358213583135841358513586135871358813589135901359113592135931359413595135961359713598135991360013601136021360313604136051360613607136081360913610136111361213613136141361513616136171361813619136201362113622136231362413625136261362713628136291363013631136321363313634136351363613637136381363913640136411364213643136441364513646136471364813649136501365113652136531365413655136561365713658136591366013661136621366313664136651366613667136681366913670136711367213673136741367513676136771367813679136801368113682136831368413685136861368713688136891369013691136921369313694136951369613697136981369913700137011370213703137041370513706137071370813709137101371113712137131371413715137161371713718137191372013721137221372313724137251372613727137281372913730137311373213733137341373513736137371373813739137401374113742137431374413745137461374713748137491375013751137521375313754137551375613757137581375913760137611376213763137641376513766137671376813769137701377113772137731377413775137761377713778137791378013781137821378313784137851378613787137881378913790137911379213793137941379513796137971379813799138001380113802138031380413805138061380713808138091381013811138121381313814138151381613817138181381913820138211382213823138241382513826138271382813829138301383113832138331383413835138361383713838138391384013841138421384313844138451384613847138481384913850138511385213853138541385513856138571385813859138601386113862138631386413865138661386713868138691387013871138721387313874138751387613877138781387913880138811388213883138841388513886138871388813889138901389113892138931389413895138961389713898138991390013901139021390313904139051390613907139081390913910139111391213913139141391513916139171391813919139201392113922139231392413925139261392713928139291393013931139321393313934139351393613937139381393913940139411394213943139441394513946139471394813949139501395113952139531395413955139561395713958139591396013961139621396313964139651396613967139681396913970139711397213973139741397513976139771397813979139801398113982139831398413985139861398713988139891399013991139921399313994139951399613997139981399914000140011400214003140041400514006140071400814009140101401114012140131401414015140161401714018140191402014021140221402314024140251402614027140281402914030140311403214033140341403514036140371403814039140401404114042140431404414045140461404714048140491405014051140521405314054140551405614057140581405914060140611406214063140641406514066140671406814069140701407114072140731407414075140761407714078140791408014081140821408314084140851408614087140881408914090140911409214093140941409514096140971409814099141001410114102141031410414105141061410714108141091411014111141121411314114141151411614117141181411914120141211412214123141241412514126141271412814129141301413114132141331413414135141361413714138141391414014141141421414314144141451414614147141481414914150141511415214153141541415514156141571415814159141601416114162141631416414165141661416714168141691417014171141721417314174141751417614177141781417914180141811418214183141841418514186141871418814189141901419114192141931419414195141961419714198141991420014201142021420314204142051420614207142081420914210142111421214213142141421514216142171421814219142201422114222142231422414225142261422714228142291423014231142321423314234142351423614237142381423914240142411424214243142441424514246142471424814249142501425114252142531425414255142561425714258142591426014261142621426314264142651426614267142681426914270142711427214273142741427514276142771427814279142801428114282142831428414285142861428714288142891429014291142921429314294142951429614297142981429914300143011430214303143041430514306143071430814309143101431114312143131431414315143161431714318143191432014321143221432314324143251432614327143281432914330143311433214333143341433514336143371433814339143401434114342143431434414345143461434714348143491435014351143521435314354143551435614357143581435914360143611436214363143641436514366143671436814369143701437114372143731437414375143761437714378143791438014381143821438314384143851438614387143881438914390143911439214393143941439514396143971439814399144001440114402144031440414405144061440714408144091441014411144121441314414144151441614417144181441914420144211442214423144241442514426144271442814429144301443114432144331443414435144361443714438144391444014441144421444314444144451444614447144481444914450144511445214453144541445514456144571445814459144601446114462144631446414465144661446714468144691447014471144721447314474144751447614477144781447914480144811448214483144841448514486144871448814489144901449114492144931449414495144961449714498144991450014501145021450314504145051450614507145081450914510145111451214513145141451514516145171451814519145201452114522145231452414525145261452714528145291453014531145321453314534145351453614537145381453914540145411454214543145441454514546145471454814549145501455114552145531455414555145561455714558145591456014561145621456314564145651456614567145681456914570145711457214573145741457514576145771457814579145801458114582145831458414585145861458714588145891459014591145921459314594145951459614597145981459914600146011460214603146041460514606146071460814609146101461114612146131461414615146161461714618146191462014621146221462314624146251462614627146281462914630146311463214633146341463514636146371463814639146401464114642146431464414645146461464714648146491465014651146521465314654146551465614657146581465914660146611466214663146641466514666146671466814669146701467114672146731467414675146761467714678146791468014681146821468314684146851468614687146881468914690146911469214693146941469514696146971469814699147001470114702147031470414705147061470714708147091471014711147121471314714147151471614717147181471914720147211472214723147241472514726147271472814729147301473114732147331473414735147361473714738147391474014741147421474314744147451474614747147481474914750147511475214753147541475514756147571475814759147601476114762147631476414765147661476714768147691477014771147721477314774147751477614777147781477914780147811478214783147841478514786147871478814789147901479114792147931479414795147961479714798147991480014801148021480314804148051480614807148081480914810148111481214813148141481514816148171481814819148201482114822148231482414825148261482714828148291483014831148321483314834148351483614837148381483914840148411484214843148441484514846148471484814849148501485114852148531485414855148561485714858148591486014861148621486314864148651486614867148681486914870148711487214873148741487514876148771487814879148801488114882148831488414885148861488714888148891489014891148921489314894148951489614897148981489914900149011490214903149041490514906149071490814909149101491114912149131491414915149161491714918149191492014921149221492314924149251492614927149281492914930149311493214933149341493514936149371493814939149401494114942149431494414945149461494714948149491495014951149521495314954149551495614957149581495914960149611496214963149641496514966149671496814969149701497114972149731497414975149761497714978149791498014981149821498314984149851498614987149881498914990149911499214993149941499514996149971499814999150001500115002150031500415005150061500715008150091501015011150121501315014150151501615017150181501915020150211502215023150241502515026150271502815029150301503115032150331503415035150361503715038150391504015041150421504315044150451504615047150481504915050150511505215053150541505515056150571505815059150601506115062150631506415065150661506715068150691507015071150721507315074150751507615077150781507915080150811508215083150841508515086150871508815089150901509115092150931509415095150961509715098150991510015101151021510315104151051510615107151081510915110151111511215113151141511515116151171511815119151201512115122151231512415125151261512715128151291513015131151321513315134151351513615137151381513915140151411514215143151441514515146151471514815149151501515115152151531515415155151561515715158151591516015161151621516315164151651516615167151681516915170151711517215173151741517515176151771517815179151801518115182151831518415185151861518715188151891519015191151921519315194151951519615197151981519915200152011520215203152041520515206152071520815209152101521115212152131521415215152161521715218152191522015221152221522315224152251522615227152281522915230152311523215233152341523515236152371523815239152401524115242152431524415245152461524715248152491525015251152521525315254152551525615257152581525915260152611526215263152641526515266152671526815269152701527115272152731527415275152761527715278152791528015281152821528315284152851528615287152881528915290152911529215293152941529515296152971529815299153001530115302153031530415305153061530715308153091531015311153121531315314153151531615317153181531915320153211532215323153241532515326153271532815329153301533115332153331533415335153361533715338153391534015341153421534315344153451534615347153481534915350153511535215353153541535515356153571535815359153601536115362153631536415365153661536715368153691537015371153721537315374153751537615377153781537915380153811538215383153841538515386153871538815389153901539115392153931539415395153961539715398153991540015401154021540315404154051540615407154081540915410154111541215413154141541515416154171541815419154201542115422154231542415425154261542715428154291543015431154321543315434154351543615437154381543915440154411544215443154441544515446154471544815449154501545115452154531545415455154561545715458154591546015461154621546315464154651546615467154681546915470154711547215473154741547515476154771547815479154801548115482154831548415485154861548715488154891549015491154921549315494154951549615497154981549915500155011550215503155041550515506155071550815509155101551115512155131551415515155161551715518155191552015521155221552315524155251552615527155281552915530155311553215533155341553515536155371553815539155401554115542155431554415545155461554715548155491555015551155521555315554155551555615557155581555915560155611556215563155641556515566155671556815569155701557115572155731557415575155761557715578155791558015581155821558315584155851558615587155881558915590155911559215593155941559515596155971559815599156001560115602156031560415605156061560715608156091561015611156121561315614156151561615617156181561915620156211562215623156241562515626156271562815629156301563115632156331563415635156361563715638156391564015641156421564315644156451564615647156481564915650156511565215653156541565515656156571565815659156601566115662156631566415665156661566715668156691567015671156721567315674156751567615677156781567915680156811568215683156841568515686156871568815689156901569115692156931569415695156961569715698156991570015701157021570315704157051570615707157081570915710157111571215713157141571515716157171571815719157201572115722157231572415725157261572715728157291573015731157321573315734157351573615737157381573915740157411574215743157441574515746157471574815749157501575115752157531575415755157561575715758157591576015761157621576315764157651576615767157681576915770157711577215773157741577515776157771577815779157801578115782157831578415785157861578715788157891579015791157921579315794157951579615797157981579915800158011580215803158041580515806158071580815809158101581115812158131581415815158161581715818158191582015821158221582315824158251582615827158281582915830158311583215833158341583515836158371583815839158401584115842158431584415845158461584715848158491585015851158521585315854158551585615857158581585915860158611586215863158641586515866158671586815869158701587115872158731587415875158761587715878158791588015881158821588315884158851588615887158881588915890158911589215893158941589515896158971589815899159001590115902159031590415905159061590715908159091591015911159121591315914159151591615917159181591915920159211592215923159241592515926159271592815929159301593115932159331593415935159361593715938159391594015941159421594315944159451594615947159481594915950159511595215953159541595515956159571595815959159601596115962159631596415965159661596715968159691597015971159721597315974159751597615977159781597915980159811598215983159841598515986159871598815989159901599115992159931599415995159961599715998159991600016001160021600316004160051600616007160081600916010160111601216013160141601516016160171601816019160201602116022160231602416025160261602716028160291603016031160321603316034160351603616037160381603916040160411604216043160441604516046160471604816049160501605116052160531605416055160561605716058160591606016061160621606316064160651606616067160681606916070160711607216073160741607516076160771607816079160801608116082160831608416085160861608716088160891609016091160921609316094160951609616097160981609916100161011610216103161041610516106161071610816109161101611116112161131611416115161161611716118161191612016121161221612316124161251612616127161281612916130161311613216133161341613516136161371613816139161401614116142161431614416145161461614716148161491615016151161521615316154161551615616157161581615916160161611616216163161641616516166161671616816169161701617116172161731617416175161761617716178161791618016181161821618316184161851618616187161881618916190161911619216193161941619516196161971619816199162001620116202162031620416205162061620716208162091621016211162121621316214162151621616217162181621916220162211622216223162241622516226162271622816229162301623116232162331623416235162361623716238162391624016241162421624316244162451624616247162481624916250162511625216253162541625516256162571625816259162601626116262162631626416265162661626716268162691627016271162721627316274162751627616277162781627916280162811628216283162841628516286162871628816289162901629116292162931629416295162961629716298162991630016301163021630316304163051630616307163081630916310163111631216313163141631516316163171631816319163201632116322163231632416325163261632716328163291633016331163321633316334163351633616337163381633916340163411634216343163441634516346163471634816349163501635116352163531635416355163561635716358163591636016361163621636316364163651636616367163681636916370163711637216373163741637516376163771637816379163801638116382163831638416385163861638716388163891639016391163921639316394163951639616397163981639916400164011640216403164041640516406164071640816409164101641116412164131641416415164161641716418164191642016421164221642316424164251642616427164281642916430164311643216433164341643516436164371643816439164401644116442164431644416445164461644716448164491645016451164521645316454164551645616457164581645916460164611646216463164641646516466164671646816469164701647116472164731647416475164761647716478164791648016481164821648316484164851648616487164881648916490164911649216493164941649516496164971649816499165001650116502165031650416505165061650716508165091651016511165121651316514165151651616517165181651916520165211652216523165241652516526165271652816529165301653116532165331653416535165361653716538165391654016541165421654316544165451654616547165481654916550165511655216553165541655516556165571655816559165601656116562165631656416565165661656716568165691657016571165721657316574165751657616577165781657916580165811658216583165841658516586165871658816589165901659116592165931659416595165961659716598165991660016601166021660316604166051660616607166081660916610166111661216613166141661516616166171661816619166201662116622166231662416625166261662716628166291663016631166321663316634166351663616637166381663916640166411664216643166441664516646166471664816649166501665116652166531665416655166561665716658166591666016661166621666316664166651666616667166681666916670166711667216673166741667516676166771667816679166801668116682166831668416685166861668716688166891669016691166921669316694166951669616697166981669916700167011670216703167041670516706167071670816709167101671116712167131671416715167161671716718167191672016721167221672316724167251672616727167281672916730167311673216733167341673516736167371673816739167401674116742167431674416745167461674716748167491675016751167521675316754167551675616757167581675916760167611676216763167641676516766167671676816769167701677116772167731677416775167761677716778167791678016781167821678316784167851678616787167881678916790167911679216793167941679516796167971679816799168001680116802168031680416805168061680716808168091681016811168121681316814168151681616817168181681916820168211682216823168241682516826168271682816829168301683116832168331683416835168361683716838168391684016841168421684316844168451684616847168481684916850168511685216853168541685516856168571685816859168601686116862168631686416865168661686716868168691687016871168721687316874168751687616877168781687916880168811688216883168841688516886168871688816889168901689116892168931689416895168961689716898168991690016901169021690316904169051690616907169081690916910169111691216913169141691516916169171691816919169201692116922169231692416925169261692716928169291693016931169321693316934169351693616937169381693916940169411694216943169441694516946169471694816949169501695116952169531695416955169561695716958169591696016961169621696316964169651696616967169681696916970169711697216973169741697516976169771697816979169801698116982169831698416985169861698716988169891699016991169921699316994169951699616997169981699917000170011700217003170041700517006170071700817009170101701117012170131701417015170161701717018170191702017021170221702317024170251702617027170281702917030170311703217033170341703517036170371703817039170401704117042170431704417045170461704717048170491705017051170521705317054170551705617057170581705917060170611706217063170641706517066170671706817069170701707117072170731707417075170761707717078170791708017081170821708317084170851708617087170881708917090170911709217093170941709517096170971709817099171001710117102171031710417105171061710717108171091711017111171121711317114171151711617117171181711917120171211712217123171241712517126171271712817129171301713117132171331713417135171361713717138171391714017141171421714317144171451714617147171481714917150171511715217153171541715517156171571715817159171601716117162171631716417165171661716717168171691717017171171721717317174171751717617177171781717917180171811718217183171841718517186171871718817189171901719117192171931719417195171961719717198171991720017201172021720317204172051720617207172081720917210172111721217213172141721517216172171721817219172201722117222172231722417225172261722717228172291723017231172321723317234172351723617237172381723917240172411724217243172441724517246172471724817249172501725117252172531725417255172561725717258172591726017261172621726317264172651726617267172681726917270172711727217273172741727517276172771727817279172801728117282172831728417285172861728717288172891729017291172921729317294172951729617297172981729917300173011730217303173041730517306173071730817309173101731117312173131731417315173161731717318173191732017321173221732317324173251732617327173281732917330173311733217333173341733517336173371733817339173401734117342173431734417345173461734717348173491735017351173521735317354173551735617357173581735917360173611736217363173641736517366173671736817369173701737117372173731737417375173761737717378173791738017381173821738317384173851738617387173881738917390173911739217393173941739517396173971739817399174001740117402174031740417405174061740717408174091741017411174121741317414174151741617417174181741917420174211742217423174241742517426174271742817429174301743117432174331743417435174361743717438174391744017441174421744317444174451744617447174481744917450174511745217453174541745517456174571745817459174601746117462174631746417465174661746717468174691747017471174721747317474174751747617477174781747917480174811748217483174841748517486174871748817489174901749117492174931749417495174961749717498174991750017501175021750317504175051750617507175081750917510175111751217513175141751517516175171751817519175201752117522175231752417525175261752717528175291753017531175321753317534175351753617537175381753917540175411754217543175441754517546175471754817549175501755117552175531755417555175561755717558175591756017561175621756317564175651756617567175681756917570175711757217573175741757517576175771757817579175801758117582175831758417585175861758717588175891759017591175921759317594175951759617597175981759917600176011760217603176041760517606176071760817609176101761117612176131761417615176161761717618176191762017621176221762317624176251762617627176281762917630176311763217633176341763517636176371763817639176401764117642176431764417645176461764717648176491765017651176521765317654176551765617657176581765917660176611766217663176641766517666176671766817669176701767117672176731767417675176761767717678176791768017681176821768317684176851768617687176881768917690176911769217693176941769517696176971769817699177001770117702177031770417705177061770717708177091771017711177121771317714177151771617717177181771917720177211772217723177241772517726177271772817729177301773117732177331773417735177361773717738177391774017741177421774317744177451774617747177481774917750177511775217753177541775517756177571775817759177601776117762177631776417765177661776717768177691777017771177721777317774177751777617777177781777917780177811778217783177841778517786177871778817789177901779117792177931779417795177961779717798177991780017801178021780317804178051780617807178081780917810178111781217813178141781517816178171781817819178201782117822178231782417825178261782717828178291783017831178321783317834178351783617837178381783917840178411784217843178441784517846178471784817849178501785117852178531785417855178561785717858178591786017861178621786317864178651786617867178681786917870178711787217873178741787517876178771787817879178801788117882178831788417885178861788717888178891789017891178921789317894178951789617897178981789917900179011790217903179041790517906179071790817909179101791117912179131791417915179161791717918179191792017921179221792317924179251792617927179281792917930179311793217933179341793517936179371793817939179401794117942179431794417945179461794717948179491795017951179521795317954179551795617957179581795917960179611796217963179641796517966179671796817969179701797117972179731797417975179761797717978179791798017981179821798317984179851798617987179881798917990179911799217993179941799517996179971799817999180001800118002180031800418005180061800718008180091801018011180121801318014180151801618017180181801918020180211802218023180241802518026180271802818029180301803118032180331803418035180361803718038180391804018041180421804318044180451804618047180481804918050180511805218053180541805518056180571805818059180601806118062180631806418065180661806718068180691807018071180721807318074180751807618077180781807918080180811808218083180841808518086180871808818089180901809118092180931809418095180961809718098180991810018101181021810318104181051810618107181081810918110181111811218113181141811518116181171811818119181201812118122181231812418125181261812718128181291813018131181321813318134181351813618137181381813918140181411814218143181441814518146181471814818149181501815118152181531815418155181561815718158181591816018161181621816318164181651816618167181681816918170181711817218173181741817518176181771817818179181801818118182181831818418185181861818718188181891819018191181921819318194181951819618197181981819918200182011820218203182041820518206182071820818209182101821118212182131821418215182161821718218182191822018221182221822318224182251822618227182281822918230182311823218233182341823518236182371823818239182401824118242182431824418245182461824718248182491825018251182521825318254182551825618257182581825918260182611826218263182641826518266182671826818269182701827118272182731827418275182761827718278182791828018281182821828318284182851828618287182881828918290182911829218293182941829518296182971829818299183001830118302183031830418305183061830718308183091831018311183121831318314183151831618317183181831918320183211832218323183241832518326183271832818329183301833118332183331833418335183361833718338183391834018341183421834318344183451834618347183481834918350183511835218353183541835518356183571835818359183601836118362183631836418365183661836718368183691837018371183721837318374183751837618377183781837918380183811838218383183841838518386183871838818389183901839118392183931839418395183961839718398183991840018401184021840318404184051840618407184081840918410184111841218413184141841518416184171841818419184201842118422184231842418425184261842718428184291843018431184321843318434184351843618437184381843918440184411844218443184441844518446184471844818449184501845118452184531845418455184561845718458184591846018461184621846318464184651846618467184681846918470184711847218473184741847518476184771847818479184801848118482184831848418485184861848718488184891849018491184921849318494184951849618497184981849918500185011850218503185041850518506185071850818509185101851118512185131851418515185161851718518185191852018521185221852318524185251852618527185281852918530185311853218533185341853518536185371853818539185401854118542185431854418545185461854718548185491855018551185521855318554185551855618557185581855918560185611856218563185641856518566185671856818569185701857118572185731857418575185761857718578185791858018581185821858318584185851858618587185881858918590185911859218593185941859518596185971859818599186001860118602186031860418605186061860718608186091861018611186121861318614186151861618617186181861918620186211862218623186241862518626186271862818629186301863118632186331863418635186361863718638186391864018641186421864318644186451864618647186481864918650186511865218653186541865518656186571865818659186601866118662186631866418665186661866718668186691867018671186721867318674186751867618677186781867918680186811868218683186841868518686186871868818689186901869118692186931869418695186961869718698186991870018701187021870318704187051870618707187081870918710187111871218713187141871518716187171871818719187201872118722187231872418725187261872718728187291873018731187321873318734187351873618737187381873918740187411874218743187441874518746187471874818749187501875118752187531875418755187561875718758187591876018761187621876318764187651876618767187681876918770187711877218773187741877518776187771877818779187801878118782187831878418785187861878718788187891879018791187921879318794187951879618797187981879918800188011880218803188041880518806188071880818809188101881118812188131881418815188161881718818188191882018821188221882318824188251882618827188281882918830188311883218833188341883518836188371883818839188401884118842188431884418845188461884718848188491885018851188521885318854188551885618857188581885918860188611886218863188641886518866188671886818869188701887118872188731887418875188761887718878188791888018881188821888318884188851888618887188881888918890188911889218893188941889518896188971889818899189001890118902189031890418905189061890718908189091891018911189121891318914189151891618917189181891918920189211892218923189241892518926189271892818929189301893118932189331893418935189361893718938189391894018941189421894318944189451894618947189481894918950189511895218953189541895518956189571895818959189601896118962189631896418965189661896718968189691897018971189721897318974189751897618977189781897918980189811898218983189841898518986189871898818989189901899118992189931899418995189961899718998189991900019001190021900319004190051900619007190081900919010190111901219013190141901519016190171901819019190201902119022190231902419025190261902719028190291903019031190321903319034190351903619037190381903919040190411904219043190441904519046190471904819049190501905119052190531905419055190561905719058190591906019061190621906319064190651906619067190681906919070190711907219073190741907519076190771907819079190801908119082190831908419085190861908719088190891909019091190921909319094190951909619097190981909919100191011910219103191041910519106191071910819109191101911119112191131911419115191161911719118191191912019121191221912319124191251912619127191281912919130191311913219133191341913519136191371913819139191401914119142191431914419145191461914719148191491915019151191521915319154191551915619157191581915919160191611916219163191641916519166191671916819169191701917119172191731917419175191761917719178191791918019181191821918319184191851918619187191881918919190191911919219193191941919519196191971919819199192001920119202192031920419205192061920719208192091921019211192121921319214192151921619217192181921919220192211922219223192241922519226192271922819229192301923119232192331923419235192361923719238192391924019241192421924319244192451924619247192481924919250192511925219253192541925519256192571925819259192601926119262192631926419265192661926719268192691927019271192721927319274192751927619277192781927919280192811928219283192841928519286192871928819289192901929119292192931929419295192961929719298192991930019301193021930319304193051930619307193081930919310193111931219313193141931519316193171931819319193201932119322193231932419325193261932719328193291933019331193321933319334193351933619337193381933919340193411934219343193441934519346193471934819349193501935119352193531935419355193561935719358193591936019361193621936319364193651936619367193681936919370193711937219373193741937519376193771937819379193801938119382193831938419385193861938719388193891939019391193921939319394193951939619397193981939919400194011940219403194041940519406194071940819409194101941119412194131941419415194161941719418194191942019421194221942319424194251942619427194281942919430194311943219433194341943519436194371943819439194401944119442194431944419445194461944719448194491945019451194521945319454194551945619457194581945919460194611946219463194641946519466194671946819469194701947119472194731947419475194761947719478194791948019481194821948319484194851948619487194881948919490194911949219493194941949519496194971949819499195001950119502195031950419505195061950719508195091951019511195121951319514195151951619517195181951919520195211952219523195241952519526195271952819529195301953119532195331953419535195361953719538195391954019541195421954319544195451954619547195481954919550195511955219553195541955519556195571955819559195601956119562195631956419565195661956719568195691957019571195721957319574195751957619577195781957919580195811958219583195841958519586195871958819589195901959119592195931959419595195961959719598195991960019601196021960319604196051960619607196081960919610196111961219613196141961519616196171961819619196201962119622196231962419625196261962719628196291963019631196321963319634196351963619637196381963919640196411964219643196441964519646196471964819649196501965119652196531965419655196561965719658196591966019661196621966319664196651966619667196681966919670196711967219673196741967519676196771967819679196801968119682196831968419685196861968719688196891969019691196921969319694196951969619697196981969919700197011970219703197041970519706197071970819709197101971119712197131971419715197161971719718197191972019721197221972319724197251972619727197281972919730197311973219733197341973519736197371973819739197401974119742197431974419745197461974719748197491975019751197521975319754197551975619757197581975919760197611976219763197641976519766197671976819769197701977119772197731977419775197761977719778197791978019781197821978319784197851978619787197881978919790197911979219793197941979519796197971979819799198001980119802198031980419805198061980719808198091981019811198121981319814198151981619817198181981919820198211982219823198241982519826198271982819829198301983119832198331983419835198361983719838198391984019841198421984319844198451984619847198481984919850198511985219853198541985519856198571985819859198601986119862198631986419865198661986719868198691987019871198721987319874198751987619877198781987919880198811988219883198841988519886198871988819889198901989119892198931989419895198961989719898198991990019901199021990319904199051990619907199081990919910199111991219913199141991519916199171991819919199201992119922199231992419925199261992719928199291993019931199321993319934199351993619937199381993919940199411994219943199441994519946199471994819949199501995119952199531995419955199561995719958199591996019961199621996319964199651996619967199681996919970199711997219973199741997519976199771997819979199801998119982199831998419985199861998719988199891999019991199921999319994199951999619997199981999920000200012000220003200042000520006200072000820009200102001120012200132001420015200162001720018200192002020021200222002320024200252002620027200282002920030200312003220033200342003520036200372003820039200402004120042200432004420045200462004720048200492005020051200522005320054200552005620057200582005920060200612006220063200642006520066200672006820069200702007120072200732007420075200762007720078200792008020081200822008320084200852008620087200882008920090200912009220093200942009520096200972009820099201002010120102201032010420105201062010720108201092011020111201122011320114201152011620117201182011920120201212012220123201242012520126201272012820129201302013120132201332013420135201362013720138201392014020141201422014320144201452014620147201482014920150201512015220153201542015520156201572015820159201602016120162201632016420165201662016720168201692017020171201722017320174201752017620177201782017920180201812018220183201842018520186201872018820189201902019120192201932019420195201962019720198201992020020201202022020320204202052020620207202082020920210202112021220213202142021520216202172021820219202202022120222202232022420225202262022720228202292023020231202322023320234202352023620237202382023920240202412024220243202442024520246202472024820249202502025120252202532025420255202562025720258202592026020261202622026320264202652026620267202682026920270202712027220273202742027520276202772027820279202802028120282202832028420285202862028720288202892029020291202922029320294202952029620297202982029920300203012030220303203042030520306203072030820309203102031120312203132031420315203162031720318203192032020321203222032320324203252032620327203282032920330203312033220333203342033520336203372033820339203402034120342203432034420345203462034720348203492035020351203522035320354203552035620357203582035920360203612036220363203642036520366203672036820369203702037120372203732037420375203762037720378203792038020381203822038320384203852038620387203882038920390203912039220393203942039520396203972039820399204002040120402204032040420405204062040720408204092041020411204122041320414204152041620417204182041920420204212042220423204242042520426204272042820429204302043120432204332043420435204362043720438204392044020441204422044320444204452044620447204482044920450204512045220453204542045520456204572045820459204602046120462204632046420465204662046720468204692047020471204722047320474204752047620477204782047920480204812048220483204842048520486204872048820489204902049120492204932049420495204962049720498204992050020501205022050320504205052050620507205082050920510205112051220513205142051520516205172051820519205202052120522205232052420525205262052720528205292053020531205322053320534205352053620537205382053920540205412054220543205442054520546205472054820549205502055120552205532055420555205562055720558205592056020561205622056320564205652056620567205682056920570205712057220573205742057520576205772057820579205802058120582205832058420585205862058720588205892059020591205922059320594205952059620597205982059920600206012060220603206042060520606206072060820609206102061120612206132061420615206162061720618206192062020621206222062320624206252062620627206282062920630206312063220633206342063520636206372063820639206402064120642206432064420645206462064720648206492065020651206522065320654206552065620657206582065920660206612066220663206642066520666206672066820669206702067120672206732067420675206762067720678206792068020681206822068320684206852068620687206882068920690206912069220693206942069520696206972069820699207002070120702207032070420705207062070720708207092071020711207122071320714207152071620717207182071920720207212072220723207242072520726207272072820729207302073120732207332073420735207362073720738207392074020741207422074320744207452074620747207482074920750207512075220753207542075520756207572075820759207602076120762207632076420765207662076720768207692077020771207722077320774207752077620777207782077920780207812078220783207842078520786207872078820789207902079120792207932079420795207962079720798207992080020801208022080320804208052080620807208082080920810208112081220813208142081520816208172081820819208202082120822208232082420825208262082720828208292083020831208322083320834208352083620837208382083920840208412084220843208442084520846208472084820849208502085120852208532085420855208562085720858208592086020861208622086320864208652086620867208682086920870208712087220873208742087520876208772087820879208802088120882208832088420885208862088720888208892089020891208922089320894208952089620897208982089920900209012090220903209042090520906209072090820909209102091120912209132091420915209162091720918209192092020921209222092320924209252092620927209282092920930209312093220933209342093520936209372093820939209402094120942209432094420945209462094720948209492095020951209522095320954209552095620957209582095920960209612096220963209642096520966209672096820969209702097120972209732097420975209762097720978209792098020981209822098320984209852098620987209882098920990209912099220993209942099520996209972099820999210002100121002210032100421005210062100721008210092101021011210122101321014210152101621017210182101921020210212102221023210242102521026210272102821029210302103121032210332103421035210362103721038210392104021041210422104321044210452104621047210482104921050210512105221053210542105521056210572105821059210602106121062210632106421065210662106721068210692107021071210722107321074210752107621077210782107921080210812108221083210842108521086210872108821089210902109121092210932109421095210962109721098210992110021101211022110321104211052110621107211082110921110211112111221113211142111521116211172111821119211202112121122211232112421125211262112721128211292113021131211322113321134211352113621137211382113921140211412114221143211442114521146211472114821149211502115121152211532115421155211562115721158211592116021161211622116321164211652116621167211682116921170211712117221173211742117521176211772117821179211802118121182211832118421185211862118721188211892119021191211922119321194211952119621197211982119921200212012120221203212042120521206212072120821209212102121121212212132121421215212162121721218212192122021221212222122321224212252122621227212282122921230212312123221233212342123521236212372123821239212402124121242212432124421245212462124721248212492125021251212522125321254212552125621257212582125921260212612126221263212642126521266212672126821269212702127121272212732127421275212762127721278212792128021281212822128321284212852128621287212882128921290212912129221293212942129521296212972129821299213002130121302213032130421305213062130721308213092131021311213122131321314213152131621317213182131921320213212132221323213242132521326213272132821329213302133121332213332133421335213362133721338213392134021341213422134321344213452134621347213482134921350213512135221353213542135521356213572135821359213602136121362213632136421365213662136721368213692137021371213722137321374213752137621377213782137921380213812138221383213842138521386213872138821389213902139121392213932139421395213962139721398213992140021401214022140321404214052140621407214082140921410214112141221413214142141521416214172141821419214202142121422214232142421425214262142721428214292143021431214322143321434214352143621437214382143921440214412144221443214442144521446214472144821449214502145121452214532145421455214562145721458214592146021461214622146321464214652146621467214682146921470214712147221473214742147521476214772147821479214802148121482214832148421485214862148721488214892149021491214922149321494214952149621497214982149921500215012150221503215042150521506215072150821509215102151121512215132151421515215162151721518215192152021521215222152321524215252152621527215282152921530215312153221533215342153521536215372153821539215402154121542215432154421545215462154721548215492155021551215522155321554215552155621557215582155921560215612156221563215642156521566215672156821569215702157121572215732157421575215762157721578215792158021581215822158321584215852158621587215882158921590215912159221593215942159521596215972159821599216002160121602216032160421605216062160721608216092161021611216122161321614216152161621617216182161921620216212162221623216242162521626216272162821629216302163121632216332163421635216362163721638216392164021641216422164321644216452164621647216482164921650216512165221653216542165521656216572165821659216602166121662216632166421665216662166721668216692167021671216722167321674216752167621677216782167921680216812168221683216842168521686216872168821689216902169121692216932169421695216962169721698216992170021701217022170321704217052170621707217082170921710217112171221713217142171521716217172171821719217202172121722217232172421725217262172721728217292173021731217322173321734217352173621737217382173921740217412174221743217442174521746217472174821749217502175121752217532175421755217562175721758217592176021761217622176321764217652176621767217682176921770217712177221773217742177521776217772177821779217802178121782217832178421785217862178721788217892179021791217922179321794217952179621797217982179921800218012180221803218042180521806218072180821809218102181121812218132181421815218162181721818218192182021821218222182321824218252182621827218282182921830218312183221833218342183521836218372183821839218402184121842218432184421845218462184721848218492185021851218522185321854218552185621857218582185921860218612186221863218642186521866218672186821869218702187121872218732187421875218762187721878218792188021881218822188321884218852188621887218882188921890218912189221893218942189521896218972189821899219002190121902219032190421905219062190721908219092191021911219122191321914219152191621917219182191921920219212192221923219242192521926219272192821929219302193121932219332193421935219362193721938219392194021941219422194321944219452194621947219482194921950219512195221953219542195521956219572195821959219602196121962219632196421965219662196721968219692197021971219722197321974219752197621977219782197921980219812198221983219842198521986219872198821989219902199121992219932199421995219962199721998219992200022001220022200322004220052200622007220082200922010220112201222013220142201522016220172201822019220202202122022220232202422025220262202722028220292203022031220322203322034220352203622037220382203922040220412204222043220442204522046220472204822049220502205122052220532205422055220562205722058220592206022061220622206322064220652206622067220682206922070220712207222073220742207522076220772207822079220802208122082220832208422085220862208722088220892209022091220922209322094220952209622097220982209922100221012210222103221042210522106221072210822109221102211122112221132211422115221162211722118221192212022121221222212322124221252212622127221282212922130221312213222133221342213522136221372213822139221402214122142221432214422145221462214722148221492215022151221522215322154221552215622157221582215922160221612216222163221642216522166221672216822169221702217122172221732217422175221762217722178221792218022181221822218322184221852218622187221882218922190221912219222193221942219522196221972219822199222002220122202222032220422205222062220722208222092221022211222122221322214222152221622217222182221922220222212222222223222242222522226222272222822229222302223122232222332223422235222362223722238222392224022241222422224322244222452224622247222482224922250222512225222253222542225522256222572225822259222602226122262222632226422265222662226722268222692227022271222722227322274222752227622277222782227922280222812228222283222842228522286222872228822289222902229122292222932229422295222962229722298222992230022301223022230322304223052230622307223082230922310223112231222313223142231522316223172231822319223202232122322223232232422325223262232722328223292233022331223322233322334223352233622337223382233922340223412234222343223442234522346223472234822349223502235122352223532235422355223562235722358223592236022361223622236322364223652236622367223682236922370223712237222373223742237522376223772237822379223802238122382223832238422385223862238722388223892239022391223922239322394223952239622397223982239922400224012240222403224042240522406224072240822409224102241122412224132241422415224162241722418224192242022421224222242322424224252242622427224282242922430224312243222433224342243522436224372243822439224402244122442224432244422445224462244722448224492245022451224522245322454224552245622457224582245922460224612246222463224642246522466224672246822469224702247122472224732247422475224762247722478224792248022481224822248322484224852248622487224882248922490224912249222493224942249522496224972249822499225002250122502225032250422505225062250722508225092251022511225122251322514225152251622517225182251922520225212252222523225242252522526225272252822529225302253122532225332253422535225362253722538225392254022541225422254322544225452254622547225482254922550225512255222553225542255522556225572255822559225602256122562225632256422565225662256722568225692257022571225722257322574225752257622577225782257922580225812258222583225842258522586225872258822589225902259122592225932259422595225962259722598225992260022601226022260322604226052260622607226082260922610226112261222613226142261522616226172261822619226202262122622226232262422625226262262722628226292263022631226322263322634226352263622637226382263922640226412264222643226442264522646226472264822649226502265122652226532265422655226562265722658226592266022661226622266322664226652266622667226682266922670226712267222673226742267522676226772267822679226802268122682226832268422685226862268722688226892269022691226922269322694226952269622697226982269922700227012270222703227042270522706227072270822709227102271122712227132271422715227162271722718227192272022721227222272322724227252272622727227282272922730227312273222733227342273522736227372273822739227402274122742227432274422745227462274722748227492275022751227522275322754227552275622757227582275922760227612276222763227642276522766227672276822769227702277122772227732277422775227762277722778227792278022781227822278322784227852278622787227882278922790227912279222793227942279522796227972279822799228002280122802228032280422805228062280722808228092281022811228122281322814228152281622817228182281922820228212282222823228242282522826228272282822829228302283122832228332283422835228362283722838228392284022841228422284322844228452284622847228482284922850228512285222853228542285522856228572285822859228602286122862228632286422865228662286722868228692287022871228722287322874228752287622877228782287922880228812288222883228842288522886228872288822889228902289122892228932289422895228962289722898228992290022901229022290322904229052290622907229082290922910229112291222913229142291522916229172291822919229202292122922229232292422925229262292722928229292293022931229322293322934229352293622937229382293922940229412294222943229442294522946229472294822949229502295122952229532295422955229562295722958229592296022961229622296322964229652296622967229682296922970229712297222973229742297522976229772297822979229802298122982229832298422985229862298722988229892299022991229922299322994229952299622997229982299923000230012300223003230042300523006230072300823009230102301123012230132301423015230162301723018230192302023021230222302323024230252302623027230282302923030230312303223033230342303523036230372303823039230402304123042230432304423045230462304723048230492305023051230522305323054230552305623057230582305923060230612306223063230642306523066230672306823069230702307123072230732307423075230762307723078230792308023081230822308323084230852308623087230882308923090230912309223093230942309523096230972309823099231002310123102231032310423105231062310723108231092311023111231122311323114231152311623117231182311923120231212312223123231242312523126231272312823129231302313123132231332313423135231362313723138231392314023141231422314323144231452314623147231482314923150231512315223153231542315523156231572315823159231602316123162231632316423165231662316723168231692317023171231722317323174231752317623177231782317923180231812318223183231842318523186231872318823189231902319123192231932319423195231962319723198231992320023201232022320323204232052320623207232082320923210232112321223213232142321523216232172321823219232202322123222232232322423225232262322723228232292323023231232322323323234232352323623237232382323923240232412324223243232442324523246232472324823249232502325123252232532325423255232562325723258232592326023261232622326323264232652326623267232682326923270232712327223273232742327523276232772327823279232802328123282232832328423285232862328723288232892329023291232922329323294232952329623297232982329923300233012330223303233042330523306233072330823309233102331123312233132331423315233162331723318233192332023321233222332323324233252332623327233282332923330233312333223333233342333523336233372333823339233402334123342233432334423345233462334723348233492335023351233522335323354233552335623357233582335923360233612336223363233642336523366233672336823369233702337123372233732337423375233762337723378233792338023381233822338323384233852338623387233882338923390233912339223393233942339523396233972339823399234002340123402234032340423405234062340723408234092341023411234122341323414234152341623417234182341923420234212342223423234242342523426234272342823429234302343123432234332343423435234362343723438234392344023441234422344323444234452344623447234482344923450234512345223453234542345523456234572345823459234602346123462234632346423465234662346723468234692347023471234722347323474234752347623477234782347923480234812348223483234842348523486234872348823489234902349123492234932349423495234962349723498234992350023501235022350323504235052350623507235082350923510235112351223513235142351523516235172351823519235202352123522235232352423525235262352723528235292353023531235322353323534235352353623537235382353923540235412354223543235442354523546235472354823549235502355123552235532355423555235562355723558235592356023561235622356323564235652356623567235682356923570235712357223573235742357523576235772357823579235802358123582235832358423585235862358723588235892359023591235922359323594235952359623597235982359923600236012360223603236042360523606236072360823609236102361123612236132361423615236162361723618236192362023621236222362323624236252362623627236282362923630236312363223633236342363523636236372363823639236402364123642236432364423645236462364723648236492365023651236522365323654236552365623657236582365923660236612366223663236642366523666236672366823669236702367123672236732367423675236762367723678236792368023681236822368323684236852368623687236882368923690236912369223693236942369523696236972369823699237002370123702237032370423705237062370723708237092371023711237122371323714237152371623717237182371923720237212372223723237242372523726237272372823729237302373123732237332373423735237362373723738237392374023741237422374323744237452374623747237482374923750237512375223753237542375523756237572375823759237602376123762237632376423765237662376723768237692377023771237722377323774237752377623777237782377923780237812378223783237842378523786237872378823789237902379123792237932379423795237962379723798237992380023801238022380323804238052380623807238082380923810238112381223813238142381523816238172381823819238202382123822238232382423825238262382723828238292383023831238322383323834238352383623837238382383923840238412384223843238442384523846238472384823849238502385123852238532385423855238562385723858238592386023861238622386323864238652386623867238682386923870238712387223873238742387523876238772387823879238802388123882238832388423885238862388723888238892389023891238922389323894238952389623897238982389923900239012390223903239042390523906239072390823909239102391123912239132391423915239162391723918239192392023921239222392323924239252392623927239282392923930239312393223933239342393523936239372393823939239402394123942239432394423945239462394723948239492395023951239522395323954239552395623957239582395923960239612396223963239642396523966239672396823969239702397123972239732397423975239762397723978239792398023981239822398323984239852398623987239882398923990239912399223993239942399523996239972399823999240002400124002240032400424005240062400724008240092401024011240122401324014240152401624017240182401924020240212402224023240242402524026240272402824029240302403124032240332403424035240362403724038240392404024041240422404324044240452404624047240482404924050240512405224053240542405524056240572405824059240602406124062240632406424065240662406724068240692407024071240722407324074240752407624077240782407924080240812408224083240842408524086240872408824089240902409124092240932409424095240962409724098240992410024101241022410324104241052410624107241082410924110241112411224113241142411524116241172411824119241202412124122241232412424125241262412724128241292413024131241322413324134241352413624137241382413924140241412414224143241442414524146241472414824149241502415124152241532415424155241562415724158241592416024161241622416324164241652416624167241682416924170241712417224173241742417524176241772417824179241802418124182241832418424185241862418724188241892419024191241922419324194241952419624197241982419924200242012420224203242042420524206242072420824209242102421124212242132421424215242162421724218242192422024221242222422324224242252422624227242282422924230242312423224233242342423524236242372423824239242402424124242242432424424245242462424724248242492425024251242522425324254242552425624257242582425924260242612426224263242642426524266242672426824269242702427124272242732427424275242762427724278242792428024281242822428324284242852428624287242882428924290242912429224293242942429524296242972429824299243002430124302243032430424305243062430724308243092431024311243122431324314243152431624317243182431924320243212432224323243242432524326243272432824329243302433124332243332433424335243362433724338243392434024341243422434324344243452434624347243482434924350243512435224353243542435524356243572435824359243602436124362243632436424365243662436724368243692437024371243722437324374243752437624377243782437924380243812438224383243842438524386243872438824389243902439124392243932439424395243962439724398243992440024401244022440324404244052440624407244082440924410244112441224413244142441524416244172441824419244202442124422244232442424425244262442724428244292443024431244322443324434244352443624437244382443924440244412444224443244442444524446244472444824449244502445124452244532445424455244562445724458244592446024461244622446324464244652446624467244682446924470244712447224473244742447524476244772447824479244802448124482244832448424485244862448724488244892449024491244922449324494244952449624497244982449924500245012450224503245042450524506245072450824509245102451124512245132451424515245162451724518245192452024521245222452324524245252452624527245282452924530245312453224533245342453524536245372453824539245402454124542245432454424545245462454724548245492455024551245522455324554245552455624557245582455924560245612456224563245642456524566245672456824569245702457124572245732457424575245762457724578245792458024581245822458324584245852458624587245882458924590245912459224593245942459524596245972459824599246002460124602246032460424605246062460724608246092461024611246122461324614246152461624617246182461924620246212462224623246242462524626246272462824629246302463124632246332463424635246362463724638246392464024641246422464324644246452464624647246482464924650246512465224653246542465524656246572465824659246602466124662246632466424665246662466724668246692467024671246722467324674246752467624677246782467924680246812468224683246842468524686246872468824689246902469124692246932469424695246962469724698246992470024701247022470324704247052470624707247082470924710247112471224713247142471524716247172471824719247202472124722247232472424725247262472724728247292473024731247322473324734247352473624737247382473924740247412474224743247442474524746247472474824749247502475124752247532475424755247562475724758247592476024761247622476324764247652476624767247682476924770247712477224773247742477524776247772477824779247802478124782247832478424785247862478724788247892479024791247922479324794247952479624797247982479924800248012480224803248042480524806248072480824809248102481124812248132481424815248162481724818248192482024821248222482324824248252482624827248282482924830248312483224833248342483524836248372483824839248402484124842248432484424845248462484724848248492485024851248522485324854248552485624857248582485924860248612486224863248642486524866248672486824869248702487124872248732487424875248762487724878248792488024881248822488324884248852488624887248882488924890248912489224893248942489524896248972489824899249002490124902249032490424905249062490724908249092491024911249122491324914249152491624917249182491924920249212492224923249242492524926249272492824929249302493124932249332493424935249362493724938249392494024941249422494324944249452494624947249482494924950249512495224953249542495524956249572495824959249602496124962249632496424965249662496724968249692497024971249722497324974249752497624977249782497924980249812498224983249842498524986249872498824989249902499124992249932499424995249962499724998249992500025001250022500325004250052500625007250082500925010250112501225013250142501525016250172501825019250202502125022250232502425025250262502725028250292503025031250322503325034250352503625037250382503925040250412504225043250442504525046250472504825049250502505125052250532505425055250562505725058250592506025061250622506325064250652506625067250682506925070250712507225073250742507525076250772507825079250802508125082250832508425085250862508725088250892509025091250922509325094250952509625097250982509925100251012510225103251042510525106251072510825109251102511125112251132511425115251162511725118251192512025121251222512325124251252512625127251282512925130251312513225133251342513525136251372513825139251402514125142251432514425145251462514725148251492515025151251522515325154251552515625157251582515925160251612516225163251642516525166251672516825169251702517125172251732517425175251762517725178251792518025181251822518325184251852518625187251882518925190251912519225193251942519525196251972519825199252002520125202252032520425205252062520725208252092521025211252122521325214252152521625217252182521925220252212522225223252242522525226252272522825229252302523125232252332523425235252362523725238252392524025241252422524325244252452524625247252482524925250252512525225253252542525525256252572525825259252602526125262252632526425265252662526725268252692527025271252722527325274252752527625277252782527925280252812528225283252842528525286252872528825289252902529125292252932529425295252962529725298252992530025301253022530325304253052530625307253082530925310253112531225313253142531525316253172531825319253202532125322253232532425325253262532725328253292533025331253322533325334253352533625337253382533925340253412534225343253442534525346253472534825349253502535125352253532535425355253562535725358253592536025361253622536325364253652536625367253682536925370253712537225373253742537525376253772537825379253802538125382253832538425385253862538725388253892539025391253922539325394253952539625397253982539925400254012540225403254042540525406254072540825409254102541125412254132541425415254162541725418254192542025421254222542325424254252542625427254282542925430254312543225433254342543525436254372543825439254402544125442254432544425445254462544725448254492545025451254522545325454254552545625457254582545925460254612546225463254642546525466254672546825469254702547125472254732547425475254762547725478254792548025481254822548325484254852548625487254882548925490254912549225493254942549525496254972549825499255002550125502255032550425505255062550725508255092551025511255122551325514255152551625517255182551925520255212552225523255242552525526255272552825529255302553125532255332553425535255362553725538255392554025541255422554325544255452554625547255482554925550255512555225553255542555525556255572555825559255602556125562255632556425565255662556725568255692557025571255722557325574255752557625577255782557925580255812558225583255842558525586255872558825589255902559125592255932559425595255962559725598255992560025601256022560325604256052560625607256082560925610256112561225613256142561525616256172561825619256202562125622256232562425625256262562725628256292563025631256322563325634256352563625637256382563925640256412564225643256442564525646256472564825649256502565125652256532565425655256562565725658256592566025661256622566325664256652566625667256682566925670256712567225673256742567525676256772567825679256802568125682256832568425685256862568725688256892569025691256922569325694256952569625697256982569925700257012570225703257042570525706257072570825709257102571125712257132571425715257162571725718257192572025721257222572325724257252572625727257282572925730257312573225733257342573525736257372573825739257402574125742257432574425745257462574725748257492575025751257522575325754257552575625757257582575925760257612576225763257642576525766257672576825769257702577125772257732577425775257762577725778257792578025781257822578325784257852578625787257882578925790257912579225793257942579525796257972579825799258002580125802258032580425805258062580725808258092581025811258122581325814258152581625817258182581925820258212582225823258242582525826258272582825829258302583125832258332583425835258362583725838258392584025841258422584325844258452584625847258482584925850258512585225853258542585525856258572585825859258602586125862258632586425865258662586725868258692587025871258722587325874258752587625877258782587925880258812588225883258842588525886258872588825889258902589125892258932589425895258962589725898258992590025901259022590325904259052590625907259082590925910259112591225913259142591525916259172591825919259202592125922259232592425925259262592725928259292593025931259322593325934259352593625937259382593925940259412594225943259442594525946259472594825949259502595125952259532595425955259562595725958259592596025961259622596325964259652596625967259682596925970259712597225973259742597525976259772597825979259802598125982259832598425985259862598725988259892599025991259922599325994259952599625997259982599926000260012600226003260042600526006260072600826009260102601126012260132601426015260162601726018260192602026021260222602326024260252602626027260282602926030260312603226033260342603526036260372603826039260402604126042260432604426045260462604726048260492605026051260522605326054260552605626057260582605926060260612606226063260642606526066260672606826069260702607126072260732607426075260762607726078260792608026081260822608326084260852608626087260882608926090260912609226093260942609526096260972609826099261002610126102261032610426105261062610726108261092611026111261122611326114261152611626117261182611926120261212612226123261242612526126261272612826129261302613126132261332613426135261362613726138261392614026141261422614326144261452614626147261482614926150261512615226153261542615526156261572615826159261602616126162261632616426165261662616726168261692617026171261722617326174261752617626177261782617926180261812618226183261842618526186261872618826189261902619126192261932619426195261962619726198261992620026201262022620326204262052620626207262082620926210262112621226213262142621526216262172621826219262202622126222262232622426225262262622726228262292623026231262322623326234262352623626237262382623926240262412624226243262442624526246262472624826249262502625126252262532625426255262562625726258262592626026261262622626326264262652626626267262682626926270262712627226273262742627526276262772627826279262802628126282262832628426285262862628726288262892629026291262922629326294262952629626297262982629926300263012630226303263042630526306263072630826309263102631126312263132631426315263162631726318263192632026321263222632326324263252632626327263282632926330263312633226333263342633526336263372633826339263402634126342263432634426345263462634726348263492635026351263522635326354263552635626357263582635926360263612636226363263642636526366263672636826369263702637126372263732637426375263762637726378263792638026381263822638326384263852638626387263882638926390263912639226393263942639526396263972639826399264002640126402264032640426405264062640726408264092641026411264122641326414264152641626417264182641926420264212642226423264242642526426264272642826429264302643126432264332643426435264362643726438264392644026441264422644326444264452644626447264482644926450264512645226453264542645526456264572645826459264602646126462264632646426465264662646726468264692647026471264722647326474264752647626477264782647926480264812648226483264842648526486264872648826489264902649126492264932649426495264962649726498264992650026501265022650326504265052650626507265082650926510265112651226513265142651526516265172651826519265202652126522265232652426525265262652726528265292653026531265322653326534265352653626537265382653926540265412654226543265442654526546265472654826549265502655126552265532655426555265562655726558265592656026561265622656326564265652656626567265682656926570265712657226573265742657526576265772657826579265802658126582265832658426585265862658726588265892659026591265922659326594265952659626597265982659926600266012660226603266042660526606266072660826609266102661126612266132661426615266162661726618266192662026621266222662326624266252662626627266282662926630266312663226633266342663526636266372663826639266402664126642266432664426645266462664726648266492665026651266522665326654266552665626657266582665926660266612666226663266642666526666266672666826669266702667126672266732667426675266762667726678266792668026681266822668326684266852668626687266882668926690266912669226693266942669526696266972669826699267002670126702267032670426705267062670726708267092671026711267122671326714267152671626717267182671926720267212672226723267242672526726267272672826729267302673126732267332673426735267362673726738267392674026741267422674326744267452674626747267482674926750267512675226753267542675526756267572675826759267602676126762267632676426765267662676726768267692677026771267722677326774267752677626777267782677926780267812678226783267842678526786267872678826789267902679126792267932679426795267962679726798267992680026801268022680326804268052680626807268082680926810268112681226813268142681526816268172681826819268202682126822268232682426825268262682726828268292683026831268322683326834268352683626837268382683926840268412684226843268442684526846268472684826849268502685126852268532685426855268562685726858268592686026861268622686326864268652686626867268682686926870268712687226873268742687526876268772687826879268802688126882268832688426885268862688726888268892689026891268922689326894268952689626897268982689926900269012690226903269042690526906269072690826909269102691126912269132691426915269162691726918269192692026921269222692326924269252692626927269282692926930269312693226933269342693526936269372693826939269402694126942269432694426945269462694726948269492695026951269522695326954269552695626957269582695926960269612696226963269642696526966269672696826969269702697126972269732697426975269762697726978269792698026981269822698326984269852698626987269882698926990269912699226993269942699526996269972699826999270002700127002270032700427005270062700727008270092701027011270122701327014270152701627017270182701927020270212702227023270242702527026270272702827029270302703127032270332703427035270362703727038270392704027041270422704327044270452704627047270482704927050270512705227053270542705527056270572705827059270602706127062270632706427065270662706727068270692707027071270722707327074270752707627077270782707927080270812708227083270842708527086270872708827089270902709127092270932709427095270962709727098270992710027101271022710327104271052710627107271082710927110271112711227113271142711527116271172711827119271202712127122271232712427125271262712727128271292713027131271322713327134271352713627137271382713927140271412714227143271442714527146271472714827149271502715127152271532715427155271562715727158271592716027161271622716327164271652716627167271682716927170271712717227173271742717527176271772717827179271802718127182271832718427185271862718727188271892719027191271922719327194271952719627197271982719927200272012720227203272042720527206272072720827209272102721127212272132721427215272162721727218272192722027221272222722327224272252722627227272282722927230272312723227233272342723527236272372723827239272402724127242272432724427245272462724727248272492725027251272522725327254272552725627257272582725927260272612726227263272642726527266272672726827269272702727127272272732727427275272762727727278272792728027281272822728327284272852728627287272882728927290272912729227293272942729527296272972729827299273002730127302273032730427305273062730727308273092731027311273122731327314273152731627317273182731927320273212732227323273242732527326273272732827329273302733127332273332733427335273362733727338273392734027341273422734327344273452734627347273482734927350273512735227353273542735527356273572735827359273602736127362273632736427365273662736727368273692737027371273722737327374273752737627377273782737927380273812738227383273842738527386273872738827389273902739127392273932739427395273962739727398273992740027401274022740327404274052740627407274082740927410274112741227413274142741527416274172741827419274202742127422274232742427425274262742727428274292743027431274322743327434274352743627437274382743927440274412744227443274442744527446274472744827449274502745127452274532745427455274562745727458274592746027461274622746327464274652746627467274682746927470274712747227473274742747527476274772747827479274802748127482274832748427485274862748727488274892749027491274922749327494274952749627497274982749927500275012750227503275042750527506275072750827509275102751127512275132751427515275162751727518275192752027521275222752327524275252752627527275282752927530275312753227533275342753527536275372753827539275402754127542275432754427545275462754727548275492755027551275522755327554275552755627557275582755927560275612756227563275642756527566275672756827569275702757127572275732757427575275762757727578275792758027581275822758327584275852758627587275882758927590275912759227593275942759527596275972759827599276002760127602276032760427605276062760727608276092761027611276122761327614276152761627617276182761927620276212762227623276242762527626276272762827629276302763127632276332763427635276362763727638276392764027641276422764327644276452764627647276482764927650276512765227653276542765527656276572765827659276602766127662276632766427665276662766727668276692767027671276722767327674276752767627677276782767927680276812768227683276842768527686276872768827689276902769127692276932769427695276962769727698276992770027701277022770327704277052770627707277082770927710277112771227713277142771527716277172771827719277202772127722277232772427725277262772727728277292773027731277322773327734277352773627737277382773927740277412774227743277442774527746277472774827749277502775127752277532775427755277562775727758277592776027761277622776327764277652776627767277682776927770277712777227773277742777527776277772777827779277802778127782277832778427785277862778727788277892779027791277922779327794277952779627797277982779927800278012780227803278042780527806278072780827809278102781127812278132781427815278162781727818278192782027821278222782327824278252782627827278282782927830278312783227833278342783527836278372783827839278402784127842278432784427845278462784727848278492785027851278522785327854278552785627857278582785927860278612786227863278642786527866278672786827869278702787127872278732787427875278762787727878278792788027881278822788327884278852788627887278882788927890278912789227893278942789527896278972789827899279002790127902279032790427905279062790727908279092791027911279122791327914279152791627917279182791927920279212792227923279242792527926279272792827929279302793127932279332793427935279362793727938279392794027941279422794327944279452794627947279482794927950279512795227953279542795527956279572795827959279602796127962279632796427965279662796727968279692797027971279722797327974279752797627977279782797927980279812798227983279842798527986279872798827989279902799127992279932799427995279962799727998279992800028001280022800328004280052800628007280082800928010280112801228013280142801528016280172801828019280202802128022280232802428025280262802728028280292803028031280322803328034280352803628037280382803928040280412804228043280442804528046280472804828049280502805128052280532805428055280562805728058280592806028061280622806328064280652806628067280682806928070280712807228073280742807528076280772807828079280802808128082280832808428085280862808728088280892809028091280922809328094280952809628097280982809928100281012810228103281042810528106281072810828109281102811128112281132811428115281162811728118281192812028121281222812328124281252812628127281282812928130281312813228133281342813528136281372813828139281402814128142281432814428145281462814728148281492815028151281522815328154281552815628157281582815928160281612816228163281642816528166281672816828169281702817128172281732817428175281762817728178281792818028181281822818328184281852818628187281882818928190281912819228193281942819528196281972819828199282002820128202282032820428205282062820728208282092821028211282122821328214282152821628217282182821928220282212822228223282242822528226282272822828229282302823128232282332823428235282362823728238282392824028241282422824328244282452824628247282482824928250282512825228253282542825528256282572825828259282602826128262282632826428265282662826728268282692827028271282722827328274282752827628277282782827928280282812828228283282842828528286282872828828289282902829128292282932829428295282962829728298282992830028301283022830328304283052830628307283082830928310283112831228313283142831528316283172831828319283202832128322283232832428325283262832728328283292833028331283322833328334283352833628337283382833928340283412834228343283442834528346283472834828349283502835128352283532835428355283562835728358283592836028361283622836328364283652836628367283682836928370283712837228373283742837528376283772837828379283802838128382283832838428385283862838728388283892839028391283922839328394283952839628397283982839928400284012840228403284042840528406284072840828409284102841128412284132841428415284162841728418284192842028421284222842328424284252842628427284282842928430284312843228433284342843528436284372843828439284402844128442284432844428445284462844728448284492845028451284522845328454284552845628457284582845928460284612846228463284642846528466284672846828469284702847128472284732847428475284762847728478284792848028481284822848328484284852848628487284882848928490284912849228493284942849528496284972849828499285002850128502285032850428505285062850728508285092851028511285122851328514285152851628517285182851928520285212852228523285242852528526285272852828529285302853128532285332853428535285362853728538285392854028541285422854328544285452854628547285482854928550285512855228553285542855528556285572855828559285602856128562285632856428565285662856728568285692857028571285722857328574285752857628577285782857928580285812858228583285842858528586285872858828589285902859128592285932859428595285962859728598285992860028601286022860328604286052860628607286082860928610286112861228613286142861528616286172861828619286202862128622286232862428625286262862728628286292863028631286322863328634286352863628637286382863928640286412864228643286442864528646286472864828649286502865128652286532865428655286562865728658286592866028661286622866328664286652866628667286682866928670286712867228673286742867528676286772867828679286802868128682286832868428685286862868728688286892869028691286922869328694286952869628697286982869928700287012870228703287042870528706287072870828709287102871128712287132871428715287162871728718287192872028721287222872328724287252872628727287282872928730287312873228733287342873528736287372873828739287402874128742287432874428745287462874728748287492875028751287522875328754287552875628757287582875928760287612876228763287642876528766287672876828769287702877128772287732877428775287762877728778287792878028781287822878328784287852878628787287882878928790287912879228793287942879528796287972879828799288002880128802288032880428805288062880728808288092881028811288122881328814288152881628817288182881928820288212882228823288242882528826288272882828829288302883128832288332883428835288362883728838288392884028841288422884328844288452884628847288482884928850288512885228853288542885528856288572885828859288602886128862288632886428865288662886728868288692887028871288722887328874288752887628877288782887928880288812888228883288842888528886288872888828889288902889128892288932889428895288962889728898288992890028901289022890328904289052890628907289082890928910289112891228913289142891528916289172891828919289202892128922289232892428925289262892728928289292893028931289322893328934289352893628937289382893928940289412894228943289442894528946289472894828949289502895128952289532895428955289562895728958289592896028961289622896328964289652896628967289682896928970289712897228973289742897528976289772897828979289802898128982289832898428985289862898728988289892899028991289922899328994289952899628997289982899929000290012900229003290042900529006290072900829009290102901129012290132901429015290162901729018290192902029021290222902329024290252902629027290282902929030290312903229033290342903529036290372903829039290402904129042290432904429045290462904729048290492905029051290522905329054290552905629057290582905929060290612906229063290642906529066290672906829069290702907129072290732907429075290762907729078290792908029081290822908329084290852908629087290882908929090290912909229093290942909529096290972909829099291002910129102291032910429105291062910729108291092911029111291122911329114291152911629117291182911929120291212912229123291242912529126291272912829129291302913129132291332913429135291362913729138291392914029141291422914329144291452914629147291482914929150291512915229153291542915529156291572915829159291602916129162291632916429165291662916729168291692917029171291722917329174291752917629177291782917929180291812918229183291842918529186291872918829189291902919129192291932919429195291962919729198291992920029201292022920329204292052920629207292082920929210292112921229213292142921529216292172921829219292202922129222292232922429225292262922729228292292923029231292322923329234292352923629237292382923929240292412924229243292442924529246292472924829249292502925129252292532925429255292562925729258292592926029261292622926329264292652926629267292682926929270292712927229273292742927529276292772927829279292802928129282292832928429285292862928729288292892929029291292922929329294292952929629297292982929929300293012930229303293042930529306293072930829309293102931129312293132931429315293162931729318293192932029321293222932329324293252932629327293282932929330293312933229333293342933529336293372933829339293402934129342293432934429345293462934729348293492935029351293522935329354293552935629357293582935929360293612936229363293642936529366293672936829369293702937129372293732937429375293762937729378293792938029381293822938329384293852938629387293882938929390293912939229393293942939529396293972939829399294002940129402294032940429405294062940729408294092941029411294122941329414294152941629417294182941929420294212942229423294242942529426294272942829429294302943129432294332943429435294362943729438294392944029441294422944329444294452944629447294482944929450294512945229453294542945529456294572945829459294602946129462294632946429465294662946729468294692947029471294722947329474294752947629477294782947929480294812948229483294842948529486294872948829489294902949129492294932949429495294962949729498294992950029501295022950329504295052950629507295082950929510295112951229513295142951529516295172951829519295202952129522295232952429525295262952729528295292953029531295322953329534295352953629537295382953929540295412954229543295442954529546295472954829549295502955129552295532955429555295562955729558295592956029561295622956329564295652956629567295682956929570295712957229573295742957529576295772957829579295802958129582295832958429585295862958729588295892959029591295922959329594295952959629597295982959929600296012960229603296042960529606296072960829609296102961129612296132961429615296162961729618296192962029621296222962329624296252962629627296282962929630296312963229633296342963529636296372963829639296402964129642296432964429645296462964729648296492965029651296522965329654296552965629657296582965929660296612966229663296642966529666296672966829669296702967129672296732967429675296762967729678296792968029681296822968329684296852968629687296882968929690296912969229693296942969529696296972969829699297002970129702297032970429705297062970729708297092971029711297122971329714297152971629717297182971929720297212972229723297242972529726297272972829729297302973129732297332973429735297362973729738297392974029741297422974329744297452974629747297482974929750297512975229753297542975529756297572975829759297602976129762297632976429765297662976729768297692977029771297722977329774297752977629777297782977929780297812978229783297842978529786297872978829789297902979129792297932979429795297962979729798297992980029801298022980329804298052980629807298082980929810298112981229813298142981529816298172981829819298202982129822298232982429825298262982729828298292983029831298322983329834298352983629837298382983929840298412984229843298442984529846298472984829849298502985129852298532985429855298562985729858298592986029861298622986329864298652986629867298682986929870298712987229873298742987529876298772987829879298802988129882298832988429885298862988729888298892989029891298922989329894298952989629897298982989929900299012990229903299042990529906299072990829909299102991129912299132991429915299162991729918299192992029921299222992329924299252992629927299282992929930299312993229933299342993529936299372993829939299402994129942299432994429945299462994729948299492995029951299522995329954299552995629957299582995929960299612996229963299642996529966299672996829969299702997129972299732997429975299762997729978299792998029981299822998329984299852998629987299882998929990299912999229993299942999529996299972999829999300003000130002300033000430005300063000730008300093001030011300123001330014300153001630017300183001930020300213002230023300243002530026300273002830029300303003130032300333003430035300363003730038300393004030041300423004330044300453004630047300483004930050300513005230053300543005530056300573005830059300603006130062300633006430065300663006730068300693007030071300723007330074300753007630077300783007930080300813008230083300843008530086300873008830089300903009130092300933009430095300963009730098300993010030101301023010330104301053010630107301083010930110301113011230113301143011530116301173011830119301203012130122301233012430125301263012730128301293013030131301323013330134301353013630137301383013930140301413014230143301443014530146301473014830149301503015130152301533015430155301563015730158301593016030161301623016330164301653016630167301683016930170301713017230173301743017530176301773017830179301803018130182301833018430185301863018730188301893019030191301923019330194301953019630197301983019930200302013020230203302043020530206302073020830209302103021130212302133021430215302163021730218302193022030221302223022330224302253022630227302283022930230302313023230233302343023530236302373023830239302403024130242302433024430245302463024730248302493025030251302523025330254302553025630257302583025930260302613026230263302643026530266302673026830269302703027130272302733027430275302763027730278302793028030281302823028330284302853028630287302883028930290302913029230293302943029530296302973029830299303003030130302303033030430305303063030730308303093031030311303123031330314303153031630317303183031930320303213032230323303243032530326303273032830329303303033130332303333033430335303363033730338303393034030341303423034330344303453034630347303483034930350303513035230353303543035530356303573035830359303603036130362303633036430365303663036730368303693037030371303723037330374303753037630377303783037930380303813038230383303843038530386303873038830389303903039130392303933039430395303963039730398303993040030401304023040330404304053040630407304083040930410304113041230413304143041530416304173041830419304203042130422304233042430425304263042730428304293043030431304323043330434304353043630437304383043930440304413044230443304443044530446304473044830449304503045130452304533045430455304563045730458304593046030461304623046330464304653046630467304683046930470304713047230473304743047530476304773047830479304803048130482304833048430485304863048730488304893049030491304923049330494304953049630497304983049930500305013050230503305043050530506305073050830509305103051130512305133051430515305163051730518305193052030521305223052330524305253052630527305283052930530305313053230533305343053530536305373053830539305403054130542305433054430545305463054730548305493055030551305523055330554305553055630557305583055930560305613056230563305643056530566305673056830569305703057130572305733057430575305763057730578305793058030581305823058330584305853058630587305883058930590305913059230593305943059530596305973059830599306003060130602306033060430605306063060730608306093061030611306123061330614306153061630617306183061930620306213062230623306243062530626306273062830629306303063130632306333063430635306363063730638306393064030641306423064330644306453064630647306483064930650306513065230653306543065530656306573065830659306603066130662306633066430665306663066730668306693067030671306723067330674306753067630677306783067930680306813068230683306843068530686306873068830689306903069130692306933069430695306963069730698306993070030701307023070330704307053070630707307083070930710307113071230713307143071530716307173071830719307203072130722307233072430725307263072730728307293073030731307323073330734307353073630737307383073930740307413074230743307443074530746307473074830749307503075130752307533075430755307563075730758307593076030761307623076330764307653076630767307683076930770307713077230773307743077530776307773077830779307803078130782307833078430785307863078730788307893079030791307923079330794307953079630797307983079930800308013080230803308043080530806308073080830809308103081130812308133081430815308163081730818308193082030821308223082330824308253082630827308283082930830308313083230833308343083530836308373083830839308403084130842308433084430845308463084730848308493085030851308523085330854308553085630857308583085930860308613086230863308643086530866308673086830869308703087130872308733087430875308763087730878308793088030881308823088330884308853088630887308883088930890308913089230893308943089530896308973089830899309003090130902309033090430905309063090730908309093091030911309123091330914309153091630917309183091930920309213092230923309243092530926309273092830929309303093130932309333093430935309363093730938309393094030941309423094330944309453094630947309483094930950309513095230953309543095530956309573095830959309603096130962309633096430965309663096730968309693097030971309723097330974309753097630977309783097930980309813098230983309843098530986309873098830989309903099130992309933099430995309963099730998309993100031001310023100331004310053100631007310083100931010310113101231013310143101531016310173101831019310203102131022310233102431025310263102731028310293103031031310323103331034310353103631037310383103931040310413104231043310443104531046310473104831049310503105131052310533105431055310563105731058310593106031061310623106331064310653106631067310683106931070310713107231073310743107531076310773107831079310803108131082310833108431085310863108731088310893109031091310923109331094310953109631097310983109931100311013110231103311043110531106311073110831109311103111131112311133111431115311163111731118311193112031121311223112331124311253112631127311283112931130311313113231133311343113531136311373113831139311403114131142311433114431145311463114731148311493115031151311523115331154311553115631157311583115931160311613116231163311643116531166311673116831169311703117131172311733117431175311763117731178311793118031181311823118331184311853118631187311883118931190311913119231193311943119531196311973119831199312003120131202312033120431205312063120731208312093121031211312123121331214312153121631217312183121931220312213122231223312243122531226312273122831229312303123131232312333123431235312363123731238312393124031241312423124331244312453124631247312483124931250312513125231253312543125531256312573125831259312603126131262312633126431265312663126731268312693127031271312723127331274312753127631277312783127931280312813128231283312843128531286312873128831289312903129131292312933129431295312963129731298312993130031301313023130331304313053130631307313083130931310313113131231313313143131531316313173131831319313203132131322313233132431325313263132731328313293133031331313323133331334313353133631337313383133931340313413134231343313443134531346313473134831349313503135131352313533135431355313563135731358313593136031361313623136331364313653136631367313683136931370313713137231373313743137531376313773137831379313803138131382313833138431385313863138731388313893139031391313923139331394313953139631397313983139931400314013140231403314043140531406314073140831409314103141131412314133141431415314163141731418314193142031421314223142331424314253142631427314283142931430314313143231433314343143531436314373143831439314403144131442314433144431445314463144731448314493145031451314523145331454314553145631457314583145931460314613146231463314643146531466314673146831469314703147131472314733147431475314763147731478314793148031481314823148331484314853148631487314883148931490314913149231493314943149531496314973149831499315003150131502315033150431505315063150731508315093151031511315123151331514315153151631517315183151931520315213152231523315243152531526315273152831529315303153131532315333153431535315363153731538315393154031541315423154331544315453154631547315483154931550315513155231553315543155531556315573155831559315603156131562315633156431565315663156731568315693157031571315723157331574315753157631577315783157931580315813158231583315843158531586315873158831589315903159131592315933159431595315963159731598315993160031601316023160331604316053160631607316083160931610316113161231613316143161531616316173161831619316203162131622316233162431625316263162731628316293163031631316323163331634316353163631637316383163931640316413164231643316443164531646316473164831649316503165131652316533165431655316563165731658316593166031661316623166331664316653166631667316683166931670316713167231673316743167531676316773167831679316803168131682316833168431685316863168731688316893169031691316923169331694316953169631697316983169931700317013170231703317043170531706317073170831709317103171131712317133171431715317163171731718317193172031721317223172331724317253172631727317283172931730317313173231733317343173531736317373173831739317403174131742317433174431745317463174731748317493175031751317523175331754317553175631757317583175931760317613176231763317643176531766317673176831769317703177131772317733177431775317763177731778317793178031781317823178331784317853178631787317883178931790317913179231793317943179531796317973179831799318003180131802318033180431805318063180731808318093181031811318123181331814318153181631817318183181931820318213182231823318243182531826318273182831829318303183131832318333183431835318363183731838318393184031841318423184331844318453184631847318483184931850318513185231853318543185531856318573185831859318603186131862318633186431865318663186731868318693187031871318723187331874318753187631877318783187931880318813188231883318843188531886318873188831889318903189131892318933189431895318963189731898318993190031901319023190331904319053190631907319083190931910319113191231913319143191531916319173191831919319203192131922319233192431925319263192731928319293193031931319323193331934319353193631937319383193931940319413194231943319443194531946319473194831949319503195131952319533195431955319563195731958319593196031961319623196331964319653196631967319683196931970319713197231973319743197531976319773197831979319803198131982319833198431985319863198731988319893199031991319923199331994319953199631997319983199932000320013200232003320043200532006320073200832009320103201132012320133201432015320163201732018320193202032021320223202332024320253202632027320283202932030320313203232033320343203532036320373203832039320403204132042320433204432045320463204732048320493205032051320523205332054320553205632057320583205932060320613206232063320643206532066320673206832069320703207132072320733207432075320763207732078320793208032081320823208332084320853208632087320883208932090320913209232093320943209532096320973209832099321003210132102321033210432105321063210732108321093211032111321123211332114321153211632117321183211932120321213212232123321243212532126321273212832129321303213132132321333213432135321363213732138321393214032141321423214332144321453214632147321483214932150321513215232153321543215532156321573215832159321603216132162321633216432165321663216732168321693217032171321723217332174321753217632177321783217932180321813218232183321843218532186321873218832189321903219132192321933219432195321963219732198321993220032201322023220332204322053220632207322083220932210322113221232213322143221532216322173221832219322203222132222322233222432225322263222732228322293223032231322323223332234322353223632237322383223932240322413224232243322443224532246322473224832249322503225132252322533225432255322563225732258322593226032261322623226332264322653226632267322683226932270322713227232273322743227532276322773227832279322803228132282322833228432285322863228732288322893229032291322923229332294322953229632297322983229932300323013230232303323043230532306323073230832309323103231132312323133231432315323163231732318323193232032321323223232332324323253232632327323283232932330323313233232333323343233532336323373233832339323403234132342323433234432345323463234732348323493235032351323523235332354323553235632357323583235932360323613236232363323643236532366323673236832369323703237132372323733237432375323763237732378323793238032381323823238332384323853238632387323883238932390323913239232393323943239532396323973239832399324003240132402324033240432405324063240732408324093241032411324123241332414324153241632417324183241932420324213242232423324243242532426324273242832429324303243132432324333243432435324363243732438324393244032441324423244332444324453244632447324483244932450324513245232453324543245532456324573245832459324603246132462324633246432465324663246732468324693247032471324723247332474324753247632477324783247932480324813248232483324843248532486324873248832489324903249132492324933249432495324963249732498324993250032501325023250332504325053250632507325083250932510325113251232513325143251532516325173251832519325203252132522325233252432525325263252732528325293253032531325323253332534325353253632537325383253932540325413254232543325443254532546325473254832549325503255132552325533255432555325563255732558325593256032561325623256332564325653256632567325683256932570325713257232573325743257532576325773257832579325803258132582325833258432585325863258732588325893259032591325923259332594325953259632597325983259932600326013260232603326043260532606326073260832609326103261132612326133261432615326163261732618326193262032621326223262332624326253262632627326283262932630326313263232633326343263532636326373263832639326403264132642326433264432645326463264732648326493265032651326523265332654326553265632657326583265932660326613266232663326643266532666326673266832669326703267132672326733267432675326763267732678326793268032681326823268332684326853268632687326883268932690326913269232693326943269532696326973269832699327003270132702327033270432705327063270732708327093271032711327123271332714327153271632717327183271932720327213272232723327243272532726327273272832729327303273132732327333273432735327363273732738327393274032741327423274332744327453274632747327483274932750327513275232753327543275532756327573275832759327603276132762327633276432765327663276732768327693277032771327723277332774327753277632777327783277932780327813278232783327843278532786327873278832789327903279132792327933279432795327963279732798327993280032801328023280332804328053280632807328083280932810328113281232813328143281532816328173281832819328203282132822328233282432825328263282732828328293283032831328323283332834328353283632837328383283932840328413284232843328443284532846328473284832849328503285132852328533285432855328563285732858328593286032861328623286332864328653286632867328683286932870328713287232873328743287532876328773287832879328803288132882328833288432885328863288732888328893289032891328923289332894328953289632897328983289932900329013290232903329043290532906329073290832909329103291132912329133291432915329163291732918329193292032921329223292332924329253292632927329283292932930329313293232933329343293532936329373293832939329403294132942329433294432945329463294732948329493295032951329523295332954329553295632957329583295932960329613296232963329643296532966329673296832969329703297132972329733297432975329763297732978329793298032981329823298332984329853298632987329883298932990329913299232993329943299532996329973299832999330003300133002330033300433005330063300733008330093301033011330123301333014330153301633017330183301933020330213302233023330243302533026330273302833029330303303133032330333303433035330363303733038330393304033041330423304333044330453304633047330483304933050330513305233053330543305533056330573305833059330603306133062330633306433065330663306733068330693307033071330723307333074330753307633077330783307933080330813308233083330843308533086330873308833089330903309133092330933309433095330963309733098330993310033101331023310333104331053310633107331083310933110331113311233113331143311533116331173311833119331203312133122331233312433125331263312733128331293313033131331323313333134331353313633137331383313933140331413314233143331443314533146331473314833149331503315133152331533315433155331563315733158331593316033161331623316333164331653316633167331683316933170331713317233173331743317533176331773317833179331803318133182331833318433185331863318733188331893319033191331923319333194331953319633197331983319933200332013320233203332043320533206332073320833209332103321133212332133321433215332163321733218332193322033221332223322333224332253322633227332283322933230332313323233233332343323533236332373323833239332403324133242332433324433245332463324733248332493325033251332523325333254332553325633257332583325933260332613326233263332643326533266332673326833269332703327133272332733327433275332763327733278332793328033281332823328333284332853328633287332883328933290332913329233293332943329533296332973329833299333003330133302333033330433305333063330733308333093331033311333123331333314333153331633317333183331933320333213332233323333243332533326333273332833329333303333133332333333333433335333363333733338333393334033341333423334333344333453334633347333483334933350333513335233353333543335533356333573335833359333603336133362333633336433365333663336733368333693337033371333723337333374333753337633377333783337933380333813338233383333843338533386333873338833389333903339133392333933339433395333963339733398333993340033401334023340333404334053340633407334083340933410334113341233413334143341533416334173341833419334203342133422334233342433425334263342733428334293343033431334323343333434334353343633437334383343933440334413344233443334443344533446334473344833449334503345133452334533345433455334563345733458334593346033461334623346333464334653346633467334683346933470334713347233473334743347533476334773347833479334803348133482334833348433485334863348733488334893349033491334923349333494334953349633497334983349933500335013350233503335043350533506335073350833509335103351133512335133351433515335163351733518335193352033521335223352333524335253352633527335283352933530335313353233533335343353533536335373353833539335403354133542335433354433545335463354733548335493355033551335523355333554335553355633557335583355933560335613356233563335643356533566335673356833569335703357133572335733357433575335763357733578335793358033581335823358333584335853358633587335883358933590335913359233593335943359533596335973359833599336003360133602336033360433605336063360733608336093361033611336123361333614336153361633617336183361933620336213362233623336243362533626336273362833629336303363133632336333363433635336363363733638336393364033641336423364333644336453364633647336483364933650336513365233653336543365533656336573365833659336603366133662336633366433665336663366733668336693367033671336723367333674336753367633677336783367933680336813368233683336843368533686336873368833689336903369133692336933369433695336963369733698336993370033701337023370333704337053370633707337083370933710337113371233713337143371533716337173371833719337203372133722337233372433725337263372733728337293373033731337323373333734337353373633737337383373933740337413374233743337443374533746337473374833749337503375133752337533375433755337563375733758337593376033761337623376333764337653376633767337683376933770337713377233773337743377533776337773377833779337803378133782337833378433785337863378733788337893379033791337923379333794337953379633797337983379933800338013380233803338043380533806338073380833809338103381133812338133381433815338163381733818338193382033821338223382333824338253382633827338283382933830338313383233833338343383533836338373383833839338403384133842338433384433845338463384733848338493385033851338523385333854338553385633857338583385933860338613386233863338643386533866338673386833869338703387133872338733387433875338763387733878338793388033881338823388333884338853388633887338883388933890338913389233893338943389533896338973389833899339003390133902339033390433905339063390733908339093391033911339123391333914339153391633917339183391933920339213392233923339243392533926339273392833929339303393133932339333393433935339363393733938339393394033941339423394333944339453394633947339483394933950339513395233953339543395533956339573395833959339603396133962339633396433965339663396733968339693397033971339723397333974339753397633977339783397933980339813398233983339843398533986339873398833989339903399133992339933399433995339963399733998339993400034001340023400334004340053400634007340083400934010340113401234013340143401534016340173401834019340203402134022340233402434025340263402734028340293403034031340323403334034340353403634037340383403934040340413404234043340443404534046340473404834049340503405134052340533405434055340563405734058340593406034061340623406334064340653406634067340683406934070340713407234073340743407534076340773407834079340803408134082340833408434085340863408734088340893409034091340923409334094340953409634097340983409934100341013410234103341043410534106341073410834109341103411134112341133411434115341163411734118341193412034121341223412334124341253412634127341283412934130341313413234133341343413534136341373413834139341403414134142341433414434145341463414734148341493415034151341523415334154341553415634157341583415934160341613416234163341643416534166341673416834169341703417134172341733417434175341763417734178341793418034181341823418334184341853418634187341883418934190341913419234193341943419534196341973419834199342003420134202342033420434205342063420734208342093421034211342123421334214342153421634217342183421934220342213422234223342243422534226342273422834229342303423134232342333423434235342363423734238342393424034241342423424334244342453424634247342483424934250342513425234253342543425534256342573425834259342603426134262342633426434265342663426734268342693427034271342723427334274342753427634277342783427934280342813428234283342843428534286342873428834289342903429134292342933429434295342963429734298342993430034301343023430334304343053430634307343083430934310343113431234313343143431534316343173431834319343203432134322343233432434325343263432734328343293433034331343323433334334343353433634337343383433934340343413434234343343443434534346343473434834349343503435134352343533435434355343563435734358343593436034361343623436334364343653436634367343683436934370343713437234373343743437534376343773437834379343803438134382343833438434385343863438734388343893439034391343923439334394343953439634397343983439934400344013440234403344043440534406344073440834409344103441134412344133441434415344163441734418344193442034421344223442334424344253442634427344283442934430344313443234433344343443534436344373443834439344403444134442344433444434445344463444734448344493445034451344523445334454344553445634457344583445934460344613446234463344643446534466344673446834469344703447134472344733447434475344763447734478344793448034481344823448334484344853448634487344883448934490344913449234493344943449534496344973449834499345003450134502345033450434505345063450734508345093451034511345123451334514345153451634517345183451934520345213452234523345243452534526345273452834529345303453134532345333453434535345363453734538345393454034541345423454334544345453454634547345483454934550345513455234553345543455534556345573455834559345603456134562345633456434565345663456734568345693457034571345723457334574345753457634577345783457934580345813458234583345843458534586345873458834589345903459134592345933459434595345963459734598345993460034601346023460334604346053460634607346083460934610346113461234613346143461534616346173461834619346203462134622346233462434625346263462734628346293463034631346323463334634346353463634637346383463934640346413464234643346443464534646346473464834649346503465134652346533465434655346563465734658346593466034661346623466334664346653466634667346683466934670346713467234673346743467534676346773467834679346803468134682346833468434685346863468734688346893469034691346923469334694346953469634697346983469934700347013470234703347043470534706347073470834709347103471134712347133471434715347163471734718347193472034721347223472334724347253472634727347283472934730347313473234733347343473534736347373473834739347403474134742347433474434745347463474734748347493475034751347523475334754347553475634757347583475934760347613476234763347643476534766347673476834769347703477134772347733477434775347763477734778347793478034781347823478334784347853478634787347883478934790347913479234793347943479534796347973479834799348003480134802348033480434805348063480734808348093481034811348123481334814348153481634817348183481934820348213482234823348243482534826348273482834829348303483134832348333483434835348363483734838348393484034841348423484334844348453484634847348483484934850348513485234853348543485534856348573485834859348603486134862348633486434865348663486734868348693487034871348723487334874348753487634877348783487934880348813488234883348843488534886348873488834889348903489134892348933489434895348963489734898348993490034901349023490334904349053490634907349083490934910349113491234913349143491534916349173491834919349203492134922349233492434925349263492734928349293493034931349323493334934349353493634937349383493934940349413494234943349443494534946349473494834949349503495134952349533495434955349563495734958349593496034961349623496334964349653496634967349683496934970349713497234973349743497534976349773497834979349803498134982349833498434985349863498734988349893499034991349923499334994349953499634997349983499935000350013500235003350043500535006350073500835009350103501135012350133501435015350163501735018350193502035021350223502335024350253502635027350283502935030350313503235033350343503535036350373503835039350403504135042350433504435045350463504735048350493505035051350523505335054350553505635057350583505935060350613506235063350643506535066350673506835069350703507135072350733507435075350763507735078350793508035081350823508335084350853508635087350883508935090350913509235093350943509535096350973509835099351003510135102351033510435105351063510735108351093511035111351123511335114351153511635117351183511935120351213512235123351243512535126351273512835129351303513135132351333513435135351363513735138351393514035141351423514335144351453514635147351483514935150351513515235153351543515535156351573515835159351603516135162351633516435165351663516735168351693517035171351723517335174351753517635177351783517935180351813518235183351843518535186351873518835189351903519135192351933519435195351963519735198351993520035201352023520335204352053520635207352083520935210352113521235213352143521535216352173521835219352203522135222352233522435225352263522735228352293523035231352323523335234352353523635237352383523935240352413524235243352443524535246352473524835249352503525135252352533525435255352563525735258352593526035261352623526335264352653526635267352683526935270352713527235273352743527535276352773527835279352803528135282352833528435285352863528735288352893529035291352923529335294352953529635297352983529935300353013530235303353043530535306353073530835309353103531135312353133531435315353163531735318353193532035321353223532335324353253532635327353283532935330353313533235333353343533535336353373533835339353403534135342353433534435345353463534735348353493535035351353523535335354353553535635357353583535935360353613536235363353643536535366353673536835369353703537135372353733537435375353763537735378353793538035381353823538335384353853538635387353883538935390353913539235393353943539535396353973539835399354003540135402354033540435405354063540735408354093541035411354123541335414354153541635417354183541935420354213542235423354243542535426354273542835429354303543135432354333543435435354363543735438354393544035441354423544335444354453544635447354483544935450354513545235453354543545535456354573545835459354603546135462354633546435465354663546735468354693547035471354723547335474354753547635477354783547935480354813548235483354843548535486354873548835489354903549135492354933549435495354963549735498354993550035501355023550335504355053550635507355083550935510355113551235513355143551535516355173551835519355203552135522355233552435525355263552735528355293553035531355323553335534355353553635537355383553935540355413554235543355443554535546355473554835549355503555135552355533555435555355563555735558355593556035561355623556335564355653556635567355683556935570355713557235573355743557535576355773557835579355803558135582355833558435585355863558735588355893559035591355923559335594355953559635597355983559935600356013560235603356043560535606356073560835609356103561135612356133561435615356163561735618356193562035621356223562335624356253562635627356283562935630356313563235633356343563535636356373563835639356403564135642356433564435645356463564735648356493565035651356523565335654356553565635657356583565935660356613566235663356643566535666356673566835669356703567135672356733567435675356763567735678356793568035681356823568335684356853568635687356883568935690356913569235693356943569535696356973569835699357003570135702357033570435705357063570735708357093571035711357123571335714357153571635717357183571935720357213572235723357243572535726357273572835729357303573135732357333573435735357363573735738357393574035741357423574335744357453574635747357483574935750357513575235753357543575535756357573575835759357603576135762357633576435765357663576735768357693577035771357723577335774357753577635777357783577935780357813578235783357843578535786357873578835789357903579135792357933579435795357963579735798357993580035801358023580335804358053580635807358083580935810358113581235813358143581535816358173581835819358203582135822358233582435825358263582735828358293583035831358323583335834358353583635837358383583935840358413584235843358443584535846358473584835849358503585135852358533585435855358563585735858358593586035861358623586335864358653586635867358683586935870358713587235873358743587535876358773587835879358803588135882358833588435885358863588735888358893589035891358923589335894358953589635897358983589935900359013590235903359043590535906359073590835909359103591135912359133591435915359163591735918359193592035921359223592335924359253592635927359283592935930359313593235933359343593535936359373593835939359403594135942359433594435945359463594735948359493595035951359523595335954359553595635957359583595935960359613596235963359643596535966359673596835969359703597135972359733597435975359763597735978359793598035981359823598335984359853598635987359883598935990359913599235993359943599535996359973599835999360003600136002360033600436005360063600736008360093601036011360123601336014360153601636017360183601936020360213602236023360243602536026360273602836029360303603136032360333603436035360363603736038360393604036041360423604336044360453604636047360483604936050360513605236053360543605536056360573605836059360603606136062360633606436065360663606736068360693607036071360723607336074360753607636077360783607936080360813608236083360843608536086360873608836089360903609136092360933609436095360963609736098360993610036101361023610336104361053610636107361083610936110361113611236113361143611536116361173611836119361203612136122361233612436125361263612736128361293613036131361323613336134361353613636137361383613936140361413614236143361443614536146361473614836149361503615136152361533615436155361563615736158361593616036161361623616336164361653616636167361683616936170361713617236173361743617536176361773617836179361803618136182361833618436185361863618736188361893619036191361923619336194361953619636197361983619936200362013620236203362043620536206362073620836209362103621136212362133621436215362163621736218362193622036221362223622336224362253622636227362283622936230362313623236233362343623536236362373623836239362403624136242362433624436245362463624736248362493625036251362523625336254362553625636257362583625936260362613626236263362643626536266362673626836269362703627136272362733627436275362763627736278362793628036281362823628336284362853628636287362883628936290362913629236293362943629536296362973629836299363003630136302363033630436305363063630736308363093631036311363123631336314363153631636317363183631936320363213632236323363243632536326363273632836329363303633136332363333633436335363363633736338363393634036341363423634336344363453634636347363483634936350363513635236353363543635536356363573635836359363603636136362363633636436365363663636736368363693637036371363723637336374363753637636377363783637936380363813638236383363843638536386363873638836389363903639136392363933639436395363963639736398363993640036401364023640336404364053640636407364083640936410364113641236413364143641536416364173641836419364203642136422364233642436425364263642736428364293643036431364323643336434364353643636437364383643936440364413644236443364443644536446364473644836449364503645136452364533645436455364563645736458364593646036461364623646336464364653646636467364683646936470364713647236473364743647536476364773647836479364803648136482364833648436485364863648736488364893649036491364923649336494364953649636497364983649936500365013650236503365043650536506365073650836509365103651136512365133651436515365163651736518365193652036521365223652336524365253652636527365283652936530365313653236533365343653536536365373653836539365403654136542365433654436545365463654736548365493655036551365523655336554365553655636557365583655936560365613656236563365643656536566365673656836569365703657136572365733657436575365763657736578365793658036581365823658336584365853658636587365883658936590365913659236593365943659536596365973659836599366003660136602366033660436605366063660736608366093661036611366123661336614366153661636617366183661936620366213662236623366243662536626366273662836629366303663136632366333663436635366363663736638366393664036641366423664336644366453664636647366483664936650366513665236653366543665536656366573665836659366603666136662366633666436665366663666736668366693667036671366723667336674366753667636677366783667936680366813668236683366843668536686366873668836689366903669136692366933669436695366963669736698366993670036701367023670336704367053670636707367083670936710367113671236713367143671536716367173671836719367203672136722367233672436725367263672736728367293673036731367323673336734367353673636737367383673936740367413674236743367443674536746367473674836749367503675136752367533675436755367563675736758367593676036761367623676336764367653676636767367683676936770367713677236773367743677536776367773677836779367803678136782367833678436785367863678736788367893679036791367923679336794367953679636797367983679936800368013680236803368043680536806368073680836809368103681136812368133681436815368163681736818368193682036821368223682336824368253682636827368283682936830368313683236833368343683536836368373683836839368403684136842368433684436845368463684736848368493685036851368523685336854368553685636857368583685936860368613686236863368643686536866368673686836869368703687136872368733687436875368763687736878368793688036881368823688336884368853688636887368883688936890368913689236893368943689536896368973689836899369003690136902369033690436905369063690736908369093691036911369123691336914369153691636917369183691936920369213692236923369243692536926369273692836929369303693136932369333693436935369363693736938369393694036941369423694336944369453694636947369483694936950369513695236953369543695536956369573695836959369603696136962369633696436965369663696736968369693697036971369723697336974369753697636977369783697936980369813698236983369843698536986369873698836989369903699136992369933699436995369963699736998369993700037001370023700337004370053700637007370083700937010370113701237013370143701537016370173701837019370203702137022370233702437025370263702737028370293703037031370323703337034370353703637037370383703937040370413704237043370443704537046370473704837049370503705137052370533705437055370563705737058370593706037061370623706337064370653706637067370683706937070370713707237073370743707537076370773707837079370803708137082370833708437085370863708737088370893709037091370923709337094370953709637097370983709937100371013710237103371043710537106371073710837109371103711137112371133711437115371163711737118371193712037121371223712337124371253712637127371283712937130371313713237133371343713537136371373713837139371403714137142371433714437145371463714737148371493715037151371523715337154371553715637157371583715937160371613716237163371643716537166371673716837169371703717137172371733717437175371763717737178371793718037181371823718337184371853718637187371883718937190371913719237193371943719537196371973719837199372003720137202372033720437205372063720737208372093721037211372123721337214372153721637217372183721937220372213722237223372243722537226372273722837229372303723137232372333723437235372363723737238372393724037241372423724337244372453724637247372483724937250372513725237253372543725537256372573725837259372603726137262372633726437265372663726737268372693727037271372723727337274372753727637277372783727937280372813728237283372843728537286372873728837289372903729137292372933729437295372963729737298372993730037301373023730337304373053730637307373083730937310373113731237313373143731537316373173731837319373203732137322373233732437325373263732737328373293733037331373323733337334373353733637337373383733937340373413734237343373443734537346373473734837349373503735137352373533735437355373563735737358373593736037361373623736337364373653736637367373683736937370373713737237373373743737537376373773737837379373803738137382373833738437385373863738737388373893739037391373923739337394373953739637397373983739937400374013740237403374043740537406374073740837409374103741137412374133741437415374163741737418374193742037421374223742337424374253742637427374283742937430374313743237433374343743537436374373743837439374403744137442374433744437445374463744737448374493745037451374523745337454374553745637457374583745937460374613746237463374643746537466374673746837469374703747137472374733747437475374763747737478374793748037481374823748337484374853748637487374883748937490374913749237493374943749537496374973749837499375003750137502375033750437505375063750737508375093751037511375123751337514375153751637517375183751937520375213752237523375243752537526375273752837529375303753137532375333753437535375363753737538375393754037541375423754337544375453754637547375483754937550375513755237553375543755537556375573755837559375603756137562375633756437565375663756737568375693757037571375723757337574375753757637577375783757937580375813758237583375843758537586375873758837589375903759137592375933759437595375963759737598375993760037601376023760337604376053760637607376083760937610376113761237613376143761537616376173761837619376203762137622376233762437625376263762737628376293763037631376323763337634376353763637637376383763937640376413764237643376443764537646376473764837649376503765137652376533765437655376563765737658376593766037661376623766337664376653766637667376683766937670376713767237673376743767537676376773767837679376803768137682376833768437685376863768737688376893769037691376923769337694376953769637697376983769937700377013770237703377043770537706377073770837709377103771137712377133771437715377163771737718377193772037721377223772337724377253772637727377283772937730377313773237733377343773537736377373773837739377403774137742377433774437745377463774737748377493775037751377523775337754377553775637757377583775937760377613776237763377643776537766377673776837769377703777137772377733777437775377763777737778377793778037781377823778337784377853778637787377883778937790377913779237793377943779537796377973779837799378003780137802378033780437805378063780737808378093781037811378123781337814378153781637817378183781937820378213782237823378243782537826378273782837829378303783137832378333783437835378363783737838378393784037841378423784337844378453784637847378483784937850378513785237853378543785537856378573785837859378603786137862378633786437865378663786737868378693787037871378723787337874378753787637877378783787937880378813788237883378843788537886378873788837889378903789137892378933789437895378963789737898378993790037901379023790337904379053790637907379083790937910379113791237913379143791537916379173791837919379203792137922379233792437925379263792737928379293793037931379323793337934379353793637937379383793937940379413794237943379443794537946379473794837949379503795137952379533795437955379563795737958379593796037961379623796337964379653796637967379683796937970379713797237973379743797537976379773797837979379803798137982379833798437985379863798737988379893799037991379923799337994379953799637997379983799938000380013800238003380043800538006380073800838009380103801138012380133801438015380163801738018380193802038021380223802338024380253802638027380283802938030380313803238033380343803538036380373803838039380403804138042380433804438045380463804738048380493805038051380523805338054380553805638057380583805938060380613806238063380643806538066380673806838069380703807138072380733807438075380763807738078380793808038081380823808338084380853808638087380883808938090380913809238093380943809538096380973809838099381003810138102381033810438105381063810738108381093811038111381123811338114381153811638117381183811938120381213812238123381243812538126381273812838129381303813138132381333813438135381363813738138381393814038141381423814338144381453814638147381483814938150381513815238153381543815538156381573815838159381603816138162381633816438165381663816738168381693817038171381723817338174381753817638177381783817938180381813818238183381843818538186381873818838189381903819138192381933819438195381963819738198381993820038201382023820338204382053820638207382083820938210382113821238213382143821538216382173821838219382203822138222382233822438225382263822738228382293823038231382323823338234382353823638237382383823938240382413824238243382443824538246382473824838249382503825138252382533825438255382563825738258382593826038261382623826338264382653826638267382683826938270382713827238273382743827538276382773827838279382803828138282382833828438285382863828738288382893829038291382923829338294382953829638297382983829938300383013830238303383043830538306383073830838309383103831138312383133831438315383163831738318383193832038321383223832338324383253832638327383283832938330383313833238333383343833538336383373833838339383403834138342383433834438345383463834738348383493835038351383523835338354383553835638357383583835938360383613836238363383643836538366383673836838369383703837138372383733837438375383763837738378383793838038381383823838338384383853838638387383883838938390383913839238393383943839538396383973839838399384003840138402384033840438405384063840738408384093841038411384123841338414384153841638417384183841938420384213842238423384243842538426384273842838429384303843138432384333843438435384363843738438384393844038441384423844338444384453844638447384483844938450384513845238453384543845538456384573845838459384603846138462384633846438465384663846738468384693847038471384723847338474384753847638477384783847938480384813848238483384843848538486384873848838489384903849138492384933849438495384963849738498384993850038501385023850338504385053850638507385083850938510385113851238513385143851538516385173851838519385203852138522385233852438525385263852738528385293853038531385323853338534385353853638537385383853938540385413854238543385443854538546385473854838549385503855138552385533855438555385563855738558385593856038561385623856338564385653856638567385683856938570385713857238573385743857538576385773857838579385803858138582385833858438585385863858738588385893859038591385923859338594385953859638597385983859938600386013860238603386043860538606386073860838609386103861138612386133861438615386163861738618386193862038621386223862338624386253862638627386283862938630386313863238633386343863538636386373863838639386403864138642386433864438645386463864738648386493865038651386523865338654386553865638657386583865938660386613866238663386643866538666386673866838669386703867138672386733867438675386763867738678386793868038681386823868338684386853868638687386883868938690386913869238693386943869538696386973869838699387003870138702387033870438705387063870738708387093871038711387123871338714387153871638717387183871938720387213872238723387243872538726387273872838729387303873138732387333873438735387363873738738387393874038741387423874338744387453874638747387483874938750387513875238753387543875538756387573875838759387603876138762387633876438765387663876738768387693877038771387723877338774387753877638777387783877938780387813878238783387843878538786387873878838789387903879138792387933879438795387963879738798387993880038801388023880338804388053880638807388083880938810388113881238813388143881538816388173881838819388203882138822388233882438825388263882738828388293883038831388323883338834388353883638837388383883938840388413884238843388443884538846388473884838849388503885138852388533885438855388563885738858388593886038861388623886338864388653886638867388683886938870388713887238873388743887538876388773887838879388803888138882388833888438885388863888738888388893889038891388923889338894388953889638897388983889938900389013890238903389043890538906389073890838909389103891138912389133891438915389163891738918389193892038921389223892338924389253892638927389283892938930389313893238933389343893538936389373893838939389403894138942389433894438945389463894738948389493895038951389523895338954389553895638957389583895938960389613896238963389643896538966389673896838969389703897138972389733897438975389763897738978389793898038981389823898338984389853898638987389883898938990389913899238993389943899538996389973899838999390003900139002390033900439005390063900739008390093901039011390123901339014390153901639017390183901939020390213902239023390243902539026390273902839029390303903139032390333903439035390363903739038390393904039041390423904339044390453904639047390483904939050390513905239053390543905539056390573905839059390603906139062390633906439065390663906739068390693907039071390723907339074390753907639077390783907939080390813908239083390843908539086390873908839089390903909139092390933909439095390963909739098390993910039101391023910339104391053910639107391083910939110391113911239113391143911539116391173911839119391203912139122391233912439125391263912739128391293913039131391323913339134391353913639137391383913939140391413914239143391443914539146391473914839149391503915139152391533915439155391563915739158391593916039161391623916339164391653916639167391683916939170391713917239173391743917539176391773917839179391803918139182391833918439185391863918739188391893919039191391923919339194391953919639197391983919939200392013920239203392043920539206392073920839209392103921139212392133921439215392163921739218392193922039221392223922339224392253922639227392283922939230392313923239233392343923539236392373923839239392403924139242392433924439245392463924739248392493925039251392523925339254392553925639257392583925939260392613926239263392643926539266392673926839269392703927139272392733927439275392763927739278392793928039281392823928339284392853928639287392883928939290392913929239293392943929539296392973929839299393003930139302393033930439305393063930739308393093931039311393123931339314393153931639317393183931939320393213932239323393243932539326393273932839329393303933139332393333933439335393363933739338393393934039341393423934339344393453934639347393483934939350393513935239353393543935539356393573935839359393603936139362393633936439365393663936739368393693937039371393723937339374393753937639377393783937939380393813938239383393843938539386393873938839389393903939139392393933939439395393963939739398393993940039401394023940339404394053940639407394083940939410394113941239413394143941539416394173941839419394203942139422394233942439425394263942739428394293943039431394323943339434394353943639437394383943939440394413944239443394443944539446394473944839449394503945139452394533945439455394563945739458394593946039461394623946339464394653946639467394683946939470394713947239473394743947539476394773947839479394803948139482394833948439485394863948739488394893949039491394923949339494394953949639497394983949939500395013950239503395043950539506395073950839509395103951139512395133951439515395163951739518395193952039521395223952339524395253952639527395283952939530395313953239533395343953539536395373953839539395403954139542395433954439545395463954739548395493955039551395523955339554395553955639557395583955939560395613956239563395643956539566395673956839569395703957139572395733957439575395763957739578395793958039581395823958339584395853958639587395883958939590395913959239593395943959539596395973959839599396003960139602396033960439605396063960739608396093961039611396123961339614396153961639617396183961939620396213962239623396243962539626396273962839629396303963139632396333963439635396363963739638396393964039641396423964339644396453964639647396483964939650396513965239653396543965539656396573965839659396603966139662396633966439665396663966739668396693967039671396723967339674396753967639677396783967939680396813968239683396843968539686396873968839689396903969139692396933969439695396963969739698396993970039701397023970339704397053970639707397083970939710397113971239713397143971539716397173971839719397203972139722397233972439725397263972739728397293973039731397323973339734397353973639737397383973939740397413974239743397443974539746397473974839749397503975139752397533975439755397563975739758397593976039761397623976339764397653976639767397683976939770397713977239773397743977539776397773977839779397803978139782397833978439785397863978739788397893979039791397923979339794397953979639797397983979939800398013980239803398043980539806398073980839809398103981139812398133981439815398163981739818398193982039821398223982339824398253982639827398283982939830398313983239833398343983539836398373983839839398403984139842398433984439845398463984739848398493985039851398523985339854398553985639857398583985939860398613986239863398643986539866398673986839869398703987139872398733987439875398763987739878398793988039881398823988339884398853988639887398883988939890398913989239893398943989539896398973989839899399003990139902399033990439905399063990739908399093991039911399123991339914399153991639917399183991939920399213992239923399243992539926399273992839929399303993139932399333993439935399363993739938399393994039941399423994339944399453994639947399483994939950399513995239953399543995539956399573995839959399603996139962399633996439965399663996739968399693997039971399723997339974399753997639977399783997939980399813998239983399843998539986399873998839989399903999139992399933999439995399963999739998399994000040001400024000340004400054000640007400084000940010400114001240013400144001540016400174001840019400204002140022400234002440025400264002740028400294003040031400324003340034400354003640037400384003940040400414004240043400444004540046400474004840049400504005140052400534005440055400564005740058400594006040061400624006340064400654006640067400684006940070400714007240073400744007540076400774007840079400804008140082400834008440085400864008740088400894009040091400924009340094400954009640097400984009940100401014010240103401044010540106401074010840109401104011140112401134011440115401164011740118401194012040121401224012340124401254012640127401284012940130401314013240133401344013540136401374013840139401404014140142401434014440145401464014740148401494015040151401524015340154401554015640157401584015940160401614016240163401644016540166401674016840169401704017140172401734017440175401764017740178401794018040181401824018340184401854018640187401884018940190401914019240193401944019540196401974019840199402004020140202402034020440205402064020740208402094021040211402124021340214402154021640217402184021940220402214022240223402244022540226402274022840229402304023140232402334023440235402364023740238402394024040241402424024340244402454024640247402484024940250402514025240253402544025540256402574025840259402604026140262402634026440265402664026740268402694027040271402724027340274402754027640277402784027940280402814028240283402844028540286402874028840289402904029140292402934029440295402964029740298402994030040301403024030340304403054030640307403084030940310403114031240313403144031540316403174031840319403204032140322403234032440325403264032740328403294033040331403324033340334403354033640337403384033940340403414034240343403444034540346403474034840349403504035140352403534035440355403564035740358403594036040361403624036340364403654036640367403684036940370403714037240373403744037540376403774037840379403804038140382403834038440385403864038740388403894039040391403924039340394403954039640397403984039940400404014040240403404044040540406404074040840409404104041140412404134041440415404164041740418404194042040421404224042340424404254042640427404284042940430404314043240433404344043540436404374043840439404404044140442404434044440445404464044740448404494045040451404524045340454404554045640457404584045940460404614046240463404644046540466404674046840469404704047140472404734047440475404764047740478404794048040481404824048340484404854048640487404884048940490404914049240493404944049540496404974049840499405004050140502405034050440505405064050740508405094051040511405124051340514405154051640517405184051940520405214052240523405244052540526405274052840529405304053140532405334053440535405364053740538405394054040541405424054340544405454054640547405484054940550405514055240553405544055540556405574055840559405604056140562405634056440565405664056740568405694057040571405724057340574405754057640577405784057940580405814058240583405844058540586405874058840589405904059140592405934059440595405964059740598405994060040601406024060340604406054060640607406084060940610406114061240613406144061540616406174061840619406204062140622406234062440625406264062740628406294063040631406324063340634406354063640637406384063940640406414064240643406444064540646406474064840649406504065140652406534065440655406564065740658406594066040661406624066340664406654066640667406684066940670406714067240673406744067540676406774067840679406804068140682406834068440685406864068740688406894069040691406924069340694406954069640697406984069940700407014070240703407044070540706407074070840709407104071140712407134071440715407164071740718407194072040721407224072340724407254072640727407284072940730407314073240733407344073540736407374073840739407404074140742407434074440745407464074740748407494075040751407524075340754407554075640757407584075940760407614076240763407644076540766407674076840769407704077140772407734077440775407764077740778407794078040781407824078340784407854078640787407884078940790407914079240793407944079540796407974079840799408004080140802408034080440805408064080740808408094081040811408124081340814408154081640817408184081940820408214082240823408244082540826408274082840829408304083140832408334083440835408364083740838408394084040841408424084340844408454084640847408484084940850408514085240853408544085540856408574085840859408604086140862408634086440865408664086740868408694087040871408724087340874408754087640877408784087940880408814088240883408844088540886408874088840889408904089140892408934089440895408964089740898408994090040901409024090340904409054090640907409084090940910409114091240913409144091540916409174091840919409204092140922409234092440925409264092740928409294093040931409324093340934409354093640937409384093940940409414094240943409444094540946409474094840949409504095140952409534095440955409564095740958409594096040961409624096340964409654096640967409684096940970409714097240973409744097540976409774097840979409804098140982409834098440985409864098740988409894099040991409924099340994409954099640997409984099941000410014100241003410044100541006410074100841009410104101141012410134101441015410164101741018410194102041021410224102341024410254102641027410284102941030410314103241033410344103541036410374103841039410404104141042410434104441045410464104741048410494105041051410524105341054410554105641057410584105941060410614106241063410644106541066410674106841069410704107141072410734107441075410764107741078410794108041081410824108341084410854108641087410884108941090410914109241093410944109541096410974109841099411004110141102411034110441105411064110741108411094111041111411124111341114411154111641117411184111941120411214112241123411244112541126411274112841129411304113141132411334113441135411364113741138411394114041141411424114341144411454114641147411484114941150411514115241153411544115541156411574115841159411604116141162411634116441165411664116741168411694117041171411724117341174411754117641177411784117941180411814118241183411844118541186411874118841189411904119141192411934119441195411964119741198411994120041201412024120341204412054120641207412084120941210412114121241213412144121541216412174121841219412204122141222412234122441225412264122741228412294123041231412324123341234412354123641237412384123941240412414124241243412444124541246412474124841249412504125141252412534125441255412564125741258412594126041261412624126341264412654126641267412684126941270412714127241273412744127541276412774127841279412804128141282412834128441285412864128741288412894129041291412924129341294412954129641297412984129941300413014130241303413044130541306413074130841309413104131141312413134131441315413164131741318413194132041321413224132341324413254132641327413284132941330413314133241333413344133541336413374133841339413404134141342413434134441345413464134741348413494135041351413524135341354413554135641357413584135941360413614136241363413644136541366413674136841369413704137141372413734137441375413764137741378413794138041381413824138341384413854138641387413884138941390413914139241393413944139541396413974139841399414004140141402414034140441405414064140741408414094141041411414124141341414414154141641417414184141941420414214142241423414244142541426414274142841429414304143141432414334143441435414364143741438414394144041441414424144341444414454144641447414484144941450414514145241453414544145541456414574145841459414604146141462414634146441465414664146741468414694147041471414724147341474414754147641477414784147941480414814148241483414844148541486414874148841489414904149141492414934149441495414964149741498414994150041501415024150341504415054150641507415084150941510415114151241513415144151541516415174151841519415204152141522415234152441525415264152741528415294153041531415324153341534415354153641537415384153941540415414154241543415444154541546415474154841549415504155141552415534155441555415564155741558415594156041561415624156341564415654156641567415684156941570415714157241573415744157541576415774157841579415804158141582415834158441585415864158741588415894159041591415924159341594415954159641597415984159941600416014160241603416044160541606416074160841609416104161141612416134161441615416164161741618416194162041621416224162341624416254162641627416284162941630416314163241633416344163541636416374163841639416404164141642416434164441645416464164741648416494165041651416524165341654416554165641657416584165941660416614166241663416644166541666416674166841669416704167141672416734167441675416764167741678416794168041681416824168341684416854168641687416884168941690416914169241693416944169541696416974169841699417004170141702417034170441705417064170741708417094171041711417124171341714417154171641717417184171941720417214172241723417244172541726417274172841729417304173141732417334173441735417364173741738417394174041741417424174341744417454174641747417484174941750417514175241753417544175541756417574175841759417604176141762417634176441765417664176741768417694177041771417724177341774417754177641777417784177941780417814178241783417844178541786417874178841789417904179141792417934179441795417964179741798417994180041801418024180341804418054180641807418084180941810418114181241813418144181541816418174181841819418204182141822418234182441825418264182741828418294183041831418324183341834418354183641837418384183941840418414184241843418444184541846418474184841849418504185141852418534185441855418564185741858418594186041861418624186341864418654186641867418684186941870418714187241873418744187541876418774187841879418804188141882418834188441885418864188741888418894189041891418924189341894418954189641897418984189941900419014190241903419044190541906419074190841909419104191141912419134191441915419164191741918419194192041921419224192341924419254192641927419284192941930419314193241933419344193541936419374193841939419404194141942419434194441945419464194741948419494195041951419524195341954419554195641957419584195941960419614196241963419644196541966419674196841969419704197141972419734197441975419764197741978419794198041981419824198341984419854198641987419884198941990419914199241993419944199541996419974199841999420004200142002420034200442005420064200742008420094201042011420124201342014420154201642017420184201942020420214202242023420244202542026420274202842029420304203142032420334203442035420364203742038420394204042041420424204342044420454204642047420484204942050420514205242053420544205542056420574205842059420604206142062420634206442065420664206742068420694207042071420724207342074420754207642077420784207942080420814208242083420844208542086420874208842089420904209142092420934209442095420964209742098420994210042101421024210342104421054210642107421084210942110421114211242113421144211542116421174211842119421204212142122421234212442125421264212742128421294213042131421324213342134421354213642137421384213942140421414214242143421444214542146421474214842149421504215142152421534215442155421564215742158421594216042161421624216342164421654216642167421684216942170421714217242173421744217542176421774217842179421804218142182421834218442185421864218742188421894219042191421924219342194421954219642197421984219942200422014220242203422044220542206422074220842209422104221142212422134221442215422164221742218422194222042221422224222342224422254222642227422284222942230422314223242233422344223542236422374223842239422404224142242422434224442245422464224742248422494225042251422524225342254422554225642257422584225942260422614226242263422644226542266422674226842269422704227142272422734227442275422764227742278422794228042281422824228342284422854228642287422884228942290422914229242293422944229542296422974229842299423004230142302423034230442305423064230742308423094231042311423124231342314423154231642317423184231942320423214232242323423244232542326423274232842329423304233142332423334233442335423364233742338423394234042341423424234342344423454234642347423484234942350423514235242353423544235542356423574235842359423604236142362423634236442365423664236742368423694237042371423724237342374423754237642377423784237942380423814238242383423844238542386423874238842389423904239142392423934239442395423964239742398423994240042401424024240342404424054240642407424084240942410424114241242413424144241542416424174241842419424204242142422424234242442425424264242742428424294243042431424324243342434424354243642437424384243942440424414244242443424444244542446424474244842449424504245142452424534245442455424564245742458424594246042461424624246342464424654246642467424684246942470424714247242473424744247542476424774247842479424804248142482424834248442485424864248742488424894249042491424924249342494424954249642497424984249942500425014250242503425044250542506425074250842509425104251142512425134251442515425164251742518425194252042521425224252342524425254252642527425284252942530425314253242533425344253542536425374253842539425404254142542425434254442545425464254742548425494255042551425524255342554425554255642557425584255942560425614256242563425644256542566425674256842569425704257142572425734257442575425764257742578425794258042581425824258342584425854258642587425884258942590425914259242593425944259542596425974259842599426004260142602426034260442605426064260742608426094261042611426124261342614426154261642617426184261942620426214262242623426244262542626426274262842629426304263142632426334263442635426364263742638426394264042641426424264342644426454264642647426484264942650426514265242653426544265542656426574265842659426604266142662426634266442665426664266742668426694267042671426724267342674426754267642677426784267942680426814268242683426844268542686426874268842689426904269142692426934269442695426964269742698426994270042701427024270342704427054270642707427084270942710427114271242713427144271542716427174271842719427204272142722427234272442725427264272742728427294273042731427324273342734427354273642737427384273942740427414274242743427444274542746427474274842749427504275142752427534275442755427564275742758427594276042761427624276342764427654276642767427684276942770427714277242773427744277542776427774277842779427804278142782427834278442785427864278742788427894279042791427924279342794427954279642797427984279942800428014280242803428044280542806428074280842809428104281142812428134281442815428164281742818428194282042821428224282342824428254282642827428284282942830428314283242833428344283542836428374283842839428404284142842428434284442845428464284742848428494285042851428524285342854428554285642857428584285942860428614286242863428644286542866428674286842869428704287142872428734287442875428764287742878428794288042881428824288342884428854288642887428884288942890428914289242893428944289542896428974289842899429004290142902429034290442905429064290742908429094291042911429124291342914429154291642917429184291942920429214292242923429244292542926429274292842929429304293142932429334293442935429364293742938429394294042941429424294342944429454294642947429484294942950429514295242953429544295542956429574295842959429604296142962429634296442965429664296742968429694297042971429724297342974429754297642977429784297942980429814298242983429844298542986429874298842989429904299142992429934299442995429964299742998429994300043001430024300343004430054300643007430084300943010430114301243013430144301543016430174301843019430204302143022430234302443025430264302743028430294303043031430324303343034430354303643037430384303943040430414304243043430444304543046430474304843049430504305143052430534305443055430564305743058430594306043061430624306343064430654306643067430684306943070430714307243073430744307543076430774307843079430804308143082430834308443085430864308743088430894309043091430924309343094430954309643097430984309943100431014310243103431044310543106431074310843109431104311143112431134311443115431164311743118431194312043121431224312343124431254312643127431284312943130431314313243133431344313543136431374313843139431404314143142431434314443145431464314743148431494315043151431524315343154431554315643157431584315943160431614316243163431644316543166431674316843169431704317143172431734317443175431764317743178431794318043181431824318343184431854318643187431884318943190431914319243193431944319543196431974319843199432004320143202432034320443205432064320743208432094321043211432124321343214432154321643217432184321943220432214322243223432244322543226432274322843229432304323143232432334323443235432364323743238432394324043241432424324343244432454324643247432484324943250432514325243253432544325543256432574325843259432604326143262432634326443265432664326743268432694327043271432724327343274432754327643277432784327943280432814328243283432844328543286432874328843289432904329143292432934329443295432964329743298432994330043301433024330343304433054330643307433084330943310433114331243313433144331543316433174331843319433204332143322433234332443325433264332743328433294333043331433324333343334433354333643337433384333943340433414334243343433444334543346433474334843349433504335143352433534335443355433564335743358433594336043361433624336343364433654336643367433684336943370433714337243373433744337543376433774337843379433804338143382433834338443385433864338743388433894339043391433924339343394433954339643397433984339943400434014340243403434044340543406434074340843409434104341143412434134341443415434164341743418434194342043421434224342343424434254342643427434284342943430434314343243433434344343543436434374343843439434404344143442434434344443445434464344743448434494345043451434524345343454434554345643457434584345943460434614346243463434644346543466434674346843469434704347143472434734347443475434764347743478434794348043481434824348343484434854348643487434884348943490434914349243493434944349543496434974349843499435004350143502435034350443505435064350743508435094351043511435124351343514435154351643517435184351943520435214352243523435244352543526435274352843529435304353143532435334353443535435364353743538435394354043541435424354343544435454354643547435484354943550435514355243553435544355543556435574355843559435604356143562435634356443565435664356743568435694357043571435724357343574435754357643577435784357943580435814358243583435844358543586435874358843589435904359143592435934359443595435964359743598435994360043601436024360343604436054360643607436084360943610436114361243613436144361543616436174361843619436204362143622436234362443625436264362743628436294363043631436324363343634436354363643637436384363943640436414364243643436444364543646436474364843649436504365143652436534365443655436564365743658436594366043661436624366343664436654366643667436684366943670436714367243673436744367543676436774367843679436804368143682436834368443685436864368743688436894369043691436924369343694436954369643697436984369943700437014370243703437044370543706437074370843709437104371143712437134371443715437164371743718437194372043721437224372343724437254372643727437284372943730437314373243733437344373543736437374373843739437404374143742437434374443745437464374743748437494375043751437524375343754437554375643757437584375943760437614376243763437644376543766437674376843769437704377143772437734377443775437764377743778437794378043781437824378343784437854378643787437884378943790437914379243793437944379543796437974379843799438004380143802438034380443805438064380743808438094381043811438124381343814438154381643817438184381943820438214382243823438244382543826438274382843829438304383143832438334383443835438364383743838438394384043841438424384343844438454384643847438484384943850438514385243853438544385543856438574385843859438604386143862438634386443865438664386743868438694387043871438724387343874438754387643877438784387943880438814388243883438844388543886438874388843889438904389143892438934389443895438964389743898438994390043901439024390343904439054390643907439084390943910439114391243913439144391543916439174391843919439204392143922439234392443925439264392743928439294393043931439324393343934439354393643937439384393943940439414394243943439444394543946439474394843949439504395143952439534395443955439564395743958439594396043961439624396343964439654396643967439684396943970439714397243973439744397543976439774397843979439804398143982439834398443985439864398743988439894399043991439924399343994439954399643997439984399944000440014400244003440044400544006440074400844009440104401144012440134401444015440164401744018440194402044021440224402344024440254402644027440284402944030440314403244033440344403544036440374403844039440404404144042440434404444045440464404744048440494405044051440524405344054440554405644057440584405944060440614406244063440644406544066440674406844069440704407144072440734407444075440764407744078440794408044081440824408344084440854408644087440884408944090440914409244093440944409544096440974409844099441004410144102441034410444105441064410744108441094411044111441124411344114441154411644117441184411944120441214412244123441244412544126441274412844129441304413144132441334413444135441364413744138441394414044141441424414344144441454414644147441484414944150441514415244153441544415544156441574415844159441604416144162441634416444165441664416744168441694417044171441724417344174441754417644177441784417944180441814418244183441844418544186441874418844189441904419144192441934419444195441964419744198441994420044201442024420344204442054420644207442084420944210442114421244213442144421544216442174421844219442204422144222442234422444225442264422744228442294423044231442324423344234442354423644237442384423944240442414424244243442444424544246442474424844249442504425144252442534425444255442564425744258442594426044261442624426344264442654426644267442684426944270442714427244273442744427544276442774427844279442804428144282442834428444285442864428744288442894429044291442924429344294442954429644297442984429944300443014430244303443044430544306443074430844309443104431144312443134431444315443164431744318443194432044321443224432344324443254432644327443284432944330443314433244333443344433544336443374433844339443404434144342443434434444345443464434744348443494435044351443524435344354443554435644357443584435944360443614436244363443644436544366443674436844369443704437144372443734437444375443764437744378443794438044381443824438344384443854438644387443884438944390443914439244393443944439544396443974439844399444004440144402444034440444405444064440744408444094441044411444124441344414444154441644417444184441944420444214442244423444244442544426444274442844429444304443144432444334443444435444364443744438444394444044441444424444344444444454444644447444484444944450444514445244453444544445544456444574445844459444604446144462444634446444465444664446744468444694447044471444724447344474444754447644477444784447944480444814448244483444844448544486444874448844489444904449144492444934449444495444964449744498444994450044501445024450344504445054450644507445084450944510445114451244513445144451544516445174451844519445204452144522445234452444525445264452744528445294453044531445324453344534445354453644537445384453944540445414454244543445444454544546445474454844549445504455144552445534455444555445564455744558445594456044561445624456344564445654456644567445684456944570445714457244573445744457544576445774457844579445804458144582445834458444585445864458744588445894459044591445924459344594445954459644597445984459944600446014460244603446044460544606446074460844609446104461144612446134461444615446164461744618446194462044621446224462344624446254462644627446284462944630446314463244633446344463544636446374463844639446404464144642446434464444645446464464744648446494465044651446524465344654446554465644657446584465944660446614466244663446644466544666446674466844669446704467144672446734467444675446764467744678446794468044681446824468344684446854468644687446884468944690446914469244693446944469544696446974469844699447004470144702447034470444705447064470744708447094471044711447124471344714447154471644717447184471944720447214472244723447244472544726447274472844729447304473144732447334473444735447364473744738447394474044741447424474344744447454474644747447484474944750447514475244753447544475544756447574475844759447604476144762447634476444765447664476744768447694477044771447724477344774447754477644777447784477944780447814478244783447844478544786447874478844789447904479144792447934479444795447964479744798447994480044801448024480344804448054480644807448084480944810448114481244813448144481544816448174481844819448204482144822448234482444825448264482744828448294483044831448324483344834448354483644837448384483944840448414484244843448444484544846448474484844849448504485144852448534485444855448564485744858448594486044861448624486344864448654486644867448684486944870448714487244873448744487544876448774487844879448804488144882448834488444885448864488744888448894489044891448924489344894448954489644897448984489944900449014490244903449044490544906449074490844909449104491144912449134491444915449164491744918449194492044921449224492344924449254492644927449284492944930449314493244933449344493544936449374493844939449404494144942449434494444945449464494744948449494495044951449524495344954449554495644957449584495944960449614496244963449644496544966449674496844969449704497144972449734497444975449764497744978449794498044981449824498344984449854498644987449884498944990449914499244993449944499544996449974499844999450004500145002450034500445005450064500745008450094501045011450124501345014450154501645017450184501945020450214502245023450244502545026450274502845029450304503145032450334503445035450364503745038450394504045041450424504345044450454504645047450484504945050450514505245053450544505545056450574505845059450604506145062450634506445065450664506745068450694507045071450724507345074450754507645077450784507945080450814508245083450844508545086450874508845089450904509145092450934509445095450964509745098450994510045101451024510345104451054510645107451084510945110451114511245113451144511545116451174511845119451204512145122451234512445125451264512745128451294513045131451324513345134451354513645137451384513945140451414514245143451444514545146451474514845149451504515145152451534515445155451564515745158451594516045161451624516345164451654516645167451684516945170451714517245173451744517545176451774517845179451804518145182451834518445185451864518745188451894519045191451924519345194451954519645197451984519945200452014520245203452044520545206452074520845209452104521145212452134521445215452164521745218452194522045221452224522345224452254522645227452284522945230452314523245233452344523545236452374523845239452404524145242452434524445245452464524745248452494525045251452524525345254452554525645257452584525945260452614526245263452644526545266452674526845269452704527145272452734527445275452764527745278452794528045281452824528345284452854528645287452884528945290452914529245293452944529545296452974529845299453004530145302453034530445305453064530745308453094531045311453124531345314453154531645317453184531945320453214532245323453244532545326453274532845329453304533145332453334533445335453364533745338453394534045341453424534345344453454534645347453484534945350453514535245353453544535545356453574535845359453604536145362453634536445365453664536745368453694537045371453724537345374453754537645377453784537945380453814538245383453844538545386453874538845389453904539145392453934539445395453964539745398453994540045401454024540345404454054540645407454084540945410454114541245413454144541545416454174541845419454204542145422454234542445425454264542745428454294543045431454324543345434454354543645437454384543945440454414544245443454444544545446454474544845449454504545145452454534545445455454564545745458454594546045461454624546345464454654546645467454684546945470454714547245473454744547545476454774547845479454804548145482454834548445485454864548745488454894549045491454924549345494454954549645497454984549945500455014550245503455044550545506455074550845509455104551145512455134551445515455164551745518455194552045521455224552345524455254552645527455284552945530455314553245533455344553545536455374553845539455404554145542455434554445545455464554745548455494555045551455524555345554455554555645557455584555945560455614556245563455644556545566455674556845569455704557145572455734557445575455764557745578455794558045581455824558345584455854558645587455884558945590455914559245593455944559545596455974559845599456004560145602456034560445605456064560745608456094561045611456124561345614456154561645617456184561945620456214562245623456244562545626456274562845629456304563145632456334563445635456364563745638456394564045641456424564345644456454564645647456484564945650456514565245653456544565545656456574565845659456604566145662456634566445665456664566745668456694567045671456724567345674456754567645677456784567945680456814568245683456844568545686456874568845689456904569145692456934569445695456964569745698456994570045701457024570345704457054570645707457084570945710457114571245713457144571545716457174571845719457204572145722457234572445725457264572745728457294573045731457324573345734457354573645737457384573945740457414574245743457444574545746457474574845749457504575145752457534575445755457564575745758457594576045761457624576345764457654576645767457684576945770457714577245773457744577545776457774577845779457804578145782457834578445785457864578745788457894579045791457924579345794457954579645797457984579945800458014580245803458044580545806458074580845809458104581145812458134581445815458164581745818458194582045821458224582345824458254582645827458284582945830458314583245833458344583545836458374583845839458404584145842458434584445845458464584745848458494585045851458524585345854458554585645857458584585945860458614586245863458644586545866458674586845869458704587145872458734587445875458764587745878458794588045881458824588345884458854588645887458884588945890458914589245893458944589545896458974589845899459004590145902459034590445905459064590745908459094591045911459124591345914459154591645917459184591945920459214592245923459244592545926459274592845929459304593145932459334593445935459364593745938459394594045941459424594345944459454594645947459484594945950459514595245953459544595545956459574595845959459604596145962459634596445965459664596745968459694597045971459724597345974459754597645977459784597945980459814598245983459844598545986459874598845989459904599145992459934599445995459964599745998459994600046001460024600346004460054600646007460084600946010460114601246013460144601546016460174601846019460204602146022460234602446025460264602746028460294603046031460324603346034460354603646037460384603946040460414604246043460444604546046460474604846049460504605146052460534605446055460564605746058460594606046061460624606346064460654606646067460684606946070460714607246073460744607546076460774607846079460804608146082460834608446085460864608746088460894609046091460924609346094460954609646097460984609946100461014610246103461044610546106461074610846109461104611146112461134611446115461164611746118461194612046121461224612346124461254612646127461284612946130461314613246133461344613546136461374613846139461404614146142461434614446145461464614746148461494615046151461524615346154461554615646157461584615946160461614616246163461644616546166461674616846169461704617146172461734617446175461764617746178461794618046181461824618346184461854618646187461884618946190461914619246193461944619546196461974619846199462004620146202462034620446205462064620746208462094621046211462124621346214462154621646217462184621946220462214622246223462244622546226462274622846229462304623146232462334623446235462364623746238462394624046241462424624346244462454624646247462484624946250462514625246253462544625546256462574625846259462604626146262462634626446265462664626746268462694627046271462724627346274462754627646277462784627946280462814628246283462844628546286462874628846289462904629146292462934629446295462964629746298462994630046301463024630346304463054630646307463084630946310463114631246313463144631546316463174631846319463204632146322463234632446325463264632746328463294633046331463324633346334463354633646337463384633946340463414634246343463444634546346463474634846349463504635146352463534635446355463564635746358463594636046361463624636346364463654636646367463684636946370463714637246373463744637546376463774637846379463804638146382463834638446385463864638746388463894639046391463924639346394463954639646397463984639946400464014640246403464044640546406464074640846409464104641146412464134641446415464164641746418464194642046421464224642346424464254642646427464284642946430464314643246433464344643546436464374643846439464404644146442464434644446445464464644746448464494645046451464524645346454464554645646457464584645946460464614646246463464644646546466464674646846469464704647146472464734647446475464764647746478464794648046481464824648346484464854648646487464884648946490464914649246493464944649546496464974649846499465004650146502465034650446505465064650746508465094651046511465124651346514465154651646517465184651946520465214652246523465244652546526465274652846529465304653146532465334653446535465364653746538465394654046541465424654346544465454654646547465484654946550465514655246553465544655546556465574655846559465604656146562465634656446565465664656746568465694657046571465724657346574465754657646577465784657946580465814658246583465844658546586465874658846589465904659146592465934659446595465964659746598465994660046601466024660346604466054660646607466084660946610466114661246613466144661546616466174661846619466204662146622466234662446625466264662746628466294663046631466324663346634466354663646637466384663946640466414664246643466444664546646466474664846649466504665146652466534665446655466564665746658466594666046661466624666346664466654666646667466684666946670466714667246673466744667546676466774667846679466804668146682466834668446685466864668746688466894669046691466924669346694466954669646697466984669946700467014670246703467044670546706467074670846709467104671146712467134671446715467164671746718467194672046721467224672346724467254672646727467284672946730467314673246733467344673546736467374673846739467404674146742467434674446745467464674746748467494675046751467524675346754467554675646757467584675946760467614676246763467644676546766467674676846769467704677146772467734677446775467764677746778467794678046781467824678346784467854678646787467884678946790467914679246793467944679546796467974679846799468004680146802468034680446805468064680746808468094681046811468124681346814468154681646817468184681946820468214682246823468244682546826468274682846829468304683146832468334683446835468364683746838468394684046841468424684346844468454684646847468484684946850468514685246853468544685546856468574685846859468604686146862468634686446865468664686746868468694687046871468724687346874468754687646877468784687946880468814688246883468844688546886468874688846889468904689146892468934689446895468964689746898468994690046901469024690346904469054690646907469084690946910469114691246913469144691546916469174691846919469204692146922469234692446925469264692746928469294693046931469324693346934469354693646937469384693946940469414694246943469444694546946469474694846949469504695146952469534695446955469564695746958469594696046961469624696346964469654696646967469684696946970469714697246973469744697546976469774697846979469804698146982469834698446985469864698746988469894699046991469924699346994469954699646997469984699947000470014700247003470044700547006470074700847009470104701147012470134701447015470164701747018470194702047021470224702347024470254702647027470284702947030470314703247033470344703547036470374703847039470404704147042470434704447045470464704747048470494705047051470524705347054470554705647057470584705947060470614706247063470644706547066470674706847069470704707147072470734707447075470764707747078470794708047081470824708347084470854708647087470884708947090470914709247093470944709547096470974709847099471004710147102471034710447105471064710747108471094711047111471124711347114471154711647117471184711947120471214712247123471244712547126471274712847129471304713147132471334713447135471364713747138471394714047141471424714347144471454714647147471484714947150471514715247153471544715547156471574715847159471604716147162471634716447165471664716747168471694717047171471724717347174471754717647177471784717947180471814718247183471844718547186471874718847189471904719147192471934719447195471964719747198471994720047201472024720347204472054720647207472084720947210472114721247213472144721547216472174721847219472204722147222472234722447225472264722747228472294723047231472324723347234472354723647237472384723947240472414724247243472444724547246472474724847249472504725147252472534725447255472564725747258472594726047261472624726347264472654726647267472684726947270472714727247273472744727547276472774727847279472804728147282472834728447285472864728747288472894729047291472924729347294472954729647297472984729947300473014730247303473044730547306473074730847309473104731147312473134731447315473164731747318473194732047321473224732347324473254732647327473284732947330473314733247333473344733547336473374733847339473404734147342473434734447345473464734747348473494735047351473524735347354473554735647357473584735947360473614736247363473644736547366473674736847369473704737147372473734737447375473764737747378473794738047381473824738347384473854738647387473884738947390473914739247393473944739547396473974739847399474004740147402474034740447405474064740747408474094741047411474124741347414474154741647417474184741947420474214742247423474244742547426474274742847429474304743147432474334743447435474364743747438474394744047441474424744347444474454744647447474484744947450474514745247453474544745547456474574745847459474604746147462474634746447465474664746747468474694747047471474724747347474474754747647477474784747947480474814748247483474844748547486474874748847489474904749147492474934749447495474964749747498474994750047501475024750347504475054750647507475084750947510475114751247513475144751547516475174751847519475204752147522475234752447525475264752747528475294753047531475324753347534475354753647537475384753947540475414754247543475444754547546475474754847549475504755147552475534755447555475564755747558475594756047561475624756347564475654756647567475684756947570475714757247573475744757547576475774757847579475804758147582475834758447585475864758747588475894759047591475924759347594475954759647597475984759947600476014760247603476044760547606476074760847609476104761147612476134761447615476164761747618476194762047621476224762347624476254762647627476284762947630476314763247633476344763547636476374763847639476404764147642476434764447645476464764747648476494765047651476524765347654476554765647657476584765947660476614766247663476644766547666476674766847669476704767147672476734767447675476764767747678476794768047681476824768347684476854768647687476884768947690476914769247693476944769547696476974769847699477004770147702477034770447705477064770747708477094771047711477124771347714477154771647717477184771947720477214772247723477244772547726477274772847729477304773147732477334773447735477364773747738477394774047741477424774347744477454774647747477484774947750477514775247753477544775547756477574775847759477604776147762477634776447765477664776747768477694777047771477724777347774477754777647777477784777947780477814778247783477844778547786477874778847789477904779147792477934779447795477964779747798477994780047801478024780347804478054780647807478084780947810478114781247813478144781547816478174781847819478204782147822478234782447825478264782747828478294783047831478324783347834478354783647837478384783947840478414784247843478444784547846478474784847849478504785147852478534785447855478564785747858478594786047861478624786347864478654786647867478684786947870478714787247873478744787547876478774787847879478804788147882478834788447885478864788747888478894789047891478924789347894478954789647897478984789947900479014790247903479044790547906479074790847909479104791147912479134791447915479164791747918479194792047921479224792347924479254792647927479284792947930479314793247933479344793547936479374793847939479404794147942479434794447945479464794747948479494795047951479524795347954479554795647957479584795947960479614796247963479644796547966479674796847969479704797147972479734797447975479764797747978479794798047981479824798347984479854798647987479884798947990479914799247993479944799547996479974799847999480004800148002480034800448005480064800748008480094801048011480124801348014480154801648017480184801948020480214802248023480244802548026480274802848029480304803148032480334803448035480364803748038480394804048041480424804348044480454804648047480484804948050480514805248053480544805548056480574805848059480604806148062480634806448065480664806748068480694807048071480724807348074480754807648077480784807948080480814808248083480844808548086480874808848089480904809148092480934809448095480964809748098480994810048101481024810348104481054810648107481084810948110481114811248113481144811548116481174811848119481204812148122481234812448125481264812748128481294813048131481324813348134481354813648137481384813948140481414814248143481444814548146481474814848149481504815148152481534815448155481564815748158481594816048161481624816348164481654816648167481684816948170481714817248173481744817548176481774817848179481804818148182481834818448185481864818748188481894819048191481924819348194481954819648197481984819948200482014820248203482044820548206482074820848209482104821148212482134821448215482164821748218482194822048221482224822348224482254822648227482284822948230482314823248233482344823548236482374823848239482404824148242482434824448245482464824748248482494825048251482524825348254482554825648257482584825948260482614826248263482644826548266482674826848269482704827148272482734827448275482764827748278482794828048281482824828348284482854828648287482884828948290482914829248293482944829548296482974829848299483004830148302483034830448305483064830748308483094831048311483124831348314483154831648317483184831948320483214832248323483244832548326483274832848329483304833148332483334833448335483364833748338483394834048341483424834348344483454834648347483484834948350483514835248353483544835548356483574835848359483604836148362483634836448365483664836748368483694837048371483724837348374483754837648377483784837948380483814838248383483844838548386483874838848389483904839148392483934839448395483964839748398483994840048401484024840348404484054840648407484084840948410484114841248413484144841548416484174841848419484204842148422484234842448425484264842748428484294843048431484324843348434484354843648437484384843948440484414844248443484444844548446484474844848449484504845148452484534845448455484564845748458484594846048461484624846348464484654846648467484684846948470484714847248473484744847548476484774847848479484804848148482484834848448485484864848748488484894849048491484924849348494484954849648497484984849948500485014850248503485044850548506485074850848509485104851148512485134851448515485164851748518485194852048521485224852348524485254852648527485284852948530485314853248533485344853548536485374853848539485404854148542485434854448545485464854748548485494855048551485524855348554485554855648557485584855948560485614856248563485644856548566485674856848569485704857148572485734857448575485764857748578485794858048581485824858348584485854858648587485884858948590485914859248593485944859548596485974859848599486004860148602486034860448605486064860748608486094861048611486124861348614486154861648617486184861948620486214862248623486244862548626486274862848629486304863148632486334863448635486364863748638486394864048641486424864348644486454864648647486484864948650486514865248653486544865548656486574865848659486604866148662486634866448665486664866748668486694867048671486724867348674486754867648677486784867948680486814868248683486844868548686486874868848689486904869148692486934869448695486964869748698486994870048701487024870348704487054870648707487084870948710487114871248713487144871548716487174871848719487204872148722487234872448725487264872748728487294873048731487324873348734487354873648737487384873948740487414874248743487444874548746487474874848749487504875148752487534875448755487564875748758487594876048761487624876348764487654876648767487684876948770487714877248773487744877548776487774877848779487804878148782487834878448785487864878748788487894879048791487924879348794487954879648797487984879948800488014880248803488044880548806488074880848809488104881148812488134881448815488164881748818488194882048821488224882348824488254882648827488284882948830488314883248833488344883548836488374883848839488404884148842488434884448845488464884748848488494885048851488524885348854488554885648857488584885948860488614886248863488644886548866488674886848869488704887148872488734887448875488764887748878488794888048881488824888348884488854888648887488884888948890488914889248893488944889548896488974889848899489004890148902489034890448905489064890748908489094891048911489124891348914489154891648917489184891948920489214892248923489244892548926489274892848929489304893148932489334893448935489364893748938489394894048941489424894348944489454894648947489484894948950489514895248953489544895548956489574895848959489604896148962489634896448965489664896748968489694897048971489724897348974489754897648977489784897948980489814898248983489844898548986489874898848989489904899148992489934899448995489964899748998489994900049001490024900349004490054900649007490084900949010490114901249013490144901549016490174901849019490204902149022490234902449025490264902749028490294903049031490324903349034490354903649037490384903949040490414904249043490444904549046490474904849049490504905149052490534905449055490564905749058490594906049061490624906349064490654906649067490684906949070490714907249073490744907549076490774907849079490804908149082490834908449085490864908749088490894909049091490924909349094490954909649097490984909949100491014910249103491044910549106491074910849109491104911149112491134911449115491164911749118491194912049121491224912349124491254912649127491284912949130491314913249133491344913549136491374913849139491404914149142491434914449145491464914749148491494915049151491524915349154491554915649157491584915949160491614916249163491644916549166491674916849169491704917149172491734917449175491764917749178491794918049181491824918349184491854918649187491884918949190491914919249193491944919549196491974919849199492004920149202492034920449205492064920749208492094921049211492124921349214492154921649217492184921949220492214922249223492244922549226492274922849229492304923149232492334923449235492364923749238492394924049241492424924349244492454924649247492484924949250492514925249253492544925549256492574925849259492604926149262492634926449265492664926749268492694927049271492724927349274492754927649277492784927949280492814928249283492844928549286492874928849289492904929149292492934929449295492964929749298492994930049301493024930349304493054930649307493084930949310493114931249313493144931549316493174931849319493204932149322493234932449325493264932749328493294933049331493324933349334493354933649337493384933949340493414934249343493444934549346493474934849349493504935149352493534935449355493564935749358493594936049361493624936349364493654936649367493684936949370493714937249373493744937549376493774937849379493804938149382493834938449385493864938749388493894939049391493924939349394493954939649397493984939949400494014940249403494044940549406494074940849409494104941149412494134941449415494164941749418494194942049421494224942349424494254942649427494284942949430494314943249433494344943549436494374943849439494404944149442494434944449445494464944749448494494945049451494524945349454494554945649457494584945949460494614946249463494644946549466494674946849469494704947149472494734947449475494764947749478494794948049481494824948349484494854948649487494884948949490494914949249493494944949549496494974949849499495004950149502495034950449505495064950749508495094951049511495124951349514495154951649517495184951949520495214952249523495244952549526495274952849529495304953149532495334953449535495364953749538495394954049541495424954349544495454954649547495484954949550495514955249553495544955549556495574955849559495604956149562495634956449565495664956749568495694957049571495724957349574495754957649577495784957949580495814958249583495844958549586495874958849589495904959149592495934959449595495964959749598495994960049601496024960349604496054960649607496084960949610496114961249613496144961549616496174961849619496204962149622496234962449625496264962749628496294963049631496324963349634496354963649637496384963949640496414964249643496444964549646496474964849649496504965149652496534965449655496564965749658496594966049661496624966349664496654966649667496684966949670496714967249673496744967549676496774967849679496804968149682496834968449685496864968749688496894969049691496924969349694496954969649697496984969949700497014970249703497044970549706497074970849709497104971149712497134971449715497164971749718497194972049721497224972349724497254972649727497284972949730497314973249733497344973549736497374973849739497404974149742497434974449745497464974749748497494975049751497524975349754497554975649757497584975949760497614976249763497644976549766497674976849769497704977149772497734977449775497764977749778497794978049781497824978349784497854978649787497884978949790497914979249793497944979549796497974979849799498004980149802498034980449805498064980749808498094981049811498124981349814498154981649817498184981949820498214982249823498244982549826498274982849829498304983149832498334983449835498364983749838498394984049841498424984349844498454984649847498484984949850498514985249853498544985549856498574985849859498604986149862498634986449865498664986749868498694987049871498724987349874498754987649877498784987949880498814988249883498844988549886498874988849889498904989149892498934989449895498964989749898498994990049901499024990349904499054990649907499084990949910499114991249913499144991549916499174991849919499204992149922499234992449925499264992749928499294993049931499324993349934499354993649937499384993949940499414994249943499444994549946499474994849949499504995149952499534995449955499564995749958499594996049961499624996349964499654996649967499684996949970499714997249973499744997549976499774997849979499804998149982499834998449985499864998749988499894999049991499924999349994499954999649997499984999950000500015000250003500045000550006500075000850009500105001150012500135001450015500165001750018500195002050021500225002350024500255002650027500285002950030500315003250033500345003550036500375003850039500405004150042500435004450045500465004750048500495005050051500525005350054500555005650057500585005950060500615006250063500645006550066500675006850069500705007150072500735007450075500765007750078500795008050081500825008350084500855008650087500885008950090500915009250093500945009550096500975009850099501005010150102501035010450105501065010750108501095011050111501125011350114501155011650117501185011950120501215012250123501245012550126501275012850129501305013150132501335013450135501365013750138501395014050141501425014350144501455014650147501485014950150501515015250153501545015550156501575015850159501605016150162501635016450165501665016750168501695017050171501725017350174501755017650177501785017950180501815018250183501845018550186501875018850189501905019150192501935019450195501965019750198501995020050201502025020350204502055020650207502085020950210502115021250213502145021550216502175021850219502205022150222502235022450225502265022750228502295023050231502325023350234502355023650237502385023950240502415024250243502445024550246502475024850249502505025150252502535025450255502565025750258502595026050261502625026350264502655026650267502685026950270502715027250273502745027550276502775027850279502805028150282502835028450285502865028750288502895029050291502925029350294502955029650297502985029950300503015030250303503045030550306503075030850309503105031150312503135031450315503165031750318503195032050321503225032350324503255032650327503285032950330503315033250333503345033550336503375033850339503405034150342503435034450345503465034750348503495035050351503525035350354503555035650357503585035950360503615036250363503645036550366503675036850369503705037150372503735037450375503765037750378503795038050381503825038350384503855038650387503885038950390503915039250393503945039550396503975039850399504005040150402504035040450405504065040750408504095041050411504125041350414504155041650417504185041950420504215042250423504245042550426504275042850429504305043150432504335043450435504365043750438504395044050441504425044350444504455044650447504485044950450504515045250453504545045550456504575045850459504605046150462504635046450465504665046750468504695047050471504725047350474504755047650477504785047950480504815048250483504845048550486504875048850489504905049150492504935049450495504965049750498504995050050501505025050350504505055050650507505085050950510505115051250513505145051550516505175051850519505205052150522505235052450525505265052750528505295053050531505325053350534505355053650537505385053950540505415054250543505445054550546505475054850549505505055150552505535055450555505565055750558505595056050561505625056350564505655056650567505685056950570505715057250573505745057550576505775057850579505805058150582505835058450585505865058750588505895059050591505925059350594505955059650597505985059950600506015060250603506045060550606506075060850609506105061150612506135061450615506165061750618506195062050621506225062350624506255062650627506285062950630506315063250633506345063550636506375063850639506405064150642506435064450645506465064750648506495065050651506525065350654506555065650657506585065950660506615066250663506645066550666506675066850669506705067150672506735067450675506765067750678506795068050681506825068350684506855068650687506885068950690506915069250693506945069550696506975069850699507005070150702507035070450705507065070750708507095071050711507125071350714507155071650717507185071950720507215072250723507245072550726507275072850729507305073150732507335073450735507365073750738507395074050741507425074350744507455074650747507485074950750507515075250753507545075550756507575075850759507605076150762507635076450765507665076750768507695077050771507725077350774507755077650777507785077950780507815078250783507845078550786507875078850789507905079150792507935079450795507965079750798507995080050801508025080350804508055080650807508085080950810508115081250813508145081550816508175081850819508205082150822508235082450825508265082750828508295083050831508325083350834508355083650837508385083950840508415084250843508445084550846508475084850849508505085150852508535085450855508565085750858508595086050861508625086350864508655086650867508685086950870508715087250873508745087550876508775087850879508805088150882508835088450885508865088750888508895089050891508925089350894508955089650897508985089950900509015090250903509045090550906509075090850909509105091150912509135091450915509165091750918509195092050921509225092350924509255092650927509285092950930509315093250933509345093550936509375093850939509405094150942509435094450945509465094750948509495095050951509525095350954509555095650957509585095950960509615096250963509645096550966509675096850969509705097150972509735097450975509765097750978509795098050981509825098350984509855098650987509885098950990509915099250993509945099550996509975099850999510005100151002510035100451005510065100751008510095101051011510125101351014510155101651017510185101951020510215102251023510245102551026510275102851029510305103151032510335103451035510365103751038510395104051041510425104351044510455104651047510485104951050510515105251053510545105551056510575105851059510605106151062510635106451065510665106751068510695107051071510725107351074510755107651077510785107951080510815108251083510845108551086510875108851089510905109151092510935109451095510965109751098510995110051101511025110351104511055110651107511085110951110511115111251113511145111551116511175111851119511205112151122511235112451125511265112751128511295113051131511325113351134511355113651137511385113951140511415114251143511445114551146511475114851149511505115151152511535115451155511565115751158511595116051161511625116351164511655116651167511685116951170511715117251173511745117551176511775117851179511805118151182511835118451185511865118751188511895119051191511925119351194511955119651197511985119951200512015120251203512045120551206512075120851209512105121151212512135121451215512165121751218512195122051221512225122351224512255122651227512285122951230512315123251233512345123551236512375123851239512405124151242512435124451245512465124751248512495125051251512525125351254512555125651257512585125951260512615126251263512645126551266512675126851269512705127151272512735127451275512765127751278512795128051281512825128351284512855128651287512885128951290512915129251293512945129551296512975129851299513005130151302513035130451305513065130751308513095131051311513125131351314513155131651317513185131951320513215132251323513245132551326513275132851329513305133151332513335133451335513365133751338513395134051341513425134351344513455134651347513485134951350513515135251353513545135551356513575135851359513605136151362513635136451365513665136751368513695137051371513725137351374513755137651377513785137951380513815138251383513845138551386513875138851389513905139151392513935139451395513965139751398513995140051401514025140351404514055140651407514085140951410514115141251413514145141551416514175141851419514205142151422514235142451425514265142751428514295143051431514325143351434514355143651437514385143951440514415144251443514445144551446514475144851449514505145151452514535145451455514565145751458514595146051461514625146351464514655146651467514685146951470514715147251473514745147551476514775147851479514805148151482514835148451485514865148751488514895149051491514925149351494514955149651497514985149951500515015150251503515045150551506515075150851509515105151151512515135151451515515165151751518515195152051521515225152351524515255152651527515285152951530515315153251533515345153551536515375153851539515405154151542515435154451545515465154751548515495155051551515525155351554515555155651557515585155951560515615156251563515645156551566515675156851569515705157151572515735157451575515765157751578515795158051581515825158351584515855158651587515885158951590515915159251593515945159551596515975159851599516005160151602516035160451605516065160751608516095161051611516125161351614516155161651617516185161951620516215162251623516245162551626516275162851629516305163151632516335163451635516365163751638516395164051641516425164351644516455164651647516485164951650516515165251653516545165551656516575165851659516605166151662516635166451665516665166751668516695167051671516725167351674516755167651677516785167951680516815168251683516845168551686516875168851689516905169151692516935169451695516965169751698516995170051701517025170351704517055170651707517085170951710517115171251713517145171551716517175171851719517205172151722517235172451725517265172751728517295173051731517325173351734517355173651737517385173951740517415174251743517445174551746517475174851749517505175151752517535175451755517565175751758517595176051761517625176351764517655176651767517685176951770517715177251773517745177551776517775177851779517805178151782517835178451785517865178751788517895179051791517925179351794517955179651797517985179951800518015180251803518045180551806518075180851809518105181151812518135181451815518165181751818518195182051821518225182351824518255182651827518285182951830518315183251833518345183551836518375183851839518405184151842518435184451845518465184751848518495185051851518525185351854518555185651857518585185951860518615186251863518645186551866518675186851869518705187151872518735187451875518765187751878518795188051881518825188351884518855188651887518885188951890518915189251893518945189551896518975189851899519005190151902519035190451905519065190751908519095191051911519125191351914519155191651917519185191951920519215192251923519245192551926519275192851929519305193151932519335193451935519365193751938519395194051941519425194351944519455194651947519485194951950519515195251953519545195551956519575195851959519605196151962519635196451965519665196751968519695197051971519725197351974519755197651977519785197951980519815198251983519845198551986519875198851989519905199151992519935199451995519965199751998519995200052001520025200352004520055200652007520085200952010520115201252013520145201552016520175201852019520205202152022520235202452025520265202752028520295203052031520325203352034520355203652037520385203952040520415204252043520445204552046520475204852049520505205152052520535205452055520565205752058520595206052061520625206352064520655206652067520685206952070520715207252073520745207552076520775207852079520805208152082520835208452085520865208752088520895209052091520925209352094520955209652097520985209952100521015210252103521045210552106521075210852109521105211152112521135211452115521165211752118521195212052121521225212352124521255212652127521285212952130521315213252133521345213552136521375213852139521405214152142521435214452145521465214752148521495215052151521525215352154521555215652157521585215952160521615216252163521645216552166521675216852169521705217152172521735217452175521765217752178521795218052181521825218352184521855218652187521885218952190521915219252193521945219552196521975219852199522005220152202522035220452205522065220752208522095221052211522125221352214522155221652217522185221952220522215222252223522245222552226522275222852229522305223152232522335223452235522365223752238522395224052241522425224352244522455224652247522485224952250522515225252253522545225552256522575225852259522605226152262522635226452265522665226752268522695227052271522725227352274522755227652277522785227952280522815228252283522845228552286522875228852289522905229152292522935229452295522965229752298522995230052301523025230352304523055230652307523085230952310523115231252313523145231552316523175231852319523205232152322523235232452325523265232752328523295233052331523325233352334523355233652337523385233952340523415234252343523445234552346523475234852349523505235152352523535235452355523565235752358523595236052361523625236352364523655236652367523685236952370523715237252373523745237552376523775237852379523805238152382523835238452385523865238752388523895239052391523925239352394523955239652397523985239952400524015240252403524045240552406524075240852409524105241152412524135241452415524165241752418524195242052421524225242352424524255242652427524285242952430524315243252433524345243552436524375243852439524405244152442524435244452445524465244752448524495245052451524525245352454524555245652457524585245952460524615246252463524645246552466524675246852469524705247152472524735247452475524765247752478524795248052481524825248352484524855248652487524885248952490524915249252493524945249552496524975249852499525005250152502525035250452505525065250752508525095251052511525125251352514525155251652517525185251952520525215252252523525245252552526525275252852529525305253152532525335253452535525365253752538525395254052541525425254352544525455254652547525485254952550525515255252553525545255552556525575255852559525605256152562525635256452565525665256752568525695257052571525725257352574525755257652577525785257952580525815258252583525845258552586525875258852589525905259152592525935259452595525965259752598525995260052601526025260352604526055260652607526085260952610526115261252613526145261552616526175261852619526205262152622526235262452625526265262752628526295263052631526325263352634526355263652637526385263952640526415264252643526445264552646526475264852649526505265152652526535265452655526565265752658526595266052661526625266352664526655266652667526685266952670526715267252673526745267552676526775267852679526805268152682526835268452685526865268752688526895269052691526925269352694526955269652697526985269952700527015270252703527045270552706527075270852709527105271152712527135271452715527165271752718527195272052721527225272352724527255272652727527285272952730527315273252733527345273552736527375273852739527405274152742527435274452745527465274752748527495275052751527525275352754527555275652757527585275952760527615276252763527645276552766527675276852769527705277152772527735277452775527765277752778527795278052781527825278352784527855278652787527885278952790527915279252793527945279552796527975279852799528005280152802528035280452805528065280752808528095281052811528125281352814528155281652817528185281952820528215282252823528245282552826528275282852829528305283152832528335283452835528365283752838528395284052841528425284352844528455284652847528485284952850528515285252853528545285552856528575285852859528605286152862528635286452865528665286752868528695287052871528725287352874528755287652877528785287952880528815288252883528845288552886528875288852889528905289152892528935289452895528965289752898528995290052901529025290352904529055290652907529085290952910529115291252913529145291552916529175291852919529205292152922529235292452925529265292752928529295293052931529325293352934529355293652937529385293952940529415294252943529445294552946529475294852949529505295152952529535295452955529565295752958529595296052961529625296352964529655296652967529685296952970529715297252973529745297552976529775297852979529805298152982529835298452985529865298752988529895299052991529925299352994529955299652997529985299953000530015300253003530045300553006530075300853009530105301153012530135301453015530165301753018530195302053021530225302353024530255302653027530285302953030530315303253033530345303553036530375303853039530405304153042530435304453045530465304753048530495305053051530525305353054530555305653057530585305953060530615306253063530645306553066530675306853069530705307153072530735307453075530765307753078530795308053081530825308353084530855308653087530885308953090530915309253093530945309553096530975309853099531005310153102531035310453105531065310753108531095311053111531125311353114531155311653117531185311953120531215312253123531245312553126531275312853129531305313153132531335313453135531365313753138531395314053141531425314353144531455314653147531485314953150531515315253153531545315553156531575315853159531605316153162531635316453165531665316753168531695317053171531725317353174531755317653177531785317953180531815318253183531845318553186531875318853189531905319153192531935319453195531965319753198531995320053201532025320353204532055320653207532085320953210532115321253213532145321553216532175321853219532205322153222532235322453225532265322753228532295323053231532325323353234532355323653237532385323953240532415324253243532445324553246532475324853249532505325153252532535325453255532565325753258532595326053261532625326353264532655326653267532685326953270532715327253273532745327553276532775327853279532805328153282532835328453285532865328753288532895329053291532925329353294532955329653297532985329953300533015330253303533045330553306533075330853309533105331153312533135331453315533165331753318533195332053321533225332353324533255332653327533285332953330533315333253333533345333553336533375333853339533405334153342533435334453345533465334753348533495335053351533525335353354533555335653357533585335953360533615336253363533645336553366533675336853369533705337153372533735337453375533765337753378533795338053381533825338353384533855338653387533885338953390533915339253393533945339553396533975339853399534005340153402534035340453405534065340753408534095341053411534125341353414534155341653417534185341953420534215342253423534245342553426534275342853429534305343153432534335343453435534365343753438534395344053441534425344353444534455344653447534485344953450534515345253453534545345553456534575345853459534605346153462534635346453465534665346753468534695347053471534725347353474534755347653477534785347953480534815348253483534845348553486534875348853489534905349153492534935349453495534965349753498534995350053501535025350353504535055350653507535085350953510535115351253513535145351553516535175351853519535205352153522535235352453525535265352753528535295353053531535325353353534535355353653537535385353953540535415354253543535445354553546535475354853549535505355153552535535355453555535565355753558535595356053561535625356353564535655356653567535685356953570535715357253573535745357553576535775357853579535805358153582535835358453585535865358753588535895359053591535925359353594535955359653597535985359953600536015360253603536045360553606536075360853609536105361153612536135361453615536165361753618536195362053621536225362353624536255362653627536285362953630536315363253633536345363553636536375363853639536405364153642536435364453645536465364753648536495365053651536525365353654536555365653657536585365953660536615366253663536645366553666536675366853669536705367153672536735367453675536765367753678536795368053681536825368353684536855368653687536885368953690536915369253693536945369553696536975369853699537005370153702537035370453705537065370753708537095371053711537125371353714537155371653717537185371953720537215372253723537245372553726537275372853729537305373153732537335373453735537365373753738537395374053741537425374353744537455374653747537485374953750537515375253753537545375553756537575375853759537605376153762537635376453765537665376753768537695377053771537725377353774537755377653777537785377953780537815378253783537845378553786537875378853789537905379153792537935379453795537965379753798537995380053801538025380353804538055380653807538085380953810538115381253813538145381553816538175381853819538205382153822538235382453825538265382753828538295383053831538325383353834538355383653837538385383953840538415384253843538445384553846538475384853849538505385153852538535385453855538565385753858538595386053861538625386353864538655386653867538685386953870538715387253873538745387553876538775387853879538805388153882538835388453885538865388753888538895389053891538925389353894538955389653897538985389953900539015390253903539045390553906539075390853909539105391153912539135391453915539165391753918539195392053921539225392353924539255392653927539285392953930539315393253933539345393553936539375393853939539405394153942539435394453945539465394753948539495395053951539525395353954539555395653957539585395953960539615396253963539645396553966539675396853969539705397153972539735397453975539765397753978539795398053981539825398353984539855398653987539885398953990539915399253993539945399553996539975399853999540005400154002540035400454005540065400754008540095401054011540125401354014540155401654017540185401954020540215402254023540245402554026540275402854029540305403154032540335403454035540365403754038540395404054041540425404354044540455404654047540485404954050540515405254053540545405554056540575405854059540605406154062540635406454065540665406754068540695407054071540725407354074540755407654077540785407954080540815408254083540845408554086540875408854089540905409154092540935409454095540965409754098540995410054101541025410354104541055410654107541085410954110541115411254113541145411554116541175411854119541205412154122541235412454125541265412754128541295413054131541325413354134541355413654137541385413954140541415414254143541445414554146541475414854149541505415154152541535415454155541565415754158541595416054161541625416354164541655416654167541685416954170541715417254173541745417554176541775417854179541805418154182541835418454185541865418754188541895419054191541925419354194541955419654197541985419954200542015420254203542045420554206542075420854209542105421154212542135421454215542165421754218542195422054221542225422354224542255422654227542285422954230542315423254233542345423554236542375423854239542405424154242542435424454245542465424754248542495425054251542525425354254542555425654257542585425954260542615426254263542645426554266542675426854269542705427154272542735427454275542765427754278542795428054281542825428354284542855428654287542885428954290542915429254293542945429554296542975429854299543005430154302543035430454305543065430754308543095431054311543125431354314543155431654317543185431954320543215432254323543245432554326543275432854329543305433154332543335433454335543365433754338543395434054341543425434354344543455434654347543485434954350543515435254353543545435554356543575435854359543605436154362543635436454365543665436754368543695437054371543725437354374543755437654377543785437954380543815438254383543845438554386543875438854389543905439154392543935439454395543965439754398543995440054401544025440354404544055440654407544085440954410544115441254413544145441554416544175441854419544205442154422544235442454425544265442754428544295443054431544325443354434544355443654437544385443954440544415444254443544445444554446544475444854449544505445154452544535445454455544565445754458544595446054461544625446354464544655446654467544685446954470544715447254473544745447554476544775447854479544805448154482544835448454485544865448754488544895449054491544925449354494544955449654497544985449954500545015450254503545045450554506545075450854509545105451154512545135451454515545165451754518545195452054521545225452354524545255452654527545285452954530545315453254533545345453554536545375453854539545405454154542545435454454545545465454754548545495455054551545525455354554545555455654557545585455954560545615456254563545645456554566545675456854569545705457154572545735457454575545765457754578545795458054581545825458354584545855458654587545885458954590545915459254593545945459554596545975459854599546005460154602546035460454605546065460754608546095461054611546125461354614546155461654617546185461954620546215462254623546245462554626546275462854629546305463154632546335463454635546365463754638546395464054641546425464354644546455464654647546485464954650546515465254653546545465554656546575465854659546605466154662546635466454665546665466754668546695467054671546725467354674546755467654677546785467954680546815468254683546845468554686546875468854689546905469154692546935469454695546965469754698546995470054701547025470354704547055470654707547085470954710547115471254713547145471554716547175471854719547205472154722547235472454725547265472754728547295473054731547325473354734547355473654737547385473954740547415474254743547445474554746547475474854749547505475154752547535475454755547565475754758547595476054761547625476354764547655476654767547685476954770547715477254773547745477554776547775477854779547805478154782547835478454785547865478754788547895479054791547925479354794547955479654797547985479954800548015480254803548045480554806548075480854809548105481154812548135481454815548165481754818548195482054821548225482354824548255482654827548285482954830548315483254833548345483554836548375483854839548405484154842548435484454845548465484754848548495485054851548525485354854548555485654857548585485954860548615486254863548645486554866548675486854869548705487154872548735487454875548765487754878548795488054881548825488354884548855488654887548885488954890548915489254893548945489554896548975489854899549005490154902549035490454905549065490754908549095491054911549125491354914549155491654917549185491954920549215492254923549245492554926549275492854929549305493154932549335493454935549365493754938549395494054941549425494354944549455494654947549485494954950549515495254953549545495554956549575495854959549605496154962549635496454965549665496754968549695497054971549725497354974549755497654977549785497954980549815498254983549845498554986549875498854989549905499154992549935499454995549965499754998549995500055001550025500355004550055500655007550085500955010550115501255013550145501555016550175501855019550205502155022550235502455025550265502755028550295503055031550325503355034550355503655037550385503955040550415504255043550445504555046550475504855049550505505155052550535505455055550565505755058550595506055061550625506355064550655506655067550685506955070550715507255073550745507555076550775507855079550805508155082550835508455085550865508755088550895509055091550925509355094550955509655097550985509955100551015510255103551045510555106551075510855109551105511155112551135511455115551165511755118551195512055121551225512355124551255512655127551285512955130551315513255133551345513555136551375513855139551405514155142551435514455145551465514755148551495515055151551525515355154551555515655157551585515955160551615516255163551645516555166551675516855169551705517155172551735517455175551765517755178551795518055181551825518355184551855518655187551885518955190551915519255193551945519555196551975519855199552005520155202552035520455205552065520755208552095521055211552125521355214552155521655217552185521955220552215522255223552245522555226552275522855229552305523155232552335523455235552365523755238552395524055241552425524355244552455524655247552485524955250552515525255253552545525555256552575525855259552605526155262552635526455265552665526755268552695527055271552725527355274552755527655277552785527955280552815528255283552845528555286552875528855289552905529155292552935529455295552965529755298552995530055301553025530355304553055530655307553085530955310553115531255313553145531555316553175531855319553205532155322553235532455325553265532755328553295533055331553325533355334553355533655337553385533955340553415534255343553445534555346553475534855349553505535155352553535535455355553565535755358553595536055361553625536355364553655536655367553685536955370553715537255373553745537555376553775537855379553805538155382553835538455385553865538755388553895539055391553925539355394553955539655397553985539955400554015540255403554045540555406554075540855409554105541155412554135541455415554165541755418554195542055421554225542355424554255542655427554285542955430554315543255433554345543555436554375543855439554405544155442554435544455445554465544755448554495545055451554525545355454554555545655457554585545955460554615546255463554645546555466554675546855469554705547155472554735547455475554765547755478554795548055481554825548355484554855548655487554885548955490554915549255493554945549555496554975549855499555005550155502555035550455505555065550755508555095551055511555125551355514555155551655517555185551955520555215552255523555245552555526555275552855529555305553155532555335553455535555365553755538555395554055541555425554355544555455554655547555485554955550555515555255553555545555555556555575555855559555605556155562555635556455565555665556755568555695557055571555725557355574555755557655577555785557955580555815558255583555845558555586555875558855589555905559155592555935559455595555965559755598555995560055601556025560355604556055560655607556085560955610556115561255613556145561555616556175561855619556205562155622556235562455625556265562755628556295563055631556325563355634556355563655637556385563955640556415564255643556445564555646556475564855649556505565155652556535565455655556565565755658556595566055661556625566355664556655566655667556685566955670556715567255673556745567555676556775567855679556805568155682556835568455685556865568755688556895569055691556925569355694556955569655697556985569955700557015570255703557045570555706557075570855709557105571155712557135571455715557165571755718557195572055721557225572355724557255572655727557285572955730557315573255733557345573555736557375573855739557405574155742557435574455745557465574755748557495575055751557525575355754557555575655757557585575955760557615576255763557645576555766557675576855769557705577155772557735577455775557765577755778557795578055781557825578355784557855578655787557885578955790557915579255793557945579555796557975579855799558005580155802558035580455805558065580755808558095581055811558125581355814558155581655817558185581955820558215582255823558245582555826558275582855829558305583155832558335583455835558365583755838558395584055841558425584355844558455584655847558485584955850558515585255853558545585555856558575585855859558605586155862558635586455865558665586755868558695587055871558725587355874558755587655877558785587955880558815588255883558845588555886558875588855889558905589155892558935589455895558965589755898558995590055901559025590355904559055590655907559085590955910559115591255913559145591555916559175591855919559205592155922559235592455925559265592755928559295593055931559325593355934559355593655937559385593955940559415594255943559445594555946559475594855949559505595155952559535595455955559565595755958559595596055961559625596355964559655596655967559685596955970559715597255973559745597555976559775597855979559805598155982559835598455985559865598755988559895599055991559925599355994559955599655997559985599956000560015600256003560045600556006560075600856009560105601156012560135601456015560165601756018560195602056021560225602356024560255602656027560285602956030560315603256033560345603556036560375603856039560405604156042560435604456045560465604756048560495605056051560525605356054560555605656057560585605956060560615606256063560645606556066560675606856069560705607156072560735607456075560765607756078560795608056081560825608356084560855608656087560885608956090560915609256093560945609556096560975609856099561005610156102561035610456105561065610756108561095611056111561125611356114561155611656117561185611956120561215612256123561245612556126561275612856129561305613156132561335613456135561365613756138561395614056141561425614356144561455614656147561485614956150561515615256153561545615556156561575615856159561605616156162561635616456165561665616756168561695617056171561725617356174561755617656177561785617956180561815618256183561845618556186561875618856189561905619156192561935619456195561965619756198561995620056201562025620356204562055620656207562085620956210562115621256213562145621556216562175621856219562205622156222562235622456225562265622756228562295623056231562325623356234562355623656237562385623956240562415624256243562445624556246562475624856249562505625156252562535625456255562565625756258562595626056261562625626356264562655626656267562685626956270562715627256273562745627556276562775627856279562805628156282562835628456285562865628756288562895629056291562925629356294562955629656297562985629956300563015630256303563045630556306563075630856309563105631156312563135631456315563165631756318563195632056321563225632356324563255632656327563285632956330563315633256333563345633556336563375633856339563405634156342563435634456345563465634756348563495635056351563525635356354563555635656357563585635956360563615636256363563645636556366563675636856369563705637156372563735637456375563765637756378563795638056381563825638356384563855638656387563885638956390563915639256393563945639556396563975639856399564005640156402564035640456405564065640756408564095641056411564125641356414564155641656417564185641956420564215642256423564245642556426564275642856429564305643156432564335643456435564365643756438564395644056441564425644356444564455644656447564485644956450564515645256453564545645556456564575645856459564605646156462564635646456465564665646756468564695647056471564725647356474564755647656477564785647956480564815648256483564845648556486564875648856489564905649156492564935649456495564965649756498564995650056501565025650356504565055650656507565085650956510565115651256513565145651556516565175651856519565205652156522565235652456525565265652756528565295653056531565325653356534565355653656537565385653956540565415654256543565445654556546565475654856549565505655156552565535655456555565565655756558565595656056561565625656356564565655656656567565685656956570565715657256573565745657556576565775657856579565805658156582565835658456585565865658756588565895659056591565925659356594565955659656597565985659956600566015660256603566045660556606566075660856609566105661156612566135661456615566165661756618566195662056621566225662356624566255662656627566285662956630566315663256633566345663556636566375663856639566405664156642566435664456645566465664756648566495665056651566525665356654566555665656657566585665956660566615666256663566645666556666566675666856669566705667156672566735667456675566765667756678566795668056681566825668356684566855668656687566885668956690566915669256693566945669556696566975669856699567005670156702567035670456705567065670756708567095671056711567125671356714567155671656717567185671956720567215672256723567245672556726567275672856729567305673156732567335673456735567365673756738567395674056741567425674356744567455674656747567485674956750567515675256753567545675556756567575675856759567605676156762567635676456765567665676756768567695677056771567725677356774567755677656777567785677956780567815678256783567845678556786567875678856789567905679156792567935679456795567965679756798567995680056801568025680356804568055680656807568085680956810568115681256813568145681556816568175681856819568205682156822568235682456825568265682756828568295683056831568325683356834568355683656837568385683956840568415684256843568445684556846568475684856849568505685156852568535685456855568565685756858568595686056861568625686356864568655686656867568685686956870568715687256873568745687556876568775687856879568805688156882568835688456885568865688756888568895689056891568925689356894568955689656897568985689956900569015690256903569045690556906569075690856909569105691156912569135691456915569165691756918569195692056921569225692356924569255692656927569285692956930569315693256933569345693556936569375693856939569405694156942569435694456945569465694756948569495695056951569525695356954569555695656957569585695956960569615696256963569645696556966569675696856969569705697156972569735697456975569765697756978569795698056981569825698356984569855698656987569885698956990569915699256993569945699556996569975699856999570005700157002570035700457005570065700757008570095701057011570125701357014570155701657017570185701957020570215702257023570245702557026570275702857029570305703157032570335703457035570365703757038570395704057041570425704357044570455704657047570485704957050570515705257053570545705557056570575705857059570605706157062570635706457065570665706757068570695707057071570725707357074570755707657077570785707957080570815708257083570845708557086570875708857089570905709157092570935709457095570965709757098570995710057101571025710357104571055710657107571085710957110571115711257113571145711557116571175711857119571205712157122571235712457125571265712757128571295713057131571325713357134571355713657137571385713957140571415714257143571445714557146571475714857149571505715157152571535715457155571565715757158571595716057161571625716357164571655716657167571685716957170571715717257173571745717557176571775717857179571805718157182571835718457185571865718757188571895719057191571925719357194571955719657197571985719957200572015720257203572045720557206572075720857209572105721157212572135721457215572165721757218572195722057221572225722357224572255722657227572285722957230572315723257233572345723557236572375723857239572405724157242572435724457245572465724757248572495725057251572525725357254572555725657257572585725957260572615726257263572645726557266572675726857269572705727157272572735727457275572765727757278572795728057281572825728357284572855728657287572885728957290572915729257293572945729557296572975729857299573005730157302573035730457305573065730757308573095731057311573125731357314573155731657317573185731957320573215732257323573245732557326573275732857329573305733157332573335733457335573365733757338573395734057341573425734357344573455734657347573485734957350573515735257353573545735557356573575735857359573605736157362573635736457365573665736757368573695737057371573725737357374573755737657377573785737957380573815738257383573845738557386573875738857389573905739157392573935739457395573965739757398573995740057401574025740357404574055740657407574085740957410574115741257413574145741557416574175741857419574205742157422574235742457425574265742757428574295743057431574325743357434574355743657437574385743957440574415744257443574445744557446574475744857449574505745157452574535745457455574565745757458574595746057461574625746357464574655746657467574685746957470574715747257473574745747557476574775747857479574805748157482574835748457485574865748757488574895749057491574925749357494574955749657497574985749957500575015750257503575045750557506575075750857509575105751157512575135751457515575165751757518575195752057521575225752357524575255752657527575285752957530575315753257533575345753557536575375753857539575405754157542575435754457545575465754757548575495755057551575525755357554575555755657557575585755957560575615756257563575645756557566575675756857569575705757157572575735757457575575765757757578575795758057581575825758357584575855758657587575885758957590575915759257593575945759557596575975759857599576005760157602576035760457605576065760757608576095761057611576125761357614576155761657617576185761957620576215762257623576245762557626576275762857629576305763157632576335763457635576365763757638576395764057641576425764357644576455764657647576485764957650576515765257653576545765557656576575765857659576605766157662576635766457665576665766757668576695767057671576725767357674576755767657677576785767957680576815768257683576845768557686576875768857689576905769157692576935769457695576965769757698576995770057701577025770357704577055770657707577085770957710577115771257713577145771557716577175771857719577205772157722577235772457725577265772757728577295773057731577325773357734577355773657737577385773957740577415774257743577445774557746577475774857749577505775157752577535775457755577565775757758577595776057761577625776357764577655776657767577685776957770577715777257773577745777557776577775777857779577805778157782577835778457785577865778757788577895779057791577925779357794577955779657797577985779957800578015780257803578045780557806578075780857809578105781157812578135781457815578165781757818578195782057821578225782357824578255782657827578285782957830578315783257833578345783557836578375783857839578405784157842578435784457845578465784757848578495785057851578525785357854578555785657857578585785957860578615786257863578645786557866578675786857869578705787157872578735787457875578765787757878578795788057881578825788357884578855788657887578885788957890578915789257893578945789557896578975789857899579005790157902579035790457905579065790757908579095791057911579125791357914579155791657917579185791957920579215792257923579245792557926579275792857929579305793157932579335793457935579365793757938579395794057941579425794357944579455794657947579485794957950579515795257953579545795557956579575795857959579605796157962579635796457965579665796757968579695797057971579725797357974579755797657977579785797957980579815798257983579845798557986579875798857989579905799157992579935799457995579965799757998579995800058001580025800358004580055800658007580085800958010580115801258013580145801558016580175801858019580205802158022580235802458025580265802758028580295803058031580325803358034580355803658037580385803958040580415804258043580445804558046580475804858049580505805158052580535805458055580565805758058580595806058061580625806358064580655806658067580685806958070580715807258073580745807558076580775807858079580805808158082580835808458085580865808758088580895809058091580925809358094580955809658097580985809958100581015810258103581045810558106581075810858109581105811158112581135811458115581165811758118581195812058121581225812358124581255812658127581285812958130581315813258133581345813558136581375813858139581405814158142581435814458145581465814758148581495815058151581525815358154581555815658157581585815958160581615816258163581645816558166581675816858169581705817158172581735817458175581765817758178581795818058181581825818358184581855818658187581885818958190581915819258193581945819558196581975819858199582005820158202582035820458205582065820758208582095821058211582125821358214582155821658217582185821958220582215822258223582245822558226582275822858229582305823158232582335823458235582365823758238582395824058241582425824358244582455824658247582485824958250582515825258253582545825558256582575825858259582605826158262582635826458265582665826758268582695827058271582725827358274582755827658277582785827958280582815828258283582845828558286582875828858289582905829158292582935829458295582965829758298582995830058301583025830358304583055830658307583085830958310583115831258313583145831558316583175831858319583205832158322583235832458325583265832758328583295833058331583325833358334583355833658337583385833958340583415834258343583445834558346583475834858349583505835158352583535835458355583565835758358583595836058361583625836358364583655836658367583685836958370583715837258373583745837558376583775837858379583805838158382583835838458385583865838758388583895839058391583925839358394583955839658397583985839958400584015840258403584045840558406584075840858409584105841158412584135841458415584165841758418584195842058421584225842358424584255842658427584285842958430584315843258433584345843558436584375843858439584405844158442584435844458445584465844758448584495845058451584525845358454584555845658457584585845958460584615846258463584645846558466584675846858469584705847158472584735847458475584765847758478584795848058481584825848358484584855848658487584885848958490584915849258493584945849558496584975849858499585005850158502585035850458505585065850758508585095851058511585125851358514585155851658517585185851958520585215852258523585245852558526585275852858529585305853158532585335853458535585365853758538585395854058541585425854358544585455854658547585485854958550585515855258553585545855558556585575855858559585605856158562585635856458565585665856758568585695857058571585725857358574585755857658577585785857958580585815858258583585845858558586585875858858589585905859158592585935859458595585965859758598585995860058601586025860358604586055860658607586085860958610586115861258613586145861558616586175861858619586205862158622586235862458625586265862758628586295863058631586325863358634586355863658637586385863958640586415864258643586445864558646586475864858649586505865158652586535865458655586565865758658586595866058661586625866358664586655866658667586685866958670586715867258673586745867558676586775867858679586805868158682586835868458685586865868758688586895869058691586925869358694586955869658697586985869958700587015870258703587045870558706587075870858709587105871158712587135871458715587165871758718587195872058721587225872358724587255872658727587285872958730587315873258733587345873558736587375873858739587405874158742587435874458745587465874758748587495875058751587525875358754587555875658757587585875958760587615876258763587645876558766587675876858769587705877158772587735877458775587765877758778587795878058781587825878358784587855878658787587885878958790587915879258793587945879558796587975879858799588005880158802588035880458805588065880758808588095881058811588125881358814588155881658817588185881958820588215882258823588245882558826588275882858829588305883158832588335883458835588365883758838588395884058841588425884358844588455884658847588485884958850588515885258853588545885558856588575885858859588605886158862588635886458865588665886758868588695887058871588725887358874588755887658877588785887958880588815888258883588845888558886588875888858889588905889158892588935889458895588965889758898588995890058901589025890358904589055890658907589085890958910589115891258913589145891558916589175891858919589205892158922589235892458925589265892758928589295893058931589325893358934589355893658937589385893958940589415894258943589445894558946589475894858949589505895158952589535895458955589565895758958589595896058961589625896358964589655896658967589685896958970589715897258973589745897558976589775897858979589805898158982589835898458985589865898758988589895899058991589925899358994589955899658997589985899959000590015900259003590045900559006590075900859009590105901159012590135901459015590165901759018590195902059021590225902359024590255902659027590285902959030590315903259033590345903559036590375903859039590405904159042590435904459045590465904759048590495905059051590525905359054590555905659057590585905959060590615906259063590645906559066590675906859069590705907159072590735907459075590765907759078590795908059081590825908359084590855908659087590885908959090590915909259093590945909559096590975909859099591005910159102591035910459105591065910759108591095911059111591125911359114591155911659117591185911959120591215912259123591245912559126591275912859129591305913159132591335913459135591365913759138591395914059141591425914359144591455914659147591485914959150591515915259153591545915559156591575915859159591605916159162591635916459165591665916759168591695917059171591725917359174591755917659177591785917959180591815918259183591845918559186591875918859189591905919159192591935919459195591965919759198591995920059201592025920359204592055920659207592085920959210592115921259213592145921559216592175921859219592205922159222592235922459225592265922759228592295923059231592325923359234592355923659237592385923959240592415924259243592445924559246592475924859249592505925159252592535925459255592565925759258592595926059261592625926359264592655926659267592685926959270592715927259273592745927559276592775927859279592805928159282592835928459285592865928759288592895929059291592925929359294592955929659297592985929959300593015930259303593045930559306593075930859309593105931159312593135931459315593165931759318593195932059321593225932359324593255932659327593285932959330593315933259333593345933559336593375933859339593405934159342593435934459345593465934759348593495935059351593525935359354593555935659357593585935959360593615936259363593645936559366593675936859369593705937159372593735937459375593765937759378593795938059381593825938359384593855938659387593885938959390593915939259393593945939559396593975939859399594005940159402594035940459405594065940759408594095941059411594125941359414594155941659417594185941959420594215942259423594245942559426594275942859429594305943159432594335943459435594365943759438594395944059441594425944359444594455944659447594485944959450594515945259453594545945559456594575945859459594605946159462594635946459465594665946759468594695947059471594725947359474594755947659477594785947959480594815948259483594845948559486594875948859489594905949159492594935949459495594965949759498594995950059501595025950359504595055950659507595085950959510595115951259513595145951559516595175951859519595205952159522595235952459525595265952759528595295953059531595325953359534595355953659537595385953959540595415954259543595445954559546595475954859549595505955159552595535955459555595565955759558595595956059561595625956359564595655956659567595685956959570595715957259573595745957559576595775957859579595805958159582595835958459585595865958759588595895959059591595925959359594595955959659597595985959959600596015960259603596045960559606596075960859609596105961159612596135961459615596165961759618596195962059621596225962359624596255962659627596285962959630596315963259633596345963559636596375963859639596405964159642596435964459645596465964759648596495965059651596525965359654596555965659657596585965959660596615966259663596645966559666596675966859669596705967159672596735967459675596765967759678596795968059681596825968359684596855968659687596885968959690596915969259693596945969559696596975969859699597005970159702597035970459705597065970759708597095971059711597125971359714597155971659717597185971959720597215972259723597245972559726597275972859729597305973159732597335973459735597365973759738597395974059741597425974359744597455974659747597485974959750597515975259753597545975559756597575975859759597605976159762597635976459765597665976759768597695977059771597725977359774597755977659777597785977959780597815978259783597845978559786597875978859789597905979159792597935979459795597965979759798597995980059801598025980359804598055980659807598085980959810598115981259813598145981559816598175981859819598205982159822598235982459825598265982759828598295983059831598325983359834598355983659837598385983959840598415984259843598445984559846598475984859849598505985159852598535985459855598565985759858598595986059861598625986359864598655986659867598685986959870598715987259873598745987559876598775987859879598805988159882598835988459885598865988759888598895989059891598925989359894598955989659897598985989959900599015990259903599045990559906599075990859909599105991159912599135991459915599165991759918599195992059921599225992359924599255992659927599285992959930599315993259933599345993559936599375993859939599405994159942599435994459945599465994759948599495995059951599525995359954599555995659957599585995959960599615996259963599645996559966599675996859969599705997159972599735997459975599765997759978599795998059981599825998359984599855998659987599885998959990599915999259993599945999559996599975999859999600006000160002600036000460005600066000760008600096001060011600126001360014600156001660017600186001960020600216002260023600246002560026600276002860029600306003160032600336003460035600366003760038600396004060041600426004360044600456004660047600486004960050600516005260053600546005560056600576005860059600606006160062600636006460065600666006760068600696007060071600726007360074600756007660077600786007960080600816008260083600846008560086600876008860089600906009160092600936009460095600966009760098600996010060101601026010360104601056010660107601086010960110601116011260113601146011560116601176011860119601206012160122601236012460125601266012760128601296013060131601326013360134601356013660137601386013960140601416014260143601446014560146601476014860149601506015160152601536015460155601566015760158601596016060161601626016360164601656016660167601686016960170601716017260173601746017560176601776017860179601806018160182601836018460185601866018760188601896019060191601926019360194601956019660197601986019960200602016020260203602046020560206602076020860209602106021160212602136021460215602166021760218602196022060221602226022360224602256022660227602286022960230602316023260233602346023560236602376023860239602406024160242602436024460245602466024760248602496025060251602526025360254602556025660257602586025960260602616026260263602646026560266602676026860269602706027160272602736027460275602766027760278602796028060281602826028360284602856028660287602886028960290602916029260293602946029560296602976029860299603006030160302603036030460305603066030760308603096031060311603126031360314603156031660317603186031960320603216032260323603246032560326603276032860329603306033160332603336033460335603366033760338603396034060341603426034360344603456034660347603486034960350603516035260353603546035560356603576035860359603606036160362603636036460365603666036760368603696037060371603726037360374603756037660377603786037960380603816038260383603846038560386603876038860389603906039160392603936039460395603966039760398603996040060401604026040360404604056040660407604086040960410604116041260413604146041560416604176041860419604206042160422604236042460425604266042760428604296043060431604326043360434604356043660437604386043960440604416044260443604446044560446604476044860449604506045160452604536045460455604566045760458604596046060461604626046360464604656046660467604686046960470604716047260473604746047560476604776047860479604806048160482604836048460485604866048760488604896049060491604926049360494604956049660497604986049960500605016050260503605046050560506605076050860509605106051160512605136051460515605166051760518605196052060521605226052360524605256052660527605286052960530605316053260533605346053560536605376053860539605406054160542605436054460545605466054760548605496055060551605526055360554605556055660557605586055960560605616056260563605646056560566605676056860569605706057160572605736057460575605766057760578605796058060581605826058360584605856058660587605886058960590605916059260593605946059560596605976059860599606006060160602606036060460605606066060760608606096061060611606126061360614606156061660617606186061960620606216062260623606246062560626606276062860629606306063160632606336063460635606366063760638606396064060641606426064360644606456064660647606486064960650606516065260653606546065560656606576065860659606606066160662606636066460665606666066760668606696067060671606726067360674606756067660677606786067960680606816068260683606846068560686606876068860689606906069160692606936069460695606966069760698606996070060701607026070360704607056070660707607086070960710607116071260713607146071560716607176071860719607206072160722607236072460725607266072760728607296073060731607326073360734607356073660737607386073960740607416074260743607446074560746607476074860749607506075160752607536075460755607566075760758607596076060761607626076360764607656076660767607686076960770607716077260773607746077560776607776077860779607806078160782607836078460785607866078760788607896079060791607926079360794607956079660797607986079960800608016080260803608046080560806608076080860809608106081160812608136081460815608166081760818608196082060821608226082360824608256082660827608286082960830608316083260833608346083560836608376083860839608406084160842608436084460845608466084760848608496085060851608526085360854608556085660857608586085960860608616086260863608646086560866608676086860869608706087160872608736087460875608766087760878608796088060881608826088360884608856088660887608886088960890608916089260893608946089560896608976089860899609006090160902609036090460905609066090760908609096091060911609126091360914609156091660917609186091960920609216092260923609246092560926609276092860929609306093160932609336093460935609366093760938609396094060941609426094360944609456094660947609486094960950609516095260953609546095560956609576095860959609606096160962609636096460965609666096760968609696097060971609726097360974609756097660977609786097960980609816098260983609846098560986609876098860989609906099160992609936099460995609966099760998609996100061001610026100361004610056100661007610086100961010610116101261013610146101561016610176101861019610206102161022610236102461025610266102761028610296103061031610326103361034610356103661037610386103961040610416104261043610446104561046610476104861049610506105161052610536105461055610566105761058610596106061061610626106361064610656106661067610686106961070610716107261073610746107561076610776107861079610806108161082610836108461085610866108761088610896109061091610926109361094610956109661097610986109961100611016110261103611046110561106611076110861109611106111161112611136111461115611166111761118611196112061121611226112361124611256112661127611286112961130611316113261133611346113561136611376113861139611406114161142611436114461145611466114761148611496115061151611526115361154611556115661157611586115961160611616116261163611646116561166611676116861169611706117161172611736117461175611766117761178611796118061181611826118361184611856118661187611886118961190611916119261193611946119561196611976119861199612006120161202612036120461205612066120761208612096121061211612126121361214612156121661217612186121961220612216122261223612246122561226612276122861229612306123161232612336123461235612366123761238612396124061241612426124361244612456124661247612486124961250612516125261253612546125561256612576125861259612606126161262612636126461265612666126761268612696127061271612726127361274612756127661277612786127961280612816128261283612846128561286612876128861289612906129161292612936129461295612966129761298612996130061301613026130361304613056130661307613086130961310613116131261313613146131561316613176131861319613206132161322613236132461325613266132761328613296133061331613326133361334613356133661337613386133961340613416134261343613446134561346613476134861349613506135161352613536135461355613566135761358613596136061361613626136361364613656136661367613686136961370613716137261373613746137561376613776137861379613806138161382613836138461385613866138761388613896139061391613926139361394613956139661397613986139961400614016140261403614046140561406614076140861409614106141161412614136141461415614166141761418614196142061421614226142361424 |
- /*
- * QuickJS Javascript Engine
- *
- * Copyright (c) 2017-2025 Fabrice Bellard
- * Copyright (c) 2017-2025 Charlie Gordon
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
- #include <stdlib.h>
- #include <stdio.h>
- #include <stdarg.h>
- #include <inttypes.h>
- #include <string.h>
- #include <assert.h>
- #include <sys/time.h>
- #include <time.h>
- #include <fenv.h>
- #include <math.h>
- #if defined(__APPLE__)
- #include <malloc/malloc.h>
- #elif defined(__linux__) || defined(__GLIBC__)
- #include <malloc.h>
- #elif defined(__FreeBSD__)
- #include <malloc_np.h>
- #endif
- #include "cutils.h"
- #include "list.h"
- #include "quickjs.h"
- #include "libregexp.h"
- #include "libunicode.h"
- #include "dtoa.h"
- #define OPTIMIZE 1
- #define SHORT_OPCODES 1
- #if defined(__EMSCRIPTEN__)
- #define DIRECT_DISPATCH 0
- #else
- #define DIRECT_DISPATCH 1
- #endif
- #if defined(__APPLE__)
- #define MALLOC_OVERHEAD 0
- #else
- #define MALLOC_OVERHEAD 8
- #endif
- #if !defined(_WIN32)
- /* define it if printf uses the RNDN rounding mode instead of RNDNA */
- #define CONFIG_PRINTF_RNDN
- #endif
- /* define to include Atomics.* operations which depend on the OS
- threads */
- #if !defined(__EMSCRIPTEN__)
- #define CONFIG_ATOMICS
- #endif
- #if !defined(__EMSCRIPTEN__)
- /* enable stack limitation */
- #define CONFIG_STACK_CHECK
- #endif
- /* dump object free */
- //#define DUMP_FREE
- //#define DUMP_CLOSURE
- /* dump the bytecode of the compiled functions: combination of bits
- 1: dump pass 3 final byte code
- 2: dump pass 2 code
- 4: dump pass 1 code
- 8: dump stdlib functions
- 16: dump bytecode in hex
- 32: dump line number table
- 64: dump compute_stack_size
- */
- //#define DUMP_BYTECODE (1)
- /* dump the occurence of the automatic GC */
- //#define DUMP_GC
- /* dump objects freed by the garbage collector */
- //#define DUMP_GC_FREE
- /* dump objects leaking when freeing the runtime */
- //#define DUMP_LEAKS 1
- /* dump memory usage before running the garbage collector */
- //#define DUMP_MEM
- //#define DUMP_OBJECTS /* dump objects in JS_FreeContext */
- //#define DUMP_ATOMS /* dump atoms in JS_FreeContext */
- //#define DUMP_SHAPES /* dump shapes in JS_FreeContext */
- //#define DUMP_MODULE_RESOLVE
- //#define DUMP_MODULE_EXEC
- //#define DUMP_PROMISE
- //#define DUMP_READ_OBJECT
- //#define DUMP_ROPE_REBALANCE
- /* add asm labels to each opcode so that it is easier to see the generated code */
- //#define OPCODE_ASM_LABEL
- /* test the GC by forcing it before each object allocation */
- //#define FORCE_GC_AT_MALLOC
- #ifdef CONFIG_ATOMICS
- #include <pthread.h>
- #include <stdatomic.h>
- #include <errno.h>
- #endif
- enum {
- /* classid tag */ /* union usage | properties */
- JS_CLASS_OBJECT = 1, /* must be first */
- JS_CLASS_ARRAY, /* u.array | length */
- JS_CLASS_ERROR,
- JS_CLASS_NUMBER, /* u.object_data */
- JS_CLASS_STRING, /* u.object_data */
- JS_CLASS_BOOLEAN, /* u.object_data */
- JS_CLASS_SYMBOL, /* u.object_data */
- JS_CLASS_ARGUMENTS, /* u.array | length */
- JS_CLASS_MAPPED_ARGUMENTS, /* u.array | length */
- JS_CLASS_DATE, /* u.object_data */
- JS_CLASS_MODULE_NS,
- JS_CLASS_C_FUNCTION, /* u.cfunc */
- JS_CLASS_BYTECODE_FUNCTION, /* u.func */
- JS_CLASS_BOUND_FUNCTION, /* u.bound_function */
- JS_CLASS_C_FUNCTION_DATA, /* u.c_function_data_record */
- JS_CLASS_GENERATOR_FUNCTION, /* u.func */
- JS_CLASS_FOR_IN_ITERATOR, /* u.for_in_iterator */
- JS_CLASS_REGEXP, /* u.regexp */
- JS_CLASS_ARRAY_BUFFER, /* u.array_buffer */
- JS_CLASS_SHARED_ARRAY_BUFFER, /* u.array_buffer */
- JS_CLASS_UINT8C_ARRAY, /* u.array (typed_array) */
- JS_CLASS_INT8_ARRAY, /* u.array (typed_array) */
- JS_CLASS_UINT8_ARRAY, /* u.array (typed_array) */
- JS_CLASS_INT16_ARRAY, /* u.array (typed_array) */
- JS_CLASS_UINT16_ARRAY, /* u.array (typed_array) */
- JS_CLASS_INT32_ARRAY, /* u.array (typed_array) */
- JS_CLASS_UINT32_ARRAY, /* u.array (typed_array) */
- JS_CLASS_BIG_INT64_ARRAY, /* u.array (typed_array) */
- JS_CLASS_BIG_UINT64_ARRAY, /* u.array (typed_array) */
- JS_CLASS_FLOAT16_ARRAY, /* u.array (typed_array) */
- JS_CLASS_FLOAT32_ARRAY, /* u.array (typed_array) */
- JS_CLASS_FLOAT64_ARRAY, /* u.array (typed_array) */
- JS_CLASS_DATAVIEW, /* u.typed_array */
- JS_CLASS_BIG_INT, /* u.object_data */
- JS_CLASS_MAP, /* u.map_state */
- JS_CLASS_SET, /* u.map_state */
- JS_CLASS_WEAKMAP, /* u.map_state */
- JS_CLASS_WEAKSET, /* u.map_state */
- JS_CLASS_ITERATOR, /* u.map_iterator_data */
- JS_CLASS_ITERATOR_CONCAT, /* u.iterator_concat_data */
- JS_CLASS_ITERATOR_HELPER, /* u.iterator_helper_data */
- JS_CLASS_ITERATOR_WRAP, /* u.iterator_wrap_data */
- JS_CLASS_MAP_ITERATOR, /* u.map_iterator_data */
- JS_CLASS_SET_ITERATOR, /* u.map_iterator_data */
- JS_CLASS_ARRAY_ITERATOR, /* u.array_iterator_data */
- JS_CLASS_STRING_ITERATOR, /* u.array_iterator_data */
- JS_CLASS_REGEXP_STRING_ITERATOR, /* u.regexp_string_iterator_data */
- JS_CLASS_GENERATOR, /* u.generator_data */
- JS_CLASS_GLOBAL_OBJECT, /* u.global_object */
- JS_CLASS_RAWJSON,
- JS_CLASS_PROXY, /* u.proxy_data */
- JS_CLASS_PROMISE, /* u.promise_data */
- JS_CLASS_PROMISE_RESOLVE_FUNCTION, /* u.promise_function_data */
- JS_CLASS_PROMISE_REJECT_FUNCTION, /* u.promise_function_data */
- JS_CLASS_ASYNC_FUNCTION, /* u.func */
- JS_CLASS_ASYNC_FUNCTION_RESOLVE, /* u.async_function_data */
- JS_CLASS_ASYNC_FUNCTION_REJECT, /* u.async_function_data */
- JS_CLASS_ASYNC_FROM_SYNC_ITERATOR, /* u.async_from_sync_iterator_data */
- JS_CLASS_ASYNC_GENERATOR_FUNCTION, /* u.func */
- JS_CLASS_ASYNC_GENERATOR, /* u.async_generator_data */
- JS_CLASS_WEAK_REF,
- JS_CLASS_FINALIZATION_REGISTRY,
-
- JS_CLASS_INIT_COUNT, /* last entry for predefined classes */
- };
- /* number of typed array types */
- #define JS_TYPED_ARRAY_COUNT (JS_CLASS_FLOAT64_ARRAY - JS_CLASS_UINT8C_ARRAY + 1)
- static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT];
- #define typed_array_size_log2(classid) (typed_array_size_log2[(classid)- JS_CLASS_UINT8C_ARRAY])
- typedef enum JSErrorEnum {
- JS_EVAL_ERROR,
- JS_RANGE_ERROR,
- JS_REFERENCE_ERROR,
- JS_SYNTAX_ERROR,
- JS_TYPE_ERROR,
- JS_URI_ERROR,
- JS_INTERNAL_ERROR,
- JS_AGGREGATE_ERROR,
- JS_NATIVE_ERROR_COUNT, /* number of different NativeError objects */
- } JSErrorEnum;
- /* the variable and scope indexes must fit on 16 bits. The (-1) and
- ARG_SCOPE_END values are reserved. */
- #define JS_MAX_LOCAL_VARS 65534
- #define JS_STACK_SIZE_MAX 65534
- #define JS_STRING_LEN_MAX ((1 << 30) - 1)
- /* strings <= this length are not concatenated using ropes. if too
- small, the rope memory overhead becomes high. */
- #define JS_STRING_ROPE_SHORT_LEN 512
- /* specific threshold for initial rope use */
- #define JS_STRING_ROPE_SHORT2_LEN 8192
- /* rope depth at which we rebalance */
- #define JS_STRING_ROPE_MAX_DEPTH 60
- #define __exception __attribute__((warn_unused_result))
- typedef struct JSShape JSShape;
- typedef struct JSString JSString;
- typedef struct JSString JSAtomStruct;
- typedef struct JSObject JSObject;
- #define JS_VALUE_GET_OBJ(v) ((JSObject *)JS_VALUE_GET_PTR(v))
- #define JS_VALUE_GET_STRING(v) ((JSString *)JS_VALUE_GET_PTR(v))
- #define JS_VALUE_GET_STRING_ROPE(v) ((JSStringRope *)JS_VALUE_GET_PTR(v))
- typedef enum {
- JS_GC_PHASE_NONE,
- JS_GC_PHASE_DECREF,
- JS_GC_PHASE_REMOVE_CYCLES,
- } JSGCPhaseEnum;
- typedef enum OPCodeEnum OPCodeEnum;
- /* JS malloc */
- #define JS_MALLOC_ALIGN 8
- #define JS_MALLOC_ARENA_SIZE 4096
- #define JS_MALLOC_BLOCK_SIZE_COUNT 31
- #define JS_MALLOC_MIN_SMALL_SIZE 16
- #define JS_MALLOC_MAX_SMALL_SIZE 512
- #if defined(__SANITIZE_ADDRESS__)
- /* use the host malloc() for all allocations */
- #define JS_MALLOC_LARGE_BLOCKS_ONLY 1
- #else
- #define JS_MALLOC_LARGE_BLOCKS_ONLY 0
- #endif
- /* allow iteration among the allocated blocks. Currently not used. May
- be used to suppress the memory overhead of JSGCObjectHeader */
- //#define JS_MALLOC_USE_ITER
- #define FREE_NIL 0xffff
- /* 8 byte header */
- /* Notes:
- - the header is necessary at least to recover a pointer to
- JSMallocArena because we don't want to enforce a page
- alignment on the system malloc().
- - could store the block offset instead of (block_idx,
- block_size_idx), but it would require a division to recover the block
- index.
- */
- typedef struct JSMallocBlockHeader {
- union {
- uint16_t block_idx; /* FREE_NIL if large block */
- uint16_t free_next; /* FREE_NIL if none */
- } u;
- uint8_t block_size_idx;
- uint8_t gc_obj_type : 7;
- uint8_t mark : 1;
- int ref_count;
- __attribute__((aligned(JS_MALLOC_ALIGN))) uint8_t user_data[];
- } JSMallocBlockHeader;
- typedef struct JSMallocLargeBlockHeader {
- #ifdef JS_MALLOC_USE_ITER
- struct list_head link;
- #endif
- JSMallocBlockHeader header;
- } JSMallocLargeBlockHeader;
- typedef struct {
- struct list_head free_link;
- struct list_head link;
- uint8_t block_size_idx;
- uint16_t n_used_blocks; /* number of allocated blocks */
- uint16_t n_blocks; /* total number of blocks */
- uint16_t first_free_block; /* FREE_NIL if none */
- #ifdef JS_MALLOC_USE_ITER
- /* bit set to 1 for allocated block */
- uint32_t bitmap[((JS_MALLOC_ARENA_SIZE / JS_MALLOC_MIN_SMALL_SIZE) + 31) / 32];
- #endif
- /* n_blocks memory blocks of identical size */
- __attribute__((aligned(JS_MALLOC_ALIGN))) uint8_t blocks[];
- } JSMallocArena;
- typedef struct {
- struct list_head arena_list[JS_MALLOC_BLOCK_SIZE_COUNT]; /* list of JSMallocArena.link (all arenas) */
- struct list_head free_arena_list[JS_MALLOC_BLOCK_SIZE_COUNT]; /* list of JSMallocArena.free_link (arenas where n_used_blocks < n_blocks) */
- #ifdef JS_MALLOC_USE_ITER
- struct list_head large_block_list; /* list of JSMallocLargeBlockHeader.link */
- #endif
- __attribute__((aligned(JS_MALLOC_ALIGN))) uint8_t zero_size_block[sizeof(JSMallocBlockHeader)];
- /* callbacks to the host malloc */
- JSMallocFunctions mf;
- JSMallocState malloc_state;
- } JSMallocContext;
- /* end JS Malloc */
- struct JSRuntime {
- JSMallocContext malloc_ctx;
- const char *rt_info;
- int atom_hash_size; /* power of two */
- int atom_count;
- int atom_size;
- int atom_count_resize; /* resize hash table at this count */
- uint32_t *atom_hash;
- JSAtomStruct **atom_array;
- int atom_free_index; /* 0 = none */
- int class_count; /* size of class_array */
- JSClass *class_array;
- struct list_head context_list; /* list of JSContext.link */
- /* list of JSGCObjectHeader.link. List of allocated GC objects (used
- by the garbage collector) */
- struct list_head gc_obj_list;
- /* list of JSGCObjectHeader.link. Used during JS_FreeValueRT() */
- struct list_head gc_zero_ref_count_list;
- struct list_head tmp_obj_list; /* used during GC */
- JSGCPhaseEnum gc_phase : 8;
- size_t malloc_gc_threshold;
- struct list_head weakref_list; /* list of JSWeakRefHeader.link */
- #ifdef DUMP_LEAKS
- struct list_head string_list; /* list of JSString.link */
- #endif
- /* stack limitation */
- uintptr_t stack_size; /* in bytes, 0 if no limit */
- uintptr_t stack_top;
- uintptr_t stack_limit; /* lower stack limit */
- JSValue current_exception;
- /* true if the current exception cannot be catched */
- BOOL current_exception_is_uncatchable : 8;
- /* true if inside an out of memory error, to avoid recursing */
- BOOL in_out_of_memory : 8;
- struct JSStackFrame *current_stack_frame;
- JSInterruptHandler *interrupt_handler;
- void *interrupt_opaque;
- JSHostPromiseRejectionTracker *host_promise_rejection_tracker;
- void *host_promise_rejection_tracker_opaque;
- struct list_head job_list; /* list of JSJobEntry.link */
- JSModuleNormalizeFunc *module_normalize_func;
- BOOL module_loader_has_attr;
- union {
- JSModuleLoaderFunc *module_loader_func;
- JSModuleLoaderFunc2 *module_loader_func2;
- } u;
- JSModuleCheckSupportedImportAttributes *module_check_attrs;
- void *module_loader_opaque;
- /* timestamp for internal use in module evaluation */
- int64_t module_async_evaluation_next_timestamp;
- BOOL can_block : 8; /* TRUE if Atomics.wait can block */
- /* used to allocate, free and clone SharedArrayBuffers */
- JSSharedArrayBufferFunctions sab_funcs;
- /* see JS_SetStripInfo() */
- uint8_t strip_flags;
-
- /* Shape hash table */
- int shape_hash_bits;
- int shape_hash_size;
- int shape_hash_count; /* number of hashed shapes */
- JSShape **shape_hash;
- void *user_opaque;
- };
- struct JSClass {
- uint32_t class_id; /* 0 means free entry */
- JSAtom class_name;
- JSClassFinalizer *finalizer;
- JSClassGCMark *gc_mark;
- JSClassCall *call;
- /* pointers for exotic behavior, can be NULL if none are present */
- const JSClassExoticMethods *exotic;
- };
- #define JS_MODE_STRICT (1 << 0)
- #define JS_MODE_ASYNC (1 << 2) /* async function */
- #define JS_MODE_BACKTRACE_BARRIER (1 << 3) /* stop backtrace before this frame */
- typedef struct JSStackFrame {
- struct JSStackFrame *prev_frame; /* NULL if first stack frame */
- JSValue cur_func; /* current function, JS_UNDEFINED if the frame is detached */
- JSValue *arg_buf; /* arguments */
- JSValue *var_buf; /* variables */
- struct JSVarRef **var_refs; /* references to arguments or local variables */
- const uint8_t *cur_pc; /* only used in bytecode functions : PC of the
- instruction after the call */
- int arg_count;
- int js_mode; /* not supported for C functions */
- /* only used in generators. Current stack pointer value. NULL if
- the function is running. */
- JSValue *cur_sp;
- } JSStackFrame;
- typedef enum {
- JS_GC_OBJ_TYPE_JS_OBJECT,
- JS_GC_OBJ_TYPE_FUNCTION_BYTECODE,
- JS_GC_OBJ_TYPE_SHAPE,
- JS_GC_OBJ_TYPE_VAR_REF,
- JS_GC_OBJ_TYPE_ASYNC_FUNCTION,
- JS_GC_OBJ_TYPE_JS_CONTEXT,
- JS_GC_OBJ_TYPE_MODULE,
- } JSGCObjectTypeEnum;
- /* header for GC objects. GC objects are C data structures with a
- reference count that can reference other GC objects. JS Objects are
- a particular type of GC object. */
- struct JSGCObjectHeader {
- struct list_head link;
- };
- typedef enum {
- JS_WEAKREF_TYPE_MAP,
- JS_WEAKREF_TYPE_WEAKREF,
- JS_WEAKREF_TYPE_FINREC,
- } JSWeakRefHeaderTypeEnum;
- typedef struct {
- struct list_head link;
- JSWeakRefHeaderTypeEnum weakref_type;
- } JSWeakRefHeader;
- typedef struct JSVarRef {
- JSGCObjectHeader header; /* must come first */
- uint8_t is_detached;
- uint8_t is_lexical; /* only used with global variables */
- uint8_t is_const; /* only used with global variables */
- JSValue *pvalue; /* pointer to the value, either on the stack or
- to 'value' */
- union {
- JSValue value; /* used when is_detached = TRUE */
- struct {
- uint16_t var_ref_idx; /* index in JSStackFrame.var_refs[] */
- JSStackFrame *stack_frame;
- }; /* used when is_detached = FALSE */
- };
- } JSVarRef;
- /* bigint */
- #if JS_LIMB_BITS == 32
- typedef int32_t js_slimb_t;
- typedef uint32_t js_limb_t;
- typedef int64_t js_sdlimb_t;
- typedef uint64_t js_dlimb_t;
- #define JS_LIMB_DIGITS 9
- #else
- typedef __int128 int128_t;
- typedef unsigned __int128 uint128_t;
- typedef int64_t js_slimb_t;
- typedef uint64_t js_limb_t;
- typedef int128_t js_sdlimb_t;
- typedef uint128_t js_dlimb_t;
- #define JS_LIMB_DIGITS 19
- #endif
- typedef struct JSBigInt {
- uint32_t len; /* number of limbs, >= 1 */
- js_limb_t tab[]; /* two's complement representation, always
- normalized so that 'len' is the minimum
- possible length >= 1 */
- } JSBigInt;
- /* this bigint structure can hold a 64 bit integer */
- typedef struct {
- js_limb_t big_int_buf[sizeof(JSBigInt) / sizeof(js_limb_t)]; /* for JSBigInt */
- /* must come just after */
- js_limb_t tab[(64 + JS_LIMB_BITS - 1) / JS_LIMB_BITS];
- } JSBigIntBuf;
-
- typedef enum {
- JS_AUTOINIT_ID_PROTOTYPE,
- JS_AUTOINIT_ID_MODULE_NS,
- JS_AUTOINIT_ID_PROP,
- } JSAutoInitIDEnum;
- /* must be large enough to have a negligible runtime cost and small
- enough to call the interrupt callback often. */
- #define JS_INTERRUPT_COUNTER_INIT 10000
- struct JSContext {
- JSGCObjectHeader header; /* must come first */
- JSRuntime *rt;
- struct list_head link;
- uint16_t binary_object_count;
- int binary_object_size;
-
- JSShape *array_shape; /* initial shape for Array objects */
- JSShape *arguments_shape; /* shape for arguments objects */
- JSShape *mapped_arguments_shape; /* shape for mapped arguments objects */
- JSShape *regexp_shape; /* shape for regexp objects */
- JSShape *regexp_result_shape; /* shape for regexp result objects */
- JSValue *class_proto;
- JSValue function_proto;
- JSValue function_ctor;
- JSValue array_ctor;
- JSValue regexp_ctor;
- JSValue promise_ctor;
- JSValue native_error_proto[JS_NATIVE_ERROR_COUNT];
- JSValue iterator_ctor;
- JSValue async_iterator_proto;
- JSValue array_proto_values;
- JSValue throw_type_error;
- JSValue eval_obj;
- JSValue global_obj; /* global object */
- JSValue global_var_obj; /* contains the global let/const definitions */
- uint64_t random_state;
- /* when the counter reaches zero, JSRutime.interrupt_handler is called */
- int interrupt_counter;
- struct list_head loaded_modules; /* list of JSModuleDef.link */
- /* if NULL, RegExp compilation is not supported */
- JSValue (*compile_regexp)(JSContext *ctx, JSValueConst pattern,
- JSValueConst flags);
- /* if NULL, eval is not supported */
- JSValue (*eval_internal)(JSContext *ctx, JSValueConst this_obj,
- const char *input, size_t input_len,
- const char *filename, int flags, int scope_idx);
- void *user_opaque;
- };
- typedef union JSFloat64Union {
- double d;
- uint64_t u64;
- uint32_t u32[2];
- } JSFloat64Union;
- enum {
- JS_ATOM_TYPE_STRING = 1,
- JS_ATOM_TYPE_GLOBAL_SYMBOL,
- JS_ATOM_TYPE_SYMBOL,
- JS_ATOM_TYPE_PRIVATE,
- };
- typedef enum {
- JS_ATOM_KIND_STRING,
- JS_ATOM_KIND_SYMBOL,
- JS_ATOM_KIND_PRIVATE,
- } JSAtomKindEnum;
- #define JS_ATOM_HASH_MASK ((1 << 30) - 1)
- #define JS_ATOM_HASH_PRIVATE JS_ATOM_HASH_MASK
- struct JSString {
- uint32_t len : 31;
- uint8_t is_wide_char : 1; /* 0 = 8 bits, 1 = 16 bits characters */
- /* for JS_ATOM_TYPE_SYMBOL: hash = weakref_count, atom_type = 3,
- for JS_ATOM_TYPE_PRIVATE: hash = JS_ATOM_HASH_PRIVATE, atom_type = 3
- XXX: could change encoding to have one more bit in hash */
- uint32_t hash : 30;
- uint8_t atom_type : 2; /* != 0 if atom, JS_ATOM_TYPE_x */
- uint32_t hash_next; /* atom_index for JS_ATOM_TYPE_SYMBOL */
- #ifdef DUMP_LEAKS
- struct list_head link; /* string list */
- #endif
- union {
- uint8_t str8[0]; /* 8 bit strings will get an extra null terminator */
- uint16_t str16[0];
- } u;
- };
- typedef struct JSStringRope {
- uint32_t len;
- uint8_t is_wide_char; /* 0 = 8 bits, 1 = 16 bits characters */
- uint8_t depth; /* max depth of the rope tree */
- /* XXX: could reduce memory usage by using a direct pointer with
- bit 0 to select rope or string */
- JSValue left;
- JSValue right; /* might be the empty string */
- } JSStringRope;
- typedef enum {
- JS_CLOSURE_LOCAL, /* 'var_idx' is the index of a local variable in the parent function */
- JS_CLOSURE_ARG, /* 'var_idx' is the index of a argument variable in the parent function */
- JS_CLOSURE_REF, /* 'var_idx' is the index of a closure variable in the parent function */
- JS_CLOSURE_GLOBAL_REF, /* 'var_idx' in the index of a closure
- variable in the parent function
- referencing a global variable */
- JS_CLOSURE_GLOBAL_DECL, /* global variable declaration (eval code only) */
- JS_CLOSURE_GLOBAL, /* global variable (eval code only) */
- JS_CLOSURE_MODULE_DECL, /* definition of a module variable (eval code only) */
- JS_CLOSURE_MODULE_IMPORT, /* definition of a module import (eval code only) */
- } JSClosureTypeEnum;
- typedef struct JSClosureVar {
- JSClosureTypeEnum closure_type : 3;
- uint8_t is_lexical : 1; /* lexical variable */
- uint8_t is_const : 1; /* const variable (is_lexical = 1 if is_const = 1 */
- uint8_t var_kind : 4; /* see JSVarKindEnum */
- uint16_t var_idx; /* is_local = TRUE: index to a normal variable of the
- parent function. otherwise: index to a closure
- variable of the parent function */
- JSAtom var_name;
- } JSClosureVar;
- #define ARG_SCOPE_INDEX 1
- #define ARG_SCOPE_END (-2)
- typedef enum {
- /* XXX: add more variable kinds here instead of using bit fields */
- JS_VAR_NORMAL,
- JS_VAR_FUNCTION_DECL, /* lexical var with function declaration */
- JS_VAR_NEW_FUNCTION_DECL, /* lexical var with async/generator
- function declaration */
- JS_VAR_CATCH,
- JS_VAR_FUNCTION_NAME, /* function expression name */
- JS_VAR_PRIVATE_FIELD,
- JS_VAR_PRIVATE_METHOD,
- JS_VAR_PRIVATE_GETTER,
- JS_VAR_PRIVATE_SETTER, /* must come after JS_VAR_PRIVATE_GETTER */
- JS_VAR_PRIVATE_GETTER_SETTER, /* must come after JS_VAR_PRIVATE_SETTER */
- JS_VAR_GLOBAL_FUNCTION_DECL, /* global function definition, only in JSVarDef */
- } JSVarKindEnum;
- typedef struct JSBytecodeVarDef {
- JSAtom var_name;
- /* index into JSFunctionBytecode.vars of the next variable in the same or
- enclosing lexical scope
- */
- int scope_next; /* XXX: store on 16 bits */
- uint8_t is_const : 1;
- uint8_t is_lexical : 1;
- uint8_t is_captured : 1; /* XXX: could remove and use a var_ref_idx value */
- uint8_t has_scope: 1; /* true if JSVarDef.scope_level != 0 */
- uint8_t var_kind : 4; /* see JSVarKindEnum */
- /* If is_captured = TRUE, provides, the index of the corresponding
- JSVarRef on stack. It would be more compact to have a separate
- table with the corresponding inverted table but it requires
- more modifications in the code. */
- uint16_t var_ref_idx;
- } JSBytecodeVarDef;
- /* for the encoding of the pc2line table */
- #define PC2LINE_BASE (-1)
- #define PC2LINE_RANGE 5
- #define PC2LINE_OP_FIRST 1
- #define PC2LINE_DIFF_PC_MAX ((255 - PC2LINE_OP_FIRST) / PC2LINE_RANGE)
- typedef enum JSFunctionKindEnum {
- JS_FUNC_NORMAL = 0,
- JS_FUNC_GENERATOR = (1 << 0),
- JS_FUNC_ASYNC = (1 << 1),
- JS_FUNC_ASYNC_GENERATOR = (JS_FUNC_GENERATOR | JS_FUNC_ASYNC),
- } JSFunctionKindEnum;
- typedef struct JSFunctionBytecode {
- JSGCObjectHeader header; /* must come first */
- uint8_t js_mode;
- uint8_t has_prototype : 1; /* true if a prototype field is necessary */
- uint8_t has_simple_parameter_list : 1;
- uint8_t is_derived_class_constructor : 1;
- /* true if home_object needs to be initialized */
- uint8_t need_home_object : 1;
- uint8_t func_kind : 2;
- uint8_t new_target_allowed : 1;
- uint8_t super_call_allowed : 1;
- uint8_t super_allowed : 1;
- uint8_t arguments_allowed : 1;
- uint8_t has_debug : 1;
- uint8_t read_only_bytecode : 1;
- uint8_t is_direct_or_indirect_eval : 1; /* used by JS_GetScriptOrModuleName() */
- /* XXX: 10 bits available */
- uint8_t *byte_code_buf; /* (self pointer) */
- int byte_code_len;
- JSAtom func_name;
- JSBytecodeVarDef *vardefs; /* arguments + local variables (arg_count + var_count) (self pointer) */
- JSClosureVar *closure_var; /* list of variables in the closure (self pointer) */
- uint16_t arg_count;
- uint16_t var_count;
- uint16_t defined_arg_count; /* for length function property */
- uint16_t stack_size; /* maximum stack size */
- uint16_t var_ref_count; /* number of local variable references */
- JSContext *realm; /* function realm */
- JSValue *cpool; /* constant pool (self pointer) */
- int cpool_count;
- int closure_var_count;
- struct {
- /* debug info, move to separate structure to save memory? */
- JSAtom filename;
- int source_len;
- int pc2line_len;
- uint8_t *pc2line_buf;
- char *source;
- } debug;
- } JSFunctionBytecode;
- typedef struct JSBoundFunction {
- JSValue func_obj;
- JSValue this_val;
- int argc;
- JSValue argv[0];
- } JSBoundFunction;
- typedef enum JSIteratorKindEnum {
- JS_ITERATOR_KIND_KEY,
- JS_ITERATOR_KIND_VALUE,
- JS_ITERATOR_KIND_KEY_AND_VALUE,
- } JSIteratorKindEnum;
- typedef struct JSForInIterator {
- JSValue obj;
- uint32_t idx;
- uint32_t atom_count;
- uint8_t in_prototype_chain;
- uint8_t is_array;
- JSPropertyEnum *tab_atom; /* is_array = FALSE */
- } JSForInIterator;
- typedef struct JSRegExp {
- JSString *pattern;
- JSString *bytecode; /* also contains the flags */
- } JSRegExp;
- typedef struct JSProxyData {
- JSValue target;
- JSValue handler;
- uint8_t is_func;
- uint8_t is_revoked;
- } JSProxyData;
- typedef struct JSArrayBuffer {
- int byte_length; /* 0 if detached */
- int max_byte_length; /* -1 if not resizable; >= byte_length otherwise */
- uint8_t detached;
- uint8_t shared; /* if shared, the array buffer cannot be detached */
- uint8_t *data; /* NULL if detached */
- struct list_head array_list;
- void *opaque;
- JSFreeArrayBufferDataFunc *free_func;
- } JSArrayBuffer;
- typedef struct JSTypedArray {
- struct list_head link; /* link to arraybuffer */
- JSObject *obj; /* back pointer to the TypedArray/DataView object */
- JSObject *buffer; /* based array buffer */
- uint32_t offset; /* byte offset in the array buffer */
- uint32_t length; /* byte length in the array buffer */
- BOOL track_rab; /* auto-track length of backing array buffer */
- } JSTypedArray;
- typedef struct JSGlobalObject {
- JSValue uninitialized_vars; /* hidden object containing the list of uninitialized variables */
- } JSGlobalObject;
- typedef struct JSAsyncFunctionState {
- JSGCObjectHeader header;
- JSValue this_val; /* 'this' argument */
- int argc; /* number of function arguments */
- BOOL throw_flag; /* used to throw an exception in JS_CallInternal() */
- BOOL is_completed; /* TRUE if the function has returned. The stack
- frame is no longer valid */
- JSValue resolving_funcs[2]; /* only used in JS async functions */
- JSStackFrame frame;
- /* arg_buf, var_buf, stack_buf and var_refs follow */
- } JSAsyncFunctionState;
- typedef enum {
- /* binary operators */
- JS_OVOP_ADD,
- JS_OVOP_SUB,
- JS_OVOP_MUL,
- JS_OVOP_DIV,
- JS_OVOP_MOD,
- JS_OVOP_POW,
- JS_OVOP_OR,
- JS_OVOP_AND,
- JS_OVOP_XOR,
- JS_OVOP_SHL,
- JS_OVOP_SAR,
- JS_OVOP_SHR,
- JS_OVOP_EQ,
- JS_OVOP_LESS,
- JS_OVOP_BINARY_COUNT,
- /* unary operators */
- JS_OVOP_POS = JS_OVOP_BINARY_COUNT,
- JS_OVOP_NEG,
- JS_OVOP_INC,
- JS_OVOP_DEC,
- JS_OVOP_NOT,
- JS_OVOP_COUNT,
- } JSOverloadableOperatorEnum;
- typedef struct {
- uint32_t operator_index;
- JSObject *ops[JS_OVOP_BINARY_COUNT]; /* self operators */
- } JSBinaryOperatorDefEntry;
- typedef struct {
- int count;
- JSBinaryOperatorDefEntry *tab;
- } JSBinaryOperatorDef;
- typedef struct {
- uint32_t operator_counter;
- BOOL is_primitive; /* OperatorSet for a primitive type */
- /* NULL if no operator is defined */
- JSObject *self_ops[JS_OVOP_COUNT]; /* self operators */
- JSBinaryOperatorDef left;
- JSBinaryOperatorDef right;
- } JSOperatorSetData;
- typedef struct JSReqModuleEntry {
- JSAtom module_name;
- JSModuleDef *module; /* used using resolution */
- JSValue attributes; /* JS_UNDEFINED or an object contains the attributes as key/value */
- } JSReqModuleEntry;
- typedef enum JSExportTypeEnum {
- JS_EXPORT_TYPE_LOCAL,
- JS_EXPORT_TYPE_INDIRECT,
- } JSExportTypeEnum;
- typedef struct JSExportEntry {
- union {
- struct {
- int var_idx; /* closure variable index */
- JSVarRef *var_ref; /* if != NULL, reference to the variable */
- } local; /* for local export */
- int req_module_idx; /* module for indirect export */
- } u;
- JSExportTypeEnum export_type;
- JSAtom local_name; /* '*' if export ns from. not used for local
- export after compilation */
- JSAtom export_name; /* exported variable name */
- } JSExportEntry;
- typedef struct JSStarExportEntry {
- int req_module_idx; /* in req_module_entries */
- } JSStarExportEntry;
- typedef struct JSImportEntry {
- int var_idx; /* closure variable index */
- BOOL is_star; /* import_name = '*' is a valid import name, so need a flag */
- JSAtom import_name;
- int req_module_idx; /* in req_module_entries */
- } JSImportEntry;
- typedef enum {
- JS_MODULE_STATUS_UNLINKED,
- JS_MODULE_STATUS_LINKING,
- JS_MODULE_STATUS_LINKED,
- JS_MODULE_STATUS_EVALUATING,
- JS_MODULE_STATUS_EVALUATING_ASYNC,
- JS_MODULE_STATUS_EVALUATED,
- } JSModuleStatus;
- struct JSModuleDef {
- JSGCObjectHeader header; /* must come first */
- JSAtom module_name;
- struct list_head link;
- JSReqModuleEntry *req_module_entries;
- int req_module_entries_count;
- int req_module_entries_size;
- JSExportEntry *export_entries;
- int export_entries_count;
- int export_entries_size;
- JSStarExportEntry *star_export_entries;
- int star_export_entries_count;
- int star_export_entries_size;
- JSImportEntry *import_entries;
- int import_entries_count;
- int import_entries_size;
- JSValue module_ns;
- JSValue func_obj; /* only used for JS modules */
- JSModuleInitFunc *init_func; /* only used for C modules */
- BOOL has_tla : 8; /* true if func_obj contains await */
- BOOL resolved : 8;
- BOOL func_created : 8;
- JSModuleStatus status : 8;
- /* temp use during js_module_link() & js_module_evaluate() */
- int dfs_index, dfs_ancestor_index;
- JSModuleDef *stack_prev;
- /* temp use during js_module_evaluate() */
- JSModuleDef **async_parent_modules;
- int async_parent_modules_count;
- int async_parent_modules_size;
- int pending_async_dependencies;
- BOOL async_evaluation; /* true: async_evaluation_timestamp corresponds to [[AsyncEvaluationOrder]]
- false: [[AsyncEvaluationOrder]] is UNSET or DONE */
- int64_t async_evaluation_timestamp;
- JSModuleDef *cycle_root;
- JSValue promise; /* corresponds to spec field: capability */
- JSValue resolving_funcs[2]; /* corresponds to spec field: capability */
- /* true if evaluation yielded an exception. It is saved in
- eval_exception */
- BOOL eval_has_exception : 8;
- JSValue eval_exception;
- JSValue meta_obj; /* for import.meta */
- JSValue private_value; /* private value for C modules */
- };
- typedef struct JSJobEntry {
- struct list_head link;
- JSContext *realm;
- JSJobFunc *job_func;
- int argc;
- JSValue argv[0];
- } JSJobEntry;
- typedef struct JSProperty {
- union {
- JSValue value; /* JS_PROP_NORMAL */
- struct { /* JS_PROP_GETSET */
- JSObject *getter; /* NULL if undefined */
- JSObject *setter; /* NULL if undefined */
- } getset;
- JSVarRef *var_ref; /* JS_PROP_VARREF */
- struct { /* JS_PROP_AUTOINIT */
- /* in order to use only 2 pointers, we compress the realm
- and the init function pointer */
- uintptr_t realm_and_id; /* realm and init_id (JS_AUTOINIT_ID_x)
- in the 2 low bits */
- void *opaque;
- } init;
- } u;
- } JSProperty;
- #define JS_PROP_INITIAL_SIZE 2
- #define JS_PROP_INITIAL_HASH_SIZE 4 /* must be a power of two */
- typedef struct JSShapeProperty {
- uint32_t hash_next : 26; /* 0 if last in list */
- uint32_t flags : 6; /* JS_PROP_XXX */
- JSAtom atom; /* JS_ATOM_NULL = free property entry */
- } JSShapeProperty;
- struct JSShape {
- JSGCObjectHeader header;
- /* true if the shape is inserted in the shape hash table. If not,
- JSShape.hash is not valid */
- uint8_t is_hashed;
- uint32_t hash; /* current hash value */
- uint32_t prop_hash_mask; /* >= 2 */
- int prop_size; /* allocated properties */
- int prop_count; /* include deleted properties */
- int deleted_prop_count;
- JSShape *shape_hash_next; /* in JSRuntime.shape_hash[h] list */
- JSObject *proto;
- uint32_t hash_table[]; /* prop_hash_mask + 1 elements */
- /* followed by JSShapeProperty prop[prop_size]; */
- };
- struct JSObject {
- JSGCObjectHeader header;
- /* TRUE if the array prototype is "normal":
- - no small index properties which are get/set or non writable
- - its prototype is Object.prototype
- - Object.prototype has no small index properties which are get/set or non writable
- - the prototype of Object.prototype is null (always true as it is immutable)
- */
- uint8_t is_std_array_prototype : 1;
-
- uint8_t extensible : 1;
- uint8_t free_mark : 1; /* only used when freeing objects with cycles */
- uint8_t is_exotic : 1; /* TRUE if object has exotic property handlers */
- uint8_t fast_array : 1; /* TRUE if u.array is used for get/put (for JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS, JS_CLASS_MAPPED_ARGUMENTS and typed arrays) */
- uint8_t is_constructor : 1; /* TRUE if object is a constructor function */
- uint8_t has_immutable_prototype : 1; /* cannot modify the prototype */
- uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */
- uint8_t is_HTMLDDA : 1; /* specific annex B IsHtmlDDA behavior */
- uint16_t class_id; /* see JS_CLASS_x */
- /* count the number of weak references to this object. The object
- structure is freed only if header.ref_count = 0 and
- weakref_count = 0 */
- uint32_t weakref_count;
- JSShape *shape; /* prototype and property names + flag */
- JSProperty *prop; /* array of properties */
- union {
- void *opaque;
- struct JSBoundFunction *bound_function; /* JS_CLASS_BOUND_FUNCTION */
- struct JSCFunctionDataRecord *c_function_data_record; /* JS_CLASS_C_FUNCTION_DATA */
- struct JSForInIterator *for_in_iterator; /* JS_CLASS_FOR_IN_ITERATOR */
- struct JSArrayBuffer *array_buffer; /* JS_CLASS_ARRAY_BUFFER, JS_CLASS_SHARED_ARRAY_BUFFER */
- struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_DATAVIEW */
- struct JSMapState *map_state; /* JS_CLASS_MAP..JS_CLASS_WEAKSET */
- struct JSMapIteratorData *map_iterator_data; /* JS_CLASS_MAP_ITERATOR, JS_CLASS_SET_ITERATOR */
- struct JSArrayIteratorData *array_iterator_data; /* JS_CLASS_ARRAY_ITERATOR, JS_CLASS_STRING_ITERATOR */
- struct JSRegExpStringIteratorData *regexp_string_iterator_data; /* JS_CLASS_REGEXP_STRING_ITERATOR */
- struct JSGeneratorData *generator_data; /* JS_CLASS_GENERATOR */
- struct JSIteratorConcatData *iterator_concat_data; /* JS_CLASS_ITERATOR_CONCAT */
- struct JSIteratorHelperData *iterator_helper_data; /* JS_CLASS_ITERATOR_HELPER */
- struct JSIteratorWrapData *iterator_wrap_data; /* JS_CLASS_ITERATOR_WRAP */
- struct JSProxyData *proxy_data; /* JS_CLASS_PROXY */
- struct JSPromiseData *promise_data; /* JS_CLASS_PROMISE */
- struct JSPromiseFunctionData *promise_function_data; /* JS_CLASS_PROMISE_RESOLVE_FUNCTION, JS_CLASS_PROMISE_REJECT_FUNCTION */
- struct JSAsyncFunctionState *async_function_data; /* JS_CLASS_ASYNC_FUNCTION_RESOLVE, JS_CLASS_ASYNC_FUNCTION_REJECT */
- struct JSAsyncFromSyncIteratorData *async_from_sync_iterator_data; /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */
- struct JSAsyncGeneratorData *async_generator_data; /* JS_CLASS_ASYNC_GENERATOR */
- struct { /* JS_CLASS_BYTECODE_FUNCTION: 12/24 bytes */
- /* also used by JS_CLASS_GENERATOR_FUNCTION, JS_CLASS_ASYNC_FUNCTION and JS_CLASS_ASYNC_GENERATOR_FUNCTION */
- struct JSFunctionBytecode *function_bytecode;
- JSVarRef **var_refs;
- JSObject *home_object; /* for 'super' access */
- } func;
- struct { /* JS_CLASS_C_FUNCTION: 12/20 bytes */
- JSContext *realm;
- JSCFunctionType c_function;
- uint8_t length;
- uint8_t cproto;
- int16_t magic;
- } cfunc;
- /* array part for fast arrays and typed arrays */
- struct { /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS, JS_CLASS_MAPPED_ARGUMENTS, JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */
- union {
- uint32_t size; /* JS_CLASS_ARRAY */
- struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */
- } u1;
- union {
- JSValue *values; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */
- JSVarRef **var_refs; /* JS_CLASS_MAPPED_ARGUMENTS */
- void *ptr; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */
- int8_t *int8_ptr; /* JS_CLASS_INT8_ARRAY */
- uint8_t *uint8_ptr; /* JS_CLASS_UINT8_ARRAY, JS_CLASS_UINT8C_ARRAY */
- int16_t *int16_ptr; /* JS_CLASS_INT16_ARRAY */
- uint16_t *uint16_ptr; /* JS_CLASS_UINT16_ARRAY */
- int32_t *int32_ptr; /* JS_CLASS_INT32_ARRAY */
- uint32_t *uint32_ptr; /* JS_CLASS_UINT32_ARRAY */
- int64_t *int64_ptr; /* JS_CLASS_INT64_ARRAY */
- uint64_t *uint64_ptr; /* JS_CLASS_UINT64_ARRAY */
- uint16_t *fp16_ptr; /* JS_CLASS_FLOAT16_ARRAY */
- float *float_ptr; /* JS_CLASS_FLOAT32_ARRAY */
- double *double_ptr; /* JS_CLASS_FLOAT64_ARRAY */
- } u;
- uint32_t count; /* <= 2^31-1. 0 for a detached typed array */
- } array; /* 12/20 bytes */
- JSRegExp regexp; /* JS_CLASS_REGEXP: 8/16 bytes */
- JSValue object_data; /* for JS_SetObjectData(): 8/16/16 bytes */
- JSGlobalObject global_object;
- } u;
- };
- typedef struct JSMapRecord {
- int ref_count; /* used during enumeration to avoid freeing the record */
- BOOL empty : 8; /* TRUE if the record is deleted */
- struct list_head link;
- struct JSMapRecord *hash_next;
- JSValue key;
- JSValue value;
- } JSMapRecord;
- typedef struct JSMapState {
- BOOL is_weak; /* TRUE if WeakSet/WeakMap */
- struct list_head records; /* list of JSMapRecord.link */
- uint32_t record_count;
- JSMapRecord **hash_table;
- int hash_bits;
- uint32_t hash_size; /* = 2 ^ hash_bits */
- uint32_t record_count_threshold; /* count at which a hash table
- resize is needed */
- JSWeakRefHeader weakref_header; /* only used if is_weak = TRUE */
- } JSMapState;
- enum {
- __JS_ATOM_NULL = JS_ATOM_NULL,
- #define DEF(name, str) JS_ATOM_ ## name,
- #include "quickjs-atom.h"
- #undef DEF
- JS_ATOM_END,
- };
- #define JS_ATOM_LAST_KEYWORD JS_ATOM_super
- #define JS_ATOM_LAST_STRICT_KEYWORD JS_ATOM_yield
- static const char js_atom_init[] =
- #define DEF(name, str) str "\0"
- #include "quickjs-atom.h"
- #undef DEF
- ;
- typedef enum OPCodeFormat {
- #define FMT(f) OP_FMT_ ## f,
- #define DEF(id, size, n_pop, n_push, f)
- #include "quickjs-opcode.h"
- #undef DEF
- #undef FMT
- } OPCodeFormat;
- enum OPCodeEnum {
- #define FMT(f)
- #define DEF(id, size, n_pop, n_push, f) OP_ ## id,
- #define def(id, size, n_pop, n_push, f)
- #include "quickjs-opcode.h"
- #undef def
- #undef DEF
- #undef FMT
- OP_COUNT, /* excluding temporary opcodes */
- /* temporary opcodes : overlap with the short opcodes */
- OP_TEMP_START = OP_nop + 1,
- OP___dummy = OP_TEMP_START - 1,
- #define FMT(f)
- #define DEF(id, size, n_pop, n_push, f)
- #define def(id, size, n_pop, n_push, f) OP_ ## id,
- #include "quickjs-opcode.h"
- #undef def
- #undef DEF
- #undef FMT
- OP_TEMP_END,
- };
- static int JS_InitAtoms(JSRuntime *rt);
- static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len,
- int atom_type);
- static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p);
- static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b);
- static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj,
- JSValueConst this_obj,
- int argc, JSValueConst *argv, int flags);
- static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj,
- JSValueConst this_obj,
- int argc, JSValueConst *argv, int flags);
- static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj,
- JSValueConst this_obj, JSValueConst new_target,
- int argc, JSValue *argv, int flags);
- static JSValue JS_CallConstructorInternal(JSContext *ctx,
- JSValueConst func_obj,
- JSValueConst new_target,
- int argc, JSValue *argv, int flags);
- static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj,
- int argc, JSValueConst *argv);
- static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom,
- int argc, JSValueConst *argv);
- static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
- JSValue val, BOOL is_array_ctor);
- static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,
- JSValueConst val, int flags, int scope_idx);
- JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...);
- static __maybe_unused void JS_DumpAtoms(JSRuntime *rt);
- static __maybe_unused void JS_DumpString(JSRuntime *rt, const JSString *p);
- static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt);
- static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p);
- static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p);
- static __maybe_unused void JS_DumpAtom(JSContext *ctx, const char *str, JSAtom atom);
- static __maybe_unused void JS_DumpValueRT(JSRuntime *rt, const char *str, JSValueConst val);
- static __maybe_unused void JS_DumpValue(JSContext *ctx, const char *str, JSValueConst val);
- static __maybe_unused void JS_DumpShapes(JSRuntime *rt);
- static void js_dump_value_write(void *opaque, const char *buf, size_t len);
- static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic);
- static void js_array_finalizer(JSRuntime *rt, JSValue val);
- static void js_array_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func);
- static void js_mapped_arguments_finalizer(JSRuntime *rt, JSValue val);
- static void js_mapped_arguments_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func);
- static void js_object_data_finalizer(JSRuntime *rt, JSValue val);
- static void js_object_data_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func);
- static void js_c_function_finalizer(JSRuntime *rt, JSValue val);
- static void js_c_function_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func);
- static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val);
- static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func);
- static void js_bound_function_finalizer(JSRuntime *rt, JSValue val);
- static void js_bound_function_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func);
- static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val);
- static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func);
- static void js_regexp_finalizer(JSRuntime *rt, JSValue val);
- static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val);
- static void js_typed_array_finalizer(JSRuntime *rt, JSValue val);
- static void js_typed_array_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func);
- static void js_proxy_finalizer(JSRuntime *rt, JSValue val);
- static void js_proxy_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func);
- static void js_map_finalizer(JSRuntime *rt, JSValue val);
- static void js_map_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func);
- static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val);
- static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func);
- static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val);
- static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func);
- static void js_iterator_concat_finalizer(JSRuntime *rt, JSValue val);
- static void js_iterator_concat_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func);
- static void js_iterator_helper_finalizer(JSRuntime *rt, JSValue val);
- static void js_iterator_helper_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func);
- static void js_iterator_wrap_finalizer(JSRuntime *rt, JSValue val);
- static void js_iterator_wrap_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func);
- static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val);
- static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func);
- static void js_generator_finalizer(JSRuntime *rt, JSValue obj);
- static void js_generator_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func);
- static void js_global_object_finalizer(JSRuntime *rt, JSValue obj);
- static void js_global_object_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func);
- static void js_promise_finalizer(JSRuntime *rt, JSValue val);
- static void js_promise_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func);
- static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val);
- static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func);
- #define HINT_STRING 0
- #define HINT_NUMBER 1
- #define HINT_NONE 2
- #define HINT_FORCE_ORDINARY (1 << 4) // don't try Symbol.toPrimitive
- static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint);
- static JSValue JS_ToStringFree(JSContext *ctx, JSValue val);
- static int JS_ToBoolFree(JSContext *ctx, JSValue val);
- static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val);
- static int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val);
- static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val);
- static JSValue js_new_string8_len(JSContext *ctx, const char *buf, int len);
- static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern,
- JSValueConst flags);
- static JSValue JS_NewRegexp(JSContext *ctx, JSValue pattern, JSValue bc);
- static void gc_decref(JSRuntime *rt);
- static int JS_NewClass1(JSRuntime *rt, JSClassID class_id,
- const JSClassDef *class_def, JSAtom name);
- typedef enum JSStrictEqModeEnum {
- JS_EQ_STRICT,
- JS_EQ_SAME_VALUE,
- JS_EQ_SAME_VALUE_ZERO,
- } JSStrictEqModeEnum;
- static BOOL js_strict_eq2(JSContext *ctx, JSValueConst op1, JSValueConst op2,
- JSStrictEqModeEnum eq_mode);
- static BOOL js_strict_eq(JSContext *ctx, JSValueConst op1, JSValueConst op2);
- static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2);
- static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2);
- static JSValue JS_ToObject(JSContext *ctx, JSValueConst val);
- static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val);
- static JSProperty *add_property(JSContext *ctx,
- JSObject *p, JSAtom prop, int prop_flags);
- static void free_property(JSRuntime *rt, JSProperty *pr, int prop_flags);
- static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val);
- JSValue JS_ThrowOutOfMemory(JSContext *ctx);
- static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx);
- static int js_resolve_proxy(JSContext *ctx, JSValueConst *pval, int throw_exception);
- static int JS_CreateProperty(JSContext *ctx, JSObject *p,
- JSAtom prop, JSValueConst val,
- JSValueConst getter, JSValueConst setter,
- int flags);
- static int js_string_memcmp(const JSString *p1, int pos1, const JSString *p2,
- int pos2, int len);
- static JSValue js_array_buffer_constructor3(JSContext *ctx,
- JSValueConst new_target,
- uint64_t len, uint64_t *max_len,
- JSClassID class_id,
- uint8_t *buf,
- JSFreeArrayBufferDataFunc *free_func,
- void *opaque, BOOL alloc_flag);
- static void js_array_buffer_free(JSRuntime *rt, void *opaque, void *ptr);
- static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj);
- static BOOL array_buffer_is_resizable(const JSArrayBuffer *abuf);
- static JSValue js_typed_array_constructor(JSContext *ctx,
- JSValueConst this_val,
- int argc, JSValueConst *argv,
- int classid);
- static JSValue js_typed_array_constructor_ta(JSContext *ctx,
- JSValueConst new_target,
- JSValueConst src_obj,
- int classid, uint32_t len);
- static BOOL typed_array_is_oob(JSObject *p);
- static int js_typed_array_get_length_unsafe(JSContext *ctx, JSValueConst obj);
- static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx);
- static JSValue JS_ThrowTypeErrorArrayBufferOOB(JSContext *ctx);
- static JSVarRef *js_create_var_ref(JSContext *ctx, BOOL is_lexical);
- static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx,
- BOOL is_arg);
- static void __async_func_free(JSRuntime *rt, JSAsyncFunctionState *s);
- static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s);
- static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,
- JSValueConst this_obj,
- int argc, JSValueConst *argv,
- int flags);
- static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val);
- static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func);
- static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
- const char *input, size_t input_len,
- const char *filename, int flags, int scope_idx);
- static void js_free_module_def(JSRuntime *rt, JSModuleDef *m);
- static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m,
- JS_MarkFunc *mark_func);
- static JSValue js_import_meta(JSContext *ctx);
- static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier, JSValueConst options);
- static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref);
- static JSValue js_new_promise_capability(JSContext *ctx,
- JSValue *resolving_funcs,
- JSValueConst ctor);
- static __exception int perform_promise_then(JSContext *ctx,
- JSValueConst promise,
- JSValueConst *resolve_reject,
- JSValueConst *cap_resolving_funcs);
- static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic);
- static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv);
- static BOOL js_string_eq(JSContext *ctx,
- const JSString *p1, const JSString *p2);
- static int js_string_compare(JSContext *ctx,
- const JSString *p1, const JSString *p2);
- static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val);
- static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,
- JSValue prop, JSValue val, int flags);
- static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val);
- static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val);
- static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val);
- static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc,
- JSObject *p, JSAtom prop);
- static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc);
- static int JS_AddIntrinsicBasicObjects(JSContext *ctx);
- static void js_free_shape(JSRuntime *rt, JSShape *sh);
- static void js_free_shape_null(JSRuntime *rt, JSShape *sh);
- static int js_shape_prepare_update(JSContext *ctx, JSObject *p,
- JSShapeProperty **pprs);
- static int init_shape_hash(JSRuntime *rt);
- static __exception int js_get_length32(JSContext *ctx, uint32_t *pres,
- JSValueConst obj);
- static __exception int js_get_length64(JSContext *ctx, int64_t *pres,
- JSValueConst obj);
- static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len);
- static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen,
- JSValueConst array_arg);
- static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj,
- JSValue **arrpp, uint32_t *countp);
- static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx,
- JSValueConst sync_iter);
- static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val);
- static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func);
- static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj,
- JSValueConst this_val,
- int argc, JSValueConst *argv, int flags);
- static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val);
- static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h,
- JSGCObjectTypeEnum type);
- static void remove_gc_object(JSGCObjectHeader *h);
- static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque);
- static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom,
- void *opaque);
- static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p,
- JSAtom atom, void *opaque);
- static JSValue js_object_groupBy(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int is_map);
- static void map_delete_weakrefs(JSRuntime *rt, JSWeakRefHeader *wh);
- static void weakref_delete_weakref(JSRuntime *rt, JSWeakRefHeader *wh);
- static void finrec_delete_weakref(JSRuntime *rt, JSWeakRefHeader *wh);
- static void JS_RunGCInternal(JSRuntime *rt, BOOL remove_weak_objects);
- static JSValue js_array_from_iterator(JSContext *ctx, uint32_t *plen,
- JSValueConst obj, JSValueConst method);
- static int js_string_find_invalid_codepoint(JSString *p);
- static JSValue js_regexp_toString(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv);
- static JSValue get_date_string(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic);
- static JSValue js_error_toString(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv);
- static JSVarRef *js_global_object_find_uninitialized_var(JSContext *ctx, JSObject *p,
- JSAtom atom, BOOL is_lexical);
- static int typed_array_init(JSContext *ctx, JSValueConst obj,
- JSValue buffer, uint64_t offset, uint64_t len,
- BOOL track_rab);
- static const JSClassExoticMethods js_arguments_exotic_methods;
- static const JSClassExoticMethods js_string_exotic_methods;
- static const JSClassExoticMethods js_proxy_exotic_methods;
- static const JSClassExoticMethods js_module_ns_exotic_methods;
- static JSClassID js_class_id_alloc = JS_CLASS_INIT_COUNT;
- /* JS malloc */
- /* max overhead for size >= 64: 12.5% */
- static const uint16_t js_malloc_block_sizes[JS_MALLOC_BLOCK_SIZE_COUNT] = {
- 16,
- 24,
- 32,
- 40,
- 48,
- 56,
- 64,
- 72,
- 80,
- 88,
- 96,
- 104,
- 112,
- 120,
- 128,
- 144,
- 160,
- 176,
- 192,
- 208,
- 224,
- 240,
- 256,
- 288,
- 320,
- 352,
- 384,
- 416,
- 448,
- 480,
- 512,
- };
- static int get_block_size_index(size_t size)
- {
- if (size <= 16) {
- return 0;
- } else if (size <= 128) {
- return (size + 7) / 8 - 2;
- } else if (size <= 256) {
- return (size + 15) / 16 + 6;
- } else if (size <= 512) {
- return (size + 31) / 32 + 14;
- } else {
- return JS_MALLOC_BLOCK_SIZE_COUNT;
- }
- }
- static JSMallocBlockHeader *get_zero_size_block(JSMallocContext *s)
- {
- return (JSMallocBlockHeader *)s->zero_size_block;
- }
- static void js_malloc_init(JSMallocContext *s)
- {
- int i;
- memset(s, 0, sizeof(*s));
- get_zero_size_block(s)->u.block_idx = FREE_NIL;
- for(i = 0; i < JS_MALLOC_BLOCK_SIZE_COUNT; i++) {
- init_list_head(&s->arena_list[i]);
- init_list_head(&s->free_arena_list[i]);
- }
- #ifdef JS_MALLOC_USE_ITER
- init_list_head(&s->large_block_list);
- #endif
- }
- static void *get_arena_block(JSMallocArena *ar, unsigned int idx, unsigned int block_size)
- {
- return ar->blocks + idx * block_size;
- }
- static inline JSMallocBlockHeader *js_rc(void *ptr)
- {
- return container_of(ptr, JSMallocBlockHeader, user_data);
- }
- static no_inline JSMallocArena *js_malloc_new_arena(JSMallocContext *s, int block_size_idx)
- {
- JSMallocBlockHeader *b;
- JSMallocArena *ar;
- int n_blocks, block_size, i;
- block_size = js_malloc_block_sizes[block_size_idx];
- n_blocks = (JS_MALLOC_ARENA_SIZE - sizeof(JSMallocArena)) / block_size;
- ar = s->mf.js_malloc(&s->malloc_state, sizeof(JSMallocArena) + n_blocks * block_size);
- if (!ar)
- return NULL;
- ar->block_size_idx = block_size_idx;
- ar->n_blocks = n_blocks;
- ar->n_used_blocks = 0;
- ar->first_free_block = 0;
- #ifdef JS_MALLOC_USE_ITER
- {
- int n_bitmap_words = (n_blocks + 31) / 32;
- for(i = 0; i < n_bitmap_words; i++)
- ar->bitmap[i] = 0;
- }
- #endif
- for(i = 0; i < n_blocks - 1; i++) {
- b = get_arena_block(ar, i, block_size);
- b->u.free_next = i + 1;
- b->block_size_idx = block_size_idx;
- }
- b = get_arena_block(ar, n_blocks - 1, block_size);
- b->u.free_next = FREE_NIL;
- b->block_size_idx = block_size_idx;
-
- /* add to the head */
- list_add(&ar->link, &s->arena_list[block_size_idx]);
- list_add(&ar->free_link, &s->free_arena_list[block_size_idx]);
- return ar;
- }
- static no_inline void *js_malloc_large(JSMallocContext *s, size_t size)
- {
- JSMallocLargeBlockHeader *b;
- b = s->mf.js_malloc(&s->malloc_state, sizeof(JSMallocLargeBlockHeader) + size);
- if (!b)
- return NULL;
- b->header.u.block_idx = FREE_NIL;
- b->header.block_size_idx = 0xff; /* fail safe */
- #ifdef JS_MALLOC_USE_ITER
- list_add_tail(&b->link, &s->large_block_list);
- #endif
- return b->header.user_data;
- }
- static void *__js_malloc(JSMallocContext *s, size_t size)
- {
- size_t total_size;
- if (unlikely(size == 0)) {
- JSMallocBlockHeader *b = get_zero_size_block(s);
- return b->user_data;
- } else {
- total_size = ((size + JS_MALLOC_ALIGN - 1) & ~(JS_MALLOC_ALIGN - 1)) +
- sizeof(JSMallocBlockHeader);
- if (!JS_MALLOC_LARGE_BLOCKS_ONLY &&
- total_size <= JS_MALLOC_MAX_SMALL_SIZE) {
- int block_size_idx;
- unsigned int block_idx, block_size;
- JSMallocBlockHeader *b;
- JSMallocArena *ar;
- struct list_head *el, *head;
-
- block_size_idx = get_block_size_index(total_size);
- block_size = js_malloc_block_sizes[block_size_idx];
- head = &s->free_arena_list[block_size_idx];
- el = head->next;
- if (unlikely(el == head)) {
- ar = js_malloc_new_arena(s, block_size_idx);
- if (!ar)
- return NULL;
- } else {
- ar = list_entry(el, JSMallocArena, free_link);
- }
- block_idx = ar->first_free_block;
- b = get_arena_block(ar, ar->first_free_block, block_size);
- ar->first_free_block = b->u.free_next;
- b->u.block_idx = block_idx;
- ar->n_used_blocks++;
- if (unlikely(ar->n_used_blocks == ar->n_blocks)) {
- list_del(&ar->free_link);
- }
- #ifdef JS_MALLOC_USE_ITER
- ar->bitmap[block_idx / 32] |= 1 << (block_idx % 32);
- #endif
- return b->user_data;
- } else {
- return js_malloc_large(s, size);
- }
- }
- }
- static void __js_free(JSMallocContext *s, void *ptr)
- {
- JSMallocBlockHeader *b;
- if (!ptr)
- return;
- b = container_of(ptr, JSMallocBlockHeader, user_data);
- if (unlikely(b->u.block_idx == FREE_NIL)) {
- /* large or zero size block */
- if (b == get_zero_size_block(s)) {
- /* nothing to do */
- } else {
- JSMallocLargeBlockHeader *lb = container_of(ptr, JSMallocLargeBlockHeader, header.user_data);
- #ifdef JS_MALLOC_USE_ITER
- list_del(&lb->link);
- #endif
- s->mf.js_free(&s->malloc_state, lb);
- }
- } else {
- unsigned int block_idx = b->u.block_idx;
- unsigned int block_size_idx = b->block_size_idx;
- unsigned int block_size = js_malloc_block_sizes[block_size_idx];
- JSMallocArena *ar = (JSMallocArena *)((uint8_t *)b - block_size * block_idx - sizeof(JSMallocArena));
- b->u.free_next = ar->first_free_block;
- ar->first_free_block = block_idx;
- #ifdef JS_MALLOC_USE_ITER
- ar->bitmap[block_idx / 32] &= ~(1 << (block_idx % 32));
- #endif
- /* add back to the free list if needed */
- if (unlikely(ar->n_used_blocks == ar->n_blocks)) {
- list_add(&ar->free_link, &s->free_arena_list[block_size_idx]);
- }
- ar->n_used_blocks--;
- if (unlikely(ar->n_used_blocks == 0)) {
- list_del(&ar->link);
- list_del(&ar->free_link);
- s->mf.js_free(&s->malloc_state, ar);
- }
- }
- }
- static void *__js_realloc(JSMallocContext *s, void *ptr, size_t size)
- {
- JSMallocBlockHeader *b;
- if (ptr == NULL) {
- return __js_malloc(s, size);
- } else if (size == 0) {
- __js_free(s, ptr);
- return NULL;
- }
- b = container_of(ptr, JSMallocBlockHeader, user_data);
- if (b->u.block_idx == FREE_NIL) {
- if (b == get_zero_size_block(s)) {
- return __js_malloc(s, size);
- } else {
- JSMallocLargeBlockHeader *lb, *new_lb;
- lb = container_of(ptr, JSMallocLargeBlockHeader, header.user_data);
- #ifdef JS_MALLOC_USE_ITER
- list_del(&lb->link);
- #endif
- new_lb = s->mf.js_realloc(&s->malloc_state, lb, sizeof(JSMallocLargeBlockHeader) + size);
- if (!new_lb) {
- #ifdef JS_MALLOC_USE_ITER
- /* add again in the list */
- list_add_tail(&lb->link, &s->large_block_list);
- #endif
- return NULL;
- }
- new_lb->header.u.block_idx = FREE_NIL;
- new_lb->header.block_size_idx = 0xff; /* fail safe */
- #ifdef JS_MALLOC_USE_ITER
- list_add_tail(&new_lb->link, &s->large_block_list);
- #endif
- return new_lb->header.user_data;
- }
- } else {
- unsigned int block_size_idx = b->block_size_idx;
- size_t block_size = js_malloc_block_sizes[block_size_idx];
- size_t total_size, old_size;
- void *new_ptr;
- JSMallocBlockHeader *new_b;
- total_size = ((size + JS_MALLOC_ALIGN - 1) & ~(JS_MALLOC_ALIGN - 1)) +
- sizeof(JSMallocBlockHeader);
- if (total_size <= block_size)
- return ptr;
- new_ptr = __js_malloc(s, size);
- if (!new_ptr)
- return NULL;
- new_b = container_of(new_ptr, JSMallocBlockHeader, user_data);
- /* copy the GC data */
- new_b->gc_obj_type = b->gc_obj_type;
- new_b->mark = b->mark;
- new_b->ref_count = b->ref_count;
- /* copy the data */
- old_size = block_size - sizeof(JSMallocBlockHeader);
- if (size > old_size)
- size = old_size;
- memcpy(new_ptr, ptr, size);
- __js_free(s, ptr);
- return new_ptr;
- }
- }
- static size_t __js_malloc_usable_size(JSMallocContext *s, const char *ptr)
- {
- JSMallocBlockHeader *b;
- if (!ptr)
- return 0;
- b = container_of(ptr, JSMallocBlockHeader, user_data);
- if (b->u.block_idx == FREE_NIL) {
- if (b == get_zero_size_block(s)) {
- return 0;
- } else {
- JSMallocLargeBlockHeader *lb;
- size_t size;
- lb = container_of(ptr, JSMallocLargeBlockHeader, header.user_data);
- if (s->mf.js_malloc_usable_size) {
- size = s->mf.js_malloc_usable_size(lb);
- if (size != 0)
- size -= sizeof(JSMallocLargeBlockHeader);
- return size;
- } else {
- return 0;
- }
- }
- } else {
- size_t block_size = js_malloc_block_sizes[b->block_size_idx];
- return block_size - sizeof(*b);
- }
- }
- static __maybe_unused void js_malloc_dump_arenas(JSMallocContext *s)
- {
- struct list_head *el;
- int block_size_idx;
- printf("%20s %10s %10s\n", "PTR", "BLK_SIZE", "ALLOC");
- for(block_size_idx = 0; block_size_idx < JS_MALLOC_BLOCK_SIZE_COUNT; block_size_idx++) {
- int block_size = js_malloc_block_sizes[block_size_idx];
- list_for_each(el, &s->arena_list[block_size_idx]) {
- JSMallocArena *ar = list_entry(el, JSMallocArena, link);
- printf("%20p %10u %9.1f%%\n",
- ar, block_size,
- (double)ar->n_used_blocks / ar->n_blocks * 100);
- }
- }
- }
- #ifdef JS_MALLOC_USE_ITER
- typedef void JSMallocIterFunc(void *opaque, void *ptr);
- /* iterate thru allocated blocks. The allocated block list should not
- be modified while iterating. */
- static __maybe_unused void js_malloc_iter(JSMallocContext *s, JSMallocIterFunc *iter_func, void *iter_opaque)
- {
- struct list_head *el;
- int block_size_idx;
- int i, j, n_words;
- uint32_t bmp;
-
- for(block_size_idx = 0; block_size_idx < JS_MALLOC_BLOCK_SIZE_COUNT; block_size_idx++) {
- unsigned int block_size = js_malloc_block_sizes[block_size_idx];
- list_for_each(el, &s->arena_list[block_size_idx]) {
- JSMallocArena *ar = list_entry(el, JSMallocArena, link);
- n_words = (ar->n_blocks + 31) / 32;
- for(i = 0; i < n_words; i++) {
- bmp = ar->bitmap[i];
- while (bmp != 0) {
- j = ctz32(bmp);
- bmp &= ~(1 << j);
- iter_func(iter_opaque, get_arena_block(ar, i * 32+ j, block_size));
- }
- }
- }
- }
- list_for_each(el, &s->large_block_list) {
- JSMallocLargeBlockHeader *lb = list_entry(el, JSMallocLargeBlockHeader, link);
- iter_func(iter_opaque, lb->header.user_data);
- }
- }
- #endif
- /* end JS malloc */
- static void js_trigger_gc(JSRuntime *rt, size_t size)
- {
- BOOL force_gc;
- #ifdef FORCE_GC_AT_MALLOC
- force_gc = TRUE;
- #else
- force_gc = ((rt->malloc_ctx.malloc_state.malloc_size + size) >
- rt->malloc_gc_threshold);
- #endif
- if (force_gc) {
- #ifdef DUMP_GC
- printf("GC: size=%" PRIu64 "\n",
- (uint64_t)rt->malloc_ctx.malloc_state.malloc_size);
- #endif
- JS_RunGC(rt);
- rt->malloc_gc_threshold = rt->malloc_ctx.malloc_state.malloc_size +
- (rt->malloc_ctx.malloc_state.malloc_size >> 1);
- }
- }
- void *js_malloc_rt(JSRuntime *rt, size_t size)
- {
- return __js_malloc(&rt->malloc_ctx, size);
- }
- void js_free_rt(JSRuntime *rt, void *ptr)
- {
- __js_free(&rt->malloc_ctx, ptr);
- }
- void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size)
- {
- return __js_realloc(&rt->malloc_ctx, ptr, size);
- }
- size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr)
- {
- return __js_malloc_usable_size(&rt->malloc_ctx, ptr);
- }
- void *js_mallocz_rt(JSRuntime *rt, size_t size)
- {
- void *ptr;
- ptr = js_malloc_rt(rt, size);
- if (unlikely(!ptr))
- return NULL;
- return memset(ptr, 0, size);
- }
- /* Throw out of memory in case of error */
- void *js_malloc(JSContext *ctx, size_t size)
- {
- void *ptr;
- ptr = js_malloc_rt(ctx->rt, size);
- if (unlikely(!ptr)) {
- JS_ThrowOutOfMemory(ctx);
- return NULL;
- }
- return ptr;
- }
- /* Throw out of memory in case of error */
- void *js_mallocz(JSContext *ctx, size_t size)
- {
- void *ptr;
- ptr = js_mallocz_rt(ctx->rt, size);
- if (unlikely(!ptr)) {
- JS_ThrowOutOfMemory(ctx);
- return NULL;
- }
- return ptr;
- }
- void js_free(JSContext *ctx, void *ptr)
- {
- js_free_rt(ctx->rt, ptr);
- }
- /* Throw out of memory in case of error */
- void *js_realloc(JSContext *ctx, void *ptr, size_t size)
- {
- void *ret;
- ret = js_realloc_rt(ctx->rt, ptr, size);
- if (unlikely(!ret && size != 0)) {
- JS_ThrowOutOfMemory(ctx);
- return NULL;
- }
- return ret;
- }
- /* store extra allocated size in *pslack if successful */
- void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack)
- {
- void *ret;
- ret = js_realloc_rt(ctx->rt, ptr, size);
- if (unlikely(!ret && size != 0)) {
- JS_ThrowOutOfMemory(ctx);
- return NULL;
- }
- if (pslack) {
- size_t new_size = js_malloc_usable_size_rt(ctx->rt, ret);
- *pslack = (new_size > size) ? new_size - size : 0;
- }
- return ret;
- }
- size_t js_malloc_usable_size(JSContext *ctx, const void *ptr)
- {
- return js_malloc_usable_size_rt(ctx->rt, ptr);
- }
- /* Throw out of memory exception in case of error */
- char *js_strndup(JSContext *ctx, const char *s, size_t n)
- {
- char *ptr;
- ptr = js_malloc(ctx, n + 1);
- if (ptr) {
- memcpy(ptr, s, n);
- ptr[n] = '\0';
- }
- return ptr;
- }
- char *js_strdup(JSContext *ctx, const char *str)
- {
- return js_strndup(ctx, str, strlen(str));
- }
- static no_inline int js_realloc_array(JSContext *ctx, void **parray,
- int elem_size, int *psize, int req_size)
- {
- int new_size;
- size_t slack;
- void *new_array;
- /* XXX: potential arithmetic overflow */
- new_size = max_int(req_size, *psize * 3 / 2);
- new_array = js_realloc2(ctx, *parray, new_size * elem_size, &slack);
- if (!new_array)
- return -1;
- new_size += slack / elem_size;
- *psize = new_size;
- *parray = new_array;
- return 0;
- }
- /* resize the array and update its size if req_size > *psize */
- static inline int js_resize_array(JSContext *ctx, void **parray, int elem_size,
- int *psize, int req_size)
- {
- if (unlikely(req_size > *psize))
- return js_realloc_array(ctx, parray, elem_size, psize, req_size);
- else
- return 0;
- }
- static inline void js_dbuf_init(JSContext *ctx, DynBuf *s)
- {
- dbuf_init2(s, ctx->rt, (DynBufReallocFunc *)js_realloc_rt);
- }
- static void *js_realloc_bytecode_rt(void *opaque, void *ptr, size_t size)
- {
- JSRuntime *rt = opaque;
- if (size > (INT32_MAX / 2)) {
- /* the bytecode cannot be larger than 2G. Leave some slack to
- avoid some overflows. */
- return NULL;
- } else {
- return js_realloc_rt(rt, ptr, size);
- }
- }
- static inline void js_dbuf_bytecode_init(JSContext *ctx, DynBuf *s)
- {
- dbuf_init2(s, ctx->rt, js_realloc_bytecode_rt);
- }
- static inline int is_digit(int c) {
- return c >= '0' && c <= '9';
- }
- static inline int string_get(const JSString *p, int idx) {
- return p->is_wide_char ? p->u.str16[idx] : p->u.str8[idx];
- }
- typedef struct JSClassShortDef {
- JSAtom class_name;
- JSClassFinalizer *finalizer;
- JSClassGCMark *gc_mark;
- } JSClassShortDef;
- static JSClassShortDef const js_std_class_def[] = {
- { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_OBJECT */
- { JS_ATOM_Array, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARRAY */
- { JS_ATOM_Error, NULL, NULL }, /* JS_CLASS_ERROR */
- { JS_ATOM_Number, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_NUMBER */
- { JS_ATOM_String, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_STRING */
- { JS_ATOM_Boolean, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BOOLEAN */
- { JS_ATOM_Symbol, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_SYMBOL */
- { JS_ATOM_Arguments, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARGUMENTS */
- { JS_ATOM_Arguments, js_mapped_arguments_finalizer, js_mapped_arguments_mark }, /* JS_CLASS_MAPPED_ARGUMENTS */
- { JS_ATOM_Date, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_DATE */
- { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_MODULE_NS */
- { JS_ATOM_Function, js_c_function_finalizer, js_c_function_mark }, /* JS_CLASS_C_FUNCTION */
- { JS_ATOM_Function, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_BYTECODE_FUNCTION */
- { JS_ATOM_Function, js_bound_function_finalizer, js_bound_function_mark }, /* JS_CLASS_BOUND_FUNCTION */
- { JS_ATOM_Function, js_c_function_data_finalizer, js_c_function_data_mark }, /* JS_CLASS_C_FUNCTION_DATA */
- { JS_ATOM_GeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_GENERATOR_FUNCTION */
- { JS_ATOM_ForInIterator, js_for_in_iterator_finalizer, js_for_in_iterator_mark }, /* JS_CLASS_FOR_IN_ITERATOR */
- { JS_ATOM_RegExp, js_regexp_finalizer, NULL }, /* JS_CLASS_REGEXP */
- { JS_ATOM_ArrayBuffer, js_array_buffer_finalizer, NULL }, /* JS_CLASS_ARRAY_BUFFER */
- { JS_ATOM_SharedArrayBuffer, js_array_buffer_finalizer, NULL }, /* JS_CLASS_SHARED_ARRAY_BUFFER */
- { JS_ATOM_Uint8ClampedArray, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8C_ARRAY */
- { JS_ATOM_Int8Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT8_ARRAY */
- { JS_ATOM_Uint8Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8_ARRAY */
- { JS_ATOM_Int16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT16_ARRAY */
- { JS_ATOM_Uint16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT16_ARRAY */
- { JS_ATOM_Int32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT32_ARRAY */
- { JS_ATOM_Uint32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT32_ARRAY */
- { JS_ATOM_BigInt64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_INT64_ARRAY */
- { JS_ATOM_BigUint64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_UINT64_ARRAY */
- { JS_ATOM_Float16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT16_ARRAY */
- { JS_ATOM_Float32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT32_ARRAY */
- { JS_ATOM_Float64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT64_ARRAY */
- { JS_ATOM_DataView, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_DATAVIEW */
- { JS_ATOM_BigInt, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_INT */
- { JS_ATOM_Map, js_map_finalizer, js_map_mark }, /* JS_CLASS_MAP */
- { JS_ATOM_Set, js_map_finalizer, js_map_mark }, /* JS_CLASS_SET */
- { JS_ATOM_WeakMap, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKMAP */
- { JS_ATOM_WeakSet, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKSET */
- { JS_ATOM_Iterator, NULL, NULL }, /* JS_CLASS_ITERATOR */
- { JS_ATOM_IteratorConcat, js_iterator_concat_finalizer, js_iterator_concat_mark }, /* JS_CLASS_ITERATOR_CONCAT */
- { JS_ATOM_IteratorHelper, js_iterator_helper_finalizer, js_iterator_helper_mark }, /* JS_CLASS_ITERATOR_HELPER */
- { JS_ATOM_IteratorWrap, js_iterator_wrap_finalizer, js_iterator_wrap_mark }, /* JS_CLASS_ITERATOR_WRAP */
- { JS_ATOM_Map_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_MAP_ITERATOR */
- { JS_ATOM_Set_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_SET_ITERATOR */
- { JS_ATOM_Array_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_ARRAY_ITERATOR */
- { JS_ATOM_String_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_STRING_ITERATOR */
- { JS_ATOM_RegExp_String_Iterator, js_regexp_string_iterator_finalizer, js_regexp_string_iterator_mark }, /* JS_CLASS_REGEXP_STRING_ITERATOR */
- { JS_ATOM_Generator, js_generator_finalizer, js_generator_mark }, /* JS_CLASS_GENERATOR */
- { JS_ATOM_Object, js_global_object_finalizer, js_global_object_mark }, /* JS_CLASS_GLOBAL_OBJECT */
- { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_RAWJSON */
- };
- static int init_class_range(JSRuntime *rt, JSClassShortDef const *tab,
- int start, int count)
- {
- JSClassDef cm_s, *cm = &cm_s;
- int i, class_id;
- for(i = 0; i < count; i++) {
- class_id = i + start;
- memset(cm, 0, sizeof(*cm));
- cm->finalizer = tab[i].finalizer;
- cm->gc_mark = tab[i].gc_mark;
- if (JS_NewClass1(rt, class_id, cm, tab[i].class_name) < 0)
- return -1;
- }
- return 0;
- }
- #if !defined(CONFIG_STACK_CHECK)
- /* no stack limitation */
- static inline uintptr_t js_get_stack_pointer(void)
- {
- return 0;
- }
- static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size)
- {
- return FALSE;
- }
- #else
- /* Note: OS and CPU dependent */
- static inline uintptr_t js_get_stack_pointer(void)
- {
- return (uintptr_t)__builtin_frame_address(0);
- }
- static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size)
- {
- uintptr_t sp;
- sp = js_get_stack_pointer() - alloca_size;
- return unlikely(sp < rt->stack_limit);
- }
- #endif
- JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque)
- {
- JSRuntime *rt;
- JSMallocState ms;
- memset(&ms, 0, sizeof(ms));
- ms.opaque = opaque;
- ms.malloc_limit = -1;
- rt = mf->js_malloc(&ms, sizeof(JSRuntime));
- if (!rt)
- return NULL;
- memset(rt, 0, sizeof(*rt));
- js_malloc_init(&rt->malloc_ctx);
- rt->malloc_ctx.mf = *mf;
- rt->malloc_ctx.malloc_state = ms;
- rt->malloc_gc_threshold = 256 * 1024;
- init_list_head(&rt->context_list);
- init_list_head(&rt->gc_obj_list);
- init_list_head(&rt->gc_zero_ref_count_list);
- rt->gc_phase = JS_GC_PHASE_NONE;
- init_list_head(&rt->weakref_list);
- #ifdef DUMP_LEAKS
- init_list_head(&rt->string_list);
- #endif
- init_list_head(&rt->job_list);
- if (JS_InitAtoms(rt))
- goto fail;
- /* create the object, array and function classes */
- if (init_class_range(rt, js_std_class_def, JS_CLASS_OBJECT,
- countof(js_std_class_def)) < 0)
- goto fail;
- rt->class_array[JS_CLASS_ARGUMENTS].exotic = &js_arguments_exotic_methods;
- rt->class_array[JS_CLASS_MAPPED_ARGUMENTS].exotic = &js_arguments_exotic_methods;
- rt->class_array[JS_CLASS_STRING].exotic = &js_string_exotic_methods;
- rt->class_array[JS_CLASS_MODULE_NS].exotic = &js_module_ns_exotic_methods;
- rt->class_array[JS_CLASS_C_FUNCTION].call = js_call_c_function;
- rt->class_array[JS_CLASS_C_FUNCTION_DATA].call = js_c_function_data_call;
- rt->class_array[JS_CLASS_BOUND_FUNCTION].call = js_call_bound_function;
- rt->class_array[JS_CLASS_GENERATOR_FUNCTION].call = js_generator_function_call;
- if (init_shape_hash(rt))
- goto fail;
- rt->stack_size = JS_DEFAULT_STACK_SIZE;
- JS_UpdateStackTop(rt);
- rt->current_exception = JS_UNINITIALIZED;
- return rt;
- fail:
- JS_FreeRuntime(rt);
- return NULL;
- }
- void *JS_GetRuntimeOpaque(JSRuntime *rt)
- {
- return rt->user_opaque;
- }
- void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque)
- {
- rt->user_opaque = opaque;
- }
- /* default memory allocation functions with memory limitation */
- static size_t js_def_malloc_usable_size(const void *ptr)
- {
- #if defined(__APPLE__)
- return malloc_size(ptr);
- #elif defined(_WIN32)
- return _msize((void *)ptr);
- #elif defined(__EMSCRIPTEN__)
- return 0;
- #elif defined(__linux__) || defined(__GLIBC__)
- return malloc_usable_size((void *)ptr);
- #else
- /* change this to `return 0;` if compilation fails */
- return malloc_usable_size((void *)ptr);
- #endif
- }
- static void *js_def_malloc(JSMallocState *s, size_t size)
- {
- void *ptr;
- /* Do not allocate zero bytes: behavior is platform dependent */
- assert(size != 0);
- if (unlikely(s->malloc_size + size > s->malloc_limit))
- return NULL;
- ptr = malloc(size);
- if (!ptr)
- return NULL;
- s->malloc_count++;
- s->malloc_size += js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
- return ptr;
- }
- static void js_def_free(JSMallocState *s, void *ptr)
- {
- if (!ptr)
- return;
- s->malloc_count--;
- s->malloc_size -= js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
- free(ptr);
- }
- static void *js_def_realloc(JSMallocState *s, void *ptr, size_t size)
- {
- size_t old_size;
- if (!ptr) {
- if (size == 0)
- return NULL;
- return js_def_malloc(s, size);
- }
- old_size = js_def_malloc_usable_size(ptr);
- if (size == 0) {
- s->malloc_count--;
- s->malloc_size -= old_size + MALLOC_OVERHEAD;
- free(ptr);
- return NULL;
- }
- if (s->malloc_size + size - old_size > s->malloc_limit)
- return NULL;
- ptr = realloc(ptr, size);
- if (!ptr)
- return NULL;
- s->malloc_size += js_def_malloc_usable_size(ptr) - old_size;
- return ptr;
- }
- static const JSMallocFunctions def_malloc_funcs = {
- js_def_malloc,
- js_def_free,
- js_def_realloc,
- js_def_malloc_usable_size,
- };
- JSRuntime *JS_NewRuntime(void)
- {
- return JS_NewRuntime2(&def_malloc_funcs, NULL);
- }
- void JS_SetMemoryLimit(JSRuntime *rt, size_t limit)
- {
- rt->malloc_ctx.malloc_state.malloc_limit = limit;
- }
- /* use -1 to disable automatic GC */
- void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold)
- {
- rt->malloc_gc_threshold = gc_threshold;
- }
- #define malloc(s) malloc_is_forbidden(s)
- #define free(p) free_is_forbidden(p)
- #define realloc(p,s) realloc_is_forbidden(p,s)
- void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque)
- {
- rt->interrupt_handler = cb;
- rt->interrupt_opaque = opaque;
- }
- void JS_SetCanBlock(JSRuntime *rt, BOOL can_block)
- {
- rt->can_block = can_block;
- }
- void JS_SetSharedArrayBufferFunctions(JSRuntime *rt,
- const JSSharedArrayBufferFunctions *sf)
- {
- rt->sab_funcs = *sf;
- }
- void JS_SetStripInfo(JSRuntime *rt, int flags)
- {
- rt->strip_flags = flags;
- }
- int JS_GetStripInfo(JSRuntime *rt)
- {
- return rt->strip_flags;
- }
- static int JS_EnqueueJob2(JSContext *ctx, JSJobFunc *job_func,
- int argc, JSValueConst *argv, BOOL no_exception)
- {
- JSRuntime *rt = ctx->rt;
- JSJobEntry *e;
- int i;
- if (no_exception)
- e = js_malloc_rt(ctx->rt, sizeof(*e) + argc * sizeof(JSValue));
- else
- e = js_malloc(ctx, sizeof(*e) + argc * sizeof(JSValue));
- if (!e)
- return -1;
- e->realm = JS_DupContext(ctx);
- e->job_func = job_func;
- e->argc = argc;
- for(i = 0; i < argc; i++) {
- e->argv[i] = JS_DupValue(ctx, argv[i]);
- }
- list_add_tail(&e->link, &rt->job_list);
- return 0;
- }
- /* return 0 if OK, < 0 if exception */
- int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func,
- int argc, JSValueConst *argv)
- {
- return JS_EnqueueJob2(ctx, job_func, argc, argv, FALSE);
- }
- BOOL JS_IsJobPending(JSRuntime *rt)
- {
- return !list_empty(&rt->job_list);
- }
- /* return < 0 if exception, 0 if no job pending, 1 if a job was
- executed successfully. The context of the job is stored in '*pctx'
- if pctx != NULL. It may be NULL if the context was already
- destroyed or if no job was pending. The 'pctx' parameter is now
- absolete. */
- int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx)
- {
- JSContext *ctx;
- JSJobEntry *e;
- JSValue res;
- int i, ret;
- if (list_empty(&rt->job_list)) {
- if (pctx)
- *pctx = NULL;
- return 0;
- }
- /* get the first pending job and execute it */
- e = list_entry(rt->job_list.next, JSJobEntry, link);
- list_del(&e->link);
- ctx = e->realm;
- res = e->job_func(ctx, e->argc, (JSValueConst *)e->argv);
- for(i = 0; i < e->argc; i++)
- JS_FreeValue(ctx, e->argv[i]);
- if (JS_IsException(res))
- ret = -1;
- else
- ret = 1;
- JS_FreeValue(ctx, res);
- js_free(ctx, e);
- if (pctx) {
- if (js_rc(ctx)->ref_count > 1)
- *pctx = ctx;
- else
- *pctx = NULL;
- }
- JS_FreeContext(ctx);
- return ret;
- }
- static inline uint32_t atom_get_free(const JSAtomStruct *p)
- {
- return (uintptr_t)p >> 1;
- }
- static inline BOOL atom_is_free(const JSAtomStruct *p)
- {
- return (uintptr_t)p & 1;
- }
- static inline JSAtomStruct *atom_set_free(uint32_t v)
- {
- return (JSAtomStruct *)(((uintptr_t)v << 1) | 1);
- }
- /* Note: the string contents are uninitialized */
- static JSString *js_alloc_string_rt(JSRuntime *rt, int max_len, int is_wide_char)
- {
- JSString *str;
- str = js_malloc_rt(rt, sizeof(JSString) + (max_len << is_wide_char) + 1 - is_wide_char);
- if (unlikely(!str))
- return NULL;
- js_rc(str)->ref_count = 1;
- str->is_wide_char = is_wide_char;
- str->len = max_len;
- str->atom_type = 0;
- str->hash = 0; /* optional but costless */
- str->hash_next = 0; /* optional */
- #ifdef DUMP_LEAKS
- list_add_tail(&str->link, &rt->string_list);
- #endif
- return str;
- }
- static JSString *js_alloc_string(JSContext *ctx, int max_len, int is_wide_char)
- {
- JSString *p;
- p = js_alloc_string_rt(ctx->rt, max_len, is_wide_char);
- if (unlikely(!p)) {
- JS_ThrowOutOfMemory(ctx);
- return NULL;
- }
- return p;
- }
- /* same as JS_FreeValueRT() but faster */
- static inline void js_free_string(JSRuntime *rt, JSString *str)
- {
- if (--js_rc(str)->ref_count <= 0) {
- if (str->atom_type) {
- JS_FreeAtomStruct(rt, str);
- } else {
- #ifdef DUMP_LEAKS
- list_del(&str->link);
- #endif
- js_free_rt(rt, str);
- }
- }
- }
- void JS_SetRuntimeInfo(JSRuntime *rt, const char *s)
- {
- if (rt)
- rt->rt_info = s;
- }
- void JS_FreeRuntime(JSRuntime *rt)
- {
- struct list_head *el, *el1;
- int i;
- JS_FreeValueRT(rt, rt->current_exception);
- list_for_each_safe(el, el1, &rt->job_list) {
- JSJobEntry *e = list_entry(el, JSJobEntry, link);
- for(i = 0; i < e->argc; i++)
- JS_FreeValueRT(rt, e->argv[i]);
- JS_FreeContext(e->realm);
- js_free_rt(rt, e);
- }
- init_list_head(&rt->job_list);
- /* don't remove the weak objects to avoid create new jobs with
- FinalizationRegistry */
- JS_RunGCInternal(rt, FALSE);
- #ifdef DUMP_LEAKS
- /* leaking objects */
- {
- BOOL header_done;
- JSGCObjectHeader *p;
- int count;
- /* remove the internal refcounts to display only the object
- referenced externally */
- list_for_each(el, &rt->gc_obj_list) {
- p = list_entry(el, JSGCObjectHeader, link);
- js_rc(p)->mark = 0;
- }
- gc_decref(rt);
- header_done = FALSE;
- list_for_each(el, &rt->gc_obj_list) {
- p = list_entry(el, JSGCObjectHeader, link);
- if (js_rc(p)->ref_count != 0) {
- if (!header_done) {
- printf("Object leaks:\n");
- JS_DumpObjectHeader(rt);
- header_done = TRUE;
- }
- JS_DumpGCObject(rt, p);
- }
- }
- count = 0;
- list_for_each(el, &rt->gc_obj_list) {
- p = list_entry(el, JSGCObjectHeader, link);
- if (js_rc(p)->ref_count == 0) {
- count++;
- }
- }
- if (count != 0)
- printf("Secondary object leaks: %d\n", count);
- }
- #endif
- assert(list_empty(&rt->gc_obj_list));
- assert(list_empty(&rt->weakref_list));
- /* free the classes */
- for(i = 0; i < rt->class_count; i++) {
- JSClass *cl = &rt->class_array[i];
- if (cl->class_id != 0) {
- JS_FreeAtomRT(rt, cl->class_name);
- }
- }
- js_free_rt(rt, rt->class_array);
- #ifdef DUMP_LEAKS
- /* only the atoms defined in JS_InitAtoms() should be left */
- {
- BOOL header_done = FALSE;
- for(i = 0; i < rt->atom_size; i++) {
- JSAtomStruct *p = rt->atom_array[i];
- if (!atom_is_free(p) /* && p->str*/) {
- if (i >= JS_ATOM_END || js_rc(p)->ref_count != 1) {
- if (!header_done) {
- header_done = TRUE;
- if (rt->rt_info) {
- printf("%s:1: atom leakage:", rt->rt_info);
- } else {
- printf("Atom leaks:\n"
- " %6s %6s %s\n",
- "ID", "REFCNT", "NAME");
- }
- }
- if (rt->rt_info) {
- printf(" ");
- } else {
- printf(" %6u %6u ", i, js_rc(p)->ref_count);
- }
- switch (p->atom_type) {
- case JS_ATOM_TYPE_STRING:
- JS_DumpString(rt, p);
- break;
- case JS_ATOM_TYPE_GLOBAL_SYMBOL:
- printf("Symbol.for(");
- JS_DumpString(rt, p);
- printf(")");
- break;
- case JS_ATOM_TYPE_SYMBOL:
- if (p->hash != JS_ATOM_HASH_PRIVATE) {
- printf("Symbol(");
- JS_DumpString(rt, p);
- printf(")");
- } else {
- printf("Private(");
- JS_DumpString(rt, p);
- printf(")");
- }
- break;
- }
- if (rt->rt_info) {
- printf(":%u", js_rc(p)->ref_count);
- } else {
- printf("\n");
- }
- }
- }
- }
- if (rt->rt_info && header_done)
- printf("\n");
- }
- #endif
- /* free the atoms */
- for(i = 0; i < rt->atom_size; i++) {
- JSAtomStruct *p = rt->atom_array[i];
- if (!atom_is_free(p)) {
- #ifdef DUMP_LEAKS
- list_del(&p->link);
- #endif
- js_free_rt(rt, p);
- }
- }
- js_free_rt(rt, rt->atom_array);
- js_free_rt(rt, rt->atom_hash);
- js_free_rt(rt, rt->shape_hash);
- #ifdef DUMP_LEAKS
- if (!list_empty(&rt->string_list)) {
- if (rt->rt_info) {
- printf("%s:1: string leakage:", rt->rt_info);
- } else {
- printf("String leaks:\n"
- " %6s %s\n",
- "REFCNT", "VALUE");
- }
- list_for_each_safe(el, el1, &rt->string_list) {
- JSString *str = list_entry(el, JSString, link);
- if (rt->rt_info) {
- printf(" ");
- } else {
- printf(" %6u ", js_rc(str)->ref_count);
- }
- JS_DumpString(rt, str);
- if (rt->rt_info) {
- printf(":%u", js_rc(str)->ref_count);
- } else {
- printf("\n");
- }
- list_del(&str->link);
- js_free_rt(rt, str);
- }
- if (rt->rt_info)
- printf("\n");
- }
- {
- JSMallocState *s = &rt->malloc_ctx.malloc_state;
- if (s->malloc_count > 1) {
- if (rt->rt_info)
- printf("%s:1: ", rt->rt_info);
- printf("Memory leak: %"PRIu64" bytes lost in %"PRIu64" block%s\n",
- (uint64_t)(s->malloc_size - sizeof(JSRuntime)),
- (uint64_t)(s->malloc_count - 1), &"s"[s->malloc_count == 2]);
- }
- }
- #endif
- {
- JSMallocState ms = rt->malloc_ctx.malloc_state;
- rt->malloc_ctx.mf.js_free(&ms, rt);
- }
- }
- JSContext *JS_NewContextRaw(JSRuntime *rt)
- {
- JSContext *ctx;
- int i;
- ctx = js_mallocz_rt(rt, sizeof(JSContext));
- if (!ctx)
- return NULL;
- js_rc(ctx)->ref_count = 1;
- add_gc_object(rt, &ctx->header, JS_GC_OBJ_TYPE_JS_CONTEXT);
- ctx->class_proto = js_malloc_rt(rt, sizeof(ctx->class_proto[0]) *
- rt->class_count);
- if (!ctx->class_proto) {
- js_free_rt(rt, ctx);
- return NULL;
- }
- ctx->rt = rt;
- list_add_tail(&ctx->link, &rt->context_list);
- for(i = 0; i < rt->class_count; i++)
- ctx->class_proto[i] = JS_NULL;
- ctx->array_ctor = JS_NULL;
- ctx->iterator_ctor = JS_NULL;
- ctx->regexp_ctor = JS_NULL;
- ctx->promise_ctor = JS_NULL;
- init_list_head(&ctx->loaded_modules);
- if (JS_AddIntrinsicBasicObjects(ctx)) {
- JS_FreeContext(ctx);
- return NULL;
- }
- return ctx;
- }
- JSContext *JS_NewContext(JSRuntime *rt)
- {
- JSContext *ctx;
- ctx = JS_NewContextRaw(rt);
- if (!ctx)
- return NULL;
- if (JS_AddIntrinsicBaseObjects(ctx) ||
- JS_AddIntrinsicDate(ctx) ||
- JS_AddIntrinsicEval(ctx) ||
- JS_AddIntrinsicStringNormalize(ctx) ||
- JS_AddIntrinsicRegExp(ctx) ||
- JS_AddIntrinsicJSON(ctx) ||
- JS_AddIntrinsicProxy(ctx) ||
- JS_AddIntrinsicMapSet(ctx) ||
- JS_AddIntrinsicTypedArrays(ctx) ||
- JS_AddIntrinsicPromise(ctx) ||
- JS_AddIntrinsicWeakRef(ctx)) {
- JS_FreeContext(ctx);
- return NULL;
- }
- return ctx;
- }
- void *JS_GetContextOpaque(JSContext *ctx)
- {
- return ctx->user_opaque;
- }
- void JS_SetContextOpaque(JSContext *ctx, void *opaque)
- {
- ctx->user_opaque = opaque;
- }
- /* set the new value and free the old value after (freeing the value
- can reallocate the object data) */
- static inline void set_value(JSContext *ctx, JSValue *pval, JSValue new_val)
- {
- JSValue old_val;
- old_val = *pval;
- *pval = new_val;
- JS_FreeValue(ctx, old_val);
- }
- void JS_SetClassProto(JSContext *ctx, JSClassID class_id, JSValue obj)
- {
- JSRuntime *rt = ctx->rt;
- assert(class_id < rt->class_count);
- set_value(ctx, &ctx->class_proto[class_id], obj);
- }
- JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id)
- {
- JSRuntime *rt = ctx->rt;
- assert(class_id < rt->class_count);
- return JS_DupValue(ctx, ctx->class_proto[class_id]);
- }
- typedef enum JSFreeModuleEnum {
- JS_FREE_MODULE_ALL,
- JS_FREE_MODULE_NOT_RESOLVED,
- } JSFreeModuleEnum;
- /* XXX: would be more efficient with separate module lists */
- static void js_free_modules(JSContext *ctx, JSFreeModuleEnum flag)
- {
- struct list_head *el, *el1;
- list_for_each_safe(el, el1, &ctx->loaded_modules) {
- JSModuleDef *m = list_entry(el, JSModuleDef, link);
- if (flag == JS_FREE_MODULE_ALL ||
- (flag == JS_FREE_MODULE_NOT_RESOLVED && !m->resolved)) {
- /* warning: the module may be referenced elsewhere. It
- could be simpler to use an array instead of a list for
- 'ctx->loaded_modules' */
- list_del(&m->link);
- m->link.prev = NULL;
- m->link.next = NULL;
- JS_FreeValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));
- }
- }
- }
- JSContext *JS_DupContext(JSContext *ctx)
- {
- js_rc(ctx)->ref_count++;
- return ctx;
- }
- /* used by the GC */
- static void JS_MarkContext(JSRuntime *rt, JSContext *ctx,
- JS_MarkFunc *mark_func)
- {
- int i;
- struct list_head *el;
- list_for_each(el, &ctx->loaded_modules) {
- JSModuleDef *m = list_entry(el, JSModuleDef, link);
- JS_MarkValue(rt, JS_MKPTR(JS_TAG_MODULE, m), mark_func);
- }
- JS_MarkValue(rt, ctx->global_obj, mark_func);
- JS_MarkValue(rt, ctx->global_var_obj, mark_func);
- JS_MarkValue(rt, ctx->throw_type_error, mark_func);
- JS_MarkValue(rt, ctx->eval_obj, mark_func);
- JS_MarkValue(rt, ctx->array_proto_values, mark_func);
- for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
- JS_MarkValue(rt, ctx->native_error_proto[i], mark_func);
- }
- for(i = 0; i < rt->class_count; i++) {
- JS_MarkValue(rt, ctx->class_proto[i], mark_func);
- }
- JS_MarkValue(rt, ctx->iterator_ctor, mark_func);
- JS_MarkValue(rt, ctx->async_iterator_proto, mark_func);
- JS_MarkValue(rt, ctx->promise_ctor, mark_func);
- JS_MarkValue(rt, ctx->array_ctor, mark_func);
- JS_MarkValue(rt, ctx->regexp_ctor, mark_func);
- JS_MarkValue(rt, ctx->function_ctor, mark_func);
- JS_MarkValue(rt, ctx->function_proto, mark_func);
- if (ctx->array_shape)
- mark_func(rt, &ctx->array_shape->header);
- if (ctx->arguments_shape)
- mark_func(rt, &ctx->arguments_shape->header);
- if (ctx->mapped_arguments_shape)
- mark_func(rt, &ctx->mapped_arguments_shape->header);
- if (ctx->regexp_shape)
- mark_func(rt, &ctx->regexp_shape->header);
- if (ctx->regexp_result_shape)
- mark_func(rt, &ctx->regexp_result_shape->header);
- }
- void JS_FreeContext(JSContext *ctx)
- {
- JSRuntime *rt = ctx->rt;
- int i;
- if (--js_rc(ctx)->ref_count > 0)
- return;
- assert(js_rc(ctx)->ref_count == 0);
- #ifdef DUMP_ATOMS
- JS_DumpAtoms(ctx->rt);
- #endif
- #ifdef DUMP_SHAPES
- JS_DumpShapes(ctx->rt);
- #endif
- #ifdef DUMP_OBJECTS
- {
- struct list_head *el;
- JSGCObjectHeader *p;
- printf("JSObjects: {\n");
- JS_DumpObjectHeader(ctx->rt);
- list_for_each(el, &rt->gc_obj_list) {
- p = list_entry(el, JSGCObjectHeader, link);
- JS_DumpGCObject(rt, p);
- }
- printf("}\n");
- }
- #endif
- #ifdef DUMP_MEM
- {
- JSMemoryUsage stats;
- JS_ComputeMemoryUsage(rt, &stats);
- JS_DumpMemoryUsage(stdout, &stats, rt);
- }
- #endif
- js_free_modules(ctx, JS_FREE_MODULE_ALL);
- JS_FreeValue(ctx, ctx->global_obj);
- JS_FreeValue(ctx, ctx->global_var_obj);
- JS_FreeValue(ctx, ctx->throw_type_error);
- JS_FreeValue(ctx, ctx->eval_obj);
- JS_FreeValue(ctx, ctx->array_proto_values);
- for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
- JS_FreeValue(ctx, ctx->native_error_proto[i]);
- }
- for(i = 0; i < rt->class_count; i++) {
- JS_FreeValue(ctx, ctx->class_proto[i]);
- }
- js_free_rt(rt, ctx->class_proto);
- JS_FreeValue(ctx, ctx->iterator_ctor);
- JS_FreeValue(ctx, ctx->async_iterator_proto);
- JS_FreeValue(ctx, ctx->promise_ctor);
- JS_FreeValue(ctx, ctx->array_ctor);
- JS_FreeValue(ctx, ctx->regexp_ctor);
- JS_FreeValue(ctx, ctx->function_ctor);
- JS_FreeValue(ctx, ctx->function_proto);
- js_free_shape_null(ctx->rt, ctx->array_shape);
- js_free_shape_null(ctx->rt, ctx->arguments_shape);
- js_free_shape_null(ctx->rt, ctx->mapped_arguments_shape);
- js_free_shape_null(ctx->rt, ctx->regexp_shape);
- js_free_shape_null(ctx->rt, ctx->regexp_result_shape);
- list_del(&ctx->link);
- remove_gc_object(&ctx->header);
- js_free_rt(ctx->rt, ctx);
- }
- JSRuntime *JS_GetRuntime(JSContext *ctx)
- {
- return ctx->rt;
- }
- static void update_stack_limit(JSRuntime *rt)
- {
- if (rt->stack_size == 0) {
- rt->stack_limit = 0; /* no limit */
- } else {
- rt->stack_limit = rt->stack_top - rt->stack_size;
- }
- }
- void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size)
- {
- rt->stack_size = stack_size;
- update_stack_limit(rt);
- }
- void JS_UpdateStackTop(JSRuntime *rt)
- {
- rt->stack_top = js_get_stack_pointer();
- update_stack_limit(rt);
- }
- static inline BOOL is_strict_mode(JSContext *ctx)
- {
- JSStackFrame *sf = ctx->rt->current_stack_frame;
- return (sf && (sf->js_mode & JS_MODE_STRICT));
- }
- /* JSAtom support */
- #define JS_ATOM_TAG_INT (1U << 31)
- #define JS_ATOM_MAX_INT (JS_ATOM_TAG_INT - 1)
- #define JS_ATOM_MAX ((1U << 30) - 1)
- /* return the max count from the hash size */
- #define JS_ATOM_COUNT_RESIZE(n) ((n) * 2)
- static inline BOOL __JS_AtomIsConst(JSAtom v)
- {
- #if defined(DUMP_LEAKS) && DUMP_LEAKS > 1
- return (int32_t)v <= 0;
- #else
- return (int32_t)v < JS_ATOM_END;
- #endif
- }
- static inline BOOL __JS_AtomIsTaggedInt(JSAtom v)
- {
- return (v & JS_ATOM_TAG_INT) != 0;
- }
- static inline JSAtom __JS_AtomFromUInt32(uint32_t v)
- {
- return v | JS_ATOM_TAG_INT;
- }
- static inline uint32_t __JS_AtomToUInt32(JSAtom atom)
- {
- return atom & ~JS_ATOM_TAG_INT;
- }
- static inline int is_num(int c)
- {
- return c >= '0' && c <= '9';
- }
- /* return TRUE if the string is a number n with 0 <= n <= 2^32-1 */
- static inline BOOL is_num_string(uint32_t *pval, const JSString *p)
- {
- uint32_t n;
- uint64_t n64;
- int c, i, len;
- len = p->len;
- if (len == 0 || len > 10)
- return FALSE;
- c = string_get(p, 0);
- if (is_num(c)) {
- if (c == '0') {
- if (len != 1)
- return FALSE;
- n = 0;
- } else {
- n = c - '0';
- for(i = 1; i < len; i++) {
- c = string_get(p, i);
- if (!is_num(c))
- return FALSE;
- n64 = (uint64_t)n * 10 + (c - '0');
- if ((n64 >> 32) != 0)
- return FALSE;
- n = n64;
- }
- }
- *pval = n;
- return TRUE;
- } else {
- return FALSE;
- }
- }
- /* XXX: could use faster version ? */
- static inline uint32_t hash_string8(const uint8_t *str, size_t len, uint32_t h)
- {
- size_t i;
- for(i = 0; i < len; i++)
- h = h * 263 + str[i];
- return h;
- }
- static inline uint32_t hash_string16(const uint16_t *str,
- size_t len, uint32_t h)
- {
- size_t i;
- for(i = 0; i < len; i++)
- h = h * 263 + str[i];
- return h;
- }
- static uint32_t hash_string(const JSString *str, uint32_t h)
- {
- if (str->is_wide_char)
- h = hash_string16(str->u.str16, str->len, h);
- else
- h = hash_string8(str->u.str8, str->len, h);
- return h;
- }
- static uint32_t hash_string_rope(JSValueConst val, uint32_t h)
- {
- if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) {
- return hash_string(JS_VALUE_GET_STRING(val), h);
- } else {
- JSStringRope *r = JS_VALUE_GET_STRING_ROPE(val);
- h = hash_string_rope(r->left, h);
- return hash_string_rope(r->right, h);
- }
- }
- static __maybe_unused void JS_DumpChar(FILE *fo, int c, int sep)
- {
- if (c == sep || c == '\\') {
- fputc('\\', fo);
- fputc(c, fo);
- } else if (c >= ' ' && c <= 126) {
- fputc(c, fo);
- } else if (c == '\n') {
- fputc('\\', fo);
- fputc('n', fo);
- } else {
- fprintf(fo, "\\u%04x", c);
- }
- }
- static __maybe_unused void JS_DumpString(JSRuntime *rt, const JSString *p)
- {
- int i, sep;
- if (p == NULL) {
- printf("<null>");
- return;
- }
- printf("%d", js_rc((void *)p)->ref_count);
- sep = (js_rc((void *)p)->ref_count == 1) ? '\"' : '\'';
- putchar(sep);
- for(i = 0; i < p->len; i++) {
- JS_DumpChar(stdout, string_get(p, i), sep);
- }
- putchar(sep);
- }
- static __maybe_unused void JS_DumpAtoms(JSRuntime *rt)
- {
- JSAtomStruct *p;
- int h, i;
- /* This only dumps hashed atoms, not JS_ATOM_TYPE_SYMBOL atoms */
- printf("JSAtom count=%d size=%d hash_size=%d:\n",
- rt->atom_count, rt->atom_size, rt->atom_hash_size);
- printf("JSAtom hash table: {\n");
- for(i = 0; i < rt->atom_hash_size; i++) {
- h = rt->atom_hash[i];
- if (h) {
- printf(" %d:", i);
- while (h) {
- p = rt->atom_array[h];
- printf(" ");
- JS_DumpString(rt, p);
- h = p->hash_next;
- }
- printf("\n");
- }
- }
- printf("}\n");
- printf("JSAtom table: {\n");
- for(i = 0; i < rt->atom_size; i++) {
- p = rt->atom_array[i];
- if (!atom_is_free(p)) {
- printf(" %d: { %d %08x ", i, p->atom_type, p->hash);
- if (!(p->len == 0 && p->is_wide_char != 0))
- JS_DumpString(rt, p);
- printf(" %d }\n", p->hash_next);
- }
- }
- printf("}\n");
- }
- static int JS_ResizeAtomHash(JSRuntime *rt, int new_hash_size)
- {
- JSAtomStruct *p;
- uint32_t new_hash_mask, h, i, hash_next1, j, *new_hash;
- assert((new_hash_size & (new_hash_size - 1)) == 0); /* power of two */
- new_hash_mask = new_hash_size - 1;
- new_hash = js_mallocz_rt(rt, sizeof(rt->atom_hash[0]) * new_hash_size);
- if (!new_hash)
- return -1;
- for(i = 0; i < rt->atom_hash_size; i++) {
- h = rt->atom_hash[i];
- while (h != 0) {
- p = rt->atom_array[h];
- hash_next1 = p->hash_next;
- /* add in new hash table */
- j = p->hash & new_hash_mask;
- p->hash_next = new_hash[j];
- new_hash[j] = h;
- h = hash_next1;
- }
- }
- js_free_rt(rt, rt->atom_hash);
- rt->atom_hash = new_hash;
- rt->atom_hash_size = new_hash_size;
- rt->atom_count_resize = JS_ATOM_COUNT_RESIZE(new_hash_size);
- // JS_DumpAtoms(rt);
- return 0;
- }
- static int JS_InitAtoms(JSRuntime *rt)
- {
- int i, len, atom_type;
- const char *p;
- rt->atom_hash_size = 0;
- rt->atom_hash = NULL;
- rt->atom_count = 0;
- rt->atom_size = 0;
- rt->atom_free_index = 0;
- if (JS_ResizeAtomHash(rt, 512)) /* there are at least 504 predefined atoms */
- return -1;
- p = js_atom_init;
- for(i = 1; i < JS_ATOM_END; i++) {
- if (i == JS_ATOM_Private_brand)
- atom_type = JS_ATOM_TYPE_PRIVATE;
- else if (i >= JS_ATOM_Symbol_toPrimitive)
- atom_type = JS_ATOM_TYPE_SYMBOL;
- else
- atom_type = JS_ATOM_TYPE_STRING;
- len = strlen(p);
- if (__JS_NewAtomInit(rt, p, len, atom_type) == JS_ATOM_NULL)
- return -1;
- p = p + len + 1;
- }
- return 0;
- }
- static JSAtom JS_DupAtomRT(JSRuntime *rt, JSAtom v)
- {
- JSAtomStruct *p;
- if (!__JS_AtomIsConst(v)) {
- p = rt->atom_array[v];
- js_rc(p)->ref_count++;
- }
- return v;
- }
- JSAtom JS_DupAtom(JSContext *ctx, JSAtom v)
- {
- JSRuntime *rt;
- JSAtomStruct *p;
- if (!__JS_AtomIsConst(v)) {
- rt = ctx->rt;
- p = rt->atom_array[v];
- js_rc(p)->ref_count++;
- }
- return v;
- }
- static JSAtomKindEnum JS_AtomGetKind(JSContext *ctx, JSAtom v)
- {
- JSRuntime *rt;
- JSAtomStruct *p;
- rt = ctx->rt;
- if (__JS_AtomIsTaggedInt(v))
- return JS_ATOM_KIND_STRING;
- p = rt->atom_array[v];
- switch(p->atom_type) {
- case JS_ATOM_TYPE_STRING:
- return JS_ATOM_KIND_STRING;
- case JS_ATOM_TYPE_GLOBAL_SYMBOL:
- return JS_ATOM_KIND_SYMBOL;
- case JS_ATOM_TYPE_SYMBOL:
- if (p->hash == JS_ATOM_HASH_PRIVATE)
- return JS_ATOM_KIND_PRIVATE;
- else
- return JS_ATOM_KIND_SYMBOL;
- default:
- abort();
- }
- }
- static BOOL JS_AtomIsString(JSContext *ctx, JSAtom v)
- {
- return JS_AtomGetKind(ctx, v) == JS_ATOM_KIND_STRING;
- }
- static JSAtom js_get_atom_index(JSRuntime *rt, JSAtomStruct *p)
- {
- uint32_t i = p->hash_next; /* atom_index */
- if (p->atom_type != JS_ATOM_TYPE_SYMBOL) {
- JSAtomStruct *p1;
- i = rt->atom_hash[p->hash & (rt->atom_hash_size - 1)];
- p1 = rt->atom_array[i];
- while (p1 != p) {
- assert(i != 0);
- i = p1->hash_next;
- p1 = rt->atom_array[i];
- }
- }
- return i;
- }
- /* string case (internal). Return JS_ATOM_NULL if error. 'str' is
- freed. */
- static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type)
- {
- uint32_t h, h1, i;
- JSAtomStruct *p;
- int len;
- #if 0
- printf("__JS_NewAtom: "); JS_DumpString(rt, str); printf("\n");
- #endif
- if (atom_type < JS_ATOM_TYPE_SYMBOL) {
- /* str is not NULL */
- if (str->atom_type == atom_type) {
- /* str is the atom, return its index */
- i = js_get_atom_index(rt, str);
- /* reduce string refcount and increase atom's unless constant */
- if (__JS_AtomIsConst(i))
- js_rc(str)->ref_count--;
- return i;
- }
- /* try and locate an already registered atom */
- len = str->len;
- h = hash_string(str, atom_type);
- h &= JS_ATOM_HASH_MASK;
- h1 = h & (rt->atom_hash_size - 1);
- i = rt->atom_hash[h1];
- while (i != 0) {
- p = rt->atom_array[i];
- if (p->hash == h &&
- p->atom_type == atom_type &&
- p->len == len &&
- js_string_memcmp(p, 0, str, 0, len) == 0) {
- if (!__JS_AtomIsConst(i))
- js_rc(p)->ref_count++;
- goto done;
- }
- i = p->hash_next;
- }
- } else {
- h1 = 0; /* avoid warning */
- if (atom_type == JS_ATOM_TYPE_SYMBOL) {
- h = 0;
- } else {
- h = JS_ATOM_HASH_PRIVATE;
- atom_type = JS_ATOM_TYPE_SYMBOL;
- }
- }
- if (rt->atom_free_index == 0) {
- /* allow new atom entries */
- uint32_t new_size, start;
- JSAtomStruct **new_array;
- /* alloc new with size progression 3/2:
- 4 6 9 13 19 28 42 63 94 141 211 316 474 711 1066 1599 2398 3597 5395 8092
- preallocating space for predefined atoms (at least 504).
- */
- new_size = max_int(711, rt->atom_size * 3 / 2);
- if (new_size > JS_ATOM_MAX)
- goto fail;
- /* XXX: should use realloc2 to use slack space */
- new_array = js_realloc_rt(rt, rt->atom_array, sizeof(*new_array) * new_size);
- if (!new_array)
- goto fail;
- /* Note: the atom 0 is not used */
- start = rt->atom_size;
- if (start == 0) {
- /* JS_ATOM_NULL entry */
- p = js_mallocz_rt(rt, sizeof(JSAtomStruct));
- if (!p) {
- js_free_rt(rt, new_array);
- goto fail;
- }
- js_rc(p)->ref_count = 1; /* not refcounted */
- p->atom_type = JS_ATOM_TYPE_SYMBOL;
- #ifdef DUMP_LEAKS
- list_add_tail(&p->link, &rt->string_list);
- #endif
- new_array[0] = p;
- rt->atom_count++;
- start = 1;
- }
- rt->atom_size = new_size;
- rt->atom_array = new_array;
- rt->atom_free_index = start;
- for(i = start; i < new_size; i++) {
- uint32_t next;
- if (i == (new_size - 1))
- next = 0;
- else
- next = i + 1;
- rt->atom_array[i] = atom_set_free(next);
- }
- }
- if (str) {
- if (str->atom_type == 0) {
- p = str;
- p->atom_type = atom_type;
- } else {
- p = js_malloc_rt(rt, sizeof(JSString) +
- (str->len << str->is_wide_char) +
- 1 - str->is_wide_char);
- if (unlikely(!p))
- goto fail;
- js_rc(p)->ref_count = 1;
- p->is_wide_char = str->is_wide_char;
- p->len = str->len;
- #ifdef DUMP_LEAKS
- list_add_tail(&p->link, &rt->string_list);
- #endif
- memcpy(p->u.str8, str->u.str8, (str->len << str->is_wide_char) +
- 1 - str->is_wide_char);
- js_free_string(rt, str);
- }
- } else {
- p = js_malloc_rt(rt, sizeof(JSAtomStruct)); /* empty wide string */
- if (!p)
- return JS_ATOM_NULL;
- js_rc(p)->ref_count = 1;
- p->is_wide_char = 1; /* Hack to represent NULL as a JSString */
- p->len = 0;
- #ifdef DUMP_LEAKS
- list_add_tail(&p->link, &rt->string_list);
- #endif
- }
- /* use an already free entry */
- i = rt->atom_free_index;
- rt->atom_free_index = atom_get_free(rt->atom_array[i]);
- rt->atom_array[i] = p;
- p->hash = h;
- p->hash_next = i; /* atom_index */
- p->atom_type = atom_type;
- rt->atom_count++;
- if (atom_type != JS_ATOM_TYPE_SYMBOL) {
- p->hash_next = rt->atom_hash[h1];
- rt->atom_hash[h1] = i;
- if (unlikely(rt->atom_count >= rt->atom_count_resize))
- JS_ResizeAtomHash(rt, rt->atom_hash_size * 2);
- }
- // JS_DumpAtoms(rt);
- return i;
- fail:
- i = JS_ATOM_NULL;
- done:
- if (str)
- js_free_string(rt, str);
- return i;
- }
- /* only works with zero terminated 8 bit strings */
- static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len,
- int atom_type)
- {
- JSString *p;
- p = js_alloc_string_rt(rt, len, 0);
- if (!p)
- return JS_ATOM_NULL;
- memcpy(p->u.str8, str, len);
- p->u.str8[len] = '\0';
- return __JS_NewAtom(rt, p, atom_type);
- }
- /* Warning: str must be ASCII only */
- static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len,
- int atom_type)
- {
- uint32_t h, h1, i;
- JSAtomStruct *p;
- h = hash_string8((const uint8_t *)str, len, JS_ATOM_TYPE_STRING);
- h &= JS_ATOM_HASH_MASK;
- h1 = h & (rt->atom_hash_size - 1);
- i = rt->atom_hash[h1];
- while (i != 0) {
- p = rt->atom_array[i];
- if (p->hash == h &&
- p->atom_type == JS_ATOM_TYPE_STRING &&
- p->len == len &&
- p->is_wide_char == 0 &&
- memcmp(p->u.str8, str, len) == 0) {
- if (!__JS_AtomIsConst(i))
- js_rc(p)->ref_count++;
- return i;
- }
- i = p->hash_next;
- }
- return JS_ATOM_NULL;
- }
- static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p)
- {
- #if 0 /* JS_ATOM_NULL is not refcounted: __JS_AtomIsConst() includes 0 */
- if (unlikely(i == JS_ATOM_NULL)) {
- js_rc(p)->ref_count = INT32_MAX / 2;
- return;
- }
- #endif
- uint32_t i = p->hash_next; /* atom_index */
- if (p->atom_type != JS_ATOM_TYPE_SYMBOL) {
- JSAtomStruct *p0, *p1;
- uint32_t h0;
- h0 = p->hash & (rt->atom_hash_size - 1);
- i = rt->atom_hash[h0];
- p1 = rt->atom_array[i];
- if (p1 == p) {
- rt->atom_hash[h0] = p1->hash_next;
- } else {
- for(;;) {
- assert(i != 0);
- p0 = p1;
- i = p1->hash_next;
- p1 = rt->atom_array[i];
- if (p1 == p) {
- p0->hash_next = p1->hash_next;
- break;
- }
- }
- }
- }
- /* insert in free atom list */
- rt->atom_array[i] = atom_set_free(rt->atom_free_index);
- rt->atom_free_index = i;
- /* free the string structure */
- #ifdef DUMP_LEAKS
- list_del(&p->link);
- #endif
- if (p->atom_type == JS_ATOM_TYPE_SYMBOL &&
- p->hash != JS_ATOM_HASH_PRIVATE && p->hash != 0) {
- /* live weak references are still present on this object: keep
- it */
- } else {
- js_free_rt(rt, p);
- }
- rt->atom_count--;
- assert(rt->atom_count >= 0);
- }
- static void __JS_FreeAtom(JSRuntime *rt, uint32_t i)
- {
- JSAtomStruct *p;
- p = rt->atom_array[i];
- if (--js_rc(p)->ref_count > 0)
- return;
- JS_FreeAtomStruct(rt, p);
- }
- /* Warning: 'p' is freed */
- static JSAtom JS_NewAtomStr(JSContext *ctx, JSString *p)
- {
- JSRuntime *rt = ctx->rt;
- uint32_t n;
- if (is_num_string(&n, p)) {
- if (n <= JS_ATOM_MAX_INT) {
- js_free_string(rt, p);
- return __JS_AtomFromUInt32(n);
- }
- }
- /* XXX: should generate an exception */
- return __JS_NewAtom(rt, p, JS_ATOM_TYPE_STRING);
- }
- /* XXX: optimize */
- static size_t count_ascii(const uint8_t *buf, size_t len)
- {
- const uint8_t *p, *p_end;
- p = buf;
- p_end = buf + len;
- while (p < p_end && *p < 128)
- p++;
- return p - buf;
- }
- /* str is UTF-8 encoded */
- JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len)
- {
- JSValue val;
- if (len == 0 ||
- (!is_digit(*str) &&
- count_ascii((const uint8_t *)str, len) == len)) {
- JSAtom atom = __JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING);
- if (atom)
- return atom;
- }
- val = JS_NewStringLen(ctx, str, len);
- if (JS_IsException(val))
- return JS_ATOM_NULL;
- return JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(val));
- }
- JSAtom JS_NewAtom(JSContext *ctx, const char *str)
- {
- return JS_NewAtomLen(ctx, str, strlen(str));
- }
- JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n)
- {
- if (n <= JS_ATOM_MAX_INT) {
- return __JS_AtomFromUInt32(n);
- } else {
- char buf[11];
- JSValue val;
- size_t len;
- len = u32toa(buf, n);
- val = js_new_string8_len(ctx, buf, len);
- if (JS_IsException(val))
- return JS_ATOM_NULL;
- return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val),
- JS_ATOM_TYPE_STRING);
- }
- }
- static JSAtom JS_NewAtomInt64(JSContext *ctx, int64_t n)
- {
- if ((uint64_t)n <= JS_ATOM_MAX_INT) {
- return __JS_AtomFromUInt32((uint32_t)n);
- } else {
- char buf[24];
- JSValue val;
- size_t len;
- len = i64toa(buf, n);
- val = js_new_string8_len(ctx, buf, len);
- if (JS_IsException(val))
- return JS_ATOM_NULL;
- return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val),
- JS_ATOM_TYPE_STRING);
- }
- }
- /* 'p' is freed */
- static JSValue JS_NewSymbol(JSContext *ctx, JSString *p, int atom_type)
- {
- JSRuntime *rt = ctx->rt;
- JSAtom atom;
- atom = __JS_NewAtom(rt, p, atom_type);
- if (atom == JS_ATOM_NULL)
- return JS_ThrowOutOfMemory(ctx);
- return JS_MKPTR(JS_TAG_SYMBOL, rt->atom_array[atom]);
- }
- /* descr must be a non-numeric string atom */
- static JSValue JS_NewSymbolFromAtom(JSContext *ctx, JSAtom descr,
- int atom_type)
- {
- JSRuntime *rt = ctx->rt;
- JSString *p;
- assert(!__JS_AtomIsTaggedInt(descr));
- assert(descr < rt->atom_size);
- p = rt->atom_array[descr];
- JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
- return JS_NewSymbol(ctx, p, atom_type);
- }
- #define ATOM_GET_STR_BUF_SIZE 64
- /* Should only be used for debug. */
- static const char *JS_AtomGetStrRT(JSRuntime *rt, char *buf, int buf_size,
- JSAtom atom)
- {
- if (__JS_AtomIsTaggedInt(atom)) {
- snprintf(buf, buf_size, "%u", __JS_AtomToUInt32(atom));
- } else {
- JSAtomStruct *p;
- assert(atom < rt->atom_size);
- if (atom == JS_ATOM_NULL) {
- snprintf(buf, buf_size, "<null>");
- } else {
- int i, c;
- char *q;
- JSString *str;
- q = buf;
- p = rt->atom_array[atom];
- assert(!atom_is_free(p));
- str = p;
- if (str) {
- if (!str->is_wide_char) {
- /* special case ASCII strings */
- c = 0;
- for(i = 0; i < str->len; i++) {
- c |= str->u.str8[i];
- }
- if (c < 0x80)
- return (const char *)str->u.str8;
- }
- for(i = 0; i < str->len; i++) {
- c = string_get(str, i);
- if ((q - buf) >= buf_size - UTF8_CHAR_LEN_MAX)
- break;
- if (c < 128) {
- *q++ = c;
- } else {
- q += unicode_to_utf8((uint8_t *)q, c);
- }
- }
- }
- *q = '\0';
- }
- }
- return buf;
- }
- static const char *JS_AtomGetStr(JSContext *ctx, char *buf, int buf_size, JSAtom atom)
- {
- return JS_AtomGetStrRT(ctx->rt, buf, buf_size, atom);
- }
- static JSValue __JS_AtomToValue(JSContext *ctx, JSAtom atom, BOOL force_string)
- {
- char buf[ATOM_GET_STR_BUF_SIZE];
- if (__JS_AtomIsTaggedInt(atom)) {
- size_t len = u32toa(buf, __JS_AtomToUInt32(atom));
- return js_new_string8_len(ctx, buf, len);
- } else {
- JSRuntime *rt = ctx->rt;
- JSAtomStruct *p;
- assert(atom < rt->atom_size);
- p = rt->atom_array[atom];
- if (p->atom_type == JS_ATOM_TYPE_STRING) {
- goto ret_string;
- } else if (force_string) {
- if (p->len == 0 && p->is_wide_char != 0) {
- /* no description string */
- p = rt->atom_array[JS_ATOM_empty_string];
- }
- ret_string:
- return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
- } else {
- return JS_DupValue(ctx, JS_MKPTR(JS_TAG_SYMBOL, p));
- }
- }
- }
- JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom)
- {
- return __JS_AtomToValue(ctx, atom, FALSE);
- }
- JSValue JS_AtomToString(JSContext *ctx, JSAtom atom)
- {
- return __JS_AtomToValue(ctx, atom, TRUE);
- }
- /* return TRUE if the atom is an array index (i.e. 0 <= index <=
- 2^32-2 and return its value */
- static BOOL JS_AtomIsArrayIndex(JSContext *ctx, uint32_t *pval, JSAtom atom)
- {
- if (__JS_AtomIsTaggedInt(atom)) {
- *pval = __JS_AtomToUInt32(atom);
- return TRUE;
- } else {
- JSRuntime *rt = ctx->rt;
- JSAtomStruct *p;
- uint32_t val;
- assert(atom < rt->atom_size);
- p = rt->atom_array[atom];
- if (p->atom_type == JS_ATOM_TYPE_STRING &&
- is_num_string(&val, p) && val != -1) {
- *pval = val;
- return TRUE;
- } else {
- *pval = 0;
- return FALSE;
- }
- }
- }
- /* This test must be fast if atom is not a numeric index (e.g. a
- method name). Return JS_UNDEFINED if not a numeric
- index. JS_EXCEPTION can also be returned. */
- static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom)
- {
- JSRuntime *rt = ctx->rt;
- JSAtomStruct *p1;
- JSString *p;
- int c, ret;
- JSValue num, str;
- if (__JS_AtomIsTaggedInt(atom))
- return JS_NewInt32(ctx, __JS_AtomToUInt32(atom));
- assert(atom < rt->atom_size);
- p1 = rt->atom_array[atom];
- if (p1->atom_type != JS_ATOM_TYPE_STRING)
- return JS_UNDEFINED;
- switch(atom) {
- case JS_ATOM_minus_zero:
- return __JS_NewFloat64(ctx, -0.0);
- case JS_ATOM_Infinity:
- return __JS_NewFloat64(ctx, INFINITY);
- case JS_ATOM_minus_Infinity:
- return __JS_NewFloat64(ctx, -INFINITY);
- case JS_ATOM_NaN:
- return __JS_NewFloat64(ctx, NAN);
- default:
- break;
- }
- p = p1;
- if (p->len == 0)
- return JS_UNDEFINED;
- c = string_get(p, 0);
- if (!is_num(c) && c != '-')
- return JS_UNDEFINED;
- /* this is ECMA CanonicalNumericIndexString primitive */
- num = JS_ToNumber(ctx, JS_MKPTR(JS_TAG_STRING, p));
- if (JS_IsException(num))
- return num;
- str = JS_ToString(ctx, num);
- if (JS_IsException(str)) {
- JS_FreeValue(ctx, num);
- return str;
- }
- ret = js_string_eq(ctx, p, JS_VALUE_GET_STRING(str));
- JS_FreeValue(ctx, str);
- if (ret) {
- return num;
- } else {
- JS_FreeValue(ctx, num);
- return JS_UNDEFINED;
- }
- }
- /* return -1 if exception or TRUE/FALSE */
- static int JS_AtomIsNumericIndex(JSContext *ctx, JSAtom atom)
- {
- JSValue num;
- num = JS_AtomIsNumericIndex1(ctx, atom);
- if (likely(JS_IsUndefined(num)))
- return FALSE;
- if (JS_IsException(num))
- return -1;
- JS_FreeValue(ctx, num);
- return TRUE;
- }
- void JS_FreeAtom(JSContext *ctx, JSAtom v)
- {
- if (!__JS_AtomIsConst(v))
- __JS_FreeAtom(ctx->rt, v);
- }
- void JS_FreeAtomRT(JSRuntime *rt, JSAtom v)
- {
- if (!__JS_AtomIsConst(v))
- __JS_FreeAtom(rt, v);
- }
- /* return TRUE if 'v' is a symbol with a string description */
- static BOOL JS_AtomSymbolHasDescription(JSContext *ctx, JSAtom v)
- {
- JSRuntime *rt;
- JSAtomStruct *p;
- rt = ctx->rt;
- if (__JS_AtomIsTaggedInt(v))
- return FALSE;
- p = rt->atom_array[v];
- return (((p->atom_type == JS_ATOM_TYPE_SYMBOL &&
- p->hash != JS_ATOM_HASH_PRIVATE) ||
- p->atom_type == JS_ATOM_TYPE_GLOBAL_SYMBOL) &&
- !(p->len == 0 && p->is_wide_char != 0));
- }
- /* free with JS_FreeCString() */
- const char *JS_AtomToCStringLen(JSContext *ctx, size_t *plen, JSAtom atom)
- {
- JSValue str;
- const char *cstr;
- str = JS_AtomToString(ctx, atom);
- if (JS_IsException(str)) {
- if (plen)
- *plen = 0;
- return NULL;
- }
- cstr = JS_ToCStringLen(ctx, plen, str);
- JS_FreeValue(ctx, str);
- return cstr;
- }
- /* return a string atom containing name concatenated with str1 */
- static JSAtom js_atom_concat_str(JSContext *ctx, JSAtom name, const char *str1)
- {
- JSValue str;
- JSAtom atom;
- const char *cstr;
- char *cstr2;
- size_t len, len1;
- str = JS_AtomToString(ctx, name);
- if (JS_IsException(str))
- return JS_ATOM_NULL;
- cstr = JS_ToCStringLen(ctx, &len, str);
- if (!cstr)
- goto fail;
- len1 = strlen(str1);
- cstr2 = js_malloc(ctx, len + len1 + 1);
- if (!cstr2)
- goto fail;
- memcpy(cstr2, cstr, len);
- memcpy(cstr2 + len, str1, len1);
- cstr2[len + len1] = '\0';
- atom = JS_NewAtomLen(ctx, cstr2, len + len1);
- js_free(ctx, cstr2);
- JS_FreeCString(ctx, cstr);
- JS_FreeValue(ctx, str);
- return atom;
- fail:
- JS_FreeCString(ctx, cstr);
- JS_FreeValue(ctx, str);
- return JS_ATOM_NULL;
- }
- static JSAtom js_atom_concat_num(JSContext *ctx, JSAtom name, uint32_t n)
- {
- char buf[16];
- size_t len;
- len = u32toa(buf, n);
- buf[len] = '\0';
- return js_atom_concat_str(ctx, name, buf);
- }
- static inline BOOL JS_IsEmptyString(JSValueConst v)
- {
- return JS_VALUE_GET_TAG(v) == JS_TAG_STRING && JS_VALUE_GET_STRING(v)->len == 0;
- }
- /* JSClass support */
- #ifdef CONFIG_ATOMICS
- static pthread_mutex_t js_class_id_mutex = PTHREAD_MUTEX_INITIALIZER;
- #endif
- /* a new class ID is allocated if *pclass_id != 0 */
- JSClassID JS_NewClassID(JSClassID *pclass_id)
- {
- JSClassID class_id;
- #ifdef CONFIG_ATOMICS
- pthread_mutex_lock(&js_class_id_mutex);
- #endif
- class_id = *pclass_id;
- if (class_id == 0) {
- class_id = js_class_id_alloc++;
- *pclass_id = class_id;
- }
- #ifdef CONFIG_ATOMICS
- pthread_mutex_unlock(&js_class_id_mutex);
- #endif
- return class_id;
- }
- JSClassID JS_GetClassID(JSValue v)
- {
- JSObject *p;
- if (JS_VALUE_GET_TAG(v) != JS_TAG_OBJECT)
- return JS_INVALID_CLASS_ID;
- p = JS_VALUE_GET_OBJ(v);
- return p->class_id;
- }
- BOOL JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id)
- {
- return (class_id < rt->class_count &&
- rt->class_array[class_id].class_id != 0);
- }
- /* create a new object internal class. Return -1 if error, 0 if
- OK. The finalizer can be NULL if none is needed. */
- static int JS_NewClass1(JSRuntime *rt, JSClassID class_id,
- const JSClassDef *class_def, JSAtom name)
- {
- int new_size, i;
- JSClass *cl, *new_class_array;
- struct list_head *el;
- if (class_id >= (1 << 16))
- return -1;
- if (class_id < rt->class_count &&
- rt->class_array[class_id].class_id != 0)
- return -1;
- if (class_id >= rt->class_count) {
- new_size = max_int(JS_CLASS_INIT_COUNT,
- max_int(class_id + 1, rt->class_count * 3 / 2));
- /* reallocate the context class prototype array, if any */
- list_for_each(el, &rt->context_list) {
- JSContext *ctx = list_entry(el, JSContext, link);
- JSValue *new_tab;
- new_tab = js_realloc_rt(rt, ctx->class_proto,
- sizeof(ctx->class_proto[0]) * new_size);
- if (!new_tab)
- return -1;
- for(i = rt->class_count; i < new_size; i++)
- new_tab[i] = JS_NULL;
- ctx->class_proto = new_tab;
- }
- /* reallocate the class array */
- new_class_array = js_realloc_rt(rt, rt->class_array,
- sizeof(JSClass) * new_size);
- if (!new_class_array)
- return -1;
- memset(new_class_array + rt->class_count, 0,
- (new_size - rt->class_count) * sizeof(JSClass));
- rt->class_array = new_class_array;
- rt->class_count = new_size;
- }
- cl = &rt->class_array[class_id];
- cl->class_id = class_id;
- cl->class_name = JS_DupAtomRT(rt, name);
- cl->finalizer = class_def->finalizer;
- cl->gc_mark = class_def->gc_mark;
- cl->call = class_def->call;
- cl->exotic = class_def->exotic;
- return 0;
- }
- int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def)
- {
- int ret, len;
- JSAtom name;
- len = strlen(class_def->class_name);
- name = __JS_FindAtom(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING);
- if (name == JS_ATOM_NULL) {
- name = __JS_NewAtomInit(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING);
- if (name == JS_ATOM_NULL)
- return -1;
- }
- ret = JS_NewClass1(rt, class_id, class_def, name);
- JS_FreeAtomRT(rt, name);
- return ret;
- }
- static JSValue js_new_string8_len(JSContext *ctx, const char *buf, int len)
- {
- JSString *str;
- if (len <= 0) {
- return JS_AtomToString(ctx, JS_ATOM_empty_string);
- }
- str = js_alloc_string(ctx, len, 0);
- if (!str)
- return JS_EXCEPTION;
- memcpy(str->u.str8, buf, len);
- str->u.str8[len] = '\0';
- return JS_MKPTR(JS_TAG_STRING, str);
- }
- static JSValue js_new_string8(JSContext *ctx, const char *buf)
- {
- return js_new_string8_len(ctx, buf, strlen(buf));
- }
- static JSValue js_new_string16_len(JSContext *ctx, const uint16_t *buf, int len)
- {
- JSString *str;
- str = js_alloc_string(ctx, len, 1);
- if (!str)
- return JS_EXCEPTION;
- memcpy(str->u.str16, buf, len * 2);
- return JS_MKPTR(JS_TAG_STRING, str);
- }
- static JSValue js_new_string_char(JSContext *ctx, uint16_t c)
- {
- if (c < 0x100) {
- uint8_t ch8 = c;
- return js_new_string8_len(ctx, (const char *)&ch8, 1);
- } else {
- uint16_t ch16 = c;
- return js_new_string16_len(ctx, &ch16, 1);
- }
- }
- static JSValue js_sub_string(JSContext *ctx, JSString *p, int start, int end)
- {
- int len = end - start;
- if (start == 0 && end == p->len) {
- return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
- }
- if (p->is_wide_char && len > 0) {
- JSString *str;
- int i;
- uint16_t c = 0;
- for (i = start; i < end; i++) {
- c |= p->u.str16[i];
- }
- if (c > 0xFF)
- return js_new_string16_len(ctx, p->u.str16 + start, len);
- str = js_alloc_string(ctx, len, 0);
- if (!str)
- return JS_EXCEPTION;
- for (i = 0; i < len; i++) {
- str->u.str8[i] = p->u.str16[start + i];
- }
- str->u.str8[len] = '\0';
- return JS_MKPTR(JS_TAG_STRING, str);
- } else {
- return js_new_string8_len(ctx, (const char *)(p->u.str8 + start), len);
- }
- }
- typedef struct StringBuffer {
- JSContext *ctx;
- JSString *str;
- int len;
- int size;
- int is_wide_char;
- int error_status;
- } StringBuffer;
- /* It is valid to call string_buffer_end() and all string_buffer functions even
- if string_buffer_init() or another string_buffer function returns an error.
- If the error_status is set, string_buffer_end() returns JS_EXCEPTION.
- */
- static int string_buffer_init2(JSContext *ctx, StringBuffer *s, int size,
- int is_wide)
- {
- s->ctx = ctx;
- s->size = size;
- s->len = 0;
- s->is_wide_char = is_wide;
- s->error_status = 0;
- s->str = js_alloc_string(ctx, size, is_wide);
- if (unlikely(!s->str)) {
- s->size = 0;
- return s->error_status = -1;
- }
- #ifdef DUMP_LEAKS
- /* the StringBuffer may reallocate the JSString, only link it at the end */
- list_del(&s->str->link);
- #endif
- return 0;
- }
- static inline int string_buffer_init(JSContext *ctx, StringBuffer *s, int size)
- {
- return string_buffer_init2(ctx, s, size, 0);
- }
- static void string_buffer_free(StringBuffer *s)
- {
- js_free(s->ctx, s->str);
- s->str = NULL;
- }
- static int string_buffer_set_error(StringBuffer *s)
- {
- js_free(s->ctx, s->str);
- s->str = NULL;
- s->size = 0;
- s->len = 0;
- return s->error_status = -1;
- }
- static no_inline int string_buffer_widen(StringBuffer *s, int size)
- {
- JSString *str;
- size_t slack;
- int i;
- if (s->error_status)
- return -1;
- str = js_realloc2(s->ctx, s->str, sizeof(JSString) + (size << 1), &slack);
- if (!str)
- return string_buffer_set_error(s);
- size += slack >> 1;
- for(i = s->len; i-- > 0;) {
- str->u.str16[i] = str->u.str8[i];
- }
- s->is_wide_char = 1;
- s->size = size;
- s->str = str;
- return 0;
- }
- static no_inline int string_buffer_realloc(StringBuffer *s, int new_len, int c)
- {
- JSString *new_str;
- int new_size;
- size_t new_size_bytes, slack;
- if (s->error_status)
- return -1;
- if (new_len > JS_STRING_LEN_MAX) {
- JS_ThrowInternalError(s->ctx, "string too long");
- return string_buffer_set_error(s);
- }
- new_size = min_int(max_int(new_len, s->size * 3 / 2), JS_STRING_LEN_MAX);
- if (!s->is_wide_char && c >= 0x100) {
- return string_buffer_widen(s, new_size);
- }
- new_size_bytes = sizeof(JSString) + (new_size << s->is_wide_char) + 1 - s->is_wide_char;
- new_str = js_realloc2(s->ctx, s->str, new_size_bytes, &slack);
- if (!new_str)
- return string_buffer_set_error(s);
- new_size = min_int(new_size + (slack >> s->is_wide_char), JS_STRING_LEN_MAX);
- s->size = new_size;
- s->str = new_str;
- return 0;
- }
- static no_inline int string_buffer_putc16_slow(StringBuffer *s, uint32_t c)
- {
- if (unlikely(s->len >= s->size)) {
- if (string_buffer_realloc(s, s->len + 1, c))
- return -1;
- }
- if (s->is_wide_char) {
- s->str->u.str16[s->len++] = c;
- } else if (c < 0x100) {
- s->str->u.str8[s->len++] = c;
- } else {
- if (string_buffer_widen(s, s->size))
- return -1;
- s->str->u.str16[s->len++] = c;
- }
- return 0;
- }
- /* 0 <= c <= 0xff */
- static int string_buffer_putc8(StringBuffer *s, uint32_t c)
- {
- if (unlikely(s->len >= s->size)) {
- if (string_buffer_realloc(s, s->len + 1, c))
- return -1;
- }
- if (s->is_wide_char) {
- s->str->u.str16[s->len++] = c;
- } else {
- s->str->u.str8[s->len++] = c;
- }
- return 0;
- }
- /* 0 <= c <= 0xffff */
- static int string_buffer_putc16(StringBuffer *s, uint32_t c)
- {
- if (likely(s->len < s->size)) {
- if (s->is_wide_char) {
- s->str->u.str16[s->len++] = c;
- return 0;
- } else if (c < 0x100) {
- s->str->u.str8[s->len++] = c;
- return 0;
- }
- }
- return string_buffer_putc16_slow(s, c);
- }
- static int string_buffer_putc_slow(StringBuffer *s, uint32_t c)
- {
- if (unlikely(c >= 0x10000)) {
- /* surrogate pair */
- if (string_buffer_putc16(s, get_hi_surrogate(c)))
- return -1;
- c = get_lo_surrogate(c);
- }
- return string_buffer_putc16(s, c);
- }
- /* 0 <= c <= 0x10ffff */
- static inline int string_buffer_putc(StringBuffer *s, uint32_t c)
- {
- if (likely(s->len < s->size)) {
- if (s->is_wide_char) {
- if (c < 0x10000) {
- s->str->u.str16[s->len++] = c;
- return 0;
- } else if (likely((s->len + 1) < s->size)) {
- s->str->u.str16[s->len++] = get_hi_surrogate(c);
- s->str->u.str16[s->len++] = get_lo_surrogate(c);
- return 0;
- }
- } else if (c < 0x100) {
- s->str->u.str8[s->len++] = c;
- return 0;
- }
- }
- return string_buffer_putc_slow(s, c);
- }
- static int string_getc(const JSString *p, int *pidx)
- {
- int idx, c, c1;
- idx = *pidx;
- if (p->is_wide_char) {
- c = p->u.str16[idx++];
- if (is_hi_surrogate(c) && idx < p->len) {
- c1 = p->u.str16[idx];
- if (is_lo_surrogate(c1)) {
- c = from_surrogate(c, c1);
- idx++;
- }
- }
- } else {
- c = p->u.str8[idx++];
- }
- *pidx = idx;
- return c;
- }
- static int string_buffer_write8(StringBuffer *s, const uint8_t *p, int len)
- {
- int i;
- if (s->len + len > s->size) {
- if (string_buffer_realloc(s, s->len + len, 0))
- return -1;
- }
- if (s->is_wide_char) {
- for (i = 0; i < len; i++) {
- s->str->u.str16[s->len + i] = p[i];
- }
- s->len += len;
- } else {
- memcpy(&s->str->u.str8[s->len], p, len);
- s->len += len;
- }
- return 0;
- }
- static int string_buffer_write16(StringBuffer *s, const uint16_t *p, int len)
- {
- int c = 0, i;
- for (i = 0; i < len; i++) {
- c |= p[i];
- }
- if (s->len + len > s->size) {
- if (string_buffer_realloc(s, s->len + len, c))
- return -1;
- } else if (!s->is_wide_char && c >= 0x100) {
- if (string_buffer_widen(s, s->size))
- return -1;
- }
- if (s->is_wide_char) {
- memcpy(&s->str->u.str16[s->len], p, len << 1);
- s->len += len;
- } else {
- for (i = 0; i < len; i++) {
- s->str->u.str8[s->len + i] = p[i];
- }
- s->len += len;
- }
- return 0;
- }
- /* appending an ASCII string */
- static int string_buffer_puts8(StringBuffer *s, const char *str)
- {
- return string_buffer_write8(s, (const uint8_t *)str, strlen(str));
- }
- static int string_buffer_concat(StringBuffer *s, const JSString *p,
- uint32_t from, uint32_t to)
- {
- if (to <= from)
- return 0;
- if (p->is_wide_char)
- return string_buffer_write16(s, p->u.str16 + from, to - from);
- else
- return string_buffer_write8(s, p->u.str8 + from, to - from);
- }
- static int string_buffer_concat_value(StringBuffer *s, JSValueConst v)
- {
- JSString *p;
- JSValue v1;
- int res;
- if (s->error_status) {
- /* prevent exception overload */
- return -1;
- }
- if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) {
- if (JS_VALUE_GET_TAG(v) == JS_TAG_STRING_ROPE) {
- JSStringRope *r = JS_VALUE_GET_STRING_ROPE(v);
- /* recursion is acceptable because the rope depth is bounded */
- if (string_buffer_concat_value(s, r->left))
- return -1;
- return string_buffer_concat_value(s, r->right);
- } else {
- v1 = JS_ToString(s->ctx, v);
- if (JS_IsException(v1))
- return string_buffer_set_error(s);
- p = JS_VALUE_GET_STRING(v1);
- res = string_buffer_concat(s, p, 0, p->len);
- JS_FreeValue(s->ctx, v1);
- return res;
- }
- }
- p = JS_VALUE_GET_STRING(v);
- return string_buffer_concat(s, p, 0, p->len);
- }
- static int string_buffer_concat_value_free(StringBuffer *s, JSValue v)
- {
- JSString *p;
- int res;
- if (s->error_status) {
- /* prevent exception overload */
- JS_FreeValue(s->ctx, v);
- return -1;
- }
- if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) {
- v = JS_ToStringFree(s->ctx, v);
- if (JS_IsException(v))
- return string_buffer_set_error(s);
- }
- p = JS_VALUE_GET_STRING(v);
- res = string_buffer_concat(s, p, 0, p->len);
- JS_FreeValue(s->ctx, v);
- return res;
- }
- static int string_buffer_fill(StringBuffer *s, int c, int count)
- {
- /* XXX: optimize */
- if (s->len + count > s->size) {
- if (string_buffer_realloc(s, s->len + count, c))
- return -1;
- }
- while (count-- > 0) {
- if (string_buffer_putc16(s, c))
- return -1;
- }
- return 0;
- }
- static JSValue string_buffer_end(StringBuffer *s)
- {
- JSString *str;
- str = s->str;
- if (s->error_status)
- return JS_EXCEPTION;
- if (s->len == 0) {
- js_free(s->ctx, str);
- s->str = NULL;
- return JS_AtomToString(s->ctx, JS_ATOM_empty_string);
- }
- if (s->len < s->size) {
- /* smaller size so js_realloc should not fail, but OK if it does */
- /* XXX: should add some slack to avoid unnecessary calls */
- /* XXX: might need to use malloc+free to ensure smaller size */
- str = js_realloc_rt(s->ctx->rt, str, sizeof(JSString) +
- (s->len << s->is_wide_char) + 1 - s->is_wide_char);
- if (str == NULL)
- str = s->str;
- s->str = str;
- }
- if (!s->is_wide_char)
- str->u.str8[s->len] = 0;
- #ifdef DUMP_LEAKS
- list_add_tail(&str->link, &s->ctx->rt->string_list);
- #endif
- str->is_wide_char = s->is_wide_char;
- str->len = s->len;
- s->str = NULL;
- return JS_MKPTR(JS_TAG_STRING, str);
- }
- /* create a string from a UTF-8 buffer */
- JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len)
- {
- const uint8_t *p, *p_end, *p_start, *p_next;
- uint32_t c;
- StringBuffer b_s, *b = &b_s;
- size_t len1;
- p_start = (const uint8_t *)buf;
- p_end = p_start + buf_len;
- len1 = count_ascii(p_start, buf_len);
- p = p_start + len1;
- if (len1 > JS_STRING_LEN_MAX)
- return JS_ThrowInternalError(ctx, "string too long");
- if (p == p_end) {
- /* ASCII string */
- return js_new_string8_len(ctx, buf, buf_len);
- } else {
- if (string_buffer_init(ctx, b, buf_len))
- goto fail;
- string_buffer_write8(b, p_start, len1);
- while (p < p_end) {
- if (*p < 128) {
- string_buffer_putc8(b, *p++);
- } else {
- /* parse utf-8 sequence, return 0xFFFFFFFF for error */
- c = unicode_from_utf8(p, p_end - p, &p_next);
- if (c < 0x10000) {
- p = p_next;
- } else if (c <= 0x10FFFF) {
- p = p_next;
- /* surrogate pair */
- string_buffer_putc16(b, get_hi_surrogate(c));
- c = get_lo_surrogate(c);
- } else {
- /* invalid char */
- c = 0xfffd;
- /* skip the invalid chars */
- /* XXX: seems incorrect. Why not just use c = *p++; ? */
- while (p < p_end && (*p >= 0x80 && *p < 0xc0))
- p++;
- if (p < p_end) {
- p++;
- while (p < p_end && (*p >= 0x80 && *p < 0xc0))
- p++;
- }
- }
- string_buffer_putc16(b, c);
- }
- }
- }
- return string_buffer_end(b);
- fail:
- string_buffer_free(b);
- return JS_EXCEPTION;
- }
- static JSValue JS_ConcatString3(JSContext *ctx, const char *str1,
- JSValue str2, const char *str3)
- {
- StringBuffer b_s, *b = &b_s;
- int len1, len3;
- JSString *p;
- if (unlikely(JS_VALUE_GET_TAG(str2) != JS_TAG_STRING)) {
- str2 = JS_ToStringFree(ctx, str2);
- if (JS_IsException(str2))
- goto fail;
- }
- p = JS_VALUE_GET_STRING(str2);
- len1 = strlen(str1);
- len3 = strlen(str3);
- if (string_buffer_init2(ctx, b, len1 + p->len + len3, p->is_wide_char))
- goto fail;
- string_buffer_write8(b, (const uint8_t *)str1, len1);
- string_buffer_concat(b, p, 0, p->len);
- string_buffer_write8(b, (const uint8_t *)str3, len3);
- JS_FreeValue(ctx, str2);
- return string_buffer_end(b);
- fail:
- JS_FreeValue(ctx, str2);
- return JS_EXCEPTION;
- }
- JSValue JS_NewAtomString(JSContext *ctx, const char *str)
- {
- JSAtom atom = JS_NewAtom(ctx, str);
- if (atom == JS_ATOM_NULL)
- return JS_EXCEPTION;
- JSValue val = JS_AtomToString(ctx, atom);
- JS_FreeAtom(ctx, atom);
- return val;
- }
- /* return (NULL, 0) if exception. */
- /* return pointer into a JSString with a live ref_count */
- /* cesu8 determines if non-BMP1 codepoints are encoded as 1 or 2 utf-8 sequences */
- const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, BOOL cesu8)
- {
- JSValue val;
- JSString *str, *str_new;
- int pos, len, c, c1;
- uint8_t *q;
- if (JS_VALUE_GET_TAG(val1) != JS_TAG_STRING) {
- val = JS_ToString(ctx, val1);
- if (JS_IsException(val))
- goto fail;
- } else {
- val = JS_DupValue(ctx, val1);
- }
- str = JS_VALUE_GET_STRING(val);
- len = str->len;
- if (!str->is_wide_char) {
- const uint8_t *src = str->u.str8;
- int count;
- /* count the number of non-ASCII characters */
- /* Scanning the whole string is required for ASCII strings,
- and computing the number of non-ASCII bytes is less expensive
- than testing each byte, hence this method is faster for ASCII
- strings, which is the most common case.
- */
- count = 0;
- for (pos = 0; pos < len; pos++) {
- count += src[pos] >> 7;
- }
- if (count == 0) {
- if (plen)
- *plen = len;
- return (const char *)src;
- }
- str_new = js_alloc_string(ctx, len + count, 0);
- if (!str_new)
- goto fail;
- q = str_new->u.str8;
- for (pos = 0; pos < len; pos++) {
- c = src[pos];
- if (c < 0x80) {
- *q++ = c;
- } else {
- *q++ = (c >> 6) | 0xc0;
- *q++ = (c & 0x3f) | 0x80;
- }
- }
- } else {
- const uint16_t *src = str->u.str16;
- /* Allocate 3 bytes per 16 bit code point. Surrogate pairs may
- produce 4 bytes but use 2 code points.
- */
- str_new = js_alloc_string(ctx, len * 3, 0);
- if (!str_new)
- goto fail;
- q = str_new->u.str8;
- pos = 0;
- while (pos < len) {
- c = src[pos++];
- if (c < 0x80) {
- *q++ = c;
- } else {
- if (is_hi_surrogate(c)) {
- if (pos < len && !cesu8) {
- c1 = src[pos];
- if (is_lo_surrogate(c1)) {
- pos++;
- c = from_surrogate(c, c1);
- } else {
- /* Keep unmatched surrogate code points */
- /* c = 0xfffd; */ /* error */
- }
- } else {
- /* Keep unmatched surrogate code points */
- /* c = 0xfffd; */ /* error */
- }
- }
- q += unicode_to_utf8(q, c);
- }
- }
- }
- *q = '\0';
- str_new->len = q - str_new->u.str8;
- JS_FreeValue(ctx, val);
- if (plen)
- *plen = str_new->len;
- return (const char *)str_new->u.str8;
- fail:
- if (plen)
- *plen = 0;
- return NULL;
- }
- void JS_FreeCString(JSContext *ctx, const char *ptr)
- {
- JSString *p;
- if (!ptr)
- return;
- /* purposely removing constness */
- p = container_of(ptr, JSString, u);
- JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
- }
- static int memcmp16_8(const uint16_t *src1, const uint8_t *src2, int len)
- {
- int c, i;
- for(i = 0; i < len; i++) {
- c = src1[i] - src2[i];
- if (c != 0)
- return c;
- }
- return 0;
- }
- static int memcmp16(const uint16_t *src1, const uint16_t *src2, int len)
- {
- int c, i;
- for(i = 0; i < len; i++) {
- c = src1[i] - src2[i];
- if (c != 0)
- return c;
- }
- return 0;
- }
- static int js_string_memcmp(const JSString *p1, int pos1, const JSString *p2,
- int pos2, int len)
- {
- int res;
- if (likely(!p1->is_wide_char)) {
- if (likely(!p2->is_wide_char))
- res = memcmp(p1->u.str8 + pos1, p2->u.str8 + pos2, len);
- else
- res = -memcmp16_8(p2->u.str16 + pos2, p1->u.str8 + pos1, len);
- } else {
- if (!p2->is_wide_char)
- res = memcmp16_8(p1->u.str16 + pos1, p2->u.str8 + pos2, len);
- else
- res = memcmp16(p1->u.str16 + pos1, p2->u.str16 + pos2, len);
- }
- return res;
- }
- static BOOL js_string_eq(JSContext *ctx,
- const JSString *p1, const JSString *p2)
- {
- if (p1->len != p2->len)
- return FALSE;
- if (p1 == p2)
- return TRUE;
- return js_string_memcmp(p1, 0, p2, 0, p1->len) == 0;
- }
- /* return < 0, 0 or > 0 */
- static int js_string_compare(JSContext *ctx,
- const JSString *p1, const JSString *p2)
- {
- int res, len;
- len = min_int(p1->len, p2->len);
- res = js_string_memcmp(p1, 0, p2, 0, len);
- if (res == 0) {
- if (p1->len == p2->len)
- res = 0;
- else if (p1->len < p2->len)
- res = -1;
- else
- res = 1;
- }
- return res;
- }
- static void copy_str16(uint16_t *dst, const JSString *p, int offset, int len)
- {
- if (p->is_wide_char) {
- memcpy(dst, p->u.str16 + offset, len * 2);
- } else {
- const uint8_t *src1 = p->u.str8 + offset;
- int i;
- for(i = 0; i < len; i++)
- dst[i] = src1[i];
- }
- }
- static JSValue JS_ConcatString1(JSContext *ctx,
- const JSString *p1, const JSString *p2)
- {
- JSString *p;
- uint32_t len;
- int is_wide_char;
- len = p1->len + p2->len;
- if (len > JS_STRING_LEN_MAX)
- return JS_ThrowInternalError(ctx, "string too long");
- is_wide_char = p1->is_wide_char | p2->is_wide_char;
- p = js_alloc_string(ctx, len, is_wide_char);
- if (!p)
- return JS_EXCEPTION;
- if (!is_wide_char) {
- memcpy(p->u.str8, p1->u.str8, p1->len);
- memcpy(p->u.str8 + p1->len, p2->u.str8, p2->len);
- p->u.str8[len] = '\0';
- } else {
- copy_str16(p->u.str16, p1, 0, p1->len);
- copy_str16(p->u.str16 + p1->len, p2, 0, p2->len);
- }
- return JS_MKPTR(JS_TAG_STRING, p);
- }
- static BOOL JS_ConcatStringInPlace(JSContext *ctx, JSString *p1, JSValueConst op2) {
- if (JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) {
- JSString *p2 = JS_VALUE_GET_STRING(op2);
- size_t size1;
- if (p2->len == 0)
- return TRUE;
- if (js_rc(p1)->ref_count != 1)
- return FALSE;
- size1 = js_malloc_usable_size(ctx, p1);
- if (p1->is_wide_char) {
- if (size1 >= sizeof(*p1) + ((p1->len + p2->len) << 1)) {
- if (p2->is_wide_char) {
- memcpy(p1->u.str16 + p1->len, p2->u.str16, p2->len << 1);
- p1->len += p2->len;
- return TRUE;
- } else {
- size_t i;
- for (i = 0; i < p2->len; i++) {
- p1->u.str16[p1->len++] = p2->u.str8[i];
- }
- return TRUE;
- }
- }
- } else if (!p2->is_wide_char) {
- if (size1 >= sizeof(*p1) + p1->len + p2->len + 1) {
- memcpy(p1->u.str8 + p1->len, p2->u.str8, p2->len);
- p1->len += p2->len;
- p1->u.str8[p1->len] = '\0';
- return TRUE;
- }
- }
- }
- return FALSE;
- }
- static JSValue JS_ConcatString2(JSContext *ctx, JSValue op1, JSValue op2)
- {
- JSValue ret;
- JSString *p1, *p2;
- p1 = JS_VALUE_GET_STRING(op1);
- if (JS_ConcatStringInPlace(ctx, p1, op2)) {
- JS_FreeValue(ctx, op2);
- return op1;
- }
- p2 = JS_VALUE_GET_STRING(op2);
- ret = JS_ConcatString1(ctx, p1, p2);
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- return ret;
- }
- /* Return the character at position 'idx'. 'val' must be a string or rope */
- static int string_rope_get(JSValueConst val, uint32_t idx)
- {
- if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) {
- return string_get(JS_VALUE_GET_STRING(val), idx);
- } else {
- JSStringRope *r = JS_VALUE_GET_STRING_ROPE(val);
- uint32_t len;
- if (JS_VALUE_GET_TAG(r->left) == JS_TAG_STRING)
- len = JS_VALUE_GET_STRING(r->left)->len;
- else
- len = JS_VALUE_GET_STRING_ROPE(r->left)->len;
- if (idx < len)
- return string_rope_get(r->left, idx);
- else
- return string_rope_get(r->right, idx - len);
- }
- }
- typedef struct {
- JSValueConst stack[JS_STRING_ROPE_MAX_DEPTH];
- int stack_len;
- } JSStringRopeIter;
- static void string_rope_iter_init(JSStringRopeIter *s, JSValueConst val)
- {
- s->stack_len = 0;
- s->stack[s->stack_len++] = val;
- }
- /* iterate thru a rope and return the strings in order */
- static JSString *string_rope_iter_next(JSStringRopeIter *s)
- {
- JSValueConst val;
- JSStringRope *r;
- if (s->stack_len == 0)
- return NULL;
- val = s->stack[--s->stack_len];
- for(;;) {
- if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING)
- return JS_VALUE_GET_STRING(val);
- r = JS_VALUE_GET_STRING_ROPE(val);
- assert(s->stack_len < JS_STRING_ROPE_MAX_DEPTH);
- s->stack[s->stack_len++] = r->right;
- val = r->left;
- }
- }
- static uint32_t string_rope_get_len(JSValueConst val)
- {
- if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING)
- return JS_VALUE_GET_STRING(val)->len;
- else
- return JS_VALUE_GET_STRING_ROPE(val)->len;
- }
- static int js_string_rope_compare(JSContext *ctx, JSValueConst op1,
- JSValueConst op2, BOOL eq_only)
- {
- uint32_t len1, len2, len, pos1, pos2, l;
- int res;
- JSStringRopeIter it1, it2;
- JSString *p1, *p2;
-
- len1 = string_rope_get_len(op1);
- len2 = string_rope_get_len(op2);
- /* no need to go further for equality test if
- different length */
- if (eq_only && len1 != len2)
- return 1;
- len = min_uint32(len1, len2);
- string_rope_iter_init(&it1, op1);
- string_rope_iter_init(&it2, op2);
- p1 = string_rope_iter_next(&it1);
- p2 = string_rope_iter_next(&it2);
- pos1 = 0;
- pos2 = 0;
- while (len != 0) {
- l = min_uint32(p1->len - pos1, p2->len - pos2);
- l = min_uint32(l, len);
- res = js_string_memcmp(p1, pos1, p2, pos2, l);
- if (res != 0)
- return res;
- len -= l;
- pos1 += l;
- if (pos1 >= p1->len) {
- p1 = string_rope_iter_next(&it1);
- pos1 = 0;
- }
- pos2 += l;
- if (pos2 >= p2->len) {
- p2 = string_rope_iter_next(&it2);
- pos2 = 0;
- }
- }
- if (len1 == len2)
- res = 0;
- else if (len1 < len2)
- res = -1;
- else
- res = 1;
- return res;
- }
- /* 'rope' must be a rope. return a string and modify the rope so that
- it won't need to be linearized again. */
- static JSValue js_linearize_string_rope(JSContext *ctx, JSValue rope)
- {
- StringBuffer b_s, *b = &b_s;
- JSStringRope *r;
- JSValue ret;
-
- r = JS_VALUE_GET_STRING_ROPE(rope);
- /* check whether it is already linearized */
- if (JS_VALUE_GET_TAG(r->right) == JS_TAG_STRING &&
- JS_VALUE_GET_STRING(r->right)->len == 0) {
- ret = JS_DupValue(ctx, r->left);
- JS_FreeValue(ctx, rope);
- return ret;
- }
- if (string_buffer_init2(ctx, b, r->len, r->is_wide_char))
- goto fail;
- if (string_buffer_concat_value(b, rope))
- goto fail;
- ret = string_buffer_end(b);
- if (js_rc(r)->ref_count > 1) {
- /* update the rope so that it won't need to be linearized again */
- JS_FreeValue(ctx, r->left);
- JS_FreeValue(ctx, r->right);
- r->left = JS_DupValue(ctx, ret);
- r->right = JS_AtomToString(ctx, JS_ATOM_empty_string);
- }
- JS_FreeValue(ctx, rope);
- return ret;
- fail:
- JS_FreeValue(ctx, rope);
- return JS_EXCEPTION;
- }
- static JSValue js_rebalancee_string_rope(JSContext *ctx, JSValueConst rope);
- /* op1 and op2 must be strings or string ropes */
- static JSValue js_new_string_rope(JSContext *ctx, JSValue op1, JSValue op2)
- {
- uint32_t len;
- int is_wide_char, depth;
- JSStringRope *r;
- JSValue res;
-
- if (JS_VALUE_GET_TAG(op1) == JS_TAG_STRING) {
- JSString *p1 = JS_VALUE_GET_STRING(op1);
- len = p1->len;
- is_wide_char = p1->is_wide_char;
- depth = 0;
- } else {
- JSStringRope *r1 = JS_VALUE_GET_STRING_ROPE(op1);
- len = r1->len;
- is_wide_char = r1->is_wide_char;
- depth = r1->depth;
- }
- if (JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) {
- JSString *p2 = JS_VALUE_GET_STRING(op2);
- len += p2->len;
- is_wide_char |= p2->is_wide_char;
- } else {
- JSStringRope *r2 = JS_VALUE_GET_STRING_ROPE(op2);
- len += r2->len;
- is_wide_char |= r2->is_wide_char;
- depth = max_int(depth, r2->depth);
- }
- if (len > JS_STRING_LEN_MAX) {
- JS_ThrowInternalError(ctx, "string too long");
- goto fail;
- }
- r = js_malloc(ctx, sizeof(*r));
- if (!r)
- goto fail;
- js_rc(r)->ref_count = 1;
- r->len = len;
- r->is_wide_char = is_wide_char;
- r->depth = depth + 1;
- r->left = op1;
- r->right = op2;
- res = JS_MKPTR(JS_TAG_STRING_ROPE, r);
- if (r->depth > JS_STRING_ROPE_MAX_DEPTH) {
- JSValue res2;
- #ifdef DUMP_ROPE_REBALANCE
- printf("rebalance: initial depth=%d\n", r->depth);
- #endif
- res2 = js_rebalancee_string_rope(ctx, res);
- #ifdef DUMP_ROPE_REBALANCE
- if (JS_VALUE_GET_TAG(res2) == JS_TAG_STRING_ROPE)
- printf("rebalance: final depth=%d\n", JS_VALUE_GET_STRING_ROPE(res2)->depth);
- #endif
- JS_FreeValue(ctx, res);
- return res2;
- } else {
- return res;
- }
- fail:
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- return JS_EXCEPTION;
- }
- #define ROPE_N_BUCKETS 44
- /* Fibonacii numbers starting from F_2 */
- static const uint32_t rope_bucket_len[ROPE_N_BUCKETS] = {
- 1, 2, 3, 5,
- 8, 13, 21, 34,
- 55, 89, 144, 233,
- 377, 610, 987, 1597,
- 2584, 4181, 6765, 10946,
- 17711, 28657, 46368, 75025,
- 121393, 196418, 317811, 514229,
- 832040, 1346269, 2178309, 3524578,
- 5702887, 9227465, 14930352, 24157817,
- 39088169, 63245986, 102334155, 165580141,
- 267914296, 433494437, 701408733, 1134903170, /* > JS_STRING_LEN_MAX */
- };
- static int js_rebalancee_string_rope_rec(JSContext *ctx, JSValue *buckets,
- JSValueConst val)
- {
- if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) {
- JSString *p = JS_VALUE_GET_STRING(val);
- uint32_t len, i;
- JSValue a, b;
-
- len = p->len;
- if (len == 0)
- return 0; /* nothing to do */
- /* find the bucket i so that rope_bucket_len[i] <= len <
- rope_bucket_len[i + 1] and concatenate the ropes in the
- buckets before */
- a = JS_NULL;
- i = 0;
- while (len >= rope_bucket_len[i + 1]) {
- b = buckets[i];
- if (!JS_IsNull(b)) {
- buckets[i] = JS_NULL;
- if (JS_IsNull(a)) {
- a = b;
- } else {
- a = js_new_string_rope(ctx, b, a);
- if (JS_IsException(a))
- return -1;
- }
- }
- i++;
- }
- if (!JS_IsNull(a)) {
- a = js_new_string_rope(ctx, a, JS_DupValue(ctx, val));
- if (JS_IsException(a))
- return -1;
- } else {
- a = JS_DupValue(ctx, val);
- }
- while (!JS_IsNull(buckets[i])) {
- a = js_new_string_rope(ctx, buckets[i], a);
- buckets[i] = JS_NULL;
- if (JS_IsException(a))
- return -1;
- i++;
- }
- buckets[i] = a;
- } else {
- JSStringRope *r = JS_VALUE_GET_STRING_ROPE(val);
- js_rebalancee_string_rope_rec(ctx, buckets, r->left);
- js_rebalancee_string_rope_rec(ctx, buckets, r->right);
- }
- return 0;
- }
- /* Return a new rope which is balanced. Algorithm from "Ropes: an
- Alternative to Strings", Hans-J. Boehm, Russ Atkinson and Michael
- Plass. */
- static JSValue js_rebalancee_string_rope(JSContext *ctx, JSValueConst rope)
- {
- JSValue buckets[ROPE_N_BUCKETS], a, b;
- int i;
-
- for(i = 0; i < ROPE_N_BUCKETS; i++)
- buckets[i] = JS_NULL;
- if (js_rebalancee_string_rope_rec(ctx, buckets, rope))
- goto fail;
- a = JS_NULL;
- for(i = 0; i < ROPE_N_BUCKETS; i++) {
- b = buckets[i];
- if (!JS_IsNull(b)) {
- buckets[i] = JS_NULL;
- if (JS_IsNull(a)) {
- a = b;
- } else {
- a = js_new_string_rope(ctx, b, a);
- if (JS_IsException(a))
- goto fail;
- }
- }
- }
- /* fail safe */
- if (JS_IsNull(a))
- return JS_AtomToString(ctx, JS_ATOM_empty_string);
- else
- return a;
- fail:
- for(i = 0; i < ROPE_N_BUCKETS; i++) {
- JS_FreeValue(ctx, buckets[i]);
- }
- return JS_EXCEPTION;
- }
- /* op1 and op2 are converted to strings. For convenience, op1 or op2 =
- JS_EXCEPTION are accepted and return JS_EXCEPTION. */
- static JSValue JS_ConcatString(JSContext *ctx, JSValue op1, JSValue op2)
- {
- JSString *p1, *p2;
- if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_STRING &&
- JS_VALUE_GET_TAG(op1) != JS_TAG_STRING_ROPE)) {
- op1 = JS_ToStringFree(ctx, op1);
- if (JS_IsException(op1)) {
- JS_FreeValue(ctx, op2);
- return JS_EXCEPTION;
- }
- }
- if (unlikely(JS_VALUE_GET_TAG(op2) != JS_TAG_STRING &&
- JS_VALUE_GET_TAG(op2) != JS_TAG_STRING_ROPE)) {
- op2 = JS_ToStringFree(ctx, op2);
- if (JS_IsException(op2)) {
- JS_FreeValue(ctx, op1);
- return JS_EXCEPTION;
- }
- }
- /* normal concatenation for short strings */
- if (JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) {
- p2 = JS_VALUE_GET_STRING(op2);
- if (p2->len == 0) {
- JS_FreeValue(ctx, op2);
- return op1;
- }
- if (p2->len <= JS_STRING_ROPE_SHORT_LEN) {
- if (JS_VALUE_GET_TAG(op1) == JS_TAG_STRING) {
- p1 = JS_VALUE_GET_STRING(op1);
- if (p1->len <= JS_STRING_ROPE_SHORT2_LEN) {
- return JS_ConcatString2(ctx, op1, op2);
- } else {
- return js_new_string_rope(ctx, op1, op2);
- }
- } else {
- JSStringRope *r1;
- r1 = JS_VALUE_GET_STRING_ROPE(op1);
- if (JS_VALUE_GET_TAG(r1->right) == JS_TAG_STRING &&
- JS_VALUE_GET_STRING(r1->right)->len <= JS_STRING_ROPE_SHORT_LEN) {
- JSValue val, ret;
- val = JS_ConcatString2(ctx, JS_DupValue(ctx, r1->right), op2);
- if (JS_IsException(val)) {
- JS_FreeValue(ctx, op1);
- return JS_EXCEPTION;
- }
- ret = js_new_string_rope(ctx, JS_DupValue(ctx, r1->left), val);
- JS_FreeValue(ctx, op1);
- return ret;
- }
- }
- }
- } else if (JS_VALUE_GET_TAG(op1) == JS_TAG_STRING) {
- JSStringRope *r2;
- p1 = JS_VALUE_GET_STRING(op1);
- if (p1->len == 0) {
- JS_FreeValue(ctx, op1);
- return op2;
- }
- r2 = JS_VALUE_GET_STRING_ROPE(op2);
- if (JS_VALUE_GET_TAG(r2->left) == JS_TAG_STRING &&
- JS_VALUE_GET_STRING(r2->left)->len <= JS_STRING_ROPE_SHORT_LEN) {
- JSValue val, ret;
- val = JS_ConcatString2(ctx, op1, JS_DupValue(ctx, r2->left));
- if (JS_IsException(val)) {
- JS_FreeValue(ctx, op2);
- return JS_EXCEPTION;
- }
- ret = js_new_string_rope(ctx, val, JS_DupValue(ctx, r2->right));
- JS_FreeValue(ctx, op2);
- return ret;
- }
- }
- return js_new_string_rope(ctx, op1, op2);
- }
- /* Shape support */
- static inline size_t get_shape_size(size_t hash_size, size_t prop_size)
- {
- return sizeof(JSShape) + hash_size * sizeof(uint32_t) +
- prop_size * sizeof(JSShapeProperty);
- }
- static inline JSShapeProperty *get_shape_prop(JSShape *sh)
- {
- return (JSShapeProperty *)((uint32_t *)(sh + 1) + sh->prop_hash_mask + 1);
- }
- static int init_shape_hash(JSRuntime *rt)
- {
- rt->shape_hash_bits = 4; /* 16 shapes */
- rt->shape_hash_size = 1 << rt->shape_hash_bits;
- rt->shape_hash_count = 0;
- rt->shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) *
- rt->shape_hash_size);
- if (!rt->shape_hash)
- return -1;
- return 0;
- }
- /* same magic hash multiplier as the Linux kernel */
- static uint32_t shape_hash(uint32_t h, uint32_t val)
- {
- return (h + val) * 0x9e370001;
- }
- /* truncate the shape hash to 'hash_bits' bits */
- static uint32_t get_shape_hash(uint32_t h, int hash_bits)
- {
- return h >> (32 - hash_bits);
- }
- static uint32_t shape_initial_hash(JSObject *proto)
- {
- uint32_t h;
- h = shape_hash(1, (uintptr_t)proto);
- if (sizeof(proto) > 4)
- h = shape_hash(h, (uint64_t)(uintptr_t)proto >> 32);
- return h;
- }
- static int resize_shape_hash(JSRuntime *rt, int new_shape_hash_bits)
- {
- int new_shape_hash_size, i;
- uint32_t h;
- JSShape **new_shape_hash, *sh, *sh_next;
- new_shape_hash_size = 1 << new_shape_hash_bits;
- new_shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) *
- new_shape_hash_size);
- if (!new_shape_hash)
- return -1;
- for(i = 0; i < rt->shape_hash_size; i++) {
- for(sh = rt->shape_hash[i]; sh != NULL; sh = sh_next) {
- sh_next = sh->shape_hash_next;
- h = get_shape_hash(sh->hash, new_shape_hash_bits);
- sh->shape_hash_next = new_shape_hash[h];
- new_shape_hash[h] = sh;
- }
- }
- js_free_rt(rt, rt->shape_hash);
- rt->shape_hash_bits = new_shape_hash_bits;
- rt->shape_hash_size = new_shape_hash_size;
- rt->shape_hash = new_shape_hash;
- return 0;
- }
- static void js_shape_hash_link(JSRuntime *rt, JSShape *sh)
- {
- uint32_t h;
- h = get_shape_hash(sh->hash, rt->shape_hash_bits);
- sh->shape_hash_next = rt->shape_hash[h];
- rt->shape_hash[h] = sh;
- rt->shape_hash_count++;
- }
- static void js_shape_hash_unlink(JSRuntime *rt, JSShape *sh)
- {
- uint32_t h;
- JSShape **psh;
- h = get_shape_hash(sh->hash, rt->shape_hash_bits);
- psh = &rt->shape_hash[h];
- while (*psh != sh)
- psh = &(*psh)->shape_hash_next;
- *psh = sh->shape_hash_next;
- rt->shape_hash_count--;
- }
- /* create a new empty shape with prototype 'proto'. It is not hashed */
- static inline JSShape *js_new_shape_nohash(JSContext *ctx, JSObject *proto,
- int hash_size, int prop_size)
- {
- JSRuntime *rt = ctx->rt;
- JSShape *sh;
- sh = js_malloc(ctx, get_shape_size(hash_size, prop_size));
- if (!sh)
- return NULL;
- js_rc(sh)->ref_count = 1;
- add_gc_object(rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE);
- if (proto)
- JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, proto));
- sh->proto = proto;
- memset(sh->hash_table, 0, sizeof(sh->hash_table[0]) * hash_size);
- sh->prop_hash_mask = hash_size - 1;
- sh->prop_size = prop_size;
- sh->prop_count = 0;
- sh->deleted_prop_count = 0;
- sh->is_hashed = FALSE;
- return sh;
- }
- /* create a new empty shape with prototype 'proto' */
- static no_inline JSShape *js_new_shape2(JSContext *ctx, JSObject *proto,
- int hash_size, int prop_size)
- {
- JSRuntime *rt = ctx->rt;
- JSShape *sh;
- /* resize the shape hash table if necessary */
- if (2 * (rt->shape_hash_count + 1) > rt->shape_hash_size) {
- resize_shape_hash(rt, rt->shape_hash_bits + 1);
- }
- sh = js_new_shape_nohash(ctx, proto, hash_size, prop_size);
- if (!sh)
- return NULL;
-
- /* insert in the hash table */
- sh->hash = shape_initial_hash(proto);
- sh->is_hashed = TRUE;
- js_shape_hash_link(ctx->rt, sh);
- return sh;
- }
- static JSShape *js_new_shape(JSContext *ctx, JSObject *proto)
- {
- return js_new_shape2(ctx, proto, JS_PROP_INITIAL_HASH_SIZE,
- JS_PROP_INITIAL_SIZE);
- }
- /* The shape is cloned. The new shape is not inserted in the shape
- hash table */
- static JSShape *js_clone_shape(JSContext *ctx, JSShape *sh1)
- {
- JSShape *sh;
- size_t size;
- JSShapeProperty *pr;
- uint32_t i, hash_size;
- hash_size = sh1->prop_hash_mask + 1;
- size = get_shape_size(hash_size, sh1->prop_size);
- sh = js_malloc(ctx, size);
- if (!sh)
- return NULL;
- memcpy(&sh->header + 1, &sh1->header + 1,
- size - sizeof(JSGCObjectHeader));
- js_rc(sh)->ref_count = 1;
- add_gc_object(ctx->rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE);
- sh->is_hashed = FALSE;
- if (sh->proto) {
- JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto));
- }
- for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) {
- JS_DupAtom(ctx, pr->atom);
- }
- return sh;
- }
- static JSShape *js_dup_shape(JSShape *sh)
- {
- js_rc(sh)->ref_count++;
- return sh;
- }
- static void js_free_shape0(JSRuntime *rt, JSShape *sh)
- {
- uint32_t i;
- JSShapeProperty *pr;
- assert(js_rc(sh)->ref_count == 0);
- if (sh->is_hashed)
- js_shape_hash_unlink(rt, sh);
- if (sh->proto != NULL) {
- JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, sh->proto));
- }
- pr = get_shape_prop(sh);
- for(i = 0; i < sh->prop_count; i++) {
- JS_FreeAtomRT(rt, pr->atom);
- pr++;
- }
- remove_gc_object(&sh->header);
- js_free_rt(rt, sh);
- }
- static void js_free_shape(JSRuntime *rt, JSShape *sh)
- {
- if (unlikely(--js_rc(sh)->ref_count <= 0)) {
- js_free_shape0(rt, sh);
- }
- }
- static void js_free_shape_null(JSRuntime *rt, JSShape *sh)
- {
- if (sh)
- js_free_shape(rt, sh);
- }
- /* make space to hold at least 'count' properties */
- static no_inline int resize_properties(JSContext *ctx, JSShape **psh,
- JSObject *p, uint32_t count)
- {
- JSShape *sh;
- uint32_t new_size, new_hash_size, new_hash_mask, i;
- JSShapeProperty *pr;
- intptr_t h;
- JSShape *old_sh;
- sh = *psh;
- new_size = max_int(count, sh->prop_size * 3 / 2);
- /* Reallocate prop array first to avoid crash or size inconsistency
- in case of memory allocation failure */
- if (p) {
- JSProperty *new_prop;
- new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size);
- if (unlikely(!new_prop))
- return -1;
- p->prop = new_prop;
- }
- new_hash_size = sh->prop_hash_mask + 1;
- while (new_hash_size < new_size)
- new_hash_size = 2 * new_hash_size;
- /* resize the property shapes. Using js_realloc() is not possible in
- case the GC runs during the allocation */
- old_sh = sh;
- sh = js_malloc(ctx, get_shape_size(new_hash_size, new_size));
- if (!sh)
- return -1;
- remove_gc_object(&old_sh->header);
- js_rc(sh)->ref_count = 1;
- add_gc_object(ctx->rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE);
- memcpy(&sh->header + 1, &old_sh->header + 1,
- sizeof(JSShape) - sizeof(JSGCObjectHeader));
-
- if (new_hash_size != (sh->prop_hash_mask + 1)) {
- /* resize the hash table and the properties */
- new_hash_mask = new_hash_size - 1;
- sh->prop_hash_mask = new_hash_mask;
- memset(sh->hash_table, 0,
- sizeof(sh->hash_table[0]) * new_hash_size);
- memcpy(get_shape_prop(sh), get_shape_prop(old_sh),
- sizeof(JSShapeProperty) * old_sh->prop_count);
- for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) {
- if (pr->atom != JS_ATOM_NULL) {
- h = ((uintptr_t)pr->atom & new_hash_mask);
- pr->hash_next = sh->hash_table[h];
- sh->hash_table[h] = i + 1;
- }
- }
- } else {
- /* just copy the previous hash table and the properties */
- memcpy(sh->hash_table, old_sh->hash_table,
- sizeof(sh->hash_table[0]) * new_hash_size);
- memcpy(get_shape_prop(sh), get_shape_prop(old_sh),
- sizeof(JSShapeProperty) * old_sh->prop_count);
- }
- js_free(ctx, old_sh);
- *psh = sh;
- sh->prop_size = new_size;
- return 0;
- }
- /* remove the deleted properties. */
- static int compact_properties(JSContext *ctx, JSObject *p)
- {
- JSShape *sh, *old_sh;
- intptr_t h;
- uint32_t new_hash_size, i, j, new_hash_mask, new_size;
- JSShapeProperty *old_pr, *pr;
- JSProperty *prop, *new_prop;
- sh = p->shape;
- assert(!sh->is_hashed);
- new_size = max_int(JS_PROP_INITIAL_SIZE,
- sh->prop_count - sh->deleted_prop_count);
- assert(new_size <= sh->prop_size);
- new_hash_size = sh->prop_hash_mask + 1;
- while ((new_hash_size / 2) >= new_size)
- new_hash_size = new_hash_size / 2;
- new_hash_mask = new_hash_size - 1;
- /* resize the hash table and the properties */
- old_sh = sh;
- sh = js_malloc(ctx, get_shape_size(new_hash_size, new_size));
- if (!sh)
- return -1;
- remove_gc_object(&old_sh->header);
- js_rc(sh)->ref_count = 1;
- add_gc_object(ctx->rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE);
- memcpy(&sh->header + 1, &old_sh->header + 1,
- sizeof(JSShape) - sizeof(JSGCObjectHeader));
- memset(sh->hash_table, 0, sizeof(sh->hash_table[0]) * new_hash_size);
- sh->prop_hash_mask = new_hash_mask;
- j = 0;
- old_pr = get_shape_prop(old_sh);
- pr = get_shape_prop(sh);
- prop = p->prop;
- for(i = 0; i < sh->prop_count; i++) {
- if (old_pr->atom != JS_ATOM_NULL) {
- pr->atom = old_pr->atom;
- pr->flags = old_pr->flags;
- h = ((uintptr_t)old_pr->atom & new_hash_mask);
- pr->hash_next = sh->hash_table[h];
- sh->hash_table[h] = j + 1;
- prop[j] = prop[i];
- j++;
- pr++;
- }
- old_pr++;
- }
- assert(j == (sh->prop_count - sh->deleted_prop_count));
- sh->prop_size = new_size;
- sh->deleted_prop_count = 0;
- sh->prop_count = j;
- p->shape = sh;
- js_free(ctx, old_sh);
- /* reduce the size of the object properties */
- new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size);
- if (new_prop)
- p->prop = new_prop;
- return 0;
- }
- static int add_shape_property(JSContext *ctx, JSShape **psh,
- JSObject *p, JSAtom atom, int prop_flags)
- {
- JSRuntime *rt = ctx->rt;
- JSShape *sh = *psh;
- JSShapeProperty *pr, *prop;
- uint32_t hash_mask, new_shape_hash = 0;
- intptr_t h;
- /* update the shape hash */
- if (sh->is_hashed) {
- js_shape_hash_unlink(rt, sh);
- new_shape_hash = shape_hash(shape_hash(sh->hash, atom), prop_flags);
- }
- if (unlikely(sh->prop_count >= sh->prop_size)) {
- if (resize_properties(ctx, psh, p, sh->prop_count + 1)) {
- /* in case of error, reinsert in the hash table.
- sh is still valid if resize_properties() failed */
- if (sh->is_hashed)
- js_shape_hash_link(rt, sh);
- return -1;
- }
- sh = *psh;
- }
- if (sh->is_hashed) {
- sh->hash = new_shape_hash;
- js_shape_hash_link(rt, sh);
- }
- /* Initialize the new shape property.
- The object property at p->prop[sh->prop_count] is uninitialized */
- prop = get_shape_prop(sh);
- pr = &prop[sh->prop_count++];
- pr->atom = JS_DupAtom(ctx, atom);
- pr->flags = prop_flags;
- /* add in hash table */
- hash_mask = sh->prop_hash_mask;
- h = atom & hash_mask;
- pr->hash_next = sh->hash_table[h];
- sh->hash_table[h] = sh->prop_count;
- return 0;
- }
- /* find a hashed empty shape matching the prototype. Return NULL if
- not found */
- static JSShape *find_hashed_shape_proto(JSRuntime *rt, JSObject *proto)
- {
- JSShape *sh1;
- uint32_t h, h1;
- h = shape_initial_hash(proto);
- h1 = get_shape_hash(h, rt->shape_hash_bits);
- for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) {
- if (sh1->hash == h &&
- sh1->proto == proto &&
- sh1->prop_count == 0) {
- return sh1;
- }
- }
- return NULL;
- }
- /* find a hashed shape matching sh + (prop, prop_flags). Return NULL if
- not found */
- static JSShape *find_hashed_shape_prop(JSRuntime *rt, JSShape *sh,
- JSAtom atom, int prop_flags)
- {
- JSShape *sh1;
- uint32_t h, h1, i, n;
- h = sh->hash;
- h = shape_hash(h, atom);
- h = shape_hash(h, prop_flags);
- h1 = get_shape_hash(h, rt->shape_hash_bits);
- for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) {
- /* we test the hash first so that the rest is done only if the
- shapes really match */
- if (sh1->hash == h &&
- sh1->proto == sh->proto &&
- sh1->prop_count == ((n = sh->prop_count) + 1)) {
- JSShapeProperty *prop = get_shape_prop(sh);
- JSShapeProperty *prop1 = get_shape_prop(sh1);
- for(i = 0; i < n; i++) {
- if (unlikely(prop1[i].atom != prop[i].atom) ||
- unlikely(prop1[i].flags != prop[i].flags))
- goto next;
- }
- if (unlikely(prop1[n].atom != atom) ||
- unlikely(prop1[n].flags != prop_flags))
- goto next;
- return sh1;
- }
- next: ;
- }
- return NULL;
- }
- static __maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh)
- {
- char atom_buf[ATOM_GET_STR_BUF_SIZE];
- int j;
- /* XXX: should output readable class prototype */
- printf("%5d %3d%c %14p %5d %5d", i,
- js_rc(sh)->ref_count, " *"[sh->is_hashed],
- (void *)sh->proto, sh->prop_size, sh->prop_count);
- for(j = 0; j < sh->prop_count; j++) {
- printf(" %s", JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf),
- get_shape_prop(sh)[j].atom));
- }
- printf("\n");
- }
- static __maybe_unused void JS_DumpShapes(JSRuntime *rt)
- {
- int i;
- JSShape *sh;
- struct list_head *el;
- JSObject *p;
- JSGCObjectHeader *gp;
- printf("JSShapes: {\n");
- printf("%5s %4s %14s %5s %5s %s\n", "SLOT", "REFS", "PROTO", "SIZE", "COUNT", "PROPS");
- for(i = 0; i < rt->shape_hash_size; i++) {
- for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) {
- JS_DumpShape(rt, i, sh);
- assert(sh->is_hashed);
- }
- }
- /* dump non-hashed shapes */
- list_for_each(el, &rt->gc_obj_list) {
- gp = list_entry(el, JSGCObjectHeader, link);
- if (js_rc(gp)->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
- p = (JSObject *)gp;
- if (!p->shape->is_hashed) {
- JS_DumpShape(rt, -1, p->shape);
- }
- }
- }
- printf("}\n");
- }
- /* 'props[]' is used to initialized the object properties. The number
- of elements depends on the shape. */
- static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID class_id,
- JSProperty *props)
- {
- JSObject *p;
- int i;
-
- js_trigger_gc(ctx->rt, sizeof(JSObject));
- p = js_malloc(ctx, sizeof(JSObject));
- if (unlikely(!p))
- goto fail;
- p->class_id = class_id;
- p->is_std_array_prototype = 0;
- p->extensible = TRUE;
- p->free_mark = 0;
- p->is_exotic = 0;
- p->fast_array = 0;
- p->is_constructor = 0;
- p->has_immutable_prototype = 0;
- p->tmp_mark = 0;
- p->is_HTMLDDA = 0;
- p->weakref_count = 0;
- p->u.opaque = NULL;
- p->shape = sh;
- p->prop = js_malloc(ctx, sizeof(JSProperty) * sh->prop_size);
- if (unlikely(!p->prop)) {
- js_free(ctx, p);
- fail:
- if (props) {
- JSShapeProperty *prs = get_shape_prop(sh);
- for(i = 0; i < sh->prop_count; i++) {
- free_property(ctx->rt, &props[i], prs->flags);
- prs++;
- }
- }
- js_free_shape(ctx->rt, sh);
- return JS_EXCEPTION;
- }
- switch(class_id) {
- case JS_CLASS_OBJECT:
- break;
- case JS_CLASS_ARRAY:
- {
- JSProperty *pr;
- p->is_exotic = 1;
- p->fast_array = 1;
- p->u.array.u.values = NULL;
- p->u.array.count = 0;
- p->u.array.u1.size = 0;
- if (!props) {
- /* XXX: remove */
- /* the length property is always the first one */
- if (likely(sh == ctx->array_shape)) {
- pr = &p->prop[0];
- } else {
- /* only used for the first array */
- /* cannot fail */
- pr = add_property(ctx, p, JS_ATOM_length,
- JS_PROP_WRITABLE | JS_PROP_LENGTH);
- }
- pr->u.value = JS_NewInt32(ctx, 0);
- }
- }
- break;
- case JS_CLASS_C_FUNCTION:
- p->prop[0].u.value = JS_UNDEFINED;
- break;
- case JS_CLASS_ARGUMENTS:
- case JS_CLASS_MAPPED_ARGUMENTS:
- case JS_CLASS_UINT8C_ARRAY:
- case JS_CLASS_INT8_ARRAY:
- case JS_CLASS_UINT8_ARRAY:
- case JS_CLASS_INT16_ARRAY:
- case JS_CLASS_UINT16_ARRAY:
- case JS_CLASS_INT32_ARRAY:
- case JS_CLASS_UINT32_ARRAY:
- case JS_CLASS_BIG_INT64_ARRAY:
- case JS_CLASS_BIG_UINT64_ARRAY:
- case JS_CLASS_FLOAT16_ARRAY:
- case JS_CLASS_FLOAT32_ARRAY:
- case JS_CLASS_FLOAT64_ARRAY:
- p->is_exotic = 1;
- p->fast_array = 1;
- p->u.array.u.ptr = NULL;
- p->u.array.count = 0;
- break;
- case JS_CLASS_DATAVIEW:
- p->u.array.u.ptr = NULL;
- p->u.array.count = 0;
- break;
- case JS_CLASS_NUMBER:
- case JS_CLASS_STRING:
- case JS_CLASS_BOOLEAN:
- case JS_CLASS_SYMBOL:
- case JS_CLASS_DATE:
- case JS_CLASS_BIG_INT:
- p->u.object_data = JS_UNDEFINED;
- goto set_exotic;
- case JS_CLASS_REGEXP:
- p->u.regexp.pattern = NULL;
- p->u.regexp.bytecode = NULL;
- break;
- case JS_CLASS_GLOBAL_OBJECT:
- p->u.global_object.uninitialized_vars = JS_UNDEFINED;
- break;
- default:
- set_exotic:
- if (ctx->rt->class_array[class_id].exotic) {
- p->is_exotic = 1;
- }
- break;
- }
- js_rc(p)->ref_count = 1;
- add_gc_object(ctx->rt, &p->header, JS_GC_OBJ_TYPE_JS_OBJECT);
- if (props) {
- for(i = 0; i < sh->prop_count; i++)
- p->prop[i] = props[i];
- }
- return JS_MKPTR(JS_TAG_OBJECT, p);
- }
- static JSObject *get_proto_obj(JSValueConst proto_val)
- {
- if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT)
- return NULL;
- else
- return JS_VALUE_GET_OBJ(proto_val);
- }
- /* WARNING: proto must be an object or JS_NULL */
- JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto_val,
- JSClassID class_id)
- {
- JSShape *sh;
- JSObject *proto;
- proto = get_proto_obj(proto_val);
- sh = find_hashed_shape_proto(ctx->rt, proto);
- if (likely(sh)) {
- sh = js_dup_shape(sh);
- } else {
- sh = js_new_shape(ctx, proto);
- if (!sh)
- return JS_EXCEPTION;
- }
- return JS_NewObjectFromShape(ctx, sh, class_id, NULL);
- }
- /* WARNING: the shape is not hashed. It is used for objects where
- factorizing the shape is not relevant (prototypes, constructors) */
- static JSValue JS_NewObjectProtoClassAlloc(JSContext *ctx, JSValueConst proto_val,
- JSClassID class_id, int n_alloc_props)
- {
- JSShape *sh;
- JSObject *proto;
- int hash_size, hash_bits;
-
- if (n_alloc_props <= JS_PROP_INITIAL_SIZE) {
- n_alloc_props = JS_PROP_INITIAL_SIZE;
- hash_size = JS_PROP_INITIAL_HASH_SIZE;
- } else {
- hash_bits = 32 - clz32(n_alloc_props - 1); /* ceil(log2(radix)) */
- hash_size = 1 << hash_bits;
- }
- proto = get_proto_obj(proto_val);
- sh = js_new_shape_nohash(ctx, proto, hash_size, n_alloc_props);
- if (!sh)
- return JS_EXCEPTION;
- return JS_NewObjectFromShape(ctx, sh, class_id, NULL);
- }
- #if 0
- static JSValue JS_GetObjectData(JSContext *ctx, JSValueConst obj)
- {
- JSObject *p;
- if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
- p = JS_VALUE_GET_OBJ(obj);
- switch(p->class_id) {
- case JS_CLASS_NUMBER:
- case JS_CLASS_STRING:
- case JS_CLASS_BOOLEAN:
- case JS_CLASS_SYMBOL:
- case JS_CLASS_DATE:
- case JS_CLASS_BIG_INT:
- return JS_DupValue(ctx, p->u.object_data);
- }
- }
- return JS_UNDEFINED;
- }
- #endif
- static int JS_SetObjectData(JSContext *ctx, JSValueConst obj, JSValue val)
- {
- JSObject *p;
- if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
- p = JS_VALUE_GET_OBJ(obj);
- switch(p->class_id) {
- case JS_CLASS_NUMBER:
- case JS_CLASS_STRING:
- case JS_CLASS_BOOLEAN:
- case JS_CLASS_SYMBOL:
- case JS_CLASS_DATE:
- case JS_CLASS_BIG_INT:
- JS_FreeValue(ctx, p->u.object_data);
- p->u.object_data = val; /* for JS_CLASS_STRING, 'val' must
- be JS_TAG_STRING (and not a
- rope) */
- return 0;
- }
- }
- JS_FreeValue(ctx, val);
- if (!JS_IsException(obj))
- JS_ThrowTypeError(ctx, "invalid object type");
- return -1;
- }
- JSValue JS_NewObjectClass(JSContext *ctx, int class_id)
- {
- return JS_NewObjectProtoClass(ctx, ctx->class_proto[class_id], class_id);
- }
- JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto)
- {
- return JS_NewObjectProtoClass(ctx, proto, JS_CLASS_OBJECT);
- }
- JSValue JS_NewArray(JSContext *ctx)
- {
- return JS_NewObjectFromShape(ctx, js_dup_shape(ctx->array_shape),
- JS_CLASS_ARRAY, NULL);
- }
- JSValue JS_NewObject(JSContext *ctx)
- {
- /* inline JS_NewObjectClass(ctx, JS_CLASS_OBJECT); */
- return JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], JS_CLASS_OBJECT);
- }
- static void js_function_set_properties(JSContext *ctx, JSValueConst func_obj,
- JSAtom name, int len)
- {
- /* ES6 feature non compatible with ES5.1: length is configurable */
- JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length, JS_NewInt32(ctx, len),
- JS_PROP_CONFIGURABLE);
- JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name,
- JS_AtomToString(ctx, name), JS_PROP_CONFIGURABLE);
- }
- static BOOL js_class_has_bytecode(JSClassID class_id)
- {
- return (class_id == JS_CLASS_BYTECODE_FUNCTION ||
- class_id == JS_CLASS_GENERATOR_FUNCTION ||
- class_id == JS_CLASS_ASYNC_FUNCTION ||
- class_id == JS_CLASS_ASYNC_GENERATOR_FUNCTION);
- }
- /* return NULL without exception if not a function or no bytecode */
- static JSFunctionBytecode *JS_GetFunctionBytecode(JSValueConst val)
- {
- JSObject *p;
- if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
- return NULL;
- p = JS_VALUE_GET_OBJ(val);
- if (!js_class_has_bytecode(p->class_id))
- return NULL;
- return p->u.func.function_bytecode;
- }
- static void js_method_set_home_object(JSContext *ctx, JSValueConst func_obj,
- JSValueConst home_obj)
- {
- JSObject *p, *p1;
- JSFunctionBytecode *b;
- if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)
- return;
- p = JS_VALUE_GET_OBJ(func_obj);
- if (!js_class_has_bytecode(p->class_id))
- return;
- b = p->u.func.function_bytecode;
- if (b->need_home_object) {
- p1 = p->u.func.home_object;
- if (p1) {
- JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));
- }
- if (JS_VALUE_GET_TAG(home_obj) == JS_TAG_OBJECT)
- p1 = JS_VALUE_GET_OBJ(JS_DupValue(ctx, home_obj));
- else
- p1 = NULL;
- p->u.func.home_object = p1;
- }
- }
- static JSValue js_get_function_name(JSContext *ctx, JSAtom name)
- {
- JSValue name_str;
- name_str = JS_AtomToString(ctx, name);
- if (JS_AtomSymbolHasDescription(ctx, name)) {
- name_str = JS_ConcatString3(ctx, "[", name_str, "]");
- }
- return name_str;
- }
- /* Modify the name of a method according to the atom and
- 'flags'. 'flags' is a bitmask of JS_PROP_HAS_GET and
- JS_PROP_HAS_SET. Also set the home object of the method.
- Return < 0 if exception. */
- static int js_method_set_properties(JSContext *ctx, JSValueConst func_obj,
- JSAtom name, int flags, JSValueConst home_obj)
- {
- JSValue name_str;
- name_str = js_get_function_name(ctx, name);
- if (flags & JS_PROP_HAS_GET) {
- name_str = JS_ConcatString3(ctx, "get ", name_str, "");
- } else if (flags & JS_PROP_HAS_SET) {
- name_str = JS_ConcatString3(ctx, "set ", name_str, "");
- }
- if (JS_IsException(name_str))
- return -1;
- if (JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name_str,
- JS_PROP_CONFIGURABLE) < 0)
- return -1;
- js_method_set_home_object(ctx, func_obj, home_obj);
- return 0;
- }
- /* Note: at least 'length' arguments will be readable in 'argv' */
- static JSValue JS_NewCFunction3(JSContext *ctx, JSCFunction *func,
- const char *name,
- int length, JSCFunctionEnum cproto, int magic,
- JSValueConst proto_val, int n_fields)
- {
- JSValue func_obj;
- JSObject *p;
- JSAtom name_atom;
- if (n_fields > 0) {
- func_obj = JS_NewObjectProtoClassAlloc(ctx, proto_val, JS_CLASS_C_FUNCTION, n_fields);
- } else {
- func_obj = JS_NewObjectProtoClass(ctx, proto_val, JS_CLASS_C_FUNCTION);
- }
- if (JS_IsException(func_obj))
- return func_obj;
- p = JS_VALUE_GET_OBJ(func_obj);
- p->u.cfunc.realm = JS_DupContext(ctx);
- p->u.cfunc.c_function.generic = func;
- p->u.cfunc.length = length;
- p->u.cfunc.cproto = cproto;
- p->u.cfunc.magic = magic;
- p->is_constructor = (cproto == JS_CFUNC_constructor ||
- cproto == JS_CFUNC_constructor_magic ||
- cproto == JS_CFUNC_constructor_or_func ||
- cproto == JS_CFUNC_constructor_or_func_magic);
- if (!name)
- name = "";
- name_atom = JS_NewAtom(ctx, name);
- if (name_atom == JS_ATOM_NULL) {
- JS_FreeValue(ctx, func_obj);
- return JS_EXCEPTION;
- }
- js_function_set_properties(ctx, func_obj, name_atom, length);
- JS_FreeAtom(ctx, name_atom);
- return func_obj;
- }
- /* Note: at least 'length' arguments will be readable in 'argv' */
- JSValue JS_NewCFunction2(JSContext *ctx, JSCFunction *func,
- const char *name,
- int length, JSCFunctionEnum cproto, int magic)
- {
- return JS_NewCFunction3(ctx, func, name, length, cproto, magic,
- ctx->function_proto, 0);
- }
- typedef struct JSCFunctionDataRecord {
- JSCFunctionData *func;
- uint8_t length;
- uint8_t data_len;
- uint16_t magic;
- JSValue data[0];
- } JSCFunctionDataRecord;
- static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val)
- {
- JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA);
- int i;
- if (s) {
- for(i = 0; i < s->data_len; i++) {
- JS_FreeValueRT(rt, s->data[i]);
- }
- js_free_rt(rt, s);
- }
- }
- static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func)
- {
- JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA);
- int i;
- if (s) {
- for(i = 0; i < s->data_len; i++) {
- JS_MarkValue(rt, s->data[i], mark_func);
- }
- }
- }
- static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj,
- JSValueConst this_val,
- int argc, JSValueConst *argv, int flags)
- {
- JSCFunctionDataRecord *s = JS_GetOpaque(func_obj, JS_CLASS_C_FUNCTION_DATA);
- JSValueConst *arg_buf;
- int i;
- /* XXX: could add the function on the stack for debug */
- if (unlikely(argc < s->length)) {
- arg_buf = alloca(sizeof(arg_buf[0]) * s->length);
- for(i = 0; i < argc; i++)
- arg_buf[i] = argv[i];
- for(i = argc; i < s->length; i++)
- arg_buf[i] = JS_UNDEFINED;
- } else {
- arg_buf = argv;
- }
- return s->func(ctx, this_val, argc, arg_buf, s->magic, s->data);
- }
- JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func,
- int length, int magic, int data_len,
- JSValueConst *data)
- {
- JSCFunctionDataRecord *s;
- JSValue func_obj;
- int i;
- func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,
- JS_CLASS_C_FUNCTION_DATA);
- if (JS_IsException(func_obj))
- return func_obj;
- s = js_malloc(ctx, sizeof(*s) + data_len * sizeof(JSValue));
- if (!s) {
- JS_FreeValue(ctx, func_obj);
- return JS_EXCEPTION;
- }
- s->func = func;
- s->length = length;
- s->data_len = data_len;
- s->magic = magic;
- for(i = 0; i < data_len; i++)
- s->data[i] = JS_DupValue(ctx, data[i]);
- JS_SetOpaque(func_obj, s);
- js_function_set_properties(ctx, func_obj,
- JS_ATOM_empty_string, length);
- return func_obj;
- }
- static JSContext *js_autoinit_get_realm(JSProperty *pr)
- {
- return (JSContext *)(pr->u.init.realm_and_id & ~3);
- }
- static JSAutoInitIDEnum js_autoinit_get_id(JSProperty *pr)
- {
- return pr->u.init.realm_and_id & 3;
- }
- static void js_autoinit_free(JSRuntime *rt, JSProperty *pr)
- {
- JS_FreeContext(js_autoinit_get_realm(pr));
- }
- static void js_autoinit_mark(JSRuntime *rt, JSProperty *pr,
- JS_MarkFunc *mark_func)
- {
- mark_func(rt, &js_autoinit_get_realm(pr)->header);
- }
- static void free_property(JSRuntime *rt, JSProperty *pr, int prop_flags)
- {
- if (unlikely(prop_flags & JS_PROP_TMASK)) {
- if ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
- if (pr->u.getset.getter)
- JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));
- if (pr->u.getset.setter)
- JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));
- } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
- free_var_ref(rt, pr->u.var_ref);
- } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
- js_autoinit_free(rt, pr);
- }
- } else {
- JS_FreeValueRT(rt, pr->u.value);
- }
- }
- static force_inline JSShapeProperty *find_own_property1(JSObject *p,
- JSAtom atom)
- {
- JSShape *sh;
- JSShapeProperty *pr, *prop;
- intptr_t h;
- sh = p->shape;
- h = (uintptr_t)atom & sh->prop_hash_mask;
- h = sh->hash_table[h];
- prop = get_shape_prop(sh);
- while (h) {
- pr = &prop[h - 1];
- if (likely(pr->atom == atom)) {
- return pr;
- }
- h = pr->hash_next;
- }
- return NULL;
- }
- static force_inline JSShapeProperty *find_own_property(JSProperty **ppr,
- JSObject *p,
- JSAtom atom)
- {
- JSShape *sh;
- JSShapeProperty *pr, *prop;
- intptr_t h;
- sh = p->shape;
- h = (uintptr_t)atom & sh->prop_hash_mask;
- h = sh->hash_table[h];
- prop = get_shape_prop(sh);
- while (h) {
- pr = &prop[h - 1];
- if (likely(pr->atom == atom)) {
- *ppr = &p->prop[h - 1];
- /* the compiler should be able to assume that pr != NULL here */
- return pr;
- }
- h = pr->hash_next;
- }
- *ppr = NULL;
- return NULL;
- }
- /* indicate that the object may be part of a function prototype cycle */
- static void set_cycle_flag(JSContext *ctx, JSValueConst obj)
- {
- }
- static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref)
- {
- if (var_ref) {
- assert(js_rc(var_ref)->ref_count > 0);
- if (--js_rc(var_ref)->ref_count == 0) {
- if (var_ref->is_detached) {
- JS_FreeValueRT(rt, var_ref->value);
- } else {
- JSStackFrame *sf = var_ref->stack_frame;
- assert(sf->var_refs[var_ref->var_ref_idx] == var_ref);
- sf->var_refs[var_ref->var_ref_idx] = NULL;
- if (sf->js_mode & JS_MODE_ASYNC) {
- JSAsyncFunctionState *async_func = container_of(sf, JSAsyncFunctionState, frame);
- async_func_free(rt, async_func);
- }
- }
- remove_gc_object(&var_ref->header);
- js_free_rt(rt, var_ref);
- }
- }
- }
- static void js_array_finalizer(JSRuntime *rt, JSValue val)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- int i;
- for(i = 0; i < p->u.array.count; i++) {
- JS_FreeValueRT(rt, p->u.array.u.values[i]);
- }
- js_free_rt(rt, p->u.array.u.values);
- }
- static void js_array_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- int i;
- for(i = 0; i < p->u.array.count; i++) {
- JS_MarkValue(rt, p->u.array.u.values[i], mark_func);
- }
- }
- static void js_object_data_finalizer(JSRuntime *rt, JSValue val)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- JS_FreeValueRT(rt, p->u.object_data);
- p->u.object_data = JS_UNDEFINED;
- }
- static void js_object_data_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- JS_MarkValue(rt, p->u.object_data, mark_func);
- }
- static void js_c_function_finalizer(JSRuntime *rt, JSValue val)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- if (p->u.cfunc.realm)
- JS_FreeContext(p->u.cfunc.realm);
- }
- static void js_c_function_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- if (p->u.cfunc.realm)
- mark_func(rt, &p->u.cfunc.realm->header);
- }
- static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val)
- {
- JSObject *p1, *p = JS_VALUE_GET_OBJ(val);
- JSFunctionBytecode *b;
- JSVarRef **var_refs;
- int i;
- p1 = p->u.func.home_object;
- if (p1) {
- JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, p1));
- }
- b = p->u.func.function_bytecode;
- if (b) {
- var_refs = p->u.func.var_refs;
- if (var_refs) {
- for(i = 0; i < b->closure_var_count; i++)
- free_var_ref(rt, var_refs[i]);
- js_free_rt(rt, var_refs);
- }
- JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b));
- }
- }
- static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- JSVarRef **var_refs = p->u.func.var_refs;
- JSFunctionBytecode *b = p->u.func.function_bytecode;
- int i;
- if (p->u.func.home_object) {
- JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object),
- mark_func);
- }
- if (b) {
- if (var_refs) {
- for(i = 0; i < b->closure_var_count; i++) {
- JSVarRef *var_ref = var_refs[i];
- if (var_ref) {
- mark_func(rt, &var_ref->header);
- }
- }
- }
- /* must mark the function bytecode because template objects may be
- part of a cycle */
- JS_MarkValue(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b), mark_func);
- }
- }
- static void js_bound_function_finalizer(JSRuntime *rt, JSValue val)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- JSBoundFunction *bf = p->u.bound_function;
- int i;
- JS_FreeValueRT(rt, bf->func_obj);
- JS_FreeValueRT(rt, bf->this_val);
- for(i = 0; i < bf->argc; i++) {
- JS_FreeValueRT(rt, bf->argv[i]);
- }
- js_free_rt(rt, bf);
- }
- static void js_bound_function_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- JSBoundFunction *bf = p->u.bound_function;
- int i;
- JS_MarkValue(rt, bf->func_obj, mark_func);
- JS_MarkValue(rt, bf->this_val, mark_func);
- for(i = 0; i < bf->argc; i++)
- JS_MarkValue(rt, bf->argv[i], mark_func);
- }
- static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- JSForInIterator *it = p->u.for_in_iterator;
- int i;
- JS_FreeValueRT(rt, it->obj);
- if (!it->is_array) {
- for(i = 0; i < it->atom_count; i++) {
- JS_FreeAtomRT(rt, it->tab_atom[i].atom);
- }
- js_free_rt(rt, it->tab_atom);
- }
- js_free_rt(rt, it);
- }
- static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- JSForInIterator *it = p->u.for_in_iterator;
- JS_MarkValue(rt, it->obj, mark_func);
- }
- static void free_object(JSRuntime *rt, JSObject *p)
- {
- int i;
- JSClassFinalizer *finalizer;
- JSShape *sh;
- JSShapeProperty *pr;
- p->free_mark = 1; /* used to tell the object is invalid when
- freeing cycles */
- /* free all the fields */
- sh = p->shape;
- pr = get_shape_prop(sh);
- for(i = 0; i < sh->prop_count; i++) {
- free_property(rt, &p->prop[i], pr->flags);
- pr++;
- }
- js_free_rt(rt, p->prop);
- /* as an optimization we destroy the shape immediately without
- putting it in gc_zero_ref_count_list */
- js_free_shape(rt, sh);
- /* fail safe */
- p->shape = NULL;
- p->prop = NULL;
- finalizer = rt->class_array[p->class_id].finalizer;
- if (finalizer)
- (*finalizer)(rt, JS_MKPTR(JS_TAG_OBJECT, p));
- /* fail safe */
- p->class_id = 0;
- p->u.opaque = NULL;
- p->u.func.var_refs = NULL;
- p->u.func.home_object = NULL;
- remove_gc_object(&p->header);
- if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES) {
- if (js_rc(p)->ref_count == 0 && p->weakref_count == 0) {
- js_free_rt(rt, p);
- } else {
- /* keep the object structure because there are may be
- references to it */
- list_add_tail(&p->header.link, &rt->gc_zero_ref_count_list);
- }
- } else {
- /* keep the object structure in case there are weak references to it */
- if (p->weakref_count == 0) {
- js_free_rt(rt, p);
- } else {
- js_rc(p)->mark = 0; /* reset the mark so that the weakref can be freed */
- }
- }
- }
- static void free_gc_object(JSRuntime *rt, JSGCObjectHeader *gp)
- {
- switch(js_rc(gp)->gc_obj_type) {
- case JS_GC_OBJ_TYPE_JS_OBJECT:
- free_object(rt, (JSObject *)gp);
- break;
- case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
- free_function_bytecode(rt, (JSFunctionBytecode *)gp);
- break;
- case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
- __async_func_free(rt, (JSAsyncFunctionState *)gp);
- break;
- case JS_GC_OBJ_TYPE_MODULE:
- js_free_module_def(rt, (JSModuleDef *)gp);
- break;
- default:
- abort();
- }
- }
- static void free_zero_refcount(JSRuntime *rt)
- {
- struct list_head *el;
- JSGCObjectHeader *p;
- rt->gc_phase = JS_GC_PHASE_DECREF;
- for(;;) {
- el = rt->gc_zero_ref_count_list.next;
- if (el == &rt->gc_zero_ref_count_list)
- break;
- p = list_entry(el, JSGCObjectHeader, link);
- assert(js_rc(p)->ref_count == 0);
- free_gc_object(rt, p);
- }
- rt->gc_phase = JS_GC_PHASE_NONE;
- }
- /* called with the ref_count of 'v' reaches zero. */
- void __JS_FreeValueRT(JSRuntime *rt, JSValue v)
- {
- uint32_t tag = JS_VALUE_GET_TAG(v);
- #ifdef DUMP_FREE
- {
- printf("Freeing ");
- if (tag == JS_TAG_OBJECT) {
- JS_DumpObject(rt, JS_VALUE_GET_OBJ(v));
- } else {
- JS_DumpValueShort(rt, v);
- printf("\n");
- }
- }
- #endif
- switch(tag) {
- case JS_TAG_STRING:
- {
- JSString *p = JS_VALUE_GET_STRING(v);
- if (p->atom_type) {
- JS_FreeAtomStruct(rt, p);
- } else {
- #ifdef DUMP_LEAKS
- list_del(&p->link);
- #endif
- js_free_rt(rt, p);
- }
- }
- break;
- case JS_TAG_STRING_ROPE:
- /* Note: recursion is acceptable because the rope depth is bounded */
- {
- JSStringRope *p = JS_VALUE_GET_STRING_ROPE(v);
- JS_FreeValueRT(rt, p->left);
- JS_FreeValueRT(rt, p->right);
- js_free_rt(rt, p);
- }
- break;
- case JS_TAG_OBJECT:
- case JS_TAG_FUNCTION_BYTECODE:
- case JS_TAG_MODULE:
- {
- JSGCObjectHeader *p = JS_VALUE_GET_PTR(v);
- if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) {
- list_del(&p->link);
- list_add(&p->link, &rt->gc_zero_ref_count_list);
- js_rc(p)->mark = 1; /* indicate that the object is about to be freed */
- if (rt->gc_phase == JS_GC_PHASE_NONE) {
- free_zero_refcount(rt);
- }
- }
- }
- break;
- case JS_TAG_BIG_INT:
- {
- JSBigInt *p = JS_VALUE_GET_PTR(v);
- js_free_rt(rt, p);
- }
- break;
- case JS_TAG_SYMBOL:
- {
- JSAtomStruct *p = JS_VALUE_GET_PTR(v);
- JS_FreeAtomStruct(rt, p);
- }
- break;
- default:
- abort();
- }
- }
- void __JS_FreeValue(JSContext *ctx, JSValue v)
- {
- __JS_FreeValueRT(ctx->rt, v);
- }
- /* garbage collection */
- static void gc_remove_weak_objects(JSRuntime *rt)
- {
- struct list_head *el;
- /* add the freed objects to rt->gc_zero_ref_count_list so that
- rt->weakref_list is not modified while we traverse it */
- rt->gc_phase = JS_GC_PHASE_DECREF;
-
- list_for_each(el, &rt->weakref_list) {
- JSWeakRefHeader *wh = list_entry(el, JSWeakRefHeader, link);
- switch(wh->weakref_type) {
- case JS_WEAKREF_TYPE_MAP:
- map_delete_weakrefs(rt, wh);
- break;
- case JS_WEAKREF_TYPE_WEAKREF:
- weakref_delete_weakref(rt, wh);
- break;
- case JS_WEAKREF_TYPE_FINREC:
- finrec_delete_weakref(rt, wh);
- break;
- default:
- abort();
- }
- }
- rt->gc_phase = JS_GC_PHASE_NONE;
- /* free the freed objects here. */
- free_zero_refcount(rt);
- }
- static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h,
- JSGCObjectTypeEnum type)
- {
- js_rc(h)->mark = 0;
- js_rc(h)->gc_obj_type = type;
- list_add_tail(&h->link, &rt->gc_obj_list);
- }
- static void remove_gc_object(JSGCObjectHeader *h)
- {
- list_del(&h->link);
- }
- void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
- {
- if (JS_VALUE_HAS_REF_COUNT(val)) {
- switch(JS_VALUE_GET_TAG(val)) {
- case JS_TAG_OBJECT:
- case JS_TAG_FUNCTION_BYTECODE:
- case JS_TAG_MODULE:
- mark_func(rt, JS_VALUE_GET_PTR(val));
- break;
- default:
- break;
- }
- }
- }
- static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp,
- JS_MarkFunc *mark_func)
- {
- switch(js_rc(gp)->gc_obj_type) {
- case JS_GC_OBJ_TYPE_JS_OBJECT:
- {
- JSObject *p = (JSObject *)gp;
- JSShapeProperty *prs;
- JSShape *sh;
- int i;
- sh = p->shape;
- mark_func(rt, &sh->header);
- /* mark all the fields */
- prs = get_shape_prop(sh);
- for(i = 0; i < sh->prop_count; i++) {
- JSProperty *pr = &p->prop[i];
- if (prs->atom != JS_ATOM_NULL) {
- if (prs->flags & JS_PROP_TMASK) {
- if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
- if (pr->u.getset.getter)
- mark_func(rt, &pr->u.getset.getter->header);
- if (pr->u.getset.setter)
- mark_func(rt, &pr->u.getset.setter->header);
- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
- /* Note: the tag does not matter
- provided it is a GC object */
- mark_func(rt, &pr->u.var_ref->header);
- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
- js_autoinit_mark(rt, pr, mark_func);
- }
- } else {
- JS_MarkValue(rt, pr->u.value, mark_func);
- }
- }
- prs++;
- }
- if (p->class_id != JS_CLASS_OBJECT) {
- JSClassGCMark *gc_mark;
- gc_mark = rt->class_array[p->class_id].gc_mark;
- if (gc_mark)
- gc_mark(rt, JS_MKPTR(JS_TAG_OBJECT, p), mark_func);
- }
- }
- break;
- case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
- /* the template objects can be part of a cycle */
- {
- JSFunctionBytecode *b = (JSFunctionBytecode *)gp;
- int i;
- for(i = 0; i < b->cpool_count; i++) {
- JS_MarkValue(rt, b->cpool[i], mark_func);
- }
- if (b->realm)
- mark_func(rt, &b->realm->header);
- }
- break;
- case JS_GC_OBJ_TYPE_VAR_REF:
- {
- JSVarRef *var_ref = (JSVarRef *)gp;
- if (var_ref->is_detached) {
- JS_MarkValue(rt, *var_ref->pvalue, mark_func);
- } else {
- JSStackFrame *sf = var_ref->stack_frame;
- if (sf->js_mode & JS_MODE_ASYNC) {
- JSAsyncFunctionState *async_func = container_of(sf, JSAsyncFunctionState, frame);
- mark_func(rt, &async_func->header);
- }
- }
- }
- break;
- case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
- {
- JSAsyncFunctionState *s = (JSAsyncFunctionState *)gp;
- JSStackFrame *sf = &s->frame;
- JSValue *sp;
- if (!s->is_completed) {
- JS_MarkValue(rt, sf->cur_func, mark_func);
- JS_MarkValue(rt, s->this_val, mark_func);
- /* sf->cur_sp = NULL if the function is running */
- if (sf->cur_sp) {
- /* if the function is running, cur_sp is not known so we
- cannot mark the stack. Marking the variables is not needed
- because a running function cannot be part of a removable
- cycle */
- for(sp = sf->arg_buf; sp < sf->cur_sp; sp++)
- JS_MarkValue(rt, *sp, mark_func);
- }
- }
- JS_MarkValue(rt, s->resolving_funcs[0], mark_func);
- JS_MarkValue(rt, s->resolving_funcs[1], mark_func);
- }
- break;
- case JS_GC_OBJ_TYPE_SHAPE:
- {
- JSShape *sh = (JSShape *)gp;
- if (sh->proto != NULL) {
- mark_func(rt, &sh->proto->header);
- }
- }
- break;
- case JS_GC_OBJ_TYPE_JS_CONTEXT:
- {
- JSContext *ctx = (JSContext *)gp;
- JS_MarkContext(rt, ctx, mark_func);
- }
- break;
- case JS_GC_OBJ_TYPE_MODULE:
- {
- JSModuleDef *m = (JSModuleDef *)gp;
- js_mark_module_def(rt, m, mark_func);
- }
- break;
- default:
- abort();
- }
- }
- static void gc_decref_child(JSRuntime *rt, JSGCObjectHeader *p)
- {
- assert(js_rc(p)->ref_count > 0);
- js_rc(p)->ref_count--;
- if (js_rc(p)->ref_count == 0 && js_rc(p)->mark == 1) {
- list_del(&p->link);
- list_add_tail(&p->link, &rt->tmp_obj_list);
- }
- }
- static void gc_decref(JSRuntime *rt)
- {
- struct list_head *el, *el1;
- JSGCObjectHeader *p;
- init_list_head(&rt->tmp_obj_list);
- /* decrement the refcount of all the children of all the GC
- objects and move the GC objects with zero refcount to
- tmp_obj_list */
- list_for_each_safe(el, el1, &rt->gc_obj_list) {
- p = list_entry(el, JSGCObjectHeader, link);
- assert(js_rc(p)->mark == 0);
- mark_children(rt, p, gc_decref_child);
- js_rc(p)->mark = 1;
- if (js_rc(p)->ref_count == 0) {
- list_del(&p->link);
- list_add_tail(&p->link, &rt->tmp_obj_list);
- }
- }
- }
- static void gc_scan_incref_child(JSRuntime *rt, JSGCObjectHeader *p)
- {
- js_rc(p)->ref_count++;
- if (js_rc(p)->ref_count == 1) {
- /* ref_count was 0: remove from tmp_obj_list and add at the
- end of gc_obj_list */
- list_del(&p->link);
- list_add_tail(&p->link, &rt->gc_obj_list);
- js_rc(p)->mark = 0; /* reset the mark for the next GC call */
- }
- }
- static void gc_scan_incref_child2(JSRuntime *rt, JSGCObjectHeader *p)
- {
- js_rc(p)->ref_count++;
- }
- static void gc_scan(JSRuntime *rt)
- {
- struct list_head *el;
- JSGCObjectHeader *p;
- /* keep the objects with a refcount > 0 and their children. */
- list_for_each(el, &rt->gc_obj_list) {
- p = list_entry(el, JSGCObjectHeader, link);
- assert(js_rc(p)->ref_count > 0);
- js_rc(p)->mark = 0; /* reset the mark for the next GC call */
- mark_children(rt, p, gc_scan_incref_child);
- }
- /* restore the refcount of the objects to be deleted. */
- list_for_each(el, &rt->tmp_obj_list) {
- p = list_entry(el, JSGCObjectHeader, link);
- mark_children(rt, p, gc_scan_incref_child2);
- }
- }
- static void gc_free_cycles(JSRuntime *rt)
- {
- struct list_head *el, *el1;
- JSGCObjectHeader *p;
- #ifdef DUMP_GC_FREE
- BOOL header_done = FALSE;
- #endif
- rt->gc_phase = JS_GC_PHASE_REMOVE_CYCLES;
- for(;;) {
- el = rt->tmp_obj_list.next;
- if (el == &rt->tmp_obj_list)
- break;
- p = list_entry(el, JSGCObjectHeader, link);
- /* Only need to free the GC object associated with JS values
- or async functions. The rest will be automatically removed
- because they must be referenced by them. */
- switch(js_rc(p)->gc_obj_type) {
- case JS_GC_OBJ_TYPE_JS_OBJECT:
- case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
- case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
- case JS_GC_OBJ_TYPE_MODULE:
- #ifdef DUMP_GC_FREE
- if (!header_done) {
- printf("Freeing cycles:\n");
- JS_DumpObjectHeader(rt);
- header_done = TRUE;
- }
- JS_DumpGCObject(rt, p);
- #endif
- free_gc_object(rt, p);
- break;
- default:
- list_del(&p->link);
- list_add_tail(&p->link, &rt->gc_zero_ref_count_list);
- break;
- }
- }
- rt->gc_phase = JS_GC_PHASE_NONE;
- list_for_each_safe(el, el1, &rt->gc_zero_ref_count_list) {
- p = list_entry(el, JSGCObjectHeader, link);
- assert(js_rc(p)->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT ||
- js_rc(p)->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE ||
- js_rc(p)->gc_obj_type == JS_GC_OBJ_TYPE_ASYNC_FUNCTION ||
- js_rc(p)->gc_obj_type == JS_GC_OBJ_TYPE_MODULE);
- if (js_rc(p)->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT &&
- ((JSObject *)p)->weakref_count != 0) {
- /* keep the object because there are weak references to it */
- js_rc(p)->mark = 0;
- } else {
- js_free_rt(rt, p);
- }
- }
- init_list_head(&rt->gc_zero_ref_count_list);
- }
- static void JS_RunGCInternal(JSRuntime *rt, BOOL remove_weak_objects)
- {
- if (remove_weak_objects) {
- /* free the weakly referenced object or symbol structures, delete
- the associated Map/Set entries and queue the finalization
- registry callbacks. */
- gc_remove_weak_objects(rt);
- }
-
- /* decrement the reference of the children of each object. mark =
- 1 after this pass. */
- gc_decref(rt);
- /* keep the GC objects with a non zero refcount and their childs */
- gc_scan(rt);
- /* free the GC objects in a cycle */
- gc_free_cycles(rt);
- }
- void JS_RunGC(JSRuntime *rt)
- {
- JS_RunGCInternal(rt, TRUE);
- }
- /* Return false if not an object or if the object has already been
- freed (zombie objects are visible in finalizers when freeing
- cycles). */
- BOOL JS_IsLiveObject(JSRuntime *rt, JSValueConst obj)
- {
- JSObject *p;
- if (!JS_IsObject(obj))
- return FALSE;
- p = JS_VALUE_GET_OBJ(obj);
- return !p->free_mark;
- }
- /* Compute memory used by various object types */
- /* XXX: poor man's approach to handling multiply referenced objects */
- typedef struct JSMemoryUsage_helper {
- double memory_used_count;
- double str_count;
- double str_size;
- int64_t js_func_count;
- double js_func_size;
- int64_t js_func_code_size;
- int64_t js_func_pc2line_count;
- int64_t js_func_pc2line_size;
- } JSMemoryUsage_helper;
- static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp);
- static void compute_jsstring_size(JSString *str, JSMemoryUsage_helper *hp)
- {
- if (!str->atom_type) { /* atoms are handled separately */
- double s_ref_count = js_rc(str)->ref_count;
- hp->str_count += 1 / s_ref_count;
- hp->str_size += ((sizeof(*str) + (str->len << str->is_wide_char) +
- 1 - str->is_wide_char) / s_ref_count);
- }
- }
- static void compute_bytecode_size(JSFunctionBytecode *b, JSMemoryUsage_helper *hp)
- {
- int memory_used_count, js_func_size, i;
- memory_used_count = 0;
- js_func_size = offsetof(JSFunctionBytecode, debug);
- if (b->vardefs) {
- js_func_size += (b->arg_count + b->var_count) * sizeof(*b->vardefs);
- }
- if (b->cpool) {
- js_func_size += b->cpool_count * sizeof(*b->cpool);
- for (i = 0; i < b->cpool_count; i++) {
- JSValueConst val = b->cpool[i];
- compute_value_size(val, hp);
- }
- }
- if (b->closure_var) {
- js_func_size += b->closure_var_count * sizeof(*b->closure_var);
- }
- if (!b->read_only_bytecode && b->byte_code_buf) {
- hp->js_func_code_size += b->byte_code_len;
- }
- if (b->has_debug) {
- js_func_size += sizeof(*b) - offsetof(JSFunctionBytecode, debug);
- if (b->debug.source) {
- memory_used_count++;
- js_func_size += b->debug.source_len + 1;
- }
- if (b->debug.pc2line_len) {
- memory_used_count++;
- hp->js_func_pc2line_count += 1;
- hp->js_func_pc2line_size += b->debug.pc2line_len;
- }
- }
- hp->js_func_size += js_func_size;
- hp->js_func_count += 1;
- hp->memory_used_count += memory_used_count;
- }
- static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp)
- {
- switch(JS_VALUE_GET_TAG(val)) {
- case JS_TAG_STRING:
- compute_jsstring_size(JS_VALUE_GET_STRING(val), hp);
- break;
- case JS_TAG_BIG_INT:
- /* should track JSBigInt usage */
- break;
- }
- }
- void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s)
- {
- struct list_head *el, *el1;
- int i;
- JSMemoryUsage_helper mem = { 0 }, *hp = &mem;
- memset(s, 0, sizeof(*s));
- s->malloc_count = rt->malloc_ctx.malloc_state.malloc_count;
- s->malloc_size = rt->malloc_ctx.malloc_state.malloc_size;
- s->malloc_limit = rt->malloc_ctx.malloc_state.malloc_limit;
- s->memory_used_count = 2; /* rt + rt->class_array */
- s->memory_used_size = sizeof(JSRuntime) + sizeof(JSValue) * rt->class_count;
- list_for_each(el, &rt->context_list) {
- JSContext *ctx = list_entry(el, JSContext, link);
- JSShape *sh = ctx->array_shape;
- s->memory_used_count += 2; /* ctx + ctx->class_proto */
- s->memory_used_size += sizeof(JSContext) +
- sizeof(JSValue) * rt->class_count;
- s->binary_object_count += ctx->binary_object_count;
- s->binary_object_size += ctx->binary_object_size;
- /* the hashed shapes are counted separately */
- if (sh && !sh->is_hashed) {
- int hash_size = sh->prop_hash_mask + 1;
- s->shape_count++;
- s->shape_size += get_shape_size(hash_size, sh->prop_size);
- }
- list_for_each(el1, &ctx->loaded_modules) {
- JSModuleDef *m = list_entry(el1, JSModuleDef, link);
- s->memory_used_count += 1;
- s->memory_used_size += sizeof(*m);
- if (m->req_module_entries) {
- s->memory_used_count += 1;
- s->memory_used_size += m->req_module_entries_count * sizeof(*m->req_module_entries);
- }
- if (m->export_entries) {
- s->memory_used_count += 1;
- s->memory_used_size += m->export_entries_count * sizeof(*m->export_entries);
- for (i = 0; i < m->export_entries_count; i++) {
- JSExportEntry *me = &m->export_entries[i];
- if (me->export_type == JS_EXPORT_TYPE_LOCAL && me->u.local.var_ref) {
- /* potential multiple count */
- s->memory_used_count += 1;
- compute_value_size(me->u.local.var_ref->value, hp);
- }
- }
- }
- if (m->star_export_entries) {
- s->memory_used_count += 1;
- s->memory_used_size += m->star_export_entries_count * sizeof(*m->star_export_entries);
- }
- if (m->import_entries) {
- s->memory_used_count += 1;
- s->memory_used_size += m->import_entries_count * sizeof(*m->import_entries);
- }
- compute_value_size(m->module_ns, hp);
- compute_value_size(m->func_obj, hp);
- }
- }
- list_for_each(el, &rt->gc_obj_list) {
- JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link);
- JSObject *p;
- JSShape *sh;
- JSShapeProperty *prs;
- /* XXX: could count the other GC object types too */
- if (js_rc(gp)->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE) {
- compute_bytecode_size((JSFunctionBytecode *)gp, hp);
- continue;
- } else if (js_rc(gp)->gc_obj_type != JS_GC_OBJ_TYPE_JS_OBJECT) {
- continue;
- }
- p = (JSObject *)gp;
- sh = p->shape;
- s->obj_count++;
- if (p->prop) {
- s->memory_used_count++;
- s->prop_size += sh->prop_size * sizeof(*p->prop);
- s->prop_count += sh->prop_count;
- prs = get_shape_prop(sh);
- for(i = 0; i < sh->prop_count; i++) {
- JSProperty *pr = &p->prop[i];
- if (prs->atom != JS_ATOM_NULL && !(prs->flags & JS_PROP_TMASK)) {
- compute_value_size(pr->u.value, hp);
- }
- prs++;
- }
- }
- /* the hashed shapes are counted separately */
- if (!sh->is_hashed) {
- int hash_size = sh->prop_hash_mask + 1;
- s->shape_count++;
- s->shape_size += get_shape_size(hash_size, sh->prop_size);
- }
- switch(p->class_id) {
- case JS_CLASS_ARRAY: /* u.array | length */
- case JS_CLASS_ARGUMENTS: /* u.array | length */
- s->array_count++;
- if (p->fast_array) {
- s->fast_array_count++;
- if (p->u.array.u.values) {
- s->memory_used_count++;
- s->memory_used_size += p->u.array.count *
- sizeof(*p->u.array.u.values);
- s->fast_array_elements += p->u.array.count;
- for (i = 0; i < p->u.array.count; i++) {
- compute_value_size(p->u.array.u.values[i], hp);
- }
- }
- }
- break;
- case JS_CLASS_MAPPED_ARGUMENTS: /* u.array | length */
- if (p->fast_array) {
- s->fast_array_count++;
- if (p->u.array.u.values) {
- s->memory_used_count++;
- s->memory_used_size += p->u.array.count *
- sizeof(*p->u.array.u.var_refs);
- s->fast_array_elements += p->u.array.count;
- for (i = 0; i < p->u.array.count; i++) {
- compute_value_size(*p->u.array.u.var_refs[i]->pvalue, hp);
- }
- }
- }
- break;
- case JS_CLASS_NUMBER: /* u.object_data */
- case JS_CLASS_STRING: /* u.object_data */
- case JS_CLASS_BOOLEAN: /* u.object_data */
- case JS_CLASS_SYMBOL: /* u.object_data */
- case JS_CLASS_DATE: /* u.object_data */
- case JS_CLASS_BIG_INT: /* u.object_data */
- compute_value_size(p->u.object_data, hp);
- break;
- case JS_CLASS_C_FUNCTION: /* u.cfunc */
- s->c_func_count++;
- break;
- case JS_CLASS_BYTECODE_FUNCTION: /* u.func */
- {
- JSFunctionBytecode *b = p->u.func.function_bytecode;
- JSVarRef **var_refs = p->u.func.var_refs;
- /* home_object: object will be accounted for in list scan */
- if (var_refs) {
- s->memory_used_count++;
- s->js_func_size += b->closure_var_count * sizeof(*var_refs);
- for (i = 0; i < b->closure_var_count; i++) {
- if (var_refs[i]) {
- double ref_count = js_rc(var_refs[i])->ref_count;
- s->memory_used_count += 1 / ref_count;
- s->js_func_size += sizeof(*var_refs[i]) / ref_count;
- /* handle non object closed values */
- if (var_refs[i]->pvalue == &var_refs[i]->value) {
- /* potential multiple count */
- compute_value_size(var_refs[i]->value, hp);
- }
- }
- }
- }
- }
- break;
- case JS_CLASS_BOUND_FUNCTION: /* u.bound_function */
- {
- JSBoundFunction *bf = p->u.bound_function;
- /* func_obj and this_val are objects */
- for (i = 0; i < bf->argc; i++) {
- compute_value_size(bf->argv[i], hp);
- }
- s->memory_used_count += 1;
- s->memory_used_size += sizeof(*bf) + bf->argc * sizeof(*bf->argv);
- }
- break;
- case JS_CLASS_C_FUNCTION_DATA: /* u.c_function_data_record */
- {
- JSCFunctionDataRecord *fd = p->u.c_function_data_record;
- if (fd) {
- for (i = 0; i < fd->data_len; i++) {
- compute_value_size(fd->data[i], hp);
- }
- s->memory_used_count += 1;
- s->memory_used_size += sizeof(*fd) + fd->data_len * sizeof(*fd->data);
- }
- }
- break;
- case JS_CLASS_REGEXP: /* u.regexp */
- compute_jsstring_size(p->u.regexp.pattern, hp);
- compute_jsstring_size(p->u.regexp.bytecode, hp);
- break;
- case JS_CLASS_FOR_IN_ITERATOR: /* u.for_in_iterator */
- {
- JSForInIterator *it = p->u.for_in_iterator;
- if (it) {
- compute_value_size(it->obj, hp);
- s->memory_used_count += 1;
- s->memory_used_size += sizeof(*it);
- }
- }
- break;
- case JS_CLASS_ARRAY_BUFFER: /* u.array_buffer */
- case JS_CLASS_SHARED_ARRAY_BUFFER: /* u.array_buffer */
- {
- JSArrayBuffer *abuf = p->u.array_buffer;
- if (abuf) {
- s->memory_used_count += 1;
- s->memory_used_size += sizeof(*abuf);
- if (abuf->data) {
- s->memory_used_count += 1;
- s->memory_used_size += abuf->byte_length;
- }
- }
- }
- break;
- case JS_CLASS_GENERATOR: /* u.generator_data */
- case JS_CLASS_UINT8C_ARRAY: /* u.typed_array / u.array */
- case JS_CLASS_INT8_ARRAY: /* u.typed_array / u.array */
- case JS_CLASS_UINT8_ARRAY: /* u.typed_array / u.array */
- case JS_CLASS_INT16_ARRAY: /* u.typed_array / u.array */
- case JS_CLASS_UINT16_ARRAY: /* u.typed_array / u.array */
- case JS_CLASS_INT32_ARRAY: /* u.typed_array / u.array */
- case JS_CLASS_UINT32_ARRAY: /* u.typed_array / u.array */
- case JS_CLASS_BIG_INT64_ARRAY: /* u.typed_array / u.array */
- case JS_CLASS_BIG_UINT64_ARRAY: /* u.typed_array / u.array */
- case JS_CLASS_FLOAT16_ARRAY: /* u.typed_array / u.array */
- case JS_CLASS_FLOAT32_ARRAY: /* u.typed_array / u.array */
- case JS_CLASS_FLOAT64_ARRAY: /* u.typed_array / u.array */
- case JS_CLASS_DATAVIEW: /* u.typed_array */
- case JS_CLASS_MAP: /* u.map_state */
- case JS_CLASS_SET: /* u.map_state */
- case JS_CLASS_WEAKMAP: /* u.map_state */
- case JS_CLASS_WEAKSET: /* u.map_state */
- case JS_CLASS_MAP_ITERATOR: /* u.map_iterator_data */
- case JS_CLASS_SET_ITERATOR: /* u.map_iterator_data */
- case JS_CLASS_ARRAY_ITERATOR: /* u.array_iterator_data */
- case JS_CLASS_STRING_ITERATOR: /* u.array_iterator_data */
- case JS_CLASS_PROXY: /* u.proxy_data */
- case JS_CLASS_PROMISE: /* u.promise_data */
- case JS_CLASS_PROMISE_RESOLVE_FUNCTION: /* u.promise_function_data */
- case JS_CLASS_PROMISE_REJECT_FUNCTION: /* u.promise_function_data */
- case JS_CLASS_ASYNC_FUNCTION_RESOLVE: /* u.async_function_data */
- case JS_CLASS_ASYNC_FUNCTION_REJECT: /* u.async_function_data */
- case JS_CLASS_ASYNC_FROM_SYNC_ITERATOR: /* u.async_from_sync_iterator_data */
- case JS_CLASS_ASYNC_GENERATOR: /* u.async_generator_data */
- /* TODO */
- default:
- /* XXX: class definition should have an opaque block size */
- if (p->u.opaque) {
- s->memory_used_count += 1;
- }
- break;
- }
- }
- s->obj_size += s->obj_count * sizeof(JSObject);
- /* hashed shapes */
- s->memory_used_count++; /* rt->shape_hash */
- s->memory_used_size += sizeof(rt->shape_hash[0]) * rt->shape_hash_size;
- for(i = 0; i < rt->shape_hash_size; i++) {
- JSShape *sh;
- for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) {
- int hash_size = sh->prop_hash_mask + 1;
- s->shape_count++;
- s->shape_size += get_shape_size(hash_size, sh->prop_size);
- }
- }
- /* atoms */
- s->memory_used_count += 2; /* rt->atom_array, rt->atom_hash */
- s->atom_count = rt->atom_count;
- s->atom_size = sizeof(rt->atom_array[0]) * rt->atom_size +
- sizeof(rt->atom_hash[0]) * rt->atom_hash_size;
- for(i = 0; i < rt->atom_size; i++) {
- JSAtomStruct *p = rt->atom_array[i];
- if (!atom_is_free(p)) {
- s->atom_size += (sizeof(*p) + (p->len << p->is_wide_char) +
- 1 - p->is_wide_char);
- }
- }
- s->str_count = round(mem.str_count);
- s->str_size = round(mem.str_size);
- s->js_func_count = mem.js_func_count;
- s->js_func_size = round(mem.js_func_size);
- s->js_func_code_size = mem.js_func_code_size;
- s->js_func_pc2line_count = mem.js_func_pc2line_count;
- s->js_func_pc2line_size = mem.js_func_pc2line_size;
- s->memory_used_count += round(mem.memory_used_count) +
- s->atom_count + s->str_count +
- s->obj_count + s->shape_count +
- s->js_func_count + s->js_func_pc2line_count;
- s->memory_used_size += s->atom_size + s->str_size +
- s->obj_size + s->prop_size + s->shape_size +
- s->js_func_size + s->js_func_code_size + s->js_func_pc2line_size;
- }
- void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt)
- {
- fprintf(fp, "QuickJS memory usage -- " CONFIG_VERSION " version, %d-bit, malloc limit: %"PRId64"\n\n",
- (int)sizeof(void *) * 8, s->malloc_limit);
- #if 1
- if (rt) {
- static const struct {
- const char *name;
- size_t size;
- } object_types[] = {
- { "JSRuntime", sizeof(JSRuntime) },
- { "JSContext", sizeof(JSContext) },
- { "JSObject", sizeof(JSObject) },
- { "JSString", sizeof(JSString) },
- { "JSFunctionBytecode", sizeof(JSFunctionBytecode) },
- };
- int i, usage_size_ok = 0;
- for(i = 0; i < countof(object_types); i++) {
- unsigned int size = object_types[i].size;
- void *p = js_malloc_rt(rt, size);
- if (p) {
- unsigned int size1 = js_malloc_usable_size_rt(rt, p);
- if (size1 >= size) {
- usage_size_ok = 1;
- fprintf(fp, " %3u + %-2u %s\n",
- size, size1 - size, object_types[i].name);
- }
- js_free_rt(rt, p);
- }
- }
- if (!usage_size_ok) {
- fprintf(fp, " malloc_usable_size unavailable\n");
- }
- {
- int obj_classes[JS_CLASS_INIT_COUNT + 1] = { 0 };
- int class_id;
- struct list_head *el;
- list_for_each(el, &rt->gc_obj_list) {
- JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link);
- JSObject *p;
- if (js_rc(gp)->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
- p = (JSObject *)gp;
- obj_classes[min_uint32(p->class_id, JS_CLASS_INIT_COUNT)]++;
- }
- }
- fprintf(fp, "\n" "JSObject classes\n");
- if (obj_classes[0])
- fprintf(fp, " %5d %2.0d %s\n", obj_classes[0], 0, "none");
- for (class_id = 1; class_id < JS_CLASS_INIT_COUNT; class_id++) {
- if (obj_classes[class_id] && class_id < rt->class_count) {
- char buf[ATOM_GET_STR_BUF_SIZE];
- fprintf(fp, " %5d %2.0d %s\n", obj_classes[class_id], class_id,
- JS_AtomGetStrRT(rt, buf, sizeof(buf), rt->class_array[class_id].class_name));
- }
- }
- if (obj_classes[JS_CLASS_INIT_COUNT])
- fprintf(fp, " %5d %2.0d %s\n", obj_classes[JS_CLASS_INIT_COUNT], 0, "other");
- }
- fprintf(fp, "\n");
- }
- #endif
- fprintf(fp, "%-20s %8s %8s\n", "NAME", "COUNT", "SIZE");
- if (s->malloc_count) {
- fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per block)\n",
- "memory allocated", s->malloc_count, s->malloc_size,
- (double)s->malloc_size / s->malloc_count);
- fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%d overhead, %0.1f average slack)\n",
- "memory used", s->memory_used_count, s->memory_used_size,
- MALLOC_OVERHEAD, ((double)(s->malloc_size - s->memory_used_size) /
- s->memory_used_count));
- }
- if (s->atom_count) {
- fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per atom)\n",
- "atoms", s->atom_count, s->atom_size,
- (double)s->atom_size / s->atom_count);
- }
- if (s->str_count) {
- fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per string)\n",
- "strings", s->str_count, s->str_size,
- (double)s->str_size / s->str_count);
- }
- if (s->obj_count) {
- fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n",
- "objects", s->obj_count, s->obj_size,
- (double)s->obj_size / s->obj_count);
- fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n",
- " properties", s->prop_count, s->prop_size,
- (double)s->prop_count / s->obj_count);
- fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per shape)\n",
- " shapes", s->shape_count, s->shape_size,
- (double)s->shape_size / s->shape_count);
- }
- if (s->js_func_count) {
- fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n",
- "bytecode functions", s->js_func_count, s->js_func_size);
- fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n",
- " bytecode", s->js_func_count, s->js_func_code_size,
- (double)s->js_func_code_size / s->js_func_count);
- if (s->js_func_pc2line_count) {
- fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n",
- " pc2line", s->js_func_pc2line_count,
- s->js_func_pc2line_size,
- (double)s->js_func_pc2line_size / s->js_func_pc2line_count);
- }
- }
- if (s->c_func_count) {
- fprintf(fp, "%-20s %8"PRId64"\n", "C functions", s->c_func_count);
- }
- if (s->array_count) {
- fprintf(fp, "%-20s %8"PRId64"\n", "arrays", s->array_count);
- if (s->fast_array_count) {
- fprintf(fp, "%-20s %8"PRId64"\n", " fast arrays", s->fast_array_count);
- fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per fast array)\n",
- " elements", s->fast_array_elements,
- s->fast_array_elements * (int)sizeof(JSValue),
- (double)s->fast_array_elements / s->fast_array_count);
- }
- }
- if (s->binary_object_count) {
- fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n",
- "binary objects", s->binary_object_count, s->binary_object_size);
- }
- }
- JSValue JS_GetGlobalObject(JSContext *ctx)
- {
- return JS_DupValue(ctx, ctx->global_obj);
- }
- /* WARNING: obj is freed */
- JSValue JS_Throw(JSContext *ctx, JSValue obj)
- {
- JSRuntime *rt = ctx->rt;
- JS_FreeValue(ctx, rt->current_exception);
- rt->current_exception = obj;
- rt->current_exception_is_uncatchable = FALSE;
- return JS_EXCEPTION;
- }
- /* return the pending exception (cannot be called twice). */
- JSValue JS_GetException(JSContext *ctx)
- {
- JSValue val;
- JSRuntime *rt = ctx->rt;
- val = rt->current_exception;
- rt->current_exception = JS_UNINITIALIZED;
- return val;
- }
- JS_BOOL JS_HasException(JSContext *ctx)
- {
- return !JS_IsUninitialized(ctx->rt->current_exception);
- }
- static void dbuf_put_leb128(DynBuf *s, uint32_t v)
- {
- uint32_t a;
- for(;;) {
- a = v & 0x7f;
- v >>= 7;
- if (v != 0) {
- dbuf_putc(s, a | 0x80);
- } else {
- dbuf_putc(s, a);
- break;
- }
- }
- }
- static void dbuf_put_sleb128(DynBuf *s, int32_t v1)
- {
- uint32_t v = v1;
- dbuf_put_leb128(s, (2 * v) ^ -(v >> 31));
- }
- static int get_leb128(uint32_t *pval, const uint8_t *buf,
- const uint8_t *buf_end)
- {
- const uint8_t *ptr = buf;
- uint32_t v, a, i;
- v = 0;
- for(i = 0; i < 5; i++) {
- if (unlikely(ptr >= buf_end))
- break;
- a = *ptr++;
- v |= (a & 0x7f) << (i * 7);
- if (!(a & 0x80)) {
- *pval = v;
- return ptr - buf;
- }
- }
- *pval = 0;
- return -1;
- }
- static int get_sleb128(int32_t *pval, const uint8_t *buf,
- const uint8_t *buf_end)
- {
- int ret;
- uint32_t val;
- ret = get_leb128(&val, buf, buf_end);
- if (ret < 0) {
- *pval = 0;
- return -1;
- }
- *pval = (val >> 1) ^ -(val & 1);
- return ret;
- }
- /* use pc_value = -1 to get the position of the function definition */
- static int find_line_num(JSContext *ctx, JSFunctionBytecode *b,
- uint32_t pc_value, int *pcol_num)
- {
- const uint8_t *p_end, *p;
- int new_line_num, line_num, pc, v, ret, new_col_num, col_num;
- uint32_t val;
- unsigned int op;
- if (!b->has_debug || !b->debug.pc2line_buf)
- goto fail; /* function was stripped */
- p = b->debug.pc2line_buf;
- p_end = p + b->debug.pc2line_len;
- /* get the function line and column numbers */
- ret = get_leb128(&val, p, p_end);
- if (ret < 0)
- goto fail;
- p += ret;
- line_num = val + 1;
- ret = get_leb128(&val, p, p_end);
- if (ret < 0)
- goto fail;
- p += ret;
- col_num = val + 1;
- if (pc_value != -1) {
- pc = 0;
- while (p < p_end) {
- op = *p++;
- if (op == 0) {
- ret = get_leb128(&val, p, p_end);
- if (ret < 0)
- goto fail;
- pc += val;
- p += ret;
- ret = get_sleb128(&v, p, p_end);
- if (ret < 0)
- goto fail;
- p += ret;
- new_line_num = line_num + v;
- } else {
- op -= PC2LINE_OP_FIRST;
- pc += (op / PC2LINE_RANGE);
- new_line_num = line_num + (op % PC2LINE_RANGE) + PC2LINE_BASE;
- }
- ret = get_sleb128(&v, p, p_end);
- if (ret < 0)
- goto fail;
- p += ret;
- new_col_num = col_num + v;
-
- if (pc_value < pc)
- goto done;
- line_num = new_line_num;
- col_num = new_col_num;
- }
- }
- done:
- *pcol_num = col_num;
- return line_num;
- fail:
- *pcol_num = 0;
- return 0;
- }
- /* return a string property without executing arbitrary JS code (used
- when dumping the stack trace or in debug print). */
- static const char *get_prop_string(JSContext *ctx, JSValueConst obj, JSAtom prop)
- {
- JSObject *p;
- JSProperty *pr;
- JSShapeProperty *prs;
- JSValueConst val;
- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
- return NULL;
- p = JS_VALUE_GET_OBJ(obj);
- prs = find_own_property(&pr, p, prop);
- if (!prs) {
- /* we look at one level in the prototype to handle the 'name'
- field of the Error objects */
- p = p->shape->proto;
- if (!p)
- return NULL;
- prs = find_own_property(&pr, p, prop);
- if (!prs)
- return NULL;
- }
-
- if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL)
- return NULL;
- val = pr->u.value;
- if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING)
- return NULL;
- return JS_ToCString(ctx, val);
- }
- #define JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL (1 << 0)
- /* if filename != NULL, an additional level is added with the filename
- and line number information (used for parse error). */
- static void build_backtrace(JSContext *ctx, JSValueConst error_obj,
- const char *filename, int line_num, int col_num,
- int backtrace_flags)
- {
- JSStackFrame *sf;
- JSValue str;
- DynBuf dbuf;
- const char *func_name_str;
- const char *str1;
- JSObject *p;
- if (!JS_IsObject(error_obj))
- return; /* protection in the out of memory case */
-
- js_dbuf_init(ctx, &dbuf);
- if (filename) {
- dbuf_printf(&dbuf, " at %s", filename);
- if (line_num != -1)
- dbuf_printf(&dbuf, ":%d:%d", line_num, col_num);
- dbuf_putc(&dbuf, '\n');
- str = JS_NewString(ctx, filename);
- if (JS_IsException(str))
- return;
- /* Note: SpiderMonkey does that, could update once there is a standard */
- if (JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_fileName, str,
- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE) < 0 ||
- JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_lineNumber, JS_NewInt32(ctx, line_num),
- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE) < 0 ||
- JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_columnNumber, JS_NewInt32(ctx, col_num),
- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE) < 0) {
- return;
- }
- }
- for(sf = ctx->rt->current_stack_frame; sf != NULL; sf = sf->prev_frame) {
- if (sf->js_mode & JS_MODE_BACKTRACE_BARRIER)
- break;
- if (backtrace_flags & JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL) {
- backtrace_flags &= ~JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL;
- continue;
- }
- func_name_str = get_prop_string(ctx, sf->cur_func, JS_ATOM_name);
- if (!func_name_str || func_name_str[0] == '\0')
- str1 = "<anonymous>";
- else
- str1 = func_name_str;
- dbuf_printf(&dbuf, " at %s", str1);
- JS_FreeCString(ctx, func_name_str);
- p = JS_VALUE_GET_OBJ(sf->cur_func);
- if (js_class_has_bytecode(p->class_id)) {
- JSFunctionBytecode *b;
- const char *atom_str;
- int line_num1, col_num1;
- b = p->u.func.function_bytecode;
- if (b->has_debug) {
- line_num1 = find_line_num(ctx, b,
- sf->cur_pc - b->byte_code_buf - 1, &col_num1);
- atom_str = JS_AtomToCString(ctx, b->debug.filename);
- dbuf_printf(&dbuf, " (%s",
- atom_str ? atom_str : "<null>");
- JS_FreeCString(ctx, atom_str);
- if (line_num1 != 0)
- dbuf_printf(&dbuf, ":%d:%d", line_num1, col_num1);
- dbuf_putc(&dbuf, ')');
- }
- } else {
- dbuf_printf(&dbuf, " (native)");
- }
- dbuf_putc(&dbuf, '\n');
- }
- dbuf_putc(&dbuf, '\0');
- if (dbuf_error(&dbuf))
- str = JS_NULL;
- else
- str = JS_NewString(ctx, (char *)dbuf.buf);
- dbuf_free(&dbuf);
- JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_stack, str,
- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
- }
- /* Note: it is important that no exception is returned by this function */
- static BOOL is_backtrace_needed(JSContext *ctx, JSValueConst obj)
- {
- JSObject *p;
- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
- return FALSE;
- p = JS_VALUE_GET_OBJ(obj);
- if (p->class_id != JS_CLASS_ERROR)
- return FALSE;
- if (find_own_property1(p, JS_ATOM_stack))
- return FALSE;
- return TRUE;
- }
- JSValue JS_NewError(JSContext *ctx)
- {
- return JS_NewObjectClass(ctx, JS_CLASS_ERROR);
- }
- static JSValue JS_ThrowError2(JSContext *ctx, JSErrorEnum error_num,
- const char *fmt, va_list ap, BOOL add_backtrace)
- {
- char buf[256];
- JSValue obj, ret;
- vsnprintf(buf, sizeof(buf), fmt, ap);
- obj = JS_NewObjectProtoClass(ctx, ctx->native_error_proto[error_num],
- JS_CLASS_ERROR);
- if (unlikely(JS_IsException(obj))) {
- /* out of memory: throw JS_NULL to avoid recursing */
- obj = JS_NULL;
- } else {
- JS_DefinePropertyValue(ctx, obj, JS_ATOM_message,
- JS_NewString(ctx, buf),
- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
- if (add_backtrace) {
- build_backtrace(ctx, obj, NULL, 0, 0, 0);
- }
- }
- ret = JS_Throw(ctx, obj);
- return ret;
- }
- static JSValue JS_ThrowError(JSContext *ctx, JSErrorEnum error_num,
- const char *fmt, va_list ap)
- {
- JSRuntime *rt = ctx->rt;
- JSStackFrame *sf;
- BOOL add_backtrace;
- /* the backtrace is added later if called from a bytecode function */
- sf = rt->current_stack_frame;
- add_backtrace = !rt->in_out_of_memory &&
- (!sf || (JS_GetFunctionBytecode(sf->cur_func) == NULL));
- return JS_ThrowError2(ctx, error_num, fmt, ap, add_backtrace);
- }
- JSValue __attribute__((format(printf, 2, 3))) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...)
- {
- JSValue val;
- va_list ap;
- va_start(ap, fmt);
- val = JS_ThrowError(ctx, JS_SYNTAX_ERROR, fmt, ap);
- va_end(ap);
- return val;
- }
- JSValue __attribute__((format(printf, 2, 3))) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...)
- {
- JSValue val;
- va_list ap;
- va_start(ap, fmt);
- val = JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap);
- va_end(ap);
- return val;
- }
- static int __attribute__((format(printf, 3, 4))) JS_ThrowTypeErrorOrFalse(JSContext *ctx, int flags, const char *fmt, ...)
- {
- va_list ap;
- if ((flags & JS_PROP_THROW) ||
- ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
- va_start(ap, fmt);
- JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap);
- va_end(ap);
- return -1;
- } else {
- return FALSE;
- }
- }
- /* never use it directly */
- static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowTypeErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...)
- {
- char buf[ATOM_GET_STR_BUF_SIZE];
- return JS_ThrowTypeError(ctx, fmt,
- JS_AtomGetStr(ctx, buf, sizeof(buf), atom));
- }
- /* never use it directly */
- static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowSyntaxErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...)
- {
- char buf[ATOM_GET_STR_BUF_SIZE];
- return JS_ThrowSyntaxError(ctx, fmt,
- JS_AtomGetStr(ctx, buf, sizeof(buf), atom));
- }
- /* %s is replaced by 'atom'. The macro is used so that gcc can check
- the format string. */
- #define JS_ThrowTypeErrorAtom(ctx, fmt, atom) __JS_ThrowTypeErrorAtom(ctx, atom, fmt, "")
- #define JS_ThrowSyntaxErrorAtom(ctx, fmt, atom) __JS_ThrowSyntaxErrorAtom(ctx, atom, fmt, "")
- static int JS_ThrowTypeErrorReadOnly(JSContext *ctx, int flags, JSAtom atom)
- {
- if ((flags & JS_PROP_THROW) ||
- ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
- JS_ThrowTypeErrorAtom(ctx, "'%s' is read-only", atom);
- return -1;
- } else {
- return FALSE;
- }
- }
- JSValue __attribute__((format(printf, 2, 3))) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...)
- {
- JSValue val;
- va_list ap;
- va_start(ap, fmt);
- val = JS_ThrowError(ctx, JS_REFERENCE_ERROR, fmt, ap);
- va_end(ap);
- return val;
- }
- JSValue __attribute__((format(printf, 2, 3))) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...)
- {
- JSValue val;
- va_list ap;
- va_start(ap, fmt);
- val = JS_ThrowError(ctx, JS_RANGE_ERROR, fmt, ap);
- va_end(ap);
- return val;
- }
- JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...)
- {
- JSValue val;
- va_list ap;
- va_start(ap, fmt);
- val = JS_ThrowError(ctx, JS_INTERNAL_ERROR, fmt, ap);
- va_end(ap);
- return val;
- }
- JSValue JS_ThrowOutOfMemory(JSContext *ctx)
- {
- JSRuntime *rt = ctx->rt;
- if (!rt->in_out_of_memory) {
- rt->in_out_of_memory = TRUE;
- JS_ThrowInternalError(ctx, "out of memory");
- rt->in_out_of_memory = FALSE;
- }
- return JS_EXCEPTION;
- }
- static JSValue JS_ThrowStackOverflow(JSContext *ctx)
- {
- return JS_ThrowInternalError(ctx, "stack overflow");
- }
- static JSValue JS_ThrowTypeErrorNotAnObject(JSContext *ctx)
- {
- return JS_ThrowTypeError(ctx, "not an object");
- }
- static JSValue JS_ThrowTypeErrorNotAConstructor(JSContext *ctx,
- JSValueConst func_obj)
- {
- const char *name;
- if (!JS_IsFunction(ctx, func_obj))
- goto fail;
- name = get_prop_string(ctx, func_obj, JS_ATOM_name);
- if (!name) {
- fail:
- return JS_ThrowTypeError(ctx, "not a constructor");
- }
- JS_ThrowTypeError(ctx, "%s is not a constructor", name);
- JS_FreeCString(ctx, name);
- return JS_EXCEPTION;
- }
- static JSValue JS_ThrowTypeErrorNotASymbol(JSContext *ctx)
- {
- return JS_ThrowTypeError(ctx, "not a symbol");
- }
- static JSValue JS_ThrowReferenceErrorNotDefined(JSContext *ctx, JSAtom name)
- {
- char buf[ATOM_GET_STR_BUF_SIZE];
- return JS_ThrowReferenceError(ctx, "'%s' is not defined",
- JS_AtomGetStr(ctx, buf, sizeof(buf), name));
- }
- static JSValue JS_ThrowReferenceErrorUninitialized(JSContext *ctx, JSAtom name)
- {
- char buf[ATOM_GET_STR_BUF_SIZE];
- return JS_ThrowReferenceError(ctx, "%s is not initialized",
- name == JS_ATOM_NULL ? "lexical variable" :
- JS_AtomGetStr(ctx, buf, sizeof(buf), name));
- }
- static JSValue JS_ThrowReferenceErrorUninitialized2(JSContext *ctx,
- JSFunctionBytecode *b,
- int idx, BOOL is_ref)
- {
- JSAtom atom = JS_ATOM_NULL;
- if (is_ref) {
- atom = b->closure_var[idx].var_name;
- } else {
- /* not present if the function is stripped and contains no eval() */
- if (b->vardefs)
- atom = b->vardefs[b->arg_count + idx].var_name;
- }
- return JS_ThrowReferenceErrorUninitialized(ctx, atom);
- }
- static JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id)
- {
- JSRuntime *rt = ctx->rt;
- JSAtom name;
- name = rt->class_array[class_id].class_name;
- return JS_ThrowTypeErrorAtom(ctx, "%s object expected", name);
- }
- static void JS_ThrowInterrupted(JSContext *ctx)
- {
- JS_ThrowInternalError(ctx, "interrupted");
- JS_SetUncatchableException(ctx, TRUE);
- }
- static no_inline __exception int __js_poll_interrupts(JSContext *ctx)
- {
- JSRuntime *rt = ctx->rt;
- ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT;
- if (rt->interrupt_handler) {
- if (rt->interrupt_handler(rt, rt->interrupt_opaque)) {
- JS_ThrowInterrupted(ctx);
- return -1;
- }
- }
- return 0;
- }
- static inline __exception int js_poll_interrupts(JSContext *ctx)
- {
- if (unlikely(--ctx->interrupt_counter <= 0)) {
- return __js_poll_interrupts(ctx);
- } else {
- return 0;
- }
- }
- static void JS_SetImmutablePrototype(JSContext *ctx, JSValueConst obj)
- {
- JSObject *p;
- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
- return;
- p = JS_VALUE_GET_OBJ(obj);
- p->has_immutable_prototype = TRUE;
- }
- /* Return -1 (exception) or TRUE/FALSE. 'throw_flag' = FALSE indicates
- that it is called from Reflect.setPrototypeOf(). */
- static int JS_SetPrototypeInternal(JSContext *ctx, JSValueConst obj,
- JSValueConst proto_val,
- BOOL throw_flag)
- {
- JSObject *proto, *p, *p1;
- JSShape *sh;
- if (throw_flag) {
- if (JS_VALUE_GET_TAG(obj) == JS_TAG_NULL ||
- JS_VALUE_GET_TAG(obj) == JS_TAG_UNDEFINED)
- goto not_obj;
- } else {
- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
- goto not_obj;
- }
- p = JS_VALUE_GET_OBJ(obj);
- if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT) {
- if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_NULL) {
- not_obj:
- JS_ThrowTypeErrorNotAnObject(ctx);
- return -1;
- }
- proto = NULL;
- } else {
- proto = JS_VALUE_GET_OBJ(proto_val);
- }
- if (throw_flag && JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
- return TRUE;
- if (unlikely(p->is_exotic)) {
- const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
- int ret;
- if (em && em->set_prototype) {
- ret = em->set_prototype(ctx, obj, proto_val);
- if (ret == 0 && throw_flag) {
- JS_ThrowTypeError(ctx, "proxy: bad prototype");
- return -1;
- } else {
- return ret;
- }
- }
- }
- sh = p->shape;
- if (sh->proto == proto)
- return TRUE;
- if (unlikely(p->has_immutable_prototype)) {
- if (throw_flag) {
- JS_ThrowTypeError(ctx, "prototype is immutable");
- return -1;
- } else {
- return FALSE;
- }
- }
- if (unlikely(!p->extensible)) {
- if (throw_flag) {
- JS_ThrowTypeError(ctx, "object is not extensible");
- return -1;
- } else {
- return FALSE;
- }
- }
- if (proto) {
- /* check if there is a cycle */
- p1 = proto;
- do {
- if (p1 == p) {
- if (throw_flag) {
- JS_ThrowTypeError(ctx, "circular prototype chain");
- return -1;
- } else {
- return FALSE;
- }
- }
- /* Note: for Proxy objects, proto is NULL */
- p1 = p1->shape->proto;
- } while (p1 != NULL);
- JS_DupValue(ctx, proto_val);
- }
- if (js_shape_prepare_update(ctx, p, NULL))
- return -1;
- sh = p->shape;
- if (sh->proto)
- JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto));
- sh->proto = proto;
- p->is_std_array_prototype = FALSE;
- return TRUE;
- }
- /* return -1 (exception) or TRUE/FALSE */
- int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val)
- {
- return JS_SetPrototypeInternal(ctx, obj, proto_val, TRUE);
- }
- /* Only works for primitive types, otherwise return JS_NULL. */
- static JSValueConst JS_GetPrototypePrimitive(JSContext *ctx, JSValueConst val)
- {
- switch(JS_VALUE_GET_NORM_TAG(val)) {
- case JS_TAG_SHORT_BIG_INT:
- case JS_TAG_BIG_INT:
- val = ctx->class_proto[JS_CLASS_BIG_INT];
- break;
- case JS_TAG_INT:
- case JS_TAG_FLOAT64:
- val = ctx->class_proto[JS_CLASS_NUMBER];
- break;
- case JS_TAG_BOOL:
- val = ctx->class_proto[JS_CLASS_BOOLEAN];
- break;
- case JS_TAG_STRING:
- case JS_TAG_STRING_ROPE:
- val = ctx->class_proto[JS_CLASS_STRING];
- break;
- case JS_TAG_SYMBOL:
- val = ctx->class_proto[JS_CLASS_SYMBOL];
- break;
- case JS_TAG_OBJECT:
- case JS_TAG_NULL:
- case JS_TAG_UNDEFINED:
- default:
- val = JS_NULL;
- break;
- }
- return val;
- }
- /* Return an Object, JS_NULL or JS_EXCEPTION in case of exotic object. */
- JSValue JS_GetPrototype(JSContext *ctx, JSValueConst obj)
- {
- JSValue val;
- if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
- JSObject *p;
- p = JS_VALUE_GET_OBJ(obj);
- if (unlikely(p->is_exotic)) {
- const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
- if (em && em->get_prototype) {
- return em->get_prototype(ctx, obj);
- }
- }
- p = p->shape->proto;
- if (!p)
- val = JS_NULL;
- else
- val = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
- } else {
- val = JS_DupValue(ctx, JS_GetPrototypePrimitive(ctx, obj));
- }
- return val;
- }
- static JSValue JS_GetPrototypeFree(JSContext *ctx, JSValue obj)
- {
- JSValue obj1;
- obj1 = JS_GetPrototype(ctx, obj);
- JS_FreeValue(ctx, obj);
- return obj1;
- }
- /* return TRUE, FALSE or (-1) in case of exception */
- static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValueConst val,
- JSValueConst obj)
- {
- JSValue obj_proto;
- JSObject *proto;
- const JSObject *p, *proto1;
- BOOL ret;
- if (!JS_IsFunction(ctx, obj))
- return FALSE;
- p = JS_VALUE_GET_OBJ(obj);
- if (p->class_id == JS_CLASS_BOUND_FUNCTION) {
- JSBoundFunction *s = p->u.bound_function;
- return JS_IsInstanceOf(ctx, val, s->func_obj);
- }
- /* Only explicitly boxed values are instances of constructors */
- if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
- return FALSE;
- obj_proto = JS_GetProperty(ctx, obj, JS_ATOM_prototype);
- if (JS_VALUE_GET_TAG(obj_proto) != JS_TAG_OBJECT) {
- if (!JS_IsException(obj_proto))
- JS_ThrowTypeError(ctx, "operand 'prototype' property is not an object");
- ret = -1;
- goto done;
- }
- proto = JS_VALUE_GET_OBJ(obj_proto);
- p = JS_VALUE_GET_OBJ(val);
- for(;;) {
- proto1 = p->shape->proto;
- if (!proto1) {
- /* slow case if exotic object in the prototype chain */
- if (unlikely(p->is_exotic && !p->fast_array)) {
- JSValue obj1;
- obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, (JSObject *)p));
- for(;;) {
- obj1 = JS_GetPrototypeFree(ctx, obj1);
- if (JS_IsException(obj1)) {
- ret = -1;
- break;
- }
- if (JS_IsNull(obj1)) {
- ret = FALSE;
- break;
- }
- if (proto == JS_VALUE_GET_OBJ(obj1)) {
- JS_FreeValue(ctx, obj1);
- ret = TRUE;
- break;
- }
- /* must check for timeout to avoid infinite loop */
- if (js_poll_interrupts(ctx)) {
- JS_FreeValue(ctx, obj1);
- ret = -1;
- break;
- }
- }
- } else {
- ret = FALSE;
- }
- break;
- }
- p = proto1;
- if (proto == p) {
- ret = TRUE;
- break;
- }
- }
- done:
- JS_FreeValue(ctx, obj_proto);
- return ret;
- }
- /* return TRUE, FALSE or (-1) in case of exception */
- int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj)
- {
- JSValue method;
- if (!JS_IsObject(obj))
- goto fail;
- method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_hasInstance);
- if (JS_IsException(method))
- return -1;
- if (!JS_IsNull(method) && !JS_IsUndefined(method)) {
- JSValue ret;
- ret = JS_CallFree(ctx, method, obj, 1, &val);
- return JS_ToBoolFree(ctx, ret);
- }
- /* legacy case */
- if (!JS_IsFunction(ctx, obj)) {
- fail:
- JS_ThrowTypeError(ctx, "invalid 'instanceof' right operand");
- return -1;
- }
- return JS_OrdinaryIsInstanceOf(ctx, val, obj);
- }
- /* return the value associated to the autoinit property or an exception */
- typedef JSValue JSAutoInitFunc(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque);
- static JSAutoInitFunc *js_autoinit_func_table[] = {
- js_instantiate_prototype, /* JS_AUTOINIT_ID_PROTOTYPE */
- js_module_ns_autoinit, /* JS_AUTOINIT_ID_MODULE_NS */
- JS_InstantiateFunctionListItem2, /* JS_AUTOINIT_ID_PROP */
- };
- /* warning: 'prs' is reallocated after it */
- static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop,
- JSProperty *pr, JSShapeProperty *prs)
- {
- JSValue val;
- JSContext *realm;
- JSAutoInitFunc *func;
- JSAutoInitIDEnum id;
-
- if (js_shape_prepare_update(ctx, p, &prs))
- return -1;
- realm = js_autoinit_get_realm(pr);
- id = js_autoinit_get_id(pr);
- func = js_autoinit_func_table[id];
- /* 'func' shall not modify the object properties 'pr' */
- val = func(realm, p, prop, pr->u.init.opaque);
- js_autoinit_free(ctx->rt, pr);
- prs->flags &= ~JS_PROP_TMASK;
- pr->u.value = JS_UNDEFINED;
- if (JS_IsException(val))
- return -1;
- if (id == JS_AUTOINIT_ID_MODULE_NS &&
- JS_VALUE_GET_TAG(val) == JS_TAG_STRING) {
- /* WARNING: a varref is returned as a string ! */
- prs->flags |= JS_PROP_VARREF;
- pr->u.var_ref = JS_VALUE_GET_PTR(val);
- js_rc(pr->u.var_ref)->ref_count++;
- } else if (p->class_id == JS_CLASS_GLOBAL_OBJECT) {
- JSVarRef *var_ref;
- /* in the global object we use references */
- var_ref = js_create_var_ref(ctx, FALSE);
- if (!var_ref)
- return -1;
- prs->flags |= JS_PROP_VARREF;
- pr->u.var_ref = var_ref;
- var_ref->value = val;
- var_ref->is_const = !(prs->flags & JS_PROP_WRITABLE);
- } else {
- pr->u.value = val;
- }
- return 0;
- }
- JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj,
- JSAtom prop, JSValueConst this_obj,
- BOOL throw_ref_error)
- {
- JSObject *p;
- JSProperty *pr;
- JSShapeProperty *prs;
- uint32_t tag;
- tag = JS_VALUE_GET_TAG(obj);
- if (unlikely(tag != JS_TAG_OBJECT)) {
- switch(tag) {
- case JS_TAG_NULL:
- return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of null", prop);
- case JS_TAG_UNDEFINED:
- return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of undefined", prop);
- case JS_TAG_EXCEPTION:
- return JS_EXCEPTION;
- case JS_TAG_STRING:
- {
- JSString *p1 = JS_VALUE_GET_STRING(obj);
- if (__JS_AtomIsTaggedInt(prop)) {
- uint32_t idx;
- idx = __JS_AtomToUInt32(prop);
- if (idx < p1->len) {
- return js_new_string_char(ctx, string_get(p1, idx));
- }
- } else if (prop == JS_ATOM_length) {
- return JS_NewInt32(ctx, p1->len);
- }
- }
- break;
- case JS_TAG_STRING_ROPE:
- {
- JSStringRope *p1 = JS_VALUE_GET_STRING_ROPE(obj);
- if (__JS_AtomIsTaggedInt(prop)) {
- uint32_t idx;
- idx = __JS_AtomToUInt32(prop);
- if (idx < p1->len) {
- return js_new_string_char(ctx, string_rope_get(obj, idx));
- }
- } else if (prop == JS_ATOM_length) {
- return JS_NewInt32(ctx, p1->len);
- }
- }
- break;
- default:
- break;
- }
- /* cannot raise an exception */
- p = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, obj));
- if (!p)
- return JS_UNDEFINED;
- } else {
- p = JS_VALUE_GET_OBJ(obj);
- }
- for(;;) {
- prs = find_own_property(&pr, p, prop);
- if (prs) {
- /* found */
- if (unlikely(prs->flags & JS_PROP_TMASK)) {
- if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
- if (unlikely(!pr->u.getset.getter)) {
- return JS_UNDEFINED;
- } else {
- JSValue func = JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter);
- /* Note: the field could be removed in the getter */
- func = JS_DupValue(ctx, func);
- return JS_CallFree(ctx, func, this_obj, 0, NULL);
- }
- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
- JSValue val = *pr->u.var_ref->pvalue;
- if (unlikely(JS_IsUninitialized(val)))
- return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
- return JS_DupValue(ctx, val);
- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
- /* Instantiate property and retry */
- if (JS_AutoInitProperty(ctx, p, prop, pr, prs))
- return JS_EXCEPTION;
- continue;
- }
- } else {
- return JS_DupValue(ctx, pr->u.value);
- }
- }
- if (unlikely(p->is_exotic)) {
- /* exotic behaviors */
- if (p->fast_array) {
- if (__JS_AtomIsTaggedInt(prop)) {
- uint32_t idx = __JS_AtomToUInt32(prop);
- if (idx < p->u.array.count) {
- /* we avoid duplicating the code */
- return JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx);
- } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
- p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
- return JS_UNDEFINED;
- }
- } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
- p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
- int ret;
- ret = JS_AtomIsNumericIndex(ctx, prop);
- if (ret != 0) {
- if (ret < 0)
- return JS_EXCEPTION;
- return JS_UNDEFINED;
- }
- }
- } else {
- const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
- if (em) {
- if (em->get_property) {
- JSValue obj1, retval;
- /* XXX: should pass throw_ref_error */
- /* Note: if 'p' is a prototype, it can be
- freed in the called function */
- obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
- retval = em->get_property(ctx, obj1, prop, this_obj);
- JS_FreeValue(ctx, obj1);
- return retval;
- }
- if (em->get_own_property) {
- JSPropertyDescriptor desc;
- int ret;
- JSValue obj1;
- /* Note: if 'p' is a prototype, it can be
- freed in the called function */
- obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
- ret = em->get_own_property(ctx, &desc, obj1, prop);
- JS_FreeValue(ctx, obj1);
- if (ret < 0)
- return JS_EXCEPTION;
- if (ret) {
- if (desc.flags & JS_PROP_GETSET) {
- JS_FreeValue(ctx, desc.setter);
- return JS_CallFree(ctx, desc.getter, this_obj, 0, NULL);
- } else {
- return desc.value;
- }
- }
- }
- }
- }
- }
- p = p->shape->proto;
- if (!p)
- break;
- }
- if (unlikely(throw_ref_error)) {
- return JS_ThrowReferenceErrorNotDefined(ctx, prop);
- } else {
- return JS_UNDEFINED;
- }
- }
- static JSValue JS_ThrowTypeErrorPrivateNotFound(JSContext *ctx, JSAtom atom)
- {
- return JS_ThrowTypeErrorAtom(ctx, "private class field '%s' does not exist",
- atom);
- }
- /* Private fields can be added even on non extensible objects or
- Proxies */
- static int JS_DefinePrivateField(JSContext *ctx, JSValueConst obj,
- JSValueConst name, JSValue val)
- {
- JSObject *p;
- JSShapeProperty *prs;
- JSProperty *pr;
- JSAtom prop;
- if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {
- JS_ThrowTypeErrorNotAnObject(ctx);
- goto fail;
- }
- /* safety check */
- if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) {
- JS_ThrowTypeErrorNotASymbol(ctx);
- goto fail;
- }
- prop = js_symbol_to_atom(ctx, (JSValue)name);
- p = JS_VALUE_GET_OBJ(obj);
- prs = find_own_property(&pr, p, prop);
- if (prs) {
- JS_ThrowTypeErrorAtom(ctx, "private class field '%s' already exists",
- prop);
- goto fail;
- }
- pr = add_property(ctx, p, prop, JS_PROP_C_W_E);
- if (unlikely(!pr)) {
- fail:
- JS_FreeValue(ctx, val);
- return -1;
- }
- pr->u.value = val;
- return 0;
- }
- static JSValue JS_GetPrivateField(JSContext *ctx, JSValueConst obj,
- JSValueConst name)
- {
- JSObject *p;
- JSShapeProperty *prs;
- JSProperty *pr;
- JSAtom prop;
- if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
- return JS_ThrowTypeErrorNotAnObject(ctx);
- /* safety check */
- if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL))
- return JS_ThrowTypeErrorNotASymbol(ctx);
- prop = js_symbol_to_atom(ctx, (JSValue)name);
- p = JS_VALUE_GET_OBJ(obj);
- prs = find_own_property(&pr, p, prop);
- if (!prs) {
- JS_ThrowTypeErrorPrivateNotFound(ctx, prop);
- return JS_EXCEPTION;
- }
- return JS_DupValue(ctx, pr->u.value);
- }
- static int JS_SetPrivateField(JSContext *ctx, JSValueConst obj,
- JSValueConst name, JSValue val)
- {
- JSObject *p;
- JSShapeProperty *prs;
- JSProperty *pr;
- JSAtom prop;
- if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {
- JS_ThrowTypeErrorNotAnObject(ctx);
- goto fail;
- }
- /* safety check */
- if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) {
- JS_ThrowTypeErrorNotASymbol(ctx);
- goto fail;
- }
- prop = js_symbol_to_atom(ctx, (JSValue)name);
- p = JS_VALUE_GET_OBJ(obj);
- prs = find_own_property(&pr, p, prop);
- if (!prs) {
- JS_ThrowTypeErrorPrivateNotFound(ctx, prop);
- fail:
- JS_FreeValue(ctx, val);
- return -1;
- }
- set_value(ctx, &pr->u.value, val);
- return 0;
- }
- /* add a private brand field to 'home_obj' if not already present and
- if obj is != null add a private brand to it */
- static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj)
- {
- JSObject *p, *p1;
- JSShapeProperty *prs;
- JSProperty *pr;
- JSValue brand;
- JSAtom brand_atom;
- if (unlikely(JS_VALUE_GET_TAG(home_obj) != JS_TAG_OBJECT)) {
- JS_ThrowTypeErrorNotAnObject(ctx);
- return -1;
- }
- p = JS_VALUE_GET_OBJ(home_obj);
- prs = find_own_property(&pr, p, JS_ATOM_Private_brand);
- if (!prs) {
- /* if the brand is not present, add it */
- brand = JS_NewSymbolFromAtom(ctx, JS_ATOM_brand, JS_ATOM_TYPE_PRIVATE);
- if (JS_IsException(brand))
- return -1;
- pr = add_property(ctx, p, JS_ATOM_Private_brand, JS_PROP_C_W_E);
- if (!pr) {
- JS_FreeValue(ctx, brand);
- return -1;
- }
- pr->u.value = JS_DupValue(ctx, brand);
- } else {
- brand = JS_DupValue(ctx, pr->u.value);
- }
- brand_atom = js_symbol_to_atom(ctx, brand);
- if (JS_IsObject(obj)) {
- p1 = JS_VALUE_GET_OBJ(obj);
- prs = find_own_property(&pr, p1, brand_atom);
- if (unlikely(prs)) {
- JS_FreeAtom(ctx, brand_atom);
- JS_ThrowTypeError(ctx, "private method is already present");
- return -1;
- }
- pr = add_property(ctx, p1, brand_atom, JS_PROP_C_W_E);
- JS_FreeAtom(ctx, brand_atom);
- if (!pr)
- return -1;
- pr->u.value = JS_UNDEFINED;
- } else {
- JS_FreeAtom(ctx, brand_atom);
- }
- return 0;
- }
- /* return a boolean telling if the brand of the home object of 'func'
- is present on 'obj' or -1 in case of exception */
- static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func)
- {
- JSObject *p, *p1, *home_obj;
- JSShapeProperty *prs;
- JSProperty *pr;
- JSValueConst brand;
- /* get the home object of 'func' */
- if (unlikely(JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT))
- goto not_obj;
- p1 = JS_VALUE_GET_OBJ(func);
- if (!js_class_has_bytecode(p1->class_id))
- goto not_obj;
- home_obj = p1->u.func.home_object;
- if (!home_obj)
- goto not_obj;
- prs = find_own_property(&pr, home_obj, JS_ATOM_Private_brand);
- if (!prs) {
- JS_ThrowTypeError(ctx, "expecting <brand> private field");
- return -1;
- }
- brand = pr->u.value;
- /* safety check */
- if (unlikely(JS_VALUE_GET_TAG(brand) != JS_TAG_SYMBOL))
- goto not_obj;
- /* get the brand array of 'obj' */
- if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {
- not_obj:
- JS_ThrowTypeErrorNotAnObject(ctx);
- return -1;
- }
- p = JS_VALUE_GET_OBJ(obj);
- prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, (JSValue)brand));
- return (prs != NULL);
- }
- static uint32_t js_string_obj_get_length(JSContext *ctx,
- JSValueConst obj)
- {
- JSObject *p;
- uint32_t len = 0;
- /* This is a class exotic method: obj class_id is JS_CLASS_STRING */
- p = JS_VALUE_GET_OBJ(obj);
- if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) {
- JSString *p1 = JS_VALUE_GET_STRING(p->u.object_data);
- len = p1->len;
- }
- return len;
- }
- static int num_keys_cmp(const void *p1, const void *p2, void *opaque)
- {
- JSContext *ctx = opaque;
- JSAtom atom1 = ((const JSPropertyEnum *)p1)->atom;
- JSAtom atom2 = ((const JSPropertyEnum *)p2)->atom;
- uint32_t v1, v2;
- BOOL atom1_is_integer, atom2_is_integer;
- atom1_is_integer = JS_AtomIsArrayIndex(ctx, &v1, atom1);
- atom2_is_integer = JS_AtomIsArrayIndex(ctx, &v2, atom2);
- assert(atom1_is_integer && atom2_is_integer);
- if (v1 < v2)
- return -1;
- else if (v1 == v2)
- return 0;
- else
- return 1;
- }
- void JS_FreePropertyEnum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len)
- {
- uint32_t i;
- if (tab) {
- for(i = 0; i < len; i++)
- JS_FreeAtom(ctx, tab[i].atom);
- js_free(ctx, tab);
- }
- }
- /* return < 0 in case if exception, 0 if OK. ptab and its atoms must
- be freed by the user. */
- static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx,
- JSPropertyEnum **ptab,
- uint32_t *plen,
- JSObject *p, int flags)
- {
- int i, j;
- JSShape *sh;
- JSShapeProperty *prs;
- JSPropertyEnum *tab_atom, *tab_exotic;
- JSAtom atom;
- uint32_t num_keys_count, str_keys_count, sym_keys_count, atom_count;
- uint32_t num_index, str_index, sym_index, exotic_count, exotic_keys_count;
- BOOL is_enumerable, num_sorted;
- uint32_t num_key;
- JSAtomKindEnum kind;
- /* clear pointer for consistency in case of failure */
- *ptab = NULL;
- *plen = 0;
- /* compute the number of returned properties */
- num_keys_count = 0;
- str_keys_count = 0;
- sym_keys_count = 0;
- exotic_keys_count = 0;
- exotic_count = 0;
- tab_exotic = NULL;
- sh = p->shape;
- for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
- atom = prs->atom;
- if (atom != JS_ATOM_NULL) {
- is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0);
- kind = JS_AtomGetKind(ctx, atom);
- if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) &&
- ((flags >> kind) & 1) != 0) {
- /* need to raise an exception in case of the module
- name space (implicit GetOwnProperty) */
- if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) &&
- (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY))) {
- JSVarRef *var_ref = p->prop[i].u.var_ref;
- if (unlikely(JS_IsUninitialized(*var_ref->pvalue))) {
- JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
- return -1;
- }
- }
- if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) {
- num_keys_count++;
- } else if (kind == JS_ATOM_KIND_STRING) {
- str_keys_count++;
- } else {
- sym_keys_count++;
- }
- }
- }
- }
- if (p->is_exotic) {
- if (p->fast_array) {
- if (flags & JS_GPN_STRING_MASK) {
- num_keys_count += p->u.array.count;
- }
- } else if (p->class_id == JS_CLASS_STRING) {
- if (flags & JS_GPN_STRING_MASK) {
- num_keys_count += js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
- }
- } else {
- const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
- if (em && em->get_own_property_names) {
- if (em->get_own_property_names(ctx, &tab_exotic, &exotic_count,
- JS_MKPTR(JS_TAG_OBJECT, p)))
- return -1;
- for(i = 0; i < exotic_count; i++) {
- atom = tab_exotic[i].atom;
- kind = JS_AtomGetKind(ctx, atom);
- if (((flags >> kind) & 1) != 0) {
- is_enumerable = FALSE;
- if (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY)) {
- JSPropertyDescriptor desc;
- int res;
- /* set the "is_enumerable" field if necessary */
- res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom);
- if (res < 0) {
- JS_FreePropertyEnum(ctx, tab_exotic, exotic_count);
- return -1;
- }
- if (res) {
- is_enumerable =
- ((desc.flags & JS_PROP_ENUMERABLE) != 0);
- js_free_desc(ctx, &desc);
- }
- tab_exotic[i].is_enumerable = is_enumerable;
- }
- if (!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) {
- exotic_keys_count++;
- }
- }
- }
- }
- }
- }
- /* fill them */
- atom_count = num_keys_count + str_keys_count;
- if (atom_count < str_keys_count)
- goto add_overflow;
- atom_count += sym_keys_count;
- if (atom_count < sym_keys_count)
- goto add_overflow;
- atom_count += exotic_keys_count;
- if (atom_count < exotic_keys_count || atom_count > INT32_MAX) {
- add_overflow:
- JS_ThrowOutOfMemory(ctx);
- JS_FreePropertyEnum(ctx, tab_exotic, exotic_count);
- return -1;
- }
- /* XXX: need generic way to test for js_malloc(ctx, a * b) overflow */
-
- /* avoid allocating 0 bytes */
- tab_atom = js_malloc(ctx, sizeof(tab_atom[0]) * max_int(atom_count, 1));
- if (!tab_atom) {
- JS_FreePropertyEnum(ctx, tab_exotic, exotic_count);
- return -1;
- }
- num_index = 0;
- str_index = num_keys_count;
- sym_index = str_index + str_keys_count;
- num_sorted = TRUE;
- sh = p->shape;
- for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
- atom = prs->atom;
- if (atom != JS_ATOM_NULL) {
- is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0);
- kind = JS_AtomGetKind(ctx, atom);
- if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) &&
- ((flags >> kind) & 1) != 0) {
- if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) {
- j = num_index++;
- num_sorted = FALSE;
- } else if (kind == JS_ATOM_KIND_STRING) {
- j = str_index++;
- } else {
- j = sym_index++;
- }
- tab_atom[j].atom = JS_DupAtom(ctx, atom);
- tab_atom[j].is_enumerable = is_enumerable;
- }
- }
- }
- if (p->is_exotic) {
- int len;
- if (p->fast_array) {
- if (flags & JS_GPN_STRING_MASK) {
- len = p->u.array.count;
- goto add_array_keys;
- }
- } else if (p->class_id == JS_CLASS_STRING) {
- if (flags & JS_GPN_STRING_MASK) {
- len = js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
- add_array_keys:
- for(i = 0; i < len; i++) {
- tab_atom[num_index].atom = __JS_AtomFromUInt32(i);
- if (tab_atom[num_index].atom == JS_ATOM_NULL) {
- JS_FreePropertyEnum(ctx, tab_atom, num_index);
- return -1;
- }
- tab_atom[num_index].is_enumerable = TRUE;
- num_index++;
- }
- }
- } else {
- /* Note: exotic keys are not reordered and comes after the object own properties. */
- for(i = 0; i < exotic_count; i++) {
- atom = tab_exotic[i].atom;
- is_enumerable = tab_exotic[i].is_enumerable;
- kind = JS_AtomGetKind(ctx, atom);
- if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) &&
- ((flags >> kind) & 1) != 0) {
- tab_atom[sym_index].atom = atom;
- tab_atom[sym_index].is_enumerable = is_enumerable;
- sym_index++;
- } else {
- JS_FreeAtom(ctx, atom);
- }
- }
- js_free(ctx, tab_exotic);
- }
- }
- assert(num_index == num_keys_count);
- assert(str_index == num_keys_count + str_keys_count);
- assert(sym_index == atom_count);
- if (num_keys_count != 0 && !num_sorted) {
- rqsort(tab_atom, num_keys_count, sizeof(tab_atom[0]), num_keys_cmp,
- ctx);
- }
- *ptab = tab_atom;
- *plen = atom_count;
- return 0;
- }
- int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab,
- uint32_t *plen, JSValueConst obj, int flags)
- {
- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {
- JS_ThrowTypeErrorNotAnObject(ctx);
- return -1;
- }
- return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen,
- JS_VALUE_GET_OBJ(obj), flags);
- }
- /* Return -1 if exception,
- FALSE if the property does not exist, TRUE if it exists. If TRUE is
- returned, the property descriptor 'desc' is filled present. */
- static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc,
- JSObject *p, JSAtom prop)
- {
- JSShapeProperty *prs;
- JSProperty *pr;
- retry:
- prs = find_own_property(&pr, p, prop);
- if (prs) {
- if (desc) {
- desc->flags = prs->flags & JS_PROP_C_W_E;
- desc->getter = JS_UNDEFINED;
- desc->setter = JS_UNDEFINED;
- desc->value = JS_UNDEFINED;
- if (unlikely(prs->flags & JS_PROP_TMASK)) {
- if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
- desc->flags |= JS_PROP_GETSET;
- if (pr->u.getset.getter)
- desc->getter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));
- if (pr->u.getset.setter)
- desc->setter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));
- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
- JSValue val = *pr->u.var_ref->pvalue;
- if (unlikely(JS_IsUninitialized(val))) {
- JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
- return -1;
- }
- desc->value = JS_DupValue(ctx, val);
- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
- /* Instantiate property and retry */
- if (JS_AutoInitProperty(ctx, p, prop, pr, prs))
- return -1;
- goto retry;
- }
- } else {
- desc->value = JS_DupValue(ctx, pr->u.value);
- }
- } else {
- /* for consistency, send the exception even if desc is NULL */
- if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF)) {
- if (unlikely(JS_IsUninitialized(*pr->u.var_ref->pvalue))) {
- JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
- return -1;
- }
- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
- /* nothing to do: delay instantiation until actual value and/or attributes are read */
- }
- }
- return TRUE;
- }
- if (p->is_exotic) {
- if (p->fast_array) {
- /* specific case for fast arrays */
- if (__JS_AtomIsTaggedInt(prop)) {
- uint32_t idx;
- idx = __JS_AtomToUInt32(prop);
- if (idx < p->u.array.count) {
- if (desc) {
- desc->flags = JS_PROP_WRITABLE | JS_PROP_ENUMERABLE |
- JS_PROP_CONFIGURABLE;
- desc->getter = JS_UNDEFINED;
- desc->setter = JS_UNDEFINED;
- desc->value = JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx);
- }
- return TRUE;
- }
- }
- } else {
- const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
- if (em && em->get_own_property) {
- return em->get_own_property(ctx, desc,
- JS_MKPTR(JS_TAG_OBJECT, p), prop);
- }
- }
- }
- return FALSE;
- }
- int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc,
- JSValueConst obj, JSAtom prop)
- {
- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {
- JS_ThrowTypeErrorNotAnObject(ctx);
- return -1;
- }
- return JS_GetOwnPropertyInternal(ctx, desc, JS_VALUE_GET_OBJ(obj), prop);
- }
- /* return -1 if exception (exotic object only) or TRUE/FALSE */
- int JS_IsExtensible(JSContext *ctx, JSValueConst obj)
- {
- JSObject *p;
- if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
- return FALSE;
- p = JS_VALUE_GET_OBJ(obj);
- if (unlikely(p->is_exotic)) {
- const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
- if (em && em->is_extensible) {
- return em->is_extensible(ctx, obj);
- }
- }
- return p->extensible;
- }
- /* return -1 if exception (exotic object only) or TRUE/FALSE */
- int JS_PreventExtensions(JSContext *ctx, JSValueConst obj)
- {
- JSObject *p;
- if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
- return FALSE;
- p = JS_VALUE_GET_OBJ(obj);
- if (unlikely(p->is_exotic)) {
- if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
- p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
- JSTypedArray *ta;
- JSArrayBuffer *abuf;
- /* resizable type arrays return FALSE */
- ta = p->u.typed_array;
- abuf = ta->buffer->u.array_buffer;
- if (ta->track_rab ||
- (array_buffer_is_resizable(abuf) && !abuf->shared))
- return FALSE;
- } else {
- const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
- if (em && em->prevent_extensions) {
- return em->prevent_extensions(ctx, obj);
- }
- }
- }
- p->extensible = FALSE;
- return TRUE;
- }
- /* return -1 if exception otherwise TRUE or FALSE */
- int JS_HasProperty(JSContext *ctx, JSValueConst obj, JSAtom prop)
- {
- JSObject *p;
- int ret;
- JSValue obj1;
- if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
- return FALSE;
- p = JS_VALUE_GET_OBJ(obj);
- for(;;) {
- if (p->is_exotic) {
- const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
- if (em && em->has_property) {
- /* has_property can free the prototype */
- obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
- ret = em->has_property(ctx, obj1, prop);
- JS_FreeValue(ctx, obj1);
- return ret;
- }
- }
- /* JS_GetOwnPropertyInternal can free the prototype */
- JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
- ret = JS_GetOwnPropertyInternal(ctx, NULL, p, prop);
- JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
- if (ret != 0)
- return ret;
- if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
- p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
- ret = JS_AtomIsNumericIndex(ctx, prop);
- if (ret != 0) {
- if (ret < 0)
- return -1;
- return FALSE;
- }
- }
- p = p->shape->proto;
- if (!p)
- break;
- }
- return FALSE;
- }
- /* val must be a symbol */
- static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val)
- {
- JSAtomStruct *p = JS_VALUE_GET_PTR(val);
- return js_get_atom_index(ctx->rt, p);
- }
- /* return JS_ATOM_NULL in case of exception */
- JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val)
- {
- JSAtom atom;
- uint32_t tag;
- tag = JS_VALUE_GET_TAG(val);
- if (tag == JS_TAG_INT &&
- (uint32_t)JS_VALUE_GET_INT(val) <= JS_ATOM_MAX_INT) {
- /* fast path for integer values */
- atom = __JS_AtomFromUInt32(JS_VALUE_GET_INT(val));
- } else if (tag == JS_TAG_SYMBOL) {
- JSAtomStruct *p = JS_VALUE_GET_PTR(val);
- atom = JS_DupAtom(ctx, js_get_atom_index(ctx->rt, p));
- } else {
- JSValue str;
- str = JS_ToPropertyKey(ctx, val);
- if (JS_IsException(str))
- return JS_ATOM_NULL;
- if (JS_VALUE_GET_TAG(str) == JS_TAG_SYMBOL) {
- atom = js_symbol_to_atom(ctx, str);
- } else {
- atom = JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(str));
- }
- }
- return atom;
- }
- static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj,
- JSValue prop)
- {
- JSAtom atom;
- JSValue ret;
- if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT &&
- JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) {
- JSObject *p;
- uint32_t idx;
- /* fast path for array access */
- p = JS_VALUE_GET_OBJ(this_obj);
- idx = JS_VALUE_GET_INT(prop);
- switch(p->class_id) {
- case JS_CLASS_ARRAY:
- case JS_CLASS_ARGUMENTS:
- if (unlikely(idx >= p->u.array.count)) goto slow_path;
- return JS_DupValue(ctx, p->u.array.u.values[idx]);
- case JS_CLASS_MAPPED_ARGUMENTS:
- if (unlikely(idx >= p->u.array.count)) goto slow_path;
- return JS_DupValue(ctx, *p->u.array.u.var_refs[idx]->pvalue);
- case JS_CLASS_INT8_ARRAY:
- if (unlikely(idx >= p->u.array.count)) goto slow_path;
- return JS_NewInt32(ctx, p->u.array.u.int8_ptr[idx]);
- case JS_CLASS_UINT8C_ARRAY:
- case JS_CLASS_UINT8_ARRAY:
- if (unlikely(idx >= p->u.array.count)) goto slow_path;
- return JS_NewInt32(ctx, p->u.array.u.uint8_ptr[idx]);
- case JS_CLASS_INT16_ARRAY:
- if (unlikely(idx >= p->u.array.count)) goto slow_path;
- return JS_NewInt32(ctx, p->u.array.u.int16_ptr[idx]);
- case JS_CLASS_UINT16_ARRAY:
- if (unlikely(idx >= p->u.array.count)) goto slow_path;
- return JS_NewInt32(ctx, p->u.array.u.uint16_ptr[idx]);
- case JS_CLASS_INT32_ARRAY:
- if (unlikely(idx >= p->u.array.count)) goto slow_path;
- return JS_NewInt32(ctx, p->u.array.u.int32_ptr[idx]);
- case JS_CLASS_UINT32_ARRAY:
- if (unlikely(idx >= p->u.array.count)) goto slow_path;
- return JS_NewUint32(ctx, p->u.array.u.uint32_ptr[idx]);
- case JS_CLASS_BIG_INT64_ARRAY:
- if (unlikely(idx >= p->u.array.count)) goto slow_path;
- return JS_NewBigInt64(ctx, p->u.array.u.int64_ptr[idx]);
- case JS_CLASS_BIG_UINT64_ARRAY:
- if (unlikely(idx >= p->u.array.count)) goto slow_path;
- return JS_NewBigUint64(ctx, p->u.array.u.uint64_ptr[idx]);
- case JS_CLASS_FLOAT16_ARRAY:
- if (unlikely(idx >= p->u.array.count)) goto slow_path;
- return __JS_NewFloat64(ctx, fromfp16(p->u.array.u.fp16_ptr[idx]));
- case JS_CLASS_FLOAT32_ARRAY:
- if (unlikely(idx >= p->u.array.count)) goto slow_path;
- return __JS_NewFloat64(ctx, p->u.array.u.float_ptr[idx]);
- case JS_CLASS_FLOAT64_ARRAY:
- if (unlikely(idx >= p->u.array.count)) goto slow_path;
- return __JS_NewFloat64(ctx, p->u.array.u.double_ptr[idx]);
- default:
- goto slow_path;
- }
- } else {
- slow_path:
- /* ToObject() must be done before ToPropertyKey() */
- if (JS_IsNull(this_obj) || JS_IsUndefined(this_obj)) {
- JS_FreeValue(ctx, prop);
- return JS_ThrowTypeError(ctx, "cannot read property of %s", JS_IsNull(this_obj) ? "null" : "undefined");
- }
- atom = JS_ValueToAtom(ctx, prop);
- JS_FreeValue(ctx, prop);
- if (unlikely(atom == JS_ATOM_NULL))
- return JS_EXCEPTION;
- ret = JS_GetProperty(ctx, this_obj, atom);
- JS_FreeAtom(ctx, atom);
- return ret;
- }
- }
- JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj,
- uint32_t idx)
- {
- return JS_GetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx));
- }
- /* Check if an object has a generalized numeric property. Return value:
- -1 for exception,
- TRUE if property exists, stored into *pval,
- FALSE if proprty does not exist.
- */
- static int JS_TryGetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, JSValue *pval)
- {
- JSValue val = JS_UNDEFINED;
- JSAtom prop;
- int present;
- if (likely((uint64_t)idx <= JS_ATOM_MAX_INT)) {
- /* fast path */
- present = JS_HasProperty(ctx, obj, __JS_AtomFromUInt32(idx));
- if (present > 0) {
- val = JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx));
- if (unlikely(JS_IsException(val)))
- present = -1;
- }
- } else {
- prop = JS_NewAtomInt64(ctx, idx);
- present = -1;
- if (likely(prop != JS_ATOM_NULL)) {
- present = JS_HasProperty(ctx, obj, prop);
- if (present > 0) {
- val = JS_GetProperty(ctx, obj, prop);
- if (unlikely(JS_IsException(val)))
- present = -1;
- }
- JS_FreeAtom(ctx, prop);
- }
- }
- *pval = val;
- return present;
- }
- static JSValue JS_GetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx)
- {
- JSAtom prop;
- JSValue val;
- if ((uint64_t)idx <= INT32_MAX) {
- /* fast path for fast arrays */
- return JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx));
- }
- prop = JS_NewAtomInt64(ctx, idx);
- if (prop == JS_ATOM_NULL)
- return JS_EXCEPTION;
- val = JS_GetProperty(ctx, obj, prop);
- JS_FreeAtom(ctx, prop);
- return val;
- }
- JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj,
- const char *prop)
- {
- JSAtom atom;
- JSValue ret;
- atom = JS_NewAtom(ctx, prop);
- if (atom == JS_ATOM_NULL)
- return JS_EXCEPTION;
- ret = JS_GetProperty(ctx, this_obj, atom);
- JS_FreeAtom(ctx, atom);
- return ret;
- }
- /* Note: the property value is not initialized. Return NULL if memory
- error. */
- static JSProperty *add_property(JSContext *ctx,
- JSObject *p, JSAtom prop, int prop_flags)
- {
- JSShape *sh, *new_sh;
- if (unlikely(__JS_AtomIsTaggedInt(prop))) {
- /* update is_std_array_prototype */
- if (unlikely(p->is_std_array_prototype)) {
- p->is_std_array_prototype = FALSE;
- } else if (unlikely(p->has_immutable_prototype)) {
- struct list_head *el;
-
- /* modifying Object.prototype : reset the corresponding is_std_array_prototype */
- list_for_each(el, &ctx->rt->context_list) {
- JSContext *ctx1 = list_entry(el, JSContext, link);
- if (JS_IsObject(ctx1->class_proto[JS_CLASS_OBJECT]) &&
- JS_VALUE_GET_OBJ(ctx1->class_proto[JS_CLASS_OBJECT]) == p) {
- if (JS_IsObject(ctx1->class_proto[JS_CLASS_ARRAY])) {
- JSObject *p1 = JS_VALUE_GET_OBJ(ctx1->class_proto[JS_CLASS_ARRAY]);
- p1->is_std_array_prototype = FALSE;
- }
- break;
- }
- }
- }
- }
- sh = p->shape;
- if (sh->is_hashed) {
- /* try to find an existing shape */
- new_sh = find_hashed_shape_prop(ctx->rt, sh, prop, prop_flags);
- if (new_sh) {
- /* matching shape found: use it */
- /* the property array may need to be resized */
- if (new_sh->prop_size != sh->prop_size) {
- JSProperty *new_prop;
- new_prop = js_realloc(ctx, p->prop, sizeof(p->prop[0]) *
- new_sh->prop_size);
- if (!new_prop)
- return NULL;
- p->prop = new_prop;
- }
- p->shape = js_dup_shape(new_sh);
- js_free_shape(ctx->rt, sh);
- return &p->prop[new_sh->prop_count - 1];
- } else if (js_rc(sh)->ref_count != 1) {
- /* if the shape is shared, clone it */
- new_sh = js_clone_shape(ctx, sh);
- if (!new_sh)
- return NULL;
- /* hash the cloned shape */
- new_sh->is_hashed = TRUE;
- js_shape_hash_link(ctx->rt, new_sh);
- js_free_shape(ctx->rt, p->shape);
- p->shape = new_sh;
- }
- }
- assert(js_rc(p->shape)->ref_count == 1);
- if (add_shape_property(ctx, &p->shape, p, prop, prop_flags))
- return NULL;
- return &p->prop[p->shape->prop_count - 1];
- }
- /* can be called on JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS or
- JS_CLASS_MAPPED_ARGUMENTS objects. return < 0 if memory alloc
- error. */
- static no_inline __exception int convert_fast_array_to_array(JSContext *ctx,
- JSObject *p)
- {
- JSProperty *pr;
- JSShape *sh;
- uint32_t i, len, new_count;
- if (js_shape_prepare_update(ctx, p, NULL))
- return -1;
- len = p->u.array.count;
- /* resize the properties once to simplify the error handling */
- sh = p->shape;
- new_count = sh->prop_count + len;
- if (new_count > sh->prop_size) {
- if (resize_properties(ctx, &p->shape, p, new_count))
- return -1;
- }
- if (p->class_id == JS_CLASS_MAPPED_ARGUMENTS) {
- JSVarRef **tab = p->u.array.u.var_refs;
- for(i = 0; i < len; i++) {
- /* add_property cannot fail here but
- __JS_AtomFromUInt32(i) fails for i > INT32_MAX */
- pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E | JS_PROP_VARREF);
- pr->u.var_ref = *tab++;
- }
- } else {
- JSValue *tab = p->u.array.u.values;
- for(i = 0; i < len; i++) {
- /* add_property cannot fail here but
- __JS_AtomFromUInt32(i) fails for i > INT32_MAX */
- pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E);
- pr->u.value = *tab++;
- }
- }
- js_free(ctx, p->u.array.u.values);
- p->u.array.count = 0;
- p->u.array.u.values = NULL; /* fail safe */
- p->u.array.u1.size = 0;
- p->fast_array = 0;
- p->is_std_array_prototype = FALSE;
- return 0;
- }
- static int remove_global_object_property(JSContext *ctx, JSObject *p,
- JSShapeProperty *prs, JSProperty *pr)
- {
- JSVarRef *var_ref;
- JSObject *p1;
- JSProperty *pr1;
-
- var_ref = pr->u.var_ref;
- if (js_rc(var_ref)->ref_count == 1)
- return 0;
- p1 = JS_VALUE_GET_OBJ(p->u.global_object.uninitialized_vars);
- pr1 = add_property(ctx, p1, prs->atom, JS_PROP_C_W_E | JS_PROP_VARREF);
- if (!pr1)
- return -1;
- pr1->u.var_ref = var_ref;
- js_rc(var_ref)->ref_count++;
- JS_FreeValue(ctx, var_ref->value);
- var_ref->is_lexical = FALSE;
- var_ref->is_const = FALSE;
- var_ref->value = JS_UNINITIALIZED;
- return 0;
- }
- static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom)
- {
- JSShape *sh;
- JSShapeProperty *pr, *lpr, *prop;
- JSProperty *pr1;
- uint32_t lpr_idx;
- intptr_t h, h1;
- redo:
- sh = p->shape;
- h1 = atom & sh->prop_hash_mask;
- h = sh->hash_table[h1];
- prop = get_shape_prop(sh);
- lpr = NULL;
- lpr_idx = 0; /* prevent warning */
- while (h != 0) {
- pr = &prop[h - 1];
- if (likely(pr->atom == atom)) {
- /* found ! */
- if (!(pr->flags & JS_PROP_CONFIGURABLE))
- return FALSE;
- /* realloc the shape if needed */
- if (lpr)
- lpr_idx = lpr - get_shape_prop(sh);
- if (js_shape_prepare_update(ctx, p, &pr))
- return -1;
- sh = p->shape;
- /* remove property */
- if (lpr) {
- lpr = get_shape_prop(sh) + lpr_idx;
- lpr->hash_next = pr->hash_next;
- } else {
- sh->hash_table[h1] = pr->hash_next;
- }
- sh->deleted_prop_count++;
- /* free the entry */
- pr1 = &p->prop[h - 1];
- if (unlikely(p->class_id == JS_CLASS_GLOBAL_OBJECT)) {
- if ((pr->flags & JS_PROP_TMASK) == JS_PROP_VARREF)
- if (remove_global_object_property(ctx, p, pr, pr1))
- return -1;
- }
- free_property(ctx->rt, pr1, pr->flags);
- JS_FreeAtom(ctx, pr->atom);
- /* put default values */
- pr->flags = 0;
- pr->atom = JS_ATOM_NULL;
- pr1->u.value = JS_UNDEFINED;
- /* compact the properties if too many deleted properties */
- if (sh->deleted_prop_count >= 8 &&
- sh->deleted_prop_count >= ((unsigned)sh->prop_count / 2)) {
- compact_properties(ctx, p);
- }
- return TRUE;
- }
- lpr = pr;
- h = pr->hash_next;
- }
- if (p->is_exotic) {
- if (p->fast_array) {
- uint32_t idx;
- if (JS_AtomIsArrayIndex(ctx, &idx, atom) &&
- idx < p->u.array.count) {
- if (p->class_id == JS_CLASS_ARRAY ||
- p->class_id == JS_CLASS_ARGUMENTS ||
- p->class_id == JS_CLASS_MAPPED_ARGUMENTS) {
- /* Special case deleting the last element of a fast Array */
- if (idx == p->u.array.count - 1) {
- if (p->class_id == JS_CLASS_MAPPED_ARGUMENTS) {
- free_var_ref(ctx->rt, p->u.array.u.var_refs[idx]);
- } else {
- JS_FreeValue(ctx, p->u.array.u.values[idx]);
- }
- p->u.array.count = idx;
- return TRUE;
- }
- if (convert_fast_array_to_array(ctx, p))
- return -1;
- goto redo;
- } else {
- return FALSE;
- }
- }
- } else {
- const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
- if (em && em->delete_property) {
- return em->delete_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), atom);
- }
- }
- }
- /* not found */
- return TRUE;
- }
- static int call_setter(JSContext *ctx, JSObject *setter,
- JSValueConst this_obj, JSValue val, int flags)
- {
- JSValue ret, func;
- if (likely(setter)) {
- func = JS_MKPTR(JS_TAG_OBJECT, setter);
- /* Note: the field could be removed in the setter */
- func = JS_DupValue(ctx, func);
- ret = JS_CallFree(ctx, func, this_obj, 1, (JSValueConst *)&val);
- JS_FreeValue(ctx, val);
- if (JS_IsException(ret))
- return -1;
- JS_FreeValue(ctx, ret);
- return TRUE;
- } else {
- JS_FreeValue(ctx, val);
- if ((flags & JS_PROP_THROW) ||
- ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
- JS_ThrowTypeError(ctx, "no setter for property");
- return -1;
- }
- return FALSE;
- }
- }
- /* set the array length and remove the array elements if necessary. */
- static int set_array_length(JSContext *ctx, JSObject *p, JSValue val,
- int flags)
- {
- uint32_t len, idx, cur_len;
- int i, ret;
- /* Note: this call can reallocate the properties of 'p' */
- ret = JS_ToArrayLengthFree(ctx, &len, val, FALSE);
- if (ret)
- return -1;
- /* JS_ToArrayLengthFree() must be done before the read-only test */
- if (unlikely(!(get_shape_prop(p->shape)[0].flags & JS_PROP_WRITABLE)))
- return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length);
- if (likely(p->fast_array)) {
- uint32_t old_len = p->u.array.count;
- if (len < old_len) {
- for(i = len; i < old_len; i++) {
- JS_FreeValue(ctx, p->u.array.u.values[i]);
- }
- p->u.array.count = len;
- }
- p->prop[0].u.value = JS_NewUint32(ctx, len);
- } else {
- /* Note: length is always a uint32 because the object is an
- array */
- JS_ToUint32(ctx, &cur_len, p->prop[0].u.value);
- if (len < cur_len) {
- uint32_t d;
- JSShape *sh;
- JSShapeProperty *pr;
- d = cur_len - len;
- sh = p->shape;
- if (d <= sh->prop_count) {
- JSAtom atom;
- /* faster to iterate */
- while (cur_len > len) {
- atom = JS_NewAtomUInt32(ctx, cur_len - 1);
- ret = delete_property(ctx, p, atom);
- JS_FreeAtom(ctx, atom);
- if (unlikely(!ret)) {
- /* unlikely case: property is not
- configurable */
- break;
- }
- cur_len--;
- }
- } else {
- /* faster to iterate thru all the properties. Need two
- passes in case one of the property is not
- configurable */
- cur_len = len;
- for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count;
- i++, pr++) {
- if (pr->atom != JS_ATOM_NULL &&
- JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) {
- if (idx >= cur_len &&
- !(pr->flags & JS_PROP_CONFIGURABLE)) {
- cur_len = idx + 1;
- }
- }
- }
- for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count;
- i++, pr++) {
- if (pr->atom != JS_ATOM_NULL &&
- JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) {
- if (idx >= cur_len) {
- /* remove the property */
- delete_property(ctx, p, pr->atom);
- /* WARNING: the shape may have been modified */
- sh = p->shape;
- pr = get_shape_prop(sh) + i;
- }
- }
- }
- }
- } else {
- cur_len = len;
- }
- set_value(ctx, &p->prop[0].u.value, JS_NewUint32(ctx, cur_len));
- if (unlikely(cur_len > len)) {
- return JS_ThrowTypeErrorOrFalse(ctx, flags, "not configurable");
- }
- }
- return TRUE;
- }
- /* return -1 if exception */
- static int expand_fast_array(JSContext *ctx, JSObject *p, uint32_t new_len)
- {
- uint32_t new_size;
- size_t slack;
- JSValue *new_array_prop;
- /* XXX: potential arithmetic overflow */
- new_size = max_int(new_len, p->u.array.u1.size * 3 / 2);
- new_array_prop = js_realloc2(ctx, p->u.array.u.values, sizeof(JSValue) * new_size, &slack);
- if (!new_array_prop)
- return -1;
- new_size += slack / sizeof(*new_array_prop);
- p->u.array.u.values = new_array_prop;
- p->u.array.u1.size = new_size;
- return 0;
- }
- /* Preconditions: 'p' must be of class JS_CLASS_ARRAY, p->fast_array =
- TRUE and p->extensible = TRUE */
- static inline int add_fast_array_element(JSContext *ctx, JSObject *p,
- JSValue val, int flags)
- {
- uint32_t new_len, array_len;
- /* extend the array by one */
- /* XXX: convert to slow array if new_len > 2^31-1 elements */
- new_len = p->u.array.count + 1;
- /* update the length if necessary. We assume that if the length is
- not an integer, then if it >= 2^31. */
- if (likely(JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT)) {
- array_len = JS_VALUE_GET_INT(p->prop[0].u.value);
- if (new_len > array_len) {
- if (unlikely(!(get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE))) {
- JS_FreeValue(ctx, val);
- return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length);
- }
- p->prop[0].u.value = JS_NewInt32(ctx, new_len);
- }
- }
- if (unlikely(new_len > p->u.array.u1.size)) {
- if (expand_fast_array(ctx, p, new_len)) {
- JS_FreeValue(ctx, val);
- return -1;
- }
- }
- p->u.array.u.values[new_len - 1] = val;
- p->u.array.count = new_len;
- return TRUE;
- }
- /* Allocate a new fast array initialized to JS_UNDEFINED. Its maximum
- size is 2^31-1 elements. For convenience, 'len' is a 64 bit
- integer. */
- static JSValue js_allocate_fast_array(JSContext *ctx, int64_t len)
- {
- JSValue arr;
- JSObject *p;
- int i;
-
- if (len > INT32_MAX)
- return JS_ThrowRangeError(ctx, "invalid array length");
- arr = JS_NewArray(ctx);
- if (JS_IsException(arr))
- return arr;
- if (len > 0) {
- p = JS_VALUE_GET_OBJ(arr);
- if (expand_fast_array(ctx, p, len) < 0) {
- JS_FreeValue(ctx, arr);
- return JS_EXCEPTION;
- }
- p->u.array.count = len;
- for(i = 0; i < len; i++)
- p->u.array.u.values[i] = JS_UNDEFINED;
- /* update the 'length' field */
- set_value(ctx, &p->prop[0].u.value, JS_NewInt32(ctx, len));
- }
- return arr;
- }
- static JSValue js_create_array(JSContext *ctx, int len, JSValueConst *tab)
- {
- JSValue obj;
- JSObject *p;
- int i;
- obj = JS_NewArray(ctx);
- if (JS_IsException(obj))
- return JS_EXCEPTION;
- if (len > 0) {
- p = JS_VALUE_GET_OBJ(obj);
- if (expand_fast_array(ctx, p, len) < 0) {
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- p->u.array.count = len;
- for(i = 0; i < len; i++)
- p->u.array.u.values[i] = JS_DupValue(ctx, tab[i]);
- /* update the 'length' field */
- set_value(ctx, &p->prop[0].u.value, JS_NewInt32(ctx, len));
- }
- return obj;
- }
- static JSValue js_create_array_free(JSContext *ctx, int len, JSValue *tab)
- {
- JSValue obj;
- JSObject *p;
- int i;
- obj = JS_NewArray(ctx);
- if (JS_IsException(obj))
- goto fail;
- if (len > 0) {
- p = JS_VALUE_GET_OBJ(obj);
- if (expand_fast_array(ctx, p, len) < 0) {
- JS_FreeValue(ctx, obj);
- fail:
- for(i = 0; i < len; i++)
- JS_FreeValue(ctx, tab[i]);
- return JS_EXCEPTION;
- }
- p->u.array.count = len;
- for(i = 0; i < len; i++)
- p->u.array.u.values[i] = tab[i];
- /* update the 'length' field */
- set_value(ctx, &p->prop[0].u.value, JS_NewInt32(ctx, len));
- }
- return obj;
- }
- static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc)
- {
- JS_FreeValue(ctx, desc->getter);
- JS_FreeValue(ctx, desc->setter);
- JS_FreeValue(ctx, desc->value);
- }
- /* return -1 in case of exception or TRUE or FALSE. Warning: 'val' is
- freed by the function. 'flags' is a bitmask of JS_PROP_THROW and
- JS_PROP_THROW_STRICT. 'this_obj' is the receiver. If obj !=
- this_obj, then obj must be an object (Reflect.set case). */
- int JS_SetPropertyInternal(JSContext *ctx, JSValueConst obj,
- JSAtom prop, JSValue val, JSValueConst this_obj, int flags)
- {
- JSObject *p, *p1;
- JSShapeProperty *prs;
- JSProperty *pr;
- uint32_t tag;
- JSPropertyDescriptor desc;
- int ret;
- #if 0
- printf("JS_SetPropertyInternal: "); print_atom(ctx, prop); printf("\n");
- #endif
- tag = JS_VALUE_GET_TAG(this_obj);
- if (unlikely(tag != JS_TAG_OBJECT)) {
- if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
- p = NULL;
- p1 = JS_VALUE_GET_OBJ(obj);
- goto prototype_lookup;
- } else {
- switch(tag) {
- case JS_TAG_NULL:
- JS_FreeValue(ctx, val);
- JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of null", prop);
- return -1;
- case JS_TAG_UNDEFINED:
- JS_FreeValue(ctx, val);
- JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of undefined", prop);
- return -1;
- default:
- /* even on a primitive type we can have setters on the prototype */
- p = NULL;
- p1 = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, obj));
- goto prototype_lookup;
- }
- }
- } else {
- p = JS_VALUE_GET_OBJ(this_obj);
- p1 = JS_VALUE_GET_OBJ(obj);
- if (unlikely(p != p1))
- goto retry2;
- }
- /* fast path if obj == this_obj */
- retry:
- prs = find_own_property(&pr, p1, prop);
- if (prs) {
- if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE |
- JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) {
- /* fast case */
- set_value(ctx, &pr->u.value, val);
- return TRUE;
- } else if (prs->flags & JS_PROP_LENGTH) {
- assert(p->class_id == JS_CLASS_ARRAY);
- assert(prop == JS_ATOM_length);
- return set_array_length(ctx, p, val, flags);
- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
- return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags);
- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
- /* XXX: already use var_ref->is_const. Cannot simplify use the
- writable flag for JS_CLASS_MODULE_NS. */
- if (p->class_id == JS_CLASS_MODULE_NS || pr->u.var_ref->is_const)
- goto read_only_prop;
- set_value(ctx, pr->u.var_ref->pvalue, val);
- return TRUE;
- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
- /* Instantiate property and retry (potentially useless) */
- if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) {
- JS_FreeValue(ctx, val);
- return -1;
- }
- goto retry;
- } else {
- goto read_only_prop;
- }
- }
- for(;;) {
- if (p1->is_exotic) {
- if (p1->fast_array) {
- if (__JS_AtomIsTaggedInt(prop)) {
- uint32_t idx = __JS_AtomToUInt32(prop);
- if (idx < p1->u.array.count) {
- if (unlikely(p == p1))
- return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val, flags);
- else
- break;
- } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY &&
- p1->class_id <= JS_CLASS_FLOAT64_ARRAY) {
- goto typed_array_oob;
- }
- } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY &&
- p1->class_id <= JS_CLASS_FLOAT64_ARRAY) {
- ret = JS_AtomIsNumericIndex(ctx, prop);
- if (ret != 0) {
- if (ret < 0) {
- JS_FreeValue(ctx, val);
- return -1;
- }
- typed_array_oob:
- if (p == p1) {
- /* must convert the argument even if out of bound access */
- if (p1->class_id == JS_CLASS_BIG_INT64_ARRAY ||
- p1->class_id == JS_CLASS_BIG_UINT64_ARRAY) {
- int64_t v;
- if (JS_ToBigInt64Free(ctx, &v, val))
- return -1;
- } else {
- val = JS_ToNumberFree(ctx, val);
- JS_FreeValue(ctx, val);
- if (JS_IsException(val))
- return -1;
- }
- } else {
- JS_FreeValue(ctx, val);
- }
- return TRUE;
- }
- }
- } else {
- const JSClassExoticMethods *em = ctx->rt->class_array[p1->class_id].exotic;
- if (em) {
- JSValue obj1;
- if (em->set_property) {
- /* set_property can free the prototype */
- obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));
- ret = em->set_property(ctx, obj1, prop,
- val, this_obj, flags);
- JS_FreeValue(ctx, obj1);
- JS_FreeValue(ctx, val);
- return ret;
- }
- if (em->get_own_property) {
- /* get_own_property can free the prototype */
- obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));
- ret = em->get_own_property(ctx, &desc,
- obj1, prop);
- JS_FreeValue(ctx, obj1);
- if (ret < 0) {
- JS_FreeValue(ctx, val);
- return ret;
- }
- if (ret) {
- if (desc.flags & JS_PROP_GETSET) {
- JSObject *setter;
- if (JS_IsUndefined(desc.setter))
- setter = NULL;
- else
- setter = JS_VALUE_GET_OBJ(desc.setter);
- ret = call_setter(ctx, setter, this_obj, val, flags);
- JS_FreeValue(ctx, desc.getter);
- JS_FreeValue(ctx, desc.setter);
- return ret;
- } else {
- JS_FreeValue(ctx, desc.value);
- if (!(desc.flags & JS_PROP_WRITABLE))
- goto read_only_prop;
- if (likely(p == p1)) {
- ret = JS_DefineProperty(ctx, this_obj, prop, val,
- JS_UNDEFINED, JS_UNDEFINED,
- JS_PROP_HAS_VALUE);
- JS_FreeValue(ctx, val);
- return ret;
- } else {
- break;
- }
- }
- }
- }
- }
- }
- }
- p1 = p1->shape->proto;
- prototype_lookup:
- if (!p1)
- break;
- retry2:
- prs = find_own_property(&pr, p1, prop);
- if (prs) {
- if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
- return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags);
- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
- /* Instantiate property and retry (potentially useless) */
- if (JS_AutoInitProperty(ctx, p1, prop, pr, prs))
- return -1;
- goto retry2;
- } else if (!(prs->flags & JS_PROP_WRITABLE)) {
- goto read_only_prop;
- } else {
- break;
- }
- }
- }
- if (unlikely(!p)) {
- JS_FreeValue(ctx, val);
- return JS_ThrowTypeErrorOrFalse(ctx, flags, "not an object");
- }
- if (unlikely(!p->extensible)) {
- JS_FreeValue(ctx, val);
- return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible");
- }
- if (likely(p == JS_VALUE_GET_OBJ(obj))) {
- if (p->is_exotic) {
- if (p->class_id == JS_CLASS_ARRAY && p->fast_array &&
- __JS_AtomIsTaggedInt(prop)) {
- uint32_t idx = __JS_AtomToUInt32(prop);
- if (idx == p->u.array.count) {
- /* fast case */
- return add_fast_array_element(ctx, p, val, flags);
- } else {
- goto generic_create_prop;
- }
- } else {
- goto generic_create_prop;
- }
- } else {
- if (unlikely(p->class_id == JS_CLASS_GLOBAL_OBJECT))
- goto generic_create_prop;
- pr = add_property(ctx, p, prop, JS_PROP_C_W_E);
- if (unlikely(!pr)) {
- JS_FreeValue(ctx, val);
- return -1;
- }
- pr->u.value = val;
- return TRUE;
- }
- } else {
- /* generic case: modify the property in this_obj if it already exists */
- ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
- if (ret < 0) {
- JS_FreeValue(ctx, val);
- return ret;
- }
- if (ret) {
- if (desc.flags & JS_PROP_GETSET) {
- JS_FreeValue(ctx, desc.getter);
- JS_FreeValue(ctx, desc.setter);
- JS_FreeValue(ctx, val);
- return JS_ThrowTypeErrorOrFalse(ctx, flags, "setter is forbidden");
- } else {
- JS_FreeValue(ctx, desc.value);
- if (!(desc.flags & JS_PROP_WRITABLE) ||
- p->class_id == JS_CLASS_MODULE_NS) {
- read_only_prop:
- JS_FreeValue(ctx, val);
- return JS_ThrowTypeErrorReadOnly(ctx, flags, prop);
- }
- }
- ret = JS_DefineProperty(ctx, this_obj, prop, val,
- JS_UNDEFINED, JS_UNDEFINED,
- JS_PROP_HAS_VALUE);
- JS_FreeValue(ctx, val);
- return ret;
- } else {
- generic_create_prop:
- ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED,
- flags |
- JS_PROP_HAS_VALUE |
- JS_PROP_HAS_ENUMERABLE |
- JS_PROP_HAS_WRITABLE |
- JS_PROP_HAS_CONFIGURABLE |
- JS_PROP_C_W_E);
- JS_FreeValue(ctx, val);
- return ret;
- }
- }
- }
- /* return true if an element can be added to a fast array without further tests */
- static force_inline BOOL can_extend_fast_array(JSObject *p)
- {
- JSObject *proto;
- if (!p->extensible)
- return FALSE;
- proto = p->shape->proto;
- if (!proto)
- return TRUE;
- return proto->is_std_array_prototype;
- }
- /* flags can be JS_PROP_THROW or JS_PROP_THROW_STRICT */
- static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,
- JSValue prop, JSValue val, int flags)
- {
- if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT &&
- JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) {
- JSObject *p;
- uint32_t idx;
- double d;
- int32_t v;
- /* fast path for array access */
- p = JS_VALUE_GET_OBJ(this_obj);
- idx = JS_VALUE_GET_INT(prop);
- switch(p->class_id) {
- case JS_CLASS_ARRAY:
- if (unlikely(idx >= (uint32_t)p->u.array.count)) {
- /* fast path to add an element to the array */
- if (unlikely(idx != (uint32_t)p->u.array.count ||
- !p->fast_array ||
- !can_extend_fast_array(p))) {
- goto slow_path;
- }
- /* add element */
- return add_fast_array_element(ctx, p, val, flags);
- }
- set_value(ctx, &p->u.array.u.values[idx], val);
- break;
- case JS_CLASS_ARGUMENTS:
- if (unlikely(idx >= (uint32_t)p->u.array.count))
- goto slow_path;
- set_value(ctx, &p->u.array.u.values[idx], val);
- break;
- case JS_CLASS_MAPPED_ARGUMENTS:
- if (unlikely(idx >= (uint32_t)p->u.array.count))
- goto slow_path;
- set_value(ctx, p->u.array.u.var_refs[idx]->pvalue, val);
- break;
- case JS_CLASS_UINT8C_ARRAY:
- if (JS_ToUint8ClampFree(ctx, &v, val))
- return -1;
- /* Note: the conversion can detach the typed array, so the
- array bound check must be done after */
- if (unlikely(idx >= (uint32_t)p->u.array.count))
- goto ta_out_of_bound;
- p->u.array.u.uint8_ptr[idx] = v;
- break;
- case JS_CLASS_INT8_ARRAY:
- case JS_CLASS_UINT8_ARRAY:
- if (JS_ToInt32Free(ctx, &v, val))
- return -1;
- if (unlikely(idx >= (uint32_t)p->u.array.count))
- goto ta_out_of_bound;
- p->u.array.u.uint8_ptr[idx] = v;
- break;
- case JS_CLASS_INT16_ARRAY:
- case JS_CLASS_UINT16_ARRAY:
- if (JS_ToInt32Free(ctx, &v, val))
- return -1;
- if (unlikely(idx >= (uint32_t)p->u.array.count))
- goto ta_out_of_bound;
- p->u.array.u.uint16_ptr[idx] = v;
- break;
- case JS_CLASS_INT32_ARRAY:
- case JS_CLASS_UINT32_ARRAY:
- if (JS_ToInt32Free(ctx, &v, val))
- return -1;
- if (unlikely(idx >= (uint32_t)p->u.array.count))
- goto ta_out_of_bound;
- p->u.array.u.uint32_ptr[idx] = v;
- break;
- case JS_CLASS_BIG_INT64_ARRAY:
- case JS_CLASS_BIG_UINT64_ARRAY:
- /* XXX: need specific conversion function */
- {
- int64_t v;
- if (JS_ToBigInt64Free(ctx, &v, val))
- return -1;
- if (unlikely(idx >= (uint32_t)p->u.array.count))
- goto ta_out_of_bound;
- p->u.array.u.uint64_ptr[idx] = v;
- }
- break;
- case JS_CLASS_FLOAT16_ARRAY:
- if (JS_ToFloat64Free(ctx, &d, val))
- return -1;
- if (unlikely(idx >= (uint32_t)p->u.array.count))
- goto ta_out_of_bound;
- p->u.array.u.fp16_ptr[idx] = tofp16(d);
- break;
- case JS_CLASS_FLOAT32_ARRAY:
- if (JS_ToFloat64Free(ctx, &d, val))
- return -1;
- if (unlikely(idx >= (uint32_t)p->u.array.count))
- goto ta_out_of_bound;
- p->u.array.u.float_ptr[idx] = d;
- break;
- case JS_CLASS_FLOAT64_ARRAY:
- if (JS_ToFloat64Free(ctx, &d, val))
- return -1;
- if (unlikely(idx >= (uint32_t)p->u.array.count)) {
- ta_out_of_bound:
- return TRUE;
- }
- p->u.array.u.double_ptr[idx] = d;
- break;
- default:
- goto slow_path;
- }
- return TRUE;
- } else {
- JSAtom atom;
- int ret;
- slow_path:
- atom = JS_ValueToAtom(ctx, prop);
- JS_FreeValue(ctx, prop);
- if (unlikely(atom == JS_ATOM_NULL)) {
- JS_FreeValue(ctx, val);
- return -1;
- }
- ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, this_obj, flags);
- JS_FreeAtom(ctx, atom);
- return ret;
- }
- }
- int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj,
- uint32_t idx, JSValue val)
- {
- return JS_SetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx), val,
- JS_PROP_THROW);
- }
- int JS_SetPropertyInt64(JSContext *ctx, JSValueConst this_obj,
- int64_t idx, JSValue val)
- {
- JSAtom prop;
- int res;
- if ((uint64_t)idx <= INT32_MAX) {
- /* fast path for fast arrays */
- return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val,
- JS_PROP_THROW);
- }
- prop = JS_NewAtomInt64(ctx, idx);
- if (prop == JS_ATOM_NULL) {
- JS_FreeValue(ctx, val);
- return -1;
- }
- res = JS_SetProperty(ctx, this_obj, prop, val);
- JS_FreeAtom(ctx, prop);
- return res;
- }
- int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj,
- const char *prop, JSValue val)
- {
- JSAtom atom;
- int ret;
- atom = JS_NewAtom(ctx, prop);
- if (atom == JS_ATOM_NULL) {
- JS_FreeValue(ctx, val);
- return -1;
- }
- ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, this_obj, JS_PROP_THROW);
- JS_FreeAtom(ctx, atom);
- return ret;
- }
- /* compute the property flags. For each flag: (JS_PROP_HAS_x forces
- it, otherwise def_flags is used)
- Note: makes assumption about the bit pattern of the flags
- */
- static int get_prop_flags(int flags, int def_flags)
- {
- int mask;
- mask = (flags >> JS_PROP_HAS_SHIFT) & JS_PROP_C_W_E;
- return (flags & mask) | (def_flags & ~mask);
- }
- static int JS_CreateProperty(JSContext *ctx, JSObject *p,
- JSAtom prop, JSValueConst val,
- JSValueConst getter, JSValueConst setter,
- int flags)
- {
- JSProperty *pr;
- int ret, prop_flags;
- JSVarRef *var_ref;
- JSObject *delete_obj;
-
- /* add a new property or modify an existing exotic one */
- if (p->is_exotic) {
- if (p->class_id == JS_CLASS_ARRAY) {
- uint32_t idx, len;
- if (p->fast_array) {
- if (__JS_AtomIsTaggedInt(prop)) {
- idx = __JS_AtomToUInt32(prop);
- if (idx == p->u.array.count) {
- if (!p->extensible)
- goto not_extensible;
- if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET))
- goto convert_to_array;
- prop_flags = get_prop_flags(flags, 0);
- if (prop_flags != JS_PROP_C_W_E)
- goto convert_to_array;
- return add_fast_array_element(ctx, p,
- JS_DupValue(ctx, val), flags);
- } else {
- goto convert_to_array;
- }
- } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) {
- /* convert the fast array to normal array */
- convert_to_array:
- if (convert_fast_array_to_array(ctx, p))
- return -1;
- goto generic_array;
- }
- } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) {
- JSProperty *plen;
- JSShapeProperty *pslen;
- generic_array:
- /* update the length field */
- plen = &p->prop[0];
- JS_ToUint32(ctx, &len, plen->u.value);
- if ((idx + 1) > len) {
- pslen = get_shape_prop(p->shape);
- if (unlikely(!(pslen->flags & JS_PROP_WRITABLE)))
- return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length);
- /* XXX: should update the length after defining
- the property */
- len = idx + 1;
- set_value(ctx, &plen->u.value, JS_NewUint32(ctx, len));
- }
- }
- } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
- p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
- ret = JS_AtomIsNumericIndex(ctx, prop);
- if (ret != 0) {
- if (ret < 0)
- return -1;
- return JS_ThrowTypeErrorOrFalse(ctx, flags, "cannot create numeric index in typed array");
- }
- } else if (!(flags & JS_PROP_NO_EXOTIC)) {
- const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
- if (em) {
- if (em->define_own_property) {
- return em->define_own_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p),
- prop, val, getter, setter, flags);
- }
- ret = JS_IsExtensible(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
- if (ret < 0)
- return -1;
- if (!ret)
- goto not_extensible;
- }
- }
- }
- if (!p->extensible) {
- not_extensible:
- return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible");
- }
- var_ref = NULL;
- delete_obj = NULL;
- if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
- prop_flags = (flags & (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) |
- JS_PROP_GETSET;
- } else {
- prop_flags = flags & JS_PROP_C_W_E;
- if (p->class_id == JS_CLASS_GLOBAL_OBJECT) {
- JSObject *p1 = JS_VALUE_GET_OBJ(p->u.global_object.uninitialized_vars);
- JSShapeProperty *prs1;
- JSProperty *pr1;
- prs1 = find_own_property(&pr1, p1, prop);
- if (prs1) {
- delete_obj = p1;
- var_ref = pr1->u.var_ref;
- js_rc(var_ref)->ref_count++;
- } else {
- var_ref = js_create_var_ref(ctx, FALSE);
- if (!var_ref)
- return -1;
- }
- var_ref->is_const = !(prop_flags & JS_PROP_WRITABLE);
- prop_flags |= JS_PROP_VARREF;
- }
- }
- pr = add_property(ctx, p, prop, prop_flags);
- if (unlikely(!pr)) {
- if (var_ref)
- free_var_ref(ctx->rt, var_ref);
- return -1;
- }
- if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
- pr->u.getset.getter = NULL;
- if ((flags & JS_PROP_HAS_GET) && JS_IsFunction(ctx, getter)) {
- pr->u.getset.getter =
- JS_VALUE_GET_OBJ(JS_DupValue(ctx, getter));
- }
- pr->u.getset.setter = NULL;
- if ((flags & JS_PROP_HAS_SET) && JS_IsFunction(ctx, setter)) {
- pr->u.getset.setter =
- JS_VALUE_GET_OBJ(JS_DupValue(ctx, setter));
- }
- } else if (p->class_id == JS_CLASS_GLOBAL_OBJECT) {
- if (delete_obj)
- delete_property(ctx, delete_obj, prop);
- pr->u.var_ref = var_ref;
- if (flags & JS_PROP_HAS_VALUE) {
- *var_ref->pvalue = JS_DupValue(ctx, val);
- } else {
- *var_ref->pvalue = JS_UNDEFINED;
- }
- } else {
- if (flags & JS_PROP_HAS_VALUE) {
- pr->u.value = JS_DupValue(ctx, val);
- } else {
- pr->u.value = JS_UNDEFINED;
- }
- }
- return TRUE;
- }
- /* return FALSE if not OK */
- static BOOL check_define_prop_flags(int prop_flags, int flags)
- {
- BOOL has_accessor, is_getset;
- if (!(prop_flags & JS_PROP_CONFIGURABLE)) {
- if ((flags & (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) ==
- (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) {
- return FALSE;
- }
- if ((flags & JS_PROP_HAS_ENUMERABLE) &&
- (flags & JS_PROP_ENUMERABLE) != (prop_flags & JS_PROP_ENUMERABLE))
- return FALSE;
- if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE |
- JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
- has_accessor = ((flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) != 0);
- is_getset = ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET);
- if (has_accessor != is_getset)
- return FALSE;
- if (!is_getset && !(prop_flags & JS_PROP_WRITABLE)) {
- /* not writable: cannot set the writable bit */
- if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) ==
- (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE))
- return FALSE;
- }
- }
- }
- return TRUE;
- }
- /* ensure that the shape can be safely modified */
- static int js_shape_prepare_update(JSContext *ctx, JSObject *p,
- JSShapeProperty **pprs)
- {
- JSShape *sh;
- uint32_t idx = 0; /* prevent warning */
- sh = p->shape;
- if (sh->is_hashed) {
- if (js_rc(sh)->ref_count != 1) {
- if (pprs)
- idx = *pprs - get_shape_prop(sh);
- /* clone the shape (the resulting one is no longer hashed) */
- sh = js_clone_shape(ctx, sh);
- if (!sh)
- return -1;
- js_free_shape(ctx->rt, p->shape);
- p->shape = sh;
- if (pprs)
- *pprs = get_shape_prop(sh) + idx;
- } else {
- js_shape_hash_unlink(ctx->rt, sh);
- sh->is_hashed = FALSE;
- }
- }
- return 0;
- }
- static int js_update_property_flags(JSContext *ctx, JSObject *p,
- JSShapeProperty **pprs, int flags)
- {
- if (flags != (*pprs)->flags) {
- if (js_shape_prepare_update(ctx, p, pprs))
- return -1;
- (*pprs)->flags = flags;
- }
- return 0;
- }
- /* allowed flags:
- JS_PROP_CONFIGURABLE, JS_PROP_WRITABLE, JS_PROP_ENUMERABLE
- JS_PROP_HAS_GET, JS_PROP_HAS_SET, JS_PROP_HAS_VALUE,
- JS_PROP_HAS_CONFIGURABLE, JS_PROP_HAS_WRITABLE, JS_PROP_HAS_ENUMERABLE,
- JS_PROP_THROW, JS_PROP_NO_EXOTIC.
- If JS_PROP_THROW is set, return an exception instead of FALSE.
- if JS_PROP_NO_EXOTIC is set, do not call the exotic
- define_own_property callback.
- return -1 (exception), FALSE or TRUE.
- */
- int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,
- JSAtom prop, JSValueConst val,
- JSValueConst getter, JSValueConst setter, int flags)
- {
- JSObject *p;
- JSShapeProperty *prs;
- JSProperty *pr;
- int mask, res;
- if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT) {
- JS_ThrowTypeErrorNotAnObject(ctx);
- return -1;
- }
- p = JS_VALUE_GET_OBJ(this_obj);
- redo_prop_update:
- prs = find_own_property(&pr, p, prop);
- if (prs) {
- /* the range of the Array length property is always tested before */
- if ((prs->flags & JS_PROP_LENGTH) && (flags & JS_PROP_HAS_VALUE)) {
- uint32_t array_length;
- if (JS_ToArrayLengthFree(ctx, &array_length,
- JS_DupValue(ctx, val), FALSE)) {
- return -1;
- }
- /* this code relies on the fact that Uint32 are never allocated */
- val = (JSValueConst)JS_NewUint32(ctx, array_length);
- /* prs may have been modified */
- prs = find_own_property(&pr, p, prop);
- assert(prs != NULL);
- }
- /* property already exists */
- if (!check_define_prop_flags(prs->flags, flags)) {
- not_configurable:
- return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable");
- }
- if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
- /* Instantiate property and retry */
- if (JS_AutoInitProperty(ctx, p, prop, pr, prs))
- return -1;
- goto redo_prop_update;
- }
- if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE |
- JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
- if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
- JSObject *new_getter, *new_setter;
- if (JS_IsFunction(ctx, getter)) {
- new_getter = JS_VALUE_GET_OBJ(getter);
- } else {
- new_getter = NULL;
- }
- if (JS_IsFunction(ctx, setter)) {
- new_setter = JS_VALUE_GET_OBJ(setter);
- } else {
- new_setter = NULL;
- }
- if ((prs->flags & JS_PROP_TMASK) != JS_PROP_GETSET) {
- if (js_shape_prepare_update(ctx, p, &prs))
- return -1;
- /* convert to getset */
- if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
- if (unlikely(p->class_id == JS_CLASS_GLOBAL_OBJECT)) {
- if (remove_global_object_property(ctx, p, prs, pr))
- return -1;
- }
- free_var_ref(ctx->rt, pr->u.var_ref);
- } else {
- JS_FreeValue(ctx, pr->u.value);
- }
- prs->flags = (prs->flags &
- (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) |
- JS_PROP_GETSET;
- pr->u.getset.getter = NULL;
- pr->u.getset.setter = NULL;
- } else {
- if (!(prs->flags & JS_PROP_CONFIGURABLE)) {
- if ((flags & JS_PROP_HAS_GET) &&
- new_getter != pr->u.getset.getter) {
- goto not_configurable;
- }
- if ((flags & JS_PROP_HAS_SET) &&
- new_setter != pr->u.getset.setter) {
- goto not_configurable;
- }
- }
- }
- if (flags & JS_PROP_HAS_GET) {
- if (pr->u.getset.getter)
- JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));
- if (new_getter)
- JS_DupValue(ctx, getter);
- pr->u.getset.getter = new_getter;
- }
- if (flags & JS_PROP_HAS_SET) {
- if (pr->u.getset.setter)
- JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));
- if (new_setter)
- JS_DupValue(ctx, setter);
- pr->u.getset.setter = new_setter;
- }
- } else {
- if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
- /* convert to data descriptor */
- JSVarRef *var_ref;
- if (unlikely(p->class_id == JS_CLASS_GLOBAL_OBJECT)) {
- var_ref = js_global_object_find_uninitialized_var(ctx, p, prop, FALSE);
- if (!var_ref)
- return -1;
- } else {
- var_ref = NULL;
- }
- if (js_shape_prepare_update(ctx, p, &prs)) {
- if (var_ref)
- free_var_ref(ctx->rt, var_ref);
- return -1;
- }
- if (pr->u.getset.getter)
- JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));
- if (pr->u.getset.setter)
- JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));
- if (var_ref) {
- prs->flags = (prs->flags & ~JS_PROP_TMASK) |
- JS_PROP_VARREF | JS_PROP_WRITABLE;
- pr->u.var_ref = var_ref;
- } else {
- prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE);
- pr->u.value = JS_UNDEFINED;
- }
- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
- /* Note: JS_PROP_VARREF is always writable */
- } else {
- if ((prs->flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 &&
- (flags & JS_PROP_HAS_VALUE)) {
- if (!js_same_value(ctx, val, pr->u.value)) {
- goto not_configurable;
- } else {
- return TRUE;
- }
- }
- }
- if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
- if (flags & JS_PROP_HAS_VALUE) {
- if (p->class_id == JS_CLASS_MODULE_NS) {
- /* JS_PROP_WRITABLE is always true for variable
- references, but they are write protected in module name
- spaces. */
- if (!js_same_value(ctx, val, *pr->u.var_ref->pvalue))
- goto not_configurable;
- } else {
- /* update the reference */
- set_value(ctx, pr->u.var_ref->pvalue,
- JS_DupValue(ctx, val));
- }
- }
- if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == JS_PROP_HAS_WRITABLE) {
- JSValue val1;
- if (p->class_id == JS_CLASS_MODULE_NS) {
- return JS_ThrowTypeErrorOrFalse(ctx, flags, "module namespace properties have writable = false");
- }
- if (js_shape_prepare_update(ctx, p, &prs))
- return -1;
- if (p->class_id == JS_CLASS_GLOBAL_OBJECT) {
- pr->u.var_ref->is_const = TRUE; /* mark as read-only */
- prs->flags &= ~JS_PROP_WRITABLE;
- } else {
- /* if writable is set to false, no longer a
- reference (for mapped arguments) */
- val1 = JS_DupValue(ctx, *pr->u.var_ref->pvalue);
- free_var_ref(ctx->rt, pr->u.var_ref);
- pr->u.value = val1;
- prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE);
- }
- }
- } else if (prs->flags & JS_PROP_LENGTH) {
- if (flags & JS_PROP_HAS_VALUE) {
- /* Note: no JS code is executable because
- 'val' is guaranted to be a Uint32 */
- res = set_array_length(ctx, p, JS_DupValue(ctx, val),
- flags);
- } else {
- res = TRUE;
- }
- /* still need to reset the writable flag if
- needed. The JS_PROP_LENGTH is kept because the
- Uint32 test is still done if the length
- property is read-only. */
- if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) ==
- JS_PROP_HAS_WRITABLE) {
- prs = get_shape_prop(p->shape);
- if (js_update_property_flags(ctx, p, &prs,
- prs->flags & ~JS_PROP_WRITABLE))
- return -1;
- }
- return res;
- } else {
- if (flags & JS_PROP_HAS_VALUE) {
- JS_FreeValue(ctx, pr->u.value);
- pr->u.value = JS_DupValue(ctx, val);
- }
- if (flags & JS_PROP_HAS_WRITABLE) {
- if (js_update_property_flags(ctx, p, &prs,
- (prs->flags & ~JS_PROP_WRITABLE) |
- (flags & JS_PROP_WRITABLE)))
- return -1;
- }
- }
- }
- }
- mask = 0;
- if (flags & JS_PROP_HAS_CONFIGURABLE)
- mask |= JS_PROP_CONFIGURABLE;
- if (flags & JS_PROP_HAS_ENUMERABLE)
- mask |= JS_PROP_ENUMERABLE;
- if (js_update_property_flags(ctx, p, &prs,
- (prs->flags & ~mask) | (flags & mask)))
- return -1;
- return TRUE;
- }
- /* handle modification of fast array elements */
- if (p->fast_array) {
- uint32_t idx;
- uint32_t prop_flags;
- if (p->class_id == JS_CLASS_ARRAY) {
- if (__JS_AtomIsTaggedInt(prop)) {
- idx = __JS_AtomToUInt32(prop);
- if (idx < p->u.array.count) {
- prop_flags = get_prop_flags(flags, JS_PROP_C_W_E);
- if (prop_flags != JS_PROP_C_W_E)
- goto convert_to_slow_array;
- if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
- convert_to_slow_array:
- if (convert_fast_array_to_array(ctx, p))
- return -1;
- else
- goto redo_prop_update;
- }
- if (flags & JS_PROP_HAS_VALUE) {
- set_value(ctx, &p->u.array.u.values[idx], JS_DupValue(ctx, val));
- }
- return TRUE;
- }
- }
- } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
- p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
- JSValue num;
- int ret;
- if (!__JS_AtomIsTaggedInt(prop)) {
- /* slow path with to handle all numeric indexes */
- num = JS_AtomIsNumericIndex1(ctx, prop);
- if (JS_IsUndefined(num))
- goto typed_array_done;
- if (JS_IsException(num))
- return -1;
- ret = JS_NumberIsInteger(ctx, num);
- if (ret < 0) {
- JS_FreeValue(ctx, num);
- return -1;
- }
- if (!ret) {
- JS_FreeValue(ctx, num);
- return JS_ThrowTypeErrorOrFalse(ctx, flags, "non integer index in typed array");
- }
- ret = JS_NumberIsNegativeOrMinusZero(ctx, num);
- JS_FreeValue(ctx, num);
- if (ret) {
- return JS_ThrowTypeErrorOrFalse(ctx, flags, "negative index in typed array");
- }
- if (!__JS_AtomIsTaggedInt(prop))
- goto typed_array_oob;
- }
- idx = __JS_AtomToUInt32(prop);
- /* if the typed array is detached, p->u.array.count = 0 */
- if (idx >= p->u.array.count) {
- typed_array_oob:
- return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound index in typed array");
- }
- prop_flags = get_prop_flags(flags, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
- if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET) ||
- prop_flags != (JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE)) {
- return JS_ThrowTypeErrorOrFalse(ctx, flags, "invalid descriptor flags");
- }
- if (flags & JS_PROP_HAS_VALUE) {
- return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), JS_DupValue(ctx, val), flags);
- }
- return TRUE;
- typed_array_done: ;
- }
- }
- return JS_CreateProperty(ctx, p, prop, val, getter, setter, flags);
- }
- static int JS_DefineAutoInitProperty(JSContext *ctx, JSValueConst this_obj,
- JSAtom prop, JSAutoInitIDEnum id,
- void *opaque, int flags)
- {
- JSObject *p;
- JSProperty *pr;
- if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT)
- return FALSE;
- p = JS_VALUE_GET_OBJ(this_obj);
- if (find_own_property(&pr, p, prop)) {
- /* property already exists */
- abort();
- return FALSE;
- }
- /* Specialized CreateProperty */
- pr = add_property(ctx, p, prop, (flags & JS_PROP_C_W_E) | JS_PROP_AUTOINIT);
- if (unlikely(!pr))
- return -1;
- pr->u.init.realm_and_id = (uintptr_t)JS_DupContext(ctx);
- assert((pr->u.init.realm_and_id & 3) == 0);
- assert(id <= 3);
- pr->u.init.realm_and_id |= id;
- pr->u.init.opaque = opaque;
- return TRUE;
- }
- /* shortcut to add or redefine a new property value */
- int JS_DefinePropertyValue(JSContext *ctx, JSValueConst this_obj,
- JSAtom prop, JSValue val, int flags)
- {
- int ret;
- ret = JS_DefineProperty(ctx, this_obj, prop, val, JS_UNDEFINED, JS_UNDEFINED,
- flags | JS_PROP_HAS_VALUE | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE);
- JS_FreeValue(ctx, val);
- return ret;
- }
- int JS_DefinePropertyValueValue(JSContext *ctx, JSValueConst this_obj,
- JSValue prop, JSValue val, int flags)
- {
- JSAtom atom;
- int ret;
- atom = JS_ValueToAtom(ctx, prop);
- JS_FreeValue(ctx, prop);
- if (unlikely(atom == JS_ATOM_NULL)) {
- JS_FreeValue(ctx, val);
- return -1;
- }
- ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags);
- JS_FreeAtom(ctx, atom);
- return ret;
- }
- int JS_DefinePropertyValueUint32(JSContext *ctx, JSValueConst this_obj,
- uint32_t idx, JSValue val, int flags)
- {
- return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewUint32(ctx, idx),
- val, flags);
- }
- int JS_DefinePropertyValueInt64(JSContext *ctx, JSValueConst this_obj,
- int64_t idx, JSValue val, int flags)
- {
- return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx),
- val, flags);
- }
- int JS_DefinePropertyValueStr(JSContext *ctx, JSValueConst this_obj,
- const char *prop, JSValue val, int flags)
- {
- JSAtom atom;
- int ret;
- atom = JS_NewAtom(ctx, prop);
- if (atom == JS_ATOM_NULL) {
- JS_FreeValue(ctx, val);
- return -1;
- }
- ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags);
- JS_FreeAtom(ctx, atom);
- return ret;
- }
- /* shortcut to add getter & setter */
- int JS_DefinePropertyGetSet(JSContext *ctx, JSValueConst this_obj,
- JSAtom prop, JSValue getter, JSValue setter,
- int flags)
- {
- int ret;
- ret = JS_DefineProperty(ctx, this_obj, prop, JS_UNDEFINED, getter, setter,
- flags | JS_PROP_HAS_GET | JS_PROP_HAS_SET |
- JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE);
- JS_FreeValue(ctx, getter);
- JS_FreeValue(ctx, setter);
- return ret;
- }
- static int JS_CreateDataPropertyUint32(JSContext *ctx, JSValueConst this_obj,
- int64_t idx, JSValue val, int flags)
- {
- return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx),
- val, flags | JS_PROP_CONFIGURABLE |
- JS_PROP_ENUMERABLE | JS_PROP_WRITABLE);
- }
- /* return TRUE if 'obj' has a non empty 'name' string */
- static BOOL js_object_has_name(JSContext *ctx, JSValueConst obj)
- {
- JSProperty *pr;
- JSShapeProperty *prs;
- JSValueConst val;
- JSString *p;
- prs = find_own_property(&pr, JS_VALUE_GET_OBJ(obj), JS_ATOM_name);
- if (!prs)
- return FALSE;
- if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL)
- return TRUE;
- val = pr->u.value;
- if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING)
- return TRUE;
- p = JS_VALUE_GET_STRING(val);
- return (p->len != 0);
- }
- static int JS_DefineObjectName(JSContext *ctx, JSValueConst obj,
- JSAtom name, int flags)
- {
- if (name != JS_ATOM_NULL
- && JS_IsObject(obj)
- && !js_object_has_name(ctx, obj)
- && JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, JS_AtomToString(ctx, name), flags) < 0) {
- return -1;
- }
- return 0;
- }
- static int JS_DefineObjectNameComputed(JSContext *ctx, JSValueConst obj,
- JSValueConst str, int flags)
- {
- if (JS_IsObject(obj) &&
- !js_object_has_name(ctx, obj)) {
- JSAtom prop;
- JSValue name_str;
- prop = JS_ValueToAtom(ctx, str);
- if (prop == JS_ATOM_NULL)
- return -1;
- name_str = js_get_function_name(ctx, prop);
- JS_FreeAtom(ctx, prop);
- if (JS_IsException(name_str))
- return -1;
- if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, name_str, flags) < 0)
- return -1;
- }
- return 0;
- }
- #define DEFINE_GLOBAL_LEX_VAR (1 << 7)
- #define DEFINE_GLOBAL_FUNC_VAR (1 << 6)
- static JSValue JS_ThrowSyntaxErrorVarRedeclaration(JSContext *ctx, JSAtom prop)
- {
- return JS_ThrowSyntaxErrorAtom(ctx, "redeclaration of '%s'", prop);
- }
- /* flags is 0, DEFINE_GLOBAL_LEX_VAR or DEFINE_GLOBAL_FUNC_VAR */
- /* XXX: could support exotic global object. */
- static int JS_CheckDefineGlobalVar(JSContext *ctx, JSAtom prop, int flags)
- {
- JSObject *p;
- JSShapeProperty *prs;
- p = JS_VALUE_GET_OBJ(ctx->global_obj);
- prs = find_own_property1(p, prop);
- /* XXX: should handle JS_PROP_AUTOINIT */
- if (flags & DEFINE_GLOBAL_LEX_VAR) {
- if (prs && !(prs->flags & JS_PROP_CONFIGURABLE))
- goto fail_redeclaration;
- } else {
- if (!prs && !p->extensible)
- goto define_error;
- if (flags & DEFINE_GLOBAL_FUNC_VAR) {
- if (prs) {
- if (!(prs->flags & JS_PROP_CONFIGURABLE) &&
- ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET ||
- ((prs->flags & (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)) !=
- (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)))) {
- define_error:
- JS_ThrowTypeErrorAtom(ctx, "cannot define variable '%s'",
- prop);
- return -1;
- }
- }
- }
- }
- /* check if there already is a lexical declaration */
- p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
- prs = find_own_property1(p, prop);
- if (prs) {
- fail_redeclaration:
- JS_ThrowSyntaxErrorVarRedeclaration(ctx, prop);
- return -1;
- }
- return 0;
- }
- /* construct a reference to a global variable */
- static int JS_GetGlobalVarRef(JSContext *ctx, JSAtom prop, JSValue *sp)
- {
- JSObject *p;
- JSShapeProperty *prs;
- JSProperty *pr;
- /* no exotic behavior is possible in global_var_obj */
- p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
- prs = find_own_property(&pr, p, prop);
- if (prs) {
- /* XXX: conformance: do these tests in
- OP_put_var_ref/OP_get_var_ref ? */
- if (unlikely(JS_IsUninitialized(*pr->u.var_ref->pvalue))) {
- JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
- return -1;
- }
- if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) {
- return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop);
- }
- sp[0] = JS_DupValue(ctx, ctx->global_var_obj);
- } else {
- int ret;
- ret = JS_HasProperty(ctx, ctx->global_obj, prop);
- if (ret < 0)
- return -1;
- if (ret) {
- sp[0] = JS_DupValue(ctx, ctx->global_obj);
- } else {
- sp[0] = JS_UNDEFINED;
- }
- }
- sp[1] = JS_AtomToValue(ctx, prop);
- return 0;
- }
- /* return -1, FALSE or TRUE */
- static int JS_DeleteGlobalVar(JSContext *ctx, JSAtom prop)
- {
- JSObject *p;
- JSShapeProperty *prs;
- JSProperty *pr;
- int ret;
- /* 9.1.1.4.7 DeleteBinding ( N ) */
- p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
- prs = find_own_property(&pr, p, prop);
- if (prs)
- return FALSE; /* lexical variables cannot be deleted */
- ret = JS_HasProperty(ctx, ctx->global_obj, prop);
- if (ret < 0)
- return -1;
- if (ret) {
- return JS_DeleteProperty(ctx, ctx->global_obj, prop, 0);
- } else {
- return TRUE;
- }
- }
- /* return -1, FALSE or TRUE. return FALSE if not configurable or
- invalid object. return -1 in case of exception.
- flags can be 0, JS_PROP_THROW or JS_PROP_THROW_STRICT */
- int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags)
- {
- JSValue obj1;
- JSObject *p;
- int res;
- obj1 = JS_ToObject(ctx, obj);
- if (JS_IsException(obj1))
- return -1;
- p = JS_VALUE_GET_OBJ(obj1);
- res = delete_property(ctx, p, prop);
- JS_FreeValue(ctx, obj1);
- if (res != FALSE)
- return res;
- if ((flags & JS_PROP_THROW) ||
- ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
- JS_ThrowTypeError(ctx, "could not delete property");
- return -1;
- }
- return FALSE;
- }
- int JS_DeletePropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, int flags)
- {
- JSAtom prop;
- int res;
- if ((uint64_t)idx <= JS_ATOM_MAX_INT) {
- /* fast path for fast arrays */
- return JS_DeleteProperty(ctx, obj, __JS_AtomFromUInt32(idx), flags);
- }
- prop = JS_NewAtomInt64(ctx, idx);
- if (prop == JS_ATOM_NULL)
- return -1;
- res = JS_DeleteProperty(ctx, obj, prop, flags);
- JS_FreeAtom(ctx, prop);
- return res;
- }
- BOOL JS_IsFunction(JSContext *ctx, JSValueConst val)
- {
- JSObject *p;
- if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
- return FALSE;
- p = JS_VALUE_GET_OBJ(val);
- switch(p->class_id) {
- case JS_CLASS_BYTECODE_FUNCTION:
- return TRUE;
- case JS_CLASS_PROXY:
- return p->u.proxy_data->is_func;
- default:
- return (ctx->rt->class_array[p->class_id].call != NULL);
- }
- }
- BOOL JS_IsCFunction(JSContext *ctx, JSValueConst val, JSCFunction *func, int magic)
- {
- JSObject *p;
- if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
- return FALSE;
- p = JS_VALUE_GET_OBJ(val);
- if (p->class_id == JS_CLASS_C_FUNCTION)
- return (p->u.cfunc.c_function.generic == func && p->u.cfunc.magic == magic);
- else
- return FALSE;
- }
- BOOL JS_IsConstructor(JSContext *ctx, JSValueConst val)
- {
- JSObject *p;
- if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
- return FALSE;
- p = JS_VALUE_GET_OBJ(val);
- return p->is_constructor;
- }
- BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, BOOL val)
- {
- JSObject *p;
- if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)
- return FALSE;
- p = JS_VALUE_GET_OBJ(func_obj);
- p->is_constructor = val;
- return TRUE;
- }
- BOOL JS_IsError(JSContext *ctx, JSValueConst val)
- {
- JSObject *p;
- if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
- return FALSE;
- p = JS_VALUE_GET_OBJ(val);
- return (p->class_id == JS_CLASS_ERROR);
- }
- /* must be called after JS_Throw() */
- void JS_SetUncatchableException(JSContext *ctx, BOOL flag)
- {
- ctx->rt->current_exception_is_uncatchable = flag;
- }
- void JS_SetOpaque(JSValue obj, void *opaque)
- {
- JSObject *p;
- if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
- p = JS_VALUE_GET_OBJ(obj);
- p->u.opaque = opaque;
- }
- }
- /* return NULL if not an object of class class_id */
- void *JS_GetOpaque(JSValueConst obj, JSClassID class_id)
- {
- JSObject *p;
- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
- return NULL;
- p = JS_VALUE_GET_OBJ(obj);
- if (p->class_id != class_id)
- return NULL;
- return p->u.opaque;
- }
- void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id)
- {
- void *p = JS_GetOpaque(obj, class_id);
- if (unlikely(!p)) {
- JS_ThrowTypeErrorInvalidClass(ctx, class_id);
- }
- return p;
- }
- void *JS_GetAnyOpaque(JSValueConst obj, JSClassID *class_id)
- {
- JSObject *p;
- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {
- *class_id = 0;
- return NULL;
- }
- p = JS_VALUE_GET_OBJ(obj);
- *class_id = p->class_id;
- return p->u.opaque;
- }
- static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint)
- {
- int i;
- BOOL force_ordinary;
- JSAtom method_name;
- JSValue method, ret;
- if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
- return val;
- force_ordinary = hint & HINT_FORCE_ORDINARY;
- hint &= ~HINT_FORCE_ORDINARY;
- if (!force_ordinary) {
- method = JS_GetProperty(ctx, val, JS_ATOM_Symbol_toPrimitive);
- if (JS_IsException(method))
- goto exception;
- /* ECMA says *If exoticToPrim is not undefined* but tests in
- test262 use null as a non callable converter */
- if (!JS_IsUndefined(method) && !JS_IsNull(method)) {
- JSAtom atom;
- JSValue arg;
- switch(hint) {
- case HINT_STRING:
- atom = JS_ATOM_string;
- break;
- case HINT_NUMBER:
- atom = JS_ATOM_number;
- break;
- default:
- case HINT_NONE:
- atom = JS_ATOM_default;
- break;
- }
- arg = JS_AtomToString(ctx, atom);
- ret = JS_CallFree(ctx, method, val, 1, (JSValueConst *)&arg);
- JS_FreeValue(ctx, arg);
- if (JS_IsException(ret))
- goto exception;
- JS_FreeValue(ctx, val);
- if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT)
- return ret;
- JS_FreeValue(ctx, ret);
- return JS_ThrowTypeError(ctx, "toPrimitive");
- }
- }
- if (hint != HINT_STRING)
- hint = HINT_NUMBER;
- for(i = 0; i < 2; i++) {
- if ((i ^ hint) == 0) {
- method_name = JS_ATOM_toString;
- } else {
- method_name = JS_ATOM_valueOf;
- }
- method = JS_GetProperty(ctx, val, method_name);
- if (JS_IsException(method))
- goto exception;
- if (JS_IsFunction(ctx, method)) {
- ret = JS_CallFree(ctx, method, val, 0, NULL);
- if (JS_IsException(ret))
- goto exception;
- if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {
- JS_FreeValue(ctx, val);
- return ret;
- }
- JS_FreeValue(ctx, ret);
- } else {
- JS_FreeValue(ctx, method);
- }
- }
- JS_ThrowTypeError(ctx, "toPrimitive");
- exception:
- JS_FreeValue(ctx, val);
- return JS_EXCEPTION;
- }
- static JSValue JS_ToPrimitive(JSContext *ctx, JSValueConst val, int hint)
- {
- return JS_ToPrimitiveFree(ctx, JS_DupValue(ctx, val), hint);
- }
- void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj)
- {
- JSObject *p;
- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
- return;
- p = JS_VALUE_GET_OBJ(obj);
- p->is_HTMLDDA = TRUE;
- }
- static inline BOOL JS_IsHTMLDDA(JSContext *ctx, JSValueConst obj)
- {
- JSObject *p;
- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
- return FALSE;
- p = JS_VALUE_GET_OBJ(obj);
- return p->is_HTMLDDA;
- }
- static int JS_ToBoolFree(JSContext *ctx, JSValue val)
- {
- uint32_t tag = JS_VALUE_GET_TAG(val);
- switch(tag) {
- case JS_TAG_INT:
- return JS_VALUE_GET_INT(val) != 0;
- case JS_TAG_BOOL:
- case JS_TAG_NULL:
- case JS_TAG_UNDEFINED:
- return JS_VALUE_GET_INT(val);
- case JS_TAG_EXCEPTION:
- return -1;
- case JS_TAG_STRING:
- {
- BOOL ret = JS_VALUE_GET_STRING(val)->len != 0;
- JS_FreeValue(ctx, val);
- return ret;
- }
- case JS_TAG_STRING_ROPE:
- {
- BOOL ret = JS_VALUE_GET_STRING_ROPE(val)->len != 0;
- JS_FreeValue(ctx, val);
- return ret;
- }
- case JS_TAG_SHORT_BIG_INT:
- return JS_VALUE_GET_SHORT_BIG_INT(val) != 0;
- case JS_TAG_BIG_INT:
- {
- JSBigInt *p = JS_VALUE_GET_PTR(val);
- BOOL ret;
- int i;
-
- /* fail safe: we assume it is not necessarily
- normalized. Beginning from the MSB ensures that the
- test is fast. */
- ret = FALSE;
- for(i = p->len - 1; i >= 0; i--) {
- if (p->tab[i] != 0) {
- ret = TRUE;
- break;
- }
- }
- JS_FreeValue(ctx, val);
- return ret;
- }
- case JS_TAG_OBJECT:
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- BOOL ret;
- ret = !p->is_HTMLDDA;
- JS_FreeValue(ctx, val);
- return ret;
- }
- break;
- default:
- if (JS_TAG_IS_FLOAT64(tag)) {
- double d = JS_VALUE_GET_FLOAT64(val);
- return !isnan(d) && d != 0;
- } else {
- JS_FreeValue(ctx, val);
- return TRUE;
- }
- }
- }
- int JS_ToBool(JSContext *ctx, JSValueConst val)
- {
- return JS_ToBoolFree(ctx, JS_DupValue(ctx, val));
- }
- static int skip_spaces(const char *pc)
- {
- const uint8_t *p, *p_next, *p_start;
- uint32_t c;
- p = p_start = (const uint8_t *)pc;
- for (;;) {
- c = *p;
- if (c < 128) {
- if (!((c >= 0x09 && c <= 0x0d) || (c == 0x20)))
- break;
- p++;
- } else {
- c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next);
- if (!lre_is_space(c))
- break;
- p = p_next;
- }
- }
- return p - p_start;
- }
- static inline int to_digit(int c)
- {
- if (c >= '0' && c <= '9')
- return c - '0';
- else if (c >= 'A' && c <= 'Z')
- return c - 'A' + 10;
- else if (c >= 'a' && c <= 'z')
- return c - 'a' + 10;
- else
- return 36;
- }
- /* bigint support */
- #define JS_BIGINT_MAX_SIZE ((1024 * 1024) / JS_LIMB_BITS) /* in limbs */
- /* it is currently assumed that JS_SHORT_BIG_INT_BITS = JS_LIMB_BITS */
- #if JS_SHORT_BIG_INT_BITS == 32
- #define JS_SHORT_BIG_INT_MIN INT32_MIN
- #define JS_SHORT_BIG_INT_MAX INT32_MAX
- #elif JS_SHORT_BIG_INT_BITS == 64
- #define JS_SHORT_BIG_INT_MIN INT64_MIN
- #define JS_SHORT_BIG_INT_MAX INT64_MAX
- #else
- #error unsupported
- #endif
- #define ADDC(res, carry_out, op1, op2, carry_in) \
- do { \
- js_limb_t __v, __a, __k, __k1; \
- __v = (op1); \
- __a = __v + (op2); \
- __k1 = __a < __v; \
- __k = (carry_in); \
- __a = __a + __k; \
- carry_out = (__a < __k) | __k1; \
- res = __a; \
- } while (0)
- #if JS_LIMB_BITS == 32
- /* a != 0 */
- static inline js_limb_t js_limb_clz(js_limb_t a)
- {
- return clz32(a);
- }
- #else
- static inline js_limb_t js_limb_clz(js_limb_t a)
- {
- return clz64(a);
- }
- #endif
- /* handle a = 0 too */
- static inline js_limb_t js_limb_safe_clz(js_limb_t a)
- {
- if (a == 0)
- return JS_LIMB_BITS;
- else
- return js_limb_clz(a);
- }
- static js_limb_t mp_add(js_limb_t *res, const js_limb_t *op1, const js_limb_t *op2,
- js_limb_t n, js_limb_t carry)
- {
- int i;
- for(i = 0;i < n; i++) {
- ADDC(res[i], carry, op1[i], op2[i], carry);
- }
- return carry;
- }
- static js_limb_t mp_sub(js_limb_t *res, const js_limb_t *op1, const js_limb_t *op2,
- int n, js_limb_t carry)
- {
- int i;
- js_limb_t k, a, v, k1;
- k = carry;
- for(i=0;i<n;i++) {
- v = op1[i];
- a = v - op2[i];
- k1 = a > v;
- v = a - k;
- k = (v > a) | k1;
- res[i] = v;
- }
- return k;
- }
- /* compute 0 - op2. carry = 0 or 1. */
- static js_limb_t mp_neg(js_limb_t *res, const js_limb_t *op2, int n)
- {
- int i;
- js_limb_t v, carry;
- carry = 1;
- for(i=0;i<n;i++) {
- v = ~op2[i] + carry;
- carry = v < carry;
- res[i] = v;
- }
- return carry;
- }
- /* tabr[] = taba[] * b + l. Return the high carry */
- static js_limb_t mp_mul1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n,
- js_limb_t b, js_limb_t l)
- {
- js_limb_t i;
- js_dlimb_t t;
- for(i = 0; i < n; i++) {
- t = (js_dlimb_t)taba[i] * (js_dlimb_t)b + l;
- tabr[i] = t;
- l = t >> JS_LIMB_BITS;
- }
- return l;
- }
- static js_limb_t mp_div1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n,
- js_limb_t b, js_limb_t r)
- {
- js_slimb_t i;
- js_dlimb_t a1;
- for(i = n - 1; i >= 0; i--) {
- a1 = ((js_dlimb_t)r << JS_LIMB_BITS) | taba[i];
- tabr[i] = a1 / b;
- r = a1 % b;
- }
- return r;
- }
- /* tabr[] += taba[] * b, return the high word. */
- static js_limb_t mp_add_mul1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n,
- js_limb_t b)
- {
- js_limb_t i, l;
- js_dlimb_t t;
- l = 0;
- for(i = 0; i < n; i++) {
- t = (js_dlimb_t)taba[i] * (js_dlimb_t)b + l + tabr[i];
- tabr[i] = t;
- l = t >> JS_LIMB_BITS;
- }
- return l;
- }
- /* size of the result : op1_size + op2_size. */
- static void mp_mul_basecase(js_limb_t *result,
- const js_limb_t *op1, js_limb_t op1_size,
- const js_limb_t *op2, js_limb_t op2_size)
- {
- int i;
- js_limb_t r;
-
- result[op1_size] = mp_mul1(result, op1, op1_size, op2[0], 0);
- for(i=1;i<op2_size;i++) {
- r = mp_add_mul1(result + i, op1, op1_size, op2[i]);
- result[i + op1_size] = r;
- }
- }
- /* tabr[] -= taba[] * b. Return the value to substract to the high
- word. */
- static js_limb_t mp_sub_mul1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n,
- js_limb_t b)
- {
- js_limb_t i, l;
- js_dlimb_t t;
- l = 0;
- for(i = 0; i < n; i++) {
- t = tabr[i] - (js_dlimb_t)taba[i] * (js_dlimb_t)b - l;
- tabr[i] = t;
- l = -(t >> JS_LIMB_BITS);
- }
- return l;
- }
- /* WARNING: d must be >= 2^(JS_LIMB_BITS-1) */
- static inline js_limb_t udiv1norm_init(js_limb_t d)
- {
- js_limb_t a0, a1;
- a1 = -d - 1;
- a0 = -1;
- return (((js_dlimb_t)a1 << JS_LIMB_BITS) | a0) / d;
- }
- /* return the quotient and the remainder in '*pr'of 'a1*2^JS_LIMB_BITS+a0
- / d' with 0 <= a1 < d. */
- static inline js_limb_t udiv1norm(js_limb_t *pr, js_limb_t a1, js_limb_t a0,
- js_limb_t d, js_limb_t d_inv)
- {
- js_limb_t n1m, n_adj, q, r, ah;
- js_dlimb_t a;
- n1m = ((js_slimb_t)a0 >> (JS_LIMB_BITS - 1));
- n_adj = a0 + (n1m & d);
- a = (js_dlimb_t)d_inv * (a1 - n1m) + n_adj;
- q = (a >> JS_LIMB_BITS) + a1;
- /* compute a - q * r and update q so that the remainder is\
- between 0 and d - 1 */
- a = ((js_dlimb_t)a1 << JS_LIMB_BITS) | a0;
- a = a - (js_dlimb_t)q * d - d;
- ah = a >> JS_LIMB_BITS;
- q += 1 + ah;
- r = (js_limb_t)a + (ah & d);
- *pr = r;
- return q;
- }
- #define UDIV1NORM_THRESHOLD 3
- /* b must be >= 1 << (JS_LIMB_BITS - 1) */
- static js_limb_t mp_div1norm(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n,
- js_limb_t b, js_limb_t r)
- {
- js_slimb_t i;
- if (n >= UDIV1NORM_THRESHOLD) {
- js_limb_t b_inv;
- b_inv = udiv1norm_init(b);
- for(i = n - 1; i >= 0; i--) {
- tabr[i] = udiv1norm(&r, r, taba[i], b, b_inv);
- }
- } else {
- js_dlimb_t a1;
- for(i = n - 1; i >= 0; i--) {
- a1 = ((js_dlimb_t)r << JS_LIMB_BITS) | taba[i];
- tabr[i] = a1 / b;
- r = a1 % b;
- }
- }
- return r;
- }
- /* base case division: divides taba[0..na-1] by tabb[0..nb-1]. tabb[nb
- - 1] must be >= 1 << (JS_LIMB_BITS - 1). na - nb must be >= 0. 'taba'
- is modified and contains the remainder (nb limbs). tabq[0..na-nb]
- contains the quotient with tabq[na - nb] <= 1. */
- static void mp_divnorm(js_limb_t *tabq, js_limb_t *taba, js_limb_t na,
- const js_limb_t *tabb, js_limb_t nb)
- {
- js_limb_t r, a, c, q, v, b1, b1_inv, n, dummy_r;
- int i, j;
- b1 = tabb[nb - 1];
- if (nb == 1) {
- taba[0] = mp_div1norm(tabq, taba, na, b1, 0);
- return;
- }
- n = na - nb;
- if (n >= UDIV1NORM_THRESHOLD)
- b1_inv = udiv1norm_init(b1);
- else
- b1_inv = 0;
- /* first iteration: the quotient is only 0 or 1 */
- q = 1;
- for(j = nb - 1; j >= 0; j--) {
- if (taba[n + j] != tabb[j]) {
- if (taba[n + j] < tabb[j])
- q = 0;
- break;
- }
- }
- tabq[n] = q;
- if (q) {
- mp_sub(taba + n, taba + n, tabb, nb, 0);
- }
- for(i = n - 1; i >= 0; i--) {
- if (unlikely(taba[i + nb] >= b1)) {
- q = -1;
- } else if (b1_inv) {
- q = udiv1norm(&dummy_r, taba[i + nb], taba[i + nb - 1], b1, b1_inv);
- } else {
- js_dlimb_t al;
- al = ((js_dlimb_t)taba[i + nb] << JS_LIMB_BITS) | taba[i + nb - 1];
- q = al / b1;
- r = al % b1;
- }
- r = mp_sub_mul1(taba + i, tabb, nb, q);
- v = taba[i + nb];
- a = v - r;
- c = (a > v);
- taba[i + nb] = a;
- if (c != 0) {
- /* negative result */
- for(;;) {
- q--;
- c = mp_add(taba + i, taba + i, tabb, nb, 0);
- /* propagate carry and test if positive result */
- if (c != 0) {
- if (++taba[i + nb] == 0) {
- break;
- }
- }
- }
- }
- tabq[i] = q;
- }
- }
- /* 1 <= shift <= JS_LIMB_BITS - 1 */
- static js_limb_t mp_shl(js_limb_t *tabr, const js_limb_t *taba, int n,
- int shift)
- {
- int i;
- js_limb_t l, v;
- l = 0;
- for(i = 0; i < n; i++) {
- v = taba[i];
- tabr[i] = (v << shift) | l;
- l = v >> (JS_LIMB_BITS - shift);
- }
- return l;
- }
- /* r = (a + high*B^n) >> shift. Return the remainder r (0 <= r < 2^shift).
- 1 <= shift <= LIMB_BITS - 1 */
- static js_limb_t mp_shr(js_limb_t *tab_r, const js_limb_t *tab, int n,
- int shift, js_limb_t high)
- {
- int i;
- js_limb_t l, a;
- l = high;
- for(i = n - 1; i >= 0; i--) {
- a = tab[i];
- tab_r[i] = (a >> shift) | (l << (JS_LIMB_BITS - shift));
- l = a;
- }
- return l & (((js_limb_t)1 << shift) - 1);
- }
- static JSBigInt *js_bigint_new(JSContext *ctx, int len)
- {
- JSBigInt *r;
- if (len > JS_BIGINT_MAX_SIZE) {
- JS_ThrowRangeError(ctx, "BigInt is too large to allocate");
- return NULL;
- }
- r = js_malloc(ctx, sizeof(JSBigInt) + len * sizeof(js_limb_t));
- if (!r)
- return NULL;
- js_rc(r)->ref_count = 1;
- r->len = len;
- return r;
- }
- static JSBigInt *js_bigint_set_si(JSBigIntBuf *buf, js_slimb_t a)
- {
- JSBigInt *r = (JSBigInt *)buf->big_int_buf;
- r->len = 1;
- r->tab[0] = a;
- return r;
- }
- static JSBigInt *js_bigint_set_si64(JSBigIntBuf *buf, int64_t a)
- {
- #if JS_LIMB_BITS == 64
- return js_bigint_set_si(buf, a);
- #else
- JSBigInt *r = (JSBigInt *)buf->big_int_buf;
- if (a >= INT32_MIN && a <= INT32_MAX) {
- r->len = 1;
- r->tab[0] = a;
- } else {
- r->len = 2;
- r->tab[0] = a;
- r->tab[1] = a >> JS_LIMB_BITS;
- }
- return r;
- #endif
- }
- /* val must be a short big int */
- static JSBigInt *js_bigint_set_short(JSBigIntBuf *buf, JSValueConst val)
- {
- return js_bigint_set_si(buf, JS_VALUE_GET_SHORT_BIG_INT(val));
- }
- static __maybe_unused void js_bigint_dump1(JSContext *ctx, const char *str,
- const js_limb_t *tab, int len)
- {
- int i;
- printf("%s: ", str);
- for(i = len - 1; i >= 0; i--) {
- #if JS_LIMB_BITS == 32
- printf(" %08x", tab[i]);
- #else
- printf(" %016" PRIx64, tab[i]);
- #endif
- }
- printf("\n");
- }
- static __maybe_unused void js_bigint_dump(JSContext *ctx, const char *str,
- const JSBigInt *p)
- {
- js_bigint_dump1(ctx, str, p->tab, p->len);
- }
- static JSBigInt *js_bigint_new_si(JSContext *ctx, js_slimb_t a)
- {
- JSBigInt *r;
- r = js_bigint_new(ctx, 1);
- if (!r)
- return NULL;
- r->tab[0] = a;
- return r;
- }
- static JSBigInt *js_bigint_new_si64(JSContext *ctx, int64_t a)
- {
- #if JS_LIMB_BITS == 64
- return js_bigint_new_si(ctx, a);
- #else
- if (a >= INT32_MIN && a <= INT32_MAX) {
- return js_bigint_new_si(ctx, a);
- } else {
- JSBigInt *r;
- r = js_bigint_new(ctx, 2);
- if (!r)
- return NULL;
- r->tab[0] = a;
- r->tab[1] = a >> 32;
- return r;
- }
- #endif
- }
- static JSBigInt *js_bigint_new_ui64(JSContext *ctx, uint64_t a)
- {
- if (a <= INT64_MAX) {
- return js_bigint_new_si64(ctx, a);
- } else {
- JSBigInt *r;
- r = js_bigint_new(ctx, (65 + JS_LIMB_BITS - 1) / JS_LIMB_BITS);
- if (!r)
- return NULL;
- #if JS_LIMB_BITS == 64
- r->tab[0] = a;
- r->tab[1] = 0;
- #else
- r->tab[0] = a;
- r->tab[1] = a >> 32;
- r->tab[2] = 0;
- #endif
- return r;
- }
- }
- static JSBigInt *js_bigint_new_di(JSContext *ctx, js_sdlimb_t a)
- {
- JSBigInt *r;
- if (a == (js_slimb_t)a) {
- r = js_bigint_new(ctx, 1);
- if (!r)
- return NULL;
- r->tab[0] = a;
- } else {
- r = js_bigint_new(ctx, 2);
- if (!r)
- return NULL;
- r->tab[0] = a;
- r->tab[1] = a >> JS_LIMB_BITS;
- }
- return r;
- }
- /* Remove redundant high order limbs. Warning: 'a' may be
- reallocated. Can never fail.
- */
- static JSBigInt *js_bigint_normalize1(JSContext *ctx, JSBigInt *a, int l)
- {
- js_limb_t v;
- assert(js_rc(a)->ref_count == 1);
- while (l > 1) {
- v = a->tab[l - 1];
- if ((v != 0 && v != -1) ||
- (v & 1) != (a->tab[l - 2] >> (JS_LIMB_BITS - 1))) {
- break;
- }
- l--;
- }
- if (l != a->len) {
- JSBigInt *a1;
- /* realloc to reduce the size */
- a->len = l;
- a1 = js_realloc(ctx, a, sizeof(JSBigInt) + l * sizeof(js_limb_t));
- if (a1)
- a = a1;
- }
- return a;
- }
- static JSBigInt *js_bigint_normalize(JSContext *ctx, JSBigInt *a)
- {
- return js_bigint_normalize1(ctx, a, a->len);
- }
- /* return 0 or 1 depending on the sign */
- static inline int js_bigint_sign(const JSBigInt *a)
- {
- return a->tab[a->len - 1] >> (JS_LIMB_BITS - 1);
- }
- static js_slimb_t js_bigint_get_si_sat(const JSBigInt *a)
- {
- if (a->len == 1) {
- return a->tab[0];
- } else {
- #if JS_LIMB_BITS == 32
- if (js_bigint_sign(a))
- return INT32_MIN;
- else
- return INT32_MAX;
- #else
- if (js_bigint_sign(a))
- return INT64_MIN;
- else
- return INT64_MAX;
- #endif
- }
- }
- /* add the op1 limb */
- static JSBigInt *js_bigint_extend(JSContext *ctx, JSBigInt *r,
- js_limb_t op1)
- {
- int n2 = r->len;
- if ((op1 != 0 && op1 != -1) ||
- (op1 & 1) != r->tab[n2 - 1] >> (JS_LIMB_BITS - 1)) {
- JSBigInt *r1;
- r1 = js_realloc(ctx, r,
- sizeof(JSBigInt) + (n2 + 1) * sizeof(js_limb_t));
- if (!r1) {
- js_free(ctx, r);
- return NULL;
- }
- r = r1;
- r->len = n2 + 1;
- r->tab[n2] = op1;
- } else {
- /* otherwise still need to normalize the result */
- r = js_bigint_normalize(ctx, r);
- }
- return r;
- }
- /* return NULL in case of error. Compute a + b (b_neg = 0) or a - b
- (b_neg = 1) */
- /* XXX: optimize */
- static JSBigInt *js_bigint_add(JSContext *ctx, const JSBigInt *a,
- const JSBigInt *b, int b_neg)
- {
- JSBigInt *r;
- int n1, n2, i;
- js_limb_t carry, op1, op2, a_sign, b_sign;
-
- n2 = max_int(a->len, b->len);
- n1 = min_int(a->len, b->len);
- r = js_bigint_new(ctx, n2);
- if (!r)
- return NULL;
- /* XXX: optimize */
- /* common part */
- carry = b_neg;
- for(i = 0; i < n1; i++) {
- op1 = a->tab[i];
- op2 = b->tab[i] ^ (-b_neg);
- ADDC(r->tab[i], carry, op1, op2, carry);
- }
- a_sign = -js_bigint_sign(a);
- b_sign = (-js_bigint_sign(b)) ^ (-b_neg);
- /* part with sign extension of one operand */
- if (a->len > b->len) {
- for(i = n1; i < n2; i++) {
- op1 = a->tab[i];
- ADDC(r->tab[i], carry, op1, b_sign, carry);
- }
- } else if (a->len < b->len) {
- for(i = n1; i < n2; i++) {
- op2 = b->tab[i] ^ (-b_neg);
- ADDC(r->tab[i], carry, a_sign, op2, carry);
- }
- }
- /* part with sign extension for both operands. Extend the result
- if necessary */
- return js_bigint_extend(ctx, r, a_sign + b_sign + carry);
- }
- /* XXX: optimize */
- static JSBigInt *js_bigint_neg(JSContext *ctx, const JSBigInt *a)
- {
- JSBigIntBuf buf;
- JSBigInt *b;
- b = js_bigint_set_si(&buf, 0);
- return js_bigint_add(ctx, b, a, 1);
- }
- static JSBigInt *js_bigint_mul(JSContext *ctx, const JSBigInt *a,
- const JSBigInt *b)
- {
- JSBigInt *r;
-
- r = js_bigint_new(ctx, a->len + b->len);
- if (!r)
- return NULL;
- mp_mul_basecase(r->tab, a->tab, a->len, b->tab, b->len);
- /* correct the result if negative operands (no overflow is
- possible) */
- if (js_bigint_sign(a))
- mp_sub(r->tab + a->len, r->tab + a->len, b->tab, b->len, 0);
- if (js_bigint_sign(b))
- mp_sub(r->tab + b->len, r->tab + b->len, a->tab, a->len, 0);
- return js_bigint_normalize(ctx, r);
- }
- /* return the division or the remainder. 'b' must be != 0. return NULL
- in case of exception (division by zero or memory error) */
- static JSBigInt *js_bigint_divrem(JSContext *ctx, const JSBigInt *a,
- const JSBigInt *b, BOOL is_rem)
- {
- JSBigInt *r, *q;
- js_limb_t *tabb, h;
- int na, nb, a_sign, b_sign, shift;
-
- if (b->len == 1 && b->tab[0] == 0) {
- JS_ThrowRangeError(ctx, "BigInt division by zero");
- return NULL;
- }
-
- a_sign = js_bigint_sign(a);
- b_sign = js_bigint_sign(b);
- na = a->len;
- nb = b->len;
- r = js_bigint_new(ctx, na + 2);
- if (!r)
- return NULL;
- if (a_sign) {
- mp_neg(r->tab, a->tab, na);
- } else {
- memcpy(r->tab, a->tab, na * sizeof(a->tab[0]));
- }
- /* normalize */
- while (na > 1 && r->tab[na - 1] == 0)
- na--;
- tabb = js_malloc(ctx, nb * sizeof(tabb[0]));
- if (!tabb) {
- js_free(ctx, r);
- return NULL;
- }
- if (b_sign) {
- mp_neg(tabb, b->tab, nb);
- } else {
- memcpy(tabb, b->tab, nb * sizeof(tabb[0]));
- }
- /* normalize */
- while (nb > 1 && tabb[nb - 1] == 0)
- nb--;
- /* trivial case if 'a' is small */
- if (na < nb) {
- js_free(ctx, r);
- js_free(ctx, tabb);
- if (is_rem) {
- /* r = a */
- r = js_bigint_new(ctx, a->len);
- if (!r)
- return NULL;
- memcpy(r->tab, a->tab, a->len * sizeof(a->tab[0]));
- return r;
- } else {
- /* q = 0 */
- return js_bigint_new_si(ctx, 0);
- }
- }
- /* normalize 'b' */
- shift = js_limb_clz(tabb[nb - 1]);
- if (shift != 0) {
- mp_shl(tabb, tabb, nb, shift);
- h = mp_shl(r->tab, r->tab, na, shift);
- if (h != 0)
- r->tab[na++] = h;
- }
- q = js_bigint_new(ctx, na - nb + 2); /* one more limb for the sign */
- if (!q) {
- js_free(ctx, r);
- js_free(ctx, tabb);
- return NULL;
- }
- // js_bigint_dump1(ctx, "a", r->tab, na);
- // js_bigint_dump1(ctx, "b", tabb, nb);
- mp_divnorm(q->tab, r->tab, na, tabb, nb);
- js_free(ctx, tabb);
- if (is_rem) {
- js_free(ctx, q);
- if (shift != 0)
- mp_shr(r->tab, r->tab, nb, shift, 0);
- r->tab[nb++] = 0;
- if (a_sign)
- mp_neg(r->tab, r->tab, nb);
- r = js_bigint_normalize1(ctx, r, nb);
- return r;
- } else {
- js_free(ctx, r);
- q->tab[na - nb + 1] = 0;
- if (a_sign ^ b_sign) {
- mp_neg(q->tab, q->tab, q->len);
- }
- q = js_bigint_normalize(ctx, q);
- return q;
- }
- }
- /* and, or, xor */
- static JSBigInt *js_bigint_logic(JSContext *ctx, const JSBigInt *a,
- const JSBigInt *b, OPCodeEnum op)
- {
- JSBigInt *r;
- js_limb_t b_sign;
- int a_len, b_len, i;
- if (a->len < b->len) {
- const JSBigInt *tmp;
- tmp = a;
- a = b;
- b = tmp;
- }
- /* a_len >= b_len */
- a_len = a->len;
- b_len = b->len;
- b_sign = -js_bigint_sign(b);
- r = js_bigint_new(ctx, a_len);
- if (!r)
- return NULL;
- switch(op) {
- case OP_or:
- for(i = 0; i < b_len; i++) {
- r->tab[i] = a->tab[i] | b->tab[i];
- }
- for(i = b_len; i < a_len; i++) {
- r->tab[i] = a->tab[i] | b_sign;
- }
- break;
- case OP_and:
- for(i = 0; i < b_len; i++) {
- r->tab[i] = a->tab[i] & b->tab[i];
- }
- for(i = b_len; i < a_len; i++) {
- r->tab[i] = a->tab[i] & b_sign;
- }
- break;
- case OP_xor:
- for(i = 0; i < b_len; i++) {
- r->tab[i] = a->tab[i] ^ b->tab[i];
- }
- for(i = b_len; i < a_len; i++) {
- r->tab[i] = a->tab[i] ^ b_sign;
- }
- break;
- default:
- abort();
- }
- return js_bigint_normalize(ctx, r);
- }
- static JSBigInt *js_bigint_not(JSContext *ctx, const JSBigInt *a)
- {
- JSBigInt *r;
- int i;
-
- r = js_bigint_new(ctx, a->len);
- if (!r)
- return NULL;
- for(i = 0; i < a->len; i++) {
- r->tab[i] = ~a->tab[i];
- }
- /* no normalization is needed */
- return r;
- }
- static JSBigInt *js_bigint_shl(JSContext *ctx, const JSBigInt *a,
- unsigned int shift1)
- {
- int d, i, shift;
- JSBigInt *r;
- js_limb_t l;
- if (a->len == 1 && a->tab[0] == 0)
- return js_bigint_new_si(ctx, 0); /* zero case */
- d = shift1 / JS_LIMB_BITS;
- shift = shift1 % JS_LIMB_BITS;
- r = js_bigint_new(ctx, a->len + d);
- if (!r)
- return NULL;
- for(i = 0; i < d; i++)
- r->tab[i] = 0;
- if (shift == 0) {
- for(i = 0; i < a->len; i++) {
- r->tab[i + d] = a->tab[i];
- }
- } else {
- l = mp_shl(r->tab + d, a->tab, a->len, shift);
- if (js_bigint_sign(a))
- l |= (js_limb_t)(-1) << shift;
- r = js_bigint_extend(ctx, r, l);
- }
- return r;
- }
- static JSBigInt *js_bigint_shr(JSContext *ctx, const JSBigInt *a,
- unsigned int shift1)
- {
- int d, i, shift, a_sign, n1;
- JSBigInt *r;
- d = shift1 / JS_LIMB_BITS;
- shift = shift1 % JS_LIMB_BITS;
- a_sign = js_bigint_sign(a);
- if (d >= a->len)
- return js_bigint_new_si(ctx, -a_sign);
- n1 = a->len - d;
- r = js_bigint_new(ctx, n1);
- if (!r)
- return NULL;
- if (shift == 0) {
- for(i = 0; i < n1; i++) {
- r->tab[i] = a->tab[i + d];
- }
- /* no normalization is needed */
- } else {
- mp_shr(r->tab, a->tab + d, n1, shift, -a_sign);
- r = js_bigint_normalize(ctx, r);
- }
- return r;
- }
- static JSBigInt *js_bigint_pow(JSContext *ctx, const JSBigInt *a, JSBigInt *b)
- {
- uint32_t e;
- int n_bits, i;
- JSBigInt *r, *r1;
-
- /* b must be >= 0 */
- if (js_bigint_sign(b)) {
- JS_ThrowRangeError(ctx, "BigInt negative exponent");
- return NULL;
- }
- if (b->len == 1 && b->tab[0] == 0) {
- /* a^0 = 1 */
- return js_bigint_new_si(ctx, 1);
- } else if (a->len == 1) {
- js_limb_t v;
- BOOL is_neg;
- v = a->tab[0];
- if (v <= 1)
- return js_bigint_new_si(ctx, v);
- else if (v == -1)
- return js_bigint_new_si(ctx, 1 - 2 * (b->tab[0] & 1));
- is_neg = (js_slimb_t)v < 0;
- if (is_neg)
- v = -v;
- if ((v & (v - 1)) == 0) {
- uint64_t e1;
- int n;
- /* v = 2^n */
- n = JS_LIMB_BITS - 1 - js_limb_clz(v);
- if (b->len > 1)
- goto overflow;
- if (b->tab[0] > INT32_MAX)
- goto overflow;
- e = b->tab[0];
- e1 = (uint64_t)e * n;
- if (e1 > JS_BIGINT_MAX_SIZE * JS_LIMB_BITS)
- goto overflow;
- e = e1;
- if (is_neg)
- is_neg = b->tab[0] & 1;
- r = js_bigint_new(ctx,
- (e + JS_LIMB_BITS + 1 - is_neg) / JS_LIMB_BITS);
- if (!r)
- return NULL;
- memset(r->tab, 0, sizeof(r->tab[0]) * r->len);
- r->tab[e / JS_LIMB_BITS] =
- (js_limb_t)(1 - 2 * is_neg) << (e % JS_LIMB_BITS);
- return r;
- }
- }
- if (b->len > 1)
- goto overflow;
- if (b->tab[0] > INT32_MAX)
- goto overflow;
- e = b->tab[0];
- n_bits = 32 - clz32(e);
- r = js_bigint_new(ctx, a->len);
- if (!r)
- return NULL;
- memcpy(r->tab, a->tab, a->len * sizeof(a->tab[0]));
- for(i = n_bits - 2; i >= 0; i--) {
- r1 = js_bigint_mul(ctx, r, r);
- if (!r1)
- return NULL;
- js_free(ctx, r);
- r = r1;
- if ((e >> i) & 1) {
- r1 = js_bigint_mul(ctx, r, a);
- if (!r1)
- return NULL;
- js_free(ctx, r);
- r = r1;
- }
- }
- return r;
- overflow:
- JS_ThrowRangeError(ctx, "BigInt is too large");
- return NULL;
- }
- /* return (mant, exp) so that abs(a) ~ mant*2^(exp - (limb_bits -
- 1). a must be != 0. */
- static uint64_t js_bigint_get_mant_exp(JSContext *ctx,
- int *pexp, const JSBigInt *a)
- {
- js_limb_t t[4 - JS_LIMB_BITS / 32], carry, v, low_bits;
- int n1, n2, sgn, shift, i, j, e;
- uint64_t a1, a0;
- n2 = 4 - JS_LIMB_BITS / 32;
- n1 = a->len - n2;
- sgn = js_bigint_sign(a);
- /* low_bits != 0 if there are a non zero low bit in abs(a) */
- low_bits = 0;
- carry = sgn;
- for(i = 0; i < n1; i++) {
- v = (a->tab[i] ^ (-sgn)) + carry;
- carry = v < carry;
- low_bits |= v;
- }
- /* get the n2 high limbs of abs(a) */
- for(j = 0; j < n2; j++) {
- i = j + n1;
- if (i < 0) {
- v = 0;
- } else {
- v = (a->tab[i] ^ (-sgn)) + carry;
- carry = v < carry;
- }
- t[j] = v;
- }
-
- #if JS_LIMB_BITS == 32
- a1 = ((uint64_t)t[2] << 32) | t[1];
- a0 = (uint64_t)t[0] << 32;
- #else
- a1 = t[1];
- a0 = t[0];
- #endif
- a0 |= (low_bits != 0);
- /* normalize */
- if (a1 == 0) {
- /* JS_LIMB_BITS = 64 bit only */
- shift = 64;
- a1 = a0;
- a0 = 0;
- } else {
- shift = clz64(a1);
- if (shift != 0) {
- a1 = (a1 << shift) | (a0 >> (64 - shift));
- a0 <<= shift;
- }
- }
- a1 |= (a0 != 0); /* keep the bits for the final rounding */
- /* compute the exponent */
- e = a->len * JS_LIMB_BITS - shift - 1;
- *pexp = e;
- return a1;
- }
- /* shift left with round to nearest, ties to even. n >= 1 */
- static uint64_t shr_rndn(uint64_t a, int n)
- {
- uint64_t addend = ((a >> n) & 1) + ((1 << (n - 1)) - 1);
- return (a + addend) >> n;
- }
- /* convert to float64 with round to nearest, ties to even. Return
- +/-infinity if too large. */
- static double js_bigint_to_float64(JSContext *ctx, const JSBigInt *a)
- {
- int sgn, e;
- uint64_t mant;
- if (a->len == 1) {
- /* fast case, including zero */
- return (double)(js_slimb_t)a->tab[0];
- }
- sgn = js_bigint_sign(a);
- mant = js_bigint_get_mant_exp(ctx, &e, a);
- if (e > 1023) {
- /* overflow: return infinity */
- mant = 0;
- e = 1024;
- } else {
- mant = (mant >> 1) | (mant & 1); /* avoid overflow in rounding */
- mant = shr_rndn(mant, 10);
- /* rounding can cause an overflow */
- if (mant >= ((uint64_t)1 << 53)) {
- mant >>= 1;
- e++;
- }
- mant &= (((uint64_t)1 << 52) - 1);
- }
- return uint64_as_float64(((uint64_t)sgn << 63) |
- ((uint64_t)(e + 1023) << 52) |
- mant);
- }
- /* return (1, NULL) if not an integer, (2, NULL) if NaN or Infinity,
- (0, n) if an integer, (0, NULL) in case of memory error */
- static JSBigInt *js_bigint_from_float64(JSContext *ctx, int *pres, double a1)
- {
- uint64_t a = float64_as_uint64(a1);
- int sgn, e, shift;
- uint64_t mant;
- JSBigIntBuf buf;
- JSBigInt *r;
-
- sgn = a >> 63;
- e = (a >> 52) & ((1 << 11) - 1);
- mant = a & (((uint64_t)1 << 52) - 1);
- if (e == 2047) {
- /* NaN, Infinity */
- *pres = 2;
- return NULL;
- }
- if (e == 0 && mant == 0) {
- /* zero */
- *pres = 0;
- return js_bigint_new_si(ctx, 0);
- }
- e -= 1023;
- /* 0 < a < 1 : not an integer */
- if (e < 0)
- goto not_an_integer;
- mant |= (uint64_t)1 << 52;
- if (e < 52) {
- shift = 52 - e;
- /* check that there is no fractional part */
- if (mant & (((uint64_t)1 << shift) - 1)) {
- not_an_integer:
- *pres = 1;
- return NULL;
- }
- mant >>= shift;
- e = 0;
- } else {
- e -= 52;
- }
- if (sgn)
- mant = -mant;
- /* the integer is mant*2^e */
- r = js_bigint_set_si64(&buf, (int64_t)mant);
- *pres = 0;
- return js_bigint_shl(ctx, r, e);
- }
- /* return -1, 0, 1 or (2) (unordered) */
- static int js_bigint_float64_cmp(JSContext *ctx, const JSBigInt *a,
- double b)
- {
- int b_sign, a_sign, e, f;
- uint64_t mant, b1, a_mant;
-
- b1 = float64_as_uint64(b);
- b_sign = b1 >> 63;
- e = (b1 >> 52) & ((1 << 11) - 1);
- mant = b1 & (((uint64_t)1 << 52) - 1);
- a_sign = js_bigint_sign(a);
- if (e == 2047) {
- if (mant != 0) {
- /* NaN */
- return 2;
- } else {
- /* +/- infinity */
- return 2 * b_sign - 1;
- }
- } else if (e == 0 && mant == 0) {
- /* b = +/-0 */
- if (a->len == 1 && a->tab[0] == 0)
- return 0;
- else
- return 1 - 2 * a_sign;
- } else if (a->len == 1 && a->tab[0] == 0) {
- /* a = 0, b != 0 */
- return 2 * b_sign - 1;
- } else if (a_sign != b_sign) {
- return 1 - 2 * a_sign;
- } else {
- e -= 1023;
- /* Note: handling denormals is not necessary because we
- compare to integers hence f >= 0 */
- /* compute f so that 2^f <= abs(a) < 2^(f+1) */
- a_mant = js_bigint_get_mant_exp(ctx, &f, a);
- if (f != e) {
- if (f < e)
- return -1;
- else
- return 1;
- } else {
- mant = (mant | ((uint64_t)1 << 52)) << 11; /* align to a_mant */
- if (a_mant < mant)
- return 2 * a_sign - 1;
- else if (a_mant > mant)
- return 1 - 2 * a_sign;
- else
- return 0;
- }
- }
- }
- /* return -1, 0 or 1 */
- static int js_bigint_cmp(JSContext *ctx, const JSBigInt *a,
- const JSBigInt *b)
- {
- int a_sign, b_sign, res, i;
- a_sign = js_bigint_sign(a);
- b_sign = js_bigint_sign(b);
- if (a_sign != b_sign) {
- res = 1 - 2 * a_sign;
- } else {
- /* we assume the numbers are normalized */
- if (a->len != b->len) {
- if (a->len < b->len)
- res = 2 * a_sign - 1;
- else
- res = 1 - 2 * a_sign;
- } else {
- res = 0;
- for(i = a->len -1; i >= 0; i--) {
- if (a->tab[i] != b->tab[i]) {
- if (a->tab[i] < b->tab[i])
- res = -1;
- else
- res = 1;
- break;
- }
- }
- }
- }
- return res;
- }
- /* contains 10^i */
- static const js_limb_t js_pow_dec[JS_LIMB_DIGITS + 1] = {
- 1U,
- 10U,
- 100U,
- 1000U,
- 10000U,
- 100000U,
- 1000000U,
- 10000000U,
- 100000000U,
- 1000000000U,
- #if JS_LIMB_BITS == 64
- 10000000000U,
- 100000000000U,
- 1000000000000U,
- 10000000000000U,
- 100000000000000U,
- 1000000000000000U,
- 10000000000000000U,
- 100000000000000000U,
- 1000000000000000000U,
- 10000000000000000000U,
- #endif
- };
- /* syntax: [-]digits in base radix. Return NULL if memory error. radix
- = 10, 2, 8 or 16. */
- static JSBigInt *js_bigint_from_string(JSContext *ctx,
- const char *str, int radix)
- {
- const char *p = str;
- size_t n_digits1;
- int is_neg, n_digits, n_limbs, len, log2_radix, n_bits, i;
- JSBigInt *r;
- js_limb_t v, c, h;
-
- is_neg = 0;
- if (*p == '-') {
- is_neg = 1;
- p++;
- }
- while (*p == '0')
- p++;
- n_digits1 = strlen(p);
- /* the real check for overflox is done js_bigint_new(). Here
- we just avoid integer overflow */
- if (n_digits1 > JS_BIGINT_MAX_SIZE * JS_LIMB_BITS) {
- JS_ThrowRangeError(ctx, "BigInt is too large to allocate");
- return NULL;
- }
- n_digits = n_digits1;
- log2_radix = 32 - clz32(radix - 1); /* ceil(log2(radix)) */
- /* compute the maximum number of limbs */
- if (radix == 10) {
- n_bits = (n_digits * 27 + 7) / 8; /* >= ceil(n_digits * log2(10)) */
- } else {
- n_bits = n_digits * log2_radix;
- }
- /* we add one extra bit for the sign */
- n_limbs = max_int(1, n_bits / JS_LIMB_BITS + 1);
- r = js_bigint_new(ctx, n_limbs);
- if (!r)
- return NULL;
- if (radix == 10) {
- int digits_per_limb = JS_LIMB_DIGITS;
- len = 1;
- r->tab[0] = 0;
- for(;;) {
- /* XXX: slow */
- v = 0;
- for(i = 0; i < digits_per_limb; i++) {
- c = to_digit(*p);
- if (c >= radix)
- break;
- p++;
- v = v * 10 + c;
- }
- if (i == 0)
- break;
- if (len == 1 && r->tab[0] == 0) {
- r->tab[0] = v;
- } else {
- h = mp_mul1(r->tab, r->tab, len, js_pow_dec[i], v);
- if (h != 0) {
- r->tab[len++] = h;
- }
- }
- }
- /* add one extra limb to have the correct sign*/
- if ((r->tab[len - 1] >> (JS_LIMB_BITS - 1)) != 0)
- r->tab[len++] = 0;
- r->len = len;
- } else {
- unsigned int bit_pos, shift, pos;
-
- /* power of two base: no multiplication is needed */
- r->len = n_limbs;
- memset(r->tab, 0, sizeof(r->tab[0]) * n_limbs);
- for(i = 0; i < n_digits; i++) {
- c = to_digit(p[n_digits - 1 - i]);
- assert(c < radix);
- bit_pos = i * log2_radix;
- shift = bit_pos & (JS_LIMB_BITS - 1);
- pos = bit_pos / JS_LIMB_BITS;
- r->tab[pos] |= c << shift;
- /* if log2_radix does not divide JS_LIMB_BITS, needed an
- additional op */
- if (shift + log2_radix > JS_LIMB_BITS) {
- r->tab[pos + 1] |= c >> (JS_LIMB_BITS - shift);
- }
- }
- }
- r = js_bigint_normalize(ctx, r);
- /* XXX: could do it in place */
- if (is_neg) {
- JSBigInt *r1;
- r1 = js_bigint_neg(ctx, r);
- js_free(ctx, r);
- r = r1;
- }
- return r;
- }
- /* 2 <= base <= 36 */
- static char const digits[36] = {
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b',
- 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
- 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
- };
- /* special version going backwards */
- /* XXX: use dtoa.c */
- static char *js_u64toa(char *q, int64_t n, unsigned int base)
- {
- int digit;
- if (base == 10) {
- /* division by known base uses multiplication */
- do {
- digit = (uint64_t)n % 10;
- n = (uint64_t)n / 10;
- *--q = '0' + digit;
- } while (n != 0);
- } else {
- do {
- digit = (uint64_t)n % base;
- n = (uint64_t)n / base;
- *--q = digits[digit];
- } while (n != 0);
- }
- return q;
- }
- /* len >= 1. 2 <= radix <= 36 */
- static char *limb_to_a(char *q, js_limb_t n, unsigned int radix, int len)
- {
- int digit, i;
- if (radix == 10) {
- /* specific case with constant divisor */
- /* XXX: optimize */
- for(i = 0; i < len; i++) {
- digit = (js_limb_t)n % 10;
- n = (js_limb_t)n / 10;
- *--q = digit + '0';
- }
- } else {
- for(i = 0; i < len; i++) {
- digit = (js_limb_t)n % radix;
- n = (js_limb_t)n / radix;
- *--q = digits[digit];
- }
- }
- return q;
- }
- #define JS_RADIX_MAX 36
- static const uint8_t digits_per_limb_table[JS_RADIX_MAX - 1] = {
- #if JS_LIMB_BITS == 32
- 32,20,16,13,12,11,10,10, 9, 9, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- #else
- 64,40,32,27,24,22,21,20,19,18,17,17,16,16,16,15,15,15,14,14,14,14,13,13,13,13,13,13,13,12,12,12,12,12,12,
- #endif
- };
- static const js_limb_t radix_base_table[JS_RADIX_MAX - 1] = {
- #if JS_LIMB_BITS == 32
- 0x00000000, 0xcfd41b91, 0x00000000, 0x48c27395,
- 0x81bf1000, 0x75db9c97, 0x40000000, 0xcfd41b91,
- 0x3b9aca00, 0x8c8b6d2b, 0x19a10000, 0x309f1021,
- 0x57f6c100, 0x98c29b81, 0x00000000, 0x18754571,
- 0x247dbc80, 0x3547667b, 0x4c4b4000, 0x6b5a6e1d,
- 0x94ace180, 0xcaf18367, 0x0b640000, 0x0e8d4a51,
- 0x1269ae40, 0x17179149, 0x1cb91000, 0x23744899,
- 0x2b73a840, 0x34e63b41, 0x40000000, 0x4cfa3cc1,
- 0x5c13d840, 0x6d91b519, 0x81bf1000,
- #else
- 0x0000000000000000, 0xa8b8b452291fe821, 0x0000000000000000, 0x6765c793fa10079d,
- 0x41c21cb8e1000000, 0x3642798750226111, 0x8000000000000000, 0xa8b8b452291fe821,
- 0x8ac7230489e80000, 0x4d28cb56c33fa539, 0x1eca170c00000000, 0x780c7372621bd74d,
- 0x1e39a5057d810000, 0x5b27ac993df97701, 0x0000000000000000, 0x27b95e997e21d9f1,
- 0x5da0e1e53c5c8000, 0xd2ae3299c1c4aedb, 0x16bcc41e90000000, 0x2d04b7fdd9c0ef49,
- 0x5658597bcaa24000, 0xa0e2073737609371, 0x0c29e98000000000, 0x14adf4b7320334b9,
- 0x226ed36478bfa000, 0x383d9170b85ff80b, 0x5a3c23e39c000000, 0x8e65137388122bcd,
- 0xdd41bb36d259e000, 0x0aee5720ee830681, 0x1000000000000000, 0x172588ad4f5f0981,
- 0x211e44f7d02c1000, 0x2ee56725f06e5c71, 0x41c21cb8e1000000,
- #endif
- };
- static JSValue js_bigint_to_string1(JSContext *ctx, JSValueConst val, int radix)
- {
- if (JS_VALUE_GET_TAG(val) == JS_TAG_SHORT_BIG_INT) {
- char buf[66];
- int len;
- len = i64toa_radix(buf, JS_VALUE_GET_SHORT_BIG_INT(val), radix);
- return js_new_string8_len(ctx, buf, len);
- } else {
- JSBigInt *r, *tmp = NULL;
- char *buf, *q, *buf_end;
- int is_neg, n_bits, log2_radix, n_digits;
- BOOL is_binary_radix;
- JSValue res;
-
- assert(JS_VALUE_GET_TAG(val) == JS_TAG_BIG_INT);
- r = JS_VALUE_GET_PTR(val);
- if (r->len == 1 && r->tab[0] == 0) {
- /* '0' case */
- return js_new_string8_len(ctx, "0", 1);
- }
- is_binary_radix = ((radix & (radix - 1)) == 0);
- is_neg = js_bigint_sign(r);
- if (is_neg) {
- tmp = js_bigint_neg(ctx, r);
- if (!tmp)
- return JS_EXCEPTION;
- r = tmp;
- } else if (!is_binary_radix) {
- /* need to modify 'r' */
- tmp = js_bigint_new(ctx, r->len);
- if (!tmp)
- return JS_EXCEPTION;
- memcpy(tmp->tab, r->tab, r->len * sizeof(r->tab[0]));
- r = tmp;
- }
- log2_radix = 31 - clz32(radix); /* floor(log2(radix)) */
- n_bits = r->len * JS_LIMB_BITS - js_limb_safe_clz(r->tab[r->len - 1]);
- /* n_digits is exact only if radix is a power of
- two. Otherwise it is >= the exact number of digits */
- n_digits = (n_bits + log2_radix - 1) / log2_radix;
- /* XXX: could directly build the JSString */
- buf = js_malloc(ctx, n_digits + is_neg + 1);
- if (!buf) {
- js_free(ctx, tmp);
- return JS_EXCEPTION;
- }
- q = buf + n_digits + is_neg + 1;
- *--q = '\0';
- buf_end = q;
- if (!is_binary_radix) {
- int len;
- js_limb_t radix_base, v;
- radix_base = radix_base_table[radix - 2];
- len = r->len;
- for(;;) {
- /* remove leading zero limbs */
- while (len > 1 && r->tab[len - 1] == 0)
- len--;
- if (len == 1 && r->tab[0] < radix_base) {
- v = r->tab[0];
- if (v != 0) {
- q = js_u64toa(q, v, radix);
- }
- break;
- } else {
- v = mp_div1(r->tab, r->tab, len, radix_base, 0);
- q = limb_to_a(q, v, radix, digits_per_limb_table[radix - 2]);
- }
- }
- } else {
- int i, shift;
- unsigned int bit_pos, pos, c;
- /* radix is a power of two */
- for(i = 0; i < n_digits; i++) {
- bit_pos = i * log2_radix;
- pos = bit_pos / JS_LIMB_BITS;
- shift = bit_pos % JS_LIMB_BITS;
- c = r->tab[pos] >> shift;
- if ((shift + log2_radix) > JS_LIMB_BITS &&
- (pos + 1) < r->len) {
- c |= r->tab[pos + 1] << (JS_LIMB_BITS - shift);
- }
- c &= (radix - 1);
- *--q = digits[c];
- }
- }
- if (is_neg)
- *--q = '-';
- js_free(ctx, tmp);
- res = js_new_string8_len(ctx, q, buf_end - q);
- js_free(ctx, buf);
- return res;
- }
- }
- /* if possible transform a BigInt to short big and free it, otherwise
- return a normal bigint */
- static JSValue JS_CompactBigInt(JSContext *ctx, JSBigInt *p)
- {
- JSValue res;
- if (p->len == 1) {
- res = __JS_NewShortBigInt(ctx, (js_slimb_t)p->tab[0]);
- js_free(ctx, p);
- return res;
- } else {
- return JS_MKPTR(JS_TAG_BIG_INT, p);
- }
- }
- #define ATOD_INT_ONLY (1 << 0)
- /* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */
- #define ATOD_ACCEPT_BIN_OCT (1 << 2)
- /* accept O prefix as octal if radix == 0 and properly formed (Annex B) */
- #define ATOD_ACCEPT_LEGACY_OCTAL (1 << 4)
- /* accept _ between digits as a digit separator */
- #define ATOD_ACCEPT_UNDERSCORES (1 << 5)
- /* allow a suffix to override the type */
- #define ATOD_ACCEPT_SUFFIX (1 << 6)
- /* default type */
- #define ATOD_TYPE_MASK (3 << 7)
- #define ATOD_TYPE_FLOAT64 (0 << 7)
- #define ATOD_TYPE_BIG_INT (1 << 7)
- /* accept -0x1 */
- #define ATOD_ACCEPT_PREFIX_AFTER_SIGN (1 << 10)
- /* return an exception in case of memory error. Return JS_NAN if
- invalid syntax */
- /* XXX: directly use js_atod() */
- static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,
- int radix, int flags)
- {
- const char *p, *p_start;
- int sep, is_neg;
- BOOL is_float, has_legacy_octal;
- int atod_type = flags & ATOD_TYPE_MASK;
- char buf1[64], *buf;
- int i, j, len;
- BOOL buf_allocated = FALSE;
- JSValue val;
- JSATODTempMem atod_mem;
-
- /* optional separator between digits */
- sep = (flags & ATOD_ACCEPT_UNDERSCORES) ? '_' : 256;
- has_legacy_octal = FALSE;
- p = str;
- p_start = p;
- is_neg = 0;
- if (p[0] == '+') {
- p++;
- p_start++;
- if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN))
- goto no_radix_prefix;
- } else if (p[0] == '-') {
- p++;
- p_start++;
- is_neg = 1;
- if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN))
- goto no_radix_prefix;
- }
- if (p[0] == '0') {
- if ((p[1] == 'x' || p[1] == 'X') &&
- (radix == 0 || radix == 16)) {
- p += 2;
- radix = 16;
- } else if ((p[1] == 'o' || p[1] == 'O') &&
- radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) {
- p += 2;
- radix = 8;
- } else if ((p[1] == 'b' || p[1] == 'B') &&
- radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) {
- p += 2;
- radix = 2;
- } else if ((p[1] >= '0' && p[1] <= '9') &&
- radix == 0 && (flags & ATOD_ACCEPT_LEGACY_OCTAL)) {
- int i;
- has_legacy_octal = TRUE;
- sep = 256;
- for (i = 1; (p[i] >= '0' && p[i] <= '7'); i++)
- continue;
- if (p[i] == '8' || p[i] == '9')
- goto no_prefix;
- p += 1;
- radix = 8;
- } else {
- goto no_prefix;
- }
- /* there must be a digit after the prefix */
- if (to_digit((uint8_t)*p) >= radix)
- goto fail;
- no_prefix: ;
- } else {
- no_radix_prefix:
- if (!(flags & ATOD_INT_ONLY) &&
- (atod_type == ATOD_TYPE_FLOAT64) &&
- strstart(p, "Infinity", &p)) {
- double d = 1.0 / 0.0;
- if (is_neg)
- d = -d;
- val = JS_NewFloat64(ctx, d);
- goto done;
- }
- }
- if (radix == 0)
- radix = 10;
- is_float = FALSE;
- p_start = p;
- while (to_digit((uint8_t)*p) < radix
- || (*p == sep && (radix != 10 ||
- p != p_start + 1 || p[-1] != '0') &&
- to_digit((uint8_t)p[1]) < radix)) {
- p++;
- }
- if (!(flags & ATOD_INT_ONLY) && radix == 10) {
- if (*p == '.' && (p > p_start || to_digit((uint8_t)p[1]) < radix)) {
- is_float = TRUE;
- p++;
- if (*p == sep)
- goto fail;
- while (to_digit((uint8_t)*p) < radix ||
- (*p == sep && to_digit((uint8_t)p[1]) < radix))
- p++;
- }
- if (p > p_start && (*p == 'e' || *p == 'E')) {
- const char *p1 = p + 1;
- is_float = TRUE;
- if (*p1 == '+') {
- p1++;
- } else if (*p1 == '-') {
- p1++;
- }
- if (is_digit((uint8_t)*p1)) {
- p = p1 + 1;
- while (is_digit((uint8_t)*p) || (*p == sep && is_digit((uint8_t)p[1])))
- p++;
- }
- }
- }
- if (p == p_start)
- goto fail;
- buf = buf1;
- buf_allocated = FALSE;
- len = p - p_start;
- if (unlikely((len + 2) > sizeof(buf1))) {
- buf = js_malloc_rt(ctx->rt, len + 2); /* no exception raised */
- if (!buf)
- goto mem_error;
- buf_allocated = TRUE;
- }
- /* remove the separators and the radix prefixes */
- j = 0;
- if (is_neg)
- buf[j++] = '-';
- for (i = 0; i < len; i++) {
- if (p_start[i] != '_')
- buf[j++] = p_start[i];
- }
- buf[j] = '\0';
- if ((flags & ATOD_ACCEPT_SUFFIX) && *p == 'n') {
- p++;
- atod_type = ATOD_TYPE_BIG_INT;
- }
- switch(atod_type) {
- case ATOD_TYPE_FLOAT64:
- {
- double d;
- d = js_atod(buf, NULL, radix, is_float ? 0 : JS_ATOD_INT_ONLY,
- &atod_mem);
- /* return int or float64 */
- val = JS_NewFloat64(ctx, d);
- }
- break;
- case ATOD_TYPE_BIG_INT:
- {
- JSBigInt *r;
- if (has_legacy_octal || is_float)
- goto fail;
- r = js_bigint_from_string(ctx, buf, radix);
- if (!r) {
- val = JS_EXCEPTION;
- goto done;
- }
- val = JS_CompactBigInt(ctx, r);
- }
- break;
- default:
- abort();
- }
- done:
- if (buf_allocated)
- js_free_rt(ctx->rt, buf);
- if (pp)
- *pp = p;
- return val;
- fail:
- val = JS_NAN;
- goto done;
- mem_error:
- val = JS_ThrowOutOfMemory(ctx);
- goto done;
- }
- typedef enum JSToNumberHintEnum {
- TON_FLAG_NUMBER,
- TON_FLAG_NUMERIC,
- } JSToNumberHintEnum;
- static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val,
- JSToNumberHintEnum flag)
- {
- uint32_t tag;
- JSValue ret;
- redo:
- tag = JS_VALUE_GET_NORM_TAG(val);
- switch(tag) {
- case JS_TAG_BIG_INT:
- case JS_TAG_SHORT_BIG_INT:
- if (flag != TON_FLAG_NUMERIC) {
- JS_FreeValue(ctx, val);
- return JS_ThrowTypeError(ctx, "cannot convert bigint to number");
- }
- ret = val;
- break;
- case JS_TAG_FLOAT64:
- case JS_TAG_INT:
- case JS_TAG_EXCEPTION:
- ret = val;
- break;
- case JS_TAG_BOOL:
- case JS_TAG_NULL:
- ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));
- break;
- case JS_TAG_UNDEFINED:
- ret = JS_NAN;
- break;
- case JS_TAG_OBJECT:
- val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
- if (JS_IsException(val))
- return JS_EXCEPTION;
- goto redo;
- case JS_TAG_STRING:
- case JS_TAG_STRING_ROPE:
- {
- const char *str;
- const char *p;
- size_t len;
- str = JS_ToCStringLen(ctx, &len, val);
- JS_FreeValue(ctx, val);
- if (!str)
- return JS_EXCEPTION;
- p = str;
- p += skip_spaces(p);
- if ((p - str) == len) {
- ret = JS_NewInt32(ctx, 0);
- } else {
- int flags = ATOD_ACCEPT_BIN_OCT;
- ret = js_atof(ctx, p, &p, 0, flags);
- if (!JS_IsException(ret)) {
- p += skip_spaces(p);
- if ((p - str) != len) {
- JS_FreeValue(ctx, ret);
- ret = JS_NAN;
- }
- }
- }
- JS_FreeCString(ctx, str);
- }
- break;
- case JS_TAG_SYMBOL:
- JS_FreeValue(ctx, val);
- return JS_ThrowTypeError(ctx, "cannot convert symbol to number");
- default:
- JS_FreeValue(ctx, val);
- ret = JS_NAN;
- break;
- }
- return ret;
- }
- static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val)
- {
- return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMBER);
- }
- static JSValue JS_ToNumericFree(JSContext *ctx, JSValue val)
- {
- return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMERIC);
- }
- static JSValue JS_ToNumeric(JSContext *ctx, JSValueConst val)
- {
- return JS_ToNumericFree(ctx, JS_DupValue(ctx, val));
- }
- static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres,
- JSValue val)
- {
- double d;
- uint32_t tag;
-
- val = JS_ToNumberFree(ctx, val);
- if (JS_IsException(val))
- goto fail;
- tag = JS_VALUE_GET_NORM_TAG(val);
- switch(tag) {
- case JS_TAG_INT:
- d = JS_VALUE_GET_INT(val);
- break;
- case JS_TAG_FLOAT64:
- d = JS_VALUE_GET_FLOAT64(val);
- break;
- default:
- abort();
- }
- *pres = d;
- return 0;
- fail:
- *pres = JS_FLOAT64_NAN;
- return -1;
- }
- static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val)
- {
- uint32_t tag;
- tag = JS_VALUE_GET_TAG(val);
- if (tag <= JS_TAG_NULL) {
- *pres = JS_VALUE_GET_INT(val);
- return 0;
- } else if (JS_TAG_IS_FLOAT64(tag)) {
- *pres = JS_VALUE_GET_FLOAT64(val);
- return 0;
- } else {
- return __JS_ToFloat64Free(ctx, pres, val);
- }
- }
- int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val)
- {
- return JS_ToFloat64Free(ctx, pres, JS_DupValue(ctx, val));
- }
- static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val)
- {
- return JS_ToNumberFree(ctx, JS_DupValue(ctx, val));
- }
- /* same as JS_ToNumber() but return 0 in case of NaN/Undefined */
- static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val)
- {
- uint32_t tag;
- JSValue ret;
- redo:
- tag = JS_VALUE_GET_NORM_TAG(val);
- switch(tag) {
- case JS_TAG_INT:
- case JS_TAG_BOOL:
- case JS_TAG_NULL:
- case JS_TAG_UNDEFINED:
- ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));
- break;
- case JS_TAG_FLOAT64:
- {
- double d = JS_VALUE_GET_FLOAT64(val);
- if (isnan(d)) {
- ret = JS_NewInt32(ctx, 0);
- } else {
- /* convert -0 to +0 */
- d = trunc(d) + 0.0;
- ret = JS_NewFloat64(ctx, d);
- }
- }
- break;
- default:
- val = JS_ToNumberFree(ctx, val);
- if (JS_IsException(val))
- return val;
- goto redo;
- }
- return ret;
- }
- /* Note: the integer value is satured to 32 bits */
- static int JS_ToInt32SatFree(JSContext *ctx, int *pres, JSValue val)
- {
- uint32_t tag;
- int ret;
- redo:
- tag = JS_VALUE_GET_NORM_TAG(val);
- switch(tag) {
- case JS_TAG_INT:
- case JS_TAG_BOOL:
- case JS_TAG_NULL:
- case JS_TAG_UNDEFINED:
- ret = JS_VALUE_GET_INT(val);
- break;
- case JS_TAG_EXCEPTION:
- *pres = 0;
- return -1;
- case JS_TAG_FLOAT64:
- {
- double d = JS_VALUE_GET_FLOAT64(val);
- if (isnan(d)) {
- ret = 0;
- } else {
- if (d < INT32_MIN)
- ret = INT32_MIN;
- else if (d > INT32_MAX)
- ret = INT32_MAX;
- else
- ret = (int)d;
- }
- }
- break;
- default:
- val = JS_ToNumberFree(ctx, val);
- if (JS_IsException(val)) {
- *pres = 0;
- return -1;
- }
- goto redo;
- }
- *pres = ret;
- return 0;
- }
- int JS_ToInt32Sat(JSContext *ctx, int *pres, JSValueConst val)
- {
- return JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val));
- }
- int JS_ToInt32Clamp(JSContext *ctx, int *pres, JSValueConst val,
- int min, int max, int min_offset)
- {
- int res = JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val));
- if (res == 0) {
- if (*pres < min) {
- *pres += min_offset;
- if (*pres < min)
- *pres = min;
- } else {
- if (*pres > max)
- *pres = max;
- }
- }
- return res;
- }
- static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val)
- {
- uint32_t tag;
- redo:
- tag = JS_VALUE_GET_NORM_TAG(val);
- switch(tag) {
- case JS_TAG_INT:
- case JS_TAG_BOOL:
- case JS_TAG_NULL:
- case JS_TAG_UNDEFINED:
- *pres = JS_VALUE_GET_INT(val);
- return 0;
- case JS_TAG_EXCEPTION:
- *pres = 0;
- return -1;
- case JS_TAG_FLOAT64:
- {
- double d = JS_VALUE_GET_FLOAT64(val);
- if (isnan(d)) {
- *pres = 0;
- } else {
- if (d < INT64_MIN)
- *pres = INT64_MIN;
- else if (d >= 0x1p63) /* must use INT64_MAX + 1 because INT64_MAX cannot be exactly represented as a double */
- *pres = INT64_MAX;
- else
- *pres = (int64_t)d;
- }
- }
- return 0;
- default:
- val = JS_ToNumberFree(ctx, val);
- if (JS_IsException(val)) {
- *pres = 0;
- return -1;
- }
- goto redo;
- }
- }
- int JS_ToInt64Sat(JSContext *ctx, int64_t *pres, JSValueConst val)
- {
- return JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val));
- }
- int JS_ToInt64Clamp(JSContext *ctx, int64_t *pres, JSValueConst val,
- int64_t min, int64_t max, int64_t neg_offset)
- {
- int res = JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val));
- if (res == 0) {
- if (*pres < 0)
- *pres += neg_offset;
- if (*pres < min)
- *pres = min;
- else if (*pres > max)
- *pres = max;
- }
- return res;
- }
- /* Same as JS_ToInt32Free() but with a 64 bit result. Return (<0, 0)
- in case of exception */
- static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val)
- {
- uint32_t tag;
- int64_t ret;
- redo:
- tag = JS_VALUE_GET_NORM_TAG(val);
- switch(tag) {
- case JS_TAG_INT:
- case JS_TAG_BOOL:
- case JS_TAG_NULL:
- case JS_TAG_UNDEFINED:
- ret = JS_VALUE_GET_INT(val);
- break;
- case JS_TAG_FLOAT64:
- {
- JSFloat64Union u;
- double d;
- int e;
- d = JS_VALUE_GET_FLOAT64(val);
- u.d = d;
- /* we avoid doing fmod(x, 2^64) */
- e = (u.u64 >> 52) & 0x7ff;
- if (likely(e <= (1023 + 62))) {
- /* fast case */
- ret = (int64_t)d;
- } else if (e <= (1023 + 62 + 53)) {
- uint64_t v;
- /* remainder modulo 2^64 */
- v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);
- ret = v << ((e - 1023) - 52);
- /* take the sign into account */
- if (u.u64 >> 63)
- ret = -ret;
- } else {
- ret = 0; /* also handles NaN and +inf */
- }
- }
- break;
- default:
- val = JS_ToNumberFree(ctx, val);
- if (JS_IsException(val)) {
- *pres = 0;
- return -1;
- }
- goto redo;
- }
- *pres = ret;
- return 0;
- }
- int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val)
- {
- return JS_ToInt64Free(ctx, pres, JS_DupValue(ctx, val));
- }
- int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val)
- {
- if (JS_IsBigInt(ctx, val))
- return JS_ToBigInt64(ctx, pres, val);
- else
- return JS_ToInt64(ctx, pres, val);
- }
- /* return (<0, 0) in case of exception */
- static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val)
- {
- uint32_t tag;
- int32_t ret;
- redo:
- tag = JS_VALUE_GET_NORM_TAG(val);
- switch(tag) {
- case JS_TAG_INT:
- case JS_TAG_BOOL:
- case JS_TAG_NULL:
- case JS_TAG_UNDEFINED:
- ret = JS_VALUE_GET_INT(val);
- break;
- case JS_TAG_FLOAT64:
- {
- JSFloat64Union u;
- double d;
- int e;
- d = JS_VALUE_GET_FLOAT64(val);
- u.d = d;
- /* we avoid doing fmod(x, 2^32) */
- e = (u.u64 >> 52) & 0x7ff;
- if (likely(e <= (1023 + 30))) {
- /* fast case */
- ret = (int32_t)d;
- } else if (e <= (1023 + 30 + 53)) {
- uint64_t v;
- /* remainder modulo 2^32 */
- v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);
- v = v << ((e - 1023) - 52 + 32);
- ret = v >> 32;
- /* take the sign into account */
- if (u.u64 >> 63)
- ret = -ret;
- } else {
- ret = 0; /* also handles NaN and +inf */
- }
- }
- break;
- default:
- val = JS_ToNumberFree(ctx, val);
- if (JS_IsException(val)) {
- *pres = 0;
- return -1;
- }
- goto redo;
- }
- *pres = ret;
- return 0;
- }
- int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val)
- {
- return JS_ToInt32Free(ctx, pres, JS_DupValue(ctx, val));
- }
- static inline int JS_ToUint32Free(JSContext *ctx, uint32_t *pres, JSValue val)
- {
- return JS_ToInt32Free(ctx, (int32_t *)pres, val);
- }
- static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val)
- {
- uint32_t tag;
- int res;
- redo:
- tag = JS_VALUE_GET_NORM_TAG(val);
- switch(tag) {
- case JS_TAG_INT:
- case JS_TAG_BOOL:
- case JS_TAG_NULL:
- case JS_TAG_UNDEFINED:
- res = JS_VALUE_GET_INT(val);
- res = max_int(0, min_int(255, res));
- break;
- case JS_TAG_FLOAT64:
- {
- double d = JS_VALUE_GET_FLOAT64(val);
- if (isnan(d)) {
- res = 0;
- } else {
- if (d < 0)
- res = 0;
- else if (d > 255)
- res = 255;
- else
- res = lrint(d);
- }
- }
- break;
- default:
- val = JS_ToNumberFree(ctx, val);
- if (JS_IsException(val)) {
- *pres = 0;
- return -1;
- }
- goto redo;
- }
- *pres = res;
- return 0;
- }
- static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
- JSValue val, BOOL is_array_ctor)
- {
- uint32_t tag, len;
- tag = JS_VALUE_GET_TAG(val);
- switch(tag) {
- case JS_TAG_INT:
- case JS_TAG_BOOL:
- case JS_TAG_NULL:
- {
- int v;
- v = JS_VALUE_GET_INT(val);
- if (v < 0)
- goto fail;
- len = v;
- }
- break;
- default:
- if (JS_TAG_IS_FLOAT64(tag)) {
- double d;
- d = JS_VALUE_GET_FLOAT64(val);
- if (!(d >= 0 && d <= UINT32_MAX))
- goto fail;
- len = (uint32_t)d;
- if (len != d)
- goto fail;
- } else {
- uint32_t len1;
- if (is_array_ctor) {
- val = JS_ToNumberFree(ctx, val);
- if (JS_IsException(val))
- return -1;
- /* cannot recurse because val is a number */
- if (JS_ToArrayLengthFree(ctx, &len, val, TRUE))
- return -1;
- } else {
- /* legacy behavior: must do the conversion twice and compare */
- if (JS_ToUint32(ctx, &len, val)) {
- JS_FreeValue(ctx, val);
- return -1;
- }
- val = JS_ToNumberFree(ctx, val);
- if (JS_IsException(val))
- return -1;
- /* cannot recurse because val is a number */
- if (JS_ToArrayLengthFree(ctx, &len1, val, FALSE))
- return -1;
- if (len1 != len) {
- fail:
- JS_ThrowRangeError(ctx, "invalid array length");
- return -1;
- }
- }
- }
- break;
- }
- *plen = len;
- return 0;
- }
- #define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1)
- static BOOL is_safe_integer(double d)
- {
- return isfinite(d) && floor(d) == d &&
- fabs(d) <= (double)MAX_SAFE_INTEGER;
- }
- int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val)
- {
- int64_t v;
- if (JS_ToInt64Sat(ctx, &v, val))
- return -1;
- if (v < 0 || v > MAX_SAFE_INTEGER) {
- JS_ThrowRangeError(ctx, "invalid array index");
- *plen = 0;
- return -1;
- }
- *plen = v;
- return 0;
- }
- /* convert a value to a length between 0 and MAX_SAFE_INTEGER.
- return -1 for exception */
- static __exception int JS_ToLengthFree(JSContext *ctx, int64_t *plen,
- JSValue val)
- {
- int res = JS_ToInt64Clamp(ctx, plen, val, 0, MAX_SAFE_INTEGER, 0);
- JS_FreeValue(ctx, val);
- return res;
- }
- /* Note: can return an exception */
- static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val)
- {
- double d;
- if (!JS_IsNumber(val))
- return FALSE;
- if (unlikely(JS_ToFloat64(ctx, &d, val)))
- return -1;
- return isfinite(d) && floor(d) == d;
- }
- static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val)
- {
- uint32_t tag;
- tag = JS_VALUE_GET_NORM_TAG(val);
- switch(tag) {
- case JS_TAG_INT:
- {
- int v;
- v = JS_VALUE_GET_INT(val);
- return (v < 0);
- }
- case JS_TAG_FLOAT64:
- {
- JSFloat64Union u;
- u.d = JS_VALUE_GET_FLOAT64(val);
- return (u.u64 >> 63);
- }
- case JS_TAG_SHORT_BIG_INT:
- return (JS_VALUE_GET_SHORT_BIG_INT(val) < 0);
- case JS_TAG_BIG_INT:
- {
- JSBigInt *p = JS_VALUE_GET_PTR(val);
- return js_bigint_sign(p);
- }
- default:
- return FALSE;
- }
- }
- static JSValue js_bigint_to_string(JSContext *ctx, JSValueConst val)
- {
- return js_bigint_to_string1(ctx, val, 10);
- }
- static JSValue js_dtoa2(JSContext *ctx,
- double d, int radix, int n_digits, int flags)
- {
- char static_buf[128], *buf, *tmp_buf;
- int len, len_max;
- JSValue res;
- JSDTOATempMem dtoa_mem;
- len_max = js_dtoa_max_len(d, radix, n_digits, flags);
-
- /* longer buffer may be used if radix != 10 */
- if (len_max > sizeof(static_buf) - 1) {
- tmp_buf = js_malloc(ctx, len_max + 1);
- if (!tmp_buf)
- return JS_EXCEPTION;
- buf = tmp_buf;
- } else {
- tmp_buf = NULL;
- buf = static_buf;
- }
- len = js_dtoa(buf, d, radix, n_digits, flags, &dtoa_mem);
- res = js_new_string8_len(ctx, buf, len);
- js_free(ctx, tmp_buf);
- return res;
- }
- static JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, BOOL is_ToPropertyKey)
- {
- uint32_t tag;
- char buf[32];
- tag = JS_VALUE_GET_NORM_TAG(val);
- switch(tag) {
- case JS_TAG_STRING:
- return JS_DupValue(ctx, val);
- case JS_TAG_STRING_ROPE:
- return js_linearize_string_rope(ctx, JS_DupValue(ctx, val));
- case JS_TAG_INT:
- {
- size_t len;
- len = i32toa(buf, JS_VALUE_GET_INT(val));
- return js_new_string8_len(ctx, buf, len);
- }
- break;
- case JS_TAG_BOOL:
- return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ?
- JS_ATOM_true : JS_ATOM_false);
- case JS_TAG_NULL:
- return JS_AtomToString(ctx, JS_ATOM_null);
- case JS_TAG_UNDEFINED:
- return JS_AtomToString(ctx, JS_ATOM_undefined);
- case JS_TAG_EXCEPTION:
- return JS_EXCEPTION;
- case JS_TAG_OBJECT:
- {
- JSValue val1, ret;
- val1 = JS_ToPrimitive(ctx, val, HINT_STRING);
- if (JS_IsException(val1))
- return val1;
- ret = JS_ToStringInternal(ctx, val1, is_ToPropertyKey);
- JS_FreeValue(ctx, val1);
- return ret;
- }
- break;
- case JS_TAG_FUNCTION_BYTECODE:
- return js_new_string8(ctx, "[function bytecode]");
- case JS_TAG_SYMBOL:
- if (is_ToPropertyKey) {
- return JS_DupValue(ctx, val);
- } else {
- return JS_ThrowTypeError(ctx, "cannot convert symbol to string");
- }
- case JS_TAG_FLOAT64:
- return js_dtoa2(ctx, JS_VALUE_GET_FLOAT64(val), 10, 0,
- JS_DTOA_FORMAT_FREE);
- case JS_TAG_SHORT_BIG_INT:
- case JS_TAG_BIG_INT:
- return js_bigint_to_string(ctx, val);
- default:
- return js_new_string8(ctx, "[unsupported type]");
- }
- }
- JSValue JS_ToString(JSContext *ctx, JSValueConst val)
- {
- return JS_ToStringInternal(ctx, val, FALSE);
- }
- static JSValue JS_ToStringFree(JSContext *ctx, JSValue val)
- {
- JSValue ret;
- ret = JS_ToString(ctx, val);
- JS_FreeValue(ctx, val);
- return ret;
- }
- static JSValue JS_ToLocaleStringFree(JSContext *ctx, JSValue val)
- {
- if (JS_IsUndefined(val) || JS_IsNull(val))
- return JS_ToStringFree(ctx, val);
- return JS_InvokeFree(ctx, val, JS_ATOM_toLocaleString, 0, NULL);
- }
- JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val)
- {
- return JS_ToStringInternal(ctx, val, TRUE);
- }
- static JSValue JS_ToStringCheckObject(JSContext *ctx, JSValueConst val)
- {
- uint32_t tag = JS_VALUE_GET_TAG(val);
- if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED)
- return JS_ThrowTypeError(ctx, "null or undefined are forbidden");
- return JS_ToString(ctx, val);
- }
- #define JS_PRINT_MAX_DEPTH 8
- typedef struct {
- JSRuntime *rt;
- JSContext *ctx; /* may be NULL */
- JSPrintValueOptions options;
- JSPrintValueWrite *write_func;
- void *write_opaque;
- int level;
- JSObject *print_stack[JS_PRINT_MAX_DEPTH]; /* level values */
- } JSPrintValueState;
- static void js_print_value(JSPrintValueState *s, JSValueConst val);
- static void js_putc(JSPrintValueState *s, char c)
- {
- s->write_func(s->write_opaque, &c, 1);
- }
- static void js_puts(JSPrintValueState *s, const char *str)
- {
- s->write_func(s->write_opaque, str, strlen(str));
- }
- static void __attribute__((format(printf, 2, 3))) js_printf(JSPrintValueState *s, const char *fmt, ...)
- {
- va_list ap;
- char buf[256];
-
- va_start(ap, fmt);
- vsnprintf(buf, sizeof(buf), fmt, ap);
- va_end(ap);
- s->write_func(s->write_opaque, buf, strlen(buf));
- }
- static void js_print_float64(JSPrintValueState *s, double d)
- {
- JSDTOATempMem dtoa_mem;
- char buf[32];
- int len;
- len = js_dtoa(buf, d, 10, 0, JS_DTOA_FORMAT_FREE | JS_DTOA_MINUS_ZERO, &dtoa_mem);
- s->write_func(s->write_opaque, buf, len);
- }
- static uint32_t js_string_get_length(JSValueConst val)
- {
- if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) {
- JSString *p = JS_VALUE_GET_STRING(val);
- return p->len;
- } else if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING_ROPE) {
- JSStringRope *r = JS_VALUE_GET_PTR(val);
- return r->len;
- } else {
- return 0;
- }
- }
- /* pretty print the first 'len' characters of 'p' */
- static void js_print_string1(JSPrintValueState *s, JSString *p, int len, int sep)
- {
- uint8_t buf[UTF8_CHAR_LEN_MAX];
- int l, i, c, c1;
- for(i = 0; i < len; i++) {
- c = string_get(p, i);
- switch(c) {
- case '\t':
- c = 't';
- goto quote;
- case '\r':
- c = 'r';
- goto quote;
- case '\n':
- c = 'n';
- goto quote;
- case '\b':
- c = 'b';
- goto quote;
- case '\f':
- c = 'f';
- goto quote;
- case '\\':
- quote:
- js_putc(s, '\\');
- js_putc(s, c);
- break;
- default:
- if (c == sep)
- goto quote;
- if (c >= 32 && c <= 126) {
- js_putc(s, c);
- } else if (c < 32 ||
- (c >= 0x7f && c <= 0x9f)) {
- escape:
- js_printf(s, "\\u%04x", c);
- } else {
- if (is_hi_surrogate(c)) {
- if ((i + 1) >= len)
- goto escape;
- c1 = string_get(p, i + 1);
- if (!is_lo_surrogate(c1))
- goto escape;
- i++;
- c = from_surrogate(c, c1);
- } else if (is_lo_surrogate(c)) {
- goto escape;
- }
- l = unicode_to_utf8(buf, c);
- s->write_func(s->write_opaque, (char *)buf, l);
- }
- break;
- }
- }
- }
- static void js_print_string_rec(JSPrintValueState *s, JSValueConst val,
- int sep, uint32_t pos)
- {
- if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) {
- JSString *p = JS_VALUE_GET_STRING(val);
- uint32_t len;
- if (pos < s->options.max_string_length) {
- len = min_uint32(p->len, s->options.max_string_length - pos);
- js_print_string1(s, p, len, sep);
- }
- } else if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING_ROPE) {
- JSStringRope *r = JS_VALUE_GET_PTR(val);
- js_print_string_rec(s, r->left, sep, pos);
- js_print_string_rec(s, r->right, sep, pos + js_string_get_length(r->left));
- } else {
- js_printf(s, "<invalid string tag %d>", (int)JS_VALUE_GET_TAG(val));
- }
- }
- static void js_print_string(JSPrintValueState *s, JSValueConst val)
- {
- int sep;
- if (s->options.raw_dump && JS_VALUE_GET_TAG(val) == JS_TAG_STRING) {
- JSString *p = JS_VALUE_GET_STRING(val);
- js_printf(s, "%d", js_rc(p)->ref_count);
- sep = (js_rc(p)->ref_count == 1) ? '\"' : '\'';
- } else {
- sep = '\"';
- }
- js_putc(s, sep);
- js_print_string_rec(s, val, sep, 0);
- js_putc(s, sep);
- if (js_string_get_length(val) > s->options.max_string_length) {
- uint32_t n = js_string_get_length(val) - s->options.max_string_length;
- js_printf(s, "... %u more character%s", n, n > 1 ? "s" : "");
- }
- }
- static void js_print_raw_string(JSPrintValueState *s, JSValueConst val)
- {
- const char *cstr;
- size_t len;
- cstr = JS_ToCStringLen(s->ctx, &len, val);
- if (cstr) {
- s->write_func(s->write_opaque, cstr, len);
- JS_FreeCString(s->ctx, cstr);
- }
- }
- static BOOL is_ascii_ident(const JSString *p)
- {
- int i, c;
- if (p->len == 0)
- return FALSE;
- for(i = 0; i < p->len; i++) {
- c = string_get(p, i);
- if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
- (c == '_' || c == '$') || (c >= '0' && c <= '9' && i > 0)))
- return FALSE;
- }
- return TRUE;
- }
- static void js_print_atom(JSPrintValueState *s, JSAtom atom)
- {
- int i;
- if (__JS_AtomIsTaggedInt(atom)) {
- js_printf(s, "%u", __JS_AtomToUInt32(atom));
- } else if (atom == JS_ATOM_NULL) {
- js_puts(s, "<null>");
- } else {
- assert(atom < s->rt->atom_size);
- JSString *p;
- p = s->rt->atom_array[atom];
- if (is_ascii_ident(p)) {
- for(i = 0; i < p->len; i++) {
- js_putc(s, string_get(p, i));
- }
- } else {
- js_putc(s, '"');
- js_print_string1(s, p, p->len, '\"');
- js_putc(s, '"');
- }
- }
- }
- /* return 0 if invalid length */
- static uint32_t js_print_array_get_length(JSObject *p)
- {
- JSProperty *pr;
- JSShapeProperty *prs;
- JSValueConst val;
- prs = find_own_property(&pr, p, JS_ATOM_length);
- if (!prs)
- return 0;
- if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL)
- return 0;
- val = pr->u.value;
- switch(JS_VALUE_GET_NORM_TAG(val)) {
- case JS_TAG_INT:
- return JS_VALUE_GET_INT(val);
- case JS_TAG_FLOAT64:
- return (uint32_t)JS_VALUE_GET_FLOAT64(val);
- default:
- return 0;
- }
- }
- static void js_print_comma(JSPrintValueState *s, int *pcomma_state)
- {
- switch(*pcomma_state) {
- case 0:
- break;
- case 1:
- js_printf(s, ", ");
- break;
- case 2:
- js_printf(s, " { ");
- break;
- }
- *pcomma_state = 1;
- }
- static void js_print_more_items(JSPrintValueState *s, int *pcomma_state,
- uint32_t n)
- {
- js_print_comma(s, pcomma_state);
- js_printf(s, "... %u more item%s", n, n > 1 ? "s" : "");
- }
- /* similar to js_regexp_toString() but without side effect */
- static void js_print_regexp(JSPrintValueState *s, JSObject *p1)
- {
- JSRegExp *re = &p1->u.regexp;
- JSString *p;
- int i, n, c, c2, bra, flags;
- static const char regexp_flags[] = { 'g', 'i', 'm', 's', 'u', 'y', 'd', 'v' };
- if (!re->pattern || !re->bytecode) {
- /* the regexp fields are zeroed at init */
- js_puts(s, "[uninitialized_regexp]");
- return;
- }
- p = re->pattern;
- js_putc(s, '/');
- if (p->len == 0) {
- js_puts(s, "(?:)");
- } else {
- bra = 0;
- for (i = 0, n = p->len; i < n;) {
- c2 = -1;
- switch (c = string_get(p, i++)) {
- case '\\':
- if (i < n)
- c2 = string_get(p, i++);
- break;
- case ']':
- bra = 0;
- break;
- case '[':
- if (!bra) {
- if (i < n && string_get(p, i) == ']')
- c2 = string_get(p, i++);
- bra = 1;
- }
- break;
- case '\n':
- c = '\\';
- c2 = 'n';
- break;
- case '\r':
- c = '\\';
- c2 = 'r';
- break;
- case '/':
- if (!bra) {
- c = '\\';
- c2 = '/';
- }
- break;
- }
- js_putc(s, c);
- if (c2 >= 0)
- js_putc(s, c2);
- }
- }
- js_putc(s, '/');
- flags = lre_get_flags(re->bytecode->u.str8);
- for(i = 0; i < countof(regexp_flags); i++) {
- if ((flags >> i) & 1) {
- js_putc(s, regexp_flags[i]);
- }
- }
- }
- /* similar to js_error_toString() but without side effect */
- static void js_print_error(JSPrintValueState *s, JSObject *p)
- {
- const char *str;
- size_t len;
- str = get_prop_string(s->ctx, JS_MKPTR(JS_TAG_OBJECT, p), JS_ATOM_name);
- if (!str) {
- js_puts(s, "Error");
- } else {
- js_puts(s, str);
- JS_FreeCString(s->ctx, str);
- }
-
- str = get_prop_string(s->ctx, JS_MKPTR(JS_TAG_OBJECT, p), JS_ATOM_message);
- if (str && str[0] != '\0') {
- js_puts(s, ": ");
- js_puts(s, str);
- }
- JS_FreeCString(s->ctx, str);
- /* dump the stack if present */
- str = get_prop_string(s->ctx, JS_MKPTR(JS_TAG_OBJECT, p), JS_ATOM_stack);
- if (str) {
- js_putc(s, '\n');
-
- /* XXX: should remove the last '\n' in stack as
- v8. SpiderMonkey does not do it */
- len = strlen(str);
- if (len > 0 && str[len - 1] == '\n')
- len--;
- s->write_func(s->write_opaque, str, len);
-
- JS_FreeCString(s->ctx, str);
- }
- }
- static void js_print_object(JSPrintValueState *s, JSObject *p)
- {
- JSRuntime *rt = s->rt;
- JSShape *sh;
- JSShapeProperty *prs;
- JSProperty *pr;
- int comma_state;
- BOOL is_array;
- uint32_t i;
-
- comma_state = 0;
- is_array = FALSE;
- if (p->class_id == JS_CLASS_ARRAY) {
- is_array = TRUE;
- js_printf(s, "[ ");
- /* XXX: print array like properties even if not fast array */
- if (p->fast_array) {
- uint32_t len, n, len1;
- len = js_print_array_get_length(p);
- len1 = min_uint32(p->u.array.count, s->options.max_item_count);
- for(i = 0; i < len1; i++) {
- js_print_comma(s, &comma_state);
- js_print_value(s, p->u.array.u.values[i]);
- }
- if (len1 < p->u.array.count)
- js_print_more_items(s, &comma_state, p->u.array.count - len1);
- if (p->u.array.count < len) {
- n = len - p->u.array.count;
- js_print_comma(s, &comma_state);
- js_printf(s, "<%u empty item%s>", n, n > 1 ? "s" : "");
- }
- }
- } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
- uint32_t size = 1 << typed_array_size_log2(p->class_id);
- uint32_t len1;
- int64_t v;
- js_print_atom(s, rt->class_array[p->class_id].class_name);
- js_printf(s, "(%u) [ ", p->u.array.count);
-
- is_array = TRUE;
- len1 = min_uint32(p->u.array.count, s->options.max_item_count);
- for(i = 0; i < len1; i++) {
- const uint8_t *ptr = p->u.array.u.uint8_ptr + i * size;
- js_print_comma(s, &comma_state);
- switch(p->class_id) {
- case JS_CLASS_UINT8C_ARRAY:
- case JS_CLASS_UINT8_ARRAY:
- v = *ptr;
- goto ta_int64;
- case JS_CLASS_INT8_ARRAY:
- v = *(int8_t *)ptr;
- goto ta_int64;
- case JS_CLASS_INT16_ARRAY:
- v = *(int16_t *)ptr;
- goto ta_int64;
- case JS_CLASS_UINT16_ARRAY:
- v = *(uint16_t *)ptr;
- goto ta_int64;
- case JS_CLASS_INT32_ARRAY:
- v = *(int32_t *)ptr;
- goto ta_int64;
- case JS_CLASS_UINT32_ARRAY:
- v = *(uint32_t *)ptr;
- goto ta_int64;
- case JS_CLASS_BIG_INT64_ARRAY:
- v = *(int64_t *)ptr;
- ta_int64:
- js_printf(s, "%" PRId64, v);
- break;
- case JS_CLASS_BIG_UINT64_ARRAY:
- js_printf(s, "%" PRIu64, *(uint64_t *)ptr);
- break;
- case JS_CLASS_FLOAT16_ARRAY:
- js_print_float64(s, fromfp16(*(uint16_t *)ptr));
- break;
- case JS_CLASS_FLOAT32_ARRAY:
- js_print_float64(s, *(float *)ptr);
- break;
- case JS_CLASS_FLOAT64_ARRAY:
- js_print_float64(s, *(double *)ptr);
- break;
- }
- }
- if (len1 < p->u.array.count)
- js_print_more_items(s, &comma_state, p->u.array.count - len1);
- } else if (p->class_id == JS_CLASS_BYTECODE_FUNCTION ||
- (rt->class_array[p->class_id].call != NULL &&
- p->class_id != JS_CLASS_PROXY)) {
- js_printf(s, "[Function");
- /* XXX: allow dump without ctx */
- if (!s->options.raw_dump && s->ctx) {
- const char *func_name_str;
- js_putc(s, ' ');
- func_name_str = get_prop_string(s->ctx, JS_MKPTR(JS_TAG_OBJECT, p), JS_ATOM_name);
- if (!func_name_str || func_name_str[0] == '\0')
- js_puts(s, "(anonymous)");
- else
- js_puts(s, func_name_str);
- JS_FreeCString(s->ctx, func_name_str);
- }
- js_printf(s, "]");
- comma_state = 2;
- } else if (p->class_id == JS_CLASS_MAP || p->class_id == JS_CLASS_SET) {
- JSMapState *ms = p->u.opaque;
- struct list_head *el;
-
- if (!ms)
- goto default_obj;
- js_print_atom(s, rt->class_array[p->class_id].class_name);
- js_printf(s, "(%u) { ", ms->record_count);
- i = 0;
- list_for_each(el, &ms->records) {
- JSMapRecord *mr = list_entry(el, JSMapRecord, link);
- js_print_comma(s, &comma_state);
- if (mr->empty)
- continue;
- js_print_value(s, mr->key);
- if (p->class_id == JS_CLASS_MAP) {
- js_printf(s, " => ");
- js_print_value(s, mr->value);
- }
- i++;
- if (i >= s->options.max_item_count)
- break;
- }
- if (i < ms->record_count)
- js_print_more_items(s, &comma_state, ms->record_count - i);
- } else if (p->class_id == JS_CLASS_REGEXP && s->ctx) {
- js_print_regexp(s, p);
- comma_state = 2;
- } else if (p->class_id == JS_CLASS_DATE && s->ctx) {
- /* get_date_string() has no side effect */
- JSValue str = get_date_string(s->ctx, JS_MKPTR(JS_TAG_OBJECT, p), 0, NULL, 0x23); /* toISOString() */
- if (JS_IsException(str))
- goto default_obj;
- js_print_raw_string(s, str);
- JS_FreeValueRT(s->rt, str);
- comma_state = 2;
- } else if (p->class_id == JS_CLASS_ERROR && s->ctx) {
- js_print_error(s, p);
- comma_state = 2;
- } else {
- default_obj:
- if (p->class_id != JS_CLASS_OBJECT) {
- js_print_atom(s, rt->class_array[p->class_id].class_name);
- js_printf(s, " ");
- }
- js_printf(s, "{ ");
- }
-
- sh = p->shape; /* the shape can be NULL while freeing an object */
- if (sh) {
- uint32_t j;
-
- j = 0;
- for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
- if (prs->atom != JS_ATOM_NULL) {
- if (!(prs->flags & JS_PROP_ENUMERABLE) &&
- !s->options.show_hidden) {
- continue;
- }
- if (j < s->options.max_item_count) {
- pr = &p->prop[i];
- js_print_comma(s, &comma_state);
- js_print_atom(s, prs->atom);
- js_printf(s, ": ");
-
- /* XXX: autoinit property */
- if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
- if (s->options.raw_dump) {
- js_printf(s, "[Getter %p Setter %p]",
- pr->u.getset.getter, pr->u.getset.setter);
- } else {
- if (pr->u.getset.getter && pr->u.getset.setter) {
- js_printf(s, "[Getter/Setter]");
- } else if (pr->u.getset.setter) {
- js_printf(s, "[Setter]");
- } else {
- js_printf(s, "[Getter]");
- }
- }
- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
- if (s->options.raw_dump) {
- js_printf(s, "[varref %p]", (void *)pr->u.var_ref);
- } else {
- js_print_value(s, *pr->u.var_ref->pvalue);
- }
- } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
- if (s->options.raw_dump) {
- js_printf(s, "[autoinit %p %d %p]",
- (void *)js_autoinit_get_realm(pr),
- js_autoinit_get_id(pr),
- (void *)pr->u.init.opaque);
- } else {
- /* XXX: could autoinit but need to restart
- the iteration */
- js_printf(s, "[autoinit]");
- }
- } else {
- js_print_value(s, pr->u.value);
- }
- }
- j++;
- }
- }
- if (j > s->options.max_item_count)
- js_print_more_items(s, &comma_state, j - s->options.max_item_count);
- }
- if (s->options.raw_dump && js_class_has_bytecode(p->class_id)) {
- JSFunctionBytecode *b = p->u.func.function_bytecode;
- if (b->closure_var_count) {
- JSVarRef **var_refs;
- var_refs = p->u.func.var_refs;
-
- js_print_comma(s, &comma_state);
- js_printf(s, "[[Closure]]: [");
- for(i = 0; i < b->closure_var_count; i++) {
- if (i != 0)
- js_printf(s, ", ");
- js_print_value(s, var_refs[i]->value);
- }
- js_printf(s, " ]");
- }
- if (p->u.func.home_object) {
- js_print_comma(s, &comma_state);
- js_printf(s, "[[HomeObject]]: ");
- js_print_value(s, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object));
- }
- }
- if (!is_array) {
- if (comma_state != 2) {
- js_printf(s, " }");
- }
- } else {
- js_printf(s, " ]");
- }
- }
- static int js_print_stack_index(JSPrintValueState *s, JSObject *p)
- {
- int i;
- for(i = 0; i < s->level; i++)
- if (s->print_stack[i] == p)
- return i;
- return -1;
- }
- static void js_print_value(JSPrintValueState *s, JSValueConst val)
- {
- uint32_t tag = JS_VALUE_GET_NORM_TAG(val);
- const char *str;
- switch(tag) {
- case JS_TAG_INT:
- js_printf(s, "%d", JS_VALUE_GET_INT(val));
- break;
- case JS_TAG_BOOL:
- if (JS_VALUE_GET_BOOL(val))
- str = "true";
- else
- str = "false";
- goto print_str;
- case JS_TAG_NULL:
- str = "null";
- goto print_str;
- case JS_TAG_EXCEPTION:
- str = "exception";
- goto print_str;
- case JS_TAG_UNINITIALIZED:
- str = "uninitialized";
- goto print_str;
- case JS_TAG_UNDEFINED:
- str = "undefined";
- print_str:
- js_puts(s, str);
- break;
- case JS_TAG_FLOAT64:
- js_print_float64(s, JS_VALUE_GET_FLOAT64(val));
- break;
- case JS_TAG_SHORT_BIG_INT:
- js_printf(s, "%" PRId64 "n", (int64_t)JS_VALUE_GET_SHORT_BIG_INT(val));
- break;
- case JS_TAG_BIG_INT:
- if (!s->options.raw_dump && s->ctx) {
- JSValue str = js_bigint_to_string(s->ctx, val);
- if (JS_IsException(str))
- goto raw_bigint;
- js_print_raw_string(s, str);
- js_putc(s, 'n');
- JS_FreeValueRT(s->rt, str);
- } else {
- JSBigInt *p;
- int sgn, i;
- raw_bigint:
- p = JS_VALUE_GET_PTR(val);
- /* In order to avoid allocations we just dump the limbs */
- sgn = js_bigint_sign(p);
- if (sgn)
- js_printf(s, "BigInt.asIntN(%d,", p->len * JS_LIMB_BITS);
- js_printf(s, "0x");
- for(i = p->len - 1; i >= 0; i--) {
- if (i != p->len - 1)
- js_putc(s, '_');
- #if JS_LIMB_BITS == 32
- js_printf(s, "%08x", p->tab[i]);
- #else
- js_printf(s, "%016" PRIx64, p->tab[i]);
- #endif
- }
- js_putc(s, 'n');
- if (sgn)
- js_putc(s, ')');
- }
- break;
- case JS_TAG_STRING:
- case JS_TAG_STRING_ROPE:
- if (s->options.raw_dump && tag == JS_TAG_STRING_ROPE) {
- JSStringRope *r = JS_VALUE_GET_STRING_ROPE(val);
- js_printf(s, "[rope len=%d depth=%d]", r->len, r->depth);
- } else {
- js_print_string(s, val);
- }
- break;
- case JS_TAG_FUNCTION_BYTECODE:
- {
- JSFunctionBytecode *b = JS_VALUE_GET_PTR(val);
- js_puts(s, "[bytecode ");
- js_print_atom(s, b->func_name);
- js_putc(s, ']');
- }
- break;
- case JS_TAG_OBJECT:
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- int idx;
- idx = js_print_stack_index(s, p);
- if (idx >= 0) {
- js_printf(s, "[circular %d]", idx);
- } else if (s->level < s->options.max_depth) {
- s->print_stack[s->level++] = p;
- js_print_object(s, JS_VALUE_GET_OBJ(val));
- s->level--;
- } else {
- JSAtom atom = s->rt->class_array[p->class_id].class_name;
- js_putc(s, '[');
- js_print_atom(s, atom);
- if (s->options.raw_dump) {
- js_printf(s, " %p", (void *)p);
- }
- js_putc(s, ']');
- }
- }
- break;
- case JS_TAG_SYMBOL:
- {
- JSAtomStruct *p = JS_VALUE_GET_PTR(val);
- js_puts(s, "Symbol(");
- js_print_atom(s, js_get_atom_index(s->rt, p));
- js_putc(s, ')');
- }
- break;
- case JS_TAG_MODULE:
- js_puts(s, "[module]");
- break;
- default:
- js_printf(s, "[unknown tag %d]", tag);
- break;
- }
- }
- void JS_PrintValueSetDefaultOptions(JSPrintValueOptions *options)
- {
- memset(options, 0, sizeof(*options));
- options->max_depth = 2;
- options->max_string_length = 1000;
- options->max_item_count = 100;
- }
- static void JS_PrintValueInternal(JSRuntime *rt, JSContext *ctx,
- JSPrintValueWrite *write_func, void *write_opaque,
- JSValueConst val, const JSPrintValueOptions *options)
- {
- JSPrintValueState ss, *s = &ss;
- if (options)
- s->options = *options;
- else
- JS_PrintValueSetDefaultOptions(&s->options);
- if (s->options.max_depth <= 0)
- s->options.max_depth = JS_PRINT_MAX_DEPTH;
- else
- s->options.max_depth = min_int(s->options.max_depth, JS_PRINT_MAX_DEPTH);
- if (s->options.max_string_length == 0)
- s->options.max_string_length = UINT32_MAX;
- if (s->options.max_item_count == 0)
- s->options.max_item_count = UINT32_MAX;
- s->rt = rt;
- s->ctx = ctx;
- s->write_func = write_func;
- s->write_opaque = write_opaque;
- s->level = 0;
- js_print_value(s, val);
- }
- void JS_PrintValueRT(JSRuntime *rt, JSPrintValueWrite *write_func, void *write_opaque,
- JSValueConst val, const JSPrintValueOptions *options)
- {
- JS_PrintValueInternal(rt, NULL, write_func, write_opaque, val, options);
- }
- void JS_PrintValue(JSContext *ctx, JSPrintValueWrite *write_func, void *write_opaque,
- JSValueConst val, const JSPrintValueOptions *options)
- {
- JS_PrintValueInternal(ctx->rt, ctx, write_func, write_opaque, val, options);
- }
- static void js_dump_value_write(void *opaque, const char *buf, size_t len)
- {
- FILE *fo = opaque;
- fwrite(buf, 1, len, fo);
- }
- static __maybe_unused void print_atom(JSContext *ctx, JSAtom atom)
- {
- JSPrintValueState ss, *s = &ss;
- memset(s, 0, sizeof(*s));
- s->rt = ctx->rt;
- s->ctx = ctx;
- s->write_func = js_dump_value_write;
- s->write_opaque = stdout;
- js_print_atom(s, atom);
- }
- static __maybe_unused void JS_DumpAtom(JSContext *ctx, const char *str, JSAtom atom)
- {
- printf("%s=", str);
- print_atom(ctx, atom);
- printf("\n");
- }
- static __maybe_unused void JS_DumpValue(JSContext *ctx, const char *str, JSValueConst val)
- {
- printf("%s=", str);
- JS_PrintValue(ctx, js_dump_value_write, stdout, val, NULL);
- printf("\n");
- }
- static __maybe_unused void JS_DumpValueRT(JSRuntime *rt, const char *str, JSValueConst val)
- {
- printf("%s=", str);
- JS_PrintValueRT(rt, js_dump_value_write, stdout, val, NULL);
- printf("\n");
- }
- static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt)
- {
- printf("%14s %4s %4s %14s %s\n",
- "ADDRESS", "REFS", "SHRF", "PROTO", "CONTENT");
- }
- /* for debug only: dump an object without side effect */
- static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p)
- {
- JSShape *sh;
- JSPrintValueOptions options;
-
- /* XXX: should encode atoms with special characters */
- sh = p->shape; /* the shape can be NULL while freeing an object */
- printf("%14p %4d ",
- (void *)p,
- js_rc(p)->ref_count);
- if (sh) {
- printf("%3d%c %14p ",
- js_rc(sh)->ref_count,
- " *"[sh->is_hashed],
- (void *)sh->proto);
- } else {
- printf("%3s %14s ", "-", "-");
- }
- JS_PrintValueSetDefaultOptions(&options);
- options.max_depth = 1;
- options.show_hidden = TRUE;
- options.raw_dump = TRUE;
- JS_PrintValueRT(rt, js_dump_value_write, stdout, JS_MKPTR(JS_TAG_OBJECT, p), &options);
- printf("\n");
- }
- static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p)
- {
- if (js_rc(p)->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
- JS_DumpObject(rt, (JSObject *)p);
- } else {
- printf("%14p %4d ",
- (void *)p,
- js_rc(p)->ref_count);
- switch(js_rc(p)->gc_obj_type) {
- case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
- printf("[function bytecode]");
- break;
- case JS_GC_OBJ_TYPE_SHAPE:
- printf("[shape]");
- break;
- case JS_GC_OBJ_TYPE_VAR_REF:
- printf("[var_ref]");
- break;
- case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
- printf("[async_function]");
- break;
- case JS_GC_OBJ_TYPE_JS_CONTEXT:
- printf("[js_context]");
- break;
- case JS_GC_OBJ_TYPE_MODULE:
- printf("[module]");
- break;
- default:
- printf("[unknown %d]", js_rc(p)->gc_obj_type);
- break;
- }
- printf("\n");
- }
- }
- /* return -1 if exception (proxy case) or TRUE/FALSE */
- // TODO: should take flags to make proxy resolution and exceptions optional
- int JS_IsArray(JSContext *ctx, JSValueConst val)
- {
- if (js_resolve_proxy(ctx, &val, TRUE))
- return -1;
- if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- return p->class_id == JS_CLASS_ARRAY;
- } else {
- return FALSE;
- }
- }
- static double js_pow(double a, double b)
- {
- if (unlikely(!isfinite(b)) && fabs(a) == 1) {
- /* not compatible with IEEE 754 */
- return JS_FLOAT64_NAN;
- } else {
- return pow(a, b);
- }
- }
- JSValue JS_NewBigInt64(JSContext *ctx, int64_t v)
- {
- #if JS_SHORT_BIG_INT_BITS == 64
- return __JS_NewShortBigInt(ctx, v);
- #else
- if (v >= JS_SHORT_BIG_INT_MIN && v <= JS_SHORT_BIG_INT_MAX) {
- return __JS_NewShortBigInt(ctx, v);
- } else {
- JSBigInt *p;
- p = js_bigint_new_si64(ctx, v);
- if (!p)
- return JS_EXCEPTION;
- return JS_MKPTR(JS_TAG_BIG_INT, p);
- }
- #endif
- }
- JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v)
- {
- if (v <= JS_SHORT_BIG_INT_MAX) {
- return __JS_NewShortBigInt(ctx, v);
- } else {
- JSBigInt *p;
- p = js_bigint_new_ui64(ctx, v);
- if (!p)
- return JS_EXCEPTION;
- return JS_MKPTR(JS_TAG_BIG_INT, p);
- }
- }
- /* return NaN if bad bigint literal */
- static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val)
- {
- const char *str, *p;
- size_t len;
- int flags;
- str = JS_ToCStringLen(ctx, &len, val);
- JS_FreeValue(ctx, val);
- if (!str)
- return JS_EXCEPTION;
- p = str;
- p += skip_spaces(p);
- if ((p - str) == len) {
- val = JS_NewBigInt64(ctx, 0);
- } else {
- flags = ATOD_INT_ONLY | ATOD_ACCEPT_BIN_OCT | ATOD_TYPE_BIG_INT;
- val = js_atof(ctx, p, &p, 0, flags);
- p += skip_spaces(p);
- if (!JS_IsException(val)) {
- if ((p - str) != len) {
- JS_FreeValue(ctx, val);
- val = JS_NAN;
- }
- }
- }
- JS_FreeCString(ctx, str);
- return val;
- }
- static JSValue JS_StringToBigIntErr(JSContext *ctx, JSValue val)
- {
- val = JS_StringToBigInt(ctx, val);
- if (JS_VALUE_IS_NAN(val))
- return JS_ThrowSyntaxError(ctx, "invalid bigint literal");
- return val;
- }
- /* JS Numbers are not allowed */
- static JSValue JS_ToBigIntFree(JSContext *ctx, JSValue val)
- {
- uint32_t tag;
- redo:
- tag = JS_VALUE_GET_NORM_TAG(val);
- switch(tag) {
- case JS_TAG_SHORT_BIG_INT:
- case JS_TAG_BIG_INT:
- break;
- case JS_TAG_INT:
- case JS_TAG_NULL:
- case JS_TAG_UNDEFINED:
- case JS_TAG_FLOAT64:
- goto fail;
- case JS_TAG_BOOL:
- val = __JS_NewShortBigInt(ctx, JS_VALUE_GET_INT(val));
- break;
- case JS_TAG_STRING:
- case JS_TAG_STRING_ROPE:
- val = JS_StringToBigIntErr(ctx, val);
- if (JS_IsException(val))
- return val;
- goto redo;
- case JS_TAG_OBJECT:
- val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
- if (JS_IsException(val))
- return val;
- goto redo;
- default:
- fail:
- JS_FreeValue(ctx, val);
- return JS_ThrowTypeError(ctx, "cannot convert to bigint");
- }
- return val;
- }
- static JSValue JS_ToBigInt(JSContext *ctx, JSValueConst val)
- {
- return JS_ToBigIntFree(ctx, JS_DupValue(ctx, val));
- }
- /* XXX: merge with JS_ToInt64Free with a specific flag ? */
- static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val)
- {
- uint64_t res;
- val = JS_ToBigIntFree(ctx, val);
- if (JS_IsException(val)) {
- *pres = 0;
- return -1;
- }
- if (JS_VALUE_GET_TAG(val) == JS_TAG_SHORT_BIG_INT) {
- res = JS_VALUE_GET_SHORT_BIG_INT(val);
- } else {
- JSBigInt *p = JS_VALUE_GET_PTR(val);
- /* return the value mod 2^64 */
- res = p->tab[0];
- #if JS_LIMB_BITS == 32
- if (p->len >= 2)
- res |= (uint64_t)p->tab[1] << 32;
- #endif
- JS_FreeValue(ctx, val);
- }
- *pres = res;
- return 0;
- }
- int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val)
- {
- return JS_ToBigInt64Free(ctx, pres, JS_DupValue(ctx, val));
- }
- static no_inline __exception int js_unary_arith_slow(JSContext *ctx,
- JSValue *sp,
- OPCodeEnum op)
- {
- JSValue op1;
- int v;
- uint32_t tag;
- JSBigIntBuf buf1;
- JSBigInt *p1;
- op1 = sp[-1];
- /* fast path for float64 */
- if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1)))
- goto handle_float64;
- op1 = JS_ToNumericFree(ctx, op1);
- if (JS_IsException(op1))
- goto exception;
- tag = JS_VALUE_GET_TAG(op1);
- switch(tag) {
- case JS_TAG_INT:
- {
- int64_t v64;
- v64 = JS_VALUE_GET_INT(op1);
- switch(op) {
- case OP_inc:
- case OP_dec:
- v = 2 * (op - OP_dec) - 1;
- v64 += v;
- break;
- case OP_plus:
- break;
- case OP_neg:
- if (v64 == 0) {
- sp[-1] = __JS_NewFloat64(ctx, -0.0);
- return 0;
- } else {
- v64 = -v64;
- }
- break;
- default:
- abort();
- }
- sp[-1] = JS_NewInt64(ctx, v64);
- }
- break;
- case JS_TAG_SHORT_BIG_INT:
- {
- int64_t v;
- v = JS_VALUE_GET_SHORT_BIG_INT(op1);
- switch(op) {
- case OP_plus:
- JS_ThrowTypeError(ctx, "bigint argument with unary +");
- goto exception;
- case OP_inc:
- if (v == JS_SHORT_BIG_INT_MAX)
- goto bigint_slow_case;
- sp[-1] = __JS_NewShortBigInt(ctx, v + 1);
- break;
- case OP_dec:
- if (v == JS_SHORT_BIG_INT_MIN)
- goto bigint_slow_case;
- sp[-1] = __JS_NewShortBigInt(ctx, v - 1);
- break;
- case OP_neg:
- v = JS_VALUE_GET_SHORT_BIG_INT(op1);
- if (v == JS_SHORT_BIG_INT_MIN) {
- bigint_slow_case:
- p1 = js_bigint_set_short(&buf1, op1);
- goto bigint_slow_case1;
- }
- sp[-1] = __JS_NewShortBigInt(ctx, -v);
- break;
- default:
- abort();
- }
- }
- break;
- case JS_TAG_BIG_INT:
- {
- JSBigInt *r;
- p1 = JS_VALUE_GET_PTR(op1);
- bigint_slow_case1:
- switch(op) {
- case OP_plus:
- JS_ThrowTypeError(ctx, "bigint argument with unary +");
- JS_FreeValue(ctx, op1);
- goto exception;
- case OP_inc:
- case OP_dec:
- {
- JSBigIntBuf buf2;
- JSBigInt *p2;
- p2 = js_bigint_set_si(&buf2, 2 * (op - OP_dec) - 1);
- r = js_bigint_add(ctx, p1, p2, 0);
- }
- break;
- case OP_neg:
- r = js_bigint_neg(ctx, p1);
- break;
- case OP_not:
- r = js_bigint_not(ctx, p1);
- break;
- default:
- abort();
- }
- JS_FreeValue(ctx, op1);
- if (!r)
- goto exception;
- sp[-1] = JS_CompactBigInt(ctx, r);
- }
- break;
- default:
- handle_float64:
- {
- double d;
- d = JS_VALUE_GET_FLOAT64(op1);
- switch(op) {
- case OP_inc:
- case OP_dec:
- v = 2 * (op - OP_dec) - 1;
- d += v;
- break;
- case OP_plus:
- break;
- case OP_neg:
- d = -d;
- break;
- default:
- abort();
- }
- sp[-1] = __JS_NewFloat64(ctx, d);
- }
- break;
- }
- return 0;
- exception:
- sp[-1] = JS_UNDEFINED;
- return -1;
- }
- static __exception int js_post_inc_slow(JSContext *ctx,
- JSValue *sp, OPCodeEnum op)
- {
- JSValue op1;
- /* XXX: allow custom operators */
- op1 = sp[-1];
- op1 = JS_ToNumericFree(ctx, op1);
- if (JS_IsException(op1)) {
- sp[-1] = JS_UNDEFINED;
- return -1;
- }
- sp[-1] = op1;
- sp[0] = JS_DupValue(ctx, op1);
- return js_unary_arith_slow(ctx, sp + 1, op - OP_post_dec + OP_dec);
- }
- static no_inline int js_not_slow(JSContext *ctx, JSValue *sp)
- {
- JSValue op1;
- op1 = sp[-1];
- op1 = JS_ToNumericFree(ctx, op1);
- if (JS_IsException(op1))
- goto exception;
- if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) {
- sp[-1] = __JS_NewShortBigInt(ctx, ~JS_VALUE_GET_SHORT_BIG_INT(op1));
- } else if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) {
- JSBigInt *r;
- r = js_bigint_not(ctx, JS_VALUE_GET_PTR(op1));
- JS_FreeValue(ctx, op1);
- if (!r)
- goto exception;
- sp[-1] = JS_CompactBigInt(ctx, r);
- } else {
- int32_t v1;
- if (unlikely(JS_ToInt32Free(ctx, &v1, op1)))
- goto exception;
- sp[-1] = JS_NewInt32(ctx, ~v1);
- }
- return 0;
- exception:
- sp[-1] = JS_UNDEFINED;
- return -1;
- }
- static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp,
- OPCodeEnum op)
- {
- JSValue op1, op2;
- uint32_t tag1, tag2;
- double d1, d2;
- op1 = sp[-2];
- op2 = sp[-1];
- tag1 = JS_VALUE_GET_NORM_TAG(op1);
- tag2 = JS_VALUE_GET_NORM_TAG(op2);
- /* fast path for float operations */
- if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) {
- d1 = JS_VALUE_GET_FLOAT64(op1);
- d2 = JS_VALUE_GET_FLOAT64(op2);
- goto handle_float64;
- }
- /* fast path for short big int operations */
- if (tag1 == JS_TAG_SHORT_BIG_INT && tag2 == JS_TAG_SHORT_BIG_INT) {
- js_slimb_t v1, v2;
- js_sdlimb_t v;
- v1 = JS_VALUE_GET_SHORT_BIG_INT(op1);
- v2 = JS_VALUE_GET_SHORT_BIG_INT(op2);
- switch(op) {
- case OP_sub:
- v = (js_sdlimb_t)v1 - (js_sdlimb_t)v2;
- break;
- case OP_mul:
- v = (js_sdlimb_t)v1 * (js_sdlimb_t)v2;
- break;
- case OP_div:
- if (v2 == 0 ||
- ((js_limb_t)v1 == (js_limb_t)1 << (JS_LIMB_BITS - 1) &&
- v2 == -1)) {
- goto slow_big_int;
- }
- sp[-2] = __JS_NewShortBigInt(ctx, v1 / v2);
- return 0;
- case OP_mod:
- if (v2 == 0 ||
- ((js_limb_t)v1 == (js_limb_t)1 << (JS_LIMB_BITS - 1) &&
- v2 == -1)) {
- goto slow_big_int;
- }
- sp[-2] = __JS_NewShortBigInt(ctx, v1 % v2);
- return 0;
- case OP_pow:
- goto slow_big_int;
- default:
- abort();
- }
- if (likely(v >= JS_SHORT_BIG_INT_MIN && v <= JS_SHORT_BIG_INT_MAX)) {
- sp[-2] = __JS_NewShortBigInt(ctx, v);
- } else {
- JSBigInt *r = js_bigint_new_di(ctx, v);
- if (!r)
- goto exception;
- sp[-2] = JS_MKPTR(JS_TAG_BIG_INT, r);
- }
- return 0;
- }
- op1 = JS_ToNumericFree(ctx, op1);
- if (JS_IsException(op1)) {
- JS_FreeValue(ctx, op2);
- goto exception;
- }
- op2 = JS_ToNumericFree(ctx, op2);
- if (JS_IsException(op2)) {
- JS_FreeValue(ctx, op1);
- goto exception;
- }
- tag1 = JS_VALUE_GET_NORM_TAG(op1);
- tag2 = JS_VALUE_GET_NORM_TAG(op2);
- if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {
- int32_t v1, v2;
- int64_t v;
- v1 = JS_VALUE_GET_INT(op1);
- v2 = JS_VALUE_GET_INT(op2);
- switch(op) {
- case OP_sub:
- v = (int64_t)v1 - (int64_t)v2;
- break;
- case OP_mul:
- v = (int64_t)v1 * (int64_t)v2;
- if (v == 0 && (v1 | v2) < 0) {
- sp[-2] = __JS_NewFloat64(ctx, -0.0);
- return 0;
- }
- break;
- case OP_div:
- sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2);
- return 0;
- case OP_mod:
- if (v1 < 0 || v2 <= 0) {
- sp[-2] = JS_NewFloat64(ctx, fmod(v1, v2));
- return 0;
- } else {
- v = (int64_t)v1 % (int64_t)v2;
- }
- break;
- case OP_pow:
- sp[-2] = JS_NewFloat64(ctx, js_pow(v1, v2));
- return 0;
- default:
- abort();
- }
- sp[-2] = JS_NewInt64(ctx, v);
- } else if ((tag1 == JS_TAG_SHORT_BIG_INT || tag1 == JS_TAG_BIG_INT) &&
- (tag2 == JS_TAG_SHORT_BIG_INT || tag2 == JS_TAG_BIG_INT)) {
- JSBigInt *p1, *p2, *r;
- JSBigIntBuf buf1, buf2;
- slow_big_int:
- /* bigint result */
- if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT)
- p1 = js_bigint_set_short(&buf1, op1);
- else
- p1 = JS_VALUE_GET_PTR(op1);
- if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT)
- p2 = js_bigint_set_short(&buf2, op2);
- else
- p2 = JS_VALUE_GET_PTR(op2);
- switch(op) {
- case OP_add:
- r = js_bigint_add(ctx, p1, p2, 0);
- break;
- case OP_sub:
- r = js_bigint_add(ctx, p1, p2, 1);
- break;
- case OP_mul:
- r = js_bigint_mul(ctx, p1, p2);
- break;
- case OP_div:
- r = js_bigint_divrem(ctx, p1, p2, FALSE);
- break;
- case OP_mod:
- r = js_bigint_divrem(ctx, p1, p2, TRUE);
- break;
- case OP_pow:
- r = js_bigint_pow(ctx, p1, p2);
- break;
- default:
- abort();
- }
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- if (!r)
- goto exception;
- sp[-2] = JS_CompactBigInt(ctx, r);
- } else {
- double dr;
- /* float64 result */
- if (JS_ToFloat64Free(ctx, &d1, op1)) {
- JS_FreeValue(ctx, op2);
- goto exception;
- }
- if (JS_ToFloat64Free(ctx, &d2, op2))
- goto exception;
- handle_float64:
- switch(op) {
- case OP_sub:
- dr = d1 - d2;
- break;
- case OP_mul:
- dr = d1 * d2;
- break;
- case OP_div:
- dr = d1 / d2;
- break;
- case OP_mod:
- dr = fmod(d1, d2);
- break;
- case OP_pow:
- dr = js_pow(d1, d2);
- break;
- default:
- abort();
- }
- sp[-2] = __JS_NewFloat64(ctx, dr);
- }
- return 0;
- exception:
- sp[-2] = JS_UNDEFINED;
- sp[-1] = JS_UNDEFINED;
- return -1;
- }
- static inline BOOL tag_is_string(uint32_t tag)
- {
- return tag == JS_TAG_STRING || tag == JS_TAG_STRING_ROPE;
- }
- static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp)
- {
- JSValue op1, op2;
- uint32_t tag1, tag2;
- op1 = sp[-2];
- op2 = sp[-1];
- tag1 = JS_VALUE_GET_NORM_TAG(op1);
- tag2 = JS_VALUE_GET_NORM_TAG(op2);
- /* fast path for float64 */
- if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) {
- double d1, d2;
- d1 = JS_VALUE_GET_FLOAT64(op1);
- d2 = JS_VALUE_GET_FLOAT64(op2);
- sp[-2] = __JS_NewFloat64(ctx, d1 + d2);
- return 0;
- }
- /* fast path for short bigint */
- if (tag1 == JS_TAG_SHORT_BIG_INT && tag2 == JS_TAG_SHORT_BIG_INT) {
- js_slimb_t v1, v2;
- js_sdlimb_t v;
- v1 = JS_VALUE_GET_SHORT_BIG_INT(op1);
- v2 = JS_VALUE_GET_SHORT_BIG_INT(op2);
- v = (js_sdlimb_t)v1 + (js_sdlimb_t)v2;
- if (likely(v >= JS_SHORT_BIG_INT_MIN && v <= JS_SHORT_BIG_INT_MAX)) {
- sp[-2] = __JS_NewShortBigInt(ctx, v);
- } else {
- JSBigInt *r = js_bigint_new_di(ctx, v);
- if (!r)
- goto exception;
- sp[-2] = JS_MKPTR(JS_TAG_BIG_INT, r);
- }
- return 0;
- }
-
- if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) {
- op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
- if (JS_IsException(op1)) {
- JS_FreeValue(ctx, op2);
- goto exception;
- }
- op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);
- if (JS_IsException(op2)) {
- JS_FreeValue(ctx, op1);
- goto exception;
- }
- tag1 = JS_VALUE_GET_NORM_TAG(op1);
- tag2 = JS_VALUE_GET_NORM_TAG(op2);
- }
- if (tag_is_string(tag1) || tag_is_string(tag2)) {
- sp[-2] = JS_ConcatString(ctx, op1, op2);
- if (JS_IsException(sp[-2]))
- goto exception;
- return 0;
- }
- op1 = JS_ToNumericFree(ctx, op1);
- if (JS_IsException(op1)) {
- JS_FreeValue(ctx, op2);
- goto exception;
- }
- op2 = JS_ToNumericFree(ctx, op2);
- if (JS_IsException(op2)) {
- JS_FreeValue(ctx, op1);
- goto exception;
- }
- tag1 = JS_VALUE_GET_NORM_TAG(op1);
- tag2 = JS_VALUE_GET_NORM_TAG(op2);
- if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {
- int32_t v1, v2;
- int64_t v;
- v1 = JS_VALUE_GET_INT(op1);
- v2 = JS_VALUE_GET_INT(op2);
- v = (int64_t)v1 + (int64_t)v2;
- sp[-2] = JS_NewInt64(ctx, v);
- } else if ((tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT) &&
- (tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT)) {
- JSBigInt *p1, *p2, *r;
- JSBigIntBuf buf1, buf2;
- /* bigint result */
- if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT)
- p1 = js_bigint_set_short(&buf1, op1);
- else
- p1 = JS_VALUE_GET_PTR(op1);
- if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT)
- p2 = js_bigint_set_short(&buf2, op2);
- else
- p2 = JS_VALUE_GET_PTR(op2);
- r = js_bigint_add(ctx, p1, p2, 0);
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- if (!r)
- goto exception;
- sp[-2] = JS_CompactBigInt(ctx, r);
- } else {
- double d1, d2;
- /* float64 result */
- if (JS_ToFloat64Free(ctx, &d1, op1)) {
- JS_FreeValue(ctx, op2);
- goto exception;
- }
- if (JS_ToFloat64Free(ctx, &d2, op2))
- goto exception;
- sp[-2] = __JS_NewFloat64(ctx, d1 + d2);
- }
- return 0;
- exception:
- sp[-2] = JS_UNDEFINED;
- sp[-1] = JS_UNDEFINED;
- return -1;
- }
- static no_inline __exception int js_binary_logic_slow(JSContext *ctx,
- JSValue *sp,
- OPCodeEnum op)
- {
- JSValue op1, op2;
- uint32_t tag1, tag2;
- uint32_t v1, v2, r;
- op1 = sp[-2];
- op2 = sp[-1];
- tag1 = JS_VALUE_GET_NORM_TAG(op1);
- tag2 = JS_VALUE_GET_NORM_TAG(op2);
- if (tag1 == JS_TAG_SHORT_BIG_INT && tag2 == JS_TAG_SHORT_BIG_INT) {
- js_slimb_t v1, v2, v;
- js_sdlimb_t vd;
- v1 = JS_VALUE_GET_SHORT_BIG_INT(op1);
- v2 = JS_VALUE_GET_SHORT_BIG_INT(op2);
- /* bigint fast path */
- switch(op) {
- case OP_and:
- v = v1 & v2;
- break;
- case OP_or:
- v = v1 | v2;
- break;
- case OP_xor:
- v = v1 ^ v2;
- break;
- case OP_sar:
- if (v2 > (JS_LIMB_BITS - 1)) {
- goto slow_big_int;
- } else if (v2 < 0) {
- if (v2 < -(JS_LIMB_BITS - 1))
- goto slow_big_int;
- v2 = -v2;
- goto bigint_shl;
- }
- bigint_sar:
- v = v1 >> v2;
- break;
- case OP_shl:
- if (v2 > (JS_LIMB_BITS - 1)) {
- goto slow_big_int;
- } else if (v2 < 0) {
- if (v2 < -(JS_LIMB_BITS - 1))
- goto slow_big_int;
- v2 = -v2;
- goto bigint_sar;
- }
- bigint_shl:
- vd = (js_dlimb_t)v1 << v2;
- if (likely(vd >= JS_SHORT_BIG_INT_MIN &&
- vd <= JS_SHORT_BIG_INT_MAX)) {
- v = vd;
- } else {
- JSBigInt *r = js_bigint_new_di(ctx, vd);
- if (!r)
- goto exception;
- sp[-2] = JS_MKPTR(JS_TAG_BIG_INT, r);
- return 0;
- }
- break;
- default:
- abort();
- }
- sp[-2] = __JS_NewShortBigInt(ctx, v);
- return 0;
- }
- op1 = JS_ToNumericFree(ctx, op1);
- if (JS_IsException(op1)) {
- JS_FreeValue(ctx, op2);
- goto exception;
- }
- op2 = JS_ToNumericFree(ctx, op2);
- if (JS_IsException(op2)) {
- JS_FreeValue(ctx, op1);
- goto exception;
- }
- tag1 = JS_VALUE_GET_TAG(op1);
- tag2 = JS_VALUE_GET_TAG(op2);
- if ((tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT) &&
- (tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT)) {
- JSBigInt *p1, *p2, *r;
- JSBigIntBuf buf1, buf2;
- slow_big_int:
- if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT)
- p1 = js_bigint_set_short(&buf1, op1);
- else
- p1 = JS_VALUE_GET_PTR(op1);
- if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT)
- p2 = js_bigint_set_short(&buf2, op2);
- else
- p2 = JS_VALUE_GET_PTR(op2);
- switch(op) {
- case OP_and:
- case OP_or:
- case OP_xor:
- r = js_bigint_logic(ctx, p1, p2, op);
- break;
- case OP_shl:
- case OP_sar:
- {
- js_slimb_t shift;
- shift = js_bigint_get_si_sat(p2);
- if (shift > INT32_MAX)
- shift = INT32_MAX;
- else if (shift < -INT32_MAX)
- shift = -INT32_MAX;
- if (op == OP_sar)
- shift = -shift;
- if (shift >= 0)
- r = js_bigint_shl(ctx, p1, shift);
- else
- r = js_bigint_shr(ctx, p1, -shift);
- }
- break;
- default:
- abort();
- }
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- if (!r)
- goto exception;
- sp[-2] = JS_CompactBigInt(ctx, r);
- } else {
- if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) {
- JS_FreeValue(ctx, op2);
- goto exception;
- }
- if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2)))
- goto exception;
- switch(op) {
- case OP_shl:
- r = v1 << (v2 & 0x1f);
- break;
- case OP_sar:
- r = (int)v1 >> (v2 & 0x1f);
- break;
- case OP_and:
- r = v1 & v2;
- break;
- case OP_or:
- r = v1 | v2;
- break;
- case OP_xor:
- r = v1 ^ v2;
- break;
- default:
- abort();
- }
- sp[-2] = JS_NewInt32(ctx, r);
- }
- return 0;
- exception:
- sp[-2] = JS_UNDEFINED;
- sp[-1] = JS_UNDEFINED;
- return -1;
- }
- /* op1 must be a bigint or int. */
- static JSBigInt *JS_ToBigIntBuf(JSContext *ctx, JSBigIntBuf *buf1,
- JSValue op1)
- {
- JSBigInt *p1;
-
- switch(JS_VALUE_GET_TAG(op1)) {
- case JS_TAG_INT:
- p1 = js_bigint_set_si(buf1, JS_VALUE_GET_INT(op1));
- break;
- case JS_TAG_SHORT_BIG_INT:
- p1 = js_bigint_set_short(buf1, op1);
- break;
- case JS_TAG_BIG_INT:
- p1 = JS_VALUE_GET_PTR(op1);
- break;
- default:
- abort();
- }
- return p1;
- }
- /* op1 and op2 must be numeric types and at least one must be a
- bigint. No exception is generated. */
- static int js_compare_bigint(JSContext *ctx, OPCodeEnum op,
- JSValue op1, JSValue op2)
- {
- int res, val, tag1, tag2;
- JSBigIntBuf buf1, buf2;
- JSBigInt *p1, *p2;
-
- tag1 = JS_VALUE_GET_NORM_TAG(op1);
- tag2 = JS_VALUE_GET_NORM_TAG(op2);
- if ((tag1 == JS_TAG_SHORT_BIG_INT || tag1 == JS_TAG_INT) &&
- (tag2 == JS_TAG_SHORT_BIG_INT || tag2 == JS_TAG_INT)) {
- /* fast path */
- js_slimb_t v1, v2;
- if (tag1 == JS_TAG_INT)
- v1 = JS_VALUE_GET_INT(op1);
- else
- v1 = JS_VALUE_GET_SHORT_BIG_INT(op1);
- if (tag2 == JS_TAG_INT)
- v2 = JS_VALUE_GET_INT(op2);
- else
- v2 = JS_VALUE_GET_SHORT_BIG_INT(op2);
- val = (v1 > v2) - (v1 < v2);
- } else {
- if (tag1 == JS_TAG_FLOAT64) {
- p2 = JS_ToBigIntBuf(ctx, &buf2, op2);
- val = js_bigint_float64_cmp(ctx, p2, JS_VALUE_GET_FLOAT64(op1));
- if (val == 2)
- goto unordered;
- val = -val;
- } else if (tag2 == JS_TAG_FLOAT64) {
- p1 = JS_ToBigIntBuf(ctx, &buf1, op1);
- val = js_bigint_float64_cmp(ctx, p1, JS_VALUE_GET_FLOAT64(op2));
- if (val == 2) {
- unordered:
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- return FALSE;
- }
- } else {
- p1 = JS_ToBigIntBuf(ctx, &buf1, op1);
- p2 = JS_ToBigIntBuf(ctx, &buf2, op2);
- val = js_bigint_cmp(ctx, p1, p2);
- }
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- }
- switch(op) {
- case OP_lt:
- res = val < 0;
- break;
- case OP_lte:
- res = val <= 0;
- break;
- case OP_gt:
- res = val > 0;
- break;
- case OP_gte:
- res = val >= 0;
- break;
- case OP_eq:
- res = val == 0;
- break;
- default:
- abort();
- }
- return res;
- }
- static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp,
- OPCodeEnum op)
- {
- JSValue op1, op2;
- int res;
- uint32_t tag1, tag2;
- op1 = sp[-2];
- op2 = sp[-1];
- tag1 = JS_VALUE_GET_NORM_TAG(op1);
- tag2 = JS_VALUE_GET_NORM_TAG(op2);
- op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER);
- if (JS_IsException(op1)) {
- JS_FreeValue(ctx, op2);
- goto exception;
- }
- op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NUMBER);
- if (JS_IsException(op2)) {
- JS_FreeValue(ctx, op1);
- goto exception;
- }
- tag1 = JS_VALUE_GET_NORM_TAG(op1);
- tag2 = JS_VALUE_GET_NORM_TAG(op2);
- if (tag_is_string(tag1) && tag_is_string(tag2)) {
- if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) {
- res = js_string_compare(ctx, JS_VALUE_GET_STRING(op1),
- JS_VALUE_GET_STRING(op2));
- } else {
- res = js_string_rope_compare(ctx, op1, op2, FALSE);
- }
- switch(op) {
- case OP_lt:
- res = (res < 0);
- break;
- case OP_lte:
- res = (res <= 0);
- break;
- case OP_gt:
- res = (res > 0);
- break;
- default:
- case OP_gte:
- res = (res >= 0);
- break;
- }
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- } else if ((tag1 <= JS_TAG_NULL || tag1 == JS_TAG_FLOAT64) &&
- (tag2 <= JS_TAG_NULL || tag2 == JS_TAG_FLOAT64)) {
- /* fast path for float64/int */
- goto float64_compare;
- } else {
- if ((((tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT) &&
- tag_is_string(tag2)) ||
- ((tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) &&
- tag_is_string(tag1)))) {
- if (tag_is_string(tag1)) {
- op1 = JS_StringToBigInt(ctx, op1);
- if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT &&
- JS_VALUE_GET_TAG(op1) != JS_TAG_SHORT_BIG_INT)
- goto invalid_bigint_string;
- }
- if (tag_is_string(tag2)) {
- op2 = JS_StringToBigInt(ctx, op2);
- if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT &&
- JS_VALUE_GET_TAG(op2) != JS_TAG_SHORT_BIG_INT) {
- invalid_bigint_string:
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- res = FALSE;
- goto done;
- }
- }
- } else {
- op1 = JS_ToNumericFree(ctx, op1);
- if (JS_IsException(op1)) {
- JS_FreeValue(ctx, op2);
- goto exception;
- }
- op2 = JS_ToNumericFree(ctx, op2);
- if (JS_IsException(op2)) {
- JS_FreeValue(ctx, op1);
- goto exception;
- }
- }
- tag1 = JS_VALUE_GET_NORM_TAG(op1);
- tag2 = JS_VALUE_GET_NORM_TAG(op2);
- if (tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT ||
- tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) {
- res = js_compare_bigint(ctx, op, op1, op2);
- } else {
- double d1, d2;
- float64_compare:
- /* can use floating point comparison */
- if (tag1 == JS_TAG_FLOAT64) {
- d1 = JS_VALUE_GET_FLOAT64(op1);
- } else {
- d1 = JS_VALUE_GET_INT(op1);
- }
- if (tag2 == JS_TAG_FLOAT64) {
- d2 = JS_VALUE_GET_FLOAT64(op2);
- } else {
- d2 = JS_VALUE_GET_INT(op2);
- }
- switch(op) {
- case OP_lt:
- res = (d1 < d2); /* if NaN return false */
- break;
- case OP_lte:
- res = (d1 <= d2); /* if NaN return false */
- break;
- case OP_gt:
- res = (d1 > d2); /* if NaN return false */
- break;
- default:
- case OP_gte:
- res = (d1 >= d2); /* if NaN return false */
- break;
- }
- }
- }
- done:
- sp[-2] = JS_NewBool(ctx, res);
- return 0;
- exception:
- sp[-2] = JS_UNDEFINED;
- sp[-1] = JS_UNDEFINED;
- return -1;
- }
- static BOOL tag_is_number(uint32_t tag)
- {
- return (tag == JS_TAG_INT ||
- tag == JS_TAG_FLOAT64 ||
- tag == JS_TAG_BIG_INT || tag == JS_TAG_SHORT_BIG_INT);
- }
- static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp,
- BOOL is_neq)
- {
- JSValue op1, op2;
- int res;
- uint32_t tag1, tag2;
- op1 = sp[-2];
- op2 = sp[-1];
- redo:
- tag1 = JS_VALUE_GET_NORM_TAG(op1);
- tag2 = JS_VALUE_GET_NORM_TAG(op2);
- if (tag_is_number(tag1) && tag_is_number(tag2)) {
- if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {
- res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2);
- } else if ((tag1 == JS_TAG_FLOAT64 &&
- (tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64)) ||
- (tag2 == JS_TAG_FLOAT64 &&
- (tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64))) {
- double d1, d2;
- if (tag1 == JS_TAG_FLOAT64) {
- d1 = JS_VALUE_GET_FLOAT64(op1);
- } else {
- d1 = JS_VALUE_GET_INT(op1);
- }
- if (tag2 == JS_TAG_FLOAT64) {
- d2 = JS_VALUE_GET_FLOAT64(op2);
- } else {
- d2 = JS_VALUE_GET_INT(op2);
- }
- res = (d1 == d2);
- } else {
- res = js_compare_bigint(ctx, OP_eq, op1, op2);
- }
- } else if (tag1 == tag2) {
- res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT);
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- } else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) ||
- (tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) {
- res = TRUE;
- } else if (tag_is_string(tag1) && tag_is_string(tag2)) {
- /* needed when comparing strings and ropes */
- res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT);
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- } else if ((tag_is_string(tag1) && tag_is_number(tag2)) ||
- (tag_is_string(tag2) && tag_is_number(tag1))) {
- if (tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT ||
- tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) {
- if (tag_is_string(tag1)) {
- op1 = JS_StringToBigInt(ctx, op1);
- if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT &&
- JS_VALUE_GET_TAG(op1) != JS_TAG_SHORT_BIG_INT)
- goto invalid_bigint_string;
- }
- if (tag_is_string(tag2)) {
- op2 = JS_StringToBigInt(ctx, op2);
- if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT &&
- JS_VALUE_GET_TAG(op2) != JS_TAG_SHORT_BIG_INT ) {
- invalid_bigint_string:
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- res = FALSE;
- goto done;
- }
- }
- } else {
- op1 = JS_ToNumericFree(ctx, op1);
- if (JS_IsException(op1)) {
- JS_FreeValue(ctx, op2);
- goto exception;
- }
- op2 = JS_ToNumericFree(ctx, op2);
- if (JS_IsException(op2)) {
- JS_FreeValue(ctx, op1);
- goto exception;
- }
- }
- res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT);
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- } else if (tag1 == JS_TAG_BOOL) {
- op1 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1));
- goto redo;
- } else if (tag2 == JS_TAG_BOOL) {
- op2 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op2));
- goto redo;
- } else if ((tag1 == JS_TAG_OBJECT &&
- (tag_is_number(tag2) || tag_is_string(tag2) || tag2 == JS_TAG_SYMBOL)) ||
- (tag2 == JS_TAG_OBJECT &&
- (tag_is_number(tag1) || tag_is_string(tag1) || tag1 == JS_TAG_SYMBOL))) {
- op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
- if (JS_IsException(op1)) {
- JS_FreeValue(ctx, op2);
- goto exception;
- }
- op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);
- if (JS_IsException(op2)) {
- JS_FreeValue(ctx, op1);
- goto exception;
- }
- goto redo;
- } else {
- /* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */
- if ((JS_IsHTMLDDA(ctx, op1) &&
- (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) ||
- (JS_IsHTMLDDA(ctx, op2) &&
- (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) {
- res = TRUE;
- } else {
- res = FALSE;
- }
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- }
- done:
- sp[-2] = JS_NewBool(ctx, res ^ is_neq);
- return 0;
- exception:
- sp[-2] = JS_UNDEFINED;
- sp[-1] = JS_UNDEFINED;
- return -1;
- }
- static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp)
- {
- JSValue op1, op2;
- uint32_t v1, v2, r;
- op1 = sp[-2];
- op2 = sp[-1];
- op1 = JS_ToNumericFree(ctx, op1);
- if (JS_IsException(op1)) {
- JS_FreeValue(ctx, op2);
- goto exception;
- }
- op2 = JS_ToNumericFree(ctx, op2);
- if (JS_IsException(op2)) {
- JS_FreeValue(ctx, op1);
- goto exception;
- }
- if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT ||
- JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT ||
- JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT ||
- JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) {
- JS_ThrowTypeError(ctx, "bigint operands are forbidden for >>>");
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- goto exception;
- }
- /* cannot give an exception */
- JS_ToUint32Free(ctx, &v1, op1);
- JS_ToUint32Free(ctx, &v2, op2);
- r = v1 >> (v2 & 0x1f);
- sp[-2] = JS_NewUint32(ctx, r);
- return 0;
- exception:
- sp[-2] = JS_UNDEFINED;
- sp[-1] = JS_UNDEFINED;
- return -1;
- }
- static BOOL js_strict_eq2(JSContext *ctx, JSValueConst op1, JSValueConst op2,
- JSStrictEqModeEnum eq_mode)
- {
- BOOL res;
- int tag1, tag2;
- double d1, d2;
- tag1 = JS_VALUE_GET_NORM_TAG(op1);
- tag2 = JS_VALUE_GET_NORM_TAG(op2);
- switch(tag1) {
- case JS_TAG_BOOL:
- if (tag1 != tag2) {
- res = FALSE;
- } else {
- res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2);
- }
- break;
- case JS_TAG_NULL:
- case JS_TAG_UNDEFINED:
- res = (tag1 == tag2);
- break;
- case JS_TAG_STRING:
- case JS_TAG_STRING_ROPE:
- {
- if (!tag_is_string(tag2)) {
- res = FALSE;
- } else if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) {
- res = js_string_eq(ctx, JS_VALUE_GET_STRING(op1),
- JS_VALUE_GET_STRING(op2));
- } else {
- res = (js_string_rope_compare(ctx, op1, op2, TRUE) == 0);
- }
- }
- break;
- case JS_TAG_SYMBOL:
- {
- JSAtomStruct *p1, *p2;
- if (tag1 != tag2) {
- res = FALSE;
- } else {
- p1 = JS_VALUE_GET_PTR(op1);
- p2 = JS_VALUE_GET_PTR(op2);
- res = (p1 == p2);
- }
- }
- break;
- case JS_TAG_OBJECT:
- if (tag1 != tag2)
- res = FALSE;
- else
- res = JS_VALUE_GET_OBJ(op1) == JS_VALUE_GET_OBJ(op2);
- break;
- case JS_TAG_INT:
- d1 = JS_VALUE_GET_INT(op1);
- if (tag2 == JS_TAG_INT) {
- d2 = JS_VALUE_GET_INT(op2);
- goto number_test;
- } else if (tag2 == JS_TAG_FLOAT64) {
- d2 = JS_VALUE_GET_FLOAT64(op2);
- goto number_test;
- } else {
- res = FALSE;
- }
- break;
- case JS_TAG_FLOAT64:
- d1 = JS_VALUE_GET_FLOAT64(op1);
- if (tag2 == JS_TAG_FLOAT64) {
- d2 = JS_VALUE_GET_FLOAT64(op2);
- } else if (tag2 == JS_TAG_INT) {
- d2 = JS_VALUE_GET_INT(op2);
- } else {
- res = FALSE;
- break;
- }
- number_test:
- if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) {
- JSFloat64Union u1, u2;
- /* NaN is not always normalized, so this test is necessary */
- if (isnan(d1) || isnan(d2)) {
- res = isnan(d1) == isnan(d2);
- } else if (eq_mode == JS_EQ_SAME_VALUE_ZERO) {
- res = (d1 == d2); /* +0 == -0 */
- } else {
- u1.d = d1;
- u2.d = d2;
- res = (u1.u64 == u2.u64); /* +0 != -0 */
- }
- } else {
- res = (d1 == d2); /* if NaN return false and +0 == -0 */
- }
- break;
- case JS_TAG_SHORT_BIG_INT:
- case JS_TAG_BIG_INT:
- {
- JSBigIntBuf buf1, buf2;
- JSBigInt *p1, *p2;
- if (tag2 != JS_TAG_SHORT_BIG_INT &&
- tag2 != JS_TAG_BIG_INT) {
- res = FALSE;
- break;
- }
-
- if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT)
- p1 = js_bigint_set_short(&buf1, op1);
- else
- p1 = JS_VALUE_GET_PTR(op1);
- if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT)
- p2 = js_bigint_set_short(&buf2, op2);
- else
- p2 = JS_VALUE_GET_PTR(op2);
- res = (js_bigint_cmp(ctx, p1, p2) == 0);
- }
- break;
- default:
- res = FALSE;
- break;
- }
- return res;
- }
- static BOOL js_strict_eq(JSContext *ctx, JSValueConst op1, JSValueConst op2)
- {
- return js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT);
- }
- BOOL JS_StrictEq(JSContext *ctx, JSValueConst op1, JSValueConst op2)
- {
- return js_strict_eq(ctx, op1, op2);
- }
- static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2)
- {
- return js_strict_eq2(ctx, op1, op2, JS_EQ_SAME_VALUE);
- }
- BOOL JS_SameValue(JSContext *ctx, JSValueConst op1, JSValueConst op2)
- {
- return js_same_value(ctx, op1, op2);
- }
- static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2)
- {
- return js_strict_eq2(ctx, op1, op2, JS_EQ_SAME_VALUE_ZERO);
- }
- BOOL JS_SameValueZero(JSContext *ctx, JSValueConst op1, JSValueConst op2)
- {
- return js_same_value_zero(ctx, op1, op2);
- }
- static __exception int js_operator_in(JSContext *ctx, JSValue *sp)
- {
- JSValue op1, op2;
- JSAtom atom;
- int ret;
- op1 = sp[-2];
- op2 = sp[-1];
- if (JS_VALUE_GET_TAG(op2) != JS_TAG_OBJECT) {
- JS_ThrowTypeError(ctx, "invalid 'in' operand");
- return -1;
- }
- atom = JS_ValueToAtom(ctx, op1);
- if (unlikely(atom == JS_ATOM_NULL))
- return -1;
- ret = JS_HasProperty(ctx, op2, atom);
- JS_FreeAtom(ctx, atom);
- if (ret < 0)
- return -1;
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- sp[-2] = JS_NewBool(ctx, ret);
- return 0;
- }
- static __exception int js_operator_private_in(JSContext *ctx, JSValue *sp)
- {
- JSValue op1, op2;
- int ret;
- op1 = sp[-2]; /* object */
- op2 = sp[-1]; /* field name or method function */
- if (JS_VALUE_GET_TAG(op1) != JS_TAG_OBJECT) {
- JS_ThrowTypeError(ctx, "invalid 'in' operand");
- return -1;
- }
- if (JS_IsObject(op2)) {
- /* method: use the brand */
- ret = JS_CheckBrand(ctx, op1, op2);
- if (ret < 0)
- return -1;
- } else {
- JSAtom atom;
- JSObject *p;
- JSShapeProperty *prs;
- JSProperty *pr;
- /* field */
- atom = JS_ValueToAtom(ctx, op2);
- if (unlikely(atom == JS_ATOM_NULL))
- return -1;
- p = JS_VALUE_GET_OBJ(op1);
- prs = find_own_property(&pr, p, atom);
- JS_FreeAtom(ctx, atom);
- ret = (prs != NULL);
- }
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- sp[-2] = JS_NewBool(ctx, ret);
- return 0;
- }
- static __exception int js_has_unscopable(JSContext *ctx, JSValueConst obj,
- JSAtom atom)
- {
- JSValue arr, val;
- int ret;
- arr = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_unscopables);
- if (JS_IsException(arr))
- return -1;
- ret = 0;
- if (JS_IsObject(arr)) {
- val = JS_GetProperty(ctx, arr, atom);
- ret = JS_ToBoolFree(ctx, val);
- }
- JS_FreeValue(ctx, arr);
- return ret;
- }
- static __exception int js_operator_instanceof(JSContext *ctx, JSValue *sp)
- {
- JSValue op1, op2;
- BOOL ret;
- op1 = sp[-2];
- op2 = sp[-1];
- ret = JS_IsInstanceOf(ctx, op1, op2);
- if (ret < 0)
- return ret;
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- sp[-2] = JS_NewBool(ctx, ret);
- return 0;
- }
- static __exception int js_operator_typeof(JSContext *ctx, JSValueConst op1)
- {
- JSAtom atom;
- uint32_t tag;
- tag = JS_VALUE_GET_NORM_TAG(op1);
- switch(tag) {
- case JS_TAG_SHORT_BIG_INT:
- case JS_TAG_BIG_INT:
- atom = JS_ATOM_bigint;
- break;
- case JS_TAG_INT:
- case JS_TAG_FLOAT64:
- atom = JS_ATOM_number;
- break;
- case JS_TAG_UNDEFINED:
- atom = JS_ATOM_undefined;
- break;
- case JS_TAG_BOOL:
- atom = JS_ATOM_boolean;
- break;
- case JS_TAG_STRING:
- case JS_TAG_STRING_ROPE:
- atom = JS_ATOM_string;
- break;
- case JS_TAG_OBJECT:
- {
- JSObject *p;
- p = JS_VALUE_GET_OBJ(op1);
- if (unlikely(p->is_HTMLDDA))
- atom = JS_ATOM_undefined;
- else if (JS_IsFunction(ctx, op1))
- atom = JS_ATOM_function;
- else
- goto obj_type;
- }
- break;
- case JS_TAG_NULL:
- obj_type:
- atom = JS_ATOM_object;
- break;
- case JS_TAG_SYMBOL:
- atom = JS_ATOM_symbol;
- break;
- default:
- atom = JS_ATOM_unknown;
- break;
- }
- return atom;
- }
- static __exception int js_operator_delete(JSContext *ctx, JSValue *sp)
- {
- JSValue op1, op2;
- JSAtom atom;
- int ret;
- op1 = sp[-2];
- op2 = sp[-1];
- atom = JS_ValueToAtom(ctx, op2);
- if (unlikely(atom == JS_ATOM_NULL))
- return -1;
- ret = JS_DeleteProperty(ctx, op1, atom, JS_PROP_THROW_STRICT);
- JS_FreeAtom(ctx, atom);
- if (unlikely(ret < 0))
- return -1;
- JS_FreeValue(ctx, op1);
- JS_FreeValue(ctx, op2);
- sp[-2] = JS_NewBool(ctx, ret);
- return 0;
- }
- /* XXX: not 100% compatible, but mozilla seems to use a similar
- implementation to ensure that caller in non strict mode does not
- throw (ES5 compatibility) */
- static JSValue js_throw_type_error(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val);
- if (!b || (b->js_mode & JS_MODE_STRICT) || !b->has_prototype || argc >= 1) {
- return JS_ThrowTypeError(ctx, "invalid property access");
- }
- return JS_UNDEFINED;
- }
- static JSValue js_function_proto_fileName(JSContext *ctx,
- JSValueConst this_val)
- {
- JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val);
- if (b && b->has_debug) {
- return JS_AtomToString(ctx, b->debug.filename);
- }
- return JS_UNDEFINED;
- }
- static JSValue js_function_proto_lineNumber(JSContext *ctx,
- JSValueConst this_val, int is_col)
- {
- JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val);
- if (b && b->has_debug) {
- int line_num, col_num;
- line_num = find_line_num(ctx, b, -1, &col_num);
- if (is_col)
- return JS_NewInt32(ctx, col_num);
- else
- return JS_NewInt32(ctx, line_num);
- }
- return JS_UNDEFINED;
- }
- static int js_arguments_define_own_property(JSContext *ctx,
- JSValueConst this_obj,
- JSAtom prop, JSValueConst val,
- JSValueConst getter, JSValueConst setter, int flags)
- {
- JSObject *p;
- uint32_t idx;
- p = JS_VALUE_GET_OBJ(this_obj);
- /* convert to normal array when redefining an existing numeric field */
- if (p->fast_array && JS_AtomIsArrayIndex(ctx, &idx, prop) &&
- idx < p->u.array.count) {
- if (convert_fast_array_to_array(ctx, p))
- return -1;
- }
- /* run the default define own property */
- return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter,
- flags | JS_PROP_NO_EXOTIC);
- }
- static const JSClassExoticMethods js_arguments_exotic_methods = {
- .define_own_property = js_arguments_define_own_property,
- };
- static JSValue js_build_arguments(JSContext *ctx, int argc, JSValueConst *argv)
- {
- JSValue val, *tab;
- JSProperty props[3];
- JSObject *p;
- int i;
- props[0].u.value = JS_NewInt32(ctx, argc); /* length */
- props[1].u.value = JS_DupValue(ctx, ctx->array_proto_values); /* Symbol.iterator */
- props[2].u.getset.getter = JS_VALUE_GET_OBJ(JS_DupValue(ctx, ctx->throw_type_error)); /* callee */
- props[2].u.getset.setter = JS_VALUE_GET_OBJ(JS_DupValue(ctx, ctx->throw_type_error)); /* callee */
-
- val = JS_NewObjectFromShape(ctx, js_dup_shape(ctx->arguments_shape),
- JS_CLASS_ARGUMENTS, props);
- if (JS_IsException(val))
- return val;
- p = JS_VALUE_GET_OBJ(val);
- /* initialize the fast array part */
- tab = NULL;
- if (argc > 0) {
- tab = js_malloc(ctx, sizeof(tab[0]) * argc);
- if (!tab)
- goto fail;
- for(i = 0; i < argc; i++) {
- tab[i] = JS_DupValue(ctx, argv[i]);
- }
- }
- p->u.array.u.values = tab;
- p->u.array.count = argc;
- return val;
- fail:
- JS_FreeValue(ctx, val);
- return JS_EXCEPTION;
- }
- #define GLOBAL_VAR_OFFSET 0x40000000
- #define ARGUMENT_VAR_OFFSET 0x20000000
- static void js_mapped_arguments_finalizer(JSRuntime *rt, JSValue val)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- JSVarRef **var_refs = p->u.array.u.var_refs;
- int i;
- for(i = 0; i < p->u.array.count; i++)
- free_var_ref(rt, var_refs[i]);
- js_free_rt(rt, var_refs);
- }
- static void js_mapped_arguments_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- JSVarRef **var_refs = p->u.array.u.var_refs;
- int i;
-
- for(i = 0; i < p->u.array.count; i++)
- mark_func(rt, &var_refs[i]->header);
- }
- /* legacy arguments object: add references to the function arguments */
- static JSValue js_build_mapped_arguments(JSContext *ctx, int argc,
- JSValueConst *argv,
- JSStackFrame *sf, int arg_count)
- {
- JSValue val;
- JSProperty props[3];
- JSVarRef **tab, *var_ref;
- JSObject *p;
- int i, j;
- props[0].u.value = JS_NewInt32(ctx, argc); /* length */
- props[1].u.value = JS_DupValue(ctx, ctx->array_proto_values); /* Symbol.iterator */
- props[2].u.value = JS_DupValue(ctx, ctx->rt->current_stack_frame->cur_func); /* callee */
-
- val = JS_NewObjectFromShape(ctx, js_dup_shape(ctx->mapped_arguments_shape),
- JS_CLASS_MAPPED_ARGUMENTS, props);
- if (JS_IsException(val))
- return val;
- p = JS_VALUE_GET_OBJ(val);
- /* initialize the fast array part */
- tab = NULL;
- if (argc > 0) {
- tab = js_malloc(ctx, sizeof(tab[0]) * argc);
- if (!tab)
- goto fail;
- for(i = 0; i < arg_count; i++) {
- var_ref = get_var_ref(ctx, sf, i, TRUE);
- if (!var_ref)
- goto fail1;
- tab[i] = var_ref;
- }
- for(i = arg_count; i < argc; i++) {
- var_ref = js_create_var_ref(ctx, FALSE);
- if (!var_ref) {
- fail1:
- for(j = 0; j < i; j++)
- free_var_ref(ctx->rt, tab[j]);
- js_free(ctx, tab);
- goto fail;
- }
- var_ref->value = JS_DupValue(ctx, argv[i]);
- tab[i] = var_ref;
- }
- }
- p->u.array.u.var_refs = tab;
- p->u.array.count = argc;
- return val;
- fail:
- JS_FreeValue(ctx, val);
- return JS_EXCEPTION;
- }
- static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj)
- {
- JSObject *p, *p1;
- JSPropertyEnum *tab_atom;
- int i;
- JSValue enum_obj;
- JSForInIterator *it;
- uint32_t tag, tab_atom_count;
- tag = JS_VALUE_GET_TAG(obj);
- if (tag != JS_TAG_OBJECT && tag != JS_TAG_NULL && tag != JS_TAG_UNDEFINED) {
- obj = JS_ToObjectFree(ctx, obj);
- }
- it = js_malloc(ctx, sizeof(*it));
- if (!it) {
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- enum_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_FOR_IN_ITERATOR);
- if (JS_IsException(enum_obj)) {
- js_free(ctx, it);
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- it->is_array = FALSE;
- it->obj = obj;
- it->idx = 0;
- it->tab_atom = NULL;
- it->atom_count = 0;
- it->in_prototype_chain = FALSE;
- p1 = JS_VALUE_GET_OBJ(enum_obj);
- p1->u.for_in_iterator = it;
- if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED)
- return enum_obj;
- p = JS_VALUE_GET_OBJ(obj);
- if (p->fast_array) {
- JSShape *sh;
- JSShapeProperty *prs;
- /* check that there are no enumerable normal fields */
- sh = p->shape;
- for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
- if (prs->flags & JS_PROP_ENUMERABLE)
- goto normal_case;
- }
- /* for fast arrays, we only store the number of elements */
- it->is_array = TRUE;
- it->atom_count = p->u.array.count;
- } else {
- normal_case:
- if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p,
- JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) {
- JS_FreeValue(ctx, enum_obj);
- return JS_EXCEPTION;
- }
- it->tab_atom = tab_atom;
- it->atom_count = tab_atom_count;
- }
- return enum_obj;
- }
- /* obj -> enum_obj */
- static __exception int js_for_in_start(JSContext *ctx, JSValue *sp)
- {
- sp[-1] = build_for_in_iterator(ctx, sp[-1]);
- if (JS_IsException(sp[-1]))
- return -1;
- return 0;
- }
- /* return -1 if exception, 0 if slow case, 1 if the enumeration is finished */
- static __exception int js_for_in_prepare_prototype_chain_enum(JSContext *ctx,
- JSValueConst enum_obj)
- {
- JSObject *p;
- JSForInIterator *it;
- JSPropertyEnum *tab_atom;
- uint32_t tab_atom_count, i;
- JSValue obj1;
- p = JS_VALUE_GET_OBJ(enum_obj);
- it = p->u.for_in_iterator;
- /* check if there are enumerable properties in the prototype chain (fast path) */
- obj1 = JS_DupValue(ctx, it->obj);
- for(;;) {
- obj1 = JS_GetPrototypeFree(ctx, obj1);
- if (JS_IsNull(obj1))
- break;
- if (JS_IsException(obj1))
- goto fail;
- if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count,
- JS_VALUE_GET_OBJ(obj1),
- JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) {
- JS_FreeValue(ctx, obj1);
- goto fail;
- }
- JS_FreePropertyEnum(ctx, tab_atom, tab_atom_count);
- if (tab_atom_count != 0) {
- JS_FreeValue(ctx, obj1);
- goto slow_path;
- }
- /* must check for timeout to avoid infinite loop */
- if (js_poll_interrupts(ctx)) {
- JS_FreeValue(ctx, obj1);
- goto fail;
- }
- }
- JS_FreeValue(ctx, obj1);
- return 1;
- slow_path:
- /* add the visited properties, even if they are not enumerable */
- if (it->is_array) {
- if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count,
- JS_VALUE_GET_OBJ(it->obj),
- JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) {
- goto fail;
- }
- it->is_array = FALSE;
- it->tab_atom = tab_atom;
- it->atom_count = tab_atom_count;
- }
- for(i = 0; i < it->atom_count; i++) {
- if (JS_DefinePropertyValue(ctx, enum_obj, it->tab_atom[i].atom, JS_NULL, JS_PROP_ENUMERABLE) < 0)
- goto fail;
- }
- return 0;
- fail:
- return -1;
- }
- /* enum_obj -> enum_obj value done */
- static __exception int js_for_in_next(JSContext *ctx, JSValue *sp)
- {
- JSValueConst enum_obj;
- JSObject *p;
- JSAtom prop;
- JSForInIterator *it;
- JSPropertyEnum *tab_atom;
- uint32_t tab_atom_count;
- int ret;
- enum_obj = sp[-1];
- /* fail safe */
- if (JS_VALUE_GET_TAG(enum_obj) != JS_TAG_OBJECT)
- goto done;
- p = JS_VALUE_GET_OBJ(enum_obj);
- if (p->class_id != JS_CLASS_FOR_IN_ITERATOR)
- goto done;
- it = p->u.for_in_iterator;
- for(;;) {
- if (it->idx >= it->atom_count) {
- if (JS_IsNull(it->obj) || JS_IsUndefined(it->obj))
- goto done; /* not an object */
- /* no more property in the current object: look in the prototype */
- if (!it->in_prototype_chain) {
- ret = js_for_in_prepare_prototype_chain_enum(ctx, enum_obj);
- if (ret < 0)
- return -1;
- if (ret)
- goto done;
- it->in_prototype_chain = TRUE;
- }
- it->obj = JS_GetPrototypeFree(ctx, it->obj);
- if (JS_IsException(it->obj))
- return -1;
- if (JS_IsNull(it->obj))
- goto done; /* no more prototype */
- /* must check for timeout to avoid infinite loop */
- if (js_poll_interrupts(ctx))
- return -1;
- if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count,
- JS_VALUE_GET_OBJ(it->obj),
- JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) {
- return -1;
- }
- JS_FreePropertyEnum(ctx, it->tab_atom, it->atom_count);
- it->tab_atom = tab_atom;
- it->atom_count = tab_atom_count;
- it->idx = 0;
- } else {
- if (it->is_array) {
- prop = __JS_AtomFromUInt32(it->idx);
- it->idx++;
- } else {
- BOOL is_enumerable;
- prop = it->tab_atom[it->idx].atom;
- is_enumerable = it->tab_atom[it->idx].is_enumerable;
- it->idx++;
- if (it->in_prototype_chain) {
- /* slow case: we are in the prototype chain */
- ret = JS_GetOwnPropertyInternal(ctx, NULL, JS_VALUE_GET_OBJ(enum_obj), prop);
- if (ret < 0)
- return ret;
- if (ret)
- continue; /* already visited */
- /* add to the visited property list */
- if (JS_DefinePropertyValue(ctx, enum_obj, prop, JS_NULL,
- JS_PROP_ENUMERABLE) < 0)
- return -1;
- }
- if (!is_enumerable)
- continue;
- }
- /* check if the property was deleted */
- ret = JS_GetOwnPropertyInternal(ctx, NULL, JS_VALUE_GET_OBJ(it->obj), prop);
- if (ret < 0)
- return ret;
- if (ret)
- break;
- }
- }
- /* return the property */
- sp[0] = JS_AtomToValue(ctx, prop);
- sp[1] = JS_FALSE;
- return 0;
- done:
- /* return the end */
- sp[0] = JS_UNDEFINED;
- sp[1] = JS_TRUE;
- return 0;
- }
- static JSValue JS_GetIterator2(JSContext *ctx, JSValueConst obj,
- JSValueConst method)
- {
- JSValue enum_obj;
- enum_obj = JS_Call(ctx, method, obj, 0, NULL);
- if (JS_IsException(enum_obj))
- return enum_obj;
- if (!JS_IsObject(enum_obj)) {
- JS_FreeValue(ctx, enum_obj);
- return JS_ThrowTypeErrorNotAnObject(ctx);
- }
- return enum_obj;
- }
- static JSValue JS_GetIterator(JSContext *ctx, JSValueConst obj, BOOL is_async)
- {
- JSValue method, ret, sync_iter;
- if (is_async) {
- method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_asyncIterator);
- if (JS_IsException(method))
- return method;
- if (JS_IsUndefined(method) || JS_IsNull(method)) {
- method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator);
- if (JS_IsException(method))
- return method;
- sync_iter = JS_GetIterator2(ctx, obj, method);
- JS_FreeValue(ctx, method);
- if (JS_IsException(sync_iter))
- return sync_iter;
- ret = JS_CreateAsyncFromSyncIterator(ctx, sync_iter);
- JS_FreeValue(ctx, sync_iter);
- return ret;
- }
- } else {
- method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator);
- if (JS_IsException(method))
- return method;
- }
- if (!JS_IsFunction(ctx, method)) {
- JS_FreeValue(ctx, method);
- return JS_ThrowTypeError(ctx, "value is not iterable");
- }
- ret = JS_GetIterator2(ctx, obj, method);
- JS_FreeValue(ctx, method);
- return ret;
- }
- /* return *pdone = 2 if the iterator object is not parsed */
- static JSValue JS_IteratorNext2(JSContext *ctx, JSValueConst enum_obj,
- JSValueConst method,
- int argc, JSValueConst *argv, int *pdone)
- {
- JSValue obj;
- /* fast path for the built-in iterators (avoid creating the
- intermediate result object) */
- if (JS_IsObject(method)) {
- JSObject *p = JS_VALUE_GET_OBJ(method);
- if (p->class_id == JS_CLASS_C_FUNCTION &&
- p->u.cfunc.cproto == JS_CFUNC_iterator_next) {
- JSCFunctionType func;
- JSValueConst args[1];
- /* in case the function expects one argument */
- if (argc == 0) {
- args[0] = JS_UNDEFINED;
- argv = args;
- }
- func = p->u.cfunc.c_function;
- return func.iterator_next(ctx, enum_obj, argc, argv,
- pdone, p->u.cfunc.magic);
- }
- }
- obj = JS_Call(ctx, method, enum_obj, argc, argv);
- if (JS_IsException(obj))
- goto fail;
- if (!JS_IsObject(obj)) {
- JS_FreeValue(ctx, obj);
- JS_ThrowTypeError(ctx, "iterator must return an object");
- goto fail;
- }
- *pdone = 2;
- return obj;
- fail:
- *pdone = FALSE;
- return JS_EXCEPTION;
- }
- /* Note: always return JS_UNDEFINED when *pdone = TRUE. */
- static JSValue JS_IteratorNext(JSContext *ctx, JSValueConst enum_obj,
- JSValueConst method,
- int argc, JSValueConst *argv, BOOL *pdone)
- {
- JSValue obj, value, done_val;
- int done;
- obj = JS_IteratorNext2(ctx, enum_obj, method, argc, argv, &done);
- if (JS_IsException(obj))
- goto fail;
- if (likely(done == 0)) {
- *pdone = FALSE;
- return obj;
- } else if (done != 2) {
- JS_FreeValue(ctx, obj);
- *pdone = TRUE;
- return JS_UNDEFINED;
- } else {
- done_val = JS_GetProperty(ctx, obj, JS_ATOM_done);
- if (JS_IsException(done_val))
- goto fail;
- *pdone = JS_ToBoolFree(ctx, done_val);
- value = JS_UNDEFINED;
- if (!*pdone) {
- value = JS_GetProperty(ctx, obj, JS_ATOM_value);
- }
- JS_FreeValue(ctx, obj);
- return value;
- }
- fail:
- JS_FreeValue(ctx, obj);
- *pdone = FALSE;
- return JS_EXCEPTION;
- }
- /* return < 0 in case of exception */
- static int JS_IteratorClose(JSContext *ctx, JSValueConst enum_obj,
- BOOL is_exception_pending)
- {
- JSValue method, ret, ex_obj;
- int res;
- if (is_exception_pending) {
- ex_obj = ctx->rt->current_exception;
- ctx->rt->current_exception = JS_UNINITIALIZED;
- res = -1;
- } else {
- ex_obj = JS_UNDEFINED;
- res = 0;
- }
- method = JS_GetProperty(ctx, enum_obj, JS_ATOM_return);
- if (JS_IsException(method)) {
- res = -1;
- goto done;
- }
- if (JS_IsUndefined(method) || JS_IsNull(method)) {
- goto done;
- }
- ret = JS_CallFree(ctx, method, enum_obj, 0, NULL);
- if (!is_exception_pending) {
- if (JS_IsException(ret)) {
- res = -1;
- } else if (!JS_IsObject(ret)) {
- JS_ThrowTypeErrorNotAnObject(ctx);
- res = -1;
- }
- }
- JS_FreeValue(ctx, ret);
- done:
- if (is_exception_pending) {
- JS_Throw(ctx, ex_obj);
- }
- return res;
- }
- /* obj -> enum_rec (3 slots) */
- static __exception int js_for_of_start(JSContext *ctx, JSValue *sp,
- BOOL is_async)
- {
- JSValue op1, obj, method;
- op1 = sp[-1];
- obj = JS_GetIterator(ctx, op1, is_async);
- if (JS_IsException(obj))
- return -1;
- JS_FreeValue(ctx, op1);
- sp[-1] = obj;
- method = JS_GetProperty(ctx, obj, JS_ATOM_next);
- if (JS_IsException(method))
- return -1;
- sp[0] = method;
- return 0;
- }
- /* enum_rec [objs] -> enum_rec [objs] value done. There are 'offset'
- objs. If 'done' is true or in case of exception, 'enum_rec' is set
- to undefined. If 'done' is true, 'value' is always set to
- undefined. */
- static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset)
- {
- JSValue value = JS_UNDEFINED;
- int done = 1;
- if (likely(!JS_IsUndefined(sp[offset]))) {
- value = JS_IteratorNext(ctx, sp[offset], sp[offset + 1], 0, NULL, &done);
- if (JS_IsException(value))
- done = -1;
- if (done) {
- /* value is JS_UNDEFINED or JS_EXCEPTION */
- /* replace the iteration object with undefined */
- JS_FreeValue(ctx, sp[offset]);
- sp[offset] = JS_UNDEFINED;
- if (done < 0) {
- return -1;
- } else {
- JS_FreeValue(ctx, value);
- value = JS_UNDEFINED;
- }
- }
- }
- sp[0] = value;
- sp[1] = JS_NewBool(ctx, done);
- return 0;
- }
- static __exception int js_for_await_of_next(JSContext *ctx, JSValue *sp)
- {
- JSValue obj, iter, next;
- sp[-1] = JS_UNDEFINED; /* disable the catch offset so that
- exceptions do not close the iterator */
- iter = sp[-3];
- next = sp[-2];
- obj = JS_Call(ctx, next, iter, 0, NULL);
- if (JS_IsException(obj))
- return -1;
- sp[0] = obj;
- return 0;
- }
- static JSValue JS_IteratorGetCompleteValue(JSContext *ctx, JSValueConst obj,
- BOOL *pdone)
- {
- JSValue done_val, value;
- BOOL done;
- done_val = JS_GetProperty(ctx, obj, JS_ATOM_done);
- if (JS_IsException(done_val))
- goto fail;
- done = JS_ToBoolFree(ctx, done_val);
- value = JS_GetProperty(ctx, obj, JS_ATOM_value);
- if (JS_IsException(value))
- goto fail;
- *pdone = done;
- return value;
- fail:
- *pdone = FALSE;
- return JS_EXCEPTION;
- }
- static __exception int js_iterator_get_value_done(JSContext *ctx, JSValue *sp)
- {
- JSValue obj, value;
- BOOL done;
- obj = sp[-1];
- if (!JS_IsObject(obj)) {
- JS_ThrowTypeError(ctx, "iterator must return an object");
- return -1;
- }
- value = JS_IteratorGetCompleteValue(ctx, obj, &done);
- if (JS_IsException(value))
- return -1;
- JS_FreeValue(ctx, obj);
- /* put again the catch offset so that exceptions close the
- iterator */
- sp[-2] = JS_NewCatchOffset(ctx, 0);
- sp[-1] = value;
- sp[0] = JS_NewBool(ctx, done);
- return 0;
- }
- static JSValue js_create_iterator_result(JSContext *ctx,
- JSValue val,
- BOOL done)
- {
- JSValue obj;
- obj = JS_NewObject(ctx);
- if (JS_IsException(obj)) {
- JS_FreeValue(ctx, val);
- return obj;
- }
- if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_value,
- val, JS_PROP_C_W_E) < 0) {
- goto fail;
- }
- if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_done,
- JS_NewBool(ctx, done), JS_PROP_C_W_E) < 0) {
- fail:
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- return obj;
- }
- static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv,
- BOOL *pdone, int magic);
- static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic);
- /* Access an Array's internal JSValue array if available */
- static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj,
- JSValue **arrpp, uint32_t *countp)
- {
- /* Try and handle fast arrays explicitly */
- if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
- JSObject *p = JS_VALUE_GET_OBJ(obj);
- if (p->class_id == JS_CLASS_ARRAY && p->fast_array) {
- *countp = p->u.array.count;
- *arrpp = p->u.array.u.values;
- return TRUE;
- }
- }
- return FALSE;
- }
- static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp)
- {
- JSValue iterator, enumobj, method, value;
- int is_array_iterator;
- JSValue *arrp;
- uint32_t i, count32, pos;
- JSCFunctionType ft;
- if (JS_VALUE_GET_TAG(sp[-2]) != JS_TAG_INT) {
- JS_ThrowInternalError(ctx, "invalid index for append");
- return -1;
- }
- pos = JS_VALUE_GET_INT(sp[-2]);
- /* XXX: further optimisations:
- - use ctx->array_proto_values?
- - check if array_iterator_prototype next method is built-in and
- avoid constructing actual iterator object?
- - build this into js_for_of_start and use in all `for (x of o)` loops
- */
- iterator = JS_GetProperty(ctx, sp[-1], JS_ATOM_Symbol_iterator);
- if (JS_IsException(iterator))
- return -1;
- ft.generic_magic = js_create_array_iterator;
- is_array_iterator = JS_IsCFunction(ctx, iterator, ft.generic,
- JS_ITERATOR_KIND_VALUE);
- JS_FreeValue(ctx, iterator);
- enumobj = JS_GetIterator(ctx, sp[-1], FALSE);
- if (JS_IsException(enumobj))
- return -1;
- method = JS_GetProperty(ctx, enumobj, JS_ATOM_next);
- if (JS_IsException(method)) {
- JS_FreeValue(ctx, enumobj);
- return -1;
- }
- ft.iterator_next = js_array_iterator_next;
- if (is_array_iterator
- && JS_IsCFunction(ctx, method, ft.generic, 0)
- && js_get_fast_array(ctx, sp[-1], &arrp, &count32)) {
- uint32_t len;
- if (js_get_length32(ctx, &len, sp[-1]))
- goto exception;
- /* if len > count32, the elements >= count32 might be read in
- the prototypes and might have side effects */
- if (len != count32)
- goto general_case;
- /* Handle fast arrays explicitly */
- for (i = 0; i < count32; i++) {
- if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++,
- JS_DupValue(ctx, arrp[i]), JS_PROP_C_W_E) < 0)
- goto exception;
- }
- } else {
- general_case:
- for (;;) {
- BOOL done;
- value = JS_IteratorNext(ctx, enumobj, method, 0, NULL, &done);
- if (JS_IsException(value))
- goto exception;
- if (done) {
- /* value is JS_UNDEFINED */
- break;
- }
- if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, value, JS_PROP_C_W_E) < 0)
- goto exception;
- }
- }
- /* Note: could raise an error if too many elements */
- sp[-2] = JS_NewInt32(ctx, pos);
- JS_FreeValue(ctx, enumobj);
- JS_FreeValue(ctx, method);
- return 0;
- exception:
- JS_IteratorClose(ctx, enumobj, TRUE);
- JS_FreeValue(ctx, enumobj);
- JS_FreeValue(ctx, method);
- return -1;
- }
- static __exception int JS_CopyDataProperties(JSContext *ctx,
- JSValueConst target,
- JSValueConst source,
- JSValueConst excluded,
- BOOL setprop)
- {
- JSPropertyEnum *tab_atom;
- JSValue val;
- uint32_t i, tab_atom_count;
- JSObject *p;
- JSObject *pexcl = NULL;
- int ret, gpn_flags;
- JSPropertyDescriptor desc;
- BOOL is_enumerable;
- if (JS_VALUE_GET_TAG(source) != JS_TAG_OBJECT)
- return 0;
- if (JS_VALUE_GET_TAG(excluded) == JS_TAG_OBJECT)
- pexcl = JS_VALUE_GET_OBJ(excluded);
- p = JS_VALUE_GET_OBJ(source);
- gpn_flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK | JS_GPN_ENUM_ONLY;
- if (p->is_exotic) {
- const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
- /* cannot use JS_GPN_ENUM_ONLY with e.g. proxies because it
- introduces a visible change */
- if (em && em->get_own_property_names) {
- gpn_flags &= ~JS_GPN_ENUM_ONLY;
- }
- }
- if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p,
- gpn_flags))
- return -1;
- for (i = 0; i < tab_atom_count; i++) {
- if (pexcl) {
- ret = JS_GetOwnPropertyInternal(ctx, NULL, pexcl, tab_atom[i].atom);
- if (ret) {
- if (ret < 0)
- goto exception;
- continue;
- }
- }
- if (!(gpn_flags & JS_GPN_ENUM_ONLY)) {
- /* test if the property is enumerable */
- ret = JS_GetOwnPropertyInternal(ctx, &desc, p, tab_atom[i].atom);
- if (ret < 0)
- goto exception;
- if (!ret)
- continue;
- is_enumerable = (desc.flags & JS_PROP_ENUMERABLE) != 0;
- js_free_desc(ctx, &desc);
- if (!is_enumerable)
- continue;
- }
- val = JS_GetProperty(ctx, source, tab_atom[i].atom);
- if (JS_IsException(val))
- goto exception;
- if (setprop)
- ret = JS_SetProperty(ctx, target, tab_atom[i].atom, val);
- else
- ret = JS_DefinePropertyValue(ctx, target, tab_atom[i].atom, val,
- JS_PROP_C_W_E);
- if (ret < 0)
- goto exception;
- }
- JS_FreePropertyEnum(ctx, tab_atom, tab_atom_count);
- return 0;
- exception:
- JS_FreePropertyEnum(ctx, tab_atom, tab_atom_count);
- return -1;
- }
- /* only valid inside C functions */
- static JSValueConst JS_GetActiveFunction(JSContext *ctx)
- {
- return ctx->rt->current_stack_frame->cur_func;
- }
- static JSVarRef *js_create_var_ref(JSContext *ctx, BOOL is_lexical)
- {
- JSVarRef *var_ref;
- var_ref = js_malloc(ctx, sizeof(JSVarRef));
- if (!var_ref)
- return NULL;
- js_rc(var_ref)->ref_count = 1;
- if (is_lexical)
- var_ref->value = JS_UNINITIALIZED;
- else
- var_ref->value = JS_UNDEFINED;
- var_ref->pvalue = &var_ref->value;
- var_ref->is_detached = TRUE;
- var_ref->is_lexical = FALSE;
- var_ref->is_const = FALSE;
- add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);
- return var_ref;
- }
- static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx,
- BOOL is_arg)
- {
- JSObject *p;
- JSFunctionBytecode *b;
- JSVarRef *var_ref;
- JSValue *pvalue;
- int var_ref_idx;
- JSBytecodeVarDef *vd;
-
- p = JS_VALUE_GET_OBJ(sf->cur_func);
- b = p->u.func.function_bytecode;
-
- if (is_arg) {
- vd = &b->vardefs[var_idx];
- pvalue = &sf->arg_buf[var_idx];
- } else {
- vd = &b->vardefs[b->arg_count + var_idx];
- pvalue = &sf->var_buf[var_idx];
- }
- assert(vd->is_captured);
- var_ref_idx = vd->var_ref_idx;
- assert(var_ref_idx < b->var_ref_count);
- var_ref = sf->var_refs[var_ref_idx];
- if (var_ref) {
- /* reference to the already created local variable */
- assert(var_ref->pvalue == pvalue);
- js_rc(var_ref)->ref_count++;
- return var_ref;
- }
- /* create a new one */
- var_ref = js_malloc(ctx, sizeof(JSVarRef));
- if (!var_ref)
- return NULL;
- js_rc(var_ref)->ref_count = 1;
- add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);
- var_ref->is_detached = FALSE;
- var_ref->is_lexical = FALSE;
- var_ref->is_const = FALSE;
- var_ref->var_ref_idx = var_ref_idx;
- var_ref->stack_frame = sf;
- sf->var_refs[var_ref_idx] = var_ref;
- if (sf->js_mode & JS_MODE_ASYNC) {
- JSAsyncFunctionState *async_func = container_of(sf, JSAsyncFunctionState, frame);
- /* The stack frame is detached and may be destroyed at any
- time so its reference count must be increased. Calling
- close_var_refs() when destroying the stack frame is not
- possible because it would change the graph between the GC
- objects. Another solution could be to temporarily detach
- the JSVarRef of async functions during the GC. It would
- have the advantage of allowing the release of unused stack
- frames in a cycle. */
- js_rc(async_func)->ref_count++;
- }
- var_ref->pvalue = pvalue;
- return var_ref;
- }
- static void js_global_object_finalizer(JSRuntime *rt, JSValue obj)
- {
- JSObject *p = JS_VALUE_GET_OBJ(obj);
- JS_FreeValueRT(rt, p->u.global_object.uninitialized_vars);
- }
- static void js_global_object_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- JS_MarkValue(rt, p->u.global_object.uninitialized_vars, mark_func);
- }
- static JSVarRef *js_global_object_get_uninitialized_var(JSContext *ctx, JSObject *p1,
- JSAtom atom)
- {
- JSObject *p = JS_VALUE_GET_OBJ(p1->u.global_object.uninitialized_vars);
- JSShapeProperty *prs;
- JSProperty *pr;
- JSVarRef *var_ref;
-
- prs = find_own_property(&pr, p, atom);
- if (prs) {
- assert((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF);
- var_ref = pr->u.var_ref;
- js_rc(var_ref)->ref_count++;
- return var_ref;
- }
- var_ref = js_create_var_ref(ctx, TRUE);
- if (!var_ref)
- return NULL;
- pr = add_property(ctx, p, atom, JS_PROP_C_W_E | JS_PROP_VARREF);
- if (unlikely(!pr)) {
- free_var_ref(ctx->rt, var_ref);
- return NULL;
- }
- pr->u.var_ref = var_ref;
- js_rc(var_ref)->ref_count++;
- return var_ref;
- }
- /* return a new variable reference. Get it from the uninitialized
- variables if it is present. Return NULL in case of memory error. */
- static JSVarRef *js_global_object_find_uninitialized_var(JSContext *ctx, JSObject *p,
- JSAtom atom, BOOL is_lexical)
- {
- JSObject *p1;
- JSShapeProperty *prs;
- JSProperty *pr;
- JSVarRef *var_ref;
-
- p1 = JS_VALUE_GET_OBJ(p->u.global_object.uninitialized_vars);
- prs = find_own_property(&pr, p1, atom);
- if (prs) {
- assert((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF);
- var_ref = pr->u.var_ref;
- js_rc(var_ref)->ref_count++;
- delete_property(ctx, p1, atom);
- if (!is_lexical)
- var_ref->value = JS_UNDEFINED;
- } else {
- var_ref = js_create_var_ref(ctx, is_lexical);
- if (!var_ref)
- return NULL;
- }
- return var_ref;
- }
- static JSVarRef *js_closure_define_global_var(JSContext *ctx, JSClosureVar *cv,
- BOOL is_direct_or_indirect_eval)
- {
- JSObject *p, *p1;
- JSShapeProperty *prs;
- int flags;
- JSProperty *pr;
- JSVarRef *var_ref;
-
- if (cv->is_lexical) {
- p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
- flags = JS_PROP_ENUMERABLE | JS_PROP_CONFIGURABLE;
- if (!cv->is_const)
- flags |= JS_PROP_WRITABLE;
- prs = find_own_property(&pr, p, cv->var_name);
- if (prs) {
- assert((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF);
- var_ref = pr->u.var_ref;
- js_rc(var_ref)->ref_count++;
- return var_ref;
- }
- /* if there is a corresponding global variable, reuse its
- reference and create a new one for the global variable */
- p1 = JS_VALUE_GET_OBJ(ctx->global_obj);
- prs = find_own_property(&pr, p1, cv->var_name);
- if (prs && (prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
- JSVarRef *var_ref1;
- var_ref1 = js_create_var_ref(ctx, FALSE);
- if (!var_ref1)
- return NULL;
- var_ref = pr->u.var_ref;
- var_ref1->value = var_ref->value;
- var_ref->value = JS_UNINITIALIZED;
- pr->u.var_ref = var_ref1;
- goto add_var_ref;
- }
- } else {
- p = JS_VALUE_GET_OBJ(ctx->global_obj);
- flags = JS_PROP_ENUMERABLE | JS_PROP_WRITABLE;
- if (is_direct_or_indirect_eval)
- flags |= JS_PROP_CONFIGURABLE;
- retry:
- prs = find_own_property(&pr, p, cv->var_name);
- if (prs) {
- if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT)) {
- if (JS_AutoInitProperty(ctx, p, cv->var_name, pr, prs))
- return NULL;
- goto retry;
- } else if ((prs->flags & JS_PROP_TMASK) != JS_PROP_VARREF) {
- var_ref = js_global_object_get_uninitialized_var(ctx, p, cv->var_name);
- if (!var_ref)
- return NULL;
- } else {
- var_ref = pr->u.var_ref;
- js_rc(var_ref)->ref_count++;
- }
- if (cv->var_kind == JS_VAR_GLOBAL_FUNCTION_DECL &&
- (prs->flags & JS_PROP_CONFIGURABLE)) {
- /* update the property flags if possible when
- declaring a global function */
- if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
- free_property(ctx->rt, pr, prs->flags);
- prs->flags = flags | JS_PROP_VARREF;
- pr->u.var_ref = var_ref;
- js_rc(var_ref)->ref_count++;
- } else {
- assert((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF);
- prs->flags = (prs->flags & ~JS_PROP_C_W_E) | flags;
- }
- var_ref->is_const = FALSE;
- }
- return var_ref;
- }
-
- if (!p->extensible) {
- return js_global_object_get_uninitialized_var(ctx, p, cv->var_name);
- }
- }
-
- /* if there is a corresponding uninitialized variable, use it */
- p1 = JS_VALUE_GET_OBJ(ctx->global_obj);
- var_ref = js_global_object_find_uninitialized_var(ctx, p1, cv->var_name, cv->is_lexical);
- if (!var_ref)
- return NULL;
- add_var_ref:
- if (cv->is_lexical) {
- var_ref->is_lexical = TRUE;
- var_ref->is_const = cv->is_const;
- }
- pr = add_property(ctx, p, cv->var_name, flags | JS_PROP_VARREF);
- if (unlikely(!pr)) {
- free_var_ref(ctx->rt, var_ref);
- return NULL;
- }
- pr->u.var_ref = var_ref;
- js_rc(var_ref)->ref_count++;
- return var_ref;
- }
- static JSVarRef *js_closure_global_var(JSContext *ctx, JSClosureVar *cv)
- {
- JSObject *p;
- JSShapeProperty *prs;
- JSProperty *pr;
- JSVarRef *var_ref;
-
- p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
- prs = find_own_property(&pr, p, cv->var_name);
- if (prs) {
- assert((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF);
- var_ref = pr->u.var_ref;
- js_rc(var_ref)->ref_count++;
- return var_ref;
- }
- p = JS_VALUE_GET_OBJ(ctx->global_obj);
- redo:
- prs = find_own_property(&pr, p, cv->var_name);
- if (prs) {
- if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT)) {
- /* Instantiate property and retry */
- if (JS_AutoInitProperty(ctx, p, cv->var_name, pr, prs))
- return NULL;
- goto redo;
- }
- if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
- var_ref = pr->u.var_ref;
- js_rc(var_ref)->ref_count++;
- return var_ref;
- }
- }
- return js_global_object_get_uninitialized_var(ctx, p, cv->var_name);
- }
- static JSValue js_closure2(JSContext *ctx, JSValue func_obj,
- JSFunctionBytecode *b,
- JSVarRef **cur_var_refs,
- JSStackFrame *sf,
- BOOL is_eval, JSModuleDef *m)
- {
- JSObject *p;
- JSVarRef **var_refs;
- int i;
- p = JS_VALUE_GET_OBJ(func_obj);
- p->u.func.function_bytecode = b;
- p->u.func.home_object = NULL;
- p->u.func.var_refs = NULL;
- if (b->closure_var_count) {
- var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count);
- if (!var_refs)
- goto fail;
- p->u.func.var_refs = var_refs;
- if (is_eval) {
- /* first pass to check the global variable definitions */
- for(i = 0; i < b->closure_var_count; i++) {
- JSClosureVar *cv = &b->closure_var[i];
- if (cv->closure_type == JS_CLOSURE_GLOBAL_DECL) {
- int flags;
- flags = 0;
- if (cv->is_lexical)
- flags |= DEFINE_GLOBAL_LEX_VAR;
- if (cv->var_kind == JS_VAR_GLOBAL_FUNCTION_DECL)
- flags |= DEFINE_GLOBAL_FUNC_VAR;
- if (JS_CheckDefineGlobalVar(ctx, cv->var_name, flags))
- goto fail;
- }
- }
- }
- for(i = 0; i < b->closure_var_count; i++) {
- JSClosureVar *cv = &b->closure_var[i];
- JSVarRef *var_ref;
- switch(cv->closure_type) {
- case JS_CLOSURE_MODULE_IMPORT:
- /* imported from other modules */
- continue;
- case JS_CLOSURE_MODULE_DECL:
- var_ref = js_create_var_ref(ctx, cv->is_lexical);
- break;
- case JS_CLOSURE_GLOBAL_DECL:
- var_ref = js_closure_define_global_var(ctx, cv, b->is_direct_or_indirect_eval);
- break;
- case JS_CLOSURE_GLOBAL:
- var_ref = js_closure_global_var(ctx, cv);
- break;
- case JS_CLOSURE_LOCAL:
- /* reuse the existing variable reference if it already exists */
- var_ref = get_var_ref(ctx, sf, cv->var_idx, FALSE);
- break;
- case JS_CLOSURE_ARG:
- /* reuse the existing variable reference if it already exists */
- var_ref = get_var_ref(ctx, sf, cv->var_idx, TRUE);
- break;
- case JS_CLOSURE_REF:
- case JS_CLOSURE_GLOBAL_REF:
- var_ref = cur_var_refs[cv->var_idx];
- js_rc(var_ref)->ref_count++;
- break;
- default:
- abort();
- }
- if (!var_ref)
- goto fail;
- var_refs[i] = var_ref;
- }
- }
- return func_obj;
- fail:
- /* bfunc is freed when func_obj is freed */
- JS_FreeValue(ctx, func_obj);
- return JS_EXCEPTION;
- }
- static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque)
- {
- JSValue obj, this_val;
- int ret;
- this_val = JS_MKPTR(JS_TAG_OBJECT, p);
- obj = JS_NewObject(ctx);
- if (JS_IsException(obj))
- return JS_EXCEPTION;
- set_cycle_flag(ctx, obj);
- set_cycle_flag(ctx, this_val);
- ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_constructor,
- JS_DupValue(ctx, this_val),
- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
- if (ret < 0) {
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- return obj;
- }
- static const uint16_t func_kind_to_class_id[] = {
- [JS_FUNC_NORMAL] = JS_CLASS_BYTECODE_FUNCTION,
- [JS_FUNC_GENERATOR] = JS_CLASS_GENERATOR_FUNCTION,
- [JS_FUNC_ASYNC] = JS_CLASS_ASYNC_FUNCTION,
- [JS_FUNC_ASYNC_GENERATOR] = JS_CLASS_ASYNC_GENERATOR_FUNCTION,
- };
- static JSValue js_closure(JSContext *ctx, JSValue bfunc,
- JSVarRef **cur_var_refs,
- JSStackFrame *sf, BOOL is_eval)
- {
- JSFunctionBytecode *b;
- JSValue func_obj;
- JSAtom name_atom;
- b = JS_VALUE_GET_PTR(bfunc);
- func_obj = JS_NewObjectClass(ctx, func_kind_to_class_id[b->func_kind]);
- if (JS_IsException(func_obj)) {
- JS_FreeValue(ctx, bfunc);
- return JS_EXCEPTION;
- }
- func_obj = js_closure2(ctx, func_obj, b, cur_var_refs, sf, is_eval, NULL);
- if (JS_IsException(func_obj)) {
- /* bfunc has been freed */
- goto fail;
- }
- name_atom = b->func_name;
- if (name_atom == JS_ATOM_NULL)
- name_atom = JS_ATOM_empty_string;
- js_function_set_properties(ctx, func_obj, name_atom,
- b->defined_arg_count);
- if (b->func_kind & JS_FUNC_GENERATOR) {
- JSValue proto;
- int proto_class_id;
- /* generators have a prototype field which is used as
- prototype for the generator object */
- if (b->func_kind == JS_FUNC_ASYNC_GENERATOR)
- proto_class_id = JS_CLASS_ASYNC_GENERATOR;
- else
- proto_class_id = JS_CLASS_GENERATOR;
- proto = JS_NewObjectProto(ctx, ctx->class_proto[proto_class_id]);
- if (JS_IsException(proto))
- goto fail;
- JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype, proto,
- JS_PROP_WRITABLE);
- } else if (b->has_prototype) {
- /* add the 'prototype' property: delay instantiation to avoid
- creating cycles for every javascript function. The prototype
- object is created on the fly when first accessed */
- JS_SetConstructorBit(ctx, func_obj, TRUE);
- JS_DefineAutoInitProperty(ctx, func_obj, JS_ATOM_prototype,
- JS_AUTOINIT_ID_PROTOTYPE, NULL,
- JS_PROP_WRITABLE);
- }
- return func_obj;
- fail:
- /* bfunc is freed when func_obj is freed */
- JS_FreeValue(ctx, func_obj);
- return JS_EXCEPTION;
- }
- #define JS_DEFINE_CLASS_HAS_HERITAGE (1 << 0)
- static int js_op_define_class(JSContext *ctx, JSValue *sp,
- JSAtom class_name, int class_flags,
- JSVarRef **cur_var_refs,
- JSStackFrame *sf, BOOL is_computed_name)
- {
- JSValue bfunc, parent_class, proto = JS_UNDEFINED;
- JSValue ctor = JS_UNDEFINED, parent_proto = JS_UNDEFINED;
- JSFunctionBytecode *b;
- parent_class = sp[-2];
- bfunc = sp[-1];
- if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE) {
- if (JS_IsNull(parent_class)) {
- parent_proto = JS_NULL;
- parent_class = JS_DupValue(ctx, ctx->function_proto);
- } else {
- if (!JS_IsConstructor(ctx, parent_class)) {
- JS_ThrowTypeError(ctx, "parent class must be constructor");
- goto fail;
- }
- parent_proto = JS_GetProperty(ctx, parent_class, JS_ATOM_prototype);
- if (JS_IsException(parent_proto))
- goto fail;
- if (!JS_IsNull(parent_proto) && !JS_IsObject(parent_proto)) {
- JS_ThrowTypeError(ctx, "parent prototype must be an object or null");
- goto fail;
- }
- }
- } else {
- /* parent_class is JS_UNDEFINED in this case */
- parent_proto = JS_DupValue(ctx, ctx->class_proto[JS_CLASS_OBJECT]);
- parent_class = JS_DupValue(ctx, ctx->function_proto);
- }
- proto = JS_NewObjectProto(ctx, parent_proto);
- if (JS_IsException(proto))
- goto fail;
- b = JS_VALUE_GET_PTR(bfunc);
- assert(b->func_kind == JS_FUNC_NORMAL);
- ctor = JS_NewObjectProtoClass(ctx, parent_class,
- JS_CLASS_BYTECODE_FUNCTION);
- if (JS_IsException(ctor))
- goto fail;
- ctor = js_closure2(ctx, ctor, b, cur_var_refs, sf, FALSE, NULL);
- bfunc = JS_UNDEFINED;
- if (JS_IsException(ctor))
- goto fail;
- js_method_set_home_object(ctx, ctor, proto);
- JS_SetConstructorBit(ctx, ctor, TRUE);
- JS_DefinePropertyValue(ctx, ctor, JS_ATOM_length,
- JS_NewInt32(ctx, b->defined_arg_count),
- JS_PROP_CONFIGURABLE);
- if (is_computed_name) {
- if (JS_DefineObjectNameComputed(ctx, ctor, sp[-3],
- JS_PROP_CONFIGURABLE) < 0)
- goto fail;
- } else {
- if (JS_DefineObjectName(ctx, ctor, class_name, JS_PROP_CONFIGURABLE) < 0)
- goto fail;
- }
- /* the constructor property must be first. It can be overriden by
- computed property names */
- if (JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor,
- JS_DupValue(ctx, ctor),
- JS_PROP_CONFIGURABLE |
- JS_PROP_WRITABLE | JS_PROP_THROW) < 0)
- goto fail;
- /* set the prototype property */
- if (JS_DefinePropertyValue(ctx, ctor, JS_ATOM_prototype,
- JS_DupValue(ctx, proto), JS_PROP_THROW) < 0)
- goto fail;
- set_cycle_flag(ctx, ctor);
- set_cycle_flag(ctx, proto);
- JS_FreeValue(ctx, parent_proto);
- JS_FreeValue(ctx, parent_class);
- sp[-2] = ctor;
- sp[-1] = proto;
- return 0;
- fail:
- JS_FreeValue(ctx, parent_class);
- JS_FreeValue(ctx, parent_proto);
- JS_FreeValue(ctx, bfunc);
- JS_FreeValue(ctx, proto);
- JS_FreeValue(ctx, ctor);
- sp[-2] = JS_UNDEFINED;
- sp[-1] = JS_UNDEFINED;
- return -1;
- }
- static void close_var_ref(JSRuntime *rt, JSStackFrame *sf, JSVarRef *var_ref)
- {
- if (sf->js_mode & JS_MODE_ASYNC) {
- JSAsyncFunctionState *async_func = container_of(sf, JSAsyncFunctionState, frame);
- async_func_free(rt, async_func);
- }
- var_ref->value = JS_DupValueRT(rt, *var_ref->pvalue);
- var_ref->pvalue = &var_ref->value;
- /* the reference is no longer to a local variable */
- var_ref->is_detached = TRUE;
- }
- static void close_var_refs(JSRuntime *rt, JSFunctionBytecode *b, JSStackFrame *sf)
- {
- JSVarRef *var_ref;
- int i;
- for(i = 0; i < b->var_ref_count; i++) {
- var_ref = sf->var_refs[i];
- if (var_ref)
- close_var_ref(rt, sf, var_ref);
- }
- }
- static void close_lexical_var(JSContext *ctx, JSFunctionBytecode *b,
- JSStackFrame *sf, int var_idx)
- {
- JSVarRef *var_ref;
- int var_ref_idx;
-
- var_ref_idx = b->vardefs[b->arg_count + var_idx].var_ref_idx;
- var_ref = sf->var_refs[var_ref_idx];
- if (var_ref) {
- close_var_ref(ctx->rt, sf, var_ref);
- sf->var_refs[var_ref_idx] = NULL;
- }
- }
- #define JS_CALL_FLAG_COPY_ARGV (1 << 1)
- #define JS_CALL_FLAG_GENERATOR (1 << 2)
- static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj,
- JSValueConst this_obj,
- int argc, JSValueConst *argv, int flags)
- {
- JSRuntime *rt = ctx->rt;
- JSCFunctionType func;
- JSObject *p;
- JSStackFrame sf_s, *sf = &sf_s, *prev_sf;
- JSValue ret_val;
- JSValueConst *arg_buf;
- int arg_count, i;
- JSCFunctionEnum cproto;
- p = JS_VALUE_GET_OBJ(func_obj);
- cproto = p->u.cfunc.cproto;
- arg_count = p->u.cfunc.length;
- /* better to always check stack overflow */
- if (js_check_stack_overflow(rt, sizeof(arg_buf[0]) * arg_count))
- return JS_ThrowStackOverflow(ctx);
- prev_sf = rt->current_stack_frame;
- sf->prev_frame = prev_sf;
- rt->current_stack_frame = sf;
- ctx = p->u.cfunc.realm; /* change the current realm */
- sf->js_mode = 0;
- sf->cur_func = (JSValue)func_obj;
- sf->arg_count = argc;
- arg_buf = argv;
- if (unlikely(argc < arg_count)) {
- /* ensure that at least argc_count arguments are readable */
- arg_buf = alloca(sizeof(arg_buf[0]) * arg_count);
- for(i = 0; i < argc; i++)
- arg_buf[i] = argv[i];
- for(i = argc; i < arg_count; i++)
- arg_buf[i] = JS_UNDEFINED;
- sf->arg_count = arg_count;
- }
- sf->arg_buf = (JSValue*)arg_buf;
- func = p->u.cfunc.c_function;
- switch(cproto) {
- case JS_CFUNC_constructor:
- case JS_CFUNC_constructor_or_func:
- if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) {
- if (cproto == JS_CFUNC_constructor) {
- not_a_constructor:
- ret_val = JS_ThrowTypeError(ctx, "must be called with new");
- break;
- } else {
- this_obj = JS_UNDEFINED;
- }
- }
- /* here this_obj is new_target */
- /* fall thru */
- case JS_CFUNC_generic:
- ret_val = func.generic(ctx, this_obj, argc, arg_buf);
- break;
- case JS_CFUNC_constructor_magic:
- case JS_CFUNC_constructor_or_func_magic:
- if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) {
- if (cproto == JS_CFUNC_constructor_magic) {
- goto not_a_constructor;
- } else {
- this_obj = JS_UNDEFINED;
- }
- }
- /* fall thru */
- case JS_CFUNC_generic_magic:
- ret_val = func.generic_magic(ctx, this_obj, argc, arg_buf,
- p->u.cfunc.magic);
- break;
- case JS_CFUNC_getter:
- ret_val = func.getter(ctx, this_obj);
- break;
- case JS_CFUNC_setter:
- ret_val = func.setter(ctx, this_obj, arg_buf[0]);
- break;
- case JS_CFUNC_getter_magic:
- ret_val = func.getter_magic(ctx, this_obj, p->u.cfunc.magic);
- break;
- case JS_CFUNC_setter_magic:
- ret_val = func.setter_magic(ctx, this_obj, arg_buf[0], p->u.cfunc.magic);
- break;
- case JS_CFUNC_f_f:
- {
- double d1;
- if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) {
- ret_val = JS_EXCEPTION;
- break;
- }
- ret_val = JS_NewFloat64(ctx, func.f_f(d1));
- }
- break;
- case JS_CFUNC_f_f_f:
- {
- double d1, d2;
- if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) {
- ret_val = JS_EXCEPTION;
- break;
- }
- if (unlikely(JS_ToFloat64(ctx, &d2, arg_buf[1]))) {
- ret_val = JS_EXCEPTION;
- break;
- }
- ret_val = JS_NewFloat64(ctx, func.f_f_f(d1, d2));
- }
- break;
- case JS_CFUNC_iterator_next:
- {
- int done;
- ret_val = func.iterator_next(ctx, this_obj, argc, arg_buf,
- &done, p->u.cfunc.magic);
- if (!JS_IsException(ret_val) && done != 2) {
- ret_val = js_create_iterator_result(ctx, ret_val, done);
- }
- }
- break;
- default:
- abort();
- }
- rt->current_stack_frame = sf->prev_frame;
- return ret_val;
- }
- static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj,
- JSValueConst this_obj,
- int argc, JSValueConst *argv, int flags)
- {
- JSObject *p;
- JSBoundFunction *bf;
- JSValueConst *arg_buf, new_target;
- int arg_count, i;
- p = JS_VALUE_GET_OBJ(func_obj);
- bf = p->u.bound_function;
- arg_count = bf->argc + argc;
- if (js_check_stack_overflow(ctx->rt, sizeof(JSValue) * arg_count))
- return JS_ThrowStackOverflow(ctx);
- arg_buf = alloca(sizeof(JSValue) * arg_count);
- for(i = 0; i < bf->argc; i++) {
- arg_buf[i] = bf->argv[i];
- }
- for(i = 0; i < argc; i++) {
- arg_buf[bf->argc + i] = argv[i];
- }
- if (flags & JS_CALL_FLAG_CONSTRUCTOR) {
- new_target = this_obj;
- if (js_same_value(ctx, func_obj, new_target))
- new_target = bf->func_obj;
- return JS_CallConstructor2(ctx, bf->func_obj, new_target,
- arg_count, arg_buf);
- } else {
- return JS_Call(ctx, bf->func_obj, bf->this_val,
- arg_count, arg_buf);
- }
- }
- /* argument of OP_special_object */
- typedef enum {
- OP_SPECIAL_OBJECT_ARGUMENTS,
- OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS,
- OP_SPECIAL_OBJECT_THIS_FUNC,
- OP_SPECIAL_OBJECT_NEW_TARGET,
- OP_SPECIAL_OBJECT_HOME_OBJECT,
- OP_SPECIAL_OBJECT_VAR_OBJECT,
- OP_SPECIAL_OBJECT_IMPORT_META,
- } OPSpecialObjectEnum;
- #define FUNC_RET_AWAIT 0
- #define FUNC_RET_YIELD 1
- #define FUNC_RET_YIELD_STAR 2
- #define FUNC_RET_INITIAL_YIELD 3
- #ifdef OPCODE_ASM_LABEL
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wunused-label"
- #endif
- /* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */
- static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
- JSValueConst this_obj, JSValueConst new_target,
- int argc, JSValue *argv, int flags)
- {
- JSRuntime *rt = caller_ctx->rt;
- JSContext *ctx;
- JSObject *p;
- JSFunctionBytecode *b;
- JSStackFrame sf_s, *sf = &sf_s;
- const uint8_t *pc;
- int opcode, arg_allocated_size, i;
- JSValue *local_buf, *stack_buf, *var_buf, *arg_buf, *sp, ret_val, *pval;
- JSVarRef **var_refs;
- size_t alloca_size;
- #if !DIRECT_DISPATCH
- #define SWITCH(pc) switch (opcode = *pc++)
- #define CASE(op) case op
- #define DEFAULT default
- #define BREAK break
- #else
- static const void * const dispatch_table[256] = {
- #define DEF(id, size, n_pop, n_push, f) && case_OP_ ## id,
- #if SHORT_OPCODES
- #define def(id, size, n_pop, n_push, f)
- #else
- #define def(id, size, n_pop, n_push, f) && case_default,
- #endif
- #include "quickjs-opcode.h"
- [ OP_COUNT ... 255 ] = &&case_default
- };
- #define SWITCH(pc) goto *dispatch_table[opcode = *pc++];
- #ifdef OPCODE_ASM_LABEL
- #define CASE(op) case_ ## op: asm volatile("label_" #op ":\n.globl label_" #op); dummy_case_ ## op
- #else
- #define CASE(op) case_ ## op
- #endif
- #define DEFAULT case_default
- #define BREAK SWITCH(pc)
- #endif
- if (js_poll_interrupts(caller_ctx))
- return JS_EXCEPTION;
- if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) {
- if (flags & JS_CALL_FLAG_GENERATOR) {
- JSAsyncFunctionState *s = JS_VALUE_GET_PTR(func_obj);
- /* func_obj get contains a pointer to JSFuncAsyncState */
- /* the stack frame is already allocated */
- sf = &s->frame;
- p = JS_VALUE_GET_OBJ(sf->cur_func);
- b = p->u.func.function_bytecode;
- ctx = b->realm;
- var_refs = p->u.func.var_refs;
- local_buf = arg_buf = sf->arg_buf;
- var_buf = sf->var_buf;
- stack_buf = sf->var_buf + b->var_count;
- sp = sf->cur_sp;
- sf->cur_sp = NULL; /* cur_sp is NULL if the function is running */
- pc = sf->cur_pc;
- sf->prev_frame = rt->current_stack_frame;
- rt->current_stack_frame = sf;
- if (s->throw_flag)
- goto exception;
- else
- goto restart;
- } else {
- goto not_a_function;
- }
- }
- p = JS_VALUE_GET_OBJ(func_obj);
- if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) {
- JSClassCall *call_func;
- call_func = rt->class_array[p->class_id].call;
- if (!call_func) {
- not_a_function:
- return JS_ThrowTypeError(caller_ctx, "not a function");
- }
- return call_func(caller_ctx, func_obj, this_obj, argc,
- (JSValueConst *)argv, flags);
- }
- b = p->u.func.function_bytecode;
- if (unlikely(argc < b->arg_count || (flags & JS_CALL_FLAG_COPY_ARGV))) {
- arg_allocated_size = b->arg_count;
- } else {
- arg_allocated_size = 0;
- }
- alloca_size = sizeof(JSValue) * (arg_allocated_size + b->var_count +
- b->stack_size) +
- sizeof(JSVarRef *) * b->var_ref_count;
- if (js_check_stack_overflow(rt, alloca_size))
- return JS_ThrowStackOverflow(caller_ctx);
- sf->js_mode = b->js_mode;
- arg_buf = argv;
- sf->arg_count = argc;
- sf->cur_func = (JSValue)func_obj;
- var_refs = p->u.func.var_refs;
- local_buf = alloca(alloca_size);
- if (unlikely(arg_allocated_size)) {
- int n = min_int(argc, b->arg_count);
- arg_buf = local_buf;
- for(i = 0; i < n; i++)
- arg_buf[i] = JS_DupValue(caller_ctx, argv[i]);
- for(; i < b->arg_count; i++)
- arg_buf[i] = JS_UNDEFINED;
- sf->arg_count = b->arg_count;
- }
- var_buf = local_buf + arg_allocated_size;
- sf->var_buf = var_buf;
- sf->arg_buf = arg_buf;
- for(i = 0; i < b->var_count; i++)
- var_buf[i] = JS_UNDEFINED;
- stack_buf = var_buf + b->var_count;
- sf->var_refs = (JSVarRef **)(stack_buf + b->stack_size);
- for(i = 0; i < b->var_ref_count; i++)
- sf->var_refs[i] = NULL;
- sp = stack_buf;
- pc = b->byte_code_buf;
- sf->prev_frame = rt->current_stack_frame;
- rt->current_stack_frame = sf;
- ctx = b->realm; /* set the current realm */
- restart:
- for(;;) {
- int call_argc;
- JSValue *call_argv;
- SWITCH(pc) {
- CASE(OP_push_i32):
- *sp++ = JS_NewInt32(ctx, get_u32(pc));
- pc += 4;
- BREAK;
- CASE(OP_push_bigint_i32):
- *sp++ = __JS_NewShortBigInt(ctx, (int)get_u32(pc));
- pc += 4;
- BREAK;
- CASE(OP_push_const):
- *sp++ = JS_DupValue(ctx, b->cpool[get_u32(pc)]);
- pc += 4;
- BREAK;
- #if SHORT_OPCODES
- CASE(OP_push_minus1):
- CASE(OP_push_0):
- CASE(OP_push_1):
- CASE(OP_push_2):
- CASE(OP_push_3):
- CASE(OP_push_4):
- CASE(OP_push_5):
- CASE(OP_push_6):
- CASE(OP_push_7):
- *sp++ = JS_NewInt32(ctx, opcode - OP_push_0);
- BREAK;
- CASE(OP_push_i8):
- *sp++ = JS_NewInt32(ctx, get_i8(pc));
- pc += 1;
- BREAK;
- CASE(OP_push_i16):
- *sp++ = JS_NewInt32(ctx, get_i16(pc));
- pc += 2;
- BREAK;
- CASE(OP_push_const8):
- *sp++ = JS_DupValue(ctx, b->cpool[*pc++]);
- BREAK;
- CASE(OP_fclosure8):
- *sp++ = js_closure(ctx, JS_DupValue(ctx, b->cpool[*pc++]), var_refs, sf, FALSE);
- if (unlikely(JS_IsException(sp[-1])))
- goto exception;
- BREAK;
- CASE(OP_push_empty_string):
- *sp++ = JS_AtomToString(ctx, JS_ATOM_empty_string);
- BREAK;
- #endif
- CASE(OP_push_atom_value):
- *sp++ = JS_AtomToValue(ctx, get_u32(pc));
- pc += 4;
- BREAK;
- CASE(OP_undefined):
- *sp++ = JS_UNDEFINED;
- BREAK;
- CASE(OP_null):
- *sp++ = JS_NULL;
- BREAK;
- CASE(OP_push_this):
- /* OP_push_this is only called at the start of a function */
- {
- JSValue val;
- if (!(b->js_mode & JS_MODE_STRICT)) {
- uint32_t tag = JS_VALUE_GET_TAG(this_obj);
- if (likely(tag == JS_TAG_OBJECT))
- goto normal_this;
- if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) {
- val = JS_DupValue(ctx, ctx->global_obj);
- } else {
- val = JS_ToObject(ctx, this_obj);
- if (JS_IsException(val))
- goto exception;
- }
- } else {
- normal_this:
- val = JS_DupValue(ctx, this_obj);
- }
- *sp++ = val;
- }
- BREAK;
- CASE(OP_push_false):
- *sp++ = JS_FALSE;
- BREAK;
- CASE(OP_push_true):
- *sp++ = JS_TRUE;
- BREAK;
- CASE(OP_object):
- *sp++ = JS_NewObject(ctx);
- if (unlikely(JS_IsException(sp[-1])))
- goto exception;
- BREAK;
- CASE(OP_special_object):
- {
- int arg = *pc++;
- switch(arg) {
- case OP_SPECIAL_OBJECT_ARGUMENTS:
- *sp++ = js_build_arguments(ctx, argc, (JSValueConst *)argv);
- if (unlikely(JS_IsException(sp[-1])))
- goto exception;
- break;
- case OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS:
- *sp++ = js_build_mapped_arguments(ctx, argc, (JSValueConst *)argv,
- sf, min_int(argc, b->arg_count));
- if (unlikely(JS_IsException(sp[-1])))
- goto exception;
- break;
- case OP_SPECIAL_OBJECT_THIS_FUNC:
- *sp++ = JS_DupValue(ctx, sf->cur_func);
- break;
- case OP_SPECIAL_OBJECT_NEW_TARGET:
- *sp++ = JS_DupValue(ctx, new_target);
- break;
- case OP_SPECIAL_OBJECT_HOME_OBJECT:
- {
- JSObject *p1;
- p1 = p->u.func.home_object;
- if (unlikely(!p1))
- *sp++ = JS_UNDEFINED;
- else
- *sp++ = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));
- }
- break;
- case OP_SPECIAL_OBJECT_VAR_OBJECT:
- *sp++ = JS_NewObjectProto(ctx, JS_NULL);
- if (unlikely(JS_IsException(sp[-1])))
- goto exception;
- break;
- case OP_SPECIAL_OBJECT_IMPORT_META:
- *sp++ = js_import_meta(ctx);
- if (unlikely(JS_IsException(sp[-1])))
- goto exception;
- break;
- default:
- abort();
- }
- }
- BREAK;
- CASE(OP_rest):
- {
- int first = get_u16(pc);
- pc += 2;
- first = min_int(first, argc);
- *sp++ = js_create_array(ctx, argc - first, (JSValueConst *)(argv + first));
- if (unlikely(JS_IsException(sp[-1])))
- goto exception;
- }
- BREAK;
- CASE(OP_drop):
- JS_FreeValue(ctx, sp[-1]);
- sp--;
- BREAK;
- CASE(OP_nip):
- JS_FreeValue(ctx, sp[-2]);
- sp[-2] = sp[-1];
- sp--;
- BREAK;
- CASE(OP_nip1): /* a b c -> b c */
- JS_FreeValue(ctx, sp[-3]);
- sp[-3] = sp[-2];
- sp[-2] = sp[-1];
- sp--;
- BREAK;
- CASE(OP_dup):
- sp[0] = JS_DupValue(ctx, sp[-1]);
- sp++;
- BREAK;
- CASE(OP_dup2): /* a b -> a b a b */
- sp[0] = JS_DupValue(ctx, sp[-2]);
- sp[1] = JS_DupValue(ctx, sp[-1]);
- sp += 2;
- BREAK;
- CASE(OP_dup3): /* a b c -> a b c a b c */
- sp[0] = JS_DupValue(ctx, sp[-3]);
- sp[1] = JS_DupValue(ctx, sp[-2]);
- sp[2] = JS_DupValue(ctx, sp[-1]);
- sp += 3;
- BREAK;
- CASE(OP_dup1): /* a b -> a a b */
- sp[0] = sp[-1];
- sp[-1] = JS_DupValue(ctx, sp[-2]);
- sp++;
- BREAK;
- CASE(OP_insert2): /* obj a -> a obj a (dup_x1) */
- sp[0] = sp[-1];
- sp[-1] = sp[-2];
- sp[-2] = JS_DupValue(ctx, sp[0]);
- sp++;
- BREAK;
- CASE(OP_insert3): /* obj prop a -> a obj prop a (dup_x2) */
- sp[0] = sp[-1];
- sp[-1] = sp[-2];
- sp[-2] = sp[-3];
- sp[-3] = JS_DupValue(ctx, sp[0]);
- sp++;
- BREAK;
- CASE(OP_insert4): /* this obj prop a -> a this obj prop a */
- sp[0] = sp[-1];
- sp[-1] = sp[-2];
- sp[-2] = sp[-3];
- sp[-3] = sp[-4];
- sp[-4] = JS_DupValue(ctx, sp[0]);
- sp++;
- BREAK;
- CASE(OP_perm3): /* obj a b -> a obj b (213) */
- {
- JSValue tmp;
- tmp = sp[-2];
- sp[-2] = sp[-3];
- sp[-3] = tmp;
- }
- BREAK;
- CASE(OP_rot3l): /* x a b -> a b x (231) */
- {
- JSValue tmp;
- tmp = sp[-3];
- sp[-3] = sp[-2];
- sp[-2] = sp[-1];
- sp[-1] = tmp;
- }
- BREAK;
- CASE(OP_rot4l): /* x a b c -> a b c x */
- {
- JSValue tmp;
- tmp = sp[-4];
- sp[-4] = sp[-3];
- sp[-3] = sp[-2];
- sp[-2] = sp[-1];
- sp[-1] = tmp;
- }
- BREAK;
- CASE(OP_rot5l): /* x a b c d -> a b c d x */
- {
- JSValue tmp;
- tmp = sp[-5];
- sp[-5] = sp[-4];
- sp[-4] = sp[-3];
- sp[-3] = sp[-2];
- sp[-2] = sp[-1];
- sp[-1] = tmp;
- }
- BREAK;
- CASE(OP_rot3r): /* a b x -> x a b (312) */
- {
- JSValue tmp;
- tmp = sp[-1];
- sp[-1] = sp[-2];
- sp[-2] = sp[-3];
- sp[-3] = tmp;
- }
- BREAK;
- CASE(OP_perm4): /* obj prop a b -> a obj prop b */
- {
- JSValue tmp;
- tmp = sp[-2];
- sp[-2] = sp[-3];
- sp[-3] = sp[-4];
- sp[-4] = tmp;
- }
- BREAK;
- CASE(OP_perm5): /* this obj prop a b -> a this obj prop b */
- {
- JSValue tmp;
- tmp = sp[-2];
- sp[-2] = sp[-3];
- sp[-3] = sp[-4];
- sp[-4] = sp[-5];
- sp[-5] = tmp;
- }
- BREAK;
- CASE(OP_swap): /* a b -> b a */
- {
- JSValue tmp;
- tmp = sp[-2];
- sp[-2] = sp[-1];
- sp[-1] = tmp;
- }
- BREAK;
- CASE(OP_swap2): /* a b c d -> c d a b */
- {
- JSValue tmp1, tmp2;
- tmp1 = sp[-4];
- tmp2 = sp[-3];
- sp[-4] = sp[-2];
- sp[-3] = sp[-1];
- sp[-2] = tmp1;
- sp[-1] = tmp2;
- }
- BREAK;
- CASE(OP_fclosure):
- {
- JSValue bfunc = JS_DupValue(ctx, b->cpool[get_u32(pc)]);
- pc += 4;
- *sp++ = js_closure(ctx, bfunc, var_refs, sf, FALSE);
- if (unlikely(JS_IsException(sp[-1])))
- goto exception;
- }
- BREAK;
- #if SHORT_OPCODES
- CASE(OP_call0):
- CASE(OP_call1):
- CASE(OP_call2):
- CASE(OP_call3):
- call_argc = opcode - OP_call0;
- goto has_call_argc;
- #endif
- CASE(OP_call):
- CASE(OP_tail_call):
- {
- call_argc = get_u16(pc);
- pc += 2;
- goto has_call_argc;
- has_call_argc:
- call_argv = sp - call_argc;
- sf->cur_pc = pc;
- ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED,
- JS_UNDEFINED, call_argc, call_argv, 0);
- if (unlikely(JS_IsException(ret_val)))
- goto exception;
- if (opcode == OP_tail_call)
- goto done;
- for(i = -1; i < call_argc; i++)
- JS_FreeValue(ctx, call_argv[i]);
- sp -= call_argc + 1;
- *sp++ = ret_val;
- }
- BREAK;
- CASE(OP_call_constructor):
- {
- call_argc = get_u16(pc);
- pc += 2;
- call_argv = sp - call_argc;
- sf->cur_pc = pc;
- ret_val = JS_CallConstructorInternal(ctx, call_argv[-2],
- call_argv[-1],
- call_argc, call_argv, 0);
- if (unlikely(JS_IsException(ret_val)))
- goto exception;
- for(i = -2; i < call_argc; i++)
- JS_FreeValue(ctx, call_argv[i]);
- sp -= call_argc + 2;
- *sp++ = ret_val;
- }
- BREAK;
- CASE(OP_call_method):
- CASE(OP_tail_call_method):
- {
- call_argc = get_u16(pc);
- pc += 2;
- call_argv = sp - call_argc;
- sf->cur_pc = pc;
- ret_val = JS_CallInternal(ctx, call_argv[-1], call_argv[-2],
- JS_UNDEFINED, call_argc, call_argv, 0);
- if (unlikely(JS_IsException(ret_val)))
- goto exception;
- if (opcode == OP_tail_call_method)
- goto done;
- for(i = -2; i < call_argc; i++)
- JS_FreeValue(ctx, call_argv[i]);
- sp -= call_argc + 2;
- *sp++ = ret_val;
- }
- BREAK;
- CASE(OP_array_from):
- call_argc = get_u16(pc);
- pc += 2;
- ret_val = js_create_array_free(ctx, call_argc, sp - call_argc);
- sp -= call_argc;
- if (unlikely(JS_IsException(ret_val)))
- goto exception;
- *sp++ = ret_val;
- BREAK;
- CASE(OP_apply):
- {
- int magic;
- magic = get_u16(pc);
- pc += 2;
- sf->cur_pc = pc;
- ret_val = js_function_apply(ctx, sp[-3], 2, (JSValueConst *)&sp[-2], magic);
- if (unlikely(JS_IsException(ret_val)))
- goto exception;
- JS_FreeValue(ctx, sp[-3]);
- JS_FreeValue(ctx, sp[-2]);
- JS_FreeValue(ctx, sp[-1]);
- sp -= 3;
- *sp++ = ret_val;
- }
- BREAK;
- CASE(OP_return):
- ret_val = *--sp;
- goto done;
- CASE(OP_return_undef):
- ret_val = JS_UNDEFINED;
- goto done;
- CASE(OP_check_ctor_return):
- /* return TRUE if 'this' should be returned */
- if (!JS_IsObject(sp[-1])) {
- if (!JS_IsUndefined(sp[-1])) {
- JS_ThrowTypeError(caller_ctx, "derived class constructor must return an object or undefined");
- goto exception;
- }
- sp[0] = JS_TRUE;
- } else {
- sp[0] = JS_FALSE;
- }
- sp++;
- BREAK;
- CASE(OP_check_ctor):
- if (JS_IsUndefined(new_target)) {
- non_ctor_call:
- JS_ThrowTypeError(ctx, "class constructors must be invoked with 'new'");
- goto exception;
- }
- BREAK;
- CASE(OP_init_ctor):
- {
- JSValue super, ret;
- sf->cur_pc = pc;
- if (JS_IsUndefined(new_target))
- goto non_ctor_call;
- super = JS_GetPrototype(ctx, func_obj);
- if (JS_IsException(super))
- goto exception;
- ret = JS_CallConstructor2(ctx, super, new_target, argc, (JSValueConst *)argv);
- JS_FreeValue(ctx, super);
- if (JS_IsException(ret))
- goto exception;
- *sp++ = ret;
- }
- BREAK;
- CASE(OP_check_brand):
- {
- int ret = JS_CheckBrand(ctx, sp[-2], sp[-1]);
- if (ret < 0)
- goto exception;
- if (!ret) {
- JS_ThrowTypeError(ctx, "invalid brand on object");
- goto exception;
- }
- }
- BREAK;
- CASE(OP_add_brand):
- if (JS_AddBrand(ctx, sp[-2], sp[-1]) < 0)
- goto exception;
- JS_FreeValue(ctx, sp[-2]);
- JS_FreeValue(ctx, sp[-1]);
- sp -= 2;
- BREAK;
- CASE(OP_throw):
- JS_Throw(ctx, *--sp);
- goto exception;
- CASE(OP_throw_error):
- #define JS_THROW_VAR_RO 0
- #define JS_THROW_VAR_REDECL 1
- #define JS_THROW_VAR_UNINITIALIZED 2
- #define JS_THROW_ERROR_DELETE_SUPER 3
- #define JS_THROW_ERROR_ITERATOR_THROW 4
- {
- JSAtom atom;
- int type;
- atom = get_u32(pc);
- type = pc[4];
- pc += 5;
- if (type == JS_THROW_VAR_RO)
- JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, atom);
- else
- if (type == JS_THROW_VAR_REDECL)
- JS_ThrowSyntaxErrorVarRedeclaration(ctx, atom);
- else
- if (type == JS_THROW_VAR_UNINITIALIZED)
- JS_ThrowReferenceErrorUninitialized(ctx, atom);
- else
- if (type == JS_THROW_ERROR_DELETE_SUPER)
- JS_ThrowReferenceError(ctx, "unsupported reference to 'super'");
- else
- if (type == JS_THROW_ERROR_ITERATOR_THROW)
- JS_ThrowTypeError(ctx, "iterator does not have a throw method");
- else
- JS_ThrowInternalError(ctx, "invalid throw var type %d", type);
- }
- goto exception;
- CASE(OP_eval):
- {
- JSValueConst obj;
- int scope_idx;
- call_argc = get_u16(pc);
- scope_idx = get_u16(pc + 2) + ARG_SCOPE_END;
- pc += 4;
- call_argv = sp - call_argc;
- sf->cur_pc = pc;
- if (js_same_value(ctx, call_argv[-1], ctx->eval_obj)) {
- if (call_argc >= 1)
- obj = call_argv[0];
- else
- obj = JS_UNDEFINED;
- ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj,
- JS_EVAL_TYPE_DIRECT, scope_idx);
- } else {
- ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED,
- JS_UNDEFINED, call_argc, call_argv, 0);
- }
- if (unlikely(JS_IsException(ret_val)))
- goto exception;
- for(i = -1; i < call_argc; i++)
- JS_FreeValue(ctx, call_argv[i]);
- sp -= call_argc + 1;
- *sp++ = ret_val;
- }
- BREAK;
- /* could merge with OP_apply */
- CASE(OP_apply_eval):
- {
- int scope_idx;
- uint32_t len;
- JSValue *tab;
- JSValueConst obj;
- scope_idx = get_u16(pc) + ARG_SCOPE_END;
- pc += 2;
- sf->cur_pc = pc;
- tab = build_arg_list(ctx, &len, sp[-1]);
- if (!tab)
- goto exception;
- if (js_same_value(ctx, sp[-2], ctx->eval_obj)) {
- if (len >= 1)
- obj = tab[0];
- else
- obj = JS_UNDEFINED;
- ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj,
- JS_EVAL_TYPE_DIRECT, scope_idx);
- } else {
- ret_val = JS_Call(ctx, sp[-2], JS_UNDEFINED, len,
- (JSValueConst *)tab);
- }
- free_arg_list(ctx, tab, len);
- if (unlikely(JS_IsException(ret_val)))
- goto exception;
- JS_FreeValue(ctx, sp[-2]);
- JS_FreeValue(ctx, sp[-1]);
- sp -= 2;
- *sp++ = ret_val;
- }
- BREAK;
- CASE(OP_regexp):
- {
- sp[-2] = JS_NewRegexp(ctx, sp[-2], sp[-1]);
- sp--;
- if (JS_IsException(sp[-1]))
- goto exception;
- }
- BREAK;
- CASE(OP_get_super):
- {
- JSValue proto;
- sf->cur_pc = pc;
- proto = JS_GetPrototype(ctx, sp[-1]);
- if (JS_IsException(proto))
- goto exception;
- JS_FreeValue(ctx, sp[-1]);
- sp[-1] = proto;
- }
- BREAK;
- CASE(OP_import):
- {
- JSValue val;
- sf->cur_pc = pc;
- val = js_dynamic_import(ctx, sp[-2], sp[-1]);
- if (JS_IsException(val))
- goto exception;
- JS_FreeValue(ctx, sp[-2]);
- JS_FreeValue(ctx, sp[-1]);
- sp--;
- sp[-1] = val;
- }
- BREAK;
- CASE(OP_get_var_undef):
- CASE(OP_get_var):
- {
- int idx;
- JSValue val;
- idx = get_u16(pc);
- pc += 2;
- val = *var_refs[idx]->pvalue;
- if (unlikely(JS_IsUninitialized(val))) {
- JSClosureVar *cv = &b->closure_var[idx];
- if (cv->is_lexical) {
- JS_ThrowReferenceErrorUninitialized(ctx, cv->var_name);
- goto exception;
- } else {
- sf->cur_pc = pc;
- sp[0] = JS_GetPropertyInternal(ctx, ctx->global_obj,
- cv->var_name,
- ctx->global_obj,
- opcode - OP_get_var_undef);
- if (JS_IsException(sp[0]))
- goto exception;
- }
- } else {
- sp[0] = JS_DupValue(ctx, val);
- }
- sp++;
- }
- BREAK;
- CASE(OP_put_var):
- CASE(OP_put_var_init):
- {
- int idx, ret;
- JSVarRef *var_ref;
- idx = get_u16(pc);
- pc += 2;
- var_ref = var_refs[idx];
- if (unlikely(JS_IsUninitialized(*var_ref->pvalue) ||
- var_ref->is_const)) {
- JSClosureVar *cv = &b->closure_var[idx];
- if (var_ref->is_lexical) {
- if (opcode == OP_put_var_init)
- goto put_var_ok;
- if (JS_IsUninitialized(*var_ref->pvalue))
- JS_ThrowReferenceErrorUninitialized(ctx, cv->var_name);
- else
- JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, cv->var_name);
- goto exception;
- } else {
- sf->cur_pc = pc;
- ret = JS_HasProperty(ctx, ctx->global_obj, cv->var_name);
- if (ret < 0)
- goto exception;
- if (ret == 0 && is_strict_mode(ctx)) {
- JS_ThrowReferenceErrorNotDefined(ctx, cv->var_name);
- goto exception;
- }
- ret = JS_SetPropertyInternal(ctx, ctx->global_obj, cv->var_name, sp[-1],
- ctx->global_obj, JS_PROP_THROW_STRICT);
- sp--;
- if (ret < 0)
- goto exception;
- }
- } else {
- put_var_ok:
- set_value(ctx, var_ref->pvalue, sp[-1]);
- sp--;
- }
- }
- BREAK;
- CASE(OP_get_loc):
- {
- int idx;
- idx = get_u16(pc);
- pc += 2;
- sp[0] = JS_DupValue(ctx, var_buf[idx]);
- sp++;
- }
- BREAK;
- CASE(OP_put_loc):
- {
- int idx;
- idx = get_u16(pc);
- pc += 2;
- set_value(ctx, &var_buf[idx], sp[-1]);
- sp--;
- }
- BREAK;
- CASE(OP_set_loc):
- {
- int idx;
- idx = get_u16(pc);
- pc += 2;
- set_value(ctx, &var_buf[idx], JS_DupValue(ctx, sp[-1]));
- }
- BREAK;
- CASE(OP_get_arg):
- {
- int idx;
- idx = get_u16(pc);
- pc += 2;
- sp[0] = JS_DupValue(ctx, arg_buf[idx]);
- sp++;
- }
- BREAK;
- CASE(OP_put_arg):
- {
- int idx;
- idx = get_u16(pc);
- pc += 2;
- set_value(ctx, &arg_buf[idx], sp[-1]);
- sp--;
- }
- BREAK;
- CASE(OP_set_arg):
- {
- int idx;
- idx = get_u16(pc);
- pc += 2;
- set_value(ctx, &arg_buf[idx], JS_DupValue(ctx, sp[-1]));
- }
- BREAK;
- #if SHORT_OPCODES
- CASE(OP_get_loc8): *sp++ = JS_DupValue(ctx, var_buf[*pc++]); BREAK;
- CASE(OP_put_loc8): set_value(ctx, &var_buf[*pc++], *--sp); BREAK;
- CASE(OP_set_loc8): set_value(ctx, &var_buf[*pc++], JS_DupValue(ctx, sp[-1])); BREAK;
- CASE(OP_get_loc0): *sp++ = JS_DupValue(ctx, var_buf[0]); BREAK;
- CASE(OP_get_loc1): *sp++ = JS_DupValue(ctx, var_buf[1]); BREAK;
- CASE(OP_get_loc2): *sp++ = JS_DupValue(ctx, var_buf[2]); BREAK;
- CASE(OP_get_loc3): *sp++ = JS_DupValue(ctx, var_buf[3]); BREAK;
- CASE(OP_put_loc0): set_value(ctx, &var_buf[0], *--sp); BREAK;
- CASE(OP_put_loc1): set_value(ctx, &var_buf[1], *--sp); BREAK;
- CASE(OP_put_loc2): set_value(ctx, &var_buf[2], *--sp); BREAK;
- CASE(OP_put_loc3): set_value(ctx, &var_buf[3], *--sp); BREAK;
- CASE(OP_set_loc0): set_value(ctx, &var_buf[0], JS_DupValue(ctx, sp[-1])); BREAK;
- CASE(OP_set_loc1): set_value(ctx, &var_buf[1], JS_DupValue(ctx, sp[-1])); BREAK;
- CASE(OP_set_loc2): set_value(ctx, &var_buf[2], JS_DupValue(ctx, sp[-1])); BREAK;
- CASE(OP_set_loc3): set_value(ctx, &var_buf[3], JS_DupValue(ctx, sp[-1])); BREAK;
- CASE(OP_get_arg0): *sp++ = JS_DupValue(ctx, arg_buf[0]); BREAK;
- CASE(OP_get_arg1): *sp++ = JS_DupValue(ctx, arg_buf[1]); BREAK;
- CASE(OP_get_arg2): *sp++ = JS_DupValue(ctx, arg_buf[2]); BREAK;
- CASE(OP_get_arg3): *sp++ = JS_DupValue(ctx, arg_buf[3]); BREAK;
- CASE(OP_put_arg0): set_value(ctx, &arg_buf[0], *--sp); BREAK;
- CASE(OP_put_arg1): set_value(ctx, &arg_buf[1], *--sp); BREAK;
- CASE(OP_put_arg2): set_value(ctx, &arg_buf[2], *--sp); BREAK;
- CASE(OP_put_arg3): set_value(ctx, &arg_buf[3], *--sp); BREAK;
- CASE(OP_set_arg0): set_value(ctx, &arg_buf[0], JS_DupValue(ctx, sp[-1])); BREAK;
- CASE(OP_set_arg1): set_value(ctx, &arg_buf[1], JS_DupValue(ctx, sp[-1])); BREAK;
- CASE(OP_set_arg2): set_value(ctx, &arg_buf[2], JS_DupValue(ctx, sp[-1])); BREAK;
- CASE(OP_set_arg3): set_value(ctx, &arg_buf[3], JS_DupValue(ctx, sp[-1])); BREAK;
- CASE(OP_get_var_ref0): *sp++ = JS_DupValue(ctx, *var_refs[0]->pvalue); BREAK;
- CASE(OP_get_var_ref1): *sp++ = JS_DupValue(ctx, *var_refs[1]->pvalue); BREAK;
- CASE(OP_get_var_ref2): *sp++ = JS_DupValue(ctx, *var_refs[2]->pvalue); BREAK;
- CASE(OP_get_var_ref3): *sp++ = JS_DupValue(ctx, *var_refs[3]->pvalue); BREAK;
- CASE(OP_put_var_ref0): set_value(ctx, var_refs[0]->pvalue, *--sp); BREAK;
- CASE(OP_put_var_ref1): set_value(ctx, var_refs[1]->pvalue, *--sp); BREAK;
- CASE(OP_put_var_ref2): set_value(ctx, var_refs[2]->pvalue, *--sp); BREAK;
- CASE(OP_put_var_ref3): set_value(ctx, var_refs[3]->pvalue, *--sp); BREAK;
- CASE(OP_set_var_ref0): set_value(ctx, var_refs[0]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;
- CASE(OP_set_var_ref1): set_value(ctx, var_refs[1]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;
- CASE(OP_set_var_ref2): set_value(ctx, var_refs[2]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;
- CASE(OP_set_var_ref3): set_value(ctx, var_refs[3]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;
- #endif
- CASE(OP_get_var_ref):
- {
- int idx;
- JSValue val;
- idx = get_u16(pc);
- pc += 2;
- val = *var_refs[idx]->pvalue;
- sp[0] = JS_DupValue(ctx, val);
- sp++;
- }
- BREAK;
- CASE(OP_put_var_ref):
- {
- int idx;
- idx = get_u16(pc);
- pc += 2;
- set_value(ctx, var_refs[idx]->pvalue, sp[-1]);
- sp--;
- }
- BREAK;
- CASE(OP_set_var_ref):
- {
- int idx;
- idx = get_u16(pc);
- pc += 2;
- set_value(ctx, var_refs[idx]->pvalue, JS_DupValue(ctx, sp[-1]));
- }
- BREAK;
- CASE(OP_get_var_ref_check):
- {
- int idx;
- JSValue val;
- idx = get_u16(pc);
- pc += 2;
- val = *var_refs[idx]->pvalue;
- if (unlikely(JS_IsUninitialized(val))) {
- JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE);
- goto exception;
- }
- sp[0] = JS_DupValue(ctx, val);
- sp++;
- }
- BREAK;
- CASE(OP_put_var_ref_check):
- {
- int idx;
- idx = get_u16(pc);
- pc += 2;
- if (unlikely(JS_IsUninitialized(*var_refs[idx]->pvalue))) {
- JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE);
- goto exception;
- }
- set_value(ctx, var_refs[idx]->pvalue, sp[-1]);
- sp--;
- }
- BREAK;
- CASE(OP_put_var_ref_check_init):
- {
- int idx;
- idx = get_u16(pc);
- pc += 2;
- if (unlikely(!JS_IsUninitialized(*var_refs[idx]->pvalue))) {
- JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE);
- goto exception;
- }
- set_value(ctx, var_refs[idx]->pvalue, sp[-1]);
- sp--;
- }
- BREAK;
- CASE(OP_set_loc_uninitialized):
- {
- int idx;
- idx = get_u16(pc);
- pc += 2;
- set_value(ctx, &var_buf[idx], JS_UNINITIALIZED);
- }
- BREAK;
- CASE(OP_get_loc_check):
- {
- int idx;
- idx = get_u16(pc);
- pc += 2;
- if (unlikely(JS_IsUninitialized(var_buf[idx]))) {
- JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE);
- goto exception;
- }
- sp[0] = JS_DupValue(ctx, var_buf[idx]);
- sp++;
- }
- BREAK;
- CASE(OP_get_loc_checkthis):
- {
- int idx;
- idx = get_u16(pc);
- pc += 2;
- if (unlikely(JS_IsUninitialized(var_buf[idx]))) {
- JS_ThrowReferenceErrorUninitialized2(caller_ctx, b, idx, FALSE);
- goto exception;
- }
- sp[0] = JS_DupValue(ctx, var_buf[idx]);
- sp++;
- }
- BREAK;
- CASE(OP_put_loc_check):
- {
- int idx;
- idx = get_u16(pc);
- pc += 2;
- if (unlikely(JS_IsUninitialized(var_buf[idx]))) {
- JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE);
- goto exception;
- }
- set_value(ctx, &var_buf[idx], sp[-1]);
- sp--;
- }
- BREAK;
- CASE(OP_set_loc_check):
- {
- int idx;
- idx = get_u16(pc);
- pc += 2;
- if (unlikely(JS_IsUninitialized(var_buf[idx]))) {
- JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE);
- goto exception;
- }
- set_value(ctx, &var_buf[idx], JS_DupValue(ctx, sp[-1]));
- }
- BREAK;
- CASE(OP_put_loc_check_init):
- {
- int idx;
- idx = get_u16(pc);
- pc += 2;
- if (unlikely(!JS_IsUninitialized(var_buf[idx]))) {
- JS_ThrowReferenceError(ctx, "'this' can be initialized only once");
- goto exception;
- }
- set_value(ctx, &var_buf[idx], sp[-1]);
- sp--;
- }
- BREAK;
- CASE(OP_close_loc):
- {
- int idx;
- idx = get_u16(pc);
- pc += 2;
- close_lexical_var(ctx, b, sf, idx);
- }
- BREAK;
- CASE(OP_make_loc_ref):
- CASE(OP_make_arg_ref):
- CASE(OP_make_var_ref_ref):
- {
- JSVarRef *var_ref;
- JSProperty *pr;
- JSAtom atom;
- int idx;
- atom = get_u32(pc);
- idx = get_u16(pc + 4);
- pc += 6;
- *sp++ = JS_NewObjectProto(ctx, JS_NULL);
- if (unlikely(JS_IsException(sp[-1])))
- goto exception;
- if (opcode == OP_make_var_ref_ref) {
- var_ref = var_refs[idx];
- js_rc(var_ref)->ref_count++;
- } else {
- var_ref = get_var_ref(ctx, sf, idx, opcode == OP_make_arg_ref);
- if (!var_ref)
- goto exception;
- }
- pr = add_property(ctx, JS_VALUE_GET_OBJ(sp[-1]), atom,
- JS_PROP_WRITABLE | JS_PROP_VARREF);
- if (!pr) {
- free_var_ref(rt, var_ref);
- goto exception;
- }
- pr->u.var_ref = var_ref;
- *sp++ = JS_AtomToValue(ctx, atom);
- }
- BREAK;
- CASE(OP_make_var_ref):
- {
- JSAtom atom;
- atom = get_u32(pc);
- pc += 4;
- sf->cur_pc = pc;
- if (JS_GetGlobalVarRef(ctx, atom, sp))
- goto exception;
- sp += 2;
- }
- BREAK;
- CASE(OP_goto):
- pc += (int32_t)get_u32(pc);
- if (unlikely(js_poll_interrupts(ctx)))
- goto exception;
- BREAK;
- #if SHORT_OPCODES
- CASE(OP_goto16):
- pc += (int16_t)get_u16(pc);
- if (unlikely(js_poll_interrupts(ctx)))
- goto exception;
- BREAK;
- CASE(OP_goto8):
- pc += (int8_t)pc[0];
- if (unlikely(js_poll_interrupts(ctx)))
- goto exception;
- BREAK;
- #endif
- CASE(OP_if_true):
- {
- int res;
- JSValue op1;
- op1 = sp[-1];
- pc += 4;
- if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
- res = JS_VALUE_GET_INT(op1);
- } else {
- res = JS_ToBoolFree(ctx, op1);
- }
- sp--;
- if (res) {
- pc += (int32_t)get_u32(pc - 4) - 4;
- }
- if (unlikely(js_poll_interrupts(ctx)))
- goto exception;
- }
- BREAK;
- CASE(OP_if_false):
- {
- int res;
- JSValue op1;
- op1 = sp[-1];
- pc += 4;
- /* quick and dirty test for JS_TAG_INT, JS_TAG_BOOL, JS_TAG_NULL and JS_TAG_UNDEFINED */
- if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
- res = JS_VALUE_GET_INT(op1);
- } else {
- res = JS_ToBoolFree(ctx, op1);
- }
- sp--;
- if (!res) {
- pc += (int32_t)get_u32(pc - 4) - 4;
- }
- if (unlikely(js_poll_interrupts(ctx)))
- goto exception;
- }
- BREAK;
- #if SHORT_OPCODES
- CASE(OP_if_true8):
- {
- int res;
- JSValue op1;
- op1 = sp[-1];
- pc += 1;
- if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
- res = JS_VALUE_GET_INT(op1);
- } else {
- res = JS_ToBoolFree(ctx, op1);
- }
- sp--;
- if (res) {
- pc += (int8_t)pc[-1] - 1;
- }
- if (unlikely(js_poll_interrupts(ctx)))
- goto exception;
- }
- BREAK;
- CASE(OP_if_false8):
- {
- int res;
- JSValue op1;
- op1 = sp[-1];
- pc += 1;
- if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
- res = JS_VALUE_GET_INT(op1);
- } else {
- res = JS_ToBoolFree(ctx, op1);
- }
- sp--;
- if (!res) {
- pc += (int8_t)pc[-1] - 1;
- }
- if (unlikely(js_poll_interrupts(ctx)))
- goto exception;
- }
- BREAK;
- #endif
- CASE(OP_catch):
- {
- int32_t diff;
- diff = get_u32(pc);
- sp[0] = JS_NewCatchOffset(ctx, pc + diff - b->byte_code_buf);
- sp++;
- pc += 4;
- }
- BREAK;
- CASE(OP_gosub):
- {
- int32_t diff;
- diff = get_u32(pc);
- /* XXX: should have a different tag to avoid security flaw */
- sp[0] = JS_NewInt32(ctx, pc + 4 - b->byte_code_buf);
- sp++;
- pc += diff;
- }
- BREAK;
- CASE(OP_ret):
- {
- JSValue op1;
- uint32_t pos;
- op1 = sp[-1];
- if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_INT))
- goto ret_fail;
- pos = JS_VALUE_GET_INT(op1);
- if (unlikely(pos >= b->byte_code_len)) {
- ret_fail:
- JS_ThrowInternalError(ctx, "invalid ret value");
- goto exception;
- }
- sp--;
- pc = b->byte_code_buf + pos;
- }
- BREAK;
- CASE(OP_for_in_start):
- sf->cur_pc = pc;
- if (js_for_in_start(ctx, sp))
- goto exception;
- BREAK;
- CASE(OP_for_in_next):
- sf->cur_pc = pc;
- if (js_for_in_next(ctx, sp))
- goto exception;
- sp += 2;
- BREAK;
- CASE(OP_for_of_start):
- sf->cur_pc = pc;
- if (js_for_of_start(ctx, sp, FALSE))
- goto exception;
- sp += 1;
- *sp++ = JS_NewCatchOffset(ctx, 0);
- BREAK;
- CASE(OP_for_of_next):
- {
- int offset = -3 - pc[0];
- pc += 1;
- sf->cur_pc = pc;
- if (js_for_of_next(ctx, sp, offset))
- goto exception;
- sp += 2;
- }
- BREAK;
- CASE(OP_for_await_of_next):
- sf->cur_pc = pc;
- if (js_for_await_of_next(ctx, sp))
- goto exception;
- sp++;
- BREAK;
- CASE(OP_for_await_of_start):
- sf->cur_pc = pc;
- if (js_for_of_start(ctx, sp, TRUE))
- goto exception;
- sp += 1;
- *sp++ = JS_NewCatchOffset(ctx, 0);
- BREAK;
- CASE(OP_iterator_get_value_done):
- sf->cur_pc = pc;
- if (js_iterator_get_value_done(ctx, sp))
- goto exception;
- sp += 1;
- BREAK;
- CASE(OP_iterator_check_object):
- if (unlikely(!JS_IsObject(sp[-1]))) {
- JS_ThrowTypeError(ctx, "iterator must return an object");
- goto exception;
- }
- BREAK;
- CASE(OP_iterator_close):
- /* iter_obj next catch_offset -> */
- sp--; /* drop the catch offset to avoid getting caught by exception */
- JS_FreeValue(ctx, sp[-1]); /* drop the next method */
- sp--;
- if (!JS_IsUndefined(sp[-1])) {
- sf->cur_pc = pc;
- if (JS_IteratorClose(ctx, sp[-1], FALSE))
- goto exception;
- JS_FreeValue(ctx, sp[-1]);
- }
- sp--;
- BREAK;
- CASE(OP_nip_catch):
- {
- JSValue ret_val;
- /* catch_offset ... ret_val -> ret_eval */
- ret_val = *--sp;
- while (sp > stack_buf &&
- JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_CATCH_OFFSET) {
- JS_FreeValue(ctx, *--sp);
- }
- if (unlikely(sp == stack_buf)) {
- JS_ThrowInternalError(ctx, "nip_catch");
- JS_FreeValue(ctx, ret_val);
- goto exception;
- }
- sp[-1] = ret_val;
- }
- BREAK;
- CASE(OP_iterator_next):
- /* stack: iter_obj next catch_offset val */
- {
- JSValue ret;
- sf->cur_pc = pc;
- ret = JS_Call(ctx, sp[-3], sp[-4],
- 1, (JSValueConst *)(sp - 1));
- if (JS_IsException(ret))
- goto exception;
- JS_FreeValue(ctx, sp[-1]);
- sp[-1] = ret;
- }
- BREAK;
- CASE(OP_iterator_call):
- /* stack: iter_obj next catch_offset val */
- {
- JSValue method, ret;
- BOOL ret_flag;
- int flags;
- flags = *pc++;
- sf->cur_pc = pc;
- method = JS_GetProperty(ctx, sp[-4], (flags & 1) ?
- JS_ATOM_throw : JS_ATOM_return);
- if (JS_IsException(method))
- goto exception;
- if (JS_IsUndefined(method) || JS_IsNull(method)) {
- ret_flag = TRUE;
- } else {
- if (flags & 2) {
- /* no argument */
- ret = JS_CallFree(ctx, method, sp[-4],
- 0, NULL);
- } else {
- ret = JS_CallFree(ctx, method, sp[-4],
- 1, (JSValueConst *)(sp - 1));
- }
- if (JS_IsException(ret))
- goto exception;
- JS_FreeValue(ctx, sp[-1]);
- sp[-1] = ret;
- ret_flag = FALSE;
- }
- sp[0] = JS_NewBool(ctx, ret_flag);
- sp += 1;
- }
- BREAK;
- CASE(OP_lnot):
- {
- int res;
- JSValue op1;
- op1 = sp[-1];
- if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
- res = JS_VALUE_GET_INT(op1) != 0;
- } else {
- res = JS_ToBoolFree(ctx, op1);
- }
- sp[-1] = JS_NewBool(ctx, !res);
- }
- BREAK;
- #define GET_FIELD_INLINE(name, keep, is_length) \
- { \
- JSValue val, obj; \
- JSAtom atom; \
- JSObject *p; \
- JSProperty *pr; \
- JSShapeProperty *prs; \
- \
- if (is_length) { \
- atom = JS_ATOM_length; \
- } else { \
- atom = get_u32(pc); \
- pc += 4; \
- } \
- \
- obj = sp[-1]; \
- if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT)) { \
- p = JS_VALUE_GET_OBJ(obj); \
- for(;;) { \
- prs = find_own_property(&pr, p, atom); \
- if (prs) { \
- /* found */ \
- if (unlikely(prs->flags & JS_PROP_TMASK)) \
- goto name ## _slow_path; \
- val = JS_DupValue(ctx, pr->u.value); \
- break; \
- } \
- if (unlikely(p->is_exotic)) { \
- /* XXX: should avoid the slow path for arrays \
- and typed arrays by ensuring that 'prop' is \
- not numeric */ \
- obj = JS_MKPTR(JS_TAG_OBJECT, p); \
- goto name ## _slow_path; \
- } \
- p = p->shape->proto; \
- if (!p) { \
- val = JS_UNDEFINED; \
- break; \
- } \
- } \
- } else { \
- name ## _slow_path: \
- sf->cur_pc = pc; \
- val = JS_GetPropertyInternal(ctx, obj, atom, sp[-1], 0); \
- if (unlikely(JS_IsException(val))) \
- goto exception; \
- } \
- if (keep) { \
- *sp++ = val; \
- } else { \
- JS_FreeValue(ctx, sp[-1]); \
- sp[-1] = val; \
- } \
- }
-
- CASE(OP_get_field):
- GET_FIELD_INLINE(get_field, 0, 0);
- BREAK;
- CASE(OP_get_field2):
- GET_FIELD_INLINE(get_field2, 1, 0);
- BREAK;
- #if SHORT_OPCODES
- CASE(OP_get_length):
- GET_FIELD_INLINE(get_length, 0, 1);
- BREAK;
- #endif
-
- CASE(OP_put_field):
- {
- int ret;
- JSValue obj;
- JSAtom atom;
- JSObject *p;
- JSProperty *pr;
- JSShapeProperty *prs;
- atom = get_u32(pc);
- pc += 4;
- obj = sp[-2];
- if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT)) {
- p = JS_VALUE_GET_OBJ(obj);
- prs = find_own_property(&pr, p, atom);
- if (!prs)
- goto put_field_slow_path;
- if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE |
- JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) {
- /* fast path */
- set_value(ctx, &pr->u.value, sp[-1]);
- } else {
- goto put_field_slow_path;
- }
- JS_FreeValue(ctx, obj);
- sp -= 2;
- } else {
- put_field_slow_path:
- sf->cur_pc = pc;
- ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-1], obj,
- JS_PROP_THROW_STRICT);
- JS_FreeValue(ctx, obj);
- sp -= 2;
- if (unlikely(ret < 0))
- goto exception;
- }
-
- }
- BREAK;
- CASE(OP_private_symbol):
- {
- JSAtom atom;
- JSValue val;
- atom = get_u32(pc);
- pc += 4;
- val = JS_NewSymbolFromAtom(ctx, atom, JS_ATOM_TYPE_PRIVATE);
- if (JS_IsException(val))
- goto exception;
- *sp++ = val;
- }
- BREAK;
- CASE(OP_get_private_field):
- {
- JSValue val;
- val = JS_GetPrivateField(ctx, sp[-2], sp[-1]);
- JS_FreeValue(ctx, sp[-1]);
- JS_FreeValue(ctx, sp[-2]);
- sp[-2] = val;
- sp--;
- if (unlikely(JS_IsException(val)))
- goto exception;
- }
- BREAK;
- CASE(OP_put_private_field):
- {
- int ret;
- ret = JS_SetPrivateField(ctx, sp[-3], sp[-1], sp[-2]);
- JS_FreeValue(ctx, sp[-3]);
- JS_FreeValue(ctx, sp[-1]);
- sp -= 3;
- if (unlikely(ret < 0))
- goto exception;
- }
- BREAK;
- CASE(OP_define_private_field):
- {
- int ret;
- ret = JS_DefinePrivateField(ctx, sp[-3], sp[-2], sp[-1]);
- JS_FreeValue(ctx, sp[-2]);
- sp -= 2;
- if (unlikely(ret < 0))
- goto exception;
- }
- BREAK;
- CASE(OP_define_field):
- {
- int ret;
- JSAtom atom;
- atom = get_u32(pc);
- pc += 4;
- ret = JS_DefinePropertyValue(ctx, sp[-2], atom, sp[-1],
- JS_PROP_C_W_E | JS_PROP_THROW);
- sp--;
- if (unlikely(ret < 0))
- goto exception;
- }
- BREAK;
- CASE(OP_set_name):
- {
- int ret;
- JSAtom atom;
- atom = get_u32(pc);
- pc += 4;
- ret = JS_DefineObjectName(ctx, sp[-1], atom, JS_PROP_CONFIGURABLE);
- if (unlikely(ret < 0))
- goto exception;
- }
- BREAK;
- CASE(OP_set_name_computed):
- {
- int ret;
- ret = JS_DefineObjectNameComputed(ctx, sp[-1], sp[-2], JS_PROP_CONFIGURABLE);
- if (unlikely(ret < 0))
- goto exception;
- }
- BREAK;
- CASE(OP_set_proto):
- {
- JSValue proto;
- sf->cur_pc = pc;
- proto = sp[-1];
- if (JS_IsObject(proto) || JS_IsNull(proto)) {
- if (JS_SetPrototypeInternal(ctx, sp[-2], proto, TRUE) < 0)
- goto exception;
- }
- JS_FreeValue(ctx, proto);
- sp--;
- }
- BREAK;
- CASE(OP_set_home_object):
- js_method_set_home_object(ctx, sp[-1], sp[-2]);
- BREAK;
- CASE(OP_define_method):
- CASE(OP_define_method_computed):
- {
- JSValue getter, setter, value;
- JSValueConst obj;
- JSAtom atom;
- int flags, ret, op_flags;
- BOOL is_computed;
- #define OP_DEFINE_METHOD_METHOD 0
- #define OP_DEFINE_METHOD_GETTER 1
- #define OP_DEFINE_METHOD_SETTER 2
- #define OP_DEFINE_METHOD_ENUMERABLE 4
- is_computed = (opcode == OP_define_method_computed);
- if (is_computed) {
- atom = JS_ValueToAtom(ctx, sp[-2]);
- if (unlikely(atom == JS_ATOM_NULL))
- goto exception;
- opcode += OP_define_method - OP_define_method_computed;
- } else {
- atom = get_u32(pc);
- pc += 4;
- }
- op_flags = *pc++;
- obj = sp[-2 - is_computed];
- flags = JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE |
- JS_PROP_HAS_ENUMERABLE | JS_PROP_THROW;
- if (op_flags & OP_DEFINE_METHOD_ENUMERABLE)
- flags |= JS_PROP_ENUMERABLE;
- op_flags &= 3;
- value = JS_UNDEFINED;
- getter = JS_UNDEFINED;
- setter = JS_UNDEFINED;
- if (op_flags == OP_DEFINE_METHOD_METHOD) {
- value = sp[-1];
- flags |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE;
- } else if (op_flags == OP_DEFINE_METHOD_GETTER) {
- getter = sp[-1];
- flags |= JS_PROP_HAS_GET;
- } else {
- setter = sp[-1];
- flags |= JS_PROP_HAS_SET;
- }
- ret = js_method_set_properties(ctx, sp[-1], atom, flags, obj);
- if (ret >= 0) {
- ret = JS_DefineProperty(ctx, obj, atom, value,
- getter, setter, flags);
- }
- JS_FreeValue(ctx, sp[-1]);
- if (is_computed) {
- JS_FreeAtom(ctx, atom);
- JS_FreeValue(ctx, sp[-2]);
- }
- sp -= 1 + is_computed;
- if (unlikely(ret < 0))
- goto exception;
- }
- BREAK;
- CASE(OP_define_class):
- CASE(OP_define_class_computed):
- {
- int class_flags;
- JSAtom atom;
- atom = get_u32(pc);
- class_flags = pc[4];
- pc += 5;
- if (js_op_define_class(ctx, sp, atom, class_flags,
- var_refs, sf,
- (opcode == OP_define_class_computed)) < 0)
- goto exception;
- }
- BREAK;
- #define GET_ARRAY_EL_INLINE(name, keep) \
- { \
- JSValue val, obj, prop; \
- JSObject *p; \
- uint32_t idx; \
- \
- obj = sp[-2]; \
- prop = sp[-1]; \
- if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT && \
- JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) { \
- p = JS_VALUE_GET_OBJ(obj); \
- idx = JS_VALUE_GET_INT(prop); \
- if (unlikely(p->class_id != JS_CLASS_ARRAY)) \
- goto name ## _slow_path; \
- if (unlikely(idx >= p->u.array.count)) \
- goto name ## _slow_path; \
- val = JS_DupValue(ctx, p->u.array.u.values[idx]); \
- } else { \
- name ## _slow_path: \
- sf->cur_pc = pc; \
- val = JS_GetPropertyValue(ctx, obj, prop); \
- if (unlikely(JS_IsException(val))) { \
- if (keep) \
- sp[-1] = JS_UNDEFINED; \
- else \
- sp--; \
- goto exception; \
- } \
- } \
- if (keep) { \
- sp[-1] = val; \
- } else { \
- JS_FreeValue(ctx, obj); \
- sp[-2] = val; \
- sp--; \
- } \
- }
-
- CASE(OP_get_array_el):
- GET_ARRAY_EL_INLINE(get_array_el, 0);
- BREAK;
- CASE(OP_get_array_el2):
- GET_ARRAY_EL_INLINE(get_array_el2, 1);
- BREAK;
- CASE(OP_get_array_el3):
- {
- JSValue val;
- JSObject *p;
- uint32_t idx;
- if (likely(JS_VALUE_GET_TAG(sp[-2]) == JS_TAG_OBJECT &&
- JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_INT)) {
- p = JS_VALUE_GET_OBJ(sp[-2]);
- idx = JS_VALUE_GET_INT(sp[-1]);
- if (unlikely(p->class_id != JS_CLASS_ARRAY))
- goto get_array_el3_slow_path;
- if (unlikely(idx >= p->u.array.count))
- goto get_array_el3_slow_path;
- val = JS_DupValue(ctx, p->u.array.u.values[idx]);
- } else {
- get_array_el3_slow_path:
- switch (JS_VALUE_GET_TAG(sp[-1])) {
- case JS_TAG_INT:
- case JS_TAG_STRING:
- case JS_TAG_SYMBOL:
- /* undefined and null are tested in JS_GetPropertyValue() */
- break;
- default:
- /* must be tested before JS_ToPropertyKey */
- if (unlikely(JS_IsUndefined(sp[-2]) || JS_IsNull(sp[-2]))) {
- JS_ThrowTypeError(ctx, "value has no property");
- goto exception;
- }
- sf->cur_pc = pc;
- ret_val = JS_ToPropertyKey(ctx, sp[-1]);
- if (JS_IsException(ret_val))
- goto exception;
- JS_FreeValue(ctx, sp[-1]);
- sp[-1] = ret_val;
- break;
- }
- sf->cur_pc = pc;
- val = JS_GetPropertyValue(ctx, sp[-2], JS_DupValue(ctx, sp[-1]));
- if (unlikely(JS_IsException(val)))
- goto exception;
- }
- *sp++ = val;
- }
- BREAK;
-
- CASE(OP_get_ref_value):
- {
- JSValue val;
- JSAtom atom;
- int ret;
-
- sf->cur_pc = pc;
- atom = JS_ValueToAtom(ctx, sp[-1]);
- if (atom == JS_ATOM_NULL)
- goto exception;
- if (unlikely(JS_IsUndefined(sp[-2]))) {
- JS_ThrowReferenceErrorNotDefined(ctx, atom);
- JS_FreeAtom(ctx, atom);
- goto exception;
- }
- ret = JS_HasProperty(ctx, sp[-2], atom);
- if (unlikely(ret <= 0)) {
- if (ret < 0) {
- JS_FreeAtom(ctx, atom);
- goto exception;
- }
- if (is_strict_mode(ctx)) {
- JS_ThrowReferenceErrorNotDefined(ctx, atom);
- JS_FreeAtom(ctx, atom);
- goto exception;
- }
- val = JS_UNDEFINED;
- } else {
- val = JS_GetProperty(ctx, sp[-2], atom);
- }
- JS_FreeAtom(ctx, atom);
- if (unlikely(JS_IsException(val)))
- goto exception;
- sp[0] = val;
- sp++;
- }
- BREAK;
- CASE(OP_get_super_value):
- {
- JSValue val;
- JSAtom atom;
- sf->cur_pc = pc;
- atom = JS_ValueToAtom(ctx, sp[-1]);
- if (unlikely(atom == JS_ATOM_NULL))
- goto exception;
- val = JS_GetPropertyInternal(ctx, sp[-2], atom, sp[-3], FALSE);
- JS_FreeAtom(ctx, atom);
- if (unlikely(JS_IsException(val)))
- goto exception;
- JS_FreeValue(ctx, sp[-1]);
- JS_FreeValue(ctx, sp[-2]);
- JS_FreeValue(ctx, sp[-3]);
- sp[-3] = val;
- sp -= 2;
- }
- BREAK;
- CASE(OP_put_array_el):
- {
- int ret;
- JSObject *p;
- uint32_t idx;
- if (likely(JS_VALUE_GET_TAG(sp[-3]) == JS_TAG_OBJECT &&
- JS_VALUE_GET_TAG(sp[-2]) == JS_TAG_INT)) {
- p = JS_VALUE_GET_OBJ(sp[-3]);
- idx = JS_VALUE_GET_INT(sp[-2]);
- if (unlikely(p->class_id != JS_CLASS_ARRAY))
- goto put_array_el_slow_path;
- if (unlikely(idx >= (uint32_t)p->u.array.count)) {
- uint32_t new_len, array_len;
- if (unlikely(idx != (uint32_t)p->u.array.count ||
- !p->fast_array ||
- !can_extend_fast_array(p))) {
- goto put_array_el_slow_path;
- }
- if (likely(JS_VALUE_GET_TAG(p->prop[0].u.value) != JS_TAG_INT))
- goto put_array_el_slow_path;
- /* cannot overflow otherwise the length would not be an integer */
- new_len = idx + 1;
- if (unlikely(new_len > p->u.array.u1.size))
- goto put_array_el_slow_path;
- array_len = JS_VALUE_GET_INT(p->prop[0].u.value);
- if (new_len > array_len) {
- if (unlikely(!(get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE)))
- goto put_array_el_slow_path;
- p->prop[0].u.value = JS_NewInt32(ctx, new_len);
- }
- p->u.array.count = new_len;
- p->u.array.u.values[idx] = sp[-1];
- } else {
- set_value(ctx, &p->u.array.u.values[idx], sp[-1]);
- }
- JS_FreeValue(ctx, sp[-3]);
- sp -= 3;
- } else {
- put_array_el_slow_path:
- sf->cur_pc = pc;
- ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], JS_PROP_THROW_STRICT);
- JS_FreeValue(ctx, sp[-3]);
- sp -= 3;
- if (unlikely(ret < 0))
- goto exception;
- }
- }
- BREAK;
- CASE(OP_put_ref_value):
- {
- int ret;
- JSAtom atom;
- sf->cur_pc = pc;
- atom = JS_ValueToAtom(ctx, sp[-2]);
- if (unlikely(atom == JS_ATOM_NULL))
- goto exception;
- if (unlikely(JS_IsUndefined(sp[-3]))) {
- if (is_strict_mode(ctx)) {
- JS_ThrowReferenceErrorNotDefined(ctx, atom);
- JS_FreeAtom(ctx, atom);
- goto exception;
- } else {
- sp[-3] = JS_DupValue(ctx, ctx->global_obj);
- }
- }
- ret = JS_HasProperty(ctx, sp[-3], atom);
- if (unlikely(ret <= 0)) {
- if (unlikely(ret < 0)) {
- JS_FreeAtom(ctx, atom);
- goto exception;
- }
- if (is_strict_mode(ctx)) {
- JS_ThrowReferenceErrorNotDefined(ctx, atom);
- JS_FreeAtom(ctx, atom);
- goto exception;
- }
- }
- ret = JS_SetPropertyInternal(ctx, sp[-3], atom, sp[-1], sp[-3], JS_PROP_THROW_STRICT);
- JS_FreeAtom(ctx, atom);
- JS_FreeValue(ctx, sp[-2]);
- JS_FreeValue(ctx, sp[-3]);
- sp -= 3;
- if (unlikely(ret < 0))
- goto exception;
- }
- BREAK;
- CASE(OP_put_super_value):
- {
- int ret;
- JSAtom atom;
- sf->cur_pc = pc;
- if (JS_VALUE_GET_TAG(sp[-3]) != JS_TAG_OBJECT) {
- JS_ThrowTypeErrorNotAnObject(ctx);
- goto exception;
- }
- atom = JS_ValueToAtom(ctx, sp[-2]);
- if (unlikely(atom == JS_ATOM_NULL))
- goto exception;
- ret = JS_SetPropertyInternal(ctx, sp[-3], atom, sp[-1], sp[-4],
- JS_PROP_THROW_STRICT);
- JS_FreeAtom(ctx, atom);
- JS_FreeValue(ctx, sp[-4]);
- JS_FreeValue(ctx, sp[-3]);
- JS_FreeValue(ctx, sp[-2]);
- sp -= 4;
- if (ret < 0)
- goto exception;
- }
- BREAK;
- CASE(OP_define_array_el):
- {
- int ret;
- ret = JS_DefinePropertyValueValue(ctx, sp[-3], JS_DupValue(ctx, sp[-2]), sp[-1],
- JS_PROP_C_W_E | JS_PROP_THROW);
- sp -= 1;
- if (unlikely(ret < 0))
- goto exception;
- }
- BREAK;
- CASE(OP_append): /* array pos enumobj -- array pos */
- {
- sf->cur_pc = pc;
- if (js_append_enumerate(ctx, sp))
- goto exception;
- JS_FreeValue(ctx, *--sp);
- }
- BREAK;
- CASE(OP_copy_data_properties): /* target source excludeList */
- {
- /* stack offsets (-1 based):
- 2 bits for target,
- 3 bits for source,
- 2 bits for exclusionList */
- int mask;
- mask = *pc++;
- sf->cur_pc = pc;
- if (JS_CopyDataProperties(ctx, sp[-1 - (mask & 3)],
- sp[-1 - ((mask >> 2) & 7)],
- sp[-1 - ((mask >> 5) & 7)], 0))
- goto exception;
- }
- BREAK;
- CASE(OP_add):
- {
- JSValue op1, op2;
- op1 = sp[-2];
- op2 = sp[-1];
- if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
- int64_t r;
- r = (int64_t)JS_VALUE_GET_INT(op1) + JS_VALUE_GET_INT(op2);
- if (unlikely((int)r != r)) {
- sp[-2] = __JS_NewFloat64(ctx, (double)r);
- } else {
- sp[-2] = JS_NewInt32(ctx, r);
- }
- sp--;
- } else if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1)) ||
- JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op2))) {
- double d1, d2;
- if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1))) {
- d1 = JS_VALUE_GET_FLOAT64(op1);
- } else if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
- d1 = JS_VALUE_GET_INT(op1);
- } else {
- goto add_slow_case;
- }
- if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op2))) {
- d2 = JS_VALUE_GET_FLOAT64(op2);
- } else if (JS_VALUE_GET_TAG(op2) == JS_TAG_INT) {
- d2 = JS_VALUE_GET_INT(op2);
- } else {
- goto add_slow_case;
- }
- sp[-2] = __JS_NewFloat64(ctx, d1 + d2);
- sp--;
- } else if (JS_IsString(op1) && JS_IsString(op2)) {
- sp[-2] = JS_ConcatString(ctx, op1, op2);
- sp--;
- if (JS_IsException(sp[-1]))
- goto exception;
- } else {
- add_slow_case:
- sf->cur_pc = pc;
- if (js_add_slow(ctx, sp))
- goto exception;
- sp--;
- }
- }
- BREAK;
- CASE(OP_add_loc):
- {
- JSValue op2;
- JSValue *pv;
- int idx;
- idx = *pc;
- pc += 1;
- op2 = sp[-1];
- pv = &var_buf[idx];
- if (likely(JS_VALUE_IS_BOTH_INT(*pv, op2))) {
- int64_t r;
- r = (int64_t)JS_VALUE_GET_INT(*pv) + JS_VALUE_GET_INT(op2);
- if (unlikely((int)r != r)) {
- *pv = __JS_NewFloat64(ctx, (double)r);
- } else {
- *pv = JS_NewInt32(ctx, r);
- }
- sp--;
- } else if (JS_VALUE_IS_BOTH_FLOAT(*pv, op2)) {
- *pv = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(*pv) +
- JS_VALUE_GET_FLOAT64(op2));
- sp--;
- } else if (JS_VALUE_GET_TAG(*pv) == JS_TAG_STRING &&
- JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) {
- sp--;
- sf->cur_pc = pc;
- if (JS_ConcatStringInPlace(ctx, JS_VALUE_GET_STRING(*pv), op2)) {
- JS_FreeValue(ctx, op2);
- } else {
- op2 = JS_ConcatString(ctx, JS_DupValue(ctx, *pv), op2);
- if (JS_IsException(op2))
- goto exception;
- set_value(ctx, pv, op2);
- }
- } else {
- JSValue ops[2];
- /* In case of exception, js_add_slow frees ops[0]
- and ops[1], so we must duplicate *pv */
- sf->cur_pc = pc;
- ops[0] = JS_DupValue(ctx, *pv);
- ops[1] = op2;
- sp--;
- if (js_add_slow(ctx, ops + 2))
- goto exception;
- set_value(ctx, pv, ops[0]);
- }
- }
- BREAK;
- CASE(OP_sub):
- {
- JSValue op1, op2;
- op1 = sp[-2];
- op2 = sp[-1];
- if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
- int64_t r;
- r = (int64_t)JS_VALUE_GET_INT(op1) - JS_VALUE_GET_INT(op2);
- if (unlikely((int)r != r)) {
- sp[-2] = __JS_NewFloat64(ctx, (double)r);
- } else {
- sp[-2] = JS_NewInt32(ctx, r);
- }
- sp--;
- } else if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1)) ||
- JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op2))) {
- double d1, d2;
- if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1))) {
- d1 = JS_VALUE_GET_FLOAT64(op1);
- } else if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
- d1 = JS_VALUE_GET_INT(op1);
- } else {
- goto binary_arith_slow;
- }
- if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op2))) {
- d2 = JS_VALUE_GET_FLOAT64(op2);
- } else if (JS_VALUE_GET_TAG(op2) == JS_TAG_INT) {
- d2 = JS_VALUE_GET_INT(op2);
- } else {
- goto binary_arith_slow;
- }
- sp[-2] = __JS_NewFloat64(ctx, d1 - d2);
- sp--;
- } else {
- goto binary_arith_slow;
- }
- }
- BREAK;
- CASE(OP_mul):
- {
- JSValue op1, op2;
- double d;
- op1 = sp[-2];
- op2 = sp[-1];
- if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
- int32_t v1, v2;
- int64_t r;
- v1 = JS_VALUE_GET_INT(op1);
- v2 = JS_VALUE_GET_INT(op2);
- r = (int64_t)v1 * v2;
- if (unlikely((int)r != r)) {
- d = (double)r;
- goto mul_fp_res;
- }
- /* need to test zero case for -0 result */
- if (unlikely(r == 0 && (v1 | v2) < 0)) {
- d = -0.0;
- goto mul_fp_res;
- }
- sp[-2] = JS_NewInt32(ctx, r);
- sp--;
- } else if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1)) ||
- JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op2))) {
- double d1, d2;
- if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1))) {
- d1 = JS_VALUE_GET_FLOAT64(op1);
- } else if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
- d1 = JS_VALUE_GET_INT(op1);
- } else {
- goto binary_arith_slow;
- }
- if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op2))) {
- d2 = JS_VALUE_GET_FLOAT64(op2);
- } else if (JS_VALUE_GET_TAG(op2) == JS_TAG_INT) {
- d2 = JS_VALUE_GET_INT(op2);
- } else {
- goto binary_arith_slow;
- }
- d = d1 * d2;
- mul_fp_res:
- sp[-2] = __JS_NewFloat64(ctx, d);
- sp--;
- } else {
- goto binary_arith_slow;
- }
- }
- BREAK;
- CASE(OP_div):
- {
- JSValue op1, op2;
- op1 = sp[-2];
- op2 = sp[-1];
- if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
- int v1, v2;
- v1 = JS_VALUE_GET_INT(op1);
- v2 = JS_VALUE_GET_INT(op2);
- sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2);
- sp--;
- } else {
- goto binary_arith_slow;
- }
- }
- BREAK;
- CASE(OP_mod):
- {
- JSValue op1, op2;
- op1 = sp[-2];
- op2 = sp[-1];
- if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
- int v1, v2, r;
- v1 = JS_VALUE_GET_INT(op1);
- v2 = JS_VALUE_GET_INT(op2);
- /* We must avoid v2 = 0, v1 = INT32_MIN and v2 =
- -1 and the cases where the result is -0. */
- if (unlikely(v1 < 0 || v2 <= 0))
- goto binary_arith_slow;
- r = v1 % v2;
- sp[-2] = JS_NewInt32(ctx, r);
- sp--;
- } else {
- goto binary_arith_slow;
- }
- }
- BREAK;
- CASE(OP_pow):
- binary_arith_slow:
- sf->cur_pc = pc;
- if (js_binary_arith_slow(ctx, sp, opcode))
- goto exception;
- sp--;
- BREAK;
- CASE(OP_plus):
- {
- JSValue op1;
- uint32_t tag;
- op1 = sp[-1];
- tag = JS_VALUE_GET_TAG(op1);
- if (tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag)) {
- } else if (tag == JS_TAG_NULL || tag == JS_TAG_BOOL) {
- sp[-1] = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1));
- } else {
- sf->cur_pc = pc;
- if (js_unary_arith_slow(ctx, sp, opcode))
- goto exception;
- }
- }
- BREAK;
- CASE(OP_neg):
- {
- JSValue op1;
- uint32_t tag;
- int val;
- double d;
- op1 = sp[-1];
- tag = JS_VALUE_GET_TAG(op1);
- if (tag == JS_TAG_INT ||
- tag == JS_TAG_BOOL ||
- tag == JS_TAG_NULL) {
- val = JS_VALUE_GET_INT(op1);
- /* Note: -0 cannot be expressed as integer */
- if (unlikely(val == 0)) {
- d = -0.0;
- goto neg_fp_res;
- }
- if (unlikely(val == INT32_MIN)) {
- d = -(double)val;
- goto neg_fp_res;
- }
- sp[-1] = JS_NewInt32(ctx, -val);
- } else if (JS_TAG_IS_FLOAT64(tag)) {
- d = -JS_VALUE_GET_FLOAT64(op1);
- neg_fp_res:
- sp[-1] = __JS_NewFloat64(ctx, d);
- } else {
- sf->cur_pc = pc;
- if (js_unary_arith_slow(ctx, sp, opcode))
- goto exception;
- }
- }
- BREAK;
- CASE(OP_inc):
- {
- JSValue op1;
- int val;
- op1 = sp[-1];
- if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
- val = JS_VALUE_GET_INT(op1);
- if (unlikely(val == INT32_MAX))
- goto inc_slow;
- sp[-1] = JS_NewInt32(ctx, val + 1);
- } else {
- inc_slow:
- sf->cur_pc = pc;
- if (js_unary_arith_slow(ctx, sp, opcode))
- goto exception;
- }
- }
- BREAK;
- CASE(OP_dec):
- {
- JSValue op1;
- int val;
- op1 = sp[-1];
- if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
- val = JS_VALUE_GET_INT(op1);
- if (unlikely(val == INT32_MIN))
- goto dec_slow;
- sp[-1] = JS_NewInt32(ctx, val - 1);
- } else {
- dec_slow:
- sf->cur_pc = pc;
- if (js_unary_arith_slow(ctx, sp, opcode))
- goto exception;
- }
- }
- BREAK;
- CASE(OP_post_inc):
- {
- JSValue op1;
- int val;
- op1 = sp[-1];
- if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
- val = JS_VALUE_GET_INT(op1);
- if (unlikely(val == INT32_MAX))
- goto post_inc_slow;
- sp[0] = JS_NewInt32(ctx, val + 1);
- } else {
- post_inc_slow:
- sf->cur_pc = pc;
- if (js_post_inc_slow(ctx, sp, opcode))
- goto exception;
- }
- sp++;
- }
- BREAK;
- CASE(OP_post_dec):
- {
- JSValue op1;
- int val;
- op1 = sp[-1];
- if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
- val = JS_VALUE_GET_INT(op1);
- if (unlikely(val == INT32_MIN))
- goto post_dec_slow;
- sp[0] = JS_NewInt32(ctx, val - 1);
- } else {
- post_dec_slow:
- sf->cur_pc = pc;
- if (js_post_inc_slow(ctx, sp, opcode))
- goto exception;
- }
- sp++;
- }
- BREAK;
- CASE(OP_inc_loc):
- {
- JSValue op1;
- int val;
- int idx;
- idx = *pc;
- pc += 1;
- op1 = var_buf[idx];
- if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
- val = JS_VALUE_GET_INT(op1);
- if (unlikely(val == INT32_MAX))
- goto inc_loc_slow;
- var_buf[idx] = JS_NewInt32(ctx, val + 1);
- } else {
- inc_loc_slow:
- sf->cur_pc = pc;
- /* must duplicate otherwise the variable value may
- be destroyed before JS code accesses it */
- op1 = JS_DupValue(ctx, op1);
- if (js_unary_arith_slow(ctx, &op1 + 1, OP_inc))
- goto exception;
- set_value(ctx, &var_buf[idx], op1);
- }
- }
- BREAK;
- CASE(OP_dec_loc):
- {
- JSValue op1;
- int val;
- int idx;
- idx = *pc;
- pc += 1;
- op1 = var_buf[idx];
- if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
- val = JS_VALUE_GET_INT(op1);
- if (unlikely(val == INT32_MIN))
- goto dec_loc_slow;
- var_buf[idx] = JS_NewInt32(ctx, val - 1);
- } else {
- dec_loc_slow:
- sf->cur_pc = pc;
- /* must duplicate otherwise the variable value may
- be destroyed before JS code accesses it */
- op1 = JS_DupValue(ctx, op1);
- if (js_unary_arith_slow(ctx, &op1 + 1, OP_dec))
- goto exception;
- set_value(ctx, &var_buf[idx], op1);
- }
- }
- BREAK;
- CASE(OP_not):
- {
- JSValue op1;
- op1 = sp[-1];
- if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
- sp[-1] = JS_NewInt32(ctx, ~JS_VALUE_GET_INT(op1));
- } else {
- sf->cur_pc = pc;
- if (js_not_slow(ctx, sp))
- goto exception;
- }
- }
- BREAK;
- CASE(OP_shl):
- {
- JSValue op1, op2;
- op1 = sp[-2];
- op2 = sp[-1];
- if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
- uint32_t v1, v2;
- v1 = JS_VALUE_GET_INT(op1);
- v2 = JS_VALUE_GET_INT(op2);
- v2 &= 0x1f;
- sp[-2] = JS_NewInt32(ctx, v1 << v2);
- sp--;
- } else {
- sf->cur_pc = pc;
- if (js_binary_logic_slow(ctx, sp, opcode))
- goto exception;
- sp--;
- }
- }
- BREAK;
- CASE(OP_shr):
- {
- JSValue op1, op2;
- op1 = sp[-2];
- op2 = sp[-1];
- if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
- uint32_t v2;
- v2 = JS_VALUE_GET_INT(op2);
- v2 &= 0x1f;
- sp[-2] = JS_NewUint32(ctx,
- (uint32_t)JS_VALUE_GET_INT(op1) >>
- v2);
- sp--;
- } else {
- sf->cur_pc = pc;
- if (js_shr_slow(ctx, sp))
- goto exception;
- sp--;
- }
- }
- BREAK;
- CASE(OP_sar):
- {
- JSValue op1, op2;
- op1 = sp[-2];
- op2 = sp[-1];
- if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
- uint32_t v2;
- v2 = JS_VALUE_GET_INT(op2);
- v2 &= 0x1f;
- sp[-2] = JS_NewInt32(ctx,
- (int)JS_VALUE_GET_INT(op1) >> v2);
- sp--;
- } else {
- sf->cur_pc = pc;
- if (js_binary_logic_slow(ctx, sp, opcode))
- goto exception;
- sp--;
- }
- }
- BREAK;
- CASE(OP_and):
- {
- JSValue op1, op2;
- op1 = sp[-2];
- op2 = sp[-1];
- if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
- sp[-2] = JS_NewInt32(ctx,
- JS_VALUE_GET_INT(op1) &
- JS_VALUE_GET_INT(op2));
- sp--;
- } else {
- sf->cur_pc = pc;
- if (js_binary_logic_slow(ctx, sp, opcode))
- goto exception;
- sp--;
- }
- }
- BREAK;
- CASE(OP_or):
- {
- JSValue op1, op2;
- op1 = sp[-2];
- op2 = sp[-1];
- if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
- sp[-2] = JS_NewInt32(ctx,
- JS_VALUE_GET_INT(op1) |
- JS_VALUE_GET_INT(op2));
- sp--;
- } else {
- sf->cur_pc = pc;
- if (js_binary_logic_slow(ctx, sp, opcode))
- goto exception;
- sp--;
- }
- }
- BREAK;
- CASE(OP_xor):
- {
- JSValue op1, op2;
- op1 = sp[-2];
- op2 = sp[-1];
- if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
- sp[-2] = JS_NewInt32(ctx,
- JS_VALUE_GET_INT(op1) ^
- JS_VALUE_GET_INT(op2));
- sp--;
- } else {
- sf->cur_pc = pc;
- if (js_binary_logic_slow(ctx, sp, opcode))
- goto exception;
- sp--;
- }
- }
- BREAK;
- #define OP_CMP(opcode, binary_op, slow_call) \
- CASE(opcode): \
- { \
- JSValue op1, op2; \
- op1 = sp[-2]; \
- op2 = sp[-1]; \
- if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { \
- sp[-2] = JS_NewBool(ctx, JS_VALUE_GET_INT(op1) binary_op JS_VALUE_GET_INT(op2)); \
- sp--; \
- } else if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1)) || \
- JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op2))) { \
- double d1, d2; \
- if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1))) { \
- d1 = JS_VALUE_GET_FLOAT64(op1); \
- } else if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { \
- d1 = JS_VALUE_GET_INT(op1); \
- } else { \
- goto opcode ## _slow_case; \
- } \
- if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op2))) { \
- d2 = JS_VALUE_GET_FLOAT64(op2); \
- } else if (JS_VALUE_GET_TAG(op2) == JS_TAG_INT) { \
- d2 = JS_VALUE_GET_INT(op2); \
- } else { \
- goto opcode ## _slow_case; \
- } \
- sp[-2] = JS_NewBool(ctx, d1 binary_op d2); \
- sp--; \
- } else { \
- opcode ## _slow_case: \
- sf->cur_pc = pc; \
- if (slow_call) \
- goto exception; \
- sp--; \
- } \
- } \
- BREAK
- OP_CMP(OP_lt, <, js_relational_slow(ctx, sp, opcode));
- OP_CMP(OP_lte, <=, js_relational_slow(ctx, sp, opcode));
- OP_CMP(OP_gt, >, js_relational_slow(ctx, sp, opcode));
- OP_CMP(OP_gte, >=, js_relational_slow(ctx, sp, opcode));
- #define OP_CMP_EQ(opcode, inv) \
- CASE(opcode): \
- { \
- JSValue op1, op2; \
- int res; \
- uint32_t tag1, tag2; \
- op1 = sp[-2]; \
- op2 = sp[-1]; \
- tag1 = JS_VALUE_GET_TAG(op1); \
- tag2 = JS_VALUE_GET_TAG(op2); \
- if (likely(tag1 == JS_TAG_INT)) { \
- if (tag2 == JS_TAG_INT) { \
- res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2); \
- } else if (JS_TAG_IS_FLOAT64(tag2)) { \
- res = (JS_VALUE_GET_INT(op1) == JS_VALUE_GET_FLOAT64(op2)); \
- } else { \
- goto slow_eq ## inv; \
- } \
- } else if (JS_TAG_IS_FLOAT64(tag1)) { \
- if (tag2 == JS_TAG_INT) { \
- res = JS_VALUE_GET_FLOAT64(op1) == JS_VALUE_GET_INT(op2); \
- } else if (JS_TAG_IS_FLOAT64(tag2)) { \
- res = (JS_VALUE_GET_FLOAT64(op1) == JS_VALUE_GET_FLOAT64(op2)); \
- } else { \
- goto slow_eq ## inv; \
- } \
- } else if (tag1 == JS_TAG_OBJECT) { \
- if (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED) { \
- JSObject *p = JS_VALUE_GET_OBJ(op1); \
- res = p->is_HTMLDDA; \
- JS_FreeValue(ctx, op1); \
- } else if (tag2 == JS_TAG_OBJECT) { \
- res = JS_VALUE_GET_OBJ(op1) == JS_VALUE_GET_OBJ(op2); \
- JS_FreeValue(ctx, op1); \
- JS_FreeValue(ctx, op2); \
- } else { \
- goto slow_eq ## inv; \
- } \
- } else if (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED) { \
- if (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED) { \
- res = TRUE; \
- } else if (tag2 == JS_TAG_OBJECT) { \
- JSObject *p = JS_VALUE_GET_OBJ(op2); \
- res = p->is_HTMLDDA; \
- JS_FreeValue(ctx, op2); \
- } else { \
- goto slow_eq ## inv; \
- } \
- } else if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) { \
- res = js_string_eq(ctx, JS_VALUE_GET_STRING(op1), \
- JS_VALUE_GET_STRING(op2)); \
- JS_FreeValue(ctx, op1); \
- JS_FreeValue(ctx, op2); \
- } else { \
- slow_eq ## inv: \
- sf->cur_pc = pc; \
- if (js_eq_slow(ctx, sp, inv)) \
- goto exception; \
- sp--; \
- goto slow_eq_done ## inv; \
- } \
- sp[-2] = JS_NewBool(ctx, res ^ inv); \
- sp--; \
- slow_eq_done ## inv: ; \
- } \
- BREAK
- OP_CMP_EQ(OP_eq, 0);
- OP_CMP_EQ(OP_neq, 1);
- #define OP_CMP_STRICT_EQ(opcode, inv) \
- CASE(opcode): \
- { \
- JSValue op1, op2; \
- int res; \
- uint32_t tag1, tag2; \
- op1 = sp[-2]; \
- op2 = sp[-1]; \
- tag1 = JS_VALUE_GET_TAG(op1); \
- tag2 = JS_VALUE_GET_TAG(op2); \
- if (likely(tag1 == JS_TAG_INT)) { \
- if (tag2 == JS_TAG_INT) { \
- res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2); \
- } else if (JS_TAG_IS_FLOAT64(tag2)) { \
- res = (JS_VALUE_GET_INT(op1) == JS_VALUE_GET_FLOAT64(op2)); \
- } else { \
- JS_FreeValue(ctx, op2); \
- res = FALSE; \
- } \
- } else if (JS_TAG_IS_FLOAT64(tag1)) { \
- if (tag2 == JS_TAG_INT) { \
- res = JS_VALUE_GET_FLOAT64(op1) == JS_VALUE_GET_INT(op2); \
- } else if (JS_TAG_IS_FLOAT64(tag2)) { \
- res = (JS_VALUE_GET_FLOAT64(op1) == JS_VALUE_GET_FLOAT64(op2)); \
- } else { \
- JS_FreeValue(ctx, op2); \
- res = FALSE; \
- } \
- } else if (tag1 == JS_TAG_OBJECT) { \
- if (tag2 == JS_TAG_OBJECT) { \
- res = JS_VALUE_GET_OBJ(op1) == JS_VALUE_GET_OBJ(op2); \
- } else { \
- res = FALSE; \
- } \
- JS_FreeValue(ctx, op1); \
- JS_FreeValue(ctx, op2); \
- } else if (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED) { \
- res = (tag1 == tag2); \
- JS_FreeValue(ctx, op2); \
- } else if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) { \
- res = js_string_eq(ctx, JS_VALUE_GET_STRING(op1), \
- JS_VALUE_GET_STRING(op2)); \
- JS_FreeValue(ctx, op1); \
- JS_FreeValue(ctx, op2); \
- } else { \
- res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT); \
- JS_FreeValue(ctx, op1); \
- JS_FreeValue(ctx, op2); \
- } \
- sp[-2] = JS_NewBool(ctx, res ^ inv); \
- sp--; \
- } \
- BREAK
- OP_CMP_STRICT_EQ(OP_strict_eq, 0);
- OP_CMP_STRICT_EQ(OP_strict_neq, 1);
- CASE(OP_in):
- sf->cur_pc = pc;
- if (js_operator_in(ctx, sp))
- goto exception;
- sp--;
- BREAK;
- CASE(OP_private_in):
- sf->cur_pc = pc;
- if (js_operator_private_in(ctx, sp))
- goto exception;
- sp--;
- BREAK;
- CASE(OP_instanceof):
- sf->cur_pc = pc;
- if (js_operator_instanceof(ctx, sp))
- goto exception;
- sp--;
- BREAK;
- CASE(OP_typeof):
- {
- JSValue op1;
- JSAtom atom;
- op1 = sp[-1];
- atom = js_operator_typeof(ctx, op1);
- JS_FreeValue(ctx, op1);
- sp[-1] = JS_AtomToString(ctx, atom);
- }
- BREAK;
- CASE(OP_delete):
- sf->cur_pc = pc;
- if (js_operator_delete(ctx, sp))
- goto exception;
- sp--;
- BREAK;
- CASE(OP_delete_var):
- {
- JSAtom atom;
- int ret;
- atom = get_u32(pc);
- pc += 4;
- sf->cur_pc = pc;
- ret = JS_DeleteGlobalVar(ctx, atom);
- if (unlikely(ret < 0))
- goto exception;
- *sp++ = JS_NewBool(ctx, ret);
- }
- BREAK;
- CASE(OP_to_object):
- if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_OBJECT) {
- sf->cur_pc = pc;
- ret_val = JS_ToObject(ctx, sp[-1]);
- if (JS_IsException(ret_val))
- goto exception;
- JS_FreeValue(ctx, sp[-1]);
- sp[-1] = ret_val;
- }
- BREAK;
- CASE(OP_to_propkey):
- switch (JS_VALUE_GET_TAG(sp[-1])) {
- case JS_TAG_INT:
- case JS_TAG_STRING:
- case JS_TAG_SYMBOL:
- break;
- default:
- sf->cur_pc = pc;
- ret_val = JS_ToPropertyKey(ctx, sp[-1]);
- if (JS_IsException(ret_val))
- goto exception;
- JS_FreeValue(ctx, sp[-1]);
- sp[-1] = ret_val;
- break;
- }
- BREAK;
- #if 0
- CASE(OP_to_string):
- if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_STRING) {
- ret_val = JS_ToString(ctx, sp[-1]);
- if (JS_IsException(ret_val))
- goto exception;
- JS_FreeValue(ctx, sp[-1]);
- sp[-1] = ret_val;
- }
- BREAK;
- #endif
- CASE(OP_with_get_var):
- CASE(OP_with_put_var):
- CASE(OP_with_delete_var):
- CASE(OP_with_make_ref):
- CASE(OP_with_get_ref):
- {
- JSAtom atom;
- int32_t diff;
- JSValue obj, val;
- int ret, is_with;
- atom = get_u32(pc);
- diff = get_u32(pc + 4);
- is_with = pc[8];
- pc += 9;
- sf->cur_pc = pc;
- obj = sp[-1];
- ret = JS_HasProperty(ctx, obj, atom);
- if (unlikely(ret < 0))
- goto exception;
- if (ret) {
- if (is_with) {
- ret = js_has_unscopable(ctx, obj, atom);
- if (unlikely(ret < 0))
- goto exception;
- if (ret)
- goto no_with;
- }
- switch (opcode) {
- case OP_with_get_var:
- /* in Object Environment Records, GetBindingValue() calls HasProperty() */
- ret = JS_HasProperty(ctx, obj, atom);
- if (unlikely(ret <= 0)) {
- if (ret < 0)
- goto exception;
- if (is_strict_mode(ctx)) {
- JS_ThrowReferenceErrorNotDefined(ctx, atom);
- goto exception;
- }
- val = JS_UNDEFINED;
- } else {
- val = JS_GetProperty(ctx, obj, atom);
- if (unlikely(JS_IsException(val)))
- goto exception;
- }
- set_value(ctx, &sp[-1], val);
- break;
- case OP_with_put_var: /* used e.g. in for in/of */
- /* in Object Environment Records, SetMutableBinding() calls HasProperty() */
- ret = JS_HasProperty(ctx, obj, atom);
- if (unlikely(ret <= 0)) {
- if (ret < 0)
- goto exception;
- if (is_strict_mode(ctx)) {
- JS_ThrowReferenceErrorNotDefined(ctx, atom);
- goto exception;
- }
- }
- ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-2], obj,
- JS_PROP_THROW_STRICT);
- JS_FreeValue(ctx, sp[-1]);
- sp -= 2;
- if (unlikely(ret < 0))
- goto exception;
- break;
- case OP_with_delete_var:
- ret = JS_DeleteProperty(ctx, obj, atom, 0);
- if (unlikely(ret < 0))
- goto exception;
- JS_FreeValue(ctx, sp[-1]);
- sp[-1] = JS_NewBool(ctx, ret);
- break;
- case OP_with_make_ref:
- /* produce a pair object/propname on the stack */
- *sp++ = JS_AtomToValue(ctx, atom);
- break;
- case OP_with_get_ref:
- /* produce a pair object/method on the stack */
- /* in Object Environment Records, GetBindingValue() calls HasProperty() */
- ret = JS_HasProperty(ctx, obj, atom);
- if (unlikely(ret < 0))
- goto exception;
- if (!ret) {
- val = JS_UNDEFINED;
- } else {
- val = JS_GetProperty(ctx, obj, atom);
- if (unlikely(JS_IsException(val)))
- goto exception;
- }
- *sp++ = val;
- break;
- }
- pc += diff - 5;
- } else {
- no_with:
- /* if not jumping, drop the object argument */
- JS_FreeValue(ctx, sp[-1]);
- sp--;
- }
- }
- BREAK;
- CASE(OP_await):
- ret_val = JS_NewInt32(ctx, FUNC_RET_AWAIT);
- goto done_generator;
- CASE(OP_yield):
- ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD);
- goto done_generator;
- CASE(OP_yield_star):
- CASE(OP_async_yield_star):
- ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD_STAR);
- goto done_generator;
- CASE(OP_return_async):
- ret_val = JS_UNDEFINED;
- goto done_generator;
- CASE(OP_initial_yield):
- ret_val = JS_NewInt32(ctx, FUNC_RET_INITIAL_YIELD);
- goto done_generator;
- CASE(OP_nop):
- BREAK;
- CASE(OP_is_undefined_or_null):
- if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED ||
- JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) {
- goto set_true;
- } else {
- goto free_and_set_false;
- }
- #if SHORT_OPCODES
- CASE(OP_is_undefined):
- if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED) {
- goto set_true;
- } else {
- goto free_and_set_false;
- }
- CASE(OP_is_null):
- if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) {
- goto set_true;
- } else {
- goto free_and_set_false;
- }
- /* XXX: could merge to a single opcode */
- CASE(OP_typeof_is_undefined):
- /* different from OP_is_undefined because of isHTMLDDA */
- if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_undefined) {
- goto free_and_set_true;
- } else {
- goto free_and_set_false;
- }
- CASE(OP_typeof_is_function):
- if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_function) {
- goto free_and_set_true;
- } else {
- goto free_and_set_false;
- }
- free_and_set_true:
- JS_FreeValue(ctx, sp[-1]);
- #endif
- set_true:
- sp[-1] = JS_TRUE;
- BREAK;
- free_and_set_false:
- JS_FreeValue(ctx, sp[-1]);
- sp[-1] = JS_FALSE;
- BREAK;
- CASE(OP_invalid):
- DEFAULT:
- JS_ThrowInternalError(ctx, "invalid opcode: pc=%u opcode=0x%02x",
- (int)(pc - b->byte_code_buf - 1), opcode);
- goto exception;
- }
- }
- exception:
- if (is_backtrace_needed(ctx, rt->current_exception)) {
- /* add the backtrace information now (it is not done
- before if the exception happens in a bytecode
- operation */
- sf->cur_pc = pc;
- build_backtrace(ctx, rt->current_exception, NULL, 0, 0, 0);
- }
- if (!rt->current_exception_is_uncatchable) {
- while (sp > stack_buf) {
- JSValue val = *--sp;
- JS_FreeValue(ctx, val);
- if (JS_VALUE_GET_TAG(val) == JS_TAG_CATCH_OFFSET) {
- int pos = JS_VALUE_GET_INT(val);
- if (pos == 0) {
- /* enumerator: close it with a throw */
- JS_FreeValue(ctx, sp[-1]); /* drop the next method */
- sp--;
- JS_IteratorClose(ctx, sp[-1], TRUE);
- } else {
- *sp++ = rt->current_exception;
- rt->current_exception = JS_UNINITIALIZED;
- pc = b->byte_code_buf + pos;
- goto restart;
- }
- }
- }
- }
- ret_val = JS_EXCEPTION;
- /* the local variables are freed by the caller in the generator
- case. Hence the label 'done' should never be reached in a
- generator function. */
- if (b->func_kind != JS_FUNC_NORMAL) {
- done_generator:
- sf->cur_pc = pc;
- sf->cur_sp = sp;
- } else {
- done:
- if (unlikely(b->var_ref_count != 0)) {
- /* variable references reference the stack: must close them */
- close_var_refs(rt, b, sf);
- }
- /* free the local variables and stack */
- for(pval = local_buf; pval < sp; pval++) {
- JS_FreeValue(ctx, *pval);
- }
- }
- rt->current_stack_frame = sf->prev_frame;
- return ret_val;
- }
- #ifdef OPCODE_ASM_LABEL
- #pragma GCC diagnostic pop
- #endif
- JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj,
- int argc, JSValueConst *argv)
- {
- return JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED,
- argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV);
- }
- static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj,
- int argc, JSValueConst *argv)
- {
- JSValue res = JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED,
- argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV);
- JS_FreeValue(ctx, func_obj);
- return res;
- }
- /* warning: the refcount of the context is not incremented. Return
- NULL in case of exception (case of revoked proxy only) */
- static JSContext *JS_GetFunctionRealm(JSContext *ctx, JSValueConst func_obj)
- {
- JSObject *p;
- JSContext *realm;
- if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)
- return ctx;
- p = JS_VALUE_GET_OBJ(func_obj);
- switch(p->class_id) {
- case JS_CLASS_C_FUNCTION:
- realm = p->u.cfunc.realm;
- break;
- case JS_CLASS_BYTECODE_FUNCTION:
- case JS_CLASS_GENERATOR_FUNCTION:
- case JS_CLASS_ASYNC_FUNCTION:
- case JS_CLASS_ASYNC_GENERATOR_FUNCTION:
- {
- JSFunctionBytecode *b;
- b = p->u.func.function_bytecode;
- realm = b->realm;
- }
- break;
- case JS_CLASS_PROXY:
- {
- JSProxyData *s = p->u.opaque;
- if (!s)
- return ctx;
- if (s->is_revoked) {
- JS_ThrowTypeErrorRevokedProxy(ctx);
- return NULL;
- } else {
- realm = JS_GetFunctionRealm(ctx, s->target);
- }
- }
- break;
- case JS_CLASS_BOUND_FUNCTION:
- {
- JSBoundFunction *bf = p->u.bound_function;
- realm = JS_GetFunctionRealm(ctx, bf->func_obj);
- }
- break;
- default:
- realm = ctx;
- break;
- }
- return realm;
- }
- static JSValue js_create_from_ctor(JSContext *ctx, JSValueConst ctor,
- int class_id)
- {
- JSValue proto, obj;
- JSContext *realm;
- if (JS_IsUndefined(ctor)) {
- proto = JS_DupValue(ctx, ctx->class_proto[class_id]);
- } else {
- proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype);
- if (JS_IsException(proto))
- return proto;
- if (!JS_IsObject(proto)) {
- JS_FreeValue(ctx, proto);
- realm = JS_GetFunctionRealm(ctx, ctor);
- if (!realm)
- return JS_EXCEPTION;
- proto = JS_DupValue(ctx, realm->class_proto[class_id]);
- }
- }
- obj = JS_NewObjectProtoClass(ctx, proto, class_id);
- JS_FreeValue(ctx, proto);
- return obj;
- }
- /* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */
- static JSValue JS_CallConstructorInternal(JSContext *ctx,
- JSValueConst func_obj,
- JSValueConst new_target,
- int argc, JSValue *argv, int flags)
- {
- JSObject *p;
- JSFunctionBytecode *b;
- if (js_poll_interrupts(ctx))
- return JS_EXCEPTION;
- flags |= JS_CALL_FLAG_CONSTRUCTOR;
- if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT))
- goto not_a_function;
- p = JS_VALUE_GET_OBJ(func_obj);
- if (unlikely(!p->is_constructor))
- return JS_ThrowTypeErrorNotAConstructor(ctx, func_obj);
- if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) {
- JSClassCall *call_func;
- call_func = ctx->rt->class_array[p->class_id].call;
- if (!call_func) {
- not_a_function:
- return JS_ThrowTypeError(ctx, "not a function");
- }
- return call_func(ctx, func_obj, new_target, argc,
- (JSValueConst *)argv, flags);
- }
- b = p->u.func.function_bytecode;
- if (b->is_derived_class_constructor) {
- return JS_CallInternal(ctx, func_obj, JS_UNDEFINED, new_target, argc, argv, flags);
- } else {
- JSValue obj, ret;
- /* legacy constructor behavior */
- obj = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT);
- if (JS_IsException(obj))
- return JS_EXCEPTION;
- ret = JS_CallInternal(ctx, func_obj, obj, new_target, argc, argv, flags);
- if (JS_VALUE_GET_TAG(ret) == JS_TAG_OBJECT ||
- JS_IsException(ret)) {
- JS_FreeValue(ctx, obj);
- return ret;
- } else {
- JS_FreeValue(ctx, ret);
- return obj;
- }
- }
- }
- JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj,
- JSValueConst new_target,
- int argc, JSValueConst *argv)
- {
- return JS_CallConstructorInternal(ctx, func_obj, new_target,
- argc, (JSValue *)argv,
- JS_CALL_FLAG_COPY_ARGV);
- }
- JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj,
- int argc, JSValueConst *argv)
- {
- return JS_CallConstructorInternal(ctx, func_obj, func_obj,
- argc, (JSValue *)argv,
- JS_CALL_FLAG_COPY_ARGV);
- }
- JSValue JS_Invoke(JSContext *ctx, JSValueConst this_val, JSAtom atom,
- int argc, JSValueConst *argv)
- {
- JSValue func_obj;
- func_obj = JS_GetProperty(ctx, this_val, atom);
- if (JS_IsException(func_obj))
- return func_obj;
- return JS_CallFree(ctx, func_obj, this_val, argc, argv);
- }
- static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom,
- int argc, JSValueConst *argv)
- {
- JSValue res = JS_Invoke(ctx, this_val, atom, argc, argv);
- JS_FreeValue(ctx, this_val);
- return res;
- }
- /* JSAsyncFunctionState (used by generator and async functions) */
- static JSAsyncFunctionState *async_func_init(JSContext *ctx,
- JSValueConst func_obj, JSValueConst this_obj,
- int argc, JSValueConst *argv)
- {
- JSAsyncFunctionState *s;
- JSObject *p;
- JSFunctionBytecode *b;
- JSStackFrame *sf;
- int i, arg_buf_len, n;
- p = JS_VALUE_GET_OBJ(func_obj);
- b = p->u.func.function_bytecode;
- arg_buf_len = max_int(b->arg_count, argc);
- s = js_malloc(ctx, sizeof(*s) + sizeof(JSValue) * (arg_buf_len + b->var_count + b->stack_size) + sizeof(JSVarRef *) * b->var_ref_count);
- if (!s)
- return NULL;
- memset(s, 0, sizeof(*s));
- js_rc(s)->ref_count = 1;
- add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION);
- sf = &s->frame;
- sf->js_mode = b->js_mode | JS_MODE_ASYNC;
- sf->cur_pc = b->byte_code_buf;
- sf->arg_buf = (JSValue *)(s + 1);
- sf->cur_func = JS_DupValue(ctx, func_obj);
- s->this_val = JS_DupValue(ctx, this_obj);
- s->argc = argc;
- sf->arg_count = arg_buf_len;
- sf->var_buf = sf->arg_buf + arg_buf_len;
- sf->cur_sp = sf->var_buf + b->var_count;
- sf->var_refs = (JSVarRef **)(sf->cur_sp + b->stack_size);
- for(i = 0; i < b->var_ref_count; i++)
- sf->var_refs[i] = NULL;
- for(i = 0; i < argc; i++)
- sf->arg_buf[i] = JS_DupValue(ctx, argv[i]);
- n = arg_buf_len + b->var_count;
- for(i = argc; i < n; i++)
- sf->arg_buf[i] = JS_UNDEFINED;
- s->resolving_funcs[0] = JS_UNDEFINED;
- s->resolving_funcs[1] = JS_UNDEFINED;
- s->is_completed = FALSE;
- return s;
- }
- static void async_func_free_frame(JSRuntime *rt, JSAsyncFunctionState *s)
- {
- JSStackFrame *sf = &s->frame;
- JSValue *sp;
- /* cannot free the function if it is running */
- assert(sf->cur_sp != NULL);
- for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) {
- JS_FreeValueRT(rt, *sp);
- }
- JS_FreeValueRT(rt, sf->cur_func);
- JS_FreeValueRT(rt, s->this_val);
- }
- static JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s)
- {
- JSRuntime *rt = ctx->rt;
- JSStackFrame *sf = &s->frame;
- JSValue func_obj, ret;
- assert(!s->is_completed);
- if (js_check_stack_overflow(ctx->rt, 0)) {
- ret = JS_ThrowStackOverflow(ctx);
- } else {
- /* the tag does not matter provided it is not an object */
- func_obj = JS_MKPTR(JS_TAG_INT, s);
- ret = JS_CallInternal(ctx, func_obj, s->this_val, JS_UNDEFINED,
- s->argc, sf->arg_buf, JS_CALL_FLAG_GENERATOR);
- }
- if (JS_IsException(ret) || JS_IsUndefined(ret)) {
- JSObject *p;
- JSFunctionBytecode *b;
-
- p = JS_VALUE_GET_OBJ(sf->cur_func);
- b = p->u.func.function_bytecode;
-
- if (JS_IsUndefined(ret)) {
- ret = sf->cur_sp[-1];
- sf->cur_sp[-1] = JS_UNDEFINED;
- }
- /* end of execution */
- s->is_completed = TRUE;
- /* close the closure variables. */
- close_var_refs(rt, b, sf);
-
- async_func_free_frame(rt, s);
- }
- return ret;
- }
- static void __async_func_free(JSRuntime *rt, JSAsyncFunctionState *s)
- {
- /* cannot close the closure variables here because it would
- potentially modify the object graph */
- if (!s->is_completed) {
- async_func_free_frame(rt, s);
- }
- JS_FreeValueRT(rt, s->resolving_funcs[0]);
- JS_FreeValueRT(rt, s->resolving_funcs[1]);
- remove_gc_object(&s->header);
- if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && js_rc(s)->ref_count != 0) {
- list_add_tail(&s->header.link, &rt->gc_zero_ref_count_list);
- } else {
- js_free_rt(rt, s);
- }
- }
- static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s)
- {
- if (--js_rc(s)->ref_count == 0) {
- if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) {
- list_del(&s->header.link);
- list_add(&s->header.link, &rt->gc_zero_ref_count_list);
- if (rt->gc_phase == JS_GC_PHASE_NONE) {
- free_zero_refcount(rt);
- }
- }
- }
- }
- /* Generators */
- typedef enum JSGeneratorStateEnum {
- JS_GENERATOR_STATE_SUSPENDED_START,
- JS_GENERATOR_STATE_SUSPENDED_YIELD,
- JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR,
- JS_GENERATOR_STATE_EXECUTING,
- JS_GENERATOR_STATE_COMPLETED,
- } JSGeneratorStateEnum;
- typedef struct JSGeneratorData {
- JSGeneratorStateEnum state;
- JSAsyncFunctionState *func_state;
- } JSGeneratorData;
- static void free_generator_stack_rt(JSRuntime *rt, JSGeneratorData *s)
- {
- if (s->state == JS_GENERATOR_STATE_COMPLETED)
- return;
- if (s->func_state) {
- async_func_free(rt, s->func_state);
- s->func_state = NULL;
- }
- s->state = JS_GENERATOR_STATE_COMPLETED;
- }
- static void js_generator_finalizer(JSRuntime *rt, JSValue obj)
- {
- JSGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_GENERATOR);
- if (s) {
- free_generator_stack_rt(rt, s);
- js_free_rt(rt, s);
- }
- }
- static void free_generator_stack(JSContext *ctx, JSGeneratorData *s)
- {
- free_generator_stack_rt(ctx->rt, s);
- }
- static void js_generator_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- JSGeneratorData *s = p->u.generator_data;
- if (!s || !s->func_state)
- return;
- mark_func(rt, &s->func_state->header);
- }
- /* XXX: use enum */
- #define GEN_MAGIC_NEXT 0
- #define GEN_MAGIC_RETURN 1
- #define GEN_MAGIC_THROW 2
- static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv,
- BOOL *pdone, int magic)
- {
- JSGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_GENERATOR);
- JSStackFrame *sf;
- JSValue ret, func_ret;
- *pdone = TRUE;
- if (!s)
- return JS_ThrowTypeError(ctx, "not a generator");
- switch(s->state) {
- default:
- case JS_GENERATOR_STATE_SUSPENDED_START:
- sf = &s->func_state->frame;
- if (magic == GEN_MAGIC_NEXT) {
- goto exec_no_arg;
- } else {
- free_generator_stack(ctx, s);
- goto done;
- }
- break;
- case JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR:
- case JS_GENERATOR_STATE_SUSPENDED_YIELD:
- sf = &s->func_state->frame;
- /* cur_sp[-1] was set to JS_UNDEFINED in the previous call */
- ret = JS_DupValue(ctx, argv[0]);
- if (magic == GEN_MAGIC_THROW &&
- s->state == JS_GENERATOR_STATE_SUSPENDED_YIELD) {
- JS_Throw(ctx, ret);
- s->func_state->throw_flag = TRUE;
- } else {
- sf->cur_sp[-1] = ret;
- sf->cur_sp[0] = JS_NewInt32(ctx, magic);
- sf->cur_sp++;
- exec_no_arg:
- s->func_state->throw_flag = FALSE;
- }
- s->state = JS_GENERATOR_STATE_EXECUTING;
- func_ret = async_func_resume(ctx, s->func_state);
- s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD;
- if (s->func_state->is_completed) {
- /* finalize the execution in case of exception or normal return */
- free_generator_stack(ctx, s);
- return func_ret;
- } else {
- assert(JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT);
- /* get the returned yield value at the top of the stack */
- ret = sf->cur_sp[-1];
- sf->cur_sp[-1] = JS_UNDEFINED;
- if (JS_VALUE_GET_INT(func_ret) == FUNC_RET_YIELD_STAR) {
- s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR;
- /* return (value, done) object */
- *pdone = 2;
- } else {
- *pdone = FALSE;
- }
- }
- break;
- case JS_GENERATOR_STATE_COMPLETED:
- done:
- /* execution is finished */
- switch(magic) {
- default:
- case GEN_MAGIC_NEXT:
- ret = JS_UNDEFINED;
- break;
- case GEN_MAGIC_RETURN:
- ret = JS_DupValue(ctx, argv[0]);
- break;
- case GEN_MAGIC_THROW:
- ret = JS_Throw(ctx, JS_DupValue(ctx, argv[0]));
- break;
- }
- break;
- case JS_GENERATOR_STATE_EXECUTING:
- ret = JS_ThrowTypeError(ctx, "cannot invoke a running generator");
- break;
- }
- return ret;
- }
- static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,
- JSValueConst this_obj,
- int argc, JSValueConst *argv,
- int flags)
- {
- JSValue obj, func_ret;
- JSGeneratorData *s;
- s = js_mallocz(ctx, sizeof(*s));
- if (!s)
- return JS_EXCEPTION;
- s->state = JS_GENERATOR_STATE_SUSPENDED_START;
- s->func_state = async_func_init(ctx, func_obj, this_obj, argc, argv);
- if (!s->func_state) {
- s->state = JS_GENERATOR_STATE_COMPLETED;
- goto fail;
- }
- /* execute the function up to 'OP_initial_yield' */
- func_ret = async_func_resume(ctx, s->func_state);
- if (JS_IsException(func_ret))
- goto fail;
- JS_FreeValue(ctx, func_ret);
- obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_GENERATOR);
- if (JS_IsException(obj))
- goto fail;
- JS_SetOpaque(obj, s);
- return obj;
- fail:
- free_generator_stack_rt(ctx->rt, s);
- js_free(ctx, s);
- return JS_EXCEPTION;
- }
- /* AsyncFunction */
- static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- JSAsyncFunctionState *s = p->u.async_function_data;
- if (s) {
- async_func_free(rt, s);
- }
- }
- static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- JSAsyncFunctionState *s = p->u.async_function_data;
- if (s) {
- mark_func(rt, &s->header);
- }
- }
- static int js_async_function_resolve_create(JSContext *ctx,
- JSAsyncFunctionState *s,
- JSValue *resolving_funcs)
- {
- int i;
- JSObject *p;
- for(i = 0; i < 2; i++) {
- resolving_funcs[i] =
- JS_NewObjectProtoClass(ctx, ctx->function_proto,
- JS_CLASS_ASYNC_FUNCTION_RESOLVE + i);
- if (JS_IsException(resolving_funcs[i])) {
- if (i == 1)
- JS_FreeValue(ctx, resolving_funcs[0]);
- return -1;
- }
- p = JS_VALUE_GET_OBJ(resolving_funcs[i]);
- js_rc(s)->ref_count++;
- p->u.async_function_data = s;
- }
- return 0;
- }
- static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionState *s)
- {
- JSValue func_ret, ret2;
- func_ret = async_func_resume(ctx, s);
- if (s->is_completed) {
- if (JS_IsException(func_ret)) {
- JSValue error;
- fail:
- error = JS_GetException(ctx);
- ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED,
- 1, (JSValueConst *)&error);
- JS_FreeValue(ctx, error);
- JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */
- } else {
- /* normal return */
- ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED,
- 1, (JSValueConst *)&func_ret);
- JS_FreeValue(ctx, func_ret);
- JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */
- }
- } else {
- JSValue value, promise, resolving_funcs[2], resolving_funcs1[2];
- int i, res;
- value = s->frame.cur_sp[-1];
- s->frame.cur_sp[-1] = JS_UNDEFINED;
- /* await */
- JS_FreeValue(ctx, func_ret); /* not used */
- promise = js_promise_resolve(ctx, ctx->promise_ctor,
- 1, (JSValueConst *)&value, 0);
- JS_FreeValue(ctx, value);
- if (JS_IsException(promise))
- goto fail;
- if (js_async_function_resolve_create(ctx, s, resolving_funcs)) {
- JS_FreeValue(ctx, promise);
- goto fail;
- }
- /* Note: no need to create 'thrownawayCapability' as in
- the spec */
- for(i = 0; i < 2; i++)
- resolving_funcs1[i] = JS_UNDEFINED;
- res = perform_promise_then(ctx, promise,
- (JSValueConst *)resolving_funcs,
- (JSValueConst *)resolving_funcs1);
- JS_FreeValue(ctx, promise);
- for(i = 0; i < 2; i++)
- JS_FreeValue(ctx, resolving_funcs[i]);
- if (res)
- goto fail;
- }
- }
- static JSValue js_async_function_resolve_call(JSContext *ctx,
- JSValueConst func_obj,
- JSValueConst this_obj,
- int argc, JSValueConst *argv,
- int flags)
- {
- JSObject *p = JS_VALUE_GET_OBJ(func_obj);
- JSAsyncFunctionState *s = p->u.async_function_data;
- BOOL is_reject = p->class_id - JS_CLASS_ASYNC_FUNCTION_RESOLVE;
- JSValueConst arg;
- if (argc > 0)
- arg = argv[0];
- else
- arg = JS_UNDEFINED;
- s->throw_flag = is_reject;
- if (is_reject) {
- JS_Throw(ctx, JS_DupValue(ctx, arg));
- } else {
- /* return value of await */
- s->frame.cur_sp[-1] = JS_DupValue(ctx, arg);
- }
- js_async_function_resume(ctx, s);
- return JS_UNDEFINED;
- }
- static JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj,
- JSValueConst this_obj,
- int argc, JSValueConst *argv, int flags)
- {
- JSValue promise;
- JSAsyncFunctionState *s;
- s = async_func_init(ctx, func_obj, this_obj, argc, argv);
- if (!s)
- return JS_EXCEPTION;
- promise = JS_NewPromiseCapability(ctx, s->resolving_funcs);
- if (JS_IsException(promise)) {
- async_func_free(ctx->rt, s);
- return JS_EXCEPTION;
- }
- js_async_function_resume(ctx, s);
- async_func_free(ctx->rt, s);
- return promise;
- }
- /* AsyncGenerator */
- typedef enum JSAsyncGeneratorStateEnum {
- JS_ASYNC_GENERATOR_STATE_SUSPENDED_START,
- JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD,
- JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR,
- JS_ASYNC_GENERATOR_STATE_EXECUTING,
- JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN,
- JS_ASYNC_GENERATOR_STATE_COMPLETED,
- } JSAsyncGeneratorStateEnum;
- typedef struct JSAsyncGeneratorRequest {
- struct list_head link;
- /* completion */
- int completion_type; /* GEN_MAGIC_x */
- JSValue result;
- /* promise capability */
- JSValue promise;
- JSValue resolving_funcs[2];
- } JSAsyncGeneratorRequest;
- typedef struct JSAsyncGeneratorData {
- JSObject *generator; /* back pointer to the object (const) */
- JSAsyncGeneratorStateEnum state;
- /* func_state is NULL is state AWAITING_RETURN and COMPLETED */
- JSAsyncFunctionState *func_state;
- struct list_head queue; /* list of JSAsyncGeneratorRequest.link */
- } JSAsyncGeneratorData;
- static void js_async_generator_free(JSRuntime *rt,
- JSAsyncGeneratorData *s)
- {
- struct list_head *el, *el1;
- JSAsyncGeneratorRequest *req;
- list_for_each_safe(el, el1, &s->queue) {
- req = list_entry(el, JSAsyncGeneratorRequest, link);
- JS_FreeValueRT(rt, req->result);
- JS_FreeValueRT(rt, req->promise);
- JS_FreeValueRT(rt, req->resolving_funcs[0]);
- JS_FreeValueRT(rt, req->resolving_funcs[1]);
- js_free_rt(rt, req);
- }
- if (s->func_state)
- async_func_free(rt, s->func_state);
- js_free_rt(rt, s);
- }
- static void js_async_generator_finalizer(JSRuntime *rt, JSValue obj)
- {
- JSAsyncGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_ASYNC_GENERATOR);
- if (s) {
- js_async_generator_free(rt, s);
- }
- }
- static void js_async_generator_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func)
- {
- JSAsyncGeneratorData *s = JS_GetOpaque(val, JS_CLASS_ASYNC_GENERATOR);
- struct list_head *el;
- JSAsyncGeneratorRequest *req;
- if (s) {
- list_for_each(el, &s->queue) {
- req = list_entry(el, JSAsyncGeneratorRequest, link);
- JS_MarkValue(rt, req->result, mark_func);
- JS_MarkValue(rt, req->promise, mark_func);
- JS_MarkValue(rt, req->resolving_funcs[0], mark_func);
- JS_MarkValue(rt, req->resolving_funcs[1], mark_func);
- }
- if (s->func_state) {
- mark_func(rt, &s->func_state->header);
- }
- }
- }
- static JSValue js_async_generator_resolve_function(JSContext *ctx,
- JSValueConst this_obj,
- int argc, JSValueConst *argv,
- int magic, JSValue *func_data);
- static int js_async_generator_resolve_function_create(JSContext *ctx,
- JSValueConst generator,
- JSValue *resolving_funcs,
- BOOL is_resume_next)
- {
- int i;
- JSValue func;
- for(i = 0; i < 2; i++) {
- func = JS_NewCFunctionData(ctx, js_async_generator_resolve_function, 1,
- i + is_resume_next * 2, 1, &generator);
- if (JS_IsException(func)) {
- if (i == 1)
- JS_FreeValue(ctx, resolving_funcs[0]);
- return -1;
- }
- resolving_funcs[i] = func;
- }
- return 0;
- }
- static int js_async_generator_await(JSContext *ctx,
- JSAsyncGeneratorData *s,
- JSValueConst value)
- {
- JSValue promise, resolving_funcs[2], resolving_funcs1[2];
- int i, res;
- promise = js_promise_resolve(ctx, ctx->promise_ctor,
- 1, &value, 0);
- if (JS_IsException(promise))
- goto fail;
- if (js_async_generator_resolve_function_create(ctx, JS_MKPTR(JS_TAG_OBJECT, s->generator),
- resolving_funcs, FALSE)) {
- JS_FreeValue(ctx, promise);
- goto fail;
- }
- /* Note: no need to create 'thrownawayCapability' as in
- the spec */
- for(i = 0; i < 2; i++)
- resolving_funcs1[i] = JS_UNDEFINED;
- res = perform_promise_then(ctx, promise,
- (JSValueConst *)resolving_funcs,
- (JSValueConst *)resolving_funcs1);
- JS_FreeValue(ctx, promise);
- for(i = 0; i < 2; i++)
- JS_FreeValue(ctx, resolving_funcs[i]);
- if (res)
- goto fail;
- return 0;
- fail:
- return -1;
- }
- static void js_async_generator_resolve_or_reject(JSContext *ctx,
- JSAsyncGeneratorData *s,
- JSValueConst result,
- int is_reject)
- {
- JSAsyncGeneratorRequest *next;
- JSValue ret;
- next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link);
- list_del(&next->link);
- ret = JS_Call(ctx, next->resolving_funcs[is_reject], JS_UNDEFINED, 1,
- &result);
- JS_FreeValue(ctx, ret);
- JS_FreeValue(ctx, next->result);
- JS_FreeValue(ctx, next->promise);
- JS_FreeValue(ctx, next->resolving_funcs[0]);
- JS_FreeValue(ctx, next->resolving_funcs[1]);
- js_free(ctx, next);
- }
- static void js_async_generator_resolve(JSContext *ctx,
- JSAsyncGeneratorData *s,
- JSValueConst value,
- BOOL done)
- {
- JSValue result;
- result = js_create_iterator_result(ctx, JS_DupValue(ctx, value), done);
- /* XXX: better exception handling ? */
- js_async_generator_resolve_or_reject(ctx, s, result, 0);
- JS_FreeValue(ctx, result);
- }
- static void js_async_generator_reject(JSContext *ctx,
- JSAsyncGeneratorData *s,
- JSValueConst exception)
- {
- js_async_generator_resolve_or_reject(ctx, s, exception, 1);
- }
- static void js_async_generator_complete(JSContext *ctx,
- JSAsyncGeneratorData *s)
- {
- if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED) {
- s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;
- async_func_free(ctx->rt, s->func_state);
- s->func_state = NULL;
- }
- }
- static int js_async_generator_completed_return(JSContext *ctx,
- JSAsyncGeneratorData *s,
- JSValueConst value)
- {
- JSValue promise, resolving_funcs[2], resolving_funcs1[2];
- int res;
- // Can fail looking up JS_ATOM_constructor when is_reject==0.
- promise = js_promise_resolve(ctx, ctx->promise_ctor, 1, &value,
- /*is_reject*/0);
- // A poisoned .constructor property is observable and the resulting
- // exception should be delivered to the catch handler.
- if (JS_IsException(promise)) {
- JSValue err = JS_GetException(ctx);
- promise = js_promise_resolve(ctx, ctx->promise_ctor, 1, (JSValueConst *)&err,
- /*is_reject*/1);
- JS_FreeValue(ctx, err);
- if (JS_IsException(promise))
- return -1;
- }
- if (js_async_generator_resolve_function_create(ctx,
- JS_MKPTR(JS_TAG_OBJECT, s->generator),
- resolving_funcs1,
- TRUE)) {
- JS_FreeValue(ctx, promise);
- return -1;
- }
- resolving_funcs[0] = JS_UNDEFINED;
- resolving_funcs[1] = JS_UNDEFINED;
- res = perform_promise_then(ctx, promise,
- (JSValueConst *)resolving_funcs1,
- (JSValueConst *)resolving_funcs);
- JS_FreeValue(ctx, resolving_funcs1[0]);
- JS_FreeValue(ctx, resolving_funcs1[1]);
- JS_FreeValue(ctx, promise);
- return res;
- }
- static void js_async_generator_resume_next(JSContext *ctx,
- JSAsyncGeneratorData *s)
- {
- JSAsyncGeneratorRequest *next;
- JSValue func_ret, value;
- for(;;) {
- if (list_empty(&s->queue))
- break;
- next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link);
- switch(s->state) {
- case JS_ASYNC_GENERATOR_STATE_EXECUTING:
- /* only happens when restarting execution after await() */
- goto resume_exec;
- case JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN:
- goto done;
- case JS_ASYNC_GENERATOR_STATE_SUSPENDED_START:
- if (next->completion_type == GEN_MAGIC_NEXT) {
- goto exec_no_arg;
- } else {
- js_async_generator_complete(ctx, s);
- }
- break;
- case JS_ASYNC_GENERATOR_STATE_COMPLETED:
- if (next->completion_type == GEN_MAGIC_NEXT) {
- js_async_generator_resolve(ctx, s, JS_UNDEFINED, TRUE);
- } else if (next->completion_type == GEN_MAGIC_RETURN) {
- s->state = JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN;
- js_async_generator_completed_return(ctx, s, next->result);
- } else {
- js_async_generator_reject(ctx, s, next->result);
- }
- goto done;
- case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD:
- case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR:
- value = JS_DupValue(ctx, next->result);
- if (next->completion_type == GEN_MAGIC_THROW &&
- s->state == JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD) {
- JS_Throw(ctx, value);
- s->func_state->throw_flag = TRUE;
- } else {
- /* 'yield' returns a value. 'yield *' also returns a value
- in case the 'throw' method is called */
- s->func_state->frame.cur_sp[-1] = value;
- s->func_state->frame.cur_sp[0] =
- JS_NewInt32(ctx, next->completion_type);
- s->func_state->frame.cur_sp++;
- exec_no_arg:
- s->func_state->throw_flag = FALSE;
- }
- s->state = JS_ASYNC_GENERATOR_STATE_EXECUTING;
- resume_exec:
- func_ret = async_func_resume(ctx, s->func_state);
- if (s->func_state->is_completed) {
- if (JS_IsException(func_ret)) {
- value = JS_GetException(ctx);
- js_async_generator_complete(ctx, s);
- js_async_generator_reject(ctx, s, value);
- JS_FreeValue(ctx, value);
- } else {
- /* end of function */
- js_async_generator_complete(ctx, s);
- js_async_generator_resolve(ctx, s, func_ret, TRUE);
- JS_FreeValue(ctx, func_ret);
- }
- } else {
- int func_ret_code, ret;
- assert(JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT);
- func_ret_code = JS_VALUE_GET_INT(func_ret);
- value = s->func_state->frame.cur_sp[-1];
- s->func_state->frame.cur_sp[-1] = JS_UNDEFINED;
- switch(func_ret_code) {
- case FUNC_RET_YIELD:
- case FUNC_RET_YIELD_STAR:
- if (func_ret_code == FUNC_RET_YIELD_STAR)
- s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR;
- else
- s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD;
- js_async_generator_resolve(ctx, s, value, FALSE);
- JS_FreeValue(ctx, value);
- break;
- case FUNC_RET_AWAIT:
- ret = js_async_generator_await(ctx, s, value);
- JS_FreeValue(ctx, value);
- if (ret < 0) {
- /* exception: throw it */
- s->func_state->throw_flag = TRUE;
- goto resume_exec;
- }
- goto done;
- default:
- abort();
- }
- }
- break;
- default:
- abort();
- }
- }
- done: ;
- }
- static JSValue js_async_generator_resolve_function(JSContext *ctx,
- JSValueConst this_obj,
- int argc, JSValueConst *argv,
- int magic, JSValue *func_data)
- {
- BOOL is_reject = magic & 1;
- JSAsyncGeneratorData *s = JS_GetOpaque(func_data[0], JS_CLASS_ASYNC_GENERATOR);
- JSValueConst arg = argv[0];
- /* XXX: what if s == NULL */
- if (magic >= 2) {
- /* resume next case in AWAITING_RETURN state */
- assert(s->state == JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN ||
- s->state == JS_ASYNC_GENERATOR_STATE_COMPLETED);
- s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;
- if (is_reject) {
- js_async_generator_reject(ctx, s, arg);
- } else {
- js_async_generator_resolve(ctx, s, arg, TRUE);
- }
- } else if (s->state == JS_ASYNC_GENERATOR_STATE_EXECUTING) {
- /* restart function execution after await() */
- s->func_state->throw_flag = is_reject;
- if (is_reject) {
- JS_Throw(ctx, JS_DupValue(ctx, arg));
- } else {
- /* return value of await */
- s->func_state->frame.cur_sp[-1] = JS_DupValue(ctx, arg);
- }
- js_async_generator_resume_next(ctx, s);
- }
- return JS_UNDEFINED;
- }
- /* magic = GEN_MAGIC_x */
- static JSValue js_async_generator_next(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv,
- int magic)
- {
- JSAsyncGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_GENERATOR);
- JSValue promise, resolving_funcs[2];
- JSAsyncGeneratorRequest *req;
- promise = JS_NewPromiseCapability(ctx, resolving_funcs);
- if (JS_IsException(promise))
- return JS_EXCEPTION;
- if (!s) {
- JSValue err, res2;
- JS_ThrowTypeError(ctx, "not an AsyncGenerator object");
- err = JS_GetException(ctx);
- res2 = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,
- 1, (JSValueConst *)&err);
- JS_FreeValue(ctx, err);
- JS_FreeValue(ctx, res2);
- JS_FreeValue(ctx, resolving_funcs[0]);
- JS_FreeValue(ctx, resolving_funcs[1]);
- return promise;
- }
- req = js_mallocz(ctx, sizeof(*req));
- if (!req)
- goto fail;
- req->completion_type = magic;
- req->result = JS_DupValue(ctx, argv[0]);
- req->promise = JS_DupValue(ctx, promise);
- req->resolving_funcs[0] = resolving_funcs[0];
- req->resolving_funcs[1] = resolving_funcs[1];
- list_add_tail(&req->link, &s->queue);
- if (s->state != JS_ASYNC_GENERATOR_STATE_EXECUTING) {
- js_async_generator_resume_next(ctx, s);
- }
- return promise;
- fail:
- JS_FreeValue(ctx, resolving_funcs[0]);
- JS_FreeValue(ctx, resolving_funcs[1]);
- JS_FreeValue(ctx, promise);
- return JS_EXCEPTION;
- }
- static JSValue js_async_generator_function_call(JSContext *ctx, JSValueConst func_obj,
- JSValueConst this_obj,
- int argc, JSValueConst *argv,
- int flags)
- {
- JSValue obj, func_ret;
- JSAsyncGeneratorData *s;
- s = js_mallocz(ctx, sizeof(*s));
- if (!s)
- return JS_EXCEPTION;
- s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_START;
- init_list_head(&s->queue);
- s->func_state = async_func_init(ctx, func_obj, this_obj, argc, argv);
- if (!s->func_state)
- goto fail;
- /* execute the function up to 'OP_initial_yield' (no yield nor
- await are possible) */
- func_ret = async_func_resume(ctx, s->func_state);
- if (JS_IsException(func_ret))
- goto fail;
- JS_FreeValue(ctx, func_ret);
- obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_ASYNC_GENERATOR);
- if (JS_IsException(obj))
- goto fail;
- s->generator = JS_VALUE_GET_OBJ(obj);
- JS_SetOpaque(obj, s);
- return obj;
- fail:
- js_async_generator_free(ctx->rt, s);
- return JS_EXCEPTION;
- }
- /* JS parser */
- enum {
- TOK_NUMBER = -128,
- TOK_STRING,
- TOK_TEMPLATE,
- TOK_IDENT,
- TOK_REGEXP,
- /* warning: order matters (see js_parse_assign_expr) */
- TOK_MUL_ASSIGN,
- TOK_DIV_ASSIGN,
- TOK_MOD_ASSIGN,
- TOK_PLUS_ASSIGN,
- TOK_MINUS_ASSIGN,
- TOK_SHL_ASSIGN,
- TOK_SAR_ASSIGN,
- TOK_SHR_ASSIGN,
- TOK_AND_ASSIGN,
- TOK_XOR_ASSIGN,
- TOK_OR_ASSIGN,
- TOK_POW_ASSIGN,
- TOK_LAND_ASSIGN,
- TOK_LOR_ASSIGN,
- TOK_DOUBLE_QUESTION_MARK_ASSIGN,
- TOK_DEC,
- TOK_INC,
- TOK_SHL,
- TOK_SAR,
- TOK_SHR,
- TOK_LT,
- TOK_LTE,
- TOK_GT,
- TOK_GTE,
- TOK_EQ,
- TOK_STRICT_EQ,
- TOK_NEQ,
- TOK_STRICT_NEQ,
- TOK_LAND,
- TOK_LOR,
- TOK_POW,
- TOK_ARROW,
- TOK_ELLIPSIS,
- TOK_DOUBLE_QUESTION_MARK,
- TOK_QUESTION_MARK_DOT,
- TOK_ERROR,
- TOK_PRIVATE_NAME,
- TOK_EOF,
- /* keywords: WARNING: same order as atoms */
- TOK_NULL, /* must be first */
- TOK_FALSE,
- TOK_TRUE,
- TOK_IF,
- TOK_ELSE,
- TOK_RETURN,
- TOK_VAR,
- TOK_THIS,
- TOK_DELETE,
- TOK_VOID,
- TOK_TYPEOF,
- TOK_NEW,
- TOK_IN,
- TOK_INSTANCEOF,
- TOK_DO,
- TOK_WHILE,
- TOK_FOR,
- TOK_BREAK,
- TOK_CONTINUE,
- TOK_SWITCH,
- TOK_CASE,
- TOK_DEFAULT,
- TOK_THROW,
- TOK_TRY,
- TOK_CATCH,
- TOK_FINALLY,
- TOK_FUNCTION,
- TOK_DEBUGGER,
- TOK_WITH,
- /* FutureReservedWord */
- TOK_CLASS,
- TOK_CONST,
- TOK_ENUM,
- TOK_EXPORT,
- TOK_EXTENDS,
- TOK_IMPORT,
- TOK_SUPER,
- /* FutureReservedWords when parsing strict mode code */
- TOK_IMPLEMENTS,
- TOK_INTERFACE,
- TOK_LET,
- TOK_PACKAGE,
- TOK_PRIVATE,
- TOK_PROTECTED,
- TOK_PUBLIC,
- TOK_STATIC,
- TOK_YIELD,
- TOK_AWAIT, /* must be last */
- TOK_OF, /* only used for js_parse_skip_parens_token() */
- };
- #define TOK_FIRST_KEYWORD TOK_NULL
- #define TOK_LAST_KEYWORD TOK_AWAIT
- /* unicode code points */
- #define CP_NBSP 0x00a0
- #define CP_BOM 0xfeff
- #define CP_LS 0x2028
- #define CP_PS 0x2029
- typedef struct BlockEnv {
- struct BlockEnv *prev;
- JSAtom label_name; /* JS_ATOM_NULL if none */
- int label_break; /* -1 if none */
- int label_cont; /* -1 if none */
- int drop_count; /* number of stack elements to drop */
- int label_finally; /* -1 if none */
- int scope_level;
- uint8_t has_iterator : 1;
- uint8_t is_regular_stmt : 1; /* i.e. not a loop statement */
- } BlockEnv;
- typedef struct JSGlobalVar {
- int cpool_idx; /* if >= 0, index in the constant pool for hoisted
- function defintion*/
- uint8_t force_init : 1; /* force initialization to undefined */
- uint8_t is_lexical : 1; /* global let/const definition */
- uint8_t is_const : 1; /* const definition */
- int scope_level; /* scope of definition */
- JSAtom var_name; /* variable name */
- } JSGlobalVar;
- typedef struct RelocEntry {
- struct RelocEntry *next;
- uint32_t addr; /* address to patch */
- int size; /* address size: 1, 2 or 4 bytes */
- } RelocEntry;
- typedef struct JumpSlot {
- int op;
- int size;
- int pos;
- int label;
- } JumpSlot;
- typedef struct LabelSlot {
- int ref_count;
- int pos; /* phase 1 address, -1 means not resolved yet */
- int pos2; /* phase 2 address, -1 means not resolved yet */
- int addr; /* phase 3 address, -1 means not resolved yet */
- RelocEntry *first_reloc;
- } LabelSlot;
- typedef struct LineNumberSlot {
- uint32_t pc;
- uint32_t source_pos;
- } LineNumberSlot;
- typedef struct {
- /* last source position */
- const uint8_t *ptr;
- int line_num;
- int col_num;
- const uint8_t *buf_start;
- } GetLineColCache;
- typedef enum JSParseFunctionEnum {
- JS_PARSE_FUNC_STATEMENT,
- JS_PARSE_FUNC_VAR,
- JS_PARSE_FUNC_EXPR,
- JS_PARSE_FUNC_ARROW,
- JS_PARSE_FUNC_GETTER,
- JS_PARSE_FUNC_SETTER,
- JS_PARSE_FUNC_METHOD,
- JS_PARSE_FUNC_CLASS_STATIC_INIT,
- JS_PARSE_FUNC_CLASS_CONSTRUCTOR,
- JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR,
- } JSParseFunctionEnum;
- typedef enum JSParseExportEnum {
- JS_PARSE_EXPORT_NONE,
- JS_PARSE_EXPORT_NAMED,
- JS_PARSE_EXPORT_DEFAULT,
- } JSParseExportEnum;
- typedef struct JSVarScope {
- int parent; /* index into fd->scopes of the enclosing scope */
- int first; /* index into fd->vars of the last variable in this scope */
- } JSVarScope;
- typedef struct JSVarDef {
- JSAtom var_name;
- /* index into fd->scopes of this variable lexical scope */
- int scope_level;
- /* - if scope_level = 0: scope in which the variable is defined
- - if scope_level != 0: index into fd->vars of the next
- variable in the same or enclosing lexical scope
- */
- int scope_next;
- uint8_t is_const : 1;
- uint8_t is_lexical : 1;
- uint8_t is_captured : 1; /* XXX: could remove and use a var_ref_idx value */
- uint8_t is_static_private : 1; /* only used during private class field parsing */
- uint8_t var_kind : 4; /* see JSVarKindEnum */
- /* if is_captured = TRUE, provides, the index of the corresponding
- JSVarRef on stack */
- uint16_t var_ref_idx;
- /* function pool index for lexical variables with var_kind =
- JS_VAR_FUNCTION_DECL/JS_VAR_NEW_FUNCTION_DECL or scope level of
- the definition of the 'var' variables (they have scope_level =
- 0) */
- int func_pool_idx;
- } JSVarDef;
- typedef struct JSFunctionDef {
- JSContext *ctx;
- struct JSFunctionDef *parent;
- int parent_cpool_idx; /* index in the constant pool of the parent
- or -1 if none */
- int parent_scope_level; /* scope level in parent at point of definition */
- struct list_head child_list; /* list of JSFunctionDef.link */
- struct list_head link;
- BOOL is_eval; /* TRUE if eval code */
- int eval_type; /* only valid if is_eval = TRUE */
- BOOL is_global_var; /* TRUE if variables are not defined locally:
- eval global, eval module or non strict eval */
- BOOL is_func_expr; /* TRUE if function expression */
- BOOL has_home_object; /* TRUE if the home object is available */
- BOOL has_prototype; /* true if a prototype field is necessary */
- BOOL has_simple_parameter_list;
- BOOL has_parameter_expressions; /* if true, an argument scope is created */
- BOOL has_use_strict; /* to reject directive in special cases */
- BOOL has_eval_call; /* true if the function contains a call to eval() */
- BOOL has_arguments_binding; /* true if the 'arguments' binding is
- available in the function */
- BOOL has_this_binding; /* true if the 'this' and new.target binding are
- available in the function */
- BOOL new_target_allowed; /* true if the 'new.target' does not
- throw a syntax error */
- BOOL super_call_allowed; /* true if super() is allowed */
- BOOL super_allowed; /* true if super. or super[] is allowed */
- BOOL arguments_allowed; /* true if the 'arguments' identifier is allowed */
- BOOL is_derived_class_constructor;
- BOOL in_function_body;
- JSFunctionKindEnum func_kind : 8;
- JSParseFunctionEnum func_type : 8;
- uint8_t js_mode; /* bitmap of JS_MODE_x */
- JSAtom func_name; /* JS_ATOM_NULL if no name */
- JSVarDef *vars;
- int var_size; /* allocated size for vars[] */
- int var_count;
- JSVarDef *args;
- int arg_size; /* allocated size for args[] */
- int arg_count; /* number of arguments */
- int defined_arg_count;
- int var_ref_count; /* number of local/arg variable references */
- int var_object_idx; /* -1 if none */
- int arg_var_object_idx; /* -1 if none (var object for the argument scope) */
- int arguments_var_idx; /* -1 if none */
- int arguments_arg_idx; /* argument variable definition in argument scope,
- -1 if none */
- int func_var_idx; /* variable containing the current function (-1
- if none, only used if is_func_expr is true) */
- int eval_ret_idx; /* variable containing the return value of the eval, -1 if none */
- int this_var_idx; /* variable containg the 'this' value, -1 if none */
- int new_target_var_idx; /* variable containg the 'new.target' value, -1 if none */
- int this_active_func_var_idx; /* variable containg the 'this.active_func' value, -1 if none */
- int home_object_var_idx;
- BOOL need_home_object;
- int scope_level; /* index into fd->scopes if the current lexical scope */
- int scope_first; /* index into vd->vars of first lexically scoped variable */
- int scope_size; /* allocated size of fd->scopes array */
- int scope_count; /* number of entries used in the fd->scopes array */
- JSVarScope *scopes;
- JSVarScope def_scope_array[4];
- int body_scope; /* scope of the body of the function or eval */
- int global_var_count;
- int global_var_size;
- JSGlobalVar *global_vars;
- DynBuf byte_code;
- int last_opcode_pos; /* -1 if no last opcode */
- const uint8_t *last_opcode_source_ptr;
- BOOL use_short_opcodes; /* true if short opcodes are used in byte_code */
- LabelSlot *label_slots;
- int label_size; /* allocated size for label_slots[] */
- int label_count;
- BlockEnv *top_break; /* break/continue label stack */
- /* constant pool (strings, functions, numbers) */
- JSValue *cpool;
- int cpool_count;
- int cpool_size;
- /* list of variables in the closure */
- int closure_var_count;
- int closure_var_size;
- JSClosureVar *closure_var;
- JumpSlot *jump_slots;
- int jump_size;
- int jump_count;
- LineNumberSlot *line_number_slots;
- int line_number_size;
- int line_number_count;
- int line_number_last;
- int line_number_last_pc;
- /* pc2line table */
- BOOL strip_debug : 1; /* strip all debug info (implies strip_source = TRUE) */
- BOOL strip_source : 1; /* strip only source code */
- JSAtom filename;
- uint32_t source_pos; /* pointer in the eval() source */
- GetLineColCache *get_line_col_cache; /* XXX: could remove to save memory */
- DynBuf pc2line;
- char *source; /* raw source, utf-8 encoded */
- int source_len;
- JSModuleDef *module; /* != NULL when parsing a module */
- BOOL has_await; /* TRUE if await is used (used in module eval) */
- } JSFunctionDef;
- typedef struct JSToken {
- int val;
- const uint8_t *ptr; /* position in the source */
- union {
- struct {
- JSValue str;
- int sep;
- } str;
- struct {
- JSValue val;
- } num;
- struct {
- JSAtom atom;
- BOOL has_escape;
- BOOL is_reserved;
- } ident;
- struct {
- JSValue body;
- JSValue flags;
- } regexp;
- } u;
- } JSToken;
- typedef struct JSParseState {
- JSContext *ctx;
- const char *filename;
- JSToken token;
- BOOL got_lf; /* true if got line feed before the current token */
- const uint8_t *last_ptr;
- const uint8_t *buf_start;
- const uint8_t *buf_ptr;
- const uint8_t *buf_end;
- /* current function code */
- JSFunctionDef *cur_func;
- BOOL is_module; /* parsing a module */
- BOOL allow_html_comments;
- BOOL ext_json; /* JSON parsing: true if accepting JSON superset */
- GetLineColCache get_line_col_cache;
- } JSParseState;
- typedef struct JSOpCode {
- #ifdef DUMP_BYTECODE
- const char *name;
- #endif
- uint8_t size; /* in bytes */
- /* the opcodes remove n_pop items from the top of the stack, then
- pushes n_push items */
- uint8_t n_pop;
- uint8_t n_push;
- uint8_t fmt;
- } JSOpCode;
- static const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = {
- #define FMT(f)
- #ifdef DUMP_BYTECODE
- #define DEF(id, size, n_pop, n_push, f) { #id, size, n_pop, n_push, OP_FMT_ ## f },
- #else
- #define DEF(id, size, n_pop, n_push, f) { size, n_pop, n_push, OP_FMT_ ## f },
- #endif
- #include "quickjs-opcode.h"
- #undef DEF
- #undef FMT
- };
- #if SHORT_OPCODES
- /* After the final compilation pass, short opcodes are used. Their
- opcodes overlap with the temporary opcodes which cannot appear in
- the final bytecode. Their description is after the temporary
- opcodes in opcode_info[]. */
- #define short_opcode_info(op) \
- opcode_info[(op) >= OP_TEMP_START ? \
- (op) + (OP_TEMP_END - OP_TEMP_START) : (op)]
- #else
- #define short_opcode_info(op) opcode_info[op]
- #endif
- static __exception int next_token(JSParseState *s);
- static void free_token(JSParseState *s, JSToken *token)
- {
- switch(token->val) {
- case TOK_NUMBER:
- JS_FreeValue(s->ctx, token->u.num.val);
- break;
- case TOK_STRING:
- case TOK_TEMPLATE:
- JS_FreeValue(s->ctx, token->u.str.str);
- break;
- case TOK_REGEXP:
- JS_FreeValue(s->ctx, token->u.regexp.body);
- JS_FreeValue(s->ctx, token->u.regexp.flags);
- break;
- case TOK_IDENT:
- case TOK_PRIVATE_NAME:
- JS_FreeAtom(s->ctx, token->u.ident.atom);
- break;
- default:
- if (token->val >= TOK_FIRST_KEYWORD &&
- token->val <= TOK_LAST_KEYWORD) {
- JS_FreeAtom(s->ctx, token->u.ident.atom);
- }
- break;
- }
- }
- static void __attribute((unused)) dump_token(JSParseState *s,
- const JSToken *token)
- {
- switch(token->val) {
- case TOK_NUMBER:
- {
- double d;
- JS_ToFloat64(s->ctx, &d, token->u.num.val); /* no exception possible */
- printf("number: %.14g\n", d);
- }
- break;
- case TOK_IDENT:
- dump_atom:
- {
- char buf[ATOM_GET_STR_BUF_SIZE];
- printf("ident: '%s'\n",
- JS_AtomGetStr(s->ctx, buf, sizeof(buf), token->u.ident.atom));
- }
- break;
- case TOK_STRING:
- {
- const char *str;
- /* XXX: quote the string */
- str = JS_ToCString(s->ctx, token->u.str.str);
- printf("string: '%s'\n", str);
- JS_FreeCString(s->ctx, str);
- }
- break;
- case TOK_TEMPLATE:
- {
- const char *str;
- str = JS_ToCString(s->ctx, token->u.str.str);
- printf("template: `%s`\n", str);
- JS_FreeCString(s->ctx, str);
- }
- break;
- case TOK_REGEXP:
- {
- const char *str, *str2;
- str = JS_ToCString(s->ctx, token->u.regexp.body);
- str2 = JS_ToCString(s->ctx, token->u.regexp.flags);
- printf("regexp: '%s' '%s'\n", str, str2);
- JS_FreeCString(s->ctx, str);
- JS_FreeCString(s->ctx, str2);
- }
- break;
- case TOK_EOF:
- printf("eof\n");
- break;
- default:
- if (s->token.val >= TOK_NULL && s->token.val <= TOK_LAST_KEYWORD) {
- goto dump_atom;
- } else if (s->token.val >= 256) {
- printf("token: %d\n", token->val);
- } else {
- printf("token: '%c'\n", token->val);
- }
- break;
- }
- }
- /* return the zero based line and column number in the source. */
- /* Note: we no longer support '\r' as line terminator */
- static int get_line_col(int *pcol_num, const uint8_t *buf, size_t len)
- {
- int line_num, col_num, c;
- size_t i;
-
- line_num = 0;
- col_num = 0;
- for(i = 0; i < len; i++) {
- c = buf[i];
- if (c == '\n') {
- line_num++;
- col_num = 0;
- } else if (c < 0x80 || c >= 0xc0) {
- col_num++;
- }
- }
- *pcol_num = col_num;
- return line_num;
- }
- static int get_line_col_cached(GetLineColCache *s, int *pcol_num, const uint8_t *ptr)
- {
- int line_num, col_num;
- if (ptr >= s->ptr) {
- line_num = get_line_col(&col_num, s->ptr, ptr - s->ptr);
- if (line_num == 0) {
- s->col_num += col_num;
- } else {
- s->line_num += line_num;
- s->col_num = col_num;
- }
- } else {
- line_num = get_line_col(&col_num, ptr, s->ptr - ptr);
- if (line_num == 0) {
- s->col_num -= col_num;
- } else {
- const uint8_t *p;
- s->line_num -= line_num;
- /* find the absolute column position */
- col_num = 0;
- for(p = ptr - 1; p >= s->buf_start; p--) {
- if (*p == '\n') {
- break;
- } else if (*p < 0x80 || *p >= 0xc0) {
- col_num++;
- }
- }
- s->col_num = col_num;
- }
- }
- s->ptr = ptr;
- *pcol_num = s->col_num;
- return s->line_num;
- }
- /* 'ptr' is the position of the error in the source */
- static int js_parse_error_v(JSParseState *s, const uint8_t *ptr, const char *fmt, va_list ap)
- {
- JSContext *ctx = s->ctx;
- int line_num, col_num;
- line_num = get_line_col(&col_num, s->buf_start, ptr - s->buf_start);
- JS_ThrowError2(ctx, JS_SYNTAX_ERROR, fmt, ap, FALSE);
- build_backtrace(ctx, ctx->rt->current_exception, s->filename,
- line_num + 1, col_num + 1, 0);
- return -1;
- }
- static __attribute__((format(printf, 3, 4))) int js_parse_error_pos(JSParseState *s, const uint8_t *ptr, const char *fmt, ...)
- {
- va_list ap;
- int ret;
-
- va_start(ap, fmt);
- ret = js_parse_error_v(s, ptr, fmt, ap);
- va_end(ap);
- return ret;
- }
- static __attribute__((format(printf, 2, 3))) int js_parse_error(JSParseState *s, const char *fmt, ...)
- {
- va_list ap;
- int ret;
-
- va_start(ap, fmt);
- ret = js_parse_error_v(s, s->token.ptr, fmt, ap);
- va_end(ap);
- return ret;
- }
- static int js_parse_expect(JSParseState *s, int tok)
- {
- if (s->token.val != tok) {
- /* XXX: dump token correctly in all cases */
- return js_parse_error(s, "expecting '%c'", tok);
- }
- return next_token(s);
- }
- static int js_parse_expect_semi(JSParseState *s)
- {
- if (s->token.val != ';') {
- /* automatic insertion of ';' */
- if (s->token.val == TOK_EOF || s->token.val == '}' || s->got_lf) {
- return 0;
- }
- return js_parse_error(s, "expecting '%c'", ';');
- }
- return next_token(s);
- }
- static int js_parse_error_reserved_identifier(JSParseState *s)
- {
- char buf1[ATOM_GET_STR_BUF_SIZE];
- return js_parse_error(s, "'%s' is a reserved identifier",
- JS_AtomGetStr(s->ctx, buf1, sizeof(buf1),
- s->token.u.ident.atom));
- }
- static __exception int js_parse_template_part(JSParseState *s, const uint8_t *p)
- {
- uint32_t c;
- StringBuffer b_s, *b = &b_s;
- JSValue str;
- /* p points to the first byte of the template part */
- if (string_buffer_init(s->ctx, b, 32))
- goto fail;
- for(;;) {
- if (p >= s->buf_end)
- goto unexpected_eof;
- c = *p++;
- if (c == '`') {
- /* template end part */
- break;
- }
- if (c == '$' && *p == '{') {
- /* template start or middle part */
- p++;
- break;
- }
- if (c == '\\') {
- if (string_buffer_putc8(b, c))
- goto fail;
- if (p >= s->buf_end)
- goto unexpected_eof;
- c = *p++;
- }
- /* newline sequences are normalized as single '\n' bytes */
- if (c == '\r') {
- if (*p == '\n')
- p++;
- c = '\n';
- }
- if (c >= 0x80) {
- const uint8_t *p_next;
- c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);
- if (c > 0x10FFFF) {
- js_parse_error_pos(s, p - 1, "invalid UTF-8 sequence");
- goto fail;
- }
- p = p_next;
- }
- if (string_buffer_putc(b, c))
- goto fail;
- }
- str = string_buffer_end(b);
- if (JS_IsException(str))
- return -1;
- s->token.val = TOK_TEMPLATE;
- s->token.u.str.sep = c;
- s->token.u.str.str = str;
- s->buf_ptr = p;
- return 0;
- unexpected_eof:
- js_parse_error(s, "unexpected end of string");
- fail:
- string_buffer_free(b);
- return -1;
- }
- static __exception int js_parse_string(JSParseState *s, int sep,
- BOOL do_throw, const uint8_t *p,
- JSToken *token, const uint8_t **pp)
- {
- int ret;
- uint32_t c;
- StringBuffer b_s, *b = &b_s;
- const uint8_t *p_escape;
- JSValue str;
- /* string */
- if (string_buffer_init(s->ctx, b, 32))
- goto fail;
- for(;;) {
- if (p >= s->buf_end)
- goto invalid_char;
- c = *p;
- if (c < 0x20) {
- if (sep == '`') {
- if (c == '\r') {
- if (p[1] == '\n')
- p++;
- c = '\n';
- }
- /* do not update s->line_num */
- } else if (c == '\n' || c == '\r')
- goto invalid_char;
- }
- p++;
- if (c == sep)
- break;
- if (c == '$' && *p == '{' && sep == '`') {
- /* template start or middle part */
- p++;
- break;
- }
- if (c == '\\') {
- p_escape = p - 1;
- c = *p;
- /* XXX: need a specific JSON case to avoid
- accepting invalid escapes */
- switch(c) {
- case '\0':
- if (p >= s->buf_end)
- goto invalid_char;
- p++;
- break;
- case '\'':
- case '\"':
- case '\\':
- p++;
- break;
- case '\r': /* accept DOS and MAC newline sequences */
- if (p[1] == '\n') {
- p++;
- }
- /* fall thru */
- case '\n':
- /* ignore escaped newline sequence */
- p++;
- continue;
- default:
- if (c >= '0' && c <= '9') {
- if (!(s->cur_func->js_mode & JS_MODE_STRICT) && sep != '`')
- goto parse_escape;
- if (c == '0' && !(p[1] >= '0' && p[1] <= '9')) {
- p++;
- c = '\0';
- } else {
- if (c >= '8' || sep == '`') {
- /* Note: according to ES2021, \8 and \9 are not
- accepted in strict mode or in templates. */
- goto invalid_escape;
- } else {
- if (do_throw)
- js_parse_error_pos(s, p_escape, "octal escape sequences are not allowed in strict mode");
- }
- goto fail;
- }
- } else if (c >= 0x80) {
- const uint8_t *p_next;
- c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next);
- if (c > 0x10FFFF) {
- goto invalid_utf8;
- }
- p = p_next;
- /* LS or PS are skipped */
- if (c == CP_LS || c == CP_PS)
- continue;
- } else {
- parse_escape:
- ret = lre_parse_escape(&p, TRUE);
- if (ret == -1) {
- invalid_escape:
- if (do_throw)
- js_parse_error_pos(s, p_escape, "malformed escape sequence in string literal");
- goto fail;
- } else if (ret < 0) {
- /* ignore the '\' (could output a warning) */
- p++;
- } else {
- c = ret;
- }
- }
- break;
- }
- } else if (c >= 0x80) {
- const uint8_t *p_next;
- c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);
- if (c > 0x10FFFF)
- goto invalid_utf8;
- p = p_next;
- }
- if (string_buffer_putc(b, c))
- goto fail;
- }
- str = string_buffer_end(b);
- if (JS_IsException(str))
- return -1;
- token->val = TOK_STRING;
- token->u.str.sep = c;
- token->u.str.str = str;
- *pp = p;
- return 0;
- invalid_utf8:
- if (do_throw)
- js_parse_error(s, "invalid UTF-8 sequence");
- goto fail;
- invalid_char:
- if (do_throw)
- js_parse_error(s, "unexpected end of string");
- fail:
- string_buffer_free(b);
- return -1;
- }
- static inline BOOL token_is_pseudo_keyword(JSParseState *s, JSAtom atom) {
- return s->token.val == TOK_IDENT && s->token.u.ident.atom == atom &&
- !s->token.u.ident.has_escape;
- }
- static __exception int js_parse_regexp(JSParseState *s)
- {
- const uint8_t *p;
- BOOL in_class;
- StringBuffer b_s, *b = &b_s;
- StringBuffer b2_s, *b2 = &b2_s;
- uint32_t c;
- JSValue body_str, flags_str;
- p = s->buf_ptr;
- p++;
- in_class = FALSE;
- if (string_buffer_init(s->ctx, b, 32))
- return -1;
- if (string_buffer_init(s->ctx, b2, 1))
- goto fail;
- for(;;) {
- if (p >= s->buf_end) {
- eof_error:
- js_parse_error(s, "unexpected end of regexp");
- goto fail;
- }
- c = *p++;
- if (c == '\n' || c == '\r') {
- goto eol_error;
- } else if (c == '/') {
- if (!in_class)
- break;
- } else if (c == '[') {
- in_class = TRUE;
- } else if (c == ']') {
- /* XXX: incorrect as the first character in a class */
- in_class = FALSE;
- } else if (c == '\\') {
- if (string_buffer_putc8(b, c))
- goto fail;
- c = *p++;
- if (c == '\n' || c == '\r')
- goto eol_error;
- else if (c == '\0' && p >= s->buf_end)
- goto eof_error;
- else if (c >= 0x80) {
- const uint8_t *p_next;
- c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);
- if (c > 0x10FFFF) {
- goto invalid_utf8;
- }
- p = p_next;
- if (c == CP_LS || c == CP_PS)
- goto eol_error;
- }
- } else if (c >= 0x80) {
- const uint8_t *p_next;
- c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);
- if (c > 0x10FFFF) {
- invalid_utf8:
- js_parse_error_pos(s, p - 1, "invalid UTF-8 sequence");
- goto fail;
- }
- /* LS or PS are considered as line terminator */
- if (c == CP_LS || c == CP_PS) {
- eol_error:
- js_parse_error_pos(s, p - 1, "unexpected line terminator in regexp");
- goto fail;
- }
- p = p_next;
- }
- if (string_buffer_putc(b, c))
- goto fail;
- }
- /* flags */
- for(;;) {
- const uint8_t *p_next = p;
- c = *p_next++;
- if (c >= 0x80) {
- c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next);
- if (c > 0x10FFFF) {
- p++;
- goto invalid_utf8;
- }
- }
- if (!lre_js_is_ident_next(c))
- break;
- if (string_buffer_putc(b2, c))
- goto fail;
- p = p_next;
- }
- body_str = string_buffer_end(b);
- flags_str = string_buffer_end(b2);
- if (JS_IsException(body_str) ||
- JS_IsException(flags_str)) {
- JS_FreeValue(s->ctx, body_str);
- JS_FreeValue(s->ctx, flags_str);
- return -1;
- }
- s->token.val = TOK_REGEXP;
- s->token.u.regexp.body = body_str;
- s->token.u.regexp.flags = flags_str;
- s->buf_ptr = p;
- return 0;
- fail:
- string_buffer_free(b);
- string_buffer_free(b2);
- return -1;
- }
- static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize,
- char *static_buf)
- {
- char *buf, *new_buf;
- size_t size, new_size;
- buf = *pbuf;
- size = *psize;
- if (size >= (SIZE_MAX / 3) * 2)
- new_size = SIZE_MAX;
- else
- new_size = size + (size >> 1);
- if (buf == static_buf) {
- new_buf = js_malloc(ctx, new_size);
- if (!new_buf)
- return -1;
- memcpy(new_buf, buf, size);
- } else {
- new_buf = js_realloc(ctx, buf, new_size);
- if (!new_buf)
- return -1;
- }
- *pbuf = new_buf;
- *psize = new_size;
- return 0;
- }
- /* convert a TOK_IDENT to a keyword when needed */
- static void update_token_ident(JSParseState *s)
- {
- if (s->token.u.ident.atom <= JS_ATOM_LAST_KEYWORD ||
- (s->token.u.ident.atom <= JS_ATOM_LAST_STRICT_KEYWORD &&
- (s->cur_func->js_mode & JS_MODE_STRICT)) ||
- (s->token.u.ident.atom == JS_ATOM_yield &&
- ((s->cur_func->func_kind & JS_FUNC_GENERATOR) ||
- (s->cur_func->func_type == JS_PARSE_FUNC_ARROW &&
- !s->cur_func->in_function_body && s->cur_func->parent &&
- (s->cur_func->parent->func_kind & JS_FUNC_GENERATOR)))) ||
- (s->token.u.ident.atom == JS_ATOM_await &&
- (s->is_module ||
- (s->cur_func->func_kind & JS_FUNC_ASYNC) ||
- s->cur_func->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT ||
- (s->cur_func->func_type == JS_PARSE_FUNC_ARROW &&
- !s->cur_func->in_function_body && s->cur_func->parent &&
- ((s->cur_func->parent->func_kind & JS_FUNC_ASYNC) ||
- s->cur_func->parent->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT))))) {
- if (s->token.u.ident.has_escape) {
- s->token.u.ident.is_reserved = TRUE;
- s->token.val = TOK_IDENT;
- } else {
- /* The keywords atoms are pre allocated */
- s->token.val = s->token.u.ident.atom - 1 + TOK_FIRST_KEYWORD;
- }
- }
- }
- /* if the current token is an identifier or keyword, reparse it
- according to the current function type */
- static void reparse_ident_token(JSParseState *s)
- {
- if (s->token.val == TOK_IDENT ||
- (s->token.val >= TOK_FIRST_KEYWORD &&
- s->token.val <= TOK_LAST_KEYWORD)) {
- s->token.val = TOK_IDENT;
- s->token.u.ident.is_reserved = FALSE;
- update_token_ident(s);
- }
- }
- /* 'c' is the first character. Return JS_ATOM_NULL in case of error */
- static JSAtom parse_ident(JSParseState *s, const uint8_t **pp,
- BOOL *pident_has_escape, int c, BOOL is_private)
- {
- const uint8_t *p, *p1;
- char ident_buf[128], *buf;
- size_t ident_size, ident_pos;
- JSAtom atom;
- p = *pp;
- buf = ident_buf;
- ident_size = sizeof(ident_buf);
- ident_pos = 0;
- if (is_private)
- buf[ident_pos++] = '#';
- for(;;) {
- p1 = p;
- if (c < 128) {
- buf[ident_pos++] = c;
- } else {
- ident_pos += unicode_to_utf8((uint8_t*)buf + ident_pos, c);
- }
- c = *p1++;
- if (c == '\\' && *p1 == 'u') {
- c = lre_parse_escape(&p1, TRUE);
- *pident_has_escape = TRUE;
- } else if (c >= 128) {
- c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1);
- }
- if (!lre_js_is_ident_next(c))
- break;
- p = p1;
- if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) {
- if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) {
- atom = JS_ATOM_NULL;
- goto done;
- }
- }
- }
- atom = JS_NewAtomLen(s->ctx, buf, ident_pos);
- done:
- if (unlikely(buf != ident_buf))
- js_free(s->ctx, buf);
- *pp = p;
- return atom;
- }
- static __exception int next_token(JSParseState *s)
- {
- const uint8_t *p;
- int c;
- BOOL ident_has_escape;
- JSAtom atom;
- if (js_check_stack_overflow(s->ctx->rt, 0)) {
- return js_parse_error(s, "stack overflow");
- }
- free_token(s, &s->token);
- p = s->last_ptr = s->buf_ptr;
- s->got_lf = FALSE;
- redo:
- s->token.ptr = p;
- c = *p;
- switch(c) {
- case 0:
- if (p >= s->buf_end) {
- s->token.val = TOK_EOF;
- } else {
- goto def_token;
- }
- break;
- case '`':
- if (js_parse_template_part(s, p + 1))
- goto fail;
- p = s->buf_ptr;
- break;
- case '\'':
- case '\"':
- if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p))
- goto fail;
- break;
- case '\r': /* accept DOS and MAC newline sequences */
- if (p[1] == '\n') {
- p++;
- }
- /* fall thru */
- case '\n':
- p++;
- line_terminator:
- s->got_lf = TRUE;
- goto redo;
- case '\f':
- case '\v':
- case ' ':
- case '\t':
- p++;
- goto redo;
- case '/':
- if (p[1] == '*') {
- /* comment */
- p += 2;
- for(;;) {
- if (*p == '\0' && p >= s->buf_end) {
- js_parse_error(s, "unexpected end of comment");
- goto fail;
- }
- if (p[0] == '*' && p[1] == '/') {
- p += 2;
- break;
- }
- if (*p == '\n' || *p == '\r') {
- s->got_lf = TRUE; /* considered as LF for ASI */
- p++;
- } else if (*p >= 0x80) {
- c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
- if (c == CP_LS || c == CP_PS) {
- s->got_lf = TRUE; /* considered as LF for ASI */
- } else if (c == -1) {
- p++; /* skip invalid UTF-8 */
- }
- } else {
- p++;
- }
- }
- goto redo;
- } else if (p[1] == '/') {
- /* line comment */
- p += 2;
- skip_line_comment:
- for(;;) {
- if (*p == '\0' && p >= s->buf_end)
- break;
- if (*p == '\r' || *p == '\n')
- break;
- if (*p >= 0x80) {
- c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
- /* LS or PS are considered as line terminator */
- if (c == CP_LS || c == CP_PS) {
- break;
- } else if (c == -1) {
- p++; /* skip invalid UTF-8 */
- }
- } else {
- p++;
- }
- }
- goto redo;
- } else if (p[1] == '=') {
- p += 2;
- s->token.val = TOK_DIV_ASSIGN;
- } else {
- p++;
- s->token.val = c;
- }
- break;
- case '\\':
- if (p[1] == 'u') {
- const uint8_t *p1 = p + 1;
- int c1 = lre_parse_escape(&p1, TRUE);
- if (c1 >= 0 && lre_js_is_ident_first(c1)) {
- c = c1;
- p = p1;
- ident_has_escape = TRUE;
- goto has_ident;
- } else {
- /* XXX: syntax error? */
- }
- }
- goto def_token;
- case 'a': case 'b': case 'c': case 'd':
- case 'e': case 'f': case 'g': case 'h':
- case 'i': case 'j': case 'k': case 'l':
- case 'm': case 'n': case 'o': case 'p':
- case 'q': case 'r': case 's': case 't':
- case 'u': case 'v': case 'w': case 'x':
- case 'y': case 'z':
- case 'A': case 'B': case 'C': case 'D':
- case 'E': case 'F': case 'G': case 'H':
- case 'I': case 'J': case 'K': case 'L':
- case 'M': case 'N': case 'O': case 'P':
- case 'Q': case 'R': case 'S': case 'T':
- case 'U': case 'V': case 'W': case 'X':
- case 'Y': case 'Z':
- case '_':
- case '$':
- /* identifier */
- p++;
- ident_has_escape = FALSE;
- has_ident:
- atom = parse_ident(s, &p, &ident_has_escape, c, FALSE);
- if (atom == JS_ATOM_NULL)
- goto fail;
- s->token.u.ident.atom = atom;
- s->token.u.ident.has_escape = ident_has_escape;
- s->token.u.ident.is_reserved = FALSE;
- s->token.val = TOK_IDENT;
- update_token_ident(s);
- break;
- case '#':
- /* private name */
- {
- const uint8_t *p1;
- p++;
- p1 = p;
- c = *p1++;
- if (c == '\\' && *p1 == 'u') {
- c = lre_parse_escape(&p1, TRUE);
- } else if (c >= 128) {
- c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1);
- }
- if (!lre_js_is_ident_first(c)) {
- js_parse_error(s, "invalid first character of private name");
- goto fail;
- }
- p = p1;
- ident_has_escape = FALSE; /* not used */
- atom = parse_ident(s, &p, &ident_has_escape, c, TRUE);
- if (atom == JS_ATOM_NULL)
- goto fail;
- s->token.u.ident.atom = atom;
- s->token.val = TOK_PRIVATE_NAME;
- }
- break;
- case '.':
- if (p[1] == '.' && p[2] == '.') {
- p += 3;
- s->token.val = TOK_ELLIPSIS;
- break;
- }
- if (p[1] >= '0' && p[1] <= '9') {
- goto parse_number;
- } else {
- goto def_token;
- }
- break;
- case '0':
- /* in strict mode, octal literals are not accepted */
- if (is_digit(p[1]) && (s->cur_func->js_mode & JS_MODE_STRICT)) {
- js_parse_error(s, "octal literals are deprecated in strict mode");
- goto fail;
- }
- goto parse_number;
- case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8':
- case '9':
- /* number */
- parse_number:
- {
- JSValue ret;
- const uint8_t *p1;
- int flags;
- flags = ATOD_ACCEPT_BIN_OCT | ATOD_ACCEPT_LEGACY_OCTAL |
- ATOD_ACCEPT_UNDERSCORES | ATOD_ACCEPT_SUFFIX;
- ret = js_atof(s->ctx, (const char *)p, (const char **)&p, 0,
- flags);
- if (JS_IsException(ret))
- goto fail;
- /* reject `10instanceof Number` */
- if (JS_VALUE_IS_NAN(ret) ||
- lre_js_is_ident_next(unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1))) {
- JS_FreeValue(s->ctx, ret);
- js_parse_error(s, "invalid number literal");
- goto fail;
- }
- s->token.val = TOK_NUMBER;
- s->token.u.num.val = ret;
- }
- break;
- case '*':
- if (p[1] == '=') {
- p += 2;
- s->token.val = TOK_MUL_ASSIGN;
- } else if (p[1] == '*') {
- if (p[2] == '=') {
- p += 3;
- s->token.val = TOK_POW_ASSIGN;
- } else {
- p += 2;
- s->token.val = TOK_POW;
- }
- } else {
- goto def_token;
- }
- break;
- case '%':
- if (p[1] == '=') {
- p += 2;
- s->token.val = TOK_MOD_ASSIGN;
- } else {
- goto def_token;
- }
- break;
- case '+':
- if (p[1] == '=') {
- p += 2;
- s->token.val = TOK_PLUS_ASSIGN;
- } else if (p[1] == '+') {
- p += 2;
- s->token.val = TOK_INC;
- } else {
- goto def_token;
- }
- break;
- case '-':
- if (p[1] == '=') {
- p += 2;
- s->token.val = TOK_MINUS_ASSIGN;
- } else if (p[1] == '-') {
- if (s->allow_html_comments && p[2] == '>' &&
- (s->got_lf || s->last_ptr == s->buf_start)) {
- /* Annex B: `-->` at beginning of line is an html comment end.
- It extends to the end of the line.
- */
- goto skip_line_comment;
- }
- p += 2;
- s->token.val = TOK_DEC;
- } else {
- goto def_token;
- }
- break;
- case '<':
- if (p[1] == '=') {
- p += 2;
- s->token.val = TOK_LTE;
- } else if (p[1] == '<') {
- if (p[2] == '=') {
- p += 3;
- s->token.val = TOK_SHL_ASSIGN;
- } else {
- p += 2;
- s->token.val = TOK_SHL;
- }
- } else if (s->allow_html_comments &&
- p[1] == '!' && p[2] == '-' && p[3] == '-') {
- /* Annex B: handle `<!--` single line html comments */
- goto skip_line_comment;
- } else {
- goto def_token;
- }
- break;
- case '>':
- if (p[1] == '=') {
- p += 2;
- s->token.val = TOK_GTE;
- } else if (p[1] == '>') {
- if (p[2] == '>') {
- if (p[3] == '=') {
- p += 4;
- s->token.val = TOK_SHR_ASSIGN;
- } else {
- p += 3;
- s->token.val = TOK_SHR;
- }
- } else if (p[2] == '=') {
- p += 3;
- s->token.val = TOK_SAR_ASSIGN;
- } else {
- p += 2;
- s->token.val = TOK_SAR;
- }
- } else {
- goto def_token;
- }
- break;
- case '=':
- if (p[1] == '=') {
- if (p[2] == '=') {
- p += 3;
- s->token.val = TOK_STRICT_EQ;
- } else {
- p += 2;
- s->token.val = TOK_EQ;
- }
- } else if (p[1] == '>') {
- p += 2;
- s->token.val = TOK_ARROW;
- } else {
- goto def_token;
- }
- break;
- case '!':
- if (p[1] == '=') {
- if (p[2] == '=') {
- p += 3;
- s->token.val = TOK_STRICT_NEQ;
- } else {
- p += 2;
- s->token.val = TOK_NEQ;
- }
- } else {
- goto def_token;
- }
- break;
- case '&':
- if (p[1] == '=') {
- p += 2;
- s->token.val = TOK_AND_ASSIGN;
- } else if (p[1] == '&') {
- if (p[2] == '=') {
- p += 3;
- s->token.val = TOK_LAND_ASSIGN;
- } else {
- p += 2;
- s->token.val = TOK_LAND;
- }
- } else {
- goto def_token;
- }
- break;
- case '^':
- if (p[1] == '=') {
- p += 2;
- s->token.val = TOK_XOR_ASSIGN;
- } else {
- goto def_token;
- }
- break;
- case '|':
- if (p[1] == '=') {
- p += 2;
- s->token.val = TOK_OR_ASSIGN;
- } else if (p[1] == '|') {
- if (p[2] == '=') {
- p += 3;
- s->token.val = TOK_LOR_ASSIGN;
- } else {
- p += 2;
- s->token.val = TOK_LOR;
- }
- } else {
- goto def_token;
- }
- break;
- case '?':
- if (p[1] == '?') {
- if (p[2] == '=') {
- p += 3;
- s->token.val = TOK_DOUBLE_QUESTION_MARK_ASSIGN;
- } else {
- p += 2;
- s->token.val = TOK_DOUBLE_QUESTION_MARK;
- }
- } else if (p[1] == '.' && !(p[2] >= '0' && p[2] <= '9')) {
- p += 2;
- s->token.val = TOK_QUESTION_MARK_DOT;
- } else {
- goto def_token;
- }
- break;
- default:
- if (c >= 128) {
- /* unicode value */
- c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
- switch(c) {
- case CP_PS:
- case CP_LS:
- /* XXX: should avoid incrementing line_number, but
- needed to handle HTML comments */
- goto line_terminator;
- default:
- if (lre_is_space(c)) {
- goto redo;
- } else if (lre_js_is_ident_first(c)) {
- ident_has_escape = FALSE;
- goto has_ident;
- } else {
- js_parse_error(s, "unexpected character");
- goto fail;
- }
- }
- }
- def_token:
- s->token.val = c;
- p++;
- break;
- }
- s->buf_ptr = p;
- // dump_token(s, &s->token);
- return 0;
- fail:
- s->token.val = TOK_ERROR;
- return -1;
- }
- /* 'c' is the first character. Return JS_ATOM_NULL in case of error */
- /* XXX: accept unicode identifiers as JSON5 ? */
- static JSAtom json_parse_ident(JSParseState *s, const uint8_t **pp, int c)
- {
- const uint8_t *p;
- char ident_buf[128], *buf;
- size_t ident_size, ident_pos;
- JSAtom atom;
- p = *pp;
- buf = ident_buf;
- ident_size = sizeof(ident_buf);
- ident_pos = 0;
- for(;;) {
- buf[ident_pos++] = c;
- c = *p;
- if (c >= 128 || !lre_is_id_continue_byte(c))
- break;
- p++;
- if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) {
- if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) {
- atom = JS_ATOM_NULL;
- goto done;
- }
- }
- }
- atom = JS_NewAtomLen(s->ctx, buf, ident_pos);
- done:
- if (unlikely(buf != ident_buf))
- js_free(s->ctx, buf);
- *pp = p;
- return atom;
- }
- static int json_parse_string(JSParseState *s, const uint8_t **pp, int sep)
- {
- const uint8_t *p, *p_next;
- int i;
- uint32_t c;
- StringBuffer b_s, *b = &b_s;
- if (string_buffer_init(s->ctx, b, 32))
- goto fail;
- p = *pp;
- for(;;) {
- if (p >= s->buf_end) {
- goto end_of_input;
- }
- c = *p++;
- if (c == sep)
- break;
- if (c < 0x20) {
- js_parse_error_pos(s, p - 1, "Bad control character in string literal");
- goto fail;
- }
- if (c == '\\') {
- c = *p++;
- switch(c) {
- case 'b': c = '\b'; break;
- case 'f': c = '\f'; break;
- case 'n': c = '\n'; break;
- case 'r': c = '\r'; break;
- case 't': c = '\t'; break;
- case '\\': break;
- case '/': break;
- case 'u':
- c = 0;
- for(i = 0; i < 4; i++) {
- int h = from_hex(*p++);
- if (h < 0) {
- js_parse_error_pos(s, p - 1, "Bad Unicode escape");
- goto fail;
- }
- c = (c << 4) | h;
- }
- break;
- case '\n':
- if (s->ext_json)
- continue;
- goto bad_escape;
- case 'v':
- if (s->ext_json) {
- c = '\v';
- break;
- }
- goto bad_escape;
- default:
- if (c == sep)
- break;
- if (p > s->buf_end)
- goto end_of_input;
- bad_escape:
- js_parse_error_pos(s, p - 1, "Bad escaped character");
- goto fail;
- }
- } else
- if (c >= 0x80) {
- c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);
- if (c > 0x10FFFF) {
- js_parse_error_pos(s, p - 1, "Bad UTF-8 sequence");
- goto fail;
- }
- p = p_next;
- }
- if (string_buffer_putc(b, c))
- goto fail;
- }
- s->token.val = TOK_STRING;
- s->token.u.str.sep = sep;
- s->token.u.str.str = string_buffer_end(b);
- *pp = p;
- return 0;
- end_of_input:
- js_parse_error(s, "Unexpected end of JSON input");
- fail:
- string_buffer_free(b);
- return -1;
- }
- static int json_parse_number(JSParseState *s, const uint8_t **pp)
- {
- const uint8_t *p = *pp;
- const uint8_t *p_start = p;
- int radix;
- double d;
- JSATODTempMem atod_mem;
-
- if (*p == '+' || *p == '-')
- p++;
- if (!is_digit(*p)) {
- if (s->ext_json) {
- if (strstart((const char *)p, "Infinity", (const char **)&p)) {
- d = 1.0 / 0.0;
- if (*p_start == '-')
- d = -d;
- goto done;
- } else if (strstart((const char *)p, "NaN", (const char **)&p)) {
- d = NAN;
- goto done;
- } else if (*p != '.') {
- goto unexpected_token;
- }
- } else {
- goto unexpected_token;
- }
- }
- if (p[0] == '0') {
- if (s->ext_json) {
- /* also accepts base 16, 8 and 2 prefix for integers */
- radix = 10;
- if (p[1] == 'x' || p[1] == 'X') {
- p += 2;
- radix = 16;
- } else if ((p[1] == 'o' || p[1] == 'O')) {
- p += 2;
- radix = 8;
- } else if ((p[1] == 'b' || p[1] == 'B')) {
- p += 2;
- radix = 2;
- }
- if (radix != 10) {
- /* prefix is present */
- if (to_digit(*p) >= radix) {
- unexpected_token:
- return js_parse_error_pos(s, p, "Unexpected token '%c'", *p);
- }
- d = js_atod((const char *)p_start, (const char **)&p, 0,
- JS_ATOD_INT_ONLY | JS_ATOD_ACCEPT_BIN_OCT, &atod_mem);
- goto done;
- }
- }
- if (is_digit(p[1]))
- return js_parse_error_pos(s, p, "Unexpected number");
- }
- while (is_digit(*p))
- p++;
- if (*p == '.') {
- p++;
- if (!is_digit(*p))
- return js_parse_error_pos(s, p, "Unterminated fractional number");
- while (is_digit(*p))
- p++;
- }
- if (*p == 'e' || *p == 'E') {
- p++;
- if (*p == '+' || *p == '-')
- p++;
- if (!is_digit(*p))
- return js_parse_error_pos(s, p, "Exponent part is missing a number");
- while (is_digit(*p))
- p++;
- }
- d = js_atod((const char *)p_start, NULL, 10, 0, &atod_mem);
- done:
- s->token.val = TOK_NUMBER;
- s->token.u.num.val = JS_NewFloat64(s->ctx, d);
- *pp = p;
- return 0;
- }
- static __exception int json_next_token(JSParseState *s)
- {
- const uint8_t *p;
- int c;
- JSAtom atom;
- if (js_check_stack_overflow(s->ctx->rt, 0)) {
- return js_parse_error(s, "stack overflow");
- }
- free_token(s, &s->token);
- p = s->last_ptr = s->buf_ptr;
- redo:
- s->token.ptr = p;
- c = *p;
- switch(c) {
- case 0:
- if (p >= s->buf_end) {
- s->token.val = TOK_EOF;
- } else {
- goto def_token;
- }
- break;
- case '\'':
- if (!s->ext_json) {
- /* JSON does not accept single quoted strings */
- goto def_token;
- }
- /* fall through */
- case '\"':
- p++;
- if (json_parse_string(s, &p, c))
- goto fail;
- break;
- case '\r': /* accept DOS and MAC newline sequences */
- if (p[1] == '\n') {
- p++;
- }
- /* fall thru */
- case '\n':
- p++;
- goto redo;
- case '\f':
- case '\v':
- if (!s->ext_json) {
- /* JSONWhitespace does not match <VT>, nor <FF> */
- goto def_token;
- }
- /* fall through */
- case ' ':
- case '\t':
- p++;
- goto redo;
- case '/':
- if (!s->ext_json) {
- /* JSON does not accept comments */
- goto def_token;
- }
- if (p[1] == '*') {
- /* comment */
- p += 2;
- for(;;) {
- if (*p == '\0' && p >= s->buf_end) {
- js_parse_error(s, "unexpected end of comment");
- goto fail;
- }
- if (p[0] == '*' && p[1] == '/') {
- p += 2;
- break;
- }
- if (*p >= 0x80) {
- c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
- if (c == -1) {
- p++; /* skip invalid UTF-8 */
- }
- } else {
- p++;
- }
- }
- goto redo;
- } else if (p[1] == '/') {
- /* line comment */
- p += 2;
- for(;;) {
- if (*p == '\0' && p >= s->buf_end)
- break;
- if (*p == '\r' || *p == '\n')
- break;
- if (*p >= 0x80) {
- c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
- /* LS or PS are considered as line terminator */
- if (c == CP_LS || c == CP_PS) {
- break;
- } else if (c == -1) {
- p++; /* skip invalid UTF-8 */
- }
- } else {
- p++;
- }
- }
- goto redo;
- } else {
- goto def_token;
- }
- break;
- case 'a': case 'b': case 'c': case 'd':
- case 'e': case 'f': case 'g': case 'h':
- case 'i': case 'j': case 'k': case 'l':
- case 'm': case 'n': case 'o': case 'p':
- case 'q': case 'r': case 's': case 't':
- case 'u': case 'v': case 'w': case 'x':
- case 'y': case 'z':
- case 'A': case 'B': case 'C': case 'D':
- case 'E': case 'F': case 'G': case 'H':
- case 'I': case 'J': case 'K': case 'L':
- case 'M': case 'N': case 'O': case 'P':
- case 'Q': case 'R': case 'S': case 'T':
- case 'U': case 'V': case 'W': case 'X':
- case 'Y': case 'Z':
- case '_':
- case '$':
- p++;
- atom = json_parse_ident(s, &p, c);
- if (atom == JS_ATOM_NULL)
- goto fail;
- s->token.u.ident.atom = atom;
- s->token.u.ident.has_escape = FALSE;
- s->token.u.ident.is_reserved = FALSE;
- s->token.val = TOK_IDENT;
- break;
- case '+':
- if (!s->ext_json)
- goto def_token;
- goto parse_number;
- case '.':
- if (s->ext_json && is_digit(p[1]))
- goto parse_number;
- else
- goto def_token;
- case '-':
- case '0':
- case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8':
- case '9':
- /* number */
- parse_number:
- if (json_parse_number(s, &p))
- goto fail;
- break;
- default:
- if (c >= 128) {
- js_parse_error(s, "unexpected character");
- goto fail;
- }
- def_token:
- s->token.val = c;
- p++;
- break;
- }
- s->buf_ptr = p;
- // dump_token(s, &s->token);
- return 0;
- fail:
- s->token.val = TOK_ERROR;
- return -1;
- }
- static int match_identifier(const uint8_t *p, const char *s) {
- uint32_t c;
- while (*s) {
- if ((uint8_t)*s++ != *p++)
- return 0;
- }
- c = *p;
- if (c >= 128)
- c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
- return !lre_js_is_ident_next(c);
- }
- /* simple_next_token() is used to check for the next token in simple cases.
- It is only used for ':' and '=>', 'let' or 'function' look-ahead.
- (*pp) is only set if TOK_IMPORT is returned for JS_DetectModule()
- Whitespace and comments are skipped correctly.
- Then the next token is analyzed, only for specific words.
- Return values:
- - '\n' if !no_line_terminator
- - TOK_ARROW, TOK_IN, TOK_IMPORT, TOK_OF, TOK_EXPORT, TOK_FUNCTION
- - TOK_IDENT is returned for other identifiers and keywords
- - otherwise the next character or unicode codepoint is returned.
- */
- static int simple_next_token(const uint8_t **pp, BOOL no_line_terminator)
- {
- const uint8_t *p;
- uint32_t c;
- /* skip spaces and comments */
- p = *pp;
- for (;;) {
- switch(c = *p++) {
- case '\r':
- case '\n':
- if (no_line_terminator)
- return '\n';
- continue;
- case ' ':
- case '\t':
- case '\v':
- case '\f':
- continue;
- case '/':
- if (*p == '/') {
- if (no_line_terminator)
- return '\n';
- while (*p && *p != '\r' && *p != '\n')
- p++;
- continue;
- }
- if (*p == '*') {
- while (*++p) {
- if ((*p == '\r' || *p == '\n') && no_line_terminator)
- return '\n';
- if (*p == '*' && p[1] == '/') {
- p += 2;
- break;
- }
- }
- continue;
- }
- break;
- case '=':
- if (*p == '>')
- return TOK_ARROW;
- break;
- case 'i':
- if (match_identifier(p, "n"))
- return TOK_IN;
- if (match_identifier(p, "mport")) {
- *pp = p + 5;
- return TOK_IMPORT;
- }
- return TOK_IDENT;
- case 'o':
- if (match_identifier(p, "f"))
- return TOK_OF;
- return TOK_IDENT;
- case 'e':
- if (match_identifier(p, "xport"))
- return TOK_EXPORT;
- return TOK_IDENT;
- case 'f':
- if (match_identifier(p, "unction"))
- return TOK_FUNCTION;
- return TOK_IDENT;
- case '\\':
- if (*p == 'u') {
- if (lre_js_is_ident_first(lre_parse_escape(&p, TRUE)))
- return TOK_IDENT;
- }
- break;
- default:
- if (c >= 128) {
- c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p);
- if (no_line_terminator && (c == CP_PS || c == CP_LS))
- return '\n';
- }
- if (lre_is_space(c))
- continue;
- if (lre_js_is_ident_first(c))
- return TOK_IDENT;
- break;
- }
- return c;
- }
- }
- static int peek_token(JSParseState *s, BOOL no_line_terminator)
- {
- const uint8_t *p = s->buf_ptr;
- return simple_next_token(&p, no_line_terminator);
- }
- static void skip_shebang(const uint8_t **pp, const uint8_t *buf_end)
- {
- const uint8_t *p = *pp;
- int c;
- if (p[0] == '#' && p[1] == '!') {
- p += 2;
- while (p < buf_end) {
- if (*p == '\n' || *p == '\r') {
- break;
- } else if (*p >= 0x80) {
- c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
- if (c == CP_LS || c == CP_PS) {
- break;
- } else if (c == -1) {
- p++; /* skip invalid UTF-8 */
- }
- } else {
- p++;
- }
- }
- *pp = p;
- }
- }
- /* return true if 'input' contains the source of a module
- (heuristic). 'input' must be a zero terminated.
- Heuristic: skip comments and expect 'import' keyword not followed
- by '(' or '.' or export keyword.
- */
- BOOL JS_DetectModule(const char *input, size_t input_len)
- {
- const uint8_t *p = (const uint8_t *)input;
- int tok;
- skip_shebang(&p, p + input_len);
- switch(simple_next_token(&p, FALSE)) {
- case TOK_IMPORT:
- tok = simple_next_token(&p, FALSE);
- return (tok != '.' && tok != '(');
- case TOK_EXPORT:
- return TRUE;
- default:
- return FALSE;
- }
- }
- static inline int get_prev_opcode(JSFunctionDef *fd) {
- if (fd->last_opcode_pos < 0 || dbuf_error(&fd->byte_code))
- return OP_invalid;
- else
- return fd->byte_code.buf[fd->last_opcode_pos];
- }
- static BOOL js_is_live_code(JSParseState *s) {
- switch (get_prev_opcode(s->cur_func)) {
- case OP_tail_call:
- case OP_tail_call_method:
- case OP_return:
- case OP_return_undef:
- case OP_return_async:
- case OP_throw:
- case OP_throw_error:
- case OP_goto:
- #if SHORT_OPCODES
- case OP_goto8:
- case OP_goto16:
- #endif
- case OP_ret:
- return FALSE;
- default:
- return TRUE;
- }
- }
- static void emit_u8(JSParseState *s, uint8_t val)
- {
- dbuf_putc(&s->cur_func->byte_code, val);
- }
- static void emit_u16(JSParseState *s, uint16_t val)
- {
- dbuf_put_u16(&s->cur_func->byte_code, val);
- }
- static void emit_u32(JSParseState *s, uint32_t val)
- {
- dbuf_put_u32(&s->cur_func->byte_code, val);
- }
- static void emit_source_pos(JSParseState *s, const uint8_t *source_ptr)
- {
- JSFunctionDef *fd = s->cur_func;
- DynBuf *bc = &fd->byte_code;
- if (unlikely(fd->last_opcode_source_ptr != source_ptr)) {
- dbuf_putc(bc, OP_line_num);
- dbuf_put_u32(bc, source_ptr - s->buf_start);
- fd->last_opcode_source_ptr = source_ptr;
- }
- }
- static void emit_op(JSParseState *s, uint8_t val)
- {
- JSFunctionDef *fd = s->cur_func;
- DynBuf *bc = &fd->byte_code;
- fd->last_opcode_pos = bc->size;
- dbuf_putc(bc, val);
- }
- static void emit_atom(JSParseState *s, JSAtom name)
- {
- DynBuf *bc = &s->cur_func->byte_code;
- if (dbuf_claim(bc, 4))
- return; /* not enough memory : don't duplicate the atom */
- put_u32(bc->buf + bc->size, JS_DupAtom(s->ctx, name));
- bc->size += 4;
- }
- static int update_label(JSFunctionDef *s, int label, int delta)
- {
- LabelSlot *ls;
- assert(label >= 0 && label < s->label_count);
- ls = &s->label_slots[label];
- ls->ref_count += delta;
- assert(ls->ref_count >= 0);
- return ls->ref_count;
- }
- static int new_label_fd(JSFunctionDef *fd)
- {
- int label;
- LabelSlot *ls;
- if (js_resize_array(fd->ctx, (void *)&fd->label_slots,
- sizeof(fd->label_slots[0]),
- &fd->label_size, fd->label_count + 1))
- return -1;
- label = fd->label_count++;
- ls = &fd->label_slots[label];
- ls->ref_count = 0;
- ls->pos = -1;
- ls->pos2 = -1;
- ls->addr = -1;
- ls->first_reloc = NULL;
- return label;
- }
- static int new_label(JSParseState *s)
- {
- int label;
- label = new_label_fd(s->cur_func);
- if (unlikely(label < 0)) {
- dbuf_set_error(&s->cur_func->byte_code);
- }
- return label;
- }
- /* don't update the last opcode and don't emit line number info */
- static void emit_label_raw(JSParseState *s, int label)
- {
- emit_u8(s, OP_label);
- emit_u32(s, label);
- s->cur_func->label_slots[label].pos = s->cur_func->byte_code.size;
- }
- /* return the label ID offset */
- static int emit_label(JSParseState *s, int label)
- {
- if (label >= 0) {
- emit_op(s, OP_label);
- emit_u32(s, label);
- s->cur_func->label_slots[label].pos = s->cur_func->byte_code.size;
- return s->cur_func->byte_code.size - 4;
- } else {
- return -1;
- }
- }
- /* return label or -1 if dead code */
- static int emit_goto(JSParseState *s, int opcode, int label)
- {
- if (js_is_live_code(s)) {
- if (label < 0) {
- label = new_label(s);
- if (label < 0)
- return -1;
- }
- emit_op(s, opcode);
- emit_u32(s, label);
- s->cur_func->label_slots[label].ref_count++;
- return label;
- }
- return -1;
- }
- /* return the constant pool index. 'val' is not duplicated. */
- static int cpool_add(JSParseState *s, JSValue val)
- {
- JSFunctionDef *fd = s->cur_func;
- if (js_resize_array(s->ctx, (void *)&fd->cpool, sizeof(fd->cpool[0]),
- &fd->cpool_size, fd->cpool_count + 1)) {
- JS_FreeValue(s->ctx, val);
- return -1;
- }
- fd->cpool[fd->cpool_count++] = val;
- return fd->cpool_count - 1;
- }
- static __exception int emit_push_const(JSParseState *s, JSValueConst val,
- BOOL as_atom)
- {
- int idx;
- if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING && as_atom) {
- JSAtom atom;
- /* warning: JS_NewAtomStr frees the string value */
- JS_DupValue(s->ctx, val);
- atom = JS_NewAtomStr(s->ctx, JS_VALUE_GET_STRING(val));
- if (atom != JS_ATOM_NULL && !__JS_AtomIsTaggedInt(atom)) {
- emit_op(s, OP_push_atom_value);
- emit_u32(s, atom);
- return 0;
- }
- }
- idx = cpool_add(s, JS_DupValue(s->ctx, val));
- if (idx < 0)
- return -1;
- emit_op(s, OP_push_const);
- emit_u32(s, idx);
- return 0;
- }
- /* return the variable index or -1 if not found,
- add ARGUMENT_VAR_OFFSET for argument variables */
- static int find_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
- {
- int i;
- for(i = fd->arg_count; i-- > 0;) {
- if (fd->args[i].var_name == name)
- return i | ARGUMENT_VAR_OFFSET;
- }
- return -1;
- }
- static int find_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
- {
- int i;
- for(i = fd->var_count; i-- > 0;) {
- if (fd->vars[i].var_name == name && fd->vars[i].scope_level == 0)
- return i;
- }
- return find_arg(ctx, fd, name);
- }
- /* find a variable declaration in a given scope */
- static int find_var_in_scope(JSContext *ctx, JSFunctionDef *fd,
- JSAtom name, int scope_level)
- {
- int scope_idx;
- for(scope_idx = fd->scopes[scope_level].first; scope_idx >= 0;
- scope_idx = fd->vars[scope_idx].scope_next) {
- if (fd->vars[scope_idx].scope_level != scope_level)
- break;
- if (fd->vars[scope_idx].var_name == name)
- return scope_idx;
- }
- return -1;
- }
- /* return true if scope == parent_scope or if scope is a child of
- parent_scope */
- static BOOL is_child_scope(JSContext *ctx, JSFunctionDef *fd,
- int scope, int parent_scope)
- {
- while (scope >= 0) {
- if (scope == parent_scope)
- return TRUE;
- scope = fd->scopes[scope].parent;
- }
- return FALSE;
- }
- /* find a 'var' declaration in the same scope or a child scope */
- static int find_var_in_child_scope(JSContext *ctx, JSFunctionDef *fd,
- JSAtom name, int scope_level)
- {
- int i;
- for(i = 0; i < fd->var_count; i++) {
- JSVarDef *vd = &fd->vars[i];
- if (vd->var_name == name && vd->scope_level == 0) {
- if (is_child_scope(ctx, fd, vd->scope_next,
- scope_level))
- return i;
- }
- }
- return -1;
- }
- static JSGlobalVar *find_global_var(JSFunctionDef *fd, JSAtom name)
- {
- int i;
- for(i = 0; i < fd->global_var_count; i++) {
- JSGlobalVar *hf = &fd->global_vars[i];
- if (hf->var_name == name)
- return hf;
- }
- return NULL;
- }
- static JSGlobalVar *find_lexical_global_var(JSFunctionDef *fd, JSAtom name)
- {
- JSGlobalVar *hf = find_global_var(fd, name);
- if (hf && hf->is_lexical)
- return hf;
- else
- return NULL;
- }
- static int find_lexical_decl(JSContext *ctx, JSFunctionDef *fd, JSAtom name,
- int scope_idx, BOOL check_catch_var)
- {
- while (scope_idx >= 0) {
- JSVarDef *vd = &fd->vars[scope_idx];
- if (vd->var_name == name &&
- (vd->is_lexical || (vd->var_kind == JS_VAR_CATCH &&
- check_catch_var)))
- return scope_idx;
- scope_idx = vd->scope_next;
- }
- if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_GLOBAL) {
- if (find_lexical_global_var(fd, name))
- return GLOBAL_VAR_OFFSET;
- }
- return -1;
- }
- static int push_scope(JSParseState *s) {
- if (s->cur_func) {
- JSFunctionDef *fd = s->cur_func;
- int scope = fd->scope_count;
- /* XXX: should check for scope overflow */
- if ((fd->scope_count + 1) > fd->scope_size) {
- int new_size;
- size_t slack;
- JSVarScope *new_buf;
- /* XXX: potential arithmetic overflow */
- new_size = max_int(fd->scope_count + 1, fd->scope_size * 3 / 2);
- if (fd->scopes == fd->def_scope_array) {
- new_buf = js_realloc2(s->ctx, NULL, new_size * sizeof(*fd->scopes), &slack);
- if (!new_buf)
- return -1;
- memcpy(new_buf, fd->scopes, fd->scope_count * sizeof(*fd->scopes));
- } else {
- new_buf = js_realloc2(s->ctx, fd->scopes, new_size * sizeof(*fd->scopes), &slack);
- if (!new_buf)
- return -1;
- }
- new_size += slack / sizeof(*new_buf);
- fd->scopes = new_buf;
- fd->scope_size = new_size;
- }
- fd->scope_count++;
- fd->scopes[scope].parent = fd->scope_level;
- fd->scopes[scope].first = fd->scope_first;
- emit_op(s, OP_enter_scope);
- emit_u16(s, scope);
- return fd->scope_level = scope;
- }
- return 0;
- }
- static int get_first_lexical_var(JSFunctionDef *fd, int scope)
- {
- while (scope >= 0) {
- int scope_idx = fd->scopes[scope].first;
- if (scope_idx >= 0)
- return scope_idx;
- scope = fd->scopes[scope].parent;
- }
- return -1;
- }
- static void pop_scope(JSParseState *s) {
- if (s->cur_func) {
- /* disable scoped variables */
- JSFunctionDef *fd = s->cur_func;
- int scope = fd->scope_level;
- emit_op(s, OP_leave_scope);
- emit_u16(s, scope);
- fd->scope_level = fd->scopes[scope].parent;
- fd->scope_first = get_first_lexical_var(fd, fd->scope_level);
- }
- }
- static void close_scopes(JSParseState *s, int scope, int scope_stop)
- {
- while (scope > scope_stop) {
- emit_op(s, OP_leave_scope);
- emit_u16(s, scope);
- scope = s->cur_func->scopes[scope].parent;
- }
- }
- /* return the variable index or -1 if error */
- static int add_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
- {
- JSVarDef *vd;
- /* the local variable indexes are currently stored on 16 bits */
- if (fd->var_count >= JS_MAX_LOCAL_VARS) {
- JS_ThrowInternalError(ctx, "too many local variables");
- return -1;
- }
- if (js_resize_array(ctx, (void **)&fd->vars, sizeof(fd->vars[0]),
- &fd->var_size, fd->var_count + 1))
- return -1;
- vd = &fd->vars[fd->var_count++];
- memset(vd, 0, sizeof(*vd));
- vd->var_name = JS_DupAtom(ctx, name);
- vd->func_pool_idx = -1;
- return fd->var_count - 1;
- }
- static int add_scope_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name,
- JSVarKindEnum var_kind)
- {
- int idx = add_var(ctx, fd, name);
- if (idx >= 0) {
- JSVarDef *vd = &fd->vars[idx];
- vd->var_kind = var_kind;
- vd->scope_level = fd->scope_level;
- vd->scope_next = fd->scope_first;
- fd->scopes[fd->scope_level].first = idx;
- fd->scope_first = idx;
- }
- return idx;
- }
- static int add_func_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
- {
- int idx = fd->func_var_idx;
- if (idx < 0 && (idx = add_var(ctx, fd, name)) >= 0) {
- fd->func_var_idx = idx;
- fd->vars[idx].var_kind = JS_VAR_FUNCTION_NAME;
- if (fd->js_mode & JS_MODE_STRICT)
- fd->vars[idx].is_const = TRUE;
- }
- return idx;
- }
- static int add_arguments_var(JSContext *ctx, JSFunctionDef *fd)
- {
- int idx = fd->arguments_var_idx;
- if (idx < 0 && (idx = add_var(ctx, fd, JS_ATOM_arguments)) >= 0) {
- fd->arguments_var_idx = idx;
- }
- return idx;
- }
- /* add an argument definition in the argument scope. Only needed when
- "eval()" may be called in the argument scope. Return 0 if OK. */
- static int add_arguments_arg(JSContext *ctx, JSFunctionDef *fd)
- {
- int idx;
- if (fd->arguments_arg_idx < 0) {
- idx = find_var_in_scope(ctx, fd, JS_ATOM_arguments, ARG_SCOPE_INDEX);
- if (idx < 0) {
- /* XXX: the scope links are not fully updated. May be an
- issue if there are child scopes of the argument
- scope */
- idx = add_var(ctx, fd, JS_ATOM_arguments);
- if (idx < 0)
- return -1;
- fd->vars[idx].scope_next = fd->scopes[ARG_SCOPE_INDEX].first;
- fd->scopes[ARG_SCOPE_INDEX].first = idx;
- fd->vars[idx].scope_level = ARG_SCOPE_INDEX;
- fd->vars[idx].is_lexical = TRUE;
- fd->arguments_arg_idx = idx;
- }
- }
- return 0;
- }
- static int add_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
- {
- JSVarDef *vd;
- /* the local variable indexes are currently stored on 16 bits */
- if (fd->arg_count >= JS_MAX_LOCAL_VARS) {
- JS_ThrowInternalError(ctx, "too many arguments");
- return -1;
- }
- if (js_resize_array(ctx, (void **)&fd->args, sizeof(fd->args[0]),
- &fd->arg_size, fd->arg_count + 1))
- return -1;
- vd = &fd->args[fd->arg_count++];
- memset(vd, 0, sizeof(*vd));
- vd->var_name = JS_DupAtom(ctx, name);
- vd->func_pool_idx = -1;
- return fd->arg_count - 1;
- }
- /* add a global variable definition */
- static JSGlobalVar *add_global_var(JSContext *ctx, JSFunctionDef *s,
- JSAtom name)
- {
- JSGlobalVar *hf;
- if (js_resize_array(ctx, (void **)&s->global_vars,
- sizeof(s->global_vars[0]),
- &s->global_var_size, s->global_var_count + 1))
- return NULL;
- hf = &s->global_vars[s->global_var_count++];
- hf->cpool_idx = -1;
- hf->force_init = FALSE;
- hf->is_lexical = FALSE;
- hf->is_const = FALSE;
- hf->scope_level = s->scope_level;
- hf->var_name = JS_DupAtom(ctx, name);
- return hf;
- }
- typedef enum {
- JS_VAR_DEF_WITH,
- JS_VAR_DEF_LET,
- JS_VAR_DEF_CONST,
- JS_VAR_DEF_FUNCTION_DECL, /* function declaration */
- JS_VAR_DEF_NEW_FUNCTION_DECL, /* async/generator function declaration */
- JS_VAR_DEF_CATCH,
- JS_VAR_DEF_VAR,
- } JSVarDefEnum;
- static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name,
- JSVarDefEnum var_def_type)
- {
- JSContext *ctx = s->ctx;
- JSVarDef *vd;
- int idx;
- switch (var_def_type) {
- case JS_VAR_DEF_WITH:
- idx = add_scope_var(ctx, fd, name, JS_VAR_NORMAL);
- break;
- case JS_VAR_DEF_LET:
- case JS_VAR_DEF_CONST:
- case JS_VAR_DEF_FUNCTION_DECL:
- case JS_VAR_DEF_NEW_FUNCTION_DECL:
- idx = find_lexical_decl(ctx, fd, name, fd->scope_first, TRUE);
- if (idx >= 0) {
- if (idx < GLOBAL_VAR_OFFSET) {
- if (fd->vars[idx].scope_level == fd->scope_level) {
- /* same scope: in non strict mode, functions
- can be redefined (annex B.3.3.4). */
- if (!(!(fd->js_mode & JS_MODE_STRICT) &&
- var_def_type == JS_VAR_DEF_FUNCTION_DECL &&
- fd->vars[idx].var_kind == JS_VAR_FUNCTION_DECL)) {
- goto redef_lex_error;
- }
- } else if (fd->vars[idx].var_kind == JS_VAR_CATCH && (fd->vars[idx].scope_level + 2) == fd->scope_level) {
- goto redef_lex_error;
- }
- } else {
- if (fd->scope_level == fd->body_scope) {
- redef_lex_error:
- /* redefining a scoped var in the same scope: error */
- return js_parse_error(s, "invalid redefinition of lexical identifier");
- }
- }
- }
- if (var_def_type != JS_VAR_DEF_FUNCTION_DECL &&
- var_def_type != JS_VAR_DEF_NEW_FUNCTION_DECL &&
- fd->scope_level == fd->body_scope &&
- find_arg(ctx, fd, name) >= 0) {
- /* lexical variable redefines a parameter name */
- return js_parse_error(s, "invalid redefinition of parameter name");
- }
- if (find_var_in_child_scope(ctx, fd, name, fd->scope_level) >= 0) {
- return js_parse_error(s, "invalid redefinition of a variable");
- }
- if (fd->is_global_var) {
- JSGlobalVar *hf;
- hf = find_global_var(fd, name);
- if (hf && is_child_scope(ctx, fd, hf->scope_level,
- fd->scope_level)) {
- return js_parse_error(s, "invalid redefinition of global identifier");
- }
- }
- if (fd->is_eval &&
- (fd->eval_type == JS_EVAL_TYPE_GLOBAL ||
- fd->eval_type == JS_EVAL_TYPE_MODULE) &&
- fd->scope_level == fd->body_scope) {
- JSGlobalVar *hf;
- hf = add_global_var(s->ctx, fd, name);
- if (!hf)
- return -1;
- hf->is_lexical = TRUE;
- hf->is_const = (var_def_type == JS_VAR_DEF_CONST);
- idx = GLOBAL_VAR_OFFSET;
- } else {
- JSVarKindEnum var_kind;
- if (var_def_type == JS_VAR_DEF_FUNCTION_DECL)
- var_kind = JS_VAR_FUNCTION_DECL;
- else if (var_def_type == JS_VAR_DEF_NEW_FUNCTION_DECL)
- var_kind = JS_VAR_NEW_FUNCTION_DECL;
- else
- var_kind = JS_VAR_NORMAL;
- idx = add_scope_var(ctx, fd, name, var_kind);
- if (idx >= 0) {
- vd = &fd->vars[idx];
- vd->is_lexical = 1;
- vd->is_const = (var_def_type == JS_VAR_DEF_CONST);
- }
- }
- break;
- case JS_VAR_DEF_CATCH:
- idx = add_scope_var(ctx, fd, name, JS_VAR_CATCH);
- break;
- case JS_VAR_DEF_VAR:
- if (find_lexical_decl(ctx, fd, name, fd->scope_first,
- FALSE) >= 0) {
- invalid_lexical_redefinition:
- /* error to redefine a var that inside a lexical scope */
- return js_parse_error(s, "invalid redefinition of lexical identifier");
- }
- if (fd->is_global_var) {
- JSGlobalVar *hf;
- hf = find_global_var(fd, name);
- if (hf && hf->is_lexical && hf->scope_level == fd->scope_level &&
- fd->eval_type == JS_EVAL_TYPE_MODULE) {
- goto invalid_lexical_redefinition;
- }
- hf = add_global_var(s->ctx, fd, name);
- if (!hf)
- return -1;
- idx = GLOBAL_VAR_OFFSET;
- } else {
- /* if the variable already exists, don't add it again */
- idx = find_var(ctx, fd, name);
- if (idx >= 0)
- break;
- idx = add_var(ctx, fd, name);
- if (idx >= 0) {
- if (name == JS_ATOM_arguments && fd->has_arguments_binding)
- fd->arguments_var_idx = idx;
- fd->vars[idx].scope_next = fd->scope_level;
- }
- }
- break;
- default:
- abort();
- }
- return idx;
- }
- /* add a private field variable in the current scope */
- static int add_private_class_field(JSParseState *s, JSFunctionDef *fd,
- JSAtom name, JSVarKindEnum var_kind, BOOL is_static)
- {
- JSContext *ctx = s->ctx;
- JSVarDef *vd;
- int idx;
- idx = add_scope_var(ctx, fd, name, var_kind);
- if (idx < 0)
- return idx;
- vd = &fd->vars[idx];
- vd->is_lexical = 1;
- vd->is_const = 1;
- vd->is_static_private = is_static;
- return idx;
- }
- static __exception int js_parse_expr(JSParseState *s);
- static __exception int js_parse_function_decl(JSParseState *s,
- JSParseFunctionEnum func_type,
- JSFunctionKindEnum func_kind,
- JSAtom func_name, const uint8_t *ptr);
- static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s);
- static __exception int js_parse_function_decl2(JSParseState *s,
- JSParseFunctionEnum func_type,
- JSFunctionKindEnum func_kind,
- JSAtom func_name,
- const uint8_t *ptr,
- JSParseExportEnum export_flag,
- JSFunctionDef **pfd);
- static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags);
- static __exception int js_parse_assign_expr(JSParseState *s);
- static __exception int js_parse_unary(JSParseState *s, int parse_flags);
- static void push_break_entry(JSFunctionDef *fd, BlockEnv *be,
- JSAtom label_name,
- int label_break, int label_cont,
- int drop_count);
- static void pop_break_entry(JSFunctionDef *fd);
- static JSExportEntry *add_export_entry(JSParseState *s, JSModuleDef *m,
- JSAtom local_name, JSAtom export_name,
- JSExportTypeEnum export_type);
- /* Note: all the fields are already sealed except length */
- static int seal_template_obj(JSContext *ctx, JSValueConst obj)
- {
- JSObject *p;
- JSShapeProperty *prs;
- p = JS_VALUE_GET_OBJ(obj);
- prs = find_own_property1(p, JS_ATOM_length);
- if (prs) {
- if (js_update_property_flags(ctx, p, &prs,
- prs->flags & ~(JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)))
- return -1;
- }
- p->extensible = FALSE;
- return 0;
- }
- static __exception int js_parse_template(JSParseState *s, int call, int *argc)
- {
- JSContext *ctx = s->ctx;
- JSValue raw_array, template_object;
- JSToken cooked;
- int depth, ret;
- raw_array = JS_UNDEFINED; /* avoid warning */
- template_object = JS_UNDEFINED; /* avoid warning */
- if (call) {
- /* Create a template object: an array of cooked strings */
- /* Create an array of raw strings and store it to the raw property */
- template_object = JS_NewArray(ctx);
- if (JS_IsException(template_object))
- return -1;
- // pool_idx = s->cur_func->cpool_count;
- ret = emit_push_const(s, template_object, 0);
- JS_FreeValue(ctx, template_object);
- if (ret)
- return -1;
- raw_array = JS_NewArray(ctx);
- if (JS_IsException(raw_array))
- return -1;
- if (JS_DefinePropertyValue(ctx, template_object, JS_ATOM_raw,
- raw_array, JS_PROP_THROW) < 0) {
- return -1;
- }
- }
- depth = 0;
- while (s->token.val == TOK_TEMPLATE) {
- const uint8_t *p = s->token.ptr + 1;
- cooked = s->token;
- if (call) {
- if (JS_DefinePropertyValueUint32(ctx, raw_array, depth,
- JS_DupValue(ctx, s->token.u.str.str),
- JS_PROP_ENUMERABLE | JS_PROP_THROW) < 0) {
- return -1;
- }
- /* re-parse the string with escape sequences but do not throw a
- syntax error if it contains invalid sequences
- */
- if (js_parse_string(s, '`', FALSE, p, &cooked, &p)) {
- cooked.u.str.str = JS_UNDEFINED;
- }
- if (JS_DefinePropertyValueUint32(ctx, template_object, depth,
- cooked.u.str.str,
- JS_PROP_ENUMERABLE | JS_PROP_THROW) < 0) {
- return -1;
- }
- } else {
- JSString *str;
- /* re-parse the string with escape sequences and throw a
- syntax error if it contains invalid sequences
- */
- JS_FreeValue(ctx, s->token.u.str.str);
- s->token.u.str.str = JS_UNDEFINED;
- if (js_parse_string(s, '`', TRUE, p, &cooked, &p))
- return -1;
- str = JS_VALUE_GET_STRING(cooked.u.str.str);
- if (str->len != 0 || depth == 0) {
- ret = emit_push_const(s, cooked.u.str.str, 1);
- JS_FreeValue(s->ctx, cooked.u.str.str);
- if (ret)
- return -1;
- if (depth == 0) {
- if (s->token.u.str.sep == '`')
- goto done1;
- emit_op(s, OP_get_field2);
- emit_atom(s, JS_ATOM_concat);
- }
- depth++;
- } else {
- JS_FreeValue(s->ctx, cooked.u.str.str);
- }
- }
- if (s->token.u.str.sep == '`')
- goto done;
- if (next_token(s))
- return -1;
- if (js_parse_expr(s))
- return -1;
- depth++;
- if (s->token.val != '}') {
- return js_parse_error(s, "expected '}' after template expression");
- }
- /* XXX: should convert to string at this stage? */
- free_token(s, &s->token);
- /* Resume TOK_TEMPLATE parsing (s->token.line_num and
- * s->token.ptr are OK) */
- s->got_lf = FALSE;
- if (js_parse_template_part(s, s->buf_ptr))
- return -1;
- }
- return js_parse_expect(s, TOK_TEMPLATE);
- done:
- if (call) {
- /* Seal the objects */
- seal_template_obj(ctx, raw_array);
- seal_template_obj(ctx, template_object);
- *argc = depth + 1;
- } else {
- emit_op(s, OP_call_method);
- emit_u16(s, depth - 1);
- }
- done1:
- return next_token(s);
- }
- #define PROP_TYPE_IDENT 0
- #define PROP_TYPE_VAR 1
- #define PROP_TYPE_GET 2
- #define PROP_TYPE_SET 3
- #define PROP_TYPE_STAR 4
- #define PROP_TYPE_ASYNC 5
- #define PROP_TYPE_ASYNC_STAR 6
- #define PROP_TYPE_PRIVATE (1 << 4)
- static BOOL token_is_ident(int tok)
- {
- /* Accept keywords and reserved words as property names */
- return (tok == TOK_IDENT ||
- (tok >= TOK_FIRST_KEYWORD &&
- tok <= TOK_LAST_KEYWORD));
- }
- /* if the property is an expression, name = JS_ATOM_NULL */
- static int __exception js_parse_property_name(JSParseState *s,
- JSAtom *pname,
- BOOL allow_method, BOOL allow_var,
- BOOL allow_private)
- {
- int is_private = 0;
- BOOL is_non_reserved_ident;
- JSAtom name;
- int prop_type;
- prop_type = PROP_TYPE_IDENT;
- if (allow_method) {
- /* if allow_private is true (for class field parsing) and
- get/set is following by ';' (or LF with ASI), then it
- is a field name */
- if ((token_is_pseudo_keyword(s, JS_ATOM_get) ||
- token_is_pseudo_keyword(s, JS_ATOM_set)) &&
- (!allow_private || peek_token(s, TRUE) != '\n')) {
- /* get x(), set x() */
- name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
- if (next_token(s))
- goto fail1;
- if (s->token.val == ':' || s->token.val == ',' ||
- s->token.val == '}' || s->token.val == '(' ||
- s->token.val == '=' ||
- (s->token.val == ';' && allow_private)) {
- is_non_reserved_ident = TRUE;
- goto ident_found;
- }
- prop_type = PROP_TYPE_GET + (name == JS_ATOM_set);
- JS_FreeAtom(s->ctx, name);
- } else if (s->token.val == '*') {
- if (next_token(s))
- goto fail;
- prop_type = PROP_TYPE_STAR;
- } else if (token_is_pseudo_keyword(s, JS_ATOM_async) &&
- peek_token(s, TRUE) != '\n') {
- name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
- if (next_token(s))
- goto fail1;
- if (s->token.val == ':' || s->token.val == ',' ||
- s->token.val == '}' || s->token.val == '(' ||
- s->token.val == '=') {
- is_non_reserved_ident = TRUE;
- goto ident_found;
- }
- JS_FreeAtom(s->ctx, name);
- if (s->token.val == '*') {
- if (next_token(s))
- goto fail;
- prop_type = PROP_TYPE_ASYNC_STAR;
- } else {
- prop_type = PROP_TYPE_ASYNC;
- }
- }
- }
- if (token_is_ident(s->token.val)) {
- /* variable can only be a non-reserved identifier */
- is_non_reserved_ident =
- (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved);
- /* keywords and reserved words have a valid atom */
- name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
- if (next_token(s))
- goto fail1;
- ident_found:
- if (is_non_reserved_ident &&
- prop_type == PROP_TYPE_IDENT && allow_var) {
- if (!(s->token.val == ':' ||
- (s->token.val == '(' && allow_method))) {
- prop_type = PROP_TYPE_VAR;
- }
- }
- } else if (s->token.val == TOK_STRING) {
- name = JS_ValueToAtom(s->ctx, s->token.u.str.str);
- if (name == JS_ATOM_NULL)
- goto fail;
- if (next_token(s))
- goto fail1;
- } else if (s->token.val == TOK_NUMBER) {
- JSValue val;
- val = s->token.u.num.val;
- name = JS_ValueToAtom(s->ctx, val);
- if (name == JS_ATOM_NULL)
- goto fail;
- if (next_token(s))
- goto fail1;
- } else if (s->token.val == '[') {
- if (next_token(s))
- goto fail;
- if (js_parse_assign_expr(s))
- goto fail;
- if (js_parse_expect(s, ']'))
- goto fail;
- name = JS_ATOM_NULL;
- } else if (s->token.val == TOK_PRIVATE_NAME && allow_private) {
- name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
- if (next_token(s))
- goto fail1;
- is_private = PROP_TYPE_PRIVATE;
- } else {
- goto invalid_prop;
- }
- if (prop_type != PROP_TYPE_IDENT && prop_type != PROP_TYPE_VAR &&
- s->token.val != '(') {
- JS_FreeAtom(s->ctx, name);
- invalid_prop:
- js_parse_error(s, "invalid property name");
- goto fail;
- }
- *pname = name;
- return prop_type | is_private;
- fail1:
- JS_FreeAtom(s->ctx, name);
- fail:
- *pname = JS_ATOM_NULL;
- return -1;
- }
- typedef struct JSParsePos {
- BOOL got_lf;
- const uint8_t *ptr;
- } JSParsePos;
- static int js_parse_get_pos(JSParseState *s, JSParsePos *sp)
- {
- sp->ptr = s->token.ptr;
- sp->got_lf = s->got_lf;
- return 0;
- }
- static __exception int js_parse_seek_token(JSParseState *s, const JSParsePos *sp)
- {
- s->buf_ptr = sp->ptr;
- s->got_lf = sp->got_lf;
- return next_token(s);
- }
- /* return TRUE if a regexp literal is allowed after this token */
- static BOOL is_regexp_allowed(int tok)
- {
- switch (tok) {
- case TOK_NUMBER:
- case TOK_STRING:
- case TOK_REGEXP:
- case TOK_DEC:
- case TOK_INC:
- case TOK_NULL:
- case TOK_FALSE:
- case TOK_TRUE:
- case TOK_THIS:
- case ')':
- case ']':
- case '}': /* XXX: regexp may occur after */
- case TOK_IDENT:
- return FALSE;
- default:
- return TRUE;
- }
- }
- #define SKIP_HAS_SEMI (1 << 0)
- #define SKIP_HAS_ELLIPSIS (1 << 1)
- #define SKIP_HAS_ASSIGNMENT (1 << 2)
- static BOOL has_lf_in_range(const uint8_t *p1, const uint8_t *p2)
- {
- const uint8_t *tmp;
- if (p1 > p2) {
- tmp = p1;
- p1 = p2;
- p2 = tmp;
- }
- return (memchr(p1, '\n', p2 - p1) != NULL);
- }
- /* XXX: improve speed with early bailout */
- /* XXX: no longer works if regexps are present. Could use previous
- regexp parsing heuristics to handle most cases */
- static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_terminator)
- {
- char state[256];
- size_t level = 0;
- JSParsePos pos;
- int last_tok, tok = TOK_EOF;
- int c, tok_len, bits = 0;
- const uint8_t *last_token_ptr;
-
- /* protect from underflow */
- state[level++] = 0;
- js_parse_get_pos(s, &pos);
- last_tok = 0;
- for (;;) {
- switch(s->token.val) {
- case '(':
- case '[':
- case '{':
- if (level >= sizeof(state))
- goto done;
- state[level++] = s->token.val;
- break;
- case ')':
- if (state[--level] != '(')
- goto done;
- break;
- case ']':
- if (state[--level] != '[')
- goto done;
- break;
- case '}':
- c = state[--level];
- if (c == '`') {
- /* continue the parsing of the template */
- free_token(s, &s->token);
- /* Resume TOK_TEMPLATE parsing (s->token.line_num and
- * s->token.ptr are OK) */
- s->got_lf = FALSE;
- if (js_parse_template_part(s, s->buf_ptr))
- goto done;
- goto handle_template;
- } else if (c != '{') {
- goto done;
- }
- break;
- case TOK_TEMPLATE:
- handle_template:
- if (s->token.u.str.sep != '`') {
- /* '${' inside the template : closing '}' and continue
- parsing the template */
- if (level >= sizeof(state))
- goto done;
- state[level++] = '`';
- }
- break;
- case TOK_EOF:
- goto done;
- case ';':
- if (level == 2) {
- bits |= SKIP_HAS_SEMI;
- }
- break;
- case TOK_ELLIPSIS:
- if (level == 2) {
- bits |= SKIP_HAS_ELLIPSIS;
- }
- break;
- case '=':
- bits |= SKIP_HAS_ASSIGNMENT;
- break;
- case TOK_DIV_ASSIGN:
- tok_len = 2;
- goto parse_regexp;
- case '/':
- tok_len = 1;
- parse_regexp:
- if (is_regexp_allowed(last_tok)) {
- s->buf_ptr -= tok_len;
- if (js_parse_regexp(s)) {
- /* XXX: should clear the exception */
- goto done;
- }
- }
- break;
- }
- /* last_tok is only used to recognize regexps */
- if (s->token.val == TOK_IDENT &&
- (token_is_pseudo_keyword(s, JS_ATOM_of) ||
- token_is_pseudo_keyword(s, JS_ATOM_yield))) {
- last_tok = TOK_OF;
- } else {
- last_tok = s->token.val;
- }
- last_token_ptr = s->token.ptr;
- if (next_token(s)) {
- /* XXX: should clear the exception generated by next_token() */
- break;
- }
- if (level <= 1) {
- tok = s->token.val;
- if (token_is_pseudo_keyword(s, JS_ATOM_of))
- tok = TOK_OF;
- if (no_line_terminator && has_lf_in_range(last_token_ptr, s->token.ptr))
- tok = '\n';
- break;
- }
- }
- done:
- if (pbits) {
- *pbits = bits;
- }
- if (js_parse_seek_token(s, &pos))
- return -1;
- return tok;
- }
- static void set_object_name(JSParseState *s, JSAtom name)
- {
- JSFunctionDef *fd = s->cur_func;
- int opcode;
- opcode = get_prev_opcode(fd);
- if (opcode == OP_set_name) {
- /* XXX: should free atom after OP_set_name? */
- fd->byte_code.size = fd->last_opcode_pos;
- fd->last_opcode_pos = -1;
- emit_op(s, OP_set_name);
- emit_atom(s, name);
- } else if (opcode == OP_set_class_name) {
- int define_class_pos;
- JSAtom atom;
- define_class_pos = fd->last_opcode_pos + 1 -
- get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
- assert(fd->byte_code.buf[define_class_pos] == OP_define_class);
- /* for consistency we free the previous atom which is
- JS_ATOM_empty_string */
- atom = get_u32(fd->byte_code.buf + define_class_pos + 1);
- JS_FreeAtom(s->ctx, atom);
- put_u32(fd->byte_code.buf + define_class_pos + 1,
- JS_DupAtom(s->ctx, name));
- fd->last_opcode_pos = -1;
- }
- }
- static void set_object_name_computed(JSParseState *s)
- {
- JSFunctionDef *fd = s->cur_func;
- int opcode;
- opcode = get_prev_opcode(fd);
- if (opcode == OP_set_name) {
- /* XXX: should free atom after OP_set_name? */
- fd->byte_code.size = fd->last_opcode_pos;
- fd->last_opcode_pos = -1;
- emit_op(s, OP_set_name_computed);
- } else if (opcode == OP_set_class_name) {
- int define_class_pos;
- define_class_pos = fd->last_opcode_pos + 1 -
- get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
- assert(fd->byte_code.buf[define_class_pos] == OP_define_class);
- fd->byte_code.buf[define_class_pos] = OP_define_class_computed;
- fd->last_opcode_pos = -1;
- }
- }
- static __exception int js_parse_object_literal(JSParseState *s)
- {
- JSAtom name = JS_ATOM_NULL;
- const uint8_t *start_ptr;
- int prop_type;
- BOOL has_proto;
- if (next_token(s))
- goto fail;
- /* XXX: add an initial length that will be patched back */
- emit_op(s, OP_object);
- has_proto = FALSE;
- while (s->token.val != '}') {
- /* specific case for getter/setter */
- start_ptr = s->token.ptr;
- if (s->token.val == TOK_ELLIPSIS) {
- if (next_token(s))
- return -1;
- if (js_parse_assign_expr(s))
- return -1;
- emit_op(s, OP_null); /* dummy excludeList */
- emit_op(s, OP_copy_data_properties);
- emit_u8(s, 2 | (1 << 2) | (0 << 5));
- emit_op(s, OP_drop); /* pop excludeList */
- emit_op(s, OP_drop); /* pop src object */
- goto next;
- }
- prop_type = js_parse_property_name(s, &name, TRUE, TRUE, FALSE);
- if (prop_type < 0)
- goto fail;
- if (prop_type == PROP_TYPE_VAR) {
- /* shortcut for x: x */
- emit_op(s, OP_scope_get_var);
- emit_atom(s, name);
- emit_u16(s, s->cur_func->scope_level);
- emit_op(s, OP_define_field);
- emit_atom(s, name);
- } else if (s->token.val == '(') {
- BOOL is_getset = (prop_type == PROP_TYPE_GET ||
- prop_type == PROP_TYPE_SET);
- JSParseFunctionEnum func_type;
- JSFunctionKindEnum func_kind;
- int op_flags;
- func_kind = JS_FUNC_NORMAL;
- if (is_getset) {
- func_type = JS_PARSE_FUNC_GETTER + prop_type - PROP_TYPE_GET;
- } else {
- func_type = JS_PARSE_FUNC_METHOD;
- if (prop_type == PROP_TYPE_STAR)
- func_kind = JS_FUNC_GENERATOR;
- else if (prop_type == PROP_TYPE_ASYNC)
- func_kind = JS_FUNC_ASYNC;
- else if (prop_type == PROP_TYPE_ASYNC_STAR)
- func_kind = JS_FUNC_ASYNC_GENERATOR;
- }
- if (js_parse_function_decl(s, func_type, func_kind, JS_ATOM_NULL,
- start_ptr))
- goto fail;
- if (name == JS_ATOM_NULL) {
- emit_op(s, OP_define_method_computed);
- } else {
- emit_op(s, OP_define_method);
- emit_atom(s, name);
- }
- if (is_getset) {
- op_flags = OP_DEFINE_METHOD_GETTER +
- prop_type - PROP_TYPE_GET;
- } else {
- op_flags = OP_DEFINE_METHOD_METHOD;
- }
- emit_u8(s, op_flags | OP_DEFINE_METHOD_ENUMERABLE);
- } else {
- if (name == JS_ATOM_NULL) {
- /* must be done before evaluating expr */
- emit_op(s, OP_to_propkey);
- }
- if (js_parse_expect(s, ':'))
- goto fail;
- if (js_parse_assign_expr(s))
- goto fail;
- if (name == JS_ATOM_NULL) {
- set_object_name_computed(s);
- emit_op(s, OP_define_array_el);
- emit_op(s, OP_drop);
- } else if (name == JS_ATOM___proto__) {
- if (has_proto) {
- js_parse_error(s, "duplicate __proto__ property name");
- goto fail;
- }
- emit_op(s, OP_set_proto);
- has_proto = TRUE;
- } else {
- set_object_name(s, name);
- emit_op(s, OP_define_field);
- emit_atom(s, name);
- }
- }
- JS_FreeAtom(s->ctx, name);
- next:
- name = JS_ATOM_NULL;
- if (s->token.val != ',')
- break;
- if (next_token(s))
- goto fail;
- }
- if (js_parse_expect(s, '}'))
- goto fail;
- return 0;
- fail:
- JS_FreeAtom(s->ctx, name);
- return -1;
- }
- /* allow the 'in' binary operator */
- #define PF_IN_ACCEPTED (1 << 0)
- /* allow function calls parsing in js_parse_postfix_expr() */
- #define PF_POSTFIX_CALL (1 << 1)
- /* allow the exponentiation operator in js_parse_unary() */
- #define PF_POW_ALLOWED (1 << 2)
- /* forbid the exponentiation operator in js_parse_unary() */
- #define PF_POW_FORBIDDEN (1 << 3)
- static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags);
- static void emit_class_field_init(JSParseState *s);
- static JSFunctionDef *js_new_function_def(JSContext *ctx,
- JSFunctionDef *parent,
- BOOL is_eval,
- BOOL is_func_expr,
- const char *filename,
- const uint8_t *source_ptr,
- GetLineColCache *get_line_col_cache);
- static void emit_return(JSParseState *s, BOOL hasval);
- static __exception int js_parse_left_hand_side_expr(JSParseState *s)
- {
- return js_parse_postfix_expr(s, PF_POSTFIX_CALL);
- }
- static __exception int js_parse_class_default_ctor(JSParseState *s,
- BOOL has_super,
- JSFunctionDef **pfd)
- {
- JSParseFunctionEnum func_type;
- JSFunctionDef *fd = s->cur_func;
- int idx;
- fd = js_new_function_def(s->ctx, fd, FALSE, FALSE, s->filename,
- s->token.ptr, &s->get_line_col_cache);
- if (!fd)
- return -1;
- s->cur_func = fd;
- fd->has_home_object = TRUE;
- fd->super_allowed = TRUE;
- fd->has_prototype = FALSE;
- fd->has_this_binding = TRUE;
- fd->new_target_allowed = TRUE;
- push_scope(s); /* enter body scope */
- fd->body_scope = fd->scope_level;
- if (has_super) {
- fd->is_derived_class_constructor = TRUE;
- fd->super_call_allowed = TRUE;
- fd->arguments_allowed = TRUE;
- fd->has_arguments_binding = TRUE;
- func_type = JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR;
- emit_op(s, OP_init_ctor);
- // TODO(bnoordhuis) roll into OP_init_ctor
- emit_op(s, OP_scope_put_var_init);
- emit_atom(s, JS_ATOM_this);
- emit_u16(s, 0);
- emit_class_field_init(s);
- } else {
- func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR;
- /* error if not invoked as a constructor */
- emit_op(s, OP_check_ctor);
- emit_class_field_init(s);
- }
- fd->func_kind = JS_FUNC_NORMAL;
- fd->func_type = func_type;
- emit_return(s, FALSE);
- s->cur_func = fd->parent;
- if (pfd)
- *pfd = fd;
- /* the real object will be set at the end of the compilation */
- idx = cpool_add(s, JS_NULL);
- fd->parent_cpool_idx = idx;
- return 0;
- }
- /* find field in the current scope */
- static int find_private_class_field(JSContext *ctx, JSFunctionDef *fd,
- JSAtom name, int scope_level)
- {
- int idx;
- idx = fd->scopes[scope_level].first;
- while (idx != -1) {
- if (fd->vars[idx].scope_level != scope_level)
- break;
- if (fd->vars[idx].var_name == name)
- return idx;
- idx = fd->vars[idx].scope_next;
- }
- return -1;
- }
- /* initialize the class fields, called by the constructor. Note:
- super() can be called in an arrow function, so <this> and
- <class_fields_init> can be variable references */
- static void emit_class_field_init(JSParseState *s)
- {
- int label_next;
- emit_op(s, OP_scope_get_var);
- emit_atom(s, JS_ATOM_class_fields_init);
- emit_u16(s, s->cur_func->scope_level);
- /* no need to call the class field initializer if not defined */
- emit_op(s, OP_dup);
- label_next = emit_goto(s, OP_if_false, -1);
- emit_op(s, OP_scope_get_var);
- emit_atom(s, JS_ATOM_this);
- emit_u16(s, 0);
- emit_op(s, OP_swap);
- emit_op(s, OP_call_method);
- emit_u16(s, 0);
- emit_label(s, label_next);
- emit_op(s, OP_drop);
- }
- /* build a private setter function name from the private getter name */
- static JSAtom get_private_setter_name(JSContext *ctx, JSAtom name)
- {
- return js_atom_concat_str(ctx, name, "<set>");
- }
- typedef struct {
- JSFunctionDef *fields_init_fd;
- int computed_fields_count;
- BOOL need_brand;
- int brand_push_pos;
- BOOL is_static;
- } ClassFieldsDef;
- static __exception int emit_class_init_start(JSParseState *s,
- ClassFieldsDef *cf)
- {
- int label_add_brand;
- cf->fields_init_fd = js_parse_function_class_fields_init(s);
- if (!cf->fields_init_fd)
- return -1;
- s->cur_func = cf->fields_init_fd;
- if (!cf->is_static) {
- /* add the brand to the newly created instance */
- /* XXX: would be better to add the code only if needed, maybe in a
- later pass */
- emit_op(s, OP_push_false); /* will be patched later */
- cf->brand_push_pos = cf->fields_init_fd->last_opcode_pos;
- label_add_brand = emit_goto(s, OP_if_false, -1);
- emit_op(s, OP_scope_get_var);
- emit_atom(s, JS_ATOM_this);
- emit_u16(s, 0);
- emit_op(s, OP_scope_get_var);
- emit_atom(s, JS_ATOM_home_object);
- emit_u16(s, 0);
- emit_op(s, OP_add_brand);
- emit_label(s, label_add_brand);
- }
- s->cur_func = s->cur_func->parent;
- return 0;
- }
- static void emit_class_init_end(JSParseState *s, ClassFieldsDef *cf)
- {
- int cpool_idx;
- s->cur_func = cf->fields_init_fd;
- emit_op(s, OP_return_undef);
- s->cur_func = s->cur_func->parent;
- cpool_idx = cpool_add(s, JS_NULL);
- cf->fields_init_fd->parent_cpool_idx = cpool_idx;
- emit_op(s, OP_fclosure);
- emit_u32(s, cpool_idx);
- emit_op(s, OP_set_home_object);
- }
- static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr,
- JSParseExportEnum export_flag)
- {
- JSContext *ctx = s->ctx;
- JSFunctionDef *fd = s->cur_func;
- JSAtom name = JS_ATOM_NULL, class_name = JS_ATOM_NULL, class_name1;
- JSAtom class_var_name = JS_ATOM_NULL;
- JSFunctionDef *method_fd, *ctor_fd;
- int saved_js_mode, class_name_var_idx, prop_type, ctor_cpool_offset;
- int class_flags = 0, i, define_class_offset;
- BOOL is_static, is_private;
- const uint8_t *class_start_ptr = s->token.ptr;
- const uint8_t *start_ptr;
- ClassFieldsDef class_fields[2];
- /* classes are parsed and executed in strict mode */
- saved_js_mode = fd->js_mode;
- fd->js_mode |= JS_MODE_STRICT;
- if (next_token(s))
- goto fail;
- if (s->token.val == TOK_IDENT) {
- if (s->token.u.ident.is_reserved) {
- js_parse_error_reserved_identifier(s);
- goto fail;
- }
- class_name = JS_DupAtom(ctx, s->token.u.ident.atom);
- if (next_token(s))
- goto fail;
- } else if (!is_class_expr && export_flag != JS_PARSE_EXPORT_DEFAULT) {
- js_parse_error(s, "class statement requires a name");
- goto fail;
- }
- if (!is_class_expr) {
- if (class_name == JS_ATOM_NULL)
- class_var_name = JS_ATOM__default_; /* export default */
- else
- class_var_name = class_name;
- class_var_name = JS_DupAtom(ctx, class_var_name);
- }
- push_scope(s);
- if (s->token.val == TOK_EXTENDS) {
- class_flags = JS_DEFINE_CLASS_HAS_HERITAGE;
- if (next_token(s))
- goto fail;
- if (js_parse_left_hand_side_expr(s))
- goto fail;
- } else {
- emit_op(s, OP_undefined);
- }
- /* add a 'const' definition for the class name */
- if (class_name != JS_ATOM_NULL) {
- class_name_var_idx = define_var(s, fd, class_name, JS_VAR_DEF_CONST);
- if (class_name_var_idx < 0)
- goto fail;
- }
- if (js_parse_expect(s, '{'))
- goto fail;
- /* this scope contains the private fields */
- push_scope(s);
- emit_op(s, OP_push_const);
- ctor_cpool_offset = fd->byte_code.size;
- emit_u32(s, 0); /* will be patched at the end of the class parsing */
- if (class_name == JS_ATOM_NULL) {
- if (class_var_name != JS_ATOM_NULL)
- class_name1 = JS_ATOM_default;
- else
- class_name1 = JS_ATOM_empty_string;
- } else {
- class_name1 = class_name;
- }
- emit_op(s, OP_define_class);
- emit_atom(s, class_name1);
- emit_u8(s, class_flags);
- define_class_offset = fd->last_opcode_pos;
- for(i = 0; i < 2; i++) {
- ClassFieldsDef *cf = &class_fields[i];
- cf->fields_init_fd = NULL;
- cf->computed_fields_count = 0;
- cf->need_brand = FALSE;
- cf->is_static = i;
- }
- ctor_fd = NULL;
- while (s->token.val != '}') {
- if (s->token.val == ';') {
- if (next_token(s))
- goto fail;
- continue;
- }
- is_static = FALSE;
- if (s->token.val == TOK_STATIC) {
- int next = peek_token(s, TRUE);
- if (!(next == ';' || next == '}' || next == '(' || next == '='))
- is_static = TRUE;
- }
- prop_type = -1;
- if (is_static) {
- if (next_token(s))
- goto fail;
- if (s->token.val == '{') {
- ClassFieldsDef *cf = &class_fields[is_static];
- JSFunctionDef *init;
- if (!cf->fields_init_fd) {
- if (emit_class_init_start(s, cf))
- goto fail;
- }
- s->cur_func = cf->fields_init_fd;
- /* XXX: could try to avoid creating a new function and
- reuse 'fields_init_fd' with a specific 'var'
- scope */
- // stack is now: <empty>
- if (js_parse_function_decl2(s, JS_PARSE_FUNC_CLASS_STATIC_INIT,
- JS_FUNC_NORMAL, JS_ATOM_NULL,
- s->token.ptr,
- JS_PARSE_EXPORT_NONE, &init) < 0) {
- goto fail;
- }
- // stack is now: fclosure
- push_scope(s);
- emit_op(s, OP_scope_get_var);
- emit_atom(s, JS_ATOM_this);
- emit_u16(s, 0);
- // stack is now: fclosure this
- emit_op(s, OP_swap);
- // stack is now: this fclosure
- emit_op(s, OP_call_method);
- emit_u16(s, 0);
- // stack is now: returnvalue
- emit_op(s, OP_drop);
- // stack is now: <empty>
- pop_scope(s);
- s->cur_func = s->cur_func->parent;
- continue;
- }
- /* allow "static" field name */
- if (s->token.val == ';' || s->token.val == '=') {
- is_static = FALSE;
- name = JS_DupAtom(ctx, JS_ATOM_static);
- prop_type = PROP_TYPE_IDENT;
- }
- }
- if (is_static)
- emit_op(s, OP_swap);
- start_ptr = s->token.ptr;
- if (prop_type < 0) {
- prop_type = js_parse_property_name(s, &name, TRUE, FALSE, TRUE);
- if (prop_type < 0)
- goto fail;
- }
- is_private = prop_type & PROP_TYPE_PRIVATE;
- prop_type &= ~PROP_TYPE_PRIVATE;
- if ((name == JS_ATOM_constructor && !is_static &&
- prop_type != PROP_TYPE_IDENT) ||
- (name == JS_ATOM_prototype && is_static) ||
- name == JS_ATOM_hash_constructor) {
- js_parse_error(s, "invalid method name");
- goto fail;
- }
- if (prop_type == PROP_TYPE_GET || prop_type == PROP_TYPE_SET) {
- BOOL is_set = prop_type - PROP_TYPE_GET;
- JSFunctionDef *method_fd;
- if (is_private) {
- int idx, var_kind, is_static1;
- idx = find_private_class_field(ctx, fd, name, fd->scope_level);
- if (idx >= 0) {
- var_kind = fd->vars[idx].var_kind;
- is_static1 = fd->vars[idx].is_static_private;
- if (var_kind == JS_VAR_PRIVATE_FIELD ||
- var_kind == JS_VAR_PRIVATE_METHOD ||
- var_kind == JS_VAR_PRIVATE_GETTER_SETTER ||
- var_kind == (JS_VAR_PRIVATE_GETTER + is_set) ||
- (var_kind == (JS_VAR_PRIVATE_GETTER + 1 - is_set) &&
- is_static != is_static1)) {
- goto private_field_already_defined;
- }
- fd->vars[idx].var_kind = JS_VAR_PRIVATE_GETTER_SETTER;
- } else {
- if (add_private_class_field(s, fd, name,
- JS_VAR_PRIVATE_GETTER + is_set, is_static) < 0)
- goto fail;
- }
- class_fields[is_static].need_brand = TRUE;
- }
- if (js_parse_function_decl2(s, JS_PARSE_FUNC_GETTER + is_set,
- JS_FUNC_NORMAL, JS_ATOM_NULL,
- start_ptr,
- JS_PARSE_EXPORT_NONE, &method_fd))
- goto fail;
- if (is_private) {
- method_fd->need_home_object = TRUE; /* needed for brand check */
- emit_op(s, OP_set_home_object);
- /* XXX: missing function name */
- emit_op(s, OP_scope_put_var_init);
- if (is_set) {
- JSAtom setter_name;
- int ret;
- setter_name = get_private_setter_name(ctx, name);
- if (setter_name == JS_ATOM_NULL)
- goto fail;
- emit_atom(s, setter_name);
- ret = add_private_class_field(s, fd, setter_name,
- JS_VAR_PRIVATE_SETTER, is_static);
- JS_FreeAtom(ctx, setter_name);
- if (ret < 0)
- goto fail;
- } else {
- emit_atom(s, name);
- }
- emit_u16(s, s->cur_func->scope_level);
- } else {
- if (name == JS_ATOM_NULL) {
- emit_op(s, OP_define_method_computed);
- } else {
- emit_op(s, OP_define_method);
- emit_atom(s, name);
- }
- emit_u8(s, OP_DEFINE_METHOD_GETTER + is_set);
- }
- } else if (prop_type == PROP_TYPE_IDENT && s->token.val != '(') {
- ClassFieldsDef *cf = &class_fields[is_static];
- JSAtom field_var_name = JS_ATOM_NULL;
- /* class field */
- /* XXX: spec: not consistent with method name checks */
- if (name == JS_ATOM_constructor || name == JS_ATOM_prototype) {
- js_parse_error(s, "invalid field name");
- goto fail;
- }
- if (is_private) {
- if (find_private_class_field(ctx, fd, name,
- fd->scope_level) >= 0) {
- goto private_field_already_defined;
- }
- if (add_private_class_field(s, fd, name,
- JS_VAR_PRIVATE_FIELD, is_static) < 0)
- goto fail;
- emit_op(s, OP_private_symbol);
- emit_atom(s, name);
- emit_op(s, OP_scope_put_var_init);
- emit_atom(s, name);
- emit_u16(s, s->cur_func->scope_level);
- }
- if (!cf->fields_init_fd) {
- if (emit_class_init_start(s, cf))
- goto fail;
- }
- if (name == JS_ATOM_NULL ) {
- /* save the computed field name into a variable */
- field_var_name = js_atom_concat_num(ctx, JS_ATOM_computed_field + is_static, cf->computed_fields_count);
- if (field_var_name == JS_ATOM_NULL)
- goto fail;
- if (define_var(s, fd, field_var_name, JS_VAR_DEF_CONST) < 0) {
- JS_FreeAtom(ctx, field_var_name);
- goto fail;
- }
- emit_op(s, OP_to_propkey);
- emit_op(s, OP_scope_put_var_init);
- emit_atom(s, field_var_name);
- emit_u16(s, s->cur_func->scope_level);
- }
- s->cur_func = cf->fields_init_fd;
- emit_op(s, OP_scope_get_var);
- emit_atom(s, JS_ATOM_this);
- emit_u16(s, 0);
- if (name == JS_ATOM_NULL) {
- emit_op(s, OP_scope_get_var);
- emit_atom(s, field_var_name);
- emit_u16(s, s->cur_func->scope_level);
- cf->computed_fields_count++;
- JS_FreeAtom(ctx, field_var_name);
- } else if (is_private) {
- emit_op(s, OP_scope_get_var);
- emit_atom(s, name);
- emit_u16(s, s->cur_func->scope_level);
- }
- if (s->token.val == '=') {
- if (next_token(s))
- goto fail;
- if (js_parse_assign_expr(s))
- goto fail;
- } else {
- emit_op(s, OP_undefined);
- }
- if (is_private) {
- set_object_name_computed(s);
- emit_op(s, OP_define_private_field);
- } else if (name == JS_ATOM_NULL) {
- set_object_name_computed(s);
- emit_op(s, OP_define_array_el);
- emit_op(s, OP_drop);
- } else {
- set_object_name(s, name);
- emit_op(s, OP_define_field);
- emit_atom(s, name);
- }
- s->cur_func = s->cur_func->parent;
- if (js_parse_expect_semi(s))
- goto fail;
- } else {
- JSParseFunctionEnum func_type;
- JSFunctionKindEnum func_kind;
- func_type = JS_PARSE_FUNC_METHOD;
- func_kind = JS_FUNC_NORMAL;
- if (prop_type == PROP_TYPE_STAR) {
- func_kind = JS_FUNC_GENERATOR;
- } else if (prop_type == PROP_TYPE_ASYNC) {
- func_kind = JS_FUNC_ASYNC;
- } else if (prop_type == PROP_TYPE_ASYNC_STAR) {
- func_kind = JS_FUNC_ASYNC_GENERATOR;
- } else if (name == JS_ATOM_constructor && !is_static) {
- if (ctor_fd) {
- js_parse_error(s, "property constructor appears more than once");
- goto fail;
- }
- if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE)
- func_type = JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR;
- else
- func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR;
- }
- if (is_private) {
- class_fields[is_static].need_brand = TRUE;
- }
- if (js_parse_function_decl2(s, func_type, func_kind, JS_ATOM_NULL, start_ptr, JS_PARSE_EXPORT_NONE, &method_fd))
- goto fail;
- if (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR ||
- func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) {
- ctor_fd = method_fd;
- } else if (is_private) {
- method_fd->need_home_object = TRUE; /* needed for brand check */
- if (find_private_class_field(ctx, fd, name,
- fd->scope_level) >= 0) {
- private_field_already_defined:
- js_parse_error(s, "private class field is already defined");
- goto fail;
- }
- if (add_private_class_field(s, fd, name,
- JS_VAR_PRIVATE_METHOD, is_static) < 0)
- goto fail;
- emit_op(s, OP_set_home_object);
- emit_op(s, OP_set_name);
- emit_atom(s, name);
- emit_op(s, OP_scope_put_var_init);
- emit_atom(s, name);
- emit_u16(s, s->cur_func->scope_level);
- } else {
- if (name == JS_ATOM_NULL) {
- emit_op(s, OP_define_method_computed);
- } else {
- emit_op(s, OP_define_method);
- emit_atom(s, name);
- }
- emit_u8(s, OP_DEFINE_METHOD_METHOD);
- }
- }
- if (is_static)
- emit_op(s, OP_swap);
- JS_FreeAtom(ctx, name);
- name = JS_ATOM_NULL;
- }
- if (s->token.val != '}') {
- js_parse_error(s, "expecting '%c'", '}');
- goto fail;
- }
- if (!ctor_fd) {
- if (js_parse_class_default_ctor(s, class_flags & JS_DEFINE_CLASS_HAS_HERITAGE, &ctor_fd))
- goto fail;
- }
- /* patch the constant pool index for the constructor */
- put_u32(fd->byte_code.buf + ctor_cpool_offset, ctor_fd->parent_cpool_idx);
- /* store the class source code in the constructor. */
- if (!fd->strip_source) {
- js_free(ctx, ctor_fd->source);
- ctor_fd->source_len = s->buf_ptr - class_start_ptr;
- ctor_fd->source = js_strndup(ctx, (const char *)class_start_ptr,
- ctor_fd->source_len);
- if (!ctor_fd->source)
- goto fail;
- }
- /* consume the '}' */
- if (next_token(s))
- goto fail;
- {
- ClassFieldsDef *cf = &class_fields[0];
- int var_idx;
- if (cf->need_brand) {
- /* add a private brand to the prototype */
- emit_op(s, OP_dup);
- emit_op(s, OP_null);
- emit_op(s, OP_swap);
- emit_op(s, OP_add_brand);
- /* define the brand field in 'this' of the initializer */
- if (!cf->fields_init_fd) {
- if (emit_class_init_start(s, cf))
- goto fail;
- }
- /* patch the start of the function to enable the
- OP_add_brand_instance code */
- cf->fields_init_fd->byte_code.buf[cf->brand_push_pos] = OP_push_true;
- }
- /* store the function to initialize the fields to that it can be
- referenced by the constructor */
- var_idx = define_var(s, fd, JS_ATOM_class_fields_init,
- JS_VAR_DEF_CONST);
- if (var_idx < 0)
- goto fail;
- if (cf->fields_init_fd) {
- emit_class_init_end(s, cf);
- } else {
- emit_op(s, OP_undefined);
- }
- emit_op(s, OP_scope_put_var_init);
- emit_atom(s, JS_ATOM_class_fields_init);
- emit_u16(s, s->cur_func->scope_level);
- }
- /* drop the prototype */
- emit_op(s, OP_drop);
- if (class_fields[1].need_brand) {
- /* add a private brand to the class */
- emit_op(s, OP_dup);
- emit_op(s, OP_dup);
- emit_op(s, OP_add_brand);
- }
- if (class_name != JS_ATOM_NULL) {
- /* store the class name in the scoped class name variable (it
- is independent from the class statement variable
- definition) */
- emit_op(s, OP_dup);
- emit_op(s, OP_scope_put_var_init);
- emit_atom(s, class_name);
- emit_u16(s, fd->scope_level);
- }
- /* initialize the static fields */
- if (class_fields[1].fields_init_fd != NULL) {
- ClassFieldsDef *cf = &class_fields[1];
- emit_op(s, OP_dup);
- emit_class_init_end(s, cf);
- emit_op(s, OP_call_method);
- emit_u16(s, 0);
- emit_op(s, OP_drop);
- }
- pop_scope(s);
- pop_scope(s);
- /* the class statements have a block level scope */
- if (class_var_name != JS_ATOM_NULL) {
- if (define_var(s, fd, class_var_name, JS_VAR_DEF_LET) < 0)
- goto fail;
- emit_op(s, OP_scope_put_var_init);
- emit_atom(s, class_var_name);
- emit_u16(s, fd->scope_level);
- } else {
- if (class_name == JS_ATOM_NULL) {
- /* cannot use OP_set_name because the name of the class
- must be defined before the static initializers are
- executed */
- emit_op(s, OP_set_class_name);
- emit_u32(s, fd->last_opcode_pos + 1 - define_class_offset);
- }
- }
- if (export_flag != JS_PARSE_EXPORT_NONE) {
- if (!add_export_entry(s, fd->module,
- class_var_name,
- export_flag == JS_PARSE_EXPORT_NAMED ? class_var_name : JS_ATOM_default,
- JS_EXPORT_TYPE_LOCAL))
- goto fail;
- }
- JS_FreeAtom(ctx, class_name);
- JS_FreeAtom(ctx, class_var_name);
- fd->js_mode = saved_js_mode;
- return 0;
- fail:
- JS_FreeAtom(ctx, name);
- JS_FreeAtom(ctx, class_name);
- JS_FreeAtom(ctx, class_var_name);
- fd->js_mode = saved_js_mode;
- return -1;
- }
- static __exception int js_parse_array_literal(JSParseState *s)
- {
- uint32_t idx;
- BOOL need_length;
- if (next_token(s))
- return -1;
- /* small regular arrays are created on the stack */
- idx = 0;
- while (s->token.val != ']' && idx < 32) {
- if (s->token.val == ',' || s->token.val == TOK_ELLIPSIS)
- break;
- if (js_parse_assign_expr(s))
- return -1;
- idx++;
- /* accept trailing comma */
- if (s->token.val == ',') {
- if (next_token(s))
- return -1;
- } else
- if (s->token.val != ']')
- goto done;
- }
- emit_op(s, OP_array_from);
- emit_u16(s, idx);
- /* larger arrays and holes are handled with explicit indices */
- need_length = FALSE;
- while (s->token.val != ']' && idx < 0x7fffffff) {
- if (s->token.val == TOK_ELLIPSIS)
- break;
- need_length = TRUE;
- if (s->token.val != ',') {
- if (js_parse_assign_expr(s))
- return -1;
- emit_op(s, OP_define_field);
- emit_u32(s, __JS_AtomFromUInt32(idx));
- need_length = FALSE;
- }
- idx++;
- /* accept trailing comma */
- if (s->token.val == ',') {
- if (next_token(s))
- return -1;
- }
- }
- if (s->token.val == ']') {
- if (need_length) {
- /* Set the length: Cannot use OP_define_field because
- length is not configurable */
- emit_op(s, OP_dup);
- emit_op(s, OP_push_i32);
- emit_u32(s, idx);
- emit_op(s, OP_put_field);
- emit_atom(s, JS_ATOM_length);
- }
- goto done;
- }
- /* huge arrays and spread elements require a dynamic index on the stack */
- emit_op(s, OP_push_i32);
- emit_u32(s, idx);
- /* stack has array, index */
- while (s->token.val != ']') {
- if (s->token.val == TOK_ELLIPSIS) {
- if (next_token(s))
- return -1;
- if (js_parse_assign_expr(s))
- return -1;
- #if 1
- emit_op(s, OP_append);
- #else
- int label_next, label_done;
- label_next = new_label(s);
- label_done = new_label(s);
- /* enumerate object */
- emit_op(s, OP_for_of_start);
- emit_op(s, OP_rot5l);
- emit_op(s, OP_rot5l);
- emit_label(s, label_next);
- /* on stack: enum_rec array idx */
- emit_op(s, OP_for_of_next);
- emit_u8(s, 2);
- emit_goto(s, OP_if_true, label_done);
- /* append element */
- /* enum_rec array idx val -> enum_rec array new_idx */
- emit_op(s, OP_define_array_el);
- emit_op(s, OP_inc);
- emit_goto(s, OP_goto, label_next);
- emit_label(s, label_done);
- /* close enumeration */
- emit_op(s, OP_drop); /* drop undef val */
- emit_op(s, OP_nip1); /* drop enum_rec */
- emit_op(s, OP_nip1);
- emit_op(s, OP_nip1);
- #endif
- } else {
- need_length = TRUE;
- if (s->token.val != ',') {
- if (js_parse_assign_expr(s))
- return -1;
- /* a idx val */
- emit_op(s, OP_define_array_el);
- need_length = FALSE;
- }
- emit_op(s, OP_inc);
- }
- if (s->token.val != ',')
- break;
- if (next_token(s))
- return -1;
- }
- if (need_length) {
- /* Set the length: cannot use OP_define_field because
- length is not configurable */
- emit_op(s, OP_dup1); /* array length - array array length */
- emit_op(s, OP_put_field);
- emit_atom(s, JS_ATOM_length);
- } else {
- emit_op(s, OP_drop); /* array length - array */
- }
- done:
- return js_parse_expect(s, ']');
- }
- /* check if scope chain contains a with statement */
- static BOOL has_with_scope(JSFunctionDef *s, int scope_level)
- {
- while (s) {
- /* no with in strict mode */
- if (!(s->js_mode & JS_MODE_STRICT)) {
- int scope_idx = s->scopes[scope_level].first;
- while (scope_idx >= 0) {
- JSVarDef *vd = &s->vars[scope_idx];
- if (vd->var_name == JS_ATOM__with_)
- return TRUE;
- scope_idx = vd->scope_next;
- }
- }
- /* check parent scopes */
- scope_level = s->parent_scope_level;
- s = s->parent;
- }
- return FALSE;
- }
- static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope,
- JSAtom *pname, int *plabel, int *pdepth, BOOL keep,
- int tok)
- {
- JSFunctionDef *fd;
- int opcode, scope, label, depth;
- JSAtom name;
- /* we check the last opcode to get the lvalue type */
- fd = s->cur_func;
- scope = 0;
- name = JS_ATOM_NULL;
- label = -1;
- depth = 0;
- switch(opcode = get_prev_opcode(fd)) {
- case OP_scope_get_var:
- name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
- scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5);
- if ((name == JS_ATOM_arguments || name == JS_ATOM_eval) &&
- (fd->js_mode & JS_MODE_STRICT)) {
- return js_parse_error(s, "invalid lvalue in strict mode");
- }
- if (name == JS_ATOM_this || name == JS_ATOM_new_target)
- goto invalid_lvalue;
- if (has_with_scope(fd, scope)) {
- depth = 2; /* will generate OP_get_ref_value */
- } else {
- depth = 0;
- }
- break;
- case OP_get_field:
- name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
- depth = 1;
- break;
- case OP_scope_get_private_field:
- name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
- scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5);
- depth = 1;
- break;
- case OP_get_array_el:
- depth = 2;
- break;
- case OP_get_super_value:
- depth = 3;
- break;
- default:
- invalid_lvalue:
- if (tok == TOK_FOR) {
- return js_parse_error(s, "invalid for in/of left hand-side");
- } else if (tok == TOK_INC || tok == TOK_DEC) {
- return js_parse_error(s, "invalid increment/decrement operand");
- } else if (tok == '[' || tok == '{') {
- return js_parse_error(s, "invalid destructuring target");
- } else {
- return js_parse_error(s, "invalid assignment left-hand side");
- }
- }
- /* remove the last opcode */
- fd->byte_code.size = fd->last_opcode_pos;
- fd->last_opcode_pos = -1;
- if (keep) {
- /* get the value but keep the object/fields on the stack */
- switch(opcode) {
- case OP_scope_get_var:
- if (depth != 0) {
- label = new_label(s);
- if (label < 0)
- return -1;
- emit_op(s, OP_scope_make_ref);
- emit_atom(s, name);
- emit_u32(s, label);
- emit_u16(s, scope);
- update_label(fd, label, 1);
- emit_op(s, OP_get_ref_value);
- opcode = OP_get_ref_value;
- } else {
- emit_op(s, OP_scope_get_var);
- emit_atom(s, name);
- emit_u16(s, scope);
- }
- break;
- case OP_get_field:
- emit_op(s, OP_get_field2);
- emit_atom(s, name);
- break;
- case OP_scope_get_private_field:
- emit_op(s, OP_scope_get_private_field2);
- emit_atom(s, name);
- emit_u16(s, scope);
- break;
- case OP_get_array_el:
- emit_op(s, OP_get_array_el3);
- break;
- case OP_get_super_value:
- emit_op(s, OP_to_propkey);
- emit_op(s, OP_dup3);
- emit_op(s, OP_get_super_value);
- break;
- default:
- abort();
- }
- } else {
- switch(opcode) {
- case OP_scope_get_var:
- if (depth != 0) {
- label = new_label(s);
- if (label < 0)
- return -1;
- emit_op(s, OP_scope_make_ref);
- emit_atom(s, name);
- emit_u32(s, label);
- emit_u16(s, scope);
- update_label(fd, label, 1);
- opcode = OP_get_ref_value;
- }
- break;
- default:
- break;
- }
- }
- *popcode = opcode;
- *pscope = scope;
- /* name has refcount for OP_get_field and OP_get_ref_value,
- and JS_ATOM_NULL for other opcodes */
- *pname = name;
- *plabel = label;
- if (pdepth)
- *pdepth = depth;
- return 0;
- }
- typedef enum {
- PUT_LVALUE_NOKEEP, /* [depth] v -> */
- PUT_LVALUE_NOKEEP_DEPTH, /* [depth] v -> , keep depth (currently
- just disable optimizations) */
- PUT_LVALUE_KEEP_TOP, /* [depth] v -> v */
- PUT_LVALUE_KEEP_SECOND, /* [depth] v0 v -> v0 */
- PUT_LVALUE_NOKEEP_BOTTOM, /* v [depth] -> */
- } PutLValueEnum;
- /* name has a live reference. 'is_let' is only used with opcode =
- OP_scope_get_var which is never generated by get_lvalue(). */
- static void put_lvalue(JSParseState *s, int opcode, int scope,
- JSAtom name, int label, PutLValueEnum special,
- BOOL is_let)
- {
- switch(opcode) {
- case OP_scope_get_var:
- /* depth = 0 */
- switch(special) {
- case PUT_LVALUE_NOKEEP:
- case PUT_LVALUE_NOKEEP_DEPTH:
- case PUT_LVALUE_KEEP_SECOND:
- case PUT_LVALUE_NOKEEP_BOTTOM:
- break;
- case PUT_LVALUE_KEEP_TOP:
- emit_op(s, OP_dup);
- break;
- default:
- abort();
- }
- break;
- case OP_get_field:
- case OP_scope_get_private_field:
- /* depth = 1 */
- switch(special) {
- case PUT_LVALUE_NOKEEP:
- case PUT_LVALUE_NOKEEP_DEPTH:
- break;
- case PUT_LVALUE_KEEP_TOP:
- emit_op(s, OP_insert2); /* obj v -> v obj v */
- break;
- case PUT_LVALUE_KEEP_SECOND:
- emit_op(s, OP_perm3); /* obj v0 v -> v0 obj v */
- break;
- case PUT_LVALUE_NOKEEP_BOTTOM:
- emit_op(s, OP_swap);
- break;
- default:
- abort();
- }
- break;
- case OP_get_array_el:
- case OP_get_ref_value:
- /* depth = 2 */
- if (opcode == OP_get_ref_value) {
- JS_FreeAtom(s->ctx, name);
- emit_label(s, label);
- }
- switch(special) {
- case PUT_LVALUE_NOKEEP:
- emit_op(s, OP_nop); /* will trigger optimization */
- break;
- case PUT_LVALUE_NOKEEP_DEPTH:
- break;
- case PUT_LVALUE_KEEP_TOP:
- emit_op(s, OP_insert3); /* obj prop v -> v obj prop v */
- break;
- case PUT_LVALUE_KEEP_SECOND:
- emit_op(s, OP_perm4); /* obj prop v0 v -> v0 obj prop v */
- break;
- case PUT_LVALUE_NOKEEP_BOTTOM:
- emit_op(s, OP_rot3l);
- break;
- default:
- abort();
- }
- break;
- case OP_get_super_value:
- /* depth = 3 */
- switch(special) {
- case PUT_LVALUE_NOKEEP:
- case PUT_LVALUE_NOKEEP_DEPTH:
- break;
- case PUT_LVALUE_KEEP_TOP:
- emit_op(s, OP_insert4); /* this obj prop v -> v this obj prop v */
- break;
- case PUT_LVALUE_KEEP_SECOND:
- emit_op(s, OP_perm5); /* this obj prop v0 v -> v0 this obj prop v */
- break;
- case PUT_LVALUE_NOKEEP_BOTTOM:
- emit_op(s, OP_rot4l);
- break;
- default:
- abort();
- }
- break;
- default:
- break;
- }
- switch(opcode) {
- case OP_scope_get_var: /* val -- */
- emit_op(s, is_let ? OP_scope_put_var_init : OP_scope_put_var);
- emit_u32(s, name); /* has refcount */
- emit_u16(s, scope);
- break;
- case OP_get_field:
- emit_op(s, OP_put_field);
- emit_u32(s, name); /* name has refcount */
- break;
- case OP_scope_get_private_field:
- emit_op(s, OP_scope_put_private_field);
- emit_u32(s, name); /* name has refcount */
- emit_u16(s, scope);
- break;
- case OP_get_array_el:
- emit_op(s, OP_put_array_el);
- break;
- case OP_get_ref_value:
- emit_op(s, OP_put_ref_value);
- break;
- case OP_get_super_value:
- emit_op(s, OP_put_super_value);
- break;
- default:
- abort();
- }
- }
- static __exception int js_parse_expr_paren(JSParseState *s)
- {
- if (js_parse_expect(s, '('))
- return -1;
- if (js_parse_expr(s))
- return -1;
- if (js_parse_expect(s, ')'))
- return -1;
- return 0;
- }
- static int js_unsupported_keyword(JSParseState *s, JSAtom atom)
- {
- char buf[ATOM_GET_STR_BUF_SIZE];
- return js_parse_error(s, "unsupported keyword: %s",
- JS_AtomGetStr(s->ctx, buf, sizeof(buf), atom));
- }
- static __exception int js_define_var(JSParseState *s, JSAtom name, int tok)
- {
- JSFunctionDef *fd = s->cur_func;
- JSVarDefEnum var_def_type;
- if (name == JS_ATOM_yield && fd->func_kind == JS_FUNC_GENERATOR) {
- return js_parse_error(s, "yield is a reserved identifier");
- }
- if ((name == JS_ATOM_arguments || name == JS_ATOM_eval)
- && (fd->js_mode & JS_MODE_STRICT)) {
- return js_parse_error(s, "invalid variable name in strict mode");
- }
- if (name == JS_ATOM_let
- && (tok == TOK_LET || tok == TOK_CONST)) {
- return js_parse_error(s, "invalid lexical variable name");
- }
- switch(tok) {
- case TOK_LET:
- var_def_type = JS_VAR_DEF_LET;
- break;
- case TOK_CONST:
- var_def_type = JS_VAR_DEF_CONST;
- break;
- case TOK_VAR:
- var_def_type = JS_VAR_DEF_VAR;
- break;
- case TOK_CATCH:
- var_def_type = JS_VAR_DEF_CATCH;
- break;
- default:
- abort();
- }
- if (define_var(s, fd, name, var_def_type) < 0)
- return -1;
- return 0;
- }
- static void js_emit_spread_code(JSParseState *s, int depth)
- {
- int label_rest_next, label_rest_done;
- /* XXX: could check if enum object is an actual array and optimize
- slice extraction. enumeration record and target array are in a
- different order from OP_append case. */
- /* enum_rec xxx -- enum_rec xxx array 0 */
- emit_op(s, OP_array_from);
- emit_u16(s, 0);
- emit_op(s, OP_push_i32);
- emit_u32(s, 0);
- emit_label(s, label_rest_next = new_label(s));
- emit_op(s, OP_for_of_next);
- emit_u8(s, 2 + depth);
- label_rest_done = emit_goto(s, OP_if_true, -1);
- /* array idx val -- array idx */
- emit_op(s, OP_define_array_el);
- emit_op(s, OP_inc);
- emit_goto(s, OP_goto, label_rest_next);
- emit_label(s, label_rest_done);
- /* enum_rec xxx array idx undef -- enum_rec xxx array */
- emit_op(s, OP_drop);
- emit_op(s, OP_drop);
- }
- static int js_parse_check_duplicate_parameter(JSParseState *s, JSAtom name)
- {
- /* Check for duplicate parameter names */
- JSFunctionDef *fd = s->cur_func;
- int i;
- for (i = 0; i < fd->arg_count; i++) {
- if (fd->args[i].var_name == name)
- goto duplicate;
- }
- for (i = 0; i < fd->var_count; i++) {
- if (fd->vars[i].var_name == name)
- goto duplicate;
- }
- return 0;
- duplicate:
- return js_parse_error(s, "duplicate parameter names not allowed in this context");
- }
- /* tok = TOK_VAR, TOK_LET or TOK_CONST. Return whether a reference
- must be taken to the variable for proper 'with' or global variable
- evaluation */
- /* Note: this function is needed only because variable references are
- not yet optimized in destructuring */
- static BOOL need_var_reference(JSParseState *s, int tok)
- {
- JSFunctionDef *fd = s->cur_func;
- if (tok != TOK_VAR)
- return FALSE; /* no reference for let/const */
- if (fd->js_mode & JS_MODE_STRICT) {
- if (!fd->is_global_var)
- return FALSE; /* local definitions in strict mode in function or direct eval */
- if (s->is_module)
- return FALSE; /* in a module global variables are like closure variables */
- }
- return TRUE;
- }
- static JSAtom js_parse_destructuring_var(JSParseState *s, int tok, int is_arg)
- {
- JSAtom name;
- if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)
- || ((s->cur_func->js_mode & JS_MODE_STRICT) &&
- (s->token.u.ident.atom == JS_ATOM_eval || s->token.u.ident.atom == JS_ATOM_arguments))) {
- js_parse_error(s, "invalid destructuring target");
- return JS_ATOM_NULL;
- }
- name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
- if (is_arg && js_parse_check_duplicate_parameter(s, name))
- goto fail;
- if (next_token(s))
- goto fail;
- return name;
- fail:
- JS_FreeAtom(s->ctx, name);
- return JS_ATOM_NULL;
- }
- /* Return -1 if error, 0 if no initializer, 1 if an initializer is
- present at the top level. */
- static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg,
- int hasval, int has_ellipsis,
- BOOL allow_initializer, BOOL export_flag)
- {
- int label_parse, label_assign, label_done, label_lvalue, depth_lvalue;
- int start_addr, assign_addr;
- JSAtom prop_name, var_name;
- int opcode, scope, tok1, skip_bits;
- BOOL has_initializer;
- if (has_ellipsis < 0) {
- /* pre-parse destructuration target for spread detection */
- js_parse_skip_parens_token(s, &skip_bits, FALSE);
- has_ellipsis = skip_bits & SKIP_HAS_ELLIPSIS;
- }
- label_parse = new_label(s);
- label_assign = new_label(s);
- start_addr = s->cur_func->byte_code.size;
- if (hasval) {
- /* consume value from the stack */
- emit_op(s, OP_dup);
- emit_op(s, OP_undefined);
- emit_op(s, OP_strict_eq);
- emit_goto(s, OP_if_true, label_parse);
- emit_label(s, label_assign);
- } else {
- emit_goto(s, OP_goto, label_parse);
- emit_label(s, label_assign);
- /* leave value on the stack */
- emit_op(s, OP_dup);
- }
- assign_addr = s->cur_func->byte_code.size;
- if (s->token.val == '{') {
- if (next_token(s))
- return -1;
- /* throw an exception if the value cannot be converted to an object */
- emit_op(s, OP_to_object);
- if (has_ellipsis) {
- /* add excludeList on stack just below src object */
- emit_op(s, OP_object);
- emit_op(s, OP_swap);
- }
- while (s->token.val != '}') {
- int prop_type;
- if (s->token.val == TOK_ELLIPSIS) {
- if (!has_ellipsis) {
- JS_ThrowInternalError(s->ctx, "unexpected ellipsis token");
- return -1;
- }
- if (next_token(s))
- return -1;
- if (tok) {
- var_name = js_parse_destructuring_var(s, tok, is_arg);
- if (var_name == JS_ATOM_NULL)
- return -1;
- if (need_var_reference(s, tok)) {
- /* Must make a reference for proper `with` semantics */
- emit_op(s, OP_scope_get_var);
- emit_atom(s, var_name);
- emit_u16(s, s->cur_func->scope_level);
- JS_FreeAtom(s->ctx, var_name);
- goto lvalue0;
- } else {
- opcode = OP_scope_get_var;
- scope = s->cur_func->scope_level;
- label_lvalue = -1;
- depth_lvalue = 0;
- }
- } else {
- if (js_parse_left_hand_side_expr(s))
- return -1;
- lvalue0:
- if (get_lvalue(s, &opcode, &scope, &var_name,
- &label_lvalue, &depth_lvalue, FALSE, '{'))
- return -1;
- }
- if (s->token.val != '}') {
- js_parse_error(s, "assignment rest property must be last");
- goto var_error;
- }
- emit_op(s, OP_object); /* target */
- emit_op(s, OP_copy_data_properties);
- emit_u8(s, 0 | ((depth_lvalue + 1) << 2) | ((depth_lvalue + 2) << 5));
- goto set_val;
- }
- prop_type = js_parse_property_name(s, &prop_name, FALSE, TRUE, FALSE);
- if (prop_type < 0)
- return -1;
- var_name = JS_ATOM_NULL;
- if (prop_type == PROP_TYPE_IDENT) {
- if (next_token(s))
- goto prop_error;
- if ((s->token.val == '[' || s->token.val == '{')
- && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == ',' ||
- tok1 == '=' || tok1 == '}')) {
- if (prop_name == JS_ATOM_NULL) {
- /* computed property name on stack */
- if (has_ellipsis) {
- /* define the property in excludeList */
- emit_op(s, OP_to_propkey); /* avoid calling ToString twice */
- emit_op(s, OP_perm3); /* TOS: src excludeList prop */
- emit_op(s, OP_null); /* TOS: src excludeList prop null */
- emit_op(s, OP_define_array_el); /* TOS: src excludeList prop */
- emit_op(s, OP_perm3); /* TOS: excludeList src prop */
- }
- /* get the computed property from the source object */
- emit_op(s, OP_get_array_el2);
- } else {
- /* named property */
- if (has_ellipsis) {
- /* define the property in excludeList */
- emit_op(s, OP_swap); /* TOS: src excludeList */
- emit_op(s, OP_null); /* TOS: src excludeList null */
- emit_op(s, OP_define_field); /* TOS: src excludeList */
- emit_atom(s, prop_name);
- emit_op(s, OP_swap); /* TOS: excludeList src */
- }
- /* get the named property from the source object */
- emit_op(s, OP_get_field2);
- emit_u32(s, prop_name);
- }
- if (js_parse_destructuring_element(s, tok, is_arg, TRUE, -1, TRUE, export_flag) < 0)
- return -1;
- if (s->token.val == '}')
- break;
- /* accept a trailing comma before the '}' */
- if (js_parse_expect(s, ','))
- return -1;
- continue;
- }
- if (prop_name == JS_ATOM_NULL) {
- emit_op(s, OP_to_propkey);
- if (has_ellipsis) {
- /* define the property in excludeList */
- emit_op(s, OP_perm3);
- emit_op(s, OP_null);
- emit_op(s, OP_define_array_el);
- emit_op(s, OP_perm3);
- }
- /* source prop -- source source prop */
- emit_op(s, OP_dup1);
- } else {
- if (has_ellipsis) {
- /* define the property in excludeList */
- emit_op(s, OP_swap);
- emit_op(s, OP_null);
- emit_op(s, OP_define_field);
- emit_atom(s, prop_name);
- emit_op(s, OP_swap);
- }
- /* source -- source source */
- emit_op(s, OP_dup);
- }
- if (tok) {
- var_name = js_parse_destructuring_var(s, tok, is_arg);
- if (var_name == JS_ATOM_NULL)
- goto prop_error;
- if (need_var_reference(s, tok)) {
- /* Must make a reference for proper `with` semantics */
- emit_op(s, OP_scope_get_var);
- emit_atom(s, var_name);
- emit_u16(s, s->cur_func->scope_level);
- JS_FreeAtom(s->ctx, var_name);
- goto lvalue1;
- } else {
- /* no need to make a reference for let/const */
- opcode = OP_scope_get_var;
- scope = s->cur_func->scope_level;
- label_lvalue = -1;
- depth_lvalue = 0;
- }
- } else {
- if (js_parse_left_hand_side_expr(s))
- goto prop_error;
- lvalue1:
- if (get_lvalue(s, &opcode, &scope, &var_name,
- &label_lvalue, &depth_lvalue, FALSE, '{'))
- goto prop_error;
- /* swap ref and lvalue object if any */
- if (prop_name == JS_ATOM_NULL) {
- switch(depth_lvalue) {
- case 0:
- break;
- case 1:
- /* source prop x -> x source prop */
- emit_op(s, OP_rot3r);
- break;
- case 2:
- /* source prop x y -> x y source prop */
- emit_op(s, OP_swap2); /* t p2 s p1 */
- break;
- case 3:
- /* source prop x y z -> x y z source prop */
- emit_op(s, OP_rot5l);
- emit_op(s, OP_rot5l);
- break;
- default:
- abort();
- }
- } else {
- switch(depth_lvalue) {
- case 0:
- break;
- case 1:
- /* source x -> x source */
- emit_op(s, OP_swap);
- break;
- case 2:
- /* source x y -> x y source */
- emit_op(s, OP_rot3l);
- break;
- case 3:
- /* source x y z -> x y z source */
- emit_op(s, OP_rot4l);
- break;
- default:
- abort();
- }
- }
- }
- if (prop_name == JS_ATOM_NULL) {
- /* computed property name on stack */
- /* XXX: should have OP_get_array_el2x with depth */
- /* source prop -- val */
- emit_op(s, OP_get_array_el);
- } else {
- /* named property */
- /* XXX: should have OP_get_field2x with depth */
- /* source -- val */
- emit_op(s, OP_get_field);
- emit_u32(s, prop_name);
- }
- } else {
- /* prop_type = PROP_TYPE_VAR, cannot be a computed property */
- if (is_arg && js_parse_check_duplicate_parameter(s, prop_name))
- goto prop_error;
- if ((s->cur_func->js_mode & JS_MODE_STRICT) &&
- (prop_name == JS_ATOM_eval || prop_name == JS_ATOM_arguments)) {
- js_parse_error(s, "invalid destructuring target");
- goto prop_error;
- }
- if (has_ellipsis) {
- /* define the property in excludeList */
- emit_op(s, OP_swap);
- emit_op(s, OP_null);
- emit_op(s, OP_define_field);
- emit_atom(s, prop_name);
- emit_op(s, OP_swap);
- }
- if (!tok || need_var_reference(s, tok)) {
- /* generate reference */
- /* source -- source source */
- emit_op(s, OP_dup);
- emit_op(s, OP_scope_get_var);
- emit_atom(s, prop_name);
- emit_u16(s, s->cur_func->scope_level);
- goto lvalue1;
- } else {
- /* no need to make a reference for let/const */
- var_name = JS_DupAtom(s->ctx, prop_name);
- opcode = OP_scope_get_var;
- scope = s->cur_func->scope_level;
- label_lvalue = -1;
- depth_lvalue = 0;
-
- /* source -- source val */
- emit_op(s, OP_get_field2);
- emit_u32(s, prop_name);
- }
- }
- set_val:
- if (tok) {
- if (js_define_var(s, var_name, tok))
- goto var_error;
- if (export_flag) {
- if (!add_export_entry(s, s->cur_func->module, var_name, var_name,
- JS_EXPORT_TYPE_LOCAL))
- goto var_error;
- }
- scope = s->cur_func->scope_level; /* XXX: check */
- }
- if (s->token.val == '=') { /* handle optional default value */
- int label_hasval;
- emit_op(s, OP_dup);
- emit_op(s, OP_undefined);
- emit_op(s, OP_strict_eq);
- label_hasval = emit_goto(s, OP_if_false, -1);
- if (next_token(s))
- goto var_error;
- emit_op(s, OP_drop);
- if (js_parse_assign_expr(s))
- goto var_error;
- if (opcode == OP_scope_get_var || opcode == OP_get_ref_value)
- set_object_name(s, var_name);
- emit_label(s, label_hasval);
- }
- /* store value into lvalue object */
- put_lvalue(s, opcode, scope, var_name, label_lvalue,
- PUT_LVALUE_NOKEEP_DEPTH,
- (tok == TOK_CONST || tok == TOK_LET));
- if (s->token.val == '}')
- break;
- /* accept a trailing comma before the '}' */
- if (js_parse_expect(s, ','))
- return -1;
- }
- /* drop the source object */
- emit_op(s, OP_drop);
- if (has_ellipsis) {
- emit_op(s, OP_drop); /* pop excludeList */
- }
- if (next_token(s))
- return -1;
- } else if (s->token.val == '[') {
- BOOL has_spread;
- int enum_depth;
- BlockEnv block_env;
- if (next_token(s))
- return -1;
- /* the block environment is only needed in generators in case
- 'yield' triggers a 'return' */
- push_break_entry(s->cur_func, &block_env,
- JS_ATOM_NULL, -1, -1, 2);
- block_env.has_iterator = TRUE;
- emit_op(s, OP_for_of_start);
- has_spread = FALSE;
- while (s->token.val != ']') {
- /* get the next value */
- if (s->token.val == TOK_ELLIPSIS) {
- if (next_token(s))
- return -1;
- if (s->token.val == ',' || s->token.val == ']')
- return js_parse_error(s, "missing binding pattern...");
- has_spread = TRUE;
- }
- if (s->token.val == ',') {
- /* do nothing, skip the value, has_spread is false */
- emit_op(s, OP_for_of_next);
- emit_u8(s, 0);
- emit_op(s, OP_drop);
- emit_op(s, OP_drop);
- } else if ((s->token.val == '[' || s->token.val == '{')
- && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == ',' ||
- tok1 == '=' || tok1 == ']')) {
- if (has_spread) {
- if (tok1 == '=')
- return js_parse_error(s, "rest element cannot have a default value");
- js_emit_spread_code(s, 0);
- } else {
- emit_op(s, OP_for_of_next);
- emit_u8(s, 0);
- emit_op(s, OP_drop);
- }
- if (js_parse_destructuring_element(s, tok, is_arg, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE, export_flag) < 0)
- return -1;
- } else {
- var_name = JS_ATOM_NULL;
- if (tok) {
- var_name = js_parse_destructuring_var(s, tok, is_arg);
- if (var_name == JS_ATOM_NULL)
- goto var_error;
- if (js_define_var(s, var_name, tok))
- goto var_error;
- if (need_var_reference(s, tok)) {
- /* Must make a reference for proper `with` semantics */
- emit_op(s, OP_scope_get_var);
- emit_atom(s, var_name);
- emit_u16(s, s->cur_func->scope_level);
- JS_FreeAtom(s->ctx, var_name);
- goto lvalue2;
- } else {
- /* no need to make a reference for let/const */
- opcode = OP_scope_get_var;
- scope = s->cur_func->scope_level;
- label_lvalue = -1;
- enum_depth = 0;
- }
- } else {
- if (js_parse_left_hand_side_expr(s))
- return -1;
- lvalue2:
- if (get_lvalue(s, &opcode, &scope, &var_name,
- &label_lvalue, &enum_depth, FALSE, '[')) {
- return -1;
- }
- }
- if (has_spread) {
- js_emit_spread_code(s, enum_depth);
- } else {
- emit_op(s, OP_for_of_next);
- emit_u8(s, enum_depth);
- emit_op(s, OP_drop);
- }
- if (s->token.val == '=' && !has_spread) {
- /* handle optional default value */
- int label_hasval;
- emit_op(s, OP_dup);
- emit_op(s, OP_undefined);
- emit_op(s, OP_strict_eq);
- label_hasval = emit_goto(s, OP_if_false, -1);
- if (next_token(s))
- goto var_error;
- emit_op(s, OP_drop);
- if (js_parse_assign_expr(s))
- goto var_error;
- if (opcode == OP_scope_get_var || opcode == OP_get_ref_value)
- set_object_name(s, var_name);
- emit_label(s, label_hasval);
- }
- /* store value into lvalue object */
- put_lvalue(s, opcode, scope, var_name,
- label_lvalue, PUT_LVALUE_NOKEEP_DEPTH,
- (tok == TOK_CONST || tok == TOK_LET));
- }
- if (s->token.val == ']')
- break;
- if (has_spread)
- return js_parse_error(s, "rest element must be the last one");
- /* accept a trailing comma before the ']' */
- if (js_parse_expect(s, ','))
- return -1;
- }
- /* close iterator object:
- if completed, enum_obj has been replaced by undefined */
- emit_op(s, OP_iterator_close);
- pop_break_entry(s->cur_func);
- if (next_token(s))
- return -1;
- } else {
- return js_parse_error(s, "invalid assignment syntax");
- }
- if (s->token.val == '=' && allow_initializer) {
- label_done = emit_goto(s, OP_goto, -1);
- if (next_token(s))
- return -1;
- emit_label(s, label_parse);
- if (hasval)
- emit_op(s, OP_drop);
- if (js_parse_assign_expr(s))
- return -1;
- emit_goto(s, OP_goto, label_assign);
- emit_label(s, label_done);
- has_initializer = TRUE;
- } else {
- /* normally hasval is true except if
- js_parse_skip_parens_token() was wrong in the parsing */
- // assert(hasval);
- if (!hasval) {
- js_parse_error(s, "too complicated destructuring expression");
- return -1;
- }
- /* remove test and decrement label ref count */
- memset(s->cur_func->byte_code.buf + start_addr, OP_nop,
- assign_addr - start_addr);
- s->cur_func->label_slots[label_parse].ref_count--;
- has_initializer = FALSE;
- }
- return has_initializer;
- prop_error:
- JS_FreeAtom(s->ctx, prop_name);
- var_error:
- JS_FreeAtom(s->ctx, var_name);
- return -1;
- }
- typedef enum FuncCallType {
- FUNC_CALL_NORMAL,
- FUNC_CALL_NEW,
- FUNC_CALL_SUPER_CTOR,
- FUNC_CALL_TEMPLATE,
- } FuncCallType;
- static void optional_chain_test(JSParseState *s, int *poptional_chaining_label,
- int drop_count)
- {
- int label_next, i;
- if (*poptional_chaining_label < 0)
- *poptional_chaining_label = new_label(s);
- /* XXX: could be more efficient with a specific opcode */
- emit_op(s, OP_dup);
- emit_op(s, OP_is_undefined_or_null);
- label_next = emit_goto(s, OP_if_false, -1);
- for(i = 0; i < drop_count; i++)
- emit_op(s, OP_drop);
- emit_op(s, OP_undefined);
- emit_goto(s, OP_goto, *poptional_chaining_label);
- emit_label(s, label_next);
- }
- /* allowed parse_flags: PF_POSTFIX_CALL */
- static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags)
- {
- FuncCallType call_type;
- int optional_chaining_label;
- BOOL accept_lparen = (parse_flags & PF_POSTFIX_CALL) != 0;
- const uint8_t *op_token_ptr;
-
- call_type = FUNC_CALL_NORMAL;
- switch(s->token.val) {
- case TOK_NUMBER:
- {
- JSValue val;
- val = s->token.u.num.val;
- if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) {
- emit_op(s, OP_push_i32);
- emit_u32(s, JS_VALUE_GET_INT(val));
- } else if (JS_VALUE_GET_TAG(val) == JS_TAG_SHORT_BIG_INT) {
- int64_t v;
- v = JS_VALUE_GET_SHORT_BIG_INT(val);
- if (v >= INT32_MIN && v <= INT32_MAX) {
- emit_op(s, OP_push_bigint_i32);
- emit_u32(s, v);
- } else {
- goto large_number;
- }
- } else {
- large_number:
- if (emit_push_const(s, val, 0) < 0)
- return -1;
- }
- }
- if (next_token(s))
- return -1;
- break;
- case TOK_TEMPLATE:
- if (js_parse_template(s, 0, NULL))
- return -1;
- break;
- case TOK_STRING:
- if (emit_push_const(s, s->token.u.str.str, 1))
- return -1;
- if (next_token(s))
- return -1;
- break;
- case TOK_DIV_ASSIGN:
- s->buf_ptr -= 2;
- goto parse_regexp;
- case '/':
- s->buf_ptr--;
- parse_regexp:
- {
- JSValue str;
- int ret;
- if (!s->ctx->compile_regexp)
- return js_parse_error(s, "RegExp are not supported");
- /* the previous token is '/' or '/=', so no need to free */
- if (js_parse_regexp(s))
- return -1;
- ret = emit_push_const(s, s->token.u.regexp.body, 0);
- str = s->ctx->compile_regexp(s->ctx, s->token.u.regexp.body,
- s->token.u.regexp.flags);
- if (JS_IsException(str)) {
- /* add the line number info */
- int line_num, col_num;
- line_num = get_line_col(&col_num, s->buf_start, s->token.ptr - s->buf_start);
- build_backtrace(s->ctx, s->ctx->rt->current_exception,
- s->filename, line_num + 1, col_num + 1, 0);
- return -1;
- }
- ret = emit_push_const(s, str, 0);
- JS_FreeValue(s->ctx, str);
- if (ret)
- return -1;
- /* we use a specific opcode to be sure the correct
- function is called (otherwise the bytecode would have
- to be verified by the RegExp constructor) */
- emit_op(s, OP_regexp);
- if (next_token(s))
- return -1;
- }
- break;
- case '(':
- if (js_parse_expr_paren(s))
- return -1;
- break;
- case TOK_FUNCTION:
- if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR,
- JS_FUNC_NORMAL, JS_ATOM_NULL,
- s->token.ptr))
- return -1;
- break;
- case TOK_CLASS:
- if (js_parse_class(s, TRUE, JS_PARSE_EXPORT_NONE))
- return -1;
- break;
- case TOK_NULL:
- if (next_token(s))
- return -1;
- emit_op(s, OP_null);
- break;
- case TOK_THIS:
- if (next_token(s))
- return -1;
- emit_op(s, OP_scope_get_var);
- emit_atom(s, JS_ATOM_this);
- emit_u16(s, 0);
- break;
- case TOK_FALSE:
- if (next_token(s))
- return -1;
- emit_op(s, OP_push_false);
- break;
- case TOK_TRUE:
- if (next_token(s))
- return -1;
- emit_op(s, OP_push_true);
- break;
- case TOK_IDENT:
- {
- JSAtom name;
- const uint8_t *source_ptr;
- if (s->token.u.ident.is_reserved) {
- return js_parse_error_reserved_identifier(s);
- }
- source_ptr = s->token.ptr;
- if (token_is_pseudo_keyword(s, JS_ATOM_async) &&
- peek_token(s, TRUE) != '\n') {
- if (next_token(s))
- return -1;
- if (s->token.val == TOK_FUNCTION) {
- if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR,
- JS_FUNC_ASYNC, JS_ATOM_NULL,
- source_ptr))
- return -1;
- } else {
- name = JS_DupAtom(s->ctx, JS_ATOM_async);
- goto do_get_var;
- }
- } else {
- if (s->token.u.ident.atom == JS_ATOM_arguments &&
- !s->cur_func->arguments_allowed) {
- js_parse_error(s, "'arguments' identifier is not allowed in class field initializer");
- return -1;
- }
- name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
- if (next_token(s)) {
- JS_FreeAtom(s->ctx, name);
- return -1;
- }
- do_get_var:
- emit_source_pos(s, source_ptr);
- emit_op(s, OP_scope_get_var);
- emit_u32(s, name);
- emit_u16(s, s->cur_func->scope_level);
- }
- }
- break;
- case '{':
- case '[':
- if (s->token.val == '{') {
- if (js_parse_object_literal(s))
- return -1;
- } else {
- if (js_parse_array_literal(s))
- return -1;
- }
- break;
- case TOK_NEW:
- if (next_token(s))
- return -1;
- if (s->token.val == '.') {
- if (next_token(s))
- return -1;
- if (!token_is_pseudo_keyword(s, JS_ATOM_target))
- return js_parse_error(s, "expecting target");
- if (!s->cur_func->new_target_allowed)
- return js_parse_error(s, "new.target only allowed within functions");
- if (next_token(s))
- return -1;
- emit_op(s, OP_scope_get_var);
- emit_atom(s, JS_ATOM_new_target);
- emit_u16(s, 0);
- } else {
- if (js_parse_postfix_expr(s, 0))
- return -1;
- accept_lparen = TRUE;
- if (s->token.val != '(') {
- /* new operator on an object */
- emit_source_pos(s, s->token.ptr);
- emit_op(s, OP_dup);
- emit_op(s, OP_call_constructor);
- emit_u16(s, 0);
- } else {
- call_type = FUNC_CALL_NEW;
- }
- }
- break;
- case TOK_SUPER:
- if (next_token(s))
- return -1;
- if (s->token.val == '(') {
- if (!s->cur_func->super_call_allowed)
- return js_parse_error(s, "super() is only valid in a derived class constructor");
- call_type = FUNC_CALL_SUPER_CTOR;
- } else if (s->token.val == '.' || s->token.val == '[') {
- if (!s->cur_func->super_allowed)
- return js_parse_error(s, "'super' is only valid in a method");
- emit_op(s, OP_scope_get_var);
- emit_atom(s, JS_ATOM_this);
- emit_u16(s, 0);
- emit_op(s, OP_scope_get_var);
- emit_atom(s, JS_ATOM_home_object);
- emit_u16(s, 0);
- emit_op(s, OP_get_super);
- } else {
- return js_parse_error(s, "invalid use of 'super'");
- }
- break;
- case TOK_IMPORT:
- if (next_token(s))
- return -1;
- if (s->token.val == '.') {
- if (next_token(s))
- return -1;
- if (!token_is_pseudo_keyword(s, JS_ATOM_meta))
- return js_parse_error(s, "meta expected");
- if (!s->is_module)
- return js_parse_error(s, "import.meta only valid in module code");
- if (next_token(s))
- return -1;
- emit_op(s, OP_special_object);
- emit_u8(s, OP_SPECIAL_OBJECT_IMPORT_META);
- } else {
- if (js_parse_expect(s, '('))
- return -1;
- if (!accept_lparen)
- return js_parse_error(s, "invalid use of 'import()'");
- if (js_parse_assign_expr(s))
- return -1;
- if (s->token.val == ',') {
- if (next_token(s))
- return -1;
- if (s->token.val != ')') {
- if (js_parse_assign_expr(s))
- return -1;
- /* accept a trailing comma */
- if (s->token.val == ',') {
- if (next_token(s))
- return -1;
- }
- } else {
- emit_op(s, OP_undefined);
- }
- } else {
- emit_op(s, OP_undefined);
- }
- if (js_parse_expect(s, ')'))
- return -1;
- emit_op(s, OP_import);
- }
- break;
- default:
- return js_parse_error(s, "unexpected token in expression: '%.*s'",
- (int)(s->buf_ptr - s->token.ptr), s->token.ptr);
- }
- optional_chaining_label = -1;
- for(;;) {
- JSFunctionDef *fd = s->cur_func;
- BOOL has_optional_chain = FALSE;
- if (s->token.val == TOK_QUESTION_MARK_DOT) {
- if ((parse_flags & PF_POSTFIX_CALL) == 0)
- return js_parse_error(s, "new keyword cannot be used with an optional chain");
- op_token_ptr = s->token.ptr;
- /* optional chaining */
- if (next_token(s))
- return -1;
- has_optional_chain = TRUE;
- if (s->token.val == '(' && accept_lparen) {
- goto parse_func_call;
- } else if (s->token.val == '[') {
- goto parse_array_access;
- } else {
- goto parse_property;
- }
- } else if (s->token.val == TOK_TEMPLATE &&
- call_type == FUNC_CALL_NORMAL) {
- if (optional_chaining_label >= 0) {
- return js_parse_error(s, "template literal cannot appear in an optional chain");
- }
- call_type = FUNC_CALL_TEMPLATE;
- op_token_ptr = s->token.ptr; /* XXX: check if right position */
- goto parse_func_call2;
- } else if (s->token.val == '(' && accept_lparen) {
- int opcode, arg_count, drop_count;
- /* function call */
- parse_func_call:
- op_token_ptr = s->token.ptr;
- if (next_token(s))
- return -1;
- if (call_type == FUNC_CALL_NORMAL) {
- parse_func_call2:
- switch(opcode = get_prev_opcode(fd)) {
- case OP_get_field:
- /* keep the object on the stack */
- fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2;
- drop_count = 2;
- break;
- case OP_get_field_opt_chain:
- {
- int opt_chain_label, next_label;
- opt_chain_label = get_u32(fd->byte_code.buf +
- fd->last_opcode_pos + 1 + 4 + 1);
- /* keep the object on the stack */
- fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2;
- fd->byte_code.size = fd->last_opcode_pos + 1 + 4;
- next_label = emit_goto(s, OP_goto, -1);
- emit_label(s, opt_chain_label);
- /* need an additional undefined value for the
- case where the optional field does not
- exists */
- emit_op(s, OP_undefined);
- emit_label(s, next_label);
- drop_count = 2;
- opcode = OP_get_field;
- }
- break;
- case OP_scope_get_private_field:
- /* keep the object on the stack */
- fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_private_field2;
- drop_count = 2;
- break;
- case OP_get_array_el:
- /* keep the object on the stack */
- fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2;
- drop_count = 2;
- break;
- case OP_get_array_el_opt_chain:
- {
- int opt_chain_label, next_label;
- opt_chain_label = get_u32(fd->byte_code.buf +
- fd->last_opcode_pos + 1 + 1);
- /* keep the object on the stack */
- fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2;
- fd->byte_code.size = fd->last_opcode_pos + 1;
- next_label = emit_goto(s, OP_goto, -1);
- emit_label(s, opt_chain_label);
- /* need an additional undefined value for the
- case where the optional field does not
- exists */
- emit_op(s, OP_undefined);
- emit_label(s, next_label);
- drop_count = 2;
- opcode = OP_get_array_el;
- }
- break;
- case OP_scope_get_var:
- {
- JSAtom name;
- int scope;
- name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
- scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5);
- if (name == JS_ATOM_eval && call_type == FUNC_CALL_NORMAL && !has_optional_chain) {
- /* direct 'eval' */
- opcode = OP_eval;
- } else {
- /* verify if function name resolves to a simple
- get_loc/get_arg: a function call inside a `with`
- statement can resolve to a method call of the
- `with` context object
- */
- /* XXX: always generate the OP_scope_get_ref
- and remove it in variable resolution
- pass ? */
- if (has_with_scope(fd, scope)) {
- opcode = OP_scope_get_ref;
- fd->byte_code.buf[fd->last_opcode_pos] = opcode;
- }
- }
- drop_count = 1;
- }
- break;
- case OP_get_super_value:
- fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el;
- /* on stack: this func_obj */
- opcode = OP_get_array_el;
- drop_count = 2;
- break;
- default:
- opcode = OP_invalid;
- drop_count = 1;
- break;
- }
- if (has_optional_chain) {
- optional_chain_test(s, &optional_chaining_label,
- drop_count);
- }
- } else {
- opcode = OP_invalid;
- }
- if (call_type == FUNC_CALL_TEMPLATE) {
- if (js_parse_template(s, 1, &arg_count))
- return -1;
- goto emit_func_call;
- } else if (call_type == FUNC_CALL_SUPER_CTOR) {
- emit_op(s, OP_scope_get_var);
- emit_atom(s, JS_ATOM_this_active_func);
- emit_u16(s, 0);
- emit_op(s, OP_get_super);
- emit_op(s, OP_scope_get_var);
- emit_atom(s, JS_ATOM_new_target);
- emit_u16(s, 0);
- } else if (call_type == FUNC_CALL_NEW) {
- emit_op(s, OP_dup); /* new.target = function */
- }
- /* parse arguments */
- arg_count = 0;
- while (s->token.val != ')') {
- if (arg_count >= 65535) {
- return js_parse_error(s, "Too many call arguments");
- }
- if (s->token.val == TOK_ELLIPSIS)
- break;
- if (js_parse_assign_expr(s))
- return -1;
- arg_count++;
- if (s->token.val == ')')
- break;
- /* accept a trailing comma before the ')' */
- if (js_parse_expect(s, ','))
- return -1;
- }
- if (s->token.val == TOK_ELLIPSIS) {
- emit_op(s, OP_array_from);
- emit_u16(s, arg_count);
- emit_op(s, OP_push_i32);
- emit_u32(s, arg_count);
- /* on stack: array idx */
- while (s->token.val != ')') {
- if (s->token.val == TOK_ELLIPSIS) {
- if (next_token(s))
- return -1;
- if (js_parse_assign_expr(s))
- return -1;
- #if 1
- /* XXX: could pass is_last indicator? */
- emit_op(s, OP_append);
- #else
- int label_next, label_done;
- label_next = new_label(s);
- label_done = new_label(s);
- /* push enumerate object below array/idx pair */
- emit_op(s, OP_for_of_start);
- emit_op(s, OP_rot5l);
- emit_op(s, OP_rot5l);
- emit_label(s, label_next);
- /* on stack: enum_rec array idx */
- emit_op(s, OP_for_of_next);
- emit_u8(s, 2);
- emit_goto(s, OP_if_true, label_done);
- /* append element */
- /* enum_rec array idx val -> enum_rec array new_idx */
- emit_op(s, OP_define_array_el);
- emit_op(s, OP_inc);
- emit_goto(s, OP_goto, label_next);
- emit_label(s, label_done);
- /* close enumeration, drop enum_rec and idx */
- emit_op(s, OP_drop); /* drop undef */
- emit_op(s, OP_nip1); /* drop enum_rec */
- emit_op(s, OP_nip1);
- emit_op(s, OP_nip1);
- #endif
- } else {
- if (js_parse_assign_expr(s))
- return -1;
- /* array idx val */
- emit_op(s, OP_define_array_el);
- emit_op(s, OP_inc);
- }
- if (s->token.val == ')')
- break;
- /* accept a trailing comma before the ')' */
- if (js_parse_expect(s, ','))
- return -1;
- }
- if (next_token(s))
- return -1;
- /* drop the index */
- emit_op(s, OP_drop);
- emit_source_pos(s, op_token_ptr);
- /* apply function call */
- switch(opcode) {
- case OP_get_field:
- case OP_scope_get_private_field:
- case OP_get_array_el:
- case OP_scope_get_ref:
- /* obj func array -> func obj array */
- emit_op(s, OP_perm3);
- emit_op(s, OP_apply);
- emit_u16(s, call_type == FUNC_CALL_NEW);
- break;
- case OP_eval:
- emit_op(s, OP_apply_eval);
- emit_u16(s, fd->scope_level);
- fd->has_eval_call = TRUE;
- break;
- default:
- if (call_type == FUNC_CALL_SUPER_CTOR) {
- emit_op(s, OP_apply);
- emit_u16(s, 1);
- /* set the 'this' value */
- emit_op(s, OP_dup);
- emit_op(s, OP_scope_put_var_init);
- emit_atom(s, JS_ATOM_this);
- emit_u16(s, 0);
- emit_class_field_init(s);
- } else if (call_type == FUNC_CALL_NEW) {
- /* obj func array -> func obj array */
- emit_op(s, OP_perm3);
- emit_op(s, OP_apply);
- emit_u16(s, 1);
- } else {
- /* func array -> func undef array */
- emit_op(s, OP_undefined);
- emit_op(s, OP_swap);
- emit_op(s, OP_apply);
- emit_u16(s, 0);
- }
- break;
- }
- } else {
- if (next_token(s))
- return -1;
- emit_func_call:
- emit_source_pos(s, op_token_ptr);
- switch(opcode) {
- case OP_get_field:
- case OP_scope_get_private_field:
- case OP_get_array_el:
- case OP_scope_get_ref:
- emit_op(s, OP_call_method);
- emit_u16(s, arg_count);
- break;
- case OP_eval:
- emit_op(s, OP_eval);
- emit_u16(s, arg_count);
- emit_u16(s, fd->scope_level);
- fd->has_eval_call = TRUE;
- break;
- default:
- if (call_type == FUNC_CALL_SUPER_CTOR) {
- emit_op(s, OP_call_constructor);
- emit_u16(s, arg_count);
- /* set the 'this' value */
- emit_op(s, OP_dup);
- emit_op(s, OP_scope_put_var_init);
- emit_atom(s, JS_ATOM_this);
- emit_u16(s, 0);
- emit_class_field_init(s);
- } else if (call_type == FUNC_CALL_NEW) {
- emit_op(s, OP_call_constructor);
- emit_u16(s, arg_count);
- } else {
- emit_op(s, OP_call);
- emit_u16(s, arg_count);
- }
- break;
- }
- }
- call_type = FUNC_CALL_NORMAL;
- } else if (s->token.val == '.') {
- op_token_ptr = s->token.ptr;
- if (next_token(s))
- return -1;
- parse_property:
- emit_source_pos(s, op_token_ptr);
- if (s->token.val == TOK_PRIVATE_NAME) {
- /* private class field */
- if (get_prev_opcode(fd) == OP_get_super) {
- return js_parse_error(s, "private class field forbidden after super");
- }
- if (has_optional_chain) {
- optional_chain_test(s, &optional_chaining_label, 1);
- }
- emit_op(s, OP_scope_get_private_field);
- emit_atom(s, s->token.u.ident.atom);
- emit_u16(s, s->cur_func->scope_level);
- } else {
- if (!token_is_ident(s->token.val)) {
- return js_parse_error(s, "expecting field name");
- }
- if (get_prev_opcode(fd) == OP_get_super) {
- JSValue val;
- int ret;
- val = JS_AtomToValue(s->ctx, s->token.u.ident.atom);
- ret = emit_push_const(s, val, 1);
- JS_FreeValue(s->ctx, val);
- if (ret)
- return -1;
- emit_op(s, OP_get_super_value);
- } else {
- if (has_optional_chain) {
- optional_chain_test(s, &optional_chaining_label, 1);
- }
- emit_op(s, OP_get_field);
- emit_atom(s, s->token.u.ident.atom);
- }
- }
- if (next_token(s))
- return -1;
- } else if (s->token.val == '[') {
- int prev_op;
- op_token_ptr = s->token.ptr;
- parse_array_access:
- prev_op = get_prev_opcode(fd);
- if (has_optional_chain) {
- optional_chain_test(s, &optional_chaining_label, 1);
- }
- if (next_token(s))
- return -1;
- if (js_parse_expr(s))
- return -1;
- if (js_parse_expect(s, ']'))
- return -1;
- emit_source_pos(s, op_token_ptr);
- if (prev_op == OP_get_super) {
- emit_op(s, OP_get_super_value);
- } else {
- emit_op(s, OP_get_array_el);
- }
- } else {
- break;
- }
- }
- if (optional_chaining_label >= 0) {
- JSFunctionDef *fd = s->cur_func;
- int opcode;
- emit_label_raw(s, optional_chaining_label);
- /* modify the last opcode so that it is an indicator of an
- optional chain */
- opcode = get_prev_opcode(fd);
- if (opcode == OP_get_field || opcode == OP_get_array_el) {
- if (opcode == OP_get_field)
- opcode = OP_get_field_opt_chain;
- else
- opcode = OP_get_array_el_opt_chain;
- fd->byte_code.buf[fd->last_opcode_pos] = opcode;
- } else {
- fd->last_opcode_pos = -1;
- }
- }
- return 0;
- }
- static __exception int js_parse_delete(JSParseState *s)
- {
- JSFunctionDef *fd = s->cur_func;
- JSAtom name;
- int opcode;
- if (next_token(s))
- return -1;
- if (js_parse_unary(s, PF_POW_FORBIDDEN))
- return -1;
- switch(opcode = get_prev_opcode(fd)) {
- case OP_get_field:
- case OP_get_field_opt_chain:
- {
- JSValue val;
- int ret, opt_chain_label, next_label;
- if (opcode == OP_get_field_opt_chain) {
- opt_chain_label = get_u32(fd->byte_code.buf +
- fd->last_opcode_pos + 1 + 4 + 1);
- } else {
- opt_chain_label = -1;
- }
- name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
- fd->byte_code.size = fd->last_opcode_pos;
- val = JS_AtomToValue(s->ctx, name);
- ret = emit_push_const(s, val, 1);
- JS_FreeValue(s->ctx, val);
- JS_FreeAtom(s->ctx, name);
- if (ret)
- return ret;
- emit_op(s, OP_delete);
- if (opt_chain_label >= 0) {
- next_label = emit_goto(s, OP_goto, -1);
- emit_label(s, opt_chain_label);
- /* if the optional chain is not taken, return 'true' */
- emit_op(s, OP_drop);
- emit_op(s, OP_push_true);
- emit_label(s, next_label);
- }
- fd->last_opcode_pos = -1;
- }
- break;
- case OP_get_array_el:
- fd->byte_code.size = fd->last_opcode_pos;
- fd->last_opcode_pos = -1;
- emit_op(s, OP_delete);
- break;
- case OP_get_array_el_opt_chain:
- {
- int opt_chain_label, next_label;
- opt_chain_label = get_u32(fd->byte_code.buf +
- fd->last_opcode_pos + 1 + 1);
- fd->byte_code.size = fd->last_opcode_pos;
- emit_op(s, OP_delete);
- next_label = emit_goto(s, OP_goto, -1);
- emit_label(s, opt_chain_label);
- /* if the optional chain is not taken, return 'true' */
- emit_op(s, OP_drop);
- emit_op(s, OP_push_true);
- emit_label(s, next_label);
- fd->last_opcode_pos = -1;
- }
- break;
- case OP_scope_get_var:
- /* 'delete this': this is not a reference */
- name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
- if (name == JS_ATOM_this || name == JS_ATOM_new_target)
- goto ret_true;
- if (fd->js_mode & JS_MODE_STRICT) {
- return js_parse_error(s, "cannot delete a direct reference in strict mode");
- } else {
- fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_delete_var;
- }
- break;
- case OP_scope_get_private_field:
- return js_parse_error(s, "cannot delete a private class field");
- case OP_get_super_value:
- fd->byte_code.size = fd->last_opcode_pos;
- fd->last_opcode_pos = -1;
- emit_op(s, OP_throw_error);
- emit_atom(s, JS_ATOM_NULL);
- emit_u8(s, JS_THROW_ERROR_DELETE_SUPER);
- break;
- default:
- ret_true:
- emit_op(s, OP_drop);
- emit_op(s, OP_push_true);
- break;
- }
- return 0;
- }
- /* allowed parse_flags: PF_POW_ALLOWED, PF_POW_FORBIDDEN */
- static __exception int js_parse_unary(JSParseState *s, int parse_flags)
- {
- int op;
- const uint8_t *op_token_ptr;
- switch(s->token.val) {
- case '+':
- case '-':
- case '!':
- case '~':
- case TOK_VOID:
- op_token_ptr = s->token.ptr;
- op = s->token.val;
- if (next_token(s))
- return -1;
- if (js_parse_unary(s, PF_POW_FORBIDDEN))
- return -1;
- switch(op) {
- case '-':
- emit_source_pos(s, op_token_ptr);
- emit_op(s, OP_neg);
- break;
- case '+':
- emit_source_pos(s, op_token_ptr);
- emit_op(s, OP_plus);
- break;
- case '!':
- emit_op(s, OP_lnot);
- break;
- case '~':
- emit_source_pos(s, op_token_ptr);
- emit_op(s, OP_not);
- break;
- case TOK_VOID:
- emit_op(s, OP_drop);
- emit_op(s, OP_undefined);
- break;
- default:
- abort();
- }
- parse_flags = 0;
- break;
- case TOK_DEC:
- case TOK_INC:
- {
- int opcode, op, scope, label;
- JSAtom name;
- op = s->token.val;
- op_token_ptr = s->token.ptr;
- if (next_token(s))
- return -1;
- if (js_parse_unary(s, 0))
- return -1;
- if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op))
- return -1;
- emit_source_pos(s, op_token_ptr);
- emit_op(s, OP_dec + op - TOK_DEC);
- put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP,
- FALSE);
- }
- break;
- case TOK_TYPEOF:
- {
- JSFunctionDef *fd;
- if (next_token(s))
- return -1;
- if (js_parse_unary(s, PF_POW_FORBIDDEN))
- return -1;
- /* reference access should not return an exception, so we
- patch the get_var */
- fd = s->cur_func;
- if (get_prev_opcode(fd) == OP_scope_get_var) {
- fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_var_undef;
- }
- emit_op(s, OP_typeof);
- parse_flags = 0;
- }
- break;
- case TOK_DELETE:
- if (js_parse_delete(s))
- return -1;
- parse_flags = 0;
- break;
- case TOK_AWAIT:
- if (!(s->cur_func->func_kind & JS_FUNC_ASYNC))
- return js_parse_error(s, "unexpected 'await' keyword");
- if (!s->cur_func->in_function_body)
- return js_parse_error(s, "await in default expression");
- if (next_token(s))
- return -1;
- if (js_parse_unary(s, PF_POW_FORBIDDEN))
- return -1;
- s->cur_func->has_await = TRUE;
- emit_op(s, OP_await);
- parse_flags = 0;
- break;
- default:
- if (js_parse_postfix_expr(s, PF_POSTFIX_CALL))
- return -1;
- if (!s->got_lf &&
- (s->token.val == TOK_DEC || s->token.val == TOK_INC)) {
- int opcode, op, scope, label;
- JSAtom name;
- op = s->token.val;
- op_token_ptr = s->token.ptr;
- if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op))
- return -1;
- emit_source_pos(s, op_token_ptr);
- emit_op(s, OP_post_dec + op - TOK_DEC);
- put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_SECOND,
- FALSE);
- if (next_token(s))
- return -1;
- }
- break;
- }
- if (parse_flags & (PF_POW_ALLOWED | PF_POW_FORBIDDEN)) {
- if (s->token.val == TOK_POW) {
- /* Strict ES7 exponentiation syntax rules: To solve
- conficting semantics between different implementations
- regarding the precedence of prefix operators and the
- postifx exponential, ES7 specifies that -2**2 is a
- syntax error. */
- if (parse_flags & PF_POW_FORBIDDEN) {
- JS_ThrowSyntaxError(s->ctx, "unparenthesized unary expression can't appear on the left-hand side of '**'");
- return -1;
- }
- op_token_ptr = s->token.ptr;
- if (next_token(s))
- return -1;
- if (js_parse_unary(s, PF_POW_ALLOWED))
- return -1;
- emit_source_pos(s, op_token_ptr);
- emit_op(s, OP_pow);
- }
- }
- return 0;
- }
- /* allowed parse_flags: PF_IN_ACCEPTED */
- static __exception int js_parse_expr_binary(JSParseState *s, int level,
- int parse_flags)
- {
- int op, opcode;
- const uint8_t *op_token_ptr;
-
- if (level == 0) {
- return js_parse_unary(s, PF_POW_ALLOWED);
- } else if (s->token.val == TOK_PRIVATE_NAME &&
- (parse_flags & PF_IN_ACCEPTED) && level == 4 &&
- peek_token(s, FALSE) == TOK_IN) {
- JSAtom atom;
- atom = JS_DupAtom(s->ctx, s->token.u.ident.atom);
- if (next_token(s))
- goto fail_private_in;
- if (s->token.val != TOK_IN)
- goto fail_private_in;
- if (next_token(s))
- goto fail_private_in;
- if (js_parse_expr_binary(s, level - 1, parse_flags)) {
- fail_private_in:
- JS_FreeAtom(s->ctx, atom);
- return -1;
- }
- emit_op(s, OP_scope_in_private_field);
- emit_atom(s, atom);
- emit_u16(s, s->cur_func->scope_level);
- JS_FreeAtom(s->ctx, atom);
- return 0;
- } else {
- if (js_parse_expr_binary(s, level - 1, parse_flags))
- return -1;
- }
- for(;;) {
- op = s->token.val;
- op_token_ptr = s->token.ptr;
- switch(level) {
- case 1:
- switch(op) {
- case '*':
- opcode = OP_mul;
- break;
- case '/':
- opcode = OP_div;
- break;
- case '%':
- opcode = OP_mod;
- break;
- default:
- return 0;
- }
- break;
- case 2:
- switch(op) {
- case '+':
- opcode = OP_add;
- break;
- case '-':
- opcode = OP_sub;
- break;
- default:
- return 0;
- }
- break;
- case 3:
- switch(op) {
- case TOK_SHL:
- opcode = OP_shl;
- break;
- case TOK_SAR:
- opcode = OP_sar;
- break;
- case TOK_SHR:
- opcode = OP_shr;
- break;
- default:
- return 0;
- }
- break;
- case 4:
- switch(op) {
- case '<':
- opcode = OP_lt;
- break;
- case '>':
- opcode = OP_gt;
- break;
- case TOK_LTE:
- opcode = OP_lte;
- break;
- case TOK_GTE:
- opcode = OP_gte;
- break;
- case TOK_INSTANCEOF:
- opcode = OP_instanceof;
- break;
- case TOK_IN:
- if (parse_flags & PF_IN_ACCEPTED) {
- opcode = OP_in;
- } else {
- return 0;
- }
- break;
- default:
- return 0;
- }
- break;
- case 5:
- switch(op) {
- case TOK_EQ:
- opcode = OP_eq;
- break;
- case TOK_NEQ:
- opcode = OP_neq;
- break;
- case TOK_STRICT_EQ:
- opcode = OP_strict_eq;
- break;
- case TOK_STRICT_NEQ:
- opcode = OP_strict_neq;
- break;
- default:
- return 0;
- }
- break;
- case 6:
- switch(op) {
- case '&':
- opcode = OP_and;
- break;
- default:
- return 0;
- }
- break;
- case 7:
- switch(op) {
- case '^':
- opcode = OP_xor;
- break;
- default:
- return 0;
- }
- break;
- case 8:
- switch(op) {
- case '|':
- opcode = OP_or;
- break;
- default:
- return 0;
- }
- break;
- default:
- abort();
- }
- if (next_token(s))
- return -1;
- if (js_parse_expr_binary(s, level - 1, parse_flags))
- return -1;
- emit_source_pos(s, op_token_ptr);
- emit_op(s, opcode);
- }
- return 0;
- }
- /* allowed parse_flags: PF_IN_ACCEPTED */
- static __exception int js_parse_logical_and_or(JSParseState *s, int op,
- int parse_flags)
- {
- int label1;
- if (op == TOK_LAND) {
- if (js_parse_expr_binary(s, 8, parse_flags))
- return -1;
- } else {
- if (js_parse_logical_and_or(s, TOK_LAND, parse_flags))
- return -1;
- }
- if (s->token.val == op) {
- label1 = new_label(s);
- for(;;) {
- if (next_token(s))
- return -1;
- emit_op(s, OP_dup);
- emit_goto(s, op == TOK_LAND ? OP_if_false : OP_if_true, label1);
- emit_op(s, OP_drop);
- if (op == TOK_LAND) {
- if (js_parse_expr_binary(s, 8, parse_flags))
- return -1;
- } else {
- if (js_parse_logical_and_or(s, TOK_LAND,
- parse_flags))
- return -1;
- }
- if (s->token.val != op) {
- if (s->token.val == TOK_DOUBLE_QUESTION_MARK)
- return js_parse_error(s, "cannot mix ?? with && or ||");
- break;
- }
- }
- emit_label(s, label1);
- }
- return 0;
- }
- static __exception int js_parse_coalesce_expr(JSParseState *s, int parse_flags)
- {
- int label1;
- if (js_parse_logical_and_or(s, TOK_LOR, parse_flags))
- return -1;
- if (s->token.val == TOK_DOUBLE_QUESTION_MARK) {
- label1 = new_label(s);
- for(;;) {
- if (next_token(s))
- return -1;
- emit_op(s, OP_dup);
- emit_op(s, OP_is_undefined_or_null);
- emit_goto(s, OP_if_false, label1);
- emit_op(s, OP_drop);
- if (js_parse_expr_binary(s, 8, parse_flags))
- return -1;
- if (s->token.val != TOK_DOUBLE_QUESTION_MARK)
- break;
- }
- emit_label(s, label1);
- }
- return 0;
- }
- /* allowed parse_flags: PF_IN_ACCEPTED */
- static __exception int js_parse_cond_expr(JSParseState *s, int parse_flags)
- {
- int label1, label2;
- if (js_parse_coalesce_expr(s, parse_flags))
- return -1;
- if (s->token.val == '?') {
- if (next_token(s))
- return -1;
- label1 = emit_goto(s, OP_if_false, -1);
- if (js_parse_assign_expr(s))
- return -1;
- if (js_parse_expect(s, ':'))
- return -1;
- label2 = emit_goto(s, OP_goto, -1);
- emit_label(s, label1);
- if (js_parse_assign_expr2(s, parse_flags & PF_IN_ACCEPTED))
- return -1;
- emit_label(s, label2);
- }
- return 0;
- }
- /* allowed parse_flags: PF_IN_ACCEPTED */
- static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags)
- {
- int opcode, op, scope, skip_bits;
- JSAtom name0 = JS_ATOM_NULL;
- JSAtom name;
- if (s->token.val == TOK_YIELD) {
- BOOL is_star = FALSE, is_async;
- if (!(s->cur_func->func_kind & JS_FUNC_GENERATOR))
- return js_parse_error(s, "unexpected 'yield' keyword");
- if (!s->cur_func->in_function_body)
- return js_parse_error(s, "yield in default expression");
- if (next_token(s))
- return -1;
- /* XXX: is there a better method to detect 'yield' without
- parameters ? */
- if (s->token.val != ';' && s->token.val != ')' &&
- s->token.val != ']' && s->token.val != '}' &&
- s->token.val != ',' && s->token.val != ':' && !s->got_lf) {
- if (s->token.val == '*') {
- is_star = TRUE;
- if (next_token(s))
- return -1;
- }
- if (js_parse_assign_expr2(s, parse_flags))
- return -1;
- } else {
- emit_op(s, OP_undefined);
- }
- is_async = (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR);
- if (is_star) {
- int label_loop, label_return, label_next;
- int label_return1, label_yield, label_throw, label_throw1;
- int label_throw2;
- label_loop = new_label(s);
- label_yield = new_label(s);
- emit_op(s, is_async ? OP_for_await_of_start : OP_for_of_start);
- /* remove the catch offset (XXX: could avoid pushing back
- undefined) */
- emit_op(s, OP_drop);
- emit_op(s, OP_undefined);
- emit_op(s, OP_undefined); /* initial value */
- emit_label(s, label_loop);
- emit_op(s, OP_iterator_next);
- if (is_async)
- emit_op(s, OP_await);
- emit_op(s, OP_iterator_check_object);
- emit_op(s, OP_get_field2);
- emit_atom(s, JS_ATOM_done);
- label_next = emit_goto(s, OP_if_true, -1); /* end of loop */
- emit_label(s, label_yield);
- if (is_async) {
- /* OP_async_yield_star takes the value as parameter */
- emit_op(s, OP_get_field);
- emit_atom(s, JS_ATOM_value);
- emit_op(s, OP_async_yield_star);
- } else {
- /* OP_yield_star takes (value, done) as parameter */
- emit_op(s, OP_yield_star);
- }
- emit_op(s, OP_dup);
- label_return = emit_goto(s, OP_if_true, -1);
- emit_op(s, OP_drop);
- emit_goto(s, OP_goto, label_loop);
- emit_label(s, label_return);
- emit_op(s, OP_push_i32);
- emit_u32(s, 2);
- emit_op(s, OP_strict_eq);
- label_throw = emit_goto(s, OP_if_true, -1);
- /* return handling */
- if (is_async)
- emit_op(s, OP_await);
- emit_op(s, OP_iterator_call);
- emit_u8(s, 0);
- label_return1 = emit_goto(s, OP_if_true, -1);
- if (is_async)
- emit_op(s, OP_await);
- emit_op(s, OP_iterator_check_object);
- emit_op(s, OP_get_field2);
- emit_atom(s, JS_ATOM_done);
- emit_goto(s, OP_if_false, label_yield);
- emit_op(s, OP_get_field);
- emit_atom(s, JS_ATOM_value);
- emit_label(s, label_return1);
- emit_op(s, OP_nip);
- emit_op(s, OP_nip);
- emit_op(s, OP_nip);
- emit_return(s, TRUE);
- /* throw handling */
- emit_label(s, label_throw);
- emit_op(s, OP_iterator_call);
- emit_u8(s, 1);
- label_throw1 = emit_goto(s, OP_if_true, -1);
- if (is_async)
- emit_op(s, OP_await);
- emit_op(s, OP_iterator_check_object);
- emit_op(s, OP_get_field2);
- emit_atom(s, JS_ATOM_done);
- emit_goto(s, OP_if_false, label_yield);
- emit_goto(s, OP_goto, label_next);
- /* close the iterator and throw a type error exception */
- emit_label(s, label_throw1);
- emit_op(s, OP_iterator_call);
- emit_u8(s, 2);
- label_throw2 = emit_goto(s, OP_if_true, -1);
- if (is_async)
- emit_op(s, OP_await);
- emit_label(s, label_throw2);
- emit_op(s, OP_throw_error);
- emit_atom(s, JS_ATOM_NULL);
- emit_u8(s, JS_THROW_ERROR_ITERATOR_THROW);
- emit_label(s, label_next);
- emit_op(s, OP_get_field);
- emit_atom(s, JS_ATOM_value);
- emit_op(s, OP_nip); /* keep the value associated with
- done = true */
- emit_op(s, OP_nip);
- emit_op(s, OP_nip);
- } else {
- int label_next;
- if (is_async)
- emit_op(s, OP_await);
- emit_op(s, OP_yield);
- label_next = emit_goto(s, OP_if_false, -1);
- emit_return(s, TRUE);
- emit_label(s, label_next);
- }
- return 0;
- } else if (s->token.val == '(' &&
- js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) {
- return js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,
- JS_FUNC_NORMAL, JS_ATOM_NULL,
- s->token.ptr);
- } else if (token_is_pseudo_keyword(s, JS_ATOM_async)) {
- const uint8_t *source_ptr;
- int tok;
- JSParsePos pos;
- /* fast test */
- tok = peek_token(s, TRUE);
- if (tok == TOK_FUNCTION || tok == '\n')
- goto next;
- source_ptr = s->token.ptr;
- js_parse_get_pos(s, &pos);
- if (next_token(s))
- return -1;
- if ((s->token.val == '(' &&
- js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) ||
- (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved &&
- peek_token(s, TRUE) == TOK_ARROW)) {
- return js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,
- JS_FUNC_ASYNC, JS_ATOM_NULL,
- source_ptr);
- } else {
- /* undo the token parsing */
- if (js_parse_seek_token(s, &pos))
- return -1;
- }
- } else if (s->token.val == TOK_IDENT &&
- peek_token(s, TRUE) == TOK_ARROW) {
- return js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,
- JS_FUNC_NORMAL, JS_ATOM_NULL,
- s->token.ptr);
- } else if ((s->token.val == '{' || s->token.val == '[') &&
- js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') {
- if (js_parse_destructuring_element(s, 0, 0, FALSE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE, FALSE) < 0)
- return -1;
- return 0;
- }
- next:
- if (s->token.val == TOK_IDENT) {
- /* name0 is used to check for OP_set_name pattern, not duplicated */
- name0 = s->token.u.ident.atom;
- }
- if (js_parse_cond_expr(s, parse_flags))
- return -1;
- op = s->token.val;
- if (op == '=' || (op >= TOK_MUL_ASSIGN && op <= TOK_POW_ASSIGN)) {
- int label;
- const uint8_t *op_token_ptr;
- op_token_ptr = s->token.ptr;
- if (next_token(s))
- return -1;
- if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, (op != '='), op) < 0)
- return -1;
- if (js_parse_assign_expr2(s, parse_flags)) {
- JS_FreeAtom(s->ctx, name);
- return -1;
- }
- if (op == '=') {
- if ((opcode == OP_get_ref_value || opcode == OP_scope_get_var) && name == name0) {
- set_object_name(s, name);
- }
- } else {
- static const uint8_t assign_opcodes[] = {
- OP_mul, OP_div, OP_mod, OP_add, OP_sub,
- OP_shl, OP_sar, OP_shr, OP_and, OP_xor, OP_or,
- OP_pow,
- };
- op = assign_opcodes[op - TOK_MUL_ASSIGN];
- emit_source_pos(s, op_token_ptr);
- emit_op(s, op);
- }
- put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP, FALSE);
- } else if (op >= TOK_LAND_ASSIGN && op <= TOK_DOUBLE_QUESTION_MARK_ASSIGN) {
- int label, label1, depth_lvalue, label2;
- if (next_token(s))
- return -1;
- if (get_lvalue(s, &opcode, &scope, &name, &label,
- &depth_lvalue, TRUE, op) < 0)
- return -1;
- emit_op(s, OP_dup);
- if (op == TOK_DOUBLE_QUESTION_MARK_ASSIGN)
- emit_op(s, OP_is_undefined_or_null);
- label1 = emit_goto(s, op == TOK_LOR_ASSIGN ? OP_if_true : OP_if_false,
- -1);
- emit_op(s, OP_drop);
- if (js_parse_assign_expr2(s, parse_flags)) {
- JS_FreeAtom(s->ctx, name);
- return -1;
- }
- if ((opcode == OP_get_ref_value || opcode == OP_scope_get_var) && name == name0) {
- set_object_name(s, name);
- }
- switch(depth_lvalue) {
- case 0:
- emit_op(s, OP_dup);
- break;
- case 1:
- emit_op(s, OP_insert2);
- break;
- case 2:
- emit_op(s, OP_insert3);
- break;
- case 3:
- emit_op(s, OP_insert4);
- break;
- default:
- abort();
- }
- /* XXX: we disable the OP_put_ref_value optimization by not
- using put_lvalue() otherwise depth_lvalue is not correct */
- put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_NOKEEP_DEPTH,
- FALSE);
- label2 = emit_goto(s, OP_goto, -1);
- emit_label(s, label1);
- /* remove the lvalue stack entries */
- while (depth_lvalue != 0) {
- emit_op(s, OP_nip);
- depth_lvalue--;
- }
- emit_label(s, label2);
- }
- return 0;
- }
- static __exception int js_parse_assign_expr(JSParseState *s)
- {
- return js_parse_assign_expr2(s, PF_IN_ACCEPTED);
- }
- /* allowed parse_flags: PF_IN_ACCEPTED */
- static __exception int js_parse_expr2(JSParseState *s, int parse_flags)
- {
- BOOL comma = FALSE;
- for(;;) {
- if (js_parse_assign_expr2(s, parse_flags))
- return -1;
- if (comma) {
- /* prevent get_lvalue from using the last expression
- as an lvalue. This also prevents the conversion of
- of get_var to get_ref for method lookup in function
- call inside `with` statement.
- */
- s->cur_func->last_opcode_pos = -1;
- }
- if (s->token.val != ',')
- break;
- comma = TRUE;
- if (next_token(s))
- return -1;
- emit_op(s, OP_drop);
- }
- return 0;
- }
- static __exception int js_parse_expr(JSParseState *s)
- {
- return js_parse_expr2(s, PF_IN_ACCEPTED);
- }
- static void push_break_entry(JSFunctionDef *fd, BlockEnv *be,
- JSAtom label_name,
- int label_break, int label_cont,
- int drop_count)
- {
- be->prev = fd->top_break;
- fd->top_break = be;
- be->label_name = label_name;
- be->label_break = label_break;
- be->label_cont = label_cont;
- be->drop_count = drop_count;
- be->label_finally = -1;
- be->scope_level = fd->scope_level;
- be->has_iterator = FALSE;
- be->is_regular_stmt = FALSE;
- }
- static void pop_break_entry(JSFunctionDef *fd)
- {
- BlockEnv *be;
- be = fd->top_break;
- fd->top_break = be->prev;
- }
- static __exception int emit_break(JSParseState *s, JSAtom name, int is_cont)
- {
- BlockEnv *top;
- int i, scope_level;
- scope_level = s->cur_func->scope_level;
- top = s->cur_func->top_break;
- while (top != NULL) {
- close_scopes(s, scope_level, top->scope_level);
- scope_level = top->scope_level;
- if (is_cont &&
- top->label_cont != -1 &&
- (name == JS_ATOM_NULL || top->label_name == name)) {
- /* continue stays inside the same block */
- emit_goto(s, OP_goto, top->label_cont);
- return 0;
- }
- if (!is_cont &&
- top->label_break != -1 &&
- ((name == JS_ATOM_NULL && !top->is_regular_stmt) ||
- top->label_name == name)) {
- emit_goto(s, OP_goto, top->label_break);
- return 0;
- }
- i = 0;
- if (top->has_iterator) {
- emit_op(s, OP_iterator_close);
- i += 3;
- }
- for(; i < top->drop_count; i++)
- emit_op(s, OP_drop);
- if (top->label_finally != -1) {
- /* must push dummy value to keep same stack depth */
- emit_op(s, OP_undefined);
- emit_goto(s, OP_gosub, top->label_finally);
- emit_op(s, OP_drop);
- }
- top = top->prev;
- }
- if (name == JS_ATOM_NULL) {
- if (is_cont)
- return js_parse_error(s, "continue must be inside loop");
- else
- return js_parse_error(s, "break must be inside loop or switch");
- } else {
- return js_parse_error(s, "break/continue label not found");
- }
- }
- /* execute the finally blocks before return */
- static void emit_return(JSParseState *s, BOOL hasval)
- {
- BlockEnv *top;
- if (s->cur_func->func_kind != JS_FUNC_NORMAL) {
- if (!hasval) {
- /* no value: direct return in case of async generator */
- emit_op(s, OP_undefined);
- hasval = TRUE;
- } else if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) {
- /* the await must be done before handling the "finally" in
- case it raises an exception */
- emit_op(s, OP_await);
- }
- }
- top = s->cur_func->top_break;
- while (top != NULL) {
- if (top->has_iterator || top->label_finally != -1) {
- if (!hasval) {
- emit_op(s, OP_undefined);
- hasval = TRUE;
- }
- /* Remove the stack elements up to and including the catch
- offset. When 'yield' is used in an expression we have
- no easy way to count them, so we use this specific
- instruction instead. */
- emit_op(s, OP_nip_catch);
- /* stack: iter_obj next ret_val */
- if (top->has_iterator) {
- if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) {
- int label_next, label_next2;
- emit_op(s, OP_nip); /* next */
- emit_op(s, OP_swap);
- emit_op(s, OP_get_field2);
- emit_atom(s, JS_ATOM_return);
- /* stack: iter_obj return_func */
- emit_op(s, OP_dup);
- emit_op(s, OP_is_undefined_or_null);
- label_next = emit_goto(s, OP_if_true, -1);
- emit_op(s, OP_call_method);
- emit_u16(s, 0);
- emit_op(s, OP_iterator_check_object);
- emit_op(s, OP_await);
- label_next2 = emit_goto(s, OP_goto, -1);
- emit_label(s, label_next);
- emit_op(s, OP_drop);
- emit_label(s, label_next2);
- emit_op(s, OP_drop);
- } else {
- emit_op(s, OP_rot3r);
- emit_op(s, OP_undefined); /* dummy catch offset */
- emit_op(s, OP_iterator_close);
- }
- } else {
- /* execute the "finally" block */
- emit_goto(s, OP_gosub, top->label_finally);
- }
- }
- top = top->prev;
- }
- if (s->cur_func->is_derived_class_constructor) {
- int label_return;
- /* 'this' can be uninitialized, so it may be accessed only if
- the derived class constructor does not return an object */
- if (hasval) {
- emit_op(s, OP_check_ctor_return);
- label_return = emit_goto(s, OP_if_false, -1);
- emit_op(s, OP_drop);
- } else {
- label_return = -1;
- }
- /* The error should be raised in the caller context, so we use
- a specific opcode */
- emit_op(s, OP_scope_get_var_checkthis);
- emit_atom(s, JS_ATOM_this);
- emit_u16(s, 0);
- emit_label(s, label_return);
- emit_op(s, OP_return);
- } else if (s->cur_func->func_kind != JS_FUNC_NORMAL) {
- emit_op(s, OP_return_async);
- } else {
- emit_op(s, hasval ? OP_return : OP_return_undef);
- }
- }
- #define DECL_MASK_FUNC (1 << 0) /* allow normal function declaration */
- /* ored with DECL_MASK_FUNC if function declarations are allowed with a label */
- #define DECL_MASK_FUNC_WITH_LABEL (1 << 1)
- #define DECL_MASK_OTHER (1 << 2) /* all other declarations */
- #define DECL_MASK_ALL (DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL | DECL_MASK_OTHER)
- static __exception int js_parse_statement_or_decl(JSParseState *s,
- int decl_mask);
- static __exception int js_parse_statement(JSParseState *s)
- {
- return js_parse_statement_or_decl(s, 0);
- }
- static __exception int js_parse_block(JSParseState *s)
- {
- if (js_parse_expect(s, '{'))
- return -1;
- if (s->token.val != '}') {
- push_scope(s);
- for(;;) {
- if (js_parse_statement_or_decl(s, DECL_MASK_ALL))
- return -1;
- if (s->token.val == '}')
- break;
- }
- pop_scope(s);
- }
- if (next_token(s))
- return -1;
- return 0;
- }
- /* allowed parse_flags: PF_IN_ACCEPTED */
- static __exception int js_parse_var(JSParseState *s, int parse_flags, int tok,
- BOOL export_flag)
- {
- JSContext *ctx = s->ctx;
- JSFunctionDef *fd = s->cur_func;
- JSAtom name = JS_ATOM_NULL;
- for (;;) {
- if (s->token.val == TOK_IDENT) {
- if (s->token.u.ident.is_reserved) {
- return js_parse_error_reserved_identifier(s);
- }
- name = JS_DupAtom(ctx, s->token.u.ident.atom);
- if (name == JS_ATOM_let && (tok == TOK_LET || tok == TOK_CONST)) {
- js_parse_error(s, "'let' is not a valid lexical identifier");
- goto var_error;
- }
- if (next_token(s))
- goto var_error;
- if (js_define_var(s, name, tok))
- goto var_error;
- if (export_flag) {
- if (!add_export_entry(s, s->cur_func->module, name, name,
- JS_EXPORT_TYPE_LOCAL))
- goto var_error;
- }
- if (s->token.val == '=') {
- const uint8_t *source_ptr = s->token.ptr;
- if (next_token(s))
- goto var_error;
- if (need_var_reference(s, tok)) {
- /* Must make a reference for proper `with` semantics */
- int opcode, scope, label;
- JSAtom name1;
- emit_op(s, OP_scope_get_var);
- emit_atom(s, name);
- emit_u16(s, fd->scope_level);
- if (get_lvalue(s, &opcode, &scope, &name1, &label, NULL, FALSE, '=') < 0)
- goto var_error;
- if (js_parse_assign_expr2(s, parse_flags)) {
- JS_FreeAtom(ctx, name1);
- goto var_error;
- }
- set_object_name(s, name);
- emit_source_pos(s, source_ptr);
- put_lvalue(s, opcode, scope, name1, label,
- PUT_LVALUE_NOKEEP, FALSE);
- } else {
- if (js_parse_assign_expr2(s, parse_flags))
- goto var_error;
- set_object_name(s, name);
- emit_source_pos(s, source_ptr);
- emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ?
- OP_scope_put_var_init : OP_scope_put_var);
- emit_atom(s, name);
- emit_u16(s, fd->scope_level);
- }
- } else {
- if (tok == TOK_CONST) {
- js_parse_error(s, "missing initializer for const variable");
- goto var_error;
- }
- if (tok == TOK_LET) {
- /* initialize lexical variable upon entering its scope */
- emit_op(s, OP_undefined);
- emit_op(s, OP_scope_put_var_init);
- emit_atom(s, name);
- emit_u16(s, fd->scope_level);
- }
- }
- JS_FreeAtom(ctx, name);
- } else {
- int skip_bits;
- if ((s->token.val == '[' || s->token.val == '{')
- && js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') {
- emit_op(s, OP_undefined);
- if (js_parse_destructuring_element(s, tok, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE, export_flag) < 0)
- return -1;
- } else {
- return js_parse_error(s, "variable name expected");
- }
- }
- if (s->token.val != ',')
- break;
- if (next_token(s))
- return -1;
- }
- return 0;
- var_error:
- JS_FreeAtom(ctx, name);
- return -1;
- }
- /* test if the current token is a label. Use simplistic look-ahead scanner */
- static BOOL is_label(JSParseState *s)
- {
- return (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved &&
- peek_token(s, FALSE) == ':');
- }
- /* test if the current token is a let keyword. Use simplistic look-ahead scanner */
- static int is_let(JSParseState *s, int decl_mask)
- {
- int res = FALSE;
- const uint8_t *last_token_ptr;
-
- if (token_is_pseudo_keyword(s, JS_ATOM_let)) {
- JSParsePos pos;
- js_parse_get_pos(s, &pos);
- for (;;) {
- last_token_ptr = s->token.ptr;
- if (next_token(s)) {
- res = -1;
- break;
- }
- if (s->token.val == '[') {
- /* let [ is a syntax restriction:
- it never introduces an ExpressionStatement */
- res = TRUE;
- break;
- }
- if (s->token.val == '{' ||
- (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved) ||
- s->token.val == TOK_LET ||
- s->token.val == TOK_YIELD ||
- s->token.val == TOK_AWAIT) {
- /* Check for possible ASI if not scanning for Declaration */
- /* XXX: should also check that `{` introduces a BindingPattern,
- but Firefox does not and rejects eval("let=1;let\n{if(1)2;}") */
- if (!has_lf_in_range(last_token_ptr, s->token.ptr) ||
- (decl_mask & DECL_MASK_OTHER)) {
- res = TRUE;
- break;
- }
- break;
- }
- break;
- }
- if (js_parse_seek_token(s, &pos)) {
- res = -1;
- }
- }
- return res;
- }
- /* XXX: handle IteratorClose when exiting the loop before the
- enumeration is done */
- static __exception int js_parse_for_in_of(JSParseState *s, int label_name,
- BOOL is_async)
- {
- JSContext *ctx = s->ctx;
- JSFunctionDef *fd = s->cur_func;
- JSAtom var_name;
- BOOL has_initializer, is_for_of, has_destructuring;
- int tok, tok1, opcode, scope, block_scope_level;
- int label_next, label_expr, label_cont, label_body, label_break;
- int pos_next, pos_expr;
- BlockEnv break_entry;
- has_initializer = FALSE;
- has_destructuring = FALSE;
- is_for_of = FALSE;
- block_scope_level = fd->scope_level;
- label_cont = new_label(s);
- label_body = new_label(s);
- label_break = new_label(s);
- label_next = new_label(s);
- /* create scope for the lexical variables declared in the enumeration
- expressions. XXX: Not completely correct because of weird capturing
- semantics in `for (i of o) a.push(function(){return i})` */
- push_scope(s);
- /* local for_in scope starts here so individual elements
- can be closed in statement. */
- push_break_entry(s->cur_func, &break_entry,
- label_name, label_break, label_cont, 1);
- break_entry.scope_level = block_scope_level;
- label_expr = emit_goto(s, OP_goto, -1);
- pos_next = s->cur_func->byte_code.size;
- emit_label(s, label_next);
- tok = s->token.val;
- switch (is_let(s, DECL_MASK_OTHER)) {
- case TRUE:
- tok = TOK_LET;
- break;
- case FALSE:
- break;
- default:
- return -1;
- }
- if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) {
- if (next_token(s))
- return -1;
- if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) {
- if (s->token.val == '[' || s->token.val == '{') {
- if (js_parse_destructuring_element(s, tok, 0, TRUE, -1, FALSE, FALSE) < 0)
- return -1;
- has_destructuring = TRUE;
- } else {
- return js_parse_error(s, "variable name expected");
- }
- var_name = JS_ATOM_NULL;
- } else {
- var_name = JS_DupAtom(ctx, s->token.u.ident.atom);
- if (next_token(s)) {
- JS_FreeAtom(s->ctx, var_name);
- return -1;
- }
- if (js_define_var(s, var_name, tok)) {
- JS_FreeAtom(s->ctx, var_name);
- return -1;
- }
- emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ?
- OP_scope_put_var_init : OP_scope_put_var);
- emit_atom(s, var_name);
- emit_u16(s, fd->scope_level);
- }
- } else if (!is_async && token_is_pseudo_keyword(s, JS_ATOM_async) &&
- peek_token(s, FALSE) == TOK_OF) {
- return js_parse_error(s, "'for of' expression cannot start with 'async'");
- } else {
- int skip_bits;
- if ((s->token.val == '[' || s->token.val == '{')
- && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == TOK_IN || tok1 == TOK_OF)) {
- if (js_parse_destructuring_element(s, 0, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE, FALSE) < 0)
- return -1;
- } else {
- int lvalue_label;
- if (js_parse_left_hand_side_expr(s))
- return -1;
- if (get_lvalue(s, &opcode, &scope, &var_name, &lvalue_label,
- NULL, FALSE, TOK_FOR))
- return -1;
- put_lvalue(s, opcode, scope, var_name, lvalue_label,
- PUT_LVALUE_NOKEEP_BOTTOM, FALSE);
- }
- var_name = JS_ATOM_NULL;
- }
- emit_goto(s, OP_goto, label_body);
- pos_expr = s->cur_func->byte_code.size;
- emit_label(s, label_expr);
- if (s->token.val == '=') {
- /* XXX: potential scoping issue if inside `with` statement */
- has_initializer = TRUE;
- /* parse and evaluate initializer prior to evaluating the
- object (only used with "for in" with a non lexical variable
- in non strict mode */
- if (next_token(s) || js_parse_assign_expr2(s, 0)) {
- JS_FreeAtom(ctx, var_name);
- return -1;
- }
- if (var_name != JS_ATOM_NULL) {
- emit_op(s, OP_scope_put_var);
- emit_atom(s, var_name);
- emit_u16(s, fd->scope_level);
- }
- }
- JS_FreeAtom(ctx, var_name);
- if (token_is_pseudo_keyword(s, JS_ATOM_of)) {
- is_for_of = TRUE;
- if (has_initializer)
- goto initializer_error;
- } else if (s->token.val == TOK_IN) {
- if (is_async)
- return js_parse_error(s, "'for await' loop should be used with 'of'");
- if (has_initializer &&
- (tok != TOK_VAR || (fd->js_mode & JS_MODE_STRICT) ||
- has_destructuring)) {
- initializer_error:
- return js_parse_error(s, "a declaration in the head of a for-%s loop can't have an initializer",
- is_for_of ? "of" : "in");
- }
- } else {
- return js_parse_error(s, "expected 'of' or 'in' in for control expression");
- }
- if (next_token(s))
- return -1;
- if (is_for_of) {
- if (js_parse_assign_expr(s))
- return -1;
- } else {
- if (js_parse_expr(s))
- return -1;
- }
- /* close the scope after having evaluated the expression so that
- the TDZ values are in the closures */
- close_scopes(s, s->cur_func->scope_level, block_scope_level);
- if (is_for_of) {
- /* set has_iterator after the iterable expression is parsed so
- that a yield in the expression does not try to close a
- not-yet-created iterator */
- break_entry.has_iterator = TRUE;
- break_entry.drop_count += 2;
- if (is_async)
- emit_op(s, OP_for_await_of_start);
- else
- emit_op(s, OP_for_of_start);
- /* on stack: enum_rec */
- } else {
- emit_op(s, OP_for_in_start);
- /* on stack: enum_obj */
- }
- emit_goto(s, OP_goto, label_cont);
- if (js_parse_expect(s, ')'))
- return -1;
- if (OPTIMIZE) {
- /* move the `next` code here */
- DynBuf *bc = &s->cur_func->byte_code;
- int chunk_size = pos_expr - pos_next;
- int offset = bc->size - pos_next;
- int i;
- if (dbuf_claim(bc, chunk_size))
- return -1;
- dbuf_put(bc, bc->buf + pos_next, chunk_size);
- memset(bc->buf + pos_next, OP_nop, chunk_size);
- /* `next` part ends with a goto */
- s->cur_func->last_opcode_pos = bc->size - 5;
- /* relocate labels */
- for (i = label_cont; i < s->cur_func->label_count; i++) {
- LabelSlot *ls = &s->cur_func->label_slots[i];
- if (ls->pos >= pos_next && ls->pos < pos_expr)
- ls->pos += offset;
- }
- }
- emit_label(s, label_body);
- if (js_parse_statement(s))
- return -1;
- close_scopes(s, s->cur_func->scope_level, block_scope_level);
- emit_label(s, label_cont);
- if (is_for_of) {
- if (is_async) {
- /* stack: iter_obj next catch_offset */
- /* call the next method */
- emit_op(s, OP_for_await_of_next);
- /* get the result of the promise */
- emit_op(s, OP_await);
- /* unwrap the value and done values */
- emit_op(s, OP_iterator_get_value_done);
- } else {
- emit_op(s, OP_for_of_next);
- emit_u8(s, 0);
- }
- } else {
- emit_op(s, OP_for_in_next);
- }
- /* on stack: enum_rec / enum_obj value bool */
- emit_goto(s, OP_if_false, label_next);
- /* drop the undefined value from for_xx_next */
- emit_op(s, OP_drop);
- emit_label(s, label_break);
- if (is_for_of) {
- /* close and drop enum_rec */
- emit_op(s, OP_iterator_close);
- } else {
- emit_op(s, OP_drop);
- }
- pop_break_entry(s->cur_func);
- pop_scope(s);
- return 0;
- }
- static void set_eval_ret_undefined(JSParseState *s)
- {
- if (s->cur_func->eval_ret_idx >= 0) {
- emit_op(s, OP_undefined);
- emit_op(s, OP_put_loc);
- emit_u16(s, s->cur_func->eval_ret_idx);
- }
- }
- static __exception int js_parse_statement_or_decl(JSParseState *s,
- int decl_mask)
- {
- JSContext *ctx = s->ctx;
- JSAtom label_name;
- int tok;
- /* specific label handling */
- /* XXX: support multiple labels on loop statements */
- label_name = JS_ATOM_NULL;
- if (is_label(s)) {
- BlockEnv *be;
- label_name = JS_DupAtom(ctx, s->token.u.ident.atom);
- for (be = s->cur_func->top_break; be; be = be->prev) {
- if (be->label_name == label_name) {
- js_parse_error(s, "duplicate label name");
- goto fail;
- }
- }
- if (next_token(s))
- goto fail;
- if (js_parse_expect(s, ':'))
- goto fail;
- if (s->token.val != TOK_FOR
- && s->token.val != TOK_DO
- && s->token.val != TOK_WHILE) {
- /* labelled regular statement */
- int label_break, mask;
- BlockEnv break_entry;
- label_break = new_label(s);
- push_break_entry(s->cur_func, &break_entry,
- label_name, label_break, -1, 0);
- break_entry.is_regular_stmt = TRUE;
- if (!(s->cur_func->js_mode & JS_MODE_STRICT) &&
- (decl_mask & DECL_MASK_FUNC_WITH_LABEL)) {
- mask = DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL;
- } else {
- mask = 0;
- }
- if (js_parse_statement_or_decl(s, mask))
- goto fail;
- emit_label(s, label_break);
- pop_break_entry(s->cur_func);
- goto done;
- }
- }
- switch(tok = s->token.val) {
- case '{':
- if (js_parse_block(s))
- goto fail;
- break;
- case TOK_RETURN:
- {
- const uint8_t *op_token_ptr;
- if (s->cur_func->is_eval) {
- js_parse_error(s, "return not in a function");
- goto fail;
- }
- if (s->cur_func->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT) {
- js_parse_error(s, "return in a static initializer block");
- goto fail;
- }
- op_token_ptr = s->token.ptr;
- if (next_token(s))
- goto fail;
- if (s->token.val != ';' && s->token.val != '}' && !s->got_lf) {
- if (js_parse_expr(s))
- goto fail;
- emit_source_pos(s, op_token_ptr);
- emit_return(s, TRUE);
- } else {
- emit_source_pos(s, op_token_ptr);
- emit_return(s, FALSE);
- }
- if (js_parse_expect_semi(s))
- goto fail;
- }
- break;
- case TOK_THROW:
- {
- const uint8_t *op_token_ptr;
- op_token_ptr = s->token.ptr;
- if (next_token(s))
- goto fail;
- if (s->got_lf) {
- js_parse_error(s, "line terminator not allowed after throw");
- goto fail;
- }
- if (js_parse_expr(s))
- goto fail;
- emit_source_pos(s, op_token_ptr);
- emit_op(s, OP_throw);
- if (js_parse_expect_semi(s))
- goto fail;
- }
- break;
- case TOK_LET:
- case TOK_CONST:
- haslet:
- if (!(decl_mask & DECL_MASK_OTHER)) {
- js_parse_error(s, "lexical declarations can't appear in single-statement context");
- goto fail;
- }
- /* fall thru */
- case TOK_VAR:
- if (next_token(s))
- goto fail;
- if (js_parse_var(s, TRUE, tok, FALSE))
- goto fail;
- if (js_parse_expect_semi(s))
- goto fail;
- break;
- case TOK_IF:
- {
- int label1, label2, mask;
- if (next_token(s))
- goto fail;
- /* create a new scope for `let f;if(1) function f(){}` */
- push_scope(s);
- set_eval_ret_undefined(s);
- if (js_parse_expr_paren(s))
- goto fail;
- label1 = emit_goto(s, OP_if_false, -1);
- if (s->cur_func->js_mode & JS_MODE_STRICT)
- mask = 0;
- else
- mask = DECL_MASK_FUNC; /* Annex B.3.4 */
- if (js_parse_statement_or_decl(s, mask))
- goto fail;
- if (s->token.val == TOK_ELSE) {
- label2 = emit_goto(s, OP_goto, -1);
- if (next_token(s))
- goto fail;
- emit_label(s, label1);
- if (js_parse_statement_or_decl(s, mask))
- goto fail;
- label1 = label2;
- }
- emit_label(s, label1);
- pop_scope(s);
- }
- break;
- case TOK_WHILE:
- {
- int label_cont, label_break;
- BlockEnv break_entry;
- label_cont = new_label(s);
- label_break = new_label(s);
- push_break_entry(s->cur_func, &break_entry,
- label_name, label_break, label_cont, 0);
- if (next_token(s))
- goto fail;
- set_eval_ret_undefined(s);
- emit_label(s, label_cont);
- if (js_parse_expr_paren(s))
- goto fail;
- emit_goto(s, OP_if_false, label_break);
- if (js_parse_statement(s))
- goto fail;
- emit_goto(s, OP_goto, label_cont);
- emit_label(s, label_break);
- pop_break_entry(s->cur_func);
- }
- break;
- case TOK_DO:
- {
- int label_cont, label_break, label1;
- BlockEnv break_entry;
- label_cont = new_label(s);
- label_break = new_label(s);
- label1 = new_label(s);
- push_break_entry(s->cur_func, &break_entry,
- label_name, label_break, label_cont, 0);
- if (next_token(s))
- goto fail;
- emit_label(s, label1);
- set_eval_ret_undefined(s);
- if (js_parse_statement(s))
- goto fail;
- emit_label(s, label_cont);
- if (js_parse_expect(s, TOK_WHILE))
- goto fail;
- if (js_parse_expr_paren(s))
- goto fail;
- /* Insert semicolon if missing */
- if (s->token.val == ';') {
- if (next_token(s))
- goto fail;
- }
- emit_goto(s, OP_if_true, label1);
- emit_label(s, label_break);
- pop_break_entry(s->cur_func);
- }
- break;
- case TOK_FOR:
- {
- int label_cont, label_break, label_body, label_test;
- int pos_cont, pos_body, block_scope_level;
- BlockEnv break_entry;
- int tok, bits;
- BOOL is_async;
- if (next_token(s))
- goto fail;
- set_eval_ret_undefined(s);
- bits = 0;
- is_async = FALSE;
- if (s->token.val == '(') {
- js_parse_skip_parens_token(s, &bits, FALSE);
- } else if (s->token.val == TOK_AWAIT) {
- if (!(s->cur_func->func_kind & JS_FUNC_ASYNC)) {
- js_parse_error(s, "for await is only valid in asynchronous functions");
- goto fail;
- }
- is_async = TRUE;
- if (next_token(s))
- goto fail;
- s->cur_func->has_await = TRUE;
- }
- if (js_parse_expect(s, '('))
- goto fail;
- if (!(bits & SKIP_HAS_SEMI)) {
- /* parse for/in or for/of */
- if (js_parse_for_in_of(s, label_name, is_async))
- goto fail;
- break;
- }
- block_scope_level = s->cur_func->scope_level;
- /* create scope for the lexical variables declared in the initial,
- test and increment expressions */
- push_scope(s);
- /* initial expression */
- tok = s->token.val;
- if (tok != ';') {
- switch (is_let(s, DECL_MASK_OTHER)) {
- case TRUE:
- tok = TOK_LET;
- break;
- case FALSE:
- break;
- default:
- goto fail;
- }
- if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) {
- if (next_token(s))
- goto fail;
- if (js_parse_var(s, FALSE, tok, FALSE))
- goto fail;
- } else {
- if (js_parse_expr2(s, FALSE))
- goto fail;
- emit_op(s, OP_drop);
- }
- /* close the closures before the first iteration */
- close_scopes(s, s->cur_func->scope_level, block_scope_level);
- }
- if (js_parse_expect(s, ';'))
- goto fail;
- label_test = new_label(s);
- label_cont = new_label(s);
- label_body = new_label(s);
- label_break = new_label(s);
- push_break_entry(s->cur_func, &break_entry,
- label_name, label_break, label_cont, 0);
- /* test expression */
- if (s->token.val == ';') {
- /* no test expression */
- label_test = label_body;
- } else {
- emit_label(s, label_test);
- if (js_parse_expr(s))
- goto fail;
- emit_goto(s, OP_if_false, label_break);
- }
- if (js_parse_expect(s, ';'))
- goto fail;
- if (s->token.val == ')') {
- /* no end expression */
- break_entry.label_cont = label_cont = label_test;
- pos_cont = 0; /* avoid warning */
- } else {
- /* skip the end expression */
- emit_goto(s, OP_goto, label_body);
- pos_cont = s->cur_func->byte_code.size;
- emit_label(s, label_cont);
- if (js_parse_expr(s))
- goto fail;
- emit_op(s, OP_drop);
- if (label_test != label_body)
- emit_goto(s, OP_goto, label_test);
- }
- if (js_parse_expect(s, ')'))
- goto fail;
- pos_body = s->cur_func->byte_code.size;
- emit_label(s, label_body);
- if (js_parse_statement(s))
- goto fail;
- /* close the closures before the next iteration */
- /* XXX: check continue case */
- close_scopes(s, s->cur_func->scope_level, block_scope_level);
- if (OPTIMIZE && label_test != label_body && label_cont != label_test) {
- /* move the increment code here */
- DynBuf *bc = &s->cur_func->byte_code;
- int chunk_size = pos_body - pos_cont;
- int offset = bc->size - pos_cont;
- int i;
- if (dbuf_claim(bc, chunk_size))
- goto fail;
- dbuf_put(bc, bc->buf + pos_cont, chunk_size);
- memset(bc->buf + pos_cont, OP_nop, chunk_size);
- /* increment part ends with a goto */
- s->cur_func->last_opcode_pos = bc->size - 5;
- /* relocate labels */
- for (i = label_cont; i < s->cur_func->label_count; i++) {
- LabelSlot *ls = &s->cur_func->label_slots[i];
- if (ls->pos >= pos_cont && ls->pos < pos_body)
- ls->pos += offset;
- }
- } else {
- emit_goto(s, OP_goto, label_cont);
- }
- emit_label(s, label_break);
- pop_break_entry(s->cur_func);
- pop_scope(s);
- }
- break;
- case TOK_BREAK:
- case TOK_CONTINUE:
- {
- int is_cont = s->token.val - TOK_BREAK;
- int label;
- if (next_token(s))
- goto fail;
- if (!s->got_lf && s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)
- label = s->token.u.ident.atom;
- else
- label = JS_ATOM_NULL;
- if (emit_break(s, label, is_cont))
- goto fail;
- if (label != JS_ATOM_NULL) {
- if (next_token(s))
- goto fail;
- }
- if (js_parse_expect_semi(s))
- goto fail;
- }
- break;
- case TOK_SWITCH:
- {
- int label_case, label_break, label1;
- int default_label_pos;
- BlockEnv break_entry;
- if (next_token(s))
- goto fail;
- set_eval_ret_undefined(s);
- if (js_parse_expr_paren(s))
- goto fail;
- push_scope(s);
- label_break = new_label(s);
- push_break_entry(s->cur_func, &break_entry,
- label_name, label_break, -1, 1);
- if (js_parse_expect(s, '{'))
- goto fail;
- default_label_pos = -1;
- label_case = -1;
- while (s->token.val != '}') {
- if (s->token.val == TOK_CASE) {
- label1 = -1;
- if (label_case >= 0) {
- /* skip the case if needed */
- label1 = emit_goto(s, OP_goto, -1);
- }
- emit_label(s, label_case);
- label_case = -1;
- for (;;) {
- /* parse a sequence of case clauses */
- if (next_token(s))
- goto fail;
- emit_op(s, OP_dup);
- if (js_parse_expr(s))
- goto fail;
- if (js_parse_expect(s, ':'))
- goto fail;
- emit_op(s, OP_strict_eq);
- if (s->token.val == TOK_CASE) {
- label1 = emit_goto(s, OP_if_true, label1);
- } else {
- label_case = emit_goto(s, OP_if_false, -1);
- emit_label(s, label1);
- break;
- }
- }
- } else if (s->token.val == TOK_DEFAULT) {
- if (next_token(s))
- goto fail;
- if (js_parse_expect(s, ':'))
- goto fail;
- if (default_label_pos >= 0) {
- js_parse_error(s, "duplicate default");
- goto fail;
- }
- if (label_case < 0) {
- /* falling thru direct from switch expression */
- label_case = emit_goto(s, OP_goto, -1);
- }
- /* Emit a dummy label opcode. Label will be patched after
- the end of the switch body. Do not use emit_label(s, 0)
- because it would clobber label 0 address, preventing
- proper optimizer operation.
- */
- emit_op(s, OP_label);
- emit_u32(s, 0);
- default_label_pos = s->cur_func->byte_code.size - 4;
- } else {
- if (label_case < 0) {
- /* falling thru direct from switch expression */
- js_parse_error(s, "invalid switch statement");
- goto fail;
- }
- if (js_parse_statement_or_decl(s, DECL_MASK_ALL))
- goto fail;
- }
- }
- if (js_parse_expect(s, '}'))
- goto fail;
- if (default_label_pos >= 0) {
- /* Ugly patch for the `default` label, shameful and risky */
- put_u32(s->cur_func->byte_code.buf + default_label_pos,
- label_case);
- s->cur_func->label_slots[label_case].pos = default_label_pos + 4;
- } else {
- emit_label(s, label_case);
- }
- emit_label(s, label_break);
- emit_op(s, OP_drop); /* drop the switch expression */
- pop_break_entry(s->cur_func);
- pop_scope(s);
- }
- break;
- case TOK_TRY:
- {
- int label_catch, label_catch2, label_finally, label_end;
- JSAtom name;
- BlockEnv block_env;
- set_eval_ret_undefined(s);
- if (next_token(s))
- goto fail;
- label_catch = new_label(s);
- label_catch2 = new_label(s);
- label_finally = new_label(s);
- label_end = new_label(s);
- emit_goto(s, OP_catch, label_catch);
- push_break_entry(s->cur_func, &block_env,
- JS_ATOM_NULL, -1, -1, 1);
- block_env.label_finally = label_finally;
- if (js_parse_block(s))
- goto fail;
- pop_break_entry(s->cur_func);
- if (js_is_live_code(s)) {
- /* drop the catch offset */
- emit_op(s, OP_drop);
- /* must push dummy value to keep same stack size */
- emit_op(s, OP_undefined);
- emit_goto(s, OP_gosub, label_finally);
- emit_op(s, OP_drop);
- emit_goto(s, OP_goto, label_end);
- }
- if (s->token.val == TOK_CATCH) {
- if (next_token(s))
- goto fail;
- push_scope(s); /* catch variable */
- emit_label(s, label_catch);
- if (s->token.val == '{') {
- /* support optional-catch-binding feature */
- emit_op(s, OP_drop); /* pop the exception object */
- } else {
- if (js_parse_expect(s, '('))
- goto fail;
- if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) {
- if (s->token.val == '[' || s->token.val == '{') {
- /* XXX: TOK_LET is not completely correct */
- if (js_parse_destructuring_element(s, TOK_LET, 0, TRUE, -1, TRUE, FALSE) < 0)
- goto fail;
- } else {
- js_parse_error(s, "identifier expected");
- goto fail;
- }
- } else {
- name = JS_DupAtom(ctx, s->token.u.ident.atom);
- if (next_token(s)
- || js_define_var(s, name, TOK_CATCH) < 0) {
- JS_FreeAtom(ctx, name);
- goto fail;
- }
- /* store the exception value in the catch variable */
- emit_op(s, OP_scope_put_var);
- emit_u32(s, name);
- emit_u16(s, s->cur_func->scope_level);
- }
- if (js_parse_expect(s, ')'))
- goto fail;
- }
- /* XXX: should keep the address to nop it out if there is no finally block */
- emit_goto(s, OP_catch, label_catch2);
- push_scope(s); /* catch block */
- push_break_entry(s->cur_func, &block_env, JS_ATOM_NULL,
- -1, -1, 1);
- block_env.label_finally = label_finally;
- if (js_parse_block(s))
- goto fail;
- pop_break_entry(s->cur_func);
- pop_scope(s); /* catch block */
- pop_scope(s); /* catch variable */
- if (js_is_live_code(s)) {
- /* drop the catch2 offset */
- emit_op(s, OP_drop);
- /* XXX: should keep the address to nop it out if there is no finally block */
- /* must push dummy value to keep same stack size */
- emit_op(s, OP_undefined);
- emit_goto(s, OP_gosub, label_finally);
- emit_op(s, OP_drop);
- emit_goto(s, OP_goto, label_end);
- }
- /* catch exceptions thrown in the catch block to execute the
- * finally clause and rethrow the exception */
- emit_label(s, label_catch2);
- /* catch value is at TOS, no need to push undefined */
- emit_goto(s, OP_gosub, label_finally);
- emit_op(s, OP_throw);
- } else if (s->token.val == TOK_FINALLY) {
- /* finally without catch : execute the finally clause
- * and rethrow the exception */
- emit_label(s, label_catch);
- /* catch value is at TOS, no need to push undefined */
- emit_goto(s, OP_gosub, label_finally);
- emit_op(s, OP_throw);
- } else {
- js_parse_error(s, "expecting catch or finally");
- goto fail;
- }
- emit_label(s, label_finally);
- if (s->token.val == TOK_FINALLY) {
- int saved_eval_ret_idx = 0; /* avoid warning */
- if (next_token(s))
- goto fail;
- /* on the stack: ret_value gosub_ret_value */
- push_break_entry(s->cur_func, &block_env, JS_ATOM_NULL,
- -1, -1, 2);
- if (s->cur_func->eval_ret_idx >= 0) {
- /* 'finally' updates eval_ret only if not a normal
- termination */
- saved_eval_ret_idx =
- add_var(s->ctx, s->cur_func, JS_ATOM__ret_);
- if (saved_eval_ret_idx < 0)
- goto fail;
- emit_op(s, OP_get_loc);
- emit_u16(s, s->cur_func->eval_ret_idx);
- emit_op(s, OP_put_loc);
- emit_u16(s, saved_eval_ret_idx);
- set_eval_ret_undefined(s);
- }
- if (js_parse_block(s))
- goto fail;
- if (s->cur_func->eval_ret_idx >= 0) {
- emit_op(s, OP_get_loc);
- emit_u16(s, saved_eval_ret_idx);
- emit_op(s, OP_put_loc);
- emit_u16(s, s->cur_func->eval_ret_idx);
- }
- pop_break_entry(s->cur_func);
- }
- emit_op(s, OP_ret);
- emit_label(s, label_end);
- }
- break;
- case ';':
- /* empty statement */
- if (next_token(s))
- goto fail;
- break;
- case TOK_WITH:
- if (s->cur_func->js_mode & JS_MODE_STRICT) {
- js_parse_error(s, "invalid keyword: with");
- goto fail;
- } else {
- int with_idx;
- if (next_token(s))
- goto fail;
- if (js_parse_expr_paren(s))
- goto fail;
- push_scope(s);
- with_idx = define_var(s, s->cur_func, JS_ATOM__with_,
- JS_VAR_DEF_WITH);
- if (with_idx < 0)
- goto fail;
- emit_op(s, OP_to_object);
- emit_op(s, OP_put_loc);
- emit_u16(s, with_idx);
- set_eval_ret_undefined(s);
- if (js_parse_statement(s))
- goto fail;
- /* Popping scope drops lexical context for the with object variable */
- pop_scope(s);
- }
- break;
- case TOK_FUNCTION:
- /* ES6 Annex B.3.2 and B.3.3 semantics */
- if (!(decl_mask & DECL_MASK_FUNC))
- goto func_decl_error;
- if (!(decl_mask & DECL_MASK_OTHER) && peek_token(s, FALSE) == '*')
- goto func_decl_error;
- goto parse_func_var;
- case TOK_IDENT:
- if (s->token.u.ident.is_reserved) {
- js_parse_error_reserved_identifier(s);
- goto fail;
- }
- /* Determine if `let` introduces a Declaration or an ExpressionStatement */
- switch (is_let(s, decl_mask)) {
- case TRUE:
- tok = TOK_LET;
- goto haslet;
- case FALSE:
- break;
- default:
- goto fail;
- }
- if (token_is_pseudo_keyword(s, JS_ATOM_async) &&
- peek_token(s, TRUE) == TOK_FUNCTION) {
- if (!(decl_mask & DECL_MASK_OTHER)) {
- func_decl_error:
- js_parse_error(s, "function declarations can't appear in single-statement context");
- goto fail;
- }
- parse_func_var:
- if (js_parse_function_decl(s, JS_PARSE_FUNC_VAR,
- JS_FUNC_NORMAL, JS_ATOM_NULL,
- s->token.ptr))
- goto fail;
- break;
- }
- goto hasexpr;
- case TOK_CLASS:
- if (!(decl_mask & DECL_MASK_OTHER)) {
- js_parse_error(s, "class declarations can't appear in single-statement context");
- goto fail;
- }
- if (js_parse_class(s, FALSE, JS_PARSE_EXPORT_NONE))
- return -1;
- break;
- case TOK_DEBUGGER:
- /* currently no debugger, so just skip the keyword */
- if (next_token(s))
- goto fail;
- if (js_parse_expect_semi(s))
- goto fail;
- break;
- case TOK_ENUM:
- case TOK_EXPORT:
- case TOK_EXTENDS:
- js_unsupported_keyword(s, s->token.u.ident.atom);
- goto fail;
- default:
- hasexpr:
- emit_source_pos(s, s->token.ptr);
- if (js_parse_expr(s))
- goto fail;
- if (s->cur_func->eval_ret_idx >= 0) {
- /* store the expression value so that it can be returned
- by eval() */
- emit_op(s, OP_put_loc);
- emit_u16(s, s->cur_func->eval_ret_idx);
- } else {
- emit_op(s, OP_drop); /* drop the result */
- }
- if (js_parse_expect_semi(s))
- goto fail;
- break;
- }
- done:
- JS_FreeAtom(ctx, label_name);
- return 0;
- fail:
- JS_FreeAtom(ctx, label_name);
- return -1;
- }
- /* 'name' is freed. The module is referenced by 'ctx->loaded_modules' */
- static JSModuleDef *js_new_module_def(JSContext *ctx, JSAtom name)
- {
- JSModuleDef *m;
- m = js_mallocz(ctx, sizeof(*m));
- if (!m) {
- JS_FreeAtom(ctx, name);
- return NULL;
- }
- js_rc(m)->ref_count = 1;
- add_gc_object(ctx->rt, &m->header, JS_GC_OBJ_TYPE_MODULE);
- m->module_name = name;
- m->module_ns = JS_UNDEFINED;
- m->func_obj = JS_UNDEFINED;
- m->eval_exception = JS_UNDEFINED;
- m->meta_obj = JS_UNDEFINED;
- m->promise = JS_UNDEFINED;
- m->resolving_funcs[0] = JS_UNDEFINED;
- m->resolving_funcs[1] = JS_UNDEFINED;
- m->private_value = JS_UNDEFINED;
- list_add_tail(&m->link, &ctx->loaded_modules);
- return m;
- }
- static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m,
- JS_MarkFunc *mark_func)
- {
- int i;
- for(i = 0; i < m->req_module_entries_count; i++) {
- JSReqModuleEntry *rme = &m->req_module_entries[i];
- JS_MarkValue(rt, rme->attributes, mark_func);
- }
-
- for(i = 0; i < m->export_entries_count; i++) {
- JSExportEntry *me = &m->export_entries[i];
- if (me->export_type == JS_EXPORT_TYPE_LOCAL &&
- me->u.local.var_ref) {
- mark_func(rt, &me->u.local.var_ref->header);
- }
- }
- JS_MarkValue(rt, m->module_ns, mark_func);
- JS_MarkValue(rt, m->func_obj, mark_func);
- JS_MarkValue(rt, m->eval_exception, mark_func);
- JS_MarkValue(rt, m->meta_obj, mark_func);
- JS_MarkValue(rt, m->promise, mark_func);
- JS_MarkValue(rt, m->resolving_funcs[0], mark_func);
- JS_MarkValue(rt, m->resolving_funcs[1], mark_func);
- JS_MarkValue(rt, m->private_value, mark_func);
- }
- static void js_free_module_def(JSRuntime *rt, JSModuleDef *m)
- {
- int i;
- JS_FreeAtomRT(rt, m->module_name);
- for(i = 0; i < m->req_module_entries_count; i++) {
- JSReqModuleEntry *rme = &m->req_module_entries[i];
- JS_FreeAtomRT(rt, rme->module_name);
- JS_FreeValueRT(rt, rme->attributes);
- }
- js_free_rt(rt, m->req_module_entries);
- for(i = 0; i < m->export_entries_count; i++) {
- JSExportEntry *me = &m->export_entries[i];
- if (me->export_type == JS_EXPORT_TYPE_LOCAL)
- free_var_ref(rt, me->u.local.var_ref);
- JS_FreeAtomRT(rt, me->export_name);
- JS_FreeAtomRT(rt, me->local_name);
- }
- js_free_rt(rt, m->export_entries);
- js_free_rt(rt, m->star_export_entries);
- for(i = 0; i < m->import_entries_count; i++) {
- JSImportEntry *mi = &m->import_entries[i];
- JS_FreeAtomRT(rt, mi->import_name);
- }
- js_free_rt(rt, m->import_entries);
- js_free_rt(rt, m->async_parent_modules);
- JS_FreeValueRT(rt, m->module_ns);
- JS_FreeValueRT(rt, m->func_obj);
- JS_FreeValueRT(rt, m->eval_exception);
- JS_FreeValueRT(rt, m->meta_obj);
- JS_FreeValueRT(rt, m->promise);
- JS_FreeValueRT(rt, m->resolving_funcs[0]);
- JS_FreeValueRT(rt, m->resolving_funcs[1]);
- JS_FreeValueRT(rt, m->private_value);
- /* during the GC the finalizers are called in an arbitrary
- order so the module may no longer be referenced by the JSContext list */
- if (m->link.next) {
- list_del(&m->link);
- }
- remove_gc_object(&m->header);
- if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && js_rc(m)->ref_count != 0) {
- list_add_tail(&m->header.link, &rt->gc_zero_ref_count_list);
- } else {
- js_free_rt(rt, m);
- }
- }
- static int add_req_module_entry(JSContext *ctx, JSModuleDef *m,
- JSAtom module_name)
- {
- JSReqModuleEntry *rme;
- if (js_resize_array(ctx, (void **)&m->req_module_entries,
- sizeof(JSReqModuleEntry),
- &m->req_module_entries_size,
- m->req_module_entries_count + 1))
- return -1;
- rme = &m->req_module_entries[m->req_module_entries_count++];
- rme->module_name = JS_DupAtom(ctx, module_name);
- rme->module = NULL;
- rme->attributes = JS_UNDEFINED;
- return m->req_module_entries_count - 1;
- }
- static JSExportEntry *find_export_entry(JSContext *ctx, JSModuleDef *m,
- JSAtom export_name)
- {
- JSExportEntry *me;
- int i;
- for(i = 0; i < m->export_entries_count; i++) {
- me = &m->export_entries[i];
- if (me->export_name == export_name)
- return me;
- }
- return NULL;
- }
- static JSExportEntry *add_export_entry2(JSContext *ctx,
- JSParseState *s, JSModuleDef *m,
- JSAtom local_name, JSAtom export_name,
- JSExportTypeEnum export_type)
- {
- JSExportEntry *me;
- if (find_export_entry(ctx, m, export_name)) {
- char buf1[ATOM_GET_STR_BUF_SIZE];
- if (s) {
- js_parse_error(s, "duplicate exported name '%s'",
- JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name));
- } else {
- JS_ThrowSyntaxErrorAtom(ctx, "duplicate exported name '%s'", export_name);
- }
- return NULL;
- }
- if (js_resize_array(ctx, (void **)&m->export_entries,
- sizeof(JSExportEntry),
- &m->export_entries_size,
- m->export_entries_count + 1))
- return NULL;
- me = &m->export_entries[m->export_entries_count++];
- memset(me, 0, sizeof(*me));
- me->local_name = JS_DupAtom(ctx, local_name);
- me->export_name = JS_DupAtom(ctx, export_name);
- me->export_type = export_type;
- return me;
- }
- static JSExportEntry *add_export_entry(JSParseState *s, JSModuleDef *m,
- JSAtom local_name, JSAtom export_name,
- JSExportTypeEnum export_type)
- {
- return add_export_entry2(s->ctx, s, m, local_name, export_name,
- export_type);
- }
- static int add_star_export_entry(JSContext *ctx, JSModuleDef *m,
- int req_module_idx)
- {
- JSStarExportEntry *se;
- if (js_resize_array(ctx, (void **)&m->star_export_entries,
- sizeof(JSStarExportEntry),
- &m->star_export_entries_size,
- m->star_export_entries_count + 1))
- return -1;
- se = &m->star_export_entries[m->star_export_entries_count++];
- se->req_module_idx = req_module_idx;
- return 0;
- }
- /* create a C module */
- JSModuleDef *JS_NewCModule(JSContext *ctx, const char *name_str,
- JSModuleInitFunc *func)
- {
- JSModuleDef *m;
- JSAtom name;
- name = JS_NewAtom(ctx, name_str);
- if (name == JS_ATOM_NULL)
- return NULL;
- m = js_new_module_def(ctx, name);
- if (!m)
- return NULL;
- m->init_func = func;
- return m;
- }
- int JS_AddModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name)
- {
- JSExportEntry *me;
- JSAtom name;
- name = JS_NewAtom(ctx, export_name);
- if (name == JS_ATOM_NULL)
- return -1;
- me = add_export_entry2(ctx, NULL, m, JS_ATOM_NULL, name,
- JS_EXPORT_TYPE_LOCAL);
- JS_FreeAtom(ctx, name);
- if (!me)
- return -1;
- else
- return 0;
- }
- int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name,
- JSValue val)
- {
- JSExportEntry *me;
- JSAtom name;
- name = JS_NewAtom(ctx, export_name);
- if (name == JS_ATOM_NULL)
- goto fail;
- me = find_export_entry(ctx, m, name);
- JS_FreeAtom(ctx, name);
- if (!me)
- goto fail;
- set_value(ctx, me->u.local.var_ref->pvalue, val);
- return 0;
- fail:
- JS_FreeValue(ctx, val);
- return -1;
- }
- int JS_SetModulePrivateValue(JSContext *ctx, JSModuleDef *m, JSValue val)
- {
- set_value(ctx, &m->private_value, val);
- return 0;
- }
- JSValue JS_GetModulePrivateValue(JSContext *ctx, JSModuleDef *m)
- {
- return JS_DupValue(ctx, m->private_value);
- }
- void JS_SetModuleLoaderFunc(JSRuntime *rt,
- JSModuleNormalizeFunc *module_normalize,
- JSModuleLoaderFunc *module_loader, void *opaque)
- {
- rt->module_normalize_func = module_normalize;
- rt->module_loader_has_attr = FALSE;
- rt->u.module_loader_func = module_loader;
- rt->module_check_attrs = NULL;
- rt->module_loader_opaque = opaque;
- }
- void JS_SetModuleLoaderFunc2(JSRuntime *rt,
- JSModuleNormalizeFunc *module_normalize,
- JSModuleLoaderFunc2 *module_loader,
- JSModuleCheckSupportedImportAttributes *module_check_attrs,
- void *opaque)
- {
- rt->module_normalize_func = module_normalize;
- rt->module_loader_has_attr = TRUE;
- rt->u.module_loader_func2 = module_loader;
- rt->module_check_attrs = module_check_attrs;
- rt->module_loader_opaque = opaque;
- }
- /* default module filename normalizer */
- static char *js_default_module_normalize_name(JSContext *ctx,
- const char *base_name,
- const char *name)
- {
- char *filename, *p;
- const char *r;
- int cap;
- int len;
- if (name[0] != '.') {
- /* if no initial dot, the module name is not modified */
- return js_strdup(ctx, name);
- }
- p = strrchr(base_name, '/');
- if (p)
- len = p - base_name;
- else
- len = 0;
- cap = len + strlen(name) + 1 + 1;
- filename = js_malloc(ctx, cap);
- if (!filename)
- return NULL;
- memcpy(filename, base_name, len);
- filename[len] = '\0';
- /* we only normalize the leading '..' or '.' */
- r = name;
- for(;;) {
- if (r[0] == '.' && r[1] == '/') {
- r += 2;
- } else if (r[0] == '.' && r[1] == '.' && r[2] == '/') {
- /* remove the last path element of filename, except if "."
- or ".." */
- if (filename[0] == '\0')
- break;
- p = strrchr(filename, '/');
- if (!p)
- p = filename;
- else
- p++;
- if (!strcmp(p, ".") || !strcmp(p, ".."))
- break;
- if (p > filename)
- p--;
- *p = '\0';
- r += 3;
- } else {
- break;
- }
- }
- if (filename[0] != '\0')
- pstrcat(filename, cap, "/");
- pstrcat(filename, cap, r);
- // printf("normalize: %s %s -> %s\n", base_name, name, filename);
- return filename;
- }
- static JSModuleDef *js_find_loaded_module(JSContext *ctx, JSAtom name)
- {
- struct list_head *el;
- JSModuleDef *m;
- /* first look at the loaded modules */
- list_for_each(el, &ctx->loaded_modules) {
- m = list_entry(el, JSModuleDef, link);
- if (m->module_name == name)
- return m;
- }
- return NULL;
- }
- /* return NULL in case of exception (e.g. module could not be loaded) */
- static JSModuleDef *js_host_resolve_imported_module(JSContext *ctx,
- const char *base_cname,
- const char *cname1,
- JSValueConst attributes)
- {
- JSRuntime *rt = ctx->rt;
- JSModuleDef *m;
- char *cname;
- JSAtom module_name;
- if (!rt->module_normalize_func) {
- cname = js_default_module_normalize_name(ctx, base_cname, cname1);
- } else {
- cname = rt->module_normalize_func(ctx, base_cname, cname1,
- rt->module_loader_opaque);
- }
- if (!cname)
- return NULL;
- module_name = JS_NewAtom(ctx, cname);
- if (module_name == JS_ATOM_NULL) {
- js_free(ctx, cname);
- return NULL;
- }
- /* first look at the loaded modules */
- m = js_find_loaded_module(ctx, module_name);
- if (m) {
- js_free(ctx, cname);
- JS_FreeAtom(ctx, module_name);
- return m;
- }
- JS_FreeAtom(ctx, module_name);
- /* load the module */
- if (!rt->u.module_loader_func) {
- /* XXX: use a syntax error ? */
- JS_ThrowReferenceError(ctx, "could not load module '%s'",
- cname);
- js_free(ctx, cname);
- return NULL;
- }
- if (rt->module_loader_has_attr) {
- m = rt->u.module_loader_func2(ctx, cname, rt->module_loader_opaque, attributes);
- } else {
- m = rt->u.module_loader_func(ctx, cname, rt->module_loader_opaque);
- }
- js_free(ctx, cname);
- return m;
- }
- static JSModuleDef *js_host_resolve_imported_module_atom(JSContext *ctx,
- JSAtom base_module_name,
- JSAtom module_name1,
- JSValueConst attributes)
- {
- const char *base_cname, *cname;
- JSModuleDef *m;
- base_cname = JS_AtomToCString(ctx, base_module_name);
- if (!base_cname)
- return NULL;
- cname = JS_AtomToCString(ctx, module_name1);
- if (!cname) {
- JS_FreeCString(ctx, base_cname);
- return NULL;
- }
- m = js_host_resolve_imported_module(ctx, base_cname, cname, attributes);
- JS_FreeCString(ctx, base_cname);
- JS_FreeCString(ctx, cname);
- return m;
- }
- typedef struct JSResolveEntry {
- JSModuleDef *module;
- JSAtom name;
- } JSResolveEntry;
- typedef struct JSResolveState {
- JSResolveEntry *array;
- int size;
- int count;
- } JSResolveState;
- static int find_resolve_entry(JSResolveState *s,
- JSModuleDef *m, JSAtom name)
- {
- int i;
- for(i = 0; i < s->count; i++) {
- JSResolveEntry *re = &s->array[i];
- if (re->module == m && re->name == name)
- return i;
- }
- return -1;
- }
- static int add_resolve_entry(JSContext *ctx, JSResolveState *s,
- JSModuleDef *m, JSAtom name)
- {
- JSResolveEntry *re;
- if (js_resize_array(ctx, (void **)&s->array,
- sizeof(JSResolveEntry),
- &s->size, s->count + 1))
- return -1;
- re = &s->array[s->count++];
- re->module = m;
- re->name = JS_DupAtom(ctx, name);
- return 0;
- }
- typedef enum JSResolveResultEnum {
- JS_RESOLVE_RES_EXCEPTION = -1, /* memory alloc error */
- JS_RESOLVE_RES_FOUND = 0,
- JS_RESOLVE_RES_NOT_FOUND,
- JS_RESOLVE_RES_CIRCULAR,
- JS_RESOLVE_RES_AMBIGUOUS,
- } JSResolveResultEnum;
- static JSResolveResultEnum js_resolve_export1(JSContext *ctx,
- JSModuleDef **pmodule,
- JSExportEntry **pme,
- JSModuleDef *m,
- JSAtom export_name,
- JSResolveState *s)
- {
- JSExportEntry *me;
- *pmodule = NULL;
- *pme = NULL;
- if (find_resolve_entry(s, m, export_name) >= 0)
- return JS_RESOLVE_RES_CIRCULAR;
- if (add_resolve_entry(ctx, s, m, export_name) < 0)
- return JS_RESOLVE_RES_EXCEPTION;
- me = find_export_entry(ctx, m, export_name);
- if (me) {
- if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
- /* local export */
- *pmodule = m;
- *pme = me;
- return JS_RESOLVE_RES_FOUND;
- } else {
- /* indirect export */
- JSModuleDef *m1;
- m1 = m->req_module_entries[me->u.req_module_idx].module;
- if (me->local_name == JS_ATOM__star_) {
- /* export ns from */
- *pmodule = m;
- *pme = me;
- return JS_RESOLVE_RES_FOUND;
- } else {
- return js_resolve_export1(ctx, pmodule, pme, m1,
- me->local_name, s);
- }
- }
- } else {
- if (export_name != JS_ATOM_default) {
- /* not found in direct or indirect exports: try star exports */
- int i;
- for(i = 0; i < m->star_export_entries_count; i++) {
- JSStarExportEntry *se = &m->star_export_entries[i];
- JSModuleDef *m1, *res_m;
- JSExportEntry *res_me;
- JSResolveResultEnum ret;
- m1 = m->req_module_entries[se->req_module_idx].module;
- ret = js_resolve_export1(ctx, &res_m, &res_me, m1,
- export_name, s);
- if (ret == JS_RESOLVE_RES_AMBIGUOUS ||
- ret == JS_RESOLVE_RES_EXCEPTION) {
- return ret;
- } else if (ret == JS_RESOLVE_RES_FOUND) {
- if (*pme != NULL) {
- if (*pmodule != res_m ||
- res_me->local_name != (*pme)->local_name) {
- *pmodule = NULL;
- *pme = NULL;
- return JS_RESOLVE_RES_AMBIGUOUS;
- }
- } else {
- *pmodule = res_m;
- *pme = res_me;
- }
- }
- }
- if (*pme != NULL)
- return JS_RESOLVE_RES_FOUND;
- }
- return JS_RESOLVE_RES_NOT_FOUND;
- }
- }
- /* If the return value is JS_RESOLVE_RES_FOUND, return the module
- (*pmodule) and the corresponding local export entry
- (*pme). Otherwise return (NULL, NULL) */
- static JSResolveResultEnum js_resolve_export(JSContext *ctx,
- JSModuleDef **pmodule,
- JSExportEntry **pme,
- JSModuleDef *m,
- JSAtom export_name)
- {
- JSResolveState ss, *s = &ss;
- int i;
- JSResolveResultEnum ret;
- s->array = NULL;
- s->size = 0;
- s->count = 0;
- ret = js_resolve_export1(ctx, pmodule, pme, m, export_name, s);
- for(i = 0; i < s->count; i++)
- JS_FreeAtom(ctx, s->array[i].name);
- js_free(ctx, s->array);
- return ret;
- }
- static void js_resolve_export_throw_error(JSContext *ctx,
- JSResolveResultEnum res,
- JSModuleDef *m, JSAtom export_name)
- {
- char buf1[ATOM_GET_STR_BUF_SIZE];
- char buf2[ATOM_GET_STR_BUF_SIZE];
- switch(res) {
- case JS_RESOLVE_RES_EXCEPTION:
- break;
- default:
- case JS_RESOLVE_RES_NOT_FOUND:
- JS_ThrowSyntaxError(ctx, "Could not find export '%s' in module '%s'",
- JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),
- JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));
- break;
- case JS_RESOLVE_RES_CIRCULAR:
- JS_ThrowSyntaxError(ctx, "circular reference when looking for export '%s' in module '%s'",
- JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),
- JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));
- break;
- case JS_RESOLVE_RES_AMBIGUOUS:
- JS_ThrowSyntaxError(ctx, "export '%s' in module '%s' is ambiguous",
- JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),
- JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));
- break;
- }
- }
- typedef enum {
- EXPORTED_NAME_AMBIGUOUS,
- EXPORTED_NAME_NORMAL,
- EXPORTED_NAME_DELAYED,
- } ExportedNameEntryEnum;
- typedef struct ExportedNameEntry {
- JSAtom export_name;
- ExportedNameEntryEnum export_type;
- union {
- JSExportEntry *me; /* using when the list is built */
- JSVarRef *var_ref; /* EXPORTED_NAME_NORMAL */
- } u;
- } ExportedNameEntry;
- typedef struct GetExportNamesState {
- JSModuleDef **modules;
- int modules_size;
- int modules_count;
- ExportedNameEntry *exported_names;
- int exported_names_size;
- int exported_names_count;
- } GetExportNamesState;
- static int find_exported_name(GetExportNamesState *s, JSAtom name)
- {
- int i;
- for(i = 0; i < s->exported_names_count; i++) {
- if (s->exported_names[i].export_name == name)
- return i;
- }
- return -1;
- }
- static __exception int get_exported_names(JSContext *ctx,
- GetExportNamesState *s,
- JSModuleDef *m, BOOL from_star)
- {
- ExportedNameEntry *en;
- int i, j;
- /* check circular reference */
- for(i = 0; i < s->modules_count; i++) {
- if (s->modules[i] == m)
- return 0;
- }
- if (js_resize_array(ctx, (void **)&s->modules, sizeof(s->modules[0]),
- &s->modules_size, s->modules_count + 1))
- return -1;
- s->modules[s->modules_count++] = m;
- for(i = 0; i < m->export_entries_count; i++) {
- JSExportEntry *me = &m->export_entries[i];
- if (from_star && me->export_name == JS_ATOM_default)
- continue;
- j = find_exported_name(s, me->export_name);
- if (j < 0) {
- if (js_resize_array(ctx, (void **)&s->exported_names, sizeof(s->exported_names[0]),
- &s->exported_names_size,
- s->exported_names_count + 1))
- return -1;
- en = &s->exported_names[s->exported_names_count++];
- en->export_name = me->export_name;
- /* avoid a second lookup for simple module exports */
- if (from_star || me->export_type != JS_EXPORT_TYPE_LOCAL)
- en->u.me = NULL;
- else
- en->u.me = me;
- } else {
- en = &s->exported_names[j];
- en->u.me = NULL;
- }
- }
- for(i = 0; i < m->star_export_entries_count; i++) {
- JSStarExportEntry *se = &m->star_export_entries[i];
- JSModuleDef *m1;
- m1 = m->req_module_entries[se->req_module_idx].module;
- if (get_exported_names(ctx, s, m1, TRUE))
- return -1;
- }
- return 0;
- }
- /* Unfortunately, the spec gives a different behavior from GetOwnProperty ! */
- static int js_module_ns_has(JSContext *ctx, JSValueConst obj, JSAtom atom)
- {
- return (find_own_property1(JS_VALUE_GET_OBJ(obj), atom) != NULL);
- }
- static const JSClassExoticMethods js_module_ns_exotic_methods = {
- .has_property = js_module_ns_has,
- };
- static int exported_names_cmp(const void *p1, const void *p2, void *opaque)
- {
- JSContext *ctx = opaque;
- const ExportedNameEntry *me1 = p1;
- const ExportedNameEntry *me2 = p2;
- JSValue str1, str2;
- int ret;
- /* XXX: should avoid allocation memory in atom comparison */
- str1 = JS_AtomToString(ctx, me1->export_name);
- str2 = JS_AtomToString(ctx, me2->export_name);
- if (JS_IsException(str1) || JS_IsException(str2)) {
- /* XXX: raise an error ? */
- ret = 0;
- } else {
- ret = js_string_compare(ctx, JS_VALUE_GET_STRING(str1),
- JS_VALUE_GET_STRING(str2));
- }
- JS_FreeValue(ctx, str1);
- JS_FreeValue(ctx, str2);
- return ret;
- }
- static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom,
- void *opaque)
- {
- JSModuleDef *m = opaque;
- JSResolveResultEnum res;
- JSExportEntry *res_me;
- JSModuleDef *res_m;
- JSVarRef *var_ref;
- res = js_resolve_export(ctx, &res_m, &res_me, m, atom);
- if (res != JS_RESOLVE_RES_FOUND) {
- /* fail safe: normally no error should happen here except for memory */
- js_resolve_export_throw_error(ctx, res, m, atom);
- return JS_EXCEPTION;
- }
- if (res_me->local_name == JS_ATOM__star_) {
- return JS_GetModuleNamespace(ctx, res_m->req_module_entries[res_me->u.req_module_idx].module);
- } else {
- if (res_me->u.local.var_ref) {
- var_ref = res_me->u.local.var_ref;
- } else {
- JSObject *p1 = JS_VALUE_GET_OBJ(res_m->func_obj);
- var_ref = p1->u.func.var_refs[res_me->u.local.var_idx];
- }
- /* WARNING: a varref is returned as a string ! */
- return JS_MKPTR(JS_TAG_STRING, var_ref);
- }
- }
- static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m)
- {
- JSValue obj;
- JSObject *p;
- GetExportNamesState s_s, *s = &s_s;
- int i, ret;
- JSProperty *pr;
- obj = JS_NewObjectClass(ctx, JS_CLASS_MODULE_NS);
- if (JS_IsException(obj))
- return obj;
- p = JS_VALUE_GET_OBJ(obj);
- memset(s, 0, sizeof(*s));
- ret = get_exported_names(ctx, s, m, FALSE);
- js_free(ctx, s->modules);
- if (ret)
- goto fail;
- /* Resolve the exported names. The ambiguous exports are removed */
- for(i = 0; i < s->exported_names_count; i++) {
- ExportedNameEntry *en = &s->exported_names[i];
- JSResolveResultEnum res;
- JSExportEntry *res_me;
- JSModuleDef *res_m;
- if (en->u.me) {
- res_me = en->u.me; /* fast case: no resolution needed */
- res_m = m;
- res = JS_RESOLVE_RES_FOUND;
- } else {
- res = js_resolve_export(ctx, &res_m, &res_me, m,
- en->export_name);
- }
- if (res != JS_RESOLVE_RES_FOUND) {
- if (res != JS_RESOLVE_RES_AMBIGUOUS) {
- js_resolve_export_throw_error(ctx, res, m, en->export_name);
- goto fail;
- }
- en->export_type = EXPORTED_NAME_AMBIGUOUS;
- } else {
- if (res_me->local_name == JS_ATOM__star_) {
- en->export_type = EXPORTED_NAME_DELAYED;
- } else {
- if (res_me->u.local.var_ref) {
- en->u.var_ref = res_me->u.local.var_ref;
- } else {
- JSObject *p1 = JS_VALUE_GET_OBJ(res_m->func_obj);
- en->u.var_ref = p1->u.func.var_refs[res_me->u.local.var_idx];
- }
- if (en->u.var_ref == NULL)
- en->export_type = EXPORTED_NAME_DELAYED;
- else
- en->export_type = EXPORTED_NAME_NORMAL;
- }
- }
- }
- /* sort the exported names */
- rqsort(s->exported_names, s->exported_names_count,
- sizeof(s->exported_names[0]), exported_names_cmp, ctx);
- for(i = 0; i < s->exported_names_count; i++) {
- ExportedNameEntry *en = &s->exported_names[i];
- switch(en->export_type) {
- case EXPORTED_NAME_NORMAL:
- {
- JSVarRef *var_ref = en->u.var_ref;
- pr = add_property(ctx, p, en->export_name,
- JS_PROP_ENUMERABLE | JS_PROP_WRITABLE |
- JS_PROP_VARREF);
- if (!pr)
- goto fail;
- js_rc(var_ref)->ref_count++;
- pr->u.var_ref = var_ref;
- }
- break;
- case EXPORTED_NAME_DELAYED:
- /* the exported namespace or reference may depend on
- circular references, so we resolve it lazily */
- if (JS_DefineAutoInitProperty(ctx, obj,
- en->export_name,
- JS_AUTOINIT_ID_MODULE_NS,
- m, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0)
- goto fail;
- break;
- default:
- break;
- }
- }
- js_free(ctx, s->exported_names);
- JS_DefinePropertyValue(ctx, obj, JS_ATOM_Symbol_toStringTag,
- JS_AtomToString(ctx, JS_ATOM_Module),
- 0);
- p->extensible = FALSE;
- return obj;
- fail:
- js_free(ctx, s->exported_names);
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- JSValue JS_GetModuleNamespace(JSContext *ctx, JSModuleDef *m)
- {
- if (JS_IsUndefined(m->module_ns)) {
- JSValue val;
- val = js_build_module_ns(ctx, m);
- if (JS_IsException(val))
- return JS_EXCEPTION;
- m->module_ns = val;
- }
- return JS_DupValue(ctx, m->module_ns);
- }
- /* Load all the required modules for module 'm' */
- static int js_resolve_module(JSContext *ctx, JSModuleDef *m)
- {
- int i;
- JSModuleDef *m1;
- if (m->resolved)
- return 0;
- #ifdef DUMP_MODULE_RESOLVE
- {
- char buf1[ATOM_GET_STR_BUF_SIZE];
- printf("resolving module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));
- }
- #endif
- m->resolved = TRUE;
- /* resolve each requested module */
- for(i = 0; i < m->req_module_entries_count; i++) {
- JSReqModuleEntry *rme = &m->req_module_entries[i];
- m1 = js_host_resolve_imported_module_atom(ctx, m->module_name,
- rme->module_name,
- rme->attributes);
- if (!m1)
- return -1;
- rme->module = m1;
- /* already done in js_host_resolve_imported_module() except if
- the module was loaded with JS_EvalBinary() */
- if (js_resolve_module(ctx, m1) < 0)
- return -1;
- }
- return 0;
- }
- /* Create the <eval> function associated with the module */
- static int js_create_module_bytecode_function(JSContext *ctx, JSModuleDef *m)
- {
- JSFunctionBytecode *b;
- JSValue func_obj, bfunc;
- bfunc = m->func_obj;
- func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,
- JS_CLASS_BYTECODE_FUNCTION);
- if (JS_IsException(func_obj))
- return -1;
- m->func_obj = func_obj;
- b = JS_VALUE_GET_PTR(bfunc);
- func_obj = js_closure2(ctx, func_obj, b, NULL, NULL, TRUE, m);
- if (JS_IsException(func_obj)) {
- m->func_obj = JS_UNDEFINED; /* XXX: keep it ? */
- JS_FreeValue(ctx, func_obj);
- return -1;
- }
- return 0;
- }
- /* must be done before js_link_module() because of cyclic references */
- static int js_create_module_function(JSContext *ctx, JSModuleDef *m)
- {
- BOOL is_c_module;
- int i;
- JSVarRef *var_ref;
- if (m->func_created)
- return 0;
- is_c_module = (m->init_func != NULL);
- if (is_c_module) {
- /* initialize the exported variables */
- for(i = 0; i < m->export_entries_count; i++) {
- JSExportEntry *me = &m->export_entries[i];
- if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
- var_ref = js_create_var_ref(ctx, FALSE);
- if (!var_ref)
- return -1;
- me->u.local.var_ref = var_ref;
- }
- }
- } else {
- if (js_create_module_bytecode_function(ctx, m))
- return -1;
- }
- m->func_created = TRUE;
- /* do it on the dependencies */
- for(i = 0; i < m->req_module_entries_count; i++) {
- JSReqModuleEntry *rme = &m->req_module_entries[i];
- if (js_create_module_function(ctx, rme->module) < 0)
- return -1;
- }
- return 0;
- }
- /* Prepare a module to be executed by resolving all the imported
- variables. */
- static int js_inner_module_linking(JSContext *ctx, JSModuleDef *m,
- JSModuleDef **pstack_top, int index)
- {
- int i;
- JSImportEntry *mi;
- JSModuleDef *m1;
- JSVarRef **var_refs, *var_ref;
- JSObject *p;
- BOOL is_c_module;
- JSValue ret_val;
- if (js_check_stack_overflow(ctx->rt, 0)) {
- JS_ThrowStackOverflow(ctx);
- return -1;
- }
- #ifdef DUMP_MODULE_RESOLVE
- {
- char buf1[ATOM_GET_STR_BUF_SIZE];
- printf("js_inner_module_linking '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));
- }
- #endif
- if (m->status == JS_MODULE_STATUS_LINKING ||
- m->status == JS_MODULE_STATUS_LINKED ||
- m->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
- m->status == JS_MODULE_STATUS_EVALUATED)
- return index;
- assert(m->status == JS_MODULE_STATUS_UNLINKED);
- m->status = JS_MODULE_STATUS_LINKING;
- m->dfs_index = index;
- m->dfs_ancestor_index = index;
- index++;
- /* push 'm' on stack */
- m->stack_prev = *pstack_top;
- *pstack_top = m;
- for(i = 0; i < m->req_module_entries_count; i++) {
- JSReqModuleEntry *rme = &m->req_module_entries[i];
- m1 = rme->module;
- index = js_inner_module_linking(ctx, m1, pstack_top, index);
- if (index < 0)
- goto fail;
- assert(m1->status == JS_MODULE_STATUS_LINKING ||
- m1->status == JS_MODULE_STATUS_LINKED ||
- m1->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
- m1->status == JS_MODULE_STATUS_EVALUATED);
- if (m1->status == JS_MODULE_STATUS_LINKING) {
- m->dfs_ancestor_index = min_int(m->dfs_ancestor_index,
- m1->dfs_ancestor_index);
- }
- }
- #ifdef DUMP_MODULE_RESOLVE
- {
- char buf1[ATOM_GET_STR_BUF_SIZE];
- printf("instantiating module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));
- }
- #endif
- /* check the indirect exports */
- for(i = 0; i < m->export_entries_count; i++) {
- JSExportEntry *me = &m->export_entries[i];
- if (me->export_type == JS_EXPORT_TYPE_INDIRECT &&
- me->local_name != JS_ATOM__star_) {
- JSResolveResultEnum ret;
- JSExportEntry *res_me;
- JSModuleDef *res_m, *m1;
- m1 = m->req_module_entries[me->u.req_module_idx].module;
- ret = js_resolve_export(ctx, &res_m, &res_me, m1, me->local_name);
- if (ret != JS_RESOLVE_RES_FOUND) {
- js_resolve_export_throw_error(ctx, ret, m, me->export_name);
- goto fail;
- }
- }
- }
- #ifdef DUMP_MODULE_RESOLVE
- {
- printf("exported bindings:\n");
- for(i = 0; i < m->export_entries_count; i++) {
- JSExportEntry *me = &m->export_entries[i];
- printf(" name="); print_atom(ctx, me->export_name);
- printf(" local="); print_atom(ctx, me->local_name);
- printf(" type=%d idx=%d\n", me->export_type, me->u.local.var_idx);
- }
- }
- #endif
- is_c_module = (m->init_func != NULL);
- if (!is_c_module) {
- p = JS_VALUE_GET_OBJ(m->func_obj);
- var_refs = p->u.func.var_refs;
- for(i = 0; i < m->import_entries_count; i++) {
- mi = &m->import_entries[i];
- #ifdef DUMP_MODULE_RESOLVE
- printf("import var_idx=%d name=", mi->var_idx);
- print_atom(ctx, mi->import_name);
- printf(": ");
- #endif
- m1 = m->req_module_entries[mi->req_module_idx].module;
- if (mi->is_star) {
- JSValue val;
- /* name space import */
- val = JS_GetModuleNamespace(ctx, m1);
- if (JS_IsException(val))
- goto fail;
- set_value(ctx, &var_refs[mi->var_idx]->value, val);
- #ifdef DUMP_MODULE_RESOLVE
- printf("namespace\n");
- #endif
- } else {
- JSResolveResultEnum ret;
- JSExportEntry *res_me;
- JSModuleDef *res_m;
- JSObject *p1;
- ret = js_resolve_export(ctx, &res_m,
- &res_me, m1, mi->import_name);
- if (ret != JS_RESOLVE_RES_FOUND) {
- js_resolve_export_throw_error(ctx, ret, m1, mi->import_name);
- goto fail;
- }
- if (res_me->local_name == JS_ATOM__star_) {
- JSValue val;
- JSModuleDef *m2;
- /* name space import from */
- m2 = res_m->req_module_entries[res_me->u.req_module_idx].module;
- val = JS_GetModuleNamespace(ctx, m2);
- if (JS_IsException(val))
- goto fail;
- var_ref = js_create_var_ref(ctx, TRUE);
- if (!var_ref) {
- JS_FreeValue(ctx, val);
- goto fail;
- }
- set_value(ctx, &var_ref->value, val);
- var_refs[mi->var_idx] = var_ref;
- #ifdef DUMP_MODULE_RESOLVE
- printf("namespace from\n");
- #endif
- } else {
- var_ref = res_me->u.local.var_ref;
- if (!var_ref) {
- p1 = JS_VALUE_GET_OBJ(res_m->func_obj);
- var_ref = p1->u.func.var_refs[res_me->u.local.var_idx];
- }
- js_rc(var_ref)->ref_count++;
- var_refs[mi->var_idx] = var_ref;
- #ifdef DUMP_MODULE_RESOLVE
- printf("local export (var_ref=%p)\n", var_ref);
- #endif
- }
- }
- }
- /* keep the exported variables in the module export entries (they
- are used when the eval function is deleted and cannot be
- initialized before in case imports are exported) */
- for(i = 0; i < m->export_entries_count; i++) {
- JSExportEntry *me = &m->export_entries[i];
- if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
- var_ref = var_refs[me->u.local.var_idx];
- js_rc(var_ref)->ref_count++;
- me->u.local.var_ref = var_ref;
- }
- }
- /* initialize the global variables */
- ret_val = JS_Call(ctx, m->func_obj, JS_TRUE, 0, NULL);
- if (JS_IsException(ret_val))
- goto fail;
- JS_FreeValue(ctx, ret_val);
- }
- assert(m->dfs_ancestor_index <= m->dfs_index);
- if (m->dfs_index == m->dfs_ancestor_index) {
- for(;;) {
- /* pop m1 from stack */
- m1 = *pstack_top;
- *pstack_top = m1->stack_prev;
- m1->status = JS_MODULE_STATUS_LINKED;
- if (m1 == m)
- break;
- }
- }
- #ifdef DUMP_MODULE_RESOLVE
- printf("js_inner_module_linking done\n");
- #endif
- return index;
- fail:
- return -1;
- }
- /* Prepare a module to be executed by resolving all the imported
- variables. */
- static int js_link_module(JSContext *ctx, JSModuleDef *m)
- {
- JSModuleDef *stack_top, *m1;
- #ifdef DUMP_MODULE_RESOLVE
- {
- char buf1[ATOM_GET_STR_BUF_SIZE];
- printf("js_link_module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));
- }
- #endif
- assert(m->status == JS_MODULE_STATUS_UNLINKED ||
- m->status == JS_MODULE_STATUS_LINKED ||
- m->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
- m->status == JS_MODULE_STATUS_EVALUATED);
- stack_top = NULL;
- if (js_inner_module_linking(ctx, m, &stack_top, 0) < 0) {
- while (stack_top != NULL) {
- m1 = stack_top;
- assert(m1->status == JS_MODULE_STATUS_LINKING);
- m1->status = JS_MODULE_STATUS_UNLINKED;
- stack_top = m1->stack_prev;
- }
- return -1;
- }
- assert(stack_top == NULL);
- assert(m->status == JS_MODULE_STATUS_LINKED ||
- m->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
- m->status == JS_MODULE_STATUS_EVALUATED);
- return 0;
- }
- /* return JS_ATOM_NULL if the name cannot be found. Only works with
- not striped bytecode functions. */
- JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels)
- {
- JSStackFrame *sf;
- JSFunctionBytecode *b;
- JSObject *p;
- /* XXX: currently we just use the filename of the englobing
- function from the debug info. May need to add a ScriptOrModule
- info in JSFunctionBytecode. */
- sf = ctx->rt->current_stack_frame;
- if (!sf)
- return JS_ATOM_NULL;
- while (n_stack_levels-- > 0) {
- sf = sf->prev_frame;
- if (!sf)
- return JS_ATOM_NULL;
- }
- for(;;) {
- if (JS_VALUE_GET_TAG(sf->cur_func) != JS_TAG_OBJECT)
- return JS_ATOM_NULL;
- p = JS_VALUE_GET_OBJ(sf->cur_func);
- if (!js_class_has_bytecode(p->class_id))
- return JS_ATOM_NULL;
- b = p->u.func.function_bytecode;
- if (!b->is_direct_or_indirect_eval) {
- if (!b->has_debug)
- return JS_ATOM_NULL;
- return JS_DupAtom(ctx, b->debug.filename);
- } else {
- sf = sf->prev_frame;
- if (!sf)
- return JS_ATOM_NULL;
- }
- }
- }
- JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m)
- {
- return JS_DupAtom(ctx, m->module_name);
- }
- JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m)
- {
- JSValue obj;
- /* allocate meta_obj only if requested to save memory */
- obj = m->meta_obj;
- if (JS_IsUndefined(obj)) {
- obj = JS_NewObjectProto(ctx, JS_NULL);
- if (JS_IsException(obj))
- return JS_EXCEPTION;
- m->meta_obj = obj;
- }
- return JS_DupValue(ctx, obj);
- }
- static JSValue js_import_meta(JSContext *ctx)
- {
- JSAtom filename;
- JSModuleDef *m;
- filename = JS_GetScriptOrModuleName(ctx, 0);
- if (filename == JS_ATOM_NULL)
- goto fail;
- /* XXX: inefficient, need to add a module or script pointer in
- JSFunctionBytecode */
- m = js_find_loaded_module(ctx, filename);
- JS_FreeAtom(ctx, filename);
- if (!m) {
- fail:
- JS_ThrowTypeError(ctx, "import.meta not supported in this context");
- return JS_EXCEPTION;
- }
- return JS_GetImportMeta(ctx, m);
- }
- static JSValue JS_NewModuleValue(JSContext *ctx, JSModuleDef *m)
- {
- return JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));
- }
- static JSValue js_load_module_rejected(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic, JSValue *func_data)
- {
- JSValueConst *resolving_funcs = (JSValueConst *)func_data;
- JSValueConst error;
- JSValue ret;
- /* XXX: check if the test is necessary */
- if (argc >= 1)
- error = argv[0];
- else
- error = JS_UNDEFINED;
- ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,
- 1, &error);
- JS_FreeValue(ctx, ret);
- return JS_UNDEFINED;
- }
- static JSValue js_load_module_fulfilled(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic, JSValue *func_data)
- {
- JSValueConst *resolving_funcs = (JSValueConst *)func_data;
- JSModuleDef *m = JS_VALUE_GET_PTR(func_data[2]);
- JSValue ret, ns;
- /* return the module namespace */
- ns = JS_GetModuleNamespace(ctx, m);
- if (JS_IsException(ns)) {
- JSValue err = JS_GetException(ctx);
- js_load_module_rejected(ctx, JS_UNDEFINED, 1, (JSValueConst *)&err, 0, func_data);
- return JS_UNDEFINED;
- }
- ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED,
- 1, (JSValueConst *)&ns);
- JS_FreeValue(ctx, ret);
- JS_FreeValue(ctx, ns);
- return JS_UNDEFINED;
- }
- static void JS_LoadModuleInternal(JSContext *ctx, const char *basename,
- const char *filename,
- JSValueConst *resolving_funcs,
- JSValueConst attributes)
- {
- JSValue evaluate_promise;
- JSModuleDef *m;
- JSValue ret, err, func_obj, evaluate_resolving_funcs[2];
- JSValueConst func_data[3];
- m = js_host_resolve_imported_module(ctx, basename, filename, attributes);
- if (!m)
- goto fail;
- if (js_resolve_module(ctx, m) < 0) {
- js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED);
- goto fail;
- }
- /* Evaluate the module code */
- func_obj = JS_NewModuleValue(ctx, m);
- evaluate_promise = JS_EvalFunction(ctx, func_obj);
- if (JS_IsException(evaluate_promise)) {
- fail:
- err = JS_GetException(ctx);
- ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,
- 1, (JSValueConst *)&err);
- JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */
- JS_FreeValue(ctx, err);
- return;
- }
- func_obj = JS_NewModuleValue(ctx, m);
- func_data[0] = resolving_funcs[0];
- func_data[1] = resolving_funcs[1];
- func_data[2] = func_obj;
- evaluate_resolving_funcs[0] = JS_NewCFunctionData(ctx, js_load_module_fulfilled, 0, 0, 3, func_data);
- evaluate_resolving_funcs[1] = JS_NewCFunctionData(ctx, js_load_module_rejected, 0, 0, 3, func_data);
- JS_FreeValue(ctx, func_obj);
- ret = js_promise_then(ctx, evaluate_promise, 2, (JSValueConst *)evaluate_resolving_funcs);
- JS_FreeValue(ctx, ret);
- JS_FreeValue(ctx, evaluate_resolving_funcs[0]);
- JS_FreeValue(ctx, evaluate_resolving_funcs[1]);
- JS_FreeValue(ctx, evaluate_promise);
- }
- /* Return a promise or an exception in case of memory error. Used by
- os.Worker() */
- JSValue JS_LoadModule(JSContext *ctx, const char *basename,
- const char *filename)
- {
- JSValue promise, resolving_funcs[2];
- promise = JS_NewPromiseCapability(ctx, resolving_funcs);
- if (JS_IsException(promise))
- return JS_EXCEPTION;
- JS_LoadModuleInternal(ctx, basename, filename,
- (JSValueConst *)resolving_funcs, JS_UNDEFINED);
- JS_FreeValue(ctx, resolving_funcs[0]);
- JS_FreeValue(ctx, resolving_funcs[1]);
- return promise;
- }
- static JSValue js_dynamic_import_job(JSContext *ctx,
- int argc, JSValueConst *argv)
- {
- JSValueConst *resolving_funcs = argv;
- JSValueConst basename_val = argv[2];
- JSValueConst specifier = argv[3];
- JSValueConst attributes = argv[4];
- const char *basename = NULL, *filename;
- JSValue ret, err;
- if (!JS_IsString(basename_val)) {
- JS_ThrowTypeError(ctx, "no function filename for import()");
- goto exception;
- }
- basename = JS_ToCString(ctx, basename_val);
- if (!basename)
- goto exception;
- filename = JS_ToCString(ctx, specifier);
- if (!filename)
- goto exception;
- JS_LoadModuleInternal(ctx, basename, filename,
- resolving_funcs, attributes);
- JS_FreeCString(ctx, filename);
- JS_FreeCString(ctx, basename);
- return JS_UNDEFINED;
- exception:
- err = JS_GetException(ctx);
- ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,
- 1, (JSValueConst *)&err);
- JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */
- JS_FreeValue(ctx, err);
- JS_FreeCString(ctx, basename);
- return JS_UNDEFINED;
- }
- static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier, JSValueConst options)
- {
- JSAtom basename;
- JSValue promise, resolving_funcs[2], basename_val, err, ret;
- JSValue specifier_str = JS_UNDEFINED, attributes = JS_UNDEFINED, attributes_obj = JS_UNDEFINED;
- JSValueConst args[5];
- basename = JS_GetScriptOrModuleName(ctx, 0);
- if (basename == JS_ATOM_NULL)
- basename_val = JS_NULL;
- else
- basename_val = JS_AtomToValue(ctx, basename);
- JS_FreeAtom(ctx, basename);
- if (JS_IsException(basename_val))
- return basename_val;
- promise = JS_NewPromiseCapability(ctx, resolving_funcs);
- if (JS_IsException(promise)) {
- JS_FreeValue(ctx, basename_val);
- return promise;
- }
- /* the string conversion must occur here */
- specifier_str = JS_ToString(ctx, specifier);
- if (JS_IsException(specifier_str))
- goto exception;
-
- if (!JS_IsUndefined(options)) {
- if (!JS_IsObject(options)) {
- JS_ThrowTypeError(ctx, "options must be an object");
- goto exception;
- }
- attributes_obj = JS_GetProperty(ctx, options, JS_ATOM_with);
- if (JS_IsException(attributes_obj))
- goto exception;
- if (!JS_IsUndefined(attributes_obj)) {
- JSPropertyEnum *atoms;
- uint32_t atoms_len, i;
- JSValue val;
-
- if (!JS_IsObject(attributes_obj)) {
- JS_ThrowTypeError(ctx, "options.with must be an object");
- goto exception;
- }
- attributes = JS_NewObjectProto(ctx, JS_NULL);
- if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &atoms_len, JS_VALUE_GET_OBJ(attributes_obj),
- JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) {
- goto exception;
- }
- for(i = 0; i < atoms_len; i++) {
- val = JS_GetProperty(ctx, attributes_obj, atoms[i].atom);
- if (JS_IsException(val))
- goto exception1;
- if (!JS_IsString(val)) {
- JS_FreeValue(ctx, val);
- JS_ThrowTypeError(ctx, "module attribute values must be strings");
- goto exception1;
- }
- if (JS_DefinePropertyValue(ctx, attributes, atoms[i].atom, val,
- JS_PROP_C_W_E) < 0) {
- exception1:
- JS_FreePropertyEnum(ctx, atoms, atoms_len);
- goto exception;
- }
- }
- JS_FreePropertyEnum(ctx, atoms, atoms_len);
- if (ctx->rt->module_check_attrs &&
- ctx->rt->module_check_attrs(ctx, ctx->rt->module_loader_opaque, attributes) < 0) {
- goto exception;
- }
- JS_FreeValue(ctx, attributes_obj);
- }
- }
- args[0] = resolving_funcs[0];
- args[1] = resolving_funcs[1];
- args[2] = basename_val;
- args[3] = specifier_str;
- args[4] = attributes;
-
- /* cannot run JS_LoadModuleInternal synchronously because it would
- cause an unexpected recursion in js_evaluate_module() */
- JS_EnqueueJob(ctx, js_dynamic_import_job, 5, args);
- done:
- JS_FreeValue(ctx, basename_val);
- JS_FreeValue(ctx, resolving_funcs[0]);
- JS_FreeValue(ctx, resolving_funcs[1]);
- JS_FreeValue(ctx, specifier_str);
- JS_FreeValue(ctx, attributes);
- return promise;
- exception:
- JS_FreeValue(ctx, attributes_obj);
- err = JS_GetException(ctx);
- ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,
- 1, (JSValueConst *)&err);
- JS_FreeValue(ctx, ret);
- JS_FreeValue(ctx, err);
- goto done;
- }
- static void js_set_module_evaluated(JSContext *ctx, JSModuleDef *m)
- {
- m->status = JS_MODULE_STATUS_EVALUATED;
- if (!JS_IsUndefined(m->promise)) {
- JSValue value, ret_val;
- assert(m->cycle_root == m);
- value = JS_UNDEFINED;
- ret_val = JS_Call(ctx, m->resolving_funcs[0], JS_UNDEFINED,
- 1, (JSValueConst *)&value);
- JS_FreeValue(ctx, ret_val);
- }
- }
- typedef struct {
- JSModuleDef **tab;
- int count;
- int size;
- } ExecModuleList;
- /* XXX: slow. Could use a linked list instead of ExecModuleList */
- static BOOL find_in_exec_module_list(ExecModuleList *exec_list, JSModuleDef *m)
- {
- int i;
- for(i = 0; i < exec_list->count; i++) {
- if (exec_list->tab[i] == m)
- return TRUE;
- }
- return FALSE;
- }
- static int gather_available_ancestors(JSContext *ctx, JSModuleDef *module,
- ExecModuleList *exec_list)
- {
- int i;
- if (js_check_stack_overflow(ctx->rt, 0)) {
- JS_ThrowStackOverflow(ctx);
- return -1;
- }
- for(i = 0; i < module->async_parent_modules_count; i++) {
- JSModuleDef *m = module->async_parent_modules[i];
- if (!find_in_exec_module_list(exec_list, m) &&
- !m->cycle_root->eval_has_exception) {
- assert(m->status == JS_MODULE_STATUS_EVALUATING_ASYNC);
- assert(!m->eval_has_exception);
- assert(m->async_evaluation);
- assert(m->pending_async_dependencies > 0);
- m->pending_async_dependencies--;
- if (m->pending_async_dependencies == 0) {
- if (js_resize_array(ctx, (void **)&exec_list->tab, sizeof(exec_list->tab[0]), &exec_list->size, exec_list->count + 1)) {
- return -1;
- }
- exec_list->tab[exec_list->count++] = m;
- if (!m->has_tla) {
- if (gather_available_ancestors(ctx, m, exec_list))
- return -1;
- }
- }
- }
- }
- return 0;
- }
- static int exec_module_list_cmp(const void *p1, const void *p2, void *opaque)
- {
- JSModuleDef *m1 = *(JSModuleDef **)p1;
- JSModuleDef *m2 = *(JSModuleDef **)p2;
- return (m1->async_evaluation_timestamp > m2->async_evaluation_timestamp) -
- (m1->async_evaluation_timestamp < m2->async_evaluation_timestamp);
- }
- static int js_execute_async_module(JSContext *ctx, JSModuleDef *m);
- static int js_execute_sync_module(JSContext *ctx, JSModuleDef *m,
- JSValue *pvalue);
- #ifdef DUMP_MODULE_EXEC
- static void js_dump_module(JSContext *ctx, const char *str, JSModuleDef *m)
- {
- char buf1[ATOM_GET_STR_BUF_SIZE];
- static const char *module_status_str[] = { "unlinked", "linking", "linked", "evaluating", "evaluating_async", "evaluated" };
- printf("%s: %s status=%s\n", str, JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name), module_status_str[m->status]);
- }
- #endif
- static JSValue js_async_module_execution_rejected(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic, JSValue *func_data)
- {
- JSModuleDef *module = JS_VALUE_GET_PTR(func_data[0]);
- JSValueConst error = argv[0];
- int i;
- #ifdef DUMP_MODULE_EXEC
- js_dump_module(ctx, __func__, module);
- #endif
- if (js_check_stack_overflow(ctx->rt, 0))
- return JS_ThrowStackOverflow(ctx);
- if (module->status == JS_MODULE_STATUS_EVALUATED) {
- assert(module->eval_has_exception);
- return JS_UNDEFINED;
- }
- assert(module->status == JS_MODULE_STATUS_EVALUATING_ASYNC);
- assert(!module->eval_has_exception);
- assert(module->async_evaluation);
- module->eval_has_exception = TRUE;
- module->eval_exception = JS_DupValue(ctx, error);
- module->status = JS_MODULE_STATUS_EVALUATED;
- module->async_evaluation = FALSE;
- if (!JS_IsUndefined(module->promise)) {
- JSValue ret_val;
- assert(module->cycle_root == module);
- ret_val = JS_Call(ctx, module->resolving_funcs[1], JS_UNDEFINED,
- 1, &error);
- JS_FreeValue(ctx, ret_val);
- }
- for(i = 0; i < module->async_parent_modules_count; i++) {
- JSModuleDef *m = module->async_parent_modules[i];
- JSValue m_obj = JS_NewModuleValue(ctx, m);
- js_async_module_execution_rejected(ctx, JS_UNDEFINED, 1, &error, 0,
- &m_obj);
- JS_FreeValue(ctx, m_obj);
- }
- return JS_UNDEFINED;
- }
- static JSValue js_async_module_execution_fulfilled(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic, JSValue *func_data)
- {
- JSModuleDef *module = JS_VALUE_GET_PTR(func_data[0]);
- ExecModuleList exec_list_s, *exec_list = &exec_list_s;
- int i;
- #ifdef DUMP_MODULE_EXEC
- js_dump_module(ctx, __func__, module);
- #endif
- if (module->status == JS_MODULE_STATUS_EVALUATED) {
- assert(module->eval_has_exception);
- return JS_UNDEFINED;
- }
- assert(module->status == JS_MODULE_STATUS_EVALUATING_ASYNC);
- assert(!module->eval_has_exception);
- assert(module->async_evaluation);
- module->async_evaluation = FALSE;
- js_set_module_evaluated(ctx, module);
- exec_list->tab = NULL;
- exec_list->count = 0;
- exec_list->size = 0;
- if (gather_available_ancestors(ctx, module, exec_list) < 0) {
- js_free(ctx, exec_list->tab);
- return JS_EXCEPTION;
- }
- /* sort by increasing async_evaluation timestamp */
- rqsort(exec_list->tab, exec_list->count, sizeof(exec_list->tab[0]),
- exec_module_list_cmp, NULL);
- for(i = 0; i < exec_list->count; i++) {
- JSModuleDef *m = exec_list->tab[i];
- #ifdef DUMP_MODULE_EXEC
- printf(" %d/%d", i, exec_list->count); js_dump_module(ctx, "", m);
- #endif
- if (m->status == JS_MODULE_STATUS_EVALUATED) {
- assert(m->eval_has_exception);
- } else if (m->has_tla) {
- js_execute_async_module(ctx, m);
- } else {
- JSValue error;
- if (js_execute_sync_module(ctx, m, &error) < 0) {
- JSValue m_obj = JS_NewModuleValue(ctx, m);
- js_async_module_execution_rejected(ctx, JS_UNDEFINED,
- 1, (JSValueConst *)&error, 0,
- &m_obj);
- JS_FreeValue(ctx, m_obj);
- JS_FreeValue(ctx, error);
- } else {
- m->async_evaluation = FALSE;
- js_set_module_evaluated(ctx, m);
- }
- }
- }
- js_free(ctx, exec_list->tab);
- return JS_UNDEFINED;
- }
- static int js_execute_async_module(JSContext *ctx, JSModuleDef *m)
- {
- JSValue promise, m_obj;
- JSValue resolve_funcs[2], ret_val;
- #ifdef DUMP_MODULE_EXEC
- js_dump_module(ctx, __func__, m);
- #endif
- promise = js_async_function_call(ctx, m->func_obj, JS_UNDEFINED, 0, NULL, 0);
- if (JS_IsException(promise))
- return -1;
- m_obj = JS_NewModuleValue(ctx, m);
- resolve_funcs[0] = JS_NewCFunctionData(ctx, js_async_module_execution_fulfilled, 0, 0, 1, (JSValueConst *)&m_obj);
- resolve_funcs[1] = JS_NewCFunctionData(ctx, js_async_module_execution_rejected, 0, 0, 1, (JSValueConst *)&m_obj);
- ret_val = js_promise_then(ctx, promise, 2, (JSValueConst *)resolve_funcs);
- JS_FreeValue(ctx, ret_val);
- JS_FreeValue(ctx, m_obj);
- JS_FreeValue(ctx, resolve_funcs[0]);
- JS_FreeValue(ctx, resolve_funcs[1]);
- JS_FreeValue(ctx, promise);
- return 0;
- }
- /* return < 0 in case of exception. *pvalue contains the exception. */
- static int js_execute_sync_module(JSContext *ctx, JSModuleDef *m,
- JSValue *pvalue)
- {
- #ifdef DUMP_MODULE_EXEC
- js_dump_module(ctx, __func__, m);
- #endif
- if (m->init_func) {
- /* C module init : no asynchronous execution */
- if (m->init_func(ctx, m) < 0)
- goto fail;
- } else {
- JSValue promise;
- JSPromiseStateEnum state;
- promise = js_async_function_call(ctx, m->func_obj, JS_UNDEFINED, 0, NULL, 0);
- if (JS_IsException(promise))
- goto fail;
- state = JS_PromiseState(ctx, promise);
- if (state == JS_PROMISE_FULFILLED) {
- JS_FreeValue(ctx, promise);
- } else if (state == JS_PROMISE_REJECTED) {
- *pvalue = JS_PromiseResult(ctx, promise);
- JS_FreeValue(ctx, promise);
- return -1;
- } else {
- JS_FreeValue(ctx, promise);
- JS_ThrowTypeError(ctx, "promise is pending");
- fail:
- *pvalue = JS_GetException(ctx);
- return -1;
- }
- }
- *pvalue = JS_UNDEFINED;
- return 0;
- }
- /* spec: InnerModuleEvaluation. Return (index, JS_UNDEFINED) or (-1,
- exception) */
- static int js_inner_module_evaluation(JSContext *ctx, JSModuleDef *m,
- int index, JSModuleDef **pstack_top,
- JSValue *pvalue)
- {
- JSModuleDef *m1;
- int i;
- #ifdef DUMP_MODULE_EXEC
- js_dump_module(ctx, __func__, m);
- #endif
- if (js_check_stack_overflow(ctx->rt, 0)) {
- JS_ThrowStackOverflow(ctx);
- *pvalue = JS_GetException(ctx);
- return -1;
- }
- if (m->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
- m->status == JS_MODULE_STATUS_EVALUATED) {
- if (m->eval_has_exception) {
- *pvalue = JS_DupValue(ctx, m->eval_exception);
- return -1;
- } else {
- *pvalue = JS_UNDEFINED;
- return index;
- }
- }
- if (m->status == JS_MODULE_STATUS_EVALUATING) {
- *pvalue = JS_UNDEFINED;
- return index;
- }
- assert(m->status == JS_MODULE_STATUS_LINKED);
- m->status = JS_MODULE_STATUS_EVALUATING;
- m->dfs_index = index;
- m->dfs_ancestor_index = index;
- m->pending_async_dependencies = 0;
- index++;
- /* push 'm' on stack */
- m->stack_prev = *pstack_top;
- *pstack_top = m;
- for(i = 0; i < m->req_module_entries_count; i++) {
- JSReqModuleEntry *rme = &m->req_module_entries[i];
- m1 = rme->module;
- index = js_inner_module_evaluation(ctx, m1, index, pstack_top, pvalue);
- if (index < 0)
- return -1;
- assert(m1->status == JS_MODULE_STATUS_EVALUATING ||
- m1->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
- m1->status == JS_MODULE_STATUS_EVALUATED);
- if (m1->status == JS_MODULE_STATUS_EVALUATING) {
- m->dfs_ancestor_index = min_int(m->dfs_ancestor_index,
- m1->dfs_ancestor_index);
- } else {
- m1 = m1->cycle_root;
- assert(m1->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
- m1->status == JS_MODULE_STATUS_EVALUATED);
- if (m1->eval_has_exception) {
- *pvalue = JS_DupValue(ctx, m1->eval_exception);
- return -1;
- }
- }
- if (m1->async_evaluation) {
- m->pending_async_dependencies++;
- if (js_resize_array(ctx, (void **)&m1->async_parent_modules, sizeof(m1->async_parent_modules[0]), &m1->async_parent_modules_size, m1->async_parent_modules_count + 1)) {
- *pvalue = JS_GetException(ctx);
- return -1;
- }
- m1->async_parent_modules[m1->async_parent_modules_count++] = m;
- }
- }
- if (m->pending_async_dependencies > 0) {
- assert(!m->async_evaluation);
- m->async_evaluation = TRUE;
- m->async_evaluation_timestamp =
- ctx->rt->module_async_evaluation_next_timestamp++;
- } else if (m->has_tla) {
- assert(!m->async_evaluation);
- m->async_evaluation = TRUE;
- m->async_evaluation_timestamp =
- ctx->rt->module_async_evaluation_next_timestamp++;
- js_execute_async_module(ctx, m);
- } else {
- if (js_execute_sync_module(ctx, m, pvalue) < 0)
- return -1;
- }
- assert(m->dfs_ancestor_index <= m->dfs_index);
- if (m->dfs_index == m->dfs_ancestor_index) {
- for(;;) {
- /* pop m1 from stack */
- m1 = *pstack_top;
- *pstack_top = m1->stack_prev;
- if (!m1->async_evaluation) {
- m1->status = JS_MODULE_STATUS_EVALUATED;
- } else {
- m1->status = JS_MODULE_STATUS_EVALUATING_ASYNC;
- }
- /* spec bug: cycle_root must be assigned before the test */
- m1->cycle_root = m;
- if (m1 == m)
- break;
- }
- }
- *pvalue = JS_UNDEFINED;
- return index;
- }
- /* Run the <eval> function of the module and of all its requested
- modules. Return a promise or an exception. */
- static JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m)
- {
- JSModuleDef *m1, *stack_top;
- JSValue ret_val, result;
- #ifdef DUMP_MODULE_EXEC
- js_dump_module(ctx, __func__, m);
- #endif
- assert(m->status == JS_MODULE_STATUS_LINKED ||
- m->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
- m->status == JS_MODULE_STATUS_EVALUATED);
- if (m->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
- m->status == JS_MODULE_STATUS_EVALUATED) {
- m = m->cycle_root;
- }
- /* a promise may be created only on the cycle_root of a cycle */
- if (!JS_IsUndefined(m->promise))
- return JS_DupValue(ctx, m->promise);
- m->promise = JS_NewPromiseCapability(ctx, m->resolving_funcs);
- if (JS_IsException(m->promise))
- return JS_EXCEPTION;
- stack_top = NULL;
- if (js_inner_module_evaluation(ctx, m, 0, &stack_top, &result) < 0) {
- while (stack_top != NULL) {
- m1 = stack_top;
- assert(m1->status == JS_MODULE_STATUS_EVALUATING);
- m1->status = JS_MODULE_STATUS_EVALUATED;
- m1->eval_has_exception = TRUE;
- m1->eval_exception = JS_DupValue(ctx, result);
- m1->cycle_root = m; /* spec bug: should be present */
- stack_top = m1->stack_prev;
- }
- JS_FreeValue(ctx, result);
- assert(m->status == JS_MODULE_STATUS_EVALUATED);
- assert(m->eval_has_exception);
- ret_val = JS_Call(ctx, m->resolving_funcs[1], JS_UNDEFINED,
- 1, (JSValueConst *)&m->eval_exception);
- JS_FreeValue(ctx, ret_val);
- } else {
- #ifdef DUMP_MODULE_EXEC
- js_dump_module(ctx, " done", m);
- #endif
- assert(m->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
- m->status == JS_MODULE_STATUS_EVALUATED);
- assert(!m->eval_has_exception);
- if (!m->async_evaluation) {
- JSValue value;
- assert(m->status == JS_MODULE_STATUS_EVALUATED);
- value = JS_UNDEFINED;
- ret_val = JS_Call(ctx, m->resolving_funcs[0], JS_UNDEFINED,
- 1, (JSValueConst *)&value);
- JS_FreeValue(ctx, ret_val);
- }
- assert(stack_top == NULL);
- }
- return JS_DupValue(ctx, m->promise);
- }
- static __exception int js_parse_with_clause(JSParseState *s, JSReqModuleEntry *rme)
- {
- JSContext *ctx = s->ctx;
- JSAtom key;
- int ret;
- const uint8_t *key_token_ptr;
-
- if (next_token(s))
- return -1;
- if (js_parse_expect(s, '{'))
- return -1;
- while (s->token.val != '}') {
- key_token_ptr = s->token.ptr;
- if (s->token.val == TOK_STRING) {
- key = JS_ValueToAtom(ctx, s->token.u.str.str);
- if (key == JS_ATOM_NULL)
- return -1;
- } else {
- if (!token_is_ident(s->token.val)) {
- js_parse_error(s, "identifier expected");
- return -1;
- }
- key = JS_DupAtom(ctx, s->token.u.ident.atom);
- }
- if (next_token(s))
- return -1;
- if (js_parse_expect(s, ':')) {
- JS_FreeAtom(ctx, key);
- return -1;
- }
- if (s->token.val != TOK_STRING) {
- js_parse_error_pos(s, key_token_ptr, "string expected");
- return -1;
- }
- if (JS_IsUndefined(rme->attributes)) {
- JSValue attributes = JS_NewObjectProto(ctx, JS_NULL);
- if (JS_IsException(attributes)) {
- JS_FreeAtom(ctx, key);
- return -1;
- }
- rme->attributes = attributes;
- }
- ret = JS_HasProperty(ctx, rme->attributes, key);
- if (ret != 0) {
- JS_FreeAtom(ctx, key);
- if (ret < 0)
- return -1;
- else
- return js_parse_error(s, "duplicate with key");
- }
- ret = JS_DefinePropertyValue(ctx, rme->attributes, key,
- JS_DupValue(ctx, s->token.u.str.str), JS_PROP_C_W_E);
- JS_FreeAtom(ctx, key);
- if (ret < 0)
- return -1;
- if (next_token(s))
- return -1;
- if (s->token.val != ',')
- break;
- if (next_token(s))
- return -1;
- }
- if (!JS_IsUndefined(rme->attributes) &&
- ctx->rt->module_check_attrs &&
- ctx->rt->module_check_attrs(ctx, ctx->rt->module_loader_opaque, rme->attributes) < 0) {
- return -1;
- }
- return js_parse_expect(s, '}');
- }
- /* return the module index in m->req_module_entries[] or < 0 if error */
- static __exception int js_parse_from_clause(JSParseState *s, JSModuleDef *m)
- {
- JSAtom module_name;
- int idx;
- if (!token_is_pseudo_keyword(s, JS_ATOM_from)) {
- js_parse_error(s, "from clause expected");
- return -1;
- }
- if (next_token(s))
- return -1;
- if (s->token.val != TOK_STRING) {
- js_parse_error(s, "string expected");
- return -1;
- }
- module_name = JS_ValueToAtom(s->ctx, s->token.u.str.str);
- if (module_name == JS_ATOM_NULL)
- return -1;
- if (next_token(s)) {
- JS_FreeAtom(s->ctx, module_name);
- return -1;
- }
- idx = add_req_module_entry(s->ctx, m, module_name);
- JS_FreeAtom(s->ctx, module_name);
- if (idx < 0)
- return -1;
- if (s->token.val == TOK_WITH) {
- if (js_parse_with_clause(s, &m->req_module_entries[idx]))
- return -1;
- }
- return idx;
- }
- static __exception int js_parse_export(JSParseState *s)
- {
- JSContext *ctx = s->ctx;
- JSModuleDef *m = s->cur_func->module;
- JSAtom local_name, export_name;
- int first_export, idx, i, tok;
- JSExportEntry *me;
- if (next_token(s))
- return -1;
- tok = s->token.val;
- if (tok == TOK_CLASS) {
- return js_parse_class(s, FALSE, JS_PARSE_EXPORT_NAMED);
- } else if (tok == TOK_FUNCTION ||
- (token_is_pseudo_keyword(s, JS_ATOM_async) &&
- peek_token(s, TRUE) == TOK_FUNCTION)) {
- return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT,
- JS_FUNC_NORMAL, JS_ATOM_NULL,
- s->token.ptr,
- JS_PARSE_EXPORT_NAMED, NULL);
- }
- if (next_token(s))
- return -1;
- switch(tok) {
- case '{':
- first_export = m->export_entries_count;
- while (s->token.val != '}') {
- if (!token_is_ident(s->token.val)) {
- js_parse_error(s, "identifier expected");
- return -1;
- }
- local_name = JS_DupAtom(ctx, s->token.u.ident.atom);
- export_name = JS_ATOM_NULL;
- if (next_token(s))
- goto fail;
- if (token_is_pseudo_keyword(s, JS_ATOM_as)) {
- if (next_token(s))
- goto fail;
- if (s->token.val == TOK_STRING) {
- if (js_string_find_invalid_codepoint(JS_VALUE_GET_STRING(s->token.u.str.str)) >= 0) {
- js_parse_error(s, "contains unpaired surrogate");
- goto fail;
- }
- export_name = JS_ValueToAtom(s->ctx, s->token.u.str.str);
- if (export_name == JS_ATOM_NULL)
- goto fail;
- } else {
- if (!token_is_ident(s->token.val)) {
- js_parse_error(s, "identifier expected");
- goto fail;
- }
- export_name = JS_DupAtom(ctx, s->token.u.ident.atom);
- }
- if (next_token(s)) {
- fail:
- JS_FreeAtom(ctx, local_name);
- fail1:
- JS_FreeAtom(ctx, export_name);
- return -1;
- }
- } else {
- export_name = JS_DupAtom(ctx, local_name);
- }
- me = add_export_entry(s, m, local_name, export_name,
- JS_EXPORT_TYPE_LOCAL);
- JS_FreeAtom(ctx, local_name);
- JS_FreeAtom(ctx, export_name);
- if (!me)
- return -1;
- if (s->token.val != ',')
- break;
- if (next_token(s))
- return -1;
- }
- if (js_parse_expect(s, '}'))
- return -1;
- if (token_is_pseudo_keyword(s, JS_ATOM_from)) {
- idx = js_parse_from_clause(s, m);
- if (idx < 0)
- return -1;
- for(i = first_export; i < m->export_entries_count; i++) {
- me = &m->export_entries[i];
- me->export_type = JS_EXPORT_TYPE_INDIRECT;
- me->u.req_module_idx = idx;
- }
- }
- break;
- case '*':
- if (token_is_pseudo_keyword(s, JS_ATOM_as)) {
- /* export ns from */
- if (next_token(s))
- return -1;
- if (!token_is_ident(s->token.val)) {
- js_parse_error(s, "identifier expected");
- return -1;
- }
- export_name = JS_DupAtom(ctx, s->token.u.ident.atom);
- if (next_token(s))
- goto fail1;
- idx = js_parse_from_clause(s, m);
- if (idx < 0)
- goto fail1;
- me = add_export_entry(s, m, JS_ATOM__star_, export_name,
- JS_EXPORT_TYPE_INDIRECT);
- JS_FreeAtom(ctx, export_name);
- if (!me)
- return -1;
- me->u.req_module_idx = idx;
- } else {
- idx = js_parse_from_clause(s, m);
- if (idx < 0)
- return -1;
- if (add_star_export_entry(ctx, m, idx) < 0)
- return -1;
- }
- break;
- case TOK_DEFAULT:
- if (s->token.val == TOK_CLASS) {
- return js_parse_class(s, FALSE, JS_PARSE_EXPORT_DEFAULT);
- } else if (s->token.val == TOK_FUNCTION ||
- (token_is_pseudo_keyword(s, JS_ATOM_async) &&
- peek_token(s, TRUE) == TOK_FUNCTION)) {
- return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT,
- JS_FUNC_NORMAL, JS_ATOM_NULL,
- s->token.ptr,
- JS_PARSE_EXPORT_DEFAULT, NULL);
- } else {
- if (js_parse_assign_expr(s))
- return -1;
- }
- /* set the name of anonymous functions */
- set_object_name(s, JS_ATOM_default);
- /* store the value in the _default_ global variable and export
- it */
- local_name = JS_ATOM__default_;
- if (define_var(s, s->cur_func, local_name, JS_VAR_DEF_LET) < 0)
- return -1;
- emit_op(s, OP_scope_put_var_init);
- emit_atom(s, local_name);
- emit_u16(s, 0);
- if (!add_export_entry(s, m, local_name, JS_ATOM_default,
- JS_EXPORT_TYPE_LOCAL))
- return -1;
- break;
- case TOK_VAR:
- case TOK_LET:
- case TOK_CONST:
- return js_parse_var(s, TRUE, tok, TRUE);
- default:
- return js_parse_error(s, "invalid export syntax");
- }
- return js_parse_expect_semi(s);
- }
- static int add_closure_var(JSContext *ctx, JSFunctionDef *s,
- JSClosureTypeEnum closure_type,
- int var_idx, JSAtom var_name,
- BOOL is_const, BOOL is_lexical,
- JSVarKindEnum var_kind);
- static int add_import(JSParseState *s, JSModuleDef *m,
- JSAtom local_name, JSAtom import_name, BOOL is_star)
- {
- JSContext *ctx = s->ctx;
- int i, var_idx;
- JSImportEntry *mi;
- if (local_name == JS_ATOM_arguments || local_name == JS_ATOM_eval)
- return js_parse_error(s, "invalid import binding");
- if (local_name != JS_ATOM_default) {
- for (i = 0; i < s->cur_func->closure_var_count; i++) {
- if (s->cur_func->closure_var[i].var_name == local_name)
- return js_parse_error(s, "duplicate import binding");
- }
- }
- var_idx = add_closure_var(ctx, s->cur_func,
- is_star ? JS_CLOSURE_MODULE_DECL : JS_CLOSURE_MODULE_IMPORT,
- m->import_entries_count,
- local_name, TRUE, TRUE, JS_VAR_NORMAL);
- if (var_idx < 0)
- return -1;
- if (js_resize_array(ctx, (void **)&m->import_entries,
- sizeof(JSImportEntry),
- &m->import_entries_size,
- m->import_entries_count + 1))
- return -1;
- mi = &m->import_entries[m->import_entries_count++];
- mi->import_name = JS_DupAtom(ctx, import_name);
- mi->var_idx = var_idx;
- mi->is_star = is_star;
- return 0;
- }
- static __exception int js_parse_import(JSParseState *s)
- {
- JSContext *ctx = s->ctx;
- JSModuleDef *m = s->cur_func->module;
- JSAtom local_name, import_name, module_name;
- int first_import, i, idx;
- if (next_token(s))
- return -1;
- first_import = m->import_entries_count;
- if (s->token.val == TOK_STRING) {
- module_name = JS_ValueToAtom(ctx, s->token.u.str.str);
- if (module_name == JS_ATOM_NULL)
- return -1;
- if (next_token(s)) {
- JS_FreeAtom(ctx, module_name);
- return -1;
- }
- idx = add_req_module_entry(ctx, m, module_name);
- JS_FreeAtom(ctx, module_name);
- if (idx < 0)
- return -1;
- if (s->token.val == TOK_WITH) {
- if (js_parse_with_clause(s, &m->req_module_entries[idx]))
- return -1;
- }
- } else {
- if (s->token.val == TOK_IDENT) {
- if (s->token.u.ident.is_reserved) {
- return js_parse_error_reserved_identifier(s);
- }
- /* "default" import */
- local_name = JS_DupAtom(ctx, s->token.u.ident.atom);
- import_name = JS_ATOM_default;
- if (next_token(s))
- goto fail;
- if (add_import(s, m, local_name, import_name, FALSE))
- goto fail;
- JS_FreeAtom(ctx, local_name);
- if (s->token.val != ',')
- goto end_import_clause;
- if (next_token(s))
- return -1;
- }
- if (s->token.val == '*') {
- /* name space import */
- if (next_token(s))
- return -1;
- if (!token_is_pseudo_keyword(s, JS_ATOM_as))
- return js_parse_error(s, "expecting 'as'");
- if (next_token(s))
- return -1;
- if (!token_is_ident(s->token.val)) {
- js_parse_error(s, "identifier expected");
- return -1;
- }
- local_name = JS_DupAtom(ctx, s->token.u.ident.atom);
- import_name = JS_ATOM__star_;
- if (next_token(s))
- goto fail;
- if (add_import(s, m, local_name, import_name, TRUE))
- goto fail;
- JS_FreeAtom(ctx, local_name);
- } else if (s->token.val == '{') {
- if (next_token(s))
- return -1;
- while (s->token.val != '}') {
- BOOL is_string;
- if (s->token.val == TOK_STRING) {
- is_string = TRUE;
- if (js_string_find_invalid_codepoint(JS_VALUE_GET_STRING(s->token.u.str.str)) >= 0) {
- js_parse_error(s, "contains unpaired surrogate");
- return -1;
- }
- import_name = JS_ValueToAtom(s->ctx, s->token.u.str.str);
- if (import_name == JS_ATOM_NULL)
- return -1;
- } else {
- is_string = FALSE;
- if (!token_is_ident(s->token.val)) {
- js_parse_error(s, "identifier expected");
- return -1;
- }
- import_name = JS_DupAtom(ctx, s->token.u.ident.atom);
- }
- local_name = JS_ATOM_NULL;
- if (next_token(s))
- goto fail;
- if (token_is_pseudo_keyword(s, JS_ATOM_as)) {
- if (next_token(s))
- goto fail;
- if (!token_is_ident(s->token.val)) {
- js_parse_error(s, "identifier expected");
- goto fail;
- }
- local_name = JS_DupAtom(ctx, s->token.u.ident.atom);
- if (next_token(s))
- goto fail;
- } else {
- if (is_string) {
- js_parse_error(s, "expecting 'as'");
- fail:
- JS_FreeAtom(ctx, local_name);
- JS_FreeAtom(ctx, import_name);
- return -1;
- }
- local_name = JS_DupAtom(ctx, import_name);
- }
- if (add_import(s, m, local_name, import_name, FALSE))
- goto fail;
- JS_FreeAtom(ctx, local_name);
- JS_FreeAtom(ctx, import_name);
- if (s->token.val != ',')
- break;
- if (next_token(s))
- return -1;
- }
- if (js_parse_expect(s, '}'))
- return -1;
- }
- end_import_clause:
- idx = js_parse_from_clause(s, m);
- if (idx < 0)
- return -1;
- }
- for(i = first_import; i < m->import_entries_count; i++)
- m->import_entries[i].req_module_idx = idx;
- return js_parse_expect_semi(s);
- }
- static __exception int js_parse_source_element(JSParseState *s)
- {
- JSFunctionDef *fd = s->cur_func;
- int tok;
- if (s->token.val == TOK_FUNCTION ||
- (token_is_pseudo_keyword(s, JS_ATOM_async) &&
- peek_token(s, TRUE) == TOK_FUNCTION)) {
- if (js_parse_function_decl(s, JS_PARSE_FUNC_STATEMENT,
- JS_FUNC_NORMAL, JS_ATOM_NULL,
- s->token.ptr))
- return -1;
- } else if (s->token.val == TOK_EXPORT && fd->module) {
- if (js_parse_export(s))
- return -1;
- } else if (s->token.val == TOK_IMPORT && fd->module &&
- ((tok = peek_token(s, FALSE)) != '(' && tok != '.')) {
- /* the peek_token is needed to avoid confusion with ImportCall
- (dynamic import) or import.meta */
- if (js_parse_import(s))
- return -1;
- } else {
- if (js_parse_statement_or_decl(s, DECL_MASK_ALL))
- return -1;
- }
- return 0;
- }
- static JSFunctionDef *js_new_function_def(JSContext *ctx,
- JSFunctionDef *parent,
- BOOL is_eval,
- BOOL is_func_expr,
- const char *filename,
- const uint8_t *source_ptr,
- GetLineColCache *get_line_col_cache)
- {
- JSFunctionDef *fd;
- fd = js_mallocz(ctx, sizeof(*fd));
- if (!fd)
- return NULL;
- fd->ctx = ctx;
- init_list_head(&fd->child_list);
- /* insert in parent list */
- fd->parent = parent;
- fd->parent_cpool_idx = -1;
- if (parent) {
- list_add_tail(&fd->link, &parent->child_list);
- fd->js_mode = parent->js_mode;
- fd->parent_scope_level = parent->scope_level;
- }
- fd->strip_debug = ((ctx->rt->strip_flags & JS_STRIP_DEBUG) != 0);
- fd->strip_source = ((ctx->rt->strip_flags & (JS_STRIP_DEBUG | JS_STRIP_SOURCE)) != 0);
- fd->is_eval = is_eval;
- fd->is_func_expr = is_func_expr;
- js_dbuf_bytecode_init(ctx, &fd->byte_code);
- fd->last_opcode_pos = -1;
- fd->func_name = JS_ATOM_NULL;
- fd->var_object_idx = -1;
- fd->arg_var_object_idx = -1;
- fd->arguments_var_idx = -1;
- fd->arguments_arg_idx = -1;
- fd->func_var_idx = -1;
- fd->eval_ret_idx = -1;
- fd->this_var_idx = -1;
- fd->new_target_var_idx = -1;
- fd->this_active_func_var_idx = -1;
- fd->home_object_var_idx = -1;
- /* XXX: should distinguish arg, var and var object and body scopes */
- fd->scopes = fd->def_scope_array;
- fd->scope_size = countof(fd->def_scope_array);
- fd->scope_count = 1;
- fd->scopes[0].first = -1;
- fd->scopes[0].parent = -1;
- fd->scope_level = 0; /* 0: var/arg scope */
- fd->scope_first = -1;
- fd->body_scope = -1;
- fd->filename = JS_NewAtom(ctx, filename);
- fd->source_pos = source_ptr - get_line_col_cache->buf_start;
- fd->get_line_col_cache = get_line_col_cache;
-
- js_dbuf_init(ctx, &fd->pc2line);
- //fd->pc2line_last_line_num = line_num;
- //fd->pc2line_last_pc = 0;
- fd->last_opcode_source_ptr = source_ptr;
- return fd;
- }
- static void free_bytecode_atoms(JSRuntime *rt,
- const uint8_t *bc_buf, int bc_len,
- BOOL use_short_opcodes)
- {
- int pos, len, op;
- JSAtom atom;
- const JSOpCode *oi;
- pos = 0;
- while (pos < bc_len) {
- op = bc_buf[pos];
- if (use_short_opcodes)
- oi = &short_opcode_info(op);
- else
- oi = &opcode_info[op];
- len = oi->size;
- switch(oi->fmt) {
- case OP_FMT_atom:
- case OP_FMT_atom_u8:
- case OP_FMT_atom_u16:
- case OP_FMT_atom_label_u8:
- case OP_FMT_atom_label_u16:
- if ((pos + 1 + 4) > bc_len)
- break; /* may happen if there is not enough memory when emiting bytecode */
- atom = get_u32(bc_buf + pos + 1);
- JS_FreeAtomRT(rt, atom);
- break;
- default:
- break;
- }
- pos += len;
- }
- }
- static void js_free_function_def(JSContext *ctx, JSFunctionDef *fd)
- {
- int i;
- struct list_head *el, *el1;
- /* free the child functions */
- list_for_each_safe(el, el1, &fd->child_list) {
- JSFunctionDef *fd1;
- fd1 = list_entry(el, JSFunctionDef, link);
- js_free_function_def(ctx, fd1);
- }
- free_bytecode_atoms(ctx->rt, fd->byte_code.buf, fd->byte_code.size,
- fd->use_short_opcodes);
- dbuf_free(&fd->byte_code);
- js_free(ctx, fd->jump_slots);
- js_free(ctx, fd->label_slots);
- js_free(ctx, fd->line_number_slots);
- for(i = 0; i < fd->cpool_count; i++) {
- JS_FreeValue(ctx, fd->cpool[i]);
- }
- js_free(ctx, fd->cpool);
- JS_FreeAtom(ctx, fd->func_name);
- for(i = 0; i < fd->var_count; i++) {
- JS_FreeAtom(ctx, fd->vars[i].var_name);
- }
- js_free(ctx, fd->vars);
- for(i = 0; i < fd->arg_count; i++) {
- JS_FreeAtom(ctx, fd->args[i].var_name);
- }
- js_free(ctx, fd->args);
- for(i = 0; i < fd->global_var_count; i++) {
- JS_FreeAtom(ctx, fd->global_vars[i].var_name);
- }
- js_free(ctx, fd->global_vars);
- for(i = 0; i < fd->closure_var_count; i++) {
- JSClosureVar *cv = &fd->closure_var[i];
- JS_FreeAtom(ctx, cv->var_name);
- }
- js_free(ctx, fd->closure_var);
- if (fd->scopes != fd->def_scope_array)
- js_free(ctx, fd->scopes);
- JS_FreeAtom(ctx, fd->filename);
- dbuf_free(&fd->pc2line);
- js_free(ctx, fd->source);
- if (fd->parent) {
- /* remove in parent list */
- list_del(&fd->link);
- }
- js_free(ctx, fd);
- }
- #ifdef DUMP_BYTECODE
- static const char *skip_lines(const char *p, int n) {
- while (n-- > 0 && *p) {
- while (*p && *p++ != '\n')
- continue;
- }
- return p;
- }
- static void print_lines(const char *source, int line, int line1) {
- const char *s = source;
- const char *p = skip_lines(s, line);
- if (*p) {
- while (line++ < line1) {
- p = skip_lines(s = p, 1);
- printf(";; %.*s", (int)(p - s), s);
- if (!*p) {
- if (p[-1] != '\n')
- printf("\n");
- break;
- }
- }
- }
- }
- static void dump_byte_code(JSContext *ctx, int pass,
- const uint8_t *tab, int len,
- const JSBytecodeVarDef *vardefs,
- const JSVarDef *args, int arg_count,
- const JSVarDef *vars, int var_count,
- const JSClosureVar *closure_var, int closure_var_count,
- const JSValue *cpool, uint32_t cpool_count,
- const char *source,
- const LabelSlot *label_slots, JSFunctionBytecode *b)
- {
- const JSOpCode *oi;
- int pos, pos_next, op, size, idx, addr, line, line1, in_source, line_num;
- uint8_t *bits = js_mallocz(ctx, len * sizeof(*bits));
- BOOL use_short_opcodes = (b != NULL), dump_pc;
- if (b) {
- int col_num;
- line_num = find_line_num(ctx, b, -1, &col_num);
- }
-
- /* scan for jump targets */
- for (pos = 0; pos < len; pos = pos_next) {
- op = tab[pos];
- if (use_short_opcodes)
- oi = &short_opcode_info(op);
- else
- oi = &opcode_info[op];
- pos_next = pos + oi->size;
- if (op < OP_COUNT) {
- switch (oi->fmt) {
- #if SHORT_OPCODES
- case OP_FMT_label8:
- pos++;
- addr = (int8_t)tab[pos];
- goto has_addr;
- case OP_FMT_label16:
- pos++;
- addr = (int16_t)get_u16(tab + pos);
- goto has_addr;
- #endif
- case OP_FMT_atom_label_u8:
- case OP_FMT_atom_label_u16:
- pos += 4;
- /* fall thru */
- case OP_FMT_label:
- case OP_FMT_label_u16:
- pos++;
- addr = get_u32(tab + pos);
- goto has_addr;
- has_addr:
- if (pass == 1)
- addr = label_slots[addr].pos;
- if (pass == 2)
- addr = label_slots[addr].pos2;
- if (pass == 3)
- addr += pos;
- if (addr >= 0 && addr < len)
- bits[addr] |= 1;
- break;
- }
- }
- }
- in_source = 0;
- if (source) {
- /* Always print first line: needed if single line */
- print_lines(source, 0, 1);
- in_source = 1;
- }
- line1 = line = 1;
- pos = 0;
- while (pos < len) {
- op = tab[pos];
- if (source && b) {
- int col_num;
- if (b) {
- line1 = find_line_num(ctx, b, pos, &col_num) - line_num + 1;
- } else if (op == OP_line_num) {
- /* XXX: no longer works */
- line1 = get_u32(tab + pos + 1) - line_num + 1;
- }
- if (line1 > line) {
- if (!in_source)
- printf("\n");
- in_source = 1;
- print_lines(source, line, line1);
- line = line1;
- //bits[pos] |= 2;
- }
- }
- if (in_source)
- printf("\n");
- in_source = 0;
- if (op >= OP_COUNT) {
- printf("invalid opcode (0x%02x)\n", op);
- pos++;
- continue;
- }
- if (use_short_opcodes)
- oi = &short_opcode_info(op);
- else
- oi = &opcode_info[op];
- size = oi->size;
- if (pos + size > len) {
- printf("truncated opcode (0x%02x)\n", op);
- break;
- }
- #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 16)
- {
- int i, x, x0;
- x = x0 = printf("%5d ", pos);
- for (i = 0; i < size; i++) {
- if (i == 6) {
- printf("\n%*s", x = x0, "");
- }
- x += printf(" %02X", tab[pos + i]);
- }
- printf("%*s", x0 + 20 - x, "");
- }
- #endif
- #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 32)
- dump_pc = TRUE;
- #else
- dump_pc = bits[pos];
- #endif
- if (dump_pc) {
- printf("%5d: ", pos);
- } else {
- printf(" ");
- }
- printf("%s", oi->name);
- pos++;
- switch(oi->fmt) {
- case OP_FMT_none_int:
- printf(" %d", op - OP_push_0);
- break;
- case OP_FMT_npopx:
- printf(" %d", op - OP_call0);
- break;
- case OP_FMT_u8:
- printf(" %u", get_u8(tab + pos));
- break;
- case OP_FMT_i8:
- printf(" %d", get_i8(tab + pos));
- break;
- case OP_FMT_u16:
- case OP_FMT_npop:
- printf(" %u", get_u16(tab + pos));
- break;
- case OP_FMT_npop_u16:
- printf(" %u,%u", get_u16(tab + pos), get_u16(tab + pos + 2));
- break;
- case OP_FMT_i16:
- printf(" %d", get_i16(tab + pos));
- break;
- case OP_FMT_i32:
- printf(" %d", get_i32(tab + pos));
- break;
- case OP_FMT_u32:
- printf(" %u", get_u32(tab + pos));
- break;
- #if SHORT_OPCODES
- case OP_FMT_label8:
- addr = get_i8(tab + pos);
- goto has_addr1;
- case OP_FMT_label16:
- addr = get_i16(tab + pos);
- goto has_addr1;
- #endif
- case OP_FMT_label:
- addr = get_u32(tab + pos);
- goto has_addr1;
- has_addr1:
- if (pass == 1)
- printf(" %u:%u", addr, label_slots[addr].pos);
- if (pass == 2)
- printf(" %u:%u", addr, label_slots[addr].pos2);
- if (pass == 3)
- printf(" %u", addr + pos);
- break;
- case OP_FMT_label_u16:
- addr = get_u32(tab + pos);
- if (pass == 1)
- printf(" %u:%u", addr, label_slots[addr].pos);
- if (pass == 2)
- printf(" %u:%u", addr, label_slots[addr].pos2);
- if (pass == 3)
- printf(" %u", addr + pos);
- printf(",%u", get_u16(tab + pos + 4));
- break;
- #if SHORT_OPCODES
- case OP_FMT_const8:
- idx = get_u8(tab + pos);
- goto has_pool_idx;
- #endif
- case OP_FMT_const:
- idx = get_u32(tab + pos);
- goto has_pool_idx;
- has_pool_idx:
- printf(" %u: ", idx);
- if (idx < cpool_count) {
- JS_PrintValue(ctx, js_dump_value_write, stdout, cpool[idx], NULL);
- }
- break;
- case OP_FMT_atom:
- printf(" ");
- print_atom(ctx, get_u32(tab + pos));
- break;
- case OP_FMT_atom_u8:
- printf(" ");
- print_atom(ctx, get_u32(tab + pos));
- printf(",%d", get_u8(tab + pos + 4));
- break;
- case OP_FMT_atom_u16:
- printf(" ");
- print_atom(ctx, get_u32(tab + pos));
- printf(",%d", get_u16(tab + pos + 4));
- break;
- case OP_FMT_atom_label_u8:
- case OP_FMT_atom_label_u16:
- printf(" ");
- print_atom(ctx, get_u32(tab + pos));
- addr = get_u32(tab + pos + 4);
- if (pass == 1)
- printf(",%u:%u", addr, label_slots[addr].pos);
- if (pass == 2)
- printf(",%u:%u", addr, label_slots[addr].pos2);
- if (pass == 3)
- printf(",%u", addr + pos + 4);
- if (oi->fmt == OP_FMT_atom_label_u8)
- printf(",%u", get_u8(tab + pos + 8));
- else
- printf(",%u", get_u16(tab + pos + 8));
- break;
- case OP_FMT_none_loc:
- idx = (op - OP_get_loc0) % 4;
- goto has_loc;
- case OP_FMT_loc8:
- idx = get_u8(tab + pos);
- goto has_loc;
- case OP_FMT_loc:
- idx = get_u16(tab + pos);
- has_loc:
- printf(" %d: ", idx);
- if (idx < var_count) {
- print_atom(ctx, vars ? vars[idx].var_name : vardefs[arg_count + idx].var_name);
- }
- break;
- case OP_FMT_none_arg:
- idx = (op - OP_get_arg0) % 4;
- goto has_arg;
- case OP_FMT_arg:
- idx = get_u16(tab + pos);
- has_arg:
- printf(" %d: ", idx);
- if (idx < arg_count) {
- print_atom(ctx, args ? args[idx].var_name : vardefs[idx].var_name);
- }
- break;
- case OP_FMT_none_var_ref:
- idx = (op - OP_get_var_ref0) % 4;
- goto has_var_ref;
- case OP_FMT_var_ref:
- idx = get_u16(tab + pos);
- has_var_ref:
- printf(" %d: ", idx);
- if (idx < closure_var_count) {
- print_atom(ctx, closure_var[idx].var_name);
- }
- break;
- default:
- break;
- }
- printf("\n");
- pos += oi->size - 1;
- }
- if (source) {
- if (!in_source)
- printf("\n");
- print_lines(source, line, INT32_MAX);
- }
- js_free(ctx, bits);
- }
- static __maybe_unused void dump_pc2line(JSContext *ctx, const uint8_t *buf, int len)
- {
- const uint8_t *p_end, *p;
- int pc, v, line_num, col_num, ret;
- unsigned int op;
- uint32_t val;
-
- if (len <= 0)
- return;
- printf("%5s %5s %5s\n", "PC", "LINE", "COL");
- p = buf;
- p_end = buf + len;
-
- /* get the function line and column numbers */
- ret = get_leb128(&val, p, p_end);
- if (ret < 0)
- goto fail;
- p += ret;
- line_num = val + 1;
- ret = get_leb128(&val, p, p_end);
- if (ret < 0)
- goto fail;
- p += ret;
- col_num = val + 1;
- printf("%5s %5d %5d\n", "-", line_num, col_num);
-
- pc = 0;
- while (p < p_end) {
- op = *p++;
- if (op == 0) {
- ret = get_leb128(&val, p, p_end);
- if (ret < 0)
- goto fail;
- pc += val;
- p += ret;
- ret = get_sleb128(&v, p, p_end);
- if (ret < 0)
- goto fail;
- p += ret;
- line_num += v;
- } else {
- op -= PC2LINE_OP_FIRST;
- pc += (op / PC2LINE_RANGE);
- line_num += (op % PC2LINE_RANGE) + PC2LINE_BASE;
- }
- ret = get_sleb128(&v, p, p_end);
- if (ret < 0)
- goto fail;
- p += ret;
- col_num += v;
-
- printf("%5d %5d %5d\n", pc, line_num, col_num);
- }
- fail: ;
- }
- static __maybe_unused void js_dump_function_bytecode(JSContext *ctx, JSFunctionBytecode *b)
- {
- int i;
- char atom_buf[ATOM_GET_STR_BUF_SIZE];
- const char *str;
- if (b->has_debug && b->debug.filename != JS_ATOM_NULL) {
- int line_num, col_num;
- str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->debug.filename);
- line_num = find_line_num(ctx, b, -1, &col_num);
- printf("%s:%d:%d: ", str, line_num, col_num);
- }
- str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->func_name);
- printf("function: %s%s\n", &"*"[b->func_kind != JS_FUNC_GENERATOR], str);
- if (b->js_mode) {
- printf(" mode:");
- if (b->js_mode & JS_MODE_STRICT)
- printf(" strict");
- printf("\n");
- }
- if (b->arg_count && b->vardefs) {
- printf(" args:");
- for(i = 0; i < b->arg_count; i++) {
- printf(" %s", JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf),
- b->vardefs[i].var_name));
- }
- printf("\n");
- }
- if (b->var_count && b->vardefs) {
- printf(" locals:\n");
- for(i = 0; i < b->var_count; i++) {
- JSBytecodeVarDef *vd = &b->vardefs[b->arg_count + i];
- printf("%5d: %s %s", i,
- vd->var_kind == JS_VAR_CATCH ? "catch" :
- (vd->var_kind == JS_VAR_FUNCTION_DECL ||
- vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) ? "function" :
- vd->is_const ? "const" :
- vd->is_lexical ? "let" : "var",
- JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), vd->var_name));
- if (vd->has_scope)
- printf(" [next:%d]", vd->scope_next);
- printf("\n");
- }
- }
- if (b->closure_var_count) {
- printf(" closure vars:\n");
- for(i = 0; i < b->closure_var_count; i++) {
- JSClosureVar *cv = &b->closure_var[i];
- printf("%5d: %s %s", i,
- cv->is_const ? "const" :
- cv->is_lexical ? "let" : "var",
- JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), cv->var_name));
- switch(cv->closure_type) {
- case JS_CLOSURE_LOCAL:
- printf(" [loc%d]\n", cv->var_idx);
- break;
- case JS_CLOSURE_ARG:
- printf(" [arg%d]\n", cv->var_idx);
- break;
- case JS_CLOSURE_REF:
- printf(" [ref%d]\n", cv->var_idx);
- break;
- case JS_CLOSURE_GLOBAL_REF:
- printf(" [global_ref%d]\n", cv->var_idx);
- break;
- case JS_CLOSURE_GLOBAL_DECL:
- printf(" [global_decl]\n");
- break;
- case JS_CLOSURE_GLOBAL:
- printf(" [global]\n");
- break;
- case JS_CLOSURE_MODULE_DECL:
- printf(" [module_decl]\n");
- break;
- case JS_CLOSURE_MODULE_IMPORT:
- printf(" [module_import]\n");
- break;
- default:
- printf(" [?]\n");
- break;
- }
- }
- }
- printf(" stack_size: %d\n", b->stack_size);
- printf(" var_ref_count: %d\n", b->var_ref_count);
- printf(" opcodes:\n");
- dump_byte_code(ctx, 3, b->byte_code_buf, b->byte_code_len,
- b->vardefs,
- NULL, b->arg_count,
- NULL, b->var_count,
- b->closure_var, b->closure_var_count,
- b->cpool, b->cpool_count,
- b->has_debug ? b->debug.source : NULL,
- NULL, b);
- #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 32)
- if (b->has_debug)
- dump_pc2line(ctx, b->debug.pc2line_buf, b->debug.pc2line_len);
- #endif
- printf("\n");
- }
- #endif
- static int add_closure_var(JSContext *ctx, JSFunctionDef *s,
- JSClosureTypeEnum closure_type,
- int var_idx, JSAtom var_name,
- BOOL is_const, BOOL is_lexical,
- JSVarKindEnum var_kind)
- {
- JSClosureVar *cv;
- /* the closure variable indexes are currently stored on 16 bits */
- if (s->closure_var_count >= JS_MAX_LOCAL_VARS) {
- JS_ThrowInternalError(ctx, "too many closure variables");
- return -1;
- }
- if (js_resize_array(ctx, (void **)&s->closure_var,
- sizeof(s->closure_var[0]),
- &s->closure_var_size, s->closure_var_count + 1))
- return -1;
- cv = &s->closure_var[s->closure_var_count++];
- cv->closure_type = closure_type;
- cv->is_const = is_const;
- cv->is_lexical = is_lexical;
- cv->var_kind = var_kind;
- cv->var_idx = var_idx;
- cv->var_name = JS_DupAtom(ctx, var_name);
- return s->closure_var_count - 1;
- }
- static int find_closure_var(JSContext *ctx, JSFunctionDef *s,
- JSAtom var_name)
- {
- int i;
- for(i = 0; i < s->closure_var_count; i++) {
- JSClosureVar *cv = &s->closure_var[i];
- if (cv->var_name == var_name)
- return i;
- }
- return -1;
- }
- /* 'fd' must be a parent of 's'. Create in 's' a closure referencing
- another one in 'fd' */
- static int get_closure_var(JSContext *ctx, JSFunctionDef *s,
- JSFunctionDef *fd, JSClosureTypeEnum closure_type,
- int var_idx, JSAtom var_name,
- BOOL is_const, BOOL is_lexical,
- JSVarKindEnum var_kind)
- {
- int i;
- if (fd != s->parent) {
- var_idx = get_closure_var(ctx, s->parent, fd, closure_type,
- var_idx, var_name,
- is_const, is_lexical, var_kind);
- if (var_idx < 0)
- return -1;
- if (closure_type != JS_CLOSURE_GLOBAL_REF)
- closure_type = JS_CLOSURE_REF;
- }
- for(i = 0; i < s->closure_var_count; i++) {
- JSClosureVar *cv = &s->closure_var[i];
- if (cv->var_idx == var_idx && cv->closure_type == closure_type)
- return i;
- }
- return add_closure_var(ctx, s, closure_type, var_idx, var_name,
- is_const, is_lexical, var_kind);
- }
- static int get_with_scope_opcode(int op)
- {
- if (op == OP_scope_get_var_undef)
- return OP_with_get_var;
- else
- return OP_with_get_var + (op - OP_scope_get_var);
- }
- static BOOL can_opt_put_ref_value(const uint8_t *bc_buf, int pos)
- {
- int opcode = bc_buf[pos];
- return (bc_buf[pos + 1] == OP_put_ref_value &&
- (opcode == OP_insert3 ||
- opcode == OP_perm4 ||
- opcode == OP_nop ||
- opcode == OP_rot3l));
- }
- static BOOL can_opt_put_global_ref_value(const uint8_t *bc_buf, int pos)
- {
- int opcode = bc_buf[pos];
- return (bc_buf[pos + 1] == OP_put_ref_value &&
- (opcode == OP_insert3 ||
- opcode == OP_perm4 ||
- opcode == OP_nop ||
- opcode == OP_rot3l));
- }
- static int optimize_scope_make_ref(JSContext *ctx, JSFunctionDef *s,
- DynBuf *bc, uint8_t *bc_buf,
- LabelSlot *ls, int pos_next,
- int get_op, int var_idx)
- {
- int label_pos, end_pos, pos;
- /* XXX: should optimize `loc(a) += expr` as `expr add_loc(a)`
- but only if expr does not modify `a`.
- should scan the code between pos_next and label_pos
- for operations that can potentially change `a`:
- OP_scope_make_ref(a), function calls, jumps and gosub.
- */
- /* replace the reference get/put with normal variable
- accesses */
- if (bc_buf[pos_next] == OP_get_ref_value) {
- dbuf_putc(bc, get_op);
- dbuf_put_u16(bc, var_idx);
- pos_next++;
- }
- /* remove the OP_label to make room for replacement */
- /* label should have a refcount of 0 anyway */
- /* XXX: should avoid this patch by inserting nops in phase 1 */
- label_pos = ls->pos;
- pos = label_pos - 5;
- assert(bc_buf[pos] == OP_label);
- /* label points to an instruction pair:
- - insert3 / put_ref_value
- - perm4 / put_ref_value
- - rot3l / put_ref_value
- - nop / put_ref_value
- */
- end_pos = label_pos + 2;
- if (bc_buf[label_pos] == OP_insert3)
- bc_buf[pos++] = OP_dup;
- bc_buf[pos] = get_op + 1;
- put_u16(bc_buf + pos + 1, var_idx);
- pos += 3;
- /* pad with OP_nop */
- while (pos < end_pos)
- bc_buf[pos++] = OP_nop;
- return pos_next;
- }
- static int add_var_this(JSContext *ctx, JSFunctionDef *fd)
- {
- int idx;
- idx = add_var(ctx, fd, JS_ATOM_this);
- if (idx >= 0 && fd->is_derived_class_constructor) {
- JSVarDef *vd = &fd->vars[idx];
- /* XXX: should have is_this flag or var type */
- vd->is_lexical = 1; /* used to trigger 'uninitialized' checks
- in a derived class constructor */
- }
- return idx;
- }
- static int resolve_pseudo_var(JSContext *ctx, JSFunctionDef *s,
- JSAtom var_name)
- {
- int var_idx;
- if (!s->has_this_binding)
- return -1;
- switch(var_name) {
- case JS_ATOM_home_object:
- /* 'home_object' pseudo variable */
- if (s->home_object_var_idx < 0)
- s->home_object_var_idx = add_var(ctx, s, var_name);
- var_idx = s->home_object_var_idx;
- break;
- case JS_ATOM_this_active_func:
- /* 'this.active_func' pseudo variable */
- if (s->this_active_func_var_idx < 0)
- s->this_active_func_var_idx = add_var(ctx, s, var_name);
- var_idx = s->this_active_func_var_idx;
- break;
- case JS_ATOM_new_target:
- /* 'new.target' pseudo variable */
- if (s->new_target_var_idx < 0)
- s->new_target_var_idx = add_var(ctx, s, var_name);
- var_idx = s->new_target_var_idx;
- break;
- case JS_ATOM_this:
- /* 'this' pseudo variable */
- if (s->this_var_idx < 0)
- s->this_var_idx = add_var_this(ctx, s);
- var_idx = s->this_var_idx;
- break;
- default:
- var_idx = -1;
- break;
- }
- return var_idx;
- }
- /* test if 'var_name' is in the variable object on the stack. If is it
- the case, handle it and jump to 'label_done' */
- static void var_object_test(JSContext *ctx, JSFunctionDef *s,
- JSAtom var_name, int op, DynBuf *bc,
- int *plabel_done, BOOL is_with)
- {
- dbuf_putc(bc, get_with_scope_opcode(op));
- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
- if (*plabel_done < 0) {
- *plabel_done = new_label_fd(s);
- if (*plabel_done < 0) {
- dbuf_set_error(bc);
- return;
- }
- }
- dbuf_put_u32(bc, *plabel_done);
- dbuf_putc(bc, is_with);
- update_label(s, *plabel_done, 1);
- s->jump_size++;
- }
- static inline void capture_var(JSFunctionDef *s, JSVarDef *vd)
- {
- if (!vd->is_captured) {
- vd->is_captured = 1;
- vd->var_ref_idx = s->var_ref_count++;
- }
- }
- /* return the position of the next opcode or -1 if error */
- static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s,
- JSAtom var_name, int scope_level, int op,
- DynBuf *bc, uint8_t *bc_buf,
- LabelSlot *ls, int pos_next)
- {
- int idx, var_idx, is_put;
- int label_done;
- JSFunctionDef *fd;
- JSVarDef *vd;
- BOOL is_pseudo_var, is_arg_scope;
- label_done = -1;
- /* XXX: could be simpler to use a specific function to
- resolve the pseudo variables */
- is_pseudo_var = (var_name == JS_ATOM_home_object ||
- var_name == JS_ATOM_this_active_func ||
- var_name == JS_ATOM_new_target ||
- var_name == JS_ATOM_this);
- /* resolve local scoped variables */
- var_idx = -1;
- for (idx = s->scopes[scope_level].first; idx >= 0;) {
- vd = &s->vars[idx];
- if (vd->var_name == var_name) {
- if (op == OP_scope_put_var || op == OP_scope_make_ref) {
- if (vd->is_const) {
- dbuf_putc(bc, OP_throw_error);
- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
- dbuf_putc(bc, JS_THROW_VAR_RO);
- goto done;
- }
- }
- var_idx = idx;
- break;
- } else
- if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) {
- dbuf_putc(bc, OP_get_loc);
- dbuf_put_u16(bc, idx);
- var_object_test(ctx, s, var_name, op, bc, &label_done, 1);
- }
- idx = vd->scope_next;
- }
- is_arg_scope = (idx == ARG_SCOPE_END);
- if (var_idx < 0) {
- /* argument scope: variables are not visible but pseudo
- variables are visible */
- if (!is_arg_scope) {
- var_idx = find_var(ctx, s, var_name);
- }
- if (var_idx < 0 && is_pseudo_var)
- var_idx = resolve_pseudo_var(ctx, s, var_name);
- if (var_idx < 0 && var_name == JS_ATOM_arguments &&
- s->has_arguments_binding) {
- /* 'arguments' pseudo variable */
- var_idx = add_arguments_var(ctx, s);
- }
- if (var_idx < 0 && s->is_func_expr && var_name == s->func_name) {
- /* add a new variable with the function name */
- var_idx = add_func_var(ctx, s, var_name);
- }
- }
- if (var_idx >= 0) {
- if ((op == OP_scope_put_var || op == OP_scope_make_ref) &&
- !(var_idx & ARGUMENT_VAR_OFFSET) &&
- s->vars[var_idx].is_const) {
- /* only happens when assigning a function expression name
- in strict mode */
- dbuf_putc(bc, OP_throw_error);
- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
- dbuf_putc(bc, JS_THROW_VAR_RO);
- goto done;
- }
- /* OP_scope_put_var_init is only used to initialize a
- lexical variable, so it is never used in a with or var object. It
- can be used with a closure (module global variable case). */
- switch (op) {
- case OP_scope_make_ref:
- if (!(var_idx & ARGUMENT_VAR_OFFSET) &&
- s->vars[var_idx].var_kind == JS_VAR_FUNCTION_NAME) {
- /* Create a dummy object reference for the func_var */
- dbuf_putc(bc, OP_object);
- dbuf_putc(bc, OP_get_loc);
- dbuf_put_u16(bc, var_idx);
- dbuf_putc(bc, OP_define_field);
- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
- dbuf_putc(bc, OP_push_atom_value);
- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
- } else
- if (label_done == -1 && can_opt_put_ref_value(bc_buf, ls->pos)) {
- int get_op;
- if (var_idx & ARGUMENT_VAR_OFFSET) {
- get_op = OP_get_arg;
- var_idx -= ARGUMENT_VAR_OFFSET;
- } else {
- if (s->vars[var_idx].is_lexical)
- get_op = OP_get_loc_check;
- else
- get_op = OP_get_loc;
- }
- pos_next = optimize_scope_make_ref(ctx, s, bc, bc_buf, ls,
- pos_next, get_op, var_idx);
- } else {
- /* Create a dummy object with a named slot that is
- a reference to the local variable */
- if (var_idx & ARGUMENT_VAR_OFFSET) {
- capture_var(s, &s->args[var_idx - ARGUMENT_VAR_OFFSET]);
- dbuf_putc(bc, OP_make_arg_ref);
- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
- dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET);
- } else {
- capture_var(s, &s->vars[var_idx]);
- dbuf_putc(bc, OP_make_loc_ref);
- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
- dbuf_put_u16(bc, var_idx);
- }
- }
- break;
- case OP_scope_put_var:
- if (!(var_idx & ARGUMENT_VAR_OFFSET) &&
- s->vars[var_idx].var_kind == JS_VAR_FUNCTION_NAME) {
- /* in non strict mode, modifying the function name is ignored */
- dbuf_putc(bc, OP_drop);
- goto done;
- }
- goto local_scope_var;
- case OP_scope_get_ref:
- dbuf_putc(bc, OP_undefined);
- goto local_scope_var;
- case OP_scope_get_var_checkthis:
- case OP_scope_get_var_undef:
- case OP_scope_get_var:
- case OP_scope_put_var_init:
- local_scope_var:
- is_put = (op == OP_scope_put_var || op == OP_scope_put_var_init);
- if (var_idx & ARGUMENT_VAR_OFFSET) {
- dbuf_putc(bc, OP_get_arg + is_put);
- dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET);
- } else {
- if (is_put) {
- if (s->vars[var_idx].is_lexical) {
- if (op == OP_scope_put_var_init) {
- /* 'this' can only be initialized once */
- if (var_name == JS_ATOM_this)
- dbuf_putc(bc, OP_put_loc_check_init);
- else
- dbuf_putc(bc, OP_put_loc);
- } else {
- dbuf_putc(bc, OP_put_loc_check);
- }
- } else {
- dbuf_putc(bc, OP_put_loc);
- }
- } else {
- if (s->vars[var_idx].is_lexical) {
- if (op == OP_scope_get_var_checkthis) {
- /* only used for 'this' return in derived class constructors */
- dbuf_putc(bc, OP_get_loc_checkthis);
- } else {
- dbuf_putc(bc, OP_get_loc_check);
- }
- } else {
- dbuf_putc(bc, OP_get_loc);
- }
- }
- dbuf_put_u16(bc, var_idx);
- }
- break;
- case OP_scope_delete_var:
- dbuf_putc(bc, OP_push_false);
- break;
- }
- goto done;
- }
- /* check eval object */
- if (!is_arg_scope && s->var_object_idx >= 0 && !is_pseudo_var) {
- dbuf_putc(bc, OP_get_loc);
- dbuf_put_u16(bc, s->var_object_idx);
- var_object_test(ctx, s, var_name, op, bc, &label_done, 0);
- }
- /* check eval object in argument scope */
- if (s->arg_var_object_idx >= 0 && !is_pseudo_var) {
- dbuf_putc(bc, OP_get_loc);
- dbuf_put_u16(bc, s->arg_var_object_idx);
- var_object_test(ctx, s, var_name, op, bc, &label_done, 0);
- }
- /* check parent scopes */
- for (fd = s; fd->parent;) {
- scope_level = fd->parent_scope_level;
- fd = fd->parent;
- for (idx = fd->scopes[scope_level].first; idx >= 0;) {
- vd = &fd->vars[idx];
- if (vd->var_name == var_name) {
- if (op == OP_scope_put_var || op == OP_scope_make_ref) {
- if (vd->is_const) {
- dbuf_putc(bc, OP_throw_error);
- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
- dbuf_putc(bc, JS_THROW_VAR_RO);
- goto done;
- }
- }
- var_idx = idx;
- break;
- } else if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) {
- capture_var(fd, vd);
- idx = get_closure_var(ctx, s, fd, JS_CLOSURE_LOCAL, idx, vd->var_name, FALSE, FALSE, JS_VAR_NORMAL);
- if (idx >= 0) {
- dbuf_putc(bc, OP_get_var_ref);
- dbuf_put_u16(bc, idx);
- var_object_test(ctx, s, var_name, op, bc, &label_done, 1);
- }
- }
- idx = vd->scope_next;
- }
- is_arg_scope = (idx == ARG_SCOPE_END);
- if (var_idx >= 0)
- break;
- if (!is_arg_scope) {
- var_idx = find_var(ctx, fd, var_name);
- if (var_idx >= 0)
- break;
- }
- if (is_pseudo_var) {
- var_idx = resolve_pseudo_var(ctx, fd, var_name);
- if (var_idx >= 0)
- break;
- }
- if (var_name == JS_ATOM_arguments && fd->has_arguments_binding) {
- var_idx = add_arguments_var(ctx, fd);
- break;
- }
- if (fd->is_func_expr && fd->func_name == var_name) {
- /* add a new variable with the function name */
- var_idx = add_func_var(ctx, fd, var_name);
- break;
- }
- /* check eval object */
- if (!is_arg_scope && fd->var_object_idx >= 0 && !is_pseudo_var) {
- vd = &fd->vars[fd->var_object_idx];
- capture_var(fd, vd);
- idx = get_closure_var(ctx, s, fd, JS_CLOSURE_LOCAL,
- fd->var_object_idx, vd->var_name,
- FALSE, FALSE, JS_VAR_NORMAL);
- dbuf_putc(bc, OP_get_var_ref);
- dbuf_put_u16(bc, idx);
- var_object_test(ctx, s, var_name, op, bc, &label_done, 0);
- }
- /* check eval object in argument scope */
- if (fd->arg_var_object_idx >= 0 && !is_pseudo_var) {
- vd = &fd->vars[fd->arg_var_object_idx];
- capture_var(fd, vd);
- idx = get_closure_var(ctx, s, fd, JS_CLOSURE_LOCAL,
- fd->arg_var_object_idx, vd->var_name,
- FALSE, FALSE, JS_VAR_NORMAL);
- dbuf_putc(bc, OP_get_var_ref);
- dbuf_put_u16(bc, idx);
- var_object_test(ctx, s, var_name, op, bc, &label_done, 0);
- }
- if (fd->is_eval)
- break; /* it it necessarily the top level function */
- }
- /* check direct eval scope (in the closure of the eval function
- which is necessarily at the top level) */
- if (!fd)
- fd = s;
- if (var_idx < 0 && fd->is_eval) {
- int idx1;
- for (idx1 = 0; idx1 < fd->closure_var_count; idx1++) {
- JSClosureVar *cv = &fd->closure_var[idx1];
- if (var_name == cv->var_name) {
- if (fd != s) {
- JSClosureTypeEnum closure_type;
- if (cv->closure_type == JS_CLOSURE_GLOBAL ||
- cv->closure_type == JS_CLOSURE_GLOBAL_DECL ||
- cv->closure_type == JS_CLOSURE_GLOBAL_REF)
- closure_type = JS_CLOSURE_GLOBAL_REF;
- else
- closure_type = JS_CLOSURE_REF;
- idx = get_closure_var(ctx, s, fd,
- closure_type,
- idx1,
- cv->var_name, cv->is_const,
- cv->is_lexical, cv->var_kind);
- } else {
- idx = idx1;
- }
- if (cv->closure_type == JS_CLOSURE_GLOBAL ||
- cv->closure_type == JS_CLOSURE_GLOBAL_DECL ||
- cv->closure_type == JS_CLOSURE_GLOBAL_REF)
- goto has_global_idx;
- else
- goto has_idx;
- } else if ((cv->var_name == JS_ATOM__var_ ||
- cv->var_name == JS_ATOM__arg_var_ ||
- cv->var_name == JS_ATOM__with_) && !is_pseudo_var) {
- int is_with = (cv->var_name == JS_ATOM__with_);
- if (fd != s) {
- idx = get_closure_var(ctx, s, fd,
- JS_CLOSURE_REF,
- idx1,
- cv->var_name, FALSE, FALSE,
- JS_VAR_NORMAL);
- } else {
- idx = idx1;
- }
- dbuf_putc(bc, OP_get_var_ref);
- dbuf_put_u16(bc, idx);
- var_object_test(ctx, s, var_name, op, bc, &label_done, is_with);
- }
- }
- /* not found: add a closure for a global variable access */
- idx1 = add_closure_var(ctx, fd, JS_CLOSURE_GLOBAL, 0, var_name,
- FALSE, FALSE, JS_VAR_NORMAL);
- if (idx1 < 0)
- return -1;
- if (fd != s) {
- idx = get_closure_var(ctx, s, fd,
- JS_CLOSURE_GLOBAL_REF,
- idx1,
- var_name, FALSE, FALSE,
- JS_VAR_NORMAL);
- } else {
- idx = idx1;
- }
- has_global_idx:
- /* global variable access */
- switch (op) {
- case OP_scope_make_ref:
- if (label_done == -1 && can_opt_put_global_ref_value(bc_buf, ls->pos)) {
- pos_next = optimize_scope_make_ref(ctx, s, bc, bc_buf, ls,
- pos_next,
- OP_get_var, idx);
- } else {
- dbuf_putc(bc, OP_make_var_ref);
- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
- }
- break;
- case OP_scope_get_ref:
- /* XXX: should create a dummy object with a named slot that is
- a reference to the global variable */
- dbuf_putc(bc, OP_undefined);
- dbuf_putc(bc, OP_get_var);
- dbuf_put_u16(bc, idx);
- break;
- case OP_scope_get_var_undef:
- case OP_scope_get_var:
- case OP_scope_put_var:
- dbuf_putc(bc, OP_get_var_undef + (op - OP_scope_get_var_undef));
- dbuf_put_u16(bc, idx);
- break;
- case OP_scope_put_var_init:
- dbuf_putc(bc, OP_put_var_init);
- dbuf_put_u16(bc, idx);
- break;
- case OP_scope_delete_var:
- dbuf_putc(bc, OP_delete_var);
- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
- break;
- }
- } else {
- /* find the corresponding closure variable */
- if (var_idx & ARGUMENT_VAR_OFFSET) {
- capture_var(fd, &fd->args[var_idx - ARGUMENT_VAR_OFFSET]);
- idx = get_closure_var(ctx, s, fd,
- JS_CLOSURE_ARG, var_idx - ARGUMENT_VAR_OFFSET,
- var_name, FALSE, FALSE, JS_VAR_NORMAL);
- } else {
- capture_var(fd, &fd->vars[var_idx]);
- idx = get_closure_var(ctx, s, fd,
- JS_CLOSURE_LOCAL, var_idx,
- var_name,
- fd->vars[var_idx].is_const,
- fd->vars[var_idx].is_lexical,
- fd->vars[var_idx].var_kind);
- }
- if (idx >= 0) {
- has_idx:
- if ((op == OP_scope_put_var || op == OP_scope_make_ref) &&
- s->closure_var[idx].is_const) {
- dbuf_putc(bc, OP_throw_error);
- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
- dbuf_putc(bc, JS_THROW_VAR_RO);
- goto done;
- }
- switch (op) {
- case OP_scope_make_ref:
- if (s->closure_var[idx].var_kind == JS_VAR_FUNCTION_NAME) {
- /* Create a dummy object reference for the func_var */
- dbuf_putc(bc, OP_object);
- dbuf_putc(bc, OP_get_var_ref);
- dbuf_put_u16(bc, idx);
- dbuf_putc(bc, OP_define_field);
- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
- dbuf_putc(bc, OP_push_atom_value);
- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
- } else
- if (label_done == -1 &&
- can_opt_put_ref_value(bc_buf, ls->pos)) {
- int get_op;
- if (s->closure_var[idx].is_lexical)
- get_op = OP_get_var_ref_check;
- else
- get_op = OP_get_var_ref;
- pos_next = optimize_scope_make_ref(ctx, s, bc, bc_buf, ls,
- pos_next,
- get_op, idx);
- } else {
- /* Create a dummy object with a named slot that is
- a reference to the closure variable */
- dbuf_putc(bc, OP_make_var_ref_ref);
- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
- dbuf_put_u16(bc, idx);
- }
- break;
- case OP_scope_put_var:
- if (s->closure_var[idx].var_kind == JS_VAR_FUNCTION_NAME) {
- /* in non strict mode, modifying the function name is ignored */
- dbuf_putc(bc, OP_drop);
- goto done;
- }
- goto closure_scope_var;
- case OP_scope_get_ref:
- /* XXX: should create a dummy object with a named slot that is
- a reference to the closure variable */
- dbuf_putc(bc, OP_undefined);
- goto closure_scope_var;
- case OP_scope_get_var_undef:
- case OP_scope_get_var:
- case OP_scope_put_var_init:
- closure_scope_var:
- is_put = (op == OP_scope_put_var ||
- op == OP_scope_put_var_init);
- if (is_put) {
- if (s->closure_var[idx].is_lexical) {
- if (op == OP_scope_put_var_init) {
- /* 'this' can only be initialized once */
- if (var_name == JS_ATOM_this)
- dbuf_putc(bc, OP_put_var_ref_check_init);
- else
- dbuf_putc(bc, OP_put_var_ref);
- } else {
- dbuf_putc(bc, OP_put_var_ref_check);
- }
- } else {
- dbuf_putc(bc, OP_put_var_ref);
- }
- } else {
- if (s->closure_var[idx].is_lexical) {
- dbuf_putc(bc, OP_get_var_ref_check);
- } else {
- dbuf_putc(bc, OP_get_var_ref);
- }
- }
- dbuf_put_u16(bc, idx);
- break;
- case OP_scope_delete_var:
- dbuf_putc(bc, OP_push_false);
- break;
- }
- goto done;
- }
- }
- done:
- if (label_done >= 0) {
- dbuf_putc(bc, OP_label);
- dbuf_put_u32(bc, label_done);
- s->label_slots[label_done].pos2 = bc->size;
- }
- return pos_next;
- }
- /* search in all scopes */
- static int find_private_class_field_all(JSContext *ctx, JSFunctionDef *fd,
- JSAtom name, int scope_level)
- {
- int idx;
- idx = fd->scopes[scope_level].first;
- while (idx >= 0) {
- if (fd->vars[idx].var_name == name)
- return idx;
- idx = fd->vars[idx].scope_next;
- }
- return -1;
- }
- static void get_loc_or_ref(DynBuf *bc, BOOL is_ref, int idx)
- {
- /* if the field is not initialized, the error is catched when
- accessing it */
- if (is_ref)
- dbuf_putc(bc, OP_get_var_ref);
- else
- dbuf_putc(bc, OP_get_loc);
- dbuf_put_u16(bc, idx);
- }
- static int resolve_scope_private_field1(JSContext *ctx,
- BOOL *pis_ref, int *pvar_kind,
- JSFunctionDef *s,
- JSAtom var_name, int scope_level)
- {
- int idx, var_kind;
- JSFunctionDef *fd;
- BOOL is_ref;
- fd = s;
- is_ref = FALSE;
- for(;;) {
- idx = find_private_class_field_all(ctx, fd, var_name, scope_level);
- if (idx >= 0) {
- var_kind = fd->vars[idx].var_kind;
- if (is_ref) {
- capture_var(fd, &fd->vars[idx]);
- idx = get_closure_var(ctx, s, fd, JS_CLOSURE_LOCAL, idx, var_name,
- TRUE, TRUE, JS_VAR_NORMAL);
- if (idx < 0)
- return -1;
- }
- break;
- }
- scope_level = fd->parent_scope_level;
- if (!fd->parent) {
- if (fd->is_eval) {
- /* closure of the eval function (top level) */
- for (idx = 0; idx < fd->closure_var_count; idx++) {
- JSClosureVar *cv = &fd->closure_var[idx];
- if (cv->var_name == var_name) {
- var_kind = cv->var_kind;
- is_ref = TRUE;
- if (fd != s) {
- idx = get_closure_var(ctx, s, fd,
- JS_CLOSURE_REF,
- idx,
- cv->var_name, cv->is_const,
- cv->is_lexical,
- cv->var_kind);
- if (idx < 0)
- return -1;
- }
- goto done;
- }
- }
- }
- /* XXX: no line number info */
- JS_ThrowSyntaxErrorAtom(ctx, "undefined private field '%s'",
- var_name);
- return -1;
- } else {
- fd = fd->parent;
- }
- is_ref = TRUE;
- }
- done:
- *pis_ref = is_ref;
- *pvar_kind = var_kind;
- return idx;
- }
- /* return 0 if OK or -1 if the private field could not be resolved */
- static int resolve_scope_private_field(JSContext *ctx, JSFunctionDef *s,
- JSAtom var_name, int scope_level, int op,
- DynBuf *bc)
- {
- int idx, var_kind;
- BOOL is_ref;
- idx = resolve_scope_private_field1(ctx, &is_ref, &var_kind, s,
- var_name, scope_level);
- if (idx < 0)
- return -1;
- assert(var_kind != JS_VAR_NORMAL);
- switch (op) {
- case OP_scope_get_private_field:
- case OP_scope_get_private_field2:
- switch(var_kind) {
- case JS_VAR_PRIVATE_FIELD:
- if (op == OP_scope_get_private_field2)
- dbuf_putc(bc, OP_dup);
- get_loc_or_ref(bc, is_ref, idx);
- dbuf_putc(bc, OP_get_private_field);
- break;
- case JS_VAR_PRIVATE_METHOD:
- get_loc_or_ref(bc, is_ref, idx);
- dbuf_putc(bc, OP_check_brand);
- if (op != OP_scope_get_private_field2)
- dbuf_putc(bc, OP_nip);
- break;
- case JS_VAR_PRIVATE_GETTER:
- case JS_VAR_PRIVATE_GETTER_SETTER:
- if (op == OP_scope_get_private_field2)
- dbuf_putc(bc, OP_dup);
- get_loc_or_ref(bc, is_ref, idx);
- dbuf_putc(bc, OP_check_brand);
- dbuf_putc(bc, OP_call_method);
- dbuf_put_u16(bc, 0);
- break;
- case JS_VAR_PRIVATE_SETTER:
- /* XXX: add clearer error message */
- dbuf_putc(bc, OP_throw_error);
- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
- dbuf_putc(bc, JS_THROW_VAR_RO);
- break;
- default:
- abort();
- }
- break;
- case OP_scope_put_private_field:
- switch(var_kind) {
- case JS_VAR_PRIVATE_FIELD:
- get_loc_or_ref(bc, is_ref, idx);
- dbuf_putc(bc, OP_put_private_field);
- break;
- case JS_VAR_PRIVATE_METHOD:
- case JS_VAR_PRIVATE_GETTER:
- /* XXX: add clearer error message */
- dbuf_putc(bc, OP_throw_error);
- dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
- dbuf_putc(bc, JS_THROW_VAR_RO);
- break;
- case JS_VAR_PRIVATE_SETTER:
- case JS_VAR_PRIVATE_GETTER_SETTER:
- {
- JSAtom setter_name = get_private_setter_name(ctx, var_name);
- if (setter_name == JS_ATOM_NULL)
- return -1;
- idx = resolve_scope_private_field1(ctx, &is_ref,
- &var_kind, s,
- setter_name, scope_level);
- JS_FreeAtom(ctx, setter_name);
- if (idx < 0)
- return -1;
- assert(var_kind == JS_VAR_PRIVATE_SETTER);
- get_loc_or_ref(bc, is_ref, idx);
- dbuf_putc(bc, OP_swap);
- /* obj func value */
- dbuf_putc(bc, OP_rot3r);
- /* value obj func */
- dbuf_putc(bc, OP_check_brand);
- dbuf_putc(bc, OP_rot3l);
- /* obj func value */
- dbuf_putc(bc, OP_call_method);
- dbuf_put_u16(bc, 1);
- dbuf_putc(bc, OP_drop);
- }
- break;
- default:
- abort();
- }
- break;
- case OP_scope_in_private_field:
- get_loc_or_ref(bc, is_ref, idx);
- dbuf_putc(bc, OP_private_in);
- break;
- default:
- abort();
- }
- return 0;
- }
- static void mark_eval_captured_variables(JSContext *ctx, JSFunctionDef *s,
- int scope_level)
- {
- int idx;
- JSVarDef *vd;
- for (idx = s->scopes[scope_level].first; idx >= 0;) {
- vd = &s->vars[idx];
- capture_var(s, vd);
- idx = vd->scope_next;
- }
- }
- /* XXX: should handle the argument scope generically */
- static BOOL is_var_in_arg_scope(JSAtom var_name, JSVarKindEnum var_kind)
- {
- return (var_name == JS_ATOM_home_object ||
- var_name == JS_ATOM_this_active_func ||
- var_name == JS_ATOM_new_target ||
- var_name == JS_ATOM_this ||
- var_name == JS_ATOM__arg_var_ ||
- var_kind == JS_VAR_FUNCTION_NAME);
- }
- static void add_eval_variables(JSContext *ctx, JSFunctionDef *s)
- {
- JSFunctionDef *fd;
- JSVarDef *vd;
- int i, scope_level, scope_idx;
- BOOL has_arguments_binding, has_this_binding, is_arg_scope;
- /* in non strict mode, variables are created in the caller's
- environment object */
- if (!s->is_eval && !(s->js_mode & JS_MODE_STRICT)) {
- s->var_object_idx = add_var(ctx, s, JS_ATOM__var_);
- if (s->has_parameter_expressions) {
- /* an additional variable object is needed for the
- argument scope */
- s->arg_var_object_idx = add_var(ctx, s, JS_ATOM__arg_var_);
- }
- }
- /* eval can potentially use 'arguments' so we must define it */
- has_this_binding = s->has_this_binding;
- if (has_this_binding) {
- if (s->this_var_idx < 0)
- s->this_var_idx = add_var_this(ctx, s);
- if (s->new_target_var_idx < 0)
- s->new_target_var_idx = add_var(ctx, s, JS_ATOM_new_target);
- if (s->is_derived_class_constructor && s->this_active_func_var_idx < 0)
- s->this_active_func_var_idx = add_var(ctx, s, JS_ATOM_this_active_func);
- if (s->has_home_object && s->home_object_var_idx < 0)
- s->home_object_var_idx = add_var(ctx, s, JS_ATOM_home_object);
- }
- has_arguments_binding = s->has_arguments_binding;
- if (has_arguments_binding) {
- add_arguments_var(ctx, s);
- /* also add an arguments binding in the argument scope to
- raise an error if a direct eval in the argument scope tries
- to redefine it */
- if (s->has_parameter_expressions && !(s->js_mode & JS_MODE_STRICT))
- add_arguments_arg(ctx, s);
- }
- if (s->is_func_expr && s->func_name != JS_ATOM_NULL)
- add_func_var(ctx, s, s->func_name);
- for(i = 0; i < s->arg_count; i++) {
- vd = &s->args[i];
- capture_var(s, vd);
- }
- for(i = 0; i < s->var_count; i++) {
- vd = &s->vars[i];
- /* do not close top level last result */
- if (vd->scope_level == 0 &&
- vd->var_name != JS_ATOM__ret_ &&
- vd->var_name != JS_ATOM_NULL) {
- capture_var(s, vd);
- }
- }
-
- /* eval can use all the variables of the enclosing functions, so
- they must be all put in the closure. The closure variables are
- ordered by scope. It works only because no closure are created
- before. */
- assert(s->is_eval || s->closure_var_count == 0);
- /* XXX: inefficient, but eval performance is less critical */
- fd = s;
- for(;;) {
- scope_level = fd->parent_scope_level;
- fd = fd->parent;
- if (!fd)
- break;
- /* add 'this' if it was not previously added */
- if (!has_this_binding && fd->has_this_binding) {
- if (fd->this_var_idx < 0)
- fd->this_var_idx = add_var_this(ctx, fd);
- if (fd->new_target_var_idx < 0)
- fd->new_target_var_idx = add_var(ctx, fd, JS_ATOM_new_target);
- if (fd->is_derived_class_constructor && fd->this_active_func_var_idx < 0)
- fd->this_active_func_var_idx = add_var(ctx, fd, JS_ATOM_this_active_func);
- if (fd->has_home_object && fd->home_object_var_idx < 0)
- fd->home_object_var_idx = add_var(ctx, fd, JS_ATOM_home_object);
- has_this_binding = TRUE;
- }
- /* add 'arguments' if it was not previously added */
- if (!has_arguments_binding && fd->has_arguments_binding) {
- add_arguments_var(ctx, fd);
- has_arguments_binding = TRUE;
- }
- /* add function name */
- if (fd->is_func_expr && fd->func_name != JS_ATOM_NULL)
- add_func_var(ctx, fd, fd->func_name);
- /* add lexical variables */
- scope_idx = fd->scopes[scope_level].first;
- while (scope_idx >= 0) {
- vd = &fd->vars[scope_idx];
- capture_var(fd, vd);
- get_closure_var(ctx, s, fd, JS_CLOSURE_LOCAL, scope_idx,
- vd->var_name, vd->is_const, vd->is_lexical, vd->var_kind);
- scope_idx = vd->scope_next;
- }
- is_arg_scope = (scope_idx == ARG_SCOPE_END);
- if (!is_arg_scope) {
- /* add unscoped variables */
- /* XXX: propagate is_const and var_kind too ? */
- for(i = 0; i < fd->arg_count; i++) {
- vd = &fd->args[i];
- if (vd->var_name != JS_ATOM_NULL) {
- capture_var(fd, vd);
- get_closure_var(ctx, s, fd,
- JS_CLOSURE_ARG, i, vd->var_name, FALSE,
- vd->is_lexical, JS_VAR_NORMAL);
- }
- }
- for(i = 0; i < fd->var_count; i++) {
- vd = &fd->vars[i];
- /* do not close top level last result */
- if (vd->scope_level == 0 &&
- vd->var_name != JS_ATOM__ret_ &&
- vd->var_name != JS_ATOM_NULL) {
- capture_var(fd, vd);
- get_closure_var(ctx, s, fd,
- JS_CLOSURE_LOCAL, i, vd->var_name, FALSE,
- vd->is_lexical, JS_VAR_NORMAL);
- }
- }
- } else {
- for(i = 0; i < fd->var_count; i++) {
- vd = &fd->vars[i];
- /* do not close top level last result */
- if (vd->scope_level == 0 && is_var_in_arg_scope(vd->var_name, vd->var_kind)) {
- capture_var(fd, vd);
- get_closure_var(ctx, s, fd,
- JS_CLOSURE_LOCAL, i, vd->var_name, FALSE,
- vd->is_lexical, JS_VAR_NORMAL);
- }
- }
- }
- if (fd->is_eval) {
- int idx;
- /* add direct eval variables (we are necessarily at the
- top level). */
- for (idx = 0; idx < fd->closure_var_count; idx++) {
- JSClosureVar *cv = &fd->closure_var[idx];
- /* Global variables are removed but module
- definitions are kept. */
- if (cv->closure_type != JS_CLOSURE_GLOBAL_REF &&
- cv->closure_type != JS_CLOSURE_GLOBAL_DECL &&
- cv->closure_type != JS_CLOSURE_GLOBAL) {
- get_closure_var(ctx, s, fd,
- JS_CLOSURE_REF,
- idx, cv->var_name, cv->is_const,
- cv->is_lexical, cv->var_kind);
- }
- }
- }
- }
- }
- static void set_closure_from_var(JSContext *ctx, JSClosureVar *cv,
- JSBytecodeVarDef *vd, int var_idx)
- {
- cv->closure_type = JS_CLOSURE_LOCAL;
- cv->is_const = vd->is_const;
- cv->is_lexical = vd->is_lexical;
- cv->var_kind = vd->var_kind;
- cv->var_idx = var_idx;
- cv->var_name = JS_DupAtom(ctx, vd->var_name);
- }
- /* for direct eval compilation: add references to the variables of the
- calling function */
- static __exception int add_closure_variables(JSContext *ctx, JSFunctionDef *s,
- JSFunctionBytecode *b, int scope_idx)
- {
- int i, count;
- JSBytecodeVarDef *vd;
- BOOL is_arg_scope;
- count = b->arg_count + b->var_count + b->closure_var_count;
- s->closure_var = NULL;
- s->closure_var_count = 0;
- s->closure_var_size = count;
- if (count == 0)
- return 0;
- s->closure_var = js_malloc(ctx, sizeof(s->closure_var[0]) * count);
- if (!s->closure_var)
- return -1;
- /* Add lexical variables in scope at the point of evaluation */
- for (i = scope_idx; i >= 0;) {
- vd = &b->vardefs[b->arg_count + i];
- if (vd->has_scope) {
- JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
- set_closure_from_var(ctx, cv, vd, i);
- }
- i = vd->scope_next;
- }
- is_arg_scope = (i == ARG_SCOPE_END);
- if (!is_arg_scope) {
- /* Add argument variables */
- for(i = 0; i < b->arg_count; i++) {
- JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
- vd = &b->vardefs[i];
- cv->closure_type = JS_CLOSURE_ARG;
- cv->is_const = FALSE;
- cv->is_lexical = FALSE;
- cv->var_kind = JS_VAR_NORMAL;
- cv->var_idx = i;
- cv->var_name = JS_DupAtom(ctx, vd->var_name);
- }
- /* Add local non lexical variables */
- for(i = 0; i < b->var_count; i++) {
- vd = &b->vardefs[b->arg_count + i];
- if (!vd->has_scope && vd->var_name != JS_ATOM__ret_) {
- JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
- set_closure_from_var(ctx, cv, vd, i);
- }
- }
- } else {
- /* only add pseudo variables */
- for(i = 0; i < b->var_count; i++) {
- vd = &b->vardefs[b->arg_count + i];
- if (!vd->has_scope && is_var_in_arg_scope(vd->var_name, vd->var_kind)) {
- JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
- set_closure_from_var(ctx, cv, vd, i);
- }
- }
- }
- for(i = 0; i < b->closure_var_count; i++) {
- JSClosureVar *cv0 = &b->closure_var[i];
- JSClosureVar *cv;
- switch(cv0->closure_type) {
- case JS_CLOSURE_LOCAL:
- case JS_CLOSURE_ARG:
- case JS_CLOSURE_REF:
- case JS_CLOSURE_MODULE_DECL:
- case JS_CLOSURE_MODULE_IMPORT:
- break;
- case JS_CLOSURE_GLOBAL_REF:
- case JS_CLOSURE_GLOBAL_DECL:
- case JS_CLOSURE_GLOBAL:
- continue; /* not necessary to add global variables */
- default:
- abort();
- }
- cv = &s->closure_var[s->closure_var_count++];
- cv->closure_type = JS_CLOSURE_REF;
- cv->is_const = cv0->is_const;
- cv->is_lexical = cv0->is_lexical;
- cv->var_kind = cv0->var_kind;
- cv->var_idx = i;
- cv->var_name = JS_DupAtom(ctx, cv0->var_name);
- }
- return 0;
- }
- typedef struct CodeContext {
- const uint8_t *bc_buf; /* code buffer */
- int bc_len; /* length of the code buffer */
- int pos; /* position past the matched code pattern */
- int line_num; /* last visited OP_line_num parameter or -1 */
- int op;
- int idx;
- int label;
- int val;
- JSAtom atom;
- } CodeContext;
- #define M2(op1, op2) ((op1) | ((op2) << 8))
- #define M3(op1, op2, op3) ((op1) | ((op2) << 8) | ((op3) << 16))
- #define M4(op1, op2, op3, op4) ((op1) | ((op2) << 8) | ((op3) << 16) | ((op4) << 24))
- static BOOL code_match(CodeContext *s, int pos, ...)
- {
- const uint8_t *tab = s->bc_buf;
- int op, len, op1, line_num, pos_next;
- va_list ap;
- BOOL ret = FALSE;
- line_num = -1;
- va_start(ap, pos);
- for(;;) {
- op1 = va_arg(ap, int);
- if (op1 == -1) {
- s->pos = pos;
- s->line_num = line_num;
- ret = TRUE;
- break;
- }
- for (;;) {
- if (pos >= s->bc_len)
- goto done;
- op = tab[pos];
- len = opcode_info[op].size;
- pos_next = pos + len;
- if (pos_next > s->bc_len)
- goto done;
- if (op == OP_line_num) {
- line_num = get_u32(tab + pos + 1);
- pos = pos_next;
- } else {
- break;
- }
- }
- if (op != op1) {
- if (op1 == (uint8_t)op1 || !op)
- break;
- if (op != (uint8_t)op1
- && op != (uint8_t)(op1 >> 8)
- && op != (uint8_t)(op1 >> 16)
- && op != (uint8_t)(op1 >> 24)) {
- break;
- }
- s->op = op;
- }
- pos++;
- switch(opcode_info[op].fmt) {
- case OP_FMT_loc8:
- case OP_FMT_u8:
- {
- int idx = tab[pos];
- int arg = va_arg(ap, int);
- if (arg == -1) {
- s->idx = idx;
- } else {
- if (arg != idx)
- goto done;
- }
- break;
- }
- case OP_FMT_u16:
- case OP_FMT_npop:
- case OP_FMT_loc:
- case OP_FMT_arg:
- case OP_FMT_var_ref:
- {
- int idx = get_u16(tab + pos);
- int arg = va_arg(ap, int);
- if (arg == -1) {
- s->idx = idx;
- } else {
- if (arg != idx)
- goto done;
- }
- break;
- }
- case OP_FMT_i32:
- case OP_FMT_u32:
- case OP_FMT_label:
- case OP_FMT_const:
- {
- s->label = get_u32(tab + pos);
- break;
- }
- case OP_FMT_label_u16:
- {
- s->label = get_u32(tab + pos);
- s->val = get_u16(tab + pos + 4);
- break;
- }
- case OP_FMT_atom:
- {
- s->atom = get_u32(tab + pos);
- break;
- }
- case OP_FMT_atom_u8:
- {
- s->atom = get_u32(tab + pos);
- s->val = get_u8(tab + pos + 4);
- break;
- }
- case OP_FMT_atom_u16:
- {
- s->atom = get_u32(tab + pos);
- s->val = get_u16(tab + pos + 4);
- break;
- }
- case OP_FMT_atom_label_u8:
- {
- s->atom = get_u32(tab + pos);
- s->label = get_u32(tab + pos + 4);
- s->val = get_u8(tab + pos + 8);
- break;
- }
- default:
- break;
- }
- pos = pos_next;
- }
- done:
- va_end(ap);
- return ret;
- }
- static void instantiate_hoisted_definitions(JSContext *ctx, JSFunctionDef *s, DynBuf *bc)
- {
- int i, idx, label_next = -1;
- /* add the hoisted functions in arguments and local variables */
- for(i = 0; i < s->arg_count; i++) {
- JSVarDef *vd = &s->args[i];
- if (vd->func_pool_idx >= 0) {
- dbuf_putc(bc, OP_fclosure);
- dbuf_put_u32(bc, vd->func_pool_idx);
- dbuf_putc(bc, OP_put_arg);
- dbuf_put_u16(bc, i);
- }
- }
- for(i = 0; i < s->var_count; i++) {
- JSVarDef *vd = &s->vars[i];
- if (vd->scope_level == 0 && vd->func_pool_idx >= 0) {
- dbuf_putc(bc, OP_fclosure);
- dbuf_put_u32(bc, vd->func_pool_idx);
- dbuf_putc(bc, OP_put_loc);
- dbuf_put_u16(bc, i);
- }
- }
- /* the module global variables must be initialized before
- evaluating the module so that the exported functions are
- visible if there are cyclic module references */
- if (s->module) {
- label_next = new_label_fd(s);
- if (label_next < 0) {
- dbuf_set_error(bc);
- return;
- }
- /* if 'this' is true, initialize the global variables and return */
- dbuf_putc(bc, OP_push_this);
- dbuf_putc(bc, OP_if_false);
- dbuf_put_u32(bc, label_next);
- update_label(s, label_next, 1);
- s->jump_size++;
- }
- /* add the global variables (only happens if s->is_global_var is
- true) */
- /* XXX: inefficient, add a closure index in JSGlobalVar */
- for(i = 0; i < s->global_var_count; i++) {
- JSGlobalVar *hf = &s->global_vars[i];
- BOOL has_var_obj = FALSE;
- BOOL force_init = hf->force_init;
- /* we are in an eval, so the closure contains all the
- enclosing variables */
- /* If the outer function has a variable environment,
- create a property for the variable there */
- for(idx = 0; idx < s->closure_var_count; idx++) {
- JSClosureVar *cv = &s->closure_var[idx];
- if (cv->var_name == hf->var_name) {
- force_init = FALSE;
- goto closure_found;
- }
- if (cv->var_name == JS_ATOM__var_ ||
- cv->var_name == JS_ATOM__arg_var_) {
- dbuf_putc(bc, OP_get_var_ref);
- dbuf_put_u16(bc, idx);
- has_var_obj = TRUE;
- force_init = TRUE;
- goto closure_found;
- }
- }
- abort();
- closure_found:
- if (hf->cpool_idx >= 0 || force_init) {
- if (hf->cpool_idx >= 0) {
- dbuf_putc(bc, OP_fclosure);
- dbuf_put_u32(bc, hf->cpool_idx);
- if (hf->var_name == JS_ATOM__default_) {
- /* set default export function name */
- dbuf_putc(bc, OP_set_name);
- dbuf_put_u32(bc, JS_DupAtom(ctx, JS_ATOM_default));
- }
- } else {
- dbuf_putc(bc, OP_undefined);
- }
- if (!has_var_obj) {
- dbuf_putc(bc, OP_put_var_ref);
- dbuf_put_u16(bc, idx);
- } else {
- dbuf_putc(bc, OP_define_field);
- dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));
- dbuf_putc(bc, OP_drop);
- }
- }
- JS_FreeAtom(ctx, hf->var_name);
- }
- if (s->module) {
- dbuf_putc(bc, OP_return_undef);
- dbuf_putc(bc, OP_label);
- dbuf_put_u32(bc, label_next);
- s->label_slots[label_next].pos2 = bc->size;
- }
- js_free(ctx, s->global_vars);
- s->global_vars = NULL;
- s->global_var_count = 0;
- s->global_var_size = 0;
- }
- static int skip_dead_code(JSFunctionDef *s, const uint8_t *bc_buf, int bc_len,
- int pos, int *linep)
- {
- int op, len, label;
- for (; pos < bc_len; pos += len) {
- op = bc_buf[pos];
- len = opcode_info[op].size;
- if (op == OP_line_num) {
- *linep = get_u32(bc_buf + pos + 1);
- } else
- if (op == OP_label) {
- label = get_u32(bc_buf + pos + 1);
- if (update_label(s, label, 0) > 0)
- break;
- #if 0
- if (s->label_slots[label].first_reloc) {
- printf("line %d: unreferenced label %d:%d has relocations\n",
- *linep, label, s->label_slots[label].pos2);
- }
- #endif
- assert(s->label_slots[label].first_reloc == NULL);
- } else {
- /* XXX: output a warning for unreachable code? */
- JSAtom atom;
- switch(opcode_info[op].fmt) {
- case OP_FMT_label:
- case OP_FMT_label_u16:
- label = get_u32(bc_buf + pos + 1);
- update_label(s, label, -1);
- break;
- case OP_FMT_atom_label_u8:
- case OP_FMT_atom_label_u16:
- label = get_u32(bc_buf + pos + 5);
- update_label(s, label, -1);
- /* fall thru */
- case OP_FMT_atom:
- case OP_FMT_atom_u8:
- case OP_FMT_atom_u16:
- atom = get_u32(bc_buf + pos + 1);
- JS_FreeAtom(s->ctx, atom);
- break;
- default:
- break;
- }
- }
- }
- return pos;
- }
- static int get_label_pos(JSFunctionDef *s, int label)
- {
- int i, pos;
- for (i = 0; i < 20; i++) {
- pos = s->label_slots[label].pos;
- for (;;) {
- switch (s->byte_code.buf[pos]) {
- case OP_line_num:
- case OP_label:
- pos += 5;
- continue;
- case OP_goto:
- label = get_u32(s->byte_code.buf + pos + 1);
- break;
- default:
- return pos;
- }
- break;
- }
- }
- return pos;
- }
- /* convert global variable accesses to local variables or closure
- variables when necessary */
- static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s)
- {
- int pos, pos_next, bc_len, op, len, line_num, i, idx;
- uint8_t *bc_buf;
- JSAtom var_name;
- DynBuf bc_out;
- CodeContext cc;
- int scope;
- cc.bc_buf = bc_buf = s->byte_code.buf;
- cc.bc_len = bc_len = s->byte_code.size;
- js_dbuf_bytecode_init(ctx, &bc_out);
- /* first pass for runtime checks (must be done before the
- variables are created) */
- /* XXX: inefficient */
- for(i = 0; i < s->global_var_count; i++) {
- JSGlobalVar *hf = &s->global_vars[i];
- /* check if global variable (XXX: simplify) */
- for(idx = 0; idx < s->closure_var_count; idx++) {
- JSClosureVar *cv = &s->closure_var[idx];
- if (cv->closure_type == JS_CLOSURE_GLOBAL_REF ||
- cv->closure_type == JS_CLOSURE_GLOBAL_DECL ||
- cv->closure_type == JS_CLOSURE_GLOBAL ||
- cv->closure_type == JS_CLOSURE_MODULE_DECL ||
- cv->closure_type == JS_CLOSURE_MODULE_IMPORT)
- goto next; /* don't look at global variables (they are at the end) */
- if (cv->var_name == hf->var_name) {
- if (s->eval_type == JS_EVAL_TYPE_DIRECT &&
- cv->is_lexical) {
- /* Check if a lexical variable is
- redefined as 'var'. XXX: Could abort
- compilation here, but for consistency
- with the other checks, we delay the
- error generation. */
- dbuf_putc(&bc_out, OP_throw_error);
- dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name));
- dbuf_putc(&bc_out, JS_THROW_VAR_REDECL);
- }
- goto next;
- }
- if (cv->var_name == JS_ATOM__var_ ||
- cv->var_name == JS_ATOM__arg_var_)
- goto next;
- }
- next: ;
- }
- line_num = 0; /* avoid warning */
- for (pos = 0; pos < bc_len; pos = pos_next) {
- op = bc_buf[pos];
- len = opcode_info[op].size;
- pos_next = pos + len;
- switch(op) {
- case OP_line_num:
- line_num = get_u32(bc_buf + pos + 1);
- s->line_number_size++;
- goto no_change;
- case OP_eval: /* convert scope index to adjusted variable index */
- {
- int call_argc = get_u16(bc_buf + pos + 1);
- scope = get_u16(bc_buf + pos + 1 + 2);
- mark_eval_captured_variables(ctx, s, scope);
- dbuf_putc(&bc_out, op);
- dbuf_put_u16(&bc_out, call_argc);
- dbuf_put_u16(&bc_out, s->scopes[scope].first - ARG_SCOPE_END);
- }
- break;
- case OP_apply_eval: /* convert scope index to adjusted variable index */
- scope = get_u16(bc_buf + pos + 1);
- mark_eval_captured_variables(ctx, s, scope);
- dbuf_putc(&bc_out, op);
- dbuf_put_u16(&bc_out, s->scopes[scope].first - ARG_SCOPE_END);
- break;
- case OP_scope_get_var_checkthis:
- case OP_scope_get_var_undef:
- case OP_scope_get_var:
- case OP_scope_put_var:
- case OP_scope_delete_var:
- case OP_scope_get_ref:
- case OP_scope_put_var_init:
- var_name = get_u32(bc_buf + pos + 1);
- scope = get_u16(bc_buf + pos + 5);
- pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out,
- NULL, NULL, pos_next);
- JS_FreeAtom(ctx, var_name);
- break;
- case OP_scope_make_ref:
- {
- int label;
- LabelSlot *ls;
- var_name = get_u32(bc_buf + pos + 1);
- label = get_u32(bc_buf + pos + 5);
- scope = get_u16(bc_buf + pos + 9);
- ls = &s->label_slots[label];
- ls->ref_count--; /* always remove label reference */
- pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out,
- bc_buf, ls, pos_next);
- JS_FreeAtom(ctx, var_name);
- }
- break;
- case OP_scope_get_private_field:
- case OP_scope_get_private_field2:
- case OP_scope_put_private_field:
- case OP_scope_in_private_field:
- {
- int ret;
- var_name = get_u32(bc_buf + pos + 1);
- scope = get_u16(bc_buf + pos + 5);
- ret = resolve_scope_private_field(ctx, s, var_name, scope, op, &bc_out);
- if (ret < 0)
- goto fail;
- JS_FreeAtom(ctx, var_name);
- }
- break;
- case OP_gosub:
- s->jump_size++;
- if (OPTIMIZE) {
- /* remove calls to empty finalizers */
- int label;
- LabelSlot *ls;
- label = get_u32(bc_buf + pos + 1);
- assert(label >= 0 && label < s->label_count);
- ls = &s->label_slots[label];
- if (code_match(&cc, ls->pos, OP_ret, -1)) {
- ls->ref_count--;
- break;
- }
- }
- goto no_change;
- case OP_drop:
- if (0) {
- /* remove drops before return_undef */
- /* do not perform this optimization in pass2 because
- it breaks patterns recognised in resolve_labels */
- int pos1 = pos_next;
- int line1 = line_num;
- while (code_match(&cc, pos1, OP_drop, -1)) {
- if (cc.line_num >= 0) line1 = cc.line_num;
- pos1 = cc.pos;
- }
- if (code_match(&cc, pos1, OP_return_undef, -1)) {
- pos_next = pos1;
- if (line1 != -1 && line1 != line_num) {
- line_num = line1;
- s->line_number_size++;
- dbuf_putc(&bc_out, OP_line_num);
- dbuf_put_u32(&bc_out, line_num);
- }
- break;
- }
- }
- goto no_change;
- case OP_insert3:
- if (OPTIMIZE) {
- /* Transformation: insert3 put_array_el|put_ref_value drop -> put_array_el|put_ref_value */
- if (code_match(&cc, pos_next, M2(OP_put_array_el, OP_put_ref_value), OP_drop, -1)) {
- dbuf_putc(&bc_out, cc.op);
- pos_next = cc.pos;
- if (cc.line_num != -1 && cc.line_num != line_num) {
- line_num = cc.line_num;
- s->line_number_size++;
- dbuf_putc(&bc_out, OP_line_num);
- dbuf_put_u32(&bc_out, line_num);
- }
- break;
- }
- }
- goto no_change;
- case OP_goto:
- s->jump_size++;
- /* fall thru */
- case OP_tail_call:
- case OP_tail_call_method:
- case OP_return:
- case OP_return_undef:
- case OP_throw:
- case OP_throw_error:
- case OP_ret:
- if (OPTIMIZE) {
- /* remove dead code */
- int line = -1;
- dbuf_put(&bc_out, bc_buf + pos, len);
- pos = skip_dead_code(s, bc_buf, bc_len, pos + len, &line);
- pos_next = pos;
- if (pos < bc_len && line >= 0 && line_num != line) {
- line_num = line;
- s->line_number_size++;
- dbuf_putc(&bc_out, OP_line_num);
- dbuf_put_u32(&bc_out, line_num);
- }
- break;
- }
- goto no_change;
- case OP_label:
- {
- int label;
- LabelSlot *ls;
- label = get_u32(bc_buf + pos + 1);
- assert(label >= 0 && label < s->label_count);
- ls = &s->label_slots[label];
- ls->pos2 = bc_out.size + opcode_info[op].size;
- }
- goto no_change;
- case OP_enter_scope:
- {
- int scope_idx, scope = get_u16(bc_buf + pos + 1);
- if (scope == s->body_scope) {
- instantiate_hoisted_definitions(ctx, s, &bc_out);
- }
- for(scope_idx = s->scopes[scope].first; scope_idx >= 0;) {
- JSVarDef *vd = &s->vars[scope_idx];
- if (vd->scope_level == scope) {
- if (scope_idx != s->arguments_arg_idx) {
- if (vd->var_kind == JS_VAR_FUNCTION_DECL ||
- vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) {
- /* Initialize lexical variable upon entering scope */
- dbuf_putc(&bc_out, OP_fclosure);
- dbuf_put_u32(&bc_out, vd->func_pool_idx);
- dbuf_putc(&bc_out, OP_put_loc);
- dbuf_put_u16(&bc_out, scope_idx);
- } else {
- /* XXX: should check if variable can be used
- before initialization */
- dbuf_putc(&bc_out, OP_set_loc_uninitialized);
- dbuf_put_u16(&bc_out, scope_idx);
- }
- }
- scope_idx = vd->scope_next;
- } else {
- break;
- }
- }
- }
- break;
- case OP_leave_scope:
- {
- int scope_idx, scope = get_u16(bc_buf + pos + 1);
- for(scope_idx = s->scopes[scope].first; scope_idx >= 0;) {
- JSVarDef *vd = &s->vars[scope_idx];
- if (vd->scope_level == scope) {
- if (vd->is_captured) {
- dbuf_putc(&bc_out, OP_close_loc);
- dbuf_put_u16(&bc_out, scope_idx);
- }
- scope_idx = vd->scope_next;
- } else {
- break;
- }
- }
- }
- break;
- case OP_set_name:
- {
- /* remove dummy set_name opcodes */
- JSAtom name = get_u32(bc_buf + pos + 1);
- if (name == JS_ATOM_NULL)
- break;
- }
- goto no_change;
- case OP_if_false:
- case OP_if_true:
- case OP_catch:
- s->jump_size++;
- goto no_change;
- case OP_dup:
- if (OPTIMIZE) {
- /* Transformation: dup if_false(l1) drop, l1: if_false(l2) -> if_false(l2) */
- /* Transformation: dup if_true(l1) drop, l1: if_true(l2) -> if_true(l2) */
- if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), OP_drop, -1)) {
- int lab0, lab1, op1, pos1, line1, pos2;
- lab0 = lab1 = cc.label;
- assert(lab1 >= 0 && lab1 < s->label_count);
- op1 = cc.op;
- pos1 = cc.pos;
- line1 = cc.line_num;
- while (code_match(&cc, (pos2 = get_label_pos(s, lab1)), OP_dup, op1, OP_drop, -1)) {
- lab1 = cc.label;
- }
- if (code_match(&cc, pos2, op1, -1)) {
- s->jump_size++;
- update_label(s, lab0, -1);
- update_label(s, cc.label, +1);
- dbuf_putc(&bc_out, op1);
- dbuf_put_u32(&bc_out, cc.label);
- pos_next = pos1;
- if (line1 != -1 && line1 != line_num) {
- line_num = line1;
- s->line_number_size++;
- dbuf_putc(&bc_out, OP_line_num);
- dbuf_put_u32(&bc_out, line_num);
- }
- break;
- }
- }
- }
- goto no_change;
- case OP_nop:
- /* remove erased code */
- break;
- case OP_set_class_name:
- /* only used during parsing */
- break;
- case OP_get_field_opt_chain: /* equivalent to OP_get_field */
- {
- JSAtom name = get_u32(bc_buf + pos + 1);
- dbuf_putc(&bc_out, OP_get_field);
- dbuf_put_u32(&bc_out, name);
- }
- break;
- case OP_get_array_el_opt_chain: /* equivalent to OP_get_array_el */
- dbuf_putc(&bc_out, OP_get_array_el);
- break;
- default:
- no_change:
- dbuf_put(&bc_out, bc_buf + pos, len);
- break;
- }
- }
- /* set the new byte code */
- dbuf_free(&s->byte_code);
- s->byte_code = bc_out;
- if (dbuf_error(&s->byte_code)) {
- JS_ThrowOutOfMemory(ctx);
- return -1;
- }
- return 0;
- fail:
- /* continue the copy to keep the atom refcounts consistent */
- /* XXX: find a better solution ? */
- for (; pos < bc_len; pos = pos_next) {
- op = bc_buf[pos];
- len = opcode_info[op].size;
- pos_next = pos + len;
- dbuf_put(&bc_out, bc_buf + pos, len);
- }
- dbuf_free(&s->byte_code);
- s->byte_code = bc_out;
- return -1;
- }
- /* the pc2line table gives a source position for each PC value */
- static void add_pc2line_info(JSFunctionDef *s, uint32_t pc, uint32_t source_pos)
- {
- if (s->line_number_slots != NULL
- && s->line_number_count < s->line_number_size
- && pc >= s->line_number_last_pc
- && source_pos != s->line_number_last) {
- s->line_number_slots[s->line_number_count].pc = pc;
- s->line_number_slots[s->line_number_count].source_pos = source_pos;
- s->line_number_count++;
- s->line_number_last_pc = pc;
- s->line_number_last = source_pos;
- }
- }
- /* XXX: could use a more compact storage */
- /* XXX: get_line_col_cached() is slow. For more predictable
- performance, line/cols could be stored every N source
- bytes. Alternatively, get_line_col_cached() could be issued in
- emit_source_pos() so that the deltas are more likely to be
- small. */
- static void compute_pc2line_info(JSFunctionDef *s)
- {
- if (!s->strip_debug) {
- int last_line_num, last_col_num;
- uint32_t last_pc = 0;
- int i, line_num, col_num;
- const uint8_t *buf_start = s->get_line_col_cache->buf_start;
- js_dbuf_init(s->ctx, &s->pc2line);
- last_line_num = get_line_col_cached(s->get_line_col_cache,
- &last_col_num,
- buf_start + s->source_pos);
- dbuf_put_leb128(&s->pc2line, last_line_num); /* line number minus 1 */
- dbuf_put_leb128(&s->pc2line, last_col_num); /* column number minus 1 */
- for (i = 0; i < s->line_number_count; i++) {
- uint32_t pc = s->line_number_slots[i].pc;
- uint32_t source_pos = s->line_number_slots[i].source_pos;
- int diff_pc, diff_line, diff_col;
- if (source_pos == -1)
- continue;
- diff_pc = pc - last_pc;
- if (diff_pc < 0)
- continue;
- line_num = get_line_col_cached(s->get_line_col_cache, &col_num,
- buf_start + source_pos);
- diff_line = line_num - last_line_num;
- diff_col = col_num - last_col_num;
- if (diff_line == 0 && diff_col == 0)
- continue;
- if (diff_line >= PC2LINE_BASE &&
- diff_line < PC2LINE_BASE + PC2LINE_RANGE &&
- diff_pc <= PC2LINE_DIFF_PC_MAX) {
- dbuf_putc(&s->pc2line, (diff_line - PC2LINE_BASE) +
- diff_pc * PC2LINE_RANGE + PC2LINE_OP_FIRST);
- } else {
- /* longer encoding */
- dbuf_putc(&s->pc2line, 0);
- dbuf_put_leb128(&s->pc2line, diff_pc);
- dbuf_put_sleb128(&s->pc2line, diff_line);
- }
- dbuf_put_sleb128(&s->pc2line, diff_col);
-
- last_pc = pc;
- last_line_num = line_num;
- last_col_num = col_num;
- }
- }
- }
- static RelocEntry *add_reloc(JSContext *ctx, LabelSlot *ls, uint32_t addr, int size)
- {
- RelocEntry *re;
- re = js_malloc(ctx, sizeof(*re));
- if (!re)
- return NULL;
- re->addr = addr;
- re->size = size;
- re->next = ls->first_reloc;
- ls->first_reloc = re;
- return re;
- }
- static BOOL code_has_label(CodeContext *s, int pos, int label)
- {
- while (pos < s->bc_len) {
- int op = s->bc_buf[pos];
- if (op == OP_line_num) {
- pos += 5;
- continue;
- }
- if (op == OP_label) {
- int lab = get_u32(s->bc_buf + pos + 1);
- if (lab == label)
- return TRUE;
- pos += 5;
- continue;
- }
- if (op == OP_goto) {
- int lab = get_u32(s->bc_buf + pos + 1);
- if (lab == label)
- return TRUE;
- }
- break;
- }
- return FALSE;
- }
- /* return the target label, following the OP_goto jumps
- the first opcode at destination is stored in *pop
- */
- static int find_jump_target(JSFunctionDef *s, int label0, int *pop, int *pline)
- {
- int i, pos, op, label;
- label = label0;
- update_label(s, label, -1);
- for (i = 0; i < 10; i++) {
- assert(label >= 0 && label < s->label_count);
- pos = s->label_slots[label].pos2;
- for (;;) {
- switch(op = s->byte_code.buf[pos]) {
- case OP_line_num:
- if (pline)
- *pline = get_u32(s->byte_code.buf + pos + 1);
- /* fall thru */
- case OP_label:
- pos += opcode_info[op].size;
- continue;
- case OP_goto:
- label = get_u32(s->byte_code.buf + pos + 1);
- break;
- case OP_drop:
- /* ignore drop opcodes if followed by OP_return_undef */
- while (s->byte_code.buf[++pos] == OP_drop)
- continue;
- if (s->byte_code.buf[pos] == OP_return_undef)
- op = OP_return_undef;
- /* fall thru */
- default:
- goto done;
- }
- break;
- }
- }
- /* cycle detected, could issue a warning */
- /* XXX: the combination of find_jump_target() and skip_dead_code()
- seems incorrect with cyclic labels. See for exemple:
- for (;;) {
- l:break l;
- l:break l;
- l:break l;
- l:break l;
- }
- Avoiding changing the target is just a workaround and might not
- suffice to completely fix the problem. */
- label = label0;
- done:
- *pop = op;
- update_label(s, label, +1);
- return label;
- }
- static void push_short_int(DynBuf *bc_out, int val)
- {
- #if SHORT_OPCODES
- if (val >= -1 && val <= 7) {
- dbuf_putc(bc_out, OP_push_0 + val);
- return;
- }
- if (val == (int8_t)val) {
- dbuf_putc(bc_out, OP_push_i8);
- dbuf_putc(bc_out, val);
- return;
- }
- if (val == (int16_t)val) {
- dbuf_putc(bc_out, OP_push_i16);
- dbuf_put_u16(bc_out, val);
- return;
- }
- #endif
- dbuf_putc(bc_out, OP_push_i32);
- dbuf_put_u32(bc_out, val);
- }
- static void put_short_code(DynBuf *bc_out, int op, int idx)
- {
- #if SHORT_OPCODES
- if (idx < 4) {
- switch (op) {
- case OP_get_loc:
- dbuf_putc(bc_out, OP_get_loc0 + idx);
- return;
- case OP_put_loc:
- dbuf_putc(bc_out, OP_put_loc0 + idx);
- return;
- case OP_set_loc:
- dbuf_putc(bc_out, OP_set_loc0 + idx);
- return;
- case OP_get_arg:
- dbuf_putc(bc_out, OP_get_arg0 + idx);
- return;
- case OP_put_arg:
- dbuf_putc(bc_out, OP_put_arg0 + idx);
- return;
- case OP_set_arg:
- dbuf_putc(bc_out, OP_set_arg0 + idx);
- return;
- case OP_get_var_ref:
- dbuf_putc(bc_out, OP_get_var_ref0 + idx);
- return;
- case OP_put_var_ref:
- dbuf_putc(bc_out, OP_put_var_ref0 + idx);
- return;
- case OP_set_var_ref:
- dbuf_putc(bc_out, OP_set_var_ref0 + idx);
- return;
- case OP_call:
- dbuf_putc(bc_out, OP_call0 + idx);
- return;
- }
- }
- if (idx < 256) {
- switch (op) {
- case OP_get_loc:
- dbuf_putc(bc_out, OP_get_loc8);
- dbuf_putc(bc_out, idx);
- return;
- case OP_put_loc:
- dbuf_putc(bc_out, OP_put_loc8);
- dbuf_putc(bc_out, idx);
- return;
- case OP_set_loc:
- dbuf_putc(bc_out, OP_set_loc8);
- dbuf_putc(bc_out, idx);
- return;
- }
- }
- #endif
- dbuf_putc(bc_out, op);
- dbuf_put_u16(bc_out, idx);
- }
- /* peephole optimizations and resolve goto/labels */
- static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s)
- {
- int pos, pos_next, bc_len, op, op1, len, i, line_num;
- const uint8_t *bc_buf;
- DynBuf bc_out;
- LabelSlot *label_slots, *ls;
- RelocEntry *re, *re_next;
- CodeContext cc;
- int label;
- #if SHORT_OPCODES
- JumpSlot *jp;
- #endif
- label_slots = s->label_slots;
- line_num = s->source_pos;
- cc.bc_buf = bc_buf = s->byte_code.buf;
- cc.bc_len = bc_len = s->byte_code.size;
- js_dbuf_bytecode_init(ctx, &bc_out);
- #if SHORT_OPCODES
- if (s->jump_size) {
- s->jump_slots = js_mallocz(s->ctx, sizeof(*s->jump_slots) * s->jump_size);
- if (s->jump_slots == NULL)
- return -1;
- }
- #endif
- /* XXX: Should skip this phase if not generating SHORT_OPCODES */
- if (s->line_number_size && !s->strip_debug) {
- s->line_number_slots = js_mallocz(s->ctx, sizeof(*s->line_number_slots) * s->line_number_size);
- if (s->line_number_slots == NULL)
- return -1;
- s->line_number_last = s->source_pos;
- s->line_number_last_pc = 0;
- }
- /* initialize the 'home_object' variable if needed */
- if (s->home_object_var_idx >= 0) {
- dbuf_putc(&bc_out, OP_special_object);
- dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_HOME_OBJECT);
- put_short_code(&bc_out, OP_put_loc, s->home_object_var_idx);
- }
- /* initialize the 'this.active_func' variable if needed */
- if (s->this_active_func_var_idx >= 0) {
- dbuf_putc(&bc_out, OP_special_object);
- dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_THIS_FUNC);
- put_short_code(&bc_out, OP_put_loc, s->this_active_func_var_idx);
- }
- /* initialize the 'new.target' variable if needed */
- if (s->new_target_var_idx >= 0) {
- dbuf_putc(&bc_out, OP_special_object);
- dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_NEW_TARGET);
- put_short_code(&bc_out, OP_put_loc, s->new_target_var_idx);
- }
- /* initialize the 'this' variable if needed. In a derived class
- constructor, this is initially uninitialized. */
- if (s->this_var_idx >= 0) {
- if (s->is_derived_class_constructor) {
- dbuf_putc(&bc_out, OP_set_loc_uninitialized);
- dbuf_put_u16(&bc_out, s->this_var_idx);
- } else {
- dbuf_putc(&bc_out, OP_push_this);
- put_short_code(&bc_out, OP_put_loc, s->this_var_idx);
- }
- }
- /* initialize the 'arguments' variable if needed */
- if (s->arguments_var_idx >= 0) {
- if ((s->js_mode & JS_MODE_STRICT) || !s->has_simple_parameter_list) {
- dbuf_putc(&bc_out, OP_special_object);
- dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_ARGUMENTS);
- } else {
- dbuf_putc(&bc_out, OP_special_object);
- dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS);
- /* the arguments are implicitly captured because
- references to them are created with the 'argument'
- object */
- for(i = 0; i < s->arg_count; i++)
- capture_var(s, &s->args[i]);
- }
- if (s->arguments_arg_idx >= 0)
- put_short_code(&bc_out, OP_set_loc, s->arguments_arg_idx);
- put_short_code(&bc_out, OP_put_loc, s->arguments_var_idx);
- }
- /* initialize a reference to the current function if needed */
- if (s->func_var_idx >= 0) {
- dbuf_putc(&bc_out, OP_special_object);
- dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_THIS_FUNC);
- put_short_code(&bc_out, OP_put_loc, s->func_var_idx);
- }
- /* initialize the variable environment object if needed */
- if (s->var_object_idx >= 0) {
- dbuf_putc(&bc_out, OP_special_object);
- dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT);
- put_short_code(&bc_out, OP_put_loc, s->var_object_idx);
- }
- if (s->arg_var_object_idx >= 0) {
- dbuf_putc(&bc_out, OP_special_object);
- dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT);
- put_short_code(&bc_out, OP_put_loc, s->arg_var_object_idx);
- }
- for (pos = 0; pos < bc_len; pos = pos_next) {
- int val;
- op = bc_buf[pos];
- len = opcode_info[op].size;
- pos_next = pos + len;
- switch(op) {
- case OP_line_num:
- /* line number info (for debug). We put it in a separate
- compressed table to reduce memory usage and get better
- performance */
- line_num = get_u32(bc_buf + pos + 1);
- break;
- case OP_label:
- {
- label = get_u32(bc_buf + pos + 1);
- assert(label >= 0 && label < s->label_count);
- ls = &label_slots[label];
- assert(ls->addr == -1);
- ls->addr = bc_out.size;
- /* resolve the relocation entries */
- for(re = ls->first_reloc; re != NULL; re = re_next) {
- int diff = ls->addr - re->addr;
- re_next = re->next;
- switch (re->size) {
- case 4:
- put_u32(bc_out.buf + re->addr, diff);
- break;
- case 2:
- assert(diff == (int16_t)diff);
- put_u16(bc_out.buf + re->addr, diff);
- break;
- case 1:
- assert(diff == (int8_t)diff);
- put_u8(bc_out.buf + re->addr, diff);
- break;
- }
- js_free(ctx, re);
- }
- ls->first_reloc = NULL;
- }
- break;
- case OP_call:
- case OP_call_method:
- {
- /* detect and transform tail calls */
- int argc;
- argc = get_u16(bc_buf + pos + 1);
- if (code_match(&cc, pos_next, OP_return, -1)) {
- if (cc.line_num >= 0) line_num = cc.line_num;
- add_pc2line_info(s, bc_out.size, line_num);
- put_short_code(&bc_out, op + 1, argc);
- pos_next = skip_dead_code(s, bc_buf, bc_len, cc.pos, &line_num);
- break;
- }
- add_pc2line_info(s, bc_out.size, line_num);
- put_short_code(&bc_out, op, argc);
- break;
- }
- goto no_change;
- case OP_return:
- case OP_return_undef:
- case OP_return_async:
- case OP_throw:
- case OP_throw_error:
- pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num);
- goto no_change;
- case OP_goto:
- label = get_u32(bc_buf + pos + 1);
- has_goto:
- if (OPTIMIZE) {
- int line1 = -1;
- /* Use custom matcher because multiple labels can follow */
- label = find_jump_target(s, label, &op1, &line1);
- if (code_has_label(&cc, pos_next, label)) {
- /* jump to next instruction: remove jump */
- update_label(s, label, -1);
- break;
- }
- if (op1 == OP_return || op1 == OP_return_undef || op1 == OP_throw) {
- /* jump to return/throw: remove jump, append return/throw */
- /* updating the line number obfuscates assembly listing */
- //if (line1 != -1) line_num = line1;
- update_label(s, label, -1);
- add_pc2line_info(s, bc_out.size, line_num);
- dbuf_putc(&bc_out, op1);
- pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num);
- break;
- }
- /* XXX: should duplicate single instructions followed by goto or return */
- /* For example, can match one of these followed by return:
- push_i32 / push_const / push_atom_value / get_var /
- undefined / null / push_false / push_true / get_ref_value /
- get_loc / get_arg / get_var_ref
- */
- }
- goto has_label;
- case OP_gosub:
- label = get_u32(bc_buf + pos + 1);
- if (0 && OPTIMIZE) {
- label = find_jump_target(s, label, &op1, NULL);
- if (op1 == OP_ret) {
- update_label(s, label, -1);
- /* empty finally clause: remove gosub */
- break;
- }
- }
- goto has_label;
- case OP_catch:
- label = get_u32(bc_buf + pos + 1);
- goto has_label;
- case OP_if_true:
- case OP_if_false:
- label = get_u32(bc_buf + pos + 1);
- if (OPTIMIZE) {
- label = find_jump_target(s, label, &op1, NULL);
- /* transform if_false/if_true(l1) label(l1) -> drop label(l1) */
- if (code_has_label(&cc, pos_next, label)) {
- update_label(s, label, -1);
- dbuf_putc(&bc_out, OP_drop);
- break;
- }
- /* transform if_false(l1) goto(l2) label(l1) -> if_false(l2) label(l1) */
- if (code_match(&cc, pos_next, OP_goto, -1)) {
- int pos1 = cc.pos;
- int line1 = cc.line_num;
- if (code_has_label(&cc, pos1, label)) {
- if (line1 != -1) line_num = line1;
- pos_next = pos1;
- update_label(s, label, -1);
- label = cc.label;
- op ^= OP_if_true ^ OP_if_false;
- }
- }
- }
- has_label:
- add_pc2line_info(s, bc_out.size, line_num);
- if (op == OP_goto) {
- pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num);
- }
- assert(label >= 0 && label < s->label_count);
- ls = &label_slots[label];
- #if SHORT_OPCODES
- jp = &s->jump_slots[s->jump_count++];
- jp->op = op;
- jp->size = 4;
- jp->pos = bc_out.size + 1;
- jp->label = label;
- if (ls->addr == -1) {
- int diff = ls->pos2 - pos - 1;
- if (diff < 128 && (op == OP_if_false || op == OP_if_true || op == OP_goto)) {
- jp->size = 1;
- jp->op = OP_if_false8 + (op - OP_if_false);
- dbuf_putc(&bc_out, OP_if_false8 + (op - OP_if_false));
- dbuf_putc(&bc_out, 0);
- if (!add_reloc(ctx, ls, bc_out.size - 1, 1))
- goto fail;
- break;
- }
- if (diff < 32768 && op == OP_goto) {
- jp->size = 2;
- jp->op = OP_goto16;
- dbuf_putc(&bc_out, OP_goto16);
- dbuf_put_u16(&bc_out, 0);
- if (!add_reloc(ctx, ls, bc_out.size - 2, 2))
- goto fail;
- break;
- }
- } else {
- int diff = ls->addr - bc_out.size - 1;
- if (diff == (int8_t)diff && (op == OP_if_false || op == OP_if_true || op == OP_goto)) {
- jp->size = 1;
- jp->op = OP_if_false8 + (op - OP_if_false);
- dbuf_putc(&bc_out, OP_if_false8 + (op - OP_if_false));
- dbuf_putc(&bc_out, diff);
- break;
- }
- if (diff == (int16_t)diff && op == OP_goto) {
- jp->size = 2;
- jp->op = OP_goto16;
- dbuf_putc(&bc_out, OP_goto16);
- dbuf_put_u16(&bc_out, diff);
- break;
- }
- }
- #endif
- dbuf_putc(&bc_out, op);
- dbuf_put_u32(&bc_out, ls->addr - bc_out.size);
- if (ls->addr == -1) {
- /* unresolved yet: create a new relocation entry */
- if (!add_reloc(ctx, ls, bc_out.size - 4, 4))
- goto fail;
- }
- break;
- case OP_with_get_var:
- case OP_with_put_var:
- case OP_with_delete_var:
- case OP_with_make_ref:
- case OP_with_get_ref:
- {
- JSAtom atom;
- int is_with;
- atom = get_u32(bc_buf + pos + 1);
- label = get_u32(bc_buf + pos + 5);
- is_with = bc_buf[pos + 9];
- if (OPTIMIZE) {
- label = find_jump_target(s, label, &op1, NULL);
- }
- assert(label >= 0 && label < s->label_count);
- ls = &label_slots[label];
- add_pc2line_info(s, bc_out.size, line_num);
- #if SHORT_OPCODES
- jp = &s->jump_slots[s->jump_count++];
- jp->op = op;
- jp->size = 4;
- jp->pos = bc_out.size + 5;
- jp->label = label;
- #endif
- dbuf_putc(&bc_out, op);
- dbuf_put_u32(&bc_out, atom);
- dbuf_put_u32(&bc_out, ls->addr - bc_out.size);
- if (ls->addr == -1) {
- /* unresolved yet: create a new relocation entry */
- if (!add_reloc(ctx, ls, bc_out.size - 4, 4))
- goto fail;
- }
- dbuf_putc(&bc_out, is_with);
- }
- break;
- case OP_drop:
- if (OPTIMIZE) {
- /* remove useless drops before return */
- if (code_match(&cc, pos_next, OP_return_undef, -1)) {
- if (cc.line_num >= 0) line_num = cc.line_num;
- break;
- }
- }
- goto no_change;
- case OP_null:
- #if SHORT_OPCODES
- if (OPTIMIZE) {
- /* transform null strict_eq into is_null */
- if (code_match(&cc, pos_next, OP_strict_eq, -1)) {
- if (cc.line_num >= 0) line_num = cc.line_num;
- add_pc2line_info(s, bc_out.size, line_num);
- dbuf_putc(&bc_out, OP_is_null);
- pos_next = cc.pos;
- break;
- }
- /* transform null strict_neq if_false/if_true -> is_null if_true/if_false */
- if (code_match(&cc, pos_next, OP_strict_neq, M2(OP_if_false, OP_if_true), -1)) {
- if (cc.line_num >= 0) line_num = cc.line_num;
- add_pc2line_info(s, bc_out.size, line_num);
- dbuf_putc(&bc_out, OP_is_null);
- pos_next = cc.pos;
- label = cc.label;
- op = cc.op ^ OP_if_false ^ OP_if_true;
- goto has_label;
- }
- }
- #endif
- /* fall thru */
- case OP_push_false:
- case OP_push_true:
- if (OPTIMIZE) {
- val = (op == OP_push_true);
- if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) {
- has_constant_test:
- if (cc.line_num >= 0) line_num = cc.line_num;
- if (val == cc.op - OP_if_false) {
- /* transform null if_false(l1) -> goto l1 */
- /* transform false if_false(l1) -> goto l1 */
- /* transform true if_true(l1) -> goto l1 */
- pos_next = cc.pos;
- op = OP_goto;
- label = cc.label;
- goto has_goto;
- } else {
- /* transform null if_true(l1) -> nop */
- /* transform false if_true(l1) -> nop */
- /* transform true if_false(l1) -> nop */
- pos_next = cc.pos;
- update_label(s, cc.label, -1);
- break;
- }
- }
- }
- goto no_change;
- case OP_push_i32:
- if (OPTIMIZE) {
- /* transform i32(val) neg -> i32(-val) */
- val = get_i32(bc_buf + pos + 1);
- if ((val != INT32_MIN && val != 0)
- && code_match(&cc, pos_next, OP_neg, -1)) {
- if (cc.line_num >= 0) line_num = cc.line_num;
- if (code_match(&cc, cc.pos, OP_drop, -1)) {
- if (cc.line_num >= 0) line_num = cc.line_num;
- } else {
- add_pc2line_info(s, bc_out.size, line_num);
- push_short_int(&bc_out, -val);
- }
- pos_next = cc.pos;
- break;
- }
- /* remove push/drop pairs generated by the parser */
- if (code_match(&cc, pos_next, OP_drop, -1)) {
- if (cc.line_num >= 0) line_num = cc.line_num;
- pos_next = cc.pos;
- break;
- }
- /* Optimize constant tests: `if (0)`, `if (1)`, `if (!0)`... */
- if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) {
- val = (val != 0);
- goto has_constant_test;
- }
- add_pc2line_info(s, bc_out.size, line_num);
- push_short_int(&bc_out, val);
- break;
- }
- goto no_change;
- case OP_push_bigint_i32:
- if (OPTIMIZE) {
- /* transform i32(val) neg -> i32(-val) */
- val = get_i32(bc_buf + pos + 1);
- if (val != INT32_MIN
- && code_match(&cc, pos_next, OP_neg, -1)) {
- if (cc.line_num >= 0) line_num = cc.line_num;
- if (code_match(&cc, cc.pos, OP_drop, -1)) {
- if (cc.line_num >= 0) line_num = cc.line_num;
- } else {
- add_pc2line_info(s, bc_out.size, line_num);
- dbuf_putc(&bc_out, OP_push_bigint_i32);
- dbuf_put_u32(&bc_out, -val);
- }
- pos_next = cc.pos;
- break;
- }
- }
- goto no_change;
- #if SHORT_OPCODES
- case OP_push_const:
- case OP_fclosure:
- if (OPTIMIZE) {
- int idx = get_u32(bc_buf + pos + 1);
- if (idx < 256) {
- add_pc2line_info(s, bc_out.size, line_num);
- dbuf_putc(&bc_out, OP_push_const8 + op - OP_push_const);
- dbuf_putc(&bc_out, idx);
- break;
- }
- }
- goto no_change;
- case OP_get_field:
- if (OPTIMIZE) {
- JSAtom atom = get_u32(bc_buf + pos + 1);
- if (atom == JS_ATOM_length) {
- JS_FreeAtom(ctx, atom);
- add_pc2line_info(s, bc_out.size, line_num);
- dbuf_putc(&bc_out, OP_get_length);
- break;
- }
- }
- goto no_change;
- #endif
- case OP_push_atom_value:
- if (OPTIMIZE) {
- JSAtom atom = get_u32(bc_buf + pos + 1);
- /* remove push/drop pairs generated by the parser */
- if (code_match(&cc, pos_next, OP_drop, -1)) {
- JS_FreeAtom(ctx, atom);
- if (cc.line_num >= 0) line_num = cc.line_num;
- pos_next = cc.pos;
- break;
- }
- #if SHORT_OPCODES
- if (atom == JS_ATOM_empty_string) {
- JS_FreeAtom(ctx, atom);
- add_pc2line_info(s, bc_out.size, line_num);
- dbuf_putc(&bc_out, OP_push_empty_string);
- break;
- }
- #endif
- }
- goto no_change;
- case OP_to_propkey:
- if (OPTIMIZE) {
- /* remove redundant to_propkey opcodes when storing simple data */
- if (code_match(&cc, pos_next, M3(OP_get_loc, OP_get_arg, OP_get_var_ref), -1, OP_put_array_el, -1)
- || code_match(&cc, pos_next, M3(OP_push_i32, OP_push_const, OP_push_atom_value), OP_put_array_el, -1)
- || code_match(&cc, pos_next, M4(OP_undefined, OP_null, OP_push_true, OP_push_false), OP_put_array_el, -1)) {
- break;
- }
- }
- goto no_change;
- case OP_undefined:
- if (OPTIMIZE) {
- /* remove push/drop pairs generated by the parser */
- if (code_match(&cc, pos_next, OP_drop, -1)) {
- if (cc.line_num >= 0) line_num = cc.line_num;
- pos_next = cc.pos;
- break;
- }
- /* transform undefined return -> return_undefined */
- if (code_match(&cc, pos_next, OP_return, -1)) {
- if (cc.line_num >= 0) line_num = cc.line_num;
- add_pc2line_info(s, bc_out.size, line_num);
- dbuf_putc(&bc_out, OP_return_undef);
- pos_next = cc.pos;
- break;
- }
- /* transform undefined if_true(l1)/if_false(l1) -> nop/goto(l1) */
- if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) {
- val = 0;
- goto has_constant_test;
- }
- #if SHORT_OPCODES
- /* transform undefined strict_eq -> is_undefined */
- if (code_match(&cc, pos_next, OP_strict_eq, -1)) {
- if (cc.line_num >= 0) line_num = cc.line_num;
- add_pc2line_info(s, bc_out.size, line_num);
- dbuf_putc(&bc_out, OP_is_undefined);
- pos_next = cc.pos;
- break;
- }
- /* transform undefined strict_neq if_false/if_true -> is_undefined if_true/if_false */
- if (code_match(&cc, pos_next, OP_strict_neq, M2(OP_if_false, OP_if_true), -1)) {
- if (cc.line_num >= 0) line_num = cc.line_num;
- add_pc2line_info(s, bc_out.size, line_num);
- dbuf_putc(&bc_out, OP_is_undefined);
- pos_next = cc.pos;
- label = cc.label;
- op = cc.op ^ OP_if_false ^ OP_if_true;
- goto has_label;
- }
- #endif
- }
- goto no_change;
- case OP_insert2:
- if (OPTIMIZE) {
- /* Transformation:
- insert2 put_field(a) drop -> put_field(a)
- */
- if (code_match(&cc, pos_next, OP_put_field, OP_drop, -1)) {
- if (cc.line_num >= 0) line_num = cc.line_num;
- add_pc2line_info(s, bc_out.size, line_num);
- dbuf_putc(&bc_out, OP_put_field);
- dbuf_put_u32(&bc_out, cc.atom);
- pos_next = cc.pos;
- break;
- }
- }
- goto no_change;
- case OP_dup:
- if (OPTIMIZE) {
- /* Transformation: dup put_x(n) drop -> put_x(n) */
- int op1, line2 = -1;
- /* Transformation: dup put_x(n) -> set_x(n) */
- if (code_match(&cc, pos_next, M4(OP_put_loc, OP_put_loc_check, OP_put_arg, OP_put_var_ref), -1, -1)) {
- if (cc.line_num >= 0) line_num = cc.line_num;
- op1 = cc.op + 1; /* put_x -> set_x */
- pos_next = cc.pos;
- if (code_match(&cc, cc.pos, OP_drop, -1)) {
- if (cc.line_num >= 0) line_num = cc.line_num;
- op1 -= 1; /* set_x drop -> put_x */
- pos_next = cc.pos;
- if (code_match(&cc, cc.pos, op1 - 1, cc.idx, -1)) {
- line2 = cc.line_num; /* delay line number update */
- op1 += 1; /* put_x(n) get_x(n) -> set_x(n) */
- pos_next = cc.pos;
- }
- }
- add_pc2line_info(s, bc_out.size, line_num);
- put_short_code(&bc_out, op1, cc.idx);
- if (line2 >= 0) line_num = line2;
- break;
- }
- }
- goto no_change;
- case OP_get_loc:
- if (OPTIMIZE) {
- /* transformation:
- get_loc(n) post_dec put_loc(n) drop -> dec_loc(n)
- get_loc(n) post_inc put_loc(n) drop -> inc_loc(n)
- get_loc(n) dec dup put_loc(n) drop -> dec_loc(n)
- get_loc(n) inc dup put_loc(n) drop -> inc_loc(n)
- */
- int idx;
- idx = get_u16(bc_buf + pos + 1);
- if (idx >= 256)
- goto no_change;
- if (code_match(&cc, pos_next, M2(OP_post_dec, OP_post_inc), OP_put_loc, idx, OP_drop, -1) ||
- code_match(&cc, pos_next, M2(OP_dec, OP_inc), OP_dup, OP_put_loc, idx, OP_drop, -1)) {
- if (cc.line_num >= 0) line_num = cc.line_num;
- add_pc2line_info(s, bc_out.size, line_num);
- dbuf_putc(&bc_out, (cc.op == OP_inc || cc.op == OP_post_inc) ? OP_inc_loc : OP_dec_loc);
- dbuf_putc(&bc_out, idx);
- pos_next = cc.pos;
- break;
- }
- /* transformation:
- get_loc(n) push_atom_value(x) add dup put_loc(n) drop -> push_atom_value(x) add_loc(n)
- */
- if (code_match(&cc, pos_next, OP_push_atom_value, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) {
- if (cc.line_num >= 0) line_num = cc.line_num;
- add_pc2line_info(s, bc_out.size, line_num);
- #if SHORT_OPCODES
- if (cc.atom == JS_ATOM_empty_string) {
- JS_FreeAtom(ctx, cc.atom);
- dbuf_putc(&bc_out, OP_push_empty_string);
- } else
- #endif
- {
- dbuf_putc(&bc_out, OP_push_atom_value);
- dbuf_put_u32(&bc_out, cc.atom);
- }
- dbuf_putc(&bc_out, OP_add_loc);
- dbuf_putc(&bc_out, idx);
- pos_next = cc.pos;
- break;
- }
- /* transformation:
- get_loc(n) push_i32(x) add dup put_loc(n) drop -> push_i32(x) add_loc(n)
- */
- if (code_match(&cc, pos_next, OP_push_i32, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) {
- if (cc.line_num >= 0) line_num = cc.line_num;
- add_pc2line_info(s, bc_out.size, line_num);
- push_short_int(&bc_out, cc.label);
- dbuf_putc(&bc_out, OP_add_loc);
- dbuf_putc(&bc_out, idx);
- pos_next = cc.pos;
- break;
- }
- /* transformation: XXX: also do these:
- get_loc(n) get_loc(x) add dup put_loc(n) drop -> get_loc(x) add_loc(n)
- get_loc(n) get_arg(x) add dup put_loc(n) drop -> get_arg(x) add_loc(n)
- get_loc(n) get_var_ref(x) add dup put_loc(n) drop -> get_var_ref(x) add_loc(n)
- */
- if (code_match(&cc, pos_next, M3(OP_get_loc, OP_get_arg, OP_get_var_ref), -1, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) {
- if (cc.line_num >= 0) line_num = cc.line_num;
- add_pc2line_info(s, bc_out.size, line_num);
- put_short_code(&bc_out, cc.op, cc.idx);
- dbuf_putc(&bc_out, OP_add_loc);
- dbuf_putc(&bc_out, idx);
- pos_next = cc.pos;
- break;
- }
- add_pc2line_info(s, bc_out.size, line_num);
- put_short_code(&bc_out, op, idx);
- break;
- }
- goto no_change;
- #if SHORT_OPCODES
- case OP_get_arg:
- case OP_get_var_ref:
- if (OPTIMIZE) {
- int idx;
- idx = get_u16(bc_buf + pos + 1);
- add_pc2line_info(s, bc_out.size, line_num);
- put_short_code(&bc_out, op, idx);
- break;
- }
- goto no_change;
- #endif
- case OP_put_loc:
- case OP_put_loc_check:
- case OP_put_arg:
- case OP_put_var_ref:
- if (OPTIMIZE) {
- /* transformation: put_x(n) get_x(n) -> set_x(n) */
- int idx;
- idx = get_u16(bc_buf + pos + 1);
- if (code_match(&cc, pos_next, op - 1, idx, -1)) {
- if (cc.line_num >= 0) line_num = cc.line_num;
- add_pc2line_info(s, bc_out.size, line_num);
- put_short_code(&bc_out, op + 1, idx);
- pos_next = cc.pos;
- break;
- }
- add_pc2line_info(s, bc_out.size, line_num);
- put_short_code(&bc_out, op, idx);
- break;
- }
- goto no_change;
- case OP_post_inc:
- case OP_post_dec:
- if (OPTIMIZE) {
- /* transformation:
- post_inc put_x drop -> inc put_x
- post_inc perm3 put_field drop -> inc put_field
- post_inc perm4 put_array_el drop -> inc put_array_el
- */
- int op1, idx;
- if (code_match(&cc, pos_next, M3(OP_put_loc, OP_put_arg, OP_put_var_ref), -1, OP_drop, -1)) {
- if (cc.line_num >= 0) line_num = cc.line_num;
- op1 = cc.op;
- idx = cc.idx;
- pos_next = cc.pos;
- if (code_match(&cc, cc.pos, op1 - 1, idx, -1)) {
- if (cc.line_num >= 0) line_num = cc.line_num;
- op1 += 1; /* put_x(n) get_x(n) -> set_x(n) */
- pos_next = cc.pos;
- }
- add_pc2line_info(s, bc_out.size, line_num);
- dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec));
- put_short_code(&bc_out, op1, idx);
- break;
- }
- if (code_match(&cc, pos_next, OP_perm3, OP_put_field, OP_drop, -1)) {
- if (cc.line_num >= 0) line_num = cc.line_num;
- add_pc2line_info(s, bc_out.size, line_num);
- dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec));
- dbuf_putc(&bc_out, OP_put_field);
- dbuf_put_u32(&bc_out, cc.atom);
- pos_next = cc.pos;
- break;
- }
- if (code_match(&cc, pos_next, OP_perm4, OP_put_array_el, OP_drop, -1)) {
- if (cc.line_num >= 0) line_num = cc.line_num;
- add_pc2line_info(s, bc_out.size, line_num);
- dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec));
- dbuf_putc(&bc_out, OP_put_array_el);
- pos_next = cc.pos;
- break;
- }
- }
- goto no_change;
- #if SHORT_OPCODES
- case OP_typeof:
- if (OPTIMIZE) {
- /* simplify typeof tests */
- if (code_match(&cc, pos_next, OP_push_atom_value, M4(OP_strict_eq, OP_strict_neq, OP_eq, OP_neq), -1)) {
- if (cc.line_num >= 0) line_num = cc.line_num;
- int op1 = (cc.op == OP_strict_eq || cc.op == OP_eq) ? OP_strict_eq : OP_strict_neq;
- int op2 = -1;
- switch (cc.atom) {
- case JS_ATOM_undefined:
- op2 = OP_typeof_is_undefined;
- break;
- case JS_ATOM_function:
- op2 = OP_typeof_is_function;
- break;
- }
- if (op2 >= 0) {
- /* transform typeof(s) == "<type>" into is_<type> */
- if (op1 == OP_strict_eq) {
- add_pc2line_info(s, bc_out.size, line_num);
- dbuf_putc(&bc_out, op2);
- JS_FreeAtom(ctx, cc.atom);
- pos_next = cc.pos;
- break;
- }
- if (op1 == OP_strict_neq && code_match(&cc, cc.pos, OP_if_false, -1)) {
- /* transform typeof(s) != "<type>" if_false into is_<type> if_true */
- if (cc.line_num >= 0) line_num = cc.line_num;
- add_pc2line_info(s, bc_out.size, line_num);
- dbuf_putc(&bc_out, op2);
- JS_FreeAtom(ctx, cc.atom);
- pos_next = cc.pos;
- label = cc.label;
- op = OP_if_true;
- goto has_label;
- }
- }
- }
- }
- goto no_change;
- #endif
- default:
- no_change:
- add_pc2line_info(s, bc_out.size, line_num);
- dbuf_put(&bc_out, bc_buf + pos, len);
- break;
- }
- }
- /* check that there were no missing labels */
- for(i = 0; i < s->label_count; i++) {
- assert(label_slots[i].first_reloc == NULL);
- }
- #if SHORT_OPCODES
- if (OPTIMIZE) {
- /* more jump optimizations */
- int patch_offsets = 0;
- for (i = 0, jp = s->jump_slots; i < s->jump_count; i++, jp++) {
- LabelSlot *ls;
- JumpSlot *jp1;
- int j, pos, diff, delta;
- delta = 3;
- switch (op = jp->op) {
- case OP_goto16:
- delta = 1;
- /* fall thru */
- case OP_if_false:
- case OP_if_true:
- case OP_goto:
- pos = jp->pos;
- diff = s->label_slots[jp->label].addr - pos;
- if (diff >= -128 && diff <= 127 + delta) {
- //put_u8(bc_out.buf + pos, diff);
- jp->size = 1;
- if (op == OP_goto16) {
- bc_out.buf[pos - 1] = jp->op = OP_goto8;
- } else {
- bc_out.buf[pos - 1] = jp->op = OP_if_false8 + (op - OP_if_false);
- }
- goto shrink;
- } else
- if (diff == (int16_t)diff && op == OP_goto) {
- //put_u16(bc_out.buf + pos, diff);
- jp->size = 2;
- delta = 2;
- bc_out.buf[pos - 1] = jp->op = OP_goto16;
- shrink:
- /* XXX: should reduce complexity, using 2 finger copy scheme */
- memmove(bc_out.buf + pos + jp->size, bc_out.buf + pos + jp->size + delta,
- bc_out.size - pos - jp->size - delta);
- bc_out.size -= delta;
- patch_offsets++;
- for (j = 0, ls = s->label_slots; j < s->label_count; j++, ls++) {
- if (ls->addr > pos)
- ls->addr -= delta;
- }
- for (j = i + 1, jp1 = jp + 1; j < s->jump_count; j++, jp1++) {
- if (jp1->pos > pos)
- jp1->pos -= delta;
- }
- for (j = 0; j < s->line_number_count; j++) {
- if (s->line_number_slots[j].pc > pos)
- s->line_number_slots[j].pc -= delta;
- }
- continue;
- }
- break;
- }
- }
- if (patch_offsets) {
- JumpSlot *jp1;
- int j;
- for (j = 0, jp1 = s->jump_slots; j < s->jump_count; j++, jp1++) {
- int diff1 = s->label_slots[jp1->label].addr - jp1->pos;
- switch (jp1->size) {
- case 1:
- put_u8(bc_out.buf + jp1->pos, diff1);
- break;
- case 2:
- put_u16(bc_out.buf + jp1->pos, diff1);
- break;
- case 4:
- put_u32(bc_out.buf + jp1->pos, diff1);
- break;
- }
- }
- }
- }
- js_free(ctx, s->jump_slots);
- s->jump_slots = NULL;
- #endif
- js_free(ctx, s->label_slots);
- s->label_slots = NULL;
- /* XXX: should delay until copying to runtime bytecode function */
- compute_pc2line_info(s);
- js_free(ctx, s->line_number_slots);
- s->line_number_slots = NULL;
- /* set the new byte code */
- dbuf_free(&s->byte_code);
- s->byte_code = bc_out;
- s->use_short_opcodes = TRUE;
- if (dbuf_error(&s->byte_code)) {
- JS_ThrowOutOfMemory(ctx);
- return -1;
- }
- return 0;
- fail:
- /* XXX: not safe */
- dbuf_free(&bc_out);
- return -1;
- }
- /* compute the maximum stack size needed by the function */
- typedef struct StackSizeState {
- int bc_len;
- int stack_len_max;
- uint16_t *stack_level_tab;
- int32_t *catch_pos_tab;
- int *pc_stack;
- int pc_stack_len;
- int pc_stack_size;
- } StackSizeState;
- /* 'op' is only used for error indication */
- static __exception int ss_check(JSContext *ctx, StackSizeState *s,
- int pos, int op, int stack_len, int catch_pos)
- {
- if ((unsigned)pos >= s->bc_len) {
- JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos);
- return -1;
- }
- if (stack_len > s->stack_len_max) {
- s->stack_len_max = stack_len;
- if (s->stack_len_max > JS_STACK_SIZE_MAX) {
- JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos);
- return -1;
- }
- }
- if (s->stack_level_tab[pos] != 0xffff) {
- /* already explored: check that the stack size is consistent */
- if (s->stack_level_tab[pos] != stack_len) {
- JS_ThrowInternalError(ctx, "inconsistent stack size: %d %d (pc=%d)",
- s->stack_level_tab[pos], stack_len, pos);
- return -1;
- } else if (s->catch_pos_tab[pos] != catch_pos) {
- JS_ThrowInternalError(ctx, "inconsistent catch position: %d %d (pc=%d)",
- s->catch_pos_tab[pos], catch_pos, pos);
- return -1;
- } else {
- return 0;
- }
- }
- /* mark as explored and store the stack size */
- s->stack_level_tab[pos] = stack_len;
- s->catch_pos_tab[pos] = catch_pos;
- /* queue the new PC to explore */
- if (js_resize_array(ctx, (void **)&s->pc_stack, sizeof(s->pc_stack[0]),
- &s->pc_stack_size, s->pc_stack_len + 1))
- return -1;
- s->pc_stack[s->pc_stack_len++] = pos;
- return 0;
- }
- static __exception int compute_stack_size(JSContext *ctx,
- JSFunctionDef *fd,
- int *pstack_size)
- {
- StackSizeState s_s, *s = &s_s;
- int i, diff, n_pop, pos_next, stack_len, pos, op, catch_pos, catch_level;
- const JSOpCode *oi;
- const uint8_t *bc_buf;
- bc_buf = fd->byte_code.buf;
- s->bc_len = fd->byte_code.size;
- /* bc_len > 0 */
- s->stack_level_tab = js_malloc(ctx, sizeof(s->stack_level_tab[0]) *
- s->bc_len);
- if (!s->stack_level_tab)
- return -1;
- for(i = 0; i < s->bc_len; i++)
- s->stack_level_tab[i] = 0xffff;
- s->pc_stack = NULL;
- s->catch_pos_tab = js_malloc(ctx, sizeof(s->catch_pos_tab[0]) *
- s->bc_len);
- if (!s->catch_pos_tab)
- goto fail;
- s->stack_len_max = 0;
- s->pc_stack_len = 0;
- s->pc_stack_size = 0;
- /* breadth-first graph exploration */
- if (ss_check(ctx, s, 0, OP_invalid, 0, -1))
- goto fail;
- while (s->pc_stack_len > 0) {
- pos = s->pc_stack[--s->pc_stack_len];
- stack_len = s->stack_level_tab[pos];
- catch_pos = s->catch_pos_tab[pos];
- op = bc_buf[pos];
- if (op == 0 || op >= OP_COUNT) {
- JS_ThrowInternalError(ctx, "invalid opcode (op=%d, pc=%d)", op, pos);
- goto fail;
- }
- oi = &short_opcode_info(op);
- #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 64)
- printf("%5d: %10s %5d %5d\n", pos, oi->name, stack_len, catch_pos);
- #endif
- pos_next = pos + oi->size;
- if (pos_next > s->bc_len) {
- JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos);
- goto fail;
- }
- n_pop = oi->n_pop;
- /* call pops a variable number of arguments */
- if (oi->fmt == OP_FMT_npop || oi->fmt == OP_FMT_npop_u16) {
- n_pop += get_u16(bc_buf + pos + 1);
- } else {
- #if SHORT_OPCODES
- if (oi->fmt == OP_FMT_npopx) {
- n_pop += op - OP_call0;
- }
- #endif
- }
- if (stack_len < n_pop) {
- JS_ThrowInternalError(ctx, "stack underflow (op=%d, pc=%d)", op, pos);
- goto fail;
- }
- stack_len += oi->n_push - n_pop;
- if (stack_len > s->stack_len_max) {
- s->stack_len_max = stack_len;
- if (s->stack_len_max > JS_STACK_SIZE_MAX) {
- JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos);
- goto fail;
- }
- }
- switch(op) {
- case OP_tail_call:
- case OP_tail_call_method:
- case OP_return:
- case OP_return_undef:
- case OP_return_async:
- case OP_throw:
- case OP_throw_error:
- case OP_ret:
- goto done_insn;
- case OP_goto:
- diff = get_u32(bc_buf + pos + 1);
- pos_next = pos + 1 + diff;
- break;
- #if SHORT_OPCODES
- case OP_goto16:
- diff = (int16_t)get_u16(bc_buf + pos + 1);
- pos_next = pos + 1 + diff;
- break;
- case OP_goto8:
- diff = (int8_t)bc_buf[pos + 1];
- pos_next = pos + 1 + diff;
- break;
- case OP_if_true8:
- case OP_if_false8:
- diff = (int8_t)bc_buf[pos + 1];
- if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos))
- goto fail;
- break;
- #endif
- case OP_if_true:
- case OP_if_false:
- diff = get_u32(bc_buf + pos + 1);
- if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos))
- goto fail;
- break;
- case OP_gosub:
- diff = get_u32(bc_buf + pos + 1);
- if (ss_check(ctx, s, pos + 1 + diff, op, stack_len + 1, catch_pos))
- goto fail;
- break;
- case OP_with_get_var:
- case OP_with_delete_var:
- diff = get_u32(bc_buf + pos + 5);
- if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 1, catch_pos))
- goto fail;
- break;
- case OP_with_make_ref:
- case OP_with_get_ref:
- diff = get_u32(bc_buf + pos + 5);
- if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 2, catch_pos))
- goto fail;
- break;
- case OP_with_put_var:
- diff = get_u32(bc_buf + pos + 5);
- if (ss_check(ctx, s, pos + 5 + diff, op, stack_len - 1, catch_pos))
- goto fail;
- break;
- case OP_catch:
- diff = get_u32(bc_buf + pos + 1);
- if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos))
- goto fail;
- catch_pos = pos;
- break;
- case OP_for_of_start:
- case OP_for_await_of_start:
- catch_pos = pos;
- break;
- /* we assume the catch offset entry is only removed with
- some op codes */
- case OP_drop:
- catch_level = stack_len;
- goto check_catch;
- case OP_nip:
- catch_level = stack_len - 1;
- goto check_catch;
- case OP_nip1:
- catch_level = stack_len - 1;
- goto check_catch;
- case OP_iterator_close:
- catch_level = stack_len + 2;
- check_catch:
- /* Note: for for_of_start/for_await_of_start we consider
- the catch offset is on the first stack entry instead of
- the thirst */
- if (catch_pos >= 0) {
- int level;
- level = s->stack_level_tab[catch_pos];
- if (bc_buf[catch_pos] != OP_catch)
- level++; /* for_of_start, for_wait_of_start */
- /* catch_level = stack_level before op_catch is executed ? */
- if (catch_level == level) {
- catch_pos = s->catch_pos_tab[catch_pos];
- }
- }
- break;
- case OP_nip_catch:
- if (catch_pos < 0) {
- JS_ThrowInternalError(ctx, "nip_catch: no catch op (pc=%d)", pos);
- goto fail;
- }
- stack_len = s->stack_level_tab[catch_pos];
- if (bc_buf[catch_pos] != OP_catch)
- stack_len++; /* for_of_start, for_wait_of_start */
- stack_len++; /* no stack overflow is possible by construction */
- catch_pos = s->catch_pos_tab[catch_pos];
- break;
- default:
- break;
- }
- if (ss_check(ctx, s, pos_next, op, stack_len, catch_pos))
- goto fail;
- done_insn: ;
- }
- js_free(ctx, s->pc_stack);
- js_free(ctx, s->catch_pos_tab);
- js_free(ctx, s->stack_level_tab);
- *pstack_size = s->stack_len_max;
- return 0;
- fail:
- js_free(ctx, s->pc_stack);
- js_free(ctx, s->catch_pos_tab);
- js_free(ctx, s->stack_level_tab);
- *pstack_size = 0;
- return -1;
- }
- static int add_global_variables(JSContext *ctx, JSFunctionDef *fd)
- {
- int i, idx;
- JSModuleDef *m = fd->module;
- JSExportEntry *me;
- JSGlobalVar *hf;
- BOOL need_global_closures;
-
- /* Script: add the defined global variables. In the non strict
- direct eval not in global scope, the global variables are
- created in the enclosing scope so they are not created as
- variable references.
- In modules, the imported global variables were added as closure
- global variables in js_parse_import().
- */
- need_global_closures = TRUE;
- if (fd->eval_type == JS_EVAL_TYPE_DIRECT && !(fd->js_mode & JS_MODE_STRICT)) {
- /* XXX: add a flag ? */
- for(idx = 0; idx < fd->closure_var_count; idx++) {
- JSClosureVar *cv = &fd->closure_var[idx];
- if (cv->var_name == JS_ATOM__var_ ||
- cv->var_name == JS_ATOM__arg_var_) {
- need_global_closures = FALSE;
- break;
- }
- }
- }
- if (need_global_closures) {
- JSClosureTypeEnum closure_type;
- if (fd->module)
- closure_type = JS_CLOSURE_MODULE_DECL;
- else
- closure_type = JS_CLOSURE_GLOBAL_DECL;
- for(i = 0; i < fd->global_var_count; i++) {
- JSVarKindEnum var_kind;
- hf = &fd->global_vars[i];
- if (hf->cpool_idx >= 0 && !hf->is_lexical) {
- var_kind = JS_VAR_GLOBAL_FUNCTION_DECL;
- } else {
- var_kind = JS_VAR_NORMAL;
- }
- if (add_closure_var(ctx, fd, closure_type, i, hf->var_name, hf->is_const,
- hf->is_lexical, var_kind) < 0)
- return -1;
- }
- }
- if (fd->module) {
- /* resolve the variable names of the local exports */
- for(i = 0; i < m->export_entries_count; i++) {
- me = &m->export_entries[i];
- if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
- idx = find_closure_var(ctx, fd, me->local_name);
- if (idx < 0) {
- JS_ThrowSyntaxErrorAtom(ctx, "exported variable '%s' does not exist",
- me->local_name);
- return -1;
- }
- me->u.local.var_idx = idx;
- }
- }
- }
- return 0;
- }
- /* create a function object from a function definition. The function
- definition is freed. All the child functions are also created. It
- must be done this way to resolve all the variables. */
- static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd)
- {
- JSValue func_obj;
- JSFunctionBytecode *b;
- struct list_head *el, *el1;
- int stack_size, scope, idx;
- int function_size, byte_code_offset, cpool_offset;
- int closure_var_offset, vardefs_offset;
- BOOL strip_var_debug;
-
- /* recompute scope linkage */
- for (scope = 0; scope < fd->scope_count; scope++) {
- fd->scopes[scope].first = -1;
- }
- if (fd->has_parameter_expressions) {
- /* special end of variable list marker for the argument scope */
- fd->scopes[ARG_SCOPE_INDEX].first = ARG_SCOPE_END;
- }
- for (idx = 0; idx < fd->var_count; idx++) {
- JSVarDef *vd = &fd->vars[idx];
- vd->scope_next = fd->scopes[vd->scope_level].first;
- fd->scopes[vd->scope_level].first = idx;
- }
- for (scope = 2; scope < fd->scope_count; scope++) {
- JSVarScope *sd = &fd->scopes[scope];
- if (sd->first < 0)
- sd->first = fd->scopes[sd->parent].first;
- }
- for (idx = 0; idx < fd->var_count; idx++) {
- JSVarDef *vd = &fd->vars[idx];
- if (vd->scope_next < 0 && vd->scope_level > 1) {
- scope = fd->scopes[vd->scope_level].parent;
- vd->scope_next = fd->scopes[scope].first;
- }
- }
- /* if the function contains an eval call, the closure variables
- are used to compile the eval and they must be ordered by scope,
- so it is necessary to create the closure variables before any
- other variable lookup is done. */
- if (fd->has_eval_call)
- add_eval_variables(ctx, fd);
- /* add the module global variables in the closure */
- if (fd->is_eval) {
- if (add_global_variables(ctx, fd))
- goto fail;
- }
- /* first create all the child functions */
- list_for_each_safe(el, el1, &fd->child_list) {
- JSFunctionDef *fd1;
- int cpool_idx;
- fd1 = list_entry(el, JSFunctionDef, link);
- cpool_idx = fd1->parent_cpool_idx;
- func_obj = js_create_function(ctx, fd1);
- if (JS_IsException(func_obj))
- goto fail;
- /* save it in the constant pool */
- assert(cpool_idx >= 0);
- fd->cpool[cpool_idx] = func_obj;
- }
- #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 4)
- if (!fd->strip_debug) {
- printf("pass 1\n");
- dump_byte_code(ctx, 1, fd->byte_code.buf, fd->byte_code.size,
- NULL, fd->args, fd->arg_count, fd->vars, fd->var_count,
- fd->closure_var, fd->closure_var_count,
- fd->cpool, fd->cpool_count, fd->source,
- fd->label_slots, NULL);
- printf("\n");
- }
- #endif
- if (resolve_variables(ctx, fd))
- goto fail;
- #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 2)
- if (!fd->strip_debug) {
- printf("pass 2\n");
- dump_byte_code(ctx, 2, fd->byte_code.buf, fd->byte_code.size,
- NULL, fd->args, fd->arg_count, fd->vars, fd->var_count,
- fd->closure_var, fd->closure_var_count,
- fd->cpool, fd->cpool_count, fd->source,
- fd->label_slots, NULL);
- printf("\n");
- }
- #endif
- if (resolve_labels(ctx, fd))
- goto fail;
- if (compute_stack_size(ctx, fd, &stack_size) < 0)
- goto fail;
- if (fd->strip_debug) {
- function_size = offsetof(JSFunctionBytecode, debug);
- } else {
- function_size = sizeof(*b);
- }
- cpool_offset = function_size;
- function_size += fd->cpool_count * sizeof(*fd->cpool);
- vardefs_offset = function_size;
- function_size += (fd->arg_count + fd->var_count) * sizeof(*b->vardefs);
- closure_var_offset = function_size;
- function_size += fd->closure_var_count * sizeof(*fd->closure_var);
- byte_code_offset = function_size;
- function_size += fd->byte_code.size;
- b = js_mallocz(ctx, function_size);
- if (!b)
- goto fail;
- js_rc(b)->ref_count = 1;
- b->byte_code_buf = (void *)((uint8_t*)b + byte_code_offset);
- b->byte_code_len = fd->byte_code.size;
- memcpy(b->byte_code_buf, fd->byte_code.buf, fd->byte_code.size);
- js_free(ctx, fd->byte_code.buf);
- fd->byte_code.buf = NULL;
- strip_var_debug = fd->strip_debug && !fd->has_eval_call; /* XXX: check */
- b->func_name = fd->func_name;
- if (fd->arg_count + fd->var_count > 0) {
- int i;
- b->vardefs = (void *)((uint8_t*)b + vardefs_offset);
- for(i = 0; i < fd->arg_count; i++) {
- JSVarDef *vd = &fd->args[i];
- JSBytecodeVarDef *vd1 = &b->vardefs[i];
- if (strip_var_debug) {
- JS_FreeAtom(ctx, vd->var_name);
- vd1->var_name = JS_ATOM_NULL;
- } else {
- vd1->var_name = vd->var_name;
- }
- vd1->has_scope = (vd->scope_level != 0);
- vd1->scope_next = vd->scope_next;
- vd1->is_const = vd->is_const;
- vd1->is_lexical = vd->is_lexical;
- vd1->is_captured = vd->is_captured;
- vd1->var_kind = vd->var_kind;
- vd1->var_ref_idx = vd->var_ref_idx;
- }
-
- for(i = 0; i < fd->var_count; i++) {
- JSVarDef *vd = &fd->vars[i];
- JSBytecodeVarDef *vd1 = &b->vardefs[i + fd->arg_count];
- if (strip_var_debug) {
- JS_FreeAtom(ctx, vd->var_name);
- vd1->var_name = JS_ATOM_NULL;
- } else {
- vd1->var_name = vd->var_name;
- }
- vd1->has_scope = (vd->scope_level != 0);
- vd1->scope_next = vd->scope_next;
- vd1->is_const = vd->is_const;
- vd1->is_lexical = vd->is_lexical;
- vd1->is_captured = vd->is_captured;
- vd1->var_kind = vd->var_kind;
- vd1->var_ref_idx = vd->var_ref_idx;
- }
- b->var_count = fd->var_count;
- b->arg_count = fd->arg_count;
- b->defined_arg_count = fd->defined_arg_count;
- b->var_ref_count = fd->var_ref_count;
- js_free(ctx, fd->args);
- js_free(ctx, fd->vars);
- }
- b->cpool_count = fd->cpool_count;
- if (b->cpool_count) {
- b->cpool = (void *)((uint8_t*)b + cpool_offset);
- memcpy(b->cpool, fd->cpool, b->cpool_count * sizeof(*b->cpool));
- }
- js_free(ctx, fd->cpool);
- fd->cpool = NULL;
- b->stack_size = stack_size;
- if (fd->strip_debug) {
- JS_FreeAtom(ctx, fd->filename);
- dbuf_free(&fd->pc2line); // probably useless
- } else {
- /* XXX: source and pc2line info should be packed at the end of the
- JSFunctionBytecode structure, avoiding allocation overhead
- */
- b->has_debug = 1;
- b->debug.filename = fd->filename;
- //DynBuf pc2line;
- //compute_pc2line_info(fd, &pc2line);
- //js_free(ctx, fd->line_number_slots)
- b->debug.pc2line_buf = js_realloc(ctx, fd->pc2line.buf, fd->pc2line.size);
- if (!b->debug.pc2line_buf)
- b->debug.pc2line_buf = fd->pc2line.buf;
- b->debug.pc2line_len = fd->pc2line.size;
- b->debug.source = fd->source;
- b->debug.source_len = fd->source_len;
- }
- if (fd->scopes != fd->def_scope_array)
- js_free(ctx, fd->scopes);
- b->closure_var_count = fd->closure_var_count;
- if (b->closure_var_count) {
- if (strip_var_debug) {
- int i;
- for(i = 0; i < fd->closure_var_count; i++) {
- JSClosureVar *cv = &fd->closure_var[i];
- if (cv->closure_type != JS_CLOSURE_GLOBAL_REF &&
- cv->closure_type != JS_CLOSURE_GLOBAL_DECL &&
- cv->closure_type != JS_CLOSURE_GLOBAL &&
- cv->closure_type != JS_CLOSURE_MODULE_DECL &&
- cv->closure_type != JS_CLOSURE_MODULE_IMPORT) {
- JS_FreeAtom(ctx, cv->var_name);
- cv->var_name = JS_ATOM_NULL;
- }
- }
- }
- b->closure_var = (void *)((uint8_t*)b + closure_var_offset);
- memcpy(b->closure_var, fd->closure_var, b->closure_var_count * sizeof(*b->closure_var));
- }
- js_free(ctx, fd->closure_var);
- fd->closure_var = NULL;
- b->has_prototype = fd->has_prototype;
- b->has_simple_parameter_list = fd->has_simple_parameter_list;
- b->js_mode = fd->js_mode;
- b->is_derived_class_constructor = fd->is_derived_class_constructor;
- b->func_kind = fd->func_kind;
- b->need_home_object = (fd->home_object_var_idx >= 0 ||
- fd->need_home_object);
- b->new_target_allowed = fd->new_target_allowed;
- b->super_call_allowed = fd->super_call_allowed;
- b->super_allowed = fd->super_allowed;
- b->arguments_allowed = fd->arguments_allowed;
- b->is_direct_or_indirect_eval = (fd->eval_type == JS_EVAL_TYPE_DIRECT ||
- fd->eval_type == JS_EVAL_TYPE_INDIRECT);
- b->realm = JS_DupContext(ctx);
- add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);
- #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 1)
- if (!fd->strip_debug) {
- js_dump_function_bytecode(ctx, b);
- }
- #endif
- if (fd->parent) {
- /* remove from parent list */
- list_del(&fd->link);
- }
- js_free(ctx, fd);
- return JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b);
- fail:
- js_free_function_def(ctx, fd);
- return JS_EXCEPTION;
- }
- static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b)
- {
- int i;
- #if 0
- {
- char buf[ATOM_GET_STR_BUF_SIZE];
- printf("freeing %s\n",
- JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name));
- }
- #endif
- if (b->byte_code_buf)
- free_bytecode_atoms(rt, b->byte_code_buf, b->byte_code_len, TRUE);
- if (b->vardefs) {
- for(i = 0; i < b->arg_count + b->var_count; i++) {
- JS_FreeAtomRT(rt, b->vardefs[i].var_name);
- }
- }
- for(i = 0; i < b->cpool_count; i++)
- JS_FreeValueRT(rt, b->cpool[i]);
- for(i = 0; i < b->closure_var_count; i++) {
- JSClosureVar *cv = &b->closure_var[i];
- JS_FreeAtomRT(rt, cv->var_name);
- }
- if (b->realm)
- JS_FreeContext(b->realm);
- JS_FreeAtomRT(rt, b->func_name);
- if (b->has_debug) {
- JS_FreeAtomRT(rt, b->debug.filename);
- js_free_rt(rt, b->debug.pc2line_buf);
- js_free_rt(rt, b->debug.source);
- }
- remove_gc_object(&b->header);
- if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && js_rc(b)->ref_count != 0) {
- list_add_tail(&b->header.link, &rt->gc_zero_ref_count_list);
- } else {
- js_free_rt(rt, b);
- }
- }
- static __exception int js_parse_directives(JSParseState *s)
- {
- char str[20];
- JSParsePos pos;
- BOOL has_semi;
- if (s->token.val != TOK_STRING)
- return 0;
- js_parse_get_pos(s, &pos);
- while(s->token.val == TOK_STRING) {
- /* Copy actual source string representation */
- snprintf(str, sizeof str, "%.*s",
- (int)(s->buf_ptr - s->token.ptr - 2), s->token.ptr + 1);
- if (next_token(s))
- return -1;
- has_semi = FALSE;
- switch (s->token.val) {
- case ';':
- if (next_token(s))
- return -1;
- has_semi = TRUE;
- break;
- case '}':
- case TOK_EOF:
- has_semi = TRUE;
- break;
- case TOK_NUMBER:
- case TOK_STRING:
- case TOK_TEMPLATE:
- case TOK_IDENT:
- case TOK_REGEXP:
- case TOK_DEC:
- case TOK_INC:
- case TOK_NULL:
- case TOK_FALSE:
- case TOK_TRUE:
- case TOK_IF:
- case TOK_RETURN:
- case TOK_VAR:
- case TOK_THIS:
- case TOK_DELETE:
- case TOK_TYPEOF:
- case TOK_NEW:
- case TOK_DO:
- case TOK_WHILE:
- case TOK_FOR:
- case TOK_SWITCH:
- case TOK_THROW:
- case TOK_TRY:
- case TOK_FUNCTION:
- case TOK_DEBUGGER:
- case TOK_WITH:
- case TOK_CLASS:
- case TOK_CONST:
- case TOK_ENUM:
- case TOK_EXPORT:
- case TOK_IMPORT:
- case TOK_SUPER:
- case TOK_INTERFACE:
- case TOK_LET:
- case TOK_PACKAGE:
- case TOK_PRIVATE:
- case TOK_PROTECTED:
- case TOK_PUBLIC:
- case TOK_STATIC:
- /* automatic insertion of ';' */
- if (s->got_lf)
- has_semi = TRUE;
- break;
- default:
- break;
- }
- if (!has_semi)
- break;
- if (!strcmp(str, "use strict")) {
- s->cur_func->has_use_strict = TRUE;
- s->cur_func->js_mode |= JS_MODE_STRICT;
- }
- }
- return js_parse_seek_token(s, &pos);
- }
- /* return TRUE if the keyword is forbidden only in strict mode */
- static BOOL is_strict_future_keyword(JSAtom atom)
- {
- return (atom >= JS_ATOM_LAST_KEYWORD + 1 && atom <= JS_ATOM_LAST_STRICT_KEYWORD);
- }
- static int js_parse_function_check_names(JSParseState *s, JSFunctionDef *fd,
- JSAtom func_name)
- {
- JSAtom name;
- int i, idx;
- if (fd->js_mode & JS_MODE_STRICT) {
- if (!fd->has_simple_parameter_list && fd->has_use_strict) {
- return js_parse_error(s, "\"use strict\" not allowed in function with default or destructuring parameter");
- }
- if (func_name == JS_ATOM_eval || func_name == JS_ATOM_arguments ||
- is_strict_future_keyword(func_name)) {
- return js_parse_error(s, "invalid function name in strict code");
- }
- for (idx = 0; idx < fd->arg_count; idx++) {
- name = fd->args[idx].var_name;
- if (name == JS_ATOM_eval || name == JS_ATOM_arguments ||
- is_strict_future_keyword(name)) {
- return js_parse_error(s, "invalid argument name in strict code");
- }
- }
- }
- /* check async_generator case */
- if ((fd->js_mode & JS_MODE_STRICT)
- || !fd->has_simple_parameter_list
- || (fd->func_type == JS_PARSE_FUNC_METHOD && fd->func_kind == JS_FUNC_ASYNC)
- || fd->func_type == JS_PARSE_FUNC_ARROW
- || fd->func_type == JS_PARSE_FUNC_METHOD) {
- for (idx = 0; idx < fd->arg_count; idx++) {
- name = fd->args[idx].var_name;
- if (name != JS_ATOM_NULL) {
- for (i = 0; i < idx; i++) {
- if (fd->args[i].var_name == name)
- goto duplicate;
- }
- /* Check if argument name duplicates a destructuring parameter */
- /* XXX: should have a flag for such variables */
- for (i = 0; i < fd->var_count; i++) {
- if (fd->vars[i].var_name == name &&
- fd->vars[i].scope_level == 0)
- goto duplicate;
- }
- }
- }
- }
- return 0;
- duplicate:
- return js_parse_error(s, "duplicate argument names not allowed in this context");
- }
- /* create a function to initialize class fields */
- static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s)
- {
- JSFunctionDef *fd;
- fd = js_new_function_def(s->ctx, s->cur_func, FALSE, FALSE,
- s->filename, s->buf_start,
- &s->get_line_col_cache);
- if (!fd)
- return NULL;
- fd->func_name = JS_ATOM_NULL;
- fd->has_prototype = FALSE;
- fd->has_home_object = TRUE;
- fd->has_arguments_binding = FALSE;
- fd->has_this_binding = TRUE;
- fd->is_derived_class_constructor = FALSE;
- fd->new_target_allowed = TRUE;
- fd->super_call_allowed = FALSE;
- fd->super_allowed = fd->has_home_object;
- fd->arguments_allowed = FALSE;
- fd->func_kind = JS_FUNC_NORMAL;
- fd->func_type = JS_PARSE_FUNC_METHOD;
- return fd;
- }
- /* func_name must be JS_ATOM_NULL for JS_PARSE_FUNC_STATEMENT and
- JS_PARSE_FUNC_EXPR, JS_PARSE_FUNC_ARROW and JS_PARSE_FUNC_VAR */
- static __exception int js_parse_function_decl2(JSParseState *s,
- JSParseFunctionEnum func_type,
- JSFunctionKindEnum func_kind,
- JSAtom func_name,
- const uint8_t *ptr,
- JSParseExportEnum export_flag,
- JSFunctionDef **pfd)
- {
- JSContext *ctx = s->ctx;
- JSFunctionDef *fd = s->cur_func;
- BOOL is_expr;
- int func_idx, lexical_func_idx = -1;
- BOOL has_opt_arg;
- BOOL create_func_var = FALSE;
- is_expr = (func_type != JS_PARSE_FUNC_STATEMENT &&
- func_type != JS_PARSE_FUNC_VAR);
- if (func_type == JS_PARSE_FUNC_STATEMENT ||
- func_type == JS_PARSE_FUNC_VAR ||
- func_type == JS_PARSE_FUNC_EXPR) {
- if (func_kind == JS_FUNC_NORMAL &&
- token_is_pseudo_keyword(s, JS_ATOM_async) &&
- peek_token(s, TRUE) != '\n') {
- if (next_token(s))
- return -1;
- func_kind = JS_FUNC_ASYNC;
- }
- if (next_token(s))
- return -1;
- if (s->token.val == '*') {
- if (next_token(s))
- return -1;
- func_kind |= JS_FUNC_GENERATOR;
- }
- if (s->token.val == TOK_IDENT) {
- if (s->token.u.ident.is_reserved ||
- (s->token.u.ident.atom == JS_ATOM_yield &&
- func_type == JS_PARSE_FUNC_EXPR &&
- (func_kind & JS_FUNC_GENERATOR)) ||
- (s->token.u.ident.atom == JS_ATOM_await &&
- ((func_type == JS_PARSE_FUNC_EXPR &&
- (func_kind & JS_FUNC_ASYNC)) ||
- func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT))) {
- return js_parse_error_reserved_identifier(s);
- }
- }
- if (s->token.val == TOK_IDENT ||
- (((s->token.val == TOK_YIELD && !(fd->js_mode & JS_MODE_STRICT)) ||
- (s->token.val == TOK_AWAIT && !s->is_module)) &&
- func_type == JS_PARSE_FUNC_EXPR)) {
- func_name = JS_DupAtom(ctx, s->token.u.ident.atom);
- if (next_token(s)) {
- JS_FreeAtom(ctx, func_name);
- return -1;
- }
- } else {
- if (func_type != JS_PARSE_FUNC_EXPR &&
- export_flag != JS_PARSE_EXPORT_DEFAULT) {
- return js_parse_error(s, "function name expected");
- }
- }
- } else if (func_type != JS_PARSE_FUNC_ARROW) {
- func_name = JS_DupAtom(ctx, func_name);
- }
- if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_MODULE &&
- (func_type == JS_PARSE_FUNC_STATEMENT || func_type == JS_PARSE_FUNC_VAR)) {
- JSGlobalVar *hf;
- hf = find_global_var(fd, func_name);
- /* XXX: should check scope chain */
- if (hf && hf->scope_level == fd->scope_level) {
- js_parse_error(s, "invalid redefinition of global identifier in module code");
- JS_FreeAtom(ctx, func_name);
- return -1;
- }
- }
- if (func_type == JS_PARSE_FUNC_VAR) {
- if (!(fd->js_mode & JS_MODE_STRICT)
- && func_kind == JS_FUNC_NORMAL
- && find_lexical_decl(ctx, fd, func_name, fd->scope_first, FALSE) < 0
- && !((func_idx = find_var(ctx, fd, func_name)) >= 0 && (func_idx & ARGUMENT_VAR_OFFSET))
- && !(func_name == JS_ATOM_arguments && fd->has_arguments_binding)) {
- create_func_var = TRUE;
- }
- /* Create the lexical name here so that the function closure
- contains it */
- if (fd->is_eval &&
- (fd->eval_type == JS_EVAL_TYPE_GLOBAL ||
- fd->eval_type == JS_EVAL_TYPE_MODULE) &&
- fd->scope_level == fd->body_scope) {
- /* avoid creating a lexical variable in the global
- scope. XXX: check annex B */
- JSGlobalVar *hf;
- hf = find_global_var(fd, func_name);
- /* XXX: should check scope chain */
- if (hf && hf->scope_level == fd->scope_level) {
- js_parse_error(s, "invalid redefinition of global identifier");
- JS_FreeAtom(ctx, func_name);
- return -1;
- }
- } else {
- /* Always create a lexical name, fail if at the same scope as
- existing name */
- /* Lexical variable will be initialized upon entering scope */
- lexical_func_idx = define_var(s, fd, func_name,
- func_kind != JS_FUNC_NORMAL ?
- JS_VAR_DEF_NEW_FUNCTION_DECL :
- JS_VAR_DEF_FUNCTION_DECL);
- if (lexical_func_idx < 0) {
- JS_FreeAtom(ctx, func_name);
- return -1;
- }
- }
- }
- fd = js_new_function_def(ctx, fd, FALSE, is_expr,
- s->filename, ptr,
- &s->get_line_col_cache);
- if (!fd) {
- JS_FreeAtom(ctx, func_name);
- return -1;
- }
- if (pfd)
- *pfd = fd;
- s->cur_func = fd;
- fd->func_name = func_name;
- /* XXX: test !fd->is_generator is always false */
- fd->has_prototype = (func_type == JS_PARSE_FUNC_STATEMENT ||
- func_type == JS_PARSE_FUNC_VAR ||
- func_type == JS_PARSE_FUNC_EXPR) &&
- func_kind == JS_FUNC_NORMAL;
- fd->has_home_object = (func_type == JS_PARSE_FUNC_METHOD ||
- func_type == JS_PARSE_FUNC_GETTER ||
- func_type == JS_PARSE_FUNC_SETTER ||
- func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR ||
- func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR);
- fd->has_arguments_binding = (func_type != JS_PARSE_FUNC_ARROW &&
- func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT);
- fd->has_this_binding = fd->has_arguments_binding;
- fd->is_derived_class_constructor = (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR);
- if (func_type == JS_PARSE_FUNC_ARROW) {
- fd->new_target_allowed = fd->parent->new_target_allowed;
- fd->super_call_allowed = fd->parent->super_call_allowed;
- fd->super_allowed = fd->parent->super_allowed;
- fd->arguments_allowed = fd->parent->arguments_allowed;
- } else if (func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT) {
- fd->new_target_allowed = TRUE; // although new.target === undefined
- fd->super_call_allowed = FALSE;
- fd->super_allowed = TRUE;
- fd->arguments_allowed = FALSE;
- } else {
- fd->new_target_allowed = TRUE;
- fd->super_call_allowed = fd->is_derived_class_constructor;
- fd->super_allowed = fd->has_home_object;
- fd->arguments_allowed = TRUE;
- }
- /* fd->in_function_body == FALSE prevents yield/await during the parsing
- of the arguments in generator/async functions. They are parsed as
- regular identifiers for other function kinds. */
- fd->func_kind = func_kind;
- fd->func_type = func_type;
- if (func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR ||
- func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR) {
- /* error if not invoked as a constructor */
- emit_op(s, OP_check_ctor);
- }
- if (func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) {
- emit_class_field_init(s);
- }
- /* parse arguments */
- fd->has_simple_parameter_list = TRUE;
- fd->has_parameter_expressions = FALSE;
- has_opt_arg = FALSE;
- if (func_type == JS_PARSE_FUNC_ARROW && s->token.val == TOK_IDENT) {
- JSAtom name;
- if (s->token.u.ident.is_reserved) {
- js_parse_error_reserved_identifier(s);
- goto fail;
- }
- name = s->token.u.ident.atom;
- if (add_arg(ctx, fd, name) < 0)
- goto fail;
- fd->defined_arg_count = 1;
- } else if (func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT) {
- if (s->token.val == '(') {
- int skip_bits;
- /* if there is an '=' inside the parameter list, we
- consider there is a parameter expression inside */
- js_parse_skip_parens_token(s, &skip_bits, FALSE);
- if (skip_bits & SKIP_HAS_ASSIGNMENT)
- fd->has_parameter_expressions = TRUE;
- if (next_token(s))
- goto fail;
- } else {
- if (js_parse_expect(s, '('))
- goto fail;
- }
- if (fd->has_parameter_expressions) {
- fd->scope_level = -1; /* force no parent scope */
- if (push_scope(s) < 0)
- return -1;
- }
- while (s->token.val != ')') {
- JSAtom name;
- BOOL rest = FALSE;
- int idx, has_initializer;
- if (s->token.val == TOK_ELLIPSIS) {
- if (func_type == JS_PARSE_FUNC_SETTER)
- goto fail_accessor;
- fd->has_simple_parameter_list = FALSE;
- rest = TRUE;
- if (next_token(s))
- goto fail;
- }
- if (s->token.val == '[' || s->token.val == '{') {
- fd->has_simple_parameter_list = FALSE;
- if (rest) {
- emit_op(s, OP_rest);
- emit_u16(s, fd->arg_count);
- } else {
- /* unnamed arg for destructuring */
- idx = add_arg(ctx, fd, JS_ATOM_NULL);
- emit_op(s, OP_get_arg);
- emit_u16(s, idx);
- }
- has_initializer = js_parse_destructuring_element(s, fd->has_parameter_expressions ? TOK_LET : TOK_VAR, 1, TRUE, -1, TRUE, FALSE);
- if (has_initializer < 0)
- goto fail;
- if (has_initializer)
- has_opt_arg = TRUE;
- if (!has_opt_arg)
- fd->defined_arg_count++;
- } else if (s->token.val == TOK_IDENT) {
- if (s->token.u.ident.is_reserved) {
- js_parse_error_reserved_identifier(s);
- goto fail;
- }
- name = s->token.u.ident.atom;
- if (name == JS_ATOM_yield && fd->func_kind == JS_FUNC_GENERATOR) {
- js_parse_error_reserved_identifier(s);
- goto fail;
- }
- if (fd->has_parameter_expressions) {
- if (js_parse_check_duplicate_parameter(s, name))
- goto fail;
- if (define_var(s, fd, name, JS_VAR_DEF_LET) < 0)
- goto fail;
- }
- /* XXX: could avoid allocating an argument if rest is true */
- idx = add_arg(ctx, fd, name);
- if (idx < 0)
- goto fail;
- if (next_token(s))
- goto fail;
- if (rest) {
- emit_op(s, OP_rest);
- emit_u16(s, idx);
- if (fd->has_parameter_expressions) {
- emit_op(s, OP_dup);
- emit_op(s, OP_scope_put_var_init);
- emit_atom(s, name);
- emit_u16(s, fd->scope_level);
- }
- emit_op(s, OP_put_arg);
- emit_u16(s, idx);
- fd->has_simple_parameter_list = FALSE;
- has_opt_arg = TRUE;
- } else if (s->token.val == '=') {
- int label;
- fd->has_simple_parameter_list = FALSE;
- has_opt_arg = TRUE;
- if (next_token(s))
- goto fail;
- label = new_label(s);
- emit_op(s, OP_get_arg);
- emit_u16(s, idx);
- emit_op(s, OP_dup);
- emit_op(s, OP_undefined);
- emit_op(s, OP_strict_eq);
- emit_goto(s, OP_if_false, label);
- emit_op(s, OP_drop);
- if (js_parse_assign_expr(s))
- goto fail;
- set_object_name(s, name);
- emit_op(s, OP_dup);
- emit_op(s, OP_put_arg);
- emit_u16(s, idx);
- emit_label(s, label);
- emit_op(s, OP_scope_put_var_init);
- emit_atom(s, name);
- emit_u16(s, fd->scope_level);
- } else {
- if (!has_opt_arg) {
- fd->defined_arg_count++;
- }
- if (fd->has_parameter_expressions) {
- /* copy the argument to the argument scope */
- emit_op(s, OP_get_arg);
- emit_u16(s, idx);
- emit_op(s, OP_scope_put_var_init);
- emit_atom(s, name);
- emit_u16(s, fd->scope_level);
- }
- }
- } else {
- js_parse_error(s, "missing formal parameter");
- goto fail;
- }
- if (rest && s->token.val != ')') {
- js_parse_expect(s, ')');
- goto fail;
- }
- if (s->token.val == ')')
- break;
- if (js_parse_expect(s, ','))
- goto fail;
- }
- if ((func_type == JS_PARSE_FUNC_GETTER && fd->arg_count != 0) ||
- (func_type == JS_PARSE_FUNC_SETTER && fd->arg_count != 1)) {
- fail_accessor:
- js_parse_error(s, "invalid number of arguments for getter or setter");
- goto fail;
- }
- }
- if (fd->has_parameter_expressions) {
- int idx;
- /* Copy the variables in the argument scope to the variable
- scope (see FunctionDeclarationInstantiation() in spec). The
- normal arguments are already present, so no need to copy
- them. */
- idx = fd->scopes[fd->scope_level].first;
- while (idx >= 0) {
- JSVarDef *vd = &fd->vars[idx];
- if (vd->scope_level != fd->scope_level)
- break;
- if (find_var(ctx, fd, vd->var_name) < 0) {
- if (add_var(ctx, fd, vd->var_name) < 0)
- goto fail;
- vd = &fd->vars[idx]; /* fd->vars may have been reallocated */
- emit_op(s, OP_scope_get_var);
- emit_atom(s, vd->var_name);
- emit_u16(s, fd->scope_level);
- emit_op(s, OP_scope_put_var);
- emit_atom(s, vd->var_name);
- emit_u16(s, 0);
- }
- idx = vd->scope_next;
- }
- /* the argument scope has no parent, hence we don't use pop_scope(s) */
- emit_op(s, OP_leave_scope);
- emit_u16(s, fd->scope_level);
- /* set the variable scope as the current scope */
- fd->scope_level = 0;
- fd->scope_first = fd->scopes[fd->scope_level].first;
- }
- if (next_token(s))
- goto fail;
- /* generator function: yield after the parameters are evaluated */
- if (func_kind == JS_FUNC_GENERATOR ||
- func_kind == JS_FUNC_ASYNC_GENERATOR)
- emit_op(s, OP_initial_yield);
- /* in generators, yield expression is forbidden during the parsing
- of the arguments */
- fd->in_function_body = TRUE;
- push_scope(s); /* enter body scope */
- fd->body_scope = fd->scope_level;
- if (s->token.val == TOK_ARROW && func_type == JS_PARSE_FUNC_ARROW) {
- if (next_token(s))
- goto fail;
- if (s->token.val != '{') {
- if (js_parse_function_check_names(s, fd, func_name))
- goto fail;
- if (js_parse_assign_expr(s))
- goto fail;
- if (func_kind != JS_FUNC_NORMAL)
- emit_op(s, OP_return_async);
- else
- emit_op(s, OP_return);
- if (!fd->strip_source) {
- /* save the function source code */
- /* the end of the function source code is after the last
- token of the function source stored into s->last_ptr */
- fd->source_len = s->last_ptr - ptr;
- fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len);
- if (!fd->source)
- goto fail;
- }
- goto done;
- }
- }
- if (func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT) {
- if (js_parse_expect(s, '{'))
- goto fail;
- }
- if (js_parse_directives(s))
- goto fail;
- /* in strict_mode, check function and argument names */
- if (js_parse_function_check_names(s, fd, func_name))
- goto fail;
- while (s->token.val != '}') {
- if (js_parse_source_element(s))
- goto fail;
- }
- if (!fd->strip_source) {
- /* save the function source code */
- fd->source_len = s->buf_ptr - ptr;
- fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len);
- if (!fd->source)
- goto fail;
- }
- if (next_token(s)) {
- /* consume the '}' */
- goto fail;
- }
- /* in case there is no return, add one */
- if (js_is_live_code(s)) {
- emit_return(s, FALSE);
- }
- done:
- s->cur_func = fd->parent;
- /* Reparse identifiers after the function is terminated so that
- the token is parsed in the englobing function. It could be done
- by just using next_token() here for normal functions, but it is
- necessary for arrow functions with an expression body. */
- reparse_ident_token(s);
- /* create the function object */
- {
- int idx;
- JSAtom func_name = fd->func_name;
- /* the real object will be set at the end of the compilation */
- idx = cpool_add(s, JS_NULL);
- fd->parent_cpool_idx = idx;
- if (is_expr) {
- /* for constructors, no code needs to be generated here */
- if (func_type != JS_PARSE_FUNC_CLASS_CONSTRUCTOR &&
- func_type != JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR) {
- /* OP_fclosure creates the function object from the bytecode
- and adds the scope information */
- emit_op(s, OP_fclosure);
- emit_u32(s, idx);
- if (func_name == JS_ATOM_NULL) {
- emit_op(s, OP_set_name);
- emit_u32(s, JS_ATOM_NULL);
- }
- }
- } else if (func_type == JS_PARSE_FUNC_VAR) {
- emit_op(s, OP_fclosure);
- emit_u32(s, idx);
- if (create_func_var) {
- if (s->cur_func->is_global_var) {
- JSGlobalVar *hf;
- /* the global variable must be defined at the start of the
- function */
- hf = add_global_var(ctx, s->cur_func, func_name);
- if (!hf)
- goto fail;
- /* it is considered as defined at the top level
- (needed for annex B.3.3.4 and B.3.3.5
- checks) */
- hf->scope_level = 0;
- hf->force_init = ((s->cur_func->js_mode & JS_MODE_STRICT) != 0);
- /* store directly into global var, bypass lexical scope */
- emit_op(s, OP_dup);
- emit_op(s, OP_scope_put_var);
- emit_atom(s, func_name);
- emit_u16(s, 0);
- } else {
- /* do not call define_var to bypass lexical scope check */
- func_idx = find_var(ctx, s->cur_func, func_name);
- if (func_idx < 0) {
- func_idx = add_var(ctx, s->cur_func, func_name);
- if (func_idx < 0)
- goto fail;
- }
- /* store directly into local var, bypass lexical catch scope */
- emit_op(s, OP_dup);
- emit_op(s, OP_scope_put_var);
- emit_atom(s, func_name);
- emit_u16(s, 0);
- }
- }
- if (lexical_func_idx >= 0) {
- /* lexical variable will be initialized upon entering scope */
- s->cur_func->vars[lexical_func_idx].func_pool_idx = idx;
- emit_op(s, OP_drop);
- } else {
- /* store function object into its lexical name */
- /* XXX: could use OP_put_loc directly */
- emit_op(s, OP_scope_put_var_init);
- emit_atom(s, func_name);
- emit_u16(s, s->cur_func->scope_level);
- }
- } else {
- if (!s->cur_func->is_global_var) {
- int var_idx = define_var(s, s->cur_func, func_name, JS_VAR_DEF_VAR);
- if (var_idx < 0)
- goto fail;
- /* the variable will be assigned at the top of the function */
- if (var_idx & ARGUMENT_VAR_OFFSET) {
- s->cur_func->args[var_idx - ARGUMENT_VAR_OFFSET].func_pool_idx = idx;
- } else {
- s->cur_func->vars[var_idx].func_pool_idx = idx;
- }
- } else {
- JSAtom func_var_name;
- JSGlobalVar *hf;
- if (func_name == JS_ATOM_NULL)
- func_var_name = JS_ATOM__default_; /* export default */
- else
- func_var_name = func_name;
- /* the variable will be assigned at the top of the function */
- hf = add_global_var(ctx, s->cur_func, func_var_name);
- if (!hf)
- goto fail;
- hf->cpool_idx = idx;
- if (export_flag != JS_PARSE_EXPORT_NONE) {
- if (!add_export_entry(s, s->cur_func->module, func_var_name,
- export_flag == JS_PARSE_EXPORT_NAMED ? func_var_name : JS_ATOM_default, JS_EXPORT_TYPE_LOCAL))
- goto fail;
- }
- }
- }
- }
- return 0;
- fail:
- s->cur_func = fd->parent;
- js_free_function_def(ctx, fd);
- if (pfd)
- *pfd = NULL;
- return -1;
- }
- static __exception int js_parse_function_decl(JSParseState *s,
- JSParseFunctionEnum func_type,
- JSFunctionKindEnum func_kind,
- JSAtom func_name,
- const uint8_t *ptr)
- {
- return js_parse_function_decl2(s, func_type, func_kind, func_name, ptr,
- JS_PARSE_EXPORT_NONE, NULL);
- }
- static __exception int js_parse_program(JSParseState *s)
- {
- JSFunctionDef *fd = s->cur_func;
- int idx;
- if (next_token(s))
- return -1;
- if (js_parse_directives(s))
- return -1;
- fd->is_global_var = (fd->eval_type == JS_EVAL_TYPE_GLOBAL) ||
- (fd->eval_type == JS_EVAL_TYPE_MODULE) ||
- !(fd->js_mode & JS_MODE_STRICT);
- if (!s->is_module) {
- /* hidden variable for the return value */
- fd->eval_ret_idx = idx = add_var(s->ctx, fd, JS_ATOM__ret_);
- if (idx < 0)
- return -1;
- }
- while (s->token.val != TOK_EOF) {
- if (js_parse_source_element(s))
- return -1;
- }
- if (!s->is_module) {
- /* return the value of the hidden variable eval_ret_idx */
- if (fd->func_kind == JS_FUNC_ASYNC) {
- /* wrap the return value in an object so that promises can
- be safely returned */
- emit_op(s, OP_object);
- emit_op(s, OP_dup);
- emit_op(s, OP_get_loc);
- emit_u16(s, fd->eval_ret_idx);
- emit_op(s, OP_put_field);
- emit_atom(s, JS_ATOM_value);
- } else {
- emit_op(s, OP_get_loc);
- emit_u16(s, fd->eval_ret_idx);
- }
- emit_return(s, TRUE);
- } else {
- emit_return(s, FALSE);
- }
- return 0;
- }
- static void js_parse_init(JSContext *ctx, JSParseState *s,
- const char *input, size_t input_len,
- const char *filename)
- {
- memset(s, 0, sizeof(*s));
- s->ctx = ctx;
- s->filename = filename;
- s->buf_start = s->buf_ptr = (const uint8_t *)input;
- s->buf_end = s->buf_ptr + input_len;
- s->token.val = ' ';
- s->token.ptr = s->buf_ptr;
- s->get_line_col_cache.ptr = s->buf_start;
- s->get_line_col_cache.buf_start = s->buf_start;
- s->get_line_col_cache.line_num = 0;
- s->get_line_col_cache.col_num = 0;
- }
- static JSValue JS_EvalFunctionInternal(JSContext *ctx, JSValue fun_obj,
- JSValueConst this_obj,
- JSVarRef **var_refs, JSStackFrame *sf)
- {
- JSValue ret_val;
- uint32_t tag;
- tag = JS_VALUE_GET_TAG(fun_obj);
- if (tag == JS_TAG_FUNCTION_BYTECODE) {
- fun_obj = js_closure(ctx, fun_obj, var_refs, sf, TRUE);
- if (JS_IsException(fun_obj))
- return JS_EXCEPTION;
- ret_val = JS_CallFree(ctx, fun_obj, this_obj, 0, NULL);
- } else if (tag == JS_TAG_MODULE) {
- JSModuleDef *m;
- m = JS_VALUE_GET_PTR(fun_obj);
- /* the module refcount should be >= 2 */
- JS_FreeValue(ctx, fun_obj);
- if (js_create_module_function(ctx, m) < 0)
- goto fail;
- if (js_link_module(ctx, m) < 0)
- goto fail;
- ret_val = js_evaluate_module(ctx, m);
- if (JS_IsException(ret_val)) {
- fail:
- return JS_EXCEPTION;
- }
- } else {
- JS_FreeValue(ctx, fun_obj);
- ret_val = JS_ThrowTypeError(ctx, "bytecode function expected");
- }
- return ret_val;
- }
- JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj)
- {
- return JS_EvalFunctionInternal(ctx, fun_obj, ctx->global_obj, NULL, NULL);
- }
- /* 'input' must be zero terminated i.e. input[input_len] = '\0'. */
- static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
- const char *input, size_t input_len,
- const char *filename, int flags, int scope_idx)
- {
- JSParseState s1, *s = &s1;
- int err, js_mode, eval_type;
- JSValue fun_obj, ret_val;
- JSStackFrame *sf;
- JSVarRef **var_refs;
- JSFunctionBytecode *b;
- JSFunctionDef *fd;
- JSModuleDef *m;
- js_parse_init(ctx, s, input, input_len, filename);
- skip_shebang(&s->buf_ptr, s->buf_end);
- eval_type = flags & JS_EVAL_TYPE_MASK;
- m = NULL;
- if (eval_type == JS_EVAL_TYPE_DIRECT) {
- JSObject *p;
- sf = ctx->rt->current_stack_frame;
- assert(sf != NULL);
- assert(JS_VALUE_GET_TAG(sf->cur_func) == JS_TAG_OBJECT);
- p = JS_VALUE_GET_OBJ(sf->cur_func);
- assert(js_class_has_bytecode(p->class_id));
- b = p->u.func.function_bytecode;
- var_refs = p->u.func.var_refs;
- js_mode = b->js_mode;
- } else {
- sf = NULL;
- b = NULL;
- var_refs = NULL;
- js_mode = 0;
- if (flags & JS_EVAL_FLAG_STRICT)
- js_mode |= JS_MODE_STRICT;
- if (eval_type == JS_EVAL_TYPE_MODULE) {
- JSAtom module_name = JS_NewAtom(ctx, filename);
- if (module_name == JS_ATOM_NULL)
- return JS_EXCEPTION;
- m = js_new_module_def(ctx, module_name);
- if (!m)
- return JS_EXCEPTION;
- js_mode |= JS_MODE_STRICT;
- }
- }
- fd = js_new_function_def(ctx, NULL, TRUE, FALSE, filename,
- s->buf_start, &s->get_line_col_cache);
- if (!fd)
- goto fail1;
- s->cur_func = fd;
- fd->eval_type = eval_type;
- fd->has_this_binding = (eval_type != JS_EVAL_TYPE_DIRECT);
- if (eval_type == JS_EVAL_TYPE_DIRECT) {
- fd->new_target_allowed = b->new_target_allowed;
- fd->super_call_allowed = b->super_call_allowed;
- fd->super_allowed = b->super_allowed;
- fd->arguments_allowed = b->arguments_allowed;
- } else {
- fd->new_target_allowed = FALSE;
- fd->super_call_allowed = FALSE;
- fd->super_allowed = FALSE;
- fd->arguments_allowed = TRUE;
- }
- fd->js_mode = js_mode;
- fd->func_name = JS_DupAtom(ctx, JS_ATOM__eval_);
- if (b) {
- if (add_closure_variables(ctx, fd, b, scope_idx))
- goto fail;
- }
- fd->module = m;
- if (m != NULL || (flags & JS_EVAL_FLAG_ASYNC)) {
- fd->in_function_body = TRUE;
- fd->func_kind = JS_FUNC_ASYNC;
- }
- s->is_module = (m != NULL);
- s->allow_html_comments = !s->is_module;
- push_scope(s); /* body scope */
- fd->body_scope = fd->scope_level;
- err = js_parse_program(s);
- if (err) {
- fail:
- free_token(s, &s->token);
- js_free_function_def(ctx, fd);
- goto fail1;
- }
- if (m != NULL)
- m->has_tla = fd->has_await;
- /* create the function object and all the enclosed functions */
- fun_obj = js_create_function(ctx, fd);
- if (JS_IsException(fun_obj))
- goto fail1;
- /* Could add a flag to avoid resolution if necessary */
- if (m) {
- m->func_obj = fun_obj;
- if (js_resolve_module(ctx, m) < 0)
- goto fail1;
- fun_obj = JS_NewModuleValue(ctx, m);
- }
- if (flags & JS_EVAL_FLAG_COMPILE_ONLY) {
- ret_val = fun_obj;
- } else {
- ret_val = JS_EvalFunctionInternal(ctx, fun_obj, this_obj, var_refs, sf);
- }
- return ret_val;
- fail1:
- /* XXX: should free all the unresolved dependencies */
- if (m)
- JS_FreeValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));
- return JS_EXCEPTION;
- }
- /* the indirection is needed to make 'eval' optional */
- static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
- const char *input, size_t input_len,
- const char *filename, int flags, int scope_idx)
- {
- BOOL backtrace_barrier = ((flags & JS_EVAL_FLAG_BACKTRACE_BARRIER) != 0);
- int saved_js_mode = 0;
- JSValue ret;
-
- if (unlikely(!ctx->eval_internal)) {
- return JS_ThrowTypeError(ctx, "eval is not supported");
- }
- if (backtrace_barrier && ctx->rt->current_stack_frame) {
- saved_js_mode = ctx->rt->current_stack_frame->js_mode;
- ctx->rt->current_stack_frame->js_mode |= JS_MODE_BACKTRACE_BARRIER;
- }
- ret = ctx->eval_internal(ctx, this_obj, input, input_len, filename,
- flags, scope_idx);
- if (backtrace_barrier && ctx->rt->current_stack_frame)
- ctx->rt->current_stack_frame->js_mode = saved_js_mode;
- return ret;
- }
- static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,
- JSValueConst val, int flags, int scope_idx)
- {
- JSValue ret;
- const char *str;
- size_t len;
- if (!JS_IsString(val))
- return JS_DupValue(ctx, val);
- str = JS_ToCStringLen(ctx, &len, val);
- if (!str)
- return JS_EXCEPTION;
- ret = JS_EvalInternal(ctx, this_obj, str, len, "<input>", flags, scope_idx);
- JS_FreeCString(ctx, str);
- return ret;
- }
- JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj,
- const char *input, size_t input_len,
- const char *filename, int eval_flags)
- {
- int eval_type = eval_flags & JS_EVAL_TYPE_MASK;
- JSValue ret;
- assert(eval_type == JS_EVAL_TYPE_GLOBAL ||
- eval_type == JS_EVAL_TYPE_MODULE);
- ret = JS_EvalInternal(ctx, this_obj, input, input_len, filename,
- eval_flags, -1);
- return ret;
- }
- JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len,
- const char *filename, int eval_flags)
- {
- return JS_EvalThis(ctx, ctx->global_obj, input, input_len, filename,
- eval_flags);
- }
- int JS_ResolveModule(JSContext *ctx, JSValueConst obj)
- {
- if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) {
- JSModuleDef *m = JS_VALUE_GET_PTR(obj);
- if (js_resolve_module(ctx, m) < 0) {
- js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED);
- return -1;
- }
- }
- return 0;
- }
- /*******************************************************************/
- /* object list */
- typedef struct {
- JSObject *obj;
- uint32_t hash_next; /* -1 if no next entry */
- } JSObjectListEntry;
- /* XXX: reuse it to optimize weak references */
- typedef struct {
- JSObjectListEntry *object_tab;
- int object_count;
- int object_size;
- uint32_t *hash_table;
- uint32_t hash_size;
- } JSObjectList;
- static void js_object_list_init(JSObjectList *s)
- {
- memset(s, 0, sizeof(*s));
- }
- static uint32_t js_object_list_get_hash(JSObject *p, uint32_t hash_size)
- {
- return ((uintptr_t)p * 3163) & (hash_size - 1);
- }
- static int js_object_list_resize_hash(JSContext *ctx, JSObjectList *s,
- uint32_t new_hash_size)
- {
- JSObjectListEntry *e;
- uint32_t i, h, *new_hash_table;
- new_hash_table = js_malloc(ctx, sizeof(new_hash_table[0]) * new_hash_size);
- if (!new_hash_table)
- return -1;
- js_free(ctx, s->hash_table);
- s->hash_table = new_hash_table;
- s->hash_size = new_hash_size;
- for(i = 0; i < s->hash_size; i++) {
- s->hash_table[i] = -1;
- }
- for(i = 0; i < s->object_count; i++) {
- e = &s->object_tab[i];
- h = js_object_list_get_hash(e->obj, s->hash_size);
- e->hash_next = s->hash_table[h];
- s->hash_table[h] = i;
- }
- return 0;
- }
- /* the reference count of 'obj' is not modified. Return 0 if OK, -1 if
- memory error */
- static int js_object_list_add(JSContext *ctx, JSObjectList *s, JSObject *obj)
- {
- JSObjectListEntry *e;
- uint32_t h, new_hash_size;
- if (js_resize_array(ctx, (void *)&s->object_tab,
- sizeof(s->object_tab[0]),
- &s->object_size, s->object_count + 1))
- return -1;
- if (unlikely((s->object_count + 1) >= s->hash_size)) {
- new_hash_size = max_uint32(s->hash_size, 4);
- while (new_hash_size <= s->object_count)
- new_hash_size *= 2;
- if (js_object_list_resize_hash(ctx, s, new_hash_size))
- return -1;
- }
- e = &s->object_tab[s->object_count++];
- h = js_object_list_get_hash(obj, s->hash_size);
- e->obj = obj;
- e->hash_next = s->hash_table[h];
- s->hash_table[h] = s->object_count - 1;
- return 0;
- }
- /* return -1 if not present or the object index */
- static int js_object_list_find(JSContext *ctx, JSObjectList *s, JSObject *obj)
- {
- JSObjectListEntry *e;
- uint32_t h, p;
- /* must test empty size because there is no hash table */
- if (s->object_count == 0)
- return -1;
- h = js_object_list_get_hash(obj, s->hash_size);
- p = s->hash_table[h];
- while (p != -1) {
- e = &s->object_tab[p];
- if (e->obj == obj)
- return p;
- p = e->hash_next;
- }
- return -1;
- }
- static void js_object_list_end(JSContext *ctx, JSObjectList *s)
- {
- js_free(ctx, s->object_tab);
- js_free(ctx, s->hash_table);
- }
- /*******************************************************************/
- /* binary object writer & reader */
- typedef enum BCTagEnum {
- BC_TAG_NULL = 1,
- BC_TAG_UNDEFINED,
- BC_TAG_BOOL_FALSE,
- BC_TAG_BOOL_TRUE,
- BC_TAG_INT32,
- BC_TAG_FLOAT64,
- BC_TAG_STRING,
- BC_TAG_OBJECT,
- BC_TAG_ARRAY,
- BC_TAG_BIG_INT,
- BC_TAG_TEMPLATE_OBJECT,
- BC_TAG_FUNCTION_BYTECODE,
- BC_TAG_MODULE,
- BC_TAG_TYPED_ARRAY,
- BC_TAG_ARRAY_BUFFER,
- BC_TAG_SHARED_ARRAY_BUFFER,
- BC_TAG_DATE,
- BC_TAG_OBJECT_VALUE,
- BC_TAG_OBJECT_REFERENCE,
- } BCTagEnum;
- #define BC_VERSION 5
- typedef struct BCWriterState {
- JSContext *ctx;
- DynBuf dbuf;
- BOOL allow_bytecode : 8;
- BOOL allow_sab : 8;
- BOOL allow_reference : 8;
- uint32_t first_atom;
- uint32_t *atom_to_idx;
- int atom_to_idx_size;
- JSAtom *idx_to_atom;
- int idx_to_atom_count;
- int idx_to_atom_size;
- uint8_t **sab_tab;
- int sab_tab_len;
- int sab_tab_size;
- /* list of referenced objects (used if allow_reference = TRUE) */
- JSObjectList object_list;
- } BCWriterState;
- #ifdef DUMP_READ_OBJECT
- static const char * const bc_tag_str[] = {
- "invalid",
- "null",
- "undefined",
- "false",
- "true",
- "int32",
- "float64",
- "string",
- "object",
- "array",
- "bigint",
- "template",
- "function",
- "module",
- "TypedArray",
- "ArrayBuffer",
- "SharedArrayBuffer",
- "Date",
- "ObjectValue",
- "ObjectReference",
- };
- #endif
- static inline BOOL is_be(void)
- {
- union {
- uint16_t a;
- uint8_t b;
- } u = {0x100};
- return u.b;
- }
- static void bc_put_u8(BCWriterState *s, uint8_t v)
- {
- dbuf_putc(&s->dbuf, v);
- }
- static void bc_put_u16(BCWriterState *s, uint16_t v)
- {
- if (is_be())
- v = bswap16(v);
- dbuf_put_u16(&s->dbuf, v);
- }
- static __maybe_unused void bc_put_u32(BCWriterState *s, uint32_t v)
- {
- if (is_be())
- v = bswap32(v);
- dbuf_put_u32(&s->dbuf, v);
- }
- static void bc_put_u64(BCWriterState *s, uint64_t v)
- {
- if (is_be())
- v = bswap64(v);
- dbuf_put(&s->dbuf, (uint8_t *)&v, sizeof(v));
- }
- static void bc_put_leb128(BCWriterState *s, uint32_t v)
- {
- dbuf_put_leb128(&s->dbuf, v);
- }
- static void bc_put_sleb128(BCWriterState *s, int32_t v)
- {
- dbuf_put_sleb128(&s->dbuf, v);
- }
- static void bc_set_flags(uint32_t *pflags, int *pidx, uint32_t val, int n)
- {
- *pflags = *pflags | (val << *pidx);
- *pidx += n;
- }
- static int bc_atom_to_idx(BCWriterState *s, uint32_t *pres, JSAtom atom)
- {
- uint32_t v;
- if (atom < s->first_atom || __JS_AtomIsTaggedInt(atom)) {
- *pres = atom;
- return 0;
- }
- atom -= s->first_atom;
- if (atom < s->atom_to_idx_size && s->atom_to_idx[atom] != 0) {
- *pres = s->atom_to_idx[atom];
- return 0;
- }
- if (atom >= s->atom_to_idx_size) {
- int old_size, i;
- old_size = s->atom_to_idx_size;
- if (js_resize_array(s->ctx, (void **)&s->atom_to_idx,
- sizeof(s->atom_to_idx[0]), &s->atom_to_idx_size,
- atom + 1))
- return -1;
- /* XXX: could add a specific js_resize_array() function to do it */
- for(i = old_size; i < s->atom_to_idx_size; i++)
- s->atom_to_idx[i] = 0;
- }
- if (js_resize_array(s->ctx, (void **)&s->idx_to_atom,
- sizeof(s->idx_to_atom[0]),
- &s->idx_to_atom_size, s->idx_to_atom_count + 1))
- goto fail;
- v = s->idx_to_atom_count++;
- s->idx_to_atom[v] = atom + s->first_atom;
- v += s->first_atom;
- s->atom_to_idx[atom] = v;
- *pres = v;
- return 0;
- fail:
- *pres = 0;
- return -1;
- }
- static int bc_put_atom(BCWriterState *s, JSAtom atom)
- {
- uint32_t v;
- if (__JS_AtomIsTaggedInt(atom)) {
- v = (__JS_AtomToUInt32(atom) << 1) | 1;
- } else {
- if (bc_atom_to_idx(s, &v, atom))
- return -1;
- v <<= 1;
- }
- bc_put_leb128(s, v);
- return 0;
- }
- static void bc_byte_swap(uint8_t *bc_buf, int bc_len)
- {
- int pos, len, op, fmt;
- pos = 0;
- while (pos < bc_len) {
- op = bc_buf[pos];
- len = short_opcode_info(op).size;
- fmt = short_opcode_info(op).fmt;
- switch(fmt) {
- case OP_FMT_u16:
- case OP_FMT_i16:
- case OP_FMT_label16:
- case OP_FMT_npop:
- case OP_FMT_loc:
- case OP_FMT_arg:
- case OP_FMT_var_ref:
- put_u16(bc_buf + pos + 1,
- bswap16(get_u16(bc_buf + pos + 1)));
- break;
- case OP_FMT_i32:
- case OP_FMT_u32:
- case OP_FMT_const:
- case OP_FMT_label:
- case OP_FMT_atom:
- case OP_FMT_atom_u8:
- put_u32(bc_buf + pos + 1,
- bswap32(get_u32(bc_buf + pos + 1)));
- break;
- case OP_FMT_atom_u16:
- case OP_FMT_label_u16:
- put_u32(bc_buf + pos + 1,
- bswap32(get_u32(bc_buf + pos + 1)));
- put_u16(bc_buf + pos + 1 + 4,
- bswap16(get_u16(bc_buf + pos + 1 + 4)));
- break;
- case OP_FMT_atom_label_u8:
- case OP_FMT_atom_label_u16:
- put_u32(bc_buf + pos + 1,
- bswap32(get_u32(bc_buf + pos + 1)));
- put_u32(bc_buf + pos + 1 + 4,
- bswap32(get_u32(bc_buf + pos + 1 + 4)));
- if (fmt == OP_FMT_atom_label_u16) {
- put_u16(bc_buf + pos + 1 + 4 + 4,
- bswap16(get_u16(bc_buf + pos + 1 + 4 + 4)));
- }
- break;
- case OP_FMT_npop_u16:
- put_u16(bc_buf + pos + 1,
- bswap16(get_u16(bc_buf + pos + 1)));
- put_u16(bc_buf + pos + 1 + 2,
- bswap16(get_u16(bc_buf + pos + 1 + 2)));
- break;
- default:
- break;
- }
- pos += len;
- }
- }
- static int JS_WriteFunctionBytecode(BCWriterState *s,
- const uint8_t *bc_buf1, int bc_len)
- {
- int pos, len, op;
- JSAtom atom;
- uint8_t *bc_buf;
- uint32_t val;
- bc_buf = js_malloc(s->ctx, bc_len);
- if (!bc_buf)
- return -1;
- memcpy(bc_buf, bc_buf1, bc_len);
- pos = 0;
- while (pos < bc_len) {
- op = bc_buf[pos];
- len = short_opcode_info(op).size;
- switch(short_opcode_info(op).fmt) {
- case OP_FMT_atom:
- case OP_FMT_atom_u8:
- case OP_FMT_atom_u16:
- case OP_FMT_atom_label_u8:
- case OP_FMT_atom_label_u16:
- atom = get_u32(bc_buf + pos + 1);
- if (bc_atom_to_idx(s, &val, atom))
- goto fail;
- put_u32(bc_buf + pos + 1, val);
- break;
- default:
- break;
- }
- pos += len;
- }
- if (is_be())
- bc_byte_swap(bc_buf, bc_len);
- dbuf_put(&s->dbuf, bc_buf, bc_len);
- js_free(s->ctx, bc_buf);
- return 0;
- fail:
- js_free(s->ctx, bc_buf);
- return -1;
- }
- static void JS_WriteString(BCWriterState *s, JSString *p)
- {
- int i;
- bc_put_leb128(s, ((uint32_t)p->len << 1) | p->is_wide_char);
- if (p->is_wide_char) {
- for(i = 0; i < p->len; i++)
- bc_put_u16(s, p->u.str16[i]);
- } else {
- dbuf_put(&s->dbuf, p->u.str8, p->len);
- }
- }
- static int JS_WriteBigInt(BCWriterState *s, JSValueConst obj)
- {
- JSBigIntBuf buf;
- JSBigInt *p;
- uint32_t len, i;
- js_limb_t v, b;
- int shift;
-
- bc_put_u8(s, BC_TAG_BIG_INT);
- if (JS_VALUE_GET_TAG(obj) == JS_TAG_SHORT_BIG_INT)
- p = js_bigint_set_short(&buf, obj);
- else
- p = JS_VALUE_GET_PTR(obj);
- if (p->len == 1 && p->tab[0] == 0) {
- /* zero case */
- len = 0;
- } else {
- /* compute the length of the two's complement representation
- in bytes */
- len = p->len * (JS_LIMB_BITS / 8);
- v = p->tab[p->len - 1];
- shift = JS_LIMB_BITS - 8;
- while (shift > 0) {
- b = (v >> shift) & 0xff;
- if (b != 0x00 && b != 0xff)
- break;
- if ((b & 1) != ((v >> (shift - 1)) & 1))
- break;
- shift -= 8;
- len--;
- }
- }
- bc_put_leb128(s, len);
- if (len > 0) {
- for(i = 0; i < (len / (JS_LIMB_BITS / 8)); i++) {
- #if JS_LIMB_BITS == 32
- bc_put_u32(s, p->tab[i]);
- #else
- bc_put_u64(s, p->tab[i]);
- #endif
- }
- for(i = 0; i < len % (JS_LIMB_BITS / 8); i++) {
- bc_put_u8(s, (p->tab[p->len - 1] >> (i * 8)) & 0xff);
- }
- }
- return 0;
- }
- static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj);
- static int JS_WriteFunctionTag(BCWriterState *s, JSValueConst obj)
- {
- JSFunctionBytecode *b = JS_VALUE_GET_PTR(obj);
- uint32_t flags;
- int idx, i;
- bc_put_u8(s, BC_TAG_FUNCTION_BYTECODE);
- flags = idx = 0;
- bc_set_flags(&flags, &idx, b->has_prototype, 1);
- bc_set_flags(&flags, &idx, b->has_simple_parameter_list, 1);
- bc_set_flags(&flags, &idx, b->is_derived_class_constructor, 1);
- bc_set_flags(&flags, &idx, b->need_home_object, 1);
- bc_set_flags(&flags, &idx, b->func_kind, 2);
- bc_set_flags(&flags, &idx, b->new_target_allowed, 1);
- bc_set_flags(&flags, &idx, b->super_call_allowed, 1);
- bc_set_flags(&flags, &idx, b->super_allowed, 1);
- bc_set_flags(&flags, &idx, b->arguments_allowed, 1);
- bc_set_flags(&flags, &idx, b->has_debug, 1);
- bc_set_flags(&flags, &idx, b->is_direct_or_indirect_eval, 1);
- assert(idx <= 16);
- bc_put_u16(s, flags);
- bc_put_u8(s, b->js_mode);
- bc_put_atom(s, b->func_name);
- bc_put_leb128(s, b->arg_count);
- bc_put_leb128(s, b->var_count);
- bc_put_leb128(s, b->defined_arg_count);
- bc_put_leb128(s, b->stack_size);
- bc_put_leb128(s, b->var_ref_count);
- bc_put_leb128(s, b->closure_var_count);
- bc_put_leb128(s, b->cpool_count);
- bc_put_leb128(s, b->byte_code_len);
- if (b->vardefs) {
- bc_put_leb128(s, b->arg_count + b->var_count);
- for(i = 0; i < b->arg_count + b->var_count; i++) {
- JSBytecodeVarDef *vd = &b->vardefs[i];
- bc_put_atom(s, vd->var_name);
- bc_put_leb128(s, vd->scope_next + 1);
- bc_put_leb128(s, vd->var_ref_idx);
- flags = idx = 0;
- bc_set_flags(&flags, &idx, vd->var_kind, 4);
- bc_set_flags(&flags, &idx, vd->is_const, 1);
- bc_set_flags(&flags, &idx, vd->is_lexical, 1);
- bc_set_flags(&flags, &idx, vd->is_captured, 1);
- bc_set_flags(&flags, &idx, vd->has_scope, 1);
- assert(idx <= 8);
- bc_put_u8(s, flags);
- }
- } else {
- bc_put_leb128(s, 0);
- }
- for(i = 0; i < b->closure_var_count; i++) {
- JSClosureVar *cv = &b->closure_var[i];
- bc_put_atom(s, cv->var_name);
- bc_put_leb128(s, cv->var_idx);
- flags = idx = 0;
- bc_set_flags(&flags, &idx, cv->closure_type, 3);
- bc_set_flags(&flags, &idx, cv->is_const, 1);
- bc_set_flags(&flags, &idx, cv->is_lexical, 1);
- bc_set_flags(&flags, &idx, cv->var_kind, 4);
- assert(idx <= 16);
- bc_put_u16(s, flags);
- }
- if (JS_WriteFunctionBytecode(s, b->byte_code_buf, b->byte_code_len))
- goto fail;
- if (b->has_debug) {
- bc_put_atom(s, b->debug.filename);
- bc_put_leb128(s, b->debug.pc2line_len);
- dbuf_put(&s->dbuf, b->debug.pc2line_buf, b->debug.pc2line_len);
- if (b->debug.source) {
- bc_put_leb128(s, b->debug.source_len);
- dbuf_put(&s->dbuf, (uint8_t *)b->debug.source, b->debug.source_len);
- } else {
- bc_put_leb128(s, 0);
- }
- }
- for(i = 0; i < b->cpool_count; i++) {
- if (JS_WriteObjectRec(s, b->cpool[i]))
- goto fail;
- }
- return 0;
- fail:
- return -1;
- }
- static int JS_WriteModule(BCWriterState *s, JSValueConst obj)
- {
- JSModuleDef *m = JS_VALUE_GET_PTR(obj);
- int i;
- bc_put_u8(s, BC_TAG_MODULE);
- bc_put_atom(s, m->module_name);
- bc_put_leb128(s, m->req_module_entries_count);
- for(i = 0; i < m->req_module_entries_count; i++) {
- JSReqModuleEntry *rme = &m->req_module_entries[i];
- bc_put_atom(s, rme->module_name);
- if (JS_WriteObjectRec(s, rme->attributes))
- goto fail;
- }
- bc_put_leb128(s, m->export_entries_count);
- for(i = 0; i < m->export_entries_count; i++) {
- JSExportEntry *me = &m->export_entries[i];
- bc_put_u8(s, me->export_type);
- if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
- bc_put_leb128(s, me->u.local.var_idx);
- } else {
- bc_put_leb128(s, me->u.req_module_idx);
- bc_put_atom(s, me->local_name);
- }
- bc_put_atom(s, me->export_name);
- }
- bc_put_leb128(s, m->star_export_entries_count);
- for(i = 0; i < m->star_export_entries_count; i++) {
- JSStarExportEntry *se = &m->star_export_entries[i];
- bc_put_leb128(s, se->req_module_idx);
- }
- bc_put_leb128(s, m->import_entries_count);
- for(i = 0; i < m->import_entries_count; i++) {
- JSImportEntry *mi = &m->import_entries[i];
- bc_put_leb128(s, mi->var_idx);
- bc_put_u8(s, mi->is_star);
- bc_put_atom(s, mi->import_name);
- bc_put_leb128(s, mi->req_module_idx);
- }
- bc_put_u8(s, m->has_tla);
- if (JS_WriteObjectRec(s, m->func_obj))
- goto fail;
- return 0;
- fail:
- return -1;
- }
- /* XXX: be compatible with the structured clone algorithm */
- static int JS_WriteArray(BCWriterState *s, JSValueConst obj)
- {
- JSContext *ctx = s->ctx;
- JSObject *p = JS_VALUE_GET_OBJ(obj);
- uint32_t i, len;
- int ret;
- BOOL is_template;
- JSShapeProperty *prs;
- JSProperty *pr;
-
- if (s->allow_bytecode && !p->extensible) {
- /* not extensible array: we consider it is a
- template when we are saving bytecode */
- bc_put_u8(s, BC_TAG_TEMPLATE_OBJECT);
- is_template = TRUE;
- } else {
- bc_put_u8(s, BC_TAG_ARRAY);
- is_template = FALSE;
- }
- if (js_get_length32(ctx, &len, obj)) /* no side effect */
- goto fail;
- bc_put_leb128(s, len);
- if (p->fast_array) {
- for(i = 0; i < p->u.array.count; i++) {
- ret = JS_WriteObjectRec(s, p->u.array.u.values[i]);
- if (ret)
- goto fail;
- }
- for(i = p->u.array.count; i < len; i++) {
- ret = JS_WriteObjectRec(s, JS_UNDEFINED);
- if (ret)
- goto fail;
- }
- } else {
- for(i = 0; i < len; i++) {
- JSAtom atom;
- atom = JS_NewAtomUInt32(ctx, i);
- if (atom == JS_ATOM_NULL)
- goto fail;
- prs = find_own_property(&pr, p, atom);
- JS_FreeAtom(ctx, atom);
- if (prs && (prs->flags & JS_PROP_ENUMERABLE)) {
- if (prs->flags & JS_PROP_TMASK) {
- JS_ThrowTypeError(ctx, "only value properties are supported");
- goto fail;
- }
- ret = JS_WriteObjectRec(s, pr->u.value);
- if (ret)
- goto fail;
- } else {
- ret = JS_WriteObjectRec(s, JS_UNDEFINED);
- if (ret)
- goto fail;
- }
- }
- }
- if (is_template) {
- /* the 'raw' property is not enumerable */
- prs = find_own_property(&pr, p, JS_ATOM_raw);
- if (prs) {
- if (prs->flags & JS_PROP_TMASK) {
- JS_ThrowTypeError(ctx, "only value properties are supported");
- goto fail;
- }
- ret = JS_WriteObjectRec(s, pr->u.value);
- if (ret)
- goto fail;
- } else {
- ret = JS_WriteObjectRec(s, JS_UNDEFINED);
- if (ret)
- goto fail;
- }
- }
- return 0;
- fail:
- return -1;
- }
- static int JS_WriteObjectTag(BCWriterState *s, JSValueConst obj)
- {
- JSObject *p = JS_VALUE_GET_OBJ(obj);
- uint32_t i, prop_count;
- JSShape *sh;
- JSShapeProperty *pr;
- int pass;
- JSAtom atom;
- bc_put_u8(s, BC_TAG_OBJECT);
- prop_count = 0;
- sh = p->shape;
- for(pass = 0; pass < 2; pass++) {
- if (pass == 1)
- bc_put_leb128(s, prop_count);
- for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) {
- atom = pr->atom;
- if (atom != JS_ATOM_NULL &&
- JS_AtomIsString(s->ctx, atom) &&
- (pr->flags & JS_PROP_ENUMERABLE)) {
- if (pr->flags & JS_PROP_TMASK) {
- JS_ThrowTypeError(s->ctx, "only value properties are supported");
- goto fail;
- }
- if (pass == 0) {
- prop_count++;
- } else {
- bc_put_atom(s, atom);
- if (JS_WriteObjectRec(s, p->prop[i].u.value))
- goto fail;
- }
- }
- }
- }
- return 0;
- fail:
- return -1;
- }
- static int JS_WriteTypedArray(BCWriterState *s, JSValueConst obj)
- {
- JSObject *p = JS_VALUE_GET_OBJ(obj);
- JSTypedArray *ta = p->u.typed_array;
- bc_put_u8(s, BC_TAG_TYPED_ARRAY);
- bc_put_u8(s, p->class_id - JS_CLASS_UINT8C_ARRAY);
- bc_put_leb128(s, p->u.array.count);
- bc_put_leb128(s, ta->offset);
- if (JS_WriteObjectRec(s, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)))
- return -1;
- return 0;
- }
- static int JS_WriteArrayBuffer(BCWriterState *s, JSValueConst obj)
- {
- JSObject *p = JS_VALUE_GET_OBJ(obj);
- JSArrayBuffer *abuf = p->u.array_buffer;
- if (abuf->detached) {
- JS_ThrowTypeErrorDetachedArrayBuffer(s->ctx);
- return -1;
- }
- bc_put_u8(s, BC_TAG_ARRAY_BUFFER);
- bc_put_leb128(s, abuf->byte_length);
- bc_put_leb128(s, abuf->max_byte_length);
- dbuf_put(&s->dbuf, abuf->data, abuf->byte_length);
- return 0;
- }
- static int JS_WriteSharedArrayBuffer(BCWriterState *s, JSValueConst obj)
- {
- JSObject *p = JS_VALUE_GET_OBJ(obj);
- JSArrayBuffer *abuf = p->u.array_buffer;
- assert(!abuf->detached); /* SharedArrayBuffer are never detached */
- bc_put_u8(s, BC_TAG_SHARED_ARRAY_BUFFER);
- bc_put_leb128(s, abuf->byte_length);
- bc_put_leb128(s, abuf->max_byte_length);
- bc_put_u64(s, (uintptr_t)abuf->data);
- if (js_resize_array(s->ctx, (void **)&s->sab_tab, sizeof(s->sab_tab[0]),
- &s->sab_tab_size, s->sab_tab_len + 1))
- return -1;
- /* keep the SAB pointer so that the user can clone it or free it */
- s->sab_tab[s->sab_tab_len++] = abuf->data;
- return 0;
- }
- static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj)
- {
- uint32_t tag;
- if (js_check_stack_overflow(s->ctx->rt, 0)) {
- JS_ThrowStackOverflow(s->ctx);
- return -1;
- }
- tag = JS_VALUE_GET_NORM_TAG(obj);
- switch(tag) {
- case JS_TAG_NULL:
- bc_put_u8(s, BC_TAG_NULL);
- break;
- case JS_TAG_UNDEFINED:
- bc_put_u8(s, BC_TAG_UNDEFINED);
- break;
- case JS_TAG_BOOL:
- bc_put_u8(s, BC_TAG_BOOL_FALSE + JS_VALUE_GET_INT(obj));
- break;
- case JS_TAG_INT:
- bc_put_u8(s, BC_TAG_INT32);
- bc_put_sleb128(s, JS_VALUE_GET_INT(obj));
- break;
- case JS_TAG_FLOAT64:
- {
- JSFloat64Union u;
- bc_put_u8(s, BC_TAG_FLOAT64);
- u.d = JS_VALUE_GET_FLOAT64(obj);
- bc_put_u64(s, u.u64);
- }
- break;
- case JS_TAG_STRING:
- {
- JSString *p = JS_VALUE_GET_STRING(obj);
- bc_put_u8(s, BC_TAG_STRING);
- JS_WriteString(s, p);
- }
- break;
- case JS_TAG_STRING_ROPE:
- {
- JSValue str;
- int ret;
- str = JS_ToString(s->ctx, obj);
- if (JS_IsException(str))
- goto fail;
- ret = JS_WriteObjectRec(s, str);
- JS_FreeValue(s->ctx, str);
- if (ret)
- goto fail;
- }
- break;
- case JS_TAG_FUNCTION_BYTECODE:
- if (!s->allow_bytecode)
- goto invalid_tag;
- if (JS_WriteFunctionTag(s, obj))
- goto fail;
- break;
- case JS_TAG_MODULE:
- if (!s->allow_bytecode)
- goto invalid_tag;
- if (JS_WriteModule(s, obj))
- goto fail;
- break;
- case JS_TAG_OBJECT:
- {
- JSObject *p = JS_VALUE_GET_OBJ(obj);
- int ret, idx;
- if (s->allow_reference) {
- idx = js_object_list_find(s->ctx, &s->object_list, p);
- if (idx >= 0) {
- bc_put_u8(s, BC_TAG_OBJECT_REFERENCE);
- bc_put_leb128(s, idx);
- break;
- } else {
- if (js_object_list_add(s->ctx, &s->object_list, p))
- goto fail;
- }
- } else {
- if (p->tmp_mark) {
- JS_ThrowTypeError(s->ctx, "circular reference");
- goto fail;
- }
- p->tmp_mark = 1;
- }
- switch(p->class_id) {
- case JS_CLASS_ARRAY:
- ret = JS_WriteArray(s, obj);
- break;
- case JS_CLASS_OBJECT:
- ret = JS_WriteObjectTag(s, obj);
- break;
- case JS_CLASS_ARRAY_BUFFER:
- ret = JS_WriteArrayBuffer(s, obj);
- break;
- case JS_CLASS_SHARED_ARRAY_BUFFER:
- if (!s->allow_sab)
- goto invalid_tag;
- ret = JS_WriteSharedArrayBuffer(s, obj);
- break;
- case JS_CLASS_DATE:
- bc_put_u8(s, BC_TAG_DATE);
- ret = JS_WriteObjectRec(s, p->u.object_data);
- break;
- case JS_CLASS_NUMBER:
- case JS_CLASS_STRING:
- case JS_CLASS_BOOLEAN:
- case JS_CLASS_BIG_INT:
- bc_put_u8(s, BC_TAG_OBJECT_VALUE);
- ret = JS_WriteObjectRec(s, p->u.object_data);
- break;
- default:
- if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
- p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
- ret = JS_WriteTypedArray(s, obj);
- } else {
- JS_ThrowTypeError(s->ctx, "unsupported object class");
- ret = -1;
- }
- break;
- }
- p->tmp_mark = 0;
- if (ret)
- goto fail;
- }
- break;
- case JS_TAG_SHORT_BIG_INT:
- case JS_TAG_BIG_INT:
- if (JS_WriteBigInt(s, obj))
- goto fail;
- break;
- default:
- invalid_tag:
- JS_ThrowInternalError(s->ctx, "unsupported tag (%d)", tag);
- goto fail;
- }
- return 0;
- fail:
- return -1;
- }
- /* create the atom table */
- static int JS_WriteObjectAtoms(BCWriterState *s)
- {
- JSRuntime *rt = s->ctx->rt;
- DynBuf dbuf1;
- int i, atoms_size;
- dbuf1 = s->dbuf;
- js_dbuf_init(s->ctx, &s->dbuf);
- bc_put_u8(s, BC_VERSION);
- bc_put_leb128(s, s->idx_to_atom_count);
- for(i = 0; i < s->idx_to_atom_count; i++) {
- JSAtomStruct *p = rt->atom_array[s->idx_to_atom[i]];
- JS_WriteString(s, p);
- }
- /* XXX: should check for OOM in above phase */
- /* move the atoms at the start */
- /* XXX: could just append dbuf1 data, but it uses more memory if
- dbuf1 is larger than dbuf */
- atoms_size = s->dbuf.size;
- if (dbuf_claim(&dbuf1, atoms_size))
- goto fail;
- memmove(dbuf1.buf + atoms_size, dbuf1.buf, dbuf1.size);
- memcpy(dbuf1.buf, s->dbuf.buf, atoms_size);
- dbuf1.size += atoms_size;
- dbuf_free(&s->dbuf);
- s->dbuf = dbuf1;
- return 0;
- fail:
- dbuf_free(&dbuf1);
- return -1;
- }
- uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj,
- int flags, uint8_t ***psab_tab, size_t *psab_tab_len)
- {
- BCWriterState ss, *s = &ss;
- memset(s, 0, sizeof(*s));
- s->ctx = ctx;
- s->allow_bytecode = ((flags & JS_WRITE_OBJ_BYTECODE) != 0);
- s->allow_sab = ((flags & JS_WRITE_OBJ_SAB) != 0);
- s->allow_reference = ((flags & JS_WRITE_OBJ_REFERENCE) != 0);
- /* XXX: could use a different version when bytecode is included */
- if (s->allow_bytecode)
- s->first_atom = JS_ATOM_END;
- else
- s->first_atom = 1;
- js_dbuf_init(ctx, &s->dbuf);
- js_object_list_init(&s->object_list);
- if (JS_WriteObjectRec(s, obj))
- goto fail;
- if (JS_WriteObjectAtoms(s))
- goto fail;
- js_object_list_end(ctx, &s->object_list);
- js_free(ctx, s->atom_to_idx);
- js_free(ctx, s->idx_to_atom);
- *psize = s->dbuf.size;
- if (psab_tab)
- *psab_tab = s->sab_tab;
- if (psab_tab_len)
- *psab_tab_len = s->sab_tab_len;
- return s->dbuf.buf;
- fail:
- js_object_list_end(ctx, &s->object_list);
- js_free(ctx, s->atom_to_idx);
- js_free(ctx, s->idx_to_atom);
- dbuf_free(&s->dbuf);
- *psize = 0;
- if (psab_tab)
- *psab_tab = NULL;
- if (psab_tab_len)
- *psab_tab_len = 0;
- return NULL;
- }
- uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj,
- int flags)
- {
- return JS_WriteObject2(ctx, psize, obj, flags, NULL, NULL);
- }
- typedef struct BCReaderState {
- JSContext *ctx;
- const uint8_t *buf_start, *ptr, *buf_end;
- uint32_t first_atom;
- uint32_t idx_to_atom_count;
- JSAtom *idx_to_atom;
- int error_state;
- BOOL allow_sab : 8;
- BOOL allow_bytecode : 8;
- BOOL is_rom_data : 8;
- BOOL allow_reference : 8;
- /* object references */
- JSObject **objects;
- int objects_count;
- int objects_size;
- #ifdef DUMP_READ_OBJECT
- const uint8_t *ptr_last;
- int level;
- #endif
- } BCReaderState;
- #ifdef DUMP_READ_OBJECT
- static void __attribute__((format(printf, 2, 3))) bc_read_trace(BCReaderState *s, const char *fmt, ...) {
- va_list ap;
- int i, n, n0;
- if (!s->ptr_last)
- s->ptr_last = s->buf_start;
- n = n0 = 0;
- if (s->ptr > s->ptr_last || s->ptr == s->buf_start) {
- n0 = printf("%04x: ", (int)(s->ptr_last - s->buf_start));
- n += n0;
- }
- for (i = 0; s->ptr_last < s->ptr; i++) {
- if ((i & 7) == 0 && i > 0) {
- printf("\n%*s", n0, "");
- n = n0;
- }
- n += printf(" %02x", *s->ptr_last++);
- }
- if (*fmt == '}')
- s->level--;
- if (n < 32 + s->level * 2) {
- printf("%*s", 32 + s->level * 2 - n, "");
- }
- va_start(ap, fmt);
- vfprintf(stdout, fmt, ap);
- va_end(ap);
- if (strchr(fmt, '{'))
- s->level++;
- }
- #else
- #define bc_read_trace(...)
- #endif
- static int bc_read_error_end(BCReaderState *s)
- {
- if (!s->error_state) {
- JS_ThrowSyntaxError(s->ctx, "read after the end of the buffer");
- }
- return s->error_state = -1;
- }
- static int bc_get_u8(BCReaderState *s, uint8_t *pval)
- {
- if (unlikely(s->buf_end - s->ptr < 1)) {
- *pval = 0; /* avoid warning */
- return bc_read_error_end(s);
- }
- *pval = *s->ptr++;
- return 0;
- }
- static int bc_get_u16(BCReaderState *s, uint16_t *pval)
- {
- uint16_t v;
- if (unlikely(s->buf_end - s->ptr < 2)) {
- *pval = 0; /* avoid warning */
- return bc_read_error_end(s);
- }
- v = get_u16(s->ptr);
- if (is_be())
- v = bswap16(v);
- *pval = v;
- s->ptr += 2;
- return 0;
- }
- static __maybe_unused int bc_get_u32(BCReaderState *s, uint32_t *pval)
- {
- uint32_t v;
- if (unlikely(s->buf_end - s->ptr < 4)) {
- *pval = 0; /* avoid warning */
- return bc_read_error_end(s);
- }
- v = get_u32(s->ptr);
- if (is_be())
- v = bswap32(v);
- *pval = v;
- s->ptr += 4;
- return 0;
- }
- static int bc_get_u64(BCReaderState *s, uint64_t *pval)
- {
- uint64_t v;
- if (unlikely(s->buf_end - s->ptr < 8)) {
- *pval = 0; /* avoid warning */
- return bc_read_error_end(s);
- }
- v = get_u64(s->ptr);
- if (is_be())
- v = bswap64(v);
- *pval = v;
- s->ptr += 8;
- return 0;
- }
- static int bc_get_leb128(BCReaderState *s, uint32_t *pval)
- {
- int ret;
- ret = get_leb128(pval, s->ptr, s->buf_end);
- if (unlikely(ret < 0))
- return bc_read_error_end(s);
- s->ptr += ret;
- return 0;
- }
- static int bc_get_sleb128(BCReaderState *s, int32_t *pval)
- {
- int ret;
- ret = get_sleb128(pval, s->ptr, s->buf_end);
- if (unlikely(ret < 0))
- return bc_read_error_end(s);
- s->ptr += ret;
- return 0;
- }
- /* XXX: used to read an `int` with a positive value */
- static int bc_get_leb128_int(BCReaderState *s, int *pval)
- {
- return bc_get_leb128(s, (uint32_t *)pval);
- }
- static int bc_get_leb128_u16(BCReaderState *s, uint16_t *pval)
- {
- uint32_t val;
- if (bc_get_leb128(s, &val)) {
- *pval = 0;
- return -1;
- }
- *pval = val;
- return 0;
- }
- static int bc_get_buf(BCReaderState *s, uint8_t *buf, uint32_t buf_len)
- {
- if (buf_len != 0) {
- if (unlikely(!buf || s->buf_end - s->ptr < buf_len))
- return bc_read_error_end(s);
- memcpy(buf, s->ptr, buf_len);
- s->ptr += buf_len;
- }
- return 0;
- }
- static int bc_idx_to_atom(BCReaderState *s, JSAtom *patom, uint32_t idx)
- {
- JSAtom atom;
- if (__JS_AtomIsTaggedInt(idx)) {
- atom = idx;
- } else if (idx < s->first_atom) {
- atom = JS_DupAtom(s->ctx, idx);
- } else {
- idx -= s->first_atom;
- if (idx >= s->idx_to_atom_count) {
- JS_ThrowSyntaxError(s->ctx, "invalid atom index (pos=%u)",
- (unsigned int)(s->ptr - s->buf_start));
- *patom = JS_ATOM_NULL;
- return s->error_state = -1;
- }
- atom = JS_DupAtom(s->ctx, s->idx_to_atom[idx]);
- }
- *patom = atom;
- return 0;
- }
- static int bc_get_atom(BCReaderState *s, JSAtom *patom)
- {
- uint32_t v;
- if (bc_get_leb128(s, &v))
- return -1;
- if (v & 1) {
- *patom = __JS_AtomFromUInt32(v >> 1);
- return 0;
- } else {
- return bc_idx_to_atom(s, patom, v >> 1);
- }
- }
- static JSString *JS_ReadString(BCReaderState *s)
- {
- uint32_t len;
- size_t size;
- BOOL is_wide_char;
- JSString *p;
- if (bc_get_leb128(s, &len))
- return NULL;
- is_wide_char = len & 1;
- len >>= 1;
- if (len > JS_STRING_LEN_MAX) {
- JS_ThrowInternalError(s->ctx, "string too long");
- return NULL;
- }
- p = js_alloc_string(s->ctx, len, is_wide_char);
- if (!p) {
- s->error_state = -1;
- return NULL;
- }
- size = (size_t)len << is_wide_char;
- if ((s->buf_end - s->ptr) < size) {
- bc_read_error_end(s);
- js_free_string(s->ctx->rt, p);
- return NULL;
- }
- memcpy(p->u.str8, s->ptr, size);
- s->ptr += size;
- if (is_wide_char) {
- if (is_be()) {
- uint32_t i;
- for (i = 0; i < len; i++)
- p->u.str16[i] = bswap16(p->u.str16[i]);
- }
- } else {
- p->u.str8[size] = '\0'; /* add the trailing zero for 8 bit strings */
- }
- #ifdef DUMP_READ_OBJECT
- JS_DumpString(s->ctx->rt, p); printf("\n");
- #endif
- return p;
- }
- static uint32_t bc_get_flags(uint32_t flags, int *pidx, int n)
- {
- uint32_t val;
- /* XXX: this does not work for n == 32 */
- val = (flags >> *pidx) & ((1U << n) - 1);
- *pidx += n;
- return val;
- }
- static int JS_ReadFunctionBytecode(BCReaderState *s, JSFunctionBytecode *b,
- int byte_code_offset, uint32_t bc_len)
- {
- uint8_t *bc_buf;
- int pos, len, op;
- JSAtom atom;
- uint32_t idx;
- if (s->is_rom_data) {
- /* directly use the input buffer */
- if (unlikely(s->buf_end - s->ptr < bc_len))
- return bc_read_error_end(s);
- bc_buf = (uint8_t *)s->ptr;
- s->ptr += bc_len;
- } else {
- bc_buf = (void *)((uint8_t*)b + byte_code_offset);
- if (bc_get_buf(s, bc_buf, bc_len))
- return -1;
- }
- b->byte_code_buf = bc_buf;
- if (is_be())
- bc_byte_swap(bc_buf, bc_len);
- pos = 0;
- while (pos < bc_len) {
- op = bc_buf[pos];
- len = short_opcode_info(op).size;
- switch(short_opcode_info(op).fmt) {
- case OP_FMT_atom:
- case OP_FMT_atom_u8:
- case OP_FMT_atom_u16:
- case OP_FMT_atom_label_u8:
- case OP_FMT_atom_label_u16:
- idx = get_u32(bc_buf + pos + 1);
- if (s->is_rom_data) {
- /* just increment the reference count of the atom */
- JS_DupAtom(s->ctx, (JSAtom)idx);
- } else {
- if (bc_idx_to_atom(s, &atom, idx)) {
- /* Note: the atoms will be freed up to this position */
- b->byte_code_len = pos;
- return -1;
- }
- put_u32(bc_buf + pos + 1, atom);
- #ifdef DUMP_READ_OBJECT
- bc_read_trace(s, "at %d, fixup atom: ", pos + 1); print_atom(s->ctx, atom); printf("\n");
- #endif
- }
- break;
- default:
- break;
- }
- pos += len;
- }
- return 0;
- }
- static JSValue JS_ReadBigInt(BCReaderState *s)
- {
- JSValue obj = JS_UNDEFINED;
- uint32_t len, i, n;
- JSBigInt *p;
- js_limb_t v;
- uint8_t v8;
-
- if (bc_get_leb128(s, &len))
- goto fail;
- bc_read_trace(s, "len=%" PRId64 "\n", (int64_t)len);
- if (len == 0) {
- /* zero case */
- bc_read_trace(s, "}\n");
- return __JS_NewShortBigInt(s->ctx, 0);
- }
- p = js_bigint_new(s->ctx, (len - 1) / (JS_LIMB_BITS / 8) + 1);
- if (!p)
- goto fail;
- for(i = 0; i < len / (JS_LIMB_BITS / 8); i++) {
- #if JS_LIMB_BITS == 32
- if (bc_get_u32(s, &v))
- goto fail;
- #else
- if (bc_get_u64(s, &v))
- goto fail;
- #endif
- p->tab[i] = v;
- }
- n = len % (JS_LIMB_BITS / 8);
- if (n != 0) {
- int shift;
- v = 0;
- for(i = 0; i < n; i++) {
- if (bc_get_u8(s, &v8))
- goto fail;
- v |= (js_limb_t)v8 << (i * 8);
- }
- shift = JS_LIMB_BITS - n * 8;
- /* extend the sign */
- if (shift != 0) {
- v = (js_slimb_t)(v << shift) >> shift;
- }
- p->tab[p->len - 1] = v;
- }
- bc_read_trace(s, "}\n");
- return JS_CompactBigInt(s->ctx, p);
- fail:
- JS_FreeValue(s->ctx, obj);
- return JS_EXCEPTION;
- }
- static JSValue JS_ReadObjectRec(BCReaderState *s);
- static int BC_add_object_ref1(BCReaderState *s, JSObject *p)
- {
- if (s->allow_reference) {
- if (js_resize_array(s->ctx, (void *)&s->objects,
- sizeof(s->objects[0]),
- &s->objects_size, s->objects_count + 1))
- return -1;
- s->objects[s->objects_count++] = p;
- }
- return 0;
- }
- static int BC_add_object_ref(BCReaderState *s, JSValueConst obj)
- {
- return BC_add_object_ref1(s, JS_VALUE_GET_OBJ(obj));
- }
- static JSValue JS_ReadFunctionTag(BCReaderState *s)
- {
- JSContext *ctx = s->ctx;
- JSFunctionBytecode bc, *b;
- JSValue obj = JS_UNDEFINED;
- uint16_t v16;
- uint8_t v8;
- int idx, i, local_count;
- int cpool_offset, byte_code_offset;
- int closure_var_offset, vardefs_offset;
- uint64_t function_size;
-
- memset(&bc, 0, sizeof(bc));
- if (bc_get_u16(s, &v16))
- goto fail;
- idx = 0;
- bc.has_prototype = bc_get_flags(v16, &idx, 1);
- bc.has_simple_parameter_list = bc_get_flags(v16, &idx, 1);
- bc.is_derived_class_constructor = bc_get_flags(v16, &idx, 1);
- bc.need_home_object = bc_get_flags(v16, &idx, 1);
- bc.func_kind = bc_get_flags(v16, &idx, 2);
- bc.new_target_allowed = bc_get_flags(v16, &idx, 1);
- bc.super_call_allowed = bc_get_flags(v16, &idx, 1);
- bc.super_allowed = bc_get_flags(v16, &idx, 1);
- bc.arguments_allowed = bc_get_flags(v16, &idx, 1);
- bc.has_debug = bc_get_flags(v16, &idx, 1);
- bc.is_direct_or_indirect_eval = bc_get_flags(v16, &idx, 1);
- bc.read_only_bytecode = s->is_rom_data;
- if (bc_get_u8(s, &v8))
- goto fail;
- bc.js_mode = v8;
- if (bc_get_atom(s, &bc.func_name)) //@ atom leak if failure
- goto fail;
- if (bc_get_leb128_u16(s, &bc.arg_count))
- goto fail;
- if (bc_get_leb128_u16(s, &bc.var_count))
- goto fail;
- if (bc_get_leb128_u16(s, &bc.defined_arg_count))
- goto fail;
- if (bc_get_leb128_u16(s, &bc.stack_size))
- goto fail;
- if (bc_get_leb128_u16(s, &bc.var_ref_count))
- goto fail;
- if (bc_get_leb128_int(s, &bc.closure_var_count))
- goto fail;
- if (bc_get_leb128_int(s, &bc.cpool_count))
- goto fail;
- if (bc_get_leb128_int(s, &bc.byte_code_len))
- goto fail;
- if (bc_get_leb128_int(s, &local_count))
- goto fail;
- if (bc.has_debug) {
- function_size = sizeof(*b);
- } else {
- function_size = offsetof(JSFunctionBytecode, debug);
- }
- cpool_offset = function_size;
- function_size += (uint64_t)bc.cpool_count * sizeof(*bc.cpool);
- vardefs_offset = function_size;
- function_size += (uint64_t)local_count * sizeof(*bc.vardefs);
- closure_var_offset = function_size;
- function_size += (uint64_t)bc.closure_var_count * sizeof(*bc.closure_var);
- byte_code_offset = function_size;
- if (!bc.read_only_bytecode) {
- function_size += bc.byte_code_len;
- }
- if (function_size > INT32_MAX)
- return JS_ThrowOutOfMemory(ctx);
- b = js_mallocz(ctx, function_size);
- if (!b)
- return JS_EXCEPTION;
- memcpy(b, &bc, offsetof(JSFunctionBytecode, debug));
- if (local_count != 0) {
- b->vardefs = (void *)((uint8_t*)b + vardefs_offset);
- }
- if (b->closure_var_count != 0) {
- b->closure_var = (void *)((uint8_t*)b + closure_var_offset);
- }
- if (b->cpool_count != 0) {
- b->cpool = (void *)((uint8_t*)b + cpool_offset);
- }
- js_rc(b)->ref_count = 1;
- add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);
- obj = JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b);
- #ifdef DUMP_READ_OBJECT
- bc_read_trace(s, "name: "); print_atom(s->ctx, b->func_name); printf("\n");
- #endif
- bc_read_trace(s, "args=%d vars=%d defargs=%d closures=%d cpool=%d\n",
- b->arg_count, b->var_count, b->defined_arg_count,
- b->closure_var_count, b->cpool_count);
- bc_read_trace(s, "stack=%d bclen=%d locals=%d\n",
- b->stack_size, b->byte_code_len, local_count);
- if (local_count != 0) {
- bc_read_trace(s, "vars {\n");
- for(i = 0; i < local_count; i++) {
- JSBytecodeVarDef *vd = &b->vardefs[i];
- if (bc_get_atom(s, &vd->var_name))
- goto fail;
- if (bc_get_leb128_int(s, &vd->scope_next))
- goto fail;
- vd->scope_next--;
- if (bc_get_leb128_u16(s, &vd->var_ref_idx))
- goto fail;
- if (bc_get_u8(s, &v8))
- goto fail;
- idx = 0;
- vd->var_kind = bc_get_flags(v8, &idx, 4);
- vd->is_const = bc_get_flags(v8, &idx, 1);
- vd->is_lexical = bc_get_flags(v8, &idx, 1);
- vd->is_captured = bc_get_flags(v8, &idx, 1);
- vd->has_scope = bc_get_flags(v8, &idx, 1);
- #ifdef DUMP_READ_OBJECT
- bc_read_trace(s, "name: "); print_atom(s->ctx, vd->var_name); printf("\n");
- #endif
- }
- bc_read_trace(s, "}\n");
- }
- if (b->closure_var_count != 0) {
- bc_read_trace(s, "closure vars {\n");
- for(i = 0; i < b->closure_var_count; i++) {
- JSClosureVar *cv = &b->closure_var[i];
- int var_idx;
- if (bc_get_atom(s, &cv->var_name))
- goto fail;
- if (bc_get_leb128_int(s, &var_idx))
- goto fail;
- cv->var_idx = var_idx;
- if (bc_get_u16(s, &v16))
- goto fail;
- idx = 0;
- cv->closure_type = bc_get_flags(v16, &idx, 3);
- cv->is_const = bc_get_flags(v16, &idx, 1);
- cv->is_lexical = bc_get_flags(v16, &idx, 1);
- cv->var_kind = bc_get_flags(v16, &idx, 4);
- #ifdef DUMP_READ_OBJECT
- bc_read_trace(s, "name: "); print_atom(s->ctx, cv->var_name); printf("\n");
- #endif
- }
- bc_read_trace(s, "}\n");
- }
- {
- bc_read_trace(s, "bytecode {\n");
- if (JS_ReadFunctionBytecode(s, b, byte_code_offset, b->byte_code_len))
- goto fail;
- bc_read_trace(s, "}\n");
- }
- if (b->has_debug) {
- /* read optional debug information */
- bc_read_trace(s, "debug {\n");
- if (bc_get_atom(s, &b->debug.filename))
- goto fail;
- #ifdef DUMP_READ_OBJECT
- bc_read_trace(s, "filename: "); print_atom(s->ctx, b->debug.filename); printf("\n");
- #endif
- if (bc_get_leb128_int(s, &b->debug.pc2line_len))
- goto fail;
- if (b->debug.pc2line_len) {
- b->debug.pc2line_buf = js_mallocz(ctx, b->debug.pc2line_len);
- if (!b->debug.pc2line_buf)
- goto fail;
- if (bc_get_buf(s, b->debug.pc2line_buf, b->debug.pc2line_len))
- goto fail;
- }
- if (bc_get_leb128_int(s, &b->debug.source_len))
- goto fail;
- if (b->debug.source_len) {
- bc_read_trace(s, "source: %d bytes\n", b->source_len);
- b->debug.source = js_mallocz(ctx, b->debug.source_len);
- if (!b->debug.source)
- goto fail;
- if (bc_get_buf(s, (uint8_t *)b->debug.source, b->debug.source_len))
- goto fail;
- }
- bc_read_trace(s, "}\n");
- }
- if (b->cpool_count != 0) {
- bc_read_trace(s, "cpool {\n");
- for(i = 0; i < b->cpool_count; i++) {
- JSValue val;
- val = JS_ReadObjectRec(s);
- if (JS_IsException(val))
- goto fail;
- b->cpool[i] = val;
- }
- bc_read_trace(s, "}\n");
- }
- b->realm = JS_DupContext(ctx);
- return obj;
- fail:
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- static JSValue JS_ReadModule(BCReaderState *s)
- {
- JSContext *ctx = s->ctx;
- JSValue obj;
- JSModuleDef *m = NULL;
- JSAtom module_name;
- int i;
- uint8_t v8;
- if (bc_get_atom(s, &module_name))
- goto fail;
- #ifdef DUMP_READ_OBJECT
- bc_read_trace(s, "name: "); print_atom(s->ctx, module_name); printf("\n");
- #endif
- m = js_new_module_def(ctx, module_name);
- if (!m)
- goto fail;
- obj = JS_NewModuleValue(ctx, m);
- if (bc_get_leb128_int(s, &m->req_module_entries_count))
- goto fail;
- if (m->req_module_entries_count != 0) {
- m->req_module_entries_size = m->req_module_entries_count;
- m->req_module_entries = js_mallocz(ctx, sizeof(m->req_module_entries[0]) * m->req_module_entries_size);
- if (!m->req_module_entries)
- goto fail;
- for(i = 0; i < m->req_module_entries_count; i++) {
- JSReqModuleEntry *rme = &m->req_module_entries[i];
- JSValue val;
- if (bc_get_atom(s, &rme->module_name))
- goto fail;
- val = JS_ReadObjectRec(s);
- if (JS_IsException(val))
- goto fail;
- rme->attributes = val;
- }
- }
- if (bc_get_leb128_int(s, &m->export_entries_count))
- goto fail;
- if (m->export_entries_count != 0) {
- m->export_entries_size = m->export_entries_count;
- m->export_entries = js_mallocz(ctx, sizeof(m->export_entries[0]) * m->export_entries_size);
- if (!m->export_entries)
- goto fail;
- for(i = 0; i < m->export_entries_count; i++) {
- JSExportEntry *me = &m->export_entries[i];
- if (bc_get_u8(s, &v8))
- goto fail;
- me->export_type = v8;
- if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
- if (bc_get_leb128_int(s, &me->u.local.var_idx))
- goto fail;
- } else {
- if (bc_get_leb128_int(s, &me->u.req_module_idx))
- goto fail;
- if (bc_get_atom(s, &me->local_name))
- goto fail;
- }
- if (bc_get_atom(s, &me->export_name))
- goto fail;
- }
- }
- if (bc_get_leb128_int(s, &m->star_export_entries_count))
- goto fail;
- if (m->star_export_entries_count != 0) {
- m->star_export_entries_size = m->star_export_entries_count;
- m->star_export_entries = js_mallocz(ctx, sizeof(m->star_export_entries[0]) * m->star_export_entries_size);
- if (!m->star_export_entries)
- goto fail;
- for(i = 0; i < m->star_export_entries_count; i++) {
- JSStarExportEntry *se = &m->star_export_entries[i];
- if (bc_get_leb128_int(s, &se->req_module_idx))
- goto fail;
- }
- }
- if (bc_get_leb128_int(s, &m->import_entries_count))
- goto fail;
- if (m->import_entries_count != 0) {
- m->import_entries_size = m->import_entries_count;
- m->import_entries = js_mallocz(ctx, sizeof(m->import_entries[0]) * m->import_entries_size);
- if (!m->import_entries)
- goto fail;
- for(i = 0; i < m->import_entries_count; i++) {
- JSImportEntry *mi = &m->import_entries[i];
- uint8_t v8;
- if (bc_get_leb128_int(s, &mi->var_idx))
- goto fail;
- if (bc_get_u8(s, &v8))
- goto fail;
- mi->is_star = (v8 != 0);
- if (bc_get_atom(s, &mi->import_name))
- goto fail;
- if (bc_get_leb128_int(s, &mi->req_module_idx))
- goto fail;
- }
- }
- if (bc_get_u8(s, &v8))
- goto fail;
- m->has_tla = (v8 != 0);
- m->func_obj = JS_ReadObjectRec(s);
- if (JS_IsException(m->func_obj))
- goto fail;
- return obj;
- fail:
- if (m) {
- JS_FreeValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));
- }
- return JS_EXCEPTION;
- }
- static JSValue JS_ReadObjectTag(BCReaderState *s)
- {
- JSContext *ctx = s->ctx;
- JSValue obj;
- uint32_t prop_count, i;
- JSAtom atom;
- JSValue val;
- int ret;
- obj = JS_NewObject(ctx);
- if (BC_add_object_ref(s, obj))
- goto fail;
- if (bc_get_leb128(s, &prop_count))
- goto fail;
- for(i = 0; i < prop_count; i++) {
- if (bc_get_atom(s, &atom))
- goto fail;
- #ifdef DUMP_READ_OBJECT
- bc_read_trace(s, "propname: "); print_atom(s->ctx, atom); printf("\n");
- #endif
- val = JS_ReadObjectRec(s);
- if (JS_IsException(val)) {
- JS_FreeAtom(ctx, atom);
- goto fail;
- }
- ret = JS_DefinePropertyValue(ctx, obj, atom, val, JS_PROP_C_W_E);
- JS_FreeAtom(ctx, atom);
- if (ret < 0)
- goto fail;
- }
- return obj;
- fail:
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- static JSValue JS_ReadArray(BCReaderState *s, int tag)
- {
- JSContext *ctx = s->ctx;
- JSValue obj;
- uint32_t len, i;
- JSValue val;
- int ret, prop_flags;
- BOOL is_template;
- obj = JS_NewArray(ctx);
- if (BC_add_object_ref(s, obj))
- goto fail;
- is_template = (tag == BC_TAG_TEMPLATE_OBJECT);
- if (bc_get_leb128(s, &len))
- goto fail;
- for(i = 0; i < len; i++) {
- val = JS_ReadObjectRec(s);
- if (JS_IsException(val))
- goto fail;
- if (is_template)
- prop_flags = JS_PROP_ENUMERABLE;
- else
- prop_flags = JS_PROP_C_W_E;
- ret = JS_DefinePropertyValueUint32(ctx, obj, i, val,
- prop_flags);
- if (ret < 0)
- goto fail;
- }
- if (is_template) {
- val = JS_ReadObjectRec(s);
- if (JS_IsException(val))
- goto fail;
- if (!JS_IsUndefined(val)) {
- ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_raw, val, 0);
- if (ret < 0)
- goto fail;
- }
- JS_PreventExtensions(ctx, obj);
- }
- return obj;
- fail:
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- static JSValue JS_ReadTypedArray(BCReaderState *s)
- {
- JSContext *ctx = s->ctx;
- JSValue obj = JS_UNDEFINED, array_buffer = JS_UNDEFINED;
- uint8_t array_tag;
- JSValueConst args[3];
- uint32_t offset, len, idx;
- if (bc_get_u8(s, &array_tag))
- return JS_EXCEPTION;
- if (array_tag >= JS_TYPED_ARRAY_COUNT)
- return JS_ThrowTypeError(ctx, "invalid typed array");
- if (bc_get_leb128(s, &len))
- return JS_EXCEPTION;
- if (bc_get_leb128(s, &offset))
- return JS_EXCEPTION;
- /* XXX: this hack could be avoided if the typed array could be
- created before the array buffer */
- idx = s->objects_count;
- if (BC_add_object_ref1(s, NULL))
- goto fail;
- array_buffer = JS_ReadObjectRec(s);
- if (JS_IsException(array_buffer))
- return JS_EXCEPTION;
- if (!js_get_array_buffer(ctx, array_buffer)) {
- JS_FreeValue(ctx, array_buffer);
- return JS_EXCEPTION;
- }
- args[0] = array_buffer;
- args[1] = JS_NewInt64(ctx, offset);
- args[2] = JS_NewInt64(ctx, len);
- obj = js_typed_array_constructor(ctx, JS_UNDEFINED,
- 3, args,
- JS_CLASS_UINT8C_ARRAY + array_tag);
- if (JS_IsException(obj))
- goto fail;
- if (s->allow_reference) {
- s->objects[idx] = JS_VALUE_GET_OBJ(obj);
- }
- JS_FreeValue(ctx, array_buffer);
- return obj;
- fail:
- JS_FreeValue(ctx, array_buffer);
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- static JSValue JS_ReadArrayBuffer(BCReaderState *s)
- {
- JSContext *ctx = s->ctx;
- uint32_t byte_length, max_byte_length;
- uint64_t max_byte_length_u64, *pmax_byte_length = NULL;
- JSValue obj;
- if (bc_get_leb128(s, &byte_length))
- return JS_EXCEPTION;
- if (bc_get_leb128(s, &max_byte_length))
- return JS_EXCEPTION;
- if (max_byte_length < byte_length)
- return JS_ThrowTypeError(ctx, "invalid array buffer");
- if (max_byte_length != UINT32_MAX) {
- max_byte_length_u64 = max_byte_length;
- pmax_byte_length = &max_byte_length_u64;
- }
- if (unlikely(s->buf_end - s->ptr < byte_length)) {
- bc_read_error_end(s);
- return JS_EXCEPTION;
- }
- // makes a copy of the input
- obj = js_array_buffer_constructor3(ctx, JS_UNDEFINED,
- byte_length, pmax_byte_length,
- JS_CLASS_ARRAY_BUFFER,
- (uint8_t*)s->ptr,
- js_array_buffer_free, NULL,
- /*alloc_flag*/TRUE);
- if (JS_IsException(obj))
- goto fail;
- if (BC_add_object_ref(s, obj))
- goto fail;
- s->ptr += byte_length;
- return obj;
- fail:
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- static JSValue JS_ReadSharedArrayBuffer(BCReaderState *s)
- {
- JSContext *ctx = s->ctx;
- uint32_t byte_length, max_byte_length;
- uint64_t max_byte_length_u64, *pmax_byte_length = NULL;
- uint8_t *data_ptr;
- JSValue obj;
- uint64_t u64;
- if (bc_get_leb128(s, &byte_length))
- return JS_EXCEPTION;
- if (bc_get_leb128(s, &max_byte_length))
- return JS_EXCEPTION;
- if (max_byte_length < byte_length)
- return JS_ThrowTypeError(ctx, "invalid array buffer");
- if (max_byte_length != UINT32_MAX) {
- max_byte_length_u64 = max_byte_length;
- pmax_byte_length = &max_byte_length_u64;
- }
- if (bc_get_u64(s, &u64))
- return JS_EXCEPTION;
- data_ptr = (uint8_t *)(uintptr_t)u64;
- /* the SharedArrayBuffer is cloned */
- obj = js_array_buffer_constructor3(ctx, JS_UNDEFINED,
- byte_length, pmax_byte_length,
- JS_CLASS_SHARED_ARRAY_BUFFER,
- data_ptr,
- NULL, NULL, FALSE);
- if (JS_IsException(obj))
- goto fail;
- if (BC_add_object_ref(s, obj))
- goto fail;
- return obj;
- fail:
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- static JSValue JS_ReadDate(BCReaderState *s)
- {
- JSContext *ctx = s->ctx;
- JSValue val, obj = JS_UNDEFINED;
- val = JS_ReadObjectRec(s);
- if (JS_IsException(val))
- goto fail;
- if (!JS_IsNumber(val)) {
- JS_ThrowTypeError(ctx, "Number tag expected for date");
- goto fail;
- }
- obj = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_DATE],
- JS_CLASS_DATE);
- if (JS_IsException(obj))
- goto fail;
- if (BC_add_object_ref(s, obj))
- goto fail;
- JS_SetObjectData(ctx, obj, val);
- return obj;
- fail:
- JS_FreeValue(ctx, val);
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- static JSValue JS_ReadObjectValue(BCReaderState *s)
- {
- JSContext *ctx = s->ctx;
- JSValue val, obj = JS_UNDEFINED;
- val = JS_ReadObjectRec(s);
- if (JS_IsException(val))
- goto fail;
- obj = JS_ToObject(ctx, val);
- if (JS_IsException(obj))
- goto fail;
- if (BC_add_object_ref(s, obj))
- goto fail;
- JS_FreeValue(ctx, val);
- return obj;
- fail:
- JS_FreeValue(ctx, val);
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- static JSValue JS_ReadObjectRec(BCReaderState *s)
- {
- JSContext *ctx = s->ctx;
- uint8_t tag;
- JSValue obj = JS_UNDEFINED;
- if (js_check_stack_overflow(ctx->rt, 0))
- return JS_ThrowStackOverflow(ctx);
- if (bc_get_u8(s, &tag))
- return JS_EXCEPTION;
- bc_read_trace(s, "%s {\n", bc_tag_str[tag]);
- switch(tag) {
- case BC_TAG_NULL:
- obj = JS_NULL;
- break;
- case BC_TAG_UNDEFINED:
- obj = JS_UNDEFINED;
- break;
- case BC_TAG_BOOL_FALSE:
- case BC_TAG_BOOL_TRUE:
- obj = JS_NewBool(ctx, tag - BC_TAG_BOOL_FALSE);
- break;
- case BC_TAG_INT32:
- {
- int32_t val;
- if (bc_get_sleb128(s, &val))
- return JS_EXCEPTION;
- bc_read_trace(s, "%d\n", val);
- obj = JS_NewInt32(ctx, val);
- }
- break;
- case BC_TAG_FLOAT64:
- {
- JSFloat64Union u;
- if (bc_get_u64(s, &u.u64))
- return JS_EXCEPTION;
- bc_read_trace(s, "%g\n", u.d);
- obj = __JS_NewFloat64(ctx, u.d);
- }
- break;
- case BC_TAG_STRING:
- {
- JSString *p;
- p = JS_ReadString(s);
- if (!p)
- return JS_EXCEPTION;
- obj = JS_MKPTR(JS_TAG_STRING, p);
- }
- break;
- case BC_TAG_FUNCTION_BYTECODE:
- if (!s->allow_bytecode)
- goto invalid_tag;
- obj = JS_ReadFunctionTag(s);
- break;
- case BC_TAG_MODULE:
- if (!s->allow_bytecode)
- goto invalid_tag;
- obj = JS_ReadModule(s);
- break;
- case BC_TAG_OBJECT:
- obj = JS_ReadObjectTag(s);
- break;
- case BC_TAG_ARRAY:
- case BC_TAG_TEMPLATE_OBJECT:
- obj = JS_ReadArray(s, tag);
- break;
- case BC_TAG_TYPED_ARRAY:
- obj = JS_ReadTypedArray(s);
- break;
- case BC_TAG_ARRAY_BUFFER:
- obj = JS_ReadArrayBuffer(s);
- break;
- case BC_TAG_SHARED_ARRAY_BUFFER:
- if (!s->allow_sab || !ctx->rt->sab_funcs.sab_dup)
- goto invalid_tag;
- obj = JS_ReadSharedArrayBuffer(s);
- break;
- case BC_TAG_DATE:
- obj = JS_ReadDate(s);
- break;
- case BC_TAG_OBJECT_VALUE:
- obj = JS_ReadObjectValue(s);
- break;
- case BC_TAG_BIG_INT:
- obj = JS_ReadBigInt(s);
- break;
- case BC_TAG_OBJECT_REFERENCE:
- {
- uint32_t val;
- if (!s->allow_reference)
- return JS_ThrowSyntaxError(ctx, "object references are not allowed");
- if (bc_get_leb128(s, &val))
- return JS_EXCEPTION;
- bc_read_trace(s, "%u\n", val);
- if (val >= s->objects_count) {
- return JS_ThrowSyntaxError(ctx, "invalid object reference (%u >= %u)",
- val, s->objects_count);
- }
- obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, s->objects[val]));
- }
- break;
- default:
- invalid_tag:
- return JS_ThrowSyntaxError(ctx, "invalid tag (tag=%d pos=%u)",
- tag, (unsigned int)(s->ptr - s->buf_start));
- }
- bc_read_trace(s, "}\n");
- return obj;
- }
- static int JS_ReadObjectAtoms(BCReaderState *s)
- {
- uint8_t v8;
- JSString *p;
- int i;
- JSAtom atom;
- if (bc_get_u8(s, &v8))
- return -1;
- if (v8 != BC_VERSION) {
- JS_ThrowSyntaxError(s->ctx, "invalid version (%d expected=%d)",
- v8, BC_VERSION);
- return -1;
- }
- if (bc_get_leb128(s, &s->idx_to_atom_count))
- return -1;
- bc_read_trace(s, "%d atom indexes {\n", s->idx_to_atom_count);
- if (s->idx_to_atom_count != 0) {
- s->idx_to_atom = js_mallocz(s->ctx, s->idx_to_atom_count *
- sizeof(s->idx_to_atom[0]));
- if (!s->idx_to_atom)
- return s->error_state = -1;
- }
- for(i = 0; i < s->idx_to_atom_count; i++) {
- p = JS_ReadString(s);
- if (!p)
- return -1;
- atom = JS_NewAtomStr(s->ctx, p);
- if (atom == JS_ATOM_NULL)
- return s->error_state = -1;
- s->idx_to_atom[i] = atom;
- if (s->is_rom_data && (atom != (i + s->first_atom)))
- s->is_rom_data = FALSE; /* atoms must be relocated */
- }
- bc_read_trace(s, "}\n");
- return 0;
- }
- static void bc_reader_free(BCReaderState *s)
- {
- int i;
- if (s->idx_to_atom) {
- for(i = 0; i < s->idx_to_atom_count; i++) {
- JS_FreeAtom(s->ctx, s->idx_to_atom[i]);
- }
- js_free(s->ctx, s->idx_to_atom);
- }
- js_free(s->ctx, s->objects);
- }
- JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len,
- int flags)
- {
- BCReaderState ss, *s = &ss;
- JSValue obj;
- ctx->binary_object_count += 1;
- ctx->binary_object_size += buf_len;
- memset(s, 0, sizeof(*s));
- s->ctx = ctx;
- s->buf_start = buf;
- s->buf_end = buf + buf_len;
- s->ptr = buf;
- s->allow_bytecode = ((flags & JS_READ_OBJ_BYTECODE) != 0);
- s->is_rom_data = ((flags & JS_READ_OBJ_ROM_DATA) != 0);
- s->allow_sab = ((flags & JS_READ_OBJ_SAB) != 0);
- s->allow_reference = ((flags & JS_READ_OBJ_REFERENCE) != 0);
- if (s->allow_bytecode)
- s->first_atom = JS_ATOM_END;
- else
- s->first_atom = 1;
- if (JS_ReadObjectAtoms(s)) {
- obj = JS_EXCEPTION;
- } else {
- obj = JS_ReadObjectRec(s);
- }
- bc_reader_free(s);
- return obj;
- }
- /*******************************************************************/
- /* runtime functions & objects */
- static JSValue js_string_constructor(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv);
- static JSValue js_boolean_constructor(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv);
- static JSValue js_number_constructor(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv);
- static int check_function(JSContext *ctx, JSValueConst obj)
- {
- if (likely(JS_IsFunction(ctx, obj)))
- return 0;
- JS_ThrowTypeError(ctx, "not a function");
- return -1;
- }
- static int check_exception_free(JSContext *ctx, JSValue obj)
- {
- JS_FreeValue(ctx, obj);
- return JS_IsException(obj);
- }
- static JSAtom find_atom(JSContext *ctx, const char *name)
- {
- JSAtom atom;
- int len;
- if (*name == '[') {
- name++;
- len = strlen(name) - 1;
- /* We assume 8 bit non null strings, which is the case for these
- symbols */
- for(atom = JS_ATOM_Symbol_toPrimitive; atom < JS_ATOM_END; atom++) {
- JSAtomStruct *p = ctx->rt->atom_array[atom];
- JSString *str = p;
- if (str->len == len && !memcmp(str->u.str8, name, len))
- return JS_DupAtom(ctx, atom);
- }
- abort();
- } else {
- atom = JS_NewAtom(ctx, name);
- }
- return atom;
- }
- static JSValue JS_NewObjectProtoList(JSContext *ctx, JSValueConst proto,
- const JSCFunctionListEntry *fields, int n_fields)
- {
- JSValue obj;
- obj = JS_NewObjectProtoClassAlloc(ctx, proto, JS_CLASS_OBJECT, n_fields);
- if (JS_IsException(obj))
- return obj;
- if (JS_SetPropertyFunctionList(ctx, obj, fields, n_fields)) {
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- return obj;
- }
- static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p,
- JSAtom atom, void *opaque)
- {
- const JSCFunctionListEntry *e = opaque;
- JSValue val, proto;
- switch(e->def_type) {
- case JS_DEF_CFUNC:
- val = JS_NewCFunction2(ctx, e->u.func.cfunc.generic,
- e->name, e->u.func.length, e->u.func.cproto, e->magic);
- break;
- case JS_DEF_PROP_STRING:
- val = JS_NewAtomString(ctx, e->u.str);
- break;
- case JS_DEF_OBJECT:
- /* XXX: could add a flag */
- if (atom == JS_ATOM_Symbol_unscopables)
- proto = JS_NULL;
- else
- proto = ctx->class_proto[JS_CLASS_OBJECT];
- val = JS_NewObjectProtoList(ctx, proto,
- e->u.prop_list.tab, e->u.prop_list.len);
- break;
- default:
- abort();
- }
- return val;
- }
- static int JS_InstantiateFunctionListItem(JSContext *ctx, JSValueConst obj,
- JSAtom atom,
- const JSCFunctionListEntry *e)
- {
- JSValue val;
- int prop_flags = e->prop_flags;
- switch(e->def_type) {
- case JS_DEF_ALIAS: /* using autoinit for aliases is not safe */
- {
- JSAtom atom1 = find_atom(ctx, e->u.alias.name);
- switch (e->u.alias.base) {
- case -1:
- val = JS_GetProperty(ctx, obj, atom1);
- break;
- case 0:
- val = JS_GetProperty(ctx, ctx->global_obj, atom1);
- break;
- case 1:
- val = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], atom1);
- break;
- default:
- abort();
- }
- JS_FreeAtom(ctx, atom1);
- if (JS_IsException(val))
- return -1;
- if (atom == JS_ATOM_Symbol_toPrimitive) {
- /* Symbol.toPrimitive functions are not writable */
- prop_flags = JS_PROP_CONFIGURABLE;
- } else if (atom == JS_ATOM_Symbol_hasInstance) {
- /* Function.prototype[Symbol.hasInstance] is not writable nor configurable */
- prop_flags = 0;
- }
- }
- break;
- case JS_DEF_CFUNC:
- if (atom == JS_ATOM_Symbol_toPrimitive) {
- /* Symbol.toPrimitive functions are not writable */
- prop_flags = JS_PROP_CONFIGURABLE;
- } else if (atom == JS_ATOM_Symbol_hasInstance) {
- /* Function.prototype[Symbol.hasInstance] is not writable nor configurable */
- prop_flags = 0;
- }
- if (JS_DefineAutoInitProperty(ctx, obj, atom, JS_AUTOINIT_ID_PROP,
- (void *)e, prop_flags) < 0)
- return -1;
- return 0;
- case JS_DEF_CGETSET: /* XXX: use autoinit again ? */
- case JS_DEF_CGETSET_MAGIC:
- {
- JSValue getter, setter;
- char buf[64];
- getter = JS_UNDEFINED;
- if (e->u.getset.get.generic) {
- snprintf(buf, sizeof(buf), "get %s", e->name);
- getter = JS_NewCFunction2(ctx, e->u.getset.get.generic,
- buf, 0, e->def_type == JS_DEF_CGETSET_MAGIC ? JS_CFUNC_getter_magic : JS_CFUNC_getter,
- e->magic);
- if (JS_IsException(getter))
- return -1;
- }
- setter = JS_UNDEFINED;
- if (e->u.getset.set.generic) {
- snprintf(buf, sizeof(buf), "set %s", e->name);
- setter = JS_NewCFunction2(ctx, e->u.getset.set.generic,
- buf, 1, e->def_type == JS_DEF_CGETSET_MAGIC ? JS_CFUNC_setter_magic : JS_CFUNC_setter,
- e->magic);
- if (JS_IsException(setter)) {
- JS_FreeValue(ctx, getter);
- return -1;
- }
- }
- if (JS_DefinePropertyGetSet(ctx, obj, atom, getter, setter, prop_flags) < 0)
- return -1;
- return 0;
- }
- break;
- case JS_DEF_PROP_INT32:
- val = JS_NewInt32(ctx, e->u.i32);
- break;
- case JS_DEF_PROP_INT64:
- val = JS_NewInt64(ctx, e->u.i64);
- break;
- case JS_DEF_PROP_DOUBLE:
- val = __JS_NewFloat64(ctx, e->u.f64);
- break;
- case JS_DEF_PROP_UNDEFINED:
- val = JS_UNDEFINED;
- break;
- case JS_DEF_PROP_ATOM:
- val = JS_AtomToValue(ctx, e->u.i32);
- break;
- case JS_DEF_PROP_BOOL:
- val = JS_NewBool(ctx, e->u.i32);
- break;
- case JS_DEF_PROP_STRING:
- case JS_DEF_OBJECT:
- if (JS_DefineAutoInitProperty(ctx, obj, atom, JS_AUTOINIT_ID_PROP,
- (void *)e, prop_flags) < 0)
- return -1;
- return 0;
- default:
- abort();
- }
- if (JS_DefinePropertyValue(ctx, obj, atom, val, prop_flags) < 0)
- return -1;
- return 0;
- }
- int JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj,
- const JSCFunctionListEntry *tab, int len)
- {
- int i, ret;
- for (i = 0; i < len; i++) {
- const JSCFunctionListEntry *e = &tab[i];
- JSAtom atom = find_atom(ctx, e->name);
- if (atom == JS_ATOM_NULL)
- return -1;
- ret = JS_InstantiateFunctionListItem(ctx, obj, atom, e);
- JS_FreeAtom(ctx, atom);
- if (ret)
- return -1;
- }
- return 0;
- }
- int JS_AddModuleExportList(JSContext *ctx, JSModuleDef *m,
- const JSCFunctionListEntry *tab, int len)
- {
- int i;
- for(i = 0; i < len; i++) {
- if (JS_AddModuleExport(ctx, m, tab[i].name))
- return -1;
- }
- return 0;
- }
- int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m,
- const JSCFunctionListEntry *tab, int len)
- {
- int i;
- JSValue val;
- for(i = 0; i < len; i++) {
- const JSCFunctionListEntry *e = &tab[i];
- switch(e->def_type) {
- case JS_DEF_CFUNC:
- val = JS_NewCFunction2(ctx, e->u.func.cfunc.generic,
- e->name, e->u.func.length, e->u.func.cproto, e->magic);
- break;
- case JS_DEF_PROP_STRING:
- val = JS_NewString(ctx, e->u.str);
- break;
- case JS_DEF_PROP_INT32:
- val = JS_NewInt32(ctx, e->u.i32);
- break;
- case JS_DEF_PROP_INT64:
- val = JS_NewInt64(ctx, e->u.i64);
- break;
- case JS_DEF_PROP_DOUBLE:
- val = __JS_NewFloat64(ctx, e->u.f64);
- break;
- case JS_DEF_OBJECT:
- val = JS_NewObjectProtoList(ctx, ctx->class_proto[JS_CLASS_OBJECT],
- e->u.prop_list.tab, e->u.prop_list.len);
- break;
- default:
- abort();
- }
- if (JS_SetModuleExport(ctx, m, e->name, val))
- return -1;
- }
- return 0;
- }
- /* Note: 'func_obj' is not necessarily a constructor */
- static int JS_SetConstructor2(JSContext *ctx,
- JSValueConst func_obj,
- JSValueConst proto,
- int proto_flags, int ctor_flags)
- {
- if (JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype,
- JS_DupValue(ctx, proto), proto_flags) < 0)
- return -1;
- if (JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor,
- JS_DupValue(ctx, func_obj),
- ctor_flags) < 0)
- return -1;
- set_cycle_flag(ctx, func_obj);
- set_cycle_flag(ctx, proto);
- return 0;
- }
- /* return 0 if OK, -1 if exception */
- int JS_SetConstructor(JSContext *ctx, JSValueConst func_obj,
- JSValueConst proto)
- {
- return JS_SetConstructor2(ctx, func_obj, proto,
- 0, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
- }
- #define JS_NEW_CTOR_NO_GLOBAL (1 << 0) /* don't create a global binding */
- #define JS_NEW_CTOR_PROTO_CLASS (1 << 1) /* the prototype class is 'class_id' instead of JS_CLASS_OBJECT */
- #define JS_NEW_CTOR_PROTO_EXIST (1 << 2) /* the prototype is already defined */
- #define JS_NEW_CTOR_READONLY (1 << 3) /* read-only constructor field */
- /* Return the constructor and. Define it as a global variable unless
- JS_NEW_CTOR_NO_GLOBAL is set. The new class inherit from
- parent_ctor if it is not JS_UNDEFINED. if class_id is != -1,
- class_proto[class_id] is set. */
- static JSValue JS_NewCConstructor(JSContext *ctx, int class_id, const char *name,
- JSCFunction *func, int length, JSCFunctionEnum cproto, int magic,
- JSValueConst parent_ctor,
- const JSCFunctionListEntry *ctor_fields, int n_ctor_fields,
- const JSCFunctionListEntry *proto_fields, int n_proto_fields,
- int flags)
- {
- JSValue ctor = JS_UNDEFINED, proto, parent_proto;
- int proto_class_id, proto_flags, ctor_flags;
- proto_flags = 0;
- if (flags & JS_NEW_CTOR_READONLY) {
- ctor_flags = JS_PROP_CONFIGURABLE;
- } else {
- ctor_flags = JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE;
- }
-
- if (JS_IsUndefined(parent_ctor)) {
- parent_proto = JS_DupValue(ctx, ctx->class_proto[JS_CLASS_OBJECT]);
- parent_ctor = ctx->function_proto;
- } else {
- parent_proto = JS_GetProperty(ctx, parent_ctor, JS_ATOM_prototype);
- if (JS_IsException(parent_proto))
- return JS_EXCEPTION;
- }
-
- if (flags & JS_NEW_CTOR_PROTO_EXIST) {
- proto = JS_DupValue(ctx, ctx->class_proto[class_id]);
- } else {
- if (flags & JS_NEW_CTOR_PROTO_CLASS)
- proto_class_id = class_id;
- else
- proto_class_id = JS_CLASS_OBJECT;
- /* one additional field: constructor */
- proto = JS_NewObjectProtoClassAlloc(ctx, parent_proto, proto_class_id,
- n_proto_fields + 1);
- if (JS_IsException(proto))
- goto fail;
- if (class_id >= 0)
- ctx->class_proto[class_id] = JS_DupValue(ctx, proto);
- }
- if (JS_SetPropertyFunctionList(ctx, proto, proto_fields, n_proto_fields))
- goto fail;
- /* additional fields: name, length, prototype */
- ctor = JS_NewCFunction3(ctx, func, name, length, cproto, magic, parent_ctor,
- n_ctor_fields + 3);
- if (JS_IsException(ctor))
- goto fail;
- if (JS_SetPropertyFunctionList(ctx, ctor, ctor_fields, n_ctor_fields))
- goto fail;
- if (!(flags & JS_NEW_CTOR_NO_GLOBAL)) {
- if (JS_DefinePropertyValueStr(ctx, ctx->global_obj, name,
- JS_DupValue(ctx, ctor),
- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE) < 0)
- goto fail;
- }
- JS_SetConstructor2(ctx, ctor, proto, proto_flags, ctor_flags);
- JS_FreeValue(ctx, proto);
- JS_FreeValue(ctx, parent_proto);
- return ctor;
- fail:
- JS_FreeValue(ctx, proto);
- JS_FreeValue(ctx, parent_proto);
- JS_FreeValue(ctx, ctor);
- return JS_EXCEPTION;
- }
- static JSValue js_global_eval(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- return JS_EvalObject(ctx, ctx->global_obj, argv[0], JS_EVAL_TYPE_INDIRECT, -1);
- }
- static JSValue js_global_isNaN(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- double d;
- if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))
- return JS_EXCEPTION;
- return JS_NewBool(ctx, isnan(d));
- }
- static JSValue js_global_isFinite(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- double d;
- if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))
- return JS_EXCEPTION;
- return JS_NewBool(ctx, isfinite(d));
- }
- /* Object class */
- static JSValue JS_ToObject(JSContext *ctx, JSValueConst val)
- {
- int tag = JS_VALUE_GET_NORM_TAG(val);
- JSValue obj;
- switch(tag) {
- default:
- case JS_TAG_NULL:
- case JS_TAG_UNDEFINED:
- return JS_ThrowTypeError(ctx, "cannot convert to object");
- case JS_TAG_OBJECT:
- case JS_TAG_EXCEPTION:
- return JS_DupValue(ctx, val);
- case JS_TAG_SHORT_BIG_INT:
- case JS_TAG_BIG_INT:
- obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_INT);
- goto set_value;
- case JS_TAG_INT:
- case JS_TAG_FLOAT64:
- obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER);
- goto set_value;
- case JS_TAG_STRING:
- case JS_TAG_STRING_ROPE:
- /* XXX: should call the string constructor */
- {
- JSValue str;
- str = JS_ToString(ctx, val); /* ensure that we never store a rope */
- if (JS_IsException(str))
- return JS_EXCEPTION;
- obj = JS_NewObjectClass(ctx, JS_CLASS_STRING);
- if (!JS_IsException(obj)) {
- JS_DefinePropertyValue(ctx, obj, JS_ATOM_length,
- JS_NewInt32(ctx, JS_VALUE_GET_STRING(str)->len), 0);
- JS_SetObjectData(ctx, obj, JS_DupValue(ctx, str));
- }
- JS_FreeValue(ctx, str);
- return obj;
- }
- case JS_TAG_BOOL:
- obj = JS_NewObjectClass(ctx, JS_CLASS_BOOLEAN);
- goto set_value;
- case JS_TAG_SYMBOL:
- obj = JS_NewObjectClass(ctx, JS_CLASS_SYMBOL);
- set_value:
- if (!JS_IsException(obj))
- JS_SetObjectData(ctx, obj, JS_DupValue(ctx, val));
- return obj;
- }
- }
- static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val)
- {
- JSValue obj = JS_ToObject(ctx, val);
- JS_FreeValue(ctx, val);
- return obj;
- }
- static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d,
- JSValueConst desc)
- {
- JSValue val, getter, setter;
- int flags;
- if (!JS_IsObject(desc)) {
- JS_ThrowTypeErrorNotAnObject(ctx);
- return -1;
- }
- flags = 0;
- val = JS_UNDEFINED;
- getter = JS_UNDEFINED;
- setter = JS_UNDEFINED;
- if (JS_HasProperty(ctx, desc, JS_ATOM_enumerable)) {
- JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_enumerable);
- if (JS_IsException(prop))
- goto fail;
- flags |= JS_PROP_HAS_ENUMERABLE;
- if (JS_ToBoolFree(ctx, prop))
- flags |= JS_PROP_ENUMERABLE;
- }
- if (JS_HasProperty(ctx, desc, JS_ATOM_configurable)) {
- JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_configurable);
- if (JS_IsException(prop))
- goto fail;
- flags |= JS_PROP_HAS_CONFIGURABLE;
- if (JS_ToBoolFree(ctx, prop))
- flags |= JS_PROP_CONFIGURABLE;
- }
- if (JS_HasProperty(ctx, desc, JS_ATOM_value)) {
- flags |= JS_PROP_HAS_VALUE;
- val = JS_GetProperty(ctx, desc, JS_ATOM_value);
- if (JS_IsException(val))
- goto fail;
- }
- if (JS_HasProperty(ctx, desc, JS_ATOM_writable)) {
- JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_writable);
- if (JS_IsException(prop))
- goto fail;
- flags |= JS_PROP_HAS_WRITABLE;
- if (JS_ToBoolFree(ctx, prop))
- flags |= JS_PROP_WRITABLE;
- }
- if (JS_HasProperty(ctx, desc, JS_ATOM_get)) {
- flags |= JS_PROP_HAS_GET;
- getter = JS_GetProperty(ctx, desc, JS_ATOM_get);
- if (JS_IsException(getter) ||
- !(JS_IsUndefined(getter) || JS_IsFunction(ctx, getter))) {
- JS_ThrowTypeError(ctx, "invalid getter");
- goto fail;
- }
- }
- if (JS_HasProperty(ctx, desc, JS_ATOM_set)) {
- flags |= JS_PROP_HAS_SET;
- setter = JS_GetProperty(ctx, desc, JS_ATOM_set);
- if (JS_IsException(setter) ||
- !(JS_IsUndefined(setter) || JS_IsFunction(ctx, setter))) {
- JS_ThrowTypeError(ctx, "invalid setter");
- goto fail;
- }
- }
- if ((flags & (JS_PROP_HAS_SET | JS_PROP_HAS_GET)) &&
- (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE))) {
- JS_ThrowTypeError(ctx, "cannot have setter/getter and value or writable");
- goto fail;
- }
- d->flags = flags;
- d->value = val;
- d->getter = getter;
- d->setter = setter;
- return 0;
- fail:
- JS_FreeValue(ctx, val);
- JS_FreeValue(ctx, getter);
- JS_FreeValue(ctx, setter);
- return -1;
- }
- static __exception int JS_DefinePropertyDesc(JSContext *ctx, JSValueConst obj,
- JSAtom prop, JSValueConst desc,
- int flags)
- {
- JSPropertyDescriptor d;
- int ret;
- if (js_obj_to_desc(ctx, &d, desc) < 0)
- return -1;
- ret = JS_DefineProperty(ctx, obj, prop,
- d.value, d.getter, d.setter, d.flags | flags);
- js_free_desc(ctx, &d);
- return ret;
- }
- static __exception int JS_ObjectDefineProperties(JSContext *ctx,
- JSValueConst obj,
- JSValueConst properties)
- {
- JSValue props, desc;
- JSObject *p;
- JSPropertyEnum *atoms;
- uint32_t len, i;
- int ret = -1;
- if (!JS_IsObject(obj)) {
- JS_ThrowTypeErrorNotAnObject(ctx);
- return -1;
- }
- desc = JS_UNDEFINED;
- props = JS_ToObject(ctx, properties);
- if (JS_IsException(props))
- return -1;
- p = JS_VALUE_GET_OBJ(props);
- /* XXX: not done in the same order as the spec */
- if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK) < 0)
- goto exception;
- for(i = 0; i < len; i++) {
- JS_FreeValue(ctx, desc);
- desc = JS_GetProperty(ctx, props, atoms[i].atom);
- if (JS_IsException(desc))
- goto exception;
- if (JS_DefinePropertyDesc(ctx, obj, atoms[i].atom, desc, JS_PROP_THROW) < 0)
- goto exception;
- }
- ret = 0;
- exception:
- JS_FreePropertyEnum(ctx, atoms, len);
- JS_FreeValue(ctx, props);
- JS_FreeValue(ctx, desc);
- return ret;
- }
- static JSValue js_object_constructor(JSContext *ctx, JSValueConst new_target,
- int argc, JSValueConst *argv)
- {
- JSValue ret;
- if (!JS_IsUndefined(new_target) &&
- JS_VALUE_GET_OBJ(new_target) !=
- JS_VALUE_GET_OBJ(JS_GetActiveFunction(ctx))) {
- ret = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT);
- } else {
- int tag = JS_VALUE_GET_NORM_TAG(argv[0]);
- switch(tag) {
- case JS_TAG_NULL:
- case JS_TAG_UNDEFINED:
- ret = JS_NewObject(ctx);
- break;
- default:
- ret = JS_ToObject(ctx, argv[0]);
- break;
- }
- }
- return ret;
- }
- static JSValue js_object_create(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValueConst proto, props;
- JSValue obj;
- proto = argv[0];
- if (!JS_IsObject(proto) && !JS_IsNull(proto))
- return JS_ThrowTypeError(ctx, "not a prototype");
- obj = JS_NewObjectProto(ctx, proto);
- if (JS_IsException(obj))
- return JS_EXCEPTION;
- props = argv[1];
- if (!JS_IsUndefined(props)) {
- if (JS_ObjectDefineProperties(ctx, obj, props)) {
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- }
- return obj;
- }
- static JSValue js_object_getPrototypeOf(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
- {
- JSValueConst val;
- val = argv[0];
- if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) {
- /* ES6 feature non compatible with ES5.1: primitive types are
- accepted */
- if (magic || JS_VALUE_GET_TAG(val) == JS_TAG_NULL ||
- JS_VALUE_GET_TAG(val) == JS_TAG_UNDEFINED)
- return JS_ThrowTypeErrorNotAnObject(ctx);
- }
- return JS_GetPrototype(ctx, val);
- }
- static JSValue js_object_setPrototypeOf(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValueConst obj;
- obj = argv[0];
- if (JS_SetPrototypeInternal(ctx, obj, argv[1], TRUE) < 0)
- return JS_EXCEPTION;
- return JS_DupValue(ctx, obj);
- }
- /* magic = 1 if called as Reflect.defineProperty */
- static JSValue js_object_defineProperty(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
- {
- JSValueConst obj, prop, desc;
- int ret, flags;
- JSAtom atom;
- obj = argv[0];
- prop = argv[1];
- desc = argv[2];
- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
- return JS_ThrowTypeErrorNotAnObject(ctx);
- atom = JS_ValueToAtom(ctx, prop);
- if (unlikely(atom == JS_ATOM_NULL))
- return JS_EXCEPTION;
- flags = 0;
- if (!magic)
- flags |= JS_PROP_THROW;
- ret = JS_DefinePropertyDesc(ctx, obj, atom, desc, flags);
- JS_FreeAtom(ctx, atom);
- if (ret < 0) {
- return JS_EXCEPTION;
- } else if (magic) {
- return JS_NewBool(ctx, ret);
- } else {
- return JS_DupValue(ctx, obj);
- }
- }
- static JSValue js_object_defineProperties(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- // defineProperties(obj, properties)
- JSValueConst obj = argv[0];
- if (JS_ObjectDefineProperties(ctx, obj, argv[1]))
- return JS_EXCEPTION;
- else
- return JS_DupValue(ctx, obj);
- }
- /* magic = 1 if called as __defineSetter__ */
- static JSValue js_object___defineGetter__(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
- {
- JSValue obj;
- JSValueConst prop, value, get, set;
- int ret, flags;
- JSAtom atom;
- prop = argv[0];
- value = argv[1];
- obj = JS_ToObject(ctx, this_val);
- if (JS_IsException(obj))
- return JS_EXCEPTION;
- if (check_function(ctx, value)) {
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- atom = JS_ValueToAtom(ctx, prop);
- if (unlikely(atom == JS_ATOM_NULL)) {
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- flags = JS_PROP_THROW |
- JS_PROP_HAS_ENUMERABLE | JS_PROP_ENUMERABLE |
- JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE;
- if (magic) {
- get = JS_UNDEFINED;
- set = value;
- flags |= JS_PROP_HAS_SET;
- } else {
- get = value;
- set = JS_UNDEFINED;
- flags |= JS_PROP_HAS_GET;
- }
- ret = JS_DefineProperty(ctx, obj, atom, JS_UNDEFINED, get, set, flags);
- JS_FreeValue(ctx, obj);
- JS_FreeAtom(ctx, atom);
- if (ret < 0) {
- return JS_EXCEPTION;
- } else {
- return JS_UNDEFINED;
- }
- }
- static JSValue js_object_getOwnPropertyDescriptor(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
- {
- JSValueConst prop;
- JSAtom atom;
- JSValue ret, obj;
- JSPropertyDescriptor desc;
- int res, flags;
- if (magic) {
- /* Reflect.getOwnPropertyDescriptor case */
- if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT)
- return JS_ThrowTypeErrorNotAnObject(ctx);
- obj = JS_DupValue(ctx, argv[0]);
- } else {
- obj = JS_ToObject(ctx, argv[0]);
- if (JS_IsException(obj))
- return obj;
- }
- prop = argv[1];
- atom = JS_ValueToAtom(ctx, prop);
- if (unlikely(atom == JS_ATOM_NULL))
- goto exception;
- ret = JS_UNDEFINED;
- if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
- res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), atom);
- if (res < 0)
- goto exception;
- if (res) {
- ret = JS_NewObject(ctx);
- if (JS_IsException(ret))
- goto exception1;
- flags = JS_PROP_C_W_E | JS_PROP_THROW;
- if (desc.flags & JS_PROP_GETSET) {
- if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, desc.getter), flags) < 0
- || JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, desc.setter), flags) < 0)
- goto exception1;
- } else {
- if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, desc.value), flags) < 0
- || JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable,
- JS_NewBool(ctx, desc.flags & JS_PROP_WRITABLE), flags) < 0)
- goto exception1;
- }
- if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable,
- JS_NewBool(ctx, desc.flags & JS_PROP_ENUMERABLE), flags) < 0
- || JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable,
- JS_NewBool(ctx, desc.flags & JS_PROP_CONFIGURABLE), flags) < 0)
- goto exception1;
- js_free_desc(ctx, &desc);
- }
- }
- JS_FreeAtom(ctx, atom);
- JS_FreeValue(ctx, obj);
- return ret;
- exception1:
- js_free_desc(ctx, &desc);
- JS_FreeValue(ctx, ret);
- exception:
- JS_FreeAtom(ctx, atom);
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- static JSValue js_object_getOwnPropertyDescriptors(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- //getOwnPropertyDescriptors(obj)
- JSValue obj, r;
- JSObject *p;
- JSPropertyEnum *props;
- uint32_t len, i;
- r = JS_UNDEFINED;
- obj = JS_ToObject(ctx, argv[0]);
- if (JS_IsException(obj))
- return JS_EXCEPTION;
- p = JS_VALUE_GET_OBJ(obj);
- if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p,
- JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK))
- goto exception;
- r = JS_NewObject(ctx);
- if (JS_IsException(r))
- goto exception;
- for(i = 0; i < len; i++) {
- JSValue atomValue, desc;
- JSValueConst args[2];
- atomValue = JS_AtomToValue(ctx, props[i].atom);
- if (JS_IsException(atomValue))
- goto exception;
- args[0] = obj;
- args[1] = atomValue;
- desc = js_object_getOwnPropertyDescriptor(ctx, JS_UNDEFINED, 2, args, 0);
- JS_FreeValue(ctx, atomValue);
- if (JS_IsException(desc))
- goto exception;
- if (!JS_IsUndefined(desc)) {
- if (JS_DefinePropertyValue(ctx, r, props[i].atom, desc,
- JS_PROP_C_W_E | JS_PROP_THROW) < 0)
- goto exception;
- }
- }
- JS_FreePropertyEnum(ctx, props, len);
- JS_FreeValue(ctx, obj);
- return r;
- exception:
- JS_FreePropertyEnum(ctx, props, len);
- JS_FreeValue(ctx, obj);
- JS_FreeValue(ctx, r);
- return JS_EXCEPTION;
- }
- static JSValue JS_GetOwnPropertyNames2(JSContext *ctx, JSValueConst obj1,
- int flags, int kind)
- {
- JSValue obj, r, val, key, value;
- JSObject *p;
- JSPropertyEnum *atoms;
- uint32_t len, i, j;
- r = JS_UNDEFINED;
- val = JS_UNDEFINED;
- obj = JS_ToObject(ctx, obj1);
- if (JS_IsException(obj))
- return JS_EXCEPTION;
- p = JS_VALUE_GET_OBJ(obj);
- if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, flags & ~JS_GPN_ENUM_ONLY))
- goto exception;
- r = JS_NewArray(ctx);
- if (JS_IsException(r))
- goto exception;
- for(j = i = 0; i < len; i++) {
- JSAtom atom = atoms[i].atom;
- if (flags & JS_GPN_ENUM_ONLY) {
- JSPropertyDescriptor desc;
- int res;
- /* Check if property is still enumerable */
- res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom);
- if (res < 0)
- goto exception;
- if (!res)
- continue;
- js_free_desc(ctx, &desc);
- if (!(desc.flags & JS_PROP_ENUMERABLE))
- continue;
- }
- switch(kind) {
- default:
- case JS_ITERATOR_KIND_KEY:
- val = JS_AtomToValue(ctx, atom);
- if (JS_IsException(val))
- goto exception;
- break;
- case JS_ITERATOR_KIND_VALUE:
- val = JS_GetProperty(ctx, obj, atom);
- if (JS_IsException(val))
- goto exception;
- break;
- case JS_ITERATOR_KIND_KEY_AND_VALUE:
- val = JS_NewArray(ctx);
- if (JS_IsException(val))
- goto exception;
- key = JS_AtomToValue(ctx, atom);
- if (JS_IsException(key))
- goto exception1;
- if (JS_CreateDataPropertyUint32(ctx, val, 0, key, JS_PROP_THROW) < 0)
- goto exception1;
- value = JS_GetProperty(ctx, obj, atom);
- if (JS_IsException(value))
- goto exception1;
- if (JS_CreateDataPropertyUint32(ctx, val, 1, value, JS_PROP_THROW) < 0)
- goto exception1;
- break;
- }
- if (JS_CreateDataPropertyUint32(ctx, r, j++, val, 0) < 0)
- goto exception;
- }
- goto done;
- exception1:
- JS_FreeValue(ctx, val);
- exception:
- JS_FreeValue(ctx, r);
- r = JS_EXCEPTION;
- done:
- JS_FreePropertyEnum(ctx, atoms, len);
- JS_FreeValue(ctx, obj);
- return r;
- }
- static JSValue js_object_getOwnPropertyNames(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- return JS_GetOwnPropertyNames2(ctx, argv[0],
- JS_GPN_STRING_MASK, JS_ITERATOR_KIND_KEY);
- }
- static JSValue js_object_getOwnPropertySymbols(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- return JS_GetOwnPropertyNames2(ctx, argv[0],
- JS_GPN_SYMBOL_MASK, JS_ITERATOR_KIND_KEY);
- }
- static JSValue js_object_keys(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int kind)
- {
- return JS_GetOwnPropertyNames2(ctx, argv[0],
- JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK, kind);
- }
- static JSValue js_object_isExtensible(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int reflect)
- {
- JSValueConst obj;
- int ret;
- obj = argv[0];
- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {
- if (reflect)
- return JS_ThrowTypeErrorNotAnObject(ctx);
- else
- return JS_FALSE;
- }
- ret = JS_IsExtensible(ctx, obj);
- if (ret < 0)
- return JS_EXCEPTION;
- else
- return JS_NewBool(ctx, ret);
- }
- static JSValue js_object_preventExtensions(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int reflect)
- {
- JSValueConst obj;
- int ret;
- obj = argv[0];
- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {
- if (reflect)
- return JS_ThrowTypeErrorNotAnObject(ctx);
- else
- return JS_DupValue(ctx, obj);
- }
- ret = JS_PreventExtensions(ctx, obj);
- if (ret < 0)
- return JS_EXCEPTION;
- if (reflect) {
- return JS_NewBool(ctx, ret);
- } else {
- if (!ret)
- return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false");
- return JS_DupValue(ctx, obj);
- }
- }
- static JSValue js_object_hasOwnProperty(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue obj;
- JSAtom atom;
- JSObject *p;
- BOOL ret;
- atom = JS_ValueToAtom(ctx, argv[0]); /* must be done first */
- if (unlikely(atom == JS_ATOM_NULL))
- return JS_EXCEPTION;
- obj = JS_ToObject(ctx, this_val);
- if (JS_IsException(obj)) {
- JS_FreeAtom(ctx, atom);
- return obj;
- }
- p = JS_VALUE_GET_OBJ(obj);
- ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom);
- JS_FreeAtom(ctx, atom);
- JS_FreeValue(ctx, obj);
- if (ret < 0)
- return JS_EXCEPTION;
- else
- return JS_NewBool(ctx, ret);
- }
- static JSValue js_object_hasOwn(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue obj;
- JSAtom atom;
- JSObject *p;
- BOOL ret;
- obj = JS_ToObject(ctx, argv[0]);
- if (JS_IsException(obj))
- return obj;
- atom = JS_ValueToAtom(ctx, argv[1]);
- if (unlikely(atom == JS_ATOM_NULL)) {
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- p = JS_VALUE_GET_OBJ(obj);
- ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom);
- JS_FreeAtom(ctx, atom);
- JS_FreeValue(ctx, obj);
- if (ret < 0)
- return JS_EXCEPTION;
- else
- return JS_NewBool(ctx, ret);
- }
- static JSValue js_object_valueOf(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- return JS_ToObject(ctx, this_val);
- }
- static JSValue js_object_toString(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue obj, tag;
- int is_array;
- JSAtom atom;
- JSObject *p;
- if (JS_IsNull(this_val)) {
- tag = js_new_string8(ctx, "Null");
- } else if (JS_IsUndefined(this_val)) {
- tag = js_new_string8(ctx, "Undefined");
- } else {
- obj = JS_ToObject(ctx, this_val);
- if (JS_IsException(obj))
- return obj;
- is_array = JS_IsArray(ctx, obj);
- if (is_array < 0) {
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- if (is_array) {
- atom = JS_ATOM_Array;
- } else if (JS_IsFunction(ctx, obj)) {
- atom = JS_ATOM_Function;
- } else {
- p = JS_VALUE_GET_OBJ(obj);
- switch(p->class_id) {
- case JS_CLASS_STRING:
- case JS_CLASS_ARGUMENTS:
- case JS_CLASS_MAPPED_ARGUMENTS:
- case JS_CLASS_ERROR:
- case JS_CLASS_BOOLEAN:
- case JS_CLASS_NUMBER:
- case JS_CLASS_DATE:
- case JS_CLASS_REGEXP:
- atom = ctx->rt->class_array[p->class_id].class_name;
- break;
- default:
- atom = JS_ATOM_Object;
- break;
- }
- }
- tag = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_toStringTag);
- JS_FreeValue(ctx, obj);
- if (JS_IsException(tag))
- return JS_EXCEPTION;
- if (!JS_IsString(tag)) {
- JS_FreeValue(ctx, tag);
- tag = JS_AtomToString(ctx, atom);
- }
- }
- return JS_ConcatString3(ctx, "[object ", tag, "]");
- }
- static JSValue js_object_toLocaleString(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- return JS_Invoke(ctx, this_val, JS_ATOM_toString, 0, NULL);
- }
- static JSValue js_object_assign(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- // Object.assign(obj, source1)
- JSValue obj, s;
- int i;
- s = JS_UNDEFINED;
- obj = JS_ToObject(ctx, argv[0]);
- if (JS_IsException(obj))
- goto exception;
- for (i = 1; i < argc; i++) {
- if (!JS_IsNull(argv[i]) && !JS_IsUndefined(argv[i])) {
- s = JS_ToObject(ctx, argv[i]);
- if (JS_IsException(s))
- goto exception;
- if (JS_CopyDataProperties(ctx, obj, s, JS_UNDEFINED, TRUE))
- goto exception;
- JS_FreeValue(ctx, s);
- }
- }
- return obj;
- exception:
- JS_FreeValue(ctx, obj);
- JS_FreeValue(ctx, s);
- return JS_EXCEPTION;
- }
- static JSValue js_object_seal(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int freeze_flag)
- {
- JSValueConst obj = argv[0];
- JSObject *p;
- JSPropertyEnum *props;
- uint32_t len, i;
- int flags, desc_flags, res;
- if (!JS_IsObject(obj))
- return JS_DupValue(ctx, obj);
- res = JS_PreventExtensions(ctx, obj);
- if (res < 0)
- return JS_EXCEPTION;
- if (!res) {
- return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false");
- }
- p = JS_VALUE_GET_OBJ(obj);
- flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK;
- if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags))
- return JS_EXCEPTION;
- for(i = 0; i < len; i++) {
- JSPropertyDescriptor desc;
- JSAtom prop = props[i].atom;
- desc_flags = JS_PROP_THROW | JS_PROP_HAS_CONFIGURABLE;
- if (freeze_flag) {
- res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
- if (res < 0)
- goto exception;
- if (res) {
- if (desc.flags & JS_PROP_WRITABLE)
- desc_flags |= JS_PROP_HAS_WRITABLE;
- js_free_desc(ctx, &desc);
- }
- }
- if (JS_DefineProperty(ctx, obj, prop, JS_UNDEFINED,
- JS_UNDEFINED, JS_UNDEFINED, desc_flags) < 0)
- goto exception;
- }
- JS_FreePropertyEnum(ctx, props, len);
- return JS_DupValue(ctx, obj);
- exception:
- JS_FreePropertyEnum(ctx, props, len);
- return JS_EXCEPTION;
- }
- static JSValue js_object_isSealed(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int is_frozen)
- {
- JSValueConst obj = argv[0];
- JSObject *p;
- JSPropertyEnum *props;
- uint32_t len, i;
- int flags, res;
- if (!JS_IsObject(obj))
- return JS_TRUE;
- p = JS_VALUE_GET_OBJ(obj);
- flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK;
- if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags))
- return JS_EXCEPTION;
- for(i = 0; i < len; i++) {
- JSPropertyDescriptor desc;
- JSAtom prop = props[i].atom;
- res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
- if (res < 0)
- goto exception;
- if (res) {
- js_free_desc(ctx, &desc);
- if ((desc.flags & JS_PROP_CONFIGURABLE)
- || (is_frozen && (desc.flags & JS_PROP_WRITABLE))) {
- res = FALSE;
- goto done;
- }
- }
- }
- res = JS_IsExtensible(ctx, obj);
- if (res < 0)
- return JS_EXCEPTION;
- res ^= 1;
- done:
- JS_FreePropertyEnum(ctx, props, len);
- return JS_NewBool(ctx, res);
- exception:
- JS_FreePropertyEnum(ctx, props, len);
- return JS_EXCEPTION;
- }
- static JSValue js_object_fromEntries(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue obj, iter, next_method = JS_UNDEFINED;
- JSValueConst iterable;
- BOOL done;
- /* RequireObjectCoercible() not necessary because it is tested in
- JS_GetIterator() by JS_GetProperty() */
- iterable = argv[0];
- obj = JS_NewObject(ctx);
- if (JS_IsException(obj))
- return obj;
- iter = JS_GetIterator(ctx, iterable, FALSE);
- if (JS_IsException(iter))
- goto fail;
- next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
- if (JS_IsException(next_method))
- goto fail;
- for(;;) {
- JSValue key, value, item;
- item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
- if (JS_IsException(item))
- goto fail;
- if (done)
- break;
- key = JS_UNDEFINED;
- value = JS_UNDEFINED;
- if (!JS_IsObject(item)) {
- JS_ThrowTypeErrorNotAnObject(ctx);
- goto fail1;
- }
- key = JS_GetPropertyUint32(ctx, item, 0);
- if (JS_IsException(key))
- goto fail1;
- value = JS_GetPropertyUint32(ctx, item, 1);
- if (JS_IsException(value)) {
- JS_FreeValue(ctx, key);
- goto fail1;
- }
- if (JS_DefinePropertyValueValue(ctx, obj, key, value,
- JS_PROP_C_W_E | JS_PROP_THROW) < 0) {
- fail1:
- JS_FreeValue(ctx, item);
- goto fail;
- }
- JS_FreeValue(ctx, item);
- }
- JS_FreeValue(ctx, next_method);
- JS_FreeValue(ctx, iter);
- return obj;
- fail:
- if (JS_IsObject(iter)) {
- /* close the iterator object, preserving pending exception */
- JS_IteratorClose(ctx, iter, TRUE);
- }
- JS_FreeValue(ctx, next_method);
- JS_FreeValue(ctx, iter);
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- static JSValue js_object_is(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- return JS_NewBool(ctx, js_same_value(ctx, argv[0], argv[1]));
- }
- static JSValue JS_SpeciesConstructor(JSContext *ctx, JSValueConst obj,
- JSValueConst defaultConstructor)
- {
- JSValue ctor, species;
- if (!JS_IsObject(obj))
- return JS_ThrowTypeErrorNotAnObject(ctx);
- ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor);
- if (JS_IsException(ctor))
- return ctor;
- if (JS_IsUndefined(ctor))
- return JS_DupValue(ctx, defaultConstructor);
- if (!JS_IsObject(ctor)) {
- JS_FreeValue(ctx, ctor);
- return JS_ThrowTypeErrorNotAnObject(ctx);
- }
- species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species);
- JS_FreeValue(ctx, ctor);
- if (JS_IsException(species))
- return species;
- if (JS_IsUndefined(species) || JS_IsNull(species))
- return JS_DupValue(ctx, defaultConstructor);
- if (!JS_IsConstructor(ctx, species)) {
- JS_ThrowTypeErrorNotAConstructor(ctx, species);
- JS_FreeValue(ctx, species);
- return JS_EXCEPTION;
- }
- return species;
- }
- static JSValue js_object_get___proto__(JSContext *ctx, JSValueConst this_val)
- {
- JSValue val, ret;
- val = JS_ToObject(ctx, this_val);
- if (JS_IsException(val))
- return val;
- ret = JS_GetPrototype(ctx, val);
- JS_FreeValue(ctx, val);
- return ret;
- }
- static JSValue js_object_set___proto__(JSContext *ctx, JSValueConst this_val,
- JSValueConst proto)
- {
- if (JS_IsUndefined(this_val) || JS_IsNull(this_val))
- return JS_ThrowTypeErrorNotAnObject(ctx);
- if (!JS_IsObject(proto) && !JS_IsNull(proto))
- return JS_UNDEFINED;
- if (JS_SetPrototypeInternal(ctx, this_val, proto, TRUE) < 0)
- return JS_EXCEPTION;
- else
- return JS_UNDEFINED;
- }
- static JSValue js_object_isPrototypeOf(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue obj, v1;
- JSValueConst v;
- int res;
- v = argv[0];
- if (!JS_IsObject(v))
- return JS_FALSE;
- obj = JS_ToObject(ctx, this_val);
- if (JS_IsException(obj))
- return JS_EXCEPTION;
- v1 = JS_DupValue(ctx, v);
- for(;;) {
- v1 = JS_GetPrototypeFree(ctx, v1);
- if (JS_IsException(v1))
- goto exception;
- if (JS_IsNull(v1)) {
- res = FALSE;
- break;
- }
- if (JS_VALUE_GET_OBJ(obj) == JS_VALUE_GET_OBJ(v1)) {
- res = TRUE;
- break;
- }
- /* avoid infinite loop (possible with proxies) */
- if (js_poll_interrupts(ctx))
- goto exception;
- }
- JS_FreeValue(ctx, v1);
- JS_FreeValue(ctx, obj);
- return JS_NewBool(ctx, res);
- exception:
- JS_FreeValue(ctx, v1);
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- static JSValue js_object_propertyIsEnumerable(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue obj = JS_UNDEFINED, res = JS_EXCEPTION;
- JSAtom prop;
- JSPropertyDescriptor desc;
- int has_prop;
- prop = JS_ValueToAtom(ctx, argv[0]);
- if (unlikely(prop == JS_ATOM_NULL))
- goto exception;
- obj = JS_ToObject(ctx, this_val);
- if (JS_IsException(obj))
- goto exception;
- has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop);
- if (has_prop < 0)
- goto exception;
- if (has_prop) {
- res = JS_NewBool(ctx, desc.flags & JS_PROP_ENUMERABLE);
- js_free_desc(ctx, &desc);
- } else {
- res = JS_FALSE;
- }
- exception:
- JS_FreeAtom(ctx, prop);
- JS_FreeValue(ctx, obj);
- return res;
- }
- static JSValue js_object___lookupGetter__(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int setter)
- {
- JSValue obj, res = JS_EXCEPTION;
- JSAtom prop = JS_ATOM_NULL;
- JSPropertyDescriptor desc;
- int has_prop;
- obj = JS_ToObject(ctx, this_val);
- if (JS_IsException(obj))
- goto exception;
- prop = JS_ValueToAtom(ctx, argv[0]);
- if (unlikely(prop == JS_ATOM_NULL))
- goto exception;
- for (;;) {
- has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop);
- if (has_prop < 0)
- goto exception;
- if (has_prop) {
- if (desc.flags & JS_PROP_GETSET)
- res = JS_DupValue(ctx, setter ? desc.setter : desc.getter);
- else
- res = JS_UNDEFINED;
- js_free_desc(ctx, &desc);
- break;
- }
- obj = JS_GetPrototypeFree(ctx, obj);
- if (JS_IsException(obj))
- goto exception;
- if (JS_IsNull(obj)) {
- res = JS_UNDEFINED;
- break;
- }
- /* avoid infinite loop (possible with proxies) */
- if (js_poll_interrupts(ctx))
- goto exception;
- }
- exception:
- JS_FreeAtom(ctx, prop);
- JS_FreeValue(ctx, obj);
- return res;
- }
- static const JSCFunctionListEntry js_object_funcs[] = {
- JS_CFUNC_DEF("create", 2, js_object_create ),
- JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 0 ),
- JS_CFUNC_DEF("setPrototypeOf", 2, js_object_setPrototypeOf ),
- JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 0 ),
- JS_CFUNC_DEF("defineProperties", 2, js_object_defineProperties ),
- JS_CFUNC_DEF("getOwnPropertyNames", 1, js_object_getOwnPropertyNames ),
- JS_CFUNC_DEF("getOwnPropertySymbols", 1, js_object_getOwnPropertySymbols ),
- JS_CFUNC_MAGIC_DEF("groupBy", 2, js_object_groupBy, 0 ),
- JS_CFUNC_MAGIC_DEF("keys", 1, js_object_keys, JS_ITERATOR_KIND_KEY ),
- JS_CFUNC_MAGIC_DEF("values", 1, js_object_keys, JS_ITERATOR_KIND_VALUE ),
- JS_CFUNC_MAGIC_DEF("entries", 1, js_object_keys, JS_ITERATOR_KIND_KEY_AND_VALUE ),
- JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 0 ),
- JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 0 ),
- JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 0 ),
- JS_CFUNC_DEF("getOwnPropertyDescriptors", 1, js_object_getOwnPropertyDescriptors ),
- JS_CFUNC_DEF("is", 2, js_object_is ),
- JS_CFUNC_DEF("assign", 2, js_object_assign ),
- JS_CFUNC_MAGIC_DEF("seal", 1, js_object_seal, 0 ),
- JS_CFUNC_MAGIC_DEF("freeze", 1, js_object_seal, 1 ),
- JS_CFUNC_MAGIC_DEF("isSealed", 1, js_object_isSealed, 0 ),
- JS_CFUNC_MAGIC_DEF("isFrozen", 1, js_object_isSealed, 1 ),
- JS_CFUNC_DEF("fromEntries", 1, js_object_fromEntries ),
- JS_CFUNC_DEF("hasOwn", 2, js_object_hasOwn ),
- };
- static const JSCFunctionListEntry js_object_proto_funcs[] = {
- JS_CFUNC_DEF("toString", 0, js_object_toString ),
- JS_CFUNC_DEF("toLocaleString", 0, js_object_toLocaleString ),
- JS_CFUNC_DEF("valueOf", 0, js_object_valueOf ),
- JS_CFUNC_DEF("hasOwnProperty", 1, js_object_hasOwnProperty ),
- JS_CFUNC_DEF("isPrototypeOf", 1, js_object_isPrototypeOf ),
- JS_CFUNC_DEF("propertyIsEnumerable", 1, js_object_propertyIsEnumerable ),
- JS_CGETSET_DEF("__proto__", js_object_get___proto__, js_object_set___proto__ ),
- JS_CFUNC_MAGIC_DEF("__defineGetter__", 2, js_object___defineGetter__, 0 ),
- JS_CFUNC_MAGIC_DEF("__defineSetter__", 2, js_object___defineGetter__, 1 ),
- JS_CFUNC_MAGIC_DEF("__lookupGetter__", 1, js_object___lookupGetter__, 0 ),
- JS_CFUNC_MAGIC_DEF("__lookupSetter__", 1, js_object___lookupGetter__, 1 ),
- };
- /* Function class */
- static JSValue js_function_proto(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- return JS_UNDEFINED;
- }
- /* XXX: add a specific eval mode so that Function("}), ({") is rejected */
- static JSValue js_function_constructor(JSContext *ctx, JSValueConst new_target,
- int argc, JSValueConst *argv, int magic)
- {
- JSFunctionKindEnum func_kind = magic;
- int i, n, ret;
- JSValue s, proto, obj = JS_UNDEFINED;
- StringBuffer b_s, *b = &b_s;
- string_buffer_init(ctx, b, 0);
- string_buffer_putc8(b, '(');
- if (func_kind == JS_FUNC_ASYNC || func_kind == JS_FUNC_ASYNC_GENERATOR) {
- string_buffer_puts8(b, "async ");
- }
- string_buffer_puts8(b, "function");
- if (func_kind == JS_FUNC_GENERATOR || func_kind == JS_FUNC_ASYNC_GENERATOR) {
- string_buffer_putc8(b, '*');
- }
- string_buffer_puts8(b, " anonymous(");
- n = argc - 1;
- for(i = 0; i < n; i++) {
- if (i != 0) {
- string_buffer_putc8(b, ',');
- }
- if (string_buffer_concat_value(b, argv[i]))
- goto fail;
- }
- string_buffer_puts8(b, "\n) {\n");
- if (n >= 0) {
- if (string_buffer_concat_value(b, argv[n]))
- goto fail;
- }
- string_buffer_puts8(b, "\n})");
- s = string_buffer_end(b);
- if (JS_IsException(s))
- goto fail1;
- obj = JS_EvalObject(ctx, ctx->global_obj, s, JS_EVAL_TYPE_INDIRECT, -1);
- JS_FreeValue(ctx, s);
- if (JS_IsException(obj))
- goto fail1;
- if (!JS_IsUndefined(new_target)) {
- /* set the prototype */
- proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype);
- if (JS_IsException(proto))
- goto fail1;
- if (!JS_IsObject(proto)) {
- JSContext *realm;
- JS_FreeValue(ctx, proto);
- realm = JS_GetFunctionRealm(ctx, new_target);
- if (!realm)
- goto fail1;
- proto = JS_DupValue(ctx, realm->class_proto[func_kind_to_class_id[func_kind]]);
- }
- ret = JS_SetPrototypeInternal(ctx, obj, proto, TRUE);
- JS_FreeValue(ctx, proto);
- if (ret < 0)
- goto fail1;
- }
- return obj;
- fail:
- string_buffer_free(b);
- fail1:
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- static __exception int js_get_length32(JSContext *ctx, uint32_t *pres,
- JSValueConst obj)
- {
- JSValue len_val;
- len_val = JS_GetProperty(ctx, obj, JS_ATOM_length);
- if (JS_IsException(len_val)) {
- *pres = 0;
- return -1;
- }
- return JS_ToUint32Free(ctx, pres, len_val);
- }
- static __exception int js_get_length64(JSContext *ctx, int64_t *pres,
- JSValueConst obj)
- {
- JSValue len_val;
- len_val = JS_GetProperty(ctx, obj, JS_ATOM_length);
- if (JS_IsException(len_val)) {
- *pres = 0;
- return -1;
- }
- return JS_ToLengthFree(ctx, pres, len_val);
- }
- static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len)
- {
- uint32_t i;
- for(i = 0; i < len; i++) {
- JS_FreeValue(ctx, tab[i]);
- }
- js_free(ctx, tab);
- }
- /* XXX: should use ValueArray */
- static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen,
- JSValueConst array_arg)
- {
- uint32_t len, i;
- int64_t len64;
- JSValue *tab, ret;
- JSObject *p;
- if (JS_VALUE_GET_TAG(array_arg) != JS_TAG_OBJECT) {
- JS_ThrowTypeError(ctx, "not a object");
- return NULL;
- }
- if (js_get_length64(ctx, &len64, array_arg))
- return NULL;
- if (len64 > JS_MAX_LOCAL_VARS) {
- // XXX: check for stack overflow?
- JS_ThrowRangeError(ctx, "too many arguments in function call (only %d allowed)",
- JS_MAX_LOCAL_VARS);
- return NULL;
- }
- len = len64;
- /* avoid allocating 0 bytes */
- tab = js_mallocz(ctx, sizeof(tab[0]) * max_uint32(1, len));
- if (!tab)
- return NULL;
- p = JS_VALUE_GET_OBJ(array_arg);
- if ((p->class_id == JS_CLASS_ARRAY || p->class_id == JS_CLASS_ARGUMENTS || p->class_id == JS_CLASS_MAPPED_ARGUMENTS) &&
- p->fast_array &&
- len == p->u.array.count) {
- if (p->class_id == JS_CLASS_MAPPED_ARGUMENTS) {
- for(i = 0; i < len; i++) {
- tab[i] = JS_DupValue(ctx, *p->u.array.u.var_refs[i]->pvalue);
- }
- } else {
- for(i = 0; i < len; i++) {
- tab[i] = JS_DupValue(ctx, p->u.array.u.values[i]);
- }
- }
- } else {
- for(i = 0; i < len; i++) {
- ret = JS_GetPropertyUint32(ctx, array_arg, i);
- if (JS_IsException(ret)) {
- free_arg_list(ctx, tab, i);
- return NULL;
- }
- tab[i] = ret;
- }
- }
- *plen = len;
- return tab;
- }
- /* magic value: 0 = normal apply, 1 = apply for constructor, 2 =
- Reflect.apply */
- static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
- {
- JSValueConst this_arg, array_arg;
- uint32_t len;
- JSValue *tab, ret;
- if (check_function(ctx, this_val))
- return JS_EXCEPTION;
- this_arg = argv[0];
- array_arg = argv[1];
- if ((JS_VALUE_GET_TAG(array_arg) == JS_TAG_UNDEFINED ||
- JS_VALUE_GET_TAG(array_arg) == JS_TAG_NULL) && magic != 2) {
- return JS_Call(ctx, this_val, this_arg, 0, NULL);
- }
- tab = build_arg_list(ctx, &len, array_arg);
- if (!tab)
- return JS_EXCEPTION;
- if (magic & 1) {
- ret = JS_CallConstructor2(ctx, this_val, this_arg, len, (JSValueConst *)tab);
- } else {
- ret = JS_Call(ctx, this_val, this_arg, len, (JSValueConst *)tab);
- }
- free_arg_list(ctx, tab, len);
- return ret;
- }
- static JSValue js_function_call(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- if (argc <= 0) {
- return JS_Call(ctx, this_val, JS_UNDEFINED, 0, NULL);
- } else {
- return JS_Call(ctx, this_val, argv[0], argc - 1, argv + 1);
- }
- }
- static JSValue js_function_bind(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSBoundFunction *bf;
- JSValue func_obj, name1, len_val;
- JSObject *p;
- int arg_count, i, ret;
- if (check_function(ctx, this_val))
- return JS_EXCEPTION;
- func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,
- JS_CLASS_BOUND_FUNCTION);
- if (JS_IsException(func_obj))
- return JS_EXCEPTION;
- p = JS_VALUE_GET_OBJ(func_obj);
- p->is_constructor = JS_IsConstructor(ctx, this_val);
- arg_count = max_int(0, argc - 1);
- bf = js_malloc(ctx, sizeof(*bf) + arg_count * sizeof(JSValue));
- if (!bf)
- goto exception;
- bf->func_obj = JS_DupValue(ctx, this_val);
- bf->this_val = JS_DupValue(ctx, argv[0]);
- bf->argc = arg_count;
- for(i = 0; i < arg_count; i++) {
- bf->argv[i] = JS_DupValue(ctx, argv[i + 1]);
- }
- p->u.bound_function = bf;
- /* XXX: the spec could be simpler by only using GetOwnProperty */
- ret = JS_GetOwnProperty(ctx, NULL, this_val, JS_ATOM_length);
- if (ret < 0)
- goto exception;
- if (!ret) {
- len_val = JS_NewInt32(ctx, 0);
- } else {
- len_val = JS_GetProperty(ctx, this_val, JS_ATOM_length);
- if (JS_IsException(len_val))
- goto exception;
- if (JS_VALUE_GET_TAG(len_val) == JS_TAG_INT) {
- /* most common case */
- int len1 = JS_VALUE_GET_INT(len_val);
- if (len1 <= arg_count)
- len1 = 0;
- else
- len1 -= arg_count;
- len_val = JS_NewInt32(ctx, len1);
- } else if (JS_VALUE_GET_NORM_TAG(len_val) == JS_TAG_FLOAT64) {
- double d = JS_VALUE_GET_FLOAT64(len_val);
- if (isnan(d)) {
- d = 0.0;
- } else {
- d = trunc(d);
- if (d <= (double)arg_count)
- d = 0.0;
- else
- d -= (double)arg_count; /* also converts -0 to +0 */
- }
- len_val = JS_NewFloat64(ctx, d);
- } else {
- JS_FreeValue(ctx, len_val);
- len_val = JS_NewInt32(ctx, 0);
- }
- }
- JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length,
- len_val, JS_PROP_CONFIGURABLE);
- name1 = JS_GetProperty(ctx, this_val, JS_ATOM_name);
- if (JS_IsException(name1))
- goto exception;
- if (!JS_IsString(name1)) {
- JS_FreeValue(ctx, name1);
- name1 = JS_AtomToString(ctx, JS_ATOM_empty_string);
- }
- name1 = JS_ConcatString3(ctx, "bound ", name1, "");
- if (JS_IsException(name1))
- goto exception;
- JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name1,
- JS_PROP_CONFIGURABLE);
- return func_obj;
- exception:
- JS_FreeValue(ctx, func_obj);
- return JS_EXCEPTION;
- }
- static JSValue js_function_toString(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSObject *p;
- JSFunctionKindEnum func_kind = JS_FUNC_NORMAL;
- if (check_function(ctx, this_val))
- return JS_EXCEPTION;
- p = JS_VALUE_GET_OBJ(this_val);
- if (js_class_has_bytecode(p->class_id)) {
- JSFunctionBytecode *b = p->u.func.function_bytecode;
- if (b->has_debug && b->debug.source) {
- return JS_NewStringLen(ctx, b->debug.source, b->debug.source_len);
- }
- func_kind = b->func_kind;
- }
- {
- JSValue name;
- const char *pref, *suff;
- switch(func_kind) {
- default:
- case JS_FUNC_NORMAL:
- pref = "function ";
- break;
- case JS_FUNC_GENERATOR:
- pref = "function *";
- break;
- case JS_FUNC_ASYNC:
- pref = "async function ";
- break;
- case JS_FUNC_ASYNC_GENERATOR:
- pref = "async function *";
- break;
- }
- suff = "() {\n [native code]\n}";
- name = JS_GetProperty(ctx, this_val, JS_ATOM_name);
- if (JS_IsUndefined(name))
- name = JS_AtomToString(ctx, JS_ATOM_empty_string);
- return JS_ConcatString3(ctx, pref, name, suff);
- }
- }
- static JSValue js_function_hasInstance(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- int ret;
- ret = JS_OrdinaryIsInstanceOf(ctx, argv[0], this_val);
- if (ret < 0)
- return JS_EXCEPTION;
- else
- return JS_NewBool(ctx, ret);
- }
- static const JSCFunctionListEntry js_function_proto_funcs[] = {
- JS_CFUNC_DEF("call", 1, js_function_call ),
- JS_CFUNC_MAGIC_DEF("apply", 2, js_function_apply, 0 ),
- JS_CFUNC_DEF("bind", 1, js_function_bind ),
- JS_CFUNC_DEF("toString", 0, js_function_toString ),
- JS_CFUNC_DEF("[Symbol.hasInstance]", 1, js_function_hasInstance ),
- JS_CGETSET_DEF("fileName", js_function_proto_fileName, NULL ),
- JS_CGETSET_MAGIC_DEF("lineNumber", js_function_proto_lineNumber, NULL, 0 ),
- JS_CGETSET_MAGIC_DEF("columnNumber", js_function_proto_lineNumber, NULL, 1 ),
- };
- /* Error class */
- static JSValue iterator_to_array(JSContext *ctx, JSValueConst items)
- {
- JSValue iter, next_method = JS_UNDEFINED;
- JSValue v, r = JS_UNDEFINED;
- int64_t k;
- BOOL done;
- iter = JS_GetIterator(ctx, items, FALSE);
- if (JS_IsException(iter))
- goto exception;
- next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
- if (JS_IsException(next_method))
- goto exception;
- r = JS_NewArray(ctx);
- if (JS_IsException(r))
- goto exception;
- for (k = 0;; k++) {
- v = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
- if (JS_IsException(v))
- goto exception_close;
- if (done)
- break;
- if (JS_DefinePropertyValueInt64(ctx, r, k, v,
- JS_PROP_C_W_E | JS_PROP_THROW) < 0)
- goto exception_close;
- }
- done:
- JS_FreeValue(ctx, next_method);
- JS_FreeValue(ctx, iter);
- return r;
- exception_close:
- JS_IteratorClose(ctx, iter, TRUE);
- exception:
- JS_FreeValue(ctx, r);
- r = JS_EXCEPTION;
- goto done;
- }
- static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target,
- int argc, JSValueConst *argv, int magic)
- {
- JSValue obj, msg, proto;
- JSValueConst message, options;
- int arg_index;
- if (JS_IsUndefined(new_target))
- new_target = JS_GetActiveFunction(ctx);
- proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype);
- if (JS_IsException(proto))
- return proto;
- if (!JS_IsObject(proto)) {
- JSContext *realm;
- JSValueConst proto1;
- JS_FreeValue(ctx, proto);
- realm = JS_GetFunctionRealm(ctx, new_target);
- if (!realm)
- return JS_EXCEPTION;
- if (magic < 0) {
- proto1 = realm->class_proto[JS_CLASS_ERROR];
- } else {
- proto1 = realm->native_error_proto[magic];
- }
- proto = JS_DupValue(ctx, proto1);
- }
- obj = JS_NewObjectProtoClass(ctx, proto, JS_CLASS_ERROR);
- JS_FreeValue(ctx, proto);
- if (JS_IsException(obj))
- return obj;
- arg_index = (magic == JS_AGGREGATE_ERROR);
- message = argv[arg_index++];
- if (!JS_IsUndefined(message)) {
- msg = JS_ToString(ctx, message);
- if (unlikely(JS_IsException(msg)))
- goto exception;
- JS_DefinePropertyValue(ctx, obj, JS_ATOM_message, msg,
- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
- }
- if (arg_index < argc) {
- options = argv[arg_index];
- if (JS_IsObject(options)) {
- int present = JS_HasProperty(ctx, options, JS_ATOM_cause);
- if (present < 0)
- goto exception;
- if (present) {
- JSValue cause = JS_GetProperty(ctx, options, JS_ATOM_cause);
- if (JS_IsException(cause))
- goto exception;
- JS_DefinePropertyValue(ctx, obj, JS_ATOM_cause, cause,
- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
- }
- }
- }
- if (magic == JS_AGGREGATE_ERROR) {
- JSValue error_list = iterator_to_array(ctx, argv[0]);
- if (JS_IsException(error_list))
- goto exception;
- JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, error_list,
- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
- }
- /* skip the Error() function in the backtrace */
- build_backtrace(ctx, obj, NULL, 0, 0, JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL);
- return obj;
- exception:
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- static JSValue js_error_toString(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue name, msg;
- if (!JS_IsObject(this_val))
- return JS_ThrowTypeErrorNotAnObject(ctx);
- name = JS_GetProperty(ctx, this_val, JS_ATOM_name);
- if (JS_IsUndefined(name))
- name = JS_AtomToString(ctx, JS_ATOM_Error);
- else
- name = JS_ToStringFree(ctx, name);
- if (JS_IsException(name))
- return JS_EXCEPTION;
- msg = JS_GetProperty(ctx, this_val, JS_ATOM_message);
- if (JS_IsUndefined(msg))
- msg = JS_AtomToString(ctx, JS_ATOM_empty_string);
- else
- msg = JS_ToStringFree(ctx, msg);
- if (JS_IsException(msg)) {
- JS_FreeValue(ctx, name);
- return JS_EXCEPTION;
- }
- if (!JS_IsEmptyString(name) && !JS_IsEmptyString(msg))
- name = JS_ConcatString3(ctx, "", name, ": ");
- return JS_ConcatString(ctx, name, msg);
- }
- static const JSCFunctionListEntry js_error_proto_funcs[] = {
- JS_CFUNC_DEF("toString", 0, js_error_toString ),
- JS_PROP_STRING_DEF("name", "Error", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
- JS_PROP_STRING_DEF("message", "", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
- };
- /* 2 entries for each native error class */
- /* Note: we use an atom to avoid the autoinit definition which does
- not work in get_prop_string() */
- static const JSCFunctionListEntry js_native_error_proto_funcs[] = {
- #define DEF(name) \
- JS_PROP_ATOM_DEF("name", name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),\
- JS_PROP_STRING_DEF("message", "", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
-
- DEF(JS_ATOM_EvalError)
- DEF(JS_ATOM_RangeError)
- DEF(JS_ATOM_ReferenceError)
- DEF(JS_ATOM_SyntaxError)
- DEF(JS_ATOM_TypeError)
- DEF(JS_ATOM_URIError)
- DEF(JS_ATOM_InternalError)
- DEF(JS_ATOM_AggregateError)
- #undef DEF
- };
- static JSValue js_error_isError(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- return JS_NewBool(ctx, JS_IsError(ctx, argv[0]));
- }
- static const JSCFunctionListEntry js_error_funcs[] = {
- JS_CFUNC_DEF("isError", 1, js_error_isError),
- };
- /* AggregateError */
- /* used by C code. */
- static JSValue js_aggregate_error_constructor(JSContext *ctx,
- JSValueConst errors)
- {
- JSValue obj;
- obj = JS_NewObjectProtoClass(ctx,
- ctx->native_error_proto[JS_AGGREGATE_ERROR],
- JS_CLASS_ERROR);
- if (JS_IsException(obj))
- return obj;
- JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, JS_DupValue(ctx, errors),
- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
- return obj;
- }
- /* Array */
- static int JS_CopySubArray(JSContext *ctx,
- JSValueConst obj, int64_t to_pos,
- int64_t from_pos, int64_t count, int dir)
- {
- JSObject *p;
- int64_t i, from, to, len;
- JSValue val;
- int fromPresent;
- p = NULL;
- if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
- p = JS_VALUE_GET_OBJ(obj);
- if (p->class_id != JS_CLASS_ARRAY || !p->fast_array) {
- p = NULL;
- }
- }
- for (i = 0; i < count; ) {
- if (dir < 0) {
- from = from_pos + count - i - 1;
- to = to_pos + count - i - 1;
- } else {
- from = from_pos + i;
- to = to_pos + i;
- }
- if (p && p->fast_array &&
- from >= 0 && from < (len = p->u.array.count) &&
- to >= 0 && to < len) {
- int64_t l, j;
- /* Fast path for fast arrays. Since we don't look at the
- prototype chain, we can optimize only the cases where
- all the elements are present in the array. */
- l = count - i;
- if (dir < 0) {
- l = min_int64(l, from + 1);
- l = min_int64(l, to + 1);
- for(j = 0; j < l; j++) {
- set_value(ctx, &p->u.array.u.values[to - j],
- JS_DupValue(ctx, p->u.array.u.values[from - j]));
- }
- } else {
- l = min_int64(l, len - from);
- l = min_int64(l, len - to);
- for(j = 0; j < l; j++) {
- set_value(ctx, &p->u.array.u.values[to + j],
- JS_DupValue(ctx, p->u.array.u.values[from + j]));
- }
- }
- i += l;
- } else {
- fromPresent = JS_TryGetPropertyInt64(ctx, obj, from, &val);
- if (fromPresent < 0)
- goto exception;
- if (fromPresent) {
- if (JS_SetPropertyInt64(ctx, obj, to, val) < 0)
- goto exception;
- } else {
- if (JS_DeletePropertyInt64(ctx, obj, to, JS_PROP_THROW) < 0)
- goto exception;
- }
- i++;
- }
- }
- return 0;
- exception:
- return -1;
- }
- static JSValue js_array_constructor(JSContext *ctx, JSValueConst new_target,
- int argc, JSValueConst *argv)
- {
- JSValue obj;
- int i;
- obj = js_create_from_ctor(ctx, new_target, JS_CLASS_ARRAY);
- if (JS_IsException(obj))
- return obj;
- if (argc == 1 && JS_IsNumber(argv[0])) {
- uint32_t len;
- if (JS_ToArrayLengthFree(ctx, &len, JS_DupValue(ctx, argv[0]), TRUE))
- goto fail;
- if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, len)) < 0)
- goto fail;
- } else {
- for(i = 0; i < argc; i++) {
- if (JS_SetPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i])) < 0)
- goto fail;
- }
- }
- return obj;
- fail:
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- static JSValue js_array_from(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- // from(items, mapfn = void 0, this_arg = void 0)
- JSValueConst items = argv[0], mapfn, this_arg;
- JSValueConst args[2];
- JSValue iter, r, v, v2, arrayLike, next_method, enum_obj;
- int64_t k, len;
- int done, mapping;
- mapping = FALSE;
- mapfn = JS_UNDEFINED;
- this_arg = JS_UNDEFINED;
- r = JS_UNDEFINED;
- arrayLike = JS_UNDEFINED;
- iter = JS_UNDEFINED;
- enum_obj = JS_UNDEFINED;
- next_method = JS_UNDEFINED;
- if (argc > 1) {
- mapfn = argv[1];
- if (!JS_IsUndefined(mapfn)) {
- if (check_function(ctx, mapfn))
- goto exception;
- mapping = 1;
- if (argc > 2)
- this_arg = argv[2];
- }
- }
- iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator);
- if (JS_IsException(iter))
- goto exception;
- if (!JS_IsUndefined(iter) && !JS_IsNull(iter)) {
- if (!JS_IsFunction(ctx, iter)) {
- JS_ThrowTypeError(ctx, "value is not iterable");
- goto exception;
- }
- if (JS_IsConstructor(ctx, this_val))
- r = JS_CallConstructor(ctx, this_val, 0, NULL);
- else
- r = JS_NewArray(ctx);
- if (JS_IsException(r))
- goto exception;
- enum_obj = JS_GetIterator2(ctx, items, iter);
- if (JS_IsException(enum_obj))
- goto exception;
- next_method = JS_GetProperty(ctx, enum_obj, JS_ATOM_next);
- if (JS_IsException(next_method))
- goto exception;
- for (k = 0;; k++) {
- v = JS_IteratorNext(ctx, enum_obj, next_method, 0, NULL, &done);
- if (JS_IsException(v))
- goto exception;
- if (done)
- break;
- if (mapping) {
- args[0] = v;
- args[1] = JS_NewInt32(ctx, k);
- v2 = JS_Call(ctx, mapfn, this_arg, 2, args);
- JS_FreeValue(ctx, v);
- v = v2;
- if (JS_IsException(v))
- goto exception_close;
- }
- if (JS_DefinePropertyValueInt64(ctx, r, k, v,
- JS_PROP_C_W_E | JS_PROP_THROW) < 0)
- goto exception_close;
- }
- } else {
- arrayLike = JS_ToObject(ctx, items);
- if (JS_IsException(arrayLike))
- goto exception;
- if (js_get_length64(ctx, &len, arrayLike) < 0)
- goto exception;
- v = JS_NewInt64(ctx, len);
- args[0] = v;
- if (JS_IsConstructor(ctx, this_val)) {
- r = JS_CallConstructor(ctx, this_val, 1, args);
- } else {
- r = js_array_constructor(ctx, JS_UNDEFINED, 1, args);
- }
- JS_FreeValue(ctx, v);
- if (JS_IsException(r))
- goto exception;
- for(k = 0; k < len; k++) {
- v = JS_GetPropertyInt64(ctx, arrayLike, k);
- if (JS_IsException(v))
- goto exception;
- if (mapping) {
- args[0] = v;
- args[1] = JS_NewInt32(ctx, k);
- v2 = JS_Call(ctx, mapfn, this_arg, 2, args);
- JS_FreeValue(ctx, v);
- v = v2;
- if (JS_IsException(v))
- goto exception;
- }
- if (JS_DefinePropertyValueInt64(ctx, r, k, v,
- JS_PROP_C_W_E | JS_PROP_THROW) < 0)
- goto exception;
- }
- }
- if (JS_SetProperty(ctx, r, JS_ATOM_length, JS_NewUint32(ctx, k)) < 0)
- goto exception;
- goto done;
- exception_close:
- JS_IteratorClose(ctx, enum_obj, TRUE);
- exception:
- JS_FreeValue(ctx, r);
- r = JS_EXCEPTION;
- done:
- JS_FreeValue(ctx, arrayLike);
- JS_FreeValue(ctx, iter);
- JS_FreeValue(ctx, enum_obj);
- JS_FreeValue(ctx, next_method);
- return r;
- }
- static JSValue js_array_of(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue obj, args[1];
- int i;
- if (JS_IsConstructor(ctx, this_val)) {
- args[0] = JS_NewInt32(ctx, argc);
- obj = JS_CallConstructor(ctx, this_val, 1, (JSValueConst *)args);
- } else {
- obj = JS_NewArray(ctx);
- }
- if (JS_IsException(obj))
- return JS_EXCEPTION;
- for(i = 0; i < argc; i++) {
- if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i]),
- JS_PROP_THROW) < 0) {
- goto fail;
- }
- }
- if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, argc)) < 0) {
- fail:
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- return obj;
- }
- static JSValue js_array_isArray(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- int ret;
- ret = JS_IsArray(ctx, argv[0]);
- if (ret < 0)
- return JS_EXCEPTION;
- else
- return JS_NewBool(ctx, ret);
- }
- static JSValue js_get_this(JSContext *ctx,
- JSValueConst this_val)
- {
- return JS_DupValue(ctx, this_val);
- }
- /* XXX: optimize */
- static JSValue JS_ArraySpeciesGetCtor(JSContext *ctx, JSValueConst obj)
- {
- JSValue ctor, species;
- int res;
- JSContext *realm;
- res = JS_IsArray(ctx, obj);
- if (res < 0)
- return JS_EXCEPTION;
- if (!res)
- return JS_UNDEFINED;
- ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor);
- if (JS_IsException(ctor))
- return ctor;
- if (JS_IsConstructor(ctx, ctor)) {
- /* legacy web compatibility */
- realm = JS_GetFunctionRealm(ctx, ctor);
- if (!realm) {
- JS_FreeValue(ctx, ctor);
- return JS_EXCEPTION;
- }
- if (realm != ctx &&
- js_same_value(ctx, ctor, realm->array_ctor)) {
- JS_FreeValue(ctx, ctor);
- ctor = JS_UNDEFINED;
- }
- }
- if (JS_IsObject(ctor)) {
- species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species);
- JS_FreeValue(ctx, ctor);
- if (JS_IsException(species))
- return species;
- ctor = species;
- if (JS_IsNull(ctor))
- ctor = JS_UNDEFINED;
- }
- if (!JS_IsUndefined(ctor) &&
- js_same_value(ctx, ctor, ctx->array_ctor)) {
- JS_FreeValue(ctx, ctor);
- ctor = JS_UNDEFINED;
- }
- return ctor;
- }
- static JSValue JS_ArrayCreateFromCtor(JSContext *ctx, JSValueConst ctor, int64_t len)
- {
- JSValue len_val, ret;
- len_val = JS_NewInt64(ctx, len);
- if (JS_IsUndefined(ctor)) {
- ret = js_array_constructor(ctx, JS_UNDEFINED, 1, (JSValueConst *)&len_val);
- } else {
- ret = JS_CallConstructor(ctx, ctor, 1, (JSValueConst *)&len_val);
- }
- JS_FreeValue(ctx, len_val);
- return ret;
- }
- /* len must be >= 0 */
- static JSValue JS_ArraySpeciesCreate(JSContext *ctx, JSValueConst obj, int64_t len)
- {
- JSValue ctor, ret;
- ctor = JS_ArraySpeciesGetCtor(ctx, obj);
- if (JS_IsException(ctor))
- return ctor;
- ret = JS_ArrayCreateFromCtor(ctx, ctor, len);
- JS_FreeValue(ctx, ctor);
- return ret;
- }
- static const JSCFunctionListEntry js_array_funcs[] = {
- JS_CFUNC_DEF("isArray", 1, js_array_isArray ),
- JS_CFUNC_DEF("from", 1, js_array_from ),
- JS_CFUNC_DEF("of", 0, js_array_of ),
- JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
- };
- static int JS_isConcatSpreadable(JSContext *ctx, JSValueConst obj)
- {
- JSValue val;
- if (!JS_IsObject(obj))
- return FALSE;
- val = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_isConcatSpreadable);
- if (JS_IsException(val))
- return -1;
- if (!JS_IsUndefined(val))
- return JS_ToBoolFree(ctx, val);
- return JS_IsArray(ctx, obj);
- }
- static JSValue js_array_at(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue obj, ret;
- int64_t len, idx;
- JSValue *arrp;
- uint32_t count;
- obj = JS_ToObject(ctx, this_val);
- if (js_get_length64(ctx, &len, obj))
- goto exception;
- if (JS_ToInt64Sat(ctx, &idx, argv[0]))
- goto exception;
- if (idx < 0)
- idx = len + idx;
- if (idx < 0 || idx >= len) {
- ret = JS_UNDEFINED;
- } else if (js_get_fast_array(ctx, obj, &arrp, &count) && idx < count) {
- ret = JS_DupValue(ctx, arrp[idx]);
- } else {
- int present = JS_TryGetPropertyInt64(ctx, obj, idx, &ret);
- if (present < 0)
- goto exception;
- if (!present)
- ret = JS_UNDEFINED;
- }
- JS_FreeValue(ctx, obj);
- return ret;
- exception:
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- static JSValue js_array_with(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue arr, obj, ret, *arrp, *pval;
- JSObject *p;
- int64_t i, len, idx;
- uint32_t count32;
- ret = JS_EXCEPTION;
- arr = JS_UNDEFINED;
- obj = JS_ToObject(ctx, this_val);
- if (js_get_length64(ctx, &len, obj))
- goto exception;
- if (JS_ToInt64Sat(ctx, &idx, argv[0]))
- goto exception;
- if (idx < 0)
- idx = len + idx;
- if (idx < 0 || idx >= len) {
- JS_ThrowRangeError(ctx, "invalid array index: %" PRId64, idx);
- goto exception;
- }
- arr = js_allocate_fast_array(ctx, len);
- if (JS_IsException(arr))
- goto exception;
- p = JS_VALUE_GET_OBJ(arr);
- i = 0;
- pval = p->u.array.u.values;
- if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
- for (; i < idx; i++, pval++)
- *pval = JS_DupValue(ctx, arrp[i]);
- *pval = JS_DupValue(ctx, argv[1]);
- for (i++, pval++; i < len; i++, pval++)
- *pval = JS_DupValue(ctx, arrp[i]);
- } else {
- for (; i < idx; i++, pval++)
- if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval))
- goto exception;
- *pval = JS_DupValue(ctx, argv[1]);
- for (i++, pval++; i < len; i++, pval++) {
- if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval))
- goto exception;
- }
- }
- ret = arr;
- arr = JS_UNDEFINED;
- exception:
- JS_FreeValue(ctx, arr);
- JS_FreeValue(ctx, obj);
- return ret;
- }
- static JSValue js_array_concat(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue obj, arr, val;
- JSValueConst e;
- int64_t len, k, n;
- int i, res;
- arr = JS_UNDEFINED;
- obj = JS_ToObject(ctx, this_val);
- if (JS_IsException(obj))
- goto exception;
- arr = JS_ArraySpeciesCreate(ctx, obj, 0);
- if (JS_IsException(arr))
- goto exception;
- n = 0;
- for (i = -1; i < argc; i++) {
- if (i < 0)
- e = obj;
- else
- e = argv[i];
- res = JS_isConcatSpreadable(ctx, e);
- if (res < 0)
- goto exception;
- if (res) {
- if (js_get_length64(ctx, &len, e))
- goto exception;
- if (n + len > MAX_SAFE_INTEGER) {
- JS_ThrowTypeError(ctx, "Array loo long");
- goto exception;
- }
- for (k = 0; k < len; k++, n++) {
- res = JS_TryGetPropertyInt64(ctx, e, k, &val);
- if (res < 0)
- goto exception;
- if (res) {
- if (JS_DefinePropertyValueInt64(ctx, arr, n, val,
- JS_PROP_C_W_E | JS_PROP_THROW) < 0)
- goto exception;
- }
- }
- } else {
- if (n >= MAX_SAFE_INTEGER) {
- JS_ThrowTypeError(ctx, "Array loo long");
- goto exception;
- }
- if (JS_DefinePropertyValueInt64(ctx, arr, n, JS_DupValue(ctx, e),
- JS_PROP_C_W_E | JS_PROP_THROW) < 0)
- goto exception;
- n++;
- }
- }
- if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0)
- goto exception;
- JS_FreeValue(ctx, obj);
- return arr;
- exception:
- JS_FreeValue(ctx, arr);
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- #define special_every 0
- #define special_some 1
- #define special_forEach 2
- #define special_map 3
- #define special_filter 4
- #define special_TA 8
- static JSValue js_typed_array___speciesCreate(JSContext *ctx,
- JSValueConst this_val,
- int argc, JSValueConst *argv);
- static JSValue js_array_every(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int special)
- {
- JSValue obj, val, index_val, res, ret;
- JSValueConst args[3];
- JSValueConst func, this_arg;
- int64_t len, k, n;
- int present;
- ret = JS_UNDEFINED;
- val = JS_UNDEFINED;
- if (special & special_TA) {
- obj = JS_DupValue(ctx, this_val);
- len = js_typed_array_get_length_unsafe(ctx, obj);
- if (len < 0)
- goto exception;
- } else {
- obj = JS_ToObject(ctx, this_val);
- if (js_get_length64(ctx, &len, obj))
- goto exception;
- }
- func = argv[0];
- this_arg = JS_UNDEFINED;
- if (argc > 1)
- this_arg = argv[1];
- if (check_function(ctx, func))
- goto exception;
- switch (special) {
- case special_every:
- case special_every | special_TA:
- ret = JS_TRUE;
- break;
- case special_some:
- case special_some | special_TA:
- ret = JS_FALSE;
- break;
- case special_map:
- ret = JS_ArraySpeciesCreate(ctx, obj, len);
- if (JS_IsException(ret))
- goto exception;
- break;
- case special_filter:
- ret = JS_ArraySpeciesCreate(ctx, obj, 0);
- if (JS_IsException(ret))
- goto exception;
- break;
- case special_map | special_TA:
- args[0] = obj;
- args[1] = JS_NewInt32(ctx, len);
- ret = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args);
- if (JS_IsException(ret))
- goto exception;
- break;
- case special_filter | special_TA:
- ret = JS_NewArray(ctx);
- if (JS_IsException(ret))
- goto exception;
- break;
- }
- n = 0;
- for(k = 0; k < len; k++) {
- if (special & special_TA) {
- val = JS_GetPropertyInt64(ctx, obj, k);
- if (JS_IsException(val))
- goto exception;
- present = TRUE;
- } else {
- present = JS_TryGetPropertyInt64(ctx, obj, k, &val);
- if (present < 0)
- goto exception;
- }
- if (present) {
- index_val = JS_NewInt64(ctx, k);
- if (JS_IsException(index_val))
- goto exception;
- args[0] = val;
- args[1] = index_val;
- args[2] = obj;
- res = JS_Call(ctx, func, this_arg, 3, args);
- JS_FreeValue(ctx, index_val);
- if (JS_IsException(res))
- goto exception;
- switch (special) {
- case special_every:
- case special_every | special_TA:
- if (!JS_ToBoolFree(ctx, res)) {
- ret = JS_FALSE;
- goto done;
- }
- break;
- case special_some:
- case special_some | special_TA:
- if (JS_ToBoolFree(ctx, res)) {
- ret = JS_TRUE;
- goto done;
- }
- break;
- case special_map:
- if (JS_DefinePropertyValueInt64(ctx, ret, k, res,
- JS_PROP_C_W_E | JS_PROP_THROW) < 0)
- goto exception;
- break;
- case special_map | special_TA:
- if (JS_SetPropertyValue(ctx, ret, JS_NewInt32(ctx, k), res, JS_PROP_THROW) < 0)
- goto exception;
- break;
- case special_filter:
- case special_filter | special_TA:
- if (JS_ToBoolFree(ctx, res)) {
- if (JS_DefinePropertyValueInt64(ctx, ret, n++, JS_DupValue(ctx, val),
- JS_PROP_C_W_E | JS_PROP_THROW) < 0)
- goto exception;
- }
- break;
- default:
- JS_FreeValue(ctx, res);
- break;
- }
- JS_FreeValue(ctx, val);
- val = JS_UNDEFINED;
- }
- }
- done:
- if (special == (special_filter | special_TA)) {
- JSValue arr;
- args[0] = obj;
- args[1] = JS_NewInt32(ctx, n);
- arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args);
- if (JS_IsException(arr))
- goto exception;
- args[0] = ret;
- res = JS_Invoke(ctx, arr, JS_ATOM_set, 1, args);
- if (check_exception_free(ctx, res)) {
- JS_FreeValue(ctx, arr);
- goto exception;
- }
- JS_FreeValue(ctx, ret);
- ret = arr;
- }
- JS_FreeValue(ctx, val);
- JS_FreeValue(ctx, obj);
- return ret;
- exception:
- JS_FreeValue(ctx, ret);
- JS_FreeValue(ctx, val);
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- #define special_reduce 0
- #define special_reduceRight 1
- static JSValue js_array_reduce(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int special)
- {
- JSValue obj, val, index_val, acc, acc1;
- JSValueConst args[4];
- JSValueConst func;
- int64_t len, k, k1;
- int present;
- acc = JS_UNDEFINED;
- val = JS_UNDEFINED;
- if (special & special_TA) {
- obj = JS_DupValue(ctx, this_val);
- len = js_typed_array_get_length_unsafe(ctx, obj);
- if (len < 0)
- goto exception;
- } else {
- obj = JS_ToObject(ctx, this_val);
- if (js_get_length64(ctx, &len, obj))
- goto exception;
- }
- func = argv[0];
- if (check_function(ctx, func))
- goto exception;
- k = 0;
- if (argc > 1) {
- acc = JS_DupValue(ctx, argv[1]);
- } else {
- for(;;) {
- if (k >= len) {
- JS_ThrowTypeError(ctx, "empty array");
- goto exception;
- }
- k1 = (special & special_reduceRight) ? len - k - 1 : k;
- k++;
- if (special & special_TA) {
- acc = JS_GetPropertyInt64(ctx, obj, k1);
- if (JS_IsException(acc))
- goto exception;
- break;
- } else {
- present = JS_TryGetPropertyInt64(ctx, obj, k1, &acc);
- if (present < 0)
- goto exception;
- if (present)
- break;
- }
- }
- }
- for (; k < len; k++) {
- k1 = (special & special_reduceRight) ? len - k - 1 : k;
- if (special & special_TA) {
- val = JS_GetPropertyInt64(ctx, obj, k1);
- if (JS_IsException(val))
- goto exception;
- present = TRUE;
- } else {
- present = JS_TryGetPropertyInt64(ctx, obj, k1, &val);
- if (present < 0)
- goto exception;
- }
- if (present) {
- index_val = JS_NewInt64(ctx, k1);
- if (JS_IsException(index_val))
- goto exception;
- args[0] = acc;
- args[1] = val;
- args[2] = index_val;
- args[3] = obj;
- acc1 = JS_Call(ctx, func, JS_UNDEFINED, 4, args);
- JS_FreeValue(ctx, index_val);
- JS_FreeValue(ctx, val);
- val = JS_UNDEFINED;
- if (JS_IsException(acc1))
- goto exception;
- JS_FreeValue(ctx, acc);
- acc = acc1;
- }
- }
- JS_FreeValue(ctx, obj);
- return acc;
- exception:
- JS_FreeValue(ctx, acc);
- JS_FreeValue(ctx, val);
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- static JSValue js_array_fill(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue obj;
- int64_t len, start, end;
- obj = JS_ToObject(ctx, this_val);
- if (js_get_length64(ctx, &len, obj))
- goto exception;
- start = 0;
- if (argc > 1 && !JS_IsUndefined(argv[1])) {
- if (JS_ToInt64Clamp(ctx, &start, argv[1], 0, len, len))
- goto exception;
- }
- end = len;
- if (argc > 2 && !JS_IsUndefined(argv[2])) {
- if (JS_ToInt64Clamp(ctx, &end, argv[2], 0, len, len))
- goto exception;
- }
- /* XXX: should special case fast arrays */
- while (start < end) {
- if (JS_SetPropertyInt64(ctx, obj, start,
- JS_DupValue(ctx, argv[0])) < 0)
- goto exception;
- start++;
- }
- return obj;
- exception:
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- static JSValue js_array_includes(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue obj, val;
- int64_t len, n;
- JSValue *arrp;
- uint32_t count;
- int res;
- obj = JS_ToObject(ctx, this_val);
- if (js_get_length64(ctx, &len, obj))
- goto exception;
- res = FALSE;
- if (len > 0) {
- n = 0;
- if (argc > 1) {
- if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len))
- goto exception;
- }
- if (js_get_fast_array(ctx, obj, &arrp, &count)) {
- for (; n < count; n++) {
- if (js_strict_eq2(ctx, argv[0], arrp[n],
- JS_EQ_SAME_VALUE_ZERO)) {
- res = TRUE;
- goto done;
- }
- }
- }
- for (; n < len; n++) {
- val = JS_GetPropertyInt64(ctx, obj, n);
- if (JS_IsException(val))
- goto exception;
- if (js_strict_eq2(ctx, argv[0], val,
- JS_EQ_SAME_VALUE_ZERO)) {
- JS_FreeValue(ctx, val);
- res = TRUE;
- break;
- }
- JS_FreeValue(ctx, val);
- }
- }
- done:
- JS_FreeValue(ctx, obj);
- return JS_NewBool(ctx, res);
- exception:
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- static JSValue js_array_indexOf(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue obj, val;
- int64_t len, n, res;
- JSValue *arrp;
- uint32_t count;
- obj = JS_ToObject(ctx, this_val);
- if (js_get_length64(ctx, &len, obj))
- goto exception;
- res = -1;
- if (len > 0) {
- n = 0;
- if (argc > 1) {
- if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len))
- goto exception;
- }
- if (js_get_fast_array(ctx, obj, &arrp, &count)) {
- for (; n < count; n++) {
- if (js_strict_eq2(ctx, argv[0], arrp[n], JS_EQ_STRICT)) {
- res = n;
- goto done;
- }
- }
- }
- for (; n < len; n++) {
- int present = JS_TryGetPropertyInt64(ctx, obj, n, &val);
- if (present < 0)
- goto exception;
- if (present) {
- if (js_strict_eq2(ctx, argv[0], val, JS_EQ_STRICT)) {
- JS_FreeValue(ctx, val);
- res = n;
- break;
- }
- JS_FreeValue(ctx, val);
- }
- }
- }
- done:
- JS_FreeValue(ctx, obj);
- return JS_NewInt64(ctx, res);
- exception:
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- static JSValue js_array_lastIndexOf(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue obj, val;
- int64_t len, n, res;
- int present;
- obj = JS_ToObject(ctx, this_val);
- if (js_get_length64(ctx, &len, obj))
- goto exception;
- res = -1;
- if (len > 0) {
- n = len - 1;
- if (argc > 1) {
- if (JS_ToInt64Clamp(ctx, &n, argv[1], -1, len - 1, len))
- goto exception;
- }
- /* XXX: should special case fast arrays */
- for (; n >= 0; n--) {
- present = JS_TryGetPropertyInt64(ctx, obj, n, &val);
- if (present < 0)
- goto exception;
- if (present) {
- if (js_strict_eq2(ctx, argv[0], val, JS_EQ_STRICT)) {
- JS_FreeValue(ctx, val);
- res = n;
- break;
- }
- JS_FreeValue(ctx, val);
- }
- }
- }
- JS_FreeValue(ctx, obj);
- return JS_NewInt64(ctx, res);
- exception:
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- enum {
- ArrayFind,
- ArrayFindIndex,
- ArrayFindLast,
- ArrayFindLastIndex,
- };
- static JSValue js_array_find(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int mode)
- {
- JSValueConst func, this_arg;
- JSValueConst args[3];
- JSValue obj, val, index_val, res;
- int64_t len, k, end;
- int dir;
- index_val = JS_UNDEFINED;
- val = JS_UNDEFINED;
- obj = JS_ToObject(ctx, this_val);
- if (js_get_length64(ctx, &len, obj))
- goto exception;
- func = argv[0];
- if (check_function(ctx, func))
- goto exception;
- this_arg = JS_UNDEFINED;
- if (argc > 1)
- this_arg = argv[1];
- k = 0;
- dir = 1;
- end = len;
- if (mode == ArrayFindLast || mode == ArrayFindLastIndex) {
- k = len - 1;
- dir = -1;
- end = -1;
- }
- // TODO(bnoordhuis) add fast path for fast arrays
- for(; k != end; k += dir) {
- index_val = JS_NewInt64(ctx, k);
- if (JS_IsException(index_val))
- goto exception;
- val = JS_GetPropertyValue(ctx, obj, index_val);
- if (JS_IsException(val))
- goto exception;
- args[0] = val;
- args[1] = index_val;
- args[2] = this_val;
- res = JS_Call(ctx, func, this_arg, 3, args);
- if (JS_IsException(res))
- goto exception;
- if (JS_ToBoolFree(ctx, res)) {
- if (mode == ArrayFindIndex || mode == ArrayFindLastIndex) {
- JS_FreeValue(ctx, val);
- JS_FreeValue(ctx, obj);
- return index_val;
- } else {
- JS_FreeValue(ctx, index_val);
- JS_FreeValue(ctx, obj);
- return val;
- }
- }
- JS_FreeValue(ctx, val);
- JS_FreeValue(ctx, index_val);
- }
- JS_FreeValue(ctx, obj);
- if (mode == ArrayFindIndex || mode == ArrayFindLastIndex)
- return JS_NewInt32(ctx, -1);
- else
- return JS_UNDEFINED;
- exception:
- JS_FreeValue(ctx, index_val);
- JS_FreeValue(ctx, val);
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- static JSValue js_array_toString(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue obj, method, ret;
- obj = JS_ToObject(ctx, this_val);
- if (JS_IsException(obj))
- return JS_EXCEPTION;
- method = JS_GetProperty(ctx, obj, JS_ATOM_join);
- if (JS_IsException(method)) {
- ret = JS_EXCEPTION;
- } else
- if (!JS_IsFunction(ctx, method)) {
- /* Use intrinsic Object.prototype.toString */
- JS_FreeValue(ctx, method);
- ret = js_object_toString(ctx, obj, 0, NULL);
- } else {
- ret = JS_CallFree(ctx, method, obj, 0, NULL);
- }
- JS_FreeValue(ctx, obj);
- return ret;
- }
- static JSValue js_array_join(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int toLocaleString)
- {
- JSValue obj, sep = JS_UNDEFINED, el;
- StringBuffer b_s, *b = &b_s;
- JSString *p = NULL;
- int64_t i, n;
- int c;
- obj = JS_ToObject(ctx, this_val);
- if (js_get_length64(ctx, &n, obj))
- goto exception;
- c = ','; /* default separator */
- if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) {
- sep = JS_ToString(ctx, argv[0]);
- if (JS_IsException(sep))
- goto exception;
- p = JS_VALUE_GET_STRING(sep);
- if (p->len == 1 && !p->is_wide_char)
- c = p->u.str8[0];
- else
- c = -1;
- }
- string_buffer_init(ctx, b, 0);
- for(i = 0; i < n; i++) {
- if (i > 0) {
- if (c >= 0) {
- string_buffer_putc8(b, c);
- } else {
- string_buffer_concat(b, p, 0, p->len);
- }
- }
- el = JS_GetPropertyUint32(ctx, obj, i);
- if (JS_IsException(el))
- goto fail;
- if (!JS_IsNull(el) && !JS_IsUndefined(el)) {
- if (toLocaleString) {
- el = JS_ToLocaleStringFree(ctx, el);
- }
- if (string_buffer_concat_value_free(b, el))
- goto fail;
- }
- }
- JS_FreeValue(ctx, sep);
- JS_FreeValue(ctx, obj);
- return string_buffer_end(b);
- fail:
- string_buffer_free(b);
- JS_FreeValue(ctx, sep);
- exception:
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- static JSValue js_array_pop(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int shift)
- {
- JSValue obj, res = JS_UNDEFINED;
- int64_t len, newLen;
- JSValue *arrp;
- uint32_t count32;
- obj = JS_ToObject(ctx, this_val);
- if (js_get_length64(ctx, &len, obj))
- goto exception;
- newLen = 0;
- if (len > 0) {
- newLen = len - 1;
- /* Special case fast arrays */
- if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
- JSObject *p = JS_VALUE_GET_OBJ(obj);
- if (shift) {
- res = arrp[0];
- memmove(arrp, arrp + 1, (count32 - 1) * sizeof(*arrp));
- p->u.array.count--;
- } else {
- res = arrp[count32 - 1];
- p->u.array.count--;
- }
- } else {
- if (shift) {
- res = JS_GetPropertyInt64(ctx, obj, 0);
- if (JS_IsException(res))
- goto exception;
- if (JS_CopySubArray(ctx, obj, 0, 1, len - 1, +1))
- goto exception;
- } else {
- res = JS_GetPropertyInt64(ctx, obj, newLen);
- if (JS_IsException(res))
- goto exception;
- }
- if (JS_DeletePropertyInt64(ctx, obj, newLen, JS_PROP_THROW) < 0)
- goto exception;
- }
- }
- if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0)
- goto exception;
- JS_FreeValue(ctx, obj);
- return res;
- exception:
- JS_FreeValue(ctx, res);
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- static JSValue js_array_push(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int unshift)
- {
- JSValue obj;
- int i;
- int64_t len, from, newLen;
- if (likely(JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT && !unshift)) {
- JSObject *p = JS_VALUE_GET_OBJ(this_val);
- if (likely(p->class_id == JS_CLASS_ARRAY && p->fast_array &&
- can_extend_fast_array(p) &&
- JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT &&
- JS_VALUE_GET_INT(p->prop[0].u.value) == p->u.array.count &&
- (get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE) != 0)) {
- /* fast case */
- uint32_t new_len;
- new_len = p->u.array.count + argc;
- if (likely(new_len <= INT32_MAX)) {
- if (unlikely(new_len > p->u.array.u1.size)) {
- if (expand_fast_array(ctx, p, new_len))
- return JS_EXCEPTION;
- }
- for(i = 0; i < argc; i++)
- p->u.array.u.values[p->u.array.count + i] = JS_DupValue(ctx, argv[i]);
- p->prop[0].u.value = JS_NewInt32(ctx, new_len);
- p->u.array.count = new_len;
- return JS_NewInt32(ctx, new_len);
- }
- }
- }
- obj = JS_ToObject(ctx, this_val);
- if (js_get_length64(ctx, &len, obj))
- goto exception;
- newLen = len + argc;
- if (newLen > MAX_SAFE_INTEGER) {
- JS_ThrowTypeError(ctx, "Array loo long");
- goto exception;
- }
- from = len;
- if (unshift && argc > 0) {
- if (JS_CopySubArray(ctx, obj, argc, 0, len, -1))
- goto exception;
- from = 0;
- }
- for(i = 0; i < argc; i++) {
- if (JS_SetPropertyInt64(ctx, obj, from + i,
- JS_DupValue(ctx, argv[i])) < 0)
- goto exception;
- }
- if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0)
- goto exception;
- JS_FreeValue(ctx, obj);
- return JS_NewInt64(ctx, newLen);
- exception:
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- static JSValue js_array_reverse(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue obj, lval, hval;
- JSValue *arrp;
- int64_t len, l, h;
- int l_present, h_present;
- uint32_t count32;
- lval = JS_UNDEFINED;
- obj = JS_ToObject(ctx, this_val);
- if (js_get_length64(ctx, &len, obj))
- goto exception;
- /* Special case fast arrays */
- if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
- uint32_t ll, hh;
- if (count32 > 1) {
- for (ll = 0, hh = count32 - 1; ll < hh; ll++, hh--) {
- lval = arrp[ll];
- arrp[ll] = arrp[hh];
- arrp[hh] = lval;
- }
- }
- return obj;
- }
- for (l = 0, h = len - 1; l < h; l++, h--) {
- l_present = JS_TryGetPropertyInt64(ctx, obj, l, &lval);
- if (l_present < 0)
- goto exception;
- h_present = JS_TryGetPropertyInt64(ctx, obj, h, &hval);
- if (h_present < 0)
- goto exception;
- if (h_present) {
- if (JS_SetPropertyInt64(ctx, obj, l, hval) < 0)
- goto exception;
- if (l_present) {
- if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) {
- lval = JS_UNDEFINED;
- goto exception;
- }
- lval = JS_UNDEFINED;
- } else {
- if (JS_DeletePropertyInt64(ctx, obj, h, JS_PROP_THROW) < 0)
- goto exception;
- }
- } else {
- if (l_present) {
- if (JS_DeletePropertyInt64(ctx, obj, l, JS_PROP_THROW) < 0)
- goto exception;
- if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) {
- lval = JS_UNDEFINED;
- goto exception;
- }
- lval = JS_UNDEFINED;
- }
- }
- }
- return obj;
- exception:
- JS_FreeValue(ctx, lval);
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- // Note: a.toReversed() is a.slice().reverse() with the twist that a.slice()
- // leaves holes in sparse arrays intact whereas a.toReversed() replaces them
- // with undefined, thus in effect creating a dense array.
- // Does not use Array[@@species], always returns a base Array.
- static JSValue js_array_toReversed(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue arr, obj, ret, *arrp, *pval;
- JSObject *p;
- int64_t i, len;
- uint32_t count32;
- ret = JS_EXCEPTION;
- arr = JS_UNDEFINED;
- obj = JS_ToObject(ctx, this_val);
- if (js_get_length64(ctx, &len, obj))
- goto exception;
- arr = js_allocate_fast_array(ctx, len);
- if (JS_IsException(arr))
- goto exception;
- if (len > 0) {
- p = JS_VALUE_GET_OBJ(arr);
- i = len - 1;
- pval = p->u.array.u.values;
- if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
- for (; i >= 0; i--, pval++)
- *pval = JS_DupValue(ctx, arrp[i]);
- } else {
- // Query order is observable; test262 expects descending order.
- for (; i >= 0; i--, pval++) {
- if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval))
- goto exception;
- }
- }
- }
- ret = arr;
- arr = JS_UNDEFINED;
- exception:
- JS_FreeValue(ctx, arr);
- JS_FreeValue(ctx, obj);
- return ret;
- }
- static JSValue js_array_slice(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue obj, arr, val, ctor;
- int64_t len, start, k, final, n, count;
- int kPresent;
- JSValue *arrp;
- uint32_t count32;
- arr = JS_UNDEFINED;
- obj = JS_ToObject(ctx, this_val);
- if (js_get_length64(ctx, &len, obj))
- goto exception;
- if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len))
- goto exception;
- final = len;
- if (!JS_IsUndefined(argv[1])) {
- if (JS_ToInt64Clamp(ctx, &final, argv[1], 0, len, len))
- goto exception;
- }
- count = max_int64(final - start, 0);
- ctor = JS_ArraySpeciesGetCtor(ctx, obj);
- if (JS_IsException(ctor))
- goto exception;
- final = start + count;
- if (JS_IsUndefined(ctor) &&
- js_get_fast_array(ctx, obj, &arrp, &count32) &&
- final <= count32) {
- /* fast case */
- arr = js_create_array(ctx, count, (JSValueConst *)arrp + start);
- } else {
- arr = JS_ArrayCreateFromCtor(ctx, ctor, count);
- JS_FreeValue(ctx, ctor);
- if (JS_IsException(arr))
- goto exception;
- n = 0;
- for (k = start; k < final; k++, n++) {
- kPresent = JS_TryGetPropertyInt64(ctx, obj, k, &val);
- if (kPresent < 0)
- goto exception;
- if (kPresent) {
- if (JS_CreateDataPropertyUint32(ctx, arr, n, val, JS_PROP_THROW) < 0)
- goto exception;
- }
- }
- if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0)
- goto exception;
- }
- JS_FreeValue(ctx, obj);
- return arr;
- exception:
- JS_FreeValue(ctx, obj);
- JS_FreeValue(ctx, arr);
- return JS_EXCEPTION;
- }
- static JSValue js_array_splice(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue obj, arr, val, ctor;
- int64_t len, start, k, final, n, del_count, new_len;
- int kPresent;
- uint32_t i, item_count;
- JSObject *p;
-
- arr = JS_UNDEFINED;
- obj = JS_ToObject(ctx, this_val);
- if (js_get_length64(ctx, &len, obj))
- goto exception;
- if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len))
- goto exception;
- if (argc == 0) {
- item_count = 0;
- del_count = 0;
- } else if (argc == 1) {
- item_count = 0;
- del_count = len - start;
- } else {
- item_count = argc - 2;
- if (JS_ToInt64Clamp(ctx, &del_count, argv[1], 0, len - start, 0))
- goto exception;
- }
- if (len + item_count - del_count > MAX_SAFE_INTEGER) {
- JS_ThrowTypeError(ctx, "Array loo long");
- goto exception;
- }
- final = start + del_count;
- /* warning: 'len' may be different from the actual array length
- because it may have been modified */
- new_len = len + item_count - del_count;
-
- ctor = JS_ArraySpeciesGetCtor(ctx, obj);
- if (JS_IsException(ctor))
- goto exception;
- p = JS_VALUE_GET_PTR(obj);
- if (JS_IsUndefined(ctor) &&
- p->class_id == JS_CLASS_ARRAY &&
- p->fast_array &&
- final <= p->u.array.count &&
- (get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE) && /* writable array length */
- can_extend_fast_array(p)) {
- uint32_t count32 = p->u.array.count;
- JSValue *arrp = p->u.array.u.values;
- /* fast case */
- arr = js_create_array(ctx, del_count, (JSValueConst *)arrp + start);
- if (JS_IsException(arr))
- goto exception;
-
- if (item_count != del_count) {
- /* resize */
- uint32_t new_count32;
- new_count32 = count32 + item_count - del_count;
- if (del_count > item_count) {
- for(i = 0; i < del_count - item_count; i++)
- JS_FreeValue(ctx, arrp[start + item_count + i]);
- memmove(arrp + start + item_count, arrp + final,
- (count32 - final) * sizeof(arrp[0]));
- } else {
- if (unlikely(new_count32 > p->u.array.u1.size)) {
- if (expand_fast_array(ctx, p, new_count32))
- goto exception;
- arrp = p->u.array.u.values;
- }
- memmove(arrp + start + item_count, arrp + final,
- (count32 - final) * sizeof(arrp[0]));
- for(i = 0; i < item_count - del_count; i++)
- arrp[start + del_count + i] = JS_UNDEFINED;
- }
- p->u.array.count = new_count32;
- }
- for(i = 0; i < item_count; i++)
- set_value(ctx, &arrp[start + i], JS_DupValue(ctx, argv[i + 2]));
- } else {
- arr = JS_ArrayCreateFromCtor(ctx, ctor, del_count);
- JS_FreeValue(ctx, ctor);
- if (JS_IsException(arr))
- goto exception;
-
- n = 0;
- for (k = start; k < final; k++, n++) {
- kPresent = JS_TryGetPropertyInt64(ctx, obj, k, &val);
- if (kPresent < 0)
- goto exception;
- if (kPresent) {
- if (JS_CreateDataPropertyUint32(ctx, arr, n, val, JS_PROP_THROW) < 0)
- goto exception;
- }
- }
- if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0)
- goto exception;
-
- if (item_count != del_count) {
- if (JS_CopySubArray(ctx, obj, start + item_count,
- start + del_count, len - (start + del_count),
- item_count <= del_count ? +1 : -1) < 0)
- goto exception;
- for (k = len; k-- > new_len; ) {
- if (JS_DeletePropertyInt64(ctx, obj, k, JS_PROP_THROW) < 0)
- goto exception;
- }
- }
- for (i = 0; i < item_count; i++) {
- if (JS_SetPropertyInt64(ctx, obj, start + i, JS_DupValue(ctx, argv[i + 2])) < 0)
- goto exception;
- }
- }
- if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, new_len)) < 0)
- goto exception;
- JS_FreeValue(ctx, obj);
- return arr;
- exception:
- JS_FreeValue(ctx, obj);
- JS_FreeValue(ctx, arr);
- return JS_EXCEPTION;
- }
- static JSValue js_array_toSpliced(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue arr, obj, ret, *arrp, *pval, *last;
- JSObject *p;
- int64_t i, j, len, newlen, start, add, del;
- uint32_t count32;
- pval = NULL;
- last = NULL;
- ret = JS_EXCEPTION;
- arr = JS_UNDEFINED;
- obj = JS_ToObject(ctx, this_val);
- if (js_get_length64(ctx, &len, obj))
- goto exception;
- start = 0;
- if (argc > 0)
- if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len))
- goto exception;
- del = 0;
- if (argc > 0)
- del = len - start;
- if (argc > 1)
- if (JS_ToInt64Clamp(ctx, &del, argv[1], 0, del, 0))
- goto exception;
- add = 0;
- if (argc > 2)
- add = argc - 2;
- newlen = len + add - del;
- if (newlen > MAX_SAFE_INTEGER) {
- JS_ThrowTypeError(ctx, "invalid array length");
- goto exception;
- }
- arr = js_allocate_fast_array(ctx, newlen);
- if (JS_IsException(arr))
- goto exception;
- if (newlen <= 0)
- goto done;
- p = JS_VALUE_GET_OBJ(arr);
- pval = &p->u.array.u.values[0];
- last = &p->u.array.u.values[newlen];
- if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
- for (i = 0; i < start; i++, pval++)
- *pval = JS_DupValue(ctx, arrp[i]);
- for (j = 0; j < add; j++, pval++)
- *pval = JS_DupValue(ctx, argv[2 + j]);
- for (i += del; i < len; i++, pval++)
- *pval = JS_DupValue(ctx, arrp[i]);
- } else {
- for (i = 0; i < start; i++, pval++)
- if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval))
- goto exception;
- for (j = 0; j < add; j++, pval++)
- *pval = JS_DupValue(ctx, argv[2 + j]);
- for (i += del; i < len; i++, pval++)
- if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval))
- goto exception;
- }
- assert(pval == last);
- done:
- ret = arr;
- arr = JS_UNDEFINED;
- exception:
- JS_FreeValue(ctx, arr);
- JS_FreeValue(ctx, obj);
- return ret;
- }
- static JSValue js_array_copyWithin(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue obj;
- int64_t len, from, to, final, count;
- obj = JS_ToObject(ctx, this_val);
- if (js_get_length64(ctx, &len, obj))
- goto exception;
- if (JS_ToInt64Clamp(ctx, &to, argv[0], 0, len, len))
- goto exception;
- if (JS_ToInt64Clamp(ctx, &from, argv[1], 0, len, len))
- goto exception;
- final = len;
- if (argc > 2 && !JS_IsUndefined(argv[2])) {
- if (JS_ToInt64Clamp(ctx, &final, argv[2], 0, len, len))
- goto exception;
- }
- count = min_int64(final - from, len - to);
- if (JS_CopySubArray(ctx, obj, to, from, count,
- (from < to && to < from + count) ? -1 : +1))
- goto exception;
- return obj;
- exception:
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- static int64_t JS_FlattenIntoArray(JSContext *ctx, JSValueConst target,
- JSValueConst source, int64_t sourceLen,
- int64_t targetIndex, int depth,
- JSValueConst mapperFunction,
- JSValueConst thisArg)
- {
- JSValue element;
- int64_t sourceIndex, elementLen;
- int present, is_array;
- if (js_check_stack_overflow(ctx->rt, 0)) {
- JS_ThrowStackOverflow(ctx);
- return -1;
- }
- for (sourceIndex = 0; sourceIndex < sourceLen; sourceIndex++) {
- present = JS_TryGetPropertyInt64(ctx, source, sourceIndex, &element);
- if (present < 0)
- return -1;
- if (!present)
- continue;
- if (!JS_IsUndefined(mapperFunction)) {
- JSValueConst args[3] = { element, JS_NewInt64(ctx, sourceIndex), source };
- element = JS_Call(ctx, mapperFunction, thisArg, 3, args);
- JS_FreeValue(ctx, (JSValue)args[0]);
- JS_FreeValue(ctx, (JSValue)args[1]);
- if (JS_IsException(element))
- return -1;
- }
- if (depth > 0) {
- is_array = JS_IsArray(ctx, element);
- if (is_array < 0)
- goto fail;
- if (is_array) {
- if (js_get_length64(ctx, &elementLen, element) < 0)
- goto fail;
- targetIndex = JS_FlattenIntoArray(ctx, target, element,
- elementLen, targetIndex,
- depth - 1,
- JS_UNDEFINED, JS_UNDEFINED);
- if (targetIndex < 0)
- goto fail;
- JS_FreeValue(ctx, element);
- continue;
- }
- }
- if (targetIndex >= MAX_SAFE_INTEGER) {
- JS_ThrowTypeError(ctx, "Array too long");
- goto fail;
- }
- if (JS_DefinePropertyValueInt64(ctx, target, targetIndex, element,
- JS_PROP_C_W_E | JS_PROP_THROW) < 0)
- return -1;
- targetIndex++;
- }
- return targetIndex;
- fail:
- JS_FreeValue(ctx, element);
- return -1;
- }
- static JSValue js_array_flatten(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int map)
- {
- JSValue obj, arr;
- JSValueConst mapperFunction, thisArg;
- int64_t sourceLen;
- int depthNum;
- arr = JS_UNDEFINED;
- obj = JS_ToObject(ctx, this_val);
- if (js_get_length64(ctx, &sourceLen, obj))
- goto exception;
- depthNum = 1;
- mapperFunction = JS_UNDEFINED;
- thisArg = JS_UNDEFINED;
- if (map) {
- mapperFunction = argv[0];
- if (argc > 1) {
- thisArg = argv[1];
- }
- if (check_function(ctx, mapperFunction))
- goto exception;
- } else {
- if (argc > 0 && !JS_IsUndefined(argv[0])) {
- if (JS_ToInt32Sat(ctx, &depthNum, argv[0]) < 0)
- goto exception;
- }
- }
- arr = JS_ArraySpeciesCreate(ctx, obj, 0);
- if (JS_IsException(arr))
- goto exception;
- if (JS_FlattenIntoArray(ctx, arr, obj, sourceLen, 0, depthNum,
- mapperFunction, thisArg) < 0)
- goto exception;
- JS_FreeValue(ctx, obj);
- return arr;
- exception:
- JS_FreeValue(ctx, obj);
- JS_FreeValue(ctx, arr);
- return JS_EXCEPTION;
- }
- /* Array sort */
- typedef struct ValueSlot {
- JSValue val;
- JSString *str;
- int64_t pos;
- } ValueSlot;
- struct array_sort_context {
- JSContext *ctx;
- int exception;
- int has_method;
- JSValueConst method;
- };
- static int js_array_cmp_generic(const void *a, const void *b, void *opaque) {
- struct array_sort_context *psc = opaque;
- JSContext *ctx = psc->ctx;
- JSValueConst argv[2];
- JSValue res;
- ValueSlot *ap = (ValueSlot *)(void *)a;
- ValueSlot *bp = (ValueSlot *)(void *)b;
- int cmp;
- if (psc->exception)
- return 0;
- if (psc->has_method) {
- /* custom sort function is specified as returning 0 for identical
- * objects: avoid method call overhead.
- */
- if (!memcmp(&ap->val, &bp->val, sizeof(ap->val)))
- goto cmp_same;
- argv[0] = ap->val;
- argv[1] = bp->val;
- res = JS_Call(ctx, psc->method, JS_UNDEFINED, 2, argv);
- if (JS_IsException(res))
- goto exception;
- if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) {
- int val = JS_VALUE_GET_INT(res);
- cmp = (val > 0) - (val < 0);
- } else {
- double val;
- if (JS_ToFloat64Free(ctx, &val, res) < 0)
- goto exception;
- cmp = (val > 0) - (val < 0);
- }
- } else {
- /* Not supposed to bypass ToString even for identical objects as
- * tested in test262/test/built-ins/Array/prototype/sort/bug_596_1.js
- */
- if (!ap->str) {
- JSValue str = JS_ToString(ctx, ap->val);
- if (JS_IsException(str))
- goto exception;
- ap->str = JS_VALUE_GET_STRING(str);
- }
- if (!bp->str) {
- JSValue str = JS_ToString(ctx, bp->val);
- if (JS_IsException(str))
- goto exception;
- bp->str = JS_VALUE_GET_STRING(str);
- }
- cmp = js_string_compare(ctx, ap->str, bp->str);
- }
- if (cmp != 0)
- return cmp;
- cmp_same:
- /* make sort stable: compare array offsets */
- return (ap->pos > bp->pos) - (ap->pos < bp->pos);
- exception:
- psc->exception = 1;
- return 0;
- }
- static JSValue js_array_sort(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- struct array_sort_context asc = { ctx, 0, 0, argv[0] };
- JSValue obj = JS_UNDEFINED;
- ValueSlot *array = NULL;
- size_t array_size = 0, pos = 0, n = 0;
- int64_t i, len, undefined_count = 0;
- int present;
- if (!JS_IsUndefined(asc.method)) {
- if (check_function(ctx, asc.method))
- goto exception;
- asc.has_method = 1;
- }
- obj = JS_ToObject(ctx, this_val);
- if (js_get_length64(ctx, &len, obj))
- goto exception;
- /* XXX: should special case fast arrays */
- for (i = 0; i < len; i++) {
- if (pos >= array_size) {
- size_t new_size, slack;
- ValueSlot *new_array;
- new_size = (array_size + (array_size >> 1) + 31) & ~15;
- new_array = js_realloc2(ctx, array, new_size * sizeof(*array), &slack);
- if (new_array == NULL)
- goto exception;
- new_size += slack / sizeof(*new_array);
- array = new_array;
- array_size = new_size;
- }
- present = JS_TryGetPropertyInt64(ctx, obj, i, &array[pos].val);
- if (present < 0)
- goto exception;
- if (present == 0)
- continue;
- if (JS_IsUndefined(array[pos].val)) {
- undefined_count++;
- continue;
- }
- array[pos].str = NULL;
- array[pos].pos = i;
- pos++;
- }
- rqsort(array, pos, sizeof(*array), js_array_cmp_generic, &asc);
- if (asc.exception)
- goto exception;
- /* XXX: should special case fast arrays */
- while (n < pos) {
- if (array[n].str)
- JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str));
- if (array[n].pos == n) {
- JS_FreeValue(ctx, array[n].val);
- } else {
- if (JS_SetPropertyInt64(ctx, obj, n, array[n].val) < 0) {
- n++;
- goto exception;
- }
- }
- n++;
- }
- js_free(ctx, array);
- for (i = n; undefined_count-- > 0; i++) {
- if (JS_SetPropertyInt64(ctx, obj, i, JS_UNDEFINED) < 0)
- goto fail;
- }
- for (; i < len; i++) {
- if (JS_DeletePropertyInt64(ctx, obj, i, JS_PROP_THROW) < 0)
- goto fail;
- }
- return obj;
- exception:
- for (; n < pos; n++) {
- JS_FreeValue(ctx, array[n].val);
- if (array[n].str)
- JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str));
- }
- js_free(ctx, array);
- fail:
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- // Note: a.toSorted() is a.slice().sort() with the twist that a.slice()
- // leaves holes in sparse arrays intact whereas a.toSorted() replaces them
- // with undefined, thus in effect creating a dense array.
- // Does not use Array[@@species], always returns a base Array.
- static JSValue js_array_toSorted(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue arr, obj, ret, *arrp, *pval;
- JSObject *p;
- int64_t i, len;
- uint32_t count32;
- int ok;
- ok = JS_IsUndefined(argv[0]) || JS_IsFunction(ctx, argv[0]);
- if (!ok)
- return JS_ThrowTypeError(ctx, "not a function");
- ret = JS_EXCEPTION;
- arr = JS_UNDEFINED;
- obj = JS_ToObject(ctx, this_val);
- if (js_get_length64(ctx, &len, obj))
- goto exception;
- arr = js_allocate_fast_array(ctx, len);
- if (JS_IsException(arr))
- goto exception;
- if (len > 0) {
- p = JS_VALUE_GET_OBJ(arr);
- i = 0;
- pval = p->u.array.u.values;
- if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
- for (; i < len; i++, pval++)
- *pval = JS_DupValue(ctx, arrp[i]);
- } else {
- for (; i < len; i++, pval++) {
- if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval))
- goto exception;
- }
- }
- }
- ret = js_array_sort(ctx, arr, argc, argv);
- if (JS_IsException(ret))
- goto exception;
- JS_FreeValue(ctx, ret);
- ret = arr;
- arr = JS_UNDEFINED;
- exception:
- JS_FreeValue(ctx, arr);
- JS_FreeValue(ctx, obj);
- return ret;
- }
- typedef struct JSArrayIteratorData {
- JSValue obj;
- JSIteratorKindEnum kind;
- uint32_t idx;
- } JSArrayIteratorData;
- static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- JSArrayIteratorData *it = p->u.array_iterator_data;
- if (it) {
- JS_FreeValueRT(rt, it->obj);
- js_free_rt(rt, it);
- }
- }
- static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- JSArrayIteratorData *it = p->u.array_iterator_data;
- if (it) {
- JS_MarkValue(rt, it->obj, mark_func);
- }
- }
- static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
- {
- JSValue enum_obj, arr;
- JSArrayIteratorData *it;
- JSIteratorKindEnum kind;
- int class_id;
- kind = magic & 3;
- if (magic & 4) {
- /* string iterator case */
- arr = JS_ToStringCheckObject(ctx, this_val);
- class_id = JS_CLASS_STRING_ITERATOR;
- } else {
- arr = JS_ToObject(ctx, this_val);
- class_id = JS_CLASS_ARRAY_ITERATOR;
- }
- if (JS_IsException(arr))
- goto fail;
- enum_obj = JS_NewObjectClass(ctx, class_id);
- if (JS_IsException(enum_obj))
- goto fail;
- it = js_malloc(ctx, sizeof(*it));
- if (!it)
- goto fail1;
- it->obj = arr;
- it->kind = kind;
- it->idx = 0;
- JS_SetOpaque(enum_obj, it);
- return enum_obj;
- fail1:
- JS_FreeValue(ctx, enum_obj);
- fail:
- JS_FreeValue(ctx, arr);
- return JS_EXCEPTION;
- }
- static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv,
- BOOL *pdone, int magic)
- {
- JSArrayIteratorData *it;
- uint32_t len, idx;
- JSValue val, obj;
- JSObject *p;
- it = JS_GetOpaque2(ctx, this_val, JS_CLASS_ARRAY_ITERATOR);
- if (!it)
- goto fail1;
- if (JS_IsUndefined(it->obj))
- goto done;
- p = JS_VALUE_GET_OBJ(it->obj);
- if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
- p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
- if (typed_array_is_oob(p)) {
- JS_ThrowTypeErrorArrayBufferOOB(ctx);
- goto fail1;
- }
- len = p->u.array.count;
- } else {
- if (js_get_length32(ctx, &len, it->obj)) {
- fail1:
- *pdone = FALSE;
- return JS_EXCEPTION;
- }
- }
- idx = it->idx;
- if (idx >= len) {
- JS_FreeValue(ctx, it->obj);
- it->obj = JS_UNDEFINED;
- done:
- *pdone = TRUE;
- return JS_UNDEFINED;
- }
- it->idx = idx + 1;
- *pdone = FALSE;
- if (it->kind == JS_ITERATOR_KIND_KEY) {
- return JS_NewUint32(ctx, idx);
- } else {
- val = JS_GetPropertyUint32(ctx, it->obj, idx);
- if (JS_IsException(val))
- return JS_EXCEPTION;
- if (it->kind == JS_ITERATOR_KIND_VALUE) {
- return val;
- } else {
- JSValueConst args[2];
- JSValue num;
- num = JS_NewUint32(ctx, idx);
- args[0] = num;
- args[1] = val;
- obj = js_create_array(ctx, 2, args);
- JS_FreeValue(ctx, val);
- JS_FreeValue(ctx, num);
- return obj;
- }
- }
- }
- /* Iterator Wrap */
- typedef struct JSIteratorWrapData {
- JSValue wrapped_iter;
- JSValue wrapped_next;
- } JSIteratorWrapData;
- static void js_iterator_wrap_finalizer(JSRuntime *rt, JSValue val)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- JSIteratorWrapData *it = p->u.iterator_wrap_data;
- if (it) {
- JS_FreeValueRT(rt, it->wrapped_iter);
- JS_FreeValueRT(rt, it->wrapped_next);
- js_free_rt(rt, it);
- }
- }
- static void js_iterator_wrap_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- JSIteratorWrapData *it = p->u.iterator_wrap_data;
- if (it) {
- JS_MarkValue(rt, it->wrapped_iter, mark_func);
- JS_MarkValue(rt, it->wrapped_next, mark_func);
- }
- }
- static JSValue js_iterator_wrap_next(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv,
- int *pdone, int magic)
- {
- JSIteratorWrapData *it;
- JSValue method, ret;
- it = JS_GetOpaque2(ctx, this_val, JS_CLASS_ITERATOR_WRAP);
- if (!it)
- return JS_EXCEPTION;
- if (magic == GEN_MAGIC_NEXT) {
- return JS_IteratorNext(ctx, it->wrapped_iter, it->wrapped_next, 0, NULL, pdone);
- } else {
- method = JS_GetProperty(ctx, it->wrapped_iter, JS_ATOM_return);
- if (JS_IsException(method))
- return JS_EXCEPTION;
- if (JS_IsNull(method) || JS_IsUndefined(method)) {
- *pdone = TRUE;
- return JS_UNDEFINED;
- }
- ret = JS_IteratorNext2(ctx, it->wrapped_iter, method, 0, NULL, pdone);
- JS_FreeValue(ctx, method);
- return ret;
- }
- }
- static const JSCFunctionListEntry js_iterator_wrap_proto_funcs[] = {
- JS_ITERATOR_NEXT_DEF("next", 0, js_iterator_wrap_next, GEN_MAGIC_NEXT ),
- JS_ITERATOR_NEXT_DEF("return", 0, js_iterator_wrap_next, GEN_MAGIC_RETURN ),
- };
- /* Iterator */
- static JSValue js_iterator_constructor_getset(JSContext *ctx,
- JSValueConst this_val,
- int argc, JSValueConst *argv,
- int magic,
- JSValue *func_data)
- {
- int ret;
- if (argc > 0) { // if setter
- if (!JS_IsObject(argv[0]))
- return JS_ThrowTypeErrorNotAnObject(ctx);
- ret = JS_DefinePropertyValue(ctx, this_val, JS_ATOM_constructor,
- JS_DupValue(ctx, argv[0]),
- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
- if (ret < 0)
- return JS_EXCEPTION;
- return JS_UNDEFINED;
- } else {
- return JS_DupValue(ctx, func_data[0]);
- }
- }
- static JSValue js_iterator_constructor(JSContext *ctx, JSValueConst new_target,
- int argc, JSValueConst *argv)
- {
- JSObject *p;
- if (JS_TAG_OBJECT != JS_VALUE_GET_TAG(new_target))
- return JS_ThrowTypeError(ctx, "constructor requires 'new'");
- p = JS_VALUE_GET_OBJ(new_target);
- if (p->class_id == JS_CLASS_C_FUNCTION &&
- p->u.cfunc.c_function.generic == js_iterator_constructor) {
- return JS_ThrowTypeError(ctx, "abstract class not constructable");
- }
- return js_create_from_ctor(ctx, new_target, JS_CLASS_ITERATOR);
- }
- // note: deliberately doesn't use space-saving bit fields for
- // |index|, |count| and |running| because tcc miscompiles them
- typedef struct JSIteratorConcatData {
- int index, count; // elements (not pairs!) in values[] array
- BOOL running;
- JSValue iter, next, values[]; // array of (object, method) pairs
- } JSIteratorConcatData;
- static void js_iterator_concat_finalizer(JSRuntime *rt, JSValue val)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- JSIteratorConcatData *it = p->u.iterator_concat_data;
- if (it) {
- JS_FreeValueRT(rt, it->iter);
- JS_FreeValueRT(rt, it->next);
- for (int i = it->index; i < it->count; i++)
- JS_FreeValueRT(rt, it->values[i]);
- js_free_rt(rt, it);
- }
- }
- static void js_iterator_concat_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- JSIteratorConcatData *it = p->u.iterator_concat_data;
- if (it) {
- JS_MarkValue(rt, it->iter, mark_func);
- JS_MarkValue(rt, it->next, mark_func);
- for (int i = it->index; i < it->count; i++)
- JS_MarkValue(rt, it->values[i], mark_func);
- }
- }
- static JSValue js_iterator_concat_next(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv,
- int *pdone, int magic)
- {
- JSValue iter, item, next, val, *obj, *meth, ret;
- JSIteratorConcatData *it;
- int done;
- *pdone = FALSE;
- it = JS_GetOpaque2(ctx, this_val, JS_CLASS_ITERATOR_CONCAT);
- if (!it)
- return JS_EXCEPTION;
- if (it->running)
- return JS_ThrowTypeError(ctx, "already running");
- it->running = TRUE;
- for(;;) {
- if (it->index >= it->count) {
- *pdone = TRUE;
- ret = JS_UNDEFINED;
- break;
- }
- obj = &it->values[it->index + 0];
- meth = &it->values[it->index + 1];
- iter = it->iter;
- if (JS_IsUndefined(iter)) {
- iter = JS_GetIterator2(ctx, *obj, *meth);
- if (JS_IsException(iter))
- goto fail;
- it->iter = iter;
- }
- next = it->next;
- if (JS_IsUndefined(next)) {
- next = JS_GetProperty(ctx, iter, JS_ATOM_next);
- if (JS_IsException(next))
- goto fail;
- it->next = next;
- }
- item = JS_IteratorNext2(ctx, iter, next, 0, NULL, &done);
- if (JS_IsException(item))
- goto fail;
- if (done == 0) {
- ret = item;
- break;
- } else if (done == 2) {
- val = JS_GetProperty(ctx, item, JS_ATOM_done);
- if (JS_IsException(val)) {
- JS_FreeValue(ctx, item);
- fail:
- ret = JS_EXCEPTION;
- break;
- }
- done = JS_ToBoolFree(ctx, val);
- if (done)
- goto done_next;
- ret = JS_GetProperty(ctx, item, JS_ATOM_value);
- JS_FreeValue(ctx, item);
- break;
- } else {
- done_next:
- JS_FreeValue(ctx, item);
- JS_FreeValue(ctx, iter);
- JS_FreeValue(ctx, next);
- it->iter = JS_UNDEFINED;
- it->next = JS_UNDEFINED;
- JS_FreeValue(ctx, *meth);
- JS_FreeValue(ctx, *obj);
- it->index += 2;
- }
- }
- it->running = FALSE;
- return ret;
- }
- static JSValue js_iterator_concat_return(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSIteratorConcatData *it;
- JSValue ret;
- it = JS_GetOpaque2(ctx, this_val, JS_CLASS_ITERATOR_CONCAT);
- if (!it)
- return JS_EXCEPTION;
- if (it->running)
- return JS_ThrowTypeError(ctx, "already running");
- ret = JS_UNDEFINED;
- if (!JS_IsUndefined(it->iter)) {
- it->running = TRUE;
- ret = JS_GetProperty(ctx, it->iter, JS_ATOM_return);
- if (JS_IsException(ret)) {
- it->running = FALSE;
- return JS_EXCEPTION;
- }
- ret = JS_CallFree(ctx, ret, it->iter, 0, NULL);
- it->running = FALSE;
- }
- while (it->index < it->count)
- JS_FreeValue(ctx, it->values[it->index++]);
- JS_FreeValue(ctx, it->iter);
- JS_FreeValue(ctx, it->next);
- it->iter = JS_UNDEFINED;
- it->next = JS_UNDEFINED;
- return ret;
- }
- static const JSCFunctionListEntry js_iterator_concat_proto_funcs[] = {
- JS_ITERATOR_NEXT_DEF("next", 0, js_iterator_concat_next, 0 ),
- JS_CFUNC_DEF("return", 0, js_iterator_concat_return ),
- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Iterator Concat", JS_PROP_CONFIGURABLE ),
- };
- static JSValue js_iterator_concat(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSIteratorConcatData *it;
- JSValue obj, method;
- it = js_malloc(ctx, sizeof(*it) + 2*argc * sizeof(it->values[0]));
- if (!it)
- return JS_EXCEPTION;
- it->running = FALSE;
- it->index = 0;
- it->count = 0;
- it->iter = JS_UNDEFINED;
- it->next = JS_UNDEFINED;
- for (int i = 0; i < argc; i++) {
- JSValueConst obj = argv[i];
- if (!JS_IsObject(obj)) {
- JS_ThrowTypeErrorNotAnObject(ctx);
- goto fail;
- }
- method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator);
- if (JS_IsException(method))
- goto fail;
- if (!JS_IsFunction(ctx, method)) {
- JS_ThrowTypeError(ctx, "not a function");
- JS_FreeValue(ctx, method);
- goto fail;
- }
- it->values[it->count++] = JS_DupValue(ctx, obj);
- it->values[it->count++] = method;
- }
- obj = JS_NewObjectClass(ctx, JS_CLASS_ITERATOR_CONCAT);
- if (JS_IsException(obj))
- goto fail;
- JS_SetOpaque(obj, it);
- return obj;
- fail:
- for (int i = 0; i < it->count; i++)
- JS_FreeValue(ctx, it->values[i]);
- js_free(ctx, it);
- return JS_EXCEPTION;
- }
- static JSValue js_iterator_from(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValueConst obj = argv[0];
- JSValue method, iter, wrapper;
- JSIteratorWrapData *it;
- int ret;
- if (!JS_IsObject(obj)) {
- if (!JS_IsString(obj))
- return JS_ThrowTypeError(ctx, "Iterator.from called on non-object");
- }
- method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator);
- if (JS_IsException(method))
- return JS_EXCEPTION;
- if (JS_IsNull(method) || JS_IsUndefined(method)) {
- iter = JS_DupValue(ctx, obj);
- } else {
- iter = JS_GetIterator2(ctx, obj, method);
- JS_FreeValue(ctx, method);
- if (JS_IsException(iter))
- return JS_EXCEPTION;
- }
- wrapper = JS_UNDEFINED;
- method = JS_GetProperty(ctx, iter, JS_ATOM_next);
- if (JS_IsException(method))
- goto fail;
- ret = JS_OrdinaryIsInstanceOf(ctx, iter, ctx->iterator_ctor);
- if (ret < 0)
- goto fail;
- if (ret) {
- JS_FreeValue(ctx, method);
- return iter;
- }
-
- wrapper = JS_NewObjectClass(ctx, JS_CLASS_ITERATOR_WRAP);
- if (JS_IsException(wrapper))
- goto fail;
- it = js_malloc(ctx, sizeof(*it));
- if (!it)
- goto fail;
- it->wrapped_iter = iter;
- it->wrapped_next = method;
- JS_SetOpaque(wrapper, it);
- return wrapper;
- fail:
- JS_FreeValue(ctx, method);
- JS_FreeValue(ctx, iter);
- JS_FreeValue(ctx, wrapper);
- return JS_EXCEPTION;
- }
- typedef enum JSIteratorHelperKindEnum {
- JS_ITERATOR_HELPER_KIND_DROP,
- JS_ITERATOR_HELPER_KIND_EVERY,
- JS_ITERATOR_HELPER_KIND_FILTER,
- JS_ITERATOR_HELPER_KIND_FIND,
- JS_ITERATOR_HELPER_KIND_FLAT_MAP,
- JS_ITERATOR_HELPER_KIND_FOR_EACH,
- JS_ITERATOR_HELPER_KIND_MAP,
- JS_ITERATOR_HELPER_KIND_SOME,
- JS_ITERATOR_HELPER_KIND_TAKE,
- } JSIteratorHelperKindEnum;
- typedef struct JSIteratorHelperData {
- JSValue obj;
- JSValue next;
- JSValue func; // predicate (filter) or mapper (flatMap, map)
- JSValue inner; // innerValue (flatMap)
- int64_t count; // limit (drop, take) or counter (filter, map, flatMap)
- JSIteratorHelperKindEnum kind : 8;
- uint8_t executing : 1;
- uint8_t done : 1;
- } JSIteratorHelperData;
- static JSValue js_create_iterator_helper(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
- {
- JSValueConst func;
- JSValue obj, method;
- int64_t count;
- JSIteratorHelperData *it;
- if (!JS_IsObject(this_val))
- return JS_ThrowTypeErrorNotAnObject(ctx);
- func = JS_UNDEFINED;
- count = 0;
- switch(magic) {
- case JS_ITERATOR_HELPER_KIND_DROP:
- case JS_ITERATOR_HELPER_KIND_TAKE:
- {
- JSValue v;
- double dlimit;
- v = JS_ToNumber(ctx, argv[0]);
- if (JS_IsException(v))
- goto fail;
- // Check for Infinity.
- if (JS_ToFloat64(ctx, &dlimit, v)) {
- JS_FreeValue(ctx, v);
- goto fail;
- }
- if (isnan(dlimit)) {
- JS_FreeValue(ctx, v);
- goto range_error;
- }
- if (!isfinite(dlimit)) {
- JS_FreeValue(ctx, v);
- if (dlimit < 0)
- goto range_error;
- else
- count = MAX_SAFE_INTEGER;
- } else {
- v = JS_ToIntegerFree(ctx, v);
- if (JS_IsException(v))
- goto fail;
- if (JS_ToInt64Free(ctx, &count, v))
- goto fail;
- }
- if (count < 0)
- goto range_error;
- }
- break;
- case JS_ITERATOR_HELPER_KIND_FILTER:
- case JS_ITERATOR_HELPER_KIND_FLAT_MAP:
- case JS_ITERATOR_HELPER_KIND_MAP:
- {
- func = argv[0];
- if (check_function(ctx, func))
- goto fail;
- }
- break;
- default:
- abort();
- break;
- }
- method = JS_GetProperty(ctx, this_val, JS_ATOM_next);
- if (JS_IsException(method))
- goto fail;
- obj = JS_NewObjectClass(ctx, JS_CLASS_ITERATOR_HELPER);
- if (JS_IsException(obj)) {
- JS_FreeValue(ctx, method);
- goto fail;
- }
- it = js_malloc(ctx, sizeof(*it));
- if (!it) {
- JS_FreeValue(ctx, obj);
- JS_FreeValue(ctx, method);
- goto fail;
- }
- it->kind = magic;
- it->obj = JS_DupValue(ctx, this_val);
- it->func = JS_DupValue(ctx, func);
- it->next = method;
- it->inner = JS_UNDEFINED;
- it->count = count;
- it->executing = 0;
- it->done = 0;
- JS_SetOpaque(obj, it);
- return obj;
- range_error:
- JS_ThrowRangeError(ctx, "must be positive");
- fail:
- JS_IteratorClose(ctx, this_val, TRUE);
- return JS_EXCEPTION;
- }
- static JSValue js_iterator_proto_func(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
- {
- JSValue item, method, ret, func, index_val, r;
- JSValueConst args[2];
- int64_t idx;
- int done;
- if (!JS_IsObject(this_val))
- return JS_ThrowTypeErrorNotAnObject(ctx);
- func = JS_UNDEFINED;
- method = JS_UNDEFINED;
-
- if (check_function(ctx, argv[0]))
- goto fail;
- func = JS_DupValue(ctx, argv[0]);
- method = JS_GetProperty(ctx, this_val, JS_ATOM_next);
- if (JS_IsException(method))
- goto fail_no_close;
- r = JS_UNDEFINED;
- switch(magic) {
- case JS_ITERATOR_HELPER_KIND_EVERY:
- {
- r = JS_TRUE;
- for (idx = 0; /*empty*/; idx++) {
- item = JS_IteratorNext(ctx, this_val, method, 0, NULL, &done);
- if (JS_IsException(item))
- goto fail_no_close;
- if (done)
- break;
- index_val = JS_NewInt64(ctx, idx);
- args[0] = item;
- args[1] = index_val;
- ret = JS_Call(ctx, func, JS_UNDEFINED, countof(args), args);
- JS_FreeValue(ctx, item);
- JS_FreeValue(ctx, index_val);
- if (JS_IsException(ret))
- goto fail;
- if (!JS_ToBoolFree(ctx, ret)) {
- if (JS_IteratorClose(ctx, this_val, FALSE) < 0)
- r = JS_EXCEPTION;
- else
- r = JS_FALSE;
- break;
- }
- index_val = JS_UNDEFINED;
- ret = JS_UNDEFINED;
- item = JS_UNDEFINED;
- }
- }
- break;
- case JS_ITERATOR_HELPER_KIND_FIND:
- {
- for (idx = 0; /*empty*/; idx++) {
- item = JS_IteratorNext(ctx, this_val, method, 0, NULL, &done);
- if (JS_IsException(item))
- goto fail_no_close;
- if (done)
- break;
- index_val = JS_NewInt64(ctx, idx);
- args[0] = item;
- args[1] = index_val;
- ret = JS_Call(ctx, func, JS_UNDEFINED, countof(args), args);
- JS_FreeValue(ctx, index_val);
- if (JS_IsException(ret)) {
- JS_FreeValue(ctx, item);
- goto fail;
- }
- if (JS_ToBoolFree(ctx, ret)) {
- if (JS_IteratorClose(ctx, this_val, FALSE) < 0) {
- JS_FreeValue(ctx, item);
- r = JS_EXCEPTION;
- } else {
- r = item;
- }
- break;
- }
- JS_FreeValue(ctx, item);
- index_val = JS_UNDEFINED;
- ret = JS_UNDEFINED;
- item = JS_UNDEFINED;
- }
- }
- break;
- case JS_ITERATOR_HELPER_KIND_FOR_EACH:
- {
- for (idx = 0; /*empty*/; idx++) {
- item = JS_IteratorNext(ctx, this_val, method, 0, NULL, &done);
- if (JS_IsException(item))
- goto fail_no_close;
- if (done)
- break;
- index_val = JS_NewInt64(ctx, idx);
- args[0] = item;
- args[1] = index_val;
- ret = JS_Call(ctx, func, JS_UNDEFINED, countof(args), args);
- JS_FreeValue(ctx, item);
- JS_FreeValue(ctx, index_val);
- if (JS_IsException(ret))
- goto fail;
- JS_FreeValue(ctx, ret);
- index_val = JS_UNDEFINED;
- ret = JS_UNDEFINED;
- item = JS_UNDEFINED;
- }
- }
- break;
- case JS_ITERATOR_HELPER_KIND_SOME:
- {
- r = JS_FALSE;
- for (idx = 0; /*empty*/; idx++) {
- item = JS_IteratorNext(ctx, this_val, method, 0, NULL, &done);
- if (JS_IsException(item))
- goto fail_no_close;
- if (done)
- break;
- index_val = JS_NewInt64(ctx, idx);
- args[0] = item;
- args[1] = index_val;
- ret = JS_Call(ctx, func, JS_UNDEFINED, countof(args), args);
- JS_FreeValue(ctx, item);
- JS_FreeValue(ctx, index_val);
- if (JS_IsException(ret))
- goto fail;
- if (JS_ToBoolFree(ctx, ret)) {
- if (JS_IteratorClose(ctx, this_val, FALSE) < 0)
- r = JS_EXCEPTION;
- else
- r = JS_TRUE;
- break;
- }
- index_val = JS_UNDEFINED;
- ret = JS_UNDEFINED;
- item = JS_UNDEFINED;
- }
- }
- break;
- default:
- abort();
- break;
- }
- JS_FreeValue(ctx, func);
- JS_FreeValue(ctx, method);
- return r;
- fail:
- JS_IteratorClose(ctx, this_val, TRUE);
- fail_no_close:
- JS_FreeValue(ctx, func);
- JS_FreeValue(ctx, method);
- return JS_EXCEPTION;
- }
- static JSValue js_iterator_proto_reduce(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue item, method, ret, func, index_val, acc;
- JSValueConst args[3];
- int64_t idx;
- int done;
- if (!JS_IsObject(this_val))
- return JS_ThrowTypeErrorNotAnObject(ctx);
- acc = JS_UNDEFINED;
- func = JS_UNDEFINED;
- method = JS_UNDEFINED;
- if (check_function(ctx, argv[0]))
- goto exception;
- func = JS_DupValue(ctx, argv[0]);
- method = JS_GetProperty(ctx, this_val, JS_ATOM_next);
- if (JS_IsException(method))
- goto exception;
- if (argc > 1) {
- acc = JS_DupValue(ctx, argv[1]);
- idx = 0;
- } else {
- acc = JS_IteratorNext(ctx, this_val, method, 0, NULL, &done);
- if (JS_IsException(acc))
- goto exception_no_close;
- if (done) {
- JS_ThrowTypeError(ctx, "empty iterator");
- goto exception;
- }
- idx = 1;
- }
- for (/* empty */; /*empty*/; idx++) {
- item = JS_IteratorNext(ctx, this_val, method, 0, NULL, &done);
- if (JS_IsException(item))
- goto exception_no_close;
- if (done)
- break;
- index_val = JS_NewInt64(ctx, idx);
- args[0] = acc;
- args[1] = item;
- args[2] = index_val;
- ret = JS_Call(ctx, func, JS_UNDEFINED, countof(args), args);
- JS_FreeValue(ctx, item);
- JS_FreeValue(ctx, index_val);
- if (JS_IsException(ret))
- goto exception;
- JS_FreeValue(ctx, acc);
- acc = ret;
- index_val = JS_UNDEFINED;
- ret = JS_UNDEFINED;
- item = JS_UNDEFINED;
- }
- JS_FreeValue(ctx, func);
- JS_FreeValue(ctx, method);
- return acc;
- exception:
- JS_IteratorClose(ctx, this_val, TRUE);
- exception_no_close:
- JS_FreeValue(ctx, acc);
- JS_FreeValue(ctx, func);
- JS_FreeValue(ctx, method);
- return JS_EXCEPTION;
- }
- static JSValue js_iterator_proto_toArray(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue item, method, result;
- int64_t idx;
- int done;
- result = JS_UNDEFINED;
- if (!JS_IsObject(this_val))
- return JS_ThrowTypeErrorNotAnObject(ctx);
- method = JS_GetProperty(ctx, this_val, JS_ATOM_next);
- if (JS_IsException(method))
- return JS_EXCEPTION;
- result = JS_NewArray(ctx);
- if (JS_IsException(result))
- goto exception;
- for (idx = 0; /*empty*/; idx++) {
- item = JS_IteratorNext(ctx, this_val, method, 0, NULL, &done);
- if (JS_IsException(item))
- goto exception;
- if (done)
- break;
- if (JS_DefinePropertyValueInt64(ctx, result, idx, item,
- JS_PROP_C_W_E | JS_PROP_THROW) < 0)
- goto exception;
- }
- if (JS_SetProperty(ctx, result, JS_ATOM_length, JS_NewUint32(ctx, idx)) < 0)
- goto exception;
- JS_FreeValue(ctx, method);
- return result;
- exception:
- JS_FreeValue(ctx, result);
- JS_FreeValue(ctx, method);
- return JS_EXCEPTION;
- }
- static JSValue js_iterator_proto_iterator(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- return JS_DupValue(ctx, this_val);
- }
- static JSValue js_iterator_proto_get_toStringTag(JSContext *ctx, JSValueConst this_val)
- {
- return JS_AtomToString(ctx, JS_ATOM_Iterator);
- }
- static JSValue js_iterator_proto_set_toStringTag(JSContext *ctx, JSValueConst this_val, JSValueConst val)
- {
- int res;
- if (!JS_IsObject(this_val))
- return JS_ThrowTypeErrorNotAnObject(ctx);
- if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_ITERATOR]))
- return JS_ThrowTypeError(ctx, "Cannot assign to read only property");
- res = JS_GetOwnProperty(ctx, NULL, this_val, JS_ATOM_Symbol_toStringTag);
- if (res < 0)
- return JS_EXCEPTION;
- if (res) {
- if (JS_SetProperty(ctx, this_val, JS_ATOM_Symbol_toStringTag, JS_DupValue(ctx, val)) < 0)
- return JS_EXCEPTION;
- } else {
- if (JS_DefinePropertyValue(ctx, this_val, JS_ATOM_Symbol_toStringTag, JS_DupValue(ctx, val), JS_PROP_C_W_E) < 0)
- return JS_EXCEPTION;
- }
- return JS_UNDEFINED;
- }
- /* Iterator Helper */
- static void js_iterator_helper_finalizer(JSRuntime *rt, JSValue val)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- JSIteratorHelperData *it = p->u.iterator_helper_data;
- if (it) {
- JS_FreeValueRT(rt, it->obj);
- JS_FreeValueRT(rt, it->func);
- JS_FreeValueRT(rt, it->next);
- JS_FreeValueRT(rt, it->inner);
- js_free_rt(rt, it);
- }
- }
- static void js_iterator_helper_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- JSIteratorHelperData *it = p->u.iterator_helper_data;
- if (it) {
- JS_MarkValue(rt, it->obj, mark_func);
- JS_MarkValue(rt, it->func, mark_func);
- JS_MarkValue(rt, it->next, mark_func);
- JS_MarkValue(rt, it->inner, mark_func);
- }
- }
- static JSValue js_iterator_helper_next(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv,
- int *pdone, int magic)
- {
- JSIteratorHelperData *it;
- JSValue ret;
- *pdone = FALSE;
- it = JS_GetOpaque2(ctx, this_val, JS_CLASS_ITERATOR_HELPER);
- if (!it)
- return JS_EXCEPTION;
- if (it->executing)
- return JS_ThrowTypeError(ctx, "cannot invoke a running iterator");
- if (it->done) {
- *pdone = TRUE;
- return JS_UNDEFINED;
- }
- it->executing = 1;
- switch (it->kind) {
- case JS_ITERATOR_HELPER_KIND_DROP:
- {
- JSValue item, method;
- if (magic == GEN_MAGIC_NEXT) {
- method = JS_DupValue(ctx, it->next);
- } else {
- method = JS_GetProperty(ctx, it->obj, JS_ATOM_return);
- if (JS_IsException(method))
- goto fail;
- }
- while (it->count > 0) {
- it->count--;
- item = JS_IteratorNext(ctx, it->obj, method, 0, NULL, pdone);
- if (JS_IsException(item)) {
- JS_FreeValue(ctx, method);
- goto fail_no_close;
- }
- JS_FreeValue(ctx, item);
- if (magic == GEN_MAGIC_RETURN)
- *pdone = TRUE;
- if (*pdone) {
- JS_FreeValue(ctx, method);
- ret = JS_UNDEFINED;
- goto done;
- }
- }
- item = JS_IteratorNext(ctx, it->obj, method, 0, NULL, pdone);
- JS_FreeValue(ctx, method);
- if (JS_IsException(item))
- goto fail_no_close;
- ret = item;
- goto done;
- }
- break;
- case JS_ITERATOR_HELPER_KIND_FILTER:
- {
- JSValue item, method, selected, index_val;
- JSValueConst args[2];
- if (magic == GEN_MAGIC_NEXT) {
- method = JS_DupValue(ctx, it->next);
- } else {
- method = JS_GetProperty(ctx, it->obj, JS_ATOM_return);
- if (JS_IsException(method))
- goto fail;
- }
- filter_again:
- item = JS_IteratorNext(ctx, it->obj, method, 0, NULL, pdone);
- if (JS_IsException(item)) {
- JS_FreeValue(ctx, method);
- goto fail_no_close;
- }
- if (*pdone || magic == GEN_MAGIC_RETURN) {
- JS_FreeValue(ctx, method);
- ret = item;
- goto done;
- }
- index_val = JS_NewInt64(ctx, it->count++);
- args[0] = item;
- args[1] = index_val;
- selected = JS_Call(ctx, it->func, JS_UNDEFINED, countof(args), args);
- JS_FreeValue(ctx, index_val);
- if (JS_IsException(selected)) {
- JS_FreeValue(ctx, item);
- JS_FreeValue(ctx, method);
- goto fail;
- }
- if (JS_ToBoolFree(ctx, selected)) {
- JS_FreeValue(ctx, method);
- ret = item;
- goto done;
- }
- JS_FreeValue(ctx, item);
- goto filter_again;
- }
- break;
- case JS_ITERATOR_HELPER_KIND_FLAT_MAP:
- {
- JSValue item, method, index_val, iter;
- JSValueConst args[2];
- flat_map_again:
- if (JS_IsUndefined(it->inner)) {
- if (magic == GEN_MAGIC_NEXT) {
- method = JS_DupValue(ctx, it->next);
- } else {
- method = JS_GetProperty(ctx, it->obj, JS_ATOM_return);
- if (JS_IsException(method))
- goto fail;
- }
- item = JS_IteratorNext(ctx, it->obj, method, 0, NULL, pdone);
- JS_FreeValue(ctx, method);
- if (JS_IsException(item))
- goto fail_no_close;
- if (*pdone || magic == GEN_MAGIC_RETURN) {
- ret = item;
- goto done;
- }
- index_val = JS_NewInt64(ctx, it->count++);
- args[0] = item;
- args[1] = index_val;
- ret = JS_Call(ctx, it->func, JS_UNDEFINED, countof(args), args);
- JS_FreeValue(ctx, item);
- JS_FreeValue(ctx, index_val);
- if (JS_IsException(ret))
- goto fail;
- if (!JS_IsObject(ret)) {
- JS_FreeValue(ctx, ret);
- JS_ThrowTypeError(ctx, "not an object");
- goto fail;
- }
- method = JS_GetProperty(ctx, ret, JS_ATOM_Symbol_iterator);
- if (JS_IsException(method)) {
- JS_FreeValue(ctx, ret);
- goto fail;
- }
- if (JS_IsNull(method) || JS_IsUndefined(method)) {
- JS_FreeValue(ctx, method);
- iter = ret;
- } else {
- iter = JS_GetIterator2(ctx, ret, method);
- JS_FreeValue(ctx, method);
- JS_FreeValue(ctx, ret);
- if (JS_IsException(iter))
- goto fail;
- }
- it->inner = iter;
- }
- if (magic == GEN_MAGIC_NEXT)
- method = JS_GetProperty(ctx, it->inner, JS_ATOM_next);
- else
- method = JS_GetProperty(ctx, it->inner, JS_ATOM_return);
- if (JS_IsException(method)) {
- inner_fail:
- JS_IteratorClose(ctx, it->inner, FALSE);
- JS_FreeValue(ctx, it->inner);
- it->inner = JS_UNDEFINED;
- goto fail;
- }
- if (magic == GEN_MAGIC_RETURN && (JS_IsUndefined(method) || JS_IsNull(method))) {
- goto inner_end;
- } else {
- item = JS_IteratorNext(ctx, it->inner, method, 0, NULL, pdone);
- JS_FreeValue(ctx, method);
- if (JS_IsException(item))
- goto inner_fail;
- }
- if (*pdone) {
- inner_end:
- *pdone = FALSE; // The outer iterator must continue.
- JS_IteratorClose(ctx, it->inner, FALSE);
- JS_FreeValue(ctx, it->inner);
- it->inner = JS_UNDEFINED;
- goto flat_map_again;
- }
- ret = item;
- goto done;
- }
- break;
- case JS_ITERATOR_HELPER_KIND_MAP:
- {
- JSValue item, method, index_val;
- JSValueConst args[2];
- if (magic == GEN_MAGIC_NEXT) {
- method = JS_DupValue(ctx, it->next);
- } else {
- method = JS_GetProperty(ctx, it->obj, JS_ATOM_return);
- if (JS_IsException(method))
- goto fail;
- }
- item = JS_IteratorNext(ctx, it->obj, method, 0, NULL, pdone);
- JS_FreeValue(ctx, method);
- if (JS_IsException(item))
- goto fail_no_close;
- if (*pdone || magic == GEN_MAGIC_RETURN) {
- ret = item;
- goto done;
- }
- index_val = JS_NewInt64(ctx, it->count++);
- args[0] = item;
- args[1] = index_val;
- ret = JS_Call(ctx, it->func, JS_UNDEFINED, countof(args), args);
- JS_FreeValue(ctx, index_val);
- JS_FreeValue(ctx, item);
- if (JS_IsException(ret))
- goto fail;
- goto done;
- }
- break;
- case JS_ITERATOR_HELPER_KIND_TAKE:
- {
- JSValue item, method;
- if (it->count > 0) {
- if (magic == GEN_MAGIC_NEXT) {
- method = JS_DupValue(ctx, it->next);
- } else {
- method = JS_GetProperty(ctx, it->obj, JS_ATOM_return);
- if (JS_IsException(method))
- goto fail;
- }
- it->count--;
- item = JS_IteratorNext(ctx, it->obj, method, 0, NULL, pdone);
- JS_FreeValue(ctx, method);
- if (JS_IsException(item))
- goto fail_no_close;
- ret = item;
- goto done;
- }
- *pdone = TRUE;
- if (JS_IteratorClose(ctx, it->obj, FALSE))
- ret = JS_EXCEPTION;
- else
- ret = JS_UNDEFINED;
- goto done;
- }
- break;
- default:
- abort();
- }
- done:
- it->done = magic == GEN_MAGIC_NEXT ? *pdone : 1;
- it->executing = 0;
- return ret;
- fail:
- /* close the iterator object, preserving pending exception */
- JS_IteratorClose(ctx, it->obj, TRUE);
- fail_no_close:
- ret = JS_EXCEPTION;
- goto done;
- }
- static const JSCFunctionListEntry js_iterator_funcs[] = {
- JS_CFUNC_DEF("concat", 0, js_iterator_concat ),
- JS_CFUNC_DEF("from", 1, js_iterator_from ),
- };
- static const JSCFunctionListEntry js_iterator_proto_funcs[] = {
- JS_CFUNC_MAGIC_DEF("drop", 1, js_create_iterator_helper, JS_ITERATOR_HELPER_KIND_DROP ),
- JS_CFUNC_MAGIC_DEF("filter", 1, js_create_iterator_helper, JS_ITERATOR_HELPER_KIND_FILTER ),
- JS_CFUNC_MAGIC_DEF("flatMap", 1, js_create_iterator_helper, JS_ITERATOR_HELPER_KIND_FLAT_MAP ),
- JS_CFUNC_MAGIC_DEF("map", 1, js_create_iterator_helper, JS_ITERATOR_HELPER_KIND_MAP ),
- JS_CFUNC_MAGIC_DEF("take", 1, js_create_iterator_helper, JS_ITERATOR_HELPER_KIND_TAKE ),
- JS_CFUNC_MAGIC_DEF("every", 1, js_iterator_proto_func, JS_ITERATOR_HELPER_KIND_EVERY ),
- JS_CFUNC_MAGIC_DEF("find", 1, js_iterator_proto_func, JS_ITERATOR_HELPER_KIND_FIND),
- JS_CFUNC_MAGIC_DEF("forEach", 1, js_iterator_proto_func, JS_ITERATOR_HELPER_KIND_FOR_EACH ),
- JS_CFUNC_MAGIC_DEF("some", 1, js_iterator_proto_func, JS_ITERATOR_HELPER_KIND_SOME ),
- JS_CFUNC_DEF("reduce", 1, js_iterator_proto_reduce ),
- JS_CFUNC_DEF("toArray", 0, js_iterator_proto_toArray ),
- JS_CFUNC_DEF("[Symbol.iterator]", 0, js_iterator_proto_iterator ),
- JS_CGETSET_DEF("[Symbol.toStringTag]", js_iterator_proto_get_toStringTag, js_iterator_proto_set_toStringTag),
- };
- static const JSCFunctionListEntry js_iterator_helper_proto_funcs[] = {
- JS_ITERATOR_NEXT_DEF("next", 0, js_iterator_helper_next, GEN_MAGIC_NEXT ),
- JS_ITERATOR_NEXT_DEF("return", 0, js_iterator_helper_next, GEN_MAGIC_RETURN ),
- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Iterator Helper", JS_PROP_CONFIGURABLE ),
- };
- static const JSCFunctionListEntry js_array_unscopables_funcs[] = {
- JS_PROP_BOOL_DEF("at", TRUE, JS_PROP_C_W_E),
- JS_PROP_BOOL_DEF("copyWithin", TRUE, JS_PROP_C_W_E),
- JS_PROP_BOOL_DEF("entries", TRUE, JS_PROP_C_W_E),
- JS_PROP_BOOL_DEF("fill", TRUE, JS_PROP_C_W_E),
- JS_PROP_BOOL_DEF("find", TRUE, JS_PROP_C_W_E),
- JS_PROP_BOOL_DEF("findIndex", TRUE, JS_PROP_C_W_E),
- JS_PROP_BOOL_DEF("findLast", TRUE, JS_PROP_C_W_E),
- JS_PROP_BOOL_DEF("findLastIndex", TRUE, JS_PROP_C_W_E),
- JS_PROP_BOOL_DEF("flat", TRUE, JS_PROP_C_W_E),
- JS_PROP_BOOL_DEF("flatMap", TRUE, JS_PROP_C_W_E),
- JS_PROP_BOOL_DEF("includes", TRUE, JS_PROP_C_W_E),
- JS_PROP_BOOL_DEF("keys", TRUE, JS_PROP_C_W_E),
- JS_PROP_BOOL_DEF("toReversed", TRUE, JS_PROP_C_W_E),
- JS_PROP_BOOL_DEF("toSorted", TRUE, JS_PROP_C_W_E),
- JS_PROP_BOOL_DEF("toSpliced", TRUE, JS_PROP_C_W_E),
- JS_PROP_BOOL_DEF("values", TRUE, JS_PROP_C_W_E),
- };
- static const JSCFunctionListEntry js_array_proto_funcs[] = {
- JS_CFUNC_DEF("at", 1, js_array_at ),
- JS_CFUNC_DEF("with", 2, js_array_with ),
- JS_CFUNC_DEF("concat", 1, js_array_concat ),
- JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every ),
- JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some ),
- JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, special_forEach ),
- JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, special_map ),
- JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, special_filter ),
- JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce ),
- JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight ),
- JS_CFUNC_DEF("fill", 1, js_array_fill ),
- JS_CFUNC_MAGIC_DEF("find", 1, js_array_find, ArrayFind ),
- JS_CFUNC_MAGIC_DEF("findIndex", 1, js_array_find, ArrayFindIndex ),
- JS_CFUNC_MAGIC_DEF("findLast", 1, js_array_find, ArrayFindLast ),
- JS_CFUNC_MAGIC_DEF("findLastIndex", 1, js_array_find, ArrayFindLastIndex ),
- JS_CFUNC_DEF("indexOf", 1, js_array_indexOf ),
- JS_CFUNC_DEF("lastIndexOf", 1, js_array_lastIndexOf ),
- JS_CFUNC_DEF("includes", 1, js_array_includes ),
- JS_CFUNC_MAGIC_DEF("join", 1, js_array_join, 0 ),
- JS_CFUNC_DEF("toString", 0, js_array_toString ),
- JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_array_join, 1 ),
- JS_CFUNC_MAGIC_DEF("pop", 0, js_array_pop, 0 ),
- JS_CFUNC_MAGIC_DEF("push", 1, js_array_push, 0 ),
- JS_CFUNC_MAGIC_DEF("shift", 0, js_array_pop, 1 ),
- JS_CFUNC_MAGIC_DEF("unshift", 1, js_array_push, 1 ),
- JS_CFUNC_DEF("reverse", 0, js_array_reverse ),
- JS_CFUNC_DEF("toReversed", 0, js_array_toReversed ),
- JS_CFUNC_DEF("sort", 1, js_array_sort ),
- JS_CFUNC_DEF("toSorted", 1, js_array_toSorted ),
- JS_CFUNC_DEF("slice", 2, js_array_slice ),
- JS_CFUNC_DEF("splice", 2, js_array_splice ),
- JS_CFUNC_DEF("toSpliced", 2, js_array_toSpliced ),
- JS_CFUNC_DEF("copyWithin", 2, js_array_copyWithin ),
- JS_CFUNC_MAGIC_DEF("flatMap", 1, js_array_flatten, 1 ),
- JS_CFUNC_MAGIC_DEF("flat", 0, js_array_flatten, 0 ),
- JS_CFUNC_MAGIC_DEF("values", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE ),
- JS_ALIAS_DEF("[Symbol.iterator]", "values" ),
- JS_CFUNC_MAGIC_DEF("keys", 0, js_create_array_iterator, JS_ITERATOR_KIND_KEY ),
- JS_CFUNC_MAGIC_DEF("entries", 0, js_create_array_iterator, JS_ITERATOR_KIND_KEY_AND_VALUE ),
- JS_OBJECT_DEF("[Symbol.unscopables]", js_array_unscopables_funcs, countof(js_array_unscopables_funcs), JS_PROP_CONFIGURABLE ),
- };
- static const JSCFunctionListEntry js_array_iterator_proto_funcs[] = {
- JS_ITERATOR_NEXT_DEF("next", 0, js_array_iterator_next, 0 ),
- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Array Iterator", JS_PROP_CONFIGURABLE ),
- };
- /* Number */
- static JSValue js_number_constructor(JSContext *ctx, JSValueConst new_target,
- int argc, JSValueConst *argv)
- {
- JSValue val, obj;
- if (argc == 0) {
- val = JS_NewInt32(ctx, 0);
- } else {
- val = JS_ToNumeric(ctx, argv[0]);
- if (JS_IsException(val))
- return val;
- switch(JS_VALUE_GET_TAG(val)) {
- case JS_TAG_SHORT_BIG_INT:
- val = JS_NewInt64(ctx, JS_VALUE_GET_SHORT_BIG_INT(val));
- if (JS_IsException(val))
- return val;
- break;
- case JS_TAG_BIG_INT:
- {
- JSBigInt *p = JS_VALUE_GET_PTR(val);
- double d;
- d = js_bigint_to_float64(ctx, p);
- JS_FreeValue(ctx, val);
- val = JS_NewFloat64(ctx, d);
- }
- break;
- default:
- break;
- }
- }
- if (!JS_IsUndefined(new_target)) {
- obj = js_create_from_ctor(ctx, new_target, JS_CLASS_NUMBER);
- if (!JS_IsException(obj))
- JS_SetObjectData(ctx, obj, val);
- return obj;
- } else {
- return val;
- }
- }
- #if 0
- static JSValue js_number___toInteger(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- return JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[0]));
- }
- static JSValue js_number___toLength(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- int64_t v;
- if (JS_ToLengthFree(ctx, &v, JS_DupValue(ctx, argv[0])))
- return JS_EXCEPTION;
- return JS_NewInt64(ctx, v);
- }
- #endif
- static JSValue js_number_isNaN(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- if (!JS_IsNumber(argv[0]))
- return JS_FALSE;
- return js_global_isNaN(ctx, this_val, argc, argv);
- }
- static JSValue js_number_isFinite(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- if (!JS_IsNumber(argv[0]))
- return JS_FALSE;
- return js_global_isFinite(ctx, this_val, argc, argv);
- }
- static JSValue js_number_isInteger(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- int ret;
- ret = JS_NumberIsInteger(ctx, argv[0]);
- if (ret < 0)
- return JS_EXCEPTION;
- else
- return JS_NewBool(ctx, ret);
- }
- static JSValue js_number_isSafeInteger(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- double d;
- if (!JS_IsNumber(argv[0]))
- return JS_FALSE;
- if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))
- return JS_EXCEPTION;
- return JS_NewBool(ctx, is_safe_integer(d));
- }
- static const JSCFunctionListEntry js_number_funcs[] = {
- /* global ParseInt and parseFloat should be defined already or delayed */
- JS_ALIAS_BASE_DEF("parseInt", "parseInt", 0 ),
- JS_ALIAS_BASE_DEF("parseFloat", "parseFloat", 0 ),
- JS_CFUNC_DEF("isNaN", 1, js_number_isNaN ),
- JS_CFUNC_DEF("isFinite", 1, js_number_isFinite ),
- JS_CFUNC_DEF("isInteger", 1, js_number_isInteger ),
- JS_CFUNC_DEF("isSafeInteger", 1, js_number_isSafeInteger ),
- JS_PROP_DOUBLE_DEF("MAX_VALUE", 1.7976931348623157e+308, 0 ),
- JS_PROP_DOUBLE_DEF("MIN_VALUE", 5e-324, 0 ),
- JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ),
- JS_PROP_DOUBLE_DEF("NEGATIVE_INFINITY", -INFINITY, 0 ),
- JS_PROP_DOUBLE_DEF("POSITIVE_INFINITY", INFINITY, 0 ),
- JS_PROP_DOUBLE_DEF("EPSILON", 2.220446049250313e-16, 0 ), /* ES6 */
- JS_PROP_DOUBLE_DEF("MAX_SAFE_INTEGER", 9007199254740991.0, 0 ), /* ES6 */
- JS_PROP_DOUBLE_DEF("MIN_SAFE_INTEGER", -9007199254740991.0, 0 ), /* ES6 */
- //JS_CFUNC_DEF("__toInteger", 1, js_number___toInteger ),
- //JS_CFUNC_DEF("__toLength", 1, js_number___toLength ),
- };
- static JSValue js_thisNumberValue(JSContext *ctx, JSValueConst this_val)
- {
- if (JS_IsNumber(this_val))
- return JS_DupValue(ctx, this_val);
- if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
- JSObject *p = JS_VALUE_GET_OBJ(this_val);
- if (p->class_id == JS_CLASS_NUMBER) {
- if (JS_IsNumber(p->u.object_data))
- return JS_DupValue(ctx, p->u.object_data);
- }
- }
- return JS_ThrowTypeError(ctx, "not a number");
- }
- static JSValue js_number_valueOf(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- return js_thisNumberValue(ctx, this_val);
- }
- static int js_get_radix(JSContext *ctx, JSValueConst val)
- {
- int radix;
- if (JS_ToInt32Sat(ctx, &radix, val))
- return -1;
- if (radix < 2 || radix > 36) {
- JS_ThrowRangeError(ctx, "radix must be between 2 and 36");
- return -1;
- }
- return radix;
- }
- static JSValue js_number_toString(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
- {
- JSValue val;
- int base, flags;
- double d;
- val = js_thisNumberValue(ctx, this_val);
- if (JS_IsException(val))
- return val;
- if (magic || JS_IsUndefined(argv[0])) {
- base = 10;
- } else {
- base = js_get_radix(ctx, argv[0]);
- if (base < 0)
- goto fail;
- }
- if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) {
- char buf1[70];
- int len;
- len = i64toa_radix(buf1, JS_VALUE_GET_INT(val), base);
- return js_new_string8_len(ctx, buf1, len);
- }
- if (JS_ToFloat64Free(ctx, &d, val))
- return JS_EXCEPTION;
- flags = JS_DTOA_FORMAT_FREE;
- if (base != 10)
- flags |= JS_DTOA_EXP_DISABLED;
- return js_dtoa2(ctx, d, base, 0, flags);
- fail:
- JS_FreeValue(ctx, val);
- return JS_EXCEPTION;
- }
- static JSValue js_number_toFixed(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue val;
- int f, flags;
- double d;
- val = js_thisNumberValue(ctx, this_val);
- if (JS_IsException(val))
- return val;
- if (JS_ToFloat64Free(ctx, &d, val))
- return JS_EXCEPTION;
- if (JS_ToInt32Sat(ctx, &f, argv[0]))
- return JS_EXCEPTION;
- if (f < 0 || f > 100)
- return JS_ThrowRangeError(ctx, "invalid number of digits");
- if (fabs(d) >= 1e21)
- flags = JS_DTOA_FORMAT_FREE;
- else
- flags = JS_DTOA_FORMAT_FRAC;
- return js_dtoa2(ctx, d, 10, f, flags);
- }
- static JSValue js_number_toExponential(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue val;
- int f, flags;
- double d;
- val = js_thisNumberValue(ctx, this_val);
- if (JS_IsException(val))
- return val;
- if (JS_ToFloat64Free(ctx, &d, val))
- return JS_EXCEPTION;
- if (JS_ToInt32Sat(ctx, &f, argv[0]))
- return JS_EXCEPTION;
- if (!isfinite(d)) {
- return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d));
- }
- if (JS_IsUndefined(argv[0])) {
- flags = JS_DTOA_FORMAT_FREE;
- f = 0;
- } else {
- if (f < 0 || f > 100)
- return JS_ThrowRangeError(ctx, "invalid number of digits");
- f++;
- flags = JS_DTOA_FORMAT_FIXED;
- }
- return js_dtoa2(ctx, d, 10, f, flags | JS_DTOA_EXP_ENABLED);
- }
- static JSValue js_number_toPrecision(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue val;
- int p;
- double d;
- val = js_thisNumberValue(ctx, this_val);
- if (JS_IsException(val))
- return val;
- if (JS_ToFloat64Free(ctx, &d, val))
- return JS_EXCEPTION;
- if (JS_IsUndefined(argv[0]))
- goto to_string;
- if (JS_ToInt32Sat(ctx, &p, argv[0]))
- return JS_EXCEPTION;
- if (!isfinite(d)) {
- to_string:
- return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d));
- }
- if (p < 1 || p > 100)
- return JS_ThrowRangeError(ctx, "invalid number of digits");
- return js_dtoa2(ctx, d, 10, p, JS_DTOA_FORMAT_FIXED);
- }
- static const JSCFunctionListEntry js_number_proto_funcs[] = {
- JS_CFUNC_DEF("toExponential", 1, js_number_toExponential ),
- JS_CFUNC_DEF("toFixed", 1, js_number_toFixed ),
- JS_CFUNC_DEF("toPrecision", 1, js_number_toPrecision ),
- JS_CFUNC_MAGIC_DEF("toString", 1, js_number_toString, 0 ),
- JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_number_toString, 1 ),
- JS_CFUNC_DEF("valueOf", 0, js_number_valueOf ),
- };
- static JSValue js_parseInt(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- const char *str, *p;
- int radix, flags;
- JSValue ret;
- str = JS_ToCString(ctx, argv[0]);
- if (!str)
- return JS_EXCEPTION;
- if (JS_ToInt32(ctx, &radix, argv[1])) {
- JS_FreeCString(ctx, str);
- return JS_EXCEPTION;
- }
- if (radix != 0 && (radix < 2 || radix > 36)) {
- ret = JS_NAN;
- } else {
- p = str;
- p += skip_spaces(p);
- flags = ATOD_INT_ONLY | ATOD_ACCEPT_PREFIX_AFTER_SIGN;
- ret = js_atof(ctx, p, NULL, radix, flags);
- }
- JS_FreeCString(ctx, str);
- return ret;
- }
- static JSValue js_parseFloat(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- const char *str, *p;
- JSValue ret;
- str = JS_ToCString(ctx, argv[0]);
- if (!str)
- return JS_EXCEPTION;
- p = str;
- p += skip_spaces(p);
- ret = js_atof(ctx, p, NULL, 10, 0);
- JS_FreeCString(ctx, str);
- return ret;
- }
- /* Boolean */
- static JSValue js_boolean_constructor(JSContext *ctx, JSValueConst new_target,
- int argc, JSValueConst *argv)
- {
- JSValue val, obj;
- val = JS_NewBool(ctx, JS_ToBool(ctx, argv[0]));
- if (!JS_IsUndefined(new_target)) {
- obj = js_create_from_ctor(ctx, new_target, JS_CLASS_BOOLEAN);
- if (!JS_IsException(obj))
- JS_SetObjectData(ctx, obj, val);
- return obj;
- } else {
- return val;
- }
- }
- static JSValue js_thisBooleanValue(JSContext *ctx, JSValueConst this_val)
- {
- if (JS_VALUE_GET_TAG(this_val) == JS_TAG_BOOL)
- return JS_DupValue(ctx, this_val);
- if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
- JSObject *p = JS_VALUE_GET_OBJ(this_val);
- if (p->class_id == JS_CLASS_BOOLEAN) {
- if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_BOOL)
- return p->u.object_data;
- }
- }
- return JS_ThrowTypeError(ctx, "not a boolean");
- }
- static JSValue js_boolean_toString(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue val = js_thisBooleanValue(ctx, this_val);
- if (JS_IsException(val))
- return val;
- return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ?
- JS_ATOM_true : JS_ATOM_false);
- }
- static JSValue js_boolean_valueOf(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- return js_thisBooleanValue(ctx, this_val);
- }
- static const JSCFunctionListEntry js_boolean_proto_funcs[] = {
- JS_CFUNC_DEF("toString", 0, js_boolean_toString ),
- JS_CFUNC_DEF("valueOf", 0, js_boolean_valueOf ),
- };
- /* String */
- static int js_string_get_own_property(JSContext *ctx,
- JSPropertyDescriptor *desc,
- JSValueConst obj, JSAtom prop)
- {
- JSObject *p;
- JSString *p1;
- uint32_t idx, ch;
- /* This is a class exotic method: obj class_id is JS_CLASS_STRING */
- if (__JS_AtomIsTaggedInt(prop)) {
- p = JS_VALUE_GET_OBJ(obj);
- if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) {
- p1 = JS_VALUE_GET_STRING(p->u.object_data);
- idx = __JS_AtomToUInt32(prop);
- if (idx < p1->len) {
- if (desc) {
- ch = string_get(p1, idx);
- desc->flags = JS_PROP_ENUMERABLE;
- desc->value = js_new_string_char(ctx, ch);
- desc->getter = JS_UNDEFINED;
- desc->setter = JS_UNDEFINED;
- }
- return TRUE;
- }
- }
- }
- return FALSE;
- }
- static int js_string_define_own_property(JSContext *ctx,
- JSValueConst this_obj,
- JSAtom prop, JSValueConst val,
- JSValueConst getter,
- JSValueConst setter, int flags)
- {
- uint32_t idx;
- JSObject *p;
- JSString *p1, *p2;
- if (__JS_AtomIsTaggedInt(prop)) {
- idx = __JS_AtomToUInt32(prop);
- p = JS_VALUE_GET_OBJ(this_obj);
- if (JS_VALUE_GET_TAG(p->u.object_data) != JS_TAG_STRING)
- goto def;
- p1 = JS_VALUE_GET_STRING(p->u.object_data);
- if (idx >= p1->len)
- goto def;
- if (!check_define_prop_flags(JS_PROP_ENUMERABLE, flags))
- goto fail;
- /* check that the same value is configured */
- if (flags & JS_PROP_HAS_VALUE) {
- if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING)
- goto fail;
- p2 = JS_VALUE_GET_STRING(val);
- if (p2->len != 1)
- goto fail;
- if (string_get(p1, idx) != string_get(p2, 0)) {
- fail:
- return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable");
- }
- }
- return TRUE;
- } else {
- def:
- return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter,
- flags | JS_PROP_NO_EXOTIC);
- }
- }
- static int js_string_delete_property(JSContext *ctx,
- JSValueConst obj, JSAtom prop)
- {
- uint32_t idx;
- if (__JS_AtomIsTaggedInt(prop)) {
- idx = __JS_AtomToUInt32(prop);
- if (idx < js_string_obj_get_length(ctx, obj)) {
- return FALSE;
- }
- }
- return TRUE;
- }
- static const JSClassExoticMethods js_string_exotic_methods = {
- .get_own_property = js_string_get_own_property,
- .define_own_property = js_string_define_own_property,
- .delete_property = js_string_delete_property,
- };
- static JSValue js_string_constructor(JSContext *ctx, JSValueConst new_target,
- int argc, JSValueConst *argv)
- {
- JSValue val, obj;
- if (argc == 0) {
- val = JS_AtomToString(ctx, JS_ATOM_empty_string);
- } else {
- if (JS_IsUndefined(new_target) && JS_IsSymbol(argv[0])) {
- JSAtomStruct *p = JS_VALUE_GET_PTR(argv[0]);
- val = JS_ConcatString3(ctx, "Symbol(", JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p)), ")");
- } else {
- val = JS_ToString(ctx, argv[0]);
- }
- if (JS_IsException(val))
- return val;
- }
- if (!JS_IsUndefined(new_target)) {
- JSString *p1 = JS_VALUE_GET_STRING(val);
- obj = js_create_from_ctor(ctx, new_target, JS_CLASS_STRING);
- if (JS_IsException(obj)) {
- JS_FreeValue(ctx, val);
- } else {
- JS_SetObjectData(ctx, obj, val);
- JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0);
- }
- return obj;
- } else {
- return val;
- }
- }
- static JSValue js_thisStringValue(JSContext *ctx, JSValueConst this_val)
- {
- if (JS_VALUE_GET_TAG(this_val) == JS_TAG_STRING ||
- JS_VALUE_GET_TAG(this_val) == JS_TAG_STRING_ROPE)
- return JS_DupValue(ctx, this_val);
- if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
- JSObject *p = JS_VALUE_GET_OBJ(this_val);
- if (p->class_id == JS_CLASS_STRING) {
- if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING)
- return JS_DupValue(ctx, p->u.object_data);
- }
- }
- return JS_ThrowTypeError(ctx, "not a string");
- }
- static JSValue js_string_fromCharCode(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- int i;
- StringBuffer b_s, *b = &b_s;
- string_buffer_init(ctx, b, argc);
- for(i = 0; i < argc; i++) {
- int32_t c;
- if (JS_ToInt32(ctx, &c, argv[i]) || string_buffer_putc16(b, c & 0xffff)) {
- string_buffer_free(b);
- return JS_EXCEPTION;
- }
- }
- return string_buffer_end(b);
- }
- static JSValue js_string_fromCodePoint(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- double d;
- int i, c;
- StringBuffer b_s, *b = &b_s;
- /* XXX: could pre-compute string length if all arguments are JS_TAG_INT */
- if (string_buffer_init(ctx, b, argc))
- goto fail;
- for(i = 0; i < argc; i++) {
- if (JS_VALUE_GET_TAG(argv[i]) == JS_TAG_INT) {
- c = JS_VALUE_GET_INT(argv[i]);
- if (c < 0 || c > 0x10ffff)
- goto range_error;
- } else {
- if (JS_ToFloat64(ctx, &d, argv[i]))
- goto fail;
- if (isnan(d) || d < 0 || d > 0x10ffff || (c = (int)d) != d)
- goto range_error;
- }
- if (string_buffer_putc(b, c))
- goto fail;
- }
- return string_buffer_end(b);
- range_error:
- JS_ThrowRangeError(ctx, "invalid code point");
- fail:
- string_buffer_free(b);
- return JS_EXCEPTION;
- }
- static JSValue js_string_raw(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- // raw(temp,...a)
- JSValue cooked, val, raw;
- StringBuffer b_s, *b = &b_s;
- int64_t i, n;
- string_buffer_init(ctx, b, 0);
- raw = JS_UNDEFINED;
- cooked = JS_ToObject(ctx, argv[0]);
- if (JS_IsException(cooked))
- goto exception;
- raw = JS_ToObjectFree(ctx, JS_GetProperty(ctx, cooked, JS_ATOM_raw));
- if (JS_IsException(raw))
- goto exception;
- if (js_get_length64(ctx, &n, raw) < 0)
- goto exception;
- for (i = 0; i < n; i++) {
- val = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, raw, i));
- if (JS_IsException(val))
- goto exception;
- string_buffer_concat_value_free(b, val);
- if (i < n - 1 && i + 1 < argc) {
- if (string_buffer_concat_value(b, argv[i + 1]))
- goto exception;
- }
- }
- JS_FreeValue(ctx, cooked);
- JS_FreeValue(ctx, raw);
- return string_buffer_end(b);
- exception:
- JS_FreeValue(ctx, cooked);
- JS_FreeValue(ctx, raw);
- string_buffer_free(b);
- return JS_EXCEPTION;
- }
- /* only used in test262 */
- JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- uint32_t start, end, i, n;
- StringBuffer b_s, *b = &b_s;
- if (JS_ToUint32(ctx, &start, argv[0]) ||
- JS_ToUint32(ctx, &end, argv[1]))
- return JS_EXCEPTION;
- end = min_uint32(end, 0x10ffff + 1);
- if (start > end) {
- start = end;
- }
- n = end - start;
- if (end > 0x10000) {
- n += end - max_uint32(start, 0x10000);
- }
- if (string_buffer_init2(ctx, b, n, end >= 0x100))
- return JS_EXCEPTION;
- for(i = start; i < end; i++) {
- string_buffer_putc(b, i);
- }
- return string_buffer_end(b);
- }
- #if 0
- static JSValue js_string___isSpace(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- int c;
- if (JS_ToInt32(ctx, &c, argv[0]))
- return JS_EXCEPTION;
- return JS_NewBool(ctx, lre_is_space(c));
- }
- #endif
- static JSValue js_string_charCodeAt(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue val, ret;
- JSString *p;
- int idx, c;
- val = JS_ToStringCheckObject(ctx, this_val);
- if (JS_IsException(val))
- return val;
- p = JS_VALUE_GET_STRING(val);
- if (JS_ToInt32Sat(ctx, &idx, argv[0])) {
- JS_FreeValue(ctx, val);
- return JS_EXCEPTION;
- }
- if (idx < 0 || idx >= p->len) {
- ret = JS_NAN;
- } else {
- c = string_get(p, idx);
- ret = JS_NewInt32(ctx, c);
- }
- JS_FreeValue(ctx, val);
- return ret;
- }
- static JSValue js_string_charAt(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int is_at)
- {
- JSValue val, ret;
- JSString *p;
- int idx, c;
- val = JS_ToStringCheckObject(ctx, this_val);
- if (JS_IsException(val))
- return val;
- p = JS_VALUE_GET_STRING(val);
- if (JS_ToInt32Sat(ctx, &idx, argv[0])) {
- JS_FreeValue(ctx, val);
- return JS_EXCEPTION;
- }
- if (idx < 0 && is_at)
- idx += p->len;
- if (idx < 0 || idx >= p->len) {
- if (is_at)
- ret = JS_UNDEFINED;
- else
- ret = JS_AtomToString(ctx, JS_ATOM_empty_string);
- } else {
- c = string_get(p, idx);
- ret = js_new_string_char(ctx, c);
- }
- JS_FreeValue(ctx, val);
- return ret;
- }
- static JSValue js_string_codePointAt(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue val, ret;
- JSString *p;
- int idx, c;
- val = JS_ToStringCheckObject(ctx, this_val);
- if (JS_IsException(val))
- return val;
- p = JS_VALUE_GET_STRING(val);
- if (JS_ToInt32Sat(ctx, &idx, argv[0])) {
- JS_FreeValue(ctx, val);
- return JS_EXCEPTION;
- }
- if (idx < 0 || idx >= p->len) {
- ret = JS_UNDEFINED;
- } else {
- c = string_getc(p, &idx);
- ret = JS_NewInt32(ctx, c);
- }
- JS_FreeValue(ctx, val);
- return ret;
- }
- static JSValue js_string_concat(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue r;
- int i;
- /* XXX: Use more efficient method */
- /* XXX: This method is OK if r has a single refcount */
- /* XXX: should use string_buffer? */
- r = JS_ToStringCheckObject(ctx, this_val);
- for (i = 0; i < argc; i++) {
- if (JS_IsException(r))
- break;
- r = JS_ConcatString(ctx, r, JS_DupValue(ctx, argv[i]));
- }
- return r;
- }
- static int string_cmp(JSString *p1, JSString *p2, int x1, int x2, int len)
- {
- int i, c1, c2;
- for (i = 0; i < len; i++) {
- if ((c1 = string_get(p1, x1 + i)) != (c2 = string_get(p2, x2 + i)))
- return c1 - c2;
- }
- return 0;
- }
- static int string_indexof_char(JSString *p, int c, int from)
- {
- /* assuming 0 <= from <= p->len */
- int i, len = p->len;
- if (p->is_wide_char) {
- for (i = from; i < len; i++) {
- if (p->u.str16[i] == c)
- return i;
- }
- } else {
- if ((c & ~0xff) == 0) {
- for (i = from; i < len; i++) {
- if (p->u.str8[i] == (uint8_t)c)
- return i;
- }
- }
- }
- return -1;
- }
- static int string_indexof(JSString *p1, JSString *p2, int from)
- {
- /* assuming 0 <= from <= p1->len */
- int c, i, j, len1 = p1->len, len2 = p2->len;
- if (len2 == 0)
- return from;
- for (i = from, c = string_get(p2, 0); i + len2 <= len1; i = j + 1) {
- j = string_indexof_char(p1, c, i);
- if (j < 0 || j + len2 > len1)
- break;
- if (!string_cmp(p1, p2, j + 1, 1, len2 - 1))
- return j;
- }
- return -1;
- }
- static int64_t string_advance_index(JSString *p, int64_t index, BOOL unicode)
- {
- if (!unicode || index >= p->len || !p->is_wide_char) {
- index++;
- } else {
- int index32 = (int)index;
- string_getc(p, &index32);
- index = index32;
- }
- return index;
- }
- /* return the position of the first invalid character in the string or
- -1 if none */
- static int js_string_find_invalid_codepoint(JSString *p)
- {
- int i;
- if (!p->is_wide_char)
- return -1;
- for(i = 0; i < p->len; i++) {
- uint32_t c = p->u.str16[i];
- if (is_surrogate(c)) {
- if (is_hi_surrogate(c) && (i + 1) < p->len
- && is_lo_surrogate(p->u.str16[i + 1])) {
- i++;
- } else {
- return i;
- }
- }
- }
- return -1;
- }
- static JSValue js_string_isWellFormed(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue str;
- JSString *p;
- BOOL ret;
- str = JS_ToStringCheckObject(ctx, this_val);
- if (JS_IsException(str))
- return JS_EXCEPTION;
- p = JS_VALUE_GET_STRING(str);
- ret = (js_string_find_invalid_codepoint(p) < 0);
- JS_FreeValue(ctx, str);
- return JS_NewBool(ctx, ret);
- }
- static JSValue js_string_toWellFormed(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue str, ret;
- JSString *p;
- int i;
- str = JS_ToStringCheckObject(ctx, this_val);
- if (JS_IsException(str))
- return JS_EXCEPTION;
- p = JS_VALUE_GET_STRING(str);
- /* avoid reallocating the string if it is well-formed */
- i = js_string_find_invalid_codepoint(p);
- if (i < 0)
- return str;
- ret = js_new_string16_len(ctx, p->u.str16, p->len);
- JS_FreeValue(ctx, str);
- if (JS_IsException(ret))
- return JS_EXCEPTION;
- p = JS_VALUE_GET_STRING(ret);
- for (; i < p->len; i++) {
- uint32_t c = p->u.str16[i];
- if (is_surrogate(c)) {
- if (is_hi_surrogate(c) && (i + 1) < p->len
- && is_lo_surrogate(p->u.str16[i + 1])) {
- i++;
- } else {
- p->u.str16[i] = 0xFFFD;
- }
- }
- }
- return ret;
- }
- static JSValue js_string_indexOf(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int lastIndexOf)
- {
- JSValue str, v;
- int i, len, v_len, pos, start, stop, ret, inc;
- JSString *p;
- JSString *p1;
- str = JS_ToStringCheckObject(ctx, this_val);
- if (JS_IsException(str))
- return str;
- v = JS_ToString(ctx, argv[0]);
- if (JS_IsException(v))
- goto fail;
- p = JS_VALUE_GET_STRING(str);
- p1 = JS_VALUE_GET_STRING(v);
- len = p->len;
- v_len = p1->len;
- if (lastIndexOf) {
- pos = len - v_len;
- if (argc > 1) {
- double d;
- if (JS_ToFloat64(ctx, &d, argv[1]))
- goto fail;
- if (!isnan(d)) {
- if (d <= 0)
- pos = 0;
- else if (d < pos)
- pos = d;
- }
- }
- start = pos;
- stop = 0;
- inc = -1;
- } else {
- pos = 0;
- if (argc > 1) {
- if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0))
- goto fail;
- }
- start = pos;
- stop = len - v_len;
- inc = 1;
- }
- ret = -1;
- if (len >= v_len && inc * (stop - start) >= 0) {
- for (i = start;; i += inc) {
- if (!string_cmp(p, p1, i, 0, v_len)) {
- ret = i;
- break;
- }
- if (i == stop)
- break;
- }
- }
- JS_FreeValue(ctx, str);
- JS_FreeValue(ctx, v);
- return JS_NewInt32(ctx, ret);
- fail:
- JS_FreeValue(ctx, str);
- JS_FreeValue(ctx, v);
- return JS_EXCEPTION;
- }
- /* return < 0 if exception or TRUE/FALSE */
- static int js_is_regexp(JSContext *ctx, JSValueConst obj);
- static JSValue js_string_includes(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
- {
- JSValue str, v = JS_UNDEFINED;
- int i, len, v_len, pos, start, stop, ret;
- JSString *p;
- JSString *p1;
- str = JS_ToStringCheckObject(ctx, this_val);
- if (JS_IsException(str))
- return str;
- ret = js_is_regexp(ctx, argv[0]);
- if (ret) {
- if (ret > 0)
- JS_ThrowTypeError(ctx, "regexp not supported");
- goto fail;
- }
- v = JS_ToString(ctx, argv[0]);
- if (JS_IsException(v))
- goto fail;
- p = JS_VALUE_GET_STRING(str);
- p1 = JS_VALUE_GET_STRING(v);
- len = p->len;
- v_len = p1->len;
- pos = (magic == 2) ? len : 0;
- if (argc > 1 && !JS_IsUndefined(argv[1])) {
- if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0))
- goto fail;
- }
- len -= v_len;
- ret = 0;
- if (magic == 0) {
- start = pos;
- stop = len;
- } else {
- if (magic == 1) {
- if (pos > len)
- goto done;
- } else {
- pos -= v_len;
- }
- start = stop = pos;
- }
- if (start >= 0 && start <= stop) {
- for (i = start;; i++) {
- if (!string_cmp(p, p1, i, 0, v_len)) {
- ret = 1;
- break;
- }
- if (i == stop)
- break;
- }
- }
- done:
- JS_FreeValue(ctx, str);
- JS_FreeValue(ctx, v);
- return JS_NewBool(ctx, ret);
- fail:
- JS_FreeValue(ctx, str);
- JS_FreeValue(ctx, v);
- return JS_EXCEPTION;
- }
- static int check_regexp_g_flag(JSContext *ctx, JSValueConst regexp)
- {
- int ret;
- JSValue flags;
- ret = js_is_regexp(ctx, regexp);
- if (ret < 0)
- return -1;
- if (ret) {
- flags = JS_GetProperty(ctx, regexp, JS_ATOM_flags);
- if (JS_IsException(flags))
- return -1;
- if (JS_IsUndefined(flags) || JS_IsNull(flags)) {
- JS_ThrowTypeError(ctx, "cannot convert to object");
- return -1;
- }
- flags = JS_ToStringFree(ctx, flags);
- if (JS_IsException(flags))
- return -1;
- ret = string_indexof_char(JS_VALUE_GET_STRING(flags), 'g', 0);
- JS_FreeValue(ctx, flags);
- if (ret < 0) {
- JS_ThrowTypeError(ctx, "regexp must have the 'g' flag");
- return -1;
- }
- }
- return 0;
- }
- static JSValue js_string_match(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int atom)
- {
- // match(rx), search(rx), matchAll(rx)
- // atom is JS_ATOM_Symbol_match, JS_ATOM_Symbol_search, or JS_ATOM_Symbol_matchAll
- JSValueConst O = this_val, regexp = argv[0], args[2];
- JSValue matcher, S, rx, result, str;
- int args_len;
- if (JS_IsUndefined(O) || JS_IsNull(O))
- return JS_ThrowTypeError(ctx, "cannot convert to object");
- if (JS_IsObject(regexp)) {
- matcher = JS_GetProperty(ctx, regexp, atom);
- if (JS_IsException(matcher))
- return JS_EXCEPTION;
- if (atom == JS_ATOM_Symbol_matchAll) {
- if (check_regexp_g_flag(ctx, regexp) < 0) {
- JS_FreeValue(ctx, matcher);
- return JS_EXCEPTION;
- }
- }
- if (!JS_IsUndefined(matcher) && !JS_IsNull(matcher)) {
- return JS_CallFree(ctx, matcher, regexp, 1, &O);
- }
- }
- S = JS_ToString(ctx, O);
- if (JS_IsException(S))
- return JS_EXCEPTION;
- args_len = 1;
- args[0] = regexp;
- str = JS_UNDEFINED;
- if (atom == JS_ATOM_Symbol_matchAll) {
- str = js_new_string8(ctx, "g");
- if (JS_IsException(str))
- goto fail;
- args[args_len++] = (JSValueConst)str;
- }
- rx = JS_CallConstructor(ctx, ctx->regexp_ctor, args_len, args);
- JS_FreeValue(ctx, str);
- if (JS_IsException(rx)) {
- fail:
- JS_FreeValue(ctx, S);
- return JS_EXCEPTION;
- }
- result = JS_InvokeFree(ctx, rx, atom, 1, (JSValueConst *)&S);
- JS_FreeValue(ctx, S);
- return result;
- }
- /* if captures != NULL, captures_val and matched are ignored. Otherwise,
- captures_len is ignored */
- static int js_string_GetSubstitution(JSContext *ctx,
- StringBuffer *b,
- JSValueConst matched,
- JSString *sp,
- uint32_t position,
- JSValueConst captures_val,
- JSValueConst namedCaptures,
- JSValueConst rep,
- uint8_t **captures,
- uint32_t captures_len)
- {
- JSValue capture, name, s;
- uint32_t len, matched_len;
- int i, j, j0, k, k1, shift;
- int c, c1;
- JSString *rp;
- if (JS_VALUE_GET_TAG(rep) != JS_TAG_STRING) {
- JS_ThrowTypeError(ctx, "not a string");
- goto exception;
- }
- shift = sp->is_wide_char;
- rp = JS_VALUE_GET_STRING(rep);
- if (captures) {
- matched_len = (captures[1] - captures[0]) >> shift;
- } else {
- captures_len = 0;
- if (!JS_IsUndefined(captures_val)) {
- if (js_get_length32(ctx, &captures_len, captures_val))
- goto exception;
- }
- if (js_get_length32(ctx, &matched_len, matched))
- goto exception;
- }
- len = rp->len;
- i = 0;
- for(;;) {
- j = string_indexof_char(rp, '$', i);
- if (j < 0 || j + 1 >= len)
- break;
- string_buffer_concat(b, rp, i, j);
- j0 = j++;
- c = string_get(rp, j++);
- if (c == '$') {
- string_buffer_putc8(b, '$');
- } else if (c == '&') {
- if (captures) {
- string_buffer_concat(b, sp, position, position + matched_len);
- } else {
- if (string_buffer_concat_value(b, matched))
- goto exception;
- }
- } else if (c == '`') {
- string_buffer_concat(b, sp, 0, position);
- } else if (c == '\'') {
- string_buffer_concat(b, sp, position + matched_len, sp->len);
- } else if (c >= '0' && c <= '9') {
- k = c - '0';
- if (j < len) {
- c1 = string_get(rp, j);
- if (c1 >= '0' && c1 <= '9') {
- /* This behavior is specified in ES6 and refined in ECMA 2019 */
- /* ECMA 2019 does not have the extra test, but
- Test262 S15.5.4.11_A3_T1..3 require this behavior */
- k1 = k * 10 + c1 - '0';
- if (k1 >= 1 && k1 < captures_len) {
- k = k1;
- j++;
- }
- }
- }
- if (k >= 1 && k < captures_len) {
- if (captures) {
- int start, end;
- if (captures[2 * k] && captures[2 * k + 1]) {
- start = (captures[2 * k] - sp->u.str8) >> shift;
- end = (captures[2 * k + 1] - sp->u.str8) >> shift;
- string_buffer_concat(b, sp, start, end);
- }
- } else {
- s = JS_GetPropertyInt64(ctx, captures_val, k);
- if (JS_IsException(s))
- goto exception;
- if (!JS_IsUndefined(s)) {
- if (string_buffer_concat_value_free(b, s))
- goto exception;
- }
- }
- } else {
- goto norep;
- }
- } else if (c == '<' && !JS_IsUndefined(namedCaptures)) {
- k = string_indexof_char(rp, '>', j);
- if (k < 0)
- goto norep;
- name = js_sub_string(ctx, rp, j, k);
- if (JS_IsException(name))
- goto exception;
- capture = JS_GetPropertyValue(ctx, namedCaptures, name);
- if (JS_IsException(capture))
- goto exception;
- if (!JS_IsUndefined(capture)) {
- if (string_buffer_concat_value_free(b, capture))
- goto exception;
- }
- j = k + 1;
- } else {
- norep:
- string_buffer_concat(b, rp, j0, j);
- }
- i = j;
- }
- string_buffer_concat(b, rp, i, rp->len);
- return 0;
- exception:
- return -1;
- }
- static JSValue js_string_replace(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv,
- int is_replaceAll)
- {
- // replace(rx, rep)
- JSValueConst O = this_val, searchValue = argv[0], replaceValue = argv[1];
- JSValueConst args[3];
- JSValue str, search_str, replaceValue_str, repl_str;
- JSString *sp, *searchp;
- StringBuffer b_s, *b = &b_s;
- int pos, functionalReplace, endOfLastMatch;
- BOOL is_first;
- if (JS_IsUndefined(O) || JS_IsNull(O))
- return JS_ThrowTypeError(ctx, "cannot convert to object");
- search_str = JS_UNDEFINED;
- replaceValue_str = JS_UNDEFINED;
- repl_str = JS_UNDEFINED;
- if (JS_IsObject(searchValue)) {
- JSValue replacer;
- if (is_replaceAll) {
- if (check_regexp_g_flag(ctx, searchValue) < 0)
- return JS_EXCEPTION;
- }
- replacer = JS_GetProperty(ctx, searchValue, JS_ATOM_Symbol_replace);
- if (JS_IsException(replacer))
- return JS_EXCEPTION;
- if (!JS_IsUndefined(replacer) && !JS_IsNull(replacer)) {
- args[0] = O;
- args[1] = replaceValue;
- return JS_CallFree(ctx, replacer, searchValue, 2, args);
- }
- }
- string_buffer_init(ctx, b, 0);
- str = JS_ToString(ctx, O);
- if (JS_IsException(str))
- goto exception;
- search_str = JS_ToString(ctx, searchValue);
- if (JS_IsException(search_str))
- goto exception;
- functionalReplace = JS_IsFunction(ctx, replaceValue);
- if (!functionalReplace) {
- replaceValue_str = JS_ToString(ctx, replaceValue);
- if (JS_IsException(replaceValue_str))
- goto exception;
- }
- sp = JS_VALUE_GET_STRING(str);
- searchp = JS_VALUE_GET_STRING(search_str);
- endOfLastMatch = 0;
- is_first = TRUE;
- for(;;) {
- if (unlikely(searchp->len == 0)) {
- if (is_first)
- pos = 0;
- else if (endOfLastMatch >= sp->len)
- pos = -1;
- else
- pos = endOfLastMatch + 1;
- } else {
- pos = string_indexof(sp, searchp, endOfLastMatch);
- }
- if (pos < 0) {
- if (is_first) {
- string_buffer_free(b);
- JS_FreeValue(ctx, search_str);
- JS_FreeValue(ctx, replaceValue_str);
- return str;
- } else {
- break;
- }
- }
- string_buffer_concat(b, sp, endOfLastMatch, pos);
- if (functionalReplace) {
- args[0] = search_str;
- args[1] = JS_NewInt32(ctx, pos);
- args[2] = str;
- repl_str = JS_ToStringFree(ctx, JS_Call(ctx, replaceValue, JS_UNDEFINED, 3, args));
- if (JS_IsException(repl_str))
- goto exception;
- string_buffer_concat_value_free(b, repl_str);
- } else {
- if (js_string_GetSubstitution(ctx, b, search_str, sp, pos,
- JS_UNDEFINED, JS_UNDEFINED, replaceValue_str,
- NULL, 0)) {
- goto exception;
- }
- }
- endOfLastMatch = pos + searchp->len;
- is_first = FALSE;
- if (!is_replaceAll)
- break;
- }
- string_buffer_concat(b, sp, endOfLastMatch, sp->len);
- JS_FreeValue(ctx, search_str);
- JS_FreeValue(ctx, replaceValue_str);
- JS_FreeValue(ctx, str);
- return string_buffer_end(b);
- exception:
- string_buffer_free(b);
- JS_FreeValue(ctx, search_str);
- JS_FreeValue(ctx, replaceValue_str);
- JS_FreeValue(ctx, str);
- return JS_EXCEPTION;
- }
- static JSValue js_string_split(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- // split(sep, limit)
- JSValueConst O = this_val, separator = argv[0], limit = argv[1];
- JSValueConst args[2];
- JSValue S, A, R, T;
- uint32_t lim, lengthA;
- int64_t p, q, s, r, e;
- JSString *sp, *rp;
- if (JS_IsUndefined(O) || JS_IsNull(O))
- return JS_ThrowTypeError(ctx, "cannot convert to object");
- S = JS_UNDEFINED;
- A = JS_UNDEFINED;
- R = JS_UNDEFINED;
- if (JS_IsObject(separator)) {
- JSValue splitter;
- splitter = JS_GetProperty(ctx, separator, JS_ATOM_Symbol_split);
- if (JS_IsException(splitter))
- return JS_EXCEPTION;
- if (!JS_IsUndefined(splitter) && !JS_IsNull(splitter)) {
- args[0] = O;
- args[1] = limit;
- return JS_CallFree(ctx, splitter, separator, 2, args);
- }
- }
- S = JS_ToString(ctx, O);
- if (JS_IsException(S))
- goto exception;
- A = JS_NewArray(ctx);
- if (JS_IsException(A))
- goto exception;
- lengthA = 0;
- if (JS_IsUndefined(limit)) {
- lim = 0xffffffff;
- } else {
- if (JS_ToUint32(ctx, &lim, limit) < 0)
- goto exception;
- }
- sp = JS_VALUE_GET_STRING(S);
- s = sp->len;
- R = JS_ToString(ctx, separator);
- if (JS_IsException(R))
- goto exception;
- rp = JS_VALUE_GET_STRING(R);
- r = rp->len;
- p = 0;
- if (lim == 0)
- goto done;
- if (JS_IsUndefined(separator))
- goto add_tail;
- if (s == 0) {
- if (r != 0)
- goto add_tail;
- goto done;
- }
- for (q = p; (q += !r) <= s - r - !r; q = p = e + r) {
- e = string_indexof(sp, rp, q);
- if (e < 0)
- break;
- T = js_sub_string(ctx, sp, p, e);
- if (JS_IsException(T))
- goto exception;
- if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T, 0) < 0)
- goto exception;
- if (lengthA == lim)
- goto done;
- }
- add_tail:
- T = js_sub_string(ctx, sp, p, s);
- if (JS_IsException(T))
- goto exception;
- if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T,0 ) < 0)
- goto exception;
- done:
- JS_FreeValue(ctx, S);
- JS_FreeValue(ctx, R);
- return A;
- exception:
- JS_FreeValue(ctx, A);
- JS_FreeValue(ctx, S);
- JS_FreeValue(ctx, R);
- return JS_EXCEPTION;
- }
- static JSValue js_string_substring(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue str, ret;
- int a, b, start, end;
- JSString *p;
- str = JS_ToStringCheckObject(ctx, this_val);
- if (JS_IsException(str))
- return str;
- p = JS_VALUE_GET_STRING(str);
- if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, p->len, 0)) {
- JS_FreeValue(ctx, str);
- return JS_EXCEPTION;
- }
- b = p->len;
- if (!JS_IsUndefined(argv[1])) {
- if (JS_ToInt32Clamp(ctx, &b, argv[1], 0, p->len, 0)) {
- JS_FreeValue(ctx, str);
- return JS_EXCEPTION;
- }
- }
- if (a < b) {
- start = a;
- end = b;
- } else {
- start = b;
- end = a;
- }
- ret = js_sub_string(ctx, p, start, end);
- JS_FreeValue(ctx, str);
- return ret;
- }
- static JSValue js_string_substr(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue str, ret;
- int a, len, n;
- JSString *p;
- str = JS_ToStringCheckObject(ctx, this_val);
- if (JS_IsException(str))
- return str;
- p = JS_VALUE_GET_STRING(str);
- len = p->len;
- if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, len, len)) {
- JS_FreeValue(ctx, str);
- return JS_EXCEPTION;
- }
- n = len - a;
- if (!JS_IsUndefined(argv[1])) {
- if (JS_ToInt32Clamp(ctx, &n, argv[1], 0, len - a, 0)) {
- JS_FreeValue(ctx, str);
- return JS_EXCEPTION;
- }
- }
- ret = js_sub_string(ctx, p, a, a + n);
- JS_FreeValue(ctx, str);
- return ret;
- }
- static JSValue js_string_slice(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue str, ret;
- int len, start, end;
- JSString *p;
- str = JS_ToStringCheckObject(ctx, this_val);
- if (JS_IsException(str))
- return str;
- p = JS_VALUE_GET_STRING(str);
- len = p->len;
- if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) {
- JS_FreeValue(ctx, str);
- return JS_EXCEPTION;
- }
- end = len;
- if (!JS_IsUndefined(argv[1])) {
- if (JS_ToInt32Clamp(ctx, &end, argv[1], 0, len, len)) {
- JS_FreeValue(ctx, str);
- return JS_EXCEPTION;
- }
- }
- ret = js_sub_string(ctx, p, start, max_int(end, start));
- JS_FreeValue(ctx, str);
- return ret;
- }
- static JSValue js_string_pad(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int padEnd)
- {
- JSValue str, v = JS_UNDEFINED;
- StringBuffer b_s, *b = &b_s;
- JSString *p, *p1 = NULL;
- int n, len, c = ' ';
- str = JS_ToStringCheckObject(ctx, this_val);
- if (JS_IsException(str))
- goto fail1;
- if (JS_ToInt32Sat(ctx, &n, argv[0]))
- goto fail2;
- p = JS_VALUE_GET_STRING(str);
- len = p->len;
- if (len >= n)
- return str;
- if (argc > 1 && !JS_IsUndefined(argv[1])) {
- v = JS_ToString(ctx, argv[1]);
- if (JS_IsException(v))
- goto fail2;
- p1 = JS_VALUE_GET_STRING(v);
- if (p1->len == 0) {
- JS_FreeValue(ctx, v);
- return str;
- }
- if (p1->len == 1) {
- c = string_get(p1, 0);
- p1 = NULL;
- }
- }
- if (n > JS_STRING_LEN_MAX) {
- JS_ThrowRangeError(ctx, "invalid string length");
- goto fail3;
- }
- if (string_buffer_init(ctx, b, n))
- goto fail3;
- n -= len;
- if (padEnd) {
- if (string_buffer_concat(b, p, 0, len))
- goto fail;
- }
- if (p1) {
- while (n > 0) {
- int chunk = min_int(n, p1->len);
- if (string_buffer_concat(b, p1, 0, chunk))
- goto fail;
- n -= chunk;
- }
- } else {
- if (string_buffer_fill(b, c, n))
- goto fail;
- }
- if (!padEnd) {
- if (string_buffer_concat(b, p, 0, len))
- goto fail;
- }
- JS_FreeValue(ctx, v);
- JS_FreeValue(ctx, str);
- return string_buffer_end(b);
- fail:
- string_buffer_free(b);
- fail3:
- JS_FreeValue(ctx, v);
- fail2:
- JS_FreeValue(ctx, str);
- fail1:
- return JS_EXCEPTION;
- }
- static JSValue js_string_repeat(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue str;
- StringBuffer b_s, *b = &b_s;
- JSString *p;
- int64_t val;
- int n, len;
- str = JS_ToStringCheckObject(ctx, this_val);
- if (JS_IsException(str))
- goto fail;
- if (JS_ToInt64Sat(ctx, &val, argv[0]))
- goto fail;
- if (val < 0 || val > 2147483647) {
- JS_ThrowRangeError(ctx, "invalid repeat count");
- goto fail;
- }
- n = val;
- p = JS_VALUE_GET_STRING(str);
- len = p->len;
- if (len == 0 || n == 1)
- return str;
- // XXX: potential arithmetic overflow
- if (val * len > JS_STRING_LEN_MAX) {
- JS_ThrowRangeError(ctx, "invalid string length");
- goto fail;
- }
- if (string_buffer_init2(ctx, b, n * len, p->is_wide_char))
- goto fail;
- if (len == 1) {
- string_buffer_fill(b, string_get(p, 0), n);
- } else {
- while (n-- > 0) {
- string_buffer_concat(b, p, 0, len);
- }
- }
- JS_FreeValue(ctx, str);
- return string_buffer_end(b);
- fail:
- JS_FreeValue(ctx, str);
- return JS_EXCEPTION;
- }
- static JSValue js_string_trim(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
- {
- JSValue str, ret;
- int a, b, len;
- JSString *p;
- str = JS_ToStringCheckObject(ctx, this_val);
- if (JS_IsException(str))
- return str;
- p = JS_VALUE_GET_STRING(str);
- a = 0;
- b = len = p->len;
- if (magic & 1) {
- while (a < len && lre_is_space(string_get(p, a)))
- a++;
- }
- if (magic & 2) {
- while (b > a && lre_is_space(string_get(p, b - 1)))
- b--;
- }
- ret = js_sub_string(ctx, p, a, b);
- JS_FreeValue(ctx, str);
- return ret;
- }
- /* return 0 if before the first char */
- static int string_prevc(JSString *p, int *pidx)
- {
- int idx, c, c1;
- idx = *pidx;
- if (idx <= 0)
- return 0;
- idx--;
- if (p->is_wide_char) {
- c = p->u.str16[idx];
- if (is_lo_surrogate(c) && idx > 0) {
- c1 = p->u.str16[idx - 1];
- if (is_hi_surrogate(c1)) {
- c = from_surrogate(c1, c);
- idx--;
- }
- }
- } else {
- c = p->u.str8[idx];
- }
- *pidx = idx;
- return c;
- }
- static BOOL test_final_sigma(JSString *p, int sigma_pos)
- {
- int k, c1;
- /* before C: skip case ignorable chars and check there is
- a cased letter */
- k = sigma_pos;
- for(;;) {
- c1 = string_prevc(p, &k);
- if (!lre_is_case_ignorable(c1))
- break;
- }
- if (!lre_is_cased(c1))
- return FALSE;
- /* after C: skip case ignorable chars and check there is
- no cased letter */
- k = sigma_pos + 1;
- for(;;) {
- if (k >= p->len)
- return TRUE;
- c1 = string_getc(p, &k);
- if (!lre_is_case_ignorable(c1))
- break;
- }
- return !lre_is_cased(c1);
- }
- static JSValue js_string_toLowerCase(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int to_lower)
- {
- JSValue val;
- StringBuffer b_s, *b = &b_s;
- JSString *p;
- int i, c, j, l;
- uint32_t res[LRE_CC_RES_LEN_MAX];
- val = JS_ToStringCheckObject(ctx, this_val);
- if (JS_IsException(val))
- return val;
- p = JS_VALUE_GET_STRING(val);
- if (p->len == 0)
- return val;
- if (string_buffer_init(ctx, b, p->len))
- goto fail;
- for(i = 0; i < p->len;) {
- c = string_getc(p, &i);
- if (c == 0x3a3 && to_lower && test_final_sigma(p, i - 1)) {
- res[0] = 0x3c2; /* final sigma */
- l = 1;
- } else {
- l = lre_case_conv(res, c, to_lower);
- }
- for(j = 0; j < l; j++) {
- if (string_buffer_putc(b, res[j]))
- goto fail;
- }
- }
- JS_FreeValue(ctx, val);
- return string_buffer_end(b);
- fail:
- JS_FreeValue(ctx, val);
- string_buffer_free(b);
- return JS_EXCEPTION;
- }
- #ifdef CONFIG_ALL_UNICODE
- /* return (-1, NULL) if exception, otherwise (len, buf) */
- static int JS_ToUTF32String(JSContext *ctx, uint32_t **pbuf, JSValueConst val1)
- {
- JSValue val;
- JSString *p;
- uint32_t *buf;
- int i, j, len;
- val = JS_ToString(ctx, val1);
- if (JS_IsException(val))
- return -1;
- p = JS_VALUE_GET_STRING(val);
- len = p->len;
- /* UTF32 buffer length is len minus the number of correct surrogates pairs */
- buf = js_malloc(ctx, sizeof(buf[0]) * max_int(len, 1));
- if (!buf) {
- JS_FreeValue(ctx, val);
- goto fail;
- }
- for(i = j = 0; i < len;)
- buf[j++] = string_getc(p, &i);
- JS_FreeValue(ctx, val);
- *pbuf = buf;
- return j;
- fail:
- *pbuf = NULL;
- return -1;
- }
- static JSValue JS_NewUTF32String(JSContext *ctx, const uint32_t *buf, int len)
- {
- int i;
- StringBuffer b_s, *b = &b_s;
- if (string_buffer_init(ctx, b, len))
- return JS_EXCEPTION;
- for(i = 0; i < len; i++) {
- if (string_buffer_putc(b, buf[i]))
- goto fail;
- }
- return string_buffer_end(b);
- fail:
- string_buffer_free(b);
- return JS_EXCEPTION;
- }
- static int js_string_normalize1(JSContext *ctx, uint32_t **pout_buf,
- JSValueConst val,
- UnicodeNormalizationEnum n_type)
- {
- int buf_len, out_len;
- uint32_t *buf, *out_buf;
- buf_len = JS_ToUTF32String(ctx, &buf, val);
- if (buf_len < 0)
- return -1;
- out_len = unicode_normalize(&out_buf, buf, buf_len, n_type,
- ctx->rt, (DynBufReallocFunc *)js_realloc_rt);
- js_free(ctx, buf);
- if (out_len < 0)
- return -1;
- *pout_buf = out_buf;
- return out_len;
- }
- static JSValue js_string_normalize(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- const char *form, *p;
- size_t form_len;
- int is_compat, out_len;
- UnicodeNormalizationEnum n_type;
- JSValue val;
- uint32_t *out_buf;
- val = JS_ToStringCheckObject(ctx, this_val);
- if (JS_IsException(val))
- return val;
- if (argc == 0 || JS_IsUndefined(argv[0])) {
- n_type = UNICODE_NFC;
- } else {
- form = JS_ToCStringLen(ctx, &form_len, argv[0]);
- if (!form)
- goto fail1;
- p = form;
- if (p[0] != 'N' || p[1] != 'F')
- goto bad_form;
- p += 2;
- is_compat = FALSE;
- if (*p == 'K') {
- is_compat = TRUE;
- p++;
- }
- if (*p == 'C' || *p == 'D') {
- n_type = UNICODE_NFC + is_compat * 2 + (*p - 'C');
- if ((p + 1 - form) != form_len)
- goto bad_form;
- } else {
- bad_form:
- JS_FreeCString(ctx, form);
- JS_ThrowRangeError(ctx, "bad normalization form");
- fail1:
- JS_FreeValue(ctx, val);
- return JS_EXCEPTION;
- }
- JS_FreeCString(ctx, form);
- }
- out_len = js_string_normalize1(ctx, &out_buf, val, n_type);
- JS_FreeValue(ctx, val);
- if (out_len < 0)
- return JS_EXCEPTION;
- val = JS_NewUTF32String(ctx, out_buf, out_len);
- js_free(ctx, out_buf);
- return val;
- }
- /* return < 0, 0 or > 0 */
- static int js_UTF32_compare(const uint32_t *buf1, int buf1_len,
- const uint32_t *buf2, int buf2_len)
- {
- int i, len, c, res;
- len = min_int(buf1_len, buf2_len);
- for(i = 0; i < len; i++) {
- /* Note: range is limited so a subtraction is valid */
- c = buf1[i] - buf2[i];
- if (c != 0)
- return c;
- }
- if (buf1_len == buf2_len)
- res = 0;
- else if (buf1_len < buf2_len)
- res = -1;
- else
- res = 1;
- return res;
- }
- static JSValue js_string_localeCompare(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue a, b;
- int cmp, a_len, b_len;
- uint32_t *a_buf, *b_buf;
- a = JS_ToStringCheckObject(ctx, this_val);
- if (JS_IsException(a))
- return JS_EXCEPTION;
- b = JS_ToString(ctx, argv[0]);
- if (JS_IsException(b)) {
- JS_FreeValue(ctx, a);
- return JS_EXCEPTION;
- }
- a_len = js_string_normalize1(ctx, &a_buf, a, UNICODE_NFC);
- JS_FreeValue(ctx, a);
- if (a_len < 0) {
- JS_FreeValue(ctx, b);
- return JS_EXCEPTION;
- }
- b_len = js_string_normalize1(ctx, &b_buf, b, UNICODE_NFC);
- JS_FreeValue(ctx, b);
- if (b_len < 0) {
- js_free(ctx, a_buf);
- return JS_EXCEPTION;
- }
- cmp = js_UTF32_compare(a_buf, a_len, b_buf, b_len);
- js_free(ctx, a_buf);
- js_free(ctx, b_buf);
- return JS_NewInt32(ctx, cmp);
- }
- #else /* CONFIG_ALL_UNICODE */
- static JSValue js_string_localeCompare(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue a, b;
- int cmp;
- a = JS_ToStringCheckObject(ctx, this_val);
- if (JS_IsException(a))
- return JS_EXCEPTION;
- b = JS_ToString(ctx, argv[0]);
- if (JS_IsException(b)) {
- JS_FreeValue(ctx, a);
- return JS_EXCEPTION;
- }
- cmp = js_string_compare(ctx, JS_VALUE_GET_STRING(a), JS_VALUE_GET_STRING(b));
- JS_FreeValue(ctx, a);
- JS_FreeValue(ctx, b);
- return JS_NewInt32(ctx, cmp);
- }
- #endif /* !CONFIG_ALL_UNICODE */
- /* also used for String.prototype.valueOf */
- static JSValue js_string_toString(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- return js_thisStringValue(ctx, this_val);
- }
- /* String Iterator */
- static JSValue js_string_iterator_next(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv,
- BOOL *pdone, int magic)
- {
- JSArrayIteratorData *it;
- uint32_t idx, c, start;
- JSString *p;
- it = JS_GetOpaque2(ctx, this_val, JS_CLASS_STRING_ITERATOR);
- if (!it) {
- *pdone = FALSE;
- return JS_EXCEPTION;
- }
- if (JS_IsUndefined(it->obj))
- goto done;
- p = JS_VALUE_GET_STRING(it->obj);
- idx = it->idx;
- if (idx >= p->len) {
- JS_FreeValue(ctx, it->obj);
- it->obj = JS_UNDEFINED;
- done:
- *pdone = TRUE;
- return JS_UNDEFINED;
- }
- start = idx;
- c = string_getc(p, (int *)&idx);
- it->idx = idx;
- *pdone = FALSE;
- if (c <= 0xffff) {
- return js_new_string_char(ctx, c);
- } else {
- return js_new_string16_len(ctx, p->u.str16 + start, 2);
- }
- }
- /* ES6 Annex B 2.3.2 etc. */
- enum {
- magic_string_anchor,
- magic_string_big,
- magic_string_blink,
- magic_string_bold,
- magic_string_fixed,
- magic_string_fontcolor,
- magic_string_fontsize,
- magic_string_italics,
- magic_string_link,
- magic_string_small,
- magic_string_strike,
- magic_string_sub,
- magic_string_sup,
- };
- static JSValue js_string_CreateHTML(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
- {
- JSValue str;
- const JSString *p;
- StringBuffer b_s, *b = &b_s;
- static struct { const char *tag, *attr; } const defs[] = {
- { "a", "name" }, { "big", NULL }, { "blink", NULL }, { "b", NULL },
- { "tt", NULL }, { "font", "color" }, { "font", "size" }, { "i", NULL },
- { "a", "href" }, { "small", NULL }, { "strike", NULL },
- { "sub", NULL }, { "sup", NULL },
- };
- str = JS_ToStringCheckObject(ctx, this_val);
- if (JS_IsException(str))
- return JS_EXCEPTION;
- string_buffer_init(ctx, b, 7);
- string_buffer_putc8(b, '<');
- string_buffer_puts8(b, defs[magic].tag);
- if (defs[magic].attr) {
- // r += " " + attr + "=\"" + value + "\"";
- JSValue value;
- int i;
- string_buffer_putc8(b, ' ');
- string_buffer_puts8(b, defs[magic].attr);
- string_buffer_puts8(b, "=\"");
- value = JS_ToStringCheckObject(ctx, argv[0]);
- if (JS_IsException(value)) {
- JS_FreeValue(ctx, str);
- string_buffer_free(b);
- return JS_EXCEPTION;
- }
- p = JS_VALUE_GET_STRING(value);
- for (i = 0; i < p->len; i++) {
- int c = string_get(p, i);
- if (c == '"') {
- string_buffer_puts8(b, """);
- } else {
- string_buffer_putc16(b, c);
- }
- }
- JS_FreeValue(ctx, value);
- string_buffer_putc8(b, '\"');
- }
- // return r + ">" + str + "</" + tag + ">";
- string_buffer_putc8(b, '>');
- string_buffer_concat_value_free(b, str);
- string_buffer_puts8(b, "</");
- string_buffer_puts8(b, defs[magic].tag);
- string_buffer_putc8(b, '>');
- return string_buffer_end(b);
- }
- static const JSCFunctionListEntry js_string_funcs[] = {
- JS_CFUNC_DEF("fromCharCode", 1, js_string_fromCharCode ),
- JS_CFUNC_DEF("fromCodePoint", 1, js_string_fromCodePoint ),
- JS_CFUNC_DEF("raw", 1, js_string_raw ),
- };
- static const JSCFunctionListEntry js_string_proto_funcs[] = {
- JS_PROP_INT32_DEF("length", 0, JS_PROP_CONFIGURABLE ),
- JS_CFUNC_MAGIC_DEF("at", 1, js_string_charAt, 1 ),
- JS_CFUNC_DEF("charCodeAt", 1, js_string_charCodeAt ),
- JS_CFUNC_MAGIC_DEF("charAt", 1, js_string_charAt, 0 ),
- JS_CFUNC_DEF("concat", 1, js_string_concat ),
- JS_CFUNC_DEF("codePointAt", 1, js_string_codePointAt ),
- JS_CFUNC_DEF("isWellFormed", 0, js_string_isWellFormed ),
- JS_CFUNC_DEF("toWellFormed", 0, js_string_toWellFormed ),
- JS_CFUNC_MAGIC_DEF("indexOf", 1, js_string_indexOf, 0 ),
- JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_string_indexOf, 1 ),
- JS_CFUNC_MAGIC_DEF("includes", 1, js_string_includes, 0 ),
- JS_CFUNC_MAGIC_DEF("endsWith", 1, js_string_includes, 2 ),
- JS_CFUNC_MAGIC_DEF("startsWith", 1, js_string_includes, 1 ),
- JS_CFUNC_MAGIC_DEF("match", 1, js_string_match, JS_ATOM_Symbol_match ),
- JS_CFUNC_MAGIC_DEF("matchAll", 1, js_string_match, JS_ATOM_Symbol_matchAll ),
- JS_CFUNC_MAGIC_DEF("search", 1, js_string_match, JS_ATOM_Symbol_search ),
- JS_CFUNC_DEF("split", 2, js_string_split ),
- JS_CFUNC_DEF("substring", 2, js_string_substring ),
- JS_CFUNC_DEF("substr", 2, js_string_substr ),
- JS_CFUNC_DEF("slice", 2, js_string_slice ),
- JS_CFUNC_DEF("repeat", 1, js_string_repeat ),
- JS_CFUNC_MAGIC_DEF("replace", 2, js_string_replace, 0 ),
- JS_CFUNC_MAGIC_DEF("replaceAll", 2, js_string_replace, 1 ),
- JS_CFUNC_MAGIC_DEF("padEnd", 1, js_string_pad, 1 ),
- JS_CFUNC_MAGIC_DEF("padStart", 1, js_string_pad, 0 ),
- JS_CFUNC_MAGIC_DEF("trim", 0, js_string_trim, 3 ),
- JS_CFUNC_MAGIC_DEF("trimEnd", 0, js_string_trim, 2 ),
- JS_ALIAS_DEF("trimRight", "trimEnd" ),
- JS_CFUNC_MAGIC_DEF("trimStart", 0, js_string_trim, 1 ),
- JS_ALIAS_DEF("trimLeft", "trimStart" ),
- JS_CFUNC_DEF("toString", 0, js_string_toString ),
- JS_CFUNC_DEF("valueOf", 0, js_string_toString ),
- JS_CFUNC_MAGIC_DEF("toLowerCase", 0, js_string_toLowerCase, 1 ),
- JS_CFUNC_MAGIC_DEF("toUpperCase", 0, js_string_toLowerCase, 0 ),
- JS_CFUNC_MAGIC_DEF("toLocaleLowerCase", 0, js_string_toLowerCase, 1 ),
- JS_CFUNC_MAGIC_DEF("toLocaleUpperCase", 0, js_string_toLowerCase, 0 ),
- JS_CFUNC_MAGIC_DEF("[Symbol.iterator]", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE | 4 ),
- /* ES6 Annex B 2.3.2 etc. */
- JS_CFUNC_MAGIC_DEF("anchor", 1, js_string_CreateHTML, magic_string_anchor ),
- JS_CFUNC_MAGIC_DEF("big", 0, js_string_CreateHTML, magic_string_big ),
- JS_CFUNC_MAGIC_DEF("blink", 0, js_string_CreateHTML, magic_string_blink ),
- JS_CFUNC_MAGIC_DEF("bold", 0, js_string_CreateHTML, magic_string_bold ),
- JS_CFUNC_MAGIC_DEF("fixed", 0, js_string_CreateHTML, magic_string_fixed ),
- JS_CFUNC_MAGIC_DEF("fontcolor", 1, js_string_CreateHTML, magic_string_fontcolor ),
- JS_CFUNC_MAGIC_DEF("fontsize", 1, js_string_CreateHTML, magic_string_fontsize ),
- JS_CFUNC_MAGIC_DEF("italics", 0, js_string_CreateHTML, magic_string_italics ),
- JS_CFUNC_MAGIC_DEF("link", 1, js_string_CreateHTML, magic_string_link ),
- JS_CFUNC_MAGIC_DEF("small", 0, js_string_CreateHTML, magic_string_small ),
- JS_CFUNC_MAGIC_DEF("strike", 0, js_string_CreateHTML, magic_string_strike ),
- JS_CFUNC_MAGIC_DEF("sub", 0, js_string_CreateHTML, magic_string_sub ),
- JS_CFUNC_MAGIC_DEF("sup", 0, js_string_CreateHTML, magic_string_sup ),
- };
- static const JSCFunctionListEntry js_string_iterator_proto_funcs[] = {
- JS_ITERATOR_NEXT_DEF("next", 0, js_string_iterator_next, 0 ),
- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "String Iterator", JS_PROP_CONFIGURABLE ),
- };
- static const JSCFunctionListEntry js_string_proto_normalize[] = {
- #ifdef CONFIG_ALL_UNICODE
- JS_CFUNC_DEF("normalize", 0, js_string_normalize ),
- #endif
- JS_CFUNC_DEF("localeCompare", 1, js_string_localeCompare ),
- };
- int JS_AddIntrinsicStringNormalize(JSContext *ctx)
- {
- return JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING], js_string_proto_normalize,
- countof(js_string_proto_normalize));
- }
- /* Math */
- /* precondition: a and b are not NaN */
- static double js_fmin(double a, double b)
- {
- if (a == 0 && b == 0) {
- JSFloat64Union a1, b1;
- a1.d = a;
- b1.d = b;
- a1.u64 |= b1.u64;
- return a1.d;
- } else {
- return fmin(a, b);
- }
- }
- /* precondition: a and b are not NaN */
- static double js_fmax(double a, double b)
- {
- if (a == 0 && b == 0) {
- JSFloat64Union a1, b1;
- a1.d = a;
- b1.d = b;
- a1.u64 &= b1.u64;
- return a1.d;
- } else {
- return fmax(a, b);
- }
- }
- static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
- {
- BOOL is_max = magic;
- double r, a;
- int i;
- uint32_t tag;
- if (unlikely(argc == 0)) {
- return __JS_NewFloat64(ctx, is_max ? -1.0 / 0.0 : 1.0 / 0.0);
- }
- tag = JS_VALUE_GET_TAG(argv[0]);
- if (tag == JS_TAG_INT) {
- int a1, r1 = JS_VALUE_GET_INT(argv[0]);
- for(i = 1; i < argc; i++) {
- tag = JS_VALUE_GET_TAG(argv[i]);
- if (tag != JS_TAG_INT) {
- r = r1;
- goto generic_case;
- }
- a1 = JS_VALUE_GET_INT(argv[i]);
- if (is_max)
- r1 = max_int(r1, a1);
- else
- r1 = min_int(r1, a1);
- }
- return JS_NewInt32(ctx, r1);
- } else {
- if (JS_ToFloat64(ctx, &r, argv[0]))
- return JS_EXCEPTION;
- i = 1;
- generic_case:
- while (i < argc) {
- if (JS_ToFloat64(ctx, &a, argv[i]))
- return JS_EXCEPTION;
- if (!isnan(r)) {
- if (isnan(a)) {
- r = a;
- } else {
- if (is_max)
- r = js_fmax(r, a);
- else
- r = js_fmin(r, a);
- }
- }
- i++;
- }
- return JS_NewFloat64(ctx, r);
- }
- }
- static double js_math_sign(double a)
- {
- if (isnan(a) || a == 0.0)
- return a;
- if (a < 0)
- return -1;
- else
- return 1;
- }
- static double js_math_round(double a)
- {
- JSFloat64Union u;
- uint64_t frac_mask, one;
- unsigned int e, s;
- u.d = a;
- e = (u.u64 >> 52) & 0x7ff;
- if (e < 1023) {
- /* abs(a) < 1 */
- if (e == (1023 - 1) && u.u64 != 0xbfe0000000000000) {
- /* abs(a) > 0.5 or a = 0.5: return +/-1.0 */
- u.u64 = (u.u64 & ((uint64_t)1 << 63)) | ((uint64_t)1023 << 52);
- } else {
- /* return +/-0.0 */
- u.u64 &= (uint64_t)1 << 63;
- }
- } else if (e < (1023 + 52)) {
- s = u.u64 >> 63;
- one = (uint64_t)1 << (52 - (e - 1023));
- frac_mask = one - 1;
- u.u64 += (one >> 1) - s;
- u.u64 &= ~frac_mask; /* truncate to an integer */
- }
- /* otherwise: abs(a) >= 2^52, or NaN, +/-Infinity: no change */
- return u.d;
- }
- static JSValue js_math_hypot(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- double r, a;
- int i;
- r = 0;
- if (argc > 0) {
- if (JS_ToFloat64(ctx, &r, argv[0]))
- return JS_EXCEPTION;
- if (argc == 1) {
- r = fabs(r);
- } else {
- /* use the built-in function to minimize precision loss */
- for (i = 1; i < argc; i++) {
- if (JS_ToFloat64(ctx, &a, argv[i]))
- return JS_EXCEPTION;
- r = hypot(r, a);
- }
- }
- }
- return JS_NewFloat64(ctx, r);
- }
- static double js_math_f16round(double a)
- {
- return fromfp16(tofp16(a));
- }
- static double js_math_fround(double a)
- {
- return (float)a;
- }
- static JSValue js_math_imul(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- uint32_t a, b, c;
- int32_t d;
- if (JS_ToUint32(ctx, &a, argv[0]))
- return JS_EXCEPTION;
- if (JS_ToUint32(ctx, &b, argv[1]))
- return JS_EXCEPTION;
- c = a * b;
- memcpy(&d, &c, sizeof(d));
- return JS_NewInt32(ctx, d);
- }
- static JSValue js_math_clz32(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- uint32_t a, r;
- if (JS_ToUint32(ctx, &a, argv[0]))
- return JS_EXCEPTION;
- if (a == 0)
- r = 32;
- else
- r = clz32(a);
- return JS_NewInt32(ctx, r);
- }
- typedef enum {
- SUM_PRECISE_STATE_FINITE,
- SUM_PRECISE_STATE_INFINITY,
- SUM_PRECISE_STATE_MINUS_INFINITY, /* must be after SUM_PRECISE_STATE_INFINITY */
- SUM_PRECISE_STATE_NAN, /* must be after SUM_PRECISE_STATE_MINUS_INFINITY */
- } SumPreciseStateEnum;
- #define SP_LIMB_BITS 56
- #define SP_RND_BITS (SP_LIMB_BITS - 53)
- /* we add one extra limb to avoid having to test for overflows during the sum */
- #define SUM_PRECISE_ACC_LEN 39
- #define SUM_PRECISE_COUNTER_INIT 250
- typedef struct {
- SumPreciseStateEnum state;
- uint32_t counter;
- int n_limbs; /* 'acc' contains n_limbs and is not necessarily
- acc[n_limb - 1] may be 0. 0 indicates minus zero
- result when state = SUM_PRECISE_STATE_FINITE */
- int64_t acc[SUM_PRECISE_ACC_LEN];
- } SumPreciseState;
- static void sum_precise_init(SumPreciseState *s)
- {
- memset(s->acc, 0, sizeof(s->acc));
- s->state = SUM_PRECISE_STATE_FINITE;
- s->counter = SUM_PRECISE_COUNTER_INIT;
- s->n_limbs = 0;
- }
- static void sum_precise_renorm(SumPreciseState *s)
- {
- int64_t v, carry;
- int i;
-
- carry = 0;
- for(i = 0; i < s->n_limbs; i++) {
- v = s->acc[i] + carry;
- s->acc[i] = v & (((uint64_t)1 << SP_LIMB_BITS) - 1);
- carry = v >> SP_LIMB_BITS;
- }
- /* we add a failsafe but it should be never reached in a
- reasonnable amount of time */
- if (carry != 0 && s->n_limbs < SUM_PRECISE_ACC_LEN)
- s->acc[s->n_limbs++] = carry;
- }
- static void sum_precise_add(SumPreciseState *s, double d)
- {
- uint64_t a, m, a0, a1;
- int sgn, e, p;
- unsigned int shift;
-
- a = float64_as_uint64(d);
- sgn = a >> 63;
- e = (a >> 52) & ((1 << 11) - 1);
- m = a & (((uint64_t)1 << 52) - 1);
- if (unlikely(e == 2047)) {
- if (m == 0) {
- /* +/- infinity */
- if (s->state == SUM_PRECISE_STATE_NAN ||
- (s->state == SUM_PRECISE_STATE_MINUS_INFINITY && !sgn) ||
- (s->state == SUM_PRECISE_STATE_INFINITY && sgn)) {
- s->state = SUM_PRECISE_STATE_NAN;
- } else {
- s->state = SUM_PRECISE_STATE_INFINITY + sgn;
- }
- } else {
- /* NaN */
- s->state = SUM_PRECISE_STATE_NAN;
- }
- } else if (e == 0) {
- if (likely(m == 0)) {
- /* zero */
- if (s->n_limbs == 0 && !sgn)
- s->n_limbs = 1;
- } else {
- /* subnormal */
- p = 0;
- shift = 0;
- goto add;
- }
- } else {
- /* Note: we sum even if state != SUM_PRECISE_STATE_FINITE to
- avoid tests */
- m |= (uint64_t)1 << 52;
- shift = e - 1;
- /* 'p' is the position of a0 in acc. The division is normally
- implementation as a multiplication by the compiler. */
- p = shift / SP_LIMB_BITS;
- shift %= SP_LIMB_BITS;
- add:
- a0 = (m << shift) & (((uint64_t)1 << SP_LIMB_BITS) - 1);
- a1 = m >> (SP_LIMB_BITS - shift);
- if (!sgn) {
- s->acc[p] += a0;
- s->acc[p + 1] += a1;
- } else {
- s->acc[p] -= a0;
- s->acc[p + 1] -= a1;
- }
- s->n_limbs = max_int(s->n_limbs, p + 2);
- if (unlikely(--s->counter == 0)) {
- s->counter = SUM_PRECISE_COUNTER_INIT;
- sum_precise_renorm(s);
- }
- }
- }
- static double sum_precise_get_result(SumPreciseState *s)
- {
- int n, shift, e, p, is_neg;
- uint64_t m, addend;
-
- if (s->state != SUM_PRECISE_STATE_FINITE) {
- switch(s->state) {
- default:
- case SUM_PRECISE_STATE_INFINITY:
- return INFINITY;
- case SUM_PRECISE_STATE_MINUS_INFINITY:
- return -INFINITY;
- case SUM_PRECISE_STATE_NAN:
- return NAN;
- }
- }
- sum_precise_renorm(s);
- /* extract the sign and absolute value */
- #if 0
- {
- int i;
- printf("len=%d:", s->n_limbs);
- for(i = s->n_limbs - 1; i >= 0; i--)
- printf(" %014lx", s->acc[i]);
- printf("\n");
- }
- #endif
- n = s->n_limbs;
- /* minus zero result */
- if (n == 0)
- return -0.0;
-
- /* normalize */
- while (n > 0 && s->acc[n - 1] == 0)
- n--;
- /* zero result. The spec tells it is always positive in the finite case */
- if (n == 0)
- return 0.0;
- is_neg = (s->acc[n - 1] < 0);
- if (is_neg) {
- uint64_t v, carry;
- int i;
- /* negate */
- /* XXX: do it only when needed */
- carry = 1;
- for(i = 0; i < n - 1; i++) {
- v = (((uint64_t)1 << SP_LIMB_BITS) - 1) - s->acc[i] + carry;
- carry = v >> SP_LIMB_BITS;
- s->acc[i] = v & (((uint64_t)1 << SP_LIMB_BITS) - 1);
- }
- s->acc[n - 1] = -s->acc[n - 1] + carry - 1;
- while (n > 1 && s->acc[n - 1] == 0)
- n--;
- }
- /* subnormal case */
- if (n == 1 && s->acc[0] < ((uint64_t)1 << 52))
- return uint64_as_float64(((uint64_t)is_neg << 63) | s->acc[0]);
- /* normal case */
- e = n * SP_LIMB_BITS;
- p = n - 1;
- m = s->acc[p];
- shift = clz64(m) - (64 - SP_LIMB_BITS);
- e = e - shift - 52;
- if (shift != 0) {
- m <<= shift;
- if (p > 0) {
- int shift1;
- uint64_t nz;
- p--;
- shift1 = SP_LIMB_BITS - shift;
- nz = s->acc[p] & (((uint64_t)1 << shift1) - 1);
- m = m | (s->acc[p] >> shift1) | (nz != 0);
- }
- }
- if ((m & ((1 << SP_RND_BITS) - 1)) == (1 << (SP_RND_BITS - 1))) {
- /* see if the LSB part is non zero for the final rounding */
- while (p > 0) {
- p--;
- if (s->acc[p] != 0) {
- m |= 1;
- break;
- }
- }
- }
- /* rounding to nearest with ties to even */
- addend = (1 << (SP_RND_BITS - 1)) - 1 + ((m >> SP_RND_BITS) & 1);
- m = (m + addend) >> SP_RND_BITS;
- /* handle overflow in the rounding */
- if (m == ((uint64_t)1 << 53))
- e++;
- if (unlikely(e >= 2047)) {
- /* infinity */
- return uint64_as_float64(((uint64_t)is_neg << 63) | ((uint64_t)2047 << 52));
- } else {
- m &= (((uint64_t)1 << 52) - 1);
- return uint64_as_float64(((uint64_t)is_neg << 63) | ((uint64_t)e << 52) | m);
- }
- }
- static JSValue js_math_sumPrecise(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue iter, next, item, ret;
- uint32_t tag;
- int done;
- double d;
- SumPreciseState s_s, *s = &s_s;
- iter = JS_GetIterator(ctx, argv[0], FALSE);
- if (JS_IsException(iter))
- return JS_EXCEPTION;
- ret = JS_EXCEPTION;
- next = JS_GetProperty(ctx, iter, JS_ATOM_next);
- if (JS_IsException(next))
- goto fail;
- sum_precise_init(s);
- for (;;) {
- item = JS_IteratorNext(ctx, iter, next, 0, NULL, &done);
- if (JS_IsException(item))
- goto fail;
- if (done)
- break;
- tag = JS_VALUE_GET_TAG(item);
- if (JS_TAG_IS_FLOAT64(tag)) {
- d = JS_VALUE_GET_FLOAT64(item);
- } else if (tag == JS_TAG_INT) {
- d = JS_VALUE_GET_INT(item);
- } else {
- JS_FreeValue(ctx, item);
- JS_ThrowTypeError(ctx, "not a number");
- JS_IteratorClose(ctx, iter, TRUE);
- goto fail;
- }
- sum_precise_add(s, d);
- }
- ret = __JS_NewFloat64(ctx, sum_precise_get_result(s));
- fail:
- JS_FreeValue(ctx, iter);
- JS_FreeValue(ctx, next);
- return ret;
- }
- /* xorshift* random number generator by Marsaglia */
- static uint64_t xorshift64star(uint64_t *pstate)
- {
- uint64_t x;
- x = *pstate;
- x ^= x >> 12;
- x ^= x << 25;
- x ^= x >> 27;
- *pstate = x;
- return x * 0x2545F4914F6CDD1D;
- }
- static void js_random_init(JSContext *ctx)
- {
- struct timeval tv;
- gettimeofday(&tv, NULL);
- ctx->random_state = ((int64_t)tv.tv_sec * 1000000) + tv.tv_usec;
- /* the state must be non zero */
- if (ctx->random_state == 0)
- ctx->random_state = 1;
- }
- static JSValue js_math_random(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSFloat64Union u;
- uint64_t v;
- v = xorshift64star(&ctx->random_state);
- /* 1.0 <= u.d < 2 */
- u.u64 = ((uint64_t)0x3ff << 52) | (v >> 12);
- return __JS_NewFloat64(ctx, u.d - 1.0);
- }
- static const JSCFunctionListEntry js_math_funcs[] = {
- JS_CFUNC_MAGIC_DEF("min", 2, js_math_min_max, 0 ),
- JS_CFUNC_MAGIC_DEF("max", 2, js_math_min_max, 1 ),
- JS_CFUNC_SPECIAL_DEF("abs", 1, f_f, fabs ),
- JS_CFUNC_SPECIAL_DEF("floor", 1, f_f, floor ),
- JS_CFUNC_SPECIAL_DEF("ceil", 1, f_f, ceil ),
- JS_CFUNC_SPECIAL_DEF("round", 1, f_f, js_math_round ),
- JS_CFUNC_SPECIAL_DEF("sqrt", 1, f_f, sqrt ),
- JS_CFUNC_SPECIAL_DEF("acos", 1, f_f, acos ),
- JS_CFUNC_SPECIAL_DEF("asin", 1, f_f, asin ),
- JS_CFUNC_SPECIAL_DEF("atan", 1, f_f, atan ),
- JS_CFUNC_SPECIAL_DEF("atan2", 2, f_f_f, atan2 ),
- JS_CFUNC_SPECIAL_DEF("cos", 1, f_f, cos ),
- JS_CFUNC_SPECIAL_DEF("exp", 1, f_f, exp ),
- JS_CFUNC_SPECIAL_DEF("log", 1, f_f, log ),
- JS_CFUNC_SPECIAL_DEF("pow", 2, f_f_f, js_pow ),
- JS_CFUNC_SPECIAL_DEF("sin", 1, f_f, sin ),
- JS_CFUNC_SPECIAL_DEF("tan", 1, f_f, tan ),
- /* ES6 */
- JS_CFUNC_SPECIAL_DEF("trunc", 1, f_f, trunc ),
- JS_CFUNC_SPECIAL_DEF("sign", 1, f_f, js_math_sign ),
- JS_CFUNC_SPECIAL_DEF("cosh", 1, f_f, cosh ),
- JS_CFUNC_SPECIAL_DEF("sinh", 1, f_f, sinh ),
- JS_CFUNC_SPECIAL_DEF("tanh", 1, f_f, tanh ),
- JS_CFUNC_SPECIAL_DEF("acosh", 1, f_f, acosh ),
- JS_CFUNC_SPECIAL_DEF("asinh", 1, f_f, asinh ),
- JS_CFUNC_SPECIAL_DEF("atanh", 1, f_f, atanh ),
- JS_CFUNC_SPECIAL_DEF("expm1", 1, f_f, expm1 ),
- JS_CFUNC_SPECIAL_DEF("log1p", 1, f_f, log1p ),
- JS_CFUNC_SPECIAL_DEF("log2", 1, f_f, log2 ),
- JS_CFUNC_SPECIAL_DEF("log10", 1, f_f, log10 ),
- JS_CFUNC_SPECIAL_DEF("cbrt", 1, f_f, cbrt ),
- JS_CFUNC_DEF("hypot", 2, js_math_hypot ),
- JS_CFUNC_DEF("random", 0, js_math_random ),
- JS_CFUNC_SPECIAL_DEF("f16round", 1, f_f, js_math_f16round ),
- JS_CFUNC_SPECIAL_DEF("fround", 1, f_f, js_math_fround ),
- JS_CFUNC_DEF("imul", 2, js_math_imul ),
- JS_CFUNC_DEF("clz32", 1, js_math_clz32 ),
- JS_CFUNC_DEF("sumPrecise", 1, js_math_sumPrecise ),
- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Math", JS_PROP_CONFIGURABLE ),
- JS_PROP_DOUBLE_DEF("E", 2.718281828459045, 0 ),
- JS_PROP_DOUBLE_DEF("LN10", 2.302585092994046, 0 ),
- JS_PROP_DOUBLE_DEF("LN2", 0.6931471805599453, 0 ),
- JS_PROP_DOUBLE_DEF("LOG2E", 1.4426950408889634, 0 ),
- JS_PROP_DOUBLE_DEF("LOG10E", 0.4342944819032518, 0 ),
- JS_PROP_DOUBLE_DEF("PI", 3.141592653589793, 0 ),
- JS_PROP_DOUBLE_DEF("SQRT1_2", 0.7071067811865476, 0 ),
- JS_PROP_DOUBLE_DEF("SQRT2", 1.4142135623730951, 0 ),
- };
- static const JSCFunctionListEntry js_math_obj[] = {
- JS_OBJECT_DEF("Math", js_math_funcs, countof(js_math_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
- };
- /* Date */
- /* OS dependent. d = argv[0] is in ms from 1970. Return the difference
- between UTC time and local time 'd' in minutes */
- static int getTimezoneOffset(int64_t time)
- {
- time_t ti;
- int res;
- time /= 1000; /* convert to seconds */
- if (sizeof(time_t) == 4) {
- /* on 32-bit systems, we need to clamp the time value to the
- range of `time_t`. This is better than truncating values to
- 32 bits and hopefully provides the same result as 64-bit
- implementation of localtime_r.
- */
- if ((time_t)-1 < 0) {
- if (time < INT32_MIN) {
- time = INT32_MIN;
- } else if (time > INT32_MAX) {
- time = INT32_MAX;
- }
- } else {
- if (time < 0) {
- time = 0;
- } else if (time > UINT32_MAX) {
- time = UINT32_MAX;
- }
- }
- }
- ti = time;
- #if defined(_WIN32)
- {
- struct tm *tm;
- time_t gm_ti, loc_ti;
- tm = gmtime(&ti);
- if (!tm)
- return 0;
- gm_ti = mktime(tm);
- tm = localtime(&ti);
- if (!tm)
- return 0;
- loc_ti = mktime(tm);
- res = (gm_ti - loc_ti) / 60;
- }
- #else
- {
- struct tm tm;
- localtime_r(&ti, &tm);
- res = -tm.tm_gmtoff / 60;
- }
- #endif
- return res;
- }
- #if 0
- static JSValue js___date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- double dd;
- if (JS_ToFloat64(ctx, &dd, argv[0]))
- return JS_EXCEPTION;
- if (isnan(dd))
- return __JS_NewFloat64(ctx, dd);
- else
- return JS_NewInt32(ctx, getTimezoneOffset((int64_t)dd));
- }
- static JSValue js_get_prototype_from_ctor(JSContext *ctx, JSValueConst ctor,
- JSValueConst def_proto)
- {
- JSValue proto;
- proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype);
- if (JS_IsException(proto))
- return proto;
- if (!JS_IsObject(proto)) {
- JS_FreeValue(ctx, proto);
- proto = JS_DupValue(ctx, def_proto);
- }
- return proto;
- }
- /* create a new date object */
- static JSValue js___date_create(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue obj, proto;
- proto = js_get_prototype_from_ctor(ctx, argv[0], argv[1]);
- if (JS_IsException(proto))
- return proto;
- obj = JS_NewObjectProtoClass(ctx, proto, JS_CLASS_DATE);
- JS_FreeValue(ctx, proto);
- if (!JS_IsException(obj))
- JS_SetObjectData(ctx, obj, JS_DupValue(ctx, argv[2]));
- return obj;
- }
- #endif
- /* RegExp */
- static void js_regexp_finalizer(JSRuntime *rt, JSValue val)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- JSRegExp *re = &p->u.regexp;
- if (re->bytecode != NULL)
- JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->bytecode));
- if (re->pattern != NULL)
- JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->pattern));
- }
- /* create a string containing the RegExp bytecode */
- static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern,
- JSValueConst flags)
- {
- const char *str;
- int re_flags, mask;
- uint8_t *re_bytecode_buf;
- size_t i, len;
- int re_bytecode_len;
- JSValue ret;
- char error_msg[64];
- re_flags = 0;
- if (!JS_IsUndefined(flags)) {
- str = JS_ToCStringLen(ctx, &len, flags);
- if (!str)
- return JS_EXCEPTION;
- /* XXX: re_flags = LRE_FLAG_OCTAL unless strict mode? */
- for (i = 0; i < len; i++) {
- switch(str[i]) {
- case 'd':
- mask = LRE_FLAG_INDICES;
- break;
- case 'g':
- mask = LRE_FLAG_GLOBAL;
- break;
- case 'i':
- mask = LRE_FLAG_IGNORECASE;
- break;
- case 'm':
- mask = LRE_FLAG_MULTILINE;
- break;
- case 's':
- mask = LRE_FLAG_DOTALL;
- break;
- case 'u':
- mask = LRE_FLAG_UNICODE;
- break;
- case 'v':
- mask = LRE_FLAG_UNICODE_SETS;
- break;
- case 'y':
- mask = LRE_FLAG_STICKY;
- break;
- default:
- goto bad_flags;
- }
- if ((re_flags & mask) != 0) {
- bad_flags:
- JS_FreeCString(ctx, str);
- goto bad_flags1;
- }
- re_flags |= mask;
- }
- JS_FreeCString(ctx, str);
- }
- /* 'u' and 'v' cannot be both set */
- if ((re_flags & LRE_FLAG_UNICODE_SETS) && (re_flags & LRE_FLAG_UNICODE)) {
- bad_flags1:
- return JS_ThrowSyntaxError(ctx, "invalid regular expression flags");
- }
-
- str = JS_ToCStringLen2(ctx, &len, pattern, !(re_flags & (LRE_FLAG_UNICODE | LRE_FLAG_UNICODE_SETS)));
- if (!str)
- return JS_EXCEPTION;
- re_bytecode_buf = lre_compile(&re_bytecode_len, error_msg,
- sizeof(error_msg), str, len, re_flags, ctx);
- JS_FreeCString(ctx, str);
- if (!re_bytecode_buf) {
- JS_ThrowSyntaxError(ctx, "%s", error_msg);
- return JS_EXCEPTION;
- }
- ret = js_new_string8_len(ctx, (const char *)re_bytecode_buf, re_bytecode_len);
- js_free(ctx, re_bytecode_buf);
- return ret;
- }
- /* fast regexp creation */
- static JSValue JS_NewRegexp(JSContext *ctx, JSValue pattern, JSValue bc)
- {
- JSValue obj;
- JSProperty props[1];
- JSObject *p;
- JSRegExp *re;
- /* sanity check */
- if (unlikely(JS_VALUE_GET_TAG(bc) != JS_TAG_STRING ||
- JS_VALUE_GET_TAG(pattern) != JS_TAG_STRING)) {
- JS_ThrowTypeError(ctx, "string expected");
- goto fail;
- }
- props[0].u.value = JS_NewInt32(ctx, 0); /* lastIndex */
- obj = JS_NewObjectFromShape(ctx, js_dup_shape(ctx->regexp_shape), JS_CLASS_REGEXP, props);
- if (JS_IsException(obj))
- goto fail;
- p = JS_VALUE_GET_OBJ(obj);
- re = &p->u.regexp;
- re->pattern = JS_VALUE_GET_STRING(pattern);
- re->bytecode = JS_VALUE_GET_STRING(bc);
- return obj;
- fail:
- JS_FreeValue(ctx, bc);
- JS_FreeValue(ctx, pattern);
- return JS_EXCEPTION;
- }
- /* set the RegExp fields */
- static JSValue js_regexp_set_internal(JSContext *ctx,
- JSValue obj,
- JSValue pattern, JSValue bc)
- {
- JSObject *p;
- JSRegExp *re;
- /* sanity check */
- if (unlikely(JS_VALUE_GET_TAG(bc) != JS_TAG_STRING ||
- JS_VALUE_GET_TAG(pattern) != JS_TAG_STRING)) {
- JS_ThrowTypeError(ctx, "string expected");
- JS_FreeValue(ctx, obj);
- JS_FreeValue(ctx, bc);
- JS_FreeValue(ctx, pattern);
- return JS_EXCEPTION;
- }
- p = JS_VALUE_GET_OBJ(obj);
- re = &p->u.regexp;
- re->pattern = JS_VALUE_GET_STRING(pattern);
- re->bytecode = JS_VALUE_GET_STRING(bc);
- /* Note: cannot fail because the field is preallocated */
- JS_DefinePropertyValue(ctx, obj, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0),
- JS_PROP_WRITABLE);
- return obj;
- }
- static JSRegExp *js_get_regexp(JSContext *ctx, JSValueConst obj, BOOL throw_error)
- {
- if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
- JSObject *p = JS_VALUE_GET_OBJ(obj);
- if (p->class_id == JS_CLASS_REGEXP)
- return &p->u.regexp;
- }
- if (throw_error) {
- JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP);
- }
- return NULL;
- }
- /* return < 0 if exception or TRUE/FALSE */
- static int js_is_regexp(JSContext *ctx, JSValueConst obj)
- {
- JSValue m;
- if (!JS_IsObject(obj))
- return FALSE;
- m = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_match);
- if (JS_IsException(m))
- return -1;
- if (!JS_IsUndefined(m))
- return JS_ToBoolFree(ctx, m);
- return js_get_regexp(ctx, obj, FALSE) != NULL;
- }
- static JSValue js_regexp_constructor(JSContext *ctx, JSValueConst new_target,
- int argc, JSValueConst *argv)
- {
- JSValue pattern, flags, bc, val, obj = JS_UNDEFINED;
- JSValueConst pat, flags1;
- JSRegExp *re;
- int pat_is_regexp;
- pat = argv[0];
- flags1 = argv[1];
- pat_is_regexp = js_is_regexp(ctx, pat);
- if (pat_is_regexp < 0)
- return JS_EXCEPTION;
- if (JS_IsUndefined(new_target)) {
- /* called as a function */
- new_target = JS_GetActiveFunction(ctx);
- if (pat_is_regexp && JS_IsUndefined(flags1)) {
- JSValue ctor;
- BOOL res;
- ctor = JS_GetProperty(ctx, pat, JS_ATOM_constructor);
- if (JS_IsException(ctor))
- return ctor;
- res = js_same_value(ctx, ctor, new_target);
- JS_FreeValue(ctx, ctor);
- if (res)
- return JS_DupValue(ctx, pat);
- }
- }
- re = js_get_regexp(ctx, pat, FALSE);
- flags = JS_UNDEFINED;
- if (re) {
- pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern));
- if (JS_IsUndefined(flags1)) {
- bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode));
- obj = js_create_from_ctor(ctx, new_target, JS_CLASS_REGEXP);
- if (JS_IsException(obj))
- goto fail;
- goto no_compilation;
- } else {
- flags = JS_DupValue(ctx, flags1);
- }
- } else {
- if (pat_is_regexp) {
- pattern = JS_GetProperty(ctx, pat, JS_ATOM_source);
- if (JS_IsException(pattern))
- goto fail;
- if (JS_IsUndefined(flags1)) {
- flags = JS_GetProperty(ctx, pat, JS_ATOM_flags);
- if (JS_IsException(flags))
- goto fail;
- } else {
- flags = JS_DupValue(ctx, flags1);
- }
- } else {
- pattern = JS_DupValue(ctx, pat);
- flags = JS_DupValue(ctx, flags1);
- }
- if (JS_IsUndefined(pattern)) {
- pattern = JS_AtomToString(ctx, JS_ATOM_empty_string);
- } else {
- val = pattern;
- pattern = JS_ToString(ctx, val);
- JS_FreeValue(ctx, val);
- if (JS_IsException(pattern))
- goto fail;
- }
- }
- obj = js_create_from_ctor(ctx, new_target, JS_CLASS_REGEXP);
- if (JS_IsException(obj))
- goto fail;
- bc = js_compile_regexp(ctx, pattern, flags);
- if (JS_IsException(bc))
- goto fail;
- JS_FreeValue(ctx, flags);
- no_compilation:
- return js_regexp_set_internal(ctx, obj, pattern, bc);
- fail:
- JS_FreeValue(ctx, pattern);
- JS_FreeValue(ctx, flags);
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- static JSValue js_regexp_compile(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSRegExp *re1, *re;
- JSValueConst pattern1, flags1;
- JSValue bc, pattern;
- re = js_get_regexp(ctx, this_val, TRUE);
- if (!re)
- return JS_EXCEPTION;
- pattern1 = argv[0];
- flags1 = argv[1];
- re1 = js_get_regexp(ctx, pattern1, FALSE);
- if (re1) {
- if (!JS_IsUndefined(flags1))
- return JS_ThrowTypeError(ctx, "flags must be undefined");
- pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->pattern));
- bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->bytecode));
- } else {
- bc = JS_UNDEFINED;
- if (JS_IsUndefined(pattern1))
- pattern = JS_AtomToString(ctx, JS_ATOM_empty_string);
- else
- pattern = JS_ToString(ctx, pattern1);
- if (JS_IsException(pattern))
- goto fail;
- bc = js_compile_regexp(ctx, pattern, flags1);
- if (JS_IsException(bc))
- goto fail;
- }
- JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern));
- JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode));
- re->pattern = JS_VALUE_GET_STRING(pattern);
- re->bytecode = JS_VALUE_GET_STRING(bc);
- if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
- JS_NewInt32(ctx, 0)) < 0)
- return JS_EXCEPTION;
- return JS_DupValue(ctx, this_val);
- fail:
- JS_FreeValue(ctx, pattern);
- JS_FreeValue(ctx, bc);
- return JS_EXCEPTION;
- }
- static JSValue js_regexp_get_source(JSContext *ctx, JSValueConst this_val)
- {
- JSRegExp *re;
- JSString *p;
- StringBuffer b_s, *b = &b_s;
- int i, n, c, c2, bra;
- if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
- return JS_ThrowTypeErrorNotAnObject(ctx);
- if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP]))
- goto empty_regex;
- re = js_get_regexp(ctx, this_val, TRUE);
- if (!re)
- return JS_EXCEPTION;
- p = re->pattern;
- if (p->len == 0) {
- empty_regex:
- return js_new_string8(ctx, "(?:)");
- }
- string_buffer_init2(ctx, b, p->len, p->is_wide_char);
- /* Escape '/' and newline sequences as needed */
- bra = 0;
- for (i = 0, n = p->len; i < n;) {
- c2 = -1;
- switch (c = string_get(p, i++)) {
- case '\\':
- if (i < n)
- c2 = string_get(p, i++);
- break;
- case ']':
- bra = 0;
- break;
- case '[':
- if (!bra) {
- if (i < n && string_get(p, i) == ']')
- c2 = string_get(p, i++);
- bra = 1;
- }
- break;
- case '\n':
- c = '\\';
- c2 = 'n';
- break;
- case '\r':
- c = '\\';
- c2 = 'r';
- break;
- case '/':
- if (!bra) {
- c = '\\';
- c2 = '/';
- }
- break;
- }
- string_buffer_putc16(b, c);
- if (c2 >= 0)
- string_buffer_putc16(b, c2);
- }
- return string_buffer_end(b);
- }
- static JSValue js_regexp_get_flag(JSContext *ctx, JSValueConst this_val, int mask)
- {
- JSRegExp *re;
- int flags;
- if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
- return JS_ThrowTypeErrorNotAnObject(ctx);
- re = js_get_regexp(ctx, this_val, FALSE);
- if (!re) {
- if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP]))
- return JS_UNDEFINED;
- else
- return JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP);
- }
- flags = lre_get_flags(re->bytecode->u.str8);
- return JS_NewBool(ctx, flags & mask);
- }
- #define RE_FLAG_COUNT 8
- static JSValue js_regexp_get_flags(JSContext *ctx, JSValueConst this_val)
- {
- char str[RE_FLAG_COUNT], *p = str;
- int res, i;
- static const int flag_atom[RE_FLAG_COUNT] = {
- JS_ATOM_hasIndices,
- JS_ATOM_global,
- JS_ATOM_ignoreCase,
- JS_ATOM_multiline,
- JS_ATOM_dotAll,
- JS_ATOM_unicode,
- JS_ATOM_unicodeSets,
- JS_ATOM_sticky,
- };
- static const char flag_char[RE_FLAG_COUNT] = { 'd', 'g', 'i', 'm', 's', 'u', 'v', 'y' };
-
- if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
- return JS_ThrowTypeErrorNotAnObject(ctx);
- for(i = 0; i < RE_FLAG_COUNT; i++) {
- res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, flag_atom[i]));
- if (res < 0)
- goto exception;
- if (res)
- *p++ = flag_char[i];
- }
- return JS_NewStringLen(ctx, str, p - str);
- exception:
- return JS_EXCEPTION;
- }
- static JSValue js_regexp_toString(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue pattern, flags;
- StringBuffer b_s, *b = &b_s;
- if (!JS_IsObject(this_val))
- return JS_ThrowTypeErrorNotAnObject(ctx);
- string_buffer_init(ctx, b, 0);
- string_buffer_putc8(b, '/');
- pattern = JS_GetProperty(ctx, this_val, JS_ATOM_source);
- if (string_buffer_concat_value_free(b, pattern))
- goto fail;
- string_buffer_putc8(b, '/');
- flags = JS_GetProperty(ctx, this_val, JS_ATOM_flags);
- if (string_buffer_concat_value_free(b, flags))
- goto fail;
- return string_buffer_end(b);
- fail:
- string_buffer_free(b);
- return JS_EXCEPTION;
- }
- int lre_check_stack_overflow(void *opaque, size_t alloca_size)
- {
- JSContext *ctx = opaque;
- return js_check_stack_overflow(ctx->rt, alloca_size);
- }
- int lre_check_timeout(void *opaque)
- {
- JSContext *ctx = opaque;
- JSRuntime *rt = ctx->rt;
- return (rt->interrupt_handler &&
- rt->interrupt_handler(rt, rt->interrupt_opaque));
- }
- void *lre_realloc(void *opaque, void *ptr, size_t size)
- {
- JSContext *ctx = opaque;
- /* No JS exception is raised here */
- return js_realloc_rt(ctx->rt, ptr, size);
- }
- static JSValue js_regexp_escape(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue str;
- StringBuffer b_s, *b = &b_s;
- JSString *p;
- uint32_t c;
- char s[16];
- int i, i0;
-
- if (!JS_IsString(argv[0]))
- return JS_ThrowTypeError(ctx, "not a string");
- str = JS_ToString(ctx, argv[0]); /* must call it to linearlize ropes */
- if (JS_IsException(str))
- return JS_EXCEPTION;
- p = JS_VALUE_GET_STRING(str);
- string_buffer_init2(ctx, b, 0, p->is_wide_char);
- for (i = 0; i < p->len; ) {
- i0 = i;
- c = string_getc(p, &i);
- if (c < 33) {
- if (c >= 9 && c <= 13) {
- string_buffer_putc8(b, '\\');
- string_buffer_putc8(b, "tnvfr"[c - 9]);
- } else {
- goto hex2;
- }
- } else if (c < 128) {
- if ((c >= '0' && c <= '9')
- || (c >= 'A' && c <= 'Z')
- || (c >= 'a' && c <= 'z')) {
- if (i0 == 0)
- goto hex2;
- } else if (strchr(",-=<>#&!%:;@~'`\"", c)) {
- goto hex2;
- } else if (c != '_') {
- string_buffer_putc8(b, '\\');
- }
- string_buffer_putc8(b, c);
- } else if (c < 256) {
- hex2:
- snprintf(s, sizeof(s), "\\x%02x", c);
- string_buffer_puts8(b, s);
- } else if (is_surrogate(c) || lre_is_space(c)) {
- snprintf(s, sizeof(s), "\\u%04x", c);
- string_buffer_puts8(b, s);
- } else {
- string_buffer_putc(b, c);
- }
- }
- JS_FreeValue(ctx, str);
- return string_buffer_end(b);
- }
- /* this_val must be of JS_CLASS_REGEXP */
- static force_inline int js_regexp_get_lastIndex(JSContext *ctx, int64_t *plast_index,
- JSValueConst this_val)
- {
- JSObject *p = JS_VALUE_GET_OBJ(this_val);
-
- /* lastIndex is always the first property (it is not configurable) */
- if (likely(JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT)) {
- *plast_index = max_int(JS_VALUE_GET_INT(p->prop[0].u.value), 0);
- return 0;
- } else {
- return JS_ToLengthFree(ctx, plast_index, JS_DupValue(ctx, p->prop[0].u.value));
- }
- }
- /* this_val must be of JS_CLASS_REGEXP */
- static force_inline int js_regexp_set_lastIndex(JSContext *ctx, JSValueConst this_val,
- int last_index)
- {
- JSObject *p = JS_VALUE_GET_OBJ(this_val);
-
- /* lastIndex is always the first property (it is not configurable) */
- if (likely(JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT &&
- (get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE))) {
- set_value(ctx, &p->prop[0].u.value, JS_NewInt32(ctx, last_index));
- } else {
- if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
- JS_NewInt32(ctx, last_index)) < 0)
- return -1;
- }
- return 0;
- }
- static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);
- JSString *str;
- JSValue t, ret, str_val, obj, groups;
- JSValue indices, indices_groups;
- uint8_t *re_bytecode;
- uint8_t **capture, *str_buf;
- int rc, capture_count, shift, i, re_flags, alloc_count;
- int64_t last_index;
- const char *group_name_ptr;
- JSObject *p_obj;
- JSAtom group_name;
-
- if (!re)
- return JS_EXCEPTION;
- str_val = JS_ToString(ctx, argv[0]);
- if (JS_IsException(str_val))
- return JS_EXCEPTION;
- ret = JS_EXCEPTION;
- obj = JS_NULL;
- groups = JS_UNDEFINED;
- indices = JS_UNDEFINED;
- indices_groups = JS_UNDEFINED;
- capture = NULL;
- group_name = JS_ATOM_NULL;
-
- if (js_regexp_get_lastIndex(ctx, &last_index, this_val))
- goto fail;
- re_bytecode = re->bytecode->u.str8;
- re_flags = lre_get_flags(re_bytecode);
- if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) {
- last_index = 0;
- }
- str = JS_VALUE_GET_STRING(str_val);
- alloc_count = lre_get_alloc_count(re_bytecode);
- if (alloc_count > 0) {
- capture = js_malloc(ctx, sizeof(capture[0]) * alloc_count);
- if (!capture)
- goto fail;
- }
- capture_count = lre_get_capture_count(re_bytecode);
- shift = str->is_wide_char;
- str_buf = str->u.str8;
- if (last_index > str->len) {
- rc = 2;
- } else {
- rc = lre_exec(capture, re_bytecode,
- str_buf, last_index, str->len,
- shift, ctx);
- }
- if (rc != 1) {
- if (rc >= 0) {
- if (rc == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) {
- if (js_regexp_set_lastIndex(ctx, this_val, 0) < 0)
- goto fail;
- }
- } else {
- if (rc == LRE_RET_TIMEOUT) {
- JS_ThrowInterrupted(ctx);
- } else {
- JS_ThrowInternalError(ctx, "out of memory in regexp execution");
- }
- goto fail;
- }
- } else {
- int prop_flags;
- JSProperty props[4];
-
- if (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) {
- if (js_regexp_set_lastIndex(ctx, this_val,
- (capture[1] - str_buf) >> shift) < 0)
- goto fail;
- }
- prop_flags = JS_PROP_C_W_E | JS_PROP_THROW;
- group_name_ptr = lre_get_groupnames(re_bytecode);
- if (group_name_ptr) {
- groups = JS_NewObjectProto(ctx, JS_NULL);
- if (JS_IsException(groups))
- goto fail;
- }
- if (re_flags & LRE_FLAG_INDICES) {
- indices = JS_NewArray(ctx);
- if (JS_IsException(indices))
- goto fail;
- if (group_name_ptr) {
- indices_groups = JS_NewObjectProto(ctx, JS_NULL);
- if (JS_IsException(indices_groups))
- goto fail;
- }
- }
- props[0].u.value = JS_NewInt32(ctx, capture_count); /* length */
- props[1].u.value = JS_NewInt32(ctx, (capture[0] - str_buf) >> shift); /* index */
- props[2].u.value = str_val; /* input */
- props[3].u.value = JS_DupValue(ctx, groups); /* groups */
- str_val = JS_UNDEFINED;
- obj = JS_NewObjectFromShape(ctx, js_dup_shape(ctx->regexp_result_shape),
- JS_CLASS_ARRAY, props);
- if (JS_IsException(obj))
- goto fail;
- p_obj = JS_VALUE_GET_OBJ(obj);
- if (expand_fast_array(ctx, p_obj, capture_count))
- goto fail;
-
- for(i = 0; i < capture_count; i++) {
- uint8_t **match = &capture[2 * i];
- int start = -1;
- int end = -1;
- JSValue val;
- if (group_name_ptr && i > 0) {
- if (*group_name_ptr) {
- /* XXX: slow, should create a shape when the regexp is
- compiled */
- group_name = JS_NewAtom(ctx, group_name_ptr);
- if (group_name == JS_ATOM_NULL)
- goto fail;
- }
- group_name_ptr += strlen(group_name_ptr) + LRE_GROUP_NAME_TRAILER_LEN;
- }
- if (match[0] && match[1]) {
- start = (match[0] - str_buf) >> shift;
- end = (match[1] - str_buf) >> shift;
- }
- if (!JS_IsUndefined(indices)) {
- val = JS_UNDEFINED;
- if (start != -1) {
- val = JS_NewArray(ctx);
- if (JS_IsException(val))
- goto fail;
- if (JS_DefinePropertyValueUint32(ctx, val, 0,
- JS_NewInt32(ctx, start),
- prop_flags) < 0) {
- JS_FreeValue(ctx, val);
- goto fail;
- }
- if (JS_DefinePropertyValueUint32(ctx, val, 1,
- JS_NewInt32(ctx, end),
- prop_flags) < 0) {
- JS_FreeValue(ctx, val);
- goto fail;
- }
- }
- if (group_name != JS_ATOM_NULL) {
- /* JS_HasProperty() cannot fail here */
- if (!JS_IsUndefined(val) ||
- !JS_HasProperty(ctx, indices_groups, group_name)) {
- if (JS_DefinePropertyValue(ctx, indices_groups,
- group_name, JS_DupValue(ctx, val), prop_flags) < 0) {
- JS_FreeValue(ctx, val);
- goto fail;
- }
- }
- }
- if (JS_DefinePropertyValueUint32(ctx, indices, i, val,
- prop_flags) < 0) {
- goto fail;
- }
- }
- val = JS_UNDEFINED;
- if (start != -1) {
- val = js_sub_string(ctx, str, start, end);
- if (JS_IsException(val))
- goto fail;
- }
- if (group_name != JS_ATOM_NULL) {
- /* JS_HasProperty() cannot fail here */
- if (!JS_IsUndefined(val) ||
- !JS_HasProperty(ctx, groups, group_name)) {
- if (JS_DefinePropertyValue(ctx, groups, group_name,
- JS_DupValue(ctx, val),
- prop_flags) < 0) {
- JS_FreeValue(ctx, val);
- goto fail;
- }
- }
- JS_FreeAtom(ctx, group_name);
- group_name = JS_ATOM_NULL;
- }
- p_obj->u.array.u.values[p_obj->u.array.count++] = val;
- }
- if (!JS_IsUndefined(indices)) {
- t = indices_groups, indices_groups = JS_UNDEFINED;
- if (JS_DefinePropertyValue(ctx, indices, JS_ATOM_groups,
- t, prop_flags) < 0) {
- goto fail;
- }
- t = indices, indices = JS_UNDEFINED;
- if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_indices,
- t, prop_flags) < 0) {
- goto fail;
- }
- }
- }
- ret = obj;
- obj = JS_UNDEFINED;
- fail:
- JS_FreeAtom(ctx, group_name);
- JS_FreeValue(ctx, indices_groups);
- JS_FreeValue(ctx, indices);
- JS_FreeValue(ctx, str_val);
- JS_FreeValue(ctx, groups);
- JS_FreeValue(ctx, obj);
- js_free(ctx, capture);
- return ret;
- }
- /* XXX: add group names support */
- static JSValue js_regexp_replace(JSContext *ctx, JSValueConst this_val, JSValueConst arg,
- JSValueConst rep_val)
- {
- JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);
- JSString *str;
- JSValue str_val;
- uint8_t *re_bytecode;
- int ret;
- uint8_t **capture, *str_buf;
- int capture_count, alloc_count, shift, re_flags;
- int next_src_pos, start, end;
- int64_t last_index;
- StringBuffer b_s, *b = &b_s;
- JSString *rp = JS_VALUE_GET_STRING(rep_val);
- const char *group_name_ptr;
- BOOL fullUnicode;
-
- if (!re)
- return JS_EXCEPTION;
- re_bytecode = re->bytecode->u.str8;
- group_name_ptr = lre_get_groupnames(re_bytecode);
- if (group_name_ptr)
- return JS_UNDEFINED; /* group names are not supported yet */
-
- string_buffer_init(ctx, b, 0);
- capture = NULL;
- str_val = JS_ToString(ctx, arg);
- if (JS_IsException(str_val))
- goto fail;
- str = JS_VALUE_GET_STRING(str_val);
- re_flags = lre_get_flags(re_bytecode);
- if (re_flags & LRE_FLAG_GLOBAL) {
- if (js_regexp_set_lastIndex(ctx, this_val, 0))
- goto fail;
- }
- if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) {
- last_index = 0;
- } else {
- if (js_regexp_get_lastIndex(ctx, &last_index, this_val))
- goto fail;
- }
- alloc_count = lre_get_alloc_count(re_bytecode);
- if (alloc_count > 0) {
- capture = js_malloc(ctx, sizeof(capture[0]) * alloc_count);
- if (!capture)
- goto fail;
- }
- capture_count = lre_get_capture_count(re_bytecode);
- fullUnicode = ((re_flags & (LRE_FLAG_UNICODE | LRE_FLAG_UNICODE_SETS)) != 0);
- shift = str->is_wide_char;
- str_buf = str->u.str8;
- next_src_pos = 0;
- for (;;) {
- if (last_index > str->len) {
- ret = 0;
- } else {
- ret = lre_exec(capture, re_bytecode,
- str_buf, last_index, str->len, shift, ctx);
- }
- if (ret != 1) {
- if (ret >= 0) {
- if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) {
- if (js_regexp_set_lastIndex(ctx, this_val, 0) < 0)
- goto fail;
- }
- } else {
- if (ret == LRE_RET_TIMEOUT) {
- JS_ThrowInterrupted(ctx);
- } else {
- JS_ThrowInternalError(ctx, "out of memory in regexp execution");
- }
- goto fail;
- }
- break;
- }
- start = (capture[0] - str_buf) >> shift;
- end = (capture[1] - str_buf) >> shift;
- last_index = end;
- if (next_src_pos < start) {
- if (string_buffer_concat(b, str, next_src_pos, start))
- goto fail;
- }
- if (rp->len != 0) {
- if (js_string_GetSubstitution(ctx, b, JS_UNDEFINED, str, start,
- JS_UNDEFINED, JS_UNDEFINED, rep_val,
- capture, capture_count)) {
- goto fail;
- }
- }
- next_src_pos = end;
- if (!(re_flags & LRE_FLAG_GLOBAL)) {
- if (re_flags & LRE_FLAG_STICKY) {
- if (js_regexp_set_lastIndex(ctx, this_val, end) < 0)
- goto fail;
- }
- break;
- }
- if (end == start) {
- end = string_advance_index(str, end, fullUnicode);
- }
- last_index = end;
- }
- if (string_buffer_concat(b, str, next_src_pos, str->len))
- goto fail;
- JS_FreeValue(ctx, str_val);
- js_free(ctx, capture);
- return string_buffer_end(b);
- fail:
- JS_FreeValue(ctx, str_val);
- js_free(ctx, capture);
- string_buffer_free(b);
- return JS_EXCEPTION;
- }
- static JSValue JS_RegExpExec(JSContext *ctx, JSValueConst r, JSValueConst s)
- {
- JSValue method, ret;
- method = JS_GetProperty(ctx, r, JS_ATOM_exec);
- if (JS_IsException(method))
- return method;
- if (JS_IsFunction(ctx, method)) {
- ret = JS_CallFree(ctx, method, r, 1, &s);
- if (JS_IsException(ret))
- return ret;
- if (!JS_IsObject(ret) && !JS_IsNull(ret)) {
- JS_FreeValue(ctx, ret);
- return JS_ThrowTypeError(ctx, "RegExp exec method must return an object or null");
- }
- return ret;
- }
- JS_FreeValue(ctx, method);
- return js_regexp_exec(ctx, r, 1, &s);
- }
- static JSValue js_regexp_test(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue val;
- BOOL ret;
- val = JS_RegExpExec(ctx, this_val, argv[0]);
- if (JS_IsException(val))
- return JS_EXCEPTION;
- ret = !JS_IsNull(val);
- JS_FreeValue(ctx, val);
- return JS_NewBool(ctx, ret);
- }
- static JSValue js_regexp_Symbol_match(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- // [Symbol.match](str)
- JSValueConst rx = this_val;
- JSValue A, S, flags, result, matchStr;
- int global, n, fullUnicode, isEmpty;
- JSString *p;
- if (!JS_IsObject(rx))
- return JS_ThrowTypeErrorNotAnObject(ctx);
- A = JS_UNDEFINED;
- flags = JS_UNDEFINED;
- result = JS_UNDEFINED;
- matchStr = JS_UNDEFINED;
- S = JS_ToString(ctx, argv[0]);
- if (JS_IsException(S))
- goto exception;
- flags = JS_GetProperty(ctx, rx, JS_ATOM_flags);
- if (JS_IsException(flags))
- goto exception;
- flags = JS_ToStringFree(ctx, flags);
- if (JS_IsException(flags))
- goto exception;
- p = JS_VALUE_GET_STRING(flags);
- global = (-1 != string_indexof_char(p, 'g', 0));
- if (!global) {
- A = JS_RegExpExec(ctx, rx, S);
- } else {
- fullUnicode = (string_indexof_char(p, 'u', 0) >= 0 ||
- string_indexof_char(p, 'v', 0) >= 0);
- if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0)
- goto exception;
- A = JS_NewArray(ctx);
- if (JS_IsException(A))
- goto exception;
- n = 0;
- for(;;) {
- JS_FreeValue(ctx, result);
- result = JS_RegExpExec(ctx, rx, S);
- if (JS_IsException(result))
- goto exception;
- if (JS_IsNull(result))
- break;
- matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));
- if (JS_IsException(matchStr))
- goto exception;
- isEmpty = JS_IsEmptyString(matchStr);
- if (JS_DefinePropertyValueInt64(ctx, A, n++, matchStr, JS_PROP_C_W_E | JS_PROP_THROW) < 0)
- goto exception;
- if (isEmpty) {
- int64_t thisIndex, nextIndex;
- if (JS_ToLengthFree(ctx, &thisIndex,
- JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0)
- goto exception;
- p = JS_VALUE_GET_STRING(S);
- nextIndex = string_advance_index(p, thisIndex, fullUnicode);
- if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0)
- goto exception;
- }
- }
- if (n == 0) {
- JS_FreeValue(ctx, A);
- A = JS_NULL;
- }
- }
- JS_FreeValue(ctx, result);
- JS_FreeValue(ctx, flags);
- JS_FreeValue(ctx, S);
- return A;
- exception:
- JS_FreeValue(ctx, A);
- JS_FreeValue(ctx, result);
- JS_FreeValue(ctx, flags);
- JS_FreeValue(ctx, S);
- return JS_EXCEPTION;
- }
- typedef struct JSRegExpStringIteratorData {
- JSValue iterating_regexp;
- JSValue iterated_string;
- BOOL global;
- BOOL unicode;
- BOOL done;
- } JSRegExpStringIteratorData;
- static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data;
- if (it) {
- JS_FreeValueRT(rt, it->iterating_regexp);
- JS_FreeValueRT(rt, it->iterated_string);
- js_free_rt(rt, it);
- }
- }
- static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data;
- if (it) {
- JS_MarkValue(rt, it->iterating_regexp, mark_func);
- JS_MarkValue(rt, it->iterated_string, mark_func);
- }
- }
- static JSValue js_regexp_string_iterator_next(JSContext *ctx,
- JSValueConst this_val,
- int argc, JSValueConst *argv,
- BOOL *pdone, int magic)
- {
- JSRegExpStringIteratorData *it;
- JSValueConst R, S;
- JSValue matchStr = JS_UNDEFINED, match = JS_UNDEFINED;
- JSString *sp;
- it = JS_GetOpaque2(ctx, this_val, JS_CLASS_REGEXP_STRING_ITERATOR);
- if (!it)
- goto exception;
- if (it->done) {
- *pdone = TRUE;
- return JS_UNDEFINED;
- }
- R = it->iterating_regexp;
- S = it->iterated_string;
- match = JS_RegExpExec(ctx, R, S);
- if (JS_IsException(match))
- goto exception;
- if (JS_IsNull(match)) {
- it->done = TRUE;
- *pdone = TRUE;
- return JS_UNDEFINED;
- } else if (it->global) {
- matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, match, 0));
- if (JS_IsException(matchStr))
- goto exception;
- if (JS_IsEmptyString(matchStr)) {
- int64_t thisIndex, nextIndex;
- if (JS_ToLengthFree(ctx, &thisIndex,
- JS_GetProperty(ctx, R, JS_ATOM_lastIndex)) < 0)
- goto exception;
- sp = JS_VALUE_GET_STRING(S);
- nextIndex = string_advance_index(sp, thisIndex, it->unicode);
- if (JS_SetProperty(ctx, R, JS_ATOM_lastIndex,
- JS_NewInt64(ctx, nextIndex)) < 0)
- goto exception;
- }
- JS_FreeValue(ctx, matchStr);
- } else {
- it->done = TRUE;
- }
- *pdone = FALSE;
- return match;
- exception:
- JS_FreeValue(ctx, match);
- JS_FreeValue(ctx, matchStr);
- *pdone = FALSE;
- return JS_EXCEPTION;
- }
- static JSValue js_regexp_Symbol_matchAll(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- // [Symbol.matchAll](str)
- JSValueConst R = this_val;
- JSValue S, C, flags, matcher, iter;
- JSValueConst args[2];
- JSString *strp;
- int64_t lastIndex;
- JSRegExpStringIteratorData *it;
- if (!JS_IsObject(R))
- return JS_ThrowTypeErrorNotAnObject(ctx);
- C = JS_UNDEFINED;
- flags = JS_UNDEFINED;
- matcher = JS_UNDEFINED;
- iter = JS_UNDEFINED;
- S = JS_ToString(ctx, argv[0]);
- if (JS_IsException(S))
- goto exception;
- C = JS_SpeciesConstructor(ctx, R, ctx->regexp_ctor);
- if (JS_IsException(C))
- goto exception;
- flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, R, JS_ATOM_flags));
- if (JS_IsException(flags))
- goto exception;
- args[0] = R;
- args[1] = flags;
- matcher = JS_CallConstructor(ctx, C, 2, args);
- if (JS_IsException(matcher))
- goto exception;
- if (JS_ToLengthFree(ctx, &lastIndex,
- JS_GetProperty(ctx, R, JS_ATOM_lastIndex)))
- goto exception;
- if (JS_SetProperty(ctx, matcher, JS_ATOM_lastIndex,
- JS_NewInt64(ctx, lastIndex)) < 0)
- goto exception;
- iter = JS_NewObjectClass(ctx, JS_CLASS_REGEXP_STRING_ITERATOR);
- if (JS_IsException(iter))
- goto exception;
- it = js_malloc(ctx, sizeof(*it));
- if (!it)
- goto exception;
- it->iterating_regexp = matcher;
- it->iterated_string = S;
- strp = JS_VALUE_GET_STRING(flags);
- it->global = string_indexof_char(strp, 'g', 0) >= 0;
- it->unicode = (string_indexof_char(strp, 'u', 0) >= 0 ||
- string_indexof_char(strp, 'v', 0) >= 0);
- it->done = FALSE;
- JS_SetOpaque(iter, it);
- JS_FreeValue(ctx, C);
- JS_FreeValue(ctx, flags);
- return iter;
- exception:
- JS_FreeValue(ctx, S);
- JS_FreeValue(ctx, C);
- JS_FreeValue(ctx, flags);
- JS_FreeValue(ctx, matcher);
- JS_FreeValue(ctx, iter);
- return JS_EXCEPTION;
- }
- typedef struct ValueBuffer {
- JSContext *ctx;
- JSValue *arr;
- JSValue def[4];
- int len;
- int size;
- int error_status;
- } ValueBuffer;
- static int value_buffer_init(JSContext *ctx, ValueBuffer *b)
- {
- b->ctx = ctx;
- b->len = 0;
- b->size = 4;
- b->error_status = 0;
- b->arr = b->def;
- return 0;
- }
- static void value_buffer_free(ValueBuffer *b)
- {
- while (b->len > 0)
- JS_FreeValue(b->ctx, b->arr[--b->len]);
- if (b->arr != b->def)
- js_free(b->ctx, b->arr);
- b->arr = b->def;
- b->size = 4;
- }
- static int value_buffer_append(ValueBuffer *b, JSValue val)
- {
- if (b->error_status)
- return -1;
- if (b->len >= b->size) {
- int new_size = (b->len + (b->len >> 1) + 31) & ~16;
- size_t slack;
- JSValue *new_arr;
- if (b->arr == b->def) {
- new_arr = js_realloc2(b->ctx, NULL, sizeof(*b->arr) * new_size, &slack);
- if (new_arr)
- memcpy(new_arr, b->def, sizeof b->def);
- } else {
- new_arr = js_realloc2(b->ctx, b->arr, sizeof(*b->arr) * new_size, &slack);
- }
- if (!new_arr) {
- value_buffer_free(b);
- JS_FreeValue(b->ctx, val);
- b->error_status = -1;
- return -1;
- }
- new_size += slack / sizeof(*new_arr);
- b->arr = new_arr;
- b->size = new_size;
- }
- b->arr[b->len++] = val;
- return 0;
- }
- /* find in 'p' or its prototypes */
- static JSShapeProperty *find_property_regexp(JSProperty **ppr,
- JSObject *p, JSAtom atom)
- {
- JSShapeProperty *prs;
- for(;;) {
- prs = find_own_property(ppr, p, atom);
- if (prs)
- return prs;
- p = p->shape->proto;
- if (!p)
- return NULL;
- if (p->is_exotic)
- return NULL;
- }
- }
- static BOOL check_regexp_getter(JSContext *ctx,
- JSObject *p, JSAtom atom,
- JSCFunction *func, int magic)
- {
- JSProperty *pr;
- JSShapeProperty *prs;
- prs = find_property_regexp(&pr, p, atom);
- if (!prs)
- return FALSE;
- if ((prs->flags & JS_PROP_TMASK) != JS_PROP_GETSET)
- return FALSE;
- if (!pr->u.getset.getter)
- return FALSE;
- return JS_IsCFunction(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter),
- func, magic);
- }
- static BOOL js_is_standard_regexp(JSContext *ctx, JSValueConst obj)
- {
- JSObject *p;
- JSProperty *pr;
- JSShapeProperty *prs;
- JSCFunctionType ft;
-
- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
- return FALSE;
- p = JS_VALUE_GET_OBJ(obj);
- if (p->class_id != JS_CLASS_REGEXP)
- return FALSE;
- /* check that the lastIndex is a number (no side effect while getting it) */
- prs = find_own_property(&pr, p, JS_ATOM_lastIndex);
- if (!prs)
- return FALSE;
- if (!JS_IsNumber(pr->u.value))
- return FALSE;
- /* check the 'exec' method. */
- prs = find_property_regexp(&pr, p, JS_ATOM_exec);
- if (!prs)
- return FALSE;
- if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL)
- return FALSE;
- if (!JS_IsCFunction(ctx, pr->u.value, js_regexp_exec, 0))
- return FALSE;
- /* check the flag getters */
- ft.getter = js_regexp_get_flags;
- if (!check_regexp_getter(ctx, p, JS_ATOM_flags, ft.generic, 0))
- return FALSE;
- ft.getter_magic = js_regexp_get_flag;
- if (!check_regexp_getter(ctx, p, JS_ATOM_global, ft.generic, LRE_FLAG_GLOBAL))
- return FALSE;
- if (!check_regexp_getter(ctx, p, JS_ATOM_unicode, ft.generic, LRE_FLAG_UNICODE))
- return FALSE;
- /* XXX: need to check all accessors, need a faster way. */
- return TRUE;
- }
- static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- // [Symbol.replace](str, rep)
- JSValueConst rx = this_val, rep = argv[1];
- JSValueConst args[6];
- JSValue flags, str, rep_val, matched, tab, rep_str, namedCaptures, res;
- JSString *p, *sp;
- StringBuffer b_s, *b = &b_s;
- ValueBuffer v_b, *results = &v_b;
- int nextSourcePosition, n, j, functionalReplace, is_global, fullUnicode;
- uint32_t nCaptures;
- int64_t position;
- if (!JS_IsObject(rx))
- return JS_ThrowTypeErrorNotAnObject(ctx);
- string_buffer_init(ctx, b, 0);
- value_buffer_init(ctx, results);
- rep_val = JS_UNDEFINED;
- matched = JS_UNDEFINED;
- tab = JS_UNDEFINED;
- flags = JS_UNDEFINED;
- rep_str = JS_UNDEFINED;
- namedCaptures = JS_UNDEFINED;
- str = JS_ToString(ctx, argv[0]);
- if (JS_IsException(str))
- goto exception;
- sp = JS_VALUE_GET_STRING(str);
- functionalReplace = JS_IsFunction(ctx, rep);
- if (!functionalReplace) {
- rep_val = JS_ToString(ctx, rep);
- if (JS_IsException(rep_val))
- goto exception;
- }
- if (!functionalReplace && js_is_standard_regexp(ctx, rx)) {
- /* use faster version for simple cases */
- res = js_regexp_replace(ctx, rx, str, rep_val);
- if (!JS_IsUndefined(res))
- goto done;
- }
-
- flags = JS_GetProperty(ctx, rx, JS_ATOM_flags);
- if (JS_IsException(flags))
- goto exception;
- flags = JS_ToStringFree(ctx, flags);
- if (JS_IsException(flags))
- goto exception;
- p = JS_VALUE_GET_STRING(flags);
- fullUnicode = 0;
- is_global = (-1 != string_indexof_char(p, 'g', 0));
- if (is_global) {
- fullUnicode = (string_indexof_char(p, 'u', 0) >= 0 ||
- string_indexof_char(p, 'v', 0) >= 0);
- if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0)
- goto exception;
- }
- for(;;) {
- JSValue result;
- result = JS_RegExpExec(ctx, rx, str);
- if (JS_IsException(result))
- goto exception;
- if (JS_IsNull(result))
- break;
- if (value_buffer_append(results, result) < 0)
- goto exception;
- if (!is_global)
- break;
- JS_FreeValue(ctx, matched);
- matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));
- if (JS_IsException(matched))
- goto exception;
- if (JS_IsEmptyString(matched)) {
- /* always advance of at least one char */
- int64_t thisIndex, nextIndex;
- if (JS_ToLengthFree(ctx, &thisIndex, JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0)
- goto exception;
- nextIndex = string_advance_index(sp, thisIndex, fullUnicode);
- if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0)
- goto exception;
- }
- }
- nextSourcePosition = 0;
- for(j = 0; j < results->len; j++) {
- JSValueConst result;
- result = results->arr[j];
- if (js_get_length32(ctx, &nCaptures, result) < 0)
- goto exception;
- JS_FreeValue(ctx, matched);
- matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));
- if (JS_IsException(matched))
- goto exception;
- if (JS_ToLengthFree(ctx, &position, JS_GetProperty(ctx, result, JS_ATOM_index)))
- goto exception;
- if (position > sp->len)
- position = sp->len;
- else if (position < 0)
- position = 0;
- /* ignore substition if going backward (can happen
- with custom regexp object) */
- JS_FreeValue(ctx, tab);
- tab = JS_NewArray(ctx);
- if (JS_IsException(tab))
- goto exception;
- if (JS_DefinePropertyValueInt64(ctx, tab, 0, JS_DupValue(ctx, matched),
- JS_PROP_C_W_E | JS_PROP_THROW) < 0)
- goto exception;
- for(n = 1; n < nCaptures; n++) {
- JSValue capN;
- capN = JS_GetPropertyInt64(ctx, result, n);
- if (JS_IsException(capN))
- goto exception;
- if (!JS_IsUndefined(capN)) {
- capN = JS_ToStringFree(ctx, capN);
- if (JS_IsException(capN))
- goto exception;
- }
- if (JS_DefinePropertyValueInt64(ctx, tab, n, capN,
- JS_PROP_C_W_E | JS_PROP_THROW) < 0)
- goto exception;
- }
- JS_FreeValue(ctx, namedCaptures);
- namedCaptures = JS_GetProperty(ctx, result, JS_ATOM_groups);
- if (JS_IsException(namedCaptures))
- goto exception;
- if (functionalReplace) {
- if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_NewInt32(ctx, position), JS_PROP_C_W_E | JS_PROP_THROW) < 0)
- goto exception;
- if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_DupValue(ctx, str), JS_PROP_C_W_E | JS_PROP_THROW) < 0)
- goto exception;
- if (!JS_IsUndefined(namedCaptures)) {
- if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_DupValue(ctx, namedCaptures), JS_PROP_C_W_E | JS_PROP_THROW) < 0)
- goto exception;
- }
- args[0] = JS_UNDEFINED;
- args[1] = tab;
- JS_FreeValue(ctx, rep_str);
- rep_str = JS_ToStringFree(ctx, js_function_apply(ctx, rep, 2, args, 0));
- } else {
- JSValue namedCaptures1;
- StringBuffer b1_s, *b1 = &b1_s;
- int ret;
-
- if (!JS_IsUndefined(namedCaptures)) {
- namedCaptures1 = JS_ToObject(ctx, namedCaptures);
- if (JS_IsException(namedCaptures1))
- goto exception;
- } else {
- namedCaptures1 = JS_UNDEFINED;
- }
- JS_FreeValue(ctx, rep_str);
-
- string_buffer_init(ctx, b1, 0);
- ret = js_string_GetSubstitution(ctx, b1, matched, sp, position,
- tab, namedCaptures1, rep_val,
- NULL, 0);
- rep_str = string_buffer_end(b1);
- JS_FreeValue(ctx, namedCaptures1);
- if (ret)
- goto exception;
- }
- if (JS_IsException(rep_str))
- goto exception;
- if (position >= nextSourcePosition) {
- string_buffer_concat(b, sp, nextSourcePosition, position);
- string_buffer_concat_value(b, rep_str);
- nextSourcePosition = position + JS_VALUE_GET_STRING(matched)->len;
- }
- }
- string_buffer_concat(b, sp, nextSourcePosition, sp->len);
- res = string_buffer_end(b);
- goto done1;
- exception:
- res = JS_EXCEPTION;
- done:
- string_buffer_free(b);
- done1:
- value_buffer_free(results);
- JS_FreeValue(ctx, rep_val);
- JS_FreeValue(ctx, matched);
- JS_FreeValue(ctx, flags);
- JS_FreeValue(ctx, tab);
- JS_FreeValue(ctx, rep_str);
- JS_FreeValue(ctx, namedCaptures);
- JS_FreeValue(ctx, str);
- return res;
- }
- static JSValue js_regexp_Symbol_search(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValueConst rx = this_val;
- JSValue str, previousLastIndex, currentLastIndex, result, index;
- if (!JS_IsObject(rx))
- return JS_ThrowTypeErrorNotAnObject(ctx);
- result = JS_UNDEFINED;
- currentLastIndex = JS_UNDEFINED;
- previousLastIndex = JS_UNDEFINED;
- str = JS_ToString(ctx, argv[0]);
- if (JS_IsException(str))
- goto exception;
- previousLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex);
- if (JS_IsException(previousLastIndex))
- goto exception;
- if (!js_same_value(ctx, previousLastIndex, JS_NewInt32(ctx, 0))) {
- if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) {
- goto exception;
- }
- }
- result = JS_RegExpExec(ctx, rx, str);
- if (JS_IsException(result))
- goto exception;
- currentLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex);
- if (JS_IsException(currentLastIndex))
- goto exception;
- if (js_same_value(ctx, currentLastIndex, previousLastIndex)) {
- JS_FreeValue(ctx, previousLastIndex);
- } else {
- if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, previousLastIndex) < 0) {
- previousLastIndex = JS_UNDEFINED;
- goto exception;
- }
- }
- JS_FreeValue(ctx, str);
- JS_FreeValue(ctx, currentLastIndex);
- if (JS_IsNull(result)) {
- return JS_NewInt32(ctx, -1);
- } else {
- index = JS_GetProperty(ctx, result, JS_ATOM_index);
- JS_FreeValue(ctx, result);
- return index;
- }
- exception:
- JS_FreeValue(ctx, result);
- JS_FreeValue(ctx, str);
- JS_FreeValue(ctx, currentLastIndex);
- JS_FreeValue(ctx, previousLastIndex);
- return JS_EXCEPTION;
- }
- static JSValue js_regexp_Symbol_split(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- // [Symbol.split](str, limit)
- JSValueConst rx = this_val;
- JSValueConst args[2];
- JSValue str, ctor, splitter, A, flags, z, sub;
- JSString *strp;
- uint32_t lim, size, p, q;
- int unicodeMatching;
- int64_t lengthA, e, numberOfCaptures, i;
- if (!JS_IsObject(rx))
- return JS_ThrowTypeErrorNotAnObject(ctx);
- ctor = JS_UNDEFINED;
- splitter = JS_UNDEFINED;
- A = JS_UNDEFINED;
- flags = JS_UNDEFINED;
- z = JS_UNDEFINED;
- str = JS_ToString(ctx, argv[0]);
- if (JS_IsException(str))
- goto exception;
- ctor = JS_SpeciesConstructor(ctx, rx, ctx->regexp_ctor);
- if (JS_IsException(ctor))
- goto exception;
- flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_flags));
- if (JS_IsException(flags))
- goto exception;
- strp = JS_VALUE_GET_STRING(flags);
- unicodeMatching = (string_indexof_char(strp, 'u', 0) >= 0 ||
- string_indexof_char(strp, 'v', 0) >= 0);
- if (string_indexof_char(strp, 'y', 0) < 0) {
- flags = JS_ConcatString3(ctx, "", flags, "y");
- if (JS_IsException(flags))
- goto exception;
- }
- args[0] = rx;
- args[1] = flags;
- splitter = JS_CallConstructor(ctx, ctor, 2, args);
- if (JS_IsException(splitter))
- goto exception;
- A = JS_NewArray(ctx);
- if (JS_IsException(A))
- goto exception;
- lengthA = 0;
- if (JS_IsUndefined(argv[1])) {
- lim = 0xffffffff;
- } else {
- if (JS_ToUint32(ctx, &lim, argv[1]) < 0)
- goto exception;
- if (lim == 0)
- goto done;
- }
- strp = JS_VALUE_GET_STRING(str);
- p = q = 0;
- size = strp->len;
- if (size == 0) {
- z = JS_RegExpExec(ctx, splitter, str);
- if (JS_IsException(z))
- goto exception;
- if (JS_IsNull(z))
- goto add_tail;
- goto done;
- }
- while (q < size) {
- if (JS_SetProperty(ctx, splitter, JS_ATOM_lastIndex, JS_NewInt32(ctx, q)) < 0)
- goto exception;
- JS_FreeValue(ctx, z);
- z = JS_RegExpExec(ctx, splitter, str);
- if (JS_IsException(z))
- goto exception;
- if (JS_IsNull(z)) {
- q = string_advance_index(strp, q, unicodeMatching);
- } else {
- if (JS_ToLengthFree(ctx, &e, JS_GetProperty(ctx, splitter, JS_ATOM_lastIndex)))
- goto exception;
- if (e > size)
- e = size;
- if (e == p) {
- q = string_advance_index(strp, q, unicodeMatching);
- } else {
- sub = js_sub_string(ctx, strp, p, q);
- if (JS_IsException(sub))
- goto exception;
- if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub,
- JS_PROP_C_W_E | JS_PROP_THROW) < 0)
- goto exception;
- if (lengthA == lim)
- goto done;
- p = e;
- if (js_get_length64(ctx, &numberOfCaptures, z))
- goto exception;
- for(i = 1; i < numberOfCaptures; i++) {
- sub = JS_GetPropertyInt64(ctx, z, i);
- if (JS_IsException(sub))
- goto exception;
- if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0)
- goto exception;
- if (lengthA == lim)
- goto done;
- }
- q = p;
- }
- }
- }
- add_tail:
- if (p > size)
- p = size;
- sub = js_sub_string(ctx, strp, p, size);
- if (JS_IsException(sub))
- goto exception;
- if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0)
- goto exception;
- goto done;
- exception:
- JS_FreeValue(ctx, A);
- A = JS_EXCEPTION;
- done:
- JS_FreeValue(ctx, str);
- JS_FreeValue(ctx, ctor);
- JS_FreeValue(ctx, splitter);
- JS_FreeValue(ctx, flags);
- JS_FreeValue(ctx, z);
- return A;
- }
- static const JSCFunctionListEntry js_regexp_funcs[] = {
- JS_CFUNC_DEF("escape", 1, js_regexp_escape ),
- JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
- };
- static const JSCFunctionListEntry js_regexp_proto_funcs[] = {
- JS_CGETSET_DEF("flags", js_regexp_get_flags, NULL ),
- JS_CGETSET_DEF("source", js_regexp_get_source, NULL ),
- JS_CGETSET_MAGIC_DEF("global", js_regexp_get_flag, NULL, LRE_FLAG_GLOBAL ),
- JS_CGETSET_MAGIC_DEF("ignoreCase", js_regexp_get_flag, NULL, LRE_FLAG_IGNORECASE ),
- JS_CGETSET_MAGIC_DEF("multiline", js_regexp_get_flag, NULL, LRE_FLAG_MULTILINE ),
- JS_CGETSET_MAGIC_DEF("dotAll", js_regexp_get_flag, NULL, LRE_FLAG_DOTALL ),
- JS_CGETSET_MAGIC_DEF("unicode", js_regexp_get_flag, NULL, LRE_FLAG_UNICODE ),
- JS_CGETSET_MAGIC_DEF("unicodeSets", js_regexp_get_flag, NULL, LRE_FLAG_UNICODE_SETS ),
- JS_CGETSET_MAGIC_DEF("sticky", js_regexp_get_flag, NULL, LRE_FLAG_STICKY ),
- JS_CGETSET_MAGIC_DEF("hasIndices", js_regexp_get_flag, NULL, LRE_FLAG_INDICES ),
- JS_CFUNC_DEF("exec", 1, js_regexp_exec ),
- JS_CFUNC_DEF("compile", 2, js_regexp_compile ),
- JS_CFUNC_DEF("test", 1, js_regexp_test ),
- JS_CFUNC_DEF("toString", 0, js_regexp_toString ),
- JS_CFUNC_DEF("[Symbol.replace]", 2, js_regexp_Symbol_replace ),
- JS_CFUNC_DEF("[Symbol.match]", 1, js_regexp_Symbol_match ),
- JS_CFUNC_DEF("[Symbol.matchAll]", 1, js_regexp_Symbol_matchAll ),
- JS_CFUNC_DEF("[Symbol.search]", 1, js_regexp_Symbol_search ),
- JS_CFUNC_DEF("[Symbol.split]", 2, js_regexp_Symbol_split ),
- };
- static const JSCFunctionListEntry js_regexp_string_iterator_proto_funcs[] = {
- JS_ITERATOR_NEXT_DEF("next", 0, js_regexp_string_iterator_next, 0 ),
- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "RegExp String Iterator", JS_PROP_CONFIGURABLE ),
- };
- void JS_AddIntrinsicRegExpCompiler(JSContext *ctx)
- {
- ctx->compile_regexp = js_compile_regexp;
- }
- int JS_AddIntrinsicRegExp(JSContext *ctx)
- {
- JSValue obj;
- JS_AddIntrinsicRegExpCompiler(ctx);
- obj = JS_NewCConstructor(ctx, JS_CLASS_REGEXP, "RegExp",
- js_regexp_constructor, 2, JS_CFUNC_constructor_or_func, 0,
- JS_UNDEFINED,
- js_regexp_funcs, countof(js_regexp_funcs),
- js_regexp_proto_funcs, countof(js_regexp_proto_funcs),
- 0);
- if (JS_IsException(obj))
- return -1;
- ctx->regexp_ctor = obj;
-
- ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR] =
- JS_NewObjectProtoList(ctx, ctx->class_proto[JS_CLASS_ITERATOR],
- js_regexp_string_iterator_proto_funcs,
- countof(js_regexp_string_iterator_proto_funcs));
- if (JS_IsException(ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR]))
- return -1;
- ctx->regexp_shape = js_new_shape2(ctx, get_proto_obj(ctx->class_proto[JS_CLASS_REGEXP]),
- JS_PROP_INITIAL_HASH_SIZE, 1);
- if (!ctx->regexp_shape)
- return -1;
- if (add_shape_property(ctx, &ctx->regexp_shape, NULL,
- JS_ATOM_lastIndex, JS_PROP_WRITABLE))
- return -1;
- ctx->regexp_result_shape = js_new_shape2(ctx, get_proto_obj(ctx->class_proto[JS_CLASS_ARRAY]),
- JS_PROP_INITIAL_HASH_SIZE, 4);
- if (!ctx->regexp_result_shape)
- return -1;
- if (add_shape_property(ctx, &ctx->regexp_result_shape, NULL,
- JS_ATOM_length, JS_PROP_WRITABLE | JS_PROP_LENGTH))
- return -1;
- if (add_shape_property(ctx, &ctx->regexp_result_shape, NULL,
- JS_ATOM_index, JS_PROP_C_W_E))
- return -1;
- if (add_shape_property(ctx, &ctx->regexp_result_shape, NULL,
- JS_ATOM_input, JS_PROP_C_W_E))
- return -1;
- if (add_shape_property(ctx, &ctx->regexp_result_shape, NULL,
- JS_ATOM_groups, JS_PROP_C_W_E))
- return -1;
- return 0;
- }
- /* JSON */
- static int json_parse_expect(JSParseState *s, int tok)
- {
- if (s->token.val != tok) {
- /* XXX: dump token correctly in all cases */
- return js_parse_error(s, "expecting '%c'", tok);
- }
- return json_next_token(s);
- }
- typedef struct {
- int count;
- uint32_t hash_size;
- struct JSONParseRecordEntry *entries;
- uint32_t *hash_table;
- } JSONParseRecordObject;
-
- typedef struct JSONParseRecord {
- JSValue value;
- union {
- JSONParseRecordObject obj;
- struct {
- int count;
- struct JSONParseRecord *elements;
- } array;
- struct {
- uint32_t source_pos;
- uint32_t source_len;
- } primitive;
- } u;
- } JSONParseRecord;
- typedef struct JSONParseRecordEntry {
- JSAtom atom;
- uint32_t hash_next;
- JSONParseRecord parse_record;
- } JSONParseRecordEntry;
- static void json_parse_record_init_obj(JSContext *ctx, JSONParseRecord *pr, JSValueConst val)
- {
- pr->value = JS_DupValue(ctx, val);
- pr->u.obj.count = 0;
- pr->u.obj.entries = NULL;
- pr->u.obj.hash_table = NULL;
- pr->u.obj.hash_size = 0;
- }
- static void json_parse_record_init_array(JSContext *ctx, JSONParseRecord *pr, JSValueConst val)
- {
- pr->value = JS_DupValue(ctx, val);
- pr->u.array.count = 0;
- pr->u.array.elements = NULL;
- }
- static void json_parse_record_init_primitive(JSContext *ctx, JSONParseRecord *pr, JSValueConst val,
- uint32_t source_pos, uint32_t source_len)
- {
- pr->value = JS_DupValue(ctx, val);
- pr->u.primitive.source_pos = source_pos;
- pr->u.primitive.source_len = source_len;
- }
- static int json_parse_record_resize_hash(JSContext *ctx, JSONParseRecordObject *po, uint32_t new_hash_size)
- {
- uint32_t i, h, *new_hash_table;
- JSONParseRecordEntry *e;
- new_hash_table = js_malloc(ctx, sizeof(new_hash_table[0]) * new_hash_size);
- if (!new_hash_table)
- return -1;
- js_free(ctx, po->hash_table);
- po->hash_table = new_hash_table;
- po->hash_size = new_hash_size;
- for(i = 0; i < po->hash_size; i++) {
- po->hash_table[i] = -1;
- }
- for(i = 0; i < po->count; i++) {
- e = &po->entries[i];
- h = e->atom & (po->hash_size - 1);
- e->hash_next = po->hash_table[h];
- po->hash_table[h] = i;
- }
- return 0;
- }
- static JSONParseRecord *json_parse_record_add(JSContext *ctx, JSONParseRecord *pr, JSAtom key, int *psize)
- {
- JSONParseRecordObject *po = &pr->u.obj;
- JSONParseRecordEntry *e;
- JSONParseRecord *pr1;
- uint32_t h;
-
- if (js_resize_array(ctx, (void **)&po->entries, sizeof(po->entries[0]),
- psize, po->count + 1)) {
- return NULL;
- }
- /* don't use a hash table when the number of entries is small */
- if (po->count >= 8 && (po->count + 1) > po->hash_size) {
- int hash_bits = 32 - clz32(po->count);
- if (json_parse_record_resize_hash(ctx, po, 1 << hash_bits))
- return NULL;
- }
- e = &po->entries[po->count++];
- e->atom = JS_DupAtom(ctx, key);
- pr1 = &e->parse_record;
- pr1->value = JS_UNDEFINED;
- if (po->hash_size != 0) {
- h = key & (po->hash_size - 1);
- e->hash_next = po->hash_table[h];
- po->hash_table[h] = po->count - 1;
- }
- return pr1;
- }
- static JSONParseRecord *json_parse_record_find(JSONParseRecord *pr, JSAtom key)
- {
- JSONParseRecordObject *po = &pr->u.obj;
- JSONParseRecordEntry *e;
- uint32_t h, i;
-
- if (po->hash_size == 0) {
- for(i = 0; i < po->count; i++) {
- if (po->entries[i].atom == key)
- return &po->entries[i].parse_record;
- }
- } else {
- h = key & (po->hash_size - 1);
- i = po->hash_table[h];
- while (i != -1) {
- e = &po->entries[i];
- if (e->atom == key)
- return &e->parse_record;
- i = e->hash_next;
- }
- }
- return NULL;
- }
- static void json_free_parse_record(JSContext *ctx, JSONParseRecord *pr)
- {
- int i;
- if (!pr)
- return;
- if (JS_IsObject(pr->value)) {
- if (JS_IsArray(ctx, pr->value)) {
- for(i = 0; i < pr->u.array.count; i++) {
- json_free_parse_record(ctx, &pr->u.array.elements[i]);
- }
- js_free(ctx, pr->u.array.elements);
- } else {
- for(i = 0; i < pr->u.obj.count; i++) {
- JS_FreeAtom(ctx, pr->u.obj.entries[i].atom);
- json_free_parse_record(ctx, &pr->u.obj.entries[i].parse_record);
- }
- js_free(ctx, pr->u.obj.entries);
- js_free(ctx, pr->u.obj.hash_table);
- }
- }
- JS_FreeValue(ctx, pr->value);
- pr->value = JS_UNDEFINED; /* fail safe */
- }
- /* 'pr' can be NULL */
- static JSValue json_parse_value(JSParseState *s, JSONParseRecord *pr)
- {
- JSContext *ctx = s->ctx;
- JSValue val = JS_NULL;
- int ret;
- if (pr) {
- pr->value = JS_UNDEFINED;
- }
-
- switch(s->token.val) {
- case '{':
- {
- JSValue prop_val;
- JSAtom prop_name;
- JSONParseRecord *pr1;
- int pr_size;
-
- if (json_next_token(s))
- goto fail;
- val = JS_NewObject(ctx);
- if (JS_IsException(val))
- goto fail;
- if (pr) {
- json_parse_record_init_obj(ctx, pr, val);
- pr_size = 0;
- }
- if (s->token.val != '}') {
- for(;;) {
- if (s->token.val == TOK_STRING) {
- prop_name = JS_ValueToAtom(ctx, s->token.u.str.str);
- if (prop_name == JS_ATOM_NULL)
- goto fail;
- } else if (s->ext_json && s->token.val == TOK_IDENT) {
- prop_name = JS_DupAtom(ctx, s->token.u.ident.atom);
- } else {
- js_parse_error(s, "expecting property name");
- goto fail;
- }
- if (json_next_token(s))
- goto fail1;
- if (json_parse_expect(s, ':'))
- goto fail1;
- if (pr) {
- pr1 = json_parse_record_add(ctx, pr, prop_name, &pr_size);
- if (!pr1)
- goto fail1;
- } else {
- pr1 = NULL;
- }
- prop_val = json_parse_value(s, pr1);
- if (JS_IsException(prop_val)) {
- fail1:
- JS_FreeAtom(ctx, prop_name);
- goto fail;
- }
- ret = JS_DefinePropertyValue(ctx, val, prop_name,
- prop_val, JS_PROP_C_W_E);
- JS_FreeAtom(ctx, prop_name);
- if (ret < 0)
- goto fail;
- if (s->token.val != ',')
- break;
- if (json_next_token(s))
- goto fail;
- if (s->ext_json && s->token.val == '}')
- break;
- }
- }
- if (json_parse_expect(s, '}'))
- goto fail;
- }
- break;
- case '[':
- {
- JSValue el;
- uint32_t idx;
- JSONParseRecord *pr1;
- int pr_size;
-
- if (json_next_token(s))
- goto fail;
- val = JS_NewArray(ctx);
- if (JS_IsException(val))
- goto fail;
- if (pr) {
- json_parse_record_init_array(ctx, pr, val);
- pr_size = 0;
- }
- if (s->token.val != ']') {
- idx = 0;
- for(;;) {
- if (pr) {
- if (js_resize_array(ctx, (void **)&pr->u.array.elements, sizeof(pr->u.array.elements[0]),
- &pr_size, pr->u.array.count + 1))
- goto fail;
- pr1 = &pr->u.array.elements[pr->u.array.count++];
- pr1->value = JS_UNDEFINED;
- } else {
- pr1 = NULL;
- }
- el = json_parse_value(s, pr1);
- if (JS_IsException(el))
- goto fail;
- ret = JS_DefinePropertyValueUint32(ctx, val, idx, el, JS_PROP_C_W_E);
- if (ret < 0)
- goto fail;
- if (s->token.val != ',')
- break;
- if (json_next_token(s))
- goto fail;
- idx++;
- if (s->ext_json && s->token.val == ']')
- break;
- }
- }
- if (json_parse_expect(s, ']'))
- goto fail;
- }
- break;
- case TOK_STRING:
- val = JS_DupValue(ctx, s->token.u.str.str);
- if (pr) {
- json_parse_record_init_primitive(ctx, pr, val,
- s->token.ptr - s->buf_start,
- s->buf_ptr - s->token.ptr);
- }
- if (json_next_token(s))
- goto fail;
- break;
- case TOK_NUMBER:
- val = s->token.u.num.val;
- if (pr) {
- json_parse_record_init_primitive(ctx, pr, val,
- s->token.ptr - s->buf_start,
- s->buf_ptr - s->token.ptr);
- }
- if (json_next_token(s))
- goto fail;
- break;
- case TOK_IDENT:
- if (s->token.u.ident.atom == JS_ATOM_false ||
- s->token.u.ident.atom == JS_ATOM_true) {
- val = JS_NewBool(ctx, s->token.u.ident.atom == JS_ATOM_true);
- if (pr) {
- json_parse_record_init_primitive(ctx, pr, val,
- s->token.ptr - s->buf_start,
- s->buf_ptr - s->token.ptr);
- }
- } else if (s->token.u.ident.atom == JS_ATOM_null) {
- val = JS_NULL;
- if (pr) {
- json_parse_record_init_primitive(ctx, pr, val,
- s->token.ptr - s->buf_start,
- s->buf_ptr - s->token.ptr);
- }
- } else if (s->token.u.ident.atom == JS_ATOM_NaN && s->ext_json) {
- /* Note: json5 identifier handling is ambiguous e.g. is
- '{ NaN: 1 }' a valid JSON5 production ? */
- val = JS_NewFloat64(s->ctx, NAN);
- } else if (s->token.u.ident.atom == JS_ATOM_Infinity && s->ext_json) {
- val = JS_NewFloat64(s->ctx, INFINITY);
- } else {
- goto def_token;
- }
- if (json_next_token(s))
- goto fail;
- break;
- default:
- def_token:
- if (s->token.val == TOK_EOF) {
- js_parse_error(s, "Unexpected end of JSON input");
- } else {
- js_parse_error(s, "unexpected token: '%.*s'",
- (int)(s->buf_ptr - s->token.ptr), s->token.ptr);
- }
- goto fail;
- }
- return val;
- fail:
- json_free_parse_record(ctx, pr);
- JS_FreeValue(ctx, val);
- return JS_EXCEPTION;
- }
- JSValue JS_ParseJSON3(JSContext *ctx, const char *buf, size_t buf_len,
- const char *filename, int flags, JSONParseRecord *pr)
- {
- JSParseState s1, *s = &s1;
- JSValue val = JS_UNDEFINED;
- js_parse_init(ctx, s, buf, buf_len, filename);
- s->ext_json = ((flags & JS_PARSE_JSON_EXT) != 0);
- if (json_next_token(s))
- goto fail;
- val = json_parse_value(s, pr);
- if (JS_IsException(val))
- goto fail;
- if (s->token.val != TOK_EOF) {
- if (js_parse_error(s, "unexpected data at the end")) {
- json_free_parse_record(ctx, pr);
- goto fail;
- }
- }
- return val;
- fail:
- JS_FreeValue(ctx, val);
- free_token(s, &s->token);
- return JS_EXCEPTION;
- }
- JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len,
- const char *filename, int flags)
- {
- return JS_ParseJSON3(ctx, buf, buf_len, filename, flags, NULL);
- }
- JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len,
- const char *filename)
- {
- return JS_ParseJSON3(ctx, buf, buf_len, filename, 0, NULL);
- }
- /* if pr != NULL, then pr->value = holder by construction */
- static JSValue internalize_json_property(JSContext *ctx, JSValueConst holder,
- JSAtom name, JSValueConst reviver,
- const char *text_str, JSONParseRecord *pr)
- {
- JSValue val, new_el, name_val, res, context;
- JSValueConst args[3];
- int ret, is_array;
- uint32_t i, len = 0;
- JSAtom prop;
- JSPropertyEnum *atoms = NULL;
- if (js_check_stack_overflow(ctx->rt, 0)) {
- return JS_ThrowStackOverflow(ctx);
- }
- val = JS_GetProperty(ctx, holder, name);
- if (JS_IsException(val))
- return val;
- if (pr) {
- if (JS_IsArray(ctx, pr->value)) {
- if (__JS_AtomIsTaggedInt(name)) {
- uint32_t idx = __JS_AtomToUInt32(name);
- if (idx < pr->u.array.count) {
- pr = &pr->u.array.elements[idx];
- } else {
- pr = NULL;
- }
- }
- } else {
- pr = json_parse_record_find(pr, name);
- }
- if (pr && !js_same_value(ctx, pr->value, val)) {
- pr = NULL;
- }
- }
- context = JS_NewObject(ctx);
- if (JS_IsException(context))
- goto fail;
-
- if (JS_IsObject(val)) {
- is_array = JS_IsArray(ctx, val);
- if (is_array < 0)
- goto fail;
- if (is_array) {
- if (js_get_length32(ctx, &len, val))
- goto fail;
- } else {
- ret = JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, JS_VALUE_GET_OBJ(val), JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK);
- if (ret < 0)
- goto fail;
- }
- for(i = 0; i < len; i++) {
- if (is_array) {
- prop = JS_NewAtomUInt32(ctx, i);
- if (prop == JS_ATOM_NULL)
- goto fail;
- } else {
- prop = JS_DupAtom(ctx, atoms[i].atom);
- }
- new_el = internalize_json_property(ctx, val, prop, reviver, text_str, pr);
- if (JS_IsException(new_el)) {
- JS_FreeAtom(ctx, prop);
- goto fail;
- }
- if (JS_IsUndefined(new_el)) {
- ret = JS_DeleteProperty(ctx, val, prop, 0);
- } else {
- ret = JS_DefinePropertyValue(ctx, val, prop, new_el, JS_PROP_C_W_E);
- }
- JS_FreeAtom(ctx, prop);
- if (ret < 0)
- goto fail;
- }
- } else {
- if (pr) {
- new_el = JS_NewStringLen(ctx, text_str + pr->u.primitive.source_pos,
- pr->u.primitive.source_len);
- if (JS_IsException(new_el))
- goto fail;
- if (JS_DefinePropertyValue(ctx, context, JS_ATOM_source, new_el, JS_PROP_C_W_E) < 0)
- goto fail;
- }
- }
- JS_FreePropertyEnum(ctx, atoms, len);
- atoms = NULL;
- name_val = JS_AtomToValue(ctx, name);
- if (JS_IsException(name_val))
- goto fail;
- args[0] = name_val;
- args[1] = val;
- args[2] = context;
- res = JS_Call(ctx, reviver, holder, 3, args);
- JS_FreeValue(ctx, name_val);
- JS_FreeValue(ctx, val);
- JS_FreeValue(ctx, context);
- return res;
- fail:
- JS_FreePropertyEnum(ctx, atoms, len);
- JS_FreeValue(ctx, context);
- JS_FreeValue(ctx, val);
- return JS_EXCEPTION;
- }
- static JSValue js_json_parse(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue obj;
- const char *str;
- size_t len;
-
- str = JS_ToCStringLen(ctx, &len, argv[0]);
- if (!str)
- return JS_EXCEPTION;
- if (argc > 1 && JS_IsFunction(ctx, argv[1])) {
- JSONParseRecord pr_s, *pr = &pr_s, *pr1;
- JSValue root;
- JSValueConst reviver;
- int size;
-
- reviver = argv[1];
- root = JS_NewObject(ctx);
- if (JS_IsException(root))
- goto fail;
- json_parse_record_init_obj(ctx, pr, root);
- size = 0;
- pr1 = json_parse_record_add(ctx, pr, JS_ATOM_empty_string, &size);
- if (!pr1)
- goto fail1;
- obj = JS_ParseJSON3(ctx, str, len, "<input>", 0, pr1);
- if (JS_IsException(obj))
- goto fail1;
-
- if (JS_DefinePropertyValue(ctx, root, JS_ATOM_empty_string, obj,
- JS_PROP_C_W_E) < 0) {
- JS_FreeValue(ctx, obj);
- fail1:
- json_free_parse_record(ctx, pr);
- JS_FreeValue(ctx, root);
- goto fail;
- }
-
- obj = internalize_json_property(ctx, root, JS_ATOM_empty_string,
- reviver, str, pr);
- json_free_parse_record(ctx, pr);
- JS_FreeValue(ctx, root);
- } else {
- obj = JS_ParseJSON3(ctx, str, len, "<input>", 0, NULL);
- }
- JS_FreeCString(ctx, str);
- return obj;
- fail:
- JS_FreeCString(ctx, str);
- return JS_EXCEPTION;
- }
- static JSValue js_json_isRawJSON(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValueConst obj = argv[0];
- if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
- JSObject *p = JS_VALUE_GET_OBJ(obj);
- return JS_NewBool(ctx, p->class_id == JS_CLASS_RAWJSON);
- } else {
- return JS_FALSE;
- }
- }
- static BOOL is_valid_raw_json_char(int c)
- {
- return ((c >= 'a' && c <= 'z') ||
- (c >= '0' && c <= '9') ||
- c == '-' ||
- c == '"');
- }
- static JSValue js_json_rawJSON(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue str, res, obj;
- JSString *p;
- str = JS_ToString(ctx, argv[0]);
- if (JS_IsException(str))
- return str;
- p = JS_VALUE_GET_STRING(str);
- if (p->len == 0 ||
- !is_valid_raw_json_char(string_get(p, 0)) ||
- !is_valid_raw_json_char(string_get(p, p->len - 1))) {
- goto syntax_error;
- }
- res = js_json_parse(ctx, JS_UNDEFINED, 1, (JSValueConst *)&str);
- if (JS_IsException(res)) {
- syntax_error:
- JS_ThrowSyntaxError(ctx, "invalid rawJSON string");
- goto fail;
- }
- JS_FreeValue(ctx, res);
-
- obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_RAWJSON);
- if (JS_IsException(obj))
- goto fail;
- if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_rawJSON, str, JS_PROP_ENUMERABLE) < 0) {
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- JS_PreventExtensions(ctx, obj);
- return obj;
- fail:
- JS_FreeValue(ctx, str);
- return JS_EXCEPTION;
- }
- typedef struct JSONStringifyContext {
- JSValueConst replacer_func;
- JSValue stack;
- JSValue property_list;
- JSValue gap;
- JSValue empty;
- StringBuffer *b;
- } JSONStringifyContext;
- static int JS_ToQuotedString(JSContext *ctx, StringBuffer *b, JSValueConst val1)
- {
- JSValue val;
- JSString *p;
- int i;
- uint32_t c;
- char buf[16];
- val = JS_ToStringCheckObject(ctx, val1);
- if (JS_IsException(val))
- return -1;
- p = JS_VALUE_GET_STRING(val);
- if (string_buffer_putc8(b, '\"'))
- goto fail;
- for(i = 0; i < p->len; ) {
- c = string_getc(p, &i);
- switch(c) {
- case '\t':
- c = 't';
- goto quote;
- case '\r':
- c = 'r';
- goto quote;
- case '\n':
- c = 'n';
- goto quote;
- case '\b':
- c = 'b';
- goto quote;
- case '\f':
- c = 'f';
- goto quote;
- case '\"':
- case '\\':
- quote:
- if (string_buffer_putc8(b, '\\'))
- goto fail;
- if (string_buffer_putc8(b, c))
- goto fail;
- break;
- default:
- if (c < 32 || is_surrogate(c)) {
- snprintf(buf, sizeof(buf), "\\u%04x", c);
- if (string_buffer_puts8(b, buf))
- goto fail;
- } else {
- if (string_buffer_putc(b, c))
- goto fail;
- }
- break;
- }
- }
- if (string_buffer_putc8(b, '\"'))
- goto fail;
- JS_FreeValue(ctx, val);
- return 0;
- fail:
- JS_FreeValue(ctx, val);
- return -1;
- }
- static int JS_ToQuotedStringFree(JSContext *ctx, StringBuffer *b, JSValue val) {
- int ret = JS_ToQuotedString(ctx, b, val);
- JS_FreeValue(ctx, val);
- return ret;
- }
- static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc,
- JSValueConst holder, JSValue val, JSValueConst key)
- {
- JSValue v;
- JSValueConst args[2];
- /* check for object.toJSON method */
- /* ECMA specifies this is done only for Object and BigInt */
- if (JS_IsObject(val) || JS_IsBigInt(ctx, val)) {
- JSValue f = JS_GetProperty(ctx, val, JS_ATOM_toJSON);
- if (JS_IsException(f))
- goto exception;
- if (JS_IsFunction(ctx, f)) {
- v = JS_CallFree(ctx, f, val, 1, &key);
- JS_FreeValue(ctx, val);
- val = v;
- if (JS_IsException(val))
- goto exception;
- } else {
- JS_FreeValue(ctx, f);
- }
- }
- if (!JS_IsUndefined(jsc->replacer_func)) {
- args[0] = key;
- args[1] = val;
- v = JS_Call(ctx, jsc->replacer_func, holder, 2, args);
- JS_FreeValue(ctx, val);
- val = v;
- if (JS_IsException(val))
- goto exception;
- }
- switch (JS_VALUE_GET_NORM_TAG(val)) {
- case JS_TAG_OBJECT:
- if (JS_IsFunction(ctx, val))
- break;
- case JS_TAG_STRING:
- case JS_TAG_STRING_ROPE:
- case JS_TAG_INT:
- case JS_TAG_FLOAT64:
- case JS_TAG_BOOL:
- case JS_TAG_NULL:
- case JS_TAG_SHORT_BIG_INT:
- case JS_TAG_BIG_INT:
- case JS_TAG_EXCEPTION:
- return val;
- default:
- break;
- }
- JS_FreeValue(ctx, val);
- return JS_UNDEFINED;
- exception:
- JS_FreeValue(ctx, val);
- return JS_EXCEPTION;
- }
- static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,
- JSValueConst holder, JSValue val,
- JSValueConst indent)
- {
- JSValue indent1, sep, sep1, tab, v, prop;
- JSObject *p;
- int64_t i, len;
- int cl, ret;
- BOOL has_content;
- indent1 = JS_UNDEFINED;
- sep = JS_UNDEFINED;
- sep1 = JS_UNDEFINED;
- tab = JS_UNDEFINED;
- prop = JS_UNDEFINED;
- if (js_check_stack_overflow(ctx->rt, 0)) {
- JS_ThrowStackOverflow(ctx);
- goto exception;
- }
- if (JS_IsObject(val)) {
- p = JS_VALUE_GET_OBJ(val);
- cl = p->class_id;
- if (cl == JS_CLASS_STRING) {
- val = JS_ToStringFree(ctx, val);
- if (JS_IsException(val))
- goto exception;
- goto concat_primitive;
- } else if (cl == JS_CLASS_NUMBER) {
- val = JS_ToNumberFree(ctx, val);
- if (JS_IsException(val))
- goto exception;
- goto concat_primitive;
- } else if (cl == JS_CLASS_BOOLEAN || cl == JS_CLASS_BIG_INT) {
- /* This will thow the same error as for the primitive object */
- set_value(ctx, &val, JS_DupValue(ctx, p->u.object_data));
- goto concat_primitive;
- } else if (cl == JS_CLASS_RAWJSON) {
- JSValue val1;
- val1 = JS_GetProperty(ctx, val, JS_ATOM_rawJSON);
- if (JS_IsException(val1))
- goto exception;
- JS_FreeValue(ctx, val);
- val = val1;
- goto concat_value;
- }
- v = js_array_includes(ctx, jsc->stack, 1, (JSValueConst *)&val);
- if (JS_IsException(v))
- goto exception;
- if (JS_ToBoolFree(ctx, v)) {
- JS_ThrowTypeError(ctx, "circular reference");
- goto exception;
- }
- indent1 = JS_ConcatString(ctx, JS_DupValue(ctx, indent), JS_DupValue(ctx, jsc->gap));
- if (JS_IsException(indent1))
- goto exception;
- if (!JS_IsEmptyString(jsc->gap)) {
- sep = JS_ConcatString3(ctx, "\n", JS_DupValue(ctx, indent1), "");
- if (JS_IsException(sep))
- goto exception;
- sep1 = js_new_string8(ctx, " ");
- if (JS_IsException(sep1))
- goto exception;
- } else {
- sep = JS_DupValue(ctx, jsc->empty);
- sep1 = JS_DupValue(ctx, jsc->empty);
- }
- v = js_array_push(ctx, jsc->stack, 1, (JSValueConst *)&val, 0);
- if (check_exception_free(ctx, v))
- goto exception;
- ret = JS_IsArray(ctx, val);
- if (ret < 0)
- goto exception;
- if (ret) {
- if (js_get_length64(ctx, &len, val))
- goto exception;
- string_buffer_putc8(jsc->b, '[');
- for(i = 0; i < len; i++) {
- if (i > 0)
- string_buffer_putc8(jsc->b, ',');
- string_buffer_concat_value(jsc->b, sep);
- v = JS_GetPropertyInt64(ctx, val, i);
- if (JS_IsException(v))
- goto exception;
- /* XXX: could do this string conversion only when needed */
- prop = JS_ToStringFree(ctx, JS_NewInt64(ctx, i));
- if (JS_IsException(prop))
- goto exception;
- v = js_json_check(ctx, jsc, val, v, prop);
- JS_FreeValue(ctx, prop);
- prop = JS_UNDEFINED;
- if (JS_IsException(v))
- goto exception;
- if (JS_IsUndefined(v))
- v = JS_NULL;
- if (js_json_to_str(ctx, jsc, val, v, indent1))
- goto exception;
- }
- if (len > 0 && !JS_IsEmptyString(jsc->gap)) {
- string_buffer_putc8(jsc->b, '\n');
- string_buffer_concat_value(jsc->b, indent);
- }
- string_buffer_putc8(jsc->b, ']');
- } else {
- if (!JS_IsUndefined(jsc->property_list))
- tab = JS_DupValue(ctx, jsc->property_list);
- else
- tab = js_object_keys(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val, JS_ITERATOR_KIND_KEY);
- if (JS_IsException(tab))
- goto exception;
- if (js_get_length64(ctx, &len, tab))
- goto exception;
- string_buffer_putc8(jsc->b, '{');
- has_content = FALSE;
- for(i = 0; i < len; i++) {
- JS_FreeValue(ctx, prop);
- prop = JS_GetPropertyInt64(ctx, tab, i);
- if (JS_IsException(prop))
- goto exception;
- v = JS_GetPropertyValue(ctx, val, JS_DupValue(ctx, prop));
- if (JS_IsException(v))
- goto exception;
- v = js_json_check(ctx, jsc, val, v, prop);
- if (JS_IsException(v))
- goto exception;
- if (!JS_IsUndefined(v)) {
- if (has_content)
- string_buffer_putc8(jsc->b, ',');
- string_buffer_concat_value(jsc->b, sep);
- if (JS_ToQuotedString(ctx, jsc->b, prop)) {
- JS_FreeValue(ctx, v);
- goto exception;
- }
- string_buffer_putc8(jsc->b, ':');
- string_buffer_concat_value(jsc->b, sep1);
- if (js_json_to_str(ctx, jsc, val, v, indent1))
- goto exception;
- has_content = TRUE;
- }
- }
- if (has_content && !JS_IsEmptyString(jsc->gap)) {
- string_buffer_putc8(jsc->b, '\n');
- string_buffer_concat_value(jsc->b, indent);
- }
- string_buffer_putc8(jsc->b, '}');
- }
- if (check_exception_free(ctx, js_array_pop(ctx, jsc->stack, 0, NULL, 0)))
- goto exception;
- JS_FreeValue(ctx, val);
- JS_FreeValue(ctx, tab);
- JS_FreeValue(ctx, sep);
- JS_FreeValue(ctx, sep1);
- JS_FreeValue(ctx, indent1);
- JS_FreeValue(ctx, prop);
- return 0;
- }
- concat_primitive:
- switch (JS_VALUE_GET_NORM_TAG(val)) {
- case JS_TAG_STRING:
- case JS_TAG_STRING_ROPE:
- return JS_ToQuotedStringFree(ctx, jsc->b, val);
- case JS_TAG_FLOAT64:
- if (!isfinite(JS_VALUE_GET_FLOAT64(val))) {
- val = JS_NULL;
- }
- goto concat_value;
- case JS_TAG_INT:
- case JS_TAG_BOOL:
- case JS_TAG_NULL:
- concat_value:
- return string_buffer_concat_value_free(jsc->b, val);
- case JS_TAG_SHORT_BIG_INT:
- case JS_TAG_BIG_INT:
- /* reject big numbers: use toJSON method to override */
- JS_ThrowTypeError(ctx, "Do not know how to serialize a BigInt");
- goto exception;
- default:
- JS_FreeValue(ctx, val);
- return 0;
- }
- exception:
- JS_FreeValue(ctx, val);
- JS_FreeValue(ctx, tab);
- JS_FreeValue(ctx, sep);
- JS_FreeValue(ctx, sep1);
- JS_FreeValue(ctx, indent1);
- JS_FreeValue(ctx, prop);
- return -1;
- }
- JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj,
- JSValueConst replacer, JSValueConst space0)
- {
- StringBuffer b_s;
- JSONStringifyContext jsc_s, *jsc = &jsc_s;
- JSValue val, v, space, ret, wrapper;
- int res;
- int64_t i, j, n;
- jsc->replacer_func = JS_UNDEFINED;
- jsc->stack = JS_UNDEFINED;
- jsc->property_list = JS_UNDEFINED;
- jsc->gap = JS_UNDEFINED;
- jsc->b = &b_s;
- jsc->empty = JS_AtomToString(ctx, JS_ATOM_empty_string);
- ret = JS_UNDEFINED;
- wrapper = JS_UNDEFINED;
- string_buffer_init(ctx, jsc->b, 0);
- jsc->stack = JS_NewArray(ctx);
- if (JS_IsException(jsc->stack))
- goto exception;
- if (JS_IsFunction(ctx, replacer)) {
- jsc->replacer_func = replacer;
- } else {
- res = JS_IsArray(ctx, replacer);
- if (res < 0)
- goto exception;
- if (res) {
- /* XXX: enumeration is not fully correct */
- jsc->property_list = JS_NewArray(ctx);
- if (JS_IsException(jsc->property_list))
- goto exception;
- if (js_get_length64(ctx, &n, replacer))
- goto exception;
- for (i = j = 0; i < n; i++) {
- JSValue present;
- v = JS_GetPropertyInt64(ctx, replacer, i);
- if (JS_IsException(v))
- goto exception;
- if (JS_IsObject(v)) {
- JSObject *p = JS_VALUE_GET_OBJ(v);
- if (p->class_id == JS_CLASS_STRING ||
- p->class_id == JS_CLASS_NUMBER) {
- v = JS_ToStringFree(ctx, v);
- if (JS_IsException(v))
- goto exception;
- } else {
- JS_FreeValue(ctx, v);
- continue;
- }
- } else if (JS_IsNumber(v)) {
- v = JS_ToStringFree(ctx, v);
- if (JS_IsException(v))
- goto exception;
- } else if (!JS_IsString(v)) {
- JS_FreeValue(ctx, v);
- continue;
- }
- present = js_array_includes(ctx, jsc->property_list,
- 1, (JSValueConst *)&v);
- if (JS_IsException(present)) {
- JS_FreeValue(ctx, v);
- goto exception;
- }
- if (!JS_ToBoolFree(ctx, present)) {
- JS_SetPropertyInt64(ctx, jsc->property_list, j++, v);
- } else {
- JS_FreeValue(ctx, v);
- }
- }
- }
- }
- space = JS_DupValue(ctx, space0);
- if (JS_IsObject(space)) {
- JSObject *p = JS_VALUE_GET_OBJ(space);
- if (p->class_id == JS_CLASS_NUMBER) {
- space = JS_ToNumberFree(ctx, space);
- } else if (p->class_id == JS_CLASS_STRING) {
- space = JS_ToStringFree(ctx, space);
- }
- if (JS_IsException(space)) {
- JS_FreeValue(ctx, space);
- goto exception;
- }
- }
- if (JS_IsNumber(space)) {
- int n;
- if (JS_ToInt32Clamp(ctx, &n, space, 0, 10, 0))
- goto exception;
- jsc->gap = js_new_string8_len(ctx, " ", n);
- } else if (JS_IsString(space)) {
- JSString *p = JS_VALUE_GET_STRING(space);
- jsc->gap = js_sub_string(ctx, p, 0, min_int(p->len, 10));
- } else {
- jsc->gap = JS_DupValue(ctx, jsc->empty);
- }
- JS_FreeValue(ctx, space);
- if (JS_IsException(jsc->gap))
- goto exception;
- wrapper = JS_NewObject(ctx);
- if (JS_IsException(wrapper))
- goto exception;
- if (JS_DefinePropertyValue(ctx, wrapper, JS_ATOM_empty_string,
- JS_DupValue(ctx, obj), JS_PROP_C_W_E) < 0)
- goto exception;
- val = JS_DupValue(ctx, obj);
- val = js_json_check(ctx, jsc, wrapper, val, jsc->empty);
- if (JS_IsException(val))
- goto exception;
- if (JS_IsUndefined(val)) {
- ret = JS_UNDEFINED;
- goto done1;
- }
- if (js_json_to_str(ctx, jsc, wrapper, val, jsc->empty))
- goto exception;
- ret = string_buffer_end(jsc->b);
- goto done;
- exception:
- ret = JS_EXCEPTION;
- done1:
- string_buffer_free(jsc->b);
- done:
- JS_FreeValue(ctx, wrapper);
- JS_FreeValue(ctx, jsc->empty);
- JS_FreeValue(ctx, jsc->gap);
- JS_FreeValue(ctx, jsc->property_list);
- JS_FreeValue(ctx, jsc->stack);
- return ret;
- }
- static JSValue js_json_stringify(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- // stringify(val, replacer, space)
- return JS_JSONStringify(ctx, argv[0], argv[1], argv[2]);
- }
- static const JSCFunctionListEntry js_json_funcs[] = {
- JS_CFUNC_DEF("isRawJSON", 1, js_json_isRawJSON ),
- JS_CFUNC_DEF("parse", 2, js_json_parse ),
- JS_CFUNC_DEF("rawJSON", 1, js_json_rawJSON ),
- JS_CFUNC_DEF("stringify", 3, js_json_stringify ),
- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "JSON", JS_PROP_CONFIGURABLE ),
- };
- static const JSCFunctionListEntry js_json_obj[] = {
- JS_OBJECT_DEF("JSON", js_json_funcs, countof(js_json_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
- };
- int JS_AddIntrinsicJSON(JSContext *ctx)
- {
- /* add JSON as autoinit object */
- return JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_json_obj, countof(js_json_obj));
- }
- /* Reflect */
- static JSValue js_reflect_apply(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- return js_function_apply(ctx, argv[0], max_int(0, argc - 1), argv + 1, 2);
- }
- static JSValue js_reflect_construct(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValueConst func, array_arg, new_target;
- JSValue *tab, ret;
- uint32_t len;
- func = argv[0];
- array_arg = argv[1];
- if (argc > 2) {
- new_target = argv[2];
- if (!JS_IsConstructor(ctx, new_target))
- return JS_ThrowTypeErrorNotAConstructor(ctx, new_target);
- } else {
- new_target = func;
- }
- tab = build_arg_list(ctx, &len, array_arg);
- if (!tab)
- return JS_EXCEPTION;
- ret = JS_CallConstructor2(ctx, func, new_target, len, (JSValueConst *)tab);
- free_arg_list(ctx, tab, len);
- return ret;
- }
- static JSValue js_reflect_deleteProperty(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValueConst obj;
- JSAtom atom;
- int ret;
- obj = argv[0];
- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
- return JS_ThrowTypeErrorNotAnObject(ctx);
- atom = JS_ValueToAtom(ctx, argv[1]);
- if (unlikely(atom == JS_ATOM_NULL))
- return JS_EXCEPTION;
- ret = JS_DeleteProperty(ctx, obj, atom, 0);
- JS_FreeAtom(ctx, atom);
- if (ret < 0)
- return JS_EXCEPTION;
- else
- return JS_NewBool(ctx, ret);
- }
- static JSValue js_reflect_get(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValueConst obj, prop, receiver;
- JSAtom atom;
- JSValue ret;
- obj = argv[0];
- prop = argv[1];
- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
- return JS_ThrowTypeErrorNotAnObject(ctx);
- if (argc > 2)
- receiver = argv[2];
- else
- receiver = obj;
- atom = JS_ValueToAtom(ctx, prop);
- if (unlikely(atom == JS_ATOM_NULL))
- return JS_EXCEPTION;
- ret = JS_GetPropertyInternal(ctx, obj, atom, receiver, FALSE);
- JS_FreeAtom(ctx, atom);
- return ret;
- }
- static JSValue js_reflect_has(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValueConst obj, prop;
- JSAtom atom;
- int ret;
- obj = argv[0];
- prop = argv[1];
- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
- return JS_ThrowTypeErrorNotAnObject(ctx);
- atom = JS_ValueToAtom(ctx, prop);
- if (unlikely(atom == JS_ATOM_NULL))
- return JS_EXCEPTION;
- ret = JS_HasProperty(ctx, obj, atom);
- JS_FreeAtom(ctx, atom);
- if (ret < 0)
- return JS_EXCEPTION;
- else
- return JS_NewBool(ctx, ret);
- }
- static JSValue js_reflect_set(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValueConst obj, prop, val, receiver;
- int ret;
- JSAtom atom;
- obj = argv[0];
- prop = argv[1];
- val = argv[2];
- if (argc > 3)
- receiver = argv[3];
- else
- receiver = obj;
- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
- return JS_ThrowTypeErrorNotAnObject(ctx);
- atom = JS_ValueToAtom(ctx, prop);
- if (unlikely(atom == JS_ATOM_NULL))
- return JS_EXCEPTION;
- ret = JS_SetPropertyInternal(ctx, obj, atom,
- JS_DupValue(ctx, val), receiver, 0);
- JS_FreeAtom(ctx, atom);
- if (ret < 0)
- return JS_EXCEPTION;
- else
- return JS_NewBool(ctx, ret);
- }
- static JSValue js_reflect_setPrototypeOf(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- int ret;
- ret = JS_SetPrototypeInternal(ctx, argv[0], argv[1], FALSE);
- if (ret < 0)
- return JS_EXCEPTION;
- else
- return JS_NewBool(ctx, ret);
- }
- static JSValue js_reflect_ownKeys(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT)
- return JS_ThrowTypeErrorNotAnObject(ctx);
- return JS_GetOwnPropertyNames2(ctx, argv[0],
- JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK,
- JS_ITERATOR_KIND_KEY);
- }
- static const JSCFunctionListEntry js_reflect_funcs[] = {
- JS_CFUNC_DEF("apply", 3, js_reflect_apply ),
- JS_CFUNC_DEF("construct", 2, js_reflect_construct ),
- JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 1 ),
- JS_CFUNC_DEF("deleteProperty", 2, js_reflect_deleteProperty ),
- JS_CFUNC_DEF("get", 2, js_reflect_get ),
- JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 1 ),
- JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 1 ),
- JS_CFUNC_DEF("has", 2, js_reflect_has ),
- JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 1 ),
- JS_CFUNC_DEF("ownKeys", 1, js_reflect_ownKeys ),
- JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 1 ),
- JS_CFUNC_DEF("set", 3, js_reflect_set ),
- JS_CFUNC_DEF("setPrototypeOf", 2, js_reflect_setPrototypeOf ),
- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Reflect", JS_PROP_CONFIGURABLE ),
- };
- static const JSCFunctionListEntry js_reflect_obj[] = {
- JS_OBJECT_DEF("Reflect", js_reflect_funcs, countof(js_reflect_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
- };
- /* Proxy */
- static void js_proxy_finalizer(JSRuntime *rt, JSValue val)
- {
- JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY);
- if (s) {
- JS_FreeValueRT(rt, s->target);
- JS_FreeValueRT(rt, s->handler);
- js_free_rt(rt, s);
- }
- }
- static void js_proxy_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func)
- {
- JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY);
- if (s) {
- JS_MarkValue(rt, s->target, mark_func);
- JS_MarkValue(rt, s->handler, mark_func);
- }
- }
- static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx)
- {
- return JS_ThrowTypeError(ctx, "revoked proxy");
- }
- static JSProxyData *get_proxy_method(JSContext *ctx, JSValue *pmethod,
- JSValueConst obj, JSAtom name)
- {
- JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY);
- JSValue method;
- /* safer to test recursion in all proxy methods */
- if (js_check_stack_overflow(ctx->rt, 0)) {
- JS_ThrowStackOverflow(ctx);
- return NULL;
- }
- /* 's' should never be NULL */
- if (s->is_revoked) {
- JS_ThrowTypeErrorRevokedProxy(ctx);
- return NULL;
- }
- method = JS_GetProperty(ctx, s->handler, name);
- if (JS_IsException(method))
- return NULL;
- if (JS_IsNull(method))
- method = JS_UNDEFINED;
- *pmethod = method;
- return s;
- }
- static JSValue js_proxy_get_prototype(JSContext *ctx, JSValueConst obj)
- {
- JSProxyData *s;
- JSValue method, ret, proto1;
- int res;
- s = get_proxy_method(ctx, &method, obj, JS_ATOM_getPrototypeOf);
- if (!s)
- return JS_EXCEPTION;
- if (JS_IsUndefined(method))
- return JS_GetPrototype(ctx, s->target);
- ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);
- if (JS_IsException(ret))
- return ret;
- if (JS_VALUE_GET_TAG(ret) != JS_TAG_NULL &&
- JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {
- goto fail;
- }
- res = JS_IsExtensible(ctx, s->target);
- if (res < 0) {
- JS_FreeValue(ctx, ret);
- return JS_EXCEPTION;
- }
- if (!res) {
- /* check invariant */
- proto1 = JS_GetPrototype(ctx, s->target);
- if (JS_IsException(proto1)) {
- JS_FreeValue(ctx, ret);
- return JS_EXCEPTION;
- }
- if (!js_same_value(ctx, proto1, ret)) {
- JS_FreeValue(ctx, proto1);
- fail:
- JS_FreeValue(ctx, ret);
- return JS_ThrowTypeError(ctx, "proxy: inconsistent prototype");
- }
- JS_FreeValue(ctx, proto1);
- }
- return ret;
- }
- static int js_proxy_set_prototype(JSContext *ctx, JSValueConst obj,
- JSValueConst proto_val)
- {
- JSProxyData *s;
- JSValue method, ret, proto1;
- JSValueConst args[2];
- BOOL res;
- int res2;
- s = get_proxy_method(ctx, &method, obj, JS_ATOM_setPrototypeOf);
- if (!s)
- return -1;
- if (JS_IsUndefined(method))
- return JS_SetPrototypeInternal(ctx, s->target, proto_val, FALSE);
- args[0] = s->target;
- args[1] = proto_val;
- ret = JS_CallFree(ctx, method, s->handler, 2, args);
- if (JS_IsException(ret))
- return -1;
- res = JS_ToBoolFree(ctx, ret);
- if (!res)
- return FALSE;
- res2 = JS_IsExtensible(ctx, s->target);
- if (res2 < 0)
- return -1;
- if (!res2) {
- proto1 = JS_GetPrototype(ctx, s->target);
- if (JS_IsException(proto1))
- return -1;
- if (!js_same_value(ctx, proto_val, proto1)) {
- JS_FreeValue(ctx, proto1);
- JS_ThrowTypeError(ctx, "proxy: inconsistent prototype");
- return -1;
- }
- JS_FreeValue(ctx, proto1);
- }
- return TRUE;
- }
- static int js_proxy_is_extensible(JSContext *ctx, JSValueConst obj)
- {
- JSProxyData *s;
- JSValue method, ret;
- BOOL res;
- int res2;
- s = get_proxy_method(ctx, &method, obj, JS_ATOM_isExtensible);
- if (!s)
- return -1;
- if (JS_IsUndefined(method))
- return JS_IsExtensible(ctx, s->target);
- ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);
- if (JS_IsException(ret))
- return -1;
- res = JS_ToBoolFree(ctx, ret);
- res2 = JS_IsExtensible(ctx, s->target);
- if (res2 < 0)
- return res2;
- if (res != res2) {
- JS_ThrowTypeError(ctx, "proxy: inconsistent isExtensible");
- return -1;
- }
- return res;
- }
- static int js_proxy_prevent_extensions(JSContext *ctx, JSValueConst obj)
- {
- JSProxyData *s;
- JSValue method, ret;
- BOOL res;
- int res2;
- s = get_proxy_method(ctx, &method, obj, JS_ATOM_preventExtensions);
- if (!s)
- return -1;
- if (JS_IsUndefined(method))
- return JS_PreventExtensions(ctx, s->target);
- ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);
- if (JS_IsException(ret))
- return -1;
- res = JS_ToBoolFree(ctx, ret);
- if (res) {
- res2 = JS_IsExtensible(ctx, s->target);
- if (res2 < 0)
- return res2;
- if (res2) {
- JS_ThrowTypeError(ctx, "proxy: inconsistent preventExtensions");
- return -1;
- }
- }
- return res;
- }
- static int js_proxy_has(JSContext *ctx, JSValueConst obj, JSAtom atom)
- {
- JSProxyData *s;
- JSValue method, ret1, atom_val;
- int ret, res;
- JSObject *p;
- JSValueConst args[2];
- BOOL res2;
- s = get_proxy_method(ctx, &method, obj, JS_ATOM_has);
- if (!s)
- return -1;
- if (JS_IsUndefined(method))
- return JS_HasProperty(ctx, s->target, atom);
- atom_val = JS_AtomToValue(ctx, atom);
- if (JS_IsException(atom_val)) {
- JS_FreeValue(ctx, method);
- return -1;
- }
- args[0] = s->target;
- args[1] = atom_val;
- ret1 = JS_CallFree(ctx, method, s->handler, 2, args);
- JS_FreeValue(ctx, atom_val);
- if (JS_IsException(ret1))
- return -1;
- ret = JS_ToBoolFree(ctx, ret1);
- if (!ret) {
- JSPropertyDescriptor desc;
- p = JS_VALUE_GET_OBJ(s->target);
- res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom);
- if (res < 0)
- return -1;
- if (res) {
- res2 = !(desc.flags & JS_PROP_CONFIGURABLE);
- js_free_desc(ctx, &desc);
- if (res2 || !p->extensible) {
- JS_ThrowTypeError(ctx, "proxy: inconsistent has");
- return -1;
- }
- }
- }
- return ret;
- }
- static JSValue js_proxy_get(JSContext *ctx, JSValueConst obj, JSAtom atom,
- JSValueConst receiver)
- {
- JSProxyData *s;
- JSValue method, ret, atom_val;
- int res;
- JSValueConst args[3];
- JSPropertyDescriptor desc;
- s = get_proxy_method(ctx, &method, obj, JS_ATOM_get);
- if (!s)
- return JS_EXCEPTION;
- /* Note: recursion is possible thru the prototype of s->target */
- if (JS_IsUndefined(method))
- return JS_GetPropertyInternal(ctx, s->target, atom, receiver, FALSE);
- atom_val = JS_AtomToValue(ctx, atom);
- if (JS_IsException(atom_val)) {
- JS_FreeValue(ctx, method);
- return JS_EXCEPTION;
- }
- args[0] = s->target;
- args[1] = atom_val;
- args[2] = receiver;
- ret = JS_CallFree(ctx, method, s->handler, 3, args);
- JS_FreeValue(ctx, atom_val);
- if (JS_IsException(ret))
- return JS_EXCEPTION;
- res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);
- if (res < 0) {
- JS_FreeValue(ctx, ret);
- return JS_EXCEPTION;
- }
- if (res) {
- if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) {
- if (!js_same_value(ctx, desc.value, ret)) {
- goto fail;
- }
- } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET) {
- if (JS_IsUndefined(desc.getter) && !JS_IsUndefined(ret)) {
- fail:
- js_free_desc(ctx, &desc);
- JS_FreeValue(ctx, ret);
- return JS_ThrowTypeError(ctx, "proxy: inconsistent get");
- }
- }
- js_free_desc(ctx, &desc);
- }
- return ret;
- }
- static int js_proxy_set(JSContext *ctx, JSValueConst obj, JSAtom atom,
- JSValueConst value, JSValueConst receiver, int flags)
- {
- JSProxyData *s;
- JSValue method, ret1, atom_val;
- int ret, res;
- JSValueConst args[4];
- s = get_proxy_method(ctx, &method, obj, JS_ATOM_set);
- if (!s)
- return -1;
- if (JS_IsUndefined(method)) {
- return JS_SetPropertyInternal(ctx, s->target, atom,
- JS_DupValue(ctx, value), receiver,
- flags);
- }
- atom_val = JS_AtomToValue(ctx, atom);
- if (JS_IsException(atom_val)) {
- JS_FreeValue(ctx, method);
- return -1;
- }
- args[0] = s->target;
- args[1] = atom_val;
- args[2] = value;
- args[3] = receiver;
- ret1 = JS_CallFree(ctx, method, s->handler, 4, args);
- JS_FreeValue(ctx, atom_val);
- if (JS_IsException(ret1))
- return -1;
- ret = JS_ToBoolFree(ctx, ret1);
- if (ret) {
- JSPropertyDescriptor desc;
- res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);
- if (res < 0)
- return -1;
- if (res) {
- if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) {
- if (!js_same_value(ctx, desc.value, value)) {
- goto fail;
- }
- } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET && JS_IsUndefined(desc.setter)) {
- fail:
- js_free_desc(ctx, &desc);
- JS_ThrowTypeError(ctx, "proxy: inconsistent set");
- return -1;
- }
- js_free_desc(ctx, &desc);
- }
- } else {
- if ((flags & JS_PROP_THROW) ||
- ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
- JS_ThrowTypeError(ctx, "proxy: cannot set property");
- return -1;
- }
- }
- return ret;
- }
- static JSValue js_create_desc(JSContext *ctx, JSValueConst val,
- JSValueConst getter, JSValueConst setter,
- int flags)
- {
- JSValue ret;
- ret = JS_NewObject(ctx);
- if (JS_IsException(ret))
- return ret;
- if (flags & JS_PROP_HAS_GET) {
- JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, getter),
- JS_PROP_C_W_E);
- }
- if (flags & JS_PROP_HAS_SET) {
- JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, setter),
- JS_PROP_C_W_E);
- }
- if (flags & JS_PROP_HAS_VALUE) {
- JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, val),
- JS_PROP_C_W_E);
- }
- if (flags & JS_PROP_HAS_WRITABLE) {
- JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable,
- JS_NewBool(ctx, flags & JS_PROP_WRITABLE),
- JS_PROP_C_W_E);
- }
- if (flags & JS_PROP_HAS_ENUMERABLE) {
- JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable,
- JS_NewBool(ctx, flags & JS_PROP_ENUMERABLE),
- JS_PROP_C_W_E);
- }
- if (flags & JS_PROP_HAS_CONFIGURABLE) {
- JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable,
- JS_NewBool(ctx, flags & JS_PROP_CONFIGURABLE),
- JS_PROP_C_W_E);
- }
- return ret;
- }
- static int js_proxy_get_own_property(JSContext *ctx, JSPropertyDescriptor *pdesc,
- JSValueConst obj, JSAtom prop)
- {
- JSProxyData *s;
- JSValue method, trap_result_obj, prop_val;
- int res, target_desc_ret, ret;
- JSObject *p;
- JSValueConst args[2];
- JSPropertyDescriptor result_desc, target_desc;
- s = get_proxy_method(ctx, &method, obj, JS_ATOM_getOwnPropertyDescriptor);
- if (!s)
- return -1;
- p = JS_VALUE_GET_OBJ(s->target);
- if (JS_IsUndefined(method)) {
- return JS_GetOwnPropertyInternal(ctx, pdesc, p, prop);
- }
- prop_val = JS_AtomToValue(ctx, prop);
- if (JS_IsException(prop_val)) {
- JS_FreeValue(ctx, method);
- return -1;
- }
- args[0] = s->target;
- args[1] = prop_val;
- trap_result_obj = JS_CallFree(ctx, method, s->handler, 2, args);
- JS_FreeValue(ctx, prop_val);
- if (JS_IsException(trap_result_obj))
- return -1;
- if (!JS_IsObject(trap_result_obj) && !JS_IsUndefined(trap_result_obj)) {
- JS_FreeValue(ctx, trap_result_obj);
- goto fail;
- }
- target_desc_ret = JS_GetOwnPropertyInternal(ctx, &target_desc, p, prop);
- if (target_desc_ret < 0) {
- JS_FreeValue(ctx, trap_result_obj);
- return -1;
- }
- if (target_desc_ret)
- js_free_desc(ctx, &target_desc);
- if (JS_IsUndefined(trap_result_obj)) {
- if (target_desc_ret) {
- if (!(target_desc.flags & JS_PROP_CONFIGURABLE) || !p->extensible)
- goto fail;
- }
- ret = FALSE;
- } else {
- int flags1, extensible_target;
- extensible_target = JS_IsExtensible(ctx, s->target);
- if (extensible_target < 0) {
- JS_FreeValue(ctx, trap_result_obj);
- return -1;
- }
- res = js_obj_to_desc(ctx, &result_desc, trap_result_obj);
- JS_FreeValue(ctx, trap_result_obj);
- if (res < 0)
- return -1;
- /* convert the result_desc.flags to property flags */
- if (result_desc.flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
- result_desc.flags |= JS_PROP_GETSET;
- } else {
- result_desc.flags |= JS_PROP_NORMAL;
- }
- result_desc.flags &= (JS_PROP_C_W_E | JS_PROP_TMASK);
-
- if (target_desc_ret) {
- /* convert result_desc.flags to defineProperty flags */
- flags1 = result_desc.flags | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE;
- if (result_desc.flags & JS_PROP_GETSET)
- flags1 |= JS_PROP_HAS_GET | JS_PROP_HAS_SET;
- else
- flags1 |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE;
- /* XXX: not complete check: need to compare value &
- getter/setter as in defineproperty */
- if (!check_define_prop_flags(target_desc.flags, flags1))
- goto fail1;
- } else {
- if (!extensible_target)
- goto fail1;
- }
- if (!(result_desc.flags & JS_PROP_CONFIGURABLE)) {
- if (!target_desc_ret || (target_desc.flags & JS_PROP_CONFIGURABLE))
- goto fail1;
- if ((result_desc.flags &
- (JS_PROP_GETSET | JS_PROP_WRITABLE)) == 0 &&
- target_desc_ret &&
- (target_desc.flags & JS_PROP_WRITABLE) != 0) {
- /* proxy-missing-checks */
- fail1:
- js_free_desc(ctx, &result_desc);
- fail:
- JS_ThrowTypeError(ctx, "proxy: inconsistent getOwnPropertyDescriptor");
- return -1;
- }
- }
- ret = TRUE;
- if (pdesc) {
- *pdesc = result_desc;
- } else {
- js_free_desc(ctx, &result_desc);
- }
- }
- return ret;
- }
- static int js_proxy_define_own_property(JSContext *ctx, JSValueConst obj,
- JSAtom prop, JSValueConst val,
- JSValueConst getter, JSValueConst setter,
- int flags)
- {
- JSProxyData *s;
- JSValue method, ret1, prop_val, desc_val;
- int res, ret;
- JSObject *p;
- JSValueConst args[3];
- JSPropertyDescriptor desc;
- BOOL setting_not_configurable;
- s = get_proxy_method(ctx, &method, obj, JS_ATOM_defineProperty);
- if (!s)
- return -1;
- if (JS_IsUndefined(method)) {
- return JS_DefineProperty(ctx, s->target, prop, val, getter, setter, flags);
- }
- prop_val = JS_AtomToValue(ctx, prop);
- if (JS_IsException(prop_val)) {
- JS_FreeValue(ctx, method);
- return -1;
- }
- desc_val = js_create_desc(ctx, val, getter, setter, flags);
- if (JS_IsException(desc_val)) {
- JS_FreeValue(ctx, prop_val);
- JS_FreeValue(ctx, method);
- return -1;
- }
- args[0] = s->target;
- args[1] = prop_val;
- args[2] = desc_val;
- ret1 = JS_CallFree(ctx, method, s->handler, 3, args);
- JS_FreeValue(ctx, prop_val);
- JS_FreeValue(ctx, desc_val);
- if (JS_IsException(ret1))
- return -1;
- ret = JS_ToBoolFree(ctx, ret1);
- if (!ret) {
- if (flags & JS_PROP_THROW) {
- JS_ThrowTypeError(ctx, "proxy: defineProperty exception");
- return -1;
- } else {
- return 0;
- }
- }
- p = JS_VALUE_GET_OBJ(s->target);
- res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
- if (res < 0)
- return -1;
- setting_not_configurable = ((flags & (JS_PROP_HAS_CONFIGURABLE |
- JS_PROP_CONFIGURABLE)) ==
- JS_PROP_HAS_CONFIGURABLE);
- if (!res) {
- if (!p->extensible || setting_not_configurable)
- goto fail;
- } else {
- if (!check_define_prop_flags(desc.flags, flags))
- goto fail1;
- /* do the missing check from check_define_prop_flags() */
- if (!(desc.flags & JS_PROP_CONFIGURABLE)) {
- if ((desc.flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
- if ((flags & JS_PROP_HAS_GET) &&
- !js_same_value(ctx, getter, desc.getter)) {
- goto fail1;
- }
- if ((flags & JS_PROP_HAS_SET) &&
- !js_same_value(ctx, setter, desc.setter)) {
- goto fail1;
- }
- } else if (!(desc.flags & JS_PROP_WRITABLE)) {
- if ((flags & JS_PROP_HAS_VALUE) &&
- !js_same_value(ctx, val, desc.value)) {
- goto fail1;
- }
- }
- }
- /* additional checks */
- if ((desc.flags & JS_PROP_CONFIGURABLE) && setting_not_configurable)
- goto fail1;
- if ((desc.flags & JS_PROP_TMASK) != JS_PROP_GETSET &&
- (desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == JS_PROP_WRITABLE &&
- (flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == JS_PROP_HAS_WRITABLE) {
- fail1:
- js_free_desc(ctx, &desc);
- fail:
- JS_ThrowTypeError(ctx, "proxy: inconsistent defineProperty");
- return -1;
- }
- js_free_desc(ctx, &desc);
- }
- return 1;
- }
- static int js_proxy_delete_property(JSContext *ctx, JSValueConst obj,
- JSAtom atom)
- {
- JSProxyData *s;
- JSValue method, ret, atom_val;
- int res, res2, is_extensible;
- JSValueConst args[2];
- s = get_proxy_method(ctx, &method, obj, JS_ATOM_deleteProperty);
- if (!s)
- return -1;
- if (JS_IsUndefined(method)) {
- return JS_DeleteProperty(ctx, s->target, atom, 0);
- }
- atom_val = JS_AtomToValue(ctx, atom);;
- if (JS_IsException(atom_val)) {
- JS_FreeValue(ctx, method);
- return -1;
- }
- args[0] = s->target;
- args[1] = atom_val;
- ret = JS_CallFree(ctx, method, s->handler, 2, args);
- JS_FreeValue(ctx, atom_val);
- if (JS_IsException(ret))
- return -1;
- res = JS_ToBoolFree(ctx, ret);
- if (res) {
- JSPropertyDescriptor desc;
- res2 = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);
- if (res2 < 0)
- return -1;
- if (res2) {
- if (!(desc.flags & JS_PROP_CONFIGURABLE))
- goto fail;
- is_extensible = JS_IsExtensible(ctx, s->target);
- if (is_extensible < 0)
- goto fail1;
- if (!is_extensible) {
- /* proxy-missing-checks */
- fail:
- JS_ThrowTypeError(ctx, "proxy: inconsistent deleteProperty");
- fail1:
- js_free_desc(ctx, &desc);
- return -1;
- }
- js_free_desc(ctx, &desc);
- }
- }
- return res;
- }
- /* return the index of the property or -1 if not found */
- static int find_prop_key(const JSPropertyEnum *tab, int n, JSAtom atom)
- {
- int i;
- for(i = 0; i < n; i++) {
- if (tab[i].atom == atom)
- return i;
- }
- return -1;
- }
- static int js_proxy_get_own_property_names(JSContext *ctx,
- JSPropertyEnum **ptab,
- uint32_t *plen,
- JSValueConst obj)
- {
- JSProxyData *s;
- JSValue method, prop_array, val;
- uint32_t len, i, len2;
- JSPropertyEnum *tab, *tab2;
- JSAtom atom;
- JSPropertyDescriptor desc;
- int res, is_extensible, idx;
- s = get_proxy_method(ctx, &method, obj, JS_ATOM_ownKeys);
- if (!s)
- return -1;
- if (JS_IsUndefined(method)) {
- return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen,
- JS_VALUE_GET_OBJ(s->target),
- JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK);
- }
- prop_array = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);
- if (JS_IsException(prop_array))
- return -1;
- tab = NULL;
- len = 0;
- tab2 = NULL;
- len2 = 0;
- if (js_get_length32(ctx, &len, prop_array))
- goto fail;
- if (len > 0) {
- tab = js_mallocz(ctx, sizeof(tab[0]) * len);
- if (!tab)
- goto fail;
- }
- for(i = 0; i < len; i++) {
- val = JS_GetPropertyUint32(ctx, prop_array, i);
- if (JS_IsException(val))
- goto fail;
- if (!JS_IsString(val) && !JS_IsSymbol(val)) {
- JS_FreeValue(ctx, val);
- JS_ThrowTypeError(ctx, "proxy: properties must be strings or symbols");
- goto fail;
- }
- atom = JS_ValueToAtom(ctx, val);
- JS_FreeValue(ctx, val);
- if (atom == JS_ATOM_NULL)
- goto fail;
- tab[i].atom = atom;
- tab[i].is_enumerable = FALSE; /* XXX: redundant? */
- }
- /* check duplicate properties (XXX: inefficient, could store the
- * properties an a temporary object to use the hash) */
- for(i = 1; i < len; i++) {
- if (find_prop_key(tab, i, tab[i].atom) >= 0) {
- JS_ThrowTypeError(ctx, "proxy: duplicate property");
- goto fail;
- }
- }
- is_extensible = JS_IsExtensible(ctx, s->target);
- if (is_extensible < 0)
- goto fail;
- /* check if there are non configurable properties */
- if (s->is_revoked) {
- JS_ThrowTypeErrorRevokedProxy(ctx);
- goto fail;
- }
- if (JS_GetOwnPropertyNamesInternal(ctx, &tab2, &len2, JS_VALUE_GET_OBJ(s->target),
- JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK))
- goto fail;
- for(i = 0; i < len2; i++) {
- if (s->is_revoked) {
- JS_ThrowTypeErrorRevokedProxy(ctx);
- goto fail;
- }
- res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target),
- tab2[i].atom);
- if (res < 0)
- goto fail;
- if (res) { /* safety, property should be found */
- js_free_desc(ctx, &desc);
- if (!(desc.flags & JS_PROP_CONFIGURABLE) || !is_extensible) {
- idx = find_prop_key(tab, len, tab2[i].atom);
- if (idx < 0) {
- JS_ThrowTypeError(ctx, "proxy: target property must be present in proxy ownKeys");
- goto fail;
- }
- /* mark the property as found */
- if (!is_extensible)
- tab[idx].is_enumerable = TRUE;
- }
- }
- }
- if (!is_extensible) {
- /* check that all property in 'tab' were checked */
- for(i = 0; i < len; i++) {
- if (!tab[i].is_enumerable) {
- JS_ThrowTypeError(ctx, "proxy: property not present in target were returned by non extensible proxy");
- goto fail;
- }
- }
- }
- JS_FreePropertyEnum(ctx, tab2, len2);
- JS_FreeValue(ctx, prop_array);
- *ptab = tab;
- *plen = len;
- return 0;
- fail:
- JS_FreePropertyEnum(ctx, tab2, len2);
- JS_FreePropertyEnum(ctx, tab, len);
- JS_FreeValue(ctx, prop_array);
- return -1;
- }
- static JSValue js_proxy_call_constructor(JSContext *ctx, JSValueConst func_obj,
- JSValueConst new_target,
- int argc, JSValueConst *argv)
- {
- JSProxyData *s;
- JSValue method, arg_array, ret;
- JSValueConst args[3];
- s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_construct);
- if (!s)
- return JS_EXCEPTION;
- if (!JS_IsConstructor(ctx, s->target))
- return JS_ThrowTypeErrorNotAConstructor(ctx, s->target);
- if (JS_IsUndefined(method))
- return JS_CallConstructor2(ctx, s->target, new_target, argc, argv);
- arg_array = js_create_array(ctx, argc, argv);
- if (JS_IsException(arg_array)) {
- ret = JS_EXCEPTION;
- goto fail;
- }
- args[0] = s->target;
- args[1] = arg_array;
- args[2] = new_target;
- ret = JS_Call(ctx, method, s->handler, 3, args);
- if (!JS_IsException(ret) && JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {
- JS_FreeValue(ctx, ret);
- ret = JS_ThrowTypeErrorNotAnObject(ctx);
- }
- fail:
- JS_FreeValue(ctx, method);
- JS_FreeValue(ctx, arg_array);
- return ret;
- }
- static JSValue js_proxy_call(JSContext *ctx, JSValueConst func_obj,
- JSValueConst this_obj,
- int argc, JSValueConst *argv, int flags)
- {
- JSProxyData *s;
- JSValue method, arg_array, ret;
- JSValueConst args[3];
- if (flags & JS_CALL_FLAG_CONSTRUCTOR)
- return js_proxy_call_constructor(ctx, func_obj, this_obj, argc, argv);
- s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_apply);
- if (!s)
- return JS_EXCEPTION;
- if (!s->is_func) {
- JS_FreeValue(ctx, method);
- return JS_ThrowTypeError(ctx, "not a function");
- }
- if (JS_IsUndefined(method))
- return JS_Call(ctx, s->target, this_obj, argc, argv);
- arg_array = js_create_array(ctx, argc, argv);
- if (JS_IsException(arg_array)) {
- ret = JS_EXCEPTION;
- goto fail;
- }
- args[0] = s->target;
- args[1] = this_obj;
- args[2] = arg_array;
- ret = JS_Call(ctx, method, s->handler, 3, args);
- fail:
- JS_FreeValue(ctx, method);
- JS_FreeValue(ctx, arg_array);
- return ret;
- }
- /* `js_resolve_proxy`: resolve the proxy chain
- `*pval` is updated with to ultimate proxy target
- `throw_exception` controls whether exceptions are thown or not
- - return -1 in case of error
- - otherwise return 0
- */
- static int js_resolve_proxy(JSContext *ctx, JSValueConst *pval, BOOL throw_exception) {
- int depth = 0;
- JSObject *p;
- JSProxyData *s;
- while (JS_VALUE_GET_TAG(*pval) == JS_TAG_OBJECT) {
- p = JS_VALUE_GET_OBJ(*pval);
- if (p->class_id != JS_CLASS_PROXY)
- break;
- if (depth++ > 1000) {
- if (throw_exception)
- JS_ThrowStackOverflow(ctx);
- return -1;
- }
- s = p->u.opaque;
- if (s->is_revoked) {
- if (throw_exception)
- JS_ThrowTypeErrorRevokedProxy(ctx);
- return -1;
- }
- *pval = s->target;
- }
- return 0;
- }
- static const JSClassExoticMethods js_proxy_exotic_methods = {
- .get_own_property = js_proxy_get_own_property,
- .define_own_property = js_proxy_define_own_property,
- .delete_property = js_proxy_delete_property,
- .get_own_property_names = js_proxy_get_own_property_names,
- .has_property = js_proxy_has,
- .get_property = js_proxy_get,
- .set_property = js_proxy_set,
- .get_prototype = js_proxy_get_prototype,
- .set_prototype = js_proxy_set_prototype,
- .is_extensible = js_proxy_is_extensible,
- .prevent_extensions = js_proxy_prevent_extensions,
- };
- static JSValue js_proxy_constructor(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValueConst target, handler;
- JSValue obj;
- JSProxyData *s;
- target = argv[0];
- handler = argv[1];
- if (JS_VALUE_GET_TAG(target) != JS_TAG_OBJECT ||
- JS_VALUE_GET_TAG(handler) != JS_TAG_OBJECT)
- return JS_ThrowTypeErrorNotAnObject(ctx);
- obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_PROXY);
- if (JS_IsException(obj))
- return obj;
- s = js_malloc(ctx, sizeof(JSProxyData));
- if (!s) {
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- s->target = JS_DupValue(ctx, target);
- s->handler = JS_DupValue(ctx, handler);
- s->is_func = JS_IsFunction(ctx, target);
- s->is_revoked = FALSE;
- JS_SetOpaque(obj, s);
- JS_SetConstructorBit(ctx, obj, JS_IsConstructor(ctx, target));
- return obj;
- }
- static JSValue js_proxy_revoke(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic,
- JSValue *func_data)
- {
- JSProxyData *s = JS_GetOpaque(func_data[0], JS_CLASS_PROXY);
- if (s) {
- /* We do not free the handler and target in case they are
- referenced as constants in the C call stack */
- s->is_revoked = TRUE;
- JS_FreeValue(ctx, func_data[0]);
- func_data[0] = JS_NULL;
- }
- return JS_UNDEFINED;
- }
- static JSValue js_proxy_revoke_constructor(JSContext *ctx,
- JSValueConst proxy_obj)
- {
- return JS_NewCFunctionData(ctx, js_proxy_revoke, 0, 0, 1, &proxy_obj);
- }
- static JSValue js_proxy_revocable(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue proxy_obj, revoke_obj = JS_UNDEFINED, obj;
- proxy_obj = js_proxy_constructor(ctx, JS_UNDEFINED, argc, argv);
- if (JS_IsException(proxy_obj))
- goto fail;
- revoke_obj = js_proxy_revoke_constructor(ctx, proxy_obj);
- if (JS_IsException(revoke_obj))
- goto fail;
- obj = JS_NewObject(ctx);
- if (JS_IsException(obj))
- goto fail;
- // XXX: exceptions?
- JS_DefinePropertyValue(ctx, obj, JS_ATOM_proxy, proxy_obj, JS_PROP_C_W_E);
- JS_DefinePropertyValue(ctx, obj, JS_ATOM_revoke, revoke_obj, JS_PROP_C_W_E);
- return obj;
- fail:
- JS_FreeValue(ctx, proxy_obj);
- JS_FreeValue(ctx, revoke_obj);
- return JS_EXCEPTION;
- }
- static const JSCFunctionListEntry js_proxy_funcs[] = {
- JS_CFUNC_DEF("revocable", 2, js_proxy_revocable ),
- };
- static const JSClassShortDef js_proxy_class_def[] = {
- { JS_ATOM_Object, js_proxy_finalizer, js_proxy_mark }, /* JS_CLASS_PROXY */
- };
- int JS_AddIntrinsicProxy(JSContext *ctx)
- {
- JSRuntime *rt = ctx->rt;
- JSValue obj1;
- if (!JS_IsRegisteredClass(rt, JS_CLASS_PROXY)) {
- if (init_class_range(rt, js_proxy_class_def, JS_CLASS_PROXY,
- countof(js_proxy_class_def)))
- return -1;
- rt->class_array[JS_CLASS_PROXY].exotic = &js_proxy_exotic_methods;
- rt->class_array[JS_CLASS_PROXY].call = js_proxy_call;
- }
- /* additional fields: name, length */
- obj1 = JS_NewCFunction3(ctx, js_proxy_constructor, "Proxy", 2,
- JS_CFUNC_constructor, 0,
- ctx->function_proto, countof(js_proxy_funcs) + 2);
- if (JS_IsException(obj1))
- return -1;
- JS_SetConstructorBit(ctx, obj1, TRUE);
- if (JS_SetPropertyFunctionList(ctx, obj1, js_proxy_funcs,
- countof(js_proxy_funcs)))
- goto fail;
- if (JS_DefinePropertyValueStr(ctx, ctx->global_obj, "Proxy",
- obj1, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE) < 0)
- goto fail;
- return 0;
- fail:
- JS_FreeValue(ctx, obj1);
- return -1;
- }
- /* Symbol */
- static JSValue js_symbol_constructor(JSContext *ctx, JSValueConst new_target,
- int argc, JSValueConst *argv)
- {
- JSValue str;
- JSString *p;
- if (!JS_IsUndefined(new_target))
- return JS_ThrowTypeErrorNotAConstructor(ctx, new_target);
- if (argc == 0 || JS_IsUndefined(argv[0])) {
- p = NULL;
- } else {
- str = JS_ToString(ctx, argv[0]);
- if (JS_IsException(str))
- return JS_EXCEPTION;
- p = JS_VALUE_GET_STRING(str);
- }
- return JS_NewSymbol(ctx, p, JS_ATOM_TYPE_SYMBOL);
- }
- static JSValue js_thisSymbolValue(JSContext *ctx, JSValueConst this_val)
- {
- if (JS_VALUE_GET_TAG(this_val) == JS_TAG_SYMBOL)
- return JS_DupValue(ctx, this_val);
- if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
- JSObject *p = JS_VALUE_GET_OBJ(this_val);
- if (p->class_id == JS_CLASS_SYMBOL) {
- if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_SYMBOL)
- return JS_DupValue(ctx, p->u.object_data);
- }
- }
- return JS_ThrowTypeError(ctx, "not a symbol");
- }
- static JSValue js_symbol_toString(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue val, ret;
- val = js_thisSymbolValue(ctx, this_val);
- if (JS_IsException(val))
- return val;
- /* XXX: use JS_ToStringInternal() with a flags */
- ret = js_string_constructor(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val);
- JS_FreeValue(ctx, val);
- return ret;
- }
- static JSValue js_symbol_valueOf(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- return js_thisSymbolValue(ctx, this_val);
- }
- static JSValue js_symbol_get_description(JSContext *ctx, JSValueConst this_val)
- {
- JSValue val, ret;
- JSAtomStruct *p;
- val = js_thisSymbolValue(ctx, this_val);
- if (JS_IsException(val))
- return val;
- p = JS_VALUE_GET_PTR(val);
- if (p->len == 0 && p->is_wide_char != 0) {
- ret = JS_UNDEFINED;
- } else {
- ret = JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p));
- }
- JS_FreeValue(ctx, val);
- return ret;
- }
- static const JSCFunctionListEntry js_symbol_proto_funcs[] = {
- JS_CFUNC_DEF("toString", 0, js_symbol_toString ),
- JS_CFUNC_DEF("valueOf", 0, js_symbol_valueOf ),
- // XXX: should have writable: false
- JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_symbol_valueOf ),
- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Symbol", JS_PROP_CONFIGURABLE ),
- JS_CGETSET_DEF("description", js_symbol_get_description, NULL ),
- };
- static JSValue js_symbol_for(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue str;
- str = JS_ToString(ctx, argv[0]);
- if (JS_IsException(str))
- return JS_EXCEPTION;
- return JS_NewSymbol(ctx, JS_VALUE_GET_STRING(str), JS_ATOM_TYPE_GLOBAL_SYMBOL);
- }
- static JSValue js_symbol_keyFor(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSAtomStruct *p;
- if (!JS_IsSymbol(argv[0]))
- return JS_ThrowTypeError(ctx, "not a symbol");
- p = JS_VALUE_GET_PTR(argv[0]);
- if (p->atom_type != JS_ATOM_TYPE_GLOBAL_SYMBOL)
- return JS_UNDEFINED;
- return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
- }
- static const JSCFunctionListEntry js_symbol_funcs[] = {
- JS_CFUNC_DEF("for", 1, js_symbol_for ),
- JS_CFUNC_DEF("keyFor", 1, js_symbol_keyFor ),
- JS_PROP_ATOM_DEF("toPrimitive", JS_ATOM_Symbol_toPrimitive, 0),
- JS_PROP_ATOM_DEF("iterator", JS_ATOM_Symbol_iterator, 0),
- JS_PROP_ATOM_DEF("match", JS_ATOM_Symbol_match, 0),
- JS_PROP_ATOM_DEF("matchAll", JS_ATOM_Symbol_matchAll, 0),
- JS_PROP_ATOM_DEF("replace", JS_ATOM_Symbol_replace, 0),
- JS_PROP_ATOM_DEF("search", JS_ATOM_Symbol_search, 0),
- JS_PROP_ATOM_DEF("split", JS_ATOM_Symbol_split, 0),
- JS_PROP_ATOM_DEF("toStringTag", JS_ATOM_Symbol_toStringTag, 0),
- JS_PROP_ATOM_DEF("isConcatSpreadable", JS_ATOM_Symbol_isConcatSpreadable, 0),
- JS_PROP_ATOM_DEF("hasInstance", JS_ATOM_Symbol_hasInstance, 0),
- JS_PROP_ATOM_DEF("species", JS_ATOM_Symbol_species, 0),
- JS_PROP_ATOM_DEF("unscopables", JS_ATOM_Symbol_unscopables, 0),
- JS_PROP_ATOM_DEF("asyncIterator", JS_ATOM_Symbol_asyncIterator, 0),
- };
- /* Set/Map/WeakSet/WeakMap */
- static BOOL js_weakref_is_target(JSValueConst val)
- {
- switch (JS_VALUE_GET_TAG(val)) {
- case JS_TAG_OBJECT:
- return TRUE;
- case JS_TAG_SYMBOL:
- {
- JSAtomStruct *p = JS_VALUE_GET_PTR(val);
- if (p->atom_type == JS_ATOM_TYPE_SYMBOL &&
- p->hash != JS_ATOM_HASH_PRIVATE)
- return TRUE;
- }
- break;
- default:
- break;
- }
- return FALSE;
- }
- /* JS_UNDEFINED is considered as a live weakref */
- /* XXX: add a specific JSWeakRef value type ? */
- static BOOL js_weakref_is_live(JSValueConst val)
- {
- void *p;
- if (JS_IsUndefined(val))
- return TRUE;
- p = JS_VALUE_GET_PTR(val);
- return (js_rc(p)->ref_count != 0);
- }
- /* 'val' can be JS_UNDEFINED */
- static void js_weakref_free(JSRuntime *rt, JSValue val)
- {
- if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- assert(p->weakref_count >= 1);
- p->weakref_count--;
- /* 'mark' is tested to avoid freeing the object structure when
- it is about to be freed in a cycle or in
- free_zero_refcount() */
- if (p->weakref_count == 0 && js_rc(p)->ref_count == 0 &&
- js_rc(p)->mark == 0) {
- js_free_rt(rt, p);
- }
- } else if (JS_VALUE_GET_TAG(val) == JS_TAG_SYMBOL) {
- JSString *p = JS_VALUE_GET_STRING(val);
- assert(p->hash >= 1);
- p->hash--;
- if (p->hash == 0 && js_rc(p)->ref_count == 0) {
- /* can remove the dummy structure */
- js_free_rt(rt, p);
- }
- }
- }
- /* val must be an object, a symbol or undefined (see
- js_weakref_is_target). */
- static JSValue js_weakref_new(JSContext *ctx, JSValueConst val)
- {
- if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- p->weakref_count++;
- } else if (JS_VALUE_GET_TAG(val) == JS_TAG_SYMBOL) {
- JSString *p = JS_VALUE_GET_STRING(val);
- /* XXX: could return an exception if too many references */
- assert(p->hash < JS_ATOM_HASH_MASK - 2);
- p->hash++;
- } else {
- assert(JS_IsUndefined(val));
- }
- return (JSValue)val;
- }
- #define MAGIC_SET (1 << 0)
- #define MAGIC_WEAK (1 << 1)
- static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target,
- int argc, JSValueConst *argv, int magic)
- {
- JSMapState *s;
- JSValue obj, adder = JS_UNDEFINED, iter = JS_UNDEFINED, next_method = JS_UNDEFINED;
- JSValueConst arr;
- BOOL is_set, is_weak;
- is_set = magic & MAGIC_SET;
- is_weak = ((magic & MAGIC_WEAK) != 0);
- obj = js_create_from_ctor(ctx, new_target, JS_CLASS_MAP + magic);
- if (JS_IsException(obj))
- return JS_EXCEPTION;
- s = js_mallocz(ctx, sizeof(*s));
- if (!s)
- goto fail;
- init_list_head(&s->records);
- s->is_weak = is_weak;
- if (is_weak) {
- s->weakref_header.weakref_type = JS_WEAKREF_TYPE_MAP;
- list_add_tail(&s->weakref_header.link, &ctx->rt->weakref_list);
- }
- JS_SetOpaque(obj, s);
- s->hash_bits = 1;
- s->hash_size = 1U << s->hash_bits;
- s->hash_table = js_mallocz(ctx, sizeof(s->hash_table[0]) * s->hash_size);
- if (!s->hash_table)
- goto fail;
- s->record_count_threshold = 4;
- arr = JS_UNDEFINED;
- if (argc > 0)
- arr = argv[0];
- if (!JS_IsUndefined(arr) && !JS_IsNull(arr)) {
- JSValue item, ret;
- BOOL done;
- adder = JS_GetProperty(ctx, obj, is_set ? JS_ATOM_add : JS_ATOM_set);
- if (JS_IsException(adder))
- goto fail;
- if (!JS_IsFunction(ctx, adder)) {
- JS_ThrowTypeError(ctx, "set/add is not a function");
- goto fail;
- }
- iter = JS_GetIterator(ctx, arr, FALSE);
- if (JS_IsException(iter))
- goto fail;
- next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
- if (JS_IsException(next_method))
- goto fail;
- for(;;) {
- item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
- if (JS_IsException(item))
- goto fail;
- if (done)
- break;
- if (is_set) {
- ret = JS_Call(ctx, adder, obj, 1, (JSValueConst *)&item);
- if (JS_IsException(ret)) {
- JS_FreeValue(ctx, item);
- goto fail_close;
- }
- } else {
- JSValue key, value;
- JSValueConst args[2];
- key = JS_UNDEFINED;
- value = JS_UNDEFINED;
- if (!JS_IsObject(item)) {
- JS_ThrowTypeErrorNotAnObject(ctx);
- goto fail1;
- }
- key = JS_GetPropertyUint32(ctx, item, 0);
- if (JS_IsException(key))
- goto fail1;
- value = JS_GetPropertyUint32(ctx, item, 1);
- if (JS_IsException(value))
- goto fail1;
- args[0] = key;
- args[1] = value;
- ret = JS_Call(ctx, adder, obj, 2, args);
- if (JS_IsException(ret)) {
- fail1:
- JS_FreeValue(ctx, item);
- JS_FreeValue(ctx, key);
- JS_FreeValue(ctx, value);
- goto fail_close;
- }
- JS_FreeValue(ctx, key);
- JS_FreeValue(ctx, value);
- }
- JS_FreeValue(ctx, ret);
- JS_FreeValue(ctx, item);
- }
- JS_FreeValue(ctx, next_method);
- JS_FreeValue(ctx, iter);
- JS_FreeValue(ctx, adder);
- }
- return obj;
- fail_close:
- /* close the iterator object, preserving pending exception */
- JS_IteratorClose(ctx, iter, TRUE);
- fail:
- JS_FreeValue(ctx, next_method);
- JS_FreeValue(ctx, iter);
- JS_FreeValue(ctx, adder);
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- /* XXX: could normalize strings to speed up comparison */
- static JSValue map_normalize_key(JSContext *ctx, JSValue key)
- {
- uint32_t tag = JS_VALUE_GET_TAG(key);
- /* convert -0.0 to +0.0 */
- if (JS_TAG_IS_FLOAT64(tag) && JS_VALUE_GET_FLOAT64(key) == 0.0) {
- key = JS_NewInt32(ctx, 0);
- }
- return key;
- }
- static JSValueConst map_normalize_key_const(JSContext *ctx, JSValueConst key)
- {
- return (JSValueConst)map_normalize_key(ctx, (JSValue)key);
- }
- /* hash multipliers, same as the Linux kernel (see Knuth vol 3,
- section 6.4, exercise 9) */
- #define HASH_MUL32 0x61C88647
- #define HASH_MUL64 UINT64_C(0x61C8864680B583EB)
- static uint32_t map_hash32(uint32_t a, int hash_bits)
- {
- return (a * HASH_MUL32) >> (32 - hash_bits);
- }
- static uint32_t map_hash64(uint64_t a, int hash_bits)
- {
- return (a * HASH_MUL64) >> (64 - hash_bits);
- }
- static uint32_t map_hash_pointer(uintptr_t a, int hash_bits)
- {
- #ifdef JS_PTR64
- return map_hash64(a, hash_bits);
- #else
- return map_hash32(a, hash_bits);
- #endif
- }
- /* XXX: better hash ? */
- /* precondition: 1 <= hash_bits <= 32 */
- static uint32_t map_hash_key(JSValueConst key, int hash_bits)
- {
- uint32_t tag = JS_VALUE_GET_NORM_TAG(key);
- uint32_t h;
- double d;
- JSBigInt *p;
- JSBigIntBuf buf;
-
- switch(tag) {
- case JS_TAG_BOOL:
- h = map_hash32(JS_VALUE_GET_INT(key) ^ JS_TAG_BOOL, hash_bits);
- break;
- case JS_TAG_STRING:
- h = map_hash32(hash_string(JS_VALUE_GET_STRING(key), 0) ^ JS_TAG_STRING, hash_bits);
- break;
- case JS_TAG_STRING_ROPE:
- h = map_hash32(hash_string_rope(key, 0) ^ JS_TAG_STRING, hash_bits);
- break;
- case JS_TAG_OBJECT:
- case JS_TAG_SYMBOL:
- h = map_hash_pointer((uintptr_t)JS_VALUE_GET_PTR(key) ^ tag, hash_bits);
- break;
- case JS_TAG_INT:
- d = JS_VALUE_GET_INT(key);
- goto hash_float64;
- case JS_TAG_FLOAT64:
- d = JS_VALUE_GET_FLOAT64(key);
- /* normalize the NaN */
- if (isnan(d))
- d = JS_FLOAT64_NAN;
- hash_float64:
- h = map_hash64(float64_as_uint64(d) ^ JS_TAG_FLOAT64, hash_bits);
- break;
- case JS_TAG_SHORT_BIG_INT:
- p = js_bigint_set_short(&buf, key);
- goto hash_bigint;
- case JS_TAG_BIG_INT:
- p = JS_VALUE_GET_PTR(key);
- hash_bigint:
- {
- int i;
- h = 1;
- for(i = p->len - 1; i >= 0; i--) {
- h = h * 263 + p->tab[i];
- }
- /* the final step is necessary otherwise h mod n only
- depends of p->tab[i] mod n */
- h = map_hash32(h ^ JS_TAG_BIG_INT, hash_bits);
- }
- break;
- default:
- h = 0;
- break;
- }
- return h;
- }
- static JSMapRecord *map_find_record(JSContext *ctx, JSMapState *s,
- JSValueConst key)
- {
- JSMapRecord *mr;
- uint32_t h;
- h = map_hash_key(key, s->hash_bits);
- for(mr = s->hash_table[h]; mr != NULL; mr = mr->hash_next) {
- if (mr->empty || (s->is_weak && !js_weakref_is_live(mr->key))) {
- /* cannot match */
- } else {
- if (js_same_value_zero(ctx, mr->key, key))
- return mr;
- }
- }
- return NULL;
- }
- static void map_hash_resize(JSContext *ctx, JSMapState *s)
- {
- uint32_t new_hash_size, h;
- int new_hash_bits;
- struct list_head *el;
- JSMapRecord *mr, **new_hash_table;
- /* XXX: no reporting of memory allocation failure */
- new_hash_bits = min_int(s->hash_bits + 1, 31);
- new_hash_size = 1U << new_hash_bits;
- new_hash_table = js_realloc(ctx, s->hash_table,
- sizeof(new_hash_table[0]) * new_hash_size);
- if (!new_hash_table)
- return;
- memset(new_hash_table, 0, sizeof(new_hash_table[0]) * new_hash_size);
- list_for_each(el, &s->records) {
- mr = list_entry(el, JSMapRecord, link);
- if (mr->empty || (s->is_weak && !js_weakref_is_live(mr->key))) {
- } else {
- h = map_hash_key(mr->key, new_hash_bits);
- mr->hash_next = new_hash_table[h];
- new_hash_table[h] = mr;
- }
- }
- s->hash_table = new_hash_table;
- s->hash_bits = new_hash_bits;
- s->hash_size = new_hash_size;
- s->record_count_threshold = new_hash_size * 2;
- }
- static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s,
- JSValueConst key)
- {
- uint32_t h;
- JSMapRecord *mr;
- mr = js_malloc(ctx, sizeof(*mr));
- if (!mr)
- return NULL;
- mr->ref_count = 1;
- mr->empty = FALSE;
- if (s->is_weak) {
- mr->key = js_weakref_new(ctx, key);
- } else {
- mr->key = JS_DupValue(ctx, key);
- }
- h = map_hash_key(key, s->hash_bits);
- mr->hash_next = s->hash_table[h];
- s->hash_table[h] = mr;
- list_add_tail(&mr->link, &s->records);
- s->record_count++;
- if (s->record_count >= s->record_count_threshold) {
- map_hash_resize(ctx, s);
- }
- return mr;
- }
- static JSMapRecord *set_add_record(JSContext *ctx, JSMapState *s,
- JSValueConst key)
- {
- JSMapRecord *mr;
- mr = map_add_record(ctx, s, key);
- if (!mr)
- return NULL;
- mr->value = JS_UNDEFINED;
- return mr;
- }
- /* warning: the record must be removed from the hash table before */
- static void map_delete_record_internal(JSRuntime *rt, JSMapState *s, JSMapRecord *mr)
- {
- if (mr->empty)
- return;
-
- if (s->is_weak) {
- js_weakref_free(rt, mr->key);
- } else {
- JS_FreeValueRT(rt, mr->key);
- }
- JS_FreeValueRT(rt, mr->value);
- if (--mr->ref_count == 0) {
- list_del(&mr->link);
- js_free_rt(rt, mr);
- } else {
- /* keep a zombie record for iterators */
- mr->empty = TRUE;
- mr->key = JS_UNDEFINED;
- mr->value = JS_UNDEFINED;
- }
- s->record_count--;
- }
- static void map_decref_record(JSRuntime *rt, JSMapRecord *mr)
- {
- if (--mr->ref_count == 0) {
- /* the record can be safely removed */
- assert(mr->empty);
- list_del(&mr->link);
- js_free_rt(rt, mr);
- }
- }
- static void map_delete_weakrefs(JSRuntime *rt, JSWeakRefHeader *wh)
- {
- JSMapState *s = container_of(wh, JSMapState, weakref_header);
- struct list_head *el, *el1;
- JSMapRecord *mr1, **pmr;
- uint32_t h;
- list_for_each_safe(el, el1, &s->records) {
- JSMapRecord *mr = list_entry(el, JSMapRecord, link);
- if (!js_weakref_is_live(mr->key)) {
- /* even if key is not live it can be hashed as a pointer */
- h = map_hash_key(mr->key, s->hash_bits);
- pmr = &s->hash_table[h];
- for(;;) {
- mr1 = *pmr;
- /* the entry may already be removed from the hash
- table if the map was resized */
- if (mr1 == NULL)
- goto done;
- if (mr1 == mr)
- break;
- pmr = &mr1->hash_next;
- }
- /* remove from the hash table */
- *pmr = mr1->hash_next;
- done:
- map_delete_record_internal(rt, s, mr);
- }
- }
- }
- static JSValue js_map_set(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
- {
- JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
- JSMapRecord *mr;
- JSValueConst key, value;
- if (!s)
- return JS_EXCEPTION;
- key = map_normalize_key_const(ctx, argv[0]);
- if (s->is_weak && !js_weakref_is_target(key))
- return JS_ThrowTypeError(ctx, "invalid value used as %s key", (magic & MAGIC_SET) ? "WeakSet" : "WeakMap");
- if (magic & MAGIC_SET)
- value = JS_UNDEFINED;
- else
- value = argv[1];
- mr = map_find_record(ctx, s, key);
- if (mr) {
- JS_FreeValue(ctx, mr->value);
- } else {
- mr = map_add_record(ctx, s, key);
- if (!mr)
- return JS_EXCEPTION;
- }
- mr->value = JS_DupValue(ctx, value);
- return JS_DupValue(ctx, this_val);
- }
- static JSValue js_map_get(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
- {
- JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
- JSMapRecord *mr;
- JSValueConst key;
- if (!s)
- return JS_EXCEPTION;
- key = map_normalize_key_const(ctx, argv[0]);
- mr = map_find_record(ctx, s, key);
- if (!mr)
- return JS_UNDEFINED;
- else
- return JS_DupValue(ctx, mr->value);
- }
- /* return JS_TRUE or JS_FALSE */
- static JSValue map_delete_record(JSContext *ctx, JSMapState *s, JSValueConst key)
- {
- JSMapRecord *mr, **pmr;
- uint32_t h;
- key = map_normalize_key_const(ctx, key);
-
- h = map_hash_key(key, s->hash_bits);
- pmr = &s->hash_table[h];
- for(;;) {
- mr = *pmr;
- if (mr == NULL)
- return JS_FALSE;
- if (mr->empty || (s->is_weak && !js_weakref_is_live(mr->key))) {
- /* not valid */
- } else {
- if (js_same_value_zero(ctx, mr->key, key))
- break;
- }
- pmr = &mr->hash_next;
- }
- /* remove from the hash table */
- *pmr = mr->hash_next;
-
- map_delete_record_internal(ctx->rt, s, mr);
- return JS_TRUE;
- }
- static JSValue js_map_getOrInsert(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
- {
- BOOL computed = magic & 1;
- JSClassID class_id = magic >> 1;
- JSMapState *s = JS_GetOpaque2(ctx, this_val, class_id);
- JSMapRecord *mr;
- JSValueConst key;
- JSValue value;
- if (!s)
- return JS_EXCEPTION;
- if (computed && !JS_IsFunction(ctx, argv[1]))
- return JS_ThrowTypeError(ctx, "not a function");
- key = map_normalize_key_const(ctx, argv[0]);
- if (s->is_weak && !js_weakref_is_target(key))
- return JS_ThrowTypeError(ctx, "invalid value used as WeakMap key");
- mr = map_find_record(ctx, s, key);
- if (!mr) {
- if (computed) {
- value = JS_Call(ctx, argv[1], JS_UNDEFINED, 1, &key);
- if (JS_IsException(value))
- return JS_EXCEPTION;
- map_delete_record(ctx, s, key);
- } else {
- value = JS_DupValue(ctx, argv[1]);
- }
- mr = map_add_record(ctx, s, key);
- if (!mr) {
- JS_FreeValue(ctx, value);
- return JS_EXCEPTION;
- }
- mr->value = value;
- }
- return JS_DupValue(ctx, mr->value);
- }
- static JSValue js_map_has(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
- {
- JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
- JSMapRecord *mr;
- JSValueConst key;
- if (!s)
- return JS_EXCEPTION;
- key = map_normalize_key_const(ctx, argv[0]);
- mr = map_find_record(ctx, s, key);
- return JS_NewBool(ctx, mr != NULL);
- }
- static JSValue js_map_delete(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
- {
- JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
- if (!s)
- return JS_EXCEPTION;
- return map_delete_record(ctx, s, argv[0]);
- }
- static JSValue js_map_clear(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
- {
- JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
- struct list_head *el, *el1;
- JSMapRecord *mr;
- if (!s)
- return JS_EXCEPTION;
- /* remove from the hash table */
- memset(s->hash_table, 0, sizeof(s->hash_table[0]) * s->hash_size);
-
- list_for_each_safe(el, el1, &s->records) {
- mr = list_entry(el, JSMapRecord, link);
- map_delete_record_internal(ctx->rt, s, mr);
- }
- return JS_UNDEFINED;
- }
- static JSValue js_map_get_size(JSContext *ctx, JSValueConst this_val, int magic)
- {
- JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
- if (!s)
- return JS_EXCEPTION;
- return JS_NewUint32(ctx, s->record_count);
- }
- static JSValue js_map_forEach(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
- {
- JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
- JSValueConst func, this_arg;
- JSValue ret, args[3];
- struct list_head *el;
- JSMapRecord *mr;
- if (!s)
- return JS_EXCEPTION;
- func = argv[0];
- if (argc > 1)
- this_arg = argv[1];
- else
- this_arg = JS_UNDEFINED;
- if (check_function(ctx, func))
- return JS_EXCEPTION;
- /* Note: the list can be modified while traversing it, but the
- current element is locked */
- el = s->records.next;
- while (el != &s->records) {
- mr = list_entry(el, JSMapRecord, link);
- if (!mr->empty) {
- mr->ref_count++;
- /* must duplicate in case the record is deleted */
- args[1] = JS_DupValue(ctx, mr->key);
- if (magic)
- args[0] = args[1];
- else
- args[0] = JS_DupValue(ctx, mr->value);
- args[2] = (JSValue)this_val;
- ret = JS_Call(ctx, func, this_arg, 3, (JSValueConst *)args);
- JS_FreeValue(ctx, args[0]);
- if (!magic)
- JS_FreeValue(ctx, args[1]);
- el = el->next;
- map_decref_record(ctx->rt, mr);
- if (JS_IsException(ret))
- return ret;
- JS_FreeValue(ctx, ret);
- } else {
- el = el->next;
- }
- }
- return JS_UNDEFINED;
- }
- static JSValue js_object_groupBy(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int is_map)
- {
- JSValueConst cb, args[2];
- JSValue res, iter, next, groups, key, v, prop;
- JSAtom key_atom = JS_ATOM_NULL;
- int64_t idx;
- BOOL done;
- // "is function?" check must be observed before argv[0] is accessed
- cb = argv[1];
- if (check_function(ctx, cb))
- return JS_EXCEPTION;
- iter = JS_GetIterator(ctx, argv[0], /*is_async*/FALSE);
- if (JS_IsException(iter))
- return JS_EXCEPTION;
- key = JS_UNDEFINED;
- key_atom = JS_ATOM_NULL;
- v = JS_UNDEFINED;
- prop = JS_UNDEFINED;
- groups = JS_UNDEFINED;
- next = JS_GetProperty(ctx, iter, JS_ATOM_next);
- if (JS_IsException(next))
- goto exception;
- if (is_map) {
- groups = js_map_constructor(ctx, JS_UNDEFINED, 0, NULL, 0);
- } else {
- groups = JS_NewObjectProto(ctx, JS_NULL);
- }
- if (JS_IsException(groups))
- goto exception;
- for (idx = 0; ; idx++) {
- if (idx >= MAX_SAFE_INTEGER) {
- JS_ThrowTypeError(ctx, "too many elements");
- goto iterator_close_exception;
- }
- v = JS_IteratorNext(ctx, iter, next, 0, NULL, &done);
- if (JS_IsException(v))
- goto exception;
- if (done)
- break; // v is JS_UNDEFINED
- args[0] = v;
- args[1] = JS_NewInt64(ctx, idx);
- key = JS_Call(ctx, cb, ctx->global_obj, 2, args);
- if (JS_IsException(key))
- goto iterator_close_exception;
- if (is_map) {
- prop = js_map_get(ctx, groups, 1, (JSValueConst *)&key, 0);
- } else {
- key_atom = JS_ValueToAtom(ctx, key);
- JS_FreeValue(ctx, key);
- key = JS_UNDEFINED;
- if (key_atom == JS_ATOM_NULL)
- goto iterator_close_exception;
- prop = JS_GetProperty(ctx, groups, key_atom);
- }
- if (JS_IsException(prop))
- goto exception;
- if (JS_IsUndefined(prop)) {
- prop = JS_NewArray(ctx);
- if (JS_IsException(prop))
- goto exception;
- if (is_map) {
- args[0] = key;
- args[1] = prop;
- res = js_map_set(ctx, groups, 2, args, 0);
- if (JS_IsException(res))
- goto exception;
- JS_FreeValue(ctx, res);
- } else {
- prop = JS_DupValue(ctx, prop);
- if (JS_DefinePropertyValue(ctx, groups, key_atom, prop,
- JS_PROP_C_W_E) < 0) {
- goto exception;
- }
- }
- }
- res = js_array_push(ctx, prop, 1, (JSValueConst *)&v, /*unshift*/0);
- if (JS_IsException(res))
- goto exception;
- // res is an int64
- JS_FreeValue(ctx, prop);
- JS_FreeValue(ctx, key);
- JS_FreeAtom(ctx, key_atom);
- JS_FreeValue(ctx, v);
- prop = JS_UNDEFINED;
- key = JS_UNDEFINED;
- key_atom = JS_ATOM_NULL;
- v = JS_UNDEFINED;
- }
- JS_FreeValue(ctx, iter);
- JS_FreeValue(ctx, next);
- return groups;
- iterator_close_exception:
- JS_IteratorClose(ctx, iter, TRUE);
- exception:
- JS_FreeAtom(ctx, key_atom);
- JS_FreeValue(ctx, prop);
- JS_FreeValue(ctx, key);
- JS_FreeValue(ctx, v);
- JS_FreeValue(ctx, groups);
- JS_FreeValue(ctx, iter);
- JS_FreeValue(ctx, next);
- return JS_EXCEPTION;
- }
- static void js_map_finalizer(JSRuntime *rt, JSValue val)
- {
- JSObject *p;
- JSMapState *s;
- struct list_head *el, *el1;
- JSMapRecord *mr;
- p = JS_VALUE_GET_OBJ(val);
- s = p->u.map_state;
- if (s) {
- /* if the object is deleted we are sure that no iterator is
- using it */
- list_for_each_safe(el, el1, &s->records) {
- mr = list_entry(el, JSMapRecord, link);
- if (!mr->empty) {
- if (s->is_weak)
- js_weakref_free(rt, mr->key);
- else
- JS_FreeValueRT(rt, mr->key);
- JS_FreeValueRT(rt, mr->value);
- }
- js_free_rt(rt, mr);
- }
- js_free_rt(rt, s->hash_table);
- if (s->is_weak) {
- list_del(&s->weakref_header.link);
- }
- js_free_rt(rt, s);
- }
- }
- static void js_map_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- JSMapState *s;
- struct list_head *el;
- JSMapRecord *mr;
- s = p->u.map_state;
- if (s) {
- list_for_each(el, &s->records) {
- mr = list_entry(el, JSMapRecord, link);
- if (!s->is_weak)
- JS_MarkValue(rt, mr->key, mark_func);
- JS_MarkValue(rt, mr->value, mark_func);
- }
- }
- }
- /* Map Iterator */
- typedef struct JSMapIteratorData {
- JSValue obj;
- JSIteratorKindEnum kind;
- JSMapRecord *cur_record;
- } JSMapIteratorData;
- static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val)
- {
- JSObject *p;
- JSMapIteratorData *it;
- p = JS_VALUE_GET_OBJ(val);
- it = p->u.map_iterator_data;
- if (it) {
- /* During the GC sweep phase the Map finalizer may be
- called before the Map iterator finalizer */
- if (JS_IsLiveObject(rt, it->obj) && it->cur_record) {
- map_decref_record(rt, it->cur_record);
- }
- JS_FreeValueRT(rt, it->obj);
- js_free_rt(rt, it);
- }
- }
- static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- JSMapIteratorData *it;
- it = p->u.map_iterator_data;
- if (it) {
- /* the record is already marked by the object */
- JS_MarkValue(rt, it->obj, mark_func);
- }
- }
- static JSValue js_create_map_iterator(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
- {
- JSIteratorKindEnum kind;
- JSMapState *s;
- JSMapIteratorData *it;
- JSValue enum_obj;
- kind = magic >> 2;
- magic &= 3;
- s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
- if (!s)
- return JS_EXCEPTION;
- enum_obj = JS_NewObjectClass(ctx, JS_CLASS_MAP_ITERATOR + magic);
- if (JS_IsException(enum_obj))
- goto fail;
- it = js_malloc(ctx, sizeof(*it));
- if (!it) {
- JS_FreeValue(ctx, enum_obj);
- goto fail;
- }
- it->obj = JS_DupValue(ctx, this_val);
- it->kind = kind;
- it->cur_record = NULL;
- JS_SetOpaque(enum_obj, it);
- return enum_obj;
- fail:
- return JS_EXCEPTION;
- }
- static JSValue js_map_iterator_next(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv,
- BOOL *pdone, int magic)
- {
- JSMapIteratorData *it;
- JSMapState *s;
- JSMapRecord *mr;
- struct list_head *el;
- it = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP_ITERATOR + magic);
- if (!it) {
- *pdone = FALSE;
- return JS_EXCEPTION;
- }
- if (JS_IsUndefined(it->obj))
- goto done;
- s = JS_GetOpaque(it->obj, JS_CLASS_MAP + magic);
- assert(s != NULL);
- if (!it->cur_record) {
- el = s->records.next;
- } else {
- mr = it->cur_record;
- el = mr->link.next;
- map_decref_record(ctx->rt, mr); /* the record can be freed here */
- }
- for(;;) {
- if (el == &s->records) {
- /* no more record */
- it->cur_record = NULL;
- JS_FreeValue(ctx, it->obj);
- it->obj = JS_UNDEFINED;
- done:
- /* end of enumeration */
- *pdone = TRUE;
- return JS_UNDEFINED;
- }
- mr = list_entry(el, JSMapRecord, link);
- if (!mr->empty)
- break;
- /* get the next record */
- el = mr->link.next;
- }
- /* lock the record so that it won't be freed */
- mr->ref_count++;
- it->cur_record = mr;
- *pdone = FALSE;
- if (it->kind == JS_ITERATOR_KIND_KEY) {
- return JS_DupValue(ctx, mr->key);
- } else {
- JSValueConst args[2];
- args[0] = mr->key;
- if (magic)
- args[1] = mr->key;
- else
- args[1] = mr->value;
- if (it->kind == JS_ITERATOR_KIND_VALUE) {
- return JS_DupValue(ctx, args[1]);
- } else {
- return js_create_array(ctx, 2, args);
- }
- }
- }
- static int get_set_record(JSContext *ctx, JSValueConst obj,
- int64_t *psize, JSValue *phas, JSValue *pkeys)
- {
- JSMapState *s;
- int64_t size;
- JSValue has = JS_UNDEFINED, keys = JS_UNDEFINED;
-
- s = JS_GetOpaque(obj, JS_CLASS_SET);
- if (s) {
- size = s->record_count;
- } else {
- JSValue v;
- double d;
- v = JS_GetProperty(ctx, obj, JS_ATOM_size);
- if (JS_IsException(v))
- goto exception;
- if (JS_ToFloat64Free(ctx, &d, v) < 0)
- goto exception;
- if (isnan(d)) {
- JS_ThrowTypeError(ctx, ".size is not a number");
- goto exception;
- }
- if (d < INT64_MIN)
- size = INT64_MIN;
- else if (d >= 0x1p63) /* must use INT64_MAX + 1 because INT64_MAX cannot be exactly represented as a double */
- size = INT64_MAX;
- else
- size = (int64_t)d;
- if (size < 0) {
- JS_ThrowRangeError(ctx, ".size must be positive");
- goto exception;
- }
- }
- has = JS_GetProperty(ctx, obj, JS_ATOM_has);
- if (JS_IsException(has))
- goto exception;
- if (JS_IsUndefined(has)) {
- JS_ThrowTypeError(ctx, ".has is undefined");
- goto exception;
- }
- if (!JS_IsFunction(ctx, has)) {
- JS_ThrowTypeError(ctx, ".has is not a function");
- goto exception;
- }
- keys = JS_GetProperty(ctx, obj, JS_ATOM_keys);
- if (JS_IsException(keys))
- goto exception;
- if (JS_IsUndefined(keys)) {
- JS_ThrowTypeError(ctx, ".keys is undefined");
- goto exception;
- }
- if (!JS_IsFunction(ctx, keys)) {
- JS_ThrowTypeError(ctx, ".keys is not a function");
- goto exception;
- }
- *psize = size;
- *phas = has;
- *pkeys = keys;
- return 0;
- exception:
- JS_FreeValue(ctx, has);
- JS_FreeValue(ctx, keys);
- *psize = 0;
- *phas = JS_UNDEFINED;
- *pkeys = JS_UNDEFINED;
- return -1;
- }
- /* copy 'this_val' in a new set without side effects */
- static JSValue js_copy_set(JSContext *ctx, JSValueConst this_val)
- {
- JSValue newset;
- JSMapState *s, *t;
- struct list_head *el;
- JSMapRecord *mr;
-
- s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET);
- if (!s)
- return JS_EXCEPTION;
- newset = js_map_constructor(ctx, JS_UNDEFINED, 0, NULL, MAGIC_SET);
- if (JS_IsException(newset))
- return JS_EXCEPTION;
- t = JS_GetOpaque(newset, JS_CLASS_SET);
- // can't clone this_val using js_map_constructor(),
- // test262 mandates we don't call the .add method
- list_for_each(el, &s->records) {
- mr = list_entry(el, JSMapRecord, link);
- if (mr->empty)
- continue;
- if (!set_add_record(ctx, t, mr->key))
- goto exception;
- }
- return newset;
- exception:
- JS_FreeValue(ctx, newset);
- return JS_EXCEPTION;
- }
- static JSValue js_set_isDisjointFrom(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue item, iter, keys, has, next, rv, rval;
- int done;
- BOOL found;
- JSMapState *s;
- int64_t size;
- int ok;
- iter = JS_UNDEFINED;
- next = JS_UNDEFINED;
- rval = JS_EXCEPTION;
- s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET);
- if (!s)
- return JS_EXCEPTION;
- if (get_set_record(ctx, argv[0], &size, &has, &keys) < 0)
- goto exception;
- if (s->record_count <= size) {
- iter = js_create_map_iterator(ctx, this_val, 0, NULL, MAGIC_SET);
- if (JS_IsException(iter))
- goto exception;
- found = FALSE;
- do {
- item = js_map_iterator_next(ctx, iter, 0, NULL, &done, MAGIC_SET);
- if (JS_IsException(item))
- goto exception;
- if (done) // item is JS_UNDEFINED
- break;
- rv = JS_Call(ctx, has, argv[0], 1, (JSValueConst *)&item);
- JS_FreeValue(ctx, item);
- ok = JS_ToBoolFree(ctx, rv); // returns -1 if rv is JS_EXCEPTION
- if (ok < 0)
- goto exception;
- found = (ok > 0);
- } while (!found);
- } else {
- iter = JS_Call(ctx, keys, argv[0], 0, NULL);
- if (JS_IsException(iter))
- goto exception;
- next = JS_GetProperty(ctx, iter, JS_ATOM_next);
- if (JS_IsException(next))
- goto exception;
- found = FALSE;
- for(;;) {
- item = JS_IteratorNext(ctx, iter, next, 0, NULL, &done);
- if (JS_IsException(item))
- goto exception;
- if (done) // item is JS_UNDEFINED
- break;
- item = map_normalize_key(ctx, item);
- found = (NULL != map_find_record(ctx, s, item));
- JS_FreeValue(ctx, item);
- if (found) {
- JS_IteratorClose(ctx, iter, FALSE);
- break;
- }
- }
- }
- rval = !found ? JS_TRUE : JS_FALSE;
- exception:
- JS_FreeValue(ctx, has);
- JS_FreeValue(ctx, keys);
- JS_FreeValue(ctx, iter);
- JS_FreeValue(ctx, next);
- return rval;
- }
- static JSValue js_set_isSubsetOf(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue item, iter, keys, has, next, rv, rval;
- BOOL found;
- JSMapState *s;
- int64_t size;
- int done, ok;
- iter = JS_UNDEFINED;
- next = JS_UNDEFINED;
- rval = JS_EXCEPTION;
- s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET);
- if (!s)
- return JS_EXCEPTION;
- if (get_set_record(ctx, argv[0], &size, &has, &keys) < 0)
- goto exception;
- found = FALSE;
- if (s->record_count > size)
- goto fini;
- iter = js_create_map_iterator(ctx, this_val, 0, NULL, MAGIC_SET);
- if (JS_IsException(iter))
- goto exception;
- found = TRUE;
- do {
- item = js_map_iterator_next(ctx, iter, 0, NULL, &done, MAGIC_SET);
- if (JS_IsException(item))
- goto exception;
- if (done) // item is JS_UNDEFINED
- break;
- rv = JS_Call(ctx, has, argv[0], 1, (JSValueConst *)&item);
- JS_FreeValue(ctx, item);
- ok = JS_ToBoolFree(ctx, rv); // returns -1 if rv is JS_EXCEPTION
- if (ok < 0)
- goto exception;
- found = (ok > 0);
- } while (found);
- fini:
- rval = found ? JS_TRUE : JS_FALSE;
- exception:
- JS_FreeValue(ctx, has);
- JS_FreeValue(ctx, keys);
- JS_FreeValue(ctx, iter);
- JS_FreeValue(ctx, next);
- return rval;
- }
- static JSValue js_set_isSupersetOf(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue item, iter, keys, has, next, rval;
- int done;
- BOOL found;
- JSMapState *s;
- int64_t size;
- iter = JS_UNDEFINED;
- next = JS_UNDEFINED;
- rval = JS_EXCEPTION;
- s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET);
- if (!s)
- return JS_EXCEPTION;
- if (get_set_record(ctx, argv[0], &size, &has, &keys) < 0)
- goto exception;
- found = FALSE;
- if (s->record_count < size)
- goto fini;
- iter = JS_Call(ctx, keys, argv[0], 0, NULL);
- if (JS_IsException(iter))
- goto exception;
- next = JS_GetProperty(ctx, iter, JS_ATOM_next);
- if (JS_IsException(next))
- goto exception;
- found = TRUE;
- for(;;) {
- item = JS_IteratorNext(ctx, iter, next, 0, NULL, &done);
- if (JS_IsException(item))
- goto exception;
- if (done) // item is JS_UNDEFINED
- break;
- item = map_normalize_key(ctx, item);
- found = (NULL != map_find_record(ctx, s, item));
- JS_FreeValue(ctx, item);
- if (!found) {
- JS_IteratorClose(ctx, iter, FALSE);
- break;
- }
- }
- fini:
- rval = found ? JS_TRUE : JS_FALSE;
- exception:
- JS_FreeValue(ctx, has);
- JS_FreeValue(ctx, keys);
- JS_FreeValue(ctx, iter);
- JS_FreeValue(ctx, next);
- return rval;
- }
- static JSValue js_set_intersection(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue newset, item, iter, keys, has, next, rv;
- JSMapState *s, *t;
- JSMapRecord *mr;
- int64_t size;
- int done, ok;
- iter = JS_UNDEFINED;
- next = JS_UNDEFINED;
- newset = JS_UNDEFINED;
- s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET);
- if (!s)
- return JS_EXCEPTION;
- if (get_set_record(ctx, argv[0], &size, &has, &keys) < 0)
- goto exception;
- if (s->record_count > size) {
- iter = JS_Call(ctx, keys, argv[0], 0, NULL);
- if (JS_IsException(iter))
- goto exception;
- next = JS_GetProperty(ctx, iter, JS_ATOM_next);
- if (JS_IsException(next))
- goto exception;
- newset = js_map_constructor(ctx, JS_UNDEFINED, 0, NULL, MAGIC_SET);
- if (JS_IsException(newset))
- goto exception;
- t = JS_GetOpaque(newset, JS_CLASS_SET);
- for (;;) {
- item = JS_IteratorNext(ctx, iter, next, 0, NULL, &done);
- if (JS_IsException(item))
- goto exception;
- if (done) // item is JS_UNDEFINED
- break;
- item = map_normalize_key(ctx, item);
- if (!map_find_record(ctx, s, item)) {
- JS_FreeValue(ctx, item);
- } else if (map_find_record(ctx, t, item)) {
- JS_FreeValue(ctx, item); // no duplicates
- } else {
- mr = set_add_record(ctx, t, item);
- JS_FreeValue(ctx, item);
- if (!mr)
- goto exception;
- }
- }
- } else {
- iter = js_create_map_iterator(ctx, this_val, 0, NULL, MAGIC_SET);
- if (JS_IsException(iter))
- goto exception;
- newset = js_map_constructor(ctx, JS_UNDEFINED, 0, NULL, MAGIC_SET);
- if (JS_IsException(newset))
- goto exception;
- t = JS_GetOpaque(newset, JS_CLASS_SET);
- for (;;) {
- item = js_map_iterator_next(ctx, iter, 0, NULL, &done, MAGIC_SET);
- if (JS_IsException(item))
- goto exception;
- if (done) // item is JS_UNDEFINED
- break;
- rv = JS_Call(ctx, has, argv[0], 1, (JSValueConst *)&item);
- ok = JS_ToBoolFree(ctx, rv); // returns -1 if rv is JS_EXCEPTION
- if (ok > 0) {
- item = map_normalize_key(ctx, item);
- if (map_find_record(ctx, t, item)) {
- JS_FreeValue(ctx, item); // no duplicates
- } else {
- mr = set_add_record(ctx, t, item);
- JS_FreeValue(ctx, item);
- if (!mr)
- goto exception;
- }
- } else {
- JS_FreeValue(ctx, item);
- if (ok < 0)
- goto exception;
- }
- }
- }
- goto fini;
- exception:
- JS_FreeValue(ctx, newset);
- newset = JS_EXCEPTION;
- fini:
- JS_FreeValue(ctx, has);
- JS_FreeValue(ctx, keys);
- JS_FreeValue(ctx, iter);
- JS_FreeValue(ctx, next);
- return newset;
- }
- static JSValue js_set_difference(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue newset, item, iter, keys, has, next, rv;
- JSMapState *s, *t;
- int64_t size;
- int done;
- int ok;
- iter = JS_UNDEFINED;
- next = JS_UNDEFINED;
- newset = JS_UNDEFINED;
- s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET);
- if (!s)
- return JS_EXCEPTION;
- if (get_set_record(ctx, argv[0], &size, &has, &keys) < 0)
- goto exception;
- newset = js_copy_set(ctx, this_val);
- if (JS_IsException(newset))
- goto exception;
- t = JS_GetOpaque(newset, JS_CLASS_SET);
-
- if (s->record_count <= size) {
- iter = js_create_map_iterator(ctx, newset, 0, NULL, MAGIC_SET);
- if (JS_IsException(iter))
- goto exception;
- for (;;) {
- item = js_map_iterator_next(ctx, iter, 0, NULL, &done, MAGIC_SET);
- if (JS_IsException(item))
- goto exception;
- if (done) // item is JS_UNDEFINED
- break;
- rv = JS_Call(ctx, has, argv[0], 1, (JSValueConst *)&item);
- ok = JS_ToBoolFree(ctx, rv); // returns -1 if rv is JS_EXCEPTION
- if (ok < 0) {
- JS_FreeValue(ctx, item);
- goto exception;
- }
- if (ok) {
- map_delete_record(ctx, t, item);
- }
- JS_FreeValue(ctx, item);
- }
- } else {
- iter = JS_Call(ctx, keys, argv[0], 0, NULL);
- if (JS_IsException(iter))
- goto exception;
- next = JS_GetProperty(ctx, iter, JS_ATOM_next);
- if (JS_IsException(next))
- goto exception;
- for (;;) {
- item = JS_IteratorNext(ctx, iter, next, 0, NULL, &done);
- if (JS_IsException(item))
- goto exception;
- if (done) // item is JS_UNDEFINED
- break;
- map_delete_record(ctx, t, item);
- JS_FreeValue(ctx, item);
- }
- }
- goto fini;
- exception:
- JS_FreeValue(ctx, newset);
- newset = JS_EXCEPTION;
- fini:
- JS_FreeValue(ctx, has);
- JS_FreeValue(ctx, keys);
- JS_FreeValue(ctx, iter);
- JS_FreeValue(ctx, next);
- return newset;
- }
- static JSValue js_set_symmetricDifference(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue newset, item, iter, next, has, keys;
- JSMapState *s, *t;
- JSMapRecord *mr;
- int64_t size;
- int done;
- BOOL present;
- s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET);
- if (!s)
- return JS_EXCEPTION;
- if (get_set_record(ctx, argv[0], &size, &has, &keys) < 0)
- return JS_EXCEPTION;
- JS_FreeValue(ctx, has);
- next = JS_UNDEFINED;
- newset = JS_UNDEFINED;
- iter = JS_Call(ctx, keys, argv[0], 0, NULL);
- if (JS_IsException(iter))
- goto exception;
- next = JS_GetProperty(ctx, iter, JS_ATOM_next);
- if (JS_IsException(next))
- goto exception;
- newset = js_copy_set(ctx, this_val);
- if (JS_IsException(newset))
- goto exception;
- t = JS_GetOpaque(newset, JS_CLASS_SET);
- for (;;) {
- item = JS_IteratorNext(ctx, iter, next, 0, NULL, &done);
- if (JS_IsException(item))
- goto exception;
- if (done) // item is JS_UNDEFINED
- break;
- // note the subtlety here: due to mutating iterators, it's
- // possible for keys to disappear during iteration; test262
- // still expects us to maintain insertion order though, so
- // we first check |this|, then |new|; |new| is a copy of |this|
- // - if item exists in |this|, delete (if it exists) from |new|
- // - if item misses in |this| and |new|, add to |new|
- // - if item exists in |new| but misses in |this|, *don't* add it,
- // mutating iterator erased it
- item = map_normalize_key(ctx, item);
- present = (NULL != map_find_record(ctx, s, item));
- mr = map_find_record(ctx, t, item);
- if (present) {
- map_delete_record(ctx, t, item);
- JS_FreeValue(ctx, item);
- } else if (mr) {
- JS_FreeValue(ctx, item);
- } else {
- mr = set_add_record(ctx, t, item);
- JS_FreeValue(ctx, item);
- if (!mr)
- goto exception;
- }
- }
- goto fini;
- exception:
- JS_FreeValue(ctx, newset);
- newset = JS_EXCEPTION;
- fini:
- JS_FreeValue(ctx, next);
- JS_FreeValue(ctx, iter);
- JS_FreeValue(ctx, keys);
- return newset;
- }
- static JSValue js_set_union(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue newset, item, iter, next, has, keys, rv;
- JSMapState *s;
- int64_t size;
- int done;
- s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET);
- if (!s)
- return JS_EXCEPTION;
- if (get_set_record(ctx, argv[0], &size, &has, &keys) < 0)
- return JS_EXCEPTION;
- JS_FreeValue(ctx, has);
- next = JS_UNDEFINED;
- newset = JS_UNDEFINED;
- iter = JS_Call(ctx, keys, argv[0], 0, NULL);
- if (JS_IsException(iter))
- goto exception;
- next = JS_GetProperty(ctx, iter, JS_ATOM_next);
- if (JS_IsException(next))
- goto exception;
- newset = js_copy_set(ctx, this_val);
- if (JS_IsException(newset))
- goto exception;
- for (;;) {
- item = JS_IteratorNext(ctx, iter, next, 0, NULL, &done);
- if (JS_IsException(item))
- goto exception;
- if (done) // item is JS_UNDEFINED
- break;
- rv = js_map_set(ctx, newset, 1, (JSValueConst *)&item, MAGIC_SET);
- JS_FreeValue(ctx, item);
- if (JS_IsException(rv))
- goto exception;
- JS_FreeValue(ctx, rv);
- }
- goto fini;
- exception:
- JS_FreeValue(ctx, newset);
- newset = JS_EXCEPTION;
- fini:
- JS_FreeValue(ctx, next);
- JS_FreeValue(ctx, iter);
- JS_FreeValue(ctx, keys);
- return newset;
- }
- static const JSCFunctionListEntry js_map_funcs[] = {
- JS_CFUNC_MAGIC_DEF("groupBy", 2, js_object_groupBy, 1 ),
- JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
- };
- static const JSCFunctionListEntry js_map_proto_funcs[] = {
- JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, 0 ),
- JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, 0 ),
- JS_CFUNC_MAGIC_DEF("getOrInsert", 2, js_map_getOrInsert,
- (JS_CLASS_MAP << 1) | /*computed*/FALSE ),
- JS_CFUNC_MAGIC_DEF("getOrInsertComputed", 2, js_map_getOrInsert,
- (JS_CLASS_MAP << 1) | /*computed*/TRUE ),
- JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, 0 ),
- JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, 0 ),
- JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, 0 ),
- JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, 0),
- JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, 0 ),
- JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_VALUE << 2) | 0 ),
- JS_CFUNC_MAGIC_DEF("keys", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | 0 ),
- JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | 0 ),
- JS_ALIAS_DEF("[Symbol.iterator]", "entries" ),
- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map", JS_PROP_CONFIGURABLE ),
- };
- static const JSCFunctionListEntry js_map_iterator_proto_funcs[] = {
- JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, 0 ),
- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map Iterator", JS_PROP_CONFIGURABLE ),
- };
- static const JSCFunctionListEntry js_set_proto_funcs[] = {
- JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET ),
- JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET ),
- JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET ),
- JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, MAGIC_SET ),
- JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, MAGIC_SET ),
- JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, MAGIC_SET ),
- JS_CFUNC_DEF("isDisjointFrom", 1, js_set_isDisjointFrom ),
- JS_CFUNC_DEF("isSubsetOf", 1, js_set_isSubsetOf ),
- JS_CFUNC_DEF("isSupersetOf", 1, js_set_isSupersetOf ),
- JS_CFUNC_DEF("intersection", 1, js_set_intersection ),
- JS_CFUNC_DEF("difference", 1, js_set_difference ),
- JS_CFUNC_DEF("symmetricDifference", 1, js_set_symmetricDifference ),
- JS_CFUNC_DEF("union", 1, js_set_union ),
- JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | MAGIC_SET ),
- JS_ALIAS_DEF("keys", "values" ),
- JS_ALIAS_DEF("[Symbol.iterator]", "values" ),
- JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | MAGIC_SET ),
- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set", JS_PROP_CONFIGURABLE ),
- };
- static const JSCFunctionListEntry js_set_iterator_proto_funcs[] = {
- JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, MAGIC_SET ),
- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set Iterator", JS_PROP_CONFIGURABLE ),
- };
- static const JSCFunctionListEntry js_weak_map_proto_funcs[] = {
- JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, MAGIC_WEAK ),
- JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, MAGIC_WEAK ),
- JS_CFUNC_MAGIC_DEF("getOrInsert", 2, js_map_getOrInsert,
- (JS_CLASS_WEAKMAP << 1) | /*computed*/FALSE ),
- JS_CFUNC_MAGIC_DEF("getOrInsertComputed", 2, js_map_getOrInsert,
- (JS_CLASS_WEAKMAP << 1) | /*computed*/TRUE ),
- JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_WEAK ),
- JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_WEAK ),
- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakMap", JS_PROP_CONFIGURABLE ),
- };
- static const JSCFunctionListEntry js_weak_set_proto_funcs[] = {
- JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET | MAGIC_WEAK ),
- JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET | MAGIC_WEAK ),
- JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET | MAGIC_WEAK ),
- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakSet", JS_PROP_CONFIGURABLE ),
- };
- static const JSCFunctionListEntry * const js_map_proto_funcs_ptr[6] = {
- js_map_proto_funcs,
- js_set_proto_funcs,
- js_weak_map_proto_funcs,
- js_weak_set_proto_funcs,
- js_map_iterator_proto_funcs,
- js_set_iterator_proto_funcs,
- };
- static const uint8_t js_map_proto_funcs_count[6] = {
- countof(js_map_proto_funcs),
- countof(js_set_proto_funcs),
- countof(js_weak_map_proto_funcs),
- countof(js_weak_set_proto_funcs),
- countof(js_map_iterator_proto_funcs),
- countof(js_set_iterator_proto_funcs),
- };
- int JS_AddIntrinsicMapSet(JSContext *ctx)
- {
- int i;
- JSValue obj1;
- char buf[ATOM_GET_STR_BUF_SIZE];
- for(i = 0; i < 4; i++) {
- JSCFunctionType ft;
- const char *name = JS_AtomGetStr(ctx, buf, sizeof(buf),
- JS_ATOM_Map + i);
- ft.constructor_magic = js_map_constructor;
- obj1 = JS_NewCConstructor(ctx, JS_CLASS_MAP + i, name,
- ft.generic, 0, JS_CFUNC_constructor_magic, i,
- JS_UNDEFINED,
- js_map_funcs, i < 2 ? countof(js_map_funcs) : 0,
- js_map_proto_funcs_ptr[i], js_map_proto_funcs_count[i],
- 0);
- if (JS_IsException(obj1))
- return -1;
- JS_FreeValue(ctx, obj1);
- }
- for(i = 0; i < 2; i++) {
- ctx->class_proto[JS_CLASS_MAP_ITERATOR + i] =
- JS_NewObjectProtoList(ctx, ctx->class_proto[JS_CLASS_ITERATOR],
- js_map_proto_funcs_ptr[i + 4],
- js_map_proto_funcs_count[i + 4]);
- if (JS_IsException(ctx->class_proto[JS_CLASS_MAP_ITERATOR + i]))
- return -1;
- }
- return 0;
- }
- /* Generator */
- static const JSCFunctionListEntry js_generator_function_proto_funcs[] = {
- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "GeneratorFunction", JS_PROP_CONFIGURABLE),
- };
- static const JSCFunctionListEntry js_generator_proto_funcs[] = {
- JS_ITERATOR_NEXT_DEF("next", 1, js_generator_next, GEN_MAGIC_NEXT ),
- JS_ITERATOR_NEXT_DEF("return", 1, js_generator_next, GEN_MAGIC_RETURN ),
- JS_ITERATOR_NEXT_DEF("throw", 1, js_generator_next, GEN_MAGIC_THROW ),
- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Generator", JS_PROP_CONFIGURABLE),
- };
- /* Promise */
- typedef struct JSPromiseData {
- JSPromiseStateEnum promise_state;
- /* 0=fulfill, 1=reject, list of JSPromiseReactionData.link */
- struct list_head promise_reactions[2];
- BOOL is_handled; /* Note: only useful to debug */
- JSValue promise_result;
- } JSPromiseData;
- typedef struct JSPromiseFunctionDataResolved {
- int ref_count;
- BOOL already_resolved;
- } JSPromiseFunctionDataResolved;
- typedef struct JSPromiseFunctionData {
- JSValue promise;
- JSPromiseFunctionDataResolved *presolved;
- } JSPromiseFunctionData;
- typedef struct JSPromiseReactionData {
- struct list_head link; /* not used in promise_reaction_job */
- JSValue resolving_funcs[2];
- JSValue handler;
- } JSPromiseReactionData;
- JSPromiseStateEnum JS_PromiseState(JSContext *ctx, JSValue promise)
- {
- JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE);
- if (!s)
- return -1;
- return s->promise_state;
- }
- JSValue JS_PromiseResult(JSContext *ctx, JSValue promise)
- {
- JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE);
- if (!s)
- return JS_UNDEFINED;
- return JS_DupValue(ctx, s->promise_result);
- }
- static int js_create_resolving_functions(JSContext *ctx, JSValue *args,
- JSValueConst promise);
- static void promise_reaction_data_free(JSRuntime *rt,
- JSPromiseReactionData *rd)
- {
- JS_FreeValueRT(rt, rd->resolving_funcs[0]);
- JS_FreeValueRT(rt, rd->resolving_funcs[1]);
- JS_FreeValueRT(rt, rd->handler);
- js_free_rt(rt, rd);
- }
- static JSValue promise_reaction_job(JSContext *ctx, int argc,
- JSValueConst *argv)
- {
- JSValueConst handler, arg, func;
- JSValue res, res2;
- BOOL is_reject;
- assert(argc == 5);
- handler = argv[2];
- is_reject = JS_ToBool(ctx, argv[3]);
- arg = argv[4];
- #ifdef DUMP_PROMISE
- printf("promise_reaction_job: is_reject=%d\n", is_reject);
- #endif
- if (JS_IsUndefined(handler)) {
- if (is_reject) {
- res = JS_Throw(ctx, JS_DupValue(ctx, arg));
- } else {
- res = JS_DupValue(ctx, arg);
- }
- } else {
- res = JS_Call(ctx, handler, JS_UNDEFINED, 1, &arg);
- }
- is_reject = JS_IsException(res);
- if (is_reject)
- res = JS_GetException(ctx);
- func = argv[is_reject];
- /* as an extension, we support undefined as value to avoid
- creating a dummy promise in the 'await' implementation of async
- functions */
- if (!JS_IsUndefined(func)) {
- res2 = JS_Call(ctx, func, JS_UNDEFINED,
- 1, (JSValueConst *)&res);
- } else {
- res2 = JS_UNDEFINED;
- }
- JS_FreeValue(ctx, res);
- return res2;
- }
- void JS_SetHostPromiseRejectionTracker(JSRuntime *rt,
- JSHostPromiseRejectionTracker *cb,
- void *opaque)
- {
- rt->host_promise_rejection_tracker = cb;
- rt->host_promise_rejection_tracker_opaque = opaque;
- }
- static void fulfill_or_reject_promise(JSContext *ctx, JSValueConst promise,
- JSValueConst value, BOOL is_reject)
- {
- JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE);
- struct list_head *el, *el1;
- JSPromiseReactionData *rd;
- JSValueConst args[5];
- if (!s || s->promise_state != JS_PROMISE_PENDING)
- return; /* should never happen */
- set_value(ctx, &s->promise_result, JS_DupValue(ctx, value));
- s->promise_state = JS_PROMISE_FULFILLED + is_reject;
- #ifdef DUMP_PROMISE
- printf("fulfill_or_reject_promise: is_reject=%d\n", is_reject);
- #endif
- if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) {
- JSRuntime *rt = ctx->rt;
- if (rt->host_promise_rejection_tracker) {
- rt->host_promise_rejection_tracker(ctx, promise, value, FALSE,
- rt->host_promise_rejection_tracker_opaque);
- }
- }
- list_for_each_safe(el, el1, &s->promise_reactions[is_reject]) {
- rd = list_entry(el, JSPromiseReactionData, link);
- args[0] = rd->resolving_funcs[0];
- args[1] = rd->resolving_funcs[1];
- args[2] = rd->handler;
- args[3] = JS_NewBool(ctx, is_reject);
- args[4] = value;
- JS_EnqueueJob(ctx, promise_reaction_job, 5, args);
- list_del(&rd->link);
- promise_reaction_data_free(ctx->rt, rd);
- }
- list_for_each_safe(el, el1, &s->promise_reactions[1 - is_reject]) {
- rd = list_entry(el, JSPromiseReactionData, link);
- list_del(&rd->link);
- promise_reaction_data_free(ctx->rt, rd);
- }
- }
- static void reject_promise(JSContext *ctx, JSValueConst promise,
- JSValueConst value)
- {
- fulfill_or_reject_promise(ctx, promise, value, TRUE);
- }
- static JSValue js_promise_resolve_thenable_job(JSContext *ctx,
- int argc, JSValueConst *argv)
- {
- JSValueConst promise, thenable, then;
- JSValue args[2], res;
- #ifdef DUMP_PROMISE
- printf("js_promise_resolve_thenable_job\n");
- #endif
- assert(argc == 3);
- promise = argv[0];
- thenable = argv[1];
- then = argv[2];
- if (js_create_resolving_functions(ctx, args, promise) < 0)
- return JS_EXCEPTION;
- res = JS_Call(ctx, then, thenable, 2, (JSValueConst *)args);
- if (JS_IsException(res)) {
- JSValue error = JS_GetException(ctx);
- res = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error);
- JS_FreeValue(ctx, error);
- }
- JS_FreeValue(ctx, args[0]);
- JS_FreeValue(ctx, args[1]);
- return res;
- }
- static void js_promise_resolve_function_free_resolved(JSRuntime *rt,
- JSPromiseFunctionDataResolved *sr)
- {
- if (--sr->ref_count == 0) {
- js_free_rt(rt, sr);
- }
- }
- static int js_create_resolving_functions(JSContext *ctx,
- JSValue *resolving_funcs,
- JSValueConst promise)
- {
- JSValue obj;
- JSPromiseFunctionData *s;
- JSPromiseFunctionDataResolved *sr;
- int i, ret;
- sr = js_malloc(ctx, sizeof(*sr));
- if (!sr)
- return -1;
- sr->ref_count = 1;
- sr->already_resolved = FALSE; /* must be shared between the two functions */
- ret = 0;
- for(i = 0; i < 2; i++) {
- obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,
- JS_CLASS_PROMISE_RESOLVE_FUNCTION + i);
- if (JS_IsException(obj))
- goto fail;
- s = js_malloc(ctx, sizeof(*s));
- if (!s) {
- JS_FreeValue(ctx, obj);
- fail:
- if (i != 0)
- JS_FreeValue(ctx, resolving_funcs[0]);
- ret = -1;
- break;
- }
- sr->ref_count++;
- s->presolved = sr;
- s->promise = JS_DupValue(ctx, promise);
- JS_SetOpaque(obj, s);
- js_function_set_properties(ctx, obj, JS_ATOM_empty_string, 1);
- resolving_funcs[i] = obj;
- }
- js_promise_resolve_function_free_resolved(ctx->rt, sr);
- return ret;
- }
- static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val)
- {
- JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data;
- if (s) {
- js_promise_resolve_function_free_resolved(rt, s->presolved);
- JS_FreeValueRT(rt, s->promise);
- js_free_rt(rt, s);
- }
- }
- static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func)
- {
- JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data;
- if (s) {
- JS_MarkValue(rt, s->promise, mark_func);
- }
- }
- static JSValue js_promise_resolve_function_call(JSContext *ctx,
- JSValueConst func_obj,
- JSValueConst this_val,
- int argc, JSValueConst *argv,
- int flags)
- {
- JSObject *p = JS_VALUE_GET_OBJ(func_obj);
- JSPromiseFunctionData *s;
- JSValueConst resolution, args[3];
- JSValue then;
- BOOL is_reject;
- s = p->u.promise_function_data;
- if (!s || s->presolved->already_resolved)
- return JS_UNDEFINED;
- s->presolved->already_resolved = TRUE;
- is_reject = p->class_id - JS_CLASS_PROMISE_RESOLVE_FUNCTION;
- if (argc > 0)
- resolution = argv[0];
- else
- resolution = JS_UNDEFINED;
- #ifdef DUMP_PROMISE
- printf("js_promise_resolving_function_call: is_reject=%d ", is_reject);
- JS_DumpValue(ctx, "resolution", resolution);
- printf("\n");
- #endif
- if (is_reject || !JS_IsObject(resolution)) {
- goto done;
- } else if (js_same_value(ctx, resolution, s->promise)) {
- JS_ThrowTypeError(ctx, "promise self resolution");
- goto fail_reject;
- }
- then = JS_GetProperty(ctx, resolution, JS_ATOM_then);
- if (JS_IsException(then)) {
- JSValue error;
- fail_reject:
- error = JS_GetException(ctx);
- reject_promise(ctx, s->promise, error);
- JS_FreeValue(ctx, error);
- } else if (!JS_IsFunction(ctx, then)) {
- JS_FreeValue(ctx, then);
- done:
- fulfill_or_reject_promise(ctx, s->promise, resolution, is_reject);
- } else {
- args[0] = s->promise;
- args[1] = resolution;
- args[2] = then;
- JS_EnqueueJob(ctx, js_promise_resolve_thenable_job, 3, args);
- JS_FreeValue(ctx, then);
- }
- return JS_UNDEFINED;
- }
- static void js_promise_finalizer(JSRuntime *rt, JSValue val)
- {
- JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE);
- struct list_head *el, *el1;
- int i;
- if (!s)
- return;
- for(i = 0; i < 2; i++) {
- list_for_each_safe(el, el1, &s->promise_reactions[i]) {
- JSPromiseReactionData *rd =
- list_entry(el, JSPromiseReactionData, link);
- promise_reaction_data_free(rt, rd);
- }
- }
- JS_FreeValueRT(rt, s->promise_result);
- js_free_rt(rt, s);
- }
- static void js_promise_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func)
- {
- JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE);
- struct list_head *el;
- int i;
- if (!s)
- return;
- for(i = 0; i < 2; i++) {
- list_for_each(el, &s->promise_reactions[i]) {
- JSPromiseReactionData *rd =
- list_entry(el, JSPromiseReactionData, link);
- JS_MarkValue(rt, rd->resolving_funcs[0], mark_func);
- JS_MarkValue(rt, rd->resolving_funcs[1], mark_func);
- JS_MarkValue(rt, rd->handler, mark_func);
- }
- }
- JS_MarkValue(rt, s->promise_result, mark_func);
- }
- static JSValue js_promise_constructor(JSContext *ctx, JSValueConst new_target,
- int argc, JSValueConst *argv)
- {
- JSValueConst executor;
- JSValue obj;
- JSPromiseData *s;
- JSValue args[2], ret;
- int i;
- executor = argv[0];
- if (check_function(ctx, executor))
- return JS_EXCEPTION;
- obj = js_create_from_ctor(ctx, new_target, JS_CLASS_PROMISE);
- if (JS_IsException(obj))
- return JS_EXCEPTION;
- s = js_mallocz(ctx, sizeof(*s));
- if (!s)
- goto fail;
- s->promise_state = JS_PROMISE_PENDING;
- s->is_handled = FALSE;
- for(i = 0; i < 2; i++)
- init_list_head(&s->promise_reactions[i]);
- s->promise_result = JS_UNDEFINED;
- JS_SetOpaque(obj, s);
- if (js_create_resolving_functions(ctx, args, obj))
- goto fail;
- ret = JS_Call(ctx, executor, JS_UNDEFINED, 2, (JSValueConst *)args);
- if (JS_IsException(ret)) {
- JSValue ret2, error;
- error = JS_GetException(ctx);
- ret2 = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error);
- JS_FreeValue(ctx, error);
- if (JS_IsException(ret2))
- goto fail1;
- JS_FreeValue(ctx, ret2);
- }
- JS_FreeValue(ctx, ret);
- JS_FreeValue(ctx, args[0]);
- JS_FreeValue(ctx, args[1]);
- return obj;
- fail1:
- JS_FreeValue(ctx, args[0]);
- JS_FreeValue(ctx, args[1]);
- fail:
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- static JSValue js_promise_executor(JSContext *ctx,
- JSValueConst this_val,
- int argc, JSValueConst *argv,
- int magic, JSValue *func_data)
- {
- int i;
- for(i = 0; i < 2; i++) {
- if (!JS_IsUndefined(func_data[i]))
- return JS_ThrowTypeError(ctx, "resolving function already set");
- func_data[i] = JS_DupValue(ctx, argv[i]);
- }
- return JS_UNDEFINED;
- }
- static JSValue js_promise_executor_new(JSContext *ctx)
- {
- JSValueConst func_data[2];
- func_data[0] = JS_UNDEFINED;
- func_data[1] = JS_UNDEFINED;
- return JS_NewCFunctionData(ctx, js_promise_executor, 2,
- 0, 2, func_data);
- }
- static JSValue js_new_promise_capability(JSContext *ctx,
- JSValue *resolving_funcs,
- JSValueConst ctor)
- {
- JSValue executor, result_promise;
- JSCFunctionDataRecord *s;
- int i;
- executor = js_promise_executor_new(ctx);
- if (JS_IsException(executor))
- return executor;
- if (JS_IsUndefined(ctor)) {
- result_promise = js_promise_constructor(ctx, ctor, 1,
- (JSValueConst *)&executor);
- } else {
- result_promise = JS_CallConstructor(ctx, ctor, 1,
- (JSValueConst *)&executor);
- }
- if (JS_IsException(result_promise))
- goto fail;
- s = JS_GetOpaque(executor, JS_CLASS_C_FUNCTION_DATA);
- for(i = 0; i < 2; i++) {
- if (check_function(ctx, s->data[i]))
- goto fail;
- }
- for(i = 0; i < 2; i++)
- resolving_funcs[i] = JS_DupValue(ctx, s->data[i]);
- JS_FreeValue(ctx, executor);
- return result_promise;
- fail:
- JS_FreeValue(ctx, executor);
- JS_FreeValue(ctx, result_promise);
- return JS_EXCEPTION;
- }
- JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs)
- {
- return js_new_promise_capability(ctx, resolving_funcs, JS_UNDEFINED);
- }
- static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
- {
- JSValue result_promise, resolving_funcs[2], ret;
- BOOL is_reject = magic;
- if (!JS_IsObject(this_val))
- return JS_ThrowTypeErrorNotAnObject(ctx);
- if (!is_reject && JS_GetOpaque(argv[0], JS_CLASS_PROMISE)) {
- JSValue ctor;
- BOOL is_same;
- ctor = JS_GetProperty(ctx, argv[0], JS_ATOM_constructor);
- if (JS_IsException(ctor))
- return ctor;
- is_same = js_same_value(ctx, ctor, this_val);
- JS_FreeValue(ctx, ctor);
- if (is_same)
- return JS_DupValue(ctx, argv[0]);
- }
- result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);
- if (JS_IsException(result_promise))
- return result_promise;
- ret = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, 1, argv);
- JS_FreeValue(ctx, resolving_funcs[0]);
- JS_FreeValue(ctx, resolving_funcs[1]);
- if (JS_IsException(ret)) {
- JS_FreeValue(ctx, result_promise);
- return ret;
- }
- JS_FreeValue(ctx, ret);
- return result_promise;
- }
- static JSValue js_promise_withResolvers(JSContext *ctx,
- JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue result_promise, resolving_funcs[2], obj;
- if (!JS_IsObject(this_val))
- return JS_ThrowTypeErrorNotAnObject(ctx);
- result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);
- if (JS_IsException(result_promise))
- return result_promise;
- obj = JS_NewObject(ctx);
- if (JS_IsException(obj))
- goto exception;
- if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_promise, result_promise,
- JS_PROP_C_W_E) < 0) {
- goto exception;
- }
- result_promise = JS_UNDEFINED;
- if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_resolve, resolving_funcs[0],
- JS_PROP_C_W_E) < 0) {
- goto exception;
- }
- resolving_funcs[0] = JS_UNDEFINED;
- if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_reject, resolving_funcs[1],
- JS_PROP_C_W_E) < 0) {
- goto exception;
- }
- return obj;
- exception:
- JS_FreeValue(ctx, resolving_funcs[0]);
- JS_FreeValue(ctx, resolving_funcs[1]);
- JS_FreeValue(ctx, result_promise);
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- static JSValue js_promise_try(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue result_promise, resolving_funcs[2], ret, ret2;
- BOOL is_reject = 0;
- if (!JS_IsObject(this_val))
- return JS_ThrowTypeErrorNotAnObject(ctx);
- result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);
- if (JS_IsException(result_promise))
- return result_promise;
- ret = JS_Call(ctx, argv[0], JS_UNDEFINED, argc - 1, argv + 1);
- if (JS_IsException(ret)) {
- is_reject = 1;
- ret = JS_GetException(ctx);
- }
- ret2 = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, 1, (JSValueConst *)&ret);
- JS_FreeValue(ctx, resolving_funcs[0]);
- JS_FreeValue(ctx, resolving_funcs[1]);
- JS_FreeValue(ctx, ret);
- if (JS_IsException(ret2)) {
- JS_FreeValue(ctx, result_promise);
- return ret2;
- }
- JS_FreeValue(ctx, ret2);
- return result_promise;
- }
- static __exception int remainingElementsCount_add(JSContext *ctx,
- JSValueConst resolve_element_env,
- int addend)
- {
- JSValue val;
- int remainingElementsCount;
- val = JS_GetPropertyUint32(ctx, resolve_element_env, 0);
- if (JS_IsException(val))
- return -1;
- if (JS_ToInt32Free(ctx, &remainingElementsCount, val))
- return -1;
- remainingElementsCount += addend;
- if (JS_SetPropertyUint32(ctx, resolve_element_env, 0,
- JS_NewInt32(ctx, remainingElementsCount)) < 0)
- return -1;
- return (remainingElementsCount == 0);
- }
- #define PROMISE_MAGIC_all 0
- #define PROMISE_MAGIC_allSettled 1
- #define PROMISE_MAGIC_any 2
- static JSValue js_promise_all_resolve_element(JSContext *ctx,
- JSValueConst this_val,
- int argc, JSValueConst *argv,
- int magic,
- JSValue *func_data)
- {
- int resolve_type = magic & 3;
- int is_reject = magic & 4;
- BOOL alreadyCalled = JS_ToBool(ctx, func_data[0]);
- JSValueConst values = func_data[2];
- JSValueConst resolve = func_data[3];
- JSValueConst resolve_element_env = func_data[4];
- JSValue ret, obj;
- int is_zero, index;
- if (JS_ToInt32(ctx, &index, func_data[1]))
- return JS_EXCEPTION;
- if (alreadyCalled)
- return JS_UNDEFINED;
- func_data[0] = JS_NewBool(ctx, TRUE);
- if (resolve_type == PROMISE_MAGIC_allSettled) {
- JSValue str;
- obj = JS_NewObject(ctx);
- if (JS_IsException(obj))
- return JS_EXCEPTION;
- str = js_new_string8(ctx, is_reject ? "rejected" : "fulfilled");
- if (JS_IsException(str))
- goto fail1;
- if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_status,
- str,
- JS_PROP_C_W_E) < 0)
- goto fail1;
- if (JS_DefinePropertyValue(ctx, obj,
- is_reject ? JS_ATOM_reason : JS_ATOM_value,
- JS_DupValue(ctx, argv[0]),
- JS_PROP_C_W_E) < 0) {
- fail1:
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- } else {
- obj = JS_DupValue(ctx, argv[0]);
- }
- if (JS_DefinePropertyValueUint32(ctx, values, index,
- obj, JS_PROP_C_W_E) < 0)
- return JS_EXCEPTION;
- is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1);
- if (is_zero < 0)
- return JS_EXCEPTION;
- if (is_zero) {
- if (resolve_type == PROMISE_MAGIC_any) {
- JSValue error;
- error = js_aggregate_error_constructor(ctx, values);
- if (JS_IsException(error))
- return JS_EXCEPTION;
- ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&error);
- JS_FreeValue(ctx, error);
- } else {
- ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&values);
- }
- if (JS_IsException(ret))
- return ret;
- JS_FreeValue(ctx, ret);
- }
- return JS_UNDEFINED;
- }
- /* magic = 0: Promise.all 1: Promise.allSettled */
- static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
- {
- JSValue result_promise, resolving_funcs[2], item, next_promise, ret;
- JSValue next_method = JS_UNDEFINED, values = JS_UNDEFINED;
- JSValue resolve_element_env = JS_UNDEFINED, resolve_element, reject_element;
- JSValue promise_resolve = JS_UNDEFINED, iter = JS_UNDEFINED;
- JSValueConst then_args[2], resolve_element_data[5];
- BOOL done;
- int index, is_zero, is_promise_any = (magic == PROMISE_MAGIC_any);
- if (!JS_IsObject(this_val))
- return JS_ThrowTypeErrorNotAnObject(ctx);
- result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);
- if (JS_IsException(result_promise))
- return result_promise;
- promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve);
- if (JS_IsException(promise_resolve) ||
- check_function(ctx, promise_resolve))
- goto fail_reject;
- iter = JS_GetIterator(ctx, argv[0], FALSE);
- if (JS_IsException(iter)) {
- JSValue error;
- fail_reject:
- error = JS_GetException(ctx);
- ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1,
- (JSValueConst *)&error);
- JS_FreeValue(ctx, error);
- if (JS_IsException(ret))
- goto fail;
- JS_FreeValue(ctx, ret);
- } else {
- next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
- if (JS_IsException(next_method))
- goto fail_reject;
- values = JS_NewArray(ctx);
- if (JS_IsException(values))
- goto fail_reject;
- resolve_element_env = JS_NewArray(ctx);
- if (JS_IsException(resolve_element_env))
- goto fail_reject;
- /* remainingElementsCount field */
- if (JS_DefinePropertyValueUint32(ctx, resolve_element_env, 0,
- JS_NewInt32(ctx, 1),
- JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0)
- goto fail_reject;
- index = 0;
- for(;;) {
- /* XXX: conformance: should close the iterator if error on 'done'
- access, but not on 'value' access */
- item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
- if (JS_IsException(item))
- goto fail_reject;
- if (done)
- break;
- next_promise = JS_Call(ctx, promise_resolve,
- this_val, 1, (JSValueConst *)&item);
- JS_FreeValue(ctx, item);
- if (JS_IsException(next_promise)) {
- fail_reject1:
- JS_IteratorClose(ctx, iter, TRUE);
- goto fail_reject;
- }
- resolve_element_data[0] = JS_NewBool(ctx, FALSE);
- resolve_element_data[1] = (JSValueConst)JS_NewInt32(ctx, index);
- resolve_element_data[2] = values;
- resolve_element_data[3] = resolving_funcs[is_promise_any];
- resolve_element_data[4] = resolve_element_env;
- resolve_element =
- JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1,
- magic, 5, resolve_element_data);
- if (JS_IsException(resolve_element)) {
- JS_FreeValue(ctx, next_promise);
- goto fail_reject1;
- }
- if (magic == PROMISE_MAGIC_allSettled) {
- reject_element =
- JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1,
- magic | 4, 5, resolve_element_data);
- if (JS_IsException(reject_element)) {
- JS_FreeValue(ctx, next_promise);
- goto fail_reject1;
- }
- } else if (magic == PROMISE_MAGIC_any) {
- if (JS_DefinePropertyValueUint32(ctx, values, index,
- JS_UNDEFINED, JS_PROP_C_W_E) < 0)
- goto fail_reject1;
- reject_element = resolve_element;
- resolve_element = JS_DupValue(ctx, resolving_funcs[0]);
- } else {
- reject_element = JS_DupValue(ctx, resolving_funcs[1]);
- }
- if (remainingElementsCount_add(ctx, resolve_element_env, 1) < 0) {
- JS_FreeValue(ctx, next_promise);
- JS_FreeValue(ctx, resolve_element);
- JS_FreeValue(ctx, reject_element);
- goto fail_reject1;
- }
- then_args[0] = resolve_element;
- then_args[1] = reject_element;
- ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2, then_args);
- JS_FreeValue(ctx, resolve_element);
- JS_FreeValue(ctx, reject_element);
- if (check_exception_free(ctx, ret))
- goto fail_reject1;
- index++;
- }
- is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1);
- if (is_zero < 0)
- goto fail_reject;
- if (is_zero) {
- if (magic == PROMISE_MAGIC_any) {
- JSValue error;
- error = js_aggregate_error_constructor(ctx, values);
- if (JS_IsException(error))
- goto fail_reject;
- JS_FreeValue(ctx, values);
- values = error;
- }
- ret = JS_Call(ctx, resolving_funcs[is_promise_any], JS_UNDEFINED,
- 1, (JSValueConst *)&values);
- if (check_exception_free(ctx, ret))
- goto fail_reject;
- }
- }
- done:
- JS_FreeValue(ctx, promise_resolve);
- JS_FreeValue(ctx, resolve_element_env);
- JS_FreeValue(ctx, values);
- JS_FreeValue(ctx, next_method);
- JS_FreeValue(ctx, iter);
- JS_FreeValue(ctx, resolving_funcs[0]);
- JS_FreeValue(ctx, resolving_funcs[1]);
- return result_promise;
- fail:
- JS_FreeValue(ctx, result_promise);
- result_promise = JS_EXCEPTION;
- goto done;
- }
- static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue result_promise, resolving_funcs[2], item, next_promise, ret;
- JSValue next_method = JS_UNDEFINED, iter = JS_UNDEFINED;
- JSValue promise_resolve = JS_UNDEFINED;
- BOOL done;
- if (!JS_IsObject(this_val))
- return JS_ThrowTypeErrorNotAnObject(ctx);
- result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);
- if (JS_IsException(result_promise))
- return result_promise;
- promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve);
- if (JS_IsException(promise_resolve) ||
- check_function(ctx, promise_resolve))
- goto fail_reject;
- iter = JS_GetIterator(ctx, argv[0], FALSE);
- if (JS_IsException(iter)) {
- JSValue error;
- fail_reject:
- error = JS_GetException(ctx);
- ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1,
- (JSValueConst *)&error);
- JS_FreeValue(ctx, error);
- if (JS_IsException(ret))
- goto fail;
- JS_FreeValue(ctx, ret);
- } else {
- next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
- if (JS_IsException(next_method))
- goto fail_reject;
- for(;;) {
- /* XXX: conformance: should close the iterator if error on 'done'
- access, but not on 'value' access */
- item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
- if (JS_IsException(item))
- goto fail_reject;
- if (done)
- break;
- next_promise = JS_Call(ctx, promise_resolve,
- this_val, 1, (JSValueConst *)&item);
- JS_FreeValue(ctx, item);
- if (JS_IsException(next_promise)) {
- fail_reject1:
- JS_IteratorClose(ctx, iter, TRUE);
- goto fail_reject;
- }
- ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2,
- (JSValueConst *)resolving_funcs);
- if (check_exception_free(ctx, ret))
- goto fail_reject1;
- }
- }
- done:
- JS_FreeValue(ctx, promise_resolve);
- JS_FreeValue(ctx, next_method);
- JS_FreeValue(ctx, iter);
- JS_FreeValue(ctx, resolving_funcs[0]);
- JS_FreeValue(ctx, resolving_funcs[1]);
- return result_promise;
- fail:
- //JS_FreeValue(ctx, next_method); // why not???
- JS_FreeValue(ctx, result_promise);
- result_promise = JS_EXCEPTION;
- goto done;
- }
- static __exception int perform_promise_then(JSContext *ctx,
- JSValueConst promise,
- JSValueConst *resolve_reject,
- JSValueConst *cap_resolving_funcs)
- {
- JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE);
- JSPromiseReactionData *rd_array[2], *rd;
- int i, j;
- rd_array[0] = NULL;
- rd_array[1] = NULL;
- for(i = 0; i < 2; i++) {
- JSValueConst handler;
- rd = js_mallocz(ctx, sizeof(*rd));
- if (!rd) {
- if (i == 1)
- promise_reaction_data_free(ctx->rt, rd_array[0]);
- return -1;
- }
- for(j = 0; j < 2; j++)
- rd->resolving_funcs[j] = JS_DupValue(ctx, cap_resolving_funcs[j]);
- handler = resolve_reject[i];
- if (!JS_IsFunction(ctx, handler))
- handler = JS_UNDEFINED;
- rd->handler = JS_DupValue(ctx, handler);
- rd_array[i] = rd;
- }
- if (s->promise_state == JS_PROMISE_PENDING) {
- for(i = 0; i < 2; i++)
- list_add_tail(&rd_array[i]->link, &s->promise_reactions[i]);
- } else {
- JSValueConst args[5];
- if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) {
- JSRuntime *rt = ctx->rt;
- if (rt->host_promise_rejection_tracker) {
- rt->host_promise_rejection_tracker(ctx, promise, s->promise_result,
- TRUE, rt->host_promise_rejection_tracker_opaque);
- }
- }
- i = s->promise_state - JS_PROMISE_FULFILLED;
- rd = rd_array[i];
- args[0] = rd->resolving_funcs[0];
- args[1] = rd->resolving_funcs[1];
- args[2] = rd->handler;
- args[3] = JS_NewBool(ctx, i);
- args[4] = s->promise_result;
- JS_EnqueueJob(ctx, promise_reaction_job, 5, args);
- for(i = 0; i < 2; i++)
- promise_reaction_data_free(ctx->rt, rd_array[i]);
- }
- s->is_handled = TRUE;
- return 0;
- }
- static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue ctor, result_promise, resolving_funcs[2];
- JSPromiseData *s;
- int i, ret;
- s = JS_GetOpaque2(ctx, this_val, JS_CLASS_PROMISE);
- if (!s)
- return JS_EXCEPTION;
- ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED);
- if (JS_IsException(ctor))
- return ctor;
- result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor);
- JS_FreeValue(ctx, ctor);
- if (JS_IsException(result_promise))
- return result_promise;
- ret = perform_promise_then(ctx, this_val, argv,
- (JSValueConst *)resolving_funcs);
- for(i = 0; i < 2; i++)
- JS_FreeValue(ctx, resolving_funcs[i]);
- if (ret) {
- JS_FreeValue(ctx, result_promise);
- return JS_EXCEPTION;
- }
- return result_promise;
- }
- static JSValue js_promise_catch(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValueConst args[2];
- args[0] = JS_UNDEFINED;
- args[1] = argv[0];
- return JS_Invoke(ctx, this_val, JS_ATOM_then, 2, args);
- }
- static JSValue js_promise_finally_value_thunk(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv,
- int magic, JSValue *func_data)
- {
- return JS_DupValue(ctx, func_data[0]);
- }
- static JSValue js_promise_finally_thrower(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv,
- int magic, JSValue *func_data)
- {
- return JS_Throw(ctx, JS_DupValue(ctx, func_data[0]));
- }
- static JSValue js_promise_then_finally_func(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv,
- int magic, JSValue *func_data)
- {
- JSValueConst ctor = func_data[0];
- JSValueConst onFinally = func_data[1];
- JSValue res, promise, ret, then_func;
- res = JS_Call(ctx, onFinally, JS_UNDEFINED, 0, NULL);
- if (JS_IsException(res))
- return res;
- promise = js_promise_resolve(ctx, ctor, 1, (JSValueConst *)&res, 0);
- JS_FreeValue(ctx, res);
- if (JS_IsException(promise))
- return promise;
- if (magic == 0) {
- then_func = JS_NewCFunctionData(ctx, js_promise_finally_value_thunk, 0,
- 0, 1, argv);
- } else {
- then_func = JS_NewCFunctionData(ctx, js_promise_finally_thrower, 0,
- 0, 1, argv);
- }
- if (JS_IsException(then_func)) {
- JS_FreeValue(ctx, promise);
- return then_func;
- }
- ret = JS_InvokeFree(ctx, promise, JS_ATOM_then, 1, (JSValueConst *)&then_func);
- JS_FreeValue(ctx, then_func);
- return ret;
- }
- static JSValue js_promise_finally(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValueConst onFinally = argv[0];
- JSValue ctor, ret;
- JSValue then_funcs[2];
- JSValueConst func_data[2];
- int i;
- ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED);
- if (JS_IsException(ctor))
- return ctor;
- if (!JS_IsFunction(ctx, onFinally)) {
- then_funcs[0] = JS_DupValue(ctx, onFinally);
- then_funcs[1] = JS_DupValue(ctx, onFinally);
- } else {
- func_data[0] = ctor;
- func_data[1] = onFinally;
- for(i = 0; i < 2; i++) {
- then_funcs[i] = JS_NewCFunctionData(ctx, js_promise_then_finally_func, 1, i, 2, func_data);
- if (JS_IsException(then_funcs[i])) {
- if (i == 1)
- JS_FreeValue(ctx, then_funcs[0]);
- JS_FreeValue(ctx, ctor);
- return JS_EXCEPTION;
- }
- }
- }
- JS_FreeValue(ctx, ctor);
- ret = JS_Invoke(ctx, this_val, JS_ATOM_then, 2, (JSValueConst *)then_funcs);
- JS_FreeValue(ctx, then_funcs[0]);
- JS_FreeValue(ctx, then_funcs[1]);
- return ret;
- }
- static const JSCFunctionListEntry js_promise_funcs[] = {
- JS_CFUNC_MAGIC_DEF("resolve", 1, js_promise_resolve, 0 ),
- JS_CFUNC_MAGIC_DEF("reject", 1, js_promise_resolve, 1 ),
- JS_CFUNC_MAGIC_DEF("all", 1, js_promise_all, PROMISE_MAGIC_all ),
- JS_CFUNC_MAGIC_DEF("allSettled", 1, js_promise_all, PROMISE_MAGIC_allSettled ),
- JS_CFUNC_MAGIC_DEF("any", 1, js_promise_all, PROMISE_MAGIC_any ),
- JS_CFUNC_DEF("try", 1, js_promise_try ),
- JS_CFUNC_DEF("race", 1, js_promise_race ),
- JS_CFUNC_DEF("withResolvers", 0, js_promise_withResolvers ),
- JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL),
- };
- static const JSCFunctionListEntry js_promise_proto_funcs[] = {
- JS_CFUNC_DEF("then", 2, js_promise_then ),
- JS_CFUNC_DEF("catch", 1, js_promise_catch ),
- JS_CFUNC_DEF("finally", 1, js_promise_finally ),
- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Promise", JS_PROP_CONFIGURABLE ),
- };
- /* AsyncFunction */
- static const JSCFunctionListEntry js_async_function_proto_funcs[] = {
- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncFunction", JS_PROP_CONFIGURABLE ),
- };
- /* AsyncIteratorPrototype */
- static const JSCFunctionListEntry js_async_iterator_proto_funcs[] = {
- JS_CFUNC_DEF("[Symbol.asyncIterator]", 0, js_iterator_proto_iterator ),
- };
- /* AsyncFromSyncIteratorPrototype */
- typedef struct JSAsyncFromSyncIteratorData {
- JSValue sync_iter;
- JSValue next_method;
- } JSAsyncFromSyncIteratorData;
- static void js_async_from_sync_iterator_finalizer(JSRuntime *rt, JSValue val)
- {
- JSAsyncFromSyncIteratorData *s =
- JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);
- if (s) {
- JS_FreeValueRT(rt, s->sync_iter);
- JS_FreeValueRT(rt, s->next_method);
- js_free_rt(rt, s);
- }
- }
- static void js_async_from_sync_iterator_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func)
- {
- JSAsyncFromSyncIteratorData *s =
- JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);
- if (s) {
- JS_MarkValue(rt, s->sync_iter, mark_func);
- JS_MarkValue(rt, s->next_method, mark_func);
- }
- }
- static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx,
- JSValueConst sync_iter)
- {
- JSValue async_iter, next_method;
- JSAsyncFromSyncIteratorData *s;
- next_method = JS_GetProperty(ctx, sync_iter, JS_ATOM_next);
- if (JS_IsException(next_method))
- return JS_EXCEPTION;
- async_iter = JS_NewObjectClass(ctx, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);
- if (JS_IsException(async_iter)) {
- JS_FreeValue(ctx, next_method);
- return async_iter;
- }
- s = js_mallocz(ctx, sizeof(*s));
- if (!s) {
- JS_FreeValue(ctx, async_iter);
- JS_FreeValue(ctx, next_method);
- return JS_EXCEPTION;
- }
- s->sync_iter = JS_DupValue(ctx, sync_iter);
- s->next_method = next_method;
- JS_SetOpaque(async_iter, s);
- return async_iter;
- }
- static JSValue js_async_from_sync_iterator_unwrap(JSContext *ctx,
- JSValueConst this_val,
- int argc, JSValueConst *argv,
- int magic, JSValue *func_data)
- {
- return js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]),
- JS_ToBool(ctx, func_data[0]));
- }
- static JSValue js_async_from_sync_iterator_unwrap_func_create(JSContext *ctx,
- BOOL done)
- {
- JSValueConst func_data[1];
- func_data[0] = (JSValueConst)JS_NewBool(ctx, done);
- return JS_NewCFunctionData(ctx, js_async_from_sync_iterator_unwrap,
- 1, 0, 1, func_data);
- }
- static JSValue js_async_from_sync_iterator_close_wrap(JSContext *ctx,
- JSValueConst this_val,
- int argc, JSValueConst *argv,
- int magic, JSValue *func_data)
- {
- JS_Throw(ctx, JS_DupValue(ctx, argv[0]));
- JS_IteratorClose(ctx, func_data[0], TRUE);
- return JS_EXCEPTION;
- }
- static JSValue js_async_from_sync_iterator_close_wrap_func_create(JSContext *ctx, JSValueConst sync_iter)
- {
- return JS_NewCFunctionData(ctx, js_async_from_sync_iterator_close_wrap,
- 1, 0, 1, &sync_iter);
- }
- static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv,
- int magic)
- {
- JSValue promise, resolving_funcs[2], value, err, method;
- JSAsyncFromSyncIteratorData *s;
- int done;
- int is_reject;
- promise = JS_NewPromiseCapability(ctx, resolving_funcs);
- if (JS_IsException(promise))
- return JS_EXCEPTION;
- s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);
- if (!s) {
- JS_ThrowTypeError(ctx, "not an Async-from-Sync Iterator");
- goto reject;
- }
- if (magic == GEN_MAGIC_NEXT) {
- method = JS_DupValue(ctx, s->next_method);
- } else {
- method = JS_GetProperty(ctx, s->sync_iter,
- magic == GEN_MAGIC_RETURN ? JS_ATOM_return :
- JS_ATOM_throw);
- if (JS_IsException(method))
- goto reject;
- if (JS_IsUndefined(method) || JS_IsNull(method)) {
- if (magic == GEN_MAGIC_RETURN) {
- err = js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]), TRUE);
- is_reject = 0;
- goto done_resolve;
- } else {
- if (JS_IteratorClose(ctx, s->sync_iter, FALSE))
- goto reject;
- JS_ThrowTypeError(ctx, "throw is not a method");
- goto reject;
- }
- }
- }
- value = JS_IteratorNext2(ctx, s->sync_iter, method,
- argc >= 1 ? 1 : 0, argv, &done);
- JS_FreeValue(ctx, method);
- if (JS_IsException(value))
- goto reject;
- if (done == 2) {
- JSValue obj = value;
- value = JS_IteratorGetCompleteValue(ctx, obj, &done);
- JS_FreeValue(ctx, obj);
- if (JS_IsException(value))
- goto reject;
- }
-
- if (JS_IsException(value))
- goto reject;
- {
- JSValue value_wrapper_promise, resolve_reject[2];
- int res;
- value_wrapper_promise = js_promise_resolve(ctx, ctx->promise_ctor,
- 1, (JSValueConst *)&value, 0);
- if (JS_IsException(value_wrapper_promise)) {
- JSValue res2;
- JS_FreeValue(ctx, value);
- if (magic != GEN_MAGIC_RETURN && !done) {
- JS_IteratorClose(ctx, s->sync_iter, TRUE);
- }
- reject:
- err = JS_GetException(ctx);
- is_reject = 1;
- done_resolve:
- res2 = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED,
- 1, (JSValueConst *)&err);
- JS_FreeValue(ctx, err);
- JS_FreeValue(ctx, res2);
- JS_FreeValue(ctx, resolving_funcs[0]);
- JS_FreeValue(ctx, resolving_funcs[1]);
- return promise;
- }
- resolve_reject[0] =
- js_async_from_sync_iterator_unwrap_func_create(ctx, done);
- if (JS_IsException(resolve_reject[0])) {
- JS_FreeValue(ctx, value_wrapper_promise);
- goto fail;
- }
- if (done || magic == GEN_MAGIC_RETURN) {
- resolve_reject[1] = JS_UNDEFINED;
- } else {
- resolve_reject[1] =
- js_async_from_sync_iterator_close_wrap_func_create(ctx, s->sync_iter);
- if (JS_IsException(resolve_reject[1])) {
- JS_FreeValue(ctx, value_wrapper_promise);
- JS_FreeValue(ctx, resolve_reject[0]);
- goto fail;
- }
- }
- JS_FreeValue(ctx, value);
- res = perform_promise_then(ctx, value_wrapper_promise,
- (JSValueConst *)resolve_reject,
- (JSValueConst *)resolving_funcs);
- JS_FreeValue(ctx, resolve_reject[0]);
- JS_FreeValue(ctx, resolve_reject[1]);
- JS_FreeValue(ctx, value_wrapper_promise);
- JS_FreeValue(ctx, resolving_funcs[0]);
- JS_FreeValue(ctx, resolving_funcs[1]);
- if (res) {
- JS_FreeValue(ctx, promise);
- return JS_EXCEPTION;
- }
- }
- return promise;
- fail:
- JS_FreeValue(ctx, value);
- JS_FreeValue(ctx, resolving_funcs[0]);
- JS_FreeValue(ctx, resolving_funcs[1]);
- JS_FreeValue(ctx, promise);
- return JS_EXCEPTION;
- }
- static const JSCFunctionListEntry js_async_from_sync_iterator_proto_funcs[] = {
- JS_CFUNC_MAGIC_DEF("next", 1, js_async_from_sync_iterator_next, GEN_MAGIC_NEXT ),
- JS_CFUNC_MAGIC_DEF("return", 1, js_async_from_sync_iterator_next, GEN_MAGIC_RETURN ),
- JS_CFUNC_MAGIC_DEF("throw", 1, js_async_from_sync_iterator_next, GEN_MAGIC_THROW ),
- };
- /* AsyncGeneratorFunction */
- static const JSCFunctionListEntry js_async_generator_function_proto_funcs[] = {
- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGeneratorFunction", JS_PROP_CONFIGURABLE ),
- };
- /* AsyncGenerator prototype */
- static const JSCFunctionListEntry js_async_generator_proto_funcs[] = {
- JS_CFUNC_MAGIC_DEF("next", 1, js_async_generator_next, GEN_MAGIC_NEXT ),
- JS_CFUNC_MAGIC_DEF("return", 1, js_async_generator_next, GEN_MAGIC_RETURN ),
- JS_CFUNC_MAGIC_DEF("throw", 1, js_async_generator_next, GEN_MAGIC_THROW ),
- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGenerator", JS_PROP_CONFIGURABLE ),
- };
- static JSClassShortDef const js_async_class_def[] = {
- { JS_ATOM_Promise, js_promise_finalizer, js_promise_mark }, /* JS_CLASS_PROMISE */
- { JS_ATOM_PromiseResolveFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_RESOLVE_FUNCTION */
- { JS_ATOM_PromiseRejectFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_REJECT_FUNCTION */
- { JS_ATOM_AsyncFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_ASYNC_FUNCTION */
- { JS_ATOM_AsyncFunctionResolve, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_RESOLVE */
- { JS_ATOM_AsyncFunctionReject, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_REJECT */
- { JS_ATOM_empty_string, js_async_from_sync_iterator_finalizer, js_async_from_sync_iterator_mark }, /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */
- { JS_ATOM_AsyncGeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_ASYNC_GENERATOR_FUNCTION */
- { JS_ATOM_AsyncGenerator, js_async_generator_finalizer, js_async_generator_mark }, /* JS_CLASS_ASYNC_GENERATOR */
- };
- int JS_AddIntrinsicPromise(JSContext *ctx)
- {
- JSRuntime *rt = ctx->rt;
- JSValue obj1;
- JSCFunctionType ft;
- if (!JS_IsRegisteredClass(rt, JS_CLASS_PROMISE)) {
- if (init_class_range(rt, js_async_class_def, JS_CLASS_PROMISE,
- countof(js_async_class_def)))
- return -1;
- rt->class_array[JS_CLASS_PROMISE_RESOLVE_FUNCTION].call = js_promise_resolve_function_call;
- rt->class_array[JS_CLASS_PROMISE_REJECT_FUNCTION].call = js_promise_resolve_function_call;
- rt->class_array[JS_CLASS_ASYNC_FUNCTION].call = js_async_function_call;
- rt->class_array[JS_CLASS_ASYNC_FUNCTION_RESOLVE].call = js_async_function_resolve_call;
- rt->class_array[JS_CLASS_ASYNC_FUNCTION_REJECT].call = js_async_function_resolve_call;
- rt->class_array[JS_CLASS_ASYNC_GENERATOR_FUNCTION].call = js_async_generator_function_call;
- }
- /* Promise */
- obj1 = JS_NewCConstructor(ctx, JS_CLASS_PROMISE, "Promise",
- js_promise_constructor, 1, JS_CFUNC_constructor, 0,
- JS_UNDEFINED,
- js_promise_funcs, countof(js_promise_funcs),
- js_promise_proto_funcs, countof(js_promise_proto_funcs),
- 0);
- if (JS_IsException(obj1))
- return -1;
- ctx->promise_ctor = obj1;
-
- /* AsyncFunction */
- ft.generic_magic = js_function_constructor;
- obj1 = JS_NewCConstructor(ctx, JS_CLASS_ASYNC_FUNCTION, "AsyncFunction",
- ft.generic, 1, JS_CFUNC_constructor_or_func_magic, JS_FUNC_ASYNC,
- ctx->function_ctor,
- NULL, 0,
- js_async_function_proto_funcs, countof(js_async_function_proto_funcs),
- JS_NEW_CTOR_NO_GLOBAL | JS_NEW_CTOR_READONLY);
- if (JS_IsException(obj1))
- return -1;
- JS_FreeValue(ctx, obj1);
-
- /* AsyncIteratorPrototype */
- ctx->async_iterator_proto =
- JS_NewObjectProtoList(ctx, ctx->class_proto[JS_CLASS_OBJECT],
- js_async_iterator_proto_funcs,
- countof(js_async_iterator_proto_funcs));
- if (JS_IsException(ctx->async_iterator_proto))
- return -1;
- /* AsyncFromSyncIteratorPrototype */
- ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR] =
- JS_NewObjectProtoList(ctx, ctx->async_iterator_proto,
- js_async_from_sync_iterator_proto_funcs,
- countof(js_async_from_sync_iterator_proto_funcs));
- if (JS_IsException(ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR]))
- return -1;
-
- /* AsyncGeneratorPrototype */
- ctx->class_proto[JS_CLASS_ASYNC_GENERATOR] =
- JS_NewObjectProtoList(ctx, ctx->async_iterator_proto,
- js_async_generator_proto_funcs,
- countof(js_async_generator_proto_funcs));
- if (JS_IsException(ctx->class_proto[JS_CLASS_ASYNC_GENERATOR]))
- return -1;
- /* AsyncGeneratorFunction */
- ft.generic_magic = js_function_constructor;
- obj1 = JS_NewCConstructor(ctx, JS_CLASS_ASYNC_GENERATOR_FUNCTION, "AsyncGeneratorFunction",
- ft.generic, 1, JS_CFUNC_constructor_or_func_magic, JS_FUNC_ASYNC_GENERATOR,
- ctx->function_ctor,
- NULL, 0,
- js_async_generator_function_proto_funcs, countof(js_async_generator_function_proto_funcs),
- JS_NEW_CTOR_NO_GLOBAL | JS_NEW_CTOR_READONLY);
- if (JS_IsException(obj1))
- return -1;
- JS_FreeValue(ctx, obj1);
- return JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],
- ctx->class_proto[JS_CLASS_ASYNC_GENERATOR],
- JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE);
- }
- /* URI handling */
- static int string_get_hex(JSString *p, int k, int n) {
- int c = 0, h;
- while (n-- > 0) {
- if ((h = from_hex(string_get(p, k++))) < 0)
- return -1;
- c = (c << 4) | h;
- }
- return c;
- }
- static int isURIReserved(int c) {
- return c < 0x100 && memchr(";/?:@&=+$,#", c, sizeof(";/?:@&=+$,#") - 1) != NULL;
- }
- static int __attribute__((format(printf, 2, 3))) js_throw_URIError(JSContext *ctx, const char *fmt, ...)
- {
- va_list ap;
- va_start(ap, fmt);
- JS_ThrowError(ctx, JS_URI_ERROR, fmt, ap);
- va_end(ap);
- return -1;
- }
- static int hex_decode(JSContext *ctx, JSString *p, int k) {
- int c;
- if (k >= p->len || string_get(p, k) != '%')
- return js_throw_URIError(ctx, "expecting %%");
- if (k + 2 >= p->len || (c = string_get_hex(p, k + 1, 2)) < 0)
- return js_throw_URIError(ctx, "expecting hex digit");
- return c;
- }
- static JSValue js_global_decodeURI(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int isComponent)
- {
- JSValue str;
- StringBuffer b_s, *b = &b_s;
- JSString *p;
- int k, c, c1, n, c_min;
- str = JS_ToString(ctx, argv[0]);
- if (JS_IsException(str))
- return str;
- string_buffer_init(ctx, b, 0);
- p = JS_VALUE_GET_STRING(str);
- for (k = 0; k < p->len;) {
- c = string_get(p, k);
- if (c == '%') {
- c = hex_decode(ctx, p, k);
- if (c < 0)
- goto fail;
- k += 3;
- if (c < 0x80) {
- if (!isComponent && isURIReserved(c)) {
- c = '%';
- k -= 2;
- }
- } else {
- /* Decode URI-encoded UTF-8 sequence */
- if (c >= 0xc0 && c <= 0xdf) {
- n = 1;
- c_min = 0x80;
- c &= 0x1f;
- } else if (c >= 0xe0 && c <= 0xef) {
- n = 2;
- c_min = 0x800;
- c &= 0xf;
- } else if (c >= 0xf0 && c <= 0xf7) {
- n = 3;
- c_min = 0x10000;
- c &= 0x7;
- } else {
- n = 0;
- c_min = 1;
- c = 0;
- }
- while (n-- > 0) {
- c1 = hex_decode(ctx, p, k);
- if (c1 < 0)
- goto fail;
- k += 3;
- if ((c1 & 0xc0) != 0x80) {
- c = 0;
- break;
- }
- c = (c << 6) | (c1 & 0x3f);
- }
- if (c < c_min || c > 0x10FFFF || is_surrogate(c)) {
- js_throw_URIError(ctx, "malformed UTF-8");
- goto fail;
- }
- }
- } else {
- k++;
- }
- string_buffer_putc(b, c);
- }
- JS_FreeValue(ctx, str);
- return string_buffer_end(b);
- fail:
- JS_FreeValue(ctx, str);
- string_buffer_free(b);
- return JS_EXCEPTION;
- }
- static int isUnescaped(int c) {
- static char const unescaped_chars[] =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "abcdefghijklmnopqrstuvwxyz"
- "0123456789"
- "@*_+-./";
- return c < 0x100 &&
- memchr(unescaped_chars, c, sizeof(unescaped_chars) - 1);
- }
- static int isURIUnescaped(int c, int isComponent) {
- return c < 0x100 &&
- ((c >= 0x61 && c <= 0x7a) ||
- (c >= 0x41 && c <= 0x5a) ||
- (c >= 0x30 && c <= 0x39) ||
- memchr("-_.!~*'()", c, sizeof("-_.!~*'()") - 1) != NULL ||
- (!isComponent && isURIReserved(c)));
- }
- static int encodeURI_hex(StringBuffer *b, int c) {
- uint8_t buf[6];
- int n = 0;
- const char *hex = "0123456789ABCDEF";
- buf[n++] = '%';
- if (c >= 256) {
- buf[n++] = 'u';
- buf[n++] = hex[(c >> 12) & 15];
- buf[n++] = hex[(c >> 8) & 15];
- }
- buf[n++] = hex[(c >> 4) & 15];
- buf[n++] = hex[(c >> 0) & 15];
- return string_buffer_write8(b, buf, n);
- }
- static JSValue js_global_encodeURI(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv,
- int isComponent)
- {
- JSValue str;
- StringBuffer b_s, *b = &b_s;
- JSString *p;
- int k, c, c1;
- str = JS_ToString(ctx, argv[0]);
- if (JS_IsException(str))
- return str;
- p = JS_VALUE_GET_STRING(str);
- string_buffer_init(ctx, b, p->len);
- for (k = 0; k < p->len;) {
- c = string_get(p, k);
- k++;
- if (isURIUnescaped(c, isComponent)) {
- string_buffer_putc16(b, c);
- } else {
- if (is_lo_surrogate(c)) {
- js_throw_URIError(ctx, "invalid character");
- goto fail;
- } else if (is_hi_surrogate(c)) {
- if (k >= p->len) {
- js_throw_URIError(ctx, "expecting surrogate pair");
- goto fail;
- }
- c1 = string_get(p, k);
- k++;
- if (!is_lo_surrogate(c1)) {
- js_throw_URIError(ctx, "expecting surrogate pair");
- goto fail;
- }
- c = from_surrogate(c, c1);
- }
- if (c < 0x80) {
- encodeURI_hex(b, c);
- } else {
- /* XXX: use C UTF-8 conversion ? */
- if (c < 0x800) {
- encodeURI_hex(b, (c >> 6) | 0xc0);
- } else {
- if (c < 0x10000) {
- encodeURI_hex(b, (c >> 12) | 0xe0);
- } else {
- encodeURI_hex(b, (c >> 18) | 0xf0);
- encodeURI_hex(b, ((c >> 12) & 0x3f) | 0x80);
- }
- encodeURI_hex(b, ((c >> 6) & 0x3f) | 0x80);
- }
- encodeURI_hex(b, (c & 0x3f) | 0x80);
- }
- }
- }
- JS_FreeValue(ctx, str);
- return string_buffer_end(b);
- fail:
- JS_FreeValue(ctx, str);
- string_buffer_free(b);
- return JS_EXCEPTION;
- }
- static JSValue js_global_escape(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue str;
- StringBuffer b_s, *b = &b_s;
- JSString *p;
- int i, len, c;
- str = JS_ToString(ctx, argv[0]);
- if (JS_IsException(str))
- return str;
- p = JS_VALUE_GET_STRING(str);
- string_buffer_init(ctx, b, p->len);
- for (i = 0, len = p->len; i < len; i++) {
- c = string_get(p, i);
- if (isUnescaped(c)) {
- string_buffer_putc16(b, c);
- } else {
- encodeURI_hex(b, c);
- }
- }
- JS_FreeValue(ctx, str);
- return string_buffer_end(b);
- }
- static JSValue js_global_unescape(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue str;
- StringBuffer b_s, *b = &b_s;
- JSString *p;
- int i, len, c, n;
- str = JS_ToString(ctx, argv[0]);
- if (JS_IsException(str))
- return str;
- string_buffer_init(ctx, b, 0);
- p = JS_VALUE_GET_STRING(str);
- for (i = 0, len = p->len; i < len; i++) {
- c = string_get(p, i);
- if (c == '%') {
- if (i + 6 <= len
- && string_get(p, i + 1) == 'u'
- && (n = string_get_hex(p, i + 2, 4)) >= 0) {
- c = n;
- i += 6 - 1;
- } else
- if (i + 3 <= len
- && (n = string_get_hex(p, i + 1, 2)) >= 0) {
- c = n;
- i += 3 - 1;
- }
- }
- string_buffer_putc16(b, c);
- }
- JS_FreeValue(ctx, str);
- return string_buffer_end(b);
- }
- /* global object */
- static const JSCFunctionListEntry js_global_funcs[] = {
- JS_CFUNC_DEF("parseInt", 2, js_parseInt ),
- JS_CFUNC_DEF("parseFloat", 1, js_parseFloat ),
- JS_CFUNC_DEF("isNaN", 1, js_global_isNaN ),
- JS_CFUNC_DEF("isFinite", 1, js_global_isFinite ),
- JS_CFUNC_MAGIC_DEF("decodeURI", 1, js_global_decodeURI, 0 ),
- JS_CFUNC_MAGIC_DEF("decodeURIComponent", 1, js_global_decodeURI, 1 ),
- JS_CFUNC_MAGIC_DEF("encodeURI", 1, js_global_encodeURI, 0 ),
- JS_CFUNC_MAGIC_DEF("encodeURIComponent", 1, js_global_encodeURI, 1 ),
- JS_CFUNC_DEF("escape", 1, js_global_escape ),
- JS_CFUNC_DEF("unescape", 1, js_global_unescape ),
- JS_PROP_DOUBLE_DEF("Infinity", 1.0 / 0.0, 0 ),
- JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ),
- JS_PROP_UNDEFINED_DEF("undefined", 0 ),
- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "global", JS_PROP_CONFIGURABLE ),
- JS_CFUNC_DEF("eval", 1, js_global_eval ),
- };
- /* Date */
- static int64_t math_mod(int64_t a, int64_t b) {
- /* return positive modulo */
- int64_t m = a % b;
- return m + (m < 0) * b;
- }
- static int64_t floor_div(int64_t a, int64_t b) {
- /* integer division rounding toward -Infinity */
- int64_t m = a % b;
- return (a - (m + (m < 0) * b)) / b;
- }
- static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv);
- static __exception int JS_ThisTimeValue(JSContext *ctx, double *valp, JSValueConst this_val)
- {
- if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
- JSObject *p = JS_VALUE_GET_OBJ(this_val);
- if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data))
- return JS_ToFloat64(ctx, valp, p->u.object_data);
- }
- JS_ThrowTypeError(ctx, "not a Date object");
- return -1;
- }
- static JSValue JS_SetThisTimeValue(JSContext *ctx, JSValueConst this_val, double v)
- {
- if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
- JSObject *p = JS_VALUE_GET_OBJ(this_val);
- if (p->class_id == JS_CLASS_DATE) {
- JS_FreeValue(ctx, p->u.object_data);
- p->u.object_data = JS_NewFloat64(ctx, v);
- return JS_DupValue(ctx, p->u.object_data);
- }
- }
- return JS_ThrowTypeError(ctx, "not a Date object");
- }
- static int64_t days_from_year(int64_t y) {
- return 365 * (y - 1970) + floor_div(y - 1969, 4) -
- floor_div(y - 1901, 100) + floor_div(y - 1601, 400);
- }
- static int64_t days_in_year(int64_t y) {
- return 365 + !(y % 4) - !(y % 100) + !(y % 400);
- }
- /* return the year, update days */
- static int64_t year_from_days(int64_t *days) {
- int64_t y, d1, nd, d = *days;
- y = floor_div(d * 10000, 3652425) + 1970;
- /* the initial approximation is very good, so only a few
- iterations are necessary */
- for(;;) {
- d1 = d - days_from_year(y);
- if (d1 < 0) {
- y--;
- d1 += days_in_year(y);
- } else {
- nd = days_in_year(y);
- if (d1 < nd)
- break;
- d1 -= nd;
- y++;
- }
- }
- *days = d1;
- return y;
- }
- static int const month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
- static char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
- static char const day_names[] = "SunMonTueWedThuFriSat";
- static __exception int get_date_fields(JSContext *ctx, JSValueConst obj,
- double fields[minimum_length(9)], int is_local, int force)
- {
- double dval;
- int64_t d, days, wd, y, i, md, h, m, s, ms, tz = 0;
- if (JS_ThisTimeValue(ctx, &dval, obj))
- return -1;
- if (isnan(dval)) {
- if (!force)
- return FALSE; /* NaN */
- d = 0; /* initialize all fields to 0 */
- } else {
- d = dval; /* assuming -8.64e15 <= dval <= -8.64e15 */
- if (is_local) {
- tz = -getTimezoneOffset(d);
- d += tz * 60000;
- }
- }
- /* result is >= 0, we can use % */
- h = math_mod(d, 86400000);
- days = (d - h) / 86400000;
- ms = h % 1000;
- h = (h - ms) / 1000;
- s = h % 60;
- h = (h - s) / 60;
- m = h % 60;
- h = (h - m) / 60;
- wd = math_mod(days + 4, 7); /* week day */
- y = year_from_days(&days);
- for(i = 0; i < 11; i++) {
- md = month_days[i];
- if (i == 1)
- md += days_in_year(y) - 365;
- if (days < md)
- break;
- days -= md;
- }
- fields[0] = y;
- fields[1] = i;
- fields[2] = days + 1;
- fields[3] = h;
- fields[4] = m;
- fields[5] = s;
- fields[6] = ms;
- fields[7] = wd;
- fields[8] = tz;
- return TRUE;
- }
- static double time_clip(double t) {
- if (t >= -8.64e15 && t <= 8.64e15)
- return trunc(t) + 0.0; /* convert -0 to +0 */
- else
- return NAN;
- }
- /* The spec mandates the use of 'double' and it specifies the order
- of the operations */
- static double set_date_fields(double fields[minimum_length(7)], int is_local) {
- double y, m, dt, ym, mn, day, h, s, milli, time, tv;
- int yi, mi, i;
- int64_t days;
- volatile double temp; /* enforce evaluation order */
- /* emulate 21.4.1.15 MakeDay ( year, month, date ) */
- y = fields[0];
- m = fields[1];
- dt = fields[2];
- ym = y + floor(m / 12);
- mn = fmod(m, 12);
- if (mn < 0)
- mn += 12;
- if (ym < -271821 || ym > 275760)
- return NAN;
- yi = ym;
- mi = mn;
- days = days_from_year(yi);
- for(i = 0; i < mi; i++) {
- days += month_days[i];
- if (i == 1)
- days += days_in_year(yi) - 365;
- }
- day = days + dt - 1;
- /* emulate 21.4.1.14 MakeTime ( hour, min, sec, ms ) */
- h = fields[3];
- m = fields[4];
- s = fields[5];
- milli = fields[6];
- /* Use a volatile intermediary variable to ensure order of evaluation
- * as specified in ECMA. This fixes a test262 error on
- * test262/test/built-ins/Date/UTC/fp-evaluation-order.js.
- * Without the volatile qualifier, the compile can generate code
- * that performs the computation in a different order or with instructions
- * that produce a different result such as FMA (float multiply and add).
- */
- time = h * 3600000;
- time += (temp = m * 60000);
- time += (temp = s * 1000);
- time += milli;
- /* emulate 21.4.1.16 MakeDate ( day, time ) */
- tv = (temp = day * 86400000) + time; /* prevent generation of FMA */
- if (!isfinite(tv))
- return NAN;
- /* adjust for local time and clip */
- if (is_local) {
- int64_t ti = tv < INT64_MIN ? INT64_MIN : tv >= 0x1p63 ? INT64_MAX : (int64_t)tv;
- tv += getTimezoneOffset(ti) * 60000;
- }
- return time_clip(tv);
- }
- static double set_date_fields_checked(double fields[minimum_length(7)], int is_local)
- {
- int i;
- double a;
- for(i = 0; i < 7; i++) {
- a = fields[i];
- if (!isfinite(a))
- return NAN;
- fields[i] = trunc(a);
- if (i == 0 && fields[0] >= 0 && fields[0] < 100)
- fields[0] += 1900;
- }
- return set_date_fields(fields, is_local);
- }
- static JSValue get_date_field(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
- {
- // get_date_field(obj, n, is_local)
- double fields[9];
- int res, n, is_local;
- is_local = magic & 0x0F;
- n = (magic >> 4) & 0x0F;
- res = get_date_fields(ctx, this_val, fields, is_local, 0);
- if (res < 0)
- return JS_EXCEPTION;
- if (!res)
- return JS_NAN;
- if (magic & 0x100) { // getYear
- fields[0] -= 1900;
- }
- return JS_NewFloat64(ctx, fields[n]);
- }
- static JSValue set_date_field(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
- {
- // _field(obj, first_field, end_field, args, is_local)
- double fields[9];
- int res, first_field, end_field, is_local, i, n, res1;
- double d, a;
- d = NAN;
- first_field = (magic >> 8) & 0x0F;
- end_field = (magic >> 4) & 0x0F;
- is_local = magic & 0x0F;
- res = get_date_fields(ctx, this_val, fields, is_local, first_field == 0);
- if (res < 0)
- return JS_EXCEPTION;
- res1 = res;
-
- // Argument coercion is observable and must be done unconditionally.
- n = min_int(argc, end_field - first_field);
- for(i = 0; i < n; i++) {
- if (JS_ToFloat64(ctx, &a, argv[i]))
- return JS_EXCEPTION;
- if (!isfinite(a))
- res = FALSE;
- fields[first_field + i] = trunc(a);
- }
- if (!res1)
- return JS_NAN; /* thisTimeValue is NaN */
- if (res && argc > 0)
- d = set_date_fields(fields, is_local);
- return JS_SetThisTimeValue(ctx, this_val, d);
- }
- /* fmt:
- 0: toUTCString: "Tue, 02 Jan 2018 23:04:46 GMT"
- 1: toString: "Wed Jan 03 2018 00:05:22 GMT+0100 (CET)"
- 2: toISOString: "2018-01-02T23:02:56.927Z"
- 3: toLocaleString: "1/2/2018, 11:40:40 PM"
- part: 1=date, 2=time 3=all
- XXX: should use a variant of strftime().
- */
- static JSValue get_date_string(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
- {
- // _string(obj, fmt, part)
- char buf[64];
- double fields[9];
- int res, fmt, part, pos;
- int y, mon, d, h, m, s, ms, wd, tz;
- fmt = (magic >> 4) & 0x0F;
- part = magic & 0x0F;
- res = get_date_fields(ctx, this_val, fields, fmt & 1, 0);
- if (res < 0)
- return JS_EXCEPTION;
- if (!res) {
- if (fmt == 2)
- return JS_ThrowRangeError(ctx, "Date value is NaN");
- else
- return js_new_string8(ctx, "Invalid Date");
- }
- y = fields[0];
- mon = fields[1];
- d = fields[2];
- h = fields[3];
- m = fields[4];
- s = fields[5];
- ms = fields[6];
- wd = fields[7];
- tz = fields[8];
- pos = 0;
- if (part & 1) { /* date part */
- switch(fmt) {
- case 0:
- pos += snprintf(buf + pos, sizeof(buf) - pos,
- "%.3s, %02d %.3s %0*d ",
- day_names + wd * 3, d,
- month_names + mon * 3, 4 + (y < 0), y);
- break;
- case 1:
- pos += snprintf(buf + pos, sizeof(buf) - pos,
- "%.3s %.3s %02d %0*d",
- day_names + wd * 3,
- month_names + mon * 3, d, 4 + (y < 0), y);
- if (part == 3) {
- buf[pos++] = ' ';
- }
- break;
- case 2:
- if (y >= 0 && y <= 9999) {
- pos += snprintf(buf + pos, sizeof(buf) - pos,
- "%04d", y);
- } else {
- pos += snprintf(buf + pos, sizeof(buf) - pos,
- "%+07d", y);
- }
- pos += snprintf(buf + pos, sizeof(buf) - pos,
- "-%02d-%02dT", mon + 1, d);
- break;
- case 3:
- pos += snprintf(buf + pos, sizeof(buf) - pos,
- "%02d/%02d/%0*d", mon + 1, d, 4 + (y < 0), y);
- if (part == 3) {
- buf[pos++] = ',';
- buf[pos++] = ' ';
- }
- break;
- }
- }
- if (part & 2) { /* time part */
- switch(fmt) {
- case 0:
- pos += snprintf(buf + pos, sizeof(buf) - pos,
- "%02d:%02d:%02d GMT", h, m, s);
- break;
- case 1:
- pos += snprintf(buf + pos, sizeof(buf) - pos,
- "%02d:%02d:%02d GMT", h, m, s);
- if (tz < 0) {
- buf[pos++] = '-';
- tz = -tz;
- } else {
- buf[pos++] = '+';
- }
- /* tz is >= 0, can use % */
- pos += snprintf(buf + pos, sizeof(buf) - pos,
- "%02d%02d", tz / 60, tz % 60);
- /* XXX: tack the time zone code? */
- break;
- case 2:
- pos += snprintf(buf + pos, sizeof(buf) - pos,
- "%02d:%02d:%02d.%03dZ", h, m, s, ms);
- break;
- case 3:
- pos += snprintf(buf + pos, sizeof(buf) - pos,
- "%02d:%02d:%02d %cM", (h + 11) % 12 + 1, m, s,
- (h < 12) ? 'A' : 'P');
- break;
- }
- }
- return JS_NewStringLen(ctx, buf, pos);
- }
- /* OS dependent: return the UTC time in ms since 1970. */
- static int64_t date_now(void) {
- struct timeval tv;
- gettimeofday(&tv, NULL);
- return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
- }
- static JSValue js_date_constructor(JSContext *ctx, JSValueConst new_target,
- int argc, JSValueConst *argv)
- {
- // Date(y, mon, d, h, m, s, ms)
- JSValue rv;
- int i, n;
- double val;
- if (JS_IsUndefined(new_target)) {
- /* invoked as function */
- argc = 0;
- }
- n = argc;
- if (n == 0) {
- val = date_now();
- } else if (n == 1) {
- JSValue v, dv;
- if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) {
- JSObject *p = JS_VALUE_GET_OBJ(argv[0]);
- if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data)) {
- if (JS_ToFloat64(ctx, &val, p->u.object_data))
- return JS_EXCEPTION;
- val = time_clip(val);
- goto has_val;
- }
- }
- v = JS_ToPrimitive(ctx, argv[0], HINT_NONE);
- if (JS_IsString(v)) {
- dv = js_Date_parse(ctx, JS_UNDEFINED, 1, (JSValueConst *)&v);
- JS_FreeValue(ctx, v);
- if (JS_IsException(dv))
- return JS_EXCEPTION;
- if (JS_ToFloat64Free(ctx, &val, dv))
- return JS_EXCEPTION;
- } else {
- if (JS_ToFloat64Free(ctx, &val, v))
- return JS_EXCEPTION;
- }
- val = time_clip(val);
- } else {
- double fields[] = { 0, 0, 1, 0, 0, 0, 0 };
- if (n > 7)
- n = 7;
- for(i = 0; i < n; i++) {
- if (JS_ToFloat64(ctx, &fields[i], argv[i]))
- return JS_EXCEPTION;
- }
- val = set_date_fields_checked(fields, 1);
- }
- has_val:
- #if 0
- JSValueConst args[3];
- args[0] = new_target;
- args[1] = ctx->class_proto[JS_CLASS_DATE];
- args[2] = JS_NewFloat64(ctx, val);
- rv = js___date_create(ctx, JS_UNDEFINED, 3, args);
- #else
- rv = js_create_from_ctor(ctx, new_target, JS_CLASS_DATE);
- if (!JS_IsException(rv))
- JS_SetObjectData(ctx, rv, JS_NewFloat64(ctx, val));
- #endif
- if (!JS_IsException(rv) && JS_IsUndefined(new_target)) {
- /* invoked as a function, return (new Date()).toString(); */
- JSValue s;
- s = get_date_string(ctx, rv, 0, NULL, 0x13);
- JS_FreeValue(ctx, rv);
- rv = s;
- }
- return rv;
- }
- static JSValue js_Date_UTC(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- // UTC(y, mon, d, h, m, s, ms)
- double fields[] = { 0, 0, 1, 0, 0, 0, 0 };
- int i, n;
- n = argc;
- if (n == 0)
- return JS_NAN;
- if (n > 7)
- n = 7;
- for(i = 0; i < n; i++) {
- if (JS_ToFloat64(ctx, &fields[i], argv[i]))
- return JS_EXCEPTION;
- }
- return JS_NewFloat64(ctx, set_date_fields_checked(fields, 0));
- }
- /* Date string parsing */
- static BOOL string_skip_char(const uint8_t *sp, int *pp, int c) {
- if (sp[*pp] == c) {
- *pp += 1;
- return TRUE;
- } else {
- return FALSE;
- }
- }
- /* skip spaces, update offset, return next char */
- static int string_skip_spaces(const uint8_t *sp, int *pp) {
- int c;
- while ((c = sp[*pp]) == ' ')
- *pp += 1;
- return c;
- }
- /* skip dashes dots and commas */
- static int string_skip_separators(const uint8_t *sp, int *pp) {
- int c;
- while ((c = sp[*pp]) == '-' || c == '/' || c == '.' || c == ',')
- *pp += 1;
- return c;
- }
- /* skip a word, stop on spaces, digits and separators, update offset */
- static int string_skip_until(const uint8_t *sp, int *pp, const char *stoplist) {
- int c;
- while (!strchr(stoplist, c = sp[*pp]))
- *pp += 1;
- return c;
- }
- /* parse a numeric field (max_digits = 0 -> no maximum) */
- static BOOL string_get_digits(const uint8_t *sp, int *pp, int *pval,
- int min_digits, int max_digits)
- {
- int v = 0;
- int c, p = *pp, p_start;
- p_start = p;
- while ((c = sp[p]) >= '0' && c <= '9') {
- /* arbitrary limit to 9 digits */
- if (v >= 100000000)
- return FALSE;
- v = v * 10 + c - '0';
- p++;
- if (p - p_start == max_digits)
- break;
- }
- if (p - p_start < min_digits)
- return FALSE;
- *pval = v;
- *pp = p;
- return TRUE;
- }
- static BOOL string_get_milliseconds(const uint8_t *sp, int *pp, int *pval) {
- /* parse optional fractional part as milliseconds and truncate. */
- /* spec does not indicate which rounding should be used */
- int mul = 100, ms = 0, c, p_start, p = *pp;
- c = sp[p];
- if (c == '.' || c == ',') {
- p++;
- p_start = p;
- while ((c = sp[p]) >= '0' && c <= '9') {
- ms += (c - '0') * mul;
- mul /= 10;
- p++;
- if (p - p_start == 9)
- break;
- }
- if (p > p_start) {
- /* only consume the separator if digits are present */
- *pval = ms;
- *pp = p;
- }
- }
- return TRUE;
- }
- static uint8_t upper_ascii(uint8_t c) {
- return c >= 'a' && c <= 'z' ? c - 'a' + 'A' : c;
- }
- static BOOL string_get_tzoffset(const uint8_t *sp, int *pp, int *tzp, BOOL strict) {
- int tz = 0, sgn, hh, mm, p = *pp;
- sgn = sp[p++];
- if (sgn == '+' || sgn == '-') {
- int n = p;
- if (!string_get_digits(sp, &p, &hh, 1, 0))
- return FALSE;
- n = p - n;
- if (strict && n != 2 && n != 4)
- return FALSE;
- while (n > 4) {
- n -= 2;
- hh /= 100;
- }
- if (n > 2) {
- mm = hh % 100;
- hh = hh / 100;
- } else {
- mm = 0;
- if (string_skip_char(sp, &p, ':')) {
- /* optional separator */
- if (!string_get_digits(sp, &p, &mm, 2, 2))
- return FALSE;
- } else {
- if (strict)
- return FALSE; /* [+-]HH is not accepted in strict mode */
- }
- }
- if (hh > 23 || mm > 59)
- return FALSE;
- tz = hh * 60 + mm;
- if (sgn != '+')
- tz = -tz;
- } else
- if (sgn != 'Z') {
- return FALSE;
- }
- *pp = p;
- *tzp = tz;
- return TRUE;
- }
- static BOOL string_match(const uint8_t *sp, int *pp, const char *s) {
- int p = *pp;
- while (*s != '\0') {
- if (upper_ascii(sp[p]) != upper_ascii(*s++))
- return FALSE;
- p++;
- }
- *pp = p;
- return TRUE;
- }
- static int find_abbrev(const uint8_t *sp, int p, const char *list, int count) {
- int n, i;
- for (n = 0; n < count; n++) {
- for (i = 0;; i++) {
- if (upper_ascii(sp[p + i]) != upper_ascii(list[n * 3 + i]))
- break;
- if (i == 2)
- return n;
- }
- }
- return -1;
- }
- static BOOL string_get_month(const uint8_t *sp, int *pp, int *pval) {
- int n;
- n = find_abbrev(sp, *pp, month_names, 12);
- if (n < 0)
- return FALSE;
- *pval = n + 1;
- *pp += 3;
- return TRUE;
- }
- /* parse toISOString format */
- static BOOL js_date_parse_isostring(const uint8_t *sp, int fields[9], BOOL *is_local) {
- int sgn, i, p = 0;
- /* initialize fields to the beginning of the Epoch */
- for (i = 0; i < 9; i++) {
- fields[i] = (i == 2);
- }
- *is_local = FALSE;
- /* year is either yyyy digits or [+-]yyyyyy */
- sgn = sp[p];
- if (sgn == '-' || sgn == '+') {
- p++;
- if (!string_get_digits(sp, &p, &fields[0], 6, 6))
- return FALSE;
- if (sgn == '-') {
- if (fields[0] == 0)
- return FALSE; // reject -000000
- fields[0] = -fields[0];
- }
- } else {
- if (!string_get_digits(sp, &p, &fields[0], 4, 4))
- return FALSE;
- }
- if (string_skip_char(sp, &p, '-')) {
- if (!string_get_digits(sp, &p, &fields[1], 2, 2)) /* month */
- return FALSE;
- if (fields[1] < 1)
- return FALSE;
- fields[1] -= 1;
- if (string_skip_char(sp, &p, '-')) {
- if (!string_get_digits(sp, &p, &fields[2], 2, 2)) /* day */
- return FALSE;
- if (fields[2] < 1)
- return FALSE;
- }
- }
- if (string_skip_char(sp, &p, 'T')) {
- *is_local = TRUE;
- if (!string_get_digits(sp, &p, &fields[3], 2, 2) /* hour */
- || !string_skip_char(sp, &p, ':')
- || !string_get_digits(sp, &p, &fields[4], 2, 2)) { /* minute */
- fields[3] = 100; // reject unconditionally
- return TRUE;
- }
- if (string_skip_char(sp, &p, ':')) {
- if (!string_get_digits(sp, &p, &fields[5], 2, 2)) /* second */
- return FALSE;
- string_get_milliseconds(sp, &p, &fields[6]);
- }
- }
- /* parse the time zone offset if present: [+-]HH:mm or [+-]HHmm */
- if (sp[p]) {
- *is_local = FALSE;
- if (!string_get_tzoffset(sp, &p, &fields[8], TRUE))
- return FALSE;
- }
- /* error if extraneous characters */
- return sp[p] == '\0';
- }
- static struct {
- char name[6];
- int16_t offset;
- } const js_tzabbr[] = {
- { "GMT", 0 }, // Greenwich Mean Time
- { "UTC", 0 }, // Coordinated Universal Time
- { "UT", 0 }, // Universal Time
- { "Z", 0 }, // Zulu Time
- { "EDT", -4 * 60 }, // Eastern Daylight Time
- { "EST", -5 * 60 }, // Eastern Standard Time
- { "CDT", -5 * 60 }, // Central Daylight Time
- { "CST", -6 * 60 }, // Central Standard Time
- { "MDT", -6 * 60 }, // Mountain Daylight Time
- { "MST", -7 * 60 }, // Mountain Standard Time
- { "PDT", -7 * 60 }, // Pacific Daylight Time
- { "PST", -8 * 60 }, // Pacific Standard Time
- { "WET", +0 * 60 }, // Western European Time
- { "WEST", +1 * 60 }, // Western European Summer Time
- { "CET", +1 * 60 }, // Central European Time
- { "CEST", +2 * 60 }, // Central European Summer Time
- { "EET", +2 * 60 }, // Eastern European Time
- { "EEST", +3 * 60 }, // Eastern European Summer Time
- };
- static BOOL string_get_tzabbr(const uint8_t *sp, int *pp, int *offset) {
- for (size_t i = 0; i < countof(js_tzabbr); i++) {
- if (string_match(sp, pp, js_tzabbr[i].name)) {
- *offset = js_tzabbr[i].offset;
- return TRUE;
- }
- }
- return FALSE;
- }
- /* parse toString, toUTCString and other formats */
- static BOOL js_date_parse_otherstring(const uint8_t *sp,
- int fields[minimum_length(9)],
- BOOL *is_local) {
- int c, i, val, p = 0, p_start;
- int num[3];
- BOOL has_year = FALSE;
- BOOL has_mon = FALSE;
- BOOL has_time = FALSE;
- int num_index = 0;
- /* initialize fields to the beginning of 2001-01-01 */
- fields[0] = 2001;
- fields[1] = 1;
- fields[2] = 1;
- for (i = 3; i < 9; i++) {
- fields[i] = 0;
- }
- *is_local = TRUE;
- while (string_skip_spaces(sp, &p)) {
- p_start = p;
- if ((c = sp[p]) == '+' || c == '-') {
- if (has_time && string_get_tzoffset(sp, &p, &fields[8], FALSE)) {
- *is_local = FALSE;
- } else {
- p++;
- if (string_get_digits(sp, &p, &val, 1, 0)) {
- if (c == '-') {
- if (val == 0)
- return FALSE;
- val = -val;
- }
- fields[0] = val;
- has_year = TRUE;
- }
- }
- } else
- if (string_get_digits(sp, &p, &val, 1, 0)) {
- if (string_skip_char(sp, &p, ':')) {
- /* time part */
- fields[3] = val;
- if (!string_get_digits(sp, &p, &fields[4], 1, 2))
- return FALSE;
- if (string_skip_char(sp, &p, ':')) {
- if (!string_get_digits(sp, &p, &fields[5], 1, 2))
- return FALSE;
- string_get_milliseconds(sp, &p, &fields[6]);
- }
- has_time = TRUE;
- if ((sp[p] == '+' || sp[p] == '-') &&
- string_get_tzoffset(sp, &p, &fields[8], FALSE)) {
- *is_local = FALSE;
- }
- } else {
- if (p - p_start > 2 && !has_year) {
- fields[0] = val;
- has_year = TRUE;
- } else
- if ((val < 1 || val > 31) && !has_year) {
- fields[0] = val + (val < 100) * 1900 + (val < 50) * 100;
- has_year = TRUE;
- } else {
- if (num_index == 3)
- return FALSE;
- num[num_index++] = val;
- }
- }
- } else
- if (string_get_month(sp, &p, &fields[1])) {
- has_mon = TRUE;
- string_skip_until(sp, &p, "0123456789 -/(");
- } else
- if (has_time && string_match(sp, &p, "PM")) {
- if (fields[3] < 12)
- fields[3] += 12;
- continue;
- } else
- if (has_time && string_match(sp, &p, "AM")) {
- if (fields[3] == 12)
- fields[3] -= 12;
- continue;
- } else
- if (string_get_tzabbr(sp, &p, &fields[8])) {
- *is_local = FALSE;
- continue;
- } else
- if (c == '(') { /* skip parenthesized phrase */
- int level = 0;
- while ((c = sp[p]) != '\0') {
- p++;
- level += (c == '(');
- level -= (c == ')');
- if (!level)
- break;
- }
- if (level > 0)
- return FALSE;
- } else
- if (c == ')') {
- return FALSE;
- } else {
- if (has_year + has_mon + has_time + num_index)
- return FALSE;
- /* skip a word */
- string_skip_until(sp, &p, " -/(");
- }
- string_skip_separators(sp, &p);
- }
- if (num_index + has_year + has_mon > 3)
- return FALSE;
- switch (num_index) {
- case 0:
- if (!has_year)
- return FALSE;
- break;
- case 1:
- if (has_mon)
- fields[2] = num[0];
- else
- fields[1] = num[0];
- break;
- case 2:
- if (has_year) {
- fields[1] = num[0];
- fields[2] = num[1];
- } else
- if (has_mon) {
- fields[0] = num[1] + (num[1] < 100) * 1900 + (num[1] < 50) * 100;
- fields[2] = num[0];
- } else {
- fields[1] = num[0];
- fields[2] = num[1];
- }
- break;
- case 3:
- fields[0] = num[2] + (num[2] < 100) * 1900 + (num[2] < 50) * 100;
- fields[1] = num[0];
- fields[2] = num[1];
- break;
- default:
- return FALSE;
- }
- if (fields[1] < 1 || fields[2] < 1)
- return FALSE;
- fields[1] -= 1;
- return TRUE;
- }
- static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue s, rv;
- int fields[9];
- double fields1[9];
- double d;
- int i, c;
- JSString *sp;
- uint8_t buf[128];
- BOOL is_local;
- rv = JS_NAN;
- s = JS_ToString(ctx, argv[0]);
- if (JS_IsException(s))
- return JS_EXCEPTION;
- sp = JS_VALUE_GET_STRING(s);
- /* convert the string as a byte array */
- for (i = 0; i < sp->len && i < (int)countof(buf) - 1; i++) {
- c = string_get(sp, i);
- if (c > 255)
- c = (c == 0x2212) ? '-' : 'x';
- buf[i] = c;
- }
- buf[i] = '\0';
- if (js_date_parse_isostring(buf, fields, &is_local)
- || js_date_parse_otherstring(buf, fields, &is_local)) {
- static int const field_max[6] = { 0, 11, 31, 24, 59, 59 };
- BOOL valid = TRUE;
- /* check field maximum values */
- for (i = 1; i < 6; i++) {
- if (fields[i] > field_max[i])
- valid = FALSE;
- }
- /* special case 24:00:00.000 */
- if (fields[3] == 24 && (fields[4] | fields[5] | fields[6]))
- valid = FALSE;
- if (valid) {
- for(i = 0; i < 7; i++)
- fields1[i] = fields[i];
- d = set_date_fields(fields1, is_local) - fields[8] * 60000;
- rv = JS_NewFloat64(ctx, d);
- }
- }
- JS_FreeValue(ctx, s);
- return rv;
- }
- static JSValue js_Date_now(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- // now()
- return JS_NewInt64(ctx, date_now());
- }
- static JSValue js_date_Symbol_toPrimitive(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- // Symbol_toPrimitive(hint)
- JSValueConst obj = this_val;
- JSAtom hint = JS_ATOM_NULL;
- int hint_num;
- if (!JS_IsObject(obj))
- return JS_ThrowTypeErrorNotAnObject(ctx);
- if (JS_IsString(argv[0])) {
- hint = JS_ValueToAtom(ctx, argv[0]);
- if (hint == JS_ATOM_NULL)
- return JS_EXCEPTION;
- JS_FreeAtom(ctx, hint);
- }
- switch (hint) {
- case JS_ATOM_number:
- case JS_ATOM_integer:
- hint_num = HINT_NUMBER;
- break;
- case JS_ATOM_string:
- case JS_ATOM_default:
- hint_num = HINT_STRING;
- break;
- default:
- return JS_ThrowTypeError(ctx, "invalid hint");
- }
- return JS_ToPrimitive(ctx, obj, hint_num | HINT_FORCE_ORDINARY);
- }
- static JSValue js_date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- // getTimezoneOffset()
- double v;
- if (JS_ThisTimeValue(ctx, &v, this_val))
- return JS_EXCEPTION;
- if (isnan(v))
- return JS_NAN;
- else
- /* assuming -8.64e15 <= v <= -8.64e15 */
- return JS_NewInt64(ctx, getTimezoneOffset((int64_t)trunc(v)));
- }
- static JSValue js_date_getTime(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- // getTime()
- double v;
- if (JS_ThisTimeValue(ctx, &v, this_val))
- return JS_EXCEPTION;
- return JS_NewFloat64(ctx, v);
- }
- static JSValue js_date_setTime(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- // setTime(v)
- double v;
- if (JS_ThisTimeValue(ctx, &v, this_val) || JS_ToFloat64(ctx, &v, argv[0]))
- return JS_EXCEPTION;
- return JS_SetThisTimeValue(ctx, this_val, time_clip(v));
- }
- static JSValue js_date_setYear(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- // setYear(y)
- double y;
- JSValueConst args[1];
- if (JS_ThisTimeValue(ctx, &y, this_val) || JS_ToFloat64(ctx, &y, argv[0]))
- return JS_EXCEPTION;
- y = +y;
- if (isfinite(y)) {
- y = trunc(y);
- if (y >= 0 && y < 100)
- y += 1900;
- }
- args[0] = JS_NewFloat64(ctx, y);
- return set_date_field(ctx, this_val, 1, args, 0x011);
- }
- static JSValue js_date_toJSON(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- // toJSON(key)
- JSValue obj, tv, method, rv;
- double d;
- rv = JS_EXCEPTION;
- tv = JS_UNDEFINED;
- obj = JS_ToObject(ctx, this_val);
- tv = JS_ToPrimitive(ctx, obj, HINT_NUMBER);
- if (JS_IsException(tv))
- goto exception;
- if (JS_IsNumber(tv)) {
- if (JS_ToFloat64(ctx, &d, tv) < 0)
- goto exception;
- if (!isfinite(d)) {
- rv = JS_NULL;
- goto done;
- }
- }
- method = JS_GetProperty(ctx, obj, JS_ATOM_toISOString);
- if (JS_IsException(method))
- goto exception;
- if (!JS_IsFunction(ctx, method)) {
- JS_ThrowTypeError(ctx, "object needs toISOString method");
- JS_FreeValue(ctx, method);
- goto exception;
- }
- rv = JS_CallFree(ctx, method, obj, 0, NULL);
- exception:
- done:
- JS_FreeValue(ctx, obj);
- JS_FreeValue(ctx, tv);
- return rv;
- }
- static const JSCFunctionListEntry js_date_funcs[] = {
- JS_CFUNC_DEF("now", 0, js_Date_now ),
- JS_CFUNC_DEF("parse", 1, js_Date_parse ),
- JS_CFUNC_DEF("UTC", 7, js_Date_UTC ),
- };
- static const JSCFunctionListEntry js_date_proto_funcs[] = {
- JS_CFUNC_DEF("valueOf", 0, js_date_getTime ),
- JS_CFUNC_MAGIC_DEF("toString", 0, get_date_string, 0x13 ),
- JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_date_Symbol_toPrimitive ),
- JS_CFUNC_MAGIC_DEF("toUTCString", 0, get_date_string, 0x03 ),
- JS_ALIAS_DEF("toGMTString", "toUTCString" ),
- JS_CFUNC_MAGIC_DEF("toISOString", 0, get_date_string, 0x23 ),
- JS_CFUNC_MAGIC_DEF("toDateString", 0, get_date_string, 0x11 ),
- JS_CFUNC_MAGIC_DEF("toTimeString", 0, get_date_string, 0x12 ),
- JS_CFUNC_MAGIC_DEF("toLocaleString", 0, get_date_string, 0x33 ),
- JS_CFUNC_MAGIC_DEF("toLocaleDateString", 0, get_date_string, 0x31 ),
- JS_CFUNC_MAGIC_DEF("toLocaleTimeString", 0, get_date_string, 0x32 ),
- JS_CFUNC_DEF("getTimezoneOffset", 0, js_date_getTimezoneOffset ),
- JS_CFUNC_DEF("getTime", 0, js_date_getTime ),
- JS_CFUNC_MAGIC_DEF("getYear", 0, get_date_field, 0x101 ),
- JS_CFUNC_MAGIC_DEF("getFullYear", 0, get_date_field, 0x01 ),
- JS_CFUNC_MAGIC_DEF("getUTCFullYear", 0, get_date_field, 0x00 ),
- JS_CFUNC_MAGIC_DEF("getMonth", 0, get_date_field, 0x11 ),
- JS_CFUNC_MAGIC_DEF("getUTCMonth", 0, get_date_field, 0x10 ),
- JS_CFUNC_MAGIC_DEF("getDate", 0, get_date_field, 0x21 ),
- JS_CFUNC_MAGIC_DEF("getUTCDate", 0, get_date_field, 0x20 ),
- JS_CFUNC_MAGIC_DEF("getHours", 0, get_date_field, 0x31 ),
- JS_CFUNC_MAGIC_DEF("getUTCHours", 0, get_date_field, 0x30 ),
- JS_CFUNC_MAGIC_DEF("getMinutes", 0, get_date_field, 0x41 ),
- JS_CFUNC_MAGIC_DEF("getUTCMinutes", 0, get_date_field, 0x40 ),
- JS_CFUNC_MAGIC_DEF("getSeconds", 0, get_date_field, 0x51 ),
- JS_CFUNC_MAGIC_DEF("getUTCSeconds", 0, get_date_field, 0x50 ),
- JS_CFUNC_MAGIC_DEF("getMilliseconds", 0, get_date_field, 0x61 ),
- JS_CFUNC_MAGIC_DEF("getUTCMilliseconds", 0, get_date_field, 0x60 ),
- JS_CFUNC_MAGIC_DEF("getDay", 0, get_date_field, 0x71 ),
- JS_CFUNC_MAGIC_DEF("getUTCDay", 0, get_date_field, 0x70 ),
- JS_CFUNC_DEF("setTime", 1, js_date_setTime ),
- JS_CFUNC_MAGIC_DEF("setMilliseconds", 1, set_date_field, 0x671 ),
- JS_CFUNC_MAGIC_DEF("setUTCMilliseconds", 1, set_date_field, 0x670 ),
- JS_CFUNC_MAGIC_DEF("setSeconds", 2, set_date_field, 0x571 ),
- JS_CFUNC_MAGIC_DEF("setUTCSeconds", 2, set_date_field, 0x570 ),
- JS_CFUNC_MAGIC_DEF("setMinutes", 3, set_date_field, 0x471 ),
- JS_CFUNC_MAGIC_DEF("setUTCMinutes", 3, set_date_field, 0x470 ),
- JS_CFUNC_MAGIC_DEF("setHours", 4, set_date_field, 0x371 ),
- JS_CFUNC_MAGIC_DEF("setUTCHours", 4, set_date_field, 0x370 ),
- JS_CFUNC_MAGIC_DEF("setDate", 1, set_date_field, 0x231 ),
- JS_CFUNC_MAGIC_DEF("setUTCDate", 1, set_date_field, 0x230 ),
- JS_CFUNC_MAGIC_DEF("setMonth", 2, set_date_field, 0x131 ),
- JS_CFUNC_MAGIC_DEF("setUTCMonth", 2, set_date_field, 0x130 ),
- JS_CFUNC_DEF("setYear", 1, js_date_setYear ),
- JS_CFUNC_MAGIC_DEF("setFullYear", 3, set_date_field, 0x031 ),
- JS_CFUNC_MAGIC_DEF("setUTCFullYear", 3, set_date_field, 0x030 ),
- JS_CFUNC_DEF("toJSON", 1, js_date_toJSON ),
- };
- JSValue JS_NewDate(JSContext *ctx, double epoch_ms)
- {
- JSValue obj = js_create_from_ctor(ctx, JS_UNDEFINED, JS_CLASS_DATE);
- if (JS_IsException(obj))
- return JS_EXCEPTION;
- JS_SetObjectData(ctx, obj, __JS_NewFloat64(ctx, time_clip(epoch_ms)));
- return obj;
- }
- int JS_AddIntrinsicDate(JSContext *ctx)
- {
- JSValue obj;
- /* Date */
- obj = JS_NewCConstructor(ctx, JS_CLASS_DATE, "Date",
- js_date_constructor, 7, JS_CFUNC_constructor_or_func, 0,
- JS_UNDEFINED,
- js_date_funcs, countof(js_date_funcs),
- js_date_proto_funcs, countof(js_date_proto_funcs),
- 0);
- if (JS_IsException(obj))
- return -1;
- JS_FreeValue(ctx, obj);
- return 0;
- }
- /* eval */
- int JS_AddIntrinsicEval(JSContext *ctx)
- {
- ctx->eval_internal = __JS_EvalInternal;
- return 0;
- }
- /* BigInt */
- static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val)
- {
- uint32_t tag;
- redo:
- tag = JS_VALUE_GET_NORM_TAG(val);
- switch(tag) {
- case JS_TAG_INT:
- case JS_TAG_BOOL:
- val = JS_NewBigInt64(ctx, JS_VALUE_GET_INT(val));
- break;
- case JS_TAG_SHORT_BIG_INT:
- case JS_TAG_BIG_INT:
- break;
- case JS_TAG_FLOAT64:
- {
- double d = JS_VALUE_GET_FLOAT64(val);
- JSBigInt *r;
- int res;
- r = js_bigint_from_float64(ctx, &res, d);
- if (!r) {
- if (res == 0) {
- val = JS_EXCEPTION;
- } else if (res == 1) {
- val = JS_ThrowRangeError(ctx, "cannot convert to BigInt: not an integer");
- } else {
- val = JS_ThrowRangeError(ctx, "cannot convert NaN or Infinity to BigInt"); }
- } else {
- val = JS_CompactBigInt(ctx, r);
- }
- }
- break;
- case JS_TAG_STRING:
- case JS_TAG_STRING_ROPE:
- val = JS_StringToBigIntErr(ctx, val);
- break;
- case JS_TAG_OBJECT:
- val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
- if (JS_IsException(val))
- break;
- goto redo;
- case JS_TAG_NULL:
- case JS_TAG_UNDEFINED:
- default:
- JS_FreeValue(ctx, val);
- return JS_ThrowTypeError(ctx, "cannot convert to BigInt");
- }
- return val;
- }
- static JSValue js_bigint_constructor(JSContext *ctx,
- JSValueConst new_target,
- int argc, JSValueConst *argv)
- {
- if (!JS_IsUndefined(new_target))
- return JS_ThrowTypeErrorNotAConstructor(ctx, new_target);
- return JS_ToBigIntCtorFree(ctx, JS_DupValue(ctx, argv[0]));
- }
- static JSValue js_thisBigIntValue(JSContext *ctx, JSValueConst this_val)
- {
- if (JS_IsBigInt(ctx, this_val))
- return JS_DupValue(ctx, this_val);
- if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
- JSObject *p = JS_VALUE_GET_OBJ(this_val);
- if (p->class_id == JS_CLASS_BIG_INT) {
- if (JS_IsBigInt(ctx, p->u.object_data))
- return JS_DupValue(ctx, p->u.object_data);
- }
- }
- return JS_ThrowTypeError(ctx, "not a BigInt");
- }
- static JSValue js_bigint_toString(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue val;
- int base;
- JSValue ret;
- val = js_thisBigIntValue(ctx, this_val);
- if (JS_IsException(val))
- return val;
- if (argc == 0 || JS_IsUndefined(argv[0])) {
- base = 10;
- } else {
- base = js_get_radix(ctx, argv[0]);
- if (base < 0)
- goto fail;
- }
- ret = js_bigint_to_string1(ctx, val, base);
- JS_FreeValue(ctx, val);
- return ret;
- fail:
- JS_FreeValue(ctx, val);
- return JS_EXCEPTION;
- }
- static JSValue js_bigint_valueOf(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- return js_thisBigIntValue(ctx, this_val);
- }
- static JSValue js_bigint_asUintN(JSContext *ctx,
- JSValueConst this_val,
- int argc, JSValueConst *argv, int asIntN)
- {
- uint64_t bits;
- JSValue res, a;
-
- if (JS_ToIndex(ctx, &bits, argv[0]))
- return JS_EXCEPTION;
- a = JS_ToBigInt(ctx, argv[1]);
- if (JS_IsException(a))
- return JS_EXCEPTION;
- if (bits == 0) {
- JS_FreeValue(ctx, a);
- res = __JS_NewShortBigInt(ctx, 0);
- } else if (JS_VALUE_GET_TAG(a) == JS_TAG_SHORT_BIG_INT) {
- /* fast case */
- if (bits >= JS_SHORT_BIG_INT_BITS) {
- res = a;
- } else {
- uint64_t v;
- int shift;
- shift = 64 - bits;
- v = JS_VALUE_GET_SHORT_BIG_INT(a);
- v = v << shift;
- if (asIntN)
- v = (int64_t)v >> shift;
- else
- v = v >> shift;
- res = __JS_NewShortBigInt(ctx, v);
- }
- } else {
- JSBigInt *r, *p = JS_VALUE_GET_PTR(a);
- if (bits >= p->len * JS_LIMB_BITS) {
- res = a;
- } else {
- int len, shift, i;
- js_limb_t v;
- len = (bits + JS_LIMB_BITS - 1) / JS_LIMB_BITS;
- r = js_bigint_new(ctx, len);
- if (!r) {
- JS_FreeValue(ctx, a);
- return JS_EXCEPTION;
- }
- r->len = len;
- for(i = 0; i < len - 1; i++)
- r->tab[i] = p->tab[i];
- shift = (-bits) & (JS_LIMB_BITS - 1);
- /* 0 <= shift <= JS_LIMB_BITS - 1 */
- v = p->tab[len - 1] << shift;
- if (asIntN)
- v = (js_slimb_t)v >> shift;
- else
- v = v >> shift;
- r->tab[len - 1] = v;
- r = js_bigint_normalize(ctx, r);
- JS_FreeValue(ctx, a);
- res = JS_CompactBigInt(ctx, r);
- }
- }
- return res;
- }
- static const JSCFunctionListEntry js_bigint_funcs[] = {
- JS_CFUNC_MAGIC_DEF("asUintN", 2, js_bigint_asUintN, 0 ),
- JS_CFUNC_MAGIC_DEF("asIntN", 2, js_bigint_asUintN, 1 ),
- };
- static const JSCFunctionListEntry js_bigint_proto_funcs[] = {
- JS_CFUNC_DEF("toString", 0, js_bigint_toString ),
- JS_CFUNC_DEF("valueOf", 0, js_bigint_valueOf ),
- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "BigInt", JS_PROP_CONFIGURABLE ),
- };
- static int JS_AddIntrinsicBigInt(JSContext *ctx)
- {
- JSValue obj1;
- obj1 = JS_NewCConstructor(ctx, JS_CLASS_BIG_INT, "BigInt",
- js_bigint_constructor, 1, JS_CFUNC_constructor_or_func, 0,
- JS_UNDEFINED,
- js_bigint_funcs, countof(js_bigint_funcs),
- js_bigint_proto_funcs, countof(js_bigint_proto_funcs),
- 0);
- if (JS_IsException(obj1))
- return -1;
- JS_FreeValue(ctx, obj1);
- return 0;
- }
- /* Minimum amount of objects to be able to compile code and display
- error messages. */
- static int JS_AddIntrinsicBasicObjects(JSContext *ctx)
- {
- JSValue obj;
- JSCFunctionType ft;
- int i;
- /* warning: ordering is tricky */
- ctx->class_proto[JS_CLASS_OBJECT] =
- JS_NewObjectProtoClassAlloc(ctx, JS_NULL, JS_CLASS_OBJECT,
- countof(js_object_proto_funcs) + 1);
- if (JS_IsException(ctx->class_proto[JS_CLASS_OBJECT]))
- return -1;
- JS_SetImmutablePrototype(ctx, ctx->class_proto[JS_CLASS_OBJECT]);
- /* 2 more properties: caller and arguments */
- ctx->function_proto = JS_NewCFunction3(ctx, js_function_proto, "", 0,
- JS_CFUNC_generic, 0,
- ctx->class_proto[JS_CLASS_OBJECT],
- countof(js_function_proto_funcs) + 3 + 2);
- if (JS_IsException(ctx->function_proto))
- return -1;
- ctx->class_proto[JS_CLASS_BYTECODE_FUNCTION] = JS_DupValue(ctx, ctx->function_proto);
- ctx->global_obj = JS_NewObjectProtoClassAlloc(ctx, ctx->class_proto[JS_CLASS_OBJECT],
- JS_CLASS_GLOBAL_OBJECT, 64);
- if (JS_IsException(ctx->global_obj))
- return -1;
- {
- JSObject *p;
- obj = JS_NewObjectProtoClassAlloc(ctx, JS_NULL, JS_CLASS_OBJECT, 4);
- p = JS_VALUE_GET_OBJ(ctx->global_obj);
- p->u.global_object.uninitialized_vars = obj;
- }
- ctx->global_var_obj = JS_NewObjectProtoClassAlloc(ctx, JS_NULL,
- JS_CLASS_OBJECT, 16);
- if (JS_IsException(ctx->global_var_obj))
- return -1;
- /* Error */
- ft.generic_magic = js_error_constructor;
- obj = JS_NewCConstructor(ctx, JS_CLASS_ERROR, "Error",
- ft.generic, 1, JS_CFUNC_constructor_or_func_magic, -1,
- JS_UNDEFINED,
- js_error_funcs, countof(js_error_funcs),
- js_error_proto_funcs, countof(js_error_proto_funcs),
- 0);
- if (JS_IsException(obj))
- return -1;
- for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
- JSValue func_obj;
- const JSCFunctionListEntry *funcs;
- int n_args;
- char buf[ATOM_GET_STR_BUF_SIZE];
- const char *name = JS_AtomGetStr(ctx, buf, sizeof(buf),
- JS_ATOM_EvalError + i);
- n_args = 1 + (i == JS_AGGREGATE_ERROR);
- funcs = js_native_error_proto_funcs + 2 * i;
- func_obj = JS_NewCConstructor(ctx, -1, name,
- ft.generic, n_args, JS_CFUNC_constructor_or_func_magic, i,
- obj,
- NULL, 0,
- funcs, 2,
- 0);
- if (JS_IsException(func_obj)) {
- JS_FreeValue(ctx, obj);
- return -1;
- }
- ctx->native_error_proto[i] = JS_GetProperty(ctx, func_obj, JS_ATOM_prototype);
- JS_FreeValue(ctx, func_obj);
- if (JS_IsException(ctx->native_error_proto[i])) {
- JS_FreeValue(ctx, obj);
- return -1;
- }
- }
- JS_FreeValue(ctx, obj);
- /* Array */
- obj = JS_NewCConstructor(ctx, JS_CLASS_ARRAY, "Array",
- js_array_constructor, 1, JS_CFUNC_constructor_or_func, 0,
- JS_UNDEFINED,
- js_array_funcs, countof(js_array_funcs),
- js_array_proto_funcs, countof(js_array_proto_funcs),
- JS_NEW_CTOR_PROTO_CLASS);
- if (JS_IsException(obj))
- return -1;
- ctx->array_ctor = obj;
- {
- JSObject *p = JS_VALUE_GET_OBJ(ctx->class_proto[JS_CLASS_ARRAY]);
- p->is_std_array_prototype = TRUE;
- }
-
- ctx->array_shape = js_new_shape2(ctx, get_proto_obj(ctx->class_proto[JS_CLASS_ARRAY]),
- JS_PROP_INITIAL_HASH_SIZE, 1);
- if (!ctx->array_shape)
- return -1;
- if (add_shape_property(ctx, &ctx->array_shape, NULL,
- JS_ATOM_length, JS_PROP_WRITABLE | JS_PROP_LENGTH))
- return -1;
- ctx->arguments_shape = js_new_shape2(ctx, get_proto_obj(ctx->class_proto[JS_CLASS_OBJECT]),
- JS_PROP_INITIAL_HASH_SIZE, 3);
- if (!ctx->arguments_shape)
- return -1;
- if (add_shape_property(ctx, &ctx->arguments_shape, NULL,
- JS_ATOM_length, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE))
- return -1;
- if (add_shape_property(ctx, &ctx->arguments_shape, NULL,
- JS_ATOM_Symbol_iterator, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE))
- return -1;
- if (add_shape_property(ctx, &ctx->arguments_shape, NULL,
- JS_ATOM_callee, JS_PROP_GETSET))
- return -1;
- ctx->mapped_arguments_shape = js_new_shape2(ctx, get_proto_obj(ctx->class_proto[JS_CLASS_OBJECT]),
- JS_PROP_INITIAL_HASH_SIZE, 3);
- if (!ctx->mapped_arguments_shape)
- return -1;
- if (add_shape_property(ctx, &ctx->mapped_arguments_shape, NULL,
- JS_ATOM_length, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE))
- return -1;
- if (add_shape_property(ctx, &ctx->mapped_arguments_shape, NULL,
- JS_ATOM_Symbol_iterator, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE))
- return -1;
- if (add_shape_property(ctx, &ctx->mapped_arguments_shape, NULL,
- JS_ATOM_callee, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE))
- return -1;
-
- return 0;
- }
- int JS_AddIntrinsicBaseObjects(JSContext *ctx)
- {
- JSValue obj1, obj2;
- JSCFunctionType ft;
- ctx->throw_type_error = JS_NewCFunction(ctx, js_throw_type_error, NULL, 0);
- if (JS_IsException(ctx->throw_type_error))
- return -1;
- /* add caller and arguments properties to throw a TypeError */
- if (JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_caller, JS_UNDEFINED,
- ctx->throw_type_error, ctx->throw_type_error,
- JS_PROP_HAS_GET | JS_PROP_HAS_SET |
- JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE) < 0)
- return -1;
- if (JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_arguments, JS_UNDEFINED,
- ctx->throw_type_error, ctx->throw_type_error,
- JS_PROP_HAS_GET | JS_PROP_HAS_SET |
- JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE) < 0)
- return -1;
- JS_FreeValue(ctx, js_object_seal(ctx, JS_UNDEFINED, 1, (JSValueConst *)&ctx->throw_type_error, 1));
- /* Object */
- obj1 = JS_NewCConstructor(ctx, JS_CLASS_OBJECT, "Object",
- js_object_constructor, 1, JS_CFUNC_constructor_or_func, 0,
- JS_UNDEFINED,
- js_object_funcs, countof(js_object_funcs),
- js_object_proto_funcs, countof(js_object_proto_funcs),
- JS_NEW_CTOR_PROTO_EXIST);
- if (JS_IsException(obj1))
- return -1;
- JS_FreeValue(ctx, obj1);
-
- /* Function */
- ft.generic_magic = js_function_constructor;
- obj1 = JS_NewCConstructor(ctx, JS_CLASS_BYTECODE_FUNCTION, "Function",
- ft.generic, 1, JS_CFUNC_constructor_or_func_magic, JS_FUNC_NORMAL,
- JS_UNDEFINED,
- NULL, 0,
- js_function_proto_funcs, countof(js_function_proto_funcs),
- JS_NEW_CTOR_PROTO_EXIST);
- if (JS_IsException(obj1))
- return -1;
- ctx->function_ctor = obj1;
- /* Iterator */
- obj2 = JS_NewCConstructor(ctx, JS_CLASS_ITERATOR, "Iterator",
- js_iterator_constructor, 0, JS_CFUNC_constructor_or_func, 0,
- JS_UNDEFINED,
- js_iterator_funcs, countof(js_iterator_funcs),
- js_iterator_proto_funcs, countof(js_iterator_proto_funcs),
- 0);
- if (JS_IsException(obj2))
- return -1;
- // quirk: Iterator.prototype.constructor is an accessor property
- // TODO(bnoordhuis) mildly inefficient because JS_NewGlobalCConstructor
- // first creates a .constructor value property that we then replace with
- // an accessor
- obj1 = JS_NewCFunctionData(ctx, js_iterator_constructor_getset,
- 0, 0, 1, (JSValueConst *)&obj2);
- if (JS_IsException(obj1)) {
- JS_FreeValue(ctx, obj2);
- return -1;
- }
- if (JS_DefineProperty(ctx, ctx->class_proto[JS_CLASS_ITERATOR],
- JS_ATOM_constructor, JS_UNDEFINED,
- obj1, obj1,
- JS_PROP_HAS_GET | JS_PROP_HAS_SET | JS_PROP_CONFIGURABLE) < 0) {
- JS_FreeValue(ctx, obj2);
- JS_FreeValue(ctx, obj1);
- return -1;
- }
- JS_FreeValue(ctx, obj1);
- ctx->iterator_ctor = obj2;
-
- ctx->class_proto[JS_CLASS_ITERATOR_CONCAT] =
- JS_NewObjectProtoList(ctx, ctx->class_proto[JS_CLASS_ITERATOR],
- js_iterator_concat_proto_funcs,
- countof(js_iterator_concat_proto_funcs));
- if (JS_IsException(ctx->class_proto[JS_CLASS_ITERATOR_CONCAT]))
- return -1;
- ctx->class_proto[JS_CLASS_ITERATOR_HELPER] =
- JS_NewObjectProtoList(ctx, ctx->class_proto[JS_CLASS_ITERATOR],
- js_iterator_helper_proto_funcs,
- countof(js_iterator_helper_proto_funcs));
- if (JS_IsException(ctx->class_proto[JS_CLASS_ITERATOR_HELPER]))
- return -1;
-
- ctx->class_proto[JS_CLASS_ITERATOR_WRAP] =
- JS_NewObjectProtoList(ctx, ctx->class_proto[JS_CLASS_ITERATOR],
- js_iterator_wrap_proto_funcs,
- countof(js_iterator_wrap_proto_funcs));
- if (JS_IsException(ctx->class_proto[JS_CLASS_ITERATOR_WRAP]))
- return -1;
- /* needed to initialize arguments[Symbol.iterator] */
- ctx->array_proto_values =
- JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_values);
- if (JS_IsException(ctx->array_proto_values))
- return -1;
- ctx->class_proto[JS_CLASS_ARRAY_ITERATOR] =
- JS_NewObjectProtoList(ctx, ctx->class_proto[JS_CLASS_ITERATOR],
- js_array_iterator_proto_funcs,
- countof(js_array_iterator_proto_funcs));
- if (JS_IsException(ctx->class_proto[JS_CLASS_ARRAY_ITERATOR]))
- return -1;
- /* parseFloat and parseInteger must be defined before Number
- because of the Number.parseFloat and Number.parseInteger
- aliases */
- if (JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_global_funcs,
- countof(js_global_funcs)))
- return -1;
- /* Number */
- obj1 = JS_NewCConstructor(ctx, JS_CLASS_NUMBER, "Number",
- js_number_constructor, 1, JS_CFUNC_constructor_or_func, 0,
- JS_UNDEFINED,
- js_number_funcs, countof(js_number_funcs),
- js_number_proto_funcs, countof(js_number_proto_funcs),
- JS_NEW_CTOR_PROTO_CLASS);
- if (JS_IsException(obj1))
- return -1;
- JS_FreeValue(ctx, obj1);
- if (JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_NUMBER], JS_NewInt32(ctx, 0)))
- return -1;
-
- /* Boolean */
- obj1 = JS_NewCConstructor(ctx, JS_CLASS_BOOLEAN, "Boolean",
- js_boolean_constructor, 1, JS_CFUNC_constructor_or_func, 0,
- JS_UNDEFINED,
- NULL, 0,
- js_boolean_proto_funcs, countof(js_boolean_proto_funcs),
- JS_NEW_CTOR_PROTO_CLASS);
- if (JS_IsException(obj1))
- return -1;
- JS_FreeValue(ctx, obj1);
- if (JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_BOOLEAN], JS_NewBool(ctx, FALSE)))
- return -1;
- /* String */
- obj1 = JS_NewCConstructor(ctx, JS_CLASS_STRING, "String",
- js_string_constructor, 1, JS_CFUNC_constructor_or_func, 0,
- JS_UNDEFINED,
- js_string_funcs, countof(js_string_funcs),
- js_string_proto_funcs, countof(js_string_proto_funcs),
- JS_NEW_CTOR_PROTO_CLASS);
- if (JS_IsException(obj1))
- return -1;
- JS_FreeValue(ctx, obj1);
- if (JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_STRING], JS_AtomToString(ctx, JS_ATOM_empty_string)))
- return -1;
- ctx->class_proto[JS_CLASS_STRING_ITERATOR] =
- JS_NewObjectProtoList(ctx, ctx->class_proto[JS_CLASS_ITERATOR],
- js_string_iterator_proto_funcs,
- countof(js_string_iterator_proto_funcs));
- if (JS_IsException(ctx->class_proto[JS_CLASS_STRING_ITERATOR]))
- return -1;
- /* Math: create as autoinit object */
- js_random_init(ctx);
- if (JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_math_obj, countof(js_math_obj)))
- return -1;
- /* ES6 Reflect: create as autoinit object */
- if (JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_reflect_obj, countof(js_reflect_obj)))
- return -1;
- /* ES6 Symbol */
- obj1 = JS_NewCConstructor(ctx, JS_CLASS_SYMBOL, "Symbol",
- js_symbol_constructor, 0, JS_CFUNC_constructor_or_func, 0,
- JS_UNDEFINED,
- js_symbol_funcs, countof(js_symbol_funcs),
- js_symbol_proto_funcs, countof(js_symbol_proto_funcs),
- 0);
- if (JS_IsException(obj1))
- return -1;
- JS_FreeValue(ctx, obj1);
-
- /* ES6 Generator */
- ctx->class_proto[JS_CLASS_GENERATOR] =
- JS_NewObjectProtoList(ctx, ctx->class_proto[JS_CLASS_ITERATOR],
- js_generator_proto_funcs,
- countof(js_generator_proto_funcs));
- if (JS_IsException(ctx->class_proto[JS_CLASS_GENERATOR]))
- return -1;
- ft.generic_magic = js_function_constructor;
- obj1 = JS_NewCConstructor(ctx, JS_CLASS_GENERATOR_FUNCTION, "GeneratorFunction",
- ft.generic, 1, JS_CFUNC_constructor_or_func_magic, JS_FUNC_GENERATOR,
- ctx->function_ctor,
- NULL, 0,
- js_generator_function_proto_funcs,
- countof(js_generator_function_proto_funcs),
- JS_NEW_CTOR_NO_GLOBAL | JS_NEW_CTOR_READONLY);
- if (JS_IsException(obj1))
- return -1;
- JS_FreeValue(ctx, obj1);
- if (JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],
- ctx->class_proto[JS_CLASS_GENERATOR],
- JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE))
- return -1;
-
- /* global properties */
- ctx->eval_obj = JS_GetProperty(ctx, ctx->global_obj, JS_ATOM_eval);
- if (JS_IsException(ctx->eval_obj))
- return -1;
-
- if (JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_globalThis,
- JS_DupValue(ctx, ctx->global_obj),
- JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE) < 0)
- return -1;
- /* BigInt */
- if (JS_AddIntrinsicBigInt(ctx))
- return -1;
- return 0;
- }
- /* Typed Arrays */
- static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT] = {
- 0, 0, 0, 1, 1, 2, 2,
- 3, 3, // BigInt64Array, BigUint64Array
- 1, 2, 3 // Float16Array, Float32Array, Float64Array
- };
- static JSValue js_array_buffer_constructor3(JSContext *ctx,
- JSValueConst new_target,
- uint64_t len, uint64_t *max_len,
- JSClassID class_id,
- uint8_t *buf,
- JSFreeArrayBufferDataFunc *free_func,
- void *opaque, BOOL alloc_flag)
- {
- JSRuntime *rt = ctx->rt;
- JSValue obj;
- JSArrayBuffer *abuf = NULL;
- uint64_t sab_alloc_len;
- if (!alloc_flag && buf && max_len && free_func != js_array_buffer_free) {
- // not observable from JS land, only through C API misuse;
- // JS code cannot create externally managed buffers directly
- return JS_ThrowInternalError(ctx,
- "resizable ArrayBuffers not supported "
- "for externally managed buffers");
- }
- obj = js_create_from_ctor(ctx, new_target, class_id);
- if (JS_IsException(obj))
- return obj;
- /* XXX: we are currently limited to 2 GB */
- if (len > INT32_MAX) {
- JS_ThrowRangeError(ctx, "invalid array buffer length");
- goto fail;
- }
- if (max_len && *max_len > INT32_MAX) {
- JS_ThrowRangeError(ctx, "invalid max array buffer length");
- goto fail;
- }
- abuf = js_malloc(ctx, sizeof(*abuf));
- if (!abuf)
- goto fail;
- abuf->byte_length = len;
- abuf->max_byte_length = max_len ? *max_len : -1;
- if (alloc_flag) {
- if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER &&
- rt->sab_funcs.sab_alloc) {
- // TOOD(bnoordhuis) resizing backing memory for SABs atomically
- // is hard so we cheat and allocate |maxByteLength| bytes upfront
- sab_alloc_len = max_len ? *max_len : len;
- abuf->data = rt->sab_funcs.sab_alloc(rt->sab_funcs.sab_opaque,
- max_int(sab_alloc_len, 1));
- if (!abuf->data)
- goto fail;
- memset(abuf->data, 0, sab_alloc_len);
- } else {
- /* the allocation must be done after the object creation */
- abuf->data = js_mallocz(ctx, max_int(len, 1));
- if (!abuf->data)
- goto fail;
- }
- } else {
- if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER &&
- rt->sab_funcs.sab_dup) {
- rt->sab_funcs.sab_dup(rt->sab_funcs.sab_opaque, buf);
- }
- abuf->data = buf;
- }
- init_list_head(&abuf->array_list);
- abuf->detached = FALSE;
- abuf->shared = (class_id == JS_CLASS_SHARED_ARRAY_BUFFER);
- abuf->opaque = opaque;
- abuf->free_func = free_func;
- if (alloc_flag && buf)
- memcpy(abuf->data, buf, len);
- JS_SetOpaque(obj, abuf);
- return obj;
- fail:
- JS_FreeValue(ctx, obj);
- js_free(ctx, abuf);
- return JS_EXCEPTION;
- }
- static void js_array_buffer_free(JSRuntime *rt, void *opaque, void *ptr)
- {
- js_free_rt(rt, ptr);
- }
- static JSValue js_array_buffer_constructor2(JSContext *ctx,
- JSValueConst new_target,
- uint64_t len, uint64_t *max_len,
- JSClassID class_id)
- {
- return js_array_buffer_constructor3(ctx, new_target, len, max_len, class_id,
- NULL, js_array_buffer_free, NULL,
- TRUE);
- }
- static JSValue js_array_buffer_constructor1(JSContext *ctx,
- JSValueConst new_target,
- uint64_t len, uint64_t *max_len)
- {
- return js_array_buffer_constructor2(ctx, new_target, len, max_len,
- JS_CLASS_ARRAY_BUFFER);
- }
- JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len,
- JSFreeArrayBufferDataFunc *free_func, void *opaque,
- BOOL is_shared)
- {
- JSClassID class_id =
- is_shared ? JS_CLASS_SHARED_ARRAY_BUFFER : JS_CLASS_ARRAY_BUFFER;
- return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len, NULL, class_id,
- buf, free_func, opaque, FALSE);
- }
- /* create a new ArrayBuffer of length 'len' and copy 'buf' to it */
- JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len)
- {
- return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len, NULL,
- JS_CLASS_ARRAY_BUFFER,
- (uint8_t *)buf,
- js_array_buffer_free, NULL,
- TRUE);
- }
- static JSValue js_array_buffer_constructor0(JSContext *ctx, JSValueConst new_target,
- int argc, JSValueConst *argv,
- JSClassID class_id)
- {
- uint64_t len, max_len, *pmax_len = NULL;
- JSValue obj, val;
- int64_t i;
- if (JS_ToIndex(ctx, &len, argv[0]))
- return JS_EXCEPTION;
- if (argc < 2)
- goto next;
- if (!JS_IsObject(argv[1]))
- goto next;
- obj = JS_ToObject(ctx, argv[1]);
- if (JS_IsException(obj))
- return JS_EXCEPTION;
- val = JS_GetProperty(ctx, obj, JS_ATOM_maxByteLength);
- JS_FreeValue(ctx, obj);
- if (JS_IsException(val))
- return JS_EXCEPTION;
- if (JS_IsUndefined(val))
- goto next;
- if (JS_ToInt64Free(ctx, &i, val))
- return JS_EXCEPTION;
- // don't have to check i < 0 because len >= 0
- if (len > i || i > MAX_SAFE_INTEGER)
- return JS_ThrowRangeError(ctx, "invalid array buffer max length");
- max_len = i;
- pmax_len = &max_len;
- next:
- return js_array_buffer_constructor2(ctx, new_target, len, pmax_len,
- class_id);
- }
- static JSValue js_array_buffer_constructor(JSContext *ctx,
- JSValueConst new_target,
- int argc, JSValueConst *argv)
- {
- return js_array_buffer_constructor0(ctx, new_target, argc, argv,
- JS_CLASS_ARRAY_BUFFER);
- }
- static JSValue js_shared_array_buffer_constructor(JSContext *ctx,
- JSValueConst new_target,
- int argc, JSValueConst *argv)
- {
- return js_array_buffer_constructor0(ctx, new_target, argc, argv,
- JS_CLASS_SHARED_ARRAY_BUFFER);
- }
- /* also used for SharedArrayBuffer */
- static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- JSArrayBuffer *abuf = p->u.array_buffer;
- struct list_head *el, *el1;
- if (abuf) {
- /* The ArrayBuffer finalizer may be called before the typed
- array finalizers using it, so abuf->array_list is not
- necessarily empty. */
- list_for_each_safe(el, el1, &abuf->array_list) {
- JSTypedArray *ta;
- JSObject *p1;
- ta = list_entry(el, JSTypedArray, link);
- ta->link.prev = NULL;
- ta->link.next = NULL;
- p1 = ta->obj;
- /* Note: the typed array length and offset fields are not modified */
- if (p1->class_id != JS_CLASS_DATAVIEW) {
- p1->u.array.count = 0;
- p1->u.array.u.ptr = NULL;
- }
- }
- if (abuf->shared && rt->sab_funcs.sab_free) {
- rt->sab_funcs.sab_free(rt->sab_funcs.sab_opaque, abuf->data);
- } else {
- if (abuf->free_func)
- abuf->free_func(rt, abuf->opaque, abuf->data);
- }
- js_free_rt(rt, abuf);
- }
- }
- static JSValue js_array_buffer_isView(JSContext *ctx,
- JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSObject *p;
- BOOL res;
- res = FALSE;
- if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) {
- p = JS_VALUE_GET_OBJ(argv[0]);
- if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
- p->class_id <= JS_CLASS_DATAVIEW) {
- res = TRUE;
- }
- }
- return JS_NewBool(ctx, res);
- }
- static const JSCFunctionListEntry js_array_buffer_funcs[] = {
- JS_CFUNC_DEF("isView", 1, js_array_buffer_isView ),
- JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
- };
- static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx)
- {
- return JS_ThrowTypeError(ctx, "ArrayBuffer is detached");
- }
- static JSValue JS_ThrowTypeErrorArrayBufferOOB(JSContext *ctx)
- {
- return JS_ThrowTypeError(ctx, "ArrayBuffer is detached or resized");
- }
- // #sec-get-arraybuffer.prototype.detached
- static JSValue js_array_buffer_get_detached(JSContext *ctx,
- JSValueConst this_val)
- {
- JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, JS_CLASS_ARRAY_BUFFER);
- if (!abuf)
- return JS_EXCEPTION;
- if (abuf->shared)
- return JS_ThrowTypeError(ctx, "detached called on SharedArrayBuffer");
- return JS_NewBool(ctx, abuf->detached);
- }
- static JSValue js_array_buffer_get_byteLength(JSContext *ctx,
- JSValueConst this_val,
- int class_id)
- {
- JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, class_id);
- if (!abuf)
- return JS_EXCEPTION;
- /* return 0 if detached */
- return JS_NewUint32(ctx, abuf->byte_length);
- }
- static JSValue js_array_buffer_get_maxByteLength(JSContext *ctx,
- JSValueConst this_val,
- int class_id)
- {
- JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, class_id);
- if (!abuf)
- return JS_EXCEPTION;
- if (array_buffer_is_resizable(abuf))
- return JS_NewUint32(ctx, abuf->max_byte_length);
- return JS_NewUint32(ctx, abuf->byte_length);
- }
- static JSValue js_array_buffer_get_resizable(JSContext *ctx,
- JSValueConst this_val,
- int class_id)
- {
- JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, class_id);
- if (!abuf)
- return JS_EXCEPTION;
- return JS_NewBool(ctx, array_buffer_is_resizable(abuf));
- }
- static void js_array_buffer_update_typed_arrays(JSArrayBuffer *abuf)
- {
- uint32_t size_log2, size_elem;
- struct list_head *el;
- JSTypedArray *ta;
- JSObject *p;
- uint8_t *data;
- int64_t len;
- len = abuf->byte_length;
- data = abuf->data;
- // update lengths of all typed arrays backed by this array buffer
- list_for_each(el, &abuf->array_list) {
- ta = list_entry(el, JSTypedArray, link);
- p = ta->obj;
- if (p->class_id == JS_CLASS_DATAVIEW) {
- if (ta->track_rab) {
- if (ta->offset < len)
- ta->length = len - ta->offset;
- else
- ta->length = 0;
- }
- } else {
- p->u.array.count = 0;
- p->u.array.u.ptr = NULL;
- size_log2 = typed_array_size_log2(p->class_id);
- size_elem = 1 << size_log2;
- if (ta->track_rab) {
- if (len >= (int64_t)ta->offset + size_elem) {
- p->u.array.count = (len - ta->offset) >> size_log2;
- p->u.array.u.ptr = &data[ta->offset];
- }
- } else {
- if (len >= (int64_t)ta->offset + ta->length) {
- p->u.array.count = ta->length >> size_log2;
- p->u.array.u.ptr = &data[ta->offset];
- }
- }
- }
- }
-
- }
- void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj)
- {
- JSArrayBuffer *abuf = JS_GetOpaque(obj, JS_CLASS_ARRAY_BUFFER);
- if (!abuf || abuf->detached)
- return;
- if (abuf->free_func)
- abuf->free_func(ctx->rt, abuf->opaque, abuf->data);
- abuf->data = NULL;
- abuf->byte_length = 0;
- abuf->detached = TRUE;
- js_array_buffer_update_typed_arrays(abuf);
- }
- /* get an ArrayBuffer or SharedArrayBuffer */
- static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj)
- {
- JSObject *p;
- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
- goto fail;
- p = JS_VALUE_GET_OBJ(obj);
- if (p->class_id != JS_CLASS_ARRAY_BUFFER &&
- p->class_id != JS_CLASS_SHARED_ARRAY_BUFFER) {
- fail:
- JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_ARRAY_BUFFER);
- return NULL;
- }
- return p->u.array_buffer;
- }
- /* return NULL if exception. WARNING: any JS call can detach the
- buffer and render the returned pointer invalid */
- uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst obj)
- {
- JSArrayBuffer *abuf = js_get_array_buffer(ctx, obj);
- if (!abuf)
- goto fail;
- if (abuf->detached) {
- JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
- goto fail;
- }
- *psize = abuf->byte_length;
- return abuf->data;
- fail:
- *psize = 0;
- return NULL;
- }
- static BOOL array_buffer_is_resizable(const JSArrayBuffer *abuf)
- {
- return abuf->max_byte_length >= 0;
- }
- // ES #sec-arraybuffer.prototype.transfer
- static JSValue js_array_buffer_transfer(JSContext *ctx,
- JSValueConst this_val,
- int argc, JSValueConst *argv,
- int transfer_to_fixed_length)
- {
- JSArrayBuffer *abuf;
- uint64_t new_len, *pmax_len, max_len;
- JSValue res;
- abuf = JS_GetOpaque2(ctx, this_val, JS_CLASS_ARRAY_BUFFER);
- if (!abuf)
- return JS_EXCEPTION;
- if (abuf->shared)
- return JS_ThrowTypeError(ctx, "cannot transfer a SharedArrayBuffer");
- if (argc < 1 || JS_IsUndefined(argv[0]))
- new_len = abuf->byte_length;
- else if (JS_ToIndex(ctx, &new_len, argv[0]))
- return JS_EXCEPTION;
- if (abuf->detached)
- return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
- pmax_len = NULL;
- if (!transfer_to_fixed_length) {
- if (array_buffer_is_resizable(abuf)) { // carry over maxByteLength
- max_len = abuf->max_byte_length;
- if (new_len > max_len)
- return JS_ThrowTypeError(ctx, "invalid array buffer length");
- // TODO(bnoordhuis) support externally managed RABs
- if (abuf->free_func == js_array_buffer_free)
- pmax_len = &max_len;
- }
- }
- /* create an empty AB */
- if (new_len == 0) {
- res = js_array_buffer_constructor2(ctx, JS_UNDEFINED, 0, pmax_len, JS_CLASS_ARRAY_BUFFER);
- if (JS_IsException(res))
- return res;
- JS_DetachArrayBuffer(ctx, this_val);
- } else {
- uint64_t old_len;
-
- old_len = abuf->byte_length;
- /* if length mismatch, realloc. Otherwise, use the same backing buffer. */
- if (new_len != old_len) {
- /* XXX: we are currently limited to 2 GB */
- if (new_len > INT32_MAX)
- return JS_ThrowRangeError(ctx, "invalid array buffer length");
- if (abuf->free_func != js_array_buffer_free) {
- JSArrayBuffer *new_abuf;
- /* cannot use js_realloc() because the buffer was
- allocated with a custom allocator */
- res = js_array_buffer_constructor2(ctx, JS_UNDEFINED, new_len, pmax_len, JS_CLASS_ARRAY_BUFFER);
- if (JS_IsException(res))
- return res;
- new_abuf = JS_GetOpaque2(ctx, res, JS_CLASS_ARRAY_BUFFER);
- memcpy(new_abuf->data, abuf->data, min_int(old_len, new_len));
- abuf->free_func(ctx->rt, abuf->opaque, abuf->data);
- } else {
- JSArrayBuffer *new_abuf;
- uint8_t *new_bs;
- /* reallocate the buffer after the new array buffer is
- created in case the new array buffer creation
- fails. */
- res = js_array_buffer_constructor2(ctx, JS_UNDEFINED, 0, pmax_len, JS_CLASS_ARRAY_BUFFER);
- if (JS_IsException(res))
- return res;
- new_bs = js_realloc(ctx, abuf->data, new_len);
- if (!new_bs) {
- JS_FreeValue(ctx, res);
- return JS_EXCEPTION;
- }
- if (new_len > old_len)
- memset(new_bs + old_len, 0, new_len - old_len);
- new_abuf = JS_GetOpaque2(ctx, res, JS_CLASS_ARRAY_BUFFER);
- js_free(ctx, new_abuf->data);
- new_abuf->data = new_bs;
- new_abuf->byte_length = new_len;
- }
- } else {
- /* can keep the custom free function */
- res = js_array_buffer_constructor3(ctx, JS_UNDEFINED, new_len, pmax_len,
- JS_CLASS_ARRAY_BUFFER,
- abuf->data, abuf->free_func,
- abuf->opaque, FALSE);
- if (JS_IsException(res))
- return res;
- }
- /* neuter the backing buffer */
- abuf->data = NULL;
- abuf->byte_length = 0;
- abuf->detached = TRUE;
- js_array_buffer_update_typed_arrays(abuf);
- }
- return res;
- }
- static JSValue js_array_buffer_resize(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int class_id)
- {
- JSArrayBuffer *abuf;
- uint8_t *data;
- int64_t len;
- abuf = JS_GetOpaque2(ctx, this_val, class_id);
- if (!abuf)
- return JS_EXCEPTION;
- if (JS_ToInt64(ctx, &len, argv[0]))
- return JS_EXCEPTION;
- if (abuf->detached)
- return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
- if (!array_buffer_is_resizable(abuf))
- return JS_ThrowTypeError(ctx, "array buffer is not resizable");
- // TODO(bnoordhuis) support externally managed RABs
- if (abuf->free_func != js_array_buffer_free)
- return JS_ThrowTypeError(ctx, "external array buffer is not resizable");
- if (len < 0 || len > abuf->max_byte_length) {
- bad_length:
- return JS_ThrowRangeError(ctx, "invalid array buffer length");
- }
- // SABs can only grow and we don't need to realloc because
- // js_array_buffer_constructor3 commits all memory upfront;
- // regular RABs are resizable both ways and realloc
- if (abuf->shared) {
- if (len < abuf->byte_length)
- goto bad_length;
- // Note this is off-spec; there's supposed to be a single atomic
- // |byteLength| property that's shared across SABs but we store
- // it per SAB instead. That means when thread A calls sab.grow(2)
- // at time t0, and thread B calls sab.grow(1) at time t1, we don't
- // throw a TypeError in thread B as the spec says we should,
- // instead both threads get their own view of the backing memory,
- // 2 bytes big in A, and 1 byte big in B
- abuf->byte_length = len;
- } else {
- data = js_realloc(ctx, abuf->data, max_int(len, 1));
- if (!data)
- return JS_EXCEPTION;
- if (len > abuf->byte_length)
- memset(&data[abuf->byte_length], 0, len - abuf->byte_length);
- abuf->byte_length = len;
- abuf->data = data;
- }
- js_array_buffer_update_typed_arrays(abuf);
- return JS_UNDEFINED;
- }
- static JSValue js_array_buffer_slice(JSContext *ctx,
- JSValueConst this_val,
- int argc, JSValueConst *argv, int class_id)
- {
- JSArrayBuffer *abuf, *new_abuf;
- int64_t len, start, end, new_len;
- JSValue ctor, new_obj;
- abuf = JS_GetOpaque2(ctx, this_val, class_id);
- if (!abuf)
- return JS_EXCEPTION;
- if (abuf->detached)
- return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
- len = abuf->byte_length;
- if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len))
- return JS_EXCEPTION;
- end = len;
- if (!JS_IsUndefined(argv[1])) {
- if (JS_ToInt64Clamp(ctx, &end, argv[1], 0, len, len))
- return JS_EXCEPTION;
- }
- new_len = max_int64(end - start, 0);
- ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED);
- if (JS_IsException(ctor))
- return ctor;
- if (JS_IsUndefined(ctor)) {
- new_obj = js_array_buffer_constructor2(ctx, JS_UNDEFINED, new_len,
- NULL, class_id);
- } else {
- JSValue args[1];
- args[0] = JS_NewInt64(ctx, new_len);
- new_obj = JS_CallConstructor(ctx, ctor, 1, (JSValueConst *)args);
- JS_FreeValue(ctx, ctor);
- JS_FreeValue(ctx, args[0]);
- }
- if (JS_IsException(new_obj))
- return new_obj;
- new_abuf = JS_GetOpaque2(ctx, new_obj, class_id);
- if (!new_abuf)
- goto fail;
- if (js_same_value(ctx, new_obj, this_val)) {
- JS_ThrowTypeError(ctx, "cannot use identical ArrayBuffer");
- goto fail;
- }
- if (new_abuf->detached) {
- JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
- goto fail;
- }
- if (new_abuf->byte_length < new_len) {
- JS_ThrowTypeError(ctx, "new ArrayBuffer is too small");
- goto fail;
- }
- /* must test again because of side effects */
- if (abuf->detached || abuf->byte_length < start + new_len) {
- JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
- goto fail;
- }
- memcpy(new_abuf->data, abuf->data + start, new_len);
- return new_obj;
- fail:
- JS_FreeValue(ctx, new_obj);
- return JS_EXCEPTION;
- }
- static const JSCFunctionListEntry js_array_buffer_proto_funcs[] = {
- JS_CGETSET_MAGIC_DEF("byteLength", js_array_buffer_get_byteLength, NULL, JS_CLASS_ARRAY_BUFFER ),
- JS_CGETSET_MAGIC_DEF("maxByteLength", js_array_buffer_get_maxByteLength, NULL, JS_CLASS_ARRAY_BUFFER ),
- JS_CGETSET_MAGIC_DEF("resizable", js_array_buffer_get_resizable, NULL, JS_CLASS_ARRAY_BUFFER ),
- JS_CGETSET_DEF("detached", js_array_buffer_get_detached, NULL ),
- JS_CFUNC_MAGIC_DEF("resize", 1, js_array_buffer_resize, JS_CLASS_ARRAY_BUFFER ),
- JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_ARRAY_BUFFER ),
- JS_CFUNC_MAGIC_DEF("transfer", 0, js_array_buffer_transfer, 0 ),
- JS_CFUNC_MAGIC_DEF("transferToFixedLength", 0, js_array_buffer_transfer, 1 ),
- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "ArrayBuffer", JS_PROP_CONFIGURABLE ),
- };
- /* SharedArrayBuffer */
- static const JSCFunctionListEntry js_shared_array_buffer_funcs[] = {
- JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
- };
- static const JSCFunctionListEntry js_shared_array_buffer_proto_funcs[] = {
- JS_CGETSET_MAGIC_DEF("byteLength", js_array_buffer_get_byteLength, NULL, JS_CLASS_SHARED_ARRAY_BUFFER ),
- JS_CGETSET_MAGIC_DEF("maxByteLength", js_array_buffer_get_maxByteLength, NULL, JS_CLASS_SHARED_ARRAY_BUFFER ),
- JS_CGETSET_MAGIC_DEF("growable", js_array_buffer_get_resizable, NULL, JS_CLASS_SHARED_ARRAY_BUFFER ),
- JS_CFUNC_MAGIC_DEF("grow", 1, js_array_buffer_resize, JS_CLASS_SHARED_ARRAY_BUFFER ),
- JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_SHARED_ARRAY_BUFFER ),
- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "SharedArrayBuffer", JS_PROP_CONFIGURABLE ),
- };
- static JSObject *get_typed_array(JSContext *ctx, JSValueConst this_val)
- {
- JSObject *p;
- if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
- goto fail;
- p = JS_VALUE_GET_OBJ(this_val);
- if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY &&
- p->class_id <= JS_CLASS_FLOAT64_ARRAY)) {
- fail:
- JS_ThrowTypeError(ctx, "not a TypedArray");
- return NULL;
- }
- return p;
- }
- // is the typed array detached or out of bounds relative to its RAB?
- // |p| must be a typed array, *not* a DataView
- static BOOL typed_array_is_oob(JSObject *p)
- {
- JSArrayBuffer *abuf;
- JSTypedArray *ta;
- int len, size_elem;
- int64_t end;
- assert(p->class_id >= JS_CLASS_UINT8C_ARRAY);
- assert(p->class_id <= JS_CLASS_FLOAT64_ARRAY);
- ta = p->u.typed_array;
- abuf = ta->buffer->u.array_buffer;
- if (abuf->detached)
- return TRUE;
- len = abuf->byte_length;
- if (ta->offset > len)
- return TRUE;
- if (ta->track_rab)
- return FALSE;
- if (len < (int64_t)ta->offset + ta->length)
- return TRUE;
- size_elem = 1 << typed_array_size_log2(p->class_id);
- end = (int64_t)ta->offset + (int64_t)p->u.array.count * size_elem;
- return end > len;
- }
- // Be *very* careful if you touch the typed array's memory directly:
- // the length is only valid until the next call into JS land because
- // JS code can detach or resize the backing array buffer. Functions
- // like JS_GetProperty and JS_ToIndex call JS code.
- //
- // Exclusively reading or writing elements with JS_GetProperty,
- // JS_GetPropertyInt64, JS_SetProperty, etc. is safe because they
- // perform bounds checks, as does js_get_fast_array_element.
- static int js_typed_array_get_length_unsafe(JSContext *ctx, JSValueConst obj)
- {
- JSObject *p;
- p = get_typed_array(ctx, obj);
- if (!p)
- return -1;
- if (typed_array_is_oob(p)) {
- JS_ThrowTypeErrorArrayBufferOOB(ctx);
- return -1;
- }
- return p->u.array.count;
- }
- static int validate_typed_array(JSContext *ctx, JSValueConst this_val)
- {
- JSObject *p;
- p = get_typed_array(ctx, this_val);
- if (!p)
- return -1;
- if (typed_array_is_oob(p)) {
- JS_ThrowTypeErrorArrayBufferOOB(ctx);
- return -1;
- }
- return 0;
- }
- static JSValue js_typed_array_get_length(JSContext *ctx,
- JSValueConst this_val)
- {
- JSObject *p;
- p = get_typed_array(ctx, this_val);
- if (!p)
- return JS_EXCEPTION;
- return JS_NewInt32(ctx, p->u.array.count);
- }
- static JSValue js_typed_array_get_buffer(JSContext *ctx,
- JSValueConst this_val)
- {
- JSObject *p;
- JSTypedArray *ta;
- p = get_typed_array(ctx, this_val);
- if (!p)
- return JS_EXCEPTION;
- ta = p->u.typed_array;
- return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, ta->buffer));
- }
- static JSValue js_typed_array_get_byteLength(JSContext *ctx,
- JSValueConst this_val)
- {
- JSObject *p;
- JSTypedArray *ta;
- int size_log2;
- p = get_typed_array(ctx, this_val);
- if (!p)
- return JS_EXCEPTION;
- if (typed_array_is_oob(p))
- return JS_NewInt32(ctx, 0);
- ta = p->u.typed_array;
- if (!ta->track_rab)
- return JS_NewUint32(ctx, ta->length);
- size_log2 = typed_array_size_log2(p->class_id);
- return JS_NewInt64(ctx, (int64_t)p->u.array.count << size_log2);
- }
- static JSValue js_typed_array_get_byteOffset(JSContext *ctx,
- JSValueConst this_val)
- {
- JSObject *p;
- JSTypedArray *ta;
- p = get_typed_array(ctx, this_val);
- if (!p)
- return JS_EXCEPTION;
- if (typed_array_is_oob(p))
- return JS_NewInt32(ctx, 0);
- ta = p->u.typed_array;
- return JS_NewUint32(ctx, ta->offset);
- }
- JSValue JS_NewTypedArray(JSContext *ctx, int argc, JSValueConst *argv,
- JSTypedArrayEnum type)
- {
- if (type < JS_TYPED_ARRAY_UINT8C || type > JS_TYPED_ARRAY_FLOAT64)
- return JS_ThrowRangeError(ctx, "invalid typed array type");
- return js_typed_array_constructor(ctx, JS_UNDEFINED, argc, argv,
- JS_CLASS_UINT8C_ARRAY + type);
- }
- /* Return the buffer associated to the typed array or an exception if
- it is not a typed array or if the buffer is detached. pbyte_offset,
- pbyte_length or pbytes_per_element can be NULL. */
- JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValueConst obj,
- size_t *pbyte_offset,
- size_t *pbyte_length,
- size_t *pbytes_per_element)
- {
- JSObject *p;
- JSTypedArray *ta;
- p = get_typed_array(ctx, obj);
- if (!p)
- return JS_EXCEPTION;
- if (typed_array_is_oob(p))
- return JS_ThrowTypeErrorArrayBufferOOB(ctx);
- ta = p->u.typed_array;
- if (pbyte_offset)
- *pbyte_offset = ta->offset;
- if (pbyte_length)
- *pbyte_length = ta->length;
- if (pbytes_per_element) {
- *pbytes_per_element = 1 << typed_array_size_log2(p->class_id);
- }
- return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, ta->buffer));
- }
- static JSValue js_typed_array_get_toStringTag(JSContext *ctx,
- JSValueConst this_val)
- {
- JSObject *p;
- if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
- return JS_UNDEFINED;
- p = JS_VALUE_GET_OBJ(this_val);
- if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY &&
- p->class_id <= JS_CLASS_FLOAT64_ARRAY))
- return JS_UNDEFINED;
- return JS_AtomToString(ctx, ctx->rt->class_array[p->class_id].class_name);
- }
- static JSValue js_typed_array_set_internal(JSContext *ctx,
- JSValueConst dst,
- JSValueConst src,
- JSValueConst off)
- {
- JSObject *p;
- JSObject *src_p;
- uint32_t i;
- int64_t dst_len, src_len, offset;
- JSValue val, src_obj = JS_UNDEFINED;
- p = get_typed_array(ctx, dst);
- if (!p)
- goto fail;
- if (JS_ToInt64Sat(ctx, &offset, off))
- goto fail;
- if (offset < 0)
- goto range_error;
- if (typed_array_is_oob(p)) {
- detached:
- JS_ThrowTypeErrorArrayBufferOOB(ctx);
- goto fail;
- }
- dst_len = p->u.array.count;
- src_obj = JS_ToObject(ctx, src);
- if (JS_IsException(src_obj))
- goto fail;
- src_p = JS_VALUE_GET_OBJ(src_obj);
- if (src_p->class_id >= JS_CLASS_UINT8C_ARRAY &&
- src_p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
- JSTypedArray *dest_ta = p->u.typed_array;
- JSArrayBuffer *dest_abuf = dest_ta->buffer->u.array_buffer;
- JSTypedArray *src_ta = src_p->u.typed_array;
- JSArrayBuffer *src_abuf = src_ta->buffer->u.array_buffer;
- int shift = typed_array_size_log2(p->class_id);
- if (typed_array_is_oob(src_p))
- goto detached;
- src_len = src_p->u.array.count;
- if (offset > dst_len - src_len)
- goto range_error;
- /* copying between typed objects */
- if (src_p->class_id == p->class_id) {
- /* same type, use memmove */
- memmove(dest_abuf->data + dest_ta->offset + (offset << shift),
- src_abuf->data + src_ta->offset, src_len << shift);
- goto done;
- }
- if (dest_abuf->data == src_abuf->data) {
- /* copying between the same buffer using different types of mappings
- would require a temporary buffer */
- }
- /* otherwise, default behavior is slow but correct */
- } else {
- // can change |dst| as a side effect; per spec,
- // perform the range check against its old length
- if (js_get_length64(ctx, &src_len, src_obj))
- goto fail;
- if (offset > dst_len - src_len) {
- range_error:
- JS_ThrowRangeError(ctx, "invalid array length");
- goto fail;
- }
- }
- for(i = 0; i < src_len; i++) {
- val = JS_GetPropertyUint32(ctx, src_obj, i);
- if (JS_IsException(val))
- goto fail;
- if (JS_SetPropertyUint32(ctx, dst, offset + i, val) < 0)
- goto fail;
- }
- done:
- JS_FreeValue(ctx, src_obj);
- return JS_UNDEFINED;
- fail:
- JS_FreeValue(ctx, src_obj);
- return JS_EXCEPTION;
- }
- static JSValue js_typed_array_at(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSObject *p;
- int64_t idx, len;
- p = get_typed_array(ctx, this_val);
- if (!p)
- return JS_EXCEPTION;
- if (typed_array_is_oob(p))
- return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
- len = p->u.array.count;
- // note: can change p->u.array.count
- if (JS_ToInt64Sat(ctx, &idx, argv[0]))
- return JS_EXCEPTION;
- if (idx < 0)
- idx = len + idx;
- len = p->u.array.count;
- if (idx < 0 || idx >= len)
- return JS_UNDEFINED;
- return JS_GetPropertyInt64(ctx, this_val, idx);
- }
- static JSValue js_typed_array_with(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue arr, val;
- JSObject *p;
- int64_t idx, len;
- p = get_typed_array(ctx, this_val);
- if (!p)
- return JS_EXCEPTION;
- if (typed_array_is_oob(p))
- return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
- len = p->u.array.count;
- if (JS_ToInt64Sat(ctx, &idx, argv[0]))
- return JS_EXCEPTION;
- if (idx < 0)
- idx = len + idx;
- val = JS_ToPrimitive(ctx, argv[1], HINT_NUMBER);
- if (JS_IsException(val))
- return JS_EXCEPTION;
- if (typed_array_is_oob(p) || idx < 0 || idx >= p->u.array.count)
- return JS_ThrowRangeError(ctx, "invalid array index");
- /* warning: 'this_val' may have been resized, so 'len' may be
- larger than its length */
- arr = js_typed_array_constructor_ta(ctx, JS_UNDEFINED, this_val,
- p->class_id, len);
- if (JS_IsException(arr)) {
- JS_FreeValue(ctx, val);
- return JS_EXCEPTION;
- }
- if (JS_SetPropertyInt64(ctx, arr, idx, val) < 0) {
- JS_FreeValue(ctx, arr);
- return JS_EXCEPTION;
- }
- return arr;
- }
- static JSValue js_typed_array_set(JSContext *ctx,
- JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValueConst offset = JS_UNDEFINED;
- if (argc > 1) {
- offset = argv[1];
- }
- return js_typed_array_set_internal(ctx, this_val, argv[0], offset);
- }
- static JSValue js_create_typed_array_iterator(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int magic)
- {
- if (validate_typed_array(ctx, this_val))
- return JS_EXCEPTION;
- return js_create_array_iterator(ctx, this_val, argc, argv, magic);
- }
- static JSValue js_typed_array_create(JSContext *ctx, JSValueConst ctor,
- int argc, JSValueConst *argv)
- {
- JSValue ret;
- int new_len;
- int64_t len;
- ret = JS_CallConstructor(ctx, ctor, argc, argv);
- if (JS_IsException(ret))
- return ret;
- /* validate the typed array */
- new_len = js_typed_array_get_length_unsafe(ctx, ret);
- if (new_len < 0)
- goto fail;
- if (argc == 1) {
- /* ensure that it is large enough */
- if (JS_ToLengthFree(ctx, &len, JS_DupValue(ctx, argv[0])))
- goto fail;
- if (new_len < len) {
- JS_ThrowTypeError(ctx, "TypedArray length is too small");
- fail:
- JS_FreeValue(ctx, ret);
- return JS_EXCEPTION;
- }
- }
- return ret;
- }
- #if 0
- static JSValue js_typed_array___create(JSContext *ctx,
- JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- return js_typed_array_create(ctx, argv[0], max_int(argc - 1, 0), argv + 1);
- }
- #endif
- static JSValue js_typed_array___speciesCreate(JSContext *ctx,
- JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValueConst obj;
- JSObject *p;
- JSValue ctor, ret;
- int argc1;
- obj = argv[0];
- p = get_typed_array(ctx, obj);
- if (!p)
- return JS_EXCEPTION;
- ctor = JS_SpeciesConstructor(ctx, obj, JS_UNDEFINED);
- if (JS_IsException(ctor))
- return ctor;
- argc1 = max_int(argc - 1, 0);
- if (JS_IsUndefined(ctor)) {
- ret = js_typed_array_constructor(ctx, JS_UNDEFINED, argc1, argv + 1,
- p->class_id);
- } else {
- ret = js_typed_array_create(ctx, ctor, argc1, argv + 1);
- JS_FreeValue(ctx, ctor);
- }
- return ret;
- }
- static JSValue js_typed_array_from(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- // from(items, mapfn = void 0, this_arg = void 0)
- JSValueConst items = argv[0], mapfn, this_arg;
- JSValueConst args[2];
- JSValue iter, arr, r, v, v2;
- int64_t k, len;
- int mapping;
- mapping = FALSE;
- mapfn = JS_UNDEFINED;
- this_arg = JS_UNDEFINED;
- r = JS_UNDEFINED;
- arr = JS_UNDEFINED;
- iter = JS_UNDEFINED;
- if (argc > 1) {
- mapfn = argv[1];
- if (!JS_IsUndefined(mapfn)) {
- if (check_function(ctx, mapfn))
- goto exception;
- mapping = 1;
- if (argc > 2)
- this_arg = argv[2];
- }
- }
- iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator);
- if (JS_IsException(iter))
- goto exception;
- if (!JS_IsUndefined(iter) && !JS_IsNull(iter)) {
- uint32_t len1;
- if (!JS_IsFunction(ctx, iter)) {
- JS_ThrowTypeError(ctx, "value is not iterable");
- goto exception;
- }
- arr = js_array_from_iterator(ctx, &len1, items, iter);
- if (JS_IsException(arr))
- goto exception;
- len = len1;
- } else {
- arr = JS_ToObject(ctx, items);
- if (JS_IsException(arr))
- goto exception;
- if (js_get_length64(ctx, &len, arr) < 0)
- goto exception;
- }
- v = JS_NewInt64(ctx, len);
- args[0] = v;
- r = js_typed_array_create(ctx, this_val, 1, args);
- JS_FreeValue(ctx, v);
- if (JS_IsException(r))
- goto exception;
- for(k = 0; k < len; k++) {
- v = JS_GetPropertyInt64(ctx, arr, k);
- if (JS_IsException(v))
- goto exception;
- if (mapping) {
- args[0] = v;
- args[1] = JS_NewInt32(ctx, k);
- v2 = JS_Call(ctx, mapfn, this_arg, 2, args);
- JS_FreeValue(ctx, v);
- v = v2;
- if (JS_IsException(v))
- goto exception;
- }
- if (JS_SetPropertyInt64(ctx, r, k, v) < 0)
- goto exception;
- }
- goto done;
- exception:
- JS_FreeValue(ctx, r);
- r = JS_EXCEPTION;
- done:
- JS_FreeValue(ctx, arr);
- JS_FreeValue(ctx, iter);
- return r;
- }
- static JSValue js_typed_array_of(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue obj;
- JSValueConst args[1];
- int i;
- args[0] = JS_NewInt32(ctx, argc);
- obj = js_typed_array_create(ctx, this_val, 1, args);
- if (JS_IsException(obj))
- return obj;
- for(i = 0; i < argc; i++) {
- if (JS_SetPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i])) < 0) {
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- }
- return obj;
- }
- static JSValue js_typed_array_copyWithin(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSObject *p;
- int len, to, from, final, count, shift, space;
- p = get_typed_array(ctx, this_val);
- if (!p)
- return JS_EXCEPTION;
- if (typed_array_is_oob(p))
- return JS_ThrowTypeErrorArrayBufferOOB(ctx);
- len = p->u.array.count;
- if (JS_ToInt32Clamp(ctx, &to, argv[0], 0, len, len))
- return JS_EXCEPTION;
- if (JS_ToInt32Clamp(ctx, &from, argv[1], 0, len, len))
- return JS_EXCEPTION;
- final = len;
- if (argc > 2 && !JS_IsUndefined(argv[2])) {
- if (JS_ToInt32Clamp(ctx, &final, argv[2], 0, len, len))
- return JS_EXCEPTION;
- }
- if (typed_array_is_oob(p))
- return JS_ThrowTypeErrorArrayBufferOOB(ctx);
- // RAB may have been resized by evil .valueOf method
- space = p->u.array.count - max_int(to, from);
- count = min_int(final - from, len - to);
- count = min_int(count, space);
- if (count > 0) {
- shift = typed_array_size_log2(p->class_id);
- memmove(p->u.array.u.uint8_ptr + (to << shift),
- p->u.array.u.uint8_ptr + (from << shift),
- count << shift);
- }
- return JS_DupValue(ctx, this_val);
- }
- static JSValue js_typed_array_fill(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSObject *p;
- int len, k, final, shift;
- uint64_t v64;
- p = get_typed_array(ctx, this_val);
- if (!p)
- return JS_EXCEPTION;
- if (typed_array_is_oob(p))
- return JS_ThrowTypeErrorArrayBufferOOB(ctx);
- len = p->u.array.count;
- if (p->class_id == JS_CLASS_UINT8C_ARRAY) {
- int32_t v;
- if (JS_ToUint8ClampFree(ctx, &v, JS_DupValue(ctx, argv[0])))
- return JS_EXCEPTION;
- v64 = v;
- } else if (p->class_id <= JS_CLASS_UINT32_ARRAY) {
- uint32_t v;
- if (JS_ToUint32(ctx, &v, argv[0]))
- return JS_EXCEPTION;
- v64 = v;
- } else if (p->class_id <= JS_CLASS_BIG_UINT64_ARRAY) {
- if (JS_ToBigInt64(ctx, (int64_t *)&v64, argv[0]))
- return JS_EXCEPTION;
- } else {
- double d;
- if (JS_ToFloat64(ctx, &d, argv[0]))
- return JS_EXCEPTION;
- if (p->class_id == JS_CLASS_FLOAT16_ARRAY) {
- v64 = tofp16(d);
- } else if (p->class_id == JS_CLASS_FLOAT32_ARRAY) {
- union {
- float f;
- uint32_t u32;
- } u;
- u.f = d;
- v64 = u.u32;
- } else {
- JSFloat64Union u;
- u.d = d;
- v64 = u.u64;
- }
- }
- k = 0;
- if (argc > 1) {
- if (JS_ToInt32Clamp(ctx, &k, argv[1], 0, len, len))
- return JS_EXCEPTION;
- }
- final = len;
- if (argc > 2 && !JS_IsUndefined(argv[2])) {
- if (JS_ToInt32Clamp(ctx, &final, argv[2], 0, len, len))
- return JS_EXCEPTION;
- }
- if (typed_array_is_oob(p))
- return JS_ThrowTypeErrorArrayBufferOOB(ctx);
- // RAB may have been resized by evil .valueOf method
- final = min_int(final, p->u.array.count);
- shift = typed_array_size_log2(p->class_id);
- switch(shift) {
- case 0:
- if (k < final) {
- memset(p->u.array.u.uint8_ptr + k, v64, final - k);
- }
- break;
- case 1:
- for(; k < final; k++) {
- p->u.array.u.uint16_ptr[k] = v64;
- }
- break;
- case 2:
- for(; k < final; k++) {
- p->u.array.u.uint32_ptr[k] = v64;
- }
- break;
- case 3:
- for(; k < final; k++) {
- p->u.array.u.uint64_ptr[k] = v64;
- }
- break;
- default:
- abort();
- }
- return JS_DupValue(ctx, this_val);
- }
- static JSValue js_typed_array_find(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int mode)
- {
- JSValueConst func, this_arg;
- JSValueConst args[3];
- JSValue val, index_val, res;
- int len, k, end;
- int dir;
- val = JS_UNDEFINED;
- len = js_typed_array_get_length_unsafe(ctx, this_val);
- if (len < 0)
- goto exception;
- func = argv[0];
- if (check_function(ctx, func))
- goto exception;
- this_arg = JS_UNDEFINED;
- if (argc > 1)
- this_arg = argv[1];
- k = 0;
- dir = 1;
- end = len;
- if (mode == ArrayFindLast || mode == ArrayFindLastIndex) {
- k = len - 1;
- dir = -1;
- end = -1;
- }
- for(; k != end; k += dir) {
- index_val = JS_NewInt32(ctx, k);
- val = JS_GetPropertyValue(ctx, this_val, index_val);
- if (JS_IsException(val))
- goto exception;
- args[0] = val;
- args[1] = index_val;
- args[2] = this_val;
- res = JS_Call(ctx, func, this_arg, 3, args);
- if (JS_IsException(res))
- goto exception;
- if (JS_ToBoolFree(ctx, res)) {
- if (mode == ArrayFindIndex || mode == ArrayFindLastIndex) {
- JS_FreeValue(ctx, val);
- return index_val;
- } else {
- return val;
- }
- }
- JS_FreeValue(ctx, val);
- }
- if (mode == ArrayFindIndex || mode == ArrayFindLastIndex)
- return JS_NewInt32(ctx, -1);
- else
- return JS_UNDEFINED;
- exception:
- JS_FreeValue(ctx, val);
- return JS_EXCEPTION;
- }
- #define special_indexOf 0
- #define special_lastIndexOf 1
- #define special_includes -1
- static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int special)
- {
- JSObject *p;
- int len, tag, is_int, is_bigint, k, stop, inc, res = -1;
- int64_t v64;
- double d;
- float f;
- uint16_t hf;
- p = get_typed_array(ctx, this_val);
- if (!p)
- return JS_EXCEPTION;
- if (typed_array_is_oob(p))
- return JS_ThrowTypeErrorArrayBufferOOB(ctx);
- len = p->u.array.count;
- if (len == 0)
- goto done;
- if (special == special_lastIndexOf) {
- k = len - 1;
- if (argc > 1) {
- int64_t k1;
- if (JS_ToInt64Clamp(ctx, &k1, argv[1], -1, len - 1, len))
- goto exception;
- k = k1;
- if (k < 0)
- goto done;
- }
- stop = -1;
- inc = -1;
- } else {
- k = 0;
- if (argc > 1) {
- if (JS_ToInt32Clamp(ctx, &k, argv[1], 0, len, len))
- goto exception;
- }
- stop = len;
- inc = 1;
- }
- /* includes function: 'undefined' can be found if searching out of bounds */
- if (len > p->u.array.count && special == special_includes &&
- JS_IsUndefined(argv[0]) && k < len) {
- res = 0;
- goto done;
- }
- // RAB may have been resized by evil .valueOf method
- len = min_int(len, p->u.array.count);
- if (len == 0)
- goto done;
- if (special == special_lastIndexOf)
- k = min_int(k, len - 1);
- else
- k = min_int(k, len);
- stop = min_int(stop, len);
- is_bigint = 0;
- is_int = 0; /* avoid warning */
- v64 = 0; /* avoid warning */
- tag = JS_VALUE_GET_NORM_TAG(argv[0]);
- if (tag == JS_TAG_INT) {
- is_int = 1;
- v64 = JS_VALUE_GET_INT(argv[0]);
- d = v64;
- } else
- if (tag == JS_TAG_FLOAT64) {
- d = JS_VALUE_GET_FLOAT64(argv[0]);
- if (d >= INT64_MIN && d < 0x1p63) {
- v64 = d;
- is_int = (v64 == d);
- }
- } else if (tag == JS_TAG_BIG_INT || tag == JS_TAG_SHORT_BIG_INT) {
- JSBigIntBuf buf1;
- JSBigInt *p1;
- int sz = (64 / JS_LIMB_BITS);
- if (tag == JS_TAG_SHORT_BIG_INT)
- p1 = js_bigint_set_short(&buf1, argv[0]);
- else
- p1 = JS_VALUE_GET_PTR(argv[0]);
-
- if (p->class_id == JS_CLASS_BIG_INT64_ARRAY) {
- if (p1->len > sz)
- goto done; /* does not fit an int64 : cannot be found */
- } else if (p->class_id == JS_CLASS_BIG_UINT64_ARRAY) {
- if (js_bigint_sign(p1))
- goto done; /* v < 0 */
- if (p1->len <= sz) {
- /* OK */
- } else if (p1->len == sz + 1 && p1->tab[sz] == 0) {
- /* 2^63 <= v <= 2^64-1 */
- } else {
- goto done;
- }
- } else {
- goto done;
- }
- if (JS_ToBigInt64(ctx, &v64, argv[0]))
- goto exception;
- d = 0;
- is_bigint = 1;
- } else {
- goto done;
- }
- switch (p->class_id) {
- case JS_CLASS_INT8_ARRAY:
- if (is_int && (int8_t)v64 == v64)
- goto scan8;
- break;
- case JS_CLASS_UINT8C_ARRAY:
- case JS_CLASS_UINT8_ARRAY:
- if (is_int && (uint8_t)v64 == v64) {
- const uint8_t *pv, *pp;
- uint16_t v;
- scan8:
- pv = p->u.array.u.uint8_ptr;
- v = v64;
- if (inc > 0) {
- pp = NULL;
- if (pv)
- pp = memchr(pv + k, v, len - k);
- if (pp)
- res = pp - pv;
- } else {
- for (; k != stop; k += inc) {
- if (pv[k] == v) {
- res = k;
- break;
- }
- }
- }
- }
- break;
- case JS_CLASS_INT16_ARRAY:
- if (is_int && (int16_t)v64 == v64)
- goto scan16;
- break;
- case JS_CLASS_UINT16_ARRAY:
- if (is_int && (uint16_t)v64 == v64) {
- const uint16_t *pv;
- uint16_t v;
- scan16:
- pv = p->u.array.u.uint16_ptr;
- v = v64;
- for (; k != stop; k += inc) {
- if (pv[k] == v) {
- res = k;
- break;
- }
- }
- }
- break;
- case JS_CLASS_INT32_ARRAY:
- if (is_int && (int32_t)v64 == v64)
- goto scan32;
- break;
- case JS_CLASS_UINT32_ARRAY:
- if (is_int && (uint32_t)v64 == v64) {
- const uint32_t *pv;
- uint32_t v;
- scan32:
- pv = p->u.array.u.uint32_ptr;
- v = v64;
- for (; k != stop; k += inc) {
- if (pv[k] == v) {
- res = k;
- break;
- }
- }
- }
- break;
- case JS_CLASS_FLOAT16_ARRAY:
- if (is_bigint)
- break;
- if (isnan(d)) {
- const uint16_t *pv = p->u.array.u.fp16_ptr;
- /* special case: indexOf returns -1, includes finds NaN */
- if (special != special_includes)
- goto done;
- for (; k != stop; k += inc) {
- if (isfp16nan(pv[k])) {
- res = k;
- break;
- }
- }
- } else if (d == 0) {
- // special case: includes also finds negative zero
- const uint16_t *pv = p->u.array.u.fp16_ptr;
- for (; k != stop; k += inc) {
- if (isfp16zero(pv[k])) {
- res = k;
- break;
- }
- }
- } else if (hf = tofp16(d), d == fromfp16(hf)) {
- const uint16_t *pv = p->u.array.u.fp16_ptr;
- for (; k != stop; k += inc) {
- if (pv[k] == hf) {
- res = k;
- break;
- }
- }
- }
- break;
- case JS_CLASS_FLOAT32_ARRAY:
- if (is_bigint)
- break;
- if (isnan(d)) {
- const float *pv = p->u.array.u.float_ptr;
- /* special case: indexOf returns -1, includes finds NaN */
- if (special != special_includes)
- goto done;
- for (; k != stop; k += inc) {
- if (isnan(pv[k])) {
- res = k;
- break;
- }
- }
- } else if ((f = (float)d) == d) {
- const float *pv = p->u.array.u.float_ptr;
- for (; k != stop; k += inc) {
- if (pv[k] == f) {
- res = k;
- break;
- }
- }
- }
- break;
- case JS_CLASS_FLOAT64_ARRAY:
- if (is_bigint)
- break;
- if (isnan(d)) {
- const double *pv = p->u.array.u.double_ptr;
- /* special case: indexOf returns -1, includes finds NaN */
- if (special != special_includes)
- goto done;
- for (; k != stop; k += inc) {
- if (isnan(pv[k])) {
- res = k;
- break;
- }
- }
- } else {
- const double *pv = p->u.array.u.double_ptr;
- for (; k != stop; k += inc) {
- if (pv[k] == d) {
- res = k;
- break;
- }
- }
- }
- break;
- case JS_CLASS_BIG_INT64_ARRAY:
- if (is_bigint) {
- goto scan64;
- }
- break;
- case JS_CLASS_BIG_UINT64_ARRAY:
- if (is_bigint) {
- const uint64_t *pv;
- uint64_t v;
- scan64:
- pv = p->u.array.u.uint64_ptr;
- v = v64;
- for (; k != stop; k += inc) {
- if (pv[k] == v) {
- res = k;
- break;
- }
- }
- }
- break;
- }
- done:
- if (special == special_includes)
- return JS_NewBool(ctx, res >= 0);
- else
- return JS_NewInt32(ctx, res);
- exception:
- return JS_EXCEPTION;
- }
- static JSValue js_typed_array_join(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv, int toLocaleString)
- {
- JSValue sep = JS_UNDEFINED, el;
- StringBuffer b_s, *b = &b_s;
- JSString *s = NULL;
- JSObject *p;
- int i, len, oldlen, newlen;
- int c;
- p = get_typed_array(ctx, this_val);
- if (!p)
- return JS_EXCEPTION;
- if (typed_array_is_oob(p))
- return JS_ThrowTypeErrorArrayBufferOOB(ctx);
- len = oldlen = newlen = p->u.array.count;
- c = ','; /* default separator */
- if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) {
- sep = JS_ToString(ctx, argv[0]);
- if (JS_IsException(sep))
- goto exception;
- s = JS_VALUE_GET_STRING(sep);
- if (s->len == 1 && !s->is_wide_char)
- c = s->u.str8[0];
- else
- c = -1;
- // ToString(sep) can detach or resize the arraybuffer as a side effect
- newlen = p->u.array.count;
- len = min_int(len, newlen);
- }
- string_buffer_init(ctx, b, 0);
- /* XXX: optimize with direct access */
- for(i = 0; i < len; i++) {
- if (i > 0) {
- if (c >= 0) {
- if (string_buffer_putc8(b, c))
- goto fail;
- } else {
- if (string_buffer_concat(b, s, 0, s->len))
- goto fail;
- }
- }
- el = JS_GetPropertyUint32(ctx, this_val, i);
- /* Can return undefined for example if the typed array is detached */
- if (!JS_IsNull(el) && !JS_IsUndefined(el)) {
- if (JS_IsException(el))
- goto fail;
- if (toLocaleString) {
- el = JS_ToLocaleStringFree(ctx, el);
- }
- if (string_buffer_concat_value_free(b, el))
- goto fail;
- }
- }
- // add extra separators in case RAB was resized by evil .valueOf method
- i = max_int(1, newlen);
- for(/*empty*/; i < oldlen; i++) {
- if (c >= 0) {
- if (string_buffer_putc8(b, c))
- goto fail;
- } else {
- if (string_buffer_concat(b, s, 0, s->len))
- goto fail;
- }
- }
- JS_FreeValue(ctx, sep);
- return string_buffer_end(b);
- fail:
- string_buffer_free(b);
- JS_FreeValue(ctx, sep);
- exception:
- return JS_EXCEPTION;
- }
- static JSValue js_typed_array_reverse(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSObject *p;
- int len;
- len = js_typed_array_get_length_unsafe(ctx, this_val);
- if (len < 0)
- return JS_EXCEPTION;
- if (len > 0) {
- p = JS_VALUE_GET_OBJ(this_val);
- switch (typed_array_size_log2(p->class_id)) {
- case 0:
- {
- uint8_t *p1 = p->u.array.u.uint8_ptr;
- uint8_t *p2 = p1 + len - 1;
- while (p1 < p2) {
- uint8_t v = *p1;
- *p1++ = *p2;
- *p2-- = v;
- }
- }
- break;
- case 1:
- {
- uint16_t *p1 = p->u.array.u.uint16_ptr;
- uint16_t *p2 = p1 + len - 1;
- while (p1 < p2) {
- uint16_t v = *p1;
- *p1++ = *p2;
- *p2-- = v;
- }
- }
- break;
- case 2:
- {
- uint32_t *p1 = p->u.array.u.uint32_ptr;
- uint32_t *p2 = p1 + len - 1;
- while (p1 < p2) {
- uint32_t v = *p1;
- *p1++ = *p2;
- *p2-- = v;
- }
- }
- break;
- case 3:
- {
- uint64_t *p1 = p->u.array.u.uint64_ptr;
- uint64_t *p2 = p1 + len - 1;
- while (p1 < p2) {
- uint64_t v = *p1;
- *p1++ = *p2;
- *p2-- = v;
- }
- }
- break;
- default:
- abort();
- }
- }
- return JS_DupValue(ctx, this_val);
- }
- static JSValue js_typed_array_toReversed(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue arr, ret;
- JSObject *p;
- p = get_typed_array(ctx, this_val);
- if (!p)
- return JS_EXCEPTION;
- arr = js_typed_array_constructor_ta(ctx, JS_UNDEFINED, this_val,
- p->class_id, p->u.array.count);
- if (JS_IsException(arr))
- return JS_EXCEPTION;
- ret = js_typed_array_reverse(ctx, arr, argc, argv);
- JS_FreeValue(ctx, arr);
- return ret;
- }
- static void slice_memcpy(uint8_t *dst, const uint8_t *src, size_t len)
- {
- if (dst + len <= src || dst >= src + len) {
- /* no overlap: can use memcpy */
- memcpy(dst, src, len);
- } else {
- /* otherwise the spec mandates byte copy */
- while (len-- != 0)
- *dst++ = *src++;
- }
- }
- static JSValue js_typed_array_slice(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValueConst args[2];
- JSValue arr, val;
- JSObject *p, *p1;
- int n, len, start, final, count, shift, space;
- arr = JS_UNDEFINED;
- p = get_typed_array(ctx, this_val);
- if (!p)
- goto exception;
- if (typed_array_is_oob(p))
- return JS_ThrowTypeErrorArrayBufferOOB(ctx);
- len = p->u.array.count;
- if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len))
- goto exception;
- final = len;
- if (!JS_IsUndefined(argv[1])) {
- if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len))
- goto exception;
- }
- count = max_int(final - start, 0);
- shift = typed_array_size_log2(p->class_id);
- args[0] = this_val;
- args[1] = JS_NewInt32(ctx, count);
- arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args);
- if (JS_IsException(arr))
- goto exception;
- if (count > 0) {
- if (validate_typed_array(ctx, this_val)
- || validate_typed_array(ctx, arr))
- goto exception;
- p1 = get_typed_array(ctx, arr);
- space = max_int(0, p->u.array.count - start);
- count = min_int(count, space);
- if (p1 != NULL && p->class_id == p1->class_id) {
- slice_memcpy(p1->u.array.u.uint8_ptr,
- p->u.array.u.uint8_ptr + (start << shift),
- count << shift);
- } else {
- for (n = 0; n < count; n++) {
- val = JS_GetPropertyValue(ctx, this_val, JS_NewInt32(ctx, start + n));
- if (JS_IsException(val))
- goto exception;
- if (JS_SetPropertyValue(ctx, arr, JS_NewInt32(ctx, n), val,
- JS_PROP_THROW) < 0)
- goto exception;
- }
- }
- }
- return arr;
- exception:
- JS_FreeValue(ctx, arr);
- return JS_EXCEPTION;
- }
- static JSValue js_typed_array_subarray(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValueConst args[4];
- JSValue arr, ta_buffer;
- JSTypedArray *ta;
- JSObject *p;
- int len, start, final, count, shift, offset;
- BOOL is_auto;
-
- p = get_typed_array(ctx, this_val);
- if (!p)
- goto exception;
- len = p->u.array.count;
- if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len))
- goto exception;
- shift = typed_array_size_log2(p->class_id);
- ta = p->u.typed_array;
- /* Read byteOffset (ta->offset) even if detached */
- offset = ta->offset + (start << shift);
- final = len;
- if (JS_IsUndefined(argv[1])) {
- is_auto = ta->track_rab;
- } else {
- is_auto = FALSE;
- if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len))
- goto exception;
- }
- count = max_int(final - start, 0);
- ta_buffer = js_typed_array_get_buffer(ctx, this_val);
- if (JS_IsException(ta_buffer))
- goto exception;
- args[0] = this_val;
- args[1] = ta_buffer;
- args[2] = JS_NewInt32(ctx, offset);
- args[3] = JS_NewInt32(ctx, count);
- arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, is_auto ? 3 : 4, args);
- JS_FreeValue(ctx, ta_buffer);
- return arr;
- exception:
- return JS_EXCEPTION;
- }
- /* TypedArray.prototype.sort */
- static int js_cmp_doubles(double x, double y)
- {
- if (isnan(x)) return isnan(y) ? 0 : +1;
- if (isnan(y)) return -1;
- if (x < y) return -1;
- if (x > y) return 1;
- if (x != 0) return 0;
- if (signbit(x)) return signbit(y) ? 0 : -1;
- else return signbit(y) ? 1 : 0;
- }
- static int js_TA_cmp_int8(const void *a, const void *b, void *opaque) {
- return *(const int8_t *)a - *(const int8_t *)b;
- }
- static int js_TA_cmp_uint8(const void *a, const void *b, void *opaque) {
- return *(const uint8_t *)a - *(const uint8_t *)b;
- }
- static int js_TA_cmp_int16(const void *a, const void *b, void *opaque) {
- return *(const int16_t *)a - *(const int16_t *)b;
- }
- static int js_TA_cmp_uint16(const void *a, const void *b, void *opaque) {
- return *(const uint16_t *)a - *(const uint16_t *)b;
- }
- static int js_TA_cmp_int32(const void *a, const void *b, void *opaque) {
- int32_t x = *(const int32_t *)a;
- int32_t y = *(const int32_t *)b;
- return (y < x) - (y > x);
- }
- static int js_TA_cmp_uint32(const void *a, const void *b, void *opaque) {
- uint32_t x = *(const uint32_t *)a;
- uint32_t y = *(const uint32_t *)b;
- return (y < x) - (y > x);
- }
- static int js_TA_cmp_int64(const void *a, const void *b, void *opaque) {
- int64_t x = *(const int64_t *)a;
- int64_t y = *(const int64_t *)b;
- return (y < x) - (y > x);
- }
- static int js_TA_cmp_uint64(const void *a, const void *b, void *opaque) {
- uint64_t x = *(const uint64_t *)a;
- uint64_t y = *(const uint64_t *)b;
- return (y < x) - (y > x);
- }
- static int js_TA_cmp_float16(const void *a, const void *b, void *opaque) {
- return js_cmp_doubles(fromfp16(*(const uint16_t *)a),
- fromfp16(*(const uint16_t *)b));
- }
- static int js_TA_cmp_float32(const void *a, const void *b, void *opaque) {
- return js_cmp_doubles(*(const float *)a, *(const float *)b);
- }
- static int js_TA_cmp_float64(const void *a, const void *b, void *opaque) {
- return js_cmp_doubles(*(const double *)a, *(const double *)b);
- }
- static JSValue js_TA_get_int8(JSContext *ctx, const void *a) {
- return JS_NewInt32(ctx, *(const int8_t *)a);
- }
- static JSValue js_TA_get_uint8(JSContext *ctx, const void *a) {
- return JS_NewInt32(ctx, *(const uint8_t *)a);
- }
- static JSValue js_TA_get_int16(JSContext *ctx, const void *a) {
- return JS_NewInt32(ctx, *(const int16_t *)a);
- }
- static JSValue js_TA_get_uint16(JSContext *ctx, const void *a) {
- return JS_NewInt32(ctx, *(const uint16_t *)a);
- }
- static JSValue js_TA_get_int32(JSContext *ctx, const void *a) {
- return JS_NewInt32(ctx, *(const int32_t *)a);
- }
- static JSValue js_TA_get_uint32(JSContext *ctx, const void *a) {
- return JS_NewUint32(ctx, *(const uint32_t *)a);
- }
- static JSValue js_TA_get_int64(JSContext *ctx, const void *a) {
- return JS_NewBigInt64(ctx, *(int64_t *)a);
- }
- static JSValue js_TA_get_uint64(JSContext *ctx, const void *a) {
- return JS_NewBigUint64(ctx, *(uint64_t *)a);
- }
- static JSValue js_TA_get_float16(JSContext *ctx, const void *a) {
- return __JS_NewFloat64(ctx, fromfp16(*(const uint16_t *)a));
- }
- static JSValue js_TA_get_float32(JSContext *ctx, const void *a) {
- return __JS_NewFloat64(ctx, *(const float *)a);
- }
- static JSValue js_TA_get_float64(JSContext *ctx, const void *a) {
- return __JS_NewFloat64(ctx, *(const double *)a);
- }
- struct TA_sort_context {
- JSContext *ctx;
- int exception; /* 1 = exception, 2 = detached typed array */
- uint8_t *array;
- JSValueConst cmp;
- JSValue (*getfun)(JSContext *ctx, const void *a);
- int elt_size;
- };
- static int js_TA_cmp_generic(const void *a, const void *b, void *opaque) {
- struct TA_sort_context *psc = opaque;
- JSContext *ctx = psc->ctx;
- uint32_t a_idx, b_idx;
- JSValueConst argv[2];
- JSValue res;
- int cmp;
-
- cmp = 0;
- if (!psc->exception) {
- /* Note: the typed array can be detached without causing an
- error */
- a_idx = *(uint32_t *)a;
- b_idx = *(uint32_t *)b;
- argv[0] = psc->getfun(ctx, psc->array +
- a_idx * (size_t)psc->elt_size);
- argv[1] = psc->getfun(ctx, psc->array +
- b_idx * (size_t)(psc->elt_size));
- res = JS_Call(ctx, psc->cmp, JS_UNDEFINED, 2, argv);
- if (JS_IsException(res)) {
- psc->exception = 1;
- goto done;
- }
- if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) {
- int val = JS_VALUE_GET_INT(res);
- cmp = (val > 0) - (val < 0);
- } else {
- double val;
- if (JS_ToFloat64Free(ctx, &val, res) < 0) {
- psc->exception = 1;
- goto done;
- } else {
- cmp = (val > 0) - (val < 0);
- }
- }
- if (cmp == 0) {
- /* make sort stable: compare array offsets */
- cmp = (a_idx > b_idx) - (a_idx < b_idx);
- }
- done:
- JS_FreeValue(ctx, (JSValue)argv[0]);
- JS_FreeValue(ctx, (JSValue)argv[1]);
- }
- return cmp;
- }
- static JSValue js_typed_array_sort(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSObject *p;
- int len;
- size_t elt_size;
- struct TA_sort_context tsc;
- int (*cmpfun)(const void *a, const void *b, void *opaque);
- tsc.ctx = ctx;
- tsc.exception = 0;
- tsc.cmp = argv[0];
- if (!JS_IsUndefined(tsc.cmp) && check_function(ctx, tsc.cmp))
- return JS_EXCEPTION;
- len = js_typed_array_get_length_unsafe(ctx, this_val);
- if (len < 0)
- return JS_EXCEPTION;
- if (len > 1) {
- p = JS_VALUE_GET_OBJ(this_val);
- switch (p->class_id) {
- case JS_CLASS_INT8_ARRAY:
- tsc.getfun = js_TA_get_int8;
- cmpfun = js_TA_cmp_int8;
- break;
- case JS_CLASS_UINT8C_ARRAY:
- case JS_CLASS_UINT8_ARRAY:
- tsc.getfun = js_TA_get_uint8;
- cmpfun = js_TA_cmp_uint8;
- break;
- case JS_CLASS_INT16_ARRAY:
- tsc.getfun = js_TA_get_int16;
- cmpfun = js_TA_cmp_int16;
- break;
- case JS_CLASS_UINT16_ARRAY:
- tsc.getfun = js_TA_get_uint16;
- cmpfun = js_TA_cmp_uint16;
- break;
- case JS_CLASS_INT32_ARRAY:
- tsc.getfun = js_TA_get_int32;
- cmpfun = js_TA_cmp_int32;
- break;
- case JS_CLASS_UINT32_ARRAY:
- tsc.getfun = js_TA_get_uint32;
- cmpfun = js_TA_cmp_uint32;
- break;
- case JS_CLASS_BIG_INT64_ARRAY:
- tsc.getfun = js_TA_get_int64;
- cmpfun = js_TA_cmp_int64;
- break;
- case JS_CLASS_BIG_UINT64_ARRAY:
- tsc.getfun = js_TA_get_uint64;
- cmpfun = js_TA_cmp_uint64;
- break;
- case JS_CLASS_FLOAT16_ARRAY:
- tsc.getfun = js_TA_get_float16;
- cmpfun = js_TA_cmp_float16;
- break;
- case JS_CLASS_FLOAT32_ARRAY:
- tsc.getfun = js_TA_get_float32;
- cmpfun = js_TA_cmp_float32;
- break;
- case JS_CLASS_FLOAT64_ARRAY:
- tsc.getfun = js_TA_get_float64;
- cmpfun = js_TA_cmp_float64;
- break;
- default:
- abort();
- }
- elt_size = 1 << typed_array_size_log2(p->class_id);
- if (!JS_IsUndefined(tsc.cmp)) {
- uint32_t *array_idx;
- void *array;
- size_t i, j;
- /* the array must be copied because the comparison
- function may modify it */
- array = js_malloc(ctx, len * elt_size);
- if (!array)
- return JS_EXCEPTION;
- memcpy(array, p->u.array.u.ptr, len * elt_size);
-
- /* array_idx is needed to have a stable sort */
- array_idx = js_malloc(ctx, len * sizeof(array_idx[0]));
- if (!array_idx) {
- js_free(ctx, array);
- return JS_EXCEPTION;
- }
- for(i = 0; i < len; i++)
- array_idx[i] = i;
- tsc.elt_size = elt_size;
- tsc.array = array;
- rqsort(array_idx, len, sizeof(array_idx[0]),
- js_TA_cmp_generic, &tsc);
- if (tsc.exception) {
- if (tsc.exception == 1) {
- js_free(ctx, array_idx);
- js_free(ctx, array);
- return JS_EXCEPTION;
- }
- /* detached typed array during the sort: no error */
- } else {
- void *array_ptr = p->u.array.u.ptr;
- len = min_int(len, p->u.array.count);
- switch(elt_size) {
- case 1:
- for(i = 0; i < len; i++) {
- j = array_idx[i];
- ((uint8_t *)array_ptr)[i] = ((uint8_t *)array)[j];
- }
- break;
- case 2:
- for(i = 0; i < len; i++) {
- j = array_idx[i];
- ((uint16_t *)array_ptr)[i] = ((uint16_t *)array)[j];
- }
- break;
- case 4:
- for(i = 0; i < len; i++) {
- j = array_idx[i];
- ((uint32_t *)array_ptr)[i] = ((uint32_t *)array)[j];
- }
- break;
- case 8:
- for(i = 0; i < len; i++) {
- j = array_idx[i];
- ((uint64_t *)array_ptr)[i] = ((uint64_t *)array)[j];
- }
- break;
- default:
- abort();
- }
- }
- js_free(ctx, array_idx);
- js_free(ctx, array);
- } else {
- rqsort(p->u.array.u.ptr, len, elt_size, cmpfun, &tsc);
- if (tsc.exception)
- return JS_EXCEPTION;
- }
- }
- return JS_DupValue(ctx, this_val);
- }
- static JSValue js_typed_array_toSorted(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValue arr, ret;
- JSObject *p;
- p = get_typed_array(ctx, this_val);
- if (!p)
- return JS_EXCEPTION;
- arr = js_typed_array_constructor_ta(ctx, JS_UNDEFINED, this_val,
- p->class_id, p->u.array.count);
- if (JS_IsException(arr))
- return JS_EXCEPTION;
- ret = js_typed_array_sort(ctx, arr, argc, argv);
- JS_FreeValue(ctx, arr);
- return ret;
- }
- /* Uint8Array base64/hex (tc39 proposal-arraybuffer-base64) */
- enum {
- B64_ALPHABET_BASE64 = 0,
- B64_ALPHABET_BASE64URL = 1,
- };
- enum {
- B64_LAST_LOOSE = 0,
- B64_LAST_STRICT = 1,
- B64_LAST_STOP_BEFORE_PARTIAL = 2,
- };
- static const unsigned char b64_enc[64] = {
- 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
- 'Q','R','S','T','U','V','W','X','Y','Z',
- 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p',
- 'q','r','s','t','u','v','w','x','y','z',
- '0','1','2','3','4','5','6','7','8','9',
- '+','/'
- };
- static const unsigned char b64url_enc[64] = {
- 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
- 'Q','R','S','T','U','V','W','X','Y','Z',
- 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p',
- 'q','r','s','t','u','v','w','x','y','z',
- '0','1','2','3','4','5','6','7','8','9',
- '-','_'
- };
- #define K_WS 64
- #define K_ER 65
- static const uint8_t b64_dec[256] = {
- [ 0]=K_ER, [ 1]=K_ER, [ 2]=K_ER, [ 3]=K_ER, [ 4]=K_ER, [ 5]=K_ER, [ 6]=K_ER, [ 7]=K_ER,
- [ 8]=K_ER, [ 9]=K_WS, [ 10]=K_WS, [ 11]=K_ER, [ 12]=K_WS, [ 13]=K_WS, [ 14]=K_ER, [ 15]=K_ER,
- [ 16]=K_ER, [ 17]=K_ER, [ 18]=K_ER, [ 19]=K_ER, [ 20]=K_ER, [ 21]=K_ER, [ 22]=K_ER, [ 23]=K_ER,
- [ 24]=K_ER, [ 25]=K_ER, [ 26]=K_ER, [ 27]=K_ER, [ 28]=K_ER, [ 29]=K_ER, [ 30]=K_ER, [ 31]=K_ER,
- [' ']=K_WS, ['!']=K_ER, ['"']=K_ER, ['#']=K_ER, ['$']=K_ER, ['%']=K_ER, ['&']=K_ER, [ 39]=K_ER,
- ['(']=K_ER, [')']=K_ER, ['*']=K_ER, ['+']= 62, [',']=K_ER, ['-']=K_ER, ['.']=K_ER, ['/']= 63,
- ['0']= 52, ['1']= 53, ['2']= 54, ['3']= 55, ['4']= 56, ['5']= 57, ['6']= 58, ['7']= 59,
- ['8']= 60, ['9']= 61, [':']=K_ER, [';']=K_ER, ['<']=K_ER, ['=']=K_ER, ['>']=K_ER, ['?']=K_ER,
- ['@']=K_ER, ['A']= 0, ['B']= 1, ['C']= 2, ['D']= 3, ['E']= 4, ['F']= 5, ['G']= 6,
- ['H']= 7, ['I']= 8, ['J']= 9, ['K']= 10, ['L']= 11, ['M']= 12, ['N']= 13, ['O']= 14,
- ['P']= 15, ['Q']= 16, ['R']= 17, ['S']= 18, ['T']= 19, ['U']= 20, ['V']= 21, ['W']= 22,
- ['X']= 23, ['Y']= 24, ['Z']= 25, ['[']=K_ER, [ 92]=K_ER, [']']=K_ER, ['^']=K_ER, ['_']=K_ER,
- ['`']=K_ER, ['a']= 26, ['b']= 27, ['c']= 28, ['d']= 29, ['e']= 30, ['f']= 31, ['g']= 32,
- ['h']= 33, ['i']= 34, ['j']= 35, ['k']= 36, ['l']= 37, ['m']= 38, ['n']= 39, ['o']= 40,
- ['p']= 41, ['q']= 42, ['r']= 43, ['s']= 44, ['t']= 45, ['u']= 46, ['v']= 47, ['w']= 48,
- ['x']= 49, ['y']= 50, ['z']= 51, ['{']=K_ER, ['|']=K_ER, ['}']=K_ER, ['~']=K_ER, [127]=K_ER,
- [128]=K_ER, [129]=K_ER, [130]=K_ER, [131]=K_ER, [132]=K_ER, [133]=K_ER, [134]=K_ER, [135]=K_ER,
- [136]=K_ER, [137]=K_ER, [138]=K_ER, [139]=K_ER, [140]=K_ER, [141]=K_ER, [142]=K_ER, [143]=K_ER,
- [144]=K_ER, [145]=K_ER, [146]=K_ER, [147]=K_ER, [148]=K_ER, [149]=K_ER, [150]=K_ER, [151]=K_ER,
- [152]=K_ER, [153]=K_ER, [154]=K_ER, [155]=K_ER, [156]=K_ER, [157]=K_ER, [158]=K_ER, [159]=K_ER,
- [160]=K_ER, [161]=K_ER, [162]=K_ER, [163]=K_ER, [164]=K_ER, [165]=K_ER, [166]=K_ER, [167]=K_ER,
- [168]=K_ER, [169]=K_ER, [170]=K_ER, [171]=K_ER, [172]=K_ER, [173]=K_ER, [174]=K_ER, [175]=K_ER,
- [176]=K_ER, [177]=K_ER, [178]=K_ER, [179]=K_ER, [180]=K_ER, [181]=K_ER, [182]=K_ER, [183]=K_ER,
- [184]=K_ER, [185]=K_ER, [186]=K_ER, [187]=K_ER, [188]=K_ER, [189]=K_ER, [190]=K_ER, [191]=K_ER,
- [192]=K_ER, [193]=K_ER, [194]=K_ER, [195]=K_ER, [196]=K_ER, [197]=K_ER, [198]=K_ER, [199]=K_ER,
- [200]=K_ER, [201]=K_ER, [202]=K_ER, [203]=K_ER, [204]=K_ER, [205]=K_ER, [206]=K_ER, [207]=K_ER,
- [208]=K_ER, [209]=K_ER, [210]=K_ER, [211]=K_ER, [212]=K_ER, [213]=K_ER, [214]=K_ER, [215]=K_ER,
- [216]=K_ER, [217]=K_ER, [218]=K_ER, [219]=K_ER, [220]=K_ER, [221]=K_ER, [222]=K_ER, [223]=K_ER,
- [224]=K_ER, [225]=K_ER, [226]=K_ER, [227]=K_ER, [228]=K_ER, [229]=K_ER, [230]=K_ER, [231]=K_ER,
- [232]=K_ER, [233]=K_ER, [234]=K_ER, [235]=K_ER, [236]=K_ER, [237]=K_ER, [238]=K_ER, [239]=K_ER,
- [240]=K_ER, [241]=K_ER, [242]=K_ER, [243]=K_ER, [244]=K_ER, [245]=K_ER, [246]=K_ER, [247]=K_ER,
- [248]=K_ER, [249]=K_ER, [250]=K_ER, [251]=K_ER, [252]=K_ER, [253]=K_ER, [254]=K_ER, [255]=K_ER,
- };
- static const uint8_t b64url_dec[256] = {
- [ 0]=K_ER, [ 1]=K_ER, [ 2]=K_ER, [ 3]=K_ER, [ 4]=K_ER, [ 5]=K_ER, [ 6]=K_ER, [ 7]=K_ER,
- [ 8]=K_ER, [ 9]=K_WS, [ 10]=K_WS, [ 11]=K_ER, [ 12]=K_WS, [ 13]=K_WS, [ 14]=K_ER, [ 15]=K_ER,
- [ 16]=K_ER, [ 17]=K_ER, [ 18]=K_ER, [ 19]=K_ER, [ 20]=K_ER, [ 21]=K_ER, [ 22]=K_ER, [ 23]=K_ER,
- [ 24]=K_ER, [ 25]=K_ER, [ 26]=K_ER, [ 27]=K_ER, [ 28]=K_ER, [ 29]=K_ER, [ 30]=K_ER, [ 31]=K_ER,
- [' ']=K_WS, ['!']=K_ER, ['"']=K_ER, ['#']=K_ER, ['$']=K_ER, ['%']=K_ER, ['&']=K_ER, [ 39]=K_ER,
- ['(']=K_ER, [')']=K_ER, ['*']=K_ER, ['+']=K_ER, [',']=K_ER, ['-']= 62, ['.']=K_ER, ['/']=K_ER,
- ['0']= 52, ['1']= 53, ['2']= 54, ['3']= 55, ['4']= 56, ['5']= 57, ['6']= 58, ['7']= 59,
- ['8']= 60, ['9']= 61, [':']=K_ER, [';']=K_ER, ['<']=K_ER, ['=']=K_ER, ['>']=K_ER, ['?']=K_ER,
- ['@']=K_ER, ['A']= 0, ['B']= 1, ['C']= 2, ['D']= 3, ['E']= 4, ['F']= 5, ['G']= 6,
- ['H']= 7, ['I']= 8, ['J']= 9, ['K']= 10, ['L']= 11, ['M']= 12, ['N']= 13, ['O']= 14,
- ['P']= 15, ['Q']= 16, ['R']= 17, ['S']= 18, ['T']= 19, ['U']= 20, ['V']= 21, ['W']= 22,
- ['X']= 23, ['Y']= 24, ['Z']= 25, ['[']=K_ER, [ 92]=K_ER, [']']=K_ER, ['^']=K_ER, ['_']= 63,
- ['`']=K_ER, ['a']= 26, ['b']= 27, ['c']= 28, ['d']= 29, ['e']= 30, ['f']= 31, ['g']= 32,
- ['h']= 33, ['i']= 34, ['j']= 35, ['k']= 36, ['l']= 37, ['m']= 38, ['n']= 39, ['o']= 40,
- ['p']= 41, ['q']= 42, ['r']= 43, ['s']= 44, ['t']= 45, ['u']= 46, ['v']= 47, ['w']= 48,
- ['x']= 49, ['y']= 50, ['z']= 51, ['{']=K_ER, ['|']=K_ER, ['}']=K_ER, ['~']=K_ER, [127]=K_ER,
- [128]=K_ER, [129]=K_ER, [130]=K_ER, [131]=K_ER, [132]=K_ER, [133]=K_ER, [134]=K_ER, [135]=K_ER,
- [136]=K_ER, [137]=K_ER, [138]=K_ER, [139]=K_ER, [140]=K_ER, [141]=K_ER, [142]=K_ER, [143]=K_ER,
- [144]=K_ER, [145]=K_ER, [146]=K_ER, [147]=K_ER, [148]=K_ER, [149]=K_ER, [150]=K_ER, [151]=K_ER,
- [152]=K_ER, [153]=K_ER, [154]=K_ER, [155]=K_ER, [156]=K_ER, [157]=K_ER, [158]=K_ER, [159]=K_ER,
- [160]=K_ER, [161]=K_ER, [162]=K_ER, [163]=K_ER, [164]=K_ER, [165]=K_ER, [166]=K_ER, [167]=K_ER,
- [168]=K_ER, [169]=K_ER, [170]=K_ER, [171]=K_ER, [172]=K_ER, [173]=K_ER, [174]=K_ER, [175]=K_ER,
- [176]=K_ER, [177]=K_ER, [178]=K_ER, [179]=K_ER, [180]=K_ER, [181]=K_ER, [182]=K_ER, [183]=K_ER,
- [184]=K_ER, [185]=K_ER, [186]=K_ER, [187]=K_ER, [188]=K_ER, [189]=K_ER, [190]=K_ER, [191]=K_ER,
- [192]=K_ER, [193]=K_ER, [194]=K_ER, [195]=K_ER, [196]=K_ER, [197]=K_ER, [198]=K_ER, [199]=K_ER,
- [200]=K_ER, [201]=K_ER, [202]=K_ER, [203]=K_ER, [204]=K_ER, [205]=K_ER, [206]=K_ER, [207]=K_ER,
- [208]=K_ER, [209]=K_ER, [210]=K_ER, [211]=K_ER, [212]=K_ER, [213]=K_ER, [214]=K_ER, [215]=K_ER,
- [216]=K_ER, [217]=K_ER, [218]=K_ER, [219]=K_ER, [220]=K_ER, [221]=K_ER, [222]=K_ER, [223]=K_ER,
- [224]=K_ER, [225]=K_ER, [226]=K_ER, [227]=K_ER, [228]=K_ER, [229]=K_ER, [230]=K_ER, [231]=K_ER,
- [232]=K_ER, [233]=K_ER, [234]=K_ER, [235]=K_ER, [236]=K_ER, [237]=K_ER, [238]=K_ER, [239]=K_ER,
- [240]=K_ER, [241]=K_ER, [242]=K_ER, [243]=K_ER, [244]=K_ER, [245]=K_ER, [246]=K_ER, [247]=K_ER,
- [248]=K_ER, [249]=K_ER, [250]=K_ER, [251]=K_ER, [252]=K_ER, [253]=K_ER, [254]=K_ER, [255]=K_ER,
- };
-
- static size_t b64_encode(const uint8_t *src, size_t len, char *dst,
- const unsigned char *alpha)
- {
- size_t i, j;
- for (i = 0, j = 0; i + 3 <= len; i += 3, j += 4) {
- uint32_t v = 65536*src[i] + 256*src[i + 1] + src[i + 2];
- dst[j + 0] = alpha[(v >> 18) & 63];
- dst[j + 1] = alpha[(v >> 12) & 63];
- dst[j + 2] = alpha[(v >> 6) & 63];
- dst[j + 3] = alpha[v & 63];
- }
- size_t rem = len - i;
- if (rem == 1) {
- uint32_t v = 65536*src[i];
- dst[j++] = alpha[(v >> 18) & 63];
- dst[j++] = alpha[(v >> 12) & 63];
- dst[j++] = '=';
- dst[j++] = '=';
- } else if (rem == 2) {
- uint32_t v = 65536*src[i] + 256*src[i + 1];
- dst[j++] = alpha[(v >> 18) & 63];
- dst[j++] = alpha[(v >> 12) & 63];
- dst[j++] = alpha[(v >> 6) & 63];
- dst[j++] = '=';
- }
- return j;
- }
- static size_t b64_skip_ws(const char *src, size_t len, size_t index,
- const uint8_t *dec_table)
- {
- while (index < len && dec_table[(unsigned char)src[index]] == K_WS)
- index++;
- return index;
- }
- /* Implements the FromBase64 abstract operation.
- src/src_len: the input string (must be ASCII/latin1)
- dst/max_len: output buffer
- flags: b64_flags or b64_flags_url (selects valid characters)
- last_chunk: B64_LAST_LOOSE, B64_LAST_STRICT, or B64_LAST_STOP_BEFORE_PARTIAL
- *p_read: set to number of input characters consumed
- *p_err: set to 1 on error, 0 on success
- Returns: number of bytes written to dst */
- static size_t from_base64(const char *src, size_t src_len,
- uint8_t *dst, size_t max_len,
- const uint8_t *dec_table, int last_chunk,
- size_t *p_read, int *p_err)
- {
- size_t read = 0, written = 0;
- uint32_t v, acc = 0;
- int seen = 0;
- size_t index = 0;
- uint8_t ch;
-
- *p_err = 0;
- if (max_len == 0) {
- *p_read = 0;
- return 0;
- }
- for (;;) {
- if (seen == 0) {
- /* Fast path: decode complete groups of 4 valid characters.
- Breaks out on whitespace, padding, invalid chars, or capacity. */
- while (index + 4 <= src_len && written + 3 <= max_len) {
- uint32_t v0, v1, v2, v3;
- v0 = dec_table[(unsigned char)src[index]];
- v1 = dec_table[(unsigned char)src[index + 1]];
- v2 = dec_table[(unsigned char)src[index + 2]];
- v3 = dec_table[(unsigned char)src[index + 3]];
- if ((v0 | v1 | v2 | v3) >= 64)
- break;
- v = (v0 << 18) | (v1 << 12) | (v2 << 6) | v3;
- dst[written] = (uint8_t)(v >> 16);
- dst[written + 1] = (uint8_t)(v >> 8);
- dst[written + 2] = (uint8_t)(v);
- written += 3;
- index += 4;
- }
- read = index;
-
- if (written >= max_len) {
- *p_read = read;
- return written;
- }
- }
-
- /* Slow path: handle whitespace, padding, partial groups, capacity. */
- index = b64_skip_ws(src, src_len, index, dec_table);
- if (index == src_len) {
- if (seen > 0) {
- if (last_chunk == B64_LAST_STOP_BEFORE_PARTIAL) {
- *p_read = read;
- return written;
- }
- if (last_chunk == B64_LAST_STRICT) {
- *p_err = 1;
- return 0;
- }
- /* loose */
- if (seen == 1) {
- *p_err = 1;
- return 0;
- }
- break;
- }
- *p_read = src_len;
- return written;
- }
- ch = src[index++];
- if (ch == '=') {
- if (seen < 2) {
- *p_err = 1;
- return 0;
- }
- index = b64_skip_ws(src, src_len, index, dec_table);
- if (seen == 2) {
- if (index == src_len) {
- if (last_chunk == B64_LAST_STOP_BEFORE_PARTIAL) {
- *p_read = read;
- return written;
- }
- *p_err = 1;
- return 0;
- }
- if (src[index] == '=') {
- index++;
- index = b64_skip_ws(src, src_len, index, dec_table);
- } else {
- *p_err = 1;
- return 0;
- }
- }
- /* After padding, only whitespace is allowed */
- if (index != src_len) {
- *p_err = 1;
- return 0;
- }
- if (last_chunk == B64_LAST_STRICT) {
- uint32_t mask = (seen == 2) ? 0xF : 0x3;
- if (acc & mask) {
- *p_err = 1;
- return 0;
- }
- }
- break;
- }
- v = dec_table[ch];
- if (v >= 64) {
- *p_err = 1;
- return 0;
- }
- /* Check remaining capacity before committing to this group */
- {
- size_t remaining = max_len - written;
- if ((remaining == 1 && seen == 2) ||
- (remaining == 2 && seen == 3)) {
- *p_read = read;
- return written;
- }
- }
- acc = (acc << 6) | v;
- seen++;
- if (seen == 4) {
- dst[written] = (uint8_t)(acc >> 16);
- dst[written + 1] = (uint8_t)(acc >> 8);
- dst[written + 2] = (uint8_t)(acc);
- written += 3;
- acc = 0;
- seen = 0;
- read = index;
- if (written >= max_len) {
- *p_read = read;
- return written;
- }
- }
- }
- if (seen == 2) {
- dst[written++] = (uint8_t)(acc >> 4);
- } else if (seen == 3) {
- dst[written] = (uint8_t)(acc >> 10);
- dst[written + 1] = (uint8_t)(acc >> 2);
- written += 2;
- }
- *p_read = src_len;
- return written;
- }
- /* Hex helpers */
- static const char u8a_hex_digits[] = "0123456789abcdef";
- static size_t u8a_hex_encode(const uint8_t *src, size_t len, char *dst)
- {
- for (size_t i = 0; i < len; i++) {
- dst[i * 2] = u8a_hex_digits[src[i] >> 4];
- dst[i * 2 + 1] = u8a_hex_digits[src[i] & 0xF];
- }
- return len * 2;
- }
- /* Decode hex string to bytes.
- Returns bytes written. Sets *p_read to chars consumed, *p_err on error. */
- static size_t u8a_hex_decode(const char *src, size_t src_len,
- uint8_t *dst, size_t max_len,
- size_t *p_read, int *p_err)
- {
- size_t written = 0, i = 0;
- *p_err = 0;
- if (src_len & 1) {
- *p_err = 1;
- return 0;
- }
- while (i < src_len && written < max_len) {
- int hi = from_hex(src[i]);
- int lo = from_hex(src[i + 1]);
- if (hi < 0 || lo < 0) {
- *p_err = 1;
- return 0;
- }
- dst[written++] = (uint8_t)((hi << 4) | lo);
- i += 2;
- }
- *p_read = i;
- return written;
- }
- static JSValue JS_NewUint8ArrayCopy(JSContext *ctx, const uint8_t *buf, size_t len)
- {
- JSValue buffer, obj;
- JSArrayBuffer *abuf;
- buffer = js_array_buffer_constructor3(ctx, JS_UNDEFINED, len, NULL,
- JS_CLASS_ARRAY_BUFFER,
- (uint8_t *)buf,
- js_array_buffer_free, NULL,
- TRUE);
- if (JS_IsException(buffer))
- return JS_EXCEPTION;
- obj = js_create_from_ctor(ctx, JS_UNDEFINED, JS_CLASS_UINT8_ARRAY);
- if (JS_IsException(obj)) {
- JS_FreeValue(ctx, buffer);
- return JS_EXCEPTION;
- }
- abuf = js_get_array_buffer(ctx, buffer);
- assert(abuf != NULL);
- if (typed_array_init(ctx, obj, buffer, 0, abuf->byte_length, /*track_rab*/FALSE)) {
- // 'buffer' is freed on error above.
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- return obj;
- }
- /* Validate that this_val is a Uint8Array (type check only, no detach check).
- Returns the JSObject pointer or NULL on error (throws). */
- static JSObject *check_uint8array(JSContext *ctx, JSValueConst this_val)
- {
- JSObject *p;
- if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
- goto fail;
- p = JS_VALUE_GET_OBJ(this_val);
- if (p->class_id != JS_CLASS_UINT8_ARRAY)
- goto fail;
- return p;
- fail:
- JS_ThrowTypeError(ctx, "not a Uint8Array");
- return NULL;
- }
- /* Get the data pointer and length of a Uint8Array, checking for detached
- buffers. Must be called after options are read (per spec ordering).
- Returns 0 on success, -1 on error (throws). */
- static int get_uint8array_bytes(JSContext *ctx, JSObject *p,
- uint8_t **pdata, size_t *plen)
- {
- if (typed_array_is_oob(p)) {
- JS_ThrowTypeErrorArrayBufferOOB(ctx);
- *pdata = NULL; /* fail safe */
- *plen = 0;
- return -1;
- }
- *pdata = p->u.array.u.uint8_ptr;
- *plen = p->u.array.count;
- return 0;
- }
- /* Validate options is undefined or an object (GetOptionsObject).
- Returns 0 on success, -1 on error (throws). */
- static int check_options_object(JSContext *ctx, JSValueConst options)
- {
- if (JS_IsUndefined(options))
- return 0;
- if (!JS_IsObject(options)) {
- JS_ThrowTypeError(ctx, "options must be an object");
- return -1;
- }
- return 0;
- }
- /* Parse the 'alphabet' option from an options object.
- Returns B64_ALPHABET_BASE64 or B64_ALPHABET_BASE64URL, or -1 on error. */
- static int parse_alphabet_option(JSContext *ctx, JSValueConst options)
- {
- JSValue val;
- const char *str;
- int ret;
- if (JS_IsUndefined(options))
- return B64_ALPHABET_BASE64;
- val = JS_GetProperty(ctx, options, JS_ATOM_alphabet);
- if (JS_IsException(val))
- return -1;
- if (JS_IsUndefined(val))
- return B64_ALPHABET_BASE64;
- if (!JS_IsString(val)) {
- JS_FreeValue(ctx, val);
- JS_ThrowTypeError(ctx, "expected string for alphabet");
- return -1;
- }
- str = JS_ToCString(ctx, val);
- JS_FreeValue(ctx, val);
- if (!str)
- return -1;
- if (!strcmp(str, "base64"))
- ret = B64_ALPHABET_BASE64;
- else if (!strcmp(str, "base64url"))
- ret = B64_ALPHABET_BASE64URL;
- else {
- JS_ThrowTypeError(ctx, "invalid alphabet");
- ret = -1;
- }
- JS_FreeCString(ctx, str);
- return ret;
- }
- /* Parse the 'lastChunkHandling' option. Returns mode or -1 on error. */
- static int parse_last_chunk_option(JSContext *ctx, JSValueConst options)
- {
- JSValue val;
- const char *str;
- int ret;
- if (JS_IsUndefined(options))
- return B64_LAST_LOOSE;
- val = JS_GetProperty(ctx, options, JS_ATOM_lastChunkHandling);
- if (JS_IsException(val))
- return -1;
- if (JS_IsUndefined(val))
- return B64_LAST_LOOSE;
- if (!JS_IsString(val)) {
- JS_FreeValue(ctx, val);
- JS_ThrowTypeError(ctx, "expected string for lastChunkHandling");
- return -1;
- }
- str = JS_ToCString(ctx, val);
- JS_FreeValue(ctx, val);
- if (!str)
- return -1;
- if (!strcmp(str, "loose"))
- ret = B64_LAST_LOOSE;
- else if (!strcmp(str, "strict"))
- ret = B64_LAST_STRICT;
- else if (!strcmp(str, "stop-before-partial"))
- ret = B64_LAST_STOP_BEFORE_PARTIAL;
- else {
- JS_ThrowTypeError(ctx, "invalid lastChunkHandling option");
- ret = -1;
- }
- JS_FreeCString(ctx, str);
- return ret;
- }
- /* Uint8Array.prototype.toBase64([options]) */
- static JSValue js_uint8array_to_base64(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- uint8_t *data;
- size_t len;
- JSValueConst options;
- JSObject *p;
- int alphabet, omit_padding;
- size_t out_len, written;
- JSString *ostr;
- char *dst;
- p = check_uint8array(ctx, this_val);
- if (!p)
- return JS_EXCEPTION;
- options = argc > 0 ? argv[0] : JS_UNDEFINED;
- if (check_options_object(ctx, options))
- return JS_EXCEPTION;
- alphabet = parse_alphabet_option(ctx, options);
- if (alphabet < 0)
- return JS_EXCEPTION;
- omit_padding = 0;
- if (!JS_IsUndefined(options)) {
- JSValue op_val = JS_GetProperty(ctx, options, JS_ATOM_omitPadding);
- if (JS_IsException(op_val))
- return JS_EXCEPTION;
- omit_padding = JS_ToBool(ctx, op_val);
- JS_FreeValue(ctx, op_val);
- }
- if (get_uint8array_bytes(ctx, p, &data, &len))
- return JS_EXCEPTION;
- out_len = 4 * ((len + 2) / 3);
- if (unlikely(out_len > JS_STRING_LEN_MAX))
- return JS_ThrowRangeError(ctx, "output too large");
- ostr = js_alloc_string(ctx, out_len, 0);
- if (!ostr)
- return JS_EXCEPTION;
- dst = (char *)ostr->u.str8;
- written = b64_encode(data, len, dst,
- alphabet == B64_ALPHABET_BASE64URL ? b64url_enc : b64_enc);
- if (omit_padding) {
- while (written > 0 && dst[written - 1] == '=')
- written--;
- }
- dst[written] = '\0';
- ostr->len = written;
- return JS_MKPTR(JS_TAG_STRING, ostr);
- }
- /* Uint8Array.prototype.toHex() */
- static JSValue js_uint8array_to_hex(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- uint8_t *data;
- size_t len, out_len;
- JSObject *p;
- JSString *ostr;
- p = check_uint8array(ctx, this_val);
- if (!p)
- return JS_EXCEPTION;
- if (get_uint8array_bytes(ctx, p, &data, &len))
- return JS_EXCEPTION;
- out_len = len * 2;
- if (unlikely(out_len > JS_STRING_LEN_MAX))
- return JS_ThrowRangeError(ctx, "output too large");
- ostr = js_alloc_string(ctx, out_len, 0);
- if (!ostr)
- return JS_EXCEPTION;
- u8a_hex_encode(data, len, (char *)ostr->u.str8);
- ostr->u.str8[out_len] = '\0';
- return JS_MKPTR(JS_TAG_STRING, ostr);
- }
- /* Uint8Array.fromBase64(string[, options]) */
- static JSValue js_uint8array_from_base64(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- const char *str;
- size_t str_len, read_pos, decoded_len, out_cap;
- int alphabet, last_chunk, err;
- uint8_t *buf;
- JSValue result;
- JSValueConst options;
-
- if (!JS_IsString(argv[0]))
- return JS_ThrowTypeError(ctx, "expected string");
- str = JS_ToCStringLen(ctx, &str_len, argv[0]);
- if (!str)
- return JS_EXCEPTION;
- options = argc > 1 ? argv[1] : JS_UNDEFINED;
- if (check_options_object(ctx, options)) {
- JS_FreeCString(ctx, str);
- return JS_EXCEPTION;
- }
- alphabet = parse_alphabet_option(ctx, options);
- if (alphabet < 0) {
- JS_FreeCString(ctx, str);
- return JS_EXCEPTION;
- }
- last_chunk = parse_last_chunk_option(ctx, options);
- if (last_chunk < 0) {
- JS_FreeCString(ctx, str);
- return JS_EXCEPTION;
- }
- out_cap = (str_len / 4) * 3 + 3;
- buf = js_malloc(ctx, out_cap);
- if (!buf) {
- JS_FreeCString(ctx, str);
- return JS_EXCEPTION;
- }
- decoded_len = from_base64(str, str_len, buf, out_cap,
- alphabet == B64_ALPHABET_BASE64URL
- ? b64url_dec : b64_dec,
- last_chunk, &read_pos, &err);
- JS_FreeCString(ctx, str);
- if (err) {
- js_free(ctx, buf);
- return JS_ThrowSyntaxError(ctx, "invalid base64 string");
- }
- result = JS_NewUint8ArrayCopy(ctx, buf, decoded_len);
- js_free(ctx, buf);
- return result;
- }
- /* Uint8Array.fromHex(string) */
- static JSValue js_uint8array_from_hex(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- const char *str;
- size_t str_len, read_pos, decoded_len, out_cap;
- int err;
- uint8_t *buf;
- JSValue result;
- if (!JS_IsString(argv[0]))
- return JS_ThrowTypeError(ctx, "expected string");
- str = JS_ToCStringLen(ctx, &str_len, argv[0]);
- if (!str)
- return JS_EXCEPTION;
- out_cap = str_len / 2 + 1;
- buf = js_malloc(ctx, out_cap);
- if (!buf) {
- JS_FreeCString(ctx, str);
- return JS_EXCEPTION;
- }
- decoded_len = u8a_hex_decode(str, str_len, buf, out_cap, &read_pos, &err);
- JS_FreeCString(ctx, str);
- if (err) {
- js_free(ctx, buf);
- return JS_ThrowSyntaxError(ctx, "invalid hex string");
- }
- /* XXX: could avoid the copy */
- result = JS_NewUint8ArrayCopy(ctx, buf, decoded_len);
- js_free(ctx, buf);
- return result;
- }
- /* Return a { read, written } result object */
- static JSValue js_make_read_written(JSContext *ctx, size_t read, size_t written)
- {
- JSValue obj = JS_NewObject(ctx);
- if (JS_IsException(obj))
- return JS_EXCEPTION;
- if (JS_DefinePropertyValueStr(ctx, obj, "read",
- JS_NewUint32(ctx, read), JS_PROP_C_W_E) < 0)
- goto fail;
- if (JS_DefinePropertyValueStr(ctx, obj, "written",
- JS_NewUint32(ctx, written), JS_PROP_C_W_E) < 0)
- goto fail;
- return obj;
- fail:
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- /* Uint8Array.prototype.setFromBase64(string[, options]) */
- static JSValue js_uint8array_set_from_base64(JSContext *ctx,
- JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- uint8_t *data;
- size_t len;
- const char *str;
- size_t str_len, read_pos, decoded_len;
- JSObject *p;
- int alphabet, last_chunk, err;
- JSValueConst options;
- p = check_uint8array(ctx, this_val);
- if (!p)
- return JS_EXCEPTION;
- if (!JS_IsString(argv[0]))
- return JS_ThrowTypeError(ctx, "expected string");
- str = JS_ToCStringLen(ctx, &str_len, argv[0]);
- if (!str)
- return JS_EXCEPTION;
- options = argc > 1 ? argv[1] : JS_UNDEFINED;
- if (check_options_object(ctx, options)) {
- JS_FreeCString(ctx, str);
- return JS_EXCEPTION;
- }
- alphabet = parse_alphabet_option(ctx, options);
- if (alphabet < 0) {
- JS_FreeCString(ctx, str);
- return JS_EXCEPTION;
- }
- last_chunk = parse_last_chunk_option(ctx, options);
- if (last_chunk < 0) {
- JS_FreeCString(ctx, str);
- return JS_EXCEPTION;
- }
- if (get_uint8array_bytes(ctx, p, &data, &len)) {
- JS_FreeCString(ctx, str);
- return JS_EXCEPTION;
- }
- decoded_len = from_base64(str, str_len, data, len,
- alphabet == B64_ALPHABET_BASE64URL
- ? b64url_dec : b64_dec,
- last_chunk, &read_pos, &err);
- JS_FreeCString(ctx, str);
- if (err)
- return JS_ThrowSyntaxError(ctx, "invalid base64 string");
- return js_make_read_written(ctx, read_pos, decoded_len);
- }
- /* Uint8Array.prototype.setFromHex(string) */
- static JSValue js_uint8array_set_from_hex(JSContext *ctx,
- JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- uint8_t *data;
- size_t len;
- const char *str;
- size_t str_len, read_pos, decoded_len;
- JSObject *p;
- int err;
- p = check_uint8array(ctx, this_val);
- if (!p)
- return JS_EXCEPTION;
- if (!JS_IsString(argv[0]))
- return JS_ThrowTypeError(ctx, "expected string");
- str = JS_ToCStringLen(ctx, &str_len, argv[0]);
- if (!str)
- return JS_EXCEPTION;
- if (get_uint8array_bytes(ctx, p, &data, &len)) {
- JS_FreeCString(ctx, str);
- return JS_EXCEPTION;
- }
- decoded_len = u8a_hex_decode(str, str_len, data, len, &read_pos, &err);
- JS_FreeCString(ctx, str);
- if (err)
- return JS_ThrowSyntaxError(ctx, "invalid hex string");
- return js_make_read_written(ctx, read_pos, decoded_len);
- }
- static const JSCFunctionListEntry js_typed_array_base_funcs[] = {
- JS_CFUNC_DEF("from", 1, js_typed_array_from ),
- JS_CFUNC_DEF("of", 0, js_typed_array_of ),
- JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
- };
- static const JSCFunctionListEntry js_typed_array_base_proto_funcs[] = {
- JS_CGETSET_DEF("length", js_typed_array_get_length, NULL ),
- JS_CFUNC_DEF("at", 1, js_typed_array_at ),
- JS_CFUNC_DEF("with", 2, js_typed_array_with ),
- JS_CGETSET_DEF("buffer", js_typed_array_get_buffer, NULL ),
- JS_CGETSET_DEF("byteLength", js_typed_array_get_byteLength, NULL ),
- JS_CGETSET_DEF("byteOffset", js_typed_array_get_byteOffset, NULL ),
- JS_CFUNC_DEF("set", 1, js_typed_array_set ),
- JS_CFUNC_MAGIC_DEF("values", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_VALUE ),
- JS_ALIAS_DEF("[Symbol.iterator]", "values" ),
- JS_CFUNC_MAGIC_DEF("keys", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_KEY ),
- JS_CFUNC_MAGIC_DEF("entries", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_KEY_AND_VALUE ),
- JS_CGETSET_DEF("[Symbol.toStringTag]", js_typed_array_get_toStringTag, NULL ),
- JS_CFUNC_DEF("copyWithin", 2, js_typed_array_copyWithin ),
- JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every | special_TA ),
- JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some | special_TA ),
- JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, special_forEach | special_TA ),
- JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, special_map | special_TA ),
- JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, special_filter | special_TA ),
- JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce | special_TA ),
- JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight | special_TA ),
- JS_CFUNC_DEF("fill", 1, js_typed_array_fill ),
- JS_CFUNC_MAGIC_DEF("find", 1, js_typed_array_find, ArrayFind ),
- JS_CFUNC_MAGIC_DEF("findIndex", 1, js_typed_array_find, ArrayFindIndex ),
- JS_CFUNC_MAGIC_DEF("findLast", 1, js_typed_array_find, ArrayFindLast ),
- JS_CFUNC_MAGIC_DEF("findLastIndex", 1, js_typed_array_find, ArrayFindLastIndex ),
- JS_CFUNC_DEF("reverse", 0, js_typed_array_reverse ),
- JS_CFUNC_DEF("toReversed", 0, js_typed_array_toReversed ),
- JS_CFUNC_DEF("slice", 2, js_typed_array_slice ),
- JS_CFUNC_DEF("subarray", 2, js_typed_array_subarray ),
- JS_CFUNC_DEF("sort", 1, js_typed_array_sort ),
- JS_CFUNC_DEF("toSorted", 1, js_typed_array_toSorted ),
- JS_CFUNC_MAGIC_DEF("join", 1, js_typed_array_join, 0 ),
- JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_typed_array_join, 1 ),
- JS_CFUNC_MAGIC_DEF("indexOf", 1, js_typed_array_indexOf, special_indexOf ),
- JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_typed_array_indexOf, special_lastIndexOf ),
- JS_CFUNC_MAGIC_DEF("includes", 1, js_typed_array_indexOf, special_includes ),
- //JS_ALIAS_BASE_DEF("toString", "toString", 2 /* Array.prototype. */), @@@
- };
- static const JSCFunctionListEntry js_typed_array_funcs[] = {
- JS_PROP_INT32_DEF("BYTES_PER_ELEMENT", 1, 0),
- JS_PROP_INT32_DEF("BYTES_PER_ELEMENT", 2, 0),
- JS_PROP_INT32_DEF("BYTES_PER_ELEMENT", 4, 0),
- JS_PROP_INT32_DEF("BYTES_PER_ELEMENT", 8, 0),
- };
- static const JSCFunctionListEntry js_uint8array_proto_funcs[] = {
- JS_PROP_INT32_DEF("BYTES_PER_ELEMENT", 1, 0),
- JS_CFUNC_DEF("toBase64", 0, js_uint8array_to_base64),
- JS_CFUNC_DEF("toHex", 0, js_uint8array_to_hex),
- JS_CFUNC_DEF("setFromBase64", 1, js_uint8array_set_from_base64),
- JS_CFUNC_DEF("setFromHex", 1, js_uint8array_set_from_hex),
- };
- static const JSCFunctionListEntry js_uint8array_funcs[] = {
- JS_PROP_INT32_DEF("BYTES_PER_ELEMENT", 1, 0),
- JS_CFUNC_DEF("fromBase64", 1, js_uint8array_from_base64),
- JS_CFUNC_DEF("fromHex", 1, js_uint8array_from_hex),
- };
- static JSValue js_typed_array_base_constructor(JSContext *ctx,
- JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- return JS_ThrowTypeError(ctx, "cannot be called");
- }
- /* 'obj' must be an allocated typed array object */
- static int typed_array_init(JSContext *ctx, JSValueConst obj,
- JSValue buffer, uint64_t offset, uint64_t len,
- BOOL track_rab)
- {
- JSTypedArray *ta;
- JSObject *p, *pbuffer;
- JSArrayBuffer *abuf;
- int size_log2;
- p = JS_VALUE_GET_OBJ(obj);
- size_log2 = typed_array_size_log2(p->class_id);
- ta = js_malloc(ctx, sizeof(*ta));
- if (!ta) {
- JS_FreeValue(ctx, buffer);
- return -1;
- }
- pbuffer = JS_VALUE_GET_OBJ(buffer);
- abuf = pbuffer->u.array_buffer;
- ta->obj = p;
- ta->buffer = pbuffer;
- ta->offset = offset;
- ta->length = len << size_log2;
- ta->track_rab = track_rab;
- list_add_tail(&ta->link, &abuf->array_list);
- p->u.typed_array = ta;
- p->u.array.count = len;
- p->u.array.u.ptr = abuf->data + offset;
- return 0;
- }
- static JSValue js_array_from_iterator(JSContext *ctx, uint32_t *plen,
- JSValueConst obj, JSValueConst method)
- {
- JSValue arr, iter, next_method = JS_UNDEFINED, val;
- BOOL done;
- uint32_t k;
- *plen = 0;
- arr = JS_NewArray(ctx);
- if (JS_IsException(arr))
- return arr;
- iter = JS_GetIterator2(ctx, obj, method);
- if (JS_IsException(iter))
- goto fail;
- next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
- if (JS_IsException(next_method))
- goto fail;
- k = 0;
- for(;;) {
- val = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
- if (JS_IsException(val))
- goto fail;
- if (done)
- break;
- if (JS_CreateDataPropertyUint32(ctx, arr, k, val, JS_PROP_THROW) < 0)
- goto fail;
- k++;
- }
- JS_FreeValue(ctx, next_method);
- JS_FreeValue(ctx, iter);
- *plen = k;
- return arr;
- fail:
- JS_FreeValue(ctx, next_method);
- JS_FreeValue(ctx, iter);
- JS_FreeValue(ctx, arr);
- return JS_EXCEPTION;
- }
- static JSValue js_typed_array_constructor_obj(JSContext *ctx,
- JSValueConst new_target,
- JSValueConst obj,
- int classid)
- {
- JSValue iter, ret, arr = JS_UNDEFINED, val, buffer;
- uint32_t i;
- int size_log2;
- int64_t len;
- size_log2 = typed_array_size_log2(classid);
- ret = js_create_from_ctor(ctx, new_target, classid);
- if (JS_IsException(ret))
- return JS_EXCEPTION;
- iter = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator);
- if (JS_IsException(iter))
- goto fail;
- if (!JS_IsUndefined(iter) && !JS_IsNull(iter)) {
- uint32_t len1;
- arr = js_array_from_iterator(ctx, &len1, obj, iter);
- JS_FreeValue(ctx, iter);
- if (JS_IsException(arr))
- goto fail;
- len = len1;
- } else {
- if (js_get_length64(ctx, &len, obj))
- goto fail;
- arr = JS_DupValue(ctx, obj);
- }
- buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED,
- len << size_log2,
- NULL);
- if (JS_IsException(buffer))
- goto fail;
- if (typed_array_init(ctx, ret, buffer, 0, len, /*track_rab*/FALSE))
- goto fail;
- for(i = 0; i < len; i++) {
- val = JS_GetPropertyUint32(ctx, arr, i);
- if (JS_IsException(val))
- goto fail;
- if (JS_SetPropertyUint32(ctx, ret, i, val) < 0)
- goto fail;
- }
- JS_FreeValue(ctx, arr);
- return ret;
- fail:
- JS_FreeValue(ctx, arr);
- JS_FreeValue(ctx, ret);
- return JS_EXCEPTION;
- }
- static JSValue js_typed_array_constructor_ta(JSContext *ctx,
- JSValueConst new_target,
- JSValueConst src_obj,
- int classid, uint32_t len)
- {
- JSObject *p, *src_buffer;
- JSTypedArray *ta;
- JSValue obj, buffer;
- uint32_t i;
- int size_log2;
- JSArrayBuffer *src_abuf, *abuf;
- obj = js_create_from_ctor(ctx, new_target, classid);
- if (JS_IsException(obj))
- return obj;
- p = JS_VALUE_GET_OBJ(src_obj);
- if (typed_array_is_oob(p)) {
- JS_ThrowTypeErrorArrayBufferOOB(ctx);
- goto fail;
- }
- size_log2 = typed_array_size_log2(classid);
- buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED,
- (uint64_t)len << size_log2,
- NULL);
- if (JS_IsException(buffer))
- goto fail;
- /* necessary because it could have been detached */
- if (typed_array_is_oob(p)) {
- JS_FreeValue(ctx, buffer);
- JS_ThrowTypeErrorArrayBufferOOB(ctx);
- goto fail;
- }
- abuf = JS_GetOpaque(buffer, JS_CLASS_ARRAY_BUFFER);
- if (typed_array_init(ctx, obj, buffer, 0, len, /*track_rab*/FALSE))
- goto fail;
- ta = p->u.typed_array;
- src_buffer = ta->buffer;
- src_abuf = src_buffer->u.array_buffer;
- if (p->class_id == classid &&
- (int64_t)ta->offset + (int64_t)abuf->byte_length <= src_abuf->byte_length) {
- /* same type and no overflow: copy the content */
- memcpy(abuf->data, src_abuf->data + ta->offset, abuf->byte_length);
- } else {
- for(i = 0; i < len; i++) {
- JSValue val;
- val = JS_GetPropertyUint32(ctx, src_obj, i);
- if (JS_IsException(val))
- goto fail;
- if (JS_SetPropertyUint32(ctx, obj, i, val) < 0)
- goto fail;
- }
- }
- return obj;
- fail:
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- static JSValue js_typed_array_constructor(JSContext *ctx,
- JSValueConst new_target,
- int argc, JSValueConst *argv,
- int classid)
- {
- BOOL track_rab = FALSE;
- JSValue buffer, obj;
- JSArrayBuffer *abuf;
- int size_log2;
- uint64_t len, offset;
- size_log2 = typed_array_size_log2(classid);
- if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT) {
- if (JS_ToIndex(ctx, &len, argv[0]))
- return JS_EXCEPTION;
- obj = js_create_from_ctor(ctx, new_target, classid);
- if (JS_IsException(obj))
- return JS_EXCEPTION;
- buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED,
- len << size_log2,
- NULL);
- if (JS_IsException(buffer))
- goto fail;
- offset = 0;
- } else {
- JSObject *p = JS_VALUE_GET_OBJ(argv[0]);
- if (p->class_id == JS_CLASS_ARRAY_BUFFER ||
- p->class_id == JS_CLASS_SHARED_ARRAY_BUFFER) {
- obj = js_create_from_ctor(ctx, new_target, classid);
- if (JS_IsException(obj))
- return JS_EXCEPTION;
- if (JS_ToIndex(ctx, &offset, argv[1]))
- goto fail;
- if ((offset & ((1 << size_log2) - 1)) != 0)
- goto invalid_offset;
- abuf = p->u.array_buffer;
- if (JS_IsUndefined(argv[2])) {
- if (abuf->detached) {
- JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
- goto fail;
- }
- if (offset > abuf->byte_length) {
- invalid_offset:
- JS_ThrowRangeError(ctx, "invalid offset");
- goto fail;
- }
- track_rab = array_buffer_is_resizable(abuf);
- if (!track_rab) {
- if ((abuf->byte_length & ((1 << size_log2) - 1)) != 0)
- goto invalid_length;
- }
- len = (abuf->byte_length - offset) >> size_log2;
- } else {
- if (JS_ToIndex(ctx, &len, argv[2]))
- goto fail;
- if (abuf->detached) {
- JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
- goto fail;
- }
- if ((offset + (len << size_log2)) > abuf->byte_length) {
- invalid_length:
- JS_ThrowRangeError(ctx, "invalid length");
- goto fail;
- }
- }
- buffer = JS_DupValue(ctx, argv[0]);
- } else {
- if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
- p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
- return js_typed_array_constructor_ta(ctx, new_target, argv[0],
- classid, p->u.array.count);
- } else {
- return js_typed_array_constructor_obj(ctx, new_target, argv[0], classid);
- }
- }
- }
- if (typed_array_init(ctx, obj, buffer, offset, len, track_rab))
- goto fail;
- return obj;
- fail:
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- static void js_typed_array_finalizer(JSRuntime *rt, JSValue val)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- JSTypedArray *ta = p->u.typed_array;
- if (ta) {
- /* during the GC the finalizers are called in an arbitrary
- order so the ArrayBuffer finalizer may have been called */
- if (ta->link.next) {
- list_del(&ta->link);
- }
- JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer));
- js_free_rt(rt, ta);
- }
- }
- static void js_typed_array_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func)
- {
- JSObject *p = JS_VALUE_GET_OBJ(val);
- JSTypedArray *ta = p->u.typed_array;
- if (ta) {
- JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer), mark_func);
- }
- }
- static JSValue js_dataview_constructor(JSContext *ctx,
- JSValueConst new_target,
- int argc, JSValueConst *argv)
- {
- BOOL recompute_len = FALSE;
- BOOL track_rab = FALSE;
- JSArrayBuffer *abuf;
- uint64_t offset;
- uint32_t len;
- JSValueConst buffer;
- JSValue obj;
- JSTypedArray *ta;
- JSObject *p;
- buffer = argv[0];
- abuf = js_get_array_buffer(ctx, buffer);
- if (!abuf)
- return JS_EXCEPTION;
- offset = 0;
- if (argc > 1) {
- if (JS_ToIndex(ctx, &offset, argv[1]))
- return JS_EXCEPTION;
- }
- if (abuf->detached)
- return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
- if (offset > abuf->byte_length)
- return JS_ThrowRangeError(ctx, "invalid byteOffset");
- len = abuf->byte_length - offset;
- if (argc > 2 && !JS_IsUndefined(argv[2])) {
- uint64_t l;
- if (JS_ToIndex(ctx, &l, argv[2]))
- return JS_EXCEPTION;
- if (l > len)
- return JS_ThrowRangeError(ctx, "invalid byteLength");
- len = l;
- } else {
- recompute_len = TRUE;
- track_rab = array_buffer_is_resizable(abuf);
- }
- obj = js_create_from_ctor(ctx, new_target, JS_CLASS_DATAVIEW);
- if (JS_IsException(obj))
- return JS_EXCEPTION;
- if (abuf->detached) {
- /* could have been detached in js_create_from_ctor() */
- JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
- goto fail;
- }
- // RAB could have been resized in js_create_from_ctor()
- if (offset > abuf->byte_length) {
- goto out_of_bound;
- } else if (recompute_len) {
- len = abuf->byte_length - offset;
- } else if (offset + len > abuf->byte_length) {
- out_of_bound:
- JS_ThrowRangeError(ctx, "invalid byteOffset or byteLength");
- goto fail;
- }
- ta = js_malloc(ctx, sizeof(*ta));
- if (!ta) {
- fail:
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- p = JS_VALUE_GET_OBJ(obj);
- ta->obj = p;
- ta->buffer = JS_VALUE_GET_OBJ(JS_DupValue(ctx, buffer));
- ta->offset = offset;
- ta->length = len;
- ta->track_rab = track_rab;
- list_add_tail(&ta->link, &abuf->array_list);
- p->u.typed_array = ta;
- return obj;
- }
- // is the DataView out of bounds relative to its parent arraybuffer?
- static BOOL dataview_is_oob(JSObject *p)
- {
- JSArrayBuffer *abuf;
- JSTypedArray *ta;
- assert(p->class_id == JS_CLASS_DATAVIEW);
- ta = p->u.typed_array;
- abuf = ta->buffer->u.array_buffer;
- if (abuf->detached)
- return TRUE;
- if (ta->offset > abuf->byte_length)
- return TRUE;
- if (ta->track_rab)
- return FALSE;
- return (int64_t)ta->offset + ta->length > abuf->byte_length;
- }
- static JSObject *get_dataview(JSContext *ctx, JSValueConst this_val)
- {
- JSObject *p;
- if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
- goto fail;
- p = JS_VALUE_GET_OBJ(this_val);
- if (p->class_id != JS_CLASS_DATAVIEW) {
- fail:
- JS_ThrowTypeError(ctx, "not a DataView");
- return NULL;
- }
- return p;
- }
- static JSValue js_dataview_get_buffer(JSContext *ctx, JSValueConst this_val)
- {
- JSObject *p;
- JSTypedArray *ta;
- p = get_dataview(ctx, this_val);
- if (!p)
- return JS_EXCEPTION;
- ta = p->u.typed_array;
- return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, ta->buffer));
- }
- static JSValue js_dataview_get_byteLength(JSContext *ctx, JSValueConst this_val)
- {
- JSArrayBuffer *abuf;
- JSTypedArray *ta;
- JSObject *p;
- p = get_dataview(ctx, this_val);
- if (!p)
- return JS_EXCEPTION;
- if (dataview_is_oob(p))
- return JS_ThrowTypeErrorArrayBufferOOB(ctx);
- ta = p->u.typed_array;
- if (ta->track_rab) {
- abuf = ta->buffer->u.array_buffer;
- return JS_NewUint32(ctx, abuf->byte_length - ta->offset);
- }
- return JS_NewUint32(ctx, ta->length);
- }
- static JSValue js_dataview_get_byteOffset(JSContext *ctx, JSValueConst this_val)
- {
- JSTypedArray *ta;
- JSObject *p;
- p = get_dataview(ctx, this_val);
- if (!p)
- return JS_EXCEPTION;
- if (dataview_is_oob(p))
- return JS_ThrowTypeErrorArrayBufferOOB(ctx);
- ta = p->u.typed_array;
- return JS_NewUint32(ctx, ta->offset);
- }
- static JSValue js_dataview_getValue(JSContext *ctx,
- JSValueConst this_obj,
- int argc, JSValueConst *argv, int class_id)
- {
- JSTypedArray *ta;
- JSArrayBuffer *abuf;
- BOOL littleEndian, is_swap;
- int size;
- uint8_t *ptr;
- uint32_t v;
- uint64_t pos;
- ta = JS_GetOpaque2(ctx, this_obj, JS_CLASS_DATAVIEW);
- if (!ta)
- return JS_EXCEPTION;
- size = 1 << typed_array_size_log2(class_id);
- if (JS_ToIndex(ctx, &pos, argv[0]))
- return JS_EXCEPTION;
- littleEndian = argc > 1 && JS_ToBool(ctx, argv[1]);
- is_swap = littleEndian ^ !is_be();
- abuf = ta->buffer->u.array_buffer;
- if (abuf->detached)
- return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
- // order matters: this check should come before the next one
- if ((pos + size) > ta->length)
- return JS_ThrowRangeError(ctx, "out of bound");
- // test262 expects a TypeError for this and V8, in its infinite wisdom,
- // throws a "detached array buffer" exception, but IMO that doesn't make
- // sense because the buffer is not in fact detached, it's still there
- if ((int64_t)ta->offset + ta->length > abuf->byte_length)
- return JS_ThrowTypeError(ctx, "out of bound");
- ptr = abuf->data + ta->offset + pos;
- switch(class_id) {
- case JS_CLASS_INT8_ARRAY:
- return JS_NewInt32(ctx, *(int8_t *)ptr);
- case JS_CLASS_UINT8_ARRAY:
- return JS_NewInt32(ctx, *(uint8_t *)ptr);
- case JS_CLASS_INT16_ARRAY:
- v = get_u16(ptr);
- if (is_swap)
- v = bswap16(v);
- return JS_NewInt32(ctx, (int16_t)v);
- case JS_CLASS_UINT16_ARRAY:
- v = get_u16(ptr);
- if (is_swap)
- v = bswap16(v);
- return JS_NewInt32(ctx, v);
- case JS_CLASS_INT32_ARRAY:
- v = get_u32(ptr);
- if (is_swap)
- v = bswap32(v);
- return JS_NewInt32(ctx, v);
- case JS_CLASS_UINT32_ARRAY:
- v = get_u32(ptr);
- if (is_swap)
- v = bswap32(v);
- return JS_NewUint32(ctx, v);
- case JS_CLASS_BIG_INT64_ARRAY:
- {
- uint64_t v;
- v = get_u64(ptr);
- if (is_swap)
- v = bswap64(v);
- return JS_NewBigInt64(ctx, v);
- }
- break;
- case JS_CLASS_BIG_UINT64_ARRAY:
- {
- uint64_t v;
- v = get_u64(ptr);
- if (is_swap)
- v = bswap64(v);
- return JS_NewBigUint64(ctx, v);
- }
- break;
- case JS_CLASS_FLOAT16_ARRAY:
- {
- uint16_t v;
- v = get_u16(ptr);
- if (is_swap)
- v = bswap16(v);
- return __JS_NewFloat64(ctx, fromfp16(v));
- }
- case JS_CLASS_FLOAT32_ARRAY:
- {
- union {
- float f;
- uint32_t i;
- } u;
- v = get_u32(ptr);
- if (is_swap)
- v = bswap32(v);
- u.i = v;
- return __JS_NewFloat64(ctx, u.f);
- }
- case JS_CLASS_FLOAT64_ARRAY:
- {
- union {
- double f;
- uint64_t i;
- } u;
- u.i = get_u64(ptr);
- if (is_swap)
- u.i = bswap64(u.i);
- return __JS_NewFloat64(ctx, u.f);
- }
- default:
- abort();
- }
- }
- static JSValue js_dataview_setValue(JSContext *ctx,
- JSValueConst this_obj,
- int argc, JSValueConst *argv, int class_id)
- {
- JSTypedArray *ta;
- JSArrayBuffer *abuf;
- BOOL littleEndian, is_swap;
- int size;
- uint8_t *ptr;
- uint64_t v64;
- uint32_t v;
- uint64_t pos;
- JSValueConst val;
- ta = JS_GetOpaque2(ctx, this_obj, JS_CLASS_DATAVIEW);
- if (!ta)
- return JS_EXCEPTION;
- size = 1 << typed_array_size_log2(class_id);
- if (JS_ToIndex(ctx, &pos, argv[0]))
- return JS_EXCEPTION;
- val = argv[1];
- v = 0; /* avoid warning */
- v64 = 0; /* avoid warning */
- if (class_id <= JS_CLASS_UINT32_ARRAY) {
- if (JS_ToUint32(ctx, &v, val))
- return JS_EXCEPTION;
- } else if (class_id <= JS_CLASS_BIG_UINT64_ARRAY) {
- if (JS_ToBigInt64(ctx, (int64_t *)&v64, val))
- return JS_EXCEPTION;
- } else {
- double d;
- if (JS_ToFloat64(ctx, &d, val))
- return JS_EXCEPTION;
- if (class_id == JS_CLASS_FLOAT16_ARRAY) {
- v = tofp16(d);
- } else if (class_id == JS_CLASS_FLOAT32_ARRAY) {
- union {
- float f;
- uint32_t i;
- } u;
- u.f = d;
- v = u.i;
- } else {
- JSFloat64Union u;
- u.d = d;
- v64 = u.u64;
- }
- }
- littleEndian = argc > 2 && JS_ToBool(ctx, argv[2]);
- is_swap = littleEndian ^ !is_be();
- abuf = ta->buffer->u.array_buffer;
- if (abuf->detached)
- return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
- // order matters: this check should come before the next one
- if ((pos + size) > ta->length)
- return JS_ThrowRangeError(ctx, "out of bound");
- // test262 expects a TypeError for this and V8, in its infinite wisdom,
- // throws a "detached array buffer" exception, but IMO that doesn't make
- // sense because the buffer is not in fact detached, it's still there
- if ((int64_t)ta->offset + ta->length > abuf->byte_length)
- return JS_ThrowTypeError(ctx, "out of bound");
- ptr = abuf->data + ta->offset + pos;
- switch(class_id) {
- case JS_CLASS_INT8_ARRAY:
- case JS_CLASS_UINT8_ARRAY:
- *ptr = v;
- break;
- case JS_CLASS_INT16_ARRAY:
- case JS_CLASS_UINT16_ARRAY:
- case JS_CLASS_FLOAT16_ARRAY:
- if (is_swap)
- v = bswap16(v);
- put_u16(ptr, v);
- break;
- case JS_CLASS_INT32_ARRAY:
- case JS_CLASS_UINT32_ARRAY:
- case JS_CLASS_FLOAT32_ARRAY:
- if (is_swap)
- v = bswap32(v);
- put_u32(ptr, v);
- break;
- case JS_CLASS_BIG_INT64_ARRAY:
- case JS_CLASS_BIG_UINT64_ARRAY:
- case JS_CLASS_FLOAT64_ARRAY:
- if (is_swap)
- v64 = bswap64(v64);
- put_u64(ptr, v64);
- break;
- default:
- abort();
- }
- return JS_UNDEFINED;
- }
- static const JSCFunctionListEntry js_dataview_proto_funcs[] = {
- JS_CGETSET_DEF("buffer", js_dataview_get_buffer, NULL ),
- JS_CGETSET_DEF("byteLength", js_dataview_get_byteLength, NULL ),
- JS_CGETSET_DEF("byteOffset", js_dataview_get_byteOffset, NULL ),
- JS_CFUNC_MAGIC_DEF("getInt8", 1, js_dataview_getValue, JS_CLASS_INT8_ARRAY ),
- JS_CFUNC_MAGIC_DEF("getUint8", 1, js_dataview_getValue, JS_CLASS_UINT8_ARRAY ),
- JS_CFUNC_MAGIC_DEF("getInt16", 1, js_dataview_getValue, JS_CLASS_INT16_ARRAY ),
- JS_CFUNC_MAGIC_DEF("getUint16", 1, js_dataview_getValue, JS_CLASS_UINT16_ARRAY ),
- JS_CFUNC_MAGIC_DEF("getInt32", 1, js_dataview_getValue, JS_CLASS_INT32_ARRAY ),
- JS_CFUNC_MAGIC_DEF("getUint32", 1, js_dataview_getValue, JS_CLASS_UINT32_ARRAY ),
- JS_CFUNC_MAGIC_DEF("getBigInt64", 1, js_dataview_getValue, JS_CLASS_BIG_INT64_ARRAY ),
- JS_CFUNC_MAGIC_DEF("getBigUint64", 1, js_dataview_getValue, JS_CLASS_BIG_UINT64_ARRAY ),
- JS_CFUNC_MAGIC_DEF("getFloat16", 1, js_dataview_getValue, JS_CLASS_FLOAT16_ARRAY ),
- JS_CFUNC_MAGIC_DEF("getFloat32", 1, js_dataview_getValue, JS_CLASS_FLOAT32_ARRAY ),
- JS_CFUNC_MAGIC_DEF("getFloat64", 1, js_dataview_getValue, JS_CLASS_FLOAT64_ARRAY ),
- JS_CFUNC_MAGIC_DEF("setInt8", 2, js_dataview_setValue, JS_CLASS_INT8_ARRAY ),
- JS_CFUNC_MAGIC_DEF("setUint8", 2, js_dataview_setValue, JS_CLASS_UINT8_ARRAY ),
- JS_CFUNC_MAGIC_DEF("setInt16", 2, js_dataview_setValue, JS_CLASS_INT16_ARRAY ),
- JS_CFUNC_MAGIC_DEF("setUint16", 2, js_dataview_setValue, JS_CLASS_UINT16_ARRAY ),
- JS_CFUNC_MAGIC_DEF("setInt32", 2, js_dataview_setValue, JS_CLASS_INT32_ARRAY ),
- JS_CFUNC_MAGIC_DEF("setUint32", 2, js_dataview_setValue, JS_CLASS_UINT32_ARRAY ),
- JS_CFUNC_MAGIC_DEF("setBigInt64", 2, js_dataview_setValue, JS_CLASS_BIG_INT64_ARRAY ),
- JS_CFUNC_MAGIC_DEF("setBigUint64", 2, js_dataview_setValue, JS_CLASS_BIG_UINT64_ARRAY ),
- JS_CFUNC_MAGIC_DEF("setFloat16", 2, js_dataview_setValue, JS_CLASS_FLOAT16_ARRAY ),
- JS_CFUNC_MAGIC_DEF("setFloat32", 2, js_dataview_setValue, JS_CLASS_FLOAT32_ARRAY ),
- JS_CFUNC_MAGIC_DEF("setFloat64", 2, js_dataview_setValue, JS_CLASS_FLOAT64_ARRAY ),
- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "DataView", JS_PROP_CONFIGURABLE ),
- };
- /* Atomics */
- #ifdef CONFIG_ATOMICS
- typedef enum AtomicsOpEnum {
- ATOMICS_OP_ADD,
- ATOMICS_OP_AND,
- ATOMICS_OP_OR,
- ATOMICS_OP_SUB,
- ATOMICS_OP_XOR,
- ATOMICS_OP_EXCHANGE,
- ATOMICS_OP_COMPARE_EXCHANGE,
- ATOMICS_OP_LOAD,
- } AtomicsOpEnum;
- static JSObject *js_atomics_get_buf(JSContext *ctx,
- JSValueConst obj, JSValueConst idx_val,
- uint64_t *pidx, int is_waitable)
- {
- JSObject *p;
- JSTypedArray *ta;
- JSArrayBuffer *abuf;
- uint64_t idx;
- BOOL err;
- int old_len;
- if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
- goto fail;
- p = JS_VALUE_GET_OBJ(obj);
- if (is_waitable)
- err = (p->class_id != JS_CLASS_INT32_ARRAY &&
- p->class_id != JS_CLASS_BIG_INT64_ARRAY);
- else
- err = !(p->class_id >= JS_CLASS_INT8_ARRAY &&
- p->class_id <= JS_CLASS_BIG_UINT64_ARRAY);
- if (err) {
- fail:
- JS_ThrowTypeError(ctx, "integer TypedArray expected");
- return NULL;
- }
- ta = p->u.typed_array;
- abuf = ta->buffer->u.array_buffer;
- if (!abuf->shared) {
- if (is_waitable == 2) {
- JS_ThrowTypeError(ctx, "not a SharedArrayBuffer TypedArray");
- return NULL;
- }
- if (abuf->detached) {
- JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
- return NULL;
- }
- }
- old_len = p->u.array.count;
-
- if (JS_ToIndex(ctx, &idx, idx_val)) {
- return NULL;
- }
- if (idx >= old_len)
- goto oob;
- if (is_waitable != 1) {
- /* RevalidateAtomicAccess() */
- if (typed_array_is_oob(p)) {
- JS_ThrowTypeErrorArrayBufferOOB(ctx);
- return NULL;
- }
- if (idx >= p->u.array.count) {
- oob:
- JS_ThrowRangeError(ctx, "out-of-bound access");
- return NULL;
- }
- }
- *pidx = idx;
- return p;
- }
- static JSValue js_atomics_op(JSContext *ctx,
- JSValueConst this_obj,
- int argc, JSValueConst *argv, int op)
- {
- int size_log2;
- uint64_t v, a, rep_val, idx;
- void *ptr;
- JSValue ret;
- JSObject *p;
-
- p = js_atomics_get_buf(ctx, argv[0], argv[1], &idx, 0);
- if (!p)
- return JS_EXCEPTION;
- size_log2 = typed_array_size_log2(p->class_id);
- rep_val = 0;
- if (op == ATOMICS_OP_LOAD) {
- v = 0;
- } else {
- if (size_log2 == 3) {
- int64_t v64;
- if (JS_ToBigInt64(ctx, &v64, argv[2]))
- return JS_EXCEPTION;
- v = v64;
- if (op == ATOMICS_OP_COMPARE_EXCHANGE) {
- if (JS_ToBigInt64(ctx, &v64, argv[3]))
- return JS_EXCEPTION;
- rep_val = v64;
- }
- } else {
- uint32_t v32;
- if (JS_ToUint32(ctx, &v32, argv[2]))
- return JS_EXCEPTION;
- v = v32;
- if (op == ATOMICS_OP_COMPARE_EXCHANGE) {
- if (JS_ToUint32(ctx, &v32, argv[3]))
- return JS_EXCEPTION;
- rep_val = v32;
- }
- }
- if (typed_array_is_oob(p))
- return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
- if (idx >= p->u.array.count)
- return JS_ThrowRangeError(ctx, "out-of-bound access");
- }
- ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2);
-
- switch(op | (size_log2 << 3)) {
- #define OP(op_name, func_name) \
- case ATOMICS_OP_ ## op_name | (0 << 3): \
- a = func_name((_Atomic(uint8_t) *)ptr, v); \
- break; \
- case ATOMICS_OP_ ## op_name | (1 << 3): \
- a = func_name((_Atomic(uint16_t) *)ptr, v); \
- break; \
- case ATOMICS_OP_ ## op_name | (2 << 3): \
- a = func_name((_Atomic(uint32_t) *)ptr, v); \
- break; \
- case ATOMICS_OP_ ## op_name | (3 << 3): \
- a = func_name((_Atomic(uint64_t) *)ptr, v); \
- break;
- OP(ADD, atomic_fetch_add)
- OP(AND, atomic_fetch_and)
- OP(OR, atomic_fetch_or)
- OP(SUB, atomic_fetch_sub)
- OP(XOR, atomic_fetch_xor)
- OP(EXCHANGE, atomic_exchange)
- #undef OP
- case ATOMICS_OP_LOAD | (0 << 3):
- a = atomic_load((_Atomic(uint8_t) *)ptr);
- break;
- case ATOMICS_OP_LOAD | (1 << 3):
- a = atomic_load((_Atomic(uint16_t) *)ptr);
- break;
- case ATOMICS_OP_LOAD | (2 << 3):
- a = atomic_load((_Atomic(uint32_t) *)ptr);
- break;
- case ATOMICS_OP_LOAD | (3 << 3):
- a = atomic_load((_Atomic(uint64_t) *)ptr);
- break;
- case ATOMICS_OP_COMPARE_EXCHANGE | (0 << 3):
- {
- uint8_t v1 = v;
- atomic_compare_exchange_strong((_Atomic(uint8_t) *)ptr, &v1, rep_val);
- a = v1;
- }
- break;
- case ATOMICS_OP_COMPARE_EXCHANGE | (1 << 3):
- {
- uint16_t v1 = v;
- atomic_compare_exchange_strong((_Atomic(uint16_t) *)ptr, &v1, rep_val);
- a = v1;
- }
- break;
- case ATOMICS_OP_COMPARE_EXCHANGE | (2 << 3):
- {
- uint32_t v1 = v;
- atomic_compare_exchange_strong((_Atomic(uint32_t) *)ptr, &v1, rep_val);
- a = v1;
- }
- break;
- case ATOMICS_OP_COMPARE_EXCHANGE | (3 << 3):
- {
- uint64_t v1 = v;
- atomic_compare_exchange_strong((_Atomic(uint64_t) *)ptr, &v1, rep_val);
- a = v1;
- }
- break;
- default:
- abort();
- }
- switch(p->class_id) {
- case JS_CLASS_INT8_ARRAY:
- a = (int8_t)a;
- goto done;
- case JS_CLASS_UINT8_ARRAY:
- a = (uint8_t)a;
- goto done;
- case JS_CLASS_INT16_ARRAY:
- a = (int16_t)a;
- goto done;
- case JS_CLASS_UINT16_ARRAY:
- a = (uint16_t)a;
- goto done;
- case JS_CLASS_INT32_ARRAY:
- done:
- ret = JS_NewInt32(ctx, a);
- break;
- case JS_CLASS_UINT32_ARRAY:
- ret = JS_NewUint32(ctx, a);
- break;
- case JS_CLASS_BIG_INT64_ARRAY:
- ret = JS_NewBigInt64(ctx, a);
- break;
- case JS_CLASS_BIG_UINT64_ARRAY:
- ret = JS_NewBigUint64(ctx, a);
- break;
- default:
- abort();
- }
- return ret;
- }
- static JSValue js_atomics_store(JSContext *ctx,
- JSValueConst this_obj,
- int argc, JSValueConst *argv)
- {
- int size_log2;
- void *ptr;
- JSValue ret;
- JSObject *p;
- uint64_t idx;
- int64_t v;
- p = js_atomics_get_buf(ctx, argv[0], argv[1], &idx, 0);
- if (!p)
- return JS_EXCEPTION;
- size_log2 = typed_array_size_log2(p->class_id);
- if (size_log2 == 3) {
- ret = JS_ToBigIntFree(ctx, JS_DupValue(ctx, argv[2]));
- if (JS_IsException(ret))
- return ret;
- if (JS_ToBigInt64(ctx, &v, ret)) {
- JS_FreeValue(ctx, ret);
- return JS_EXCEPTION;
- }
- } else {
- uint32_t v32;
- /* XXX: spec, would be simpler to return the written value */
- ret = JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[2]));
- if (JS_IsException(ret))
- return ret;
- if (JS_ToUint32(ctx, &v32, ret)) {
- JS_FreeValue(ctx, ret);
- return JS_EXCEPTION;
- }
- v = v32;
- }
- if (typed_array_is_oob(p))
- return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
- if (idx >= p->u.array.count)
- return JS_ThrowRangeError(ctx, "out-of-bound access");
- ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2);
-
- switch(size_log2) {
- case 0:
- atomic_store((_Atomic(uint8_t) *)ptr, v);
- break;
- case 1:
- atomic_store((_Atomic(uint16_t) *)ptr, v);
- break;
- case 2:
- atomic_store((_Atomic(uint32_t) *)ptr, v);
- break;
- case 3:
- atomic_store((_Atomic(uint64_t) *)ptr, v);
- break;
- default:
- abort();
- }
- return ret;
- }
- static JSValue js_atomics_isLockFree(JSContext *ctx,
- JSValueConst this_obj,
- int argc, JSValueConst *argv)
- {
- int v, ret;
- if (JS_ToInt32Sat(ctx, &v, argv[0]))
- return JS_EXCEPTION;
- ret = (v == 1 || v == 2 || v == 4 || v == 8);
- return JS_NewBool(ctx, ret);
- }
- typedef struct JSAtomicsWaiter {
- struct list_head link;
- BOOL linked;
- pthread_cond_t cond;
- int32_t *ptr;
- } JSAtomicsWaiter;
- static pthread_mutex_t js_atomics_mutex = PTHREAD_MUTEX_INITIALIZER;
- static struct list_head js_atomics_waiter_list =
- LIST_HEAD_INIT(js_atomics_waiter_list);
- #if defined(__aarch64__)
- static inline void cpu_pause(void)
- {
- asm volatile("yield" ::: "memory");
- }
- #elif defined(__x86_64) || defined(__i386__)
- static inline void cpu_pause(void)
- {
- asm volatile("pause" ::: "memory");
- }
- #else
- static inline void cpu_pause(void)
- {
- }
- #endif
- // no-op: Atomics.pause() is not allowed to block or yield to another
- // thread, only to hint the CPU that it should back off for a bit;
- // the amount of work we do here is a good enough substitute
- static JSValue js_atomics_pause(JSContext *ctx, JSValueConst this_obj,
- int argc, JSValueConst *argv)
- {
- double d;
- if (argc > 0) {
- switch (JS_VALUE_GET_NORM_TAG(argv[0])) {
- case JS_TAG_FLOAT64: // accepted if and only if fraction == 0.0
- d = JS_VALUE_GET_FLOAT64(argv[0]);
- if (isfinite(d))
- if (0 == modf(d, &d))
- break;
- // fallthru
- default:
- return JS_ThrowTypeError(ctx, "not an integral number");
- case JS_TAG_UNDEFINED:
- case JS_TAG_INT:
- break;
- }
- }
- cpu_pause();
- return JS_UNDEFINED;
- }
- static JSValue js_atomics_wait(JSContext *ctx,
- JSValueConst this_obj,
- int argc, JSValueConst *argv)
- {
- JSObject *p;
- int64_t v;
- int32_t v32;
- uint64_t idx;
- void *ptr;
- int64_t timeout;
- struct timespec ts;
- JSAtomicsWaiter waiter_s, *waiter;
- int ret, size_log2, res;
- double d;
- p = js_atomics_get_buf(ctx, argv[0], argv[1], &idx, 2);
- if (!p)
- return JS_EXCEPTION;
- size_log2 = typed_array_size_log2(p->class_id);
- ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2);
-
- /* 'argv[0]' is a SharedArrayBuffer so it cannot be detached nor reduced */
- if (size_log2 == 3) {
- if (JS_ToBigInt64(ctx, &v, argv[2]))
- return JS_EXCEPTION;
- } else {
- if (JS_ToInt32(ctx, &v32, argv[2]))
- return JS_EXCEPTION;
- v = v32;
- }
- if (JS_ToFloat64(ctx, &d, argv[3]))
- return JS_EXCEPTION;
- /* must use INT64_MAX + 1 because INT64_MAX cannot be exactly represented as a double */
- if (isnan(d) || d >= 0x1p63)
- timeout = INT64_MAX;
- else if (d < 0)
- timeout = 0;
- else
- timeout = (int64_t)d;
- if (!ctx->rt->can_block)
- return JS_ThrowTypeError(ctx, "cannot block in this thread");
- /* XXX: inefficient if large number of waiters, should hash on
- 'ptr' value */
- /* XXX: use Linux futexes when available ? */
- pthread_mutex_lock(&js_atomics_mutex);
- if (size_log2 == 3) {
- res = *(int64_t *)ptr != v;
- } else {
- res = *(int32_t *)ptr != v;
- }
- if (res) {
- pthread_mutex_unlock(&js_atomics_mutex);
- return JS_AtomToString(ctx, JS_ATOM_not_equal);
- }
- waiter = &waiter_s;
- waiter->ptr = ptr;
- pthread_cond_init(&waiter->cond, NULL);
- waiter->linked = TRUE;
- list_add_tail(&waiter->link, &js_atomics_waiter_list);
- if (timeout == INT64_MAX) {
- pthread_cond_wait(&waiter->cond, &js_atomics_mutex);
- ret = 0;
- } else {
- /* XXX: use clock monotonic */
- clock_gettime(CLOCK_REALTIME, &ts);
- ts.tv_sec += timeout / 1000;
- ts.tv_nsec += (timeout % 1000) * 1000000;
- if (ts.tv_nsec >= 1000000000) {
- ts.tv_nsec -= 1000000000;
- ts.tv_sec++;
- }
- ret = pthread_cond_timedwait(&waiter->cond, &js_atomics_mutex,
- &ts);
- }
- if (waiter->linked)
- list_del(&waiter->link);
- pthread_mutex_unlock(&js_atomics_mutex);
- pthread_cond_destroy(&waiter->cond);
- if (ret == ETIMEDOUT) {
- return JS_AtomToString(ctx, JS_ATOM_timed_out);
- } else {
- return JS_AtomToString(ctx, JS_ATOM_ok);
- }
- }
- static JSValue js_atomics_notify(JSContext *ctx,
- JSValueConst this_obj,
- int argc, JSValueConst *argv)
- {
- struct list_head *el, *el1, waiter_list;
- int32_t count, n;
- uint64_t idx;
- int size_log2;
- void *ptr;
- JSAtomicsWaiter *waiter;
- JSArrayBuffer *abuf;
- JSObject *p;
-
- p = js_atomics_get_buf(ctx, argv[0], argv[1], &idx, 1);
- if (!p)
- return JS_EXCEPTION;
- size_log2 = typed_array_size_log2(p->class_id);
-
- if (JS_IsUndefined(argv[2])) {
- count = INT32_MAX;
- } else {
- if (JS_ToInt32Clamp(ctx, &count, argv[2], 0, INT32_MAX, 0))
- return JS_EXCEPTION;
- }
- n = 0;
- abuf = p->u.typed_array->buffer->u.array_buffer;
- if (abuf->shared && count > 0) {
- /* 'argv[0]' is a SharedArrayBuffer so it cannot be detached nor reduced */
- ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2);
- pthread_mutex_lock(&js_atomics_mutex);
- init_list_head(&waiter_list);
- list_for_each_safe(el, el1, &js_atomics_waiter_list) {
- waiter = list_entry(el, JSAtomicsWaiter, link);
- if (waiter->ptr == ptr) {
- list_del(&waiter->link);
- waiter->linked = FALSE;
- list_add_tail(&waiter->link, &waiter_list);
- n++;
- if (n >= count)
- break;
- }
- }
- list_for_each(el, &waiter_list) {
- waiter = list_entry(el, JSAtomicsWaiter, link);
- pthread_cond_signal(&waiter->cond);
- }
- pthread_mutex_unlock(&js_atomics_mutex);
- }
- return JS_NewInt32(ctx, n);
- }
- static const JSCFunctionListEntry js_atomics_funcs[] = {
- JS_CFUNC_MAGIC_DEF("add", 3, js_atomics_op, ATOMICS_OP_ADD ),
- JS_CFUNC_MAGIC_DEF("and", 3, js_atomics_op, ATOMICS_OP_AND ),
- JS_CFUNC_MAGIC_DEF("or", 3, js_atomics_op, ATOMICS_OP_OR ),
- JS_CFUNC_MAGIC_DEF("sub", 3, js_atomics_op, ATOMICS_OP_SUB ),
- JS_CFUNC_MAGIC_DEF("xor", 3, js_atomics_op, ATOMICS_OP_XOR ),
- JS_CFUNC_MAGIC_DEF("exchange", 3, js_atomics_op, ATOMICS_OP_EXCHANGE ),
- JS_CFUNC_MAGIC_DEF("compareExchange", 4, js_atomics_op, ATOMICS_OP_COMPARE_EXCHANGE ),
- JS_CFUNC_MAGIC_DEF("load", 2, js_atomics_op, ATOMICS_OP_LOAD ),
- JS_CFUNC_DEF("store", 3, js_atomics_store ),
- JS_CFUNC_DEF("isLockFree", 1, js_atomics_isLockFree ),
- JS_CFUNC_DEF("pause", 0, js_atomics_pause ),
- JS_CFUNC_DEF("wait", 4, js_atomics_wait ),
- JS_CFUNC_DEF("notify", 3, js_atomics_notify ),
- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Atomics", JS_PROP_CONFIGURABLE ),
- };
- static const JSCFunctionListEntry js_atomics_obj[] = {
- JS_OBJECT_DEF("Atomics", js_atomics_funcs, countof(js_atomics_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
- };
- static int JS_AddIntrinsicAtomics(JSContext *ctx)
- {
- /* add Atomics as autoinit object */
- return JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_atomics_obj, countof(js_atomics_obj));
- }
- #endif /* CONFIG_ATOMICS */
- int JS_AddIntrinsicTypedArrays(JSContext *ctx)
- {
- JSValue typed_array_base_func, typed_array_base_proto, obj;
- int i, ret;
- obj = JS_NewCConstructor(ctx, JS_CLASS_ARRAY_BUFFER, "ArrayBuffer",
- js_array_buffer_constructor, 1, JS_CFUNC_constructor, 0,
- JS_UNDEFINED,
- js_array_buffer_funcs, countof(js_array_buffer_funcs),
- js_array_buffer_proto_funcs, countof(js_array_buffer_proto_funcs),
- 0);
- if (JS_IsException(obj))
- return -1;
- JS_FreeValue(ctx, obj);
- obj = JS_NewCConstructor(ctx, JS_CLASS_SHARED_ARRAY_BUFFER, "SharedArrayBuffer",
- js_shared_array_buffer_constructor, 1, JS_CFUNC_constructor, 0,
- JS_UNDEFINED,
- js_shared_array_buffer_funcs, countof(js_shared_array_buffer_funcs),
- js_shared_array_buffer_proto_funcs, countof(js_shared_array_buffer_proto_funcs),
- 0);
- if (JS_IsException(obj))
- return -1;
- JS_FreeValue(ctx, obj);
- typed_array_base_func =
- JS_NewCConstructor(ctx, -1, "TypedArray",
- js_typed_array_base_constructor, 0, JS_CFUNC_constructor_or_func, 0,
- JS_UNDEFINED,
- js_typed_array_base_funcs, countof(js_typed_array_base_funcs),
- js_typed_array_base_proto_funcs, countof(js_typed_array_base_proto_funcs),
- JS_NEW_CTOR_NO_GLOBAL);
- if (JS_IsException(typed_array_base_func))
- return -1;
- /* TypedArray.prototype.toString must be the same object as Array.prototype.toString */
- obj = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_toString);
- if (JS_IsException(obj))
- goto fail;
- /* XXX: should use alias method in JSCFunctionListEntry */ //@@@
- typed_array_base_proto = JS_GetProperty(ctx, typed_array_base_func, JS_ATOM_prototype);
- if (JS_IsException(typed_array_base_proto))
- goto fail;
- ret = JS_DefinePropertyValue(ctx, typed_array_base_proto, JS_ATOM_toString, obj,
- JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
- JS_FreeValue(ctx, typed_array_base_proto);
- if (ret < 0)
- goto fail;
-
- /* Used to squelch a -Wcast-function-type warning. */
- JSCFunctionType ft = { .generic_magic = js_typed_array_constructor };
- for(i = JS_CLASS_UINT8C_ARRAY; i < JS_CLASS_UINT8C_ARRAY + JS_TYPED_ARRAY_COUNT; i++) {
- char buf[ATOM_GET_STR_BUF_SIZE];
- const char *name;
-
- name = JS_AtomGetStr(ctx, buf, sizeof(buf),
- JS_ATOM_Uint8ClampedArray + i - JS_CLASS_UINT8C_ARRAY);
- if (i == JS_CLASS_UINT8_ARRAY) {
- obj = JS_NewCConstructor(ctx, i, name,
- ft.generic, 3, JS_CFUNC_constructor_magic, i,
- typed_array_base_func,
- js_uint8array_funcs, countof(js_uint8array_funcs),
- js_uint8array_proto_funcs, countof(js_uint8array_proto_funcs),
- 0);
- } else {
- const JSCFunctionListEntry *bpe = js_typed_array_funcs + typed_array_size_log2(i);
- obj = JS_NewCConstructor(ctx, i, name,
- ft.generic, 3, JS_CFUNC_constructor_magic, i,
- typed_array_base_func,
- bpe, 1,
- bpe, 1,
- 0);
- }
- if (JS_IsException(obj)) {
- fail:
- JS_FreeValue(ctx, typed_array_base_func);
- return -1;
- }
- JS_FreeValue(ctx, obj);
- }
- JS_FreeValue(ctx, typed_array_base_func);
- /* DataView */
- obj = JS_NewCConstructor(ctx, JS_CLASS_DATAVIEW, "DataView",
- js_dataview_constructor, 1, JS_CFUNC_constructor, 0,
- JS_UNDEFINED,
- NULL, 0,
- js_dataview_proto_funcs, countof(js_dataview_proto_funcs),
- 0);
- if (JS_IsException(obj))
- return -1;
- JS_FreeValue(ctx, obj);
- /* Atomics */
- #ifdef CONFIG_ATOMICS
- if (JS_AddIntrinsicAtomics(ctx))
- return -1;
- #endif
- return 0;
- }
- /* WeakRef */
- typedef struct JSWeakRefData {
- JSWeakRefHeader weakref_header;
- JSValue target;
- } JSWeakRefData;
- static void js_weakref_finalizer(JSRuntime *rt, JSValue val)
- {
- JSWeakRefData *wrd = JS_GetOpaque(val, JS_CLASS_WEAK_REF);
- if (!wrd)
- return;
- js_weakref_free(rt, wrd->target);
- list_del(&wrd->weakref_header.link);
- js_free_rt(rt, wrd);
- }
- static void weakref_delete_weakref(JSRuntime *rt, JSWeakRefHeader *wh)
- {
- JSWeakRefData *wrd = container_of(wh, JSWeakRefData, weakref_header);
- if (!js_weakref_is_live(wrd->target)) {
- js_weakref_free(rt, wrd->target);
- wrd->target = JS_UNDEFINED;
- }
- }
- static JSValue js_weakref_constructor(JSContext *ctx, JSValueConst new_target,
- int argc, JSValueConst *argv)
- {
- JSValueConst arg;
- JSValue obj;
- if (JS_IsUndefined(new_target))
- return JS_ThrowTypeError(ctx, "constructor requires 'new'");
- arg = argv[0];
- if (!js_weakref_is_target(arg))
- return JS_ThrowTypeError(ctx, "invalid target");
- obj = js_create_from_ctor(ctx, new_target, JS_CLASS_WEAK_REF);
- if (JS_IsException(obj))
- return JS_EXCEPTION;
- JSWeakRefData *wrd = js_mallocz(ctx, sizeof(*wrd));
- if (!wrd) {
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- wrd->target = js_weakref_new(ctx, arg);
- wrd->weakref_header.weakref_type = JS_WEAKREF_TYPE_WEAKREF;
- list_add_tail(&wrd->weakref_header.link, &ctx->rt->weakref_list);
- JS_SetOpaque(obj, wrd);
- return obj;
- }
- static JSValue js_weakref_deref(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
- {
- JSWeakRefData *wrd = JS_GetOpaque2(ctx, this_val, JS_CLASS_WEAK_REF);
- if (!wrd)
- return JS_EXCEPTION;
- if (js_weakref_is_live(wrd->target))
- return JS_DupValue(ctx, wrd->target);
- else
- return JS_UNDEFINED;
- }
- static const JSCFunctionListEntry js_weakref_proto_funcs[] = {
- JS_CFUNC_DEF("deref", 0, js_weakref_deref ),
- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakRef", JS_PROP_CONFIGURABLE ),
- };
- static const JSClassShortDef js_weakref_class_def[] = {
- { JS_ATOM_WeakRef, js_weakref_finalizer, NULL }, /* JS_CLASS_WEAK_REF */
- };
- typedef struct JSFinRecEntry {
- struct list_head link;
- JSValue target;
- JSValue held_val;
- JSValue token;
- } JSFinRecEntry;
- typedef struct JSFinalizationRegistryData {
- JSWeakRefHeader weakref_header;
- struct list_head entries; /* list of JSFinRecEntry.link */
- JSContext *realm;
- JSValue cb;
- } JSFinalizationRegistryData;
- static void js_finrec_finalizer(JSRuntime *rt, JSValue val)
- {
- JSFinalizationRegistryData *frd = JS_GetOpaque(val, JS_CLASS_FINALIZATION_REGISTRY);
- if (frd) {
- struct list_head *el, *el1;
- list_for_each_safe(el, el1, &frd->entries) {
- JSFinRecEntry *fre = list_entry(el, JSFinRecEntry, link);
- js_weakref_free(rt, fre->target);
- js_weakref_free(rt, fre->token);
- JS_FreeValueRT(rt, fre->held_val);
- js_free_rt(rt, fre);
- }
- JS_FreeValueRT(rt, frd->cb);
- JS_FreeContext(frd->realm);
- list_del(&frd->weakref_header.link);
- js_free_rt(rt, frd);
- }
- }
- static void js_finrec_mark(JSRuntime *rt, JSValueConst val,
- JS_MarkFunc *mark_func)
- {
- JSFinalizationRegistryData *frd = JS_GetOpaque(val, JS_CLASS_FINALIZATION_REGISTRY);
- struct list_head *el;
- if (frd) {
- list_for_each(el, &frd->entries) {
- JSFinRecEntry *fre = list_entry(el, JSFinRecEntry, link);
- JS_MarkValue(rt, fre->held_val, mark_func);
- }
- JS_MarkValue(rt, frd->cb, mark_func);
- mark_func(rt, &frd->realm->header);
- }
- }
- static JSValue js_finrec_job(JSContext *ctx, int argc, JSValueConst *argv)
- {
- return JS_Call(ctx, argv[0], JS_UNDEFINED, 1, &argv[1]);
- }
- static void finrec_delete_weakref(JSRuntime *rt, JSWeakRefHeader *wh)
- {
- JSFinalizationRegistryData *frd = container_of(wh, JSFinalizationRegistryData, weakref_header);
- struct list_head *el, *el1;
- list_for_each_safe(el, el1, &frd->entries) {
- JSFinRecEntry *fre = list_entry(el, JSFinRecEntry, link);
- if (!js_weakref_is_live(fre->token)) {
- js_weakref_free(rt, fre->token);
- fre->token = JS_UNDEFINED;
- }
- if (!js_weakref_is_live(fre->target)) {
- JSValueConst args[2];
- args[0] = frd->cb;
- args[1] = fre->held_val;
- /* no exception is raised to avoid recursing into the GC */
- JS_EnqueueJob2(frd->realm, js_finrec_job, 2, args, TRUE);
-
- js_weakref_free(rt, fre->target);
- js_weakref_free(rt, fre->token);
- JS_FreeValueRT(rt, fre->held_val);
- list_del(&fre->link);
- js_free_rt(rt, fre);
- }
- }
- }
- static JSValue js_finrec_constructor(JSContext *ctx, JSValueConst new_target,
- int argc, JSValueConst *argv)
- {
- JSValueConst cb;
- JSValue obj;
- JSFinalizationRegistryData *frd;
-
- if (JS_IsUndefined(new_target))
- return JS_ThrowTypeError(ctx, "constructor requires 'new'");
- cb = argv[0];
- if (!JS_IsFunction(ctx, cb))
- return JS_ThrowTypeError(ctx, "argument must be a function");
- obj = js_create_from_ctor(ctx, new_target, JS_CLASS_FINALIZATION_REGISTRY);
- if (JS_IsException(obj))
- return JS_EXCEPTION;
- frd = js_mallocz(ctx, sizeof(*frd));
- if (!frd) {
- JS_FreeValue(ctx, obj);
- return JS_EXCEPTION;
- }
- frd->weakref_header.weakref_type = JS_WEAKREF_TYPE_FINREC;
- list_add_tail(&frd->weakref_header.link, &ctx->rt->weakref_list);
- init_list_head(&frd->entries);
- frd->realm = JS_DupContext(ctx);
- frd->cb = JS_DupValue(ctx, cb);
- JS_SetOpaque(obj, frd);
- return obj;
- }
- static JSValue js_finrec_register(JSContext *ctx, JSValueConst this_val,
- int argc, JSValueConst *argv)
- {
- JSValueConst target, held_val, token;
- JSFinalizationRegistryData *frd;
- JSFinRecEntry *fre;
- frd = JS_GetOpaque2(ctx, this_val, JS_CLASS_FINALIZATION_REGISTRY);
- if (!frd)
- return JS_EXCEPTION;
- target = argv[0];
- held_val = argv[1];
- token = argc > 2 ? argv[2] : JS_UNDEFINED;
- if (!js_weakref_is_target(target))
- return JS_ThrowTypeError(ctx, "invalid target");
- if (js_same_value(ctx, target, held_val))
- return JS_ThrowTypeError(ctx, "held value cannot be the target");
- if (!JS_IsUndefined(token) && !js_weakref_is_target(token))
- return JS_ThrowTypeError(ctx, "invalid unregister token");
- fre = js_malloc(ctx, sizeof(*fre));
- if (!fre)
- return JS_EXCEPTION;
- fre->target = js_weakref_new(ctx, target);
- fre->held_val = JS_DupValue(ctx, held_val);
- fre->token = js_weakref_new(ctx, token);
- list_add_tail(&fre->link, &frd->entries);
- return JS_UNDEFINED;
- }
- static JSValue js_finrec_unregister(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
- {
- JSFinalizationRegistryData *frd = JS_GetOpaque2(ctx, this_val, JS_CLASS_FINALIZATION_REGISTRY);
- JSValueConst token;
- BOOL removed;
- struct list_head *el, *el1;
- if (!frd)
- return JS_EXCEPTION;
- token = argv[0];
- if (!js_weakref_is_target(token))
- return JS_ThrowTypeError(ctx, "invalid unregister token");
- removed = FALSE;
- list_for_each_safe(el, el1, &frd->entries) {
- JSFinRecEntry *fre = list_entry(el, JSFinRecEntry, link);
- if (js_weakref_is_live(fre->token) && js_same_value(ctx, fre->token, token)) {
- js_weakref_free(ctx->rt, fre->target);
- js_weakref_free(ctx->rt, fre->token);
- JS_FreeValue(ctx, fre->held_val);
- list_del(&fre->link);
- js_free(ctx, fre);
- removed = TRUE;
- }
- }
- return JS_NewBool(ctx, removed);
- }
- static const JSCFunctionListEntry js_finrec_proto_funcs[] = {
- JS_CFUNC_DEF("register", 2, js_finrec_register ),
- JS_CFUNC_DEF("unregister", 1, js_finrec_unregister ),
- JS_PROP_STRING_DEF("[Symbol.toStringTag]", "FinalizationRegistry", JS_PROP_CONFIGURABLE ),
- };
- static const JSClassShortDef js_finrec_class_def[] = {
- { JS_ATOM_FinalizationRegistry, js_finrec_finalizer, js_finrec_mark }, /* JS_CLASS_FINALIZATION_REGISTRY */
- };
- int JS_AddIntrinsicWeakRef(JSContext *ctx)
- {
- JSRuntime *rt = ctx->rt;
- JSValue obj;
-
- /* WeakRef */
- if (!JS_IsRegisteredClass(rt, JS_CLASS_WEAK_REF)) {
- if (init_class_range(rt, js_weakref_class_def, JS_CLASS_WEAK_REF,
- countof(js_weakref_class_def)))
- return -1;
- }
- obj = JS_NewCConstructor(ctx, JS_CLASS_WEAK_REF, "WeakRef",
- js_weakref_constructor, 1, JS_CFUNC_constructor_or_func, 0,
- JS_UNDEFINED,
- NULL, 0,
- js_weakref_proto_funcs, countof(js_weakref_proto_funcs),
- 0);
- if (JS_IsException(obj))
- return -1;
- JS_FreeValue(ctx, obj);
- /* FinalizationRegistry */
- if (!JS_IsRegisteredClass(rt, JS_CLASS_FINALIZATION_REGISTRY)) {
- if (init_class_range(rt, js_finrec_class_def, JS_CLASS_FINALIZATION_REGISTRY,
- countof(js_finrec_class_def)))
- return -1;
- }
- obj = JS_NewCConstructor(ctx, JS_CLASS_FINALIZATION_REGISTRY, "FinalizationRegistry",
- js_finrec_constructor, 1, JS_CFUNC_constructor_or_func, 0,
- JS_UNDEFINED,
- NULL, 0,
- js_finrec_proto_funcs, countof(js_finrec_proto_funcs),
- 0);
- if (JS_IsException(obj))
- return -1;
- JS_FreeValue(ctx, obj);
- return 0;
- }
|