1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262132631326413265132661326713268132691327013271132721327313274132751327613277132781327913280132811328213283132841328513286132871328813289132901329113292132931329413295132961329713298132991330013301133021330313304133051330613307133081330913310133111331213313133141331513316133171331813319133201332113322133231332413325133261332713328133291333013331133321333313334133351333613337133381333913340133411334213343133441334513346133471334813349133501335113352133531335413355133561335713358133591336013361133621336313364133651336613367133681336913370133711337213373133741337513376133771337813379133801338113382133831338413385133861338713388133891339013391133921339313394133951339613397133981339913400134011340213403134041340513406134071340813409134101341113412134131341413415134161341713418134191342013421134221342313424134251342613427134281342913430134311343213433134341343513436134371343813439134401344113442134431344413445134461344713448134491345013451134521345313454134551345613457134581345913460134611346213463134641346513466134671346813469134701347113472134731347413475134761347713478134791348013481134821348313484134851348613487134881348913490134911349213493134941349513496134971349813499135001350113502135031350413505135061350713508135091351013511135121351313514135151351613517135181351913520135211352213523135241352513526135271352813529135301353113532135331353413535135361353713538135391354013541135421354313544135451354613547135481354913550135511355213553135541355513556135571355813559135601356113562135631356413565135661356713568135691357013571135721357313574135751357613577135781357913580135811358213583135841358513586135871358813589135901359113592135931359413595135961359713598135991360013601136021360313604136051360613607136081360913610136111361213613136141361513616136171361813619136201362113622136231362413625136261362713628136291363013631136321363313634136351363613637136381363913640136411364213643136441364513646136471364813649136501365113652136531365413655136561365713658136591366013661136621366313664136651366613667136681366913670136711367213673136741367513676136771367813679136801368113682136831368413685136861368713688136891369013691136921369313694136951369613697136981369913700137011370213703137041370513706137071370813709137101371113712137131371413715137161371713718137191372013721137221372313724137251372613727137281372913730137311373213733137341373513736137371373813739137401374113742137431374413745137461374713748137491375013751137521375313754137551375613757137581375913760137611376213763137641376513766137671376813769137701377113772137731377413775137761377713778137791378013781137821378313784137851378613787137881378913790137911379213793137941379513796137971379813799138001380113802138031380413805138061380713808138091381013811138121381313814138151381613817138181381913820138211382213823138241382513826138271382813829138301383113832138331383413835138361383713838138391384013841138421384313844138451384613847138481384913850138511385213853138541385513856138571385813859138601386113862138631386413865138661386713868138691387013871138721387313874138751387613877138781387913880138811388213883138841388513886138871388813889138901389113892138931389413895138961389713898138991390013901139021390313904139051390613907139081390913910139111391213913139141391513916139171391813919139201392113922139231392413925139261392713928139291393013931139321393313934139351393613937139381393913940139411394213943139441394513946139471394813949139501395113952139531395413955139561395713958139591396013961139621396313964139651396613967139681396913970139711397213973139741397513976139771397813979139801398113982139831398413985139861398713988139891399013991139921399313994139951399613997139981399914000140011400214003140041400514006140071400814009140101401114012140131401414015140161401714018140191402014021140221402314024140251402614027140281402914030140311403214033140341403514036140371403814039140401404114042140431404414045140461404714048140491405014051140521405314054140551405614057140581405914060140611406214063140641406514066140671406814069140701407114072140731407414075140761407714078140791408014081140821408314084140851408614087140881408914090140911409214093140941409514096140971409814099141001410114102141031410414105141061410714108141091411014111141121411314114141151411614117141181411914120141211412214123141241412514126141271412814129141301413114132141331413414135141361413714138141391414014141141421414314144141451414614147141481414914150141511415214153141541415514156141571415814159141601416114162141631416414165141661416714168141691417014171141721417314174141751417614177141781417914180141811418214183141841418514186141871418814189141901419114192141931419414195141961419714198141991420014201142021420314204142051420614207142081420914210142111421214213142141421514216142171421814219142201422114222142231422414225142261422714228142291423014231142321423314234142351423614237142381423914240142411424214243142441424514246142471424814249142501425114252142531425414255142561425714258142591426014261142621426314264142651426614267142681426914270142711427214273142741427514276142771427814279142801428114282142831428414285142861428714288142891429014291142921429314294142951429614297142981429914300143011430214303143041430514306143071430814309143101431114312143131431414315143161431714318143191432014321143221432314324143251432614327143281432914330143311433214333143341433514336143371433814339143401434114342143431434414345143461434714348143491435014351143521435314354143551435614357143581435914360143611436214363143641436514366143671436814369143701437114372143731437414375143761437714378143791438014381143821438314384143851438614387143881438914390143911439214393143941439514396143971439814399144001440114402144031440414405144061440714408144091441014411144121441314414144151441614417144181441914420144211442214423144241442514426144271442814429144301443114432144331443414435144361443714438144391444014441144421444314444144451444614447144481444914450144511445214453144541445514456144571445814459144601446114462144631446414465144661446714468144691447014471144721447314474144751447614477144781447914480144811448214483144841448514486144871448814489144901449114492144931449414495144961449714498144991450014501145021450314504145051450614507145081450914510145111451214513145141451514516145171451814519145201452114522145231452414525145261452714528145291453014531145321453314534145351453614537145381453914540145411454214543145441454514546145471454814549145501455114552145531455414555145561455714558145591456014561145621456314564145651456614567145681456914570145711457214573145741457514576145771457814579145801458114582145831458414585145861458714588145891459014591145921459314594145951459614597145981459914600146011460214603146041460514606146071460814609146101461114612146131461414615146161461714618146191462014621146221462314624146251462614627146281462914630146311463214633146341463514636146371463814639146401464114642146431464414645146461464714648146491465014651146521465314654146551465614657146581465914660146611466214663146641466514666146671466814669146701467114672146731467414675146761467714678146791468014681146821468314684146851468614687146881468914690146911469214693146941469514696146971469814699147001470114702147031470414705147061470714708147091471014711147121471314714147151471614717147181471914720147211472214723147241472514726147271472814729147301473114732147331473414735147361473714738147391474014741147421474314744147451474614747147481474914750147511475214753147541475514756147571475814759147601476114762147631476414765147661476714768147691477014771147721477314774147751477614777147781477914780147811478214783147841478514786147871478814789147901479114792147931479414795147961479714798147991480014801148021480314804148051480614807148081480914810148111481214813148141481514816148171481814819148201482114822148231482414825148261482714828148291483014831148321483314834148351483614837148381483914840148411484214843148441484514846148471484814849148501485114852148531485414855148561485714858148591486014861148621486314864148651486614867148681486914870148711487214873148741487514876148771487814879148801488114882148831488414885148861488714888148891489014891148921489314894148951489614897148981489914900149011490214903149041490514906149071490814909149101491114912149131491414915149161491714918149191492014921149221492314924149251492614927149281492914930149311493214933149341493514936149371493814939149401494114942149431494414945149461494714948149491495014951149521495314954149551495614957149581495914960149611496214963149641496514966149671496814969149701497114972149731497414975149761497714978149791498014981149821498314984149851498614987149881498914990149911499214993149941499514996149971499814999150001500115002150031500415005150061500715008150091501015011150121501315014150151501615017150181501915020150211502215023150241502515026150271502815029150301503115032150331503415035150361503715038150391504015041150421504315044150451504615047150481504915050150511505215053150541505515056150571505815059150601506115062150631506415065150661506715068150691507015071150721507315074150751507615077150781507915080150811508215083150841508515086150871508815089150901509115092150931509415095150961509715098150991510015101151021510315104151051510615107151081510915110151111511215113151141511515116151171511815119151201512115122151231512415125151261512715128151291513015131151321513315134151351513615137151381513915140151411514215143151441514515146151471514815149151501515115152151531515415155151561515715158151591516015161151621516315164151651516615167151681516915170151711517215173151741517515176151771517815179151801518115182151831518415185151861518715188151891519015191151921519315194151951519615197151981519915200152011520215203152041520515206152071520815209152101521115212152131521415215152161521715218152191522015221152221522315224152251522615227152281522915230152311523215233152341523515236152371523815239152401524115242152431524415245152461524715248152491525015251152521525315254152551525615257152581525915260152611526215263152641526515266152671526815269152701527115272152731527415275152761527715278152791528015281152821528315284152851528615287152881528915290152911529215293152941529515296152971529815299153001530115302153031530415305153061530715308153091531015311153121531315314153151531615317153181531915320153211532215323153241532515326153271532815329153301533115332153331533415335153361533715338153391534015341153421534315344153451534615347153481534915350153511535215353153541535515356153571535815359153601536115362153631536415365153661536715368153691537015371153721537315374153751537615377153781537915380153811538215383153841538515386153871538815389153901539115392153931539415395153961539715398153991540015401154021540315404154051540615407154081540915410154111541215413154141541515416154171541815419154201542115422154231542415425154261542715428154291543015431154321543315434154351543615437154381543915440154411544215443154441544515446154471544815449154501545115452154531545415455154561545715458154591546015461154621546315464154651546615467154681546915470154711547215473154741547515476154771547815479154801548115482154831548415485154861548715488154891549015491154921549315494154951549615497154981549915500155011550215503155041550515506155071550815509155101551115512155131551415515155161551715518155191552015521155221552315524155251552615527155281552915530155311553215533155341553515536155371553815539155401554115542155431554415545155461554715548155491555015551155521555315554155551555615557155581555915560155611556215563155641556515566155671556815569155701557115572155731557415575155761557715578155791558015581155821558315584155851558615587155881558915590155911559215593155941559515596155971559815599156001560115602156031560415605156061560715608156091561015611156121561315614156151561615617156181561915620156211562215623156241562515626156271562815629156301563115632156331563415635156361563715638156391564015641156421564315644156451564615647156481564915650156511565215653156541565515656156571565815659156601566115662156631566415665156661566715668156691567015671156721567315674156751567615677156781567915680156811568215683156841568515686156871568815689156901569115692156931569415695156961569715698156991570015701157021570315704157051570615707157081570915710157111571215713157141571515716157171571815719157201572115722157231572415725157261572715728157291573015731157321573315734157351573615737157381573915740157411574215743157441574515746157471574815749157501575115752157531575415755157561575715758157591576015761157621576315764157651576615767157681576915770157711577215773157741577515776157771577815779157801578115782157831578415785157861578715788157891579015791157921579315794157951579615797157981579915800158011580215803158041580515806158071580815809158101581115812158131581415815158161581715818158191582015821158221582315824158251582615827158281582915830158311583215833158341583515836158371583815839158401584115842158431584415845158461584715848158491585015851158521585315854158551585615857158581585915860158611586215863158641586515866158671586815869158701587115872158731587415875158761587715878158791588015881158821588315884158851588615887158881588915890158911589215893158941589515896158971589815899159001590115902159031590415905159061590715908159091591015911159121591315914159151591615917159181591915920159211592215923159241592515926159271592815929159301593115932159331593415935159361593715938159391594015941159421594315944159451594615947159481594915950159511595215953159541595515956159571595815959159601596115962159631596415965159661596715968159691597015971159721597315974159751597615977159781597915980159811598215983159841598515986159871598815989159901599115992159931599415995159961599715998159991600016001160021600316004160051600616007160081600916010160111601216013160141601516016160171601816019160201602116022160231602416025160261602716028160291603016031160321603316034160351603616037160381603916040160411604216043160441604516046160471604816049160501605116052160531605416055160561605716058160591606016061160621606316064160651606616067160681606916070160711607216073160741607516076160771607816079160801608116082160831608416085160861608716088160891609016091160921609316094160951609616097160981609916100161011610216103161041610516106161071610816109161101611116112161131611416115161161611716118161191612016121161221612316124161251612616127161281612916130161311613216133161341613516136161371613816139161401614116142161431614416145161461614716148161491615016151161521615316154161551615616157161581615916160161611616216163161641616516166161671616816169161701617116172161731617416175161761617716178161791618016181161821618316184161851618616187161881618916190161911619216193161941619516196161971619816199162001620116202162031620416205162061620716208162091621016211162121621316214162151621616217162181621916220162211622216223162241622516226162271622816229162301623116232162331623416235162361623716238162391624016241162421624316244162451624616247162481624916250162511625216253162541625516256162571625816259162601626116262162631626416265162661626716268162691627016271162721627316274162751627616277162781627916280162811628216283162841628516286162871628816289162901629116292162931629416295162961629716298162991630016301163021630316304163051630616307163081630916310163111631216313163141631516316163171631816319163201632116322163231632416325163261632716328163291633016331163321633316334163351633616337163381633916340163411634216343163441634516346163471634816349163501635116352163531635416355163561635716358163591636016361163621636316364163651636616367163681636916370163711637216373163741637516376163771637816379163801638116382163831638416385163861638716388163891639016391163921639316394163951639616397163981639916400164011640216403164041640516406164071640816409164101641116412164131641416415164161641716418164191642016421164221642316424164251642616427164281642916430164311643216433164341643516436164371643816439164401644116442164431644416445164461644716448164491645016451164521645316454164551645616457164581645916460164611646216463164641646516466164671646816469164701647116472164731647416475164761647716478164791648016481164821648316484164851648616487164881648916490164911649216493164941649516496164971649816499165001650116502165031650416505165061650716508165091651016511165121651316514165151651616517165181651916520165211652216523165241652516526165271652816529165301653116532165331653416535165361653716538165391654016541165421654316544165451654616547165481654916550165511655216553165541655516556165571655816559165601656116562165631656416565165661656716568165691657016571165721657316574165751657616577165781657916580165811658216583165841658516586165871658816589165901659116592165931659416595165961659716598165991660016601166021660316604166051660616607166081660916610166111661216613166141661516616166171661816619166201662116622166231662416625166261662716628166291663016631166321663316634166351663616637166381663916640166411664216643166441664516646166471664816649166501665116652166531665416655166561665716658166591666016661166621666316664166651666616667166681666916670166711667216673166741667516676166771667816679166801668116682166831668416685166861668716688166891669016691166921669316694166951669616697166981669916700167011670216703167041670516706167071670816709167101671116712167131671416715167161671716718167191672016721167221672316724167251672616727167281672916730167311673216733167341673516736167371673816739167401674116742167431674416745167461674716748167491675016751167521675316754167551675616757167581675916760167611676216763167641676516766167671676816769167701677116772167731677416775167761677716778167791678016781167821678316784167851678616787167881678916790167911679216793167941679516796167971679816799168001680116802168031680416805168061680716808168091681016811168121681316814168151681616817168181681916820168211682216823168241682516826168271682816829168301683116832168331683416835168361683716838168391684016841168421684316844168451684616847168481684916850168511685216853168541685516856168571685816859168601686116862168631686416865168661686716868168691687016871168721687316874168751687616877168781687916880168811688216883168841688516886168871688816889168901689116892168931689416895168961689716898168991690016901169021690316904169051690616907169081690916910169111691216913169141691516916169171691816919169201692116922169231692416925169261692716928169291693016931169321693316934169351693616937169381693916940169411694216943169441694516946169471694816949169501695116952169531695416955169561695716958169591696016961169621696316964169651696616967169681696916970169711697216973169741697516976169771697816979169801698116982169831698416985169861698716988169891699016991169921699316994169951699616997169981699917000170011700217003170041700517006170071700817009170101701117012170131701417015170161701717018170191702017021170221702317024170251702617027170281702917030170311703217033170341703517036170371703817039170401704117042170431704417045170461704717048170491705017051170521705317054170551705617057170581705917060170611706217063170641706517066170671706817069170701707117072170731707417075170761707717078170791708017081170821708317084170851708617087170881708917090170911709217093170941709517096170971709817099171001710117102171031710417105171061710717108171091711017111171121711317114171151711617117171181711917120171211712217123171241712517126171271712817129171301713117132171331713417135171361713717138171391714017141171421714317144171451714617147171481714917150171511715217153171541715517156171571715817159171601716117162171631716417165171661716717168171691717017171171721717317174171751717617177171781717917180171811718217183171841718517186171871718817189171901719117192171931719417195171961719717198171991720017201172021720317204172051720617207172081720917210172111721217213172141721517216172171721817219172201722117222172231722417225172261722717228172291723017231172321723317234172351723617237172381723917240172411724217243172441724517246172471724817249172501725117252172531725417255172561725717258172591726017261172621726317264172651726617267172681726917270172711727217273172741727517276172771727817279172801728117282172831728417285172861728717288172891729017291172921729317294172951729617297172981729917300173011730217303173041730517306173071730817309173101731117312173131731417315173161731717318173191732017321173221732317324173251732617327173281732917330173311733217333173341733517336173371733817339173401734117342173431734417345173461734717348173491735017351173521735317354173551735617357173581735917360173611736217363173641736517366173671736817369173701737117372173731737417375173761737717378173791738017381173821738317384173851738617387173881738917390173911739217393173941739517396173971739817399174001740117402174031740417405174061740717408174091741017411174121741317414174151741617417174181741917420174211742217423174241742517426174271742817429174301743117432174331743417435174361743717438174391744017441174421744317444174451744617447174481744917450174511745217453174541745517456174571745817459174601746117462174631746417465174661746717468174691747017471174721747317474174751747617477174781747917480174811748217483174841748517486174871748817489174901749117492174931749417495174961749717498174991750017501175021750317504175051750617507175081750917510175111751217513175141751517516175171751817519175201752117522175231752417525175261752717528175291753017531175321753317534175351753617537175381753917540175411754217543175441754517546175471754817549175501755117552 |
- # This Source Code Form is subject to the terms of the Mozilla Public
- # License, v. 2.0. If a copy of the MPL was not distributed with this file,
- # You can obtain one at http://mozilla.org/MPL/2.0/.
- # Common codegen classes.
- import os
- import re
- import string
- import math
- import textwrap
- import functools
- from WebIDL import BuiltinTypes, IDLBuiltinType, IDLNullValue, IDLSequenceType, IDLType, IDLAttribute, IDLInterfaceMember, IDLUndefinedValue, IDLEmptySequenceValue, IDLDictionary
- from Configuration import NoSuchDescriptorError, getTypesFromDescriptor, getTypesFromDictionary, getTypesFromCallback, getAllTypes, Descriptor, MemberIsUnforgeable, iteratorNativeType
- AUTOGENERATED_WARNING_COMMENT = \
- "/* THIS FILE IS AUTOGENERATED BY Codegen.py - DO NOT EDIT */\n\n"
- AUTOGENERATED_WITH_SOURCE_WARNING_COMMENT = \
- "/* THIS FILE IS AUTOGENERATED FROM %s BY Codegen.py - DO NOT EDIT */\n\n"
- ADDPROPERTY_HOOK_NAME = '_addProperty'
- FINALIZE_HOOK_NAME = '_finalize'
- OBJECT_MOVED_HOOK_NAME = '_objectMoved'
- CONSTRUCT_HOOK_NAME = '_constructor'
- LEGACYCALLER_HOOK_NAME = '_legacycaller'
- HASINSTANCE_HOOK_NAME = '_hasInstance'
- RESOLVE_HOOK_NAME = '_resolve'
- MAY_RESOLVE_HOOK_NAME = '_mayResolve'
- ENUMERATE_HOOK_NAME = '_enumerate'
- ENUM_ENTRY_VARIABLE_NAME = 'strings'
- INSTANCE_RESERVED_SLOTS = 1
- def memberReservedSlot(member, descriptor):
- return ("(DOM_INSTANCE_RESERVED_SLOTS + %d)" %
- member.slotIndices[descriptor.interface.identifier.name])
- def memberXrayExpandoReservedSlot(member, descriptor):
- return ("(xpc::JSSLOT_EXPANDO_COUNT + %d)" %
- member.slotIndices[descriptor.interface.identifier.name])
- def mayUseXrayExpandoSlots(descriptor, attr):
- assert not attr.getExtendedAttribute("NewObject")
- # For attributes whose type is a Gecko interface we always use
- # slots on the reflector for caching. Also, for interfaces that
- # don't want Xrays we obviously never use the Xray expando slot.
- return descriptor.wantsXrays and not attr.type.isGeckoInterface()
- def toStringBool(arg):
- return str(not not arg).lower()
- def toBindingNamespace(arg):
- return arg + "Binding"
- def isTypeCopyConstructible(type):
- # Nullable and sequence stuff doesn't affect copy-constructibility
- type = type.unroll()
- return (type.isPrimitive() or type.isString() or type.isEnum() or
- (type.isUnion() and
- CGUnionStruct.isUnionCopyConstructible(type)) or
- (type.isDictionary() and
- CGDictionary.isDictionaryCopyConstructible(type.inner)) or
- # Interface types are only copy-constructible if they're Gecko
- # interfaces. SpiderMonkey interfaces are not copy-constructible
- # because of rooting issues.
- (type.isInterface() and type.isGeckoInterface()))
- def idlTypeNeedsCycleCollection(type):
- type = type.unroll() # Takes care of sequences and nullables
- if ((type.isPrimitive() and type.tag() in builtinNames) or
- type.isEnum() or
- type.isString() or
- type.isAny() or
- type.isObject() or
- type.isSpiderMonkeyInterface()):
- return False
- elif type.isCallback() or type.isGeckoInterface():
- return True
- elif type.isUnion():
- return any(idlTypeNeedsCycleCollection(t) for t in type.flatMemberTypes)
- elif type.isRecord():
- if idlTypeNeedsCycleCollection(type.inner):
- raise TypeError("Cycle collection for type %s is not supported" % type)
- return False
- elif type.isDictionary():
- if any(idlTypeNeedsCycleCollection(m.type) for m in type.inner.members):
- raise TypeError("Cycle collection for type %s is not supported" % type)
- return False
- else:
- raise TypeError("Don't know whether to cycle-collect type %s" % type)
- def wantsAddProperty(desc):
- return (desc.concrete and desc.wrapperCache and not desc.isGlobal())
- # We'll want to insert the indent at the beginnings of lines, but we
- # don't want to indent empty lines. So only indent lines that have a
- # non-newline character on them.
- lineStartDetector = re.compile("^(?=[^\n#])", re.MULTILINE)
- def indent(s, indentLevel=2):
- """
- Indent C++ code.
- Weird secret feature: this doesn't indent lines that start with # (such as
- #include lines or #ifdef/#endif).
- """
- if s == "":
- return s
- return re.sub(lineStartDetector, indentLevel * " ", s)
- # dedent() and fill() are often called on the same string multiple
- # times. We want to memoize their return values so we don't keep
- # recomputing them all the time.
- def memoize(fn):
- """
- Decorator to memoize a function of one argument. The cache just
- grows without bound.
- """
- cache = {}
- @functools.wraps(fn)
- def wrapper(arg):
- retval = cache.get(arg)
- if retval is None:
- retval = cache[arg] = fn(arg)
- return retval
- return wrapper
- @memoize
- def dedent(s):
- """
- Remove all leading whitespace from s, and remove a blank line
- at the beginning.
- """
- if s.startswith('\n'):
- s = s[1:]
- return textwrap.dedent(s)
- # This works by transforming the fill()-template to an equivalent
- # string.Template.
- fill_multiline_substitution_re = re.compile(r"( *)\$\*{(\w+)}(\n)?")
- find_substitutions = re.compile(r"\${")
- @memoize
- def compile_fill_template(template):
- """
- Helper function for fill(). Given the template string passed to fill(),
- do the reusable part of template processing and return a pair (t,
- argModList) that can be used every time fill() is called with that
- template argument.
- argsModList is list of tuples that represent modifications to be
- made to args. Each modification has, in order: i) the arg name,
- ii) the modified name, iii) the indent depth.
- """
- t = dedent(template)
- assert t.endswith("\n") or "\n" not in t
- argModList = []
- def replace(match):
- """
- Replaces a line like ' $*{xyz}\n' with '${xyz_n}',
- where n is the indent depth, and add a corresponding entry to
- argModList.
- Note that this needs to close over argModList, so it has to be
- defined inside compile_fill_template().
- """
- indentation, name, nl = match.groups()
- depth = len(indentation)
- # Check that $*{xyz} appears by itself on a line.
- prev = match.string[:match.start()]
- if (prev and not prev.endswith("\n")) or nl is None:
- raise ValueError("Invalid fill() template: $*{%s} must appear by itself on a line" % name)
- # Now replace this whole line of template with the indented equivalent.
- modified_name = name + "_" + str(depth)
- argModList.append((name, modified_name, depth))
- return "${" + modified_name + "}"
- t = re.sub(fill_multiline_substitution_re, replace, t)
- if not re.search(find_substitutions, t):
- raise TypeError("Using fill() when dedent() would do.")
- return (string.Template(t), argModList)
- def fill(template, **args):
- """
- Convenience function for filling in a multiline template.
- `fill(template, name1=v1, name2=v2)` is a lot like
- `string.Template(template).substitute({"name1": v1, "name2": v2})`.
- However, it's shorter, and has a few nice features:
- * If `template` is indented, fill() automatically dedents it!
- This makes code using fill() with Python's multiline strings
- much nicer to look at.
- * If `template` starts with a blank line, fill() strips it off.
- (Again, convenient with multiline strings.)
- * fill() recognizes a special kind of substitution
- of the form `$*{name}`.
- Use this to paste in, and automatically indent, multiple lines.
- (Mnemonic: The `*` is for "multiple lines").
- A `$*` substitution must appear by itself on a line, with optional
- preceding indentation (spaces only). The whole line is replaced by the
- corresponding keyword argument, indented appropriately. If the
- argument is an empty string, no output is generated, not even a blank
- line.
- """
- t, argModList = compile_fill_template(template)
- # Now apply argModList to args
- for (name, modified_name, depth) in argModList:
- if not (args[name] == "" or args[name].endswith("\n")):
- raise ValueError("Argument %s with value %r is missing a newline" % (name, args[name]))
- args[modified_name] = indent(args[name], depth)
- return t.substitute(args)
- class CGThing():
- """
- Abstract base class for things that spit out code.
- """
- def __init__(self):
- pass # Nothing for now
- def declare(self):
- """Produce code for a header file."""
- assert False # Override me!
- def define(self):
- """Produce code for a cpp file."""
- assert False # Override me!
- def deps(self):
- """Produce the deps for a pp file"""
- assert False # Override me!
- class CGStringTable(CGThing):
- """
- Generate a string table for the given strings with a function accessor:
- const char *accessorName(unsigned int index) {
- static const char table[] = "...";
- static const uint16_t indices = { ... };
- return &table[indices[index]];
- }
- This is more efficient than the more natural:
- const char *table[] = {
- ...
- };
- The uint16_t indices are smaller than the pointer equivalents, and the
- string table requires no runtime relocations.
- """
- def __init__(self, accessorName, strings):
- CGThing.__init__(self)
- self.accessorName = accessorName
- self.strings = strings
- def declare(self):
- return "extern const char *%s(unsigned int aIndex);\n" % self.accessorName
- def define(self):
- table = ' "\\0" '.join('"%s"' % s for s in self.strings)
- indices = []
- currentIndex = 0
- for s in self.strings:
- indices.append(currentIndex)
- currentIndex += len(s) + 1 # for the null terminator
- return fill(
- """
- const char *${name}(unsigned int aIndex)
- {
- static const char table[] = ${table};
- static const uint16_t indices[] = { ${indices} };
- static_assert(${currentIndex} <= UINT16_MAX, "string table overflow!");
- return &table[indices[aIndex]];
- }
- """,
- name=self.accessorName,
- table=table,
- indices=", ".join("%d" % index for index in indices),
- currentIndex=currentIndex)
- class CGNativePropertyHooks(CGThing):
- """
- Generate a NativePropertyHooks for a given descriptor
- """
- def __init__(self, descriptor, properties):
- CGThing.__init__(self)
- self.descriptor = descriptor
- self.properties = properties
- def declare(self):
- if not self.descriptor.wantsXrays:
- return ""
- return dedent("""
- // We declare this as an array so that retrieving a pointer to this
- // binding's property hooks only requires compile/link-time resolvable
- // address arithmetic. Declaring it as a pointer instead would require
- // doing a run-time load to fetch a pointer to this binding's property
- // hooks. And then structures which embedded a pointer to this structure
- // would require a run-time load for proper initialization, which would
- // then induce static constructors. Lots of static constructors.
- extern const NativePropertyHooks sNativePropertyHooks[];
- """)
- def define(self):
- if not self.descriptor.wantsXrays:
- return ""
- deleteNamedProperty = "nullptr"
- if self.descriptor.concrete and self.descriptor.proxy:
- resolveOwnProperty = "ResolveOwnProperty"
- enumerateOwnProperties = "EnumerateOwnProperties"
- if self.descriptor.needsXrayNamedDeleterHook():
- deleteNamedProperty = "DeleteNamedProperty"
- elif self.descriptor.needsXrayResolveHooks():
- resolveOwnProperty = "ResolveOwnPropertyViaResolve"
- enumerateOwnProperties = "EnumerateOwnPropertiesViaGetOwnPropertyNames"
- else:
- resolveOwnProperty = "nullptr"
- enumerateOwnProperties = "nullptr"
- if self.properties.hasNonChromeOnly():
- regular = "sNativeProperties.Upcast()"
- else:
- regular = "nullptr"
- if self.properties.hasChromeOnly():
- chrome = "sChromeOnlyNativeProperties.Upcast()"
- else:
- chrome = "nullptr"
- constructorID = "constructors::id::"
- if self.descriptor.interface.hasInterfaceObject():
- constructorID += self.descriptor.name
- else:
- constructorID += "_ID_Count"
- prototypeID = "prototypes::id::"
- if self.descriptor.interface.hasInterfacePrototypeObject():
- prototypeID += self.descriptor.name
- else:
- prototypeID += "_ID_Count"
- parentProtoName = self.descriptor.parentPrototypeName
- parentHooks = (toBindingNamespace(parentProtoName) + "::sNativePropertyHooks"
- if parentProtoName else 'nullptr')
- if self.descriptor.wantsXrayExpandoClass:
- expandoClass = "&sXrayExpandoObjectClass"
- else:
- expandoClass = "&DefaultXrayExpandoObjectClass"
- return fill(
- """
- const NativePropertyHooks sNativePropertyHooks[] = { {
- ${resolveOwnProperty},
- ${enumerateOwnProperties},
- ${deleteNamedProperty},
- { ${regular}, ${chrome} },
- ${prototypeID},
- ${constructorID},
- ${parentHooks},
- ${expandoClass}
- } };
- """,
- resolveOwnProperty=resolveOwnProperty,
- enumerateOwnProperties=enumerateOwnProperties,
- deleteNamedProperty=deleteNamedProperty,
- regular=regular,
- chrome=chrome,
- prototypeID=prototypeID,
- constructorID=constructorID,
- parentHooks=parentHooks,
- expandoClass=expandoClass)
- def NativePropertyHooks(descriptor):
- return "&sEmptyNativePropertyHooks" if not descriptor.wantsXrays else "sNativePropertyHooks"
- def DOMClass(descriptor):
- protoList = ['prototypes::id::' + proto for proto in descriptor.prototypeNameChain]
- # Pad out the list to the right length with _ID_Count so we
- # guarantee that all the lists are the same length. _ID_Count
- # is never the ID of any prototype, so it's safe to use as
- # padding.
- protoList.extend(['prototypes::id::_ID_Count'] * (descriptor.config.maxProtoChainLength - len(protoList)))
- return fill(
- """
- { ${protoChain} },
- IsBaseOf<nsISupports, ${nativeType} >::value,
- ${hooks},
- FindAssociatedGlobalForNative<${nativeType}>::Get,
- GetProtoObjectHandle,
- GetCCParticipant<${nativeType}>::Get()
- """,
- protoChain=', '.join(protoList),
- nativeType=descriptor.nativeType,
- hooks=NativePropertyHooks(descriptor))
- class CGDOMJSClass(CGThing):
- """
- Generate a DOMJSClass for a given descriptor
- """
- def __init__(self, descriptor):
- CGThing.__init__(self)
- self.descriptor = descriptor
- def declare(self):
- return ""
- def define(self):
- callHook = LEGACYCALLER_HOOK_NAME if self.descriptor.operations["LegacyCaller"] else 'nullptr'
- objectMovedHook = OBJECT_MOVED_HOOK_NAME if self.descriptor.wrapperCache else 'nullptr'
- slotCount = INSTANCE_RESERVED_SLOTS + self.descriptor.interface.totalMembersInSlots
- classFlags = "JSCLASS_IS_DOMJSCLASS | JSCLASS_FOREGROUND_FINALIZE | "
- if self.descriptor.isGlobal():
- classFlags += "JSCLASS_DOM_GLOBAL | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS)"
- traceHook = "JS_GlobalObjectTraceHook"
- reservedSlots = "JSCLASS_GLOBAL_APPLICATION_SLOTS"
- else:
- classFlags += "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount
- traceHook = 'nullptr'
- reservedSlots = slotCount
- if self.descriptor.interface.isProbablyShortLivingObject():
- classFlags += " | JSCLASS_SKIP_NURSERY_FINALIZE"
- if self.descriptor.interface.getExtendedAttribute("NeedResolve"):
- resolveHook = RESOLVE_HOOK_NAME
- mayResolveHook = MAY_RESOLVE_HOOK_NAME
- enumerateHook = ENUMERATE_HOOK_NAME
- elif self.descriptor.isGlobal():
- resolveHook = "mozilla::dom::ResolveGlobal"
- mayResolveHook = "mozilla::dom::MayResolveGlobal"
- enumerateHook = "mozilla::dom::EnumerateGlobal"
- else:
- resolveHook = "nullptr"
- mayResolveHook = "nullptr"
- enumerateHook = "nullptr"
- return fill(
- """
- static const js::ClassOps sClassOps = {
- ${addProperty}, /* addProperty */
- nullptr, /* delProperty */
- nullptr, /* getProperty */
- nullptr, /* setProperty */
- ${enumerate}, /* enumerate */
- ${resolve}, /* resolve */
- ${mayResolve}, /* mayResolve */
- ${finalize}, /* finalize */
- ${call}, /* call */
- nullptr, /* hasInstance */
- nullptr, /* construct */
- ${trace}, /* trace */
- };
- static const js::ClassExtension sClassExtension = {
- nullptr, /* weakmapKeyDelegateOp */
- ${objectMoved} /* objectMovedOp */
- };
- static const DOMJSClass sClass = {
- { "${name}",
- ${flags},
- &sClassOps,
- JS_NULL_CLASS_SPEC,
- &sClassExtension,
- JS_NULL_OBJECT_OPS
- },
- $*{descriptor}
- };
- static_assert(${instanceReservedSlots} == DOM_INSTANCE_RESERVED_SLOTS,
- "Must have the right minimal number of reserved slots.");
- static_assert(${reservedSlots} >= ${slotCount},
- "Must have enough reserved slots.");
- """,
- name=self.descriptor.interface.identifier.name,
- flags=classFlags,
- addProperty=ADDPROPERTY_HOOK_NAME if wantsAddProperty(self.descriptor) else 'nullptr',
- enumerate=enumerateHook,
- resolve=resolveHook,
- mayResolve=mayResolveHook,
- finalize=FINALIZE_HOOK_NAME,
- call=callHook,
- trace=traceHook,
- objectMoved=objectMovedHook,
- descriptor=DOMClass(self.descriptor),
- instanceReservedSlots=INSTANCE_RESERVED_SLOTS,
- reservedSlots=reservedSlots,
- slotCount=slotCount)
- class CGDOMProxyJSClass(CGThing):
- """
- Generate a DOMJSClass for a given proxy descriptor
- """
- def __init__(self, descriptor):
- CGThing.__init__(self)
- self.descriptor = descriptor
- def declare(self):
- return ""
- def define(self):
- flags = ["JSCLASS_IS_DOMJSCLASS"]
- # We don't use an IDL annotation for JSCLASS_EMULATES_UNDEFINED because
- # we don't want people ever adding that to any interface other than
- # HTMLAllCollection. So just hardcode it here.
- if self.descriptor.interface.identifier.name == "HTMLAllCollection":
- flags.append("JSCLASS_EMULATES_UNDEFINED")
- objectMovedHook = OBJECT_MOVED_HOOK_NAME if self.descriptor.wrapperCache else 'nullptr'
- return fill(
- """
- static const js::ClassExtension sClassExtension = PROXY_MAKE_EXT(
- ${objectMoved}
- );
- static const DOMJSClass sClass = {
- PROXY_CLASS_WITH_EXT("${name}",
- ${flags},
- &sClassExtension),
- $*{descriptor}
- };
- """,
- name=self.descriptor.interface.identifier.name,
- flags=" | ".join(flags),
- objectMoved=objectMovedHook,
- descriptor=DOMClass(self.descriptor))
- class CGXrayExpandoJSClass(CGThing):
- """
- Generate a JSClass for an Xray expando object. This is only
- needed if we have members in slots (for [Cached] or [StoreInSlot]
- stuff).
- """
- def __init__(self, descriptor):
- assert descriptor.interface.totalMembersInSlots != 0
- assert descriptor.wantsXrays
- assert descriptor.wantsXrayExpandoClass
- CGThing.__init__(self)
- self.descriptor = descriptor;
- def declare(self):
- return ""
- def define(self):
- return fill(
- """
- // This may allocate too many slots, because we only really need
- // slots for our non-interface-typed members that we cache. But
- // allocating slots only for those would make the slot index
- // computations much more complicated, so let's do this the simple
- // way for now.
- DEFINE_XRAY_EXPANDO_CLASS(static, sXrayExpandoObjectClass, ${memberSlots});
- """,
- memberSlots=self.descriptor.interface.totalMembersInSlots)
- def PrototypeIDAndDepth(descriptor):
- prototypeID = "prototypes::id::"
- if descriptor.interface.hasInterfacePrototypeObject():
- prototypeID += descriptor.interface.identifier.name
- depth = "PrototypeTraits<%s>::Depth" % prototypeID
- else:
- prototypeID += "_ID_Count"
- depth = "0"
- return (prototypeID, depth)
- def InterfacePrototypeObjectProtoGetter(descriptor):
- """
- Returns a tuple with two elements:
- 1) The name of the function to call to get the prototype to use for the
- interface prototype object as a JSObject*.
- 2) The name of the function to call to get the prototype to use for the
- interface prototype object as a JS::Handle<JSObject*> or None if no
- such function exists.
- """
- parentProtoName = descriptor.parentPrototypeName
- if descriptor.hasNamedPropertiesObject:
- protoGetter = "GetNamedPropertiesObject"
- protoHandleGetter = None
- elif parentProtoName is None:
- if descriptor.interface.getExtendedAttribute("ArrayClass"):
- protoGetter = "JS::GetRealmArrayPrototype"
- elif descriptor.interface.getExtendedAttribute("ExceptionClass"):
- protoGetter = "JS::GetRealmErrorPrototype"
- elif descriptor.interface.isIteratorInterface():
- protoGetter = "JS::GetRealmIteratorPrototype"
- else:
- protoGetter = "JS::GetRealmObjectPrototype"
- protoHandleGetter = None
- else:
- prefix = toBindingNamespace(parentProtoName)
- protoGetter = prefix + "::GetProtoObject"
- protoHandleGetter = prefix + "::GetProtoObjectHandle"
- return (protoGetter, protoHandleGetter)
- class CGPrototypeJSClass(CGThing):
- def __init__(self, descriptor, properties):
- CGThing.__init__(self)
- self.descriptor = descriptor
- self.properties = properties
- def declare(self):
- # We're purely for internal consumption
- return ""
- def define(self):
- prototypeID, depth = PrototypeIDAndDepth(self.descriptor)
- slotCount = "DOM_INTERFACE_PROTO_SLOTS_BASE"
- # Globals handle unforgeables directly in Wrap() instead of
- # via a holder.
- if (self.descriptor.hasUnforgeableMembers and
- not self.descriptor.isGlobal()):
- slotCount += " + 1 /* slot for the JSObject holding the unforgeable properties */"
- (protoGetter, _) = InterfacePrototypeObjectProtoGetter(self.descriptor)
- type = "eGlobalInterfacePrototype" if self.descriptor.isGlobal() else "eInterfacePrototype"
- return fill(
- """
- static const DOMIfaceAndProtoJSClass sPrototypeClass = {
- {
- "${name}Prototype",
- JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}),
- JS_NULL_CLASS_OPS,
- JS_NULL_CLASS_SPEC,
- JS_NULL_CLASS_EXT,
- JS_NULL_OBJECT_OPS
- },
- ${type},
- false,
- ${prototypeID},
- ${depth},
- ${hooks},
- "[object ${name}Prototype]",
- ${protoGetter}
- };
- """,
- name=self.descriptor.interface.identifier.name,
- slotCount=slotCount,
- type=type,
- hooks=NativePropertyHooks(self.descriptor),
- prototypeID=prototypeID,
- depth=depth,
- protoGetter=protoGetter)
- def NeedsGeneratedHasInstance(descriptor):
- assert descriptor.interface.hasInterfaceObject()
- return descriptor.hasXPConnectImpls or descriptor.interface.isConsequential()
- def InterfaceObjectProtoGetter(descriptor, forXrays=False):
- """
- Returns a tuple with two elements:
- 1) The name of the function to call to get the prototype to use for the
- interface object as a JSObject*.
- 2) The name of the function to call to get the prototype to use for the
- interface prototype as a JS::Handle<JSObject*> or None if no such
- function exists.
- """
- parentInterface = descriptor.interface.parent
- if parentInterface:
- assert not descriptor.interface.isNamespace()
- parentIfaceName = parentInterface.identifier.name
- parentDesc = descriptor.getDescriptor(parentIfaceName)
- prefix = toBindingNamespace(parentDesc.name)
- protoGetter = prefix + "::GetConstructorObject"
- protoHandleGetter = prefix + "::GetConstructorObjectHandle"
- elif descriptor.interface.isNamespace():
- if (forXrays or
- not descriptor.interface.getExtendedAttribute("ProtoObjectHack")):
- protoGetter = "JS::GetRealmObjectPrototype"
- else:
- protoGetter = "binding_detail::GetHackedNamespaceProtoObject"
- protoHandleGetter = None
- else:
- protoGetter = "JS::GetRealmFunctionPrototype"
- protoHandleGetter = None
- return (protoGetter, protoHandleGetter)
- class CGInterfaceObjectJSClass(CGThing):
- def __init__(self, descriptor, properties):
- CGThing.__init__(self)
- self.descriptor = descriptor
- self.properties = properties
- def declare(self):
- # We're purely for internal consumption
- return ""
- def define(self):
- if self.descriptor.interface.ctor():
- assert not self.descriptor.interface.isNamespace()
- ctorname = CONSTRUCT_HOOK_NAME
- elif self.descriptor.interface.isNamespace():
- ctorname = "nullptr"
- else:
- ctorname = "ThrowingConstructor"
- needsHasInstance = (
- not NeedsGeneratedHasInstance(self.descriptor) and
- self.descriptor.interface.hasInterfacePrototypeObject())
- prototypeID, depth = PrototypeIDAndDepth(self.descriptor)
- slotCount = "DOM_INTERFACE_SLOTS_BASE"
- if len(self.descriptor.interface.namedConstructors) > 0:
- slotCount += (" + %i /* slots for the named constructors */" %
- len(self.descriptor.interface.namedConstructors))
- (protoGetter, _) = InterfaceObjectProtoGetter(self.descriptor,
- forXrays=True)
- if ctorname == "ThrowingConstructor":
- ret = ""
- classOpsPtr = "&sBoringInterfaceObjectClassClassOps"
- elif ctorname == "nullptr":
- ret = ""
- classOpsPtr = "JS_NULL_CLASS_OPS"
- else:
- ret = fill(
- """
- static const js::ClassOps sInterfaceObjectClassOps = {
- nullptr, /* addProperty */
- nullptr, /* delProperty */
- nullptr, /* getProperty */
- nullptr, /* setProperty */
- nullptr, /* enumerate */
- nullptr, /* resolve */
- nullptr, /* mayResolve */
- nullptr, /* finalize */
- ${ctorname}, /* call */
- nullptr, /* hasInstance */
- ${ctorname}, /* construct */
- nullptr, /* trace */
- };
- """,
- ctorname=ctorname)
- classOpsPtr = "&sInterfaceObjectClassOps"
- if self.descriptor.interface.isNamespace():
- classString = self.descriptor.interface.getExtendedAttribute("ClassString")
- if classString is None:
- classString = "Object"
- else:
- classString = classString[0]
- toStringResult = "[object %s]" % classString
- objectOps = "JS_NULL_OBJECT_OPS"
- else:
- classString = "Function"
- toStringResult = ("function %s() {\\n [native code]\\n}" %
- self.descriptor.interface.identifier.name)
- # We need non-default ObjectOps so we can actually make
- # use of our toStringResult.
- objectOps = "&sInterfaceObjectClassObjectOps"
- ret = ret + fill(
- """
- static const DOMIfaceAndProtoJSClass sInterfaceObjectClass = {
- {
- "${classString}",
- JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}),
- ${classOpsPtr},
- JS_NULL_CLASS_SPEC,
- JS_NULL_CLASS_EXT,
- ${objectOps}
- },
- eInterface,
- ${needsHasInstance},
- ${prototypeID},
- ${depth},
- ${hooks},
- "${toStringResult}",
- ${protoGetter}
- };
- """,
- classString=classString,
- slotCount=slotCount,
- classOpsPtr=classOpsPtr,
- hooks=NativePropertyHooks(self.descriptor),
- objectOps=objectOps,
- needsHasInstance=toStringBool(needsHasInstance),
- prototypeID=prototypeID,
- depth=depth,
- toStringResult=toStringResult,
- protoGetter=protoGetter)
- return ret
- class CGList(CGThing):
- """
- Generate code for a list of GCThings. Just concatenates them together, with
- an optional joiner string. "\n" is a common joiner.
- """
- def __init__(self, children, joiner=""):
- CGThing.__init__(self)
- # Make a copy of the kids into a list, because if someone passes in a
- # generator we won't be able to both declare and define ourselves, or
- # define ourselves more than once!
- self.children = list(children)
- self.joiner = joiner
- def append(self, child):
- self.children.append(child)
- def prepend(self, child):
- self.children.insert(0, child)
- def extend(self, kids):
- self.children.extend(kids)
- def join(self, iterable):
- return self.joiner.join(s for s in iterable if len(s) > 0)
- def declare(self):
- return self.join(child.declare() for child in self.children if child is not None)
- def define(self):
- return self.join(child.define() for child in self.children if child is not None)
- def deps(self):
- deps = set()
- for child in self.children:
- if child is None:
- continue
- deps = deps.union(child.deps())
- return deps
- def __len__(self):
- return len(self.children)
- class CGGeneric(CGThing):
- """
- A class that spits out a fixed string into the codegen. Can spit out a
- separate string for the declaration too.
- """
- def __init__(self, define="", declare=""):
- self.declareText = declare
- self.defineText = define
- def declare(self):
- return self.declareText
- def define(self):
- return self.defineText
- def deps(self):
- return set()
- class CGIndenter(CGThing):
- """
- A class that takes another CGThing and generates code that indents that
- CGThing by some number of spaces. The default indent is two spaces.
- """
- def __init__(self, child, indentLevel=2, declareOnly=False):
- assert isinstance(child, CGThing)
- CGThing.__init__(self)
- self.child = child
- self.indentLevel = indentLevel
- self.declareOnly = declareOnly
- def declare(self):
- return indent(self.child.declare(), self.indentLevel)
- def define(self):
- defn = self.child.define()
- if self.declareOnly:
- return defn
- else:
- return indent(defn, self.indentLevel)
- class CGWrapper(CGThing):
- """
- Generic CGThing that wraps other CGThings with pre and post text.
- """
- def __init__(self, child, pre="", post="", declarePre=None,
- declarePost=None, definePre=None, definePost=None,
- declareOnly=False, defineOnly=False, reindent=False):
- CGThing.__init__(self)
- self.child = child
- self.declarePre = declarePre or pre
- self.declarePost = declarePost or post
- self.definePre = definePre or pre
- self.definePost = definePost or post
- self.declareOnly = declareOnly
- self.defineOnly = defineOnly
- self.reindent = reindent
- def declare(self):
- if self.defineOnly:
- return ''
- decl = self.child.declare()
- if self.reindent:
- decl = self.reindentString(decl, self.declarePre)
- return self.declarePre + decl + self.declarePost
- def define(self):
- if self.declareOnly:
- return ''
- defn = self.child.define()
- if self.reindent:
- defn = self.reindentString(defn, self.definePre)
- return self.definePre + defn + self.definePost
- @staticmethod
- def reindentString(stringToIndent, widthString):
- # We don't use lineStartDetector because we don't want to
- # insert whitespace at the beginning of our _first_ line.
- # Use the length of the last line of width string, in case
- # it is a multiline string.
- lastLineWidth = len(widthString.splitlines()[-1])
- return stripTrailingWhitespace(
- stringToIndent.replace("\n", "\n" + (" " * lastLineWidth)))
- def deps(self):
- return self.child.deps()
- class CGIfWrapper(CGList):
- def __init__(self, child, condition):
- CGList.__init__(self, [
- CGWrapper(CGGeneric(condition), pre="if (", post=") {\n", reindent=True),
- CGIndenter(child),
- CGGeneric("}\n")
- ])
- class CGIfElseWrapper(CGList):
- def __init__(self, condition, ifTrue, ifFalse):
- CGList.__init__(self, [
- CGWrapper(CGGeneric(condition), pre="if (", post=") {\n", reindent=True),
- CGIndenter(ifTrue),
- CGGeneric("} else {\n"),
- CGIndenter(ifFalse),
- CGGeneric("}\n")
- ])
- class CGElseChain(CGThing):
- """
- Concatenate if statements in an if-else-if-else chain.
- """
- def __init__(self, children):
- self.children = [c for c in children if c is not None]
- def declare(self):
- assert False
- def define(self):
- if not self.children:
- return ""
- s = self.children[0].define()
- assert s.endswith("\n")
- for child in self.children[1:]:
- code = child.define()
- assert code.startswith("if") or code.startswith("{")
- assert code.endswith("\n")
- s = s.rstrip() + " else " + code
- return s
- class CGTemplatedType(CGWrapper):
- def __init__(self, templateName, child, isConst=False, isReference=False):
- if isinstance(child, list):
- child = CGList(child, ", ")
- const = "const " if isConst else ""
- pre = "%s%s<" % (const, templateName)
- ref = "&" if isReference else ""
- post = ">%s" % ref
- CGWrapper.__init__(self, child, pre=pre, post=post)
- class CGNamespace(CGWrapper):
- def __init__(self, namespace, child, declareOnly=False):
- pre = "namespace %s {\n" % namespace
- post = "} // namespace %s\n" % namespace
- CGWrapper.__init__(self, child, pre=pre, post=post,
- declareOnly=declareOnly)
- @staticmethod
- def build(namespaces, child, declareOnly=False):
- """
- Static helper method to build multiple wrapped namespaces.
- """
- if not namespaces:
- return CGWrapper(child, declareOnly=declareOnly)
- inner = CGNamespace.build(namespaces[1:], child, declareOnly=declareOnly)
- return CGNamespace(namespaces[0], inner, declareOnly=declareOnly)
- class CGIncludeGuard(CGWrapper):
- """
- Generates include guards for a header.
- """
- def __init__(self, prefix, child):
- """|prefix| is the filename without the extension."""
- define = 'mozilla_dom_%s_h' % prefix
- CGWrapper.__init__(self, child,
- declarePre='#ifndef %s\n#define %s\n\n' % (define, define),
- declarePost='\n#endif // %s\n' % define)
- class CGHeaders(CGWrapper):
- """
- Generates the appropriate include statements.
- """
- def __init__(self, descriptors, dictionaries, callbacks,
- callbackDescriptors,
- declareIncludes, defineIncludes, prefix, child,
- config=None, jsImplementedDescriptors=[]):
- """
- Builds a set of includes to cover |descriptors|.
- Also includes the files in |declareIncludes| in the header
- file and the files in |defineIncludes| in the .cpp.
- |prefix| contains the basename of the file that we generate include
- statements for.
- """
- # Determine the filenames for which we need headers.
- interfaceDeps = [d.interface for d in descriptors]
- ancestors = []
- for iface in interfaceDeps:
- if iface.parent:
- # We're going to need our parent's prototype, to use as the
- # prototype of our prototype object.
- ancestors.append(iface.parent)
- # And if we have an interface object, we'll need the nearest
- # ancestor with an interface object too, so we can use its
- # interface object as the proto of our interface object.
- if iface.hasInterfaceObject():
- parent = iface.parent
- while parent and not parent.hasInterfaceObject():
- parent = parent.parent
- if parent:
- ancestors.append(parent)
- interfaceDeps.extend(ancestors)
- # Include parent interface headers needed for jsonifier code.
- jsonInterfaceParents = []
- for desc in descriptors:
- if not desc.operations['Jsonifier']:
- continue
- parent = desc.interface.parent
- while parent:
- parentDesc = desc.getDescriptor(parent.identifier.name)
- if parentDesc.operations['Jsonifier']:
- jsonInterfaceParents.append(parentDesc.interface)
- parent = parent.parent
- interfaceDeps.extend(jsonInterfaceParents)
- bindingIncludes = set(self.getDeclarationFilename(d) for d in interfaceDeps)
- # Grab all the implementation declaration files we need.
- implementationIncludes = set(d.headerFile for d in descriptors if d.needsHeaderInclude())
- # Grab the includes for checking hasInstance
- interfacesImplementingSelf = set()
- for d in descriptors:
- interfacesImplementingSelf |= d.interface.interfacesImplementingSelf
- implementationIncludes |= set(self.getDeclarationFilename(i) for i in
- interfacesImplementingSelf)
- # Grab the includes for the things that involve XPCOM interfaces
- hasInstanceIncludes = set("nsIDOM" + d.interface.identifier.name + ".h" for d
- in descriptors if
- d.interface.hasInterfaceObject() and
- NeedsGeneratedHasInstance(d) and
- d.interface.hasInterfacePrototypeObject())
- if len(hasInstanceIncludes) > 0:
- hasInstanceIncludes.add("nsContentUtils.h")
- # Now find all the things we'll need as arguments because we
- # need to wrap or unwrap them.
- bindingHeaders = set()
- # KeyframeAnimationOptions.webidl is doing something VERY screwy and
- # Unified Building really sucks so directly include this
- if prefix == "KeyframeAnimationOptionsBinding":
- bindingHeaders.add("mozilla/dom/PrimitiveConversions.h")
- declareIncludes = set(declareIncludes)
- def addHeadersForType((t, dictionary)):
- """
- Add the relevant headers for this type. We use dictionary, if
- passed, to decide what to do with interface types.
- """
- # Dictionaries have members that need to be actually
- # declared, not just forward-declared.
- if dictionary:
- headerSet = declareIncludes
- else:
- headerSet = bindingHeaders
- if t.nullable():
- # Need to make sure that Nullable as a dictionary
- # member works.
- headerSet.add("mozilla/dom/Nullable.h")
- unrolled = t.unroll()
- if unrolled.isUnion():
- headerSet.add(self.getUnionDeclarationFilename(config, unrolled))
- bindingHeaders.add("mozilla/dom/UnionConversions.h")
- elif unrolled.isDate():
- if dictionary or jsImplementedDescriptors:
- declareIncludes.add("mozilla/dom/Date.h")
- else:
- bindingHeaders.add("mozilla/dom/Date.h")
- elif unrolled.isPromise():
- # See comment in the isInterface() case for why we add
- # Promise.h to headerSet, not bindingHeaders.
- headerSet.add("mozilla/dom/Promise.h")
- # We need ToJSValue to do the Promise to JS conversion.
- bindingHeaders.add("mozilla/dom/ToJSValue.h")
- elif unrolled.isInterface():
- if unrolled.isSpiderMonkeyInterface():
- bindingHeaders.add("jsfriendapi.h")
- if jsImplementedDescriptors:
- # Since we can't forward-declare typed array types
- # (because they're typedefs), we have to go ahead and
- # just include their header if we need to have functions
- # taking references to them declared in that header.
- headerSet = declareIncludes
- headerSet.add("mozilla/dom/TypedArray.h")
- else:
- try:
- typeDesc = config.getDescriptor(unrolled.inner.identifier.name)
- except NoSuchDescriptorError:
- return
- # Dictionaries with interface members rely on the
- # actual class definition of that interface member
- # being visible in the binding header, because they
- # store them in RefPtr and have inline
- # constructors/destructors.
- #
- # XXXbz maybe dictionaries with interface members
- # should just have out-of-line constructors and
- # destructors?
- headerSet.add(typeDesc.headerFile)
- elif unrolled.isDictionary():
- headerSet.add(self.getDeclarationFilename(unrolled.inner))
- elif unrolled.isCallback():
- headerSet.add(self.getDeclarationFilename(unrolled.callback))
- elif unrolled.isFloat() and not unrolled.isUnrestricted():
- # Restricted floats are tested for finiteness
- bindingHeaders.add("mozilla/FloatingPoint.h")
- bindingHeaders.add("mozilla/dom/PrimitiveConversions.h")
- elif unrolled.isEnum():
- filename = self.getDeclarationFilename(unrolled.inner)
- declareIncludes.add(filename)
- elif unrolled.isPrimitive():
- bindingHeaders.add("mozilla/dom/PrimitiveConversions.h")
- elif unrolled.isRecord():
- if dictionary or jsImplementedDescriptors:
- declareIncludes.add("mozilla/dom/Record.h")
- else:
- bindingHeaders.add("mozilla/dom/Record.h")
- # Also add headers for the type the record is
- # parametrized over, if needed.
- addHeadersForType((t.inner, dictionary))
- map(addHeadersForType,
- getAllTypes(descriptors + callbackDescriptors, dictionaries,
- callbacks))
- # Now make sure we're not trying to include the header from inside itself
- declareIncludes.discard(prefix + ".h")
- def addHeaderForFunc(func, desc):
- if func is None:
- return
- # Include the right class header, which we can only do
- # if this is a class member function.
- if desc is not None and not desc.headerIsDefault:
- # An explicit header file was provided, assume that we know
- # what we're doing.
- return
- if "::" in func:
- # Strip out the function name and convert "::" to "/"
- bindingHeaders.add("/".join(func.split("::")[:-1]) + ".h")
- # Now for non-callback descriptors make sure we include any
- # headers needed by Func declarations and other things like that.
- for desc in descriptors:
- # If this is an iterator interface generated for a seperate
- # iterable interface, skip generating type includes, as we have
- # what we need in IterableIterator.h
- if desc.interface.isExternal() or desc.interface.isIteratorInterface():
- continue
- for m in desc.interface.members:
- addHeaderForFunc(PropertyDefiner.getStringAttr(m, "Func"), desc)
- staticTypeOverride = PropertyDefiner.getStringAttr(m, "StaticClassOverride")
- if staticTypeOverride:
- bindingHeaders.add("/".join(staticTypeOverride.split("::")) + ".h")
- # getExtendedAttribute() returns a list, extract the entry.
- funcList = desc.interface.getExtendedAttribute("Func")
- if funcList is not None:
- addHeaderForFunc(funcList[0], desc)
- if desc.interface.maplikeOrSetlikeOrIterable:
- # We need ToJSValue.h for maplike/setlike type conversions
- bindingHeaders.add("mozilla/dom/ToJSValue.h")
- # Add headers for the key and value types of the
- # maplike/setlike/iterable, since they'll be needed for
- # convenience functions
- if desc.interface.maplikeOrSetlikeOrIterable.hasKeyType():
- addHeadersForType((desc.interface.maplikeOrSetlikeOrIterable.keyType,
- None))
- if desc.interface.maplikeOrSetlikeOrIterable.hasValueType():
- addHeadersForType((desc.interface.maplikeOrSetlikeOrIterable.valueType,
- None))
- for d in dictionaries:
- if d.parent:
- declareIncludes.add(self.getDeclarationFilename(d.parent))
- bindingHeaders.add(self.getDeclarationFilename(d))
- for m in d.members:
- addHeaderForFunc(PropertyDefiner.getStringAttr(m, "Func"),
- None)
- # No need to worry about Func on members of ancestors, because that
- # will happen automatically in whatever files those ancestors live
- # in.
- for c in callbacks:
- bindingHeaders.add(self.getDeclarationFilename(c))
- for c in callbackDescriptors:
- bindingHeaders.add(self.getDeclarationFilename(c.interface))
- if len(callbacks) != 0:
- # We need CallbackFunction to serve as our parent class
- declareIncludes.add("mozilla/dom/CallbackFunction.h")
- # And we need ToJSValue.h so we can wrap "this" objects
- declareIncludes.add("mozilla/dom/ToJSValue.h")
- if len(callbackDescriptors) != 0 or len(jsImplementedDescriptors) != 0:
- # We need CallbackInterface to serve as our parent class
- declareIncludes.add("mozilla/dom/CallbackInterface.h")
- # And we need ToJSValue.h so we can wrap "this" objects
- declareIncludes.add("mozilla/dom/ToJSValue.h")
- # Also need to include the headers for ancestors of
- # JS-implemented interfaces.
- for jsImplemented in jsImplementedDescriptors:
- jsParent = jsImplemented.interface.parent
- if jsParent:
- parentDesc = jsImplemented.getDescriptor(jsParent.identifier.name)
- declareIncludes.add(parentDesc.jsImplParentHeader)
- # Let the machinery do its thing.
- def _includeString(includes):
- return ''.join(['#include "%s"\n' % i for i in includes]) + '\n'
- CGWrapper.__init__(self, child,
- declarePre=_includeString(sorted(declareIncludes)),
- definePre=_includeString(sorted(set(defineIncludes) |
- bindingIncludes |
- bindingHeaders |
- hasInstanceIncludes |
- implementationIncludes)))
- @staticmethod
- def getDeclarationFilename(decl):
- # Use our local version of the header, not the exported one, so that
- # test bindings, which don't export, will work correctly.
- basename = os.path.basename(decl.filename())
- return basename.replace('.webidl', 'Binding.h')
- @staticmethod
- def getUnionDeclarationFilename(config, unionType):
- assert unionType.isUnion()
- assert unionType.unroll() == unionType
- # If a union is "defined" in multiple files, it goes in UnionTypes.h.
- if len(config.filenamesPerUnion[unionType.name]) > 1:
- return "mozilla/dom/UnionTypes.h"
- # If a union is defined by a built-in typedef, it also goes in
- # UnionTypes.h.
- assert len(config.filenamesPerUnion[unionType.name]) == 1
- if "<unknown>" in config.filenamesPerUnion[unionType.name]:
- return "mozilla/dom/UnionTypes.h"
- return CGHeaders.getDeclarationFilename(unionType)
- def SortedDictValues(d):
- """
- Returns a list of values from the dict sorted by key.
- """
- return [v for k, v in sorted(d.items())]
- def UnionsForFile(config, webIDLFile):
- """
- Returns a list of union types for all union types that are only used in
- webIDLFile. If webIDLFile is None this will return the list of tuples for
- union types that are used in more than one WebIDL file.
- """
- return config.unionsPerFilename.get(webIDLFile, [])
- def UnionTypes(unionTypes, config):
- """
- The unionTypes argument should be a list of union types. This is typically
- the list generated by UnionsForFile.
- Returns a tuple containing a set of header filenames to include in
- the header for the types in unionTypes, a set of header filenames to
- include in the implementation file for the types in unionTypes, a set
- of tuples containing a type declaration and a boolean if the type is a
- struct for member types of the union, a list of traverse methods,
- unlink methods and a list of union types. These last three lists only
- contain unique union types.
- """
- headers = set()
- implheaders = set()
- declarations = set()
- unionStructs = dict()
- traverseMethods = dict()
- unlinkMethods = dict()
- for t in unionTypes:
- name = str(t)
- if name not in unionStructs:
- unionStructs[name] = t
- def addHeadersForType(f):
- if f.nullable():
- headers.add("mozilla/dom/Nullable.h")
- isSequence = f.isSequence()
- f = f.unroll()
- if f.isPromise():
- headers.add("mozilla/dom/Promise.h")
- # We need ToJSValue to do the Promise to JS conversion.
- headers.add("mozilla/dom/ToJSValue.h")
- elif f.isInterface():
- if f.isSpiderMonkeyInterface():
- headers.add("jsfriendapi.h")
- headers.add("mozilla/dom/TypedArray.h")
- else:
- try:
- typeDesc = config.getDescriptor(f.inner.identifier.name)
- except NoSuchDescriptorError:
- return
- if typeDesc.interface.isCallback() or isSequence:
- # Callback interfaces always use strong refs, so
- # we need to include the right header to be able
- # to Release() in our inlined code.
- #
- # Similarly, sequences always contain strong
- # refs, so we'll need the header to handler
- # those.
- headers.add(typeDesc.headerFile)
- else:
- declarations.add((typeDesc.nativeType, False))
- implheaders.add(typeDesc.headerFile)
- elif f.isDictionary():
- # For a dictionary, we need to see its declaration in
- # UnionTypes.h so we have its sizeof and know how big to
- # make our union.
- headers.add(CGHeaders.getDeclarationFilename(f.inner))
- # And if it needs rooting, we need RootedDictionary too
- if typeNeedsRooting(f):
- headers.add("mozilla/dom/RootedDictionary.h")
- elif f.isEnum():
- # Need to see the actual definition of the enum,
- # unfortunately.
- headers.add(CGHeaders.getDeclarationFilename(f.inner))
- elif f.isCallback():
- # Callbacks always use strong refs, so we need to include
- # the right header to be able to Release() in our inlined
- # code.
- headers.add(CGHeaders.getDeclarationFilename(f.callback))
- elif f.isRecord():
- headers.add("mozilla/dom/Record.h")
- # And add headers for the type we're parametrized over
- addHeadersForType(f.inner)
- implheaders.add(CGHeaders.getUnionDeclarationFilename(config, t))
- for f in t.flatMemberTypes:
- assert not f.nullable()
- addHeadersForType(f)
- if idlTypeNeedsCycleCollection(t):
- declarations.add(("mozilla::dom::%s" % CGUnionStruct.unionTypeName(t, True), False))
- traverseMethods[name] = CGCycleCollectionTraverseForOwningUnionMethod(t)
- unlinkMethods[name] = CGCycleCollectionUnlinkForOwningUnionMethod(t)
- # The order of items in CGList is important.
- # Since the union structs friend the unlinkMethods, the forward-declaration
- # for these methods should come before the class declaration. Otherwise
- # some compilers treat the friend declaration as a forward-declaration in
- # the class scope.
- return (headers, implheaders, declarations,
- SortedDictValues(traverseMethods), SortedDictValues(unlinkMethods),
- SortedDictValues(unionStructs))
- def UnionConversions(unionTypes, config):
- """
- The unionTypes argument should be a list of tuples, each containing two
- elements: a union type and a descriptor. This is typically the list
- generated by UnionsForFile.
- Returns a tuple containing a list of headers and a CGThing to declare all
- union argument conversion helper structs.
- """
- headers = set()
- unionConversions = dict()
- for t in unionTypes:
- name = str(t)
- if name not in unionConversions:
- unionConversions[name] = CGUnionConversionStruct(t, config)
- def addHeadersForType(f):
- f = f.unroll()
- if f.isPromise():
- headers.add("mozilla/dom/Promise.h")
- # We need ToJSValue to do the Promise to JS conversion.
- headers.add("mozilla/dom/ToJSValue.h")
- elif f.isInterface():
- if f.isSpiderMonkeyInterface():
- headers.add("jsfriendapi.h")
- headers.add("mozilla/dom/TypedArray.h")
- elif f.inner.isExternal():
- try:
- typeDesc = config.getDescriptor(f.inner.identifier.name)
- except NoSuchDescriptorError:
- return
- headers.add(typeDesc.headerFile)
- else:
- headers.add(CGHeaders.getDeclarationFilename(f.inner))
- elif f.isDictionary():
- headers.add(CGHeaders.getDeclarationFilename(f.inner))
- elif f.isPrimitive():
- headers.add("mozilla/dom/PrimitiveConversions.h")
- elif f.isRecord():
- headers.add("mozilla/dom/Record.h")
- # And the internal type of the record
- addHeadersForType(f.inner)
- # We plan to include UnionTypes.h no matter what, so it's
- # OK if we throw it into the set here.
- headers.add(CGHeaders.getUnionDeclarationFilename(config, t))
- for f in t.flatMemberTypes:
- addHeadersForType(f)
- return (headers,
- CGWrapper(CGList(SortedDictValues(unionConversions), "\n"),
- post="\n\n"))
- class Argument():
- """
- A class for outputting the type and name of an argument
- """
- def __init__(self, argType, name, default=None):
- self.argType = argType
- self.name = name
- self.default = default
- def declare(self):
- string = self.argType + ' ' + self.name
- if self.default is not None:
- string += " = " + self.default
- return string
- def define(self):
- return self.argType + ' ' + self.name
- class CGAbstractMethod(CGThing):
- """
- An abstract class for generating code for a method. Subclasses
- should override definition_body to create the actual code.
- descriptor is the descriptor for the interface the method is associated with
- name is the name of the method as a string
- returnType is the IDLType of the return value
- args is a list of Argument objects
- inline should be True to generate an inline method, whose body is
- part of the declaration.
- alwaysInline should be True to generate an inline method annotated with
- MOZ_ALWAYS_INLINE.
- static should be True to generate a static method, which only has
- a definition.
- If templateArgs is not None it should be a list of strings containing
- template arguments, and the function will be templatized using those
- arguments.
- """
- def __init__(self, descriptor, name, returnType, args, inline=False, alwaysInline=False, static=False, templateArgs=None):
- CGThing.__init__(self)
- self.descriptor = descriptor
- self.name = name
- self.returnType = returnType
- self.args = args
- self.inline = inline
- self.alwaysInline = alwaysInline
- self.static = static
- self.templateArgs = templateArgs
- def _argstring(self, declare):
- return ', '.join([a.declare() if declare else a.define() for a in self.args])
- def _template(self):
- if self.templateArgs is None:
- return ''
- return 'template <%s>\n' % ', '.join(self.templateArgs)
- def _decorators(self):
- decorators = []
- if self.alwaysInline:
- decorators.append('MOZ_ALWAYS_INLINE')
- elif self.inline:
- decorators.append('inline')
- if self.static:
- decorators.append('static')
- decorators.append(self.returnType)
- maybeNewline = " " if self.inline else "\n"
- return ' '.join(decorators) + maybeNewline
- def declare(self):
- if self.inline:
- return self._define(True)
- return "%s%s%s(%s);\n" % (self._template(), self._decorators(), self.name, self._argstring(True))
- def indent_body(self, body):
- """
- Indent the code returned by self.definition_body(). Most classes
- simply indent everything two spaces. This is here for
- CGRegisterProtos, which needs custom indentation.
- """
- return indent(body)
- def _define(self, fromDeclare=False):
- return (self.definition_prologue(fromDeclare) +
- self.indent_body(self.definition_body()) +
- self.definition_epilogue())
- def define(self):
- return "" if self.inline else self._define()
- def definition_prologue(self, fromDeclare):
- return "%s%s%s(%s)\n{\n" % (self._template(), self._decorators(),
- self.name, self._argstring(fromDeclare))
- def definition_epilogue(self):
- return "}\n"
- def definition_body(self):
- assert False # Override me!
- class CGAbstractStaticMethod(CGAbstractMethod):
- """
- Abstract base class for codegen of implementation-only (no
- declaration) static methods.
- """
- def __init__(self, descriptor, name, returnType, args):
- CGAbstractMethod.__init__(self, descriptor, name, returnType, args,
- inline=False, static=True)
- def declare(self):
- # We only have implementation
- return ""
- class CGAbstractClassHook(CGAbstractStaticMethod):
- """
- Meant for implementing JSClass hooks, like Finalize or Trace. Does very raw
- 'this' unwrapping as it assumes that the unwrapped type is always known.
- """
- def __init__(self, descriptor, name, returnType, args):
- CGAbstractStaticMethod.__init__(self, descriptor, name, returnType,
- args)
- def definition_body_prologue(self):
- return ("%s* self = UnwrapPossiblyNotInitializedDOMObject<%s>(obj);\n" %
- (self.descriptor.nativeType, self.descriptor.nativeType))
- def definition_body(self):
- return self.definition_body_prologue() + self.generate_code()
- def generate_code(self):
- assert False # Override me!
- class CGGetJSClassMethod(CGAbstractMethod):
- def __init__(self, descriptor):
- CGAbstractMethod.__init__(self, descriptor, 'GetJSClass', 'const JSClass*',
- [])
- def definition_body(self):
- return "return sClass.ToJSClass();\n"
- class CGAddPropertyHook(CGAbstractClassHook):
- """
- A hook for addProperty, used to preserve our wrapper from GC.
- """
- def __init__(self, descriptor):
- args = [Argument('JSContext*', 'cx'),
- Argument('JS::Handle<JSObject*>', 'obj'),
- Argument('JS::Handle<jsid>', 'id'),
- Argument('JS::Handle<JS::Value>', 'val')]
- CGAbstractClassHook.__init__(self, descriptor, ADDPROPERTY_HOOK_NAME,
- 'bool', args)
- def generate_code(self):
- assert self.descriptor.wrapperCache
- return dedent("""
- // We don't want to preserve if we don't have a wrapper, and we
- // obviously can't preserve if we're not initialized.
- if (self && self->GetWrapperPreserveColor()) {
- PreserveWrapper(self);
- }
- return true;
- """)
- def finalizeHook(descriptor, hookName, freeOp):
- finalize = ""
- if descriptor.wrapperCache:
- finalize += "ClearWrapper(self, self);\n"
- if descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
- finalize += "self->mExpandoAndGeneration.expando = JS::UndefinedValue();\n"
- if descriptor.isGlobal():
- finalize += "mozilla::dom::FinalizeGlobal(CastToJSFreeOp(%s), obj);\n" % freeOp
- finalize += ("AddForDeferredFinalization<%s>(self);\n" %
- descriptor.nativeType)
- return CGIfWrapper(CGGeneric(finalize), "self")
- class CGClassFinalizeHook(CGAbstractClassHook):
- """
- A hook for finalize, used to release our native object.
- """
- def __init__(self, descriptor):
- args = [Argument('js::FreeOp*', 'fop'), Argument('JSObject*', 'obj')]
- CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME,
- 'void', args)
- def generate_code(self):
- return finalizeHook(self.descriptor, self.name, self.args[0].name).define()
- class CGClassObjectMovedHook(CGAbstractClassHook):
- """
- A hook for objectMovedOp, used to update the wrapper cache when an object it
- is holding moves.
- """
- def __init__(self, descriptor):
- args = [Argument('JSObject*', 'obj'), Argument('const JSObject*', 'old')]
- CGAbstractClassHook.__init__(self, descriptor, OBJECT_MOVED_HOOK_NAME,
- 'void', args)
- def generate_code(self):
- assert self.descriptor.wrapperCache
- return CGIfWrapper(CGGeneric("UpdateWrapper(self, self, obj, old);\n"),
- "self").define()
- def JSNativeArguments():
- return [Argument('JSContext*', 'cx'),
- Argument('unsigned', 'argc'),
- Argument('JS::Value*', 'vp')]
- class CGClassConstructor(CGAbstractStaticMethod):
- """
- JS-visible constructor for our objects
- """
- def __init__(self, descriptor, ctor, name=CONSTRUCT_HOOK_NAME):
- CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool',
- JSNativeArguments())
- self._ctor = ctor
- def define(self):
- if not self._ctor:
- return ""
- return CGAbstractStaticMethod.define(self)
- def definition_body(self):
- return self.generate_code()
- def generate_code(self):
- # [ChromeOnly] interfaces may only be constructed by chrome.
- chromeOnlyCheck = ""
- if isChromeOnly(self._ctor):
- chromeOnlyCheck = dedent("""
- if (!nsContentUtils::ThreadsafeIsCallerChrome()) {
- return ThrowingConstructor(cx, argc, vp);
- }
- """)
- # Additionally, we want to throw if a caller does a bareword invocation
- # of a constructor without |new|. We don't enforce this for chrome in
- # realease builds to avoid the addon compat fallout of making that
- # change. See bug 916644.
- #
- # Figure out the name of our constructor for error reporting purposes.
- # For unnamed webidl constructors, identifier.name is "constructor" but
- # the name JS sees is the interface name; for named constructors
- # identifier.name is the actual name.
- name = self._ctor.identifier.name
- if name != "constructor":
- ctorName = name
- else:
- ctorName = self.descriptor.interface.identifier.name
- # [HTMLConstructor] for custom element
- # This needs to live in bindings code because it directly examines
- # newtarget and the callee function to do HTMLConstructor specific things.
- if self._ctor.isHTMLConstructor():
- htmlConstructorSanityCheck = dedent("""
- // The newTarget might be a cross-compartment wrapper. Get the underlying object
- // so we can do the spec's object-identity checks.
- JS::Rooted<JSObject*> newTarget(cx, js::CheckedUnwrap(&args.newTarget().toObject()));
- if (!newTarget) {
- return ThrowErrorMessage(cx, MSG_ILLEGAL_CONSTRUCTOR);
- }
- // Step 2 of https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor.
- // Enter the compartment of our underlying newTarget object, so we end
- // up comparing to the constructor object for our interface from that global.
- {
- JSAutoCompartment ac(cx, newTarget);
- JS::Handle<JSObject*> constructor(GetConstructorObjectHandle(cx));
- if (!constructor) {
- return false;
- }
- if (newTarget == constructor) {
- return ThrowErrorMessage(cx, MSG_ILLEGAL_CONSTRUCTOR);
- }
- }
- """)
- # If we are unable to get desired prototype from newTarget, then we
- # fall back to the interface prototype object from newTarget's realm.
- htmlConstructorFallback = dedent("""
- if (!desiredProto) {
- // Step 7 of https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor.
- // This fallback behavior is designed to match analogous behavior for the
- // JavaScript built-ins. So we enter the compartment of our underlying
- // newTarget object and fall back to the prototype object from that global.
- // XXX The spec says to use GetFunctionRealm(), which is not actually
- // the same thing as what we have here (e.g. in the case of scripted callable proxies
- // whose target is not same-compartment with the proxy, or bound functions, etc).
- // https://bugzilla.mozilla.org/show_bug.cgi?id=1317658
- {
- JSAutoCompartment ac(cx, newTarget);
- desiredProto = GetProtoObjectHandle(cx);
- if (!desiredProto) {
- return false;
- }
- }
- // desiredProto is in the compartment of the underlying newTarget object.
- // Wrap it into the context compartment.
- if (!JS_WrapObject(cx, &desiredProto)) {
- return false;
- }
- }
- """)
- else:
- htmlConstructorSanityCheck = ""
- htmlConstructorFallback = ""
- # If we're a constructor, "obj" may not be a function, so calling
- # XrayAwareCalleeGlobal() on it is not safe. Of course in the
- # constructor case either "obj" is an Xray or we're already in the
- # content compartment, not the Xray compartment, so just
- # constructing the GlobalObject from "obj" is fine.
- preamble = fill(
- """
- JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
- JS::Rooted<JSObject*> obj(cx, &args.callee());
- $*{chromeOnlyCheck}
- if (!args.isConstructing()) {
- // XXXbz wish I could get the name from the callee instead of
- // Adding more relocations
- return ThrowConstructorWithoutNew(cx, "${ctorName}");
- }
- GlobalObject global(cx, obj);
- if (global.Failed()) {
- return false;
- }
- $*{htmlConstructorSanityCheck}
- JS::Rooted<JSObject*> desiredProto(cx);
- if (!GetDesiredProto(cx, args, &desiredProto)) {
- return false;
- }
- $*{htmlConstructorFallback}
- """,
- chromeOnlyCheck=chromeOnlyCheck,
- ctorName=ctorName,
- htmlConstructorSanityCheck=htmlConstructorSanityCheck,
- htmlConstructorFallback=htmlConstructorFallback)
- if self._ctor.isHTMLConstructor():
- signatures = self._ctor.signatures()
- assert len(signatures) == 1
- # Given that HTMLConstructor takes no args, we can just codegen a
- # call to CreateHTMLElement() in BindingUtils which reuses the
- # factory thing in HTMLContentSink. Then we don't have to implement
- # Constructor on all the HTML elements.
- callGenerator = CGPerSignatureCall(signatures[0][0], signatures[0][1],
- "CreateHTMLElement", True,
- self.descriptor, self._ctor,
- isConstructor=True)
- else:
- name = self._ctor.identifier.name
- nativeName = MakeNativeName(self.descriptor.binaryNameFor(name))
- callGenerator = CGMethodCall(nativeName, True, self.descriptor,
- self._ctor, isConstructor=True,
- constructorName=ctorName)
- return preamble + "\n" + callGenerator.define()
- # Encapsulate the constructor in a helper method to share genConstructorBody with CGJSImplMethod.
- class CGConstructNavigatorObject(CGAbstractMethod):
- """
- Construct a new JS-implemented WebIDL DOM object, for use on navigator.
- """
- def __init__(self, descriptor):
- args = [Argument('JSContext*', 'cx'),
- Argument('JS::Handle<JSObject*>', 'obj'),
- Argument('ErrorResult&', 'aRv')]
- rtype = 'already_AddRefed<%s>' % descriptor.name
- CGAbstractMethod.__init__(self, descriptor, "ConstructNavigatorObject",
- rtype, args)
- def definition_body(self):
- if not self.descriptor.interface.isJSImplemented():
- raise TypeError("Only JS-implemented classes are currently supported "
- "on navigator. See bug 856820.")
- return dedent(
- """
- GlobalObject global(cx, obj);
- if (global.Failed()) {
- aRv.Throw(NS_ERROR_FAILURE);
- return nullptr;
- }
- """) + genConstructorBody(self.descriptor)
- def NamedConstructorName(m):
- return '_' + m.identifier.name
- class CGNamedConstructors(CGThing):
- def __init__(self, descriptor):
- self.descriptor = descriptor
- CGThing.__init__(self)
- def declare(self):
- return ""
- def define(self):
- if len(self.descriptor.interface.namedConstructors) == 0:
- return ""
- constructorID = "constructors::id::"
- if self.descriptor.interface.hasInterfaceObject():
- constructorID += self.descriptor.name
- else:
- constructorID += "_ID_Count"
- namedConstructors = ""
- for n in self.descriptor.interface.namedConstructors:
- namedConstructors += (
- "{ \"%s\", { %s, &sNamedConstructorNativePropertyHooks }, %i },\n" %
- (n.identifier.name, NamedConstructorName(n), methodLength(n)))
- return fill(
- """
- const NativePropertyHooks sNamedConstructorNativePropertyHooks = {
- nullptr,
- nullptr,
- nullptr,
- { nullptr, nullptr },
- prototypes::id::${name},
- ${constructorID},
- nullptr
- };
- static const NamedConstructor namedConstructors[] = {
- $*{namedConstructors}
- { nullptr, { nullptr, nullptr }, 0 }
- };
- """,
- name=self.descriptor.name,
- constructorID=constructorID,
- namedConstructors=namedConstructors)
- class CGHasInstanceHook(CGAbstractStaticMethod):
- def __init__(self, descriptor):
- args = [Argument('JSContext*', 'cx'),
- Argument('unsigned', 'argc'),
- Argument('JS::Value*', 'vp')]
- assert descriptor.interface.hasInterfaceObject()
- assert NeedsGeneratedHasInstance(descriptor)
- CGAbstractStaticMethod.__init__(self, descriptor, HASINSTANCE_HOOK_NAME,
- 'bool', args)
- def define(self):
- return CGAbstractStaticMethod.define(self)
- def definition_body(self):
- return self.generate_code()
- def generate_code(self):
- header = dedent("""
- JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
- if (!args.get(0).isObject()) {
- args.rval().setBoolean(false);
- return true;
- }
- JS::Rooted<JSObject*> instance(cx, &args[0].toObject());
- """)
- if self.descriptor.interface.hasInterfacePrototypeObject():
- return (
- header +
- fill(
- """
- static_assert(IsBaseOf<nsISupports, ${nativeType}>::value,
- "HasInstance only works for nsISupports-based classes.");
- bool ok = InterfaceHasInstance(cx, argc, vp);
- if (!ok || args.rval().toBoolean()) {
- return ok;
- }
- // FIXME Limit this to chrome by checking xpc::AccessCheck::isChrome(obj).
- nsCOMPtr<nsISupports> native =
- xpc::UnwrapReflectorToISupports(js::UncheckedUnwrap(instance, /* stopAtWindowProxy = */ false));
- nsCOMPtr<nsIDOM${name}> qiResult = do_QueryInterface(native);
- args.rval().setBoolean(!!qiResult);
- return true;
- """,
- nativeType=self.descriptor.nativeType,
- name=self.descriptor.interface.identifier.name))
- hasInstanceCode = dedent("""
- const DOMJSClass* domClass = GetDOMClass(js::UncheckedUnwrap(instance, /* stopAtWindowProxy = */ false));
- if (!domClass) {
- // Not a DOM object, so certainly not an instance of this interface
- args.rval().setBoolean(false);
- return true;
- }
- """)
- if self.descriptor.interface.identifier.name == "ChromeWindow":
- setRval = "args.rval().setBoolean(UnwrapDOMObject<nsGlobalWindow>(js::UncheckedUnwrap(instance, /* stopAtWindowProxy = */ false))->IsChromeWindow())"
- else:
- setRval = "args.rval().setBoolean(true)"
- # Sort interaces implementing self by name so we get stable output.
- for iface in sorted(self.descriptor.interface.interfacesImplementingSelf,
- key=lambda iface: iface.identifier.name):
- hasInstanceCode += fill(
- """
- if (domClass->mInterfaceChain[PrototypeTraits<prototypes::id::${name}>::Depth] == prototypes::id::${name}) {
- ${setRval};
- return true;
- }
- """,
- name=iface.identifier.name,
- setRval=setRval)
- hasInstanceCode += ("args.rval().setBoolean(false);\n"
- "return true;\n")
- return header + hasInstanceCode
- def isChromeOnly(m):
- return m.getExtendedAttribute("ChromeOnly")
- class MemberCondition:
- """
- An object representing the condition for a member to actually be
- exposed. Any of the arguments can be None. If not
- None, they should have the following types:
- pref: The name of the preference.
- func: The name of the function.
- secureContext: A bool indicating whether a secure context is required.
- nonExposedGlobals: A set of names of globals. Can be empty, in which case
- it's treated the same way as None.
- """
- def __init__(self, pref=None, func=None, secureContext=False,
- nonExposedGlobals=None):
- assert pref is None or isinstance(pref, str)
- assert func is None or isinstance(func, str)
- assert isinstance(secureContext, bool)
- assert nonExposedGlobals is None or isinstance(nonExposedGlobals, set)
- self.pref = pref
- self.secureContext = secureContext
- def toFuncPtr(val):
- if val is None:
- return "nullptr"
- return "&" + val
- self.func = toFuncPtr(func)
- if nonExposedGlobals:
- # Nonempty set
- self.nonExposedGlobals = " | ".join(
- map(lambda g: "GlobalNames::%s" % g,
- sorted(nonExposedGlobals)))
- else:
- self.nonExposedGlobals = "0"
- def __eq__(self, other):
- return (self.pref == other.pref and self.func == other.func and
- self.secureContext == other.secureContext and
- self.nonExposedGlobals == other.nonExposedGlobals)
- def __ne__(self, other):
- return not self.__eq__(other)
- def hasDisablers(self):
- return (self.pref is not None or
- self.secureContext or
- self.func != "nullptr" or
- self.nonExposedGlobals != "0")
- class PropertyDefiner:
- """
- A common superclass for defining things on prototype objects.
- Subclasses should implement generateArray to generate the actual arrays of
- things we're defining. They should also set self.chrome to the list of
- things only exposed to chrome and self.regular to the list of things exposed
- to both chrome and web pages.
- """
- def __init__(self, descriptor, name):
- self.descriptor = descriptor
- self.name = name
- # self.prefCacheData will store an array of (prefname, bool*)
- # pairs for our bool var caches. generateArray will fill it
- # in as needed.
- self.prefCacheData = []
- def hasChromeOnly(self):
- return len(self.chrome) > 0
- def hasNonChromeOnly(self):
- return len(self.regular) > 0
- def variableName(self, chrome):
- if chrome:
- if self.hasChromeOnly():
- return "sChrome" + self.name
- else:
- if self.hasNonChromeOnly():
- return "s" + self.name
- return "nullptr"
- def usedForXrays(self):
- return self.descriptor.wantsXrays
- def __str__(self):
- # We only need to generate id arrays for things that will end
- # up used via ResolveProperty or EnumerateProperties.
- str = self.generateArray(self.regular, self.variableName(False),
- self.usedForXrays())
- if self.hasChromeOnly():
- str += self.generateArray(self.chrome, self.variableName(True),
- self.usedForXrays())
- return str
- @staticmethod
- def getStringAttr(member, name):
- attr = member.getExtendedAttribute(name)
- if attr is None:
- return None
- # It's a list of strings
- assert len(attr) == 1
- assert attr[0] is not None
- return attr[0]
- @staticmethod
- def getControllingCondition(interfaceMember, descriptor):
- interface = descriptor.interface
- nonExposureSet = interface.exposureSet - interfaceMember.exposureSet
- return MemberCondition(
- PropertyDefiner.getStringAttr(interfaceMember,
- "Pref"),
- PropertyDefiner.getStringAttr(interfaceMember,
- "Func"),
- interfaceMember.getExtendedAttribute("SecureContext") is not None,
- nonExposureSet)
- def generatePrefableArray(self, array, name, specFormatter, specTerminator,
- specType, getCondition, getDataTuple, doIdArrays):
- """
- This method generates our various arrays.
- array is an array of interface members as passed to generateArray
- name is the name as passed to generateArray
- specFormatter is a function that takes a single argument, a tuple,
- and returns a string, a spec array entry
- specTerminator is a terminator for the spec array (inserted every time
- our controlling pref changes and at the end of the array)
- specType is the actual typename of our spec
- getCondition is a callback function that takes an array entry and
- returns the corresponding MemberCondition.
- getDataTuple is a callback function that takes an array entry and
- returns a tuple suitable to be passed to specFormatter.
- """
- # We want to generate a single list of specs, but with specTerminator
- # inserted at every point where the pref name controlling the member
- # changes. That will make sure the order of the properties as exposed
- # on the interface and interface prototype objects does not change when
- # pref control is added to members while still allowing us to define all
- # the members in the smallest number of JSAPI calls.
- assert len(array) != 0
- # So we won't put a specTerminator at the very front of the list:
- lastCondition = getCondition(array[0], self.descriptor)
- specs = []
- disablers = []
- prefableSpecs = []
- disablersTemplate = dedent(
- """
- static PrefableDisablers %s_disablers%d = {
- true, %s, %s, %s
- };
- """)
- prefableWithDisablersTemplate = ' { &%s_disablers%d, &%s_specs[%d] }'
- prefableWithoutDisablersTemplate = ' { nullptr, &%s_specs[%d] }'
- prefCacheTemplate = '&%s[%d].disablers->enabled'
- def switchToCondition(props, condition):
- # Remember the info about where our pref-controlled
- # booleans live.
- if condition.pref is not None:
- props.prefCacheData.append(
- (condition.pref,
- prefCacheTemplate % (name, len(prefableSpecs))))
- # Set up pointers to the new sets of specs inside prefableSpecs
- if condition.hasDisablers():
- prefableSpecs.append(prefableWithDisablersTemplate %
- (name, len(specs), name, len(specs)))
- disablers.append(disablersTemplate %
- (name, len(specs),
- toStringBool(condition.secureContext),
- condition.nonExposedGlobals,
- condition.func))
- else:
- prefableSpecs.append(prefableWithoutDisablersTemplate %
- (name, len(specs)))
- switchToCondition(self, lastCondition)
- for member in array:
- curCondition = getCondition(member, self.descriptor)
- if lastCondition != curCondition:
- # Terminate previous list
- specs.append(specTerminator)
- # And switch to our new condition
- switchToCondition(self, curCondition)
- lastCondition = curCondition
- # And the actual spec
- specs.append(specFormatter(getDataTuple(member)))
- specs.append(specTerminator)
- prefableSpecs.append(" { nullptr, nullptr }")
- specType = "const " + specType
- arrays = fill(
- """
- static ${specType} ${name}_specs[] = {
- ${specs}
- };
- ${disablers}
- // Can't be const because the pref-enabled boolean needs to be writable
- static Prefable<${specType}> ${name}[] = {
- ${prefableSpecs}
- };
- """,
- specType=specType,
- name=name,
- disablers='\n'.join(disablers),
- specs=',\n'.join(specs),
- prefableSpecs=',\n'.join(prefableSpecs))
- if doIdArrays:
- arrays += "static jsid %s_ids[%i];\n\n" % (name, len(specs))
- return arrays
- # The length of a method is the minimum of the lengths of the
- # argument lists of all its overloads.
- def overloadLength(arguments):
- i = len(arguments)
- while i > 0 and arguments[i - 1].optional:
- i -= 1
- return i
- def methodLength(method):
- signatures = method.signatures()
- return min(overloadLength(arguments) for retType, arguments in signatures)
- def clearableCachedAttrs(descriptor):
- return (m for m in descriptor.interface.members if
- m.isAttr() and
- # Constants should never need clearing!
- m.dependsOn != "Nothing" and
- m.slotIndices is not None)
- def MakeClearCachedValueNativeName(member):
- return "ClearCached%sValue" % MakeNativeName(member.identifier.name)
- def MakeJSImplClearCachedValueNativeName(member):
- return "_" + MakeClearCachedValueNativeName(member)
- def IDLToCIdentifier(name):
- return name.replace("-", "_")
- class MethodDefiner(PropertyDefiner):
- """
- A class for defining methods on a prototype object.
- """
- def __init__(self, descriptor, name, static, unforgeable=False):
- assert not (static and unforgeable)
- PropertyDefiner.__init__(self, descriptor, name)
- # FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=772822
- # We should be able to check for special operations without an
- # identifier. For now we check if the name starts with __
- # Ignore non-static methods for interfaces without a proto object
- if descriptor.interface.hasInterfacePrototypeObject() or static:
- methods = [m for m in descriptor.interface.members if
- m.isMethod() and m.isStatic() == static and
- MemberIsUnforgeable(m, descriptor) == unforgeable and
- not m.isIdentifierLess()]
- else:
- methods = []
- self.chrome = []
- self.regular = []
- for m in methods:
- if m.identifier.name == 'queryInterface':
- if m.isStatic():
- raise TypeError("Legacy queryInterface member shouldn't be static")
- signatures = m.signatures()
- def argTypeIsIID(arg):
- return arg.type.inner.isExternal() and arg.type.inner.identifier.name == 'IID'
- if len(signatures) > 1 or len(signatures[0][1]) > 1 or not argTypeIsIID(signatures[0][1][0]):
- raise TypeError("There should be only one queryInterface method with 1 argument of type IID")
- # Make sure to not stick QueryInterface on abstract interfaces.
- if (not self.descriptor.interface.hasInterfacePrototypeObject() or
- not self.descriptor.concrete):
- raise TypeError("QueryInterface is only supported on "
- "interfaces that are concrete: " +
- self.descriptor.name)
- condition = "WantsQueryInterface<%s>::Enabled" % descriptor.nativeType
- self.regular.append({
- "name": 'QueryInterface',
- "methodInfo": False,
- "length": 1,
- "flags": "0",
- "condition": MemberCondition(func=condition)
- })
- continue
- # Iterable methods should be enumerable, maplike/setlike methods
- # should not.
- isMaplikeOrSetlikeMethod = (m.isMaplikeOrSetlikeOrIterableMethod() and
- (m.maplikeOrSetlikeOrIterable.isMaplike() or
- m.maplikeOrSetlikeOrIterable.isSetlike()))
- method = {
- "name": m.identifier.name,
- "methodInfo": not m.isStatic(),
- "length": methodLength(m),
- # Methods generated for a maplike/setlike declaration are not
- # enumerable.
- "flags": "JSPROP_ENUMERATE" if not isMaplikeOrSetlikeMethod else "0",
- "condition": PropertyDefiner.getControllingCondition(m, descriptor),
- "allowCrossOriginThis": m.getExtendedAttribute("CrossOriginCallable"),
- "returnsPromise": m.returnsPromise(),
- "hasIteratorAlias": "@@iterator" in m.aliases
- }
- if m.isStatic():
- method["nativeName"] = CppKeywords.checkMethodName(IDLToCIdentifier(m.identifier.name))
- if isChromeOnly(m):
- self.chrome.append(method)
- else:
- self.regular.append(method)
- # TODO: Once iterable is implemented, use tiebreak rules instead of
- # failing. Also, may be more tiebreak rules to implement once spec bug
- # is resolved.
- # https://www.w3.org/Bugs/Public/show_bug.cgi?id=28592
- def hasIterator(methods, regular):
- return (any("@@iterator" in m.aliases for m in methods) or
- any("@@iterator" == r["name"] for r in regular))
- # Check whether we need to output an @@iterator due to having an indexed
- # getter. We only do this while outputting non-static and
- # non-unforgeable methods, since the @@iterator function will be
- # neither.
- if (not static and
- not unforgeable and
- descriptor.supportsIndexedProperties()):
- if hasIterator(methods, self.regular):
- raise TypeError("Cannot have indexed getter/attr on "
- "interface %s with other members "
- "that generate @@iterator, such as "
- "maplike/setlike or aliased functions." %
- self.descriptor.interface.identifier.name)
- self.regular.append({
- "name": "@@iterator",
- "methodInfo": False,
- "selfHostedName": "ArrayValues",
- "length": 0,
- "flags": "JSPROP_ENUMERATE",
- "condition": MemberCondition()
- })
- if (static and
- not unforgeable and
- descriptor.interface.hasInterfaceObject() and
- NeedsGeneratedHasInstance(descriptor)):
- self.regular.append({
- "name": "@@hasInstance",
- "methodInfo": False,
- "nativeName": HASINSTANCE_HOOK_NAME,
- "length": 1,
- # Flags match those of Function[Symbol.hasInstance]
- "flags": "JSPROP_READONLY | JSPROP_PERMANENT",
- "condition": MemberCondition()
- })
- # Generate the keys/values/entries aliases for value iterables.
- maplikeOrSetlikeOrIterable = descriptor.interface.maplikeOrSetlikeOrIterable
- if (not static and
- not unforgeable and
- maplikeOrSetlikeOrIterable and
- maplikeOrSetlikeOrIterable.isIterable() and
- maplikeOrSetlikeOrIterable.isValueIterator()):
- # Add our keys/values/entries/forEach
- self.regular.append({
- "name": "keys",
- "methodInfo": False,
- "selfHostedName": "ArrayKeys",
- "length": 0,
- "flags": "JSPROP_ENUMERATE",
- "condition": PropertyDefiner.getControllingCondition(m,
- descriptor)
- })
- self.regular.append({
- "name": "values",
- "methodInfo": False,
- "selfHostedName": "ArrayValues",
- "length": 0,
- "flags": "JSPROP_ENUMERATE",
- "condition": PropertyDefiner.getControllingCondition(m,
- descriptor)
- })
- self.regular.append({
- "name": "entries",
- "methodInfo": False,
- "selfHostedName": "ArrayEntries",
- "length": 0,
- "flags": "JSPROP_ENUMERATE",
- "condition": PropertyDefiner.getControllingCondition(m,
- descriptor)
- })
- self.regular.append({
- "name": "forEach",
- "methodInfo": False,
- "selfHostedName": "ArrayForEach",
- "length": 1,
- "flags": "JSPROP_ENUMERATE",
- "condition": PropertyDefiner.getControllingCondition(m,
- descriptor)
- })
- if not static:
- stringifier = descriptor.operations['Stringifier']
- if (stringifier and
- unforgeable == MemberIsUnforgeable(stringifier, descriptor)):
- toStringDesc = {
- "name": "toString",
- "nativeName": stringifier.identifier.name,
- "length": 0,
- "flags": "JSPROP_ENUMERATE",
- "condition": PropertyDefiner.getControllingCondition(stringifier, descriptor)
- }
- if isChromeOnly(stringifier):
- self.chrome.append(toStringDesc)
- else:
- self.regular.append(toStringDesc)
- jsonifier = descriptor.operations['Jsonifier']
- if (jsonifier and
- unforgeable == MemberIsUnforgeable(jsonifier, descriptor)):
- toJSONDesc = {
- "name": "toJSON",
- "nativeName": jsonifier.identifier.name,
- "length": 0,
- "flags": "JSPROP_ENUMERATE",
- "condition": PropertyDefiner.getControllingCondition(jsonifier, descriptor)
- }
- if isChromeOnly(jsonifier):
- self.chrome.append(toJSONDesc)
- else:
- self.regular.append(toJSONDesc)
- if (unforgeable and
- descriptor.interface.getExtendedAttribute("Unforgeable")):
- # Synthesize our valueOf method
- self.regular.append({
- "name": 'valueOf',
- "selfHostedName": "Object_valueOf",
- "methodInfo": False,
- "length": 0,
- "flags": "0", # readonly/permanent added automatically.
- "condition": MemberCondition()
- })
- if descriptor.interface.isJSImplemented():
- if static:
- if descriptor.interface.hasInterfaceObject():
- self.chrome.append({
- "name": '_create',
- "nativeName": ("%s::_Create" % descriptor.name),
- "methodInfo": False,
- "length": 2,
- "flags": "0",
- "condition": MemberCondition()
- })
- else:
- for m in clearableCachedAttrs(descriptor):
- attrName = MakeNativeName(m.identifier.name)
- self.chrome.append({
- "name": "_clearCached%sValue" % attrName,
- "nativeName": MakeJSImplClearCachedValueNativeName(m),
- "methodInfo": False,
- "length": "0",
- "flags": "0",
- "condition": MemberCondition()
- })
- self.unforgeable = unforgeable
- if static:
- if not descriptor.interface.hasInterfaceObject():
- # static methods go on the interface object
- assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
- else:
- if not descriptor.interface.hasInterfacePrototypeObject():
- # non-static methods go on the interface prototype object
- assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
- def generateArray(self, array, name, doIdArrays):
- if len(array) == 0:
- return ""
- def condition(m, d):
- return m["condition"]
- def flags(m):
- unforgeable = " | JSPROP_PERMANENT | JSPROP_READONLY" if self.unforgeable else ""
- return m["flags"] + unforgeable
- def specData(m):
- if "selfHostedName" in m:
- selfHostedName = '"%s"' % m["selfHostedName"]
- assert not m.get("methodInfo", True)
- accessor = "nullptr"
- jitinfo = "nullptr"
- else:
- selfHostedName = "nullptr"
- # When defining symbols, function name may not match symbol name
- methodName = m.get("methodName", m["name"])
- accessor = m.get("nativeName", IDLToCIdentifier(methodName))
- if m.get("methodInfo", True):
- # Cast this in case the methodInfo is a
- # JSTypedMethodJitInfo.
- jitinfo = ("reinterpret_cast<const JSJitInfo*>(&%s_methodinfo)" % accessor)
- if m.get("allowCrossOriginThis", False):
- if m.get("returnsPromise", False):
- raise TypeError("%s returns a Promise but should "
- "be allowed cross-origin?" %
- accessor)
- accessor = "genericCrossOriginMethod"
- elif self.descriptor.needsSpecialGenericOps():
- if m.get("returnsPromise", False):
- accessor = "genericPromiseReturningMethod"
- else:
- accessor = "genericMethod"
- elif m.get("returnsPromise", False):
- accessor = "GenericPromiseReturningBindingMethod"
- else:
- accessor = "GenericBindingMethod"
- else:
- if m.get("returnsPromise", False):
- jitinfo = "&%s_methodinfo" % accessor
- accessor = "StaticMethodPromiseWrapper"
- else:
- jitinfo = "nullptr"
- return (m["name"], accessor, jitinfo, m["length"], flags(m), selfHostedName)
- def formatSpec(fields):
- if fields[0].startswith("@@"):
- fields = (fields[0][2:],) + fields[1:]
- return ' JS_SYM_FNSPEC(%s, %s, %s, %s, %s, %s)' % fields
- return ' JS_FNSPEC("%s", %s, %s, %s, %s, %s)' % fields
- return self.generatePrefableArray(
- array, name,
- formatSpec,
- ' JS_FS_END',
- 'JSFunctionSpec',
- condition, specData, doIdArrays)
- def IsCrossOriginWritable(attr, descriptor):
- """
- Return whether the IDLAttribute in question is cross-origin writable on the
- interface represented by descriptor. This is needed to handle the fact that
- some, but not all, interfaces implementing URLUtils want a cross-origin
- writable .href.
- """
- crossOriginWritable = attr.getExtendedAttribute("CrossOriginWritable")
- if not crossOriginWritable:
- return False
- if crossOriginWritable is True:
- return True
- assert (isinstance(crossOriginWritable, list) and
- len(crossOriginWritable) == 1)
- return crossOriginWritable[0] == descriptor.interface.identifier.name
- def isNonExposedNavigatorObjectGetter(attr, descriptor):
- return (attr.navigatorObjectGetter and
- not descriptor.getDescriptor(attr.type.inner.identifier.name).register)
- class AttrDefiner(PropertyDefiner):
- def __init__(self, descriptor, name, static, unforgeable=False):
- assert not (static and unforgeable)
- PropertyDefiner.__init__(self, descriptor, name)
- self.name = name
- # Ignore non-static attributes for interfaces without a proto object
- if descriptor.interface.hasInterfacePrototypeObject() or static:
- attributes = [m for m in descriptor.interface.members if
- m.isAttr() and m.isStatic() == static and
- MemberIsUnforgeable(m, descriptor) == unforgeable and
- not isNonExposedNavigatorObjectGetter(m, descriptor)]
- else:
- attributes = []
- self.chrome = [m for m in attributes if isChromeOnly(m)]
- self.regular = [m for m in attributes if not isChromeOnly(m)]
- self.static = static
- self.unforgeable = unforgeable
- if static:
- if not descriptor.interface.hasInterfaceObject():
- # static attributes go on the interface object
- assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
- else:
- if not descriptor.interface.hasInterfacePrototypeObject():
- # non-static attributes go on the interface prototype object
- assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
- def generateArray(self, array, name, doIdArrays):
- if len(array) == 0:
- return ""
- def flags(attr):
- unforgeable = " | JSPROP_PERMANENT" if self.unforgeable else ""
- # Attributes generated as part of a maplike/setlike declaration are
- # not enumerable.
- enumerable = " | JSPROP_ENUMERATE" if not attr.isMaplikeOrSetlikeAttr() else ""
- return ("JSPROP_SHARED" + enumerable + unforgeable)
- def getter(attr):
- if self.static:
- accessor = 'get_' + IDLToCIdentifier(attr.identifier.name)
- jitinfo = "nullptr"
- else:
- if attr.hasLenientThis():
- accessor = "genericLenientGetter"
- elif attr.getExtendedAttribute("CrossOriginReadable"):
- accessor = "genericCrossOriginGetter"
- elif self.descriptor.needsSpecialGenericOps():
- accessor = "genericGetter"
- else:
- accessor = "GenericBindingGetter"
- jitinfo = ("&%s_getterinfo" %
- IDLToCIdentifier(attr.identifier.name))
- return "{ { %s, %s } }" % \
- (accessor, jitinfo)
- def setter(attr):
- if (attr.readonly and
- attr.getExtendedAttribute("PutForwards") is None and
- attr.getExtendedAttribute("Replaceable") is None and
- attr.getExtendedAttribute("LenientSetter") is None):
- return "JSNATIVE_WRAPPER(nullptr)"
- if self.static:
- accessor = 'set_' + IDLToCIdentifier(attr.identifier.name)
- jitinfo = "nullptr"
- else:
- if attr.hasLenientThis():
- accessor = "genericLenientSetter"
- elif IsCrossOriginWritable(attr, self.descriptor):
- accessor = "genericCrossOriginSetter"
- elif self.descriptor.needsSpecialGenericOps():
- accessor = "genericSetter"
- else:
- accessor = "GenericBindingSetter"
- jitinfo = "&%s_setterinfo" % IDLToCIdentifier(attr.identifier.name)
- return "{ { %s, %s } }" % \
- (accessor, jitinfo)
- def specData(attr):
- return (attr.identifier.name, flags(attr), getter(attr),
- setter(attr))
- return self.generatePrefableArray(
- array, name,
- lambda fields: ' { "%s", %s, { { %s, %s } } }' % fields,
- ' JS_PS_END',
- 'JSPropertySpec',
- PropertyDefiner.getControllingCondition, specData, doIdArrays)
- class ConstDefiner(PropertyDefiner):
- """
- A class for definining constants on the interface object
- """
- def __init__(self, descriptor, name):
- PropertyDefiner.__init__(self, descriptor, name)
- self.name = name
- constants = [m for m in descriptor.interface.members if m.isConst()]
- self.chrome = [m for m in constants if isChromeOnly(m)]
- self.regular = [m for m in constants if not isChromeOnly(m)]
- def generateArray(self, array, name, doIdArrays):
- if len(array) == 0:
- return ""
- def specData(const):
- return (const.identifier.name,
- convertConstIDLValueToJSVal(const.value))
- return self.generatePrefableArray(
- array, name,
- lambda fields: ' { "%s", %s }' % fields,
- ' { 0, JS::UndefinedValue() }',
- 'ConstantSpec',
- PropertyDefiner.getControllingCondition, specData, doIdArrays)
- class PropertyArrays():
- def __init__(self, descriptor):
- self.staticMethods = MethodDefiner(descriptor, "StaticMethods",
- static=True)
- self.staticAttrs = AttrDefiner(descriptor, "StaticAttributes",
- static=True)
- self.methods = MethodDefiner(descriptor, "Methods", static=False)
- self.attrs = AttrDefiner(descriptor, "Attributes", static=False)
- self.unforgeableMethods = MethodDefiner(descriptor, "UnforgeableMethods",
- static=False, unforgeable=True)
- self.unforgeableAttrs = AttrDefiner(descriptor, "UnforgeableAttributes",
- static=False, unforgeable=True)
- self.consts = ConstDefiner(descriptor, "Constants")
- @staticmethod
- def arrayNames():
- return ["staticMethods", "staticAttrs", "methods", "attrs",
- "unforgeableMethods", "unforgeableAttrs", "consts"]
- def hasChromeOnly(self):
- return any(getattr(self, a).hasChromeOnly() for a in self.arrayNames())
- def hasNonChromeOnly(self):
- return any(getattr(self, a).hasNonChromeOnly() for a in self.arrayNames())
- def __str__(self):
- define = ""
- for array in self.arrayNames():
- define += str(getattr(self, array))
- return define
- class CGNativeProperties(CGList):
- def __init__(self, descriptor, properties):
- def generateNativeProperties(name, chrome):
- def check(p):
- return p.hasChromeOnly() if chrome else p.hasNonChromeOnly()
- nativePropsInts = []
- nativePropsTrios = []
- iteratorAliasIndex = -1
- for index, item in enumerate(properties.methods.regular):
- if item.get("hasIteratorAlias"):
- iteratorAliasIndex = index
- break
- nativePropsInts.append(CGGeneric(str(iteratorAliasIndex)))
- offset = 0
- for array in properties.arrayNames():
- propertyArray = getattr(properties, array)
- if check(propertyArray):
- varName = propertyArray.variableName(chrome)
- bitfields = "true, %d /* %s */" % (offset, varName)
- offset += 1
- nativePropsInts.append(CGGeneric(bitfields))
- if propertyArray.usedForXrays():
- ids = "%(name)s_ids"
- else:
- ids = "nullptr"
- trio = "{ %(name)s, " + ids + ", %(name)s_specs }"
- trio = trio % {'name': varName}
- nativePropsTrios.append(CGGeneric(trio))
- else:
- bitfields = "false, 0"
- nativePropsInts.append(CGGeneric(bitfields))
- nativePropsTrios = \
- [CGWrapper(CGIndenter(CGList(nativePropsTrios, ",\n")),
- pre='{\n', post='\n}')]
- nativeProps = nativePropsInts + nativePropsTrios
- pre = ("static const NativePropertiesN<%d> %s = {\n" %
- (offset, name))
- return CGWrapper(CGIndenter(CGList(nativeProps, ",\n")),
- pre=pre, post="\n};\n")
- nativeProperties = []
- if properties.hasNonChromeOnly():
- nativeProperties.append(
- generateNativeProperties("sNativeProperties", False))
- if properties.hasChromeOnly():
- nativeProperties.append(
- generateNativeProperties("sChromeOnlyNativeProperties", True))
- CGList.__init__(self, nativeProperties, "\n")
- def declare(self):
- return ""
- def define(self):
- return CGList.define(self)
- class CGJsonifyAttributesMethod(CGAbstractMethod):
- """
- Generate the JsonifyAttributes method for an interface descriptor
- """
- def __init__(self, descriptor):
- args = [Argument('JSContext*', 'aCx'),
- Argument('JS::Handle<JSObject*>', 'obj'),
- Argument('%s*' % descriptor.nativeType, 'self'),
- Argument('JS::Rooted<JSObject*>&', 'aResult')]
- CGAbstractMethod.__init__(self, descriptor, 'JsonifyAttributes', 'bool', args)
- def definition_body(self):
- ret = ''
- interface = self.descriptor.interface
- for m in interface.members:
- if m.isAttr() and not m.isStatic() and m.type.isSerializable():
- ret += fill(
- """
- { // scope for "temp"
- JS::Rooted<JS::Value> temp(aCx);
- if (!get_${name}(aCx, obj, self, JSJitGetterCallArgs(&temp))) {
- return false;
- }
- if (!JS_DefineProperty(aCx, aResult, "${name}", temp, JSPROP_ENUMERATE)) {
- return false;
- }
- }
- """,
- name=IDLToCIdentifier(m.identifier.name))
- ret += 'return true;\n'
- return ret
- class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
- """
- Generate the CreateInterfaceObjects method for an interface descriptor.
- properties should be a PropertyArrays instance.
- """
- def __init__(self, descriptor, properties, haveUnscopables):
- args = [Argument('JSContext*', 'aCx'),
- Argument('JS::Handle<JSObject*>', 'aGlobal'),
- Argument('ProtoAndIfaceCache&', 'aProtoAndIfaceCache'),
- Argument('bool', 'aDefineOnGlobal')]
- CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', 'void', args)
- self.properties = properties
- self.haveUnscopables = haveUnscopables
- def definition_body(self):
- (protoGetter, protoHandleGetter) = InterfacePrototypeObjectProtoGetter(self.descriptor)
- if protoHandleGetter is None:
- parentProtoType = "Rooted"
- getParentProto = "aCx, " + protoGetter
- else:
- parentProtoType = "Handle"
- getParentProto = protoHandleGetter
- getParentProto = getParentProto + "(aCx)"
- (protoGetter, protoHandleGetter) = InterfaceObjectProtoGetter(self.descriptor)
- if protoHandleGetter is None:
- getConstructorProto = "aCx, " + protoGetter
- constructorProtoType = "Rooted"
- else:
- getConstructorProto = protoHandleGetter
- constructorProtoType = "Handle"
- getConstructorProto += "(aCx)"
- needInterfaceObject = self.descriptor.interface.hasInterfaceObject()
- needInterfacePrototypeObject = self.descriptor.interface.hasInterfacePrototypeObject()
- # if we don't need to create anything, why are we generating this?
- assert needInterfaceObject or needInterfacePrototypeObject
- getParentProto = fill(
- """
- JS::${type}<JSObject*> parentProto(${getParentProto});
- if (!parentProto) {
- return;
- }
- """,
- type=parentProtoType,
- getParentProto=getParentProto)
- getConstructorProto = fill(
- """
- JS::${type}<JSObject*> constructorProto(${getConstructorProto});
- if (!constructorProto) {
- return;
- }
- """,
- type=constructorProtoType,
- getConstructorProto=getConstructorProto)
- idsToInit = []
- # There is no need to init any IDs in bindings that don't want Xrays.
- if self.descriptor.wantsXrays:
- for var in self.properties.arrayNames():
- props = getattr(self.properties, var)
- # We only have non-chrome ids to init if we have no chrome ids.
- if props.hasChromeOnly():
- idsToInit.append(props.variableName(True))
- if props.hasNonChromeOnly():
- idsToInit.append(props.variableName(False))
- if len(idsToInit) > 0:
- initIdCalls = ["!InitIds(aCx, %s, %s_ids)" % (varname, varname)
- for varname in idsToInit]
- idsInitedFlag = CGGeneric("static bool sIdsInited = false;\n")
- setFlag = CGGeneric("sIdsInited = true;\n")
- initIdConditionals = [CGIfWrapper(CGGeneric("return;\n"), call)
- for call in initIdCalls]
- initIds = CGList([idsInitedFlag,
- CGIfWrapper(CGList(initIdConditionals + [setFlag]),
- "!sIdsInited && NS_IsMainThread()")])
- else:
- initIds = None
- prefCacheData = []
- for var in self.properties.arrayNames():
- props = getattr(self.properties, var)
- prefCacheData.extend(props.prefCacheData)
- if len(prefCacheData) != 0:
- prefCacheData = [
- CGGeneric('Preferences::AddBoolVarCache(%s, "%s");\n' % (ptr, pref))
- for pref, ptr in prefCacheData]
- prefCache = CGWrapper(CGIndenter(CGList(prefCacheData)),
- pre=("static bool sPrefCachesInited = false;\n"
- "if (!sPrefCachesInited && NS_IsMainThread()) {\n"
- " sPrefCachesInited = true;\n"),
- post="}\n")
- else:
- prefCache = None
- if self.descriptor.interface.ctor():
- constructArgs = methodLength(self.descriptor.interface.ctor())
- else:
- constructArgs = 0
- if len(self.descriptor.interface.namedConstructors) > 0:
- namedConstructors = "namedConstructors"
- else:
- namedConstructors = "nullptr"
- if needInterfacePrototypeObject:
- protoClass = "&sPrototypeClass.mBase"
- protoCache = "&aProtoAndIfaceCache.EntrySlotOrCreate(prototypes::id::%s)" % self.descriptor.name
- parentProto = "parentProto"
- getParentProto = CGGeneric(getParentProto)
- else:
- protoClass = "nullptr"
- protoCache = "nullptr"
- parentProto = "nullptr"
- getParentProto = None
- if needInterfaceObject:
- interfaceClass = "&sInterfaceObjectClass.mBase"
- interfaceCache = "&aProtoAndIfaceCache.EntrySlotOrCreate(constructors::id::%s)" % self.descriptor.name
- getConstructorProto = CGGeneric(getConstructorProto)
- constructorProto = "constructorProto"
- else:
- # We don't have slots to store the named constructors.
- assert len(self.descriptor.interface.namedConstructors) == 0
- interfaceClass = "nullptr"
- interfaceCache = "nullptr"
- getConstructorProto = None
- constructorProto = "nullptr"
- isGlobal = self.descriptor.isGlobal() is not None
- if self.properties.hasNonChromeOnly():
- properties = "sNativeProperties.Upcast()"
- else:
- properties = "nullptr"
- if self.properties.hasChromeOnly():
- chromeProperties = "nsContentUtils::ThreadsafeIsCallerChrome() ? sChromeOnlyNativeProperties.Upcast() : nullptr"
- else:
- chromeProperties = "nullptr"
- call = fill(
- """
- JS::Heap<JSObject*>* protoCache = ${protoCache};
- JS::Heap<JSObject*>* interfaceCache = ${interfaceCache};
- dom::CreateInterfaceObjects(aCx, aGlobal, ${parentProto},
- ${protoClass}, protoCache,
- ${constructorProto}, ${interfaceClass}, ${constructArgs}, ${namedConstructors},
- interfaceCache,
- ${properties},
- ${chromeProperties},
- ${name}, aDefineOnGlobal,
- ${unscopableNames},
- ${isGlobal});
- """,
- protoClass=protoClass,
- parentProto=parentProto,
- protoCache=protoCache,
- constructorProto=constructorProto,
- interfaceClass=interfaceClass,
- constructArgs=constructArgs,
- namedConstructors=namedConstructors,
- interfaceCache=interfaceCache,
- properties=properties,
- chromeProperties=chromeProperties,
- name='"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "nullptr",
- unscopableNames="unscopableNames" if self.haveUnscopables else "nullptr",
- isGlobal=toStringBool(isGlobal))
- # If we fail after here, we must clear interface and prototype caches
- # using this code: intermediate failure must not expose the interface in
- # partially-constructed state. Note that every case after here needs an
- # interface prototype object.
- failureCode = dedent(
- """
- *protoCache = nullptr;
- if (interfaceCache) {
- *interfaceCache = nullptr;
- }
- return;
- """)
- aliasedMembers = [m for m in self.descriptor.interface.members if m.isMethod() and m.aliases]
- if aliasedMembers:
- assert needInterfacePrototypeObject
- def defineAlias(alias):
- if alias == "@@iterator":
- symbolJSID = "SYMBOL_TO_JSID(JS::GetWellKnownSymbol(aCx, JS::SymbolCode::iterator))"
- getSymbolJSID = CGGeneric(fill("JS::Rooted<jsid> iteratorId(aCx, ${symbolJSID});",
- symbolJSID=symbolJSID))
- defineFn = "JS_DefinePropertyById"
- prop = "iteratorId"
- elif alias.startswith("@@"):
- raise TypeError("Can't handle any well-known Symbol other than @@iterator")
- else:
- getSymbolJSID = None
- defineFn = "JS_DefineProperty"
- prop = '"%s"' % alias
- return CGList([
- getSymbolJSID,
- # XXX If we ever create non-enumerable properties that can
- # be aliased, we should consider making the aliases
- # match the enumerability of the property being aliased.
- CGGeneric(fill(
- """
- if (!${defineFn}(aCx, proto, ${prop}, aliasedVal, JSPROP_ENUMERATE)) {
- $*{failureCode}
- }
- """,
- defineFn=defineFn,
- prop=prop,
- failureCode=failureCode))
- ], "\n")
- def defineAliasesFor(m):
- return CGList([
- CGGeneric(fill(
- """
- if (!JS_GetProperty(aCx, proto, \"${prop}\", &aliasedVal)) {
- $*{failureCode}
- }
- """,
- failureCode=failureCode,
- prop=m.identifier.name))
- ] + [defineAlias(alias) for alias in sorted(m.aliases)])
- defineAliases = CGList([
- CGGeneric(fill("""
- // Set up aliases on the interface prototype object we just created.
- JS::Handle<JSObject*> proto = GetProtoObjectHandle(aCx);
- if (!proto) {
- $*{failureCode}
- }
- """,
- failureCode=failureCode)),
- CGGeneric("JS::Rooted<JS::Value> aliasedVal(aCx);\n\n")
- ] + [defineAliasesFor(m) for m in sorted(aliasedMembers)])
- else:
- defineAliases = None
- # Globals handle unforgeables directly in Wrap() instead of
- # via a holder.
- if self.descriptor.hasUnforgeableMembers and not self.descriptor.isGlobal():
- assert needInterfacePrototypeObject
- # We want to use the same JSClass and prototype as the object we'll
- # end up defining the unforgeable properties on in the end, so that
- # we can use JS_InitializePropertiesFromCompatibleNativeObject to do
- # a fast copy. In the case of proxies that's null, because the
- # expando object is a vanilla object, but in the case of other DOM
- # objects it's whatever our class is.
- if self.descriptor.proxy:
- holderClass = "nullptr"
- holderProto = "nullptr"
- else:
- holderClass = "sClass.ToJSClass()"
- holderProto = "*protoCache"
- createUnforgeableHolder = CGGeneric(fill(
- """
- JS::Rooted<JSObject*> unforgeableHolder(aCx);
- {
- JS::Rooted<JSObject*> holderProto(aCx, ${holderProto});
- unforgeableHolder = JS_NewObjectWithoutMetadata(aCx, ${holderClass}, holderProto);
- if (!unforgeableHolder) {
- $*{failureCode}
- }
- }
- """,
- holderProto=holderProto,
- holderClass=holderClass,
- failureCode=failureCode))
- defineUnforgeables = InitUnforgeablePropertiesOnHolder(self.descriptor,
- self.properties,
- failureCode)
- createUnforgeableHolder = CGList(
- [createUnforgeableHolder, defineUnforgeables])
- installUnforgeableHolder = CGGeneric(dedent(
- """
- if (*protoCache) {
- js::SetReservedSlot(*protoCache, DOM_INTERFACE_PROTO_SLOTS_BASE,
- JS::ObjectValue(*unforgeableHolder));
- }
- """))
- unforgeableHolderSetup = CGList(
- [createUnforgeableHolder, installUnforgeableHolder], "\n")
- else:
- unforgeableHolderSetup = None
- if (self.descriptor.interface.isOnGlobalProtoChain() and
- needInterfacePrototypeObject):
- makeProtoPrototypeImmutable = CGGeneric(fill(
- """
- if (*${protoCache}) {
- bool succeeded;
- JS::Handle<JSObject*> prot = GetProtoObjectHandle(aCx);
- if (!JS_SetImmutablePrototype(aCx, prot, &succeeded)) {
- $*{failureCode}
- }
- MOZ_ASSERT(succeeded,
- "making a fresh prototype object's [[Prototype]] "
- "immutable can internally fail, but it should "
- "never be unsuccessful");
- }
- """,
- protoCache=protoCache,
- failureCode=failureCode))
- else:
- makeProtoPrototypeImmutable = None
- return CGList(
- [getParentProto, getConstructorProto, initIds,
- prefCache, CGGeneric(call), defineAliases, unforgeableHolderSetup,
- makeProtoPrototypeImmutable],
- "\n").define()
- class CGGetPerInterfaceObject(CGAbstractMethod):
- """
- A method for getting a per-interface object (a prototype object or interface
- constructor object).
- """
- def __init__(self, descriptor, name, idPrefix="", extraArgs=[]):
- args = [Argument('JSContext*', 'aCx')] + extraArgs
- CGAbstractMethod.__init__(self, descriptor, name,
- 'JS::Handle<JSObject*>', args)
- self.id = idPrefix + "id::" + self.descriptor.name
- def definition_body(self):
- return fill(
- """
- /* Make sure our global is sane. Hopefully we can remove this sometime */
- JSObject* global = JS::CurrentGlobalOrNull(aCx);
- if (!(js::GetObjectClass(global)->flags & JSCLASS_DOM_GLOBAL)) {
- return nullptr;
- }
- /* Check to see whether the interface objects are already installed */
- ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(global);
- if (!protoAndIfaceCache.EntrySlotIfExists(${id})) {
- JS::Rooted<JSObject*> rootedGlobal(aCx, global);
- CreateInterfaceObjects(aCx, rootedGlobal, protoAndIfaceCache, aDefineOnGlobal);
- }
- /*
- * The object might _still_ be null, but that's OK.
- *
- * Calling fromMarkedLocation() is safe because protoAndIfaceCache is
- * traced by TraceProtoAndIfaceCache() and its contents are never
- * changed after they have been set.
- *
- * Calling address() avoids the read read barrier that does gray
- * unmarking, but it's not possible for the object to be gray here.
- */
- const JS::Heap<JSObject*>& entrySlot = protoAndIfaceCache.EntrySlotMustExist(${id});
- MOZ_ASSERT_IF(entrySlot, !JS::ObjectIsMarkedGray(entrySlot));
- return JS::Handle<JSObject*>::fromMarkedLocation(entrySlot.address());
- """,
- id=self.id)
- class CGGetProtoObjectHandleMethod(CGGetPerInterfaceObject):
- """
- A method for getting the interface prototype object.
- """
- def __init__(self, descriptor):
- CGGetPerInterfaceObject.__init__(self, descriptor, "GetProtoObjectHandle",
- "prototypes::")
- def definition_body(self):
- return dedent("""
- /* Get the interface prototype object for this class. This will create the
- object as needed. */
- bool aDefineOnGlobal = true;
- """) + CGGetPerInterfaceObject.definition_body(self)
- class CGGetProtoObjectMethod(CGAbstractMethod):
- """
- A method for getting the interface prototype object.
- """
- def __init__(self, descriptor):
- CGAbstractMethod.__init__(
- self, descriptor, "GetProtoObject", "JSObject*",
- [Argument('JSContext*', 'aCx')])
- def definition_body(self):
- return "return GetProtoObjectHandle(aCx);\n"
- class CGGetConstructorObjectHandleMethod(CGGetPerInterfaceObject):
- """
- A method for getting the interface constructor object.
- """
- def __init__(self, descriptor):
- CGGetPerInterfaceObject.__init__(
- self, descriptor, "GetConstructorObjectHandle",
- "constructors::",
- extraArgs=[Argument("bool", "aDefineOnGlobal", "true")])
- def definition_body(self):
- return dedent("""
- /* Get the interface object for this class. This will create the object as
- needed. */
- """) + CGGetPerInterfaceObject.definition_body(self)
- class CGGetConstructorObjectMethod(CGAbstractMethod):
- """
- A method for getting the interface constructor object.
- """
- def __init__(self, descriptor):
- CGAbstractMethod.__init__(
- self, descriptor, "GetConstructorObject", "JSObject*",
- [Argument('JSContext*', 'aCx')])
- def definition_body(self):
- return "return GetConstructorObjectHandle(aCx);\n"
- class CGGetNamedPropertiesObjectMethod(CGAbstractStaticMethod):
- def __init__(self, descriptor):
- args = [Argument('JSContext*', 'aCx')]
- CGAbstractStaticMethod.__init__(self, descriptor,
- 'GetNamedPropertiesObject',
- 'JSObject*', args)
- def definition_body(self):
- parentProtoName = self.descriptor.parentPrototypeName
- if parentProtoName is None:
- getParentProto = ""
- parentProto = "nullptr"
- else:
- getParentProto = fill(
- """
- JS::Rooted<JSObject*> parentProto(aCx, ${parent}::GetProtoObjectHandle(aCx));
- if (!parentProto) {
- return nullptr;
- }
- """,
- parent=toBindingNamespace(parentProtoName))
- parentProto = "parentProto"
- return fill(
- """
- /* Make sure our global is sane. Hopefully we can remove this sometime */
- JSObject* global = JS::CurrentGlobalOrNull(aCx);
- if (!(js::GetObjectClass(global)->flags & JSCLASS_DOM_GLOBAL)) {
- return nullptr;
- }
- /* Check to see whether the named properties object has already been created */
- ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(global);
- JS::Heap<JSObject*>& namedPropertiesObject = protoAndIfaceCache.EntrySlotOrCreate(namedpropertiesobjects::id::${ifaceName});
- if (!namedPropertiesObject) {
- $*{getParentProto}
- namedPropertiesObject = ${nativeType}::CreateNamedPropertiesObject(aCx, ${parentProto});
- DebugOnly<const DOMIfaceAndProtoJSClass*> clasp =
- DOMIfaceAndProtoJSClass::FromJSClass(js::GetObjectClass(namedPropertiesObject));
- MOZ_ASSERT(clasp->mType == eNamedPropertiesObject,
- "Expected ${nativeType}::CreateNamedPropertiesObject to return a named properties object");
- MOZ_ASSERT(clasp->mNativeHooks,
- "The named properties object for ${nativeType} should have NativePropertyHooks.");
- MOZ_ASSERT(clasp->mNativeHooks->mResolveOwnProperty,
- "Don't know how to resolve the properties of the named properties object for ${nativeType}.");
- MOZ_ASSERT(clasp->mNativeHooks->mEnumerateOwnProperties,
- "Don't know how to enumerate the properties of the named properties object for ${nativeType}.");
- }
- return namedPropertiesObject.get();
- """,
- getParentProto=getParentProto,
- ifaceName=self.descriptor.name,
- parentProto=parentProto,
- nativeType=self.descriptor.nativeType)
- class CGDefineDOMInterfaceMethod(CGAbstractMethod):
- """
- A method for resolve hooks to try to lazily define the interface object for
- a given interface.
- """
- def __init__(self, descriptor):
- args = [Argument('JSContext*', 'aCx'),
- Argument('JS::Handle<JSObject*>', 'aGlobal'),
- Argument('JS::Handle<jsid>', 'id'),
- Argument('bool', 'aDefineOnGlobal')]
- CGAbstractMethod.__init__(self, descriptor, 'DefineDOMInterface', 'JSObject*', args)
- def definition_body(self):
- if len(self.descriptor.interface.namedConstructors) > 0:
- getConstructor = dedent("""
- JSObject* interfaceObject = GetConstructorObjectHandle(aCx, aDefineOnGlobal);
- if (!interfaceObject) {
- return nullptr;
- }
- for (unsigned slot = DOM_INTERFACE_SLOTS_BASE; slot < JSCLASS_RESERVED_SLOTS(&sInterfaceObjectClass.mBase); ++slot) {
- JSObject* constructor = &js::GetReservedSlot(interfaceObject, slot).toObject();
- if (JS_GetFunctionId(JS_GetObjectFunction(constructor)) == JSID_TO_STRING(id)) {
- return constructor;
- }
- }
- return interfaceObject;
- """)
- else:
- getConstructor = "return GetConstructorObjectHandle(aCx, aDefineOnGlobal);\n"
- return getConstructor
- def getConditionList(idlobj, cxName, objName):
- """
- Get the list of conditions for idlobj (to be used in "is this enabled"
- checks). This will be returned as a CGList with " &&\n" as the separator,
- for readability.
- objName is the name of the object that we're working with, because some of
- our test functions want that.
- """
- conditions = []
- pref = idlobj.getExtendedAttribute("Pref")
- if pref:
- assert isinstance(pref, list) and len(pref) == 1
- conditions.append('Preferences::GetBool("%s")' % pref[0])
- if idlobj.getExtendedAttribute("ChromeOnly"):
- conditions.append("nsContentUtils::ThreadsafeIsCallerChrome()")
- func = idlobj.getExtendedAttribute("Func")
- if func:
- assert isinstance(func, list) and len(func) == 1
- conditions.append("%s(%s, %s)" % (func[0], cxName, objName))
- if idlobj.getExtendedAttribute("SecureContext"):
- conditions.append("mozilla::dom::IsSecureContextOrObjectIsFromSecureContext(%s, %s)" % (cxName, objName))
- return CGList((CGGeneric(cond) for cond in conditions), " &&\n")
- class CGConstructorEnabled(CGAbstractMethod):
- """
- A method for testing whether we should be exposing this interface
- object or navigator property. This can perform various tests
- depending on what conditions are specified on the interface.
- """
- def __init__(self, descriptor):
- CGAbstractMethod.__init__(self, descriptor,
- 'ConstructorEnabled', 'bool',
- [Argument("JSContext*", "aCx"),
- Argument("JS::Handle<JSObject*>", "aObj")])
- def definition_body(self):
- body = CGList([], "\n")
- iface = self.descriptor.interface
- if not iface.isExposedOnMainThread():
- exposedInWindowCheck = dedent(
- """
- MOZ_ASSERT(!NS_IsMainThread(), "Why did we even get called?");
- """)
- body.append(CGGeneric(exposedInWindowCheck))
- if iface.isExposedInSomeButNotAllWorkers():
- workerGlobals = sorted(iface.getWorkerExposureSet())
- workerCondition = CGList((CGGeneric('strcmp(name, "%s")' % workerGlobal)
- for workerGlobal in workerGlobals), " && ")
- exposedInWorkerCheck = fill(
- """
- const char* name = js::GetObjectClass(aObj)->name;
- if (${workerCondition}) {
- return false;
- }
- """, workerCondition=workerCondition.define())
- exposedInWorkerCheck = CGGeneric(exposedInWorkerCheck)
- if iface.isExposedOnMainThread():
- exposedInWorkerCheck = CGIfWrapper(exposedInWorkerCheck,
- "!NS_IsMainThread()")
- body.append(exposedInWorkerCheck)
- conditions = getConditionList(iface, "aCx", "aObj")
- # We should really have some conditions
- assert len(body) or len(conditions)
- conditionsWrapper = ""
- if len(conditions):
- conditionsWrapper = CGWrapper(conditions,
- pre="return ",
- post=";\n",
- reindent=True)
- else:
- conditionsWrapper = CGGeneric("return true;\n")
- body.append(conditionsWrapper)
- return body.define()
- def CreateBindingJSObject(descriptor, properties):
- objDecl = "BindingJSObjectCreator<%s> creator(aCx);\n" % descriptor.nativeType
- # We don't always need to root obj, but there are a variety
- # of cases where we do, so for simplicity, just always root it.
- if descriptor.proxy:
- create = dedent(
- """
- creator.CreateProxyObject(aCx, &sClass.mBase, DOMProxyHandler::getInstance(),
- proto, aObject, aReflector);
- if (!aReflector) {
- return false;
- }
- """)
- if descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
- create += dedent("""
- js::SetProxyExtra(aReflector, JSPROXYSLOT_EXPANDO,
- JS::PrivateValue(&aObject->mExpandoAndGeneration));
- """)
- else:
- create = dedent(
- """
- creator.CreateObject(aCx, sClass.ToJSClass(), proto, aObject, aReflector);
- if (!aReflector) {
- return false;
- }
- """)
- return objDecl + create
- def InitUnforgeablePropertiesOnHolder(descriptor, properties, failureCode,
- holderName="unforgeableHolder"):
- """
- Define the unforgeable properties on the unforgeable holder for
- the interface represented by descriptor.
- properties is a PropertyArrays instance.
- """
- assert (properties.unforgeableAttrs.hasNonChromeOnly() or
- properties.unforgeableAttrs.hasChromeOnly() or
- properties.unforgeableMethods.hasNonChromeOnly() or
- properties.unforgeableMethods.hasChromeOnly())
- unforgeables = []
- defineUnforgeableAttrs = fill(
- """
- if (!DefineUnforgeableAttributes(aCx, ${holderName}, %s)) {
- $*{failureCode}
- }
- """,
- failureCode=failureCode,
- holderName=holderName)
- defineUnforgeableMethods = fill(
- """
- if (!DefineUnforgeableMethods(aCx, ${holderName}, %s)) {
- $*{failureCode}
- }
- """,
- failureCode=failureCode,
- holderName=holderName)
- unforgeableMembers = [
- (defineUnforgeableAttrs, properties.unforgeableAttrs),
- (defineUnforgeableMethods, properties.unforgeableMethods)
- ]
- for (template, array) in unforgeableMembers:
- if array.hasNonChromeOnly():
- unforgeables.append(CGGeneric(template % array.variableName(False)))
- if array.hasChromeOnly():
- unforgeables.append(
- CGIfWrapper(CGGeneric(template % array.variableName(True)),
- "nsContentUtils::ThreadsafeIsCallerChrome()"))
- if descriptor.interface.getExtendedAttribute("Unforgeable"):
- # We do our undefined toPrimitive here, not as a regular property
- # because we don't have a concept of value props anywhere in IDL.
- unforgeables.append(CGGeneric(fill(
- """
- JS::RootedId toPrimitive(aCx,
- SYMBOL_TO_JSID(JS::GetWellKnownSymbol(aCx, JS::SymbolCode::toPrimitive)));
- if (!JS_DefinePropertyById(aCx, ${holderName}, toPrimitive,
- JS::UndefinedHandleValue,
- JSPROP_READONLY | JSPROP_PERMANENT)) {
- $*{failureCode}
- }
- """,
- failureCode=failureCode,
- holderName=holderName)))
- return CGWrapper(CGList(unforgeables), pre="\n")
- def CopyUnforgeablePropertiesToInstance(descriptor, failureCode):
- """
- Copy the unforgeable properties from the unforgeable holder for
- this interface to the instance object we have.
- """
- assert not descriptor.isGlobal();
- if not descriptor.hasUnforgeableMembers:
- return ""
- copyCode = [
- CGGeneric(dedent(
- """
- // Important: do unforgeable property setup after we have handed
- // over ownership of the C++ object to obj as needed, so that if
- // we fail and it ends up GCed it won't have problems in the
- // finalizer trying to drop its ownership of the C++ object.
- """))
- ]
- # For proxies, we want to define on the expando object, not directly on the
- # reflector, so we can make sure we don't get confused by named getters.
- if descriptor.proxy:
- copyCode.append(CGGeneric(fill(
- """
- JS::Rooted<JSObject*> expando(aCx,
- DOMProxyHandler::EnsureExpandoObject(aCx, aReflector));
- if (!expando) {
- $*{failureCode}
- }
- """,
- failureCode=failureCode)))
- obj = "expando"
- else:
- obj = "aReflector"
- copyCode.append(CGGeneric(fill(
- """
- JS::Rooted<JSObject*> unforgeableHolder(aCx,
- &js::GetReservedSlot(canonicalProto, DOM_INTERFACE_PROTO_SLOTS_BASE).toObject());
- if (!JS_InitializePropertiesFromCompatibleNativeObject(aCx, ${obj}, unforgeableHolder)) {
- $*{failureCode}
- }
- """,
- obj=obj,
- failureCode=failureCode)))
- return CGWrapper(CGList(copyCode), pre="\n").define()
- def AssertInheritanceChain(descriptor):
- asserts = ""
- iface = descriptor.interface
- while iface:
- desc = descriptor.getDescriptor(iface.identifier.name)
- asserts += (
- "MOZ_ASSERT(static_cast<%s*>(aObject) == \n"
- " reinterpret_cast<%s*>(aObject),\n"
- " \"Multiple inheritance for %s is broken.\");\n" %
- (desc.nativeType, desc.nativeType, desc.nativeType))
- iface = iface.parent
- asserts += "MOZ_ASSERT(ToSupportsIsCorrect(aObject));\n"
- return asserts
- def InitMemberSlots(descriptor, failureCode):
- """
- Initialize member slots on our JS object if we're supposed to have some.
- Note that this is called after the SetWrapper() call in the
- wrapperCache case, since that can affect how our getters behave
- and we plan to invoke them here. So if we fail, we need to
- ClearWrapper.
- """
- if not descriptor.interface.hasMembersInSlots():
- return ""
- return fill(
- """
- if (!UpdateMemberSlots(aCx, aReflector, aObject)) {
- $*{failureCode}
- }
- """,
- failureCode=failureCode)
- def SetImmutablePrototype(descriptor, failureCode):
- if not descriptor.hasNonOrdinaryGetPrototypeOf():
- return ""
- return fill(
- """
- bool succeeded;
- if (!JS_SetImmutablePrototype(aCx, aReflector, &succeeded)) {
- ${failureCode}
- }
- MOZ_ASSERT(succeeded,
- "Making a fresh reflector instance have an immutable "
- "prototype can internally fail, but it should never be "
- "unsuccessful");
- """,
- failureCode=failureCode)
- def DeclareProto():
- """
- Declare the canonicalProto and proto we have for our wrapping operation.
- """
- return dedent(
- """
- JS::Handle<JSObject*> canonicalProto = GetProtoObjectHandle(aCx);
- if (!canonicalProto) {
- return false;
- }
- JS::Rooted<JSObject*> proto(aCx);
- if (aGivenProto) {
- proto = aGivenProto;
- // Unfortunately, while aGivenProto was in the compartment of aCx
- // coming in, we changed compartments to that of "parent" so may need
- // to wrap the proto here.
- if (js::GetContextCompartment(aCx) != js::GetObjectCompartment(proto)) {
- if (!JS_WrapObject(aCx, &proto)) {
- return false;
- }
- }
- } else {
- proto = canonicalProto;
- }
- """)
- class CGWrapWithCacheMethod(CGAbstractMethod):
- """
- Create a wrapper JSObject for a given native that implements nsWrapperCache.
- properties should be a PropertyArrays instance.
- """
- def __init__(self, descriptor, properties):
- assert descriptor.interface.hasInterfacePrototypeObject()
- args = [Argument('JSContext*', 'aCx'),
- Argument(descriptor.nativeType + '*', 'aObject'),
- Argument('nsWrapperCache*', 'aCache'),
- Argument('JS::Handle<JSObject*>', 'aGivenProto'),
- Argument('JS::MutableHandle<JSObject*>', 'aReflector')]
- CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'bool', args)
- self.properties = properties
- def definition_body(self):
- if self.descriptor.proxy:
- preserveWrapper = dedent(
- """
- // For DOM proxies, the only reliable way to preserve the wrapper
- // is to force creation of the expando object.
- JS::Rooted<JSObject*> unused(aCx,
- DOMProxyHandler::EnsureExpandoObject(aCx, aReflector));
- """)
- else:
- preserveWrapper = "PreserveWrapper(aObject);\n"
- failureCode = dedent(
- """
- aCache->ReleaseWrapper(aObject);
- aCache->ClearWrapper();
- return false;
- """)
- return fill(
- """
- $*{assertInheritance}
- MOZ_ASSERT(!aCache->GetWrapper(),
- "You should probably not be using Wrap() directly; use "
- "GetOrCreateDOMReflector instead");
- MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache),
- "nsISupports must be on our primary inheritance chain");
- JS::Rooted<JSObject*> global(aCx, FindAssociatedGlobal(aCx, aObject->GetParentObject()));
- if (!global) {
- return false;
- }
- MOZ_ASSERT(JS_IsGlobalObject(global));
- MOZ_ASSERT(!JS::ObjectIsMarkedGray(global));
- // That might have ended up wrapping us already, due to the wonders
- // of XBL. Check for that, and bail out as needed.
- aReflector.set(aCache->GetWrapper());
- if (aReflector) {
- #ifdef DEBUG
- binding_detail::AssertReflectorHasGivenProto(aCx, aReflector, aGivenProto);
- #endif // DEBUG
- return true;
- }
- JSAutoCompartment ac(aCx, global);
- $*{declareProto}
- $*{createObject}
- aCache->SetWrapper(aReflector);
- $*{unforgeable}
- $*{slots}
- $*{setImmutablePrototype}
- creator.InitializationSucceeded();
- MOZ_ASSERT(aCache->GetWrapperPreserveColor() &&
- aCache->GetWrapperPreserveColor() == aReflector);
- // If proto != canonicalProto, we have to preserve our wrapper;
- // otherwise we won't be able to properly recreate it later, since
- // we won't know what proto to use. Note that we don't check
- // aGivenProto here, since it's entirely possible (and even
- // somewhat common) to have a non-null aGivenProto which is the
- // same as canonicalProto.
- if (proto != canonicalProto) {
- $*{preserveWrapper}
- }
- return true;
- """,
- assertInheritance=AssertInheritanceChain(self.descriptor),
- declareProto=DeclareProto(),
- createObject=CreateBindingJSObject(self.descriptor, self.properties),
- unforgeable=CopyUnforgeablePropertiesToInstance(self.descriptor,
- failureCode),
- slots=InitMemberSlots(self.descriptor, failureCode),
- setImmutablePrototype=SetImmutablePrototype(self.descriptor,
- failureCode),
- preserveWrapper=preserveWrapper)
- class CGWrapMethod(CGAbstractMethod):
- def __init__(self, descriptor):
- # XXX can we wrap if we don't have an interface prototype object?
- assert descriptor.interface.hasInterfacePrototypeObject()
- args = [Argument('JSContext*', 'aCx'),
- Argument('T*', 'aObject'),
- Argument('JS::Handle<JSObject*>', 'aGivenProto')]
- CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args,
- inline=True, templateArgs=["class T"])
- def definition_body(self):
- return dedent("""
- JS::Rooted<JSObject*> reflector(aCx);
- return Wrap(aCx, aObject, aObject, aGivenProto, &reflector) ? reflector.get() : nullptr;
- """)
- class CGWrapNonWrapperCacheMethod(CGAbstractMethod):
- """
- Create a wrapper JSObject for a given native that does not implement
- nsWrapperCache.
- properties should be a PropertyArrays instance.
- """
- def __init__(self, descriptor, properties):
- # XXX can we wrap if we don't have an interface prototype object?
- assert descriptor.interface.hasInterfacePrototypeObject()
- args = [Argument('JSContext*', 'aCx'),
- Argument(descriptor.nativeType + '*', 'aObject'),
- Argument('JS::Handle<JSObject*>', 'aGivenProto'),
- Argument('JS::MutableHandle<JSObject*>', 'aReflector')]
- CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'bool', args)
- self.properties = properties
- def definition_body(self):
- failureCode = "return false;\n"
- return fill(
- """
- $*{assertions}
- JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
- $*{declareProto}
- $*{createObject}
- $*{unforgeable}
- $*{slots}
- $*{setImmutablePrototype}
- creator.InitializationSucceeded();
- return true;
- """,
- assertions=AssertInheritanceChain(self.descriptor),
- declareProto=DeclareProto(),
- createObject=CreateBindingJSObject(self.descriptor, self.properties),
- unforgeable=CopyUnforgeablePropertiesToInstance(self.descriptor,
- failureCode),
- slots=InitMemberSlots(self.descriptor, failureCode),
- setImmutablePrototype=SetImmutablePrototype(self.descriptor,
- failureCode))
- class CGWrapGlobalMethod(CGAbstractMethod):
- """
- Create a wrapper JSObject for a global. The global must implement
- nsWrapperCache.
- properties should be a PropertyArrays instance.
- """
- def __init__(self, descriptor, properties):
- assert descriptor.interface.hasInterfacePrototypeObject()
- args = [Argument('JSContext*', 'aCx'),
- Argument(descriptor.nativeType + '*', 'aObject'),
- Argument('nsWrapperCache*', 'aCache'),
- Argument('JS::CompartmentOptions&', 'aOptions'),
- Argument('JSPrincipals*', 'aPrincipal'),
- Argument('bool', 'aInitStandardClasses'),
- Argument('JS::MutableHandle<JSObject*>', 'aReflector')]
- CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'bool', args)
- self.descriptor = descriptor
- self.properties = properties
- def definition_body(self):
- if self.properties.hasNonChromeOnly():
- properties = "sNativeProperties.Upcast()"
- else:
- properties = "nullptr"
- if self.properties.hasChromeOnly():
- chromeProperties = "nsContentUtils::ThreadsafeIsCallerChrome() ? sChromeOnlyNativeProperties.Upcast() : nullptr"
- else:
- chromeProperties = "nullptr"
- failureCode = dedent(
- """
- aCache->ReleaseWrapper(aObject);
- aCache->ClearWrapper();
- return false;
- """);
- if self.descriptor.hasUnforgeableMembers:
- unforgeable = InitUnforgeablePropertiesOnHolder(
- self.descriptor, self.properties, failureCode,
- "aReflector").define();
- else:
- unforgeable = ""
- return fill(
- """
- $*{assertions}
- MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache),
- "nsISupports must be on our primary inheritance chain");
- if (!CreateGlobal<${nativeType}, GetProtoObjectHandle>(aCx,
- aObject,
- aCache,
- sClass.ToJSClass(),
- aOptions,
- aPrincipal,
- aInitStandardClasses,
- aReflector)) {
- $*{failureCode}
- }
- // aReflector is a new global, so has a new compartment. Enter it
- // before doing anything with it.
- JSAutoCompartment ac(aCx, aReflector);
- if (!DefineProperties(aCx, aReflector, ${properties}, ${chromeProperties})) {
- $*{failureCode}
- }
- $*{unforgeable}
- $*{slots}
- return true;
- """,
- assertions=AssertInheritanceChain(self.descriptor),
- nativeType=self.descriptor.nativeType,
- properties=properties,
- chromeProperties=chromeProperties,
- failureCode=failureCode,
- unforgeable=unforgeable,
- slots=InitMemberSlots(self.descriptor, failureCode))
- class CGUpdateMemberSlotsMethod(CGAbstractStaticMethod):
- def __init__(self, descriptor):
- args = [Argument('JSContext*', 'aCx'),
- Argument('JS::Handle<JSObject*>', 'aWrapper'),
- Argument(descriptor.nativeType + '*', 'aObject')]
- CGAbstractStaticMethod.__init__(self, descriptor, 'UpdateMemberSlots', 'bool', args)
- def definition_body(self):
- body = ("JS::Rooted<JS::Value> temp(aCx);\n"
- "JSJitGetterCallArgs args(&temp);\n")
- for m in self.descriptor.interface.members:
- if m.isAttr() and m.getExtendedAttribute("StoreInSlot"):
- # Skip doing this for the "window" and "self" attributes on the
- # Window interface, because those can't be gotten safely until
- # we have hooked it up correctly to the outer window. The
- # window code handles doing the get itself.
- if (self.descriptor.interface.identifier.name == "Window" and
- (m.identifier.name == "window" or m.identifier.name == "self")):
- continue
- body += fill(
- """
- if (!get_${member}(aCx, aWrapper, aObject, args)) {
- return false;
- }
- // Getter handled setting our reserved slots
- """,
- member=m.identifier.name)
- body += "\nreturn true;\n"
- return body
- class CGClearCachedValueMethod(CGAbstractMethod):
- def __init__(self, descriptor, member):
- self.member = member
- # If we're StoreInSlot, we'll need to call the getter
- if member.getExtendedAttribute("StoreInSlot"):
- args = [Argument('JSContext*', 'aCx')]
- returnType = 'bool'
- else:
- args = []
- returnType = 'void'
- args.append(Argument(descriptor.nativeType + '*', 'aObject'))
- name = MakeClearCachedValueNativeName(member)
- CGAbstractMethod.__init__(self, descriptor, name, returnType, args)
- def definition_body(self):
- slotIndex = memberReservedSlot(self.member, self.descriptor)
- if self.member.getExtendedAttribute("StoreInSlot"):
- # We have to root things and save the old value in case
- # regetting fails, so we can restore it.
- declObj = "JS::Rooted<JSObject*> obj(aCx);\n"
- noopRetval = " true"
- saveMember = (
- "JS::Rooted<JS::Value> oldValue(aCx, js::GetReservedSlot(obj, %s));\n" %
- slotIndex)
- regetMember = fill(
- """
- JS::Rooted<JS::Value> temp(aCx);
- JSJitGetterCallArgs args(&temp);
- JSAutoCompartment ac(aCx, obj);
- if (!get_${name}(aCx, obj, aObject, args)) {
- js::SetReservedSlot(obj, ${slotIndex}, oldValue);
- return false;
- }
- return true;
- """,
- name=self.member.identifier.name,
- slotIndex=slotIndex)
- else:
- declObj = "JSObject* obj;\n"
- noopRetval = ""
- saveMember = ""
- regetMember = ""
- if self.descriptor.wantsXrays:
- clearXrayExpandoSlots = fill(
- """
- xpc::ClearXrayExpandoSlots(obj, ${xraySlotIndex});
- """,
- xraySlotIndex=memberXrayExpandoReservedSlot(self.member,
- self.descriptor))
- else :
- clearXrayExpandoSlots = ""
- return fill(
- """
- $*{declObj}
- obj = aObject->GetWrapper();
- if (!obj) {
- return${noopRetval};
- }
- $*{saveMember}
- js::SetReservedSlot(obj, ${slotIndex}, JS::UndefinedValue());
- $*{clearXrayExpandoSlots}
- $*{regetMember}
- """,
- declObj=declObj,
- noopRetval=noopRetval,
- saveMember=saveMember,
- slotIndex=slotIndex,
- clearXrayExpandoSlots=clearXrayExpandoSlots,
- regetMember=regetMember)
- class CGIsPermittedMethod(CGAbstractMethod):
- """
- crossOriginGetters/Setters/Methods are sets of names of the relevant members.
- """
- def __init__(self, descriptor, crossOriginGetters, crossOriginSetters,
- crossOriginMethods):
- self.crossOriginGetters = crossOriginGetters
- self.crossOriginSetters = crossOriginSetters
- self.crossOriginMethods = crossOriginMethods
- args = [Argument("JSFlatString*", "prop"),
- Argument("char16_t", "propFirstChar"),
- Argument("bool", "set")]
- CGAbstractMethod.__init__(self, descriptor, "IsPermitted", "bool", args,
- inline=True)
- def definition_body(self):
- allNames = self.crossOriginGetters | self.crossOriginSetters | self.crossOriginMethods
- readwrite = self.crossOriginGetters & self.crossOriginSetters
- readonly = (self.crossOriginGetters - self.crossOriginSetters) | self.crossOriginMethods
- writeonly = self.crossOriginSetters - self.crossOriginGetters
- cases = {}
- for name in sorted(allNames):
- cond = 'JS_FlatStringEqualsAscii(prop, "%s")' % name
- if name in readonly:
- cond = "!set && %s" % cond
- elif name in writeonly:
- cond = "set && %s" % cond
- else:
- assert name in readwrite
- firstLetter = name[0]
- case = cases.get(firstLetter, CGList([]))
- case.append(CGGeneric("if (%s) {\n"
- " return true;\n"
- "}\n" % cond))
- cases[firstLetter] = case
- caseList = []
- for firstLetter in sorted(cases.keys()):
- caseList.append(CGCase("'%s'" % firstLetter, cases[firstLetter]))
- switch = CGSwitch("propFirstChar", caseList)
- return switch.define() + "\nreturn false;\n"
- class CGCycleCollectionTraverseForOwningUnionMethod(CGAbstractMethod):
- """
- ImplCycleCollectionUnlink for owning union type.
- """
- def __init__(self, type):
- self.type = type
- args = [Argument("nsCycleCollectionTraversalCallback&", "aCallback"),
- Argument("%s&" % CGUnionStruct.unionTypeName(type, True), "aUnion"),
- Argument("const char*", "aName"),
- Argument("uint32_t", "aFlags", "0")]
- CGAbstractMethod.__init__(self, None, "ImplCycleCollectionTraverse", "void", args)
- def deps(self):
- return self.type.getDeps()
- def definition_body(self):
- memberNames = [getUnionMemberName(t)
- for t in self.type.flatMemberTypes
- if idlTypeNeedsCycleCollection(t)]
- assert memberNames
- conditionTemplate = 'aUnion.Is%s()'
- functionCallTemplate = 'ImplCycleCollectionTraverse(aCallback, aUnion.GetAs%s(), "m%s", aFlags);\n'
- ifStaments = (CGIfWrapper(CGGeneric(functionCallTemplate % (m, m)),
- conditionTemplate % m)
- for m in memberNames)
- return CGElseChain(ifStaments).define()
- class CGCycleCollectionUnlinkForOwningUnionMethod(CGAbstractMethod):
- """
- ImplCycleCollectionUnlink for owning union type.
- """
- def __init__(self, type):
- self.type = type
- args = [Argument("%s&" % CGUnionStruct.unionTypeName(type, True), "aUnion")]
- CGAbstractMethod.__init__(self, None, "ImplCycleCollectionUnlink", "void", args)
- def deps(self):
- return self.type.getDeps()
- def definition_body(self):
- return "aUnion.Uninit();\n"
- builtinNames = {
- IDLType.Tags.bool: 'bool',
- IDLType.Tags.int8: 'int8_t',
- IDLType.Tags.int16: 'int16_t',
- IDLType.Tags.int32: 'int32_t',
- IDLType.Tags.int64: 'int64_t',
- IDLType.Tags.uint8: 'uint8_t',
- IDLType.Tags.uint16: 'uint16_t',
- IDLType.Tags.uint32: 'uint32_t',
- IDLType.Tags.uint64: 'uint64_t',
- IDLType.Tags.unrestricted_float: 'float',
- IDLType.Tags.float: 'float',
- IDLType.Tags.unrestricted_double: 'double',
- IDLType.Tags.double: 'double'
- }
- numericSuffixes = {
- IDLType.Tags.int8: '',
- IDLType.Tags.uint8: '',
- IDLType.Tags.int16: '',
- IDLType.Tags.uint16: '',
- IDLType.Tags.int32: '',
- IDLType.Tags.uint32: 'U',
- IDLType.Tags.int64: 'LL',
- IDLType.Tags.uint64: 'ULL',
- IDLType.Tags.unrestricted_float: 'F',
- IDLType.Tags.float: 'F',
- IDLType.Tags.unrestricted_double: '',
- IDLType.Tags.double: ''
- }
- def numericValue(t, v):
- if (t == IDLType.Tags.unrestricted_double or
- t == IDLType.Tags.unrestricted_float):
- typeName = builtinNames[t]
- if v == float("inf"):
- return "mozilla::PositiveInfinity<%s>()" % typeName
- if v == float("-inf"):
- return "mozilla::NegativeInfinity<%s>()" % typeName
- if math.isnan(v):
- return "mozilla::UnspecifiedNaN<%s>()" % typeName
- return "%s%s" % (v, numericSuffixes[t])
- class CastableObjectUnwrapper():
- """
- A class for unwrapping an object stored in a JS Value (or
- MutableHandle<Value> or Handle<Value>) named by the "source" and
- "mutableSource" arguments based on the passed-in descriptor and storing it
- in a variable called by the name in the "target" argument. The "source"
- argument should be able to produce a Value or Handle<Value>; the
- "mutableSource" argument should be able to produce a MutableHandle<Value>
- codeOnFailure is the code to run if unwrapping fails.
- If isCallbackReturnValue is "JSImpl" and our descriptor is also
- JS-implemented, fall back to just creating the right object if what we
- have isn't one already.
- If allowCrossOriginObj is True, then we'll first do an
- UncheckedUnwrap and then operate on the result.
- """
- def __init__(self, descriptor, source, mutableSource, target, codeOnFailure,
- exceptionCode=None, isCallbackReturnValue=False,
- allowCrossOriginObj=False):
- self.substitution = {
- "type": descriptor.nativeType,
- "protoID": "prototypes::id::" + descriptor.name,
- "target": target,
- "codeOnFailure": codeOnFailure,
- }
- # Supporting both the "cross origin object" case and the "has
- # XPConnect impls" case at the same time is a pain, so let's
- # not do that. That allows us to assume that our source is
- # always a Handle or MutableHandle.
- if allowCrossOriginObj and descriptor.hasXPConnectImpls:
- raise TypeError("Interface %s both allows a cross-origin 'this' "
- "and has XPConnect impls. We don't support that" %
- descriptor.name)
- if allowCrossOriginObj:
- self.substitution["uncheckedObjDecl"] = fill(
- """
- JS::Rooted<JSObject*> maybeUncheckedObj(cx, &${source}.toObject());
- """,
- source=source)
- self.substitution["uncheckedObjGet"] = fill(
- """
- if (xpc::WrapperFactory::IsXrayWrapper(maybeUncheckedObj)) {
- maybeUncheckedObj = js::UncheckedUnwrap(maybeUncheckedObj);
- } else {
- maybeUncheckedObj = js::CheckedUnwrap(maybeUncheckedObj);
- if (!maybeUncheckedObj) {
- $*{codeOnFailure}
- }
- }
- """,
- codeOnFailure=(codeOnFailure % { 'securityError': 'true'}))
- self.substitution["source"] = "maybeUncheckedObj"
- self.substitution["mutableSource"] = "&maybeUncheckedObj"
- # No need to set up xpconnectUnwrap, since it won't be
- # used in the allowCrossOriginObj case.
- else:
- self.substitution["uncheckedObjDecl"] = ""
- self.substitution["uncheckedObjGet"] = ""
- self.substitution["source"] = source
- self.substitution["mutableSource"] = mutableSource
- xpconnectUnwrap = (
- "nsresult rv = UnwrapXPConnect<${type}>(cx, ${mutableSource}, getter_AddRefs(objPtr));\n")
- if descriptor.hasXPConnectImpls:
- self.substitution["codeOnFailure"] = string.Template(
- "RefPtr<${type}> objPtr;\n" +
- xpconnectUnwrap +
- "if (NS_FAILED(rv)) {\n"
- "${indentedCodeOnFailure}"
- "}\n"
- "// We should have an object\n"
- "MOZ_ASSERT(objPtr);\n"
- "${target} = objPtr;\n"
- ).substitute(self.substitution,
- indentedCodeOnFailure=indent(codeOnFailure))
- elif (isCallbackReturnValue == "JSImpl" and
- descriptor.interface.isJSImplemented()):
- exceptionCode = exceptionCode or codeOnFailure
- self.substitution["codeOnFailure"] = fill(
- """
- // Be careful to not wrap random DOM objects here, even if
- // they're wrapped in opaque security wrappers for some reason.
- // XXXbz Wish we could check for a JS-implemented object
- // that already has a content reflection...
- if (!IsDOMObject(js::UncheckedUnwrap(&${source}.toObject()))) {
- nsCOMPtr<nsIGlobalObject> contentGlobal;
- if (!GetContentGlobalForJSImplementedObject(cx, Callback(), getter_AddRefs(contentGlobal))) {
- $*{exceptionCode}
- }
- JS::Rooted<JSObject*> jsImplSourceObj(cx, &${source}.toObject());
- ${target} = new ${type}(jsImplSourceObj, contentGlobal);
- } else {
- $*{codeOnFailure}
- }
- """,
- exceptionCode=exceptionCode,
- **self.substitution)
- else:
- self.substitution["codeOnFailure"] = codeOnFailure
- def __str__(self):
- substitution = self.substitution.copy()
- substitution["codeOnFailure"] %= {
- 'securityError': 'rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO'
- }
- return fill(
- """
- $*{uncheckedObjDecl}
- {
- $*{uncheckedObjGet}
- nsresult rv = UnwrapObject<${protoID}, ${type}>(${mutableSource}, ${target});
- if (NS_FAILED(rv)) {
- $*{codeOnFailure}
- }
- }
- """,
- **substitution)
- class FailureFatalCastableObjectUnwrapper(CastableObjectUnwrapper):
- """
- As CastableObjectUnwrapper, but defaulting to throwing if unwrapping fails
- """
- def __init__(self, descriptor, source, mutableSource, target, exceptionCode,
- isCallbackReturnValue, sourceDescription):
- CastableObjectUnwrapper.__init__(
- self, descriptor, source, mutableSource, target,
- 'ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s", "%s");\n'
- '%s' % (sourceDescription, descriptor.interface.identifier.name,
- exceptionCode),
- exceptionCode,
- isCallbackReturnValue)
- class CGCallbackTempRoot(CGGeneric):
- def __init__(self, name):
- define = dedent("""
- { // Scope for tempRoot
- JS::Rooted<JSObject*> tempRoot(cx, &${val}.toObject());
- ${declName} = new %s(cx, tempRoot, mozilla::dom::GetIncumbentGlobal());
- }
- """) % name
- CGGeneric.__init__(self, define=define)
- def getCallbackConversionInfo(type, idlObject, isMember, isCallbackReturnValue,
- isOptional):
- """
- Returns a tuple containing the declType, declArgs, and basic
- conversion for the given callback type, with the given callback
- idl object in the given context (isMember/isCallbackReturnValue/isOptional).
- """
- name = idlObject.identifier.name
- # We can't use fast callbacks if isOptional because then we get an
- # Optional<RootedCallback> thing, which is not transparent to consumers.
- useFastCallback = (not isMember and not isCallbackReturnValue and
- not isOptional)
- if useFastCallback:
- name = "binding_detail::Fast%s" % name
- if type.nullable() or isCallbackReturnValue:
- declType = CGGeneric("RefPtr<%s>" % name)
- else:
- declType = CGGeneric("OwningNonNull<%s>" % name)
- if useFastCallback:
- declType = CGTemplatedType("RootedCallback", declType)
- declArgs = "cx"
- else:
- declArgs = None
- conversion = indent(CGCallbackTempRoot(name).define())
- return (declType, declArgs, conversion)
- class JSToNativeConversionInfo():
- """
- An object representing information about a JS-to-native conversion.
- """
- def __init__(self, template, declType=None, holderType=None,
- dealWithOptional=False, declArgs=None,
- holderArgs=None):
- """
- template: A string representing the conversion code. This will have
- template substitution performed on it as follows:
- ${val} is a handle to the JS::Value in question
- ${maybeMutableVal} May be a mutable handle to the JS::Value in
- question. This is only OK to use if ${val} is
- known to not be undefined.
- ${holderName} replaced by the holder's name, if any
- ${declName} replaced by the declaration's name
- ${haveValue} replaced by an expression that evaluates to a boolean
- for whether we have a JS::Value. Only used when
- defaultValue is not None or when True is passed for
- checkForValue to instantiateJSToNativeConversion.
- This expression may not be already-parenthesized, so if
- you use it with && or || make sure to put parens
- around it.
- ${passedToJSImpl} replaced by an expression that evaluates to a boolean
- for whether this value is being passed to a JS-
- implemented interface.
- declType: A CGThing representing the native C++ type we're converting
- to. This is allowed to be None if the conversion code is
- supposed to be used as-is.
- holderType: A CGThing representing the type of a "holder" which will
- hold a possible reference to the C++ thing whose type we
- returned in declType, or None if no such holder is needed.
- dealWithOptional: A boolean indicating whether the caller has to do
- optional-argument handling. This should only be set
- to true if the JS-to-native conversion is being done
- for an optional argument or dictionary member with no
- default value and if the returned template expects
- both declType and holderType to be wrapped in
- Optional<>, with ${declName} and ${holderName}
- adjusted to point to the Value() of the Optional, and
- Construct() calls to be made on the Optional<>s as
- needed.
- declArgs: If not None, the arguments to pass to the ${declName}
- constructor. These will have template substitution performed
- on them so you can use things like ${val}. This is a
- single string, not a list of strings.
- holderArgs: If not None, the arguments to pass to the ${holderName}
- constructor. These will have template substitution
- performed on them so you can use things like ${val}.
- This is a single string, not a list of strings.
- ${declName} must be in scope before the code from 'template' is entered.
- If holderType is not None then ${holderName} must be in scope before
- the code from 'template' is entered.
- """
- assert isinstance(template, str)
- assert declType is None or isinstance(declType, CGThing)
- assert holderType is None or isinstance(holderType, CGThing)
- self.template = template
- self.declType = declType
- self.holderType = holderType
- self.dealWithOptional = dealWithOptional
- self.declArgs = declArgs
- self.holderArgs = holderArgs
- def getHandleDefault(defaultValue):
- tag = defaultValue.type.tag()
- if tag in numericSuffixes:
- # Some numeric literals require a suffix to compile without warnings
- return numericValue(tag, defaultValue.value)
- assert tag == IDLType.Tags.bool
- return toStringBool(defaultValue.value)
- def handleDefaultStringValue(defaultValue, method):
- """
- Returns a string which ends up calling 'method' with a (char_t*, length)
- pair that sets this string default value. This string is suitable for
- passing as the second argument of handleDefault; in particular it does not
- end with a ';'
- """
- assert (defaultValue.type.isDOMString() or
- defaultValue.type.isUSVString() or
- defaultValue.type.isByteString())
- return ("static const %(char_t)s data[] = { %(data)s };\n"
- "%(method)s(data, ArrayLength(data) - 1)") % {
- 'char_t': "char" if defaultValue.type.isByteString() else "char16_t",
- 'method': method,
- 'data': ", ".join(["'" + char + "'" for char in
- defaultValue.value] + ["0"])
- }
- def recordKeyType(recordType):
- assert recordType.keyType.isString()
- if recordType.keyType.isByteString():
- return "nsCString"
- return "nsString"
- def recordKeyDeclType(recordType):
- return CGGeneric(recordKeyType(recordType))
- # If this function is modified, modify CGNativeMember.getArg and
- # CGNativeMember.getRetvalInfo accordingly. The latter cares about the decltype
- # and holdertype we end up using, because it needs to be able to return the code
- # that will convert those to the actual return value of the callback function.
- def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
- isDefinitelyObject=False,
- isMember=False,
- isOptional=False,
- invalidEnumValueFatal=True,
- defaultValue=None,
- treatNullAs="Default",
- isEnforceRange=False,
- isClamp=False,
- isNullOrUndefined=False,
- exceptionCode=None,
- lenientFloatCode=None,
- allowTreatNonCallableAsNull=False,
- isCallbackReturnValue=False,
- sourceDescription="value",
- nestingLevel=""):
- """
- Get a template for converting a JS value to a native object based on the
- given type and descriptor. If failureCode is given, then we're actually
- testing whether we can convert the argument to the desired type. That
- means that failures to convert due to the JS value being the wrong type of
- value need to use failureCode instead of throwing exceptions. Failures to
- convert that are due to JS exceptions (from toString or valueOf methods) or
- out of memory conditions need to throw exceptions no matter what
- failureCode is. However what actually happens when throwing an exception
- can be controlled by exceptionCode. The only requirement on that is that
- exceptionCode must end up doing a return, and every return from this
- function must happen via exceptionCode if exceptionCode is not None.
- If isDefinitelyObject is True, that means we know the value
- isObject() and we have no need to recheck that.
- if isMember is not False, we're being converted from a property of some JS
- object, not from an actual method argument, so we can't rely on our jsval
- being rooted or outliving us in any way. Callers can pass "Dictionary",
- "Variadic", "Sequence", or "OwningUnion" to indicate that the conversion is
- for something that is a dictionary member, a variadic argument, a sequence,
- or an owning union respectively.
- If isOptional is true, then we are doing conversion of an optional
- argument with no default value.
- invalidEnumValueFatal controls whether an invalid enum value conversion
- attempt will throw (if true) or simply return without doing anything (if
- false).
- If defaultValue is not None, it's the IDL default value for this conversion
- If isEnforceRange is true, we're converting an integer and throwing if the
- value is out of range.
- If isClamp is true, we're converting an integer and clamping if the
- value is out of range.
- If lenientFloatCode is not None, it should be used in cases when
- we're a non-finite float that's not unrestricted.
- If allowTreatNonCallableAsNull is true, then [TreatNonCallableAsNull] and
- [TreatNonObjectAsNull] extended attributes on nullable callback functions
- will be honored.
- If isCallbackReturnValue is "JSImpl" or "Callback", then the declType may be
- adjusted to make it easier to return from a callback. Since that type is
- never directly observable by any consumers of the callback code, this is OK.
- Furthermore, if isCallbackReturnValue is "JSImpl", that affects the behavior
- of the FailureFatalCastableObjectUnwrapper conversion; this is used for
- implementing auto-wrapping of JS-implemented return values from a
- JS-implemented interface.
- sourceDescription is a description of what this JS value represents, to be
- used in error reporting. Callers should assume that it might get placed in
- the middle of a sentence. If it ends up at the beginning of a sentence, its
- first character will be automatically uppercased.
- The return value from this function is a JSToNativeConversionInfo.
- """
- # If we have a defaultValue then we're not actually optional for
- # purposes of what we need to be declared as.
- assert defaultValue is None or not isOptional
- # Also, we should not have a defaultValue if we know we're an object
- assert not isDefinitelyObject or defaultValue is None
- # And we can't both be an object and be null or undefined
- assert not isDefinitelyObject or not isNullOrUndefined
- # If exceptionCode is not set, we'll just rethrow the exception we got.
- # Note that we can't just set failureCode to exceptionCode, because setting
- # failureCode will prevent pending exceptions from being set in cases when
- # they really should be!
- if exceptionCode is None:
- exceptionCode = "return false;\n"
- # Unfortunately, .capitalize() on a string will lowercase things inside the
- # string, which we do not want.
- def firstCap(string):
- return string[0].upper() + string[1:]
- # Helper functions for dealing with failures due to the JS value being the
- # wrong type of value
- def onFailureNotAnObject(failureCode):
- return CGGeneric(
- failureCode or
- ('ThrowErrorMessage(cx, MSG_NOT_OBJECT, "%s");\n'
- '%s' % (firstCap(sourceDescription), exceptionCode)))
- def onFailureBadType(failureCode, typeName):
- return CGGeneric(
- failureCode or
- ('ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s", "%s");\n'
- '%s' % (firstCap(sourceDescription), typeName, exceptionCode)))
- def onFailureNotCallable(failureCode):
- return CGGeneric(
- failureCode or
- ('ThrowErrorMessage(cx, MSG_NOT_CALLABLE, "%s");\n'
- '%s' % (firstCap(sourceDescription), exceptionCode)))
- # A helper function for handling default values. Takes a template
- # body and the C++ code to set the default value and wraps the
- # given template body in handling for the default value.
- def handleDefault(template, setDefault):
- if defaultValue is None:
- return template
- return (
- "if (${haveValue}) {\n" +
- indent(template) +
- "} else {\n" +
- indent(setDefault) +
- "}\n")
- # A helper function for wrapping up the template body for
- # possibly-nullable objecty stuff
- def wrapObjectTemplate(templateBody, type, codeToSetNull, failureCode=None):
- if isNullOrUndefined and type.nullable():
- # Just ignore templateBody and set ourselves to null.
- # Note that we don't have to worry about default values
- # here either, since we already examined this value.
- return codeToSetNull
- if not isDefinitelyObject:
- # Handle the non-object cases by wrapping up the whole
- # thing in an if cascade.
- if type.nullable():
- elifLine = "} else if (${val}.isNullOrUndefined()) {\n"
- elifBody = codeToSetNull
- else:
- elifLine = ""
- elifBody = ""
- # Note that $${val} below expands to ${val}. This string is
- # used as a template later, and val will be filled in then.
- templateBody = fill(
- """
- if ($${val}.isObject()) {
- $*{templateBody}
- $*{elifLine}
- $*{elifBody}
- } else {
- $*{failureBody}
- }
- """,
- templateBody=templateBody,
- elifLine=elifLine,
- elifBody=elifBody,
- failureBody=onFailureNotAnObject(failureCode).define())
- if isinstance(defaultValue, IDLNullValue):
- assert type.nullable() # Parser should enforce this
- templateBody = handleDefault(templateBody, codeToSetNull)
- elif isinstance(defaultValue, IDLEmptySequenceValue):
- # Our caller will handle it
- pass
- else:
- assert defaultValue is None
- return templateBody
- # A helper function for converting things that look like a JSObject*.
- def handleJSObjectType(type, isMember, failureCode, exceptionCode, sourceDescription):
- if not isMember:
- if isOptional:
- # We have a specialization of Optional that will use a
- # Rooted for the storage here.
- declType = CGGeneric("JS::Handle<JSObject*>")
- else:
- declType = CGGeneric("JS::Rooted<JSObject*>")
- declArgs = "cx"
- else:
- assert (isMember in
- ("Sequence", "Variadic", "Dictionary", "OwningUnion", "Record"))
- # We'll get traced by the sequence or dictionary or union tracer
- declType = CGGeneric("JSObject*")
- declArgs = None
- templateBody = "${declName} = &${val}.toObject();\n"
- # For JS-implemented APIs, we refuse to allow passing objects that the
- # API consumer does not subsume. The extra parens around
- # ($${passedToJSImpl}) suppress unreachable code warnings when
- # $${passedToJSImpl} is the literal `false`.
- if not isinstance(descriptorProvider, Descriptor) or descriptorProvider.interface.isJSImplemented():
- templateBody = fill(
- """
- if (($${passedToJSImpl}) && !CallerSubsumes($${val})) {
- ThrowErrorMessage(cx, MSG_PERMISSION_DENIED_TO_PASS_ARG, "${sourceDescription}");
- $*{exceptionCode}
- }
- """,
- sourceDescription=sourceDescription,
- exceptionCode=exceptionCode) + templateBody
- setToNullCode = "${declName} = nullptr;\n"
- template = wrapObjectTemplate(templateBody, type, setToNullCode,
- failureCode)
- return JSToNativeConversionInfo(template, declType=declType,
- dealWithOptional=isOptional,
- declArgs=declArgs)
- def incrementNestingLevel():
- if nestingLevel is "":
- return 1
- return nestingLevel + 1
- assert not (isEnforceRange and isClamp) # These are mutually exclusive
- if type.isSequence():
- assert not isEnforceRange and not isClamp
- if failureCode is None:
- notSequence = ('ThrowErrorMessage(cx, MSG_NOT_SEQUENCE, "%s");\n'
- "%s" % (firstCap(sourceDescription), exceptionCode))
- else:
- notSequence = failureCode
- nullable = type.nullable()
- # Be very careful not to change "type": we need it later
- if nullable:
- elementType = type.inner.inner
- else:
- elementType = type.inner
- # We want to use auto arrays if we can, but we have to be careful with
- # reallocation behavior for arrays. In particular, if we use auto
- # arrays for sequences and have a sequence of elements which are
- # themselves sequences or have sequences as members, we have a problem.
- # In that case, resizing the outermost AutoTArray to the right size
- # will memmove its elements, but AutoTArrays are not memmovable and
- # hence will end up with pointers to bogus memory, which is bad. To
- # deal with this, we typically map WebIDL sequences to our Sequence
- # type, which is in fact memmovable. The one exception is when we're
- # passing in a sequence directly as an argument without any sort of
- # optional or nullable complexity going on. In that situation, we can
- # use an AutoSequence instead. We have to keep using Sequence in the
- # nullable and optional cases because we don't want to leak the
- # AutoSequence type to consumers, which would be unavoidable with
- # Nullable<AutoSequence> or Optional<AutoSequence>.
- if isMember or isOptional or nullable or isCallbackReturnValue:
- sequenceClass = "Sequence"
- else:
- sequenceClass = "binding_detail::AutoSequence"
- # XXXbz we can't include the index in the sourceDescription, because
- # we don't really have a way to pass one in dynamically at runtime...
- elementInfo = getJSToNativeConversionInfo(
- elementType, descriptorProvider, isMember="Sequence",
- exceptionCode=exceptionCode, lenientFloatCode=lenientFloatCode,
- isCallbackReturnValue=isCallbackReturnValue,
- sourceDescription="element of %s" % sourceDescription,
- nestingLevel=incrementNestingLevel())
- if elementInfo.dealWithOptional:
- raise TypeError("Shouldn't have optional things in sequences")
- if elementInfo.holderType is not None:
- raise TypeError("Shouldn't need holders for sequences")
- typeName = CGTemplatedType(sequenceClass, elementInfo.declType)
- sequenceType = typeName.define()
- if nullable:
- typeName = CGTemplatedType("Nullable", typeName)
- arrayRef = "${declName}.SetValue()"
- else:
- arrayRef = "${declName}"
- elementConversion = string.Template(elementInfo.template).substitute({
- "val": "temp" + str(nestingLevel),
- "maybeMutableVal": "&temp" + str(nestingLevel),
- "declName": "slot" + str(nestingLevel),
- # We only need holderName here to handle isExternal()
- # interfaces, which use an internal holder for the
- # conversion even when forceOwningType ends up true.
- "holderName": "tempHolder" + str(nestingLevel),
- "passedToJSImpl": "${passedToJSImpl}"
- })
- # NOTE: Keep this in sync with variadic conversions as needed
- templateBody = fill(
- """
- JS::ForOfIterator iter${nestingLevel}(cx);
- if (!iter${nestingLevel}.init($${val}, JS::ForOfIterator::AllowNonIterable)) {
- $*{exceptionCode}
- }
- if (!iter${nestingLevel}.valueIsIterable()) {
- $*{notSequence}
- }
- ${sequenceType} &arr${nestingLevel} = ${arrayRef};
- JS::Rooted<JS::Value> temp${nestingLevel}(cx);
- while (true) {
- bool done${nestingLevel};
- if (!iter${nestingLevel}.next(&temp${nestingLevel}, &done${nestingLevel})) {
- $*{exceptionCode}
- }
- if (done${nestingLevel}) {
- break;
- }
- ${elementType}* slotPtr${nestingLevel} = arr${nestingLevel}.AppendElement(mozilla::fallible);
- if (!slotPtr${nestingLevel}) {
- JS_ReportOutOfMemory(cx);
- $*{exceptionCode}
- }
- ${elementType}& slot${nestingLevel} = *slotPtr${nestingLevel};
- $*{elementConversion}
- }
- """,
- exceptionCode=exceptionCode,
- notSequence=notSequence,
- sequenceType=sequenceType,
- arrayRef=arrayRef,
- elementType=elementInfo.declType.define(),
- elementConversion=elementConversion,
- nestingLevel=str(nestingLevel))
- templateBody = wrapObjectTemplate(templateBody, type,
- "${declName}.SetNull();\n", notSequence)
- if isinstance(defaultValue, IDLEmptySequenceValue):
- if type.nullable():
- codeToSetEmpty = "${declName}.SetValue();\n"
- else:
- codeToSetEmpty = "/* Array is already empty; nothing to do */\n"
- templateBody = handleDefault(templateBody, codeToSetEmpty)
- # Sequence arguments that might contain traceable things need
- # to get traced
- if not isMember and typeNeedsRooting(elementType):
- holderType = CGTemplatedType("SequenceRooter", elementInfo.declType)
- # If our sequence is nullable, this will set the Nullable to be
- # not-null, but that's ok because we make an explicit SetNull() call
- # on it as needed if our JS value is actually null.
- holderArgs = "cx, &%s" % arrayRef
- else:
- holderType = None
- holderArgs = None
- return JSToNativeConversionInfo(templateBody, declType=typeName,
- holderType=holderType,
- dealWithOptional=isOptional,
- holderArgs=holderArgs)
- if type.isRecord():
- assert not isEnforceRange and not isClamp
- if failureCode is None:
- notRecord = ('ThrowErrorMessage(cx, MSG_NOT_OBJECT, "%s");\n'
- "%s" % (firstCap(sourceDescription), exceptionCode))
- else:
- notRecord = failureCode
- nullable = type.nullable()
- # Be very careful not to change "type": we need it later
- if nullable:
- recordType = type.inner
- else:
- recordType = type
- valueType = recordType.inner
- valueInfo = getJSToNativeConversionInfo(
- valueType, descriptorProvider, isMember="Record",
- exceptionCode=exceptionCode, lenientFloatCode=lenientFloatCode,
- isCallbackReturnValue=isCallbackReturnValue,
- sourceDescription="value in %s" % sourceDescription,
- nestingLevel=incrementNestingLevel())
- if valueInfo.dealWithOptional:
- raise TypeError("Shouldn't have optional things in record")
- if valueInfo.holderType is not None:
- raise TypeError("Shouldn't need holders for record")
- declType = CGTemplatedType("Record", [recordKeyDeclType(recordType),
- valueInfo.declType])
- typeName = declType.define()
- if nullable:
- declType = CGTemplatedType("Nullable", declType)
- recordRef = "${declName}.SetValue()"
- else:
- recordRef = "${declName}"
- valueConversion = string.Template(valueInfo.template).substitute({
- "val": "temp",
- "maybeMutableVal": "&temp",
- "declName": "slot",
- # We only need holderName here to handle isExternal()
- # interfaces, which use an internal holder for the
- # conversion even when forceOwningType ends up true.
- "holderName": "tempHolder",
- "passedToJSImpl": "${passedToJSImpl}"
- })
- keyType = recordKeyType(recordType)
- if recordType.keyType.isByteString():
- keyConversionFunction = "ConvertJSValueToByteString"
- hashKeyType = "nsCStringHashKey"
- else:
- hashKeyType = "nsStringHashKey"
- if recordType.keyType.isDOMString():
- keyConversionFunction = "ConvertJSValueToString"
- else:
- assert recordType.keyType.isUSVString()
- keyConversionFunction = "ConvertJSValueToUSVString"
- templateBody = fill(
- """
- auto& recordEntries = ${recordRef}.Entries();
- JS::Rooted<JSObject*> recordObj(cx, &$${val}.toObject());
- JS::AutoIdVector ids(cx);
- if (!js::GetPropertyKeys(cx, recordObj,
- JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &ids)) {
- $*{exceptionCode}
- }
- if (!recordEntries.SetCapacity(ids.length(), mozilla::fallible)) {
- JS_ReportOutOfMemory(cx);
- $*{exceptionCode}
- }
- JS::Rooted<JS::Value> propNameValue(cx);
- JS::Rooted<JS::Value> temp(cx);
- JS::Rooted<jsid> curId(cx);
- JS::Rooted<JS::Value> idVal(cx);
- // Use a hashset to keep track of ids seen, to avoid
- // introducing nasty O(N^2) behavior scanning for them all the
- // time. Ideally we'd use a data structure with O(1) lookup
- // _and_ ordering for the MozMap, but we don't have one lying
- // around.
- nsTHashtable<${hashKeyType}> idsSeen;
- for (size_t i = 0; i < ids.length(); ++i) {
- curId = ids[i];
- JS::Rooted<JS::PropertyDescriptor> desc(cx);
- if (!JS_GetOwnPropertyDescriptorById(cx, recordObj, curId,
- &desc)) {
- $*{exceptionCode}
- }
- if (!desc.object() /* == undefined in spec terms */ ||
- !desc.enumerable()) {
- continue;
- }
- idVal = js::IdToValue(curId);
- ${keyType} propName;
- // This will just throw if idVal is a Symbol, like the spec says
- // to do.
- if (!${keyConversionFunction}(cx, idVal, propName)) {
- $*{exceptionCode}
- }
- if (!JS_GetPropertyById(cx, recordObj, curId, &temp)) {
- $*{exceptionCode}
- }
- ${typeName}::EntryType* entry;
- if (idsSeen.Contains(propName)) {
- // Find the existing entry.
- auto idx = recordEntries.IndexOf(propName);
- MOZ_ASSERT(idx != recordEntries.NoIndex,
- "Why is it not found?");
- // Now blow it away to make it look like it was just added
- // to the array, because it's not obvious that it's
- // safe to write to its already-initialized mValue via our
- // normal codegen conversions. For example, the value
- // could be a union and this would change its type, but
- // codegen assumes we won't do that.
- entry = recordEntries.ReconstructElementAt(idx);
- } else {
- // Safe to do an infallible append here, because we did a
- // SetCapacity above to the right capacity.
- entry = recordEntries.AppendElement();
- idsSeen.PutEntry(propName);
- }
- entry->mKey = propName;
- ${valueType}& slot = entry->mValue;
- $*{valueConversion}
- }
- """,
- exceptionCode=exceptionCode,
- recordRef=recordRef,
- hashKeyType=hashKeyType,
- keyType=keyType,
- keyConversionFunction=keyConversionFunction,
- typeName=typeName,
- valueType=valueInfo.declType.define(),
- valueConversion=valueConversion)
- templateBody = wrapObjectTemplate(templateBody, type,
- "${declName}.SetNull();\n",
- notRecord)
- declArgs = None
- holderType = None
- holderArgs = None
- # record arguments that might contain traceable things need
- # to get traced
- if not isMember and isCallbackReturnValue:
- # Go ahead and just convert directly into our actual return value
- declType = CGWrapper(declType, post="&")
- declArgs = "aRetVal"
- elif not isMember and typeNeedsRooting(valueType):
- holderType = CGTemplatedType("RecordRooter",
- [recordKeyDeclType(recordType),
- valueInfo.declType])
- # If our record is nullable, this will set the Nullable to be
- # not-null, but that's ok because we make an explicit SetNull() call
- # on it as needed if our JS value is actually null.
- holderArgs = "cx, &%s" % recordRef
- return JSToNativeConversionInfo(templateBody, declType=declType,
- declArgs=declArgs,
- holderType=holderType,
- dealWithOptional=isOptional,
- holderArgs=holderArgs)
- if type.isUnion():
- nullable = type.nullable()
- if nullable:
- type = type.inner
- isOwningUnion = isMember or isCallbackReturnValue
- unionArgumentObj = "${declName}" if isOwningUnion else "${holderName}"
- if nullable:
- # If we're owning, we're a Nullable, which hasn't been told it has
- # a value. Otherwise we're an already-constructed Maybe.
- unionArgumentObj += ".SetValue()" if isOwningUnion else ".ref()"
- memberTypes = type.flatMemberTypes
- names = []
- interfaceMemberTypes = filter(lambda t: t.isNonCallbackInterface(), memberTypes)
- if len(interfaceMemberTypes) > 0:
- interfaceObject = []
- for memberType in interfaceMemberTypes:
- name = getUnionMemberName(memberType)
- interfaceObject.append(
- CGGeneric("(failed = !%s.TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext" %
- (unionArgumentObj, name)))
- names.append(name)
- interfaceObject = CGWrapper(CGList(interfaceObject, " ||\n"),
- pre="done = ", post=";\n\n", reindent=True)
- else:
- interfaceObject = None
- sequenceObjectMemberTypes = filter(lambda t: t.isSequence(), memberTypes)
- if len(sequenceObjectMemberTypes) > 0:
- assert len(sequenceObjectMemberTypes) == 1
- name = getUnionMemberName(sequenceObjectMemberTypes[0])
- sequenceObject = CGGeneric(
- "done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n" %
- (unionArgumentObj, name))
- names.append(name)
- else:
- sequenceObject = None
- dateObjectMemberTypes = filter(lambda t: t.isDate(), memberTypes)
- if len(dateObjectMemberTypes) > 0:
- assert len(dateObjectMemberTypes) == 1
- memberType = dateObjectMemberTypes[0]
- name = getUnionMemberName(memberType)
- dateObject = CGGeneric("%s.SetTo%s(cx, ${val});\n"
- "done = true;\n" % (unionArgumentObj, name))
- dateObject = CGIfWrapper(dateObject, "JS_ObjectIsDate(cx, argObj)")
- names.append(name)
- else:
- dateObject = None
- callbackMemberTypes = filter(lambda t: t.isCallback() or t.isCallbackInterface(), memberTypes)
- if len(callbackMemberTypes) > 0:
- assert len(callbackMemberTypes) == 1
- memberType = callbackMemberTypes[0]
- name = getUnionMemberName(memberType)
- callbackObject = CGGeneric(
- "done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n" %
- (unionArgumentObj, name))
- names.append(name)
- else:
- callbackObject = None
- dictionaryMemberTypes = filter(lambda t: t.isDictionary(), memberTypes)
- if len(dictionaryMemberTypes) > 0:
- assert len(dictionaryMemberTypes) == 1
- name = getUnionMemberName(dictionaryMemberTypes[0])
- setDictionary = CGGeneric(
- "done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n" %
- (unionArgumentObj, name))
- names.append(name)
- else:
- setDictionary = None
- recordMemberTypes = filter(lambda t: t.isRecord(), memberTypes)
- if len(recordMemberTypes) > 0:
- assert len(recordMemberTypes) == 1
- name = getUnionMemberName(recordMemberTypes[0])
- recordObject = CGGeneric(
- "done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n" %
- (unionArgumentObj, name))
- names.append(name)
- else:
- recordObject = None
- objectMemberTypes = filter(lambda t: t.isObject(), memberTypes)
- if len(objectMemberTypes) > 0:
- assert len(objectMemberTypes) == 1
- # Very important to NOT construct a temporary Rooted here, since the
- # SetToObject call can call a Rooted constructor and we need to keep
- # stack discipline for Rooted.
- object = CGGeneric("if (!%s.SetToObject(cx, &${val}.toObject(), ${passedToJSImpl})) {\n"
- "%s"
- "}\n"
- "done = true;\n" % (unionArgumentObj, indent(exceptionCode)))
- names.append(objectMemberTypes[0].name)
- else:
- object = None
- hasObjectTypes = interfaceObject or sequenceObject or dateObject or callbackObject or object or recordObject
- if hasObjectTypes:
- # "object" is not distinguishable from other types
- assert not object or not (interfaceObject or sequenceObject or dateObject or callbackObject or recordObject)
- if sequenceObject or dateObject or callbackObject:
- # An object can be both an sequence object and a callback or
- # dictionary, but we shouldn't have both in the union's members
- # because they are not distinguishable.
- assert not (sequenceObject and callbackObject)
- templateBody = CGElseChain([sequenceObject, dateObject, callbackObject])
- else:
- templateBody = None
- if interfaceObject:
- assert not object
- if templateBody:
- templateBody = CGIfWrapper(templateBody, "!done")
- templateBody = CGList([interfaceObject, templateBody])
- else:
- templateBody = CGList([templateBody, object])
- if dateObject:
- templateBody.prepend(CGGeneric("JS::Rooted<JSObject*> argObj(cx, &${val}.toObject());\n"))
- if recordObject:
- templateBody = CGList([templateBody,
- CGIfWrapper(recordObject, "!done")])
- templateBody = CGIfWrapper(templateBody, "${val}.isObject()")
- else:
- templateBody = CGGeneric()
- if setDictionary:
- assert not object
- templateBody = CGList([templateBody,
- CGIfWrapper(setDictionary, "!done")])
- stringTypes = [t for t in memberTypes if t.isString() or t.isEnum()]
- numericTypes = [t for t in memberTypes if t.isNumeric()]
- booleanTypes = [t for t in memberTypes if t.isBoolean()]
- if stringTypes or numericTypes or booleanTypes:
- assert len(stringTypes) <= 1
- assert len(numericTypes) <= 1
- assert len(booleanTypes) <= 1
- # We will wrap all this stuff in a do { } while (0); so we
- # can use "break" for flow control.
- def getStringOrPrimitiveConversion(memberType):
- name = getUnionMemberName(memberType)
- return CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext)) || !tryNext;\n"
- "break;\n" % (unionArgumentObj, name))
- other = CGList([])
- stringConversion = map(getStringOrPrimitiveConversion, stringTypes)
- numericConversion = map(getStringOrPrimitiveConversion, numericTypes)
- booleanConversion = map(getStringOrPrimitiveConversion, booleanTypes)
- if stringConversion:
- if booleanConversion:
- other.append(CGIfWrapper(booleanConversion[0],
- "${val}.isBoolean()"))
- if numericConversion:
- other.append(CGIfWrapper(numericConversion[0],
- "${val}.isNumber()"))
- other.append(stringConversion[0])
- elif numericConversion:
- if booleanConversion:
- other.append(CGIfWrapper(booleanConversion[0],
- "${val}.isBoolean()"))
- other.append(numericConversion[0])
- else:
- assert booleanConversion
- other.append(booleanConversion[0])
- other = CGWrapper(CGIndenter(other), pre="do {\n", post="} while (0);\n")
- if hasObjectTypes or setDictionary:
- other = CGWrapper(CGIndenter(other), "{\n", post="}\n")
- if object:
- templateBody = CGElseChain([templateBody, other])
- else:
- other = CGWrapper(other, pre="if (!done) ")
- templateBody = CGList([templateBody, other])
- else:
- assert templateBody.define() == ""
- templateBody = other
- else:
- other = None
- templateBody = CGWrapper(templateBody, pre="bool done = false, failed = false, tryNext;\n")
- throw = CGGeneric(fill(
- """
- if (failed) {
- $*{exceptionCode}
- }
- if (!done) {
- ThrowErrorMessage(cx, MSG_NOT_IN_UNION, "${desc}", "${names}");
- $*{exceptionCode}
- }
- """,
- exceptionCode=exceptionCode,
- desc=firstCap(sourceDescription),
- names=", ".join(names)))
- templateBody = CGWrapper(CGIndenter(CGList([templateBody, throw])), pre="{\n", post="}\n")
- typeName = CGUnionStruct.unionTypeDecl(type, isOwningUnion)
- argumentTypeName = typeName + "Argument"
- if nullable:
- typeName = "Nullable<" + typeName + " >"
- def handleNull(templateBody, setToNullVar, extraConditionForNull=""):
- nullTest = "%s${val}.isNullOrUndefined()" % extraConditionForNull
- return CGIfElseWrapper(nullTest,
- CGGeneric("%s.SetNull();\n" % setToNullVar),
- templateBody)
- if type.hasNullableType:
- assert not nullable
- # Make sure to handle a null default value here
- if defaultValue and isinstance(defaultValue, IDLNullValue):
- assert defaultValue.type == type
- extraConditionForNull = "!(${haveValue}) || "
- else:
- extraConditionForNull = ""
- templateBody = handleNull(templateBody, unionArgumentObj,
- extraConditionForNull=extraConditionForNull)
- declType = CGGeneric(typeName)
- if isOwningUnion:
- holderType = None
- else:
- holderType = CGGeneric(argumentTypeName)
- if nullable:
- holderType = CGTemplatedType("Maybe", holderType)
- # If we're isOptional and not nullable the normal optional handling will
- # handle lazy construction of our holder. If we're nullable and not
- # owning we do it all by hand because we do not want our holder
- # constructed if we're null. But if we're owning we don't have a
- # holder anyway, so we can do the normal Optional codepath.
- declLoc = "${declName}"
- constructDecl = None
- if nullable:
- if isOptional and not isOwningUnion:
- holderArgs = "${declName}.Value().SetValue()"
- declType = CGTemplatedType("Optional", declType)
- constructDecl = CGGeneric("${declName}.Construct();\n")
- declLoc = "${declName}.Value()"
- else:
- holderArgs = "${declName}.SetValue()"
- if holderType is not None:
- constructHolder = CGGeneric("${holderName}.emplace(%s);\n" % holderArgs)
- else:
- constructHolder = None
- # Don't need to pass those args when the holder is being constructed
- holderArgs = None
- else:
- holderArgs = "${declName}"
- constructHolder = None
- if not isMember and isCallbackReturnValue:
- declType = CGWrapper(declType, post="&")
- declArgs = "aRetVal"
- else:
- declArgs = None
- if defaultValue and not isinstance(defaultValue, IDLNullValue):
- tag = defaultValue.type.tag()
- if tag in numericSuffixes or tag is IDLType.Tags.bool:
- defaultStr = getHandleDefault(defaultValue)
- # Make sure we actually construct the thing inside the nullable.
- value = declLoc + (".SetValue()" if nullable else "")
- name = getUnionMemberName(defaultValue.type)
- default = CGGeneric("%s.RawSetAs%s() = %s;\n" %
- (value, name, defaultStr))
- elif isinstance(defaultValue, IDLEmptySequenceValue):
- name = getUnionMemberName(defaultValue.type)
- # Make sure we actually construct the thing inside the nullable.
- value = declLoc + (".SetValue()" if nullable else "")
- # It's enough to set us to the right type; that will
- # create an empty array, which is all we need here.
- default = CGGeneric("%s.RawSetAs%s();\n" %
- (value, name))
- elif defaultValue.type.isEnum():
- name = getUnionMemberName(defaultValue.type)
- # Make sure we actually construct the thing inside the nullable.
- value = declLoc + (".SetValue()" if nullable else "")
- default = CGGeneric(
- "%s.RawSetAs%s() = %s::%s;\n" %
- (value, name,
- defaultValue.type.inner.identifier.name,
- getEnumValueName(defaultValue.value)))
- else:
- default = CGGeneric(
- handleDefaultStringValue(
- defaultValue, "%s.SetStringData" % unionArgumentObj) +
- ";\n")
- templateBody = CGIfElseWrapper("!(${haveValue})", default, templateBody)
- templateBody = CGList([constructHolder, templateBody])
- if nullable:
- if defaultValue:
- if isinstance(defaultValue, IDLNullValue):
- extraConditionForNull = "!(${haveValue}) || "
- else:
- extraConditionForNull = "(${haveValue}) && "
- else:
- extraConditionForNull = ""
- templateBody = handleNull(templateBody, declLoc,
- extraConditionForNull=extraConditionForNull)
- elif (not type.hasNullableType and defaultValue and
- isinstance(defaultValue, IDLNullValue)):
- assert type.hasDictionaryType()
- assert defaultValue.type.isDictionary()
- if not isOwningUnion and typeNeedsRooting(defaultValue.type):
- ctorArgs = "cx"
- else:
- ctorArgs = ""
- initDictionaryWithNull = CGIfWrapper(
- CGGeneric("return false;\n"),
- ('!%s.RawSetAs%s(%s).Init(cx, JS::NullHandleValue, "Member of %s")'
- % (declLoc, getUnionMemberName(defaultValue.type),
- ctorArgs, type)))
- templateBody = CGIfElseWrapper("!(${haveValue})",
- initDictionaryWithNull,
- templateBody)
- templateBody = CGList([constructDecl, templateBody])
- return JSToNativeConversionInfo(templateBody.define(),
- declType=declType,
- declArgs=declArgs,
- holderType=holderType,
- holderArgs=holderArgs,
- dealWithOptional=isOptional and (not nullable or isOwningUnion))
- if type.isPromise():
- assert not type.nullable()
- assert defaultValue is None
- # We always have to hold a strong ref to Promise here, because
- # Promise::resolve returns an addrefed thing.
- argIsPointer = isCallbackReturnValue
- if argIsPointer:
- declType = CGGeneric("RefPtr<Promise>")
- else:
- declType = CGGeneric("OwningNonNull<Promise>")
- # Per spec, what we're supposed to do is take the original
- # Promise.resolve and call it with the original Promise as this
- # value to make a Promise out of whatever value we actually have
- # here. The question is which global we should use. There are
- # several cases to consider:
- #
- # 1) Normal call to API with a Promise argument. This is a case the
- # spec covers, and we should be using the current Realm's
- # Promise. That means the current compartment.
- # 2) Call to API with a Promise argument over Xrays. In practice,
- # this sort of thing seems to be used for giving an API
- # implementation a way to wait for conclusion of an asyc
- # operation, _not_ to expose the Promise to content code. So we
- # probably want to allow callers to use such an API in a
- # "natural" way, by passing chrome-side promises; indeed, that
- # may be all that the caller has to represent their async
- # operation. That means we really need to do the
- # Promise.resolve() in the caller (chrome) compartment: if we do
- # it in the content compartment, we will try to call .then() on
- # the chrome promise while in the content compartment, which will
- # throw and we'll just get a rejected Promise. Note that this is
- # also the reason why a caller who has a chrome Promise
- # representing an async operation can't itself convert it to a
- # content-side Promise (at least not without some serious
- # gyrations).
- # 3) Promise return value from a callback or callback interface.
- # Per spec, this should use the Realm of the callback object. In
- # our case, that's the compartment of the underlying callback,
- # not the current compartment (which may be the compartment of
- # some cross-compartment wrapper around said callback).
- # 4) Return value from a JS-implemented interface. In this case we
- # have a problem. Our current compartment is the compartment of
- # the JS implementation. But if the JS implementation returned
- # a page-side Promise (which is a totally sane thing to do, and
- # in fact the right thing to do given that this return value is
- # going right to content script) then we don't want to
- # Promise.resolve with our current compartment Promise, because
- # that will wrap it up in a chrome-side Promise, which is
- # decidedly _not_ what's desired here. So in that case we
- # should really unwrap the return value and use the global of
- # the result. CheckedUnwrap should be good enough for that; if
- # it fails, then we're failing unwrap while in a
- # system-privileged compartment, so presumably we have a dead
- # object wrapper. Just error out. Do NOT fall back to using
- # the current compartment instead: that will return a
- # system-privileged rejected (because getting .then inside
- # resolve() failed) Promise to the caller, which they won't be
- # able to touch. That's not helpful. If we error out, on the
- # other hand, they will get a content-side rejected promise.
- # Same thing if the value returned is not even an object.
- if isCallbackReturnValue == "JSImpl":
- # Case 4 above. Note that globalObj defaults to the current
- # compartment global. Note that we don't use $*{exceptionCode}
- # here because that will try to aRv.Throw(NS_ERROR_UNEXPECTED)
- # which we don't really want here.
- assert exceptionCode == "aRv.Throw(NS_ERROR_UNEXPECTED);\nreturn nullptr;\n"
- getPromiseGlobal = fill(
- """
- if (!$${val}.isObject()) {
- aRv.ThrowTypeError<MSG_NOT_OBJECT>(NS_LITERAL_STRING("${sourceDescription}"));
- return nullptr;
- }
- JSObject* unwrappedVal = js::CheckedUnwrap(&$${val}.toObject());
- if (!unwrappedVal) {
- // A slight lie, but not much of one, for a dead object wrapper.
- aRv.ThrowTypeError<MSG_NOT_OBJECT>(NS_LITERAL_STRING("${sourceDescription}"));
- return nullptr;
- }
- globalObj = js::GetGlobalForObjectCrossCompartment(unwrappedVal);
- """,
- sourceDescription=sourceDescription)
- elif isCallbackReturnValue == "Callback":
- getPromiseGlobal = dedent(
- """
- // We basically want our entry global here. Play it safe
- // and use GetEntryGlobal() to get it, with whatever
- // principal-clamping it ends up doing.
- globalObj = GetEntryGlobal()->GetGlobalJSObject();
- """)
- else:
- getPromiseGlobal = ""
- templateBody = fill(
- """
- { // Scope for our GlobalObject, FastErrorResult, JSAutoCompartment,
- // etc.
- JS::Rooted<JSObject*> globalObj(cx, JS::CurrentGlobalOrNull(cx));
- $*{getPromiseGlobal}
- JSAutoCompartment ac(cx, globalObj);
- GlobalObject promiseGlobal(cx, globalObj);
- if (promiseGlobal.Failed()) {
- $*{exceptionCode}
- }
- JS::Rooted<JS::Value> valueToResolve(cx, $${val});
- if (!JS_WrapValue(cx, &valueToResolve)) {
- $*{exceptionCode}
- }
- binding_detail::FastErrorResult promiseRv;
- nsCOMPtr<nsIGlobalObject> global =
- do_QueryInterface(promiseGlobal.GetAsSupports());
- if (!global) {
- promiseRv.Throw(NS_ERROR_UNEXPECTED);
- promiseRv.MaybeSetPendingException(cx);
- $*{exceptionCode}
- }
- $${declName} = Promise::Resolve(global, cx, valueToResolve,
- promiseRv);
- if (promiseRv.MaybeSetPendingException(cx)) {
- $*{exceptionCode}
- }
- }
- """,
- getPromiseGlobal=getPromiseGlobal,
- exceptionCode=exceptionCode)
- return JSToNativeConversionInfo(templateBody,
- declType=declType,
- dealWithOptional=isOptional)
- if type.isGeckoInterface():
- assert not isEnforceRange and not isClamp
- descriptor = descriptorProvider.getDescriptor(
- type.unroll().inner.identifier.name)
- assert descriptor.nativeType != 'JSObject'
- if descriptor.interface.isCallback():
- (declType, declArgs,
- conversion) = getCallbackConversionInfo(type, descriptor.interface,
- isMember,
- isCallbackReturnValue,
- isOptional)
- template = wrapObjectTemplate(conversion, type,
- "${declName} = nullptr;\n",
- failureCode)
- return JSToNativeConversionInfo(template, declType=declType,
- declArgs=declArgs,
- dealWithOptional=isOptional)
- # This is an interface that we implement as a concrete class
- # or an XPCOM interface.
- # Allow null pointers for nullable types and old-binding classes, and
- # use an RefPtr or raw pointer for callback return values to make
- # them easier to return.
- argIsPointer = (type.nullable() or type.unroll().inner.isExternal() or
- isCallbackReturnValue)
- # Sequence and dictionary members, as well as owning unions (which can
- # appear here as return values in JS-implemented interfaces) have to
- # hold a strong ref to the thing being passed down. Those all set
- # isMember.
- #
- # Also, callback return values always end up addrefing anyway, so there
- # is no point trying to avoid it here and it makes other things simpler
- # since we can assume the return value is a strong ref.
- assert not descriptor.interface.isCallback()
- forceOwningType = isMember or isCallbackReturnValue
- typeName = descriptor.nativeType
- typePtr = typeName + "*"
- # Compute a few things:
- # - declType is the type we want to return as the first element of our
- # tuple.
- # - holderType is the type we want to return as the third element
- # of our tuple.
- # Set up some sensible defaults for these things insofar as we can.
- holderType = None
- if argIsPointer:
- if forceOwningType:
- declType = "RefPtr<" + typeName + ">"
- else:
- declType = typePtr
- else:
- if forceOwningType:
- declType = "OwningNonNull<" + typeName + ">"
- else:
- declType = "NonNull<" + typeName + ">"
- templateBody = ""
- if forceOwningType:
- templateBody += 'static_assert(IsRefcounted<%s>::value, "We can only store refcounted classes.");' % typeName
- if (not descriptor.interface.isConsequential() and
- not descriptor.interface.isExternal()):
- if failureCode is not None:
- templateBody += str(CastableObjectUnwrapper(
- descriptor,
- "${val}",
- "${maybeMutableVal}",
- "${declName}",
- failureCode))
- else:
- templateBody += str(FailureFatalCastableObjectUnwrapper(
- descriptor,
- "${val}",
- "${maybeMutableVal}",
- "${declName}",
- exceptionCode,
- isCallbackReturnValue,
- firstCap(sourceDescription)))
- else:
- # Either external, or new-binding non-castable. We always have a
- # holder for these, because we don't actually know whether we have
- # to addref when unwrapping or not. So we just pass an
- # getter_AddRefs(RefPtr) to XPConnect and if we'll need a release
- # it'll put a non-null pointer in there.
- if forceOwningType:
- # Don't return a holderType in this case; our declName
- # will just own stuff.
- templateBody += "RefPtr<" + typeName + "> ${holderName};\n"
- else:
- holderType = "RefPtr<" + typeName + ">"
- templateBody += (
- "JS::Rooted<JSObject*> source(cx, &${val}.toObject());\n" +
- "if (NS_FAILED(UnwrapArg<" + typeName + ">(source, getter_AddRefs(${holderName})))) {\n")
- templateBody += CGIndenter(onFailureBadType(failureCode,
- descriptor.interface.identifier.name)).define()
- templateBody += ("}\n"
- "MOZ_ASSERT(${holderName});\n")
- # And store our value in ${declName}
- templateBody += "${declName} = ${holderName};\n"
- # Just pass failureCode, not onFailureBadType, here, so we'll report
- # the thing as not an object as opposed to not implementing whatever
- # our interface is.
- templateBody = wrapObjectTemplate(templateBody, type,
- "${declName} = nullptr;\n",
- failureCode)
- declType = CGGeneric(declType)
- if holderType is not None:
- holderType = CGGeneric(holderType)
- return JSToNativeConversionInfo(templateBody,
- declType=declType,
- holderType=holderType,
- dealWithOptional=isOptional)
- if type.isSpiderMonkeyInterface():
- assert not isEnforceRange and not isClamp
- name = type.unroll().name # unroll() because it may be nullable
- arrayType = CGGeneric(name)
- declType = arrayType
- if type.nullable():
- declType = CGTemplatedType("Nullable", declType)
- objRef = "${declName}.SetValue()"
- else:
- objRef = "${declName}"
- # Again, this is a bit strange since we are actually building a
- # template string here. ${objRef} and $*{badType} below are filled in
- # right now; $${val} expands to ${val}, to be filled in later.
- template = fill(
- """
- if (!${objRef}.Init(&$${val}.toObject())) {
- $*{badType}
- }
- """,
- objRef=objRef,
- badType=onFailureBadType(failureCode, type.name).define())
- template = wrapObjectTemplate(template, type, "${declName}.SetNull();\n",
- failureCode)
- if not isMember:
- # This is a bit annoying. In a union we don't want to have a
- # holder, since unions don't support that. But if we're optional we
- # want to have a holder, so that the callee doesn't see
- # Optional<RootedTypedArray<ArrayType> >. So do a holder if we're
- # optional and use a RootedTypedArray otherwise.
- if isOptional:
- holderType = CGTemplatedType("TypedArrayRooter", arrayType)
- # If our typed array is nullable, this will set the Nullable to
- # be not-null, but that's ok because we make an explicit
- # SetNull() call on it as needed if our JS value is actually
- # null. XXXbz Because "Maybe" takes const refs for constructor
- # arguments, we can't pass a reference here; have to pass a
- # pointer.
- holderArgs = "cx, &%s" % objRef
- declArgs = None
- else:
- holderType = None
- holderArgs = None
- declType = CGTemplatedType("RootedTypedArray", declType)
- declArgs = "cx"
- else:
- holderType = None
- holderArgs = None
- declArgs = None
- return JSToNativeConversionInfo(template,
- declType=declType,
- holderType=holderType,
- dealWithOptional=isOptional,
- declArgs=declArgs,
- holderArgs=holderArgs)
- if type.isDOMString() or type.isUSVString():
- assert not isEnforceRange and not isClamp
- treatAs = {
- "Default": "eStringify",
- "EmptyString": "eEmpty",
- "Null": "eNull",
- }
- if type.nullable():
- # For nullable strings null becomes a null string.
- treatNullAs = "Null"
- # For nullable strings undefined also becomes a null string.
- undefinedBehavior = "eNull"
- else:
- undefinedBehavior = "eStringify"
- nullBehavior = treatAs[treatNullAs]
- def getConversionCode(varName):
- normalizeCode = ""
- if type.isUSVString():
- normalizeCode = "NormalizeUSVString(%s);\n" % varName
- conversionCode = fill("""
- if (!ConvertJSValueToString(cx, $${val}, ${nullBehavior}, ${undefinedBehavior}, ${varName})) {
- $*{exceptionCode}
- }
- $*{normalizeCode}
- """
- ,
- nullBehavior=nullBehavior,
- undefinedBehavior=undefinedBehavior,
- varName=varName,
- exceptionCode=exceptionCode,
- normalizeCode=normalizeCode)
- if defaultValue is None:
- return conversionCode
- if isinstance(defaultValue, IDLNullValue):
- assert(type.nullable())
- defaultCode = "%s.SetIsVoid(true)" % varName
- else:
- defaultCode = handleDefaultStringValue(defaultValue,
- "%s.Rebind" % varName)
- return handleDefault(conversionCode, defaultCode + ";\n")
- if isMember:
- # Convert directly into the nsString member we have.
- declType = CGGeneric("nsString")
- return JSToNativeConversionInfo(
- getConversionCode("${declName}"),
- declType=declType,
- dealWithOptional=isOptional)
- if isOptional:
- declType = "Optional<nsAString>"
- holderType = CGGeneric("binding_detail::FakeString")
- conversionCode = ("%s"
- "${declName} = &${holderName};\n" %
- getConversionCode("${holderName}"))
- else:
- declType = "binding_detail::FakeString"
- holderType = None
- conversionCode = getConversionCode("${declName}")
- # No need to deal with optional here; we handled it already
- return JSToNativeConversionInfo(
- conversionCode,
- declType=CGGeneric(declType),
- holderType=holderType)
- if type.isByteString():
- assert not isEnforceRange and not isClamp
- nullable = toStringBool(type.nullable())
- conversionCode = fill("""
- if (!ConvertJSValueToByteString(cx, $${val}, ${nullable}, $${declName})) {
- $*{exceptionCode}
- }
- """,
- nullable=nullable,
- exceptionCode=exceptionCode)
- if defaultValue is not None:
- if isinstance(defaultValue, IDLNullValue):
- assert(type.nullable())
- defaultCode = "${declName}.SetIsVoid(true)"
- else:
- defaultCode = handleDefaultStringValue(defaultValue,
- "${declName}.Rebind")
- conversionCode = handleDefault(conversionCode, defaultCode + ";\n")
- return JSToNativeConversionInfo(
- conversionCode,
- declType=CGGeneric("nsCString"),
- dealWithOptional=isOptional)
- if type.isEnum():
- assert not isEnforceRange and not isClamp
- enumName = type.unroll().inner.identifier.name
- declType = CGGeneric(enumName)
- if type.nullable():
- declType = CGTemplatedType("Nullable", declType)
- declType = declType.define()
- enumLoc = "${declName}.SetValue()"
- else:
- enumLoc = "${declName}"
- declType = declType.define()
- if invalidEnumValueFatal:
- handleInvalidEnumValueCode = "MOZ_ASSERT(index >= 0);\n"
- else:
- # invalidEnumValueFatal is false only for attributes. So we won't
- # have a non-default exceptionCode here unless attribute "arg
- # conversion" code starts passing in an exceptionCode. At which
- # point we'll need to figure out what that even means.
- assert exceptionCode == "return false;\n"
- handleInvalidEnumValueCode = dedent("""
- if (index < 0) {
- return true;
- }
- """)
- template = fill(
- """
- {
- int index;
- if (!FindEnumStringIndex<${invalidEnumValueFatal}>(cx, $${val}, ${values}, "${enumtype}", "${sourceDescription}", &index)) {
- $*{exceptionCode}
- }
- $*{handleInvalidEnumValueCode}
- ${enumLoc} = static_cast<${enumtype}>(index);
- }
- """,
- enumtype=enumName,
- values=enumName + "Values::" + ENUM_ENTRY_VARIABLE_NAME,
- invalidEnumValueFatal=toStringBool(invalidEnumValueFatal),
- handleInvalidEnumValueCode=handleInvalidEnumValueCode,
- exceptionCode=exceptionCode,
- enumLoc=enumLoc,
- sourceDescription=firstCap(sourceDescription))
- setNull = "${declName}.SetNull();\n"
- if type.nullable():
- template = CGIfElseWrapper("${val}.isNullOrUndefined()",
- CGGeneric(setNull),
- CGGeneric(template)).define()
- if defaultValue is not None:
- if isinstance(defaultValue, IDLNullValue):
- assert type.nullable()
- template = handleDefault(template, setNull)
- else:
- assert(defaultValue.type.tag() == IDLType.Tags.domstring)
- template = handleDefault(template,
- ("%s = %s::%s;\n" %
- (enumLoc, enumName,
- getEnumValueName(defaultValue.value))))
- return JSToNativeConversionInfo(template, declType=CGGeneric(declType),
- dealWithOptional=isOptional)
- if type.isCallback():
- assert not isEnforceRange and not isClamp
- assert not type.treatNonCallableAsNull() or type.nullable()
- assert not type.treatNonObjectAsNull() or type.nullable()
- assert not type.treatNonObjectAsNull() or not type.treatNonCallableAsNull()
- callback = type.unroll().callback
- name = callback.identifier.name
- (declType, declArgs,
- conversion) = getCallbackConversionInfo(type, callback, isMember,
- isCallbackReturnValue,
- isOptional)
- if allowTreatNonCallableAsNull and type.treatNonCallableAsNull():
- haveCallable = "JS::IsCallable(&${val}.toObject())"
- if not isDefinitelyObject:
- haveCallable = "${val}.isObject() && " + haveCallable
- if defaultValue is not None:
- assert(isinstance(defaultValue, IDLNullValue))
- haveCallable = "(${haveValue}) && " + haveCallable
- template = (
- ("if (%s) {\n" % haveCallable) +
- conversion +
- "} else {\n"
- " ${declName} = nullptr;\n"
- "}\n")
- elif allowTreatNonCallableAsNull and type.treatNonObjectAsNull():
- if not isDefinitelyObject:
- haveObject = "${val}.isObject()"
- if defaultValue is not None:
- assert(isinstance(defaultValue, IDLNullValue))
- haveObject = "(${haveValue}) && " + haveObject
- template = CGIfElseWrapper(haveObject,
- CGGeneric(conversion),
- CGGeneric("${declName} = nullptr;\n")).define()
- else:
- template = conversion
- else:
- template = wrapObjectTemplate(
- "if (JS::IsCallable(&${val}.toObject())) {\n" +
- conversion +
- "} else {\n" +
- indent(onFailureNotCallable(failureCode).define()) +
- "}\n",
- type,
- "${declName} = nullptr;\n",
- failureCode)
- return JSToNativeConversionInfo(template, declType=declType,
- declArgs=declArgs,
- dealWithOptional=isOptional)
- if type.isAny():
- assert not isEnforceRange and not isClamp
- declArgs = None
- if isMember in ("Variadic", "Sequence", "Dictionary", "Record"):
- # Rooting is handled by the sequence and dictionary tracers.
- declType = "JS::Value"
- else:
- assert not isMember
- declType = "JS::Rooted<JS::Value>"
- declArgs = "cx"
- assert not isOptional
- templateBody = "${declName} = ${val};\n"
- # For JS-implemented APIs, we refuse to allow passing objects that the
- # API consumer does not subsume. The extra parens around
- # ($${passedToJSImpl}) suppress unreachable code warnings when
- # $${passedToJSImpl} is the literal `false`.
- if not isinstance(descriptorProvider, Descriptor) or descriptorProvider.interface.isJSImplemented():
- templateBody = fill(
- """
- if (($${passedToJSImpl}) && !CallerSubsumes($${val})) {
- ThrowErrorMessage(cx, MSG_PERMISSION_DENIED_TO_PASS_ARG, "${sourceDescription}");
- $*{exceptionCode}
- }
- """,
- sourceDescription=sourceDescription,
- exceptionCode=exceptionCode) + templateBody
- # We may not have a default value if we're being converted for
- # a setter, say.
- if defaultValue:
- if isinstance(defaultValue, IDLNullValue):
- defaultHandling = "${declName} = JS::NullValue();\n"
- else:
- assert isinstance(defaultValue, IDLUndefinedValue)
- defaultHandling = "${declName} = JS::UndefinedValue();\n"
- templateBody = handleDefault(templateBody, defaultHandling)
- return JSToNativeConversionInfo(templateBody,
- declType=CGGeneric(declType),
- declArgs=declArgs)
- if type.isObject():
- assert not isEnforceRange and not isClamp
- return handleJSObjectType(type, isMember, failureCode, exceptionCode, sourceDescription)
- if type.isDictionary():
- # There are no nullable dictionary arguments or dictionary members
- assert(not type.nullable() or isCallbackReturnValue or
- (isMember and isMember != "Dictionary"))
- # All optional dictionaries always have default values, so we
- # should be able to assume not isOptional here.
- assert not isOptional
- # In the callback return value case we never have to worry
- # about a default value; we always have a value.
- assert not isCallbackReturnValue or defaultValue is None
- typeName = CGDictionary.makeDictionaryName(type.unroll().inner)
- if not isMember and not isCallbackReturnValue:
- # Since we're not a member and not nullable or optional, no one will
- # see our real type, so we can do the fast version of the dictionary
- # that doesn't pre-initialize members.
- typeName = "binding_detail::Fast" + typeName
- declType = CGGeneric(typeName)
- # We do manual default value handling here, because we
- # actually do want a jsval, and we only handle null anyway
- # NOTE: if isNullOrUndefined or isDefinitelyObject are true,
- # we know we have a value, so we don't have to worry about the
- # default value.
- if (not isNullOrUndefined and not isDefinitelyObject and
- defaultValue is not None):
- assert(isinstance(defaultValue, IDLNullValue))
- val = "(${haveValue}) ? ${val} : JS::NullHandleValue"
- else:
- val = "${val}"
- dictLoc = "${declName}"
- if type.nullable():
- dictLoc += ".SetValue()"
- conversionCode = fill("""
- if (!${dictLoc}.Init(cx, ${val}, "${desc}", $${passedToJSImpl})) {
- $*{exceptionCode}
- }
- """,
- dictLoc=dictLoc,
- val=val,
- desc=firstCap(sourceDescription),
- exceptionCode=exceptionCode)
- if failureCode is not None:
- if isDefinitelyObject:
- dictionaryTest = "IsObjectValueConvertibleToDictionary"
- else:
- dictionaryTest = "IsConvertibleToDictionary"
- template = fill("""
- { // scope for isConvertible
- bool isConvertible;
- if (!${testConvertible}(cx, ${val}, &isConvertible)) {
- $*{exceptionCode}
- }
- if (!isConvertible) {
- $*{failureCode}
- }
- $*{conversionCode}
- }
- """,
- testConvertible=dictionaryTest,
- val=val,
- exceptionCode=exceptionCode,
- failureCode=failureCode,
- conversionCode=conversionCode)
- else:
- template = conversionCode
- if type.nullable():
- declType = CGTemplatedType("Nullable", declType)
- template = CGIfElseWrapper("${val}.isNullOrUndefined()",
- CGGeneric("${declName}.SetNull();\n"),
- CGGeneric(template)).define()
- # Dictionary arguments that might contain traceable things need to get
- # traced
- if not isMember and isCallbackReturnValue:
- # Go ahead and just convert directly into our actual return value
- declType = CGWrapper(declType, post="&")
- declArgs = "aRetVal"
- elif not isMember and typeNeedsRooting(type):
- declType = CGTemplatedType("RootedDictionary", declType)
- declArgs = "cx"
- else:
- declArgs = None
- return JSToNativeConversionInfo(template, declType=declType,
- declArgs=declArgs)
- if type.isVoid():
- assert not isOptional
- # This one only happens for return values, and its easy: Just
- # ignore the jsval.
- return JSToNativeConversionInfo("")
- if type.isDate():
- assert not isEnforceRange and not isClamp
- declType = CGGeneric("Date")
- if type.nullable():
- declType = CGTemplatedType("Nullable", declType)
- dateVal = "${declName}.SetValue()"
- else:
- dateVal = "${declName}"
- if failureCode is None:
- notDate = ('ThrowErrorMessage(cx, MSG_NOT_DATE, "%s");\n'
- "%s" % (firstCap(sourceDescription), exceptionCode))
- else:
- notDate = failureCode
- conversion = fill(
- """
- JS::Rooted<JSObject*> possibleDateObject(cx, &$${val}.toObject());
- { // scope for isDate
- bool isDate;
- if (!JS_ObjectIsDate(cx, possibleDateObject, &isDate)) {
- $*{exceptionCode}
- }
- if (!isDate) {
- $*{notDate}
- }
- if (!${dateVal}.SetTimeStamp(cx, possibleDateObject)) {
- $*{exceptionCode}
- }
- }
- """,
- exceptionCode=exceptionCode,
- dateVal=dateVal,
- notDate=notDate)
- conversion = wrapObjectTemplate(conversion, type,
- "${declName}.SetNull();\n", notDate)
- return JSToNativeConversionInfo(conversion,
- declType=declType,
- dealWithOptional=isOptional)
- if not type.isPrimitive():
- raise TypeError("Need conversion for argument type '%s'" % str(type))
- typeName = builtinNames[type.tag()]
- conversionBehavior = "eDefault"
- if isEnforceRange:
- assert type.isInteger()
- conversionBehavior = "eEnforceRange"
- elif isClamp:
- assert type.isInteger()
- conversionBehavior = "eClamp"
- if type.nullable():
- declType = CGGeneric("Nullable<" + typeName + ">")
- writeLoc = "${declName}.SetValue()"
- readLoc = "${declName}.Value()"
- nullCondition = "${val}.isNullOrUndefined()"
- if defaultValue is not None and isinstance(defaultValue, IDLNullValue):
- nullCondition = "!(${haveValue}) || " + nullCondition
- template = fill("""
- if (${nullCondition}) {
- $${declName}.SetNull();
- } else if (!ValueToPrimitive<${typeName}, ${conversionBehavior}>(cx, $${val}, &${writeLoc})) {
- $*{exceptionCode}
- }
- """,
- nullCondition=nullCondition,
- typeName=typeName,
- conversionBehavior=conversionBehavior,
- writeLoc=writeLoc,
- exceptionCode=exceptionCode)
- else:
- assert(defaultValue is None or
- not isinstance(defaultValue, IDLNullValue))
- writeLoc = "${declName}"
- readLoc = writeLoc
- template = fill("""
- if (!ValueToPrimitive<${typeName}, ${conversionBehavior}>(cx, $${val}, &${writeLoc})) {
- $*{exceptionCode}
- }
- """,
- typeName=typeName,
- conversionBehavior=conversionBehavior,
- writeLoc=writeLoc,
- exceptionCode=exceptionCode)
- declType = CGGeneric(typeName)
- if type.isFloat() and not type.isUnrestricted():
- if lenientFloatCode is not None:
- nonFiniteCode = lenientFloatCode
- else:
- nonFiniteCode = ('ThrowErrorMessage(cx, MSG_NOT_FINITE, "%s");\n'
- "%s" % (firstCap(sourceDescription), exceptionCode))
- # We're appending to an if-block brace, so strip trailing whitespace
- # and add an extra space before the else.
- template = template.rstrip()
- template += fill("""
- else if (!mozilla::IsFinite(${readLoc})) {
- $*{nonFiniteCode}
- }
- """,
- readLoc=readLoc,
- nonFiniteCode=nonFiniteCode)
- if (defaultValue is not None and
- # We already handled IDLNullValue, so just deal with the other ones
- not isinstance(defaultValue, IDLNullValue)):
- tag = defaultValue.type.tag()
- defaultStr = getHandleDefault(defaultValue)
- template = CGIfElseWrapper(
- "${haveValue}",
- CGGeneric(template),
- CGGeneric("%s = %s;\n" % (writeLoc, defaultStr))).define()
- return JSToNativeConversionInfo(template, declType=declType,
- dealWithOptional=isOptional)
- def instantiateJSToNativeConversion(info, replacements, checkForValue=False):
- """
- Take a JSToNativeConversionInfo as returned by getJSToNativeConversionInfo
- and a set of replacements as required by the strings in such an object, and
- generate code to convert into stack C++ types.
- If checkForValue is True, then the conversion will get wrapped in
- a check for ${haveValue}.
- """
- templateBody, declType, holderType, dealWithOptional = (
- info.template, info.declType, info.holderType, info.dealWithOptional)
- if dealWithOptional and not checkForValue:
- raise TypeError("Have to deal with optional things, but don't know how")
- if checkForValue and declType is None:
- raise TypeError("Need to predeclare optional things, so they will be "
- "outside the check for big enough arg count!")
- # We can't precompute our holder constructor arguments, since
- # those might depend on ${declName}, which we change below. Just
- # compute arguments at the point when we need them as we go.
- def getArgsCGThing(args):
- return CGGeneric(string.Template(args).substitute(replacements))
- result = CGList([])
- # Make a copy of "replacements" since we may be about to start modifying it
- replacements = dict(replacements)
- originalDeclName = replacements["declName"]
- if declType is not None:
- if dealWithOptional:
- replacements["declName"] = "%s.Value()" % originalDeclName
- declType = CGTemplatedType("Optional", declType)
- declCtorArgs = None
- elif info.declArgs is not None:
- declCtorArgs = CGWrapper(getArgsCGThing(info.declArgs),
- pre="(", post=")")
- else:
- declCtorArgs = None
- result.append(
- CGList([declType, CGGeneric(" "),
- CGGeneric(originalDeclName),
- declCtorArgs, CGGeneric(";\n")]))
- originalHolderName = replacements["holderName"]
- if holderType is not None:
- if dealWithOptional:
- replacements["holderName"] = "%s.ref()" % originalHolderName
- holderType = CGTemplatedType("Maybe", holderType)
- holderCtorArgs = None
- elif info.holderArgs is not None:
- holderCtorArgs = CGWrapper(getArgsCGThing(info.holderArgs),
- pre="(", post=")")
- else:
- holderCtorArgs = None
- result.append(
- CGList([holderType, CGGeneric(" "),
- CGGeneric(originalHolderName),
- holderCtorArgs, CGGeneric(";\n")]))
- if "maybeMutableVal" not in replacements:
- replacements["maybeMutableVal"] = replacements["val"]
- conversion = CGGeneric(
- string.Template(templateBody).substitute(replacements))
- if checkForValue:
- if dealWithOptional:
- declConstruct = CGIndenter(
- CGGeneric("%s.Construct(%s);\n" %
- (originalDeclName,
- getArgsCGThing(info.declArgs).define() if
- info.declArgs else "")))
- if holderType is not None:
- holderConstruct = CGIndenter(
- CGGeneric("%s.emplace(%s);\n" %
- (originalHolderName,
- getArgsCGThing(info.holderArgs).define() if
- info.holderArgs else "")))
- else:
- holderConstruct = None
- else:
- declConstruct = None
- holderConstruct = None
- conversion = CGList([
- CGGeneric(
- string.Template("if (${haveValue}) {\n").substitute(replacements)),
- declConstruct,
- holderConstruct,
- CGIndenter(conversion),
- CGGeneric("}\n")
- ])
- result.append(conversion)
- return result
- def convertConstIDLValueToJSVal(value):
- if isinstance(value, IDLNullValue):
- return "JS::NullValue()"
- if isinstance(value, IDLUndefinedValue):
- return "JS::UndefinedValue()"
- tag = value.type.tag()
- if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
- IDLType.Tags.uint16, IDLType.Tags.int32]:
- return "JS::Int32Value(%s)" % (value.value)
- if tag == IDLType.Tags.uint32:
- return "JS::NumberValue(%sU)" % (value.value)
- if tag in [IDLType.Tags.int64, IDLType.Tags.uint64]:
- return "JS::CanonicalizedDoubleValue(%s)" % numericValue(tag, value.value)
- if tag == IDLType.Tags.bool:
- return "JS::BooleanValue(true)" if value.value else "JS::BooleanValue(false)"
- if tag in [IDLType.Tags.float, IDLType.Tags.double]:
- return "JS::CanonicalizedDoubleValue(%s)" % (value.value)
- raise TypeError("Const value of unhandled type: %s" % value.type)
- class CGArgumentConverter(CGThing):
- """
- A class that takes an IDL argument object and its index in the
- argument list and generates code to unwrap the argument to the
- right native type.
- argDescription is a description of the argument for error-reporting
- purposes. Callers should assume that it might get placed in the middle of a
- sentence. If it ends up at the beginning of a sentence, its first character
- will be automatically uppercased.
- """
- def __init__(self, argument, index, descriptorProvider,
- argDescription, member,
- invalidEnumValueFatal=True, lenientFloatCode=None):
- CGThing.__init__(self)
- self.argument = argument
- self.argDescription = argDescription
- assert(not argument.defaultValue or argument.optional)
- replacer = {
- "index": index,
- "argc": "args.length()"
- }
- self.replacementVariables = {
- "declName": "arg%d" % index,
- "holderName": ("arg%d" % index) + "_holder",
- "obj": "obj",
- "passedToJSImpl": toStringBool(isJSImplementedDescriptor(descriptorProvider))
- }
- # If we have a method generated by the maplike/setlike portion of an
- # interface, arguments can possibly be undefined, but will need to be
- # converted to the key/value type of the backing object. In this case,
- # use .get() instead of direct access to the argument. This won't
- # matter for iterable since generated functions for those interface
- # don't take arguments.
- if member.isMethod() and member.isMaplikeOrSetlikeOrIterableMethod():
- self.replacementVariables["val"] = string.Template(
- "args.get(${index})").substitute(replacer)
- self.replacementVariables["maybeMutableVal"] = string.Template(
- "args[${index}]").substitute(replacer)
- else:
- self.replacementVariables["val"] = string.Template(
- "args[${index}]").substitute(replacer)
- haveValueCheck = string.Template(
- "args.hasDefined(${index})").substitute(replacer)
- self.replacementVariables["haveValue"] = haveValueCheck
- self.descriptorProvider = descriptorProvider
- if self.argument.canHaveMissingValue():
- self.argcAndIndex = replacer
- else:
- self.argcAndIndex = None
- self.invalidEnumValueFatal = invalidEnumValueFatal
- self.lenientFloatCode = lenientFloatCode
- def define(self):
- typeConversion = getJSToNativeConversionInfo(
- self.argument.type,
- self.descriptorProvider,
- isOptional=(self.argcAndIndex is not None and
- not self.argument.variadic),
- invalidEnumValueFatal=self.invalidEnumValueFatal,
- defaultValue=self.argument.defaultValue,
- treatNullAs=self.argument.treatNullAs,
- isEnforceRange=self.argument.enforceRange,
- isClamp=self.argument.clamp,
- lenientFloatCode=self.lenientFloatCode,
- isMember="Variadic" if self.argument.variadic else False,
- allowTreatNonCallableAsNull=self.argument.allowTreatNonCallableAsNull(),
- sourceDescription=self.argDescription)
- if not self.argument.variadic:
- return instantiateJSToNativeConversion(
- typeConversion,
- self.replacementVariables,
- self.argcAndIndex is not None).define()
- # Variadic arguments get turned into a sequence.
- if typeConversion.dealWithOptional:
- raise TypeError("Shouldn't have optional things in variadics")
- if typeConversion.holderType is not None:
- raise TypeError("Shouldn't need holders for variadics")
- replacer = dict(self.argcAndIndex, **self.replacementVariables)
- replacer["seqType"] = CGTemplatedType("binding_detail::AutoSequence",
- typeConversion.declType).define()
- if typeNeedsRooting(self.argument.type):
- rooterDecl = ("SequenceRooter<%s> ${holderName}(cx, &${declName});\n" %
- typeConversion.declType.define())
- else:
- rooterDecl = ""
- replacer["elemType"] = typeConversion.declType.define()
- # NOTE: Keep this in sync with sequence conversions as needed
- variadicConversion = string.Template(
- "${seqType} ${declName};\n" +
- rooterDecl +
- dedent("""
- if (${argc} > ${index}) {
- if (!${declName}.SetCapacity(${argc} - ${index}, mozilla::fallible)) {
- JS_ReportOutOfMemory(cx);
- return false;
- }
- for (uint32_t variadicArg = ${index}; variadicArg < ${argc}; ++variadicArg) {
- ${elemType}& slot = *${declName}.AppendElement(mozilla::fallible);
- """)
- ).substitute(replacer)
- val = string.Template("args[variadicArg]").substitute(replacer)
- variadicConversion += indent(
- string.Template(typeConversion.template).substitute({
- "val": val,
- "maybeMutableVal": val,
- "declName": "slot",
- # We only need holderName here to handle isExternal()
- # interfaces, which use an internal holder for the
- # conversion even when forceOwningType ends up true.
- "holderName": "tempHolder",
- # Use the same ${obj} as for the variadic arg itself
- "obj": replacer["obj"],
- "passedToJSImpl": toStringBool(isJSImplementedDescriptor(self.descriptorProvider))
- }), 4)
- variadicConversion += (" }\n"
- "}\n")
- return variadicConversion
- def getMaybeWrapValueFuncForType(type):
- # Callbacks might actually be DOM objects; nothing prevents a page from
- # doing that.
- if type.isCallback() or type.isCallbackInterface() or type.isObject():
- if type.nullable():
- return "MaybeWrapObjectOrNullValue"
- return "MaybeWrapObjectValue"
- # Spidermonkey interfaces are never DOM objects. Neither are sequences or
- # dictionaries, since those are always plain JS objects.
- if type.isSpiderMonkeyInterface() or type.isDictionary() or type.isSequence():
- if type.nullable():
- return "MaybeWrapNonDOMObjectOrNullValue"
- return "MaybeWrapNonDOMObjectValue"
- if type.isAny():
- return "MaybeWrapValue"
- # For other types, just go ahead an fall back on MaybeWrapValue for now:
- # it's always safe to do, and shouldn't be particularly slow for any of
- # them
- return "MaybeWrapValue"
- sequenceWrapLevel = 0
- recordWrapLevel = 0
- def getWrapTemplateForType(type, descriptorProvider, result, successCode,
- returnsNewObject, exceptionCode, typedArraysAreStructs,
- isConstructorRetval=False):
- """
- Reflect a C++ value stored in "result", of IDL type "type" into JS. The
- "successCode" is the code to run once we have successfully done the
- conversion and must guarantee that execution of the conversion template
- stops once the successCode has executed (e.g. by doing a 'return', or by
- doing a 'break' if the entire conversion template is inside a block that
- the 'break' will exit).
- If typedArraysAreStructs is true, then if the type is a typed array,
- "result" is one of the dom::TypedArray subclasses, not a JSObject*.
- The resulting string should be used with string.Template. It
- needs the following keys when substituting:
- jsvalHandle: something that can be passed to methods taking a
- JS::MutableHandle<JS::Value>. This can be a
- JS::MutableHandle<JS::Value> or a JS::Rooted<JS::Value>*.
- jsvalRef: something that can have .address() called on it to get a
- JS::Value* and .set() called on it to set it to a JS::Value.
- This can be a JS::MutableHandle<JS::Value> or a
- JS::Rooted<JS::Value>.
- obj: a JS::Handle<JSObject*>.
- Returns (templateString, infallibility of conversion template)
- """
- if successCode is None:
- successCode = "return true;\n"
- def setUndefined():
- return _setValue("", setter="setUndefined")
- def setNull():
- return _setValue("", setter="setNull")
- def setInt32(value):
- return _setValue(value, setter="setInt32")
- def setString(value):
- return _setValue(value, setter="setString")
- def setObject(value, wrapAsType=None):
- return _setValue(value, wrapAsType=wrapAsType, setter="setObject")
- def setObjectOrNull(value, wrapAsType=None):
- return _setValue(value, wrapAsType=wrapAsType, setter="setObjectOrNull")
- def setUint32(value):
- return _setValue(value, setter="setNumber")
- def setDouble(value):
- return _setValue("JS_NumberValue(%s)" % value)
- def setBoolean(value):
- return _setValue(value, setter="setBoolean")
- def _setValue(value, wrapAsType=None, setter="set"):
- """
- Returns the code to set the jsval to value.
- If wrapAsType is not None, then will wrap the resulting value using the
- function that getMaybeWrapValueFuncForType(wrapAsType) returns.
- Otherwise, no wrapping will be done.
- """
- if wrapAsType is None:
- tail = successCode
- else:
- tail = fill(
- """
- if (!${maybeWrap}(cx, $${jsvalHandle})) {
- $*{exceptionCode}
- }
- $*{successCode}
- """,
- maybeWrap=getMaybeWrapValueFuncForType(wrapAsType),
- exceptionCode=exceptionCode,
- successCode=successCode)
- return ("${jsvalRef}.%s(%s);\n" % (setter, value)) + tail
- def wrapAndSetPtr(wrapCall, failureCode=None):
- """
- Returns the code to set the jsval by calling "wrapCall". "failureCode"
- is the code to run if calling "wrapCall" fails
- """
- if failureCode is None:
- failureCode = exceptionCode
- return fill(
- """
- if (!${wrapCall}) {
- $*{failureCode}
- }
- $*{successCode}
- """,
- wrapCall=wrapCall,
- failureCode=failureCode,
- successCode=successCode)
- if type is None or type.isVoid():
- return (setUndefined(), True)
- if (type.isSequence() or type.isRecord()) and type.nullable():
- # These are both wrapped in Nullable<>
- recTemplate, recInfall = getWrapTemplateForType(type.inner, descriptorProvider,
- "%s.Value()" % result, successCode,
- returnsNewObject, exceptionCode,
- typedArraysAreStructs)
- code = fill(
- """
- if (${result}.IsNull()) {
- $*{setNull}
- }
- $*{recTemplate}
- """,
- result=result,
- setNull=setNull(),
- recTemplate=recTemplate)
- return code, recInfall
- if type.isSequence():
- # Now do non-nullable sequences. Our success code is just to break to
- # where we set the element in the array. Note that we bump the
- # sequenceWrapLevel around this call so that nested sequence conversions
- # will use different iteration variables.
- global sequenceWrapLevel
- index = "sequenceIdx%d" % sequenceWrapLevel
- sequenceWrapLevel += 1
- innerTemplate = wrapForType(
- type.inner, descriptorProvider,
- {
- 'result': "%s[%s]" % (result, index),
- 'successCode': "break;\n",
- 'jsvalRef': "tmp",
- 'jsvalHandle': "&tmp",
- 'returnsNewObject': returnsNewObject,
- 'exceptionCode': exceptionCode,
- 'obj': "returnArray",
- 'typedArraysAreStructs': typedArraysAreStructs
- })
- sequenceWrapLevel -= 1
- code = fill(
- """
- uint32_t length = ${result}.Length();
- JS::Rooted<JSObject*> returnArray(cx, JS_NewArrayObject(cx, length));
- if (!returnArray) {
- $*{exceptionCode}
- }
- // Scope for 'tmp'
- {
- JS::Rooted<JS::Value> tmp(cx);
- for (uint32_t ${index} = 0; ${index} < length; ++${index}) {
- // Control block to let us common up the JS_DefineElement calls when there
- // are different ways to succeed at wrapping the object.
- do {
- $*{innerTemplate}
- } while (0);
- if (!JS_DefineElement(cx, returnArray, ${index}, tmp,
- JSPROP_ENUMERATE)) {
- $*{exceptionCode}
- }
- }
- }
- $*{set}
- """,
- result=result,
- exceptionCode=exceptionCode,
- index=index,
- innerTemplate=innerTemplate,
- set=setObject("*returnArray"))
- return (code, False)
- if type.isRecord():
- # Now do non-nullable record. Our success code is just to break to
- # where we define the property on the object. Note that we bump the
- # recordWrapLevel around this call so that nested record conversions
- # will use different temp value names.
- global recordWrapLevel
- valueName = "recordValue%d" % recordWrapLevel
- recordWrapLevel += 1
- innerTemplate = wrapForType(
- type.inner, descriptorProvider,
- {
- 'result': valueName,
- 'successCode': "break;\n",
- 'jsvalRef': "tmp",
- 'jsvalHandle': "&tmp",
- 'returnsNewObject': returnsNewObject,
- 'exceptionCode': exceptionCode,
- 'obj': "returnObj",
- 'typedArraysAreStructs': typedArraysAreStructs
- })
- recordWrapLevel -= 1
- if type.keyType.isByteString():
- # There is no length-taking JS_DefineProperty. So to keep
- # things sane with embedded nulls, we want to byte-inflate
- # to an nsAString. The only byte-inflation function we
- # have around is AppendASCIItoUTF16, which luckily doesn't
- # assert anything about the input being ASCII.
- expandedKeyDecl = "NS_ConvertASCIItoUTF16 expandedKey(entry.mKey);\n"
- keyName = "expandedKey"
- else:
- expandedKeyDecl = ""
- keyName = "entry.mKey"
- code = fill(
- """
- JS::Rooted<JSObject*> returnObj(cx, JS_NewPlainObject(cx));
- if (!returnObj) {
- $*{exceptionCode}
- }
- // Scope for 'tmp'
- {
- JS::Rooted<JS::Value> tmp(cx);
- for (auto& entry : ${result}.Entries()) {
- auto& ${valueName} = entry.mValue;
- // Control block to let us common up the JS_DefineUCProperty calls when there
- // are different ways to succeed at wrapping the value.
- do {
- $*{innerTemplate}
- } while (0);
- $*{expandedKeyDecl}
- if (!JS_DefineUCProperty(cx, returnObj,
- ${keyName}.BeginReading(),
- ${keyName}.Length(), tmp,
- JSPROP_ENUMERATE)) {
- $*{exceptionCode}
- }
- }
- }
- $*{set}
- """,
- result=result,
- exceptionCode=exceptionCode,
- valueName=valueName,
- innerTemplate=innerTemplate,
- expandedKeyDecl=expandedKeyDecl,
- keyName=keyName,
- set=setObject("*returnObj"))
- return (code, False)
- if type.isPromise():
- assert not type.nullable()
- # The use of ToJSValue here is a bit annoying because the Promise
- # version is not inlined, but we can't put an inline version in either
- # ToJSValue.h or BindingUtils.h, because Promise.h includes ToJSValue.h
- # and that includes BindingUtils.h, so we'd get an include loop if
- # either of those headers included Promise.h. Trying to write the
- # conversion by hand here is annoying because we'd have to handle
- # the various RefPtr, rawptr, NonNull, etc. cases, which ToJSValue will
- # already handle for us, so we just use the function call.
- return (wrapAndSetPtr("ToJSValue(cx, %s, ${jsvalHandle})" % result),
- False)
- if type.isGeckoInterface() and not type.isCallbackInterface():
- descriptor = descriptorProvider.getDescriptor(type.unroll().inner.identifier.name)
- if type.nullable():
- wrappingCode = ("if (!%s) {\n" % (result) +
- indent(setNull()) +
- "}\n")
- else:
- wrappingCode = ""
- if not descriptor.interface.isExternal():
- if descriptor.wrapperCache:
- wrapMethod = "GetOrCreateDOMReflector"
- wrapArgs = "cx, %s, ${jsvalHandle}" % result
- else:
- wrapMethod = "WrapNewBindingNonWrapperCachedObject"
- wrapArgs = "cx, ${obj}, %s, ${jsvalHandle}" % result
- if isConstructorRetval:
- wrapArgs += ", desiredProto"
- wrap = "%s(%s)" % (wrapMethod, wrapArgs)
- if not descriptor.hasXPConnectImpls:
- # Can only fail to wrap as a new-binding object
- # if they already threw an exception.
- # XXX Assertion disabled for now, see bug 991271.
- failed = ("MOZ_ASSERT(true || JS_IsExceptionPending(cx));\n" +
- exceptionCode)
- else:
- if descriptor.notflattened:
- raise TypeError("%s has XPConnect impls but not flattened; "
- "fallback won't work correctly" %
- descriptor.interface.identifier.name)
- # Try old-style wrapping for bindings which might be XPConnect impls.
- failed = wrapAndSetPtr("HandleNewBindingWrappingFailure(cx, ${obj}, %s, ${jsvalHandle})" % result)
- else:
- if descriptor.notflattened:
- getIID = "&NS_GET_IID(%s), " % descriptor.nativeType
- else:
- getIID = ""
- wrap = "WrapObject(cx, %s, %s${jsvalHandle})" % (result, getIID)
- failed = None
- wrappingCode += wrapAndSetPtr(wrap, failed)
- return (wrappingCode, False)
- if type.isDOMString() or type.isUSVString():
- if type.nullable():
- return (wrapAndSetPtr("xpc::StringToJsval(cx, %s, ${jsvalHandle})" % result), False)
- else:
- return (wrapAndSetPtr("xpc::NonVoidStringToJsval(cx, %s, ${jsvalHandle})" % result), False)
- if type.isByteString():
- if type.nullable():
- return (wrapAndSetPtr("ByteStringToJsval(cx, %s, ${jsvalHandle})" % result), False)
- else:
- return (wrapAndSetPtr("NonVoidByteStringToJsval(cx, %s, ${jsvalHandle})" % result), False)
- if type.isEnum():
- if type.nullable():
- resultLoc = "%s.Value()" % result
- else:
- resultLoc = result
- conversion = fill(
- """
- if (!ToJSValue(cx, ${result}, $${jsvalHandle})) {
- $*{exceptionCode}
- }
- $*{successCode}
- """,
- result=resultLoc,
- exceptionCode=exceptionCode,
- successCode=successCode)
- if type.nullable():
- conversion = CGIfElseWrapper(
- "%s.IsNull()" % result,
- CGGeneric(setNull()),
- CGGeneric(conversion)).define()
- return conversion, False
- if type.isCallback() or type.isCallbackInterface():
- wrapCode = setObject(
- "*GetCallbackFromCallbackObject(%(result)s)",
- wrapAsType=type)
- if type.nullable():
- wrapCode = (
- "if (%(result)s) {\n" +
- indent(wrapCode) +
- "} else {\n" +
- indent(setNull()) +
- "}\n")
- wrapCode = wrapCode % {"result": result}
- return wrapCode, False
- if type.isAny():
- # See comments in GetOrCreateDOMReflector explaining why we need
- # to wrap here.
- # NB: _setValue(..., type-that-is-any) calls JS_WrapValue(), so is fallible
- head = "JS::ExposeValueToActiveJS(%s);\n" % result
- return (head + _setValue(result, wrapAsType=type), False)
- if (type.isObject() or (type.isSpiderMonkeyInterface() and
- not typedArraysAreStructs)):
- # See comments in GetOrCreateDOMReflector explaining why we need
- # to wrap here.
- if type.nullable():
- toValue = "%s"
- setter = setObjectOrNull
- head = """if (%s) {
- JS::ExposeObjectToActiveJS(%s);
- }
- """ % (result, result)
- else:
- toValue = "*%s"
- setter = setObject
- head = "JS::ExposeObjectToActiveJS(%s);\n" % result
- # NB: setObject{,OrNull}(..., some-object-type) calls JS_WrapValue(), so is fallible
- return (head + setter(toValue % result, wrapAsType=type), False)
- if not (type.isUnion() or type.isPrimitive() or type.isDictionary() or
- type.isDate() or
- (type.isSpiderMonkeyInterface() and typedArraysAreStructs)):
- raise TypeError("Need to learn to wrap %s" % type)
- if type.nullable():
- recTemplate, recInfal = getWrapTemplateForType(type.inner, descriptorProvider,
- "%s.Value()" % result, successCode,
- returnsNewObject, exceptionCode,
- typedArraysAreStructs)
- return ("if (%s.IsNull()) {\n" % result +
- indent(setNull()) +
- "}\n" +
- recTemplate, recInfal)
- if type.isSpiderMonkeyInterface():
- assert typedArraysAreStructs
- # See comments in GetOrCreateDOMReflector explaining why we need
- # to wrap here.
- # NB: setObject(..., some-object-type) calls JS_WrapValue(), so is fallible
- return (setObject("*%s.Obj()" % result,
- wrapAsType=type), False)
- if type.isUnion():
- return (wrapAndSetPtr("%s.ToJSVal(cx, ${obj}, ${jsvalHandle})" % result),
- False)
- if type.isDictionary():
- return (wrapAndSetPtr("%s.ToObjectInternal(cx, ${jsvalHandle})" % result),
- False)
- if type.isDate():
- return (wrapAndSetPtr("%s.ToDateObject(cx, ${jsvalHandle})" % result),
- False)
- tag = type.tag()
- if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
- IDLType.Tags.uint16, IDLType.Tags.int32]:
- return (setInt32("int32_t(%s)" % result), True)
- elif tag in [IDLType.Tags.int64, IDLType.Tags.uint64,
- IDLType.Tags.unrestricted_float, IDLType.Tags.float,
- IDLType.Tags.unrestricted_double, IDLType.Tags.double]:
- # XXXbz will cast to double do the "even significand" thing that webidl
- # calls for for 64-bit ints? Do we care?
- return (setDouble("double(%s)" % result), True)
- elif tag == IDLType.Tags.uint32:
- return (setUint32(result), True)
- elif tag == IDLType.Tags.bool:
- return (setBoolean(result), True)
- else:
- raise TypeError("Need to learn to wrap primitive: %s" % type)
- def wrapForType(type, descriptorProvider, templateValues):
- """
- Reflect a C++ value of IDL type "type" into JS. TemplateValues is a dict
- that should contain:
- * 'jsvalRef': something that can have .address() called on it to get a
- JS::Value* and .set() called on it to set it to a JS::Value.
- This can be a JS::MutableHandle<JS::Value> or a
- JS::Rooted<JS::Value>.
- * 'jsvalHandle': something that can be passed to methods taking a
- JS::MutableHandle<JS::Value>. This can be a
- JS::MutableHandle<JS::Value> or a JS::Rooted<JS::Value>*.
- * 'obj' (optional): the name of the variable that contains the JSObject to
- use as a scope when wrapping, if not supplied 'obj'
- will be used as the name
- * 'result' (optional): the name of the variable in which the C++ value is
- stored, if not supplied 'result' will be used as
- the name
- * 'successCode' (optional): the code to run once we have successfully
- done the conversion, if not supplied 'return
- true;' will be used as the code. The
- successCode must ensure that once it runs no
- more of the conversion template will be
- executed (e.g. by doing a 'return' or 'break'
- as appropriate).
- * 'returnsNewObject' (optional): If true, we're wrapping for the return
- value of a [NewObject] method. Assumed
- false if not set.
- * 'exceptionCode' (optional): Code to run when a JS exception is thrown.
- The default is "return false;". The code
- passed here must return.
- * 'isConstructorRetval' (optional): If true, we're wrapping a constructor
- return value.
- """
- wrap = getWrapTemplateForType(
- type, descriptorProvider,
- templateValues.get('result', 'result'),
- templateValues.get('successCode', None),
- templateValues.get('returnsNewObject', False),
- templateValues.get('exceptionCode', "return false;\n"),
- templateValues.get('typedArraysAreStructs', False),
- isConstructorRetval=templateValues.get('isConstructorRetval', False))[0]
- defaultValues = {'obj': 'obj'}
- return string.Template(wrap).substitute(defaultValues, **templateValues)
- def infallibleForMember(member, type, descriptorProvider):
- """
- Determine the fallibility of changing a C++ value of IDL type "type" into
- JS for the given attribute. Apart from returnsNewObject, all the defaults
- are used, since the fallbility does not change based on the boolean values,
- and the template will be discarded.
- CURRENT ASSUMPTIONS:
- We assume that successCode for wrapping up return values cannot contain
- failure conditions.
- """
- return getWrapTemplateForType(type, descriptorProvider, 'result', None,
- memberReturnsNewObject(member), "return false;\n",
- False)[1]
- def leafTypeNeedsCx(type, retVal):
- return (type.isAny() or type.isObject() or
- (retVal and type.isSpiderMonkeyInterface()))
- def leafTypeNeedsScopeObject(type, retVal):
- return retVal and type.isSpiderMonkeyInterface()
- def leafTypeNeedsRooting(type):
- return leafTypeNeedsCx(type, False) or type.isSpiderMonkeyInterface()
- def typeNeedsRooting(type):
- return typeMatchesLambda(type,
- lambda t: leafTypeNeedsRooting(t))
- def typeNeedsCx(type, retVal=False):
- return typeMatchesLambda(type,
- lambda t: leafTypeNeedsCx(t, retVal))
- def typeNeedsScopeObject(type, retVal=False):
- return typeMatchesLambda(type,
- lambda t: leafTypeNeedsScopeObject(t, retVal))
- def typeMatchesLambda(type, func):
- if type is None:
- return False
- if type.nullable():
- return typeMatchesLambda(type.inner, func)
- if type.isSequence() or type.isRecord():
- return typeMatchesLambda(type.inner, func)
- if type.isUnion():
- return any(typeMatchesLambda(t, func) for t in
- type.unroll().flatMemberTypes)
- if type.isDictionary():
- return dictionaryMatchesLambda(type.inner, func)
- return func(type)
- def dictionaryMatchesLambda(dictionary, func):
- return (any(typeMatchesLambda(m.type, func) for m in dictionary.members) or
- (dictionary.parent and dictionaryMatchesLambda(dictionary.parent, func)))
- # Whenever this is modified, please update CGNativeMember.getRetvalInfo as
- # needed to keep the types compatible.
- def getRetvalDeclarationForType(returnType, descriptorProvider,
- isMember=False):
- """
- Returns a tuple containing five things:
- 1) A CGThing for the type of the return value, or None if there is no need
- for a return value.
- 2) A value indicating the kind of ourparam to pass the value as. Valid
- options are None to not pass as an out param at all, "ref" (to pass a
- reference as an out param), and "ptr" (to pass a pointer as an out
- param).
- 3) A CGThing for a tracer for the return value, or None if no tracing is
- needed.
- 4) An argument string to pass to the retval declaration
- constructor or None if there are no arguments.
- 5) The name of a function that needs to be called with the return value
- before using it, or None if no function needs to be called.
- """
- if returnType is None or returnType.isVoid():
- # Nothing to declare
- return None, None, None, None, None
- if returnType.isPrimitive() and returnType.tag() in builtinNames:
- result = CGGeneric(builtinNames[returnType.tag()])
- if returnType.nullable():
- result = CGTemplatedType("Nullable", result)
- return result, None, None, None, None
- if returnType.isDOMString() or returnType.isUSVString():
- if isMember:
- return CGGeneric("nsString"), "ref", None, None, None
- return CGGeneric("DOMString"), "ref", None, None, None
- if returnType.isByteString():
- return CGGeneric("nsCString"), "ref", None, None, None
- if returnType.isEnum():
- result = CGGeneric(returnType.unroll().inner.identifier.name)
- if returnType.nullable():
- result = CGTemplatedType("Nullable", result)
- return result, None, None, None, None
- if returnType.isGeckoInterface() or returnType.isPromise():
- if returnType.isGeckoInterface():
- typeName = descriptorProvider.getDescriptor(
- returnType.unroll().inner.identifier.name).nativeType
- else:
- typeName = "Promise"
- if isMember:
- conversion = None
- result = CGGeneric("StrongPtrForMember<%s>::Type" % typeName)
- else:
- conversion = CGGeneric("StrongOrRawPtr<%s>" % typeName)
- result = CGGeneric("auto")
- return result, None, None, None, conversion
- if returnType.isCallback():
- name = returnType.unroll().callback.identifier.name
- return CGGeneric("RefPtr<%s>" % name), None, None, None, None
- if returnType.isAny():
- if isMember:
- return CGGeneric("JS::Value"), None, None, None, None
- return CGGeneric("JS::Rooted<JS::Value>"), "ptr", None, "cx", None
- if returnType.isObject() or returnType.isSpiderMonkeyInterface():
- if isMember:
- return CGGeneric("JSObject*"), None, None, None, None
- return CGGeneric("JS::Rooted<JSObject*>"), "ptr", None, "cx", None
- if returnType.isSequence():
- nullable = returnType.nullable()
- if nullable:
- returnType = returnType.inner
- result, _, _, _, _ = getRetvalDeclarationForType(returnType.inner,
- descriptorProvider,
- isMember="Sequence")
- # While we have our inner type, set up our rooter, if needed
- if not isMember and typeNeedsRooting(returnType):
- rooter = CGGeneric("SequenceRooter<%s > resultRooter(cx, &result);\n" %
- result.define())
- else:
- rooter = None
- result = CGTemplatedType("nsTArray", result)
- if nullable:
- result = CGTemplatedType("Nullable", result)
- return result, "ref", rooter, None, None
- if returnType.isRecord():
- nullable = returnType.nullable()
- if nullable:
- returnType = returnType.inner
- result, _, _, _, _ = getRetvalDeclarationForType(returnType.inner,
- descriptorProvider,
- isMember="Record")
- # While we have our inner type, set up our rooter, if needed
- if not isMember and typeNeedsRooting(returnType):
- rooter = CGGeneric("RecordRooter<%s> resultRooter(cx, &result);\n" %
- ("nsString, " + result.define()))
- else:
- rooter = None
- result = CGTemplatedType("Record", [recordKeyDeclType(returnType),
- result])
- if nullable:
- result = CGTemplatedType("Nullable", result)
- return result, "ref", rooter, None, None
- if returnType.isDictionary():
- nullable = returnType.nullable()
- dictName = CGDictionary.makeDictionaryName(returnType.unroll().inner)
- result = CGGeneric(dictName)
- if not isMember and typeNeedsRooting(returnType):
- if nullable:
- result = CGTemplatedType("NullableRootedDictionary", result)
- else:
- result = CGTemplatedType("RootedDictionary", result)
- resultArgs = "cx"
- else:
- if nullable:
- result = CGTemplatedType("Nullable", result)
- resultArgs = None
- return result, "ref", None, resultArgs, None
- if returnType.isUnion():
- result = CGGeneric(CGUnionStruct.unionTypeName(returnType.unroll(), True))
- if not isMember and typeNeedsRooting(returnType):
- if returnType.nullable():
- result = CGTemplatedType("NullableRootedUnion", result)
- else:
- result = CGTemplatedType("RootedUnion", result)
- resultArgs = "cx"
- else:
- if returnType.nullable():
- result = CGTemplatedType("Nullable", result)
- resultArgs = None
- return result, "ref", None, resultArgs, None
- if returnType.isDate():
- result = CGGeneric("Date")
- if returnType.nullable():
- result = CGTemplatedType("Nullable", result)
- return result, None, None, None, None
- raise TypeError("Don't know how to declare return value for %s" %
- returnType)
- def needCx(returnType, arguments, extendedAttributes, considerTypes,
- static=False):
- return (not static and considerTypes and
- (typeNeedsCx(returnType, True) or
- any(typeNeedsCx(a.type) for a in arguments)) or
- 'implicitJSContext' in extendedAttributes)
- def needScopeObject(returnType, arguments, extendedAttributes,
- isWrapperCached, considerTypes, isMember):
- """
- isMember should be true if we're dealing with an attribute
- annotated as [StoreInSlot].
- """
- return (considerTypes and not isWrapperCached and
- ((not isMember and typeNeedsScopeObject(returnType, True)) or
- any(typeNeedsScopeObject(a.type) for a in arguments)))
- class CGCallGenerator(CGThing):
- """
- A class to generate an actual call to a C++ object. Assumes that the C++
- object is stored in a variable whose name is given by the |object| argument.
- needsSubjectPrincipal is a boolean indicating whether the call should
- receive the subject nsIPrincipal as argument.
- needsCallerType is a boolean indicating whether the call should receive
- a PrincipalType for the caller.
- isFallible is a boolean indicating whether the call should be fallible.
- resultVar: If the returnType is not void, then the result of the call is
- stored in a C++ variable named by resultVar. The caller is responsible for
- declaring the result variable. If the caller doesn't care about the result
- value, resultVar can be omitted.
- """
- def __init__(self, isFallible, needsSubjectPrincipal, needsCallerType,
- arguments, argsPre, returnType, extendedAttributes, descriptor,
- nativeMethodName, static, object="self", argsPost=[],
- resultVar=None):
- CGThing.__init__(self)
- result, resultOutParam, resultRooter, resultArgs, resultConversion = \
- getRetvalDeclarationForType(returnType, descriptor)
- args = CGList([CGGeneric(arg) for arg in argsPre], ", ")
- for a, name in arguments:
- arg = CGGeneric(name)
- # Now constify the things that need it
- def needsConst(a):
- if a.type.isDictionary():
- return True
- if a.type.isSequence():
- return True
- if a.type.isRecord():
- return True
- # isObject() types are always a JS::Rooted, whether
- # nullable or not, and it turns out a const JS::Rooted
- # is not very helpful at all (in particular, it won't
- # even convert to a JS::Handle).
- # XXX bz Well, why not???
- if a.type.nullable() and not a.type.isObject():
- return True
- if a.type.isString():
- return True
- if a.canHaveMissingValue():
- # This will need an Optional or it's a variadic;
- # in both cases it should be const.
- return True
- if a.type.isUnion():
- return True
- if a.type.isSpiderMonkeyInterface():
- return True
- return False
- if needsConst(a):
- arg = CGWrapper(arg, pre="Constify(", post=")")
- # And convert NonNull<T> to T&
- if (((a.type.isGeckoInterface() or a.type.isCallback() or
- a.type.isPromise()) and
- not a.type.nullable()) or
- a.type.isDOMString()):
- arg = CGWrapper(arg, pre="NonNullHelper(", post=")")
- args.append(arg)
- needResultDecl = False
- # Return values that go in outparams go here
- if resultOutParam is not None:
- if resultVar is None:
- needResultDecl = True
- resultVar = "result"
- if resultOutParam == "ref":
- args.append(CGGeneric(resultVar))
- else:
- assert resultOutParam == "ptr"
- args.append(CGGeneric("&" + resultVar))
- if needsSubjectPrincipal:
- args.append(CGGeneric("subjectPrincipal"))
- if needsCallerType:
- args.append(CGGeneric("callerType"))
- if isFallible:
- args.append(CGGeneric("rv"))
- args.extend(CGGeneric(arg) for arg in argsPost)
- # Build up our actual call
- self.cgRoot = CGList([])
- call = CGGeneric(nativeMethodName)
- if not static:
- call = CGWrapper(call, pre="%s->" % object)
- call = CGList([call, CGWrapper(args, pre="(", post=")")])
- if resultConversion is not None:
- call = CGList([resultConversion, CGWrapper(call, pre="(", post=")")])
- if resultVar is None and result is not None:
- needResultDecl = True
- resultVar = "result"
- if needResultDecl:
- if resultRooter is not None:
- self.cgRoot.prepend(resultRooter)
- if resultArgs is not None:
- resultArgsStr = "(%s)" % resultArgs
- else:
- resultArgsStr = ""
- result = CGWrapper(result, post=(" %s%s" % (resultVar, resultArgsStr)))
- if resultOutParam is None and resultArgs is None:
- call = CGList([result, CGWrapper(call, pre="(", post=")")])
- else:
- self.cgRoot.prepend(CGWrapper(result, post=";\n"))
- if resultOutParam is None:
- call = CGWrapper(call, pre=resultVar + " = ")
- elif result is not None:
- assert resultOutParam is None
- call = CGWrapper(call, pre=resultVar + " = ")
- call = CGWrapper(call, post=";\n")
- self.cgRoot.append(call)
- if needsSubjectPrincipal:
- getPrincipal = dedent(
- """
- JSCompartment* compartment = js::GetContextCompartment(cx);
- MOZ_ASSERT(compartment);
- JSPrincipals* principals = JS_GetCompartmentPrincipals(compartment);
- """)
- if descriptor.interface.isExposedInAnyWorker():
- self.cgRoot.prepend(CGGeneric(fill(
- """
- Maybe<nsIPrincipal*> subjectPrincipal;
- if (NS_IsMainThread()) {
- $*{getPrincipal}
- subjectPrincipal.emplace(nsJSPrincipals::get(principals));
- }
- """,
- getPrincipal=getPrincipal)))
- else:
- self.cgRoot.prepend(CGGeneric(fill(
- """
- $*{getPrincipal}
- // Initializing a nonnull is pretty darn annoying...
- NonNull<nsIPrincipal> subjectPrincipal;
- subjectPrincipal = static_cast<nsIPrincipal*>(nsJSPrincipals::get(principals));
- """,
- getPrincipal=getPrincipal)))
- if needsCallerType:
- # Note that we do not want to use
- # IsCallerChrome/ThreadsafeIsCallerChrome directly because those
- # will pull in the check for UniversalXPConnect, which we ideally
- # don't want to have in the new thing we're doing here. If not
- # NS_IsMainThread(), though, we'll go ahead and call
- # ThreasafeIsCallerChrome(), since that won't mess with
- # UnivesalXPConnect and we don't want to worry about the right
- # worker includes here.
- callerCheck = CGGeneric("callerType = nsContentUtils::IsSystemPrincipal(nsContentUtils::SubjectPrincipal()) ? CallerType::System : CallerType::NonSystem;\n")
- if descriptor.interface.isExposedInAnyWorker():
- callerCheck = CGIfElseWrapper(
- "NS_IsMainThread()",
- callerCheck,
- CGGeneric("callerType = nsContentUtils::ThreadsafeIsCallerChrome() ? CallerType::System : CallerType::NonSystem;\n"));
- self.cgRoot.prepend(callerCheck)
- self.cgRoot.prepend(CGGeneric("CallerType callerType;\n"))
- if isFallible:
- self.cgRoot.prepend(CGGeneric("binding_detail::FastErrorResult rv;\n"))
- self.cgRoot.append(CGGeneric(dedent(
- """
- if (MOZ_UNLIKELY(rv.MaybeSetPendingException(cx))) {
- return false;
- }
- """)))
- self.cgRoot.append(CGGeneric("MOZ_ASSERT(!JS_IsExceptionPending(cx));\n"))
- def define(self):
- return self.cgRoot.define()
- def getUnionMemberName(type):
- # Promises can't be in unions, because they're not distinguishable
- # from anything else.
- assert not type.isPromise()
- if type.isGeckoInterface():
- return type.inner.identifier.name
- if type.isEnum():
- return type.inner.identifier.name
- return type.name
- class MethodNotNewObjectError(Exception):
- def __init__(self, typename):
- self.typename = typename
- # A counter for making sure that when we're wrapping up things in
- # nested sequences we don't use the same variable name to iterate over
- # different sequences.
- sequenceWrapLevel = 0
- recordWrapLevel = 0
- def wrapTypeIntoCurrentCompartment(type, value, isMember=True):
- """
- Take the thing named by "value" and if it contains "any",
- "object", or spidermonkey-interface types inside return a CGThing
- that will wrap them into the current compartment.
- """
- if type.isAny():
- assert not type.nullable()
- if isMember:
- value = "JS::MutableHandle<JS::Value>::fromMarkedLocation(&%s)" % value
- else:
- value = "&" + value
- return CGGeneric("if (!JS_WrapValue(cx, %s)) {\n"
- " return false;\n"
- "}\n" % value)
- if type.isObject():
- if isMember:
- value = "JS::MutableHandle<JSObject*>::fromMarkedLocation(&%s)" % value
- else:
- value = "&" + value
- return CGGeneric("if (!JS_WrapObject(cx, %s)) {\n"
- " return false;\n"
- "}\n" % value)
- if type.isSpiderMonkeyInterface():
- origValue = value
- if type.nullable():
- value = "%s.Value()" % value
- wrapCode = CGGeneric("if (!%s.WrapIntoNewCompartment(cx)) {\n"
- " return false;\n"
- "}\n" % value)
- if type.nullable():
- wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue)
- return wrapCode
- if type.isSequence():
- origValue = value
- origType = type
- if type.nullable():
- type = type.inner
- value = "%s.Value()" % value
- global sequenceWrapLevel
- index = "indexName%d" % sequenceWrapLevel
- sequenceWrapLevel += 1
- wrapElement = wrapTypeIntoCurrentCompartment(type.inner,
- "%s[%s]" % (value, index))
- sequenceWrapLevel -= 1
- if not wrapElement:
- return None
- wrapCode = CGWrapper(CGIndenter(wrapElement),
- pre=("for (uint32_t %s = 0; %s < %s.Length(); ++%s) {\n" %
- (index, index, value, index)),
- post="}\n")
- if origType.nullable():
- wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue)
- return wrapCode
- if type.isRecord():
- origType = type
- if type.nullable():
- type = type.inner
- recordRef = "%s.Value()" % value
- else:
- recordRef = value
- global recordWrapLevel
- entryRef = "mapEntry%d" % recordWrapLevel
- recordWrapLevel += 1
- wrapElement = wrapTypeIntoCurrentCompartment(type.inner,
- "%s.mValue" % entryRef)
- recordWrapLevel -= 1
- if not wrapElement:
- return None
- wrapCode = CGWrapper(CGIndenter(wrapElement),
- pre=("for (auto& %s : %s.Entries()) {\n" %
- (entryRef, recordRef)),
- post="}\n")
- if origType.nullable():
- wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % value)
- return wrapCode
- if type.isDictionary():
- assert not type.nullable()
- myDict = type.inner
- memberWraps = []
- while myDict:
- for member in myDict.members:
- memberWrap = wrapArgIntoCurrentCompartment(
- member,
- "%s.%s" % (value, CGDictionary.makeMemberName(member.identifier.name)))
- if memberWrap:
- memberWraps.append(memberWrap)
- myDict = myDict.parent
- return CGList(memberWraps) if len(memberWraps) != 0 else None
- if type.isUnion():
- memberWraps = []
- if type.nullable():
- type = type.inner
- value = "%s.Value()" % value
- for member in type.flatMemberTypes:
- memberName = getUnionMemberName(member)
- memberWrap = wrapTypeIntoCurrentCompartment(
- member, "%s.GetAs%s()" % (value, memberName))
- if memberWrap:
- memberWrap = CGIfWrapper(
- memberWrap, "%s.Is%s()" % (value, memberName))
- memberWraps.append(memberWrap)
- return CGList(memberWraps, "else ") if len(memberWraps) != 0 else None
- if (type.isString() or type.isPrimitive() or type.isEnum() or
- type.isGeckoInterface() or type.isCallback() or type.isDate() or
- type.isPromise()):
- # All of these don't need wrapping.
- return None
- raise TypeError("Unknown type; we don't know how to wrap it in constructor "
- "arguments: %s" % type)
- def wrapArgIntoCurrentCompartment(arg, value, isMember=True):
- """
- As wrapTypeIntoCurrentCompartment but handles things being optional
- """
- origValue = value
- isOptional = arg.canHaveMissingValue()
- if isOptional:
- value = value + ".Value()"
- wrap = wrapTypeIntoCurrentCompartment(arg.type, value, isMember)
- if wrap and isOptional:
- wrap = CGIfWrapper(wrap, "%s.WasPassed()" % origValue)
- return wrap
- def needsContainsHack(m):
- return m.getExtendedAttribute("ReturnValueNeedsContainsHack")
- def needsCallerType(m):
- return m.getExtendedAttribute("NeedsCallerType")
- class CGPerSignatureCall(CGThing):
- """
- This class handles the guts of generating code for a particular
- call signature. A call signature consists of four things:
- 1) A return type, which can be None to indicate that there is no
- actual return value (e.g. this is an attribute setter) or an
- IDLType if there's an IDL type involved (including |void|).
- 2) An argument list, which is allowed to be empty.
- 3) A name of a native method to call.
- 4) Whether or not this method is static. Note that this only controls how
- the method is called (|self->nativeMethodName(...)| vs
- |nativeMethodName(...)|).
- We also need to know whether this is a method or a getter/setter
- to do error reporting correctly.
- The idlNode parameter can be either a method or an attr. We can query
- |idlNode.identifier| in both cases, so we can be agnostic between the two.
- """
- # XXXbz For now each entry in the argument list is either an
- # IDLArgument or a FakeArgument, but longer-term we may want to
- # have ways of flagging things like JSContext* or optional_argc in
- # there.
- def __init__(self, returnType, arguments, nativeMethodName, static,
- descriptor, idlNode, argConversionStartsAt=0, getter=False,
- setter=False, isConstructor=False, useCounterName=None,
- resultVar=None, objectName="obj"):
- assert idlNode.isMethod() == (not getter and not setter)
- assert idlNode.isAttr() == (getter or setter)
- # Constructors are always static
- assert not isConstructor or static
- CGThing.__init__(self)
- self.returnType = returnType
- self.descriptor = descriptor
- self.idlNode = idlNode
- self.extendedAttributes = descriptor.getExtendedAttributes(idlNode,
- getter=getter,
- setter=setter)
- self.arguments = arguments
- self.argCount = len(arguments)
- self.isConstructor = isConstructor
- cgThings = []
- # Here, we check if the current getter, setter, method, interface or
- # inherited interfaces have the UnsafeInPrerendering extended attribute
- # and if so, we add a check to make sure it is safe.
- if (idlNode.getExtendedAttribute("UnsafeInPrerendering") or
- descriptor.interface.getExtendedAttribute("UnsafeInPrerendering") or
- any(i.getExtendedAttribute("UnsafeInPrerendering")
- for i in descriptor.interface.getInheritedInterfaces())):
- cgThings.append(CGGeneric(dedent(
- """
- if (!mozilla::dom::EnforceNotInPrerendering(cx, obj)) {
- // Return false from the JSNative in order to trigger
- // an uncatchable exception.
- MOZ_ASSERT(!JS_IsExceptionPending(cx));
- return false;
- }
- """)))
- deprecated = (idlNode.getExtendedAttribute("Deprecated") or
- (idlNode.isStatic() and descriptor.interface.getExtendedAttribute("Deprecated")))
- if deprecated:
- cgThings.append(CGGeneric(dedent(
- """
- DeprecationWarning(cx, obj, nsIDocument::e%s);
- """ % deprecated[0])))
- lenientFloatCode = None
- if (idlNode.getExtendedAttribute('LenientFloat') is not None and
- (setter or idlNode.isMethod())):
- cgThings.append(CGGeneric(dedent(
- """
- bool foundNonFiniteFloat = false;
- """)))
- lenientFloatCode = "foundNonFiniteFloat = true;\n"
- argsPre = []
- if idlNode.isStatic():
- # If we're a constructor, the GlobalObject struct will be created in
- # CGClassConstructor.
- if not isConstructor:
- cgThings.append(CGGeneric(dedent(
- """
- GlobalObject global(cx, xpc::XrayAwareCalleeGlobal(obj));
- if (global.Failed()) {
- return false;
- }
- """)))
- argsPre.append("global")
- if isConstructor and idlNode.isHTMLConstructor():
- argsPre.extend(["args", "desiredProto"])
- # For JS-implemented interfaces we do not want to base the
- # needsCx decision on the types involved, just on our extended
- # attributes. Also, JSContext is not needed for the static case
- # since GlobalObject already contains the context.
- needsCx = needCx(returnType, arguments, self.extendedAttributes,
- not descriptor.interface.isJSImplemented(), static)
- if needsCx:
- argsPre.append("cx")
- needsUnwrap = False
- argsPost = []
- if isConstructor:
- needsUnwrap = True
- needsUnwrappedVar = False
- unwrappedVar = "obj"
- elif descriptor.interface.isJSImplemented():
- if not idlNode.isStatic():
- needsUnwrap = True
- needsUnwrappedVar = True
- argsPost.append("js::GetObjectCompartment(unwrappedObj ? *unwrappedObj : obj)")
- elif needScopeObject(returnType, arguments, self.extendedAttributes,
- descriptor.wrapperCache, True,
- idlNode.getExtendedAttribute("StoreInSlot")):
- needsUnwrap = True
- needsUnwrappedVar = True
- argsPre.append("unwrappedObj ? *unwrappedObj : obj")
- if needsUnwrap and needsUnwrappedVar:
- # We cannot assign into obj because it's a Handle, not a
- # MutableHandle, so we need a separate Rooted.
- cgThings.append(CGGeneric("Maybe<JS::Rooted<JSObject*> > unwrappedObj;\n"))
- unwrappedVar = "unwrappedObj.ref()"
- if idlNode.isMethod() and idlNode.isLegacycaller():
- # If we can have legacycaller with identifier, we can't
- # just use the idlNode to determine whether we're
- # generating code for the legacycaller or not.
- assert idlNode.isIdentifierLess()
- # Pass in our thisVal
- argsPre.append("args.thisv()")
- ourName = "%s.%s" % (descriptor.interface.identifier.name,
- idlNode.identifier.name)
- if idlNode.isMethod():
- argDescription = "argument %(index)d of " + ourName
- elif setter:
- argDescription = "value being assigned to %s" % ourName
- else:
- assert self.argCount == 0
- if needsUnwrap:
- # It's very important that we construct our unwrappedObj, if we need
- # to do it, before we might start setting up Rooted things for our
- # arguments, so that we don't violate the stack discipline Rooted
- # depends on.
- cgThings.append(CGGeneric(
- "bool objIsXray = xpc::WrapperFactory::IsXrayWrapper(obj);\n"))
- if needsUnwrappedVar:
- cgThings.append(CGIfWrapper(
- CGGeneric("unwrappedObj.emplace(cx, obj);\n"),
- "objIsXray"))
- for i in range(argConversionStartsAt, self.argCount):
- cgThings.append(
- CGArgumentConverter(arguments[i], i, self.descriptor,
- argDescription % {"index": i + 1},
- idlNode, invalidEnumValueFatal=not setter,
- lenientFloatCode=lenientFloatCode))
- # Now that argument processing is done, enforce the LenientFloat stuff
- if lenientFloatCode:
- if setter:
- foundNonFiniteFloatBehavior = "return true;\n"
- else:
- assert idlNode.isMethod()
- foundNonFiniteFloatBehavior = dedent(
- """
- args.rval().setUndefined();
- return true;
- """)
- cgThings.append(CGGeneric(fill(
- """
- if (foundNonFiniteFloat) {
- $*{returnSteps}
- }
- """,
- returnSteps=foundNonFiniteFloatBehavior)))
- if needsUnwrap:
- # Something depends on having the unwrapped object, so unwrap it now.
- xraySteps = []
- # XXXkhuey we should be able to MOZ_ASSERT that ${obj} is
- # not null.
- xraySteps.append(
- CGGeneric(fill(
- """
- ${obj} = js::CheckedUnwrap(${obj});
- if (!${obj}) {
- return false;
- }
- """,
- obj=unwrappedVar)))
- if isConstructor:
- # If we're called via an xray, we need to enter the underlying
- # object's compartment and then wrap up all of our arguments into
- # that compartment as needed. This is all happening after we've
- # already done the conversions from JS values to WebIDL (C++)
- # values, so we only need to worry about cases where there are 'any'
- # or 'object' types, or other things that we represent as actual
- # JSAPI types, present. Effectively, we're emulating a
- # CrossCompartmentWrapper, but working with the C++ types, not the
- # original list of JS::Values.
- cgThings.append(CGGeneric("Maybe<JSAutoCompartment> ac;\n"))
- xraySteps.append(CGGeneric("ac.emplace(cx, obj);\n"))
- xraySteps.append(CGGeneric(dedent(
- """
- if (!JS_WrapObject(cx, &desiredProto)) {
- return false;
- }
- """)))
- xraySteps.extend(
- wrapArgIntoCurrentCompartment(arg, argname, isMember=False)
- for arg, argname in self.getArguments())
- cgThings.append(
- CGIfWrapper(CGList(xraySteps),
- "objIsXray"))
- if (idlNode.getExtendedAttribute('CEReactions') is not None and
- not getter):
- cgThings.append(CGGeneric(dedent(
- """
- Maybe<AutoCEReaction> ceReaction;
- DocGroup* docGroup = self->GetDocGroup();
- if (docGroup) {
- ceReaction.emplace(docGroup->CustomElementReactionsStack(), cx);
- }
- """)))
- # If this is a method that was generated by a maplike/setlike
- # interface, use the maplike/setlike generator to fill in the body.
- # Otherwise, use CGCallGenerator to call the native method.
- if idlNode.isMethod() and idlNode.isMaplikeOrSetlikeOrIterableMethod():
- if (idlNode.maplikeOrSetlikeOrIterable.isMaplike() or
- idlNode.maplikeOrSetlikeOrIterable.isSetlike()):
- cgThings.append(CGMaplikeOrSetlikeMethodGenerator(descriptor,
- idlNode.maplikeOrSetlikeOrIterable,
- idlNode.identifier.name))
- else:
- cgThings.append(CGIterableMethodGenerator(descriptor,
- idlNode.maplikeOrSetlikeOrIterable,
- idlNode.identifier.name))
- else:
- cgThings.append(CGCallGenerator(
- self.isFallible(),
- idlNode.getExtendedAttribute('NeedsSubjectPrincipal'),
- needsCallerType(idlNode),
- self.getArguments(), argsPre, returnType,
- self.extendedAttributes, descriptor,
- nativeMethodName,
- static, argsPost=argsPost, resultVar=resultVar))
- if useCounterName:
- # Generate a telemetry call for when [UseCounter] is used.
- code = "SetDocumentAndPageUseCounter(cx, obj, eUseCounter_%s);\n" % useCounterName
- cgThings.append(CGGeneric(code))
- self.cgRoot = CGList(cgThings)
- def getArguments(self):
- return [(a, "arg" + str(i)) for i, a in enumerate(self.arguments)]
- def isFallible(self):
- return 'infallible' not in self.extendedAttributes
- def wrap_return_value(self):
- wrapCode = ""
- returnsNewObject = memberReturnsNewObject(self.idlNode)
- if (returnsNewObject and
- (self.returnType.isGeckoInterface() or
- self.returnType.isPromise())):
- wrapCode += dedent(
- """
- static_assert(!IsPointer<decltype(result)>::value,
- "NewObject implies that we need to keep the object alive with a strong reference.");
- """)
- setSlot = self.idlNode.isAttr() and self.idlNode.slotIndices is not None
- if setSlot:
- # For attributes in slots, we want to do some
- # post-processing once we've wrapped them.
- successCode = "break;\n"
- else:
- successCode = None
- resultTemplateValues = {
- 'jsvalRef': 'args.rval()',
- 'jsvalHandle': 'args.rval()',
- 'returnsNewObject': returnsNewObject,
- 'isConstructorRetval': self.isConstructor,
- 'successCode': successCode,
- # 'obj' in this dictionary is the thing whose compartment we are
- # trying to do the to-JS conversion in. We're going to put that
- # thing in a variable named "conversionScope" if setSlot is true.
- # Otherwise, just use "obj" for lack of anything better.
- 'obj': "conversionScope" if setSlot else "obj"
- }
- try:
- wrapCode += wrapForType(self.returnType, self.descriptor, resultTemplateValues)
- except MethodNotNewObjectError, err:
- assert not returnsNewObject
- raise TypeError("%s being returned from non-NewObject method or property %s.%s" %
- (err.typename,
- self.descriptor.interface.identifier.name,
- self.idlNode.identifier.name))
- if setSlot:
- # When using a slot on the Xray expando, we need to make sure that
- # our initial conversion to a JS::Value is done in the caller
- # compartment. When using a slot on our reflector, we want to do
- # the conversion in the compartment of that reflector (that is,
- # slotStorage). In both cases we want to make sure that we finally
- # set up args.rval() to be in the caller compartment. We also need
- # to make sure that the conversion steps happen inside a do/while
- # that they can break out of on success.
- #
- # Of course we always have to wrap the value into the slotStorage
- # compartment before we store it in slotStorage.
- # postConversionSteps are the steps that run while we're still in
- # the compartment we do our conversion in but after we've finished
- # the initial conversion into args.rval().
- postConversionSteps = ""
- if needsContainsHack(self.idlNode):
- # Define a .contains on the object that has the same value as
- # .includes; needed for backwards compat in extensions as we
- # migrate some DOMStringLists to FrozenArray.
- postConversionSteps += dedent(
- """
- if (args.rval().isObject() && nsContentUtils::ThreadsafeIsCallerChrome()) {
- JS::Rooted<JSObject*> rvalObj(cx, &args.rval().toObject());
- JS::Rooted<JS::Value> includesVal(cx);
- if (!JS_GetProperty(cx, rvalObj, "includes", &includesVal) ||
- !JS_DefineProperty(cx, rvalObj, "contains", includesVal, JSPROP_ENUMERATE)) {
- return false;
- }
- }
- """)
- if self.idlNode.getExtendedAttribute("Frozen"):
- assert self.idlNode.type.isSequence() or self.idlNode.type.isDictionary()
- freezeValue = CGGeneric(
- "JS::Rooted<JSObject*> rvalObj(cx, &args.rval().toObject());\n"
- "if (!JS_FreezeObject(cx, rvalObj)) {\n"
- " return false;\n"
- "}\n")
- if self.idlNode.type.nullable():
- freezeValue = CGIfWrapper(freezeValue,
- "args.rval().isObject()")
- postConversionSteps += freezeValue.define()
- # slotStorageSteps are steps that run once we have entered the
- # slotStorage compartment.
- slotStorageSteps= fill(
- """
- // Make a copy so that we don't do unnecessary wrapping on args.rval().
- JS::Rooted<JS::Value> storedVal(cx, args.rval());
- if (!${maybeWrap}(cx, &storedVal)) {
- return false;
- }
- js::SetReservedSlot(slotStorage, slotIndex, storedVal);
- """,
- maybeWrap=getMaybeWrapValueFuncForType(self.idlNode.type))
- checkForXray = mayUseXrayExpandoSlots(self.descriptor, self.idlNode)
- # For the case of Cached attributes, go ahead and preserve our
- # wrapper if needed. We need to do this because otherwise the
- # wrapper could get garbage-collected and the cached value would
- # suddenly disappear, but the whole premise of cached values is that
- # they never change without explicit action on someone's part. We
- # don't do this for StoreInSlot, since those get dealt with during
- # wrapper setup, and failure would involve us trying to clear an
- # already-preserved wrapper.
- if (self.idlNode.getExtendedAttribute("Cached") and
- self.descriptor.wrapperCache):
- preserveWrapper = dedent(
- """
- PreserveWrapper(self);
- """)
- if checkForXray:
- preserveWrapper = fill(
- """
- if (!isXray) {
- // In the Xray case we don't need to do this, because getting the
- // expando object already preserved our wrapper.
- $*{preserveWrapper}
- }
- """,
- preserveWrapper=preserveWrapper)
- slotStorageSteps += preserveWrapper
- if checkForXray:
- conversionScope = "isXray ? obj : slotStorage"
- else:
- conversionScope = "slotStorage"
- wrapCode = fill(
- """
- {
- JS::Rooted<JSObject*> conversionScope(cx, ${conversionScope});
- JSAutoCompartment ac(cx, conversionScope);
- do { // block we break out of when done wrapping
- $*{wrapCode}
- } while (0);
- $*{postConversionSteps}
- }
- { // And now store things in the compartment of our slotStorage.
- JSAutoCompartment ac(cx, slotStorage);
- $*{slotStorageSteps}
- }
- // And now make sure args.rval() is in the caller compartment
- return ${maybeWrap}(cx, args.rval());
- """,
- conversionScope=conversionScope,
- wrapCode=wrapCode,
- postConversionSteps=postConversionSteps,
- slotStorageSteps=slotStorageSteps,
- maybeWrap=getMaybeWrapValueFuncForType(self.idlNode.type))
- return wrapCode
- def define(self):
- return (self.cgRoot.define() + self.wrap_return_value())
- class CGSwitch(CGList):
- """
- A class to generate code for a switch statement.
- Takes three constructor arguments: an expression, a list of cases,
- and an optional default.
- Each case is a CGCase. The default is a CGThing for the body of
- the default case, if any.
- """
- def __init__(self, expression, cases, default=None):
- CGList.__init__(self, [CGIndenter(c) for c in cases])
- self.prepend(CGGeneric("switch (" + expression + ") {\n"))
- if default is not None:
- self.append(
- CGIndenter(
- CGWrapper(
- CGIndenter(default),
- pre="default: {\n",
- post=" break;\n}\n")))
- self.append(CGGeneric("}\n"))
- class CGCase(CGList):
- """
- A class to generate code for a case statement.
- Takes three constructor arguments: an expression, a CGThing for
- the body (allowed to be None if there is no body), and an optional
- argument (defaulting to False) for whether to fall through.
- """
- def __init__(self, expression, body, fallThrough=False):
- CGList.__init__(self, [])
- self.append(CGGeneric("case " + expression + ": {\n"))
- bodyList = CGList([body])
- if fallThrough:
- bodyList.append(CGGeneric("MOZ_FALLTHROUGH;\n"))
- else:
- bodyList.append(CGGeneric("break;\n"))
- self.append(CGIndenter(bodyList))
- self.append(CGGeneric("}\n"))
- class CGMethodCall(CGThing):
- """
- A class to generate selection of a method signature from a set of
- signatures and generation of a call to that signature.
- """
- def __init__(self, nativeMethodName, static, descriptor, method,
- isConstructor=False, constructorName=None):
- CGThing.__init__(self)
- if isConstructor:
- assert constructorName is not None
- methodName = constructorName
- else:
- methodName = "%s.%s" % (descriptor.interface.identifier.name, method.identifier.name)
- argDesc = "argument %d of " + methodName
- if method.getExtendedAttribute("UseCounter"):
- useCounterName = methodName.replace(".", "_")
- else:
- useCounterName = None
- if method.isStatic():
- nativeType = descriptor.nativeType
- staticTypeOverride = PropertyDefiner.getStringAttr(method, "StaticClassOverride")
- if (staticTypeOverride):
- nativeType = staticTypeOverride
- nativeMethodName = "%s::%s" % (nativeType, nativeMethodName)
- def requiredArgCount(signature):
- arguments = signature[1]
- if len(arguments) == 0:
- return 0
- requiredArgs = len(arguments)
- while requiredArgs and arguments[requiredArgs-1].optional:
- requiredArgs -= 1
- return requiredArgs
- def getPerSignatureCall(signature, argConversionStartsAt=0):
- return CGPerSignatureCall(signature[0], signature[1],
- nativeMethodName, static, descriptor,
- method,
- argConversionStartsAt=argConversionStartsAt,
- isConstructor=isConstructor,
- useCounterName=useCounterName)
- signatures = method.signatures()
- if len(signatures) == 1:
- # Special case: we can just do a per-signature method call
- # here for our one signature and not worry about switching
- # on anything.
- signature = signatures[0]
- self.cgRoot = CGList([getPerSignatureCall(signature)])
- requiredArgs = requiredArgCount(signature)
- # Skip required arguments check for maplike/setlike interfaces, as
- # they can have arguments which are not passed, and are treated as
- # if undefined had been explicitly passed.
- if requiredArgs > 0 and not method.isMaplikeOrSetlikeOrIterableMethod():
- code = fill(
- """
- if (MOZ_UNLIKELY(args.length() < ${requiredArgs})) {
- return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${methodName}");
- }
- """,
- requiredArgs=requiredArgs,
- methodName=methodName)
- self.cgRoot.prepend(CGGeneric(code))
- return
- # Need to find the right overload
- maxArgCount = method.maxArgCount
- allowedArgCounts = method.allowedArgCounts
- argCountCases = []
- for argCountIdx, argCount in enumerate(allowedArgCounts):
- possibleSignatures = method.signaturesForArgCount(argCount)
- # Try to optimize away cases when the next argCount in the list
- # will have the same code as us; if it does, we can fall through to
- # that case.
- if argCountIdx+1 < len(allowedArgCounts):
- nextPossibleSignatures = method.signaturesForArgCount(allowedArgCounts[argCountIdx+1])
- else:
- nextPossibleSignatures = None
- if possibleSignatures == nextPossibleSignatures:
- # Same set of signatures means we better have the same
- # distinguishing index. So we can in fact just fall through to
- # the next case here.
- assert (len(possibleSignatures) == 1 or
- (method.distinguishingIndexForArgCount(argCount) ==
- method.distinguishingIndexForArgCount(allowedArgCounts[argCountIdx+1])))
- argCountCases.append(CGCase(str(argCount), None, True))
- continue
- if len(possibleSignatures) == 1:
- # easy case!
- signature = possibleSignatures[0]
- argCountCases.append(
- CGCase(str(argCount), getPerSignatureCall(signature)))
- continue
- distinguishingIndex = method.distinguishingIndexForArgCount(argCount)
- def distinguishingArgument(signature):
- args = signature[1]
- if distinguishingIndex < len(args):
- return args[distinguishingIndex]
- assert args[-1].variadic
- return args[-1]
- def distinguishingType(signature):
- return distinguishingArgument(signature).type
- for sig in possibleSignatures:
- # We should not have "any" args at distinguishingIndex,
- # since we have multiple possible signatures remaining,
- # but "any" is never distinguishable from anything else.
- assert not distinguishingType(sig).isAny()
- # We can't handle unions at the distinguishing index.
- if distinguishingType(sig).isUnion():
- raise TypeError("No support for unions as distinguishing "
- "arguments yet: %s" %
- distinguishingArgument(sig).location)
- # We don't support variadics as the distinguishingArgument yet.
- # If you want to add support, consider this case:
- #
- # void(long... foo);
- # void(long bar, Int32Array baz);
- #
- # in which we have to convert argument 0 to long before picking
- # an overload... but all the variadic stuff needs to go into a
- # single array in case we pick that overload, so we have to have
- # machinery for converting argument 0 to long and then either
- # placing it in the variadic bit or not. Or something. We may
- # be able to loosen this restriction if the variadic arg is in
- # fact at distinguishingIndex, perhaps. Would need to
- # double-check.
- if distinguishingArgument(sig).variadic:
- raise TypeError("No support for variadics as distinguishing "
- "arguments yet: %s" %
- distinguishingArgument(sig).location)
- # Convert all our arguments up to the distinguishing index.
- # Doesn't matter which of the possible signatures we use, since
- # they all have the same types up to that point; just use
- # possibleSignatures[0]
- caseBody = [CGArgumentConverter(possibleSignatures[0][1][i],
- i, descriptor,
- argDesc % (i + 1), method)
- for i in range(0, distinguishingIndex)]
- # Select the right overload from our set.
- distinguishingArg = "args[%d]" % distinguishingIndex
- def tryCall(signature, indent, isDefinitelyObject=False,
- isNullOrUndefined=False):
- assert not isDefinitelyObject or not isNullOrUndefined
- assert isDefinitelyObject or isNullOrUndefined
- if isDefinitelyObject:
- failureCode = "break;\n"
- else:
- failureCode = None
- type = distinguishingType(signature)
- # The argument at index distinguishingIndex can't possibly be
- # unset here, because we've already checked that argc is large
- # enough that we can examine this argument. But note that we
- # still want to claim that optional arguments are optional, in
- # case undefined was passed in.
- argIsOptional = distinguishingArgument(signature).canHaveMissingValue()
- testCode = instantiateJSToNativeConversion(
- getJSToNativeConversionInfo(type, descriptor,
- failureCode=failureCode,
- isDefinitelyObject=isDefinitelyObject,
- isNullOrUndefined=isNullOrUndefined,
- isOptional=argIsOptional,
- sourceDescription=(argDesc % (distinguishingIndex + 1))),
- {
- "declName": "arg%d" % distinguishingIndex,
- "holderName": ("arg%d" % distinguishingIndex) + "_holder",
- "val": distinguishingArg,
- "obj": "obj",
- "haveValue": "args.hasDefined(%d)" % distinguishingIndex,
- "passedToJSImpl": toStringBool(isJSImplementedDescriptor(descriptor))
- },
- checkForValue=argIsOptional)
- caseBody.append(CGIndenter(testCode, indent))
- # If we got this far, we know we unwrapped to the right
- # C++ type, so just do the call. Start conversion with
- # distinguishingIndex + 1, since we already converted
- # distinguishingIndex.
- caseBody.append(CGIndenter(
- getPerSignatureCall(signature, distinguishingIndex + 1),
- indent))
- def hasConditionalConversion(type):
- """
- Return whether the argument conversion for this type will be
- conditional on the type of incoming JS value. For example, for
- interface types the conversion is conditional on the incoming
- value being isObject().
- For the types for which this returns false, we do not have to
- output extra isUndefined() or isNullOrUndefined() cases, because
- null/undefined values will just fall through into our
- unconditional conversion.
- """
- if type.isString() or type.isEnum():
- return False
- if type.isBoolean():
- distinguishingTypes = (distinguishingType(s) for s in
- possibleSignatures)
- return any(t.isString() or t.isEnum() or t.isNumeric()
- for t in distinguishingTypes)
- if type.isNumeric():
- distinguishingTypes = (distinguishingType(s) for s in
- possibleSignatures)
- return any(t.isString() or t.isEnum()
- for t in distinguishingTypes)
- return True
- def needsNullOrUndefinedCase(type):
- """
- Return true if the type needs a special isNullOrUndefined() case
- """
- return ((type.nullable() and
- hasConditionalConversion(type)) or
- type.isDictionary())
- # First check for undefined and optional distinguishing arguments
- # and output a special branch for that case. Note that we don't
- # use distinguishingArgument here because we actualy want to
- # exclude variadic arguments. Also note that we skip this check if
- # we plan to output a isNullOrUndefined() special case for this
- # argument anyway, since that will subsume our isUndefined() check.
- # This is safe, because there can be at most one nullable
- # distinguishing argument, so if we're it we'll definitely get
- # picked up by the nullable handling. Also, we can skip this check
- # if the argument has an unconditional conversion later on.
- undefSigs = [s for s in possibleSignatures if
- distinguishingIndex < len(s[1]) and
- s[1][distinguishingIndex].optional and
- hasConditionalConversion(s[1][distinguishingIndex].type) and
- not needsNullOrUndefinedCase(s[1][distinguishingIndex].type)]
- # Can't have multiple signatures with an optional argument at the
- # same index.
- assert len(undefSigs) < 2
- if len(undefSigs) > 0:
- caseBody.append(CGGeneric("if (%s.isUndefined()) {\n" %
- distinguishingArg))
- tryCall(undefSigs[0], 2, isNullOrUndefined=True)
- caseBody.append(CGGeneric("}\n"))
- # Next, check for null or undefined. That means looking for
- # nullable arguments at the distinguishing index and outputting a
- # separate branch for them. But if the nullable argument has an
- # unconditional conversion, we don't need to do that. The reason
- # for that is that at most one argument at the distinguishing index
- # is nullable (since two nullable arguments are not
- # distinguishable), and null/undefined values will always fall
- # through to the unconditional conversion we have, if any, since
- # they will fail whatever the conditions on the input value are for
- # our other conversions.
- nullOrUndefSigs = [s for s in possibleSignatures
- if needsNullOrUndefinedCase(distinguishingType(s))]
- # Can't have multiple nullable types here
- assert len(nullOrUndefSigs) < 2
- if len(nullOrUndefSigs) > 0:
- caseBody.append(CGGeneric("if (%s.isNullOrUndefined()) {\n" %
- distinguishingArg))
- tryCall(nullOrUndefSigs[0], 2, isNullOrUndefined=True)
- caseBody.append(CGGeneric("}\n"))
- # Now check for distinguishingArg being various kinds of objects.
- # The spec says to check for the following things in order:
- # 1) A platform object that's not a platform array object, being
- # passed to an interface or "object" arg.
- # 2) A Date object being passed to a Date or "object" arg.
- # 3) A RegExp object being passed to a RegExp or "object" arg.
- # 4) A callable object being passed to a callback or "object" arg.
- # 5) An iterable object being passed to a sequence arg.
- # 6) Any non-Date and non-RegExp object being passed to a
- # array or callback interface or dictionary or
- # "object" arg.
- # First grab all the overloads that have a non-callback interface
- # (which includes typed arrays and arraybuffers) at the
- # distinguishing index. We can also include the ones that have an
- # "object" here, since if those are present no other object-typed
- # argument will be.
- objectSigs = [
- s for s in possibleSignatures
- if (distinguishingType(s).isObject() or
- distinguishingType(s).isNonCallbackInterface())]
- # And all the overloads that take Date
- objectSigs.extend(s for s in possibleSignatures
- if distinguishingType(s).isDate())
- # And all the overloads that take callbacks
- objectSigs.extend(s for s in possibleSignatures
- if distinguishingType(s).isCallback())
- # And all the overloads that take sequences
- objectSigs.extend(s for s in possibleSignatures
- if distinguishingType(s).isSequence())
- # Now append all the overloads that take a dictionary or callback
- # interface or record. There should be only one of these!
- genericObjectSigs = [
- s for s in possibleSignatures
- if (distinguishingType(s).isDictionary() or
- distinguishingType(s).isRecord() or
- distinguishingType(s).isCallbackInterface())]
- assert len(genericObjectSigs) <= 1
- objectSigs.extend(genericObjectSigs)
- # There might be more than one thing in objectSigs; we need to check
- # which ones we unwrap to.
- if len(objectSigs) > 0:
- # Here it's enough to guard on our argument being an object. The
- # code for unwrapping non-callback interfaces, typed arrays,
- # sequences, and Dates will just bail out and move on to
- # the next overload if the object fails to unwrap correctly,
- # while "object" accepts any object anyway. We could even not
- # do the isObject() check up front here, but in cases where we
- # have multiple object overloads it makes sense to do it only
- # once instead of for each overload. That will also allow the
- # unwrapping test to skip having to do codegen for the
- # null-or-undefined case, which we already handled above.
- caseBody.append(CGGeneric("if (%s.isObject()) {\n" %
- distinguishingArg))
- for sig in objectSigs:
- caseBody.append(CGIndenter(CGGeneric("do {\n")))
- # Indent by 4, since we need to indent further
- # than our "do" statement
- tryCall(sig, 4, isDefinitelyObject=True)
- caseBody.append(CGIndenter(CGGeneric("} while (0);\n")))
- caseBody.append(CGGeneric("}\n"))
- # Now we only have to consider booleans, numerics, and strings. If
- # we only have one of them, then we can just output it. But if not,
- # then we need to output some of the cases conditionally: if we have
- # a string overload, then boolean and numeric are conditional, and
- # if not then boolean is conditional if we have a numeric overload.
- def findUniqueSignature(filterLambda):
- sigs = filter(filterLambda, possibleSignatures)
- assert len(sigs) < 2
- if len(sigs) > 0:
- return sigs[0]
- return None
- stringSignature = findUniqueSignature(
- lambda s: (distinguishingType(s).isString() or
- distinguishingType(s).isEnum()))
- numericSignature = findUniqueSignature(
- lambda s: distinguishingType(s).isNumeric())
- booleanSignature = findUniqueSignature(
- lambda s: distinguishingType(s).isBoolean())
- if stringSignature or numericSignature:
- booleanCondition = "%s.isBoolean()"
- else:
- booleanCondition = None
- if stringSignature:
- numericCondition = "%s.isNumber()"
- else:
- numericCondition = None
- def addCase(sig, condition):
- sigCode = getPerSignatureCall(sig, distinguishingIndex)
- if condition:
- sigCode = CGIfWrapper(sigCode,
- condition % distinguishingArg)
- caseBody.append(sigCode)
- if booleanSignature:
- addCase(booleanSignature, booleanCondition)
- if numericSignature:
- addCase(numericSignature, numericCondition)
- if stringSignature:
- addCase(stringSignature, None)
- if (not booleanSignature and not numericSignature and
- not stringSignature):
- # Just throw; we have no idea what we're supposed to
- # do with this.
- caseBody.append(CGGeneric(
- 'return ThrowErrorMessage(cx, MSG_OVERLOAD_RESOLUTION_FAILED, "%d", "%d", "%s");\n' %
- (distinguishingIndex + 1, argCount, methodName)))
- argCountCases.append(CGCase(str(argCount), CGList(caseBody)))
- overloadCGThings = []
- overloadCGThings.append(
- CGGeneric("unsigned argcount = std::min(args.length(), %du);\n" %
- maxArgCount))
- overloadCGThings.append(
- CGSwitch("argcount",
- argCountCases,
- CGGeneric('return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "%s");\n' %
- methodName)))
- overloadCGThings.append(
- CGGeneric('MOZ_CRASH("We have an always-returning default case");\n'
- 'return false;\n'))
- self.cgRoot = CGList(overloadCGThings)
- def define(self):
- return self.cgRoot.define()
- class CGGetterCall(CGPerSignatureCall):
- """
- A class to generate a native object getter call for a particular IDL
- getter.
- """
- def __init__(self, returnType, nativeMethodName, descriptor, attr):
- if attr.getExtendedAttribute("UseCounter"):
- useCounterName = "%s_%s_getter" % (descriptor.interface.identifier.name,
- attr.identifier.name)
- else:
- useCounterName = None
- if attr.isStatic():
- nativeMethodName = "%s::%s" % (descriptor.nativeType, nativeMethodName)
- CGPerSignatureCall.__init__(self, returnType, [], nativeMethodName,
- attr.isStatic(), descriptor, attr,
- getter=True, useCounterName=useCounterName)
- class CGNavigatorGetterCall(CGPerSignatureCall):
- """
- A class to generate a native object getter call for an IDL getter for a
- property generated by NavigatorProperty.
- """
- def __init__(self, returnType, _, descriptor, attr):
- nativeMethodName = "%s::ConstructNavigatorObject" % (toBindingNamespace(returnType.inner.identifier.name))
- CGPerSignatureCall.__init__(self, returnType, [], nativeMethodName,
- True, descriptor, attr, getter=True)
- def getArguments(self):
- # The navigator object should be associated with the global of
- # the navigator it's coming from, which will be the global of
- # the object whose slot it gets cached in. That's stored in
- # "slotStorage".
- return [(FakeArgument(BuiltinTypes[IDLBuiltinType.Types.object],
- self.idlNode),
- "slotStorage")]
- class FakeIdentifier():
- def __init__(self, name):
- self.name = name
- class FakeArgument():
- """
- A class that quacks like an IDLArgument. This is used to make
- setters look like method calls or for special operations.
- """
- def __init__(self, type, interfaceMember, name="arg", allowTreatNonCallableAsNull=False):
- self.type = type
- self.optional = False
- self.variadic = False
- self.defaultValue = None
- self._allowTreatNonCallableAsNull = allowTreatNonCallableAsNull
- # For FakeArguments generated by maplike/setlike convenience functions,
- # we won't have an interfaceMember to pass in.
- if interfaceMember:
- self.treatNullAs = interfaceMember.treatNullAs
- else:
- self.treatNullAs = "Default"
- if isinstance(interfaceMember, IDLAttribute):
- self.enforceRange = interfaceMember.enforceRange
- self.clamp = interfaceMember.clamp
- else:
- self.enforceRange = False
- self.clamp = False
- self.identifier = FakeIdentifier(name)
- def allowTreatNonCallableAsNull(self):
- return self._allowTreatNonCallableAsNull
- def canHaveMissingValue(self):
- return False
- class CGSetterCall(CGPerSignatureCall):
- """
- A class to generate a native object setter call for a particular IDL
- setter.
- """
- def __init__(self, argType, nativeMethodName, descriptor, attr):
- if attr.getExtendedAttribute("UseCounter"):
- useCounterName = "%s_%s_setter" % (descriptor.interface.identifier.name,
- attr.identifier.name)
- else:
- useCounterName = None
- if attr.isStatic():
- nativeMethodName = "%s::%s" % (descriptor.nativeType, nativeMethodName)
- CGPerSignatureCall.__init__(self, None,
- [FakeArgument(argType, attr, allowTreatNonCallableAsNull=True)],
- nativeMethodName, attr.isStatic(),
- descriptor, attr, setter=True, useCounterName=useCounterName)
- def wrap_return_value(self):
- attr = self.idlNode
- if self.descriptor.wrapperCache and attr.slotIndices is not None:
- if attr.getExtendedAttribute("StoreInSlot"):
- args = "cx, self"
- else:
- args = "self"
- clearSlot = ("%s(%s);\n" %
- (MakeClearCachedValueNativeName(self.idlNode), args))
- else:
- clearSlot = ""
- # We have no return value
- return ("\n"
- "%s"
- "return true;\n" % clearSlot)
- class CGAbstractBindingMethod(CGAbstractStaticMethod):
- """
- Common class to generate the JSNatives for all our methods, getters, and
- setters. This will generate the function declaration and unwrap the
- |this| object. Subclasses are expected to override the generate_code
- function to do the rest of the work. This function should return a
- CGThing which is already properly indented.
- getThisObj should be code for getting a JSObject* for the binding
- object. If this is None, we will auto-generate code based on
- descriptor to do the right thing. "" can be passed in if the
- binding object is already stored in 'obj'.
- callArgs should be code for getting a JS::CallArgs into a variable
- called 'args'. This can be "" if there is already such a variable
- around.
- If allowCrossOriginThis is true, then this-unwrapping will first do an
- UncheckedUnwrap and after that operate on the result.
- """
- def __init__(self, descriptor, name, args, unwrapFailureCode=None,
- getThisObj=None,
- callArgs="JS::CallArgs args = JS::CallArgsFromVp(argc, vp);\n",
- allowCrossOriginThis=False):
- CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args)
- if unwrapFailureCode is None:
- self.unwrapFailureCode = 'return ThrowErrorMessage(cx, MSG_THIS_DOES_NOT_IMPLEMENT_INTERFACE, "Value", "%s");\n' % descriptor.interface.identifier.name
- else:
- self.unwrapFailureCode = unwrapFailureCode
- if getThisObj == "":
- self.getThisObj = None
- else:
- if getThisObj is None:
- if descriptor.interface.isOnGlobalProtoChain():
- ensureCondition = "!args.thisv().isNullOrUndefined() && !args.thisv().isObject()"
- getThisObj = "args.thisv().isObject() ? &args.thisv().toObject() : js::GetGlobalForObjectCrossCompartment(&args.callee())"
- else:
- ensureCondition = "!args.thisv().isObject()"
- getThisObj = "&args.thisv().toObject()"
- unwrapFailureCode = self.unwrapFailureCode % {'securityError': 'false'}
- ensureThisObj = CGIfWrapper(CGGeneric(unwrapFailureCode),
- ensureCondition)
- else:
- ensureThisObj = None
- self.getThisObj = CGList(
- [ensureThisObj,
- CGGeneric("JS::Rooted<JSObject*> obj(cx, %s);\n" %
- getThisObj)])
- self.callArgs = callArgs
- self.allowCrossOriginThis = allowCrossOriginThis
- def definition_body(self):
- body = self.callArgs
- if self.getThisObj is not None:
- body += self.getThisObj.define() + "\n"
- body += "%s* self;\n" % self.descriptor.nativeType
- body += dedent(
- """
- JS::Rooted<JS::Value> rootSelf(cx, JS::ObjectValue(*obj));
- """)
- # Our descriptor might claim that we're not castable, simply because
- # we're someone's consequential interface. But for this-unwrapping, we
- # know that we're the real deal. So fake a descriptor here for
- # consumption by CastableObjectUnwrapper.
- body += str(CastableObjectUnwrapper(
- self.descriptor,
- "rootSelf",
- "&rootSelf",
- "self",
- self.unwrapFailureCode,
- allowCrossOriginObj=self.allowCrossOriginThis))
- return body + self.generate_code().define()
- def generate_code(self):
- assert False # Override me
- class CGAbstractStaticBindingMethod(CGAbstractStaticMethod):
- """
- Common class to generate the JSNatives for all our static methods, getters
- and setters. This will generate the function declaration and unwrap the
- global object. Subclasses are expected to override the generate_code
- function to do the rest of the work. This function should return a
- CGThing which is already properly indented.
- """
- def __init__(self, descriptor, name):
- CGAbstractStaticMethod.__init__(self, descriptor, name, "bool",
- JSNativeArguments())
- def definition_body(self):
- # Make sure that "obj" is in the same compartment as "cx", since we'll
- # later use it to wrap return values.
- unwrap = dedent("""
- JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
- JS::Rooted<JSObject*> obj(cx, &args.callee());
- """)
- return unwrap + self.generate_code().define()
- def generate_code(self):
- assert False # Override me
- def MakeNativeName(name):
- return name[0].upper() + IDLToCIdentifier(name[1:])
- class CGGenericMethod(CGAbstractBindingMethod):
- """
- A class for generating the C++ code for an IDL method.
- If allowCrossOriginThis is true, then this-unwrapping will first do an
- UncheckedUnwrap and after that operate on the result.
- """
- def __init__(self, descriptor, allowCrossOriginThis=False):
- unwrapFailureCode = (
- 'return ThrowInvalidThis(cx, args, %%(securityError)s, "%s");\n' %
- descriptor.interface.identifier.name)
- name = "genericCrossOriginMethod" if allowCrossOriginThis else "genericMethod"
- CGAbstractBindingMethod.__init__(self, descriptor, name,
- JSNativeArguments(),
- unwrapFailureCode=unwrapFailureCode,
- allowCrossOriginThis=allowCrossOriginThis)
- def generate_code(self):
- return CGGeneric(dedent("""
- const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
- MOZ_ASSERT(info->type() == JSJitInfo::Method);
- JSJitMethodOp method = info->method;
- bool ok = method(cx, obj, self, JSJitMethodCallArgs(args));
- #ifdef DEBUG
- if (ok) {
- AssertReturnTypeMatchesJitinfo(info, args.rval());
- }
- #endif
- return ok;
- """))
- class CGGenericPromiseReturningMethod(CGAbstractBindingMethod):
- """
- A class for generating the C++ code for an IDL method that returns a Promise.
- Does not handle cross-origin this.
- """
- def __init__(self, descriptor):
- unwrapFailureCode = dedent("""
- ThrowInvalidThis(cx, args, %%(securityError)s, "%s");\n
- return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
- args.rval());\n""" %
- descriptor.interface.identifier.name)
- name = "genericPromiseReturningMethod"
- customCallArgs = dedent("""
- JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
- // Make sure to save the callee before someone maybe messes with rval().
- JS::Rooted<JSObject*> callee(cx, &args.callee());
- """)
- CGAbstractBindingMethod.__init__(self, descriptor, name,
- JSNativeArguments(),
- callArgs=customCallArgs,
- unwrapFailureCode=unwrapFailureCode)
- def generate_code(self):
- return CGGeneric(dedent("""
- const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
- MOZ_ASSERT(info->type() == JSJitInfo::Method);
- JSJitMethodOp method = info->method;
- bool ok = method(cx, obj, self, JSJitMethodCallArgs(args));
- if (ok) {
- #ifdef DEBUG
- AssertReturnTypeMatchesJitinfo(info, args.rval());
- #endif
- return true;
- }
- MOZ_ASSERT(info->returnType() == JSVAL_TYPE_OBJECT);
- return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
- args.rval());
- """))
- class CGSpecializedMethod(CGAbstractStaticMethod):
- """
- A class for generating the C++ code for a specialized method that the JIT
- can call with lower overhead.
- """
- def __init__(self, descriptor, method):
- self.method = method
- name = CppKeywords.checkMethodName(IDLToCIdentifier(method.identifier.name))
- args = [Argument('JSContext*', 'cx'),
- Argument('JS::Handle<JSObject*>', 'obj'),
- Argument('%s*' % descriptor.nativeType, 'self'),
- Argument('const JSJitMethodCallArgs&', 'args')]
- CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool', args)
- def definition_body(self):
- nativeName = CGSpecializedMethod.makeNativeName(self.descriptor,
- self.method)
- return CGMethodCall(nativeName, self.method.isStatic(), self.descriptor,
- self.method).define()
- @staticmethod
- def makeNativeName(descriptor, method):
- name = method.identifier.name
- return MakeNativeName(descriptor.binaryNameFor(name))
- class CGMethodPromiseWrapper(CGAbstractStaticMethod):
- """
- A class for generating a wrapper around another method that will
- convert exceptions to promises.
- """
- def __init__(self, descriptor, methodToWrap):
- self.method = methodToWrap
- name = self.makeName(methodToWrap.name)
- args = list(methodToWrap.args)
- CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool', args)
- def definition_body(self):
- return fill(
- """
- // Make sure to save the callee before someone maybe messes
- // with rval().
- JS::Rooted<JSObject*> callee(cx, &args.callee());
- bool ok = ${methodName}(${args});
- if (ok) {
- return true;
- }
- return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
- args.rval());
- """,
- methodName=self.method.name,
- args=", ".join(arg.name for arg in self.args))
- @staticmethod
- def makeName(methodName):
- return methodName + "_promiseWrapper"
- class CGJsonifierMethod(CGSpecializedMethod):
- def __init__(self, descriptor, method):
- assert method.isJsonifier()
- CGSpecializedMethod.__init__(self, descriptor, method)
- def definition_body(self):
- ret = dedent("""
- JS::Rooted<JSObject*> result(cx, JS_NewPlainObject(cx));
- if (!result) {
- return false;
- }
- """)
- jsonDescriptors = [self.descriptor]
- interface = self.descriptor.interface.parent
- while interface:
- descriptor = self.descriptor.getDescriptor(interface.identifier.name)
- if descriptor.operations['Jsonifier']:
- jsonDescriptors.append(descriptor)
- interface = interface.parent
- # Iterate the array in reverse: oldest ancestor first
- for descriptor in jsonDescriptors[::-1]:
- ret += fill(
- """
- if (!${parentclass}::JsonifyAttributes(cx, obj, self, result)) {
- return false;
- }
- """,
- parentclass=toBindingNamespace(descriptor.name)
- )
- ret += ('args.rval().setObject(*result);\n'
- 'return true;\n')
- return ret
- class CGLegacyCallHook(CGAbstractBindingMethod):
- """
- Call hook for our object
- """
- def __init__(self, descriptor):
- self._legacycaller = descriptor.operations["LegacyCaller"]
- # Our "self" is actually the callee in this case, not the thisval.
- CGAbstractBindingMethod.__init__(
- self, descriptor, LEGACYCALLER_HOOK_NAME,
- JSNativeArguments(), getThisObj="&args.callee()")
- def define(self):
- if not self._legacycaller:
- return ""
- return CGAbstractBindingMethod.define(self)
- def generate_code(self):
- name = self._legacycaller.identifier.name
- nativeName = MakeNativeName(self.descriptor.binaryNameFor(name))
- return CGMethodCall(nativeName, False, self.descriptor,
- self._legacycaller)
- class CGResolveHook(CGAbstractClassHook):
- """
- Resolve hook for objects that have the NeedResolve extended attribute.
- """
- def __init__(self, descriptor):
- assert descriptor.interface.getExtendedAttribute("NeedResolve")
- args = [Argument('JSContext*', 'cx'),
- Argument('JS::Handle<JSObject*>', 'obj'),
- Argument('JS::Handle<jsid>', 'id'),
- Argument('bool*', 'resolvedp')]
- CGAbstractClassHook.__init__(self, descriptor, RESOLVE_HOOK_NAME,
- "bool", args)
- def generate_code(self):
- return dedent("""
- JS::Rooted<JS::PropertyDescriptor> desc(cx);
- if (!self->DoResolve(cx, obj, id, &desc)) {
- return false;
- }
- if (!desc.object()) {
- return true;
- }
- // If desc.value() is undefined, then the DoResolve call
- // has already defined it on the object. Don't try to also
- // define it.
- if (!desc.value().isUndefined()) {
- desc.attributesRef() |= JSPROP_RESOLVING;
- if (!JS_DefinePropertyById(cx, obj, id, desc)) {
- return false;
- }
- }
- *resolvedp = true;
- return true;
- """)
- def definition_body(self):
- if self.descriptor.isGlobal():
- # Resolve standard classes
- prefix = dedent("""
- if (!ResolveGlobal(cx, obj, id, resolvedp)) {
- return false;
- }
- if (*resolvedp) {
- return true;
- }
- """)
- else:
- prefix = ""
- return prefix + CGAbstractClassHook.definition_body(self)
- class CGMayResolveHook(CGAbstractStaticMethod):
- """
- Resolve hook for objects that have the NeedResolve extended attribute.
- """
- def __init__(self, descriptor):
- assert descriptor.interface.getExtendedAttribute("NeedResolve")
- args = [Argument('const JSAtomState&', 'names'),
- Argument('jsid', 'id'),
- Argument('JSObject*', 'maybeObj')]
- CGAbstractStaticMethod.__init__(self, descriptor, MAY_RESOLVE_HOOK_NAME,
- "bool", args)
- def definition_body(self):
- if self.descriptor.isGlobal():
- # Check whether this would resolve as a standard class.
- prefix = dedent("""
- if (MayResolveGlobal(names, id, maybeObj)) {
- return true;
- }
- """)
- else:
- prefix = ""
- return (prefix +
- "return %s::MayResolve(id);\n" % self.descriptor.nativeType)
- class CGEnumerateHook(CGAbstractBindingMethod):
- """
- Enumerate hook for objects with custom hooks.
- """
- def __init__(self, descriptor):
- assert descriptor.interface.getExtendedAttribute("NeedResolve")
- args = [Argument('JSContext*', 'cx'),
- Argument('JS::Handle<JSObject*>', 'obj')]
- # Our "self" is actually the "obj" argument in this case, not the thisval.
- CGAbstractBindingMethod.__init__(
- self, descriptor, ENUMERATE_HOOK_NAME,
- args, getThisObj="", callArgs="")
- def generate_code(self):
- return CGGeneric(dedent("""
- AutoTArray<nsString, 8> names;
- binding_detail::FastErrorResult rv;
- self->GetOwnPropertyNames(cx, names, rv);
- if (rv.MaybeSetPendingException(cx)) {
- return false;
- }
- bool dummy;
- for (uint32_t i = 0; i < names.Length(); ++i) {
- if (!JS_HasUCProperty(cx, obj, names[i].get(), names[i].Length(), &dummy)) {
- return false;
- }
- }
- return true;
- """))
- def definition_body(self):
- if self.descriptor.isGlobal():
- # Enumerate standard classes
- prefix = dedent("""
- if (!EnumerateGlobal(cx, obj)) {
- return false;
- }
- """)
- else:
- prefix = ""
- return prefix + CGAbstractBindingMethod.definition_body(self)
- class CppKeywords():
- """
- A class for checking if method names declared in webidl
- are not in conflict with C++ keywords.
- """
- keywords = frozenset([
- 'alignas', 'alignof', 'and', 'and_eq', 'asm', 'assert', 'auto', 'bitand', 'bitor', 'bool',
- 'break', 'case', 'catch', 'char', 'char16_t', 'char32_t', 'class', 'compl', 'const',
- 'constexpr', 'const_cast', 'continue', 'decltype', 'default', 'delete', 'do', 'double',
- 'dynamic_cast', 'else', 'enum', 'explicit', 'export', 'extern', 'false', 'final', 'float',
- 'for', 'friend', 'goto', 'if', 'inline', 'int', 'long', 'mutable', 'namespace', 'new',
- 'noexcept', 'not', 'not_eq', 'nullptr', 'operator', 'or', 'or_eq', 'override', 'private',
- 'protected', 'public', 'register', 'reinterpret_cast', 'return', 'short', 'signed',
- 'sizeof', 'static', 'static_assert', 'static_cast', 'struct', 'switch', 'template', 'this',
- 'thread_local', 'throw', 'true', 'try', 'typedef', 'typeid', 'typename', 'union',
- 'unsigned', 'using', 'virtual', 'void', 'volatile', 'wchar_t', 'while', 'xor', 'xor_eq'])
- @staticmethod
- def checkMethodName(name):
- # Double '_' because 'assert' and '_assert' cannot be used in MS2013 compiler.
- # Bug 964892 and bug 963560.
- if name in CppKeywords.keywords:
- name = '_' + name + '_'
- return name
- class CGStaticMethod(CGAbstractStaticBindingMethod):
- """
- A class for generating the C++ code for an IDL static method.
- """
- def __init__(self, descriptor, method):
- self.method = method
- name = CppKeywords.checkMethodName(IDLToCIdentifier(method.identifier.name))
- CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
- def generate_code(self):
- nativeName = CGSpecializedMethod.makeNativeName(self.descriptor,
- self.method)
- return CGMethodCall(nativeName, True, self.descriptor, self.method)
- class CGGenericGetter(CGAbstractBindingMethod):
- """
- A class for generating the C++ code for an IDL attribute getter.
- """
- def __init__(self, descriptor, lenientThis=False, allowCrossOriginThis=False):
- if lenientThis:
- name = "genericLenientGetter"
- unwrapFailureCode = dedent("""
- MOZ_ASSERT(!JS_IsExceptionPending(cx));
- if (!ReportLenientThisUnwrappingFailure(cx, &args.callee())) {
- return false;
- }
- args.rval().set(JS::UndefinedValue());
- return true;
- """)
- else:
- if allowCrossOriginThis:
- name = "genericCrossOriginGetter"
- else:
- name = "genericGetter"
- unwrapFailureCode = (
- 'return ThrowInvalidThis(cx, args, %%(securityError)s, "%s");\n' %
- descriptor.interface.identifier.name)
- CGAbstractBindingMethod.__init__(self, descriptor, name, JSNativeArguments(),
- unwrapFailureCode,
- allowCrossOriginThis=allowCrossOriginThis)
- def generate_code(self):
- return CGGeneric(dedent("""
- const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
- MOZ_ASSERT(info->type() == JSJitInfo::Getter);
- JSJitGetterOp getter = info->getter;
- bool ok = getter(cx, obj, self, JSJitGetterCallArgs(args));
- #ifdef DEBUG
- if (ok) {
- AssertReturnTypeMatchesJitinfo(info, args.rval());
- }
- #endif
- return ok;
- """))
- class CGSpecializedGetter(CGAbstractStaticMethod):
- """
- A class for generating the code for a specialized attribute getter
- that the JIT can call with lower overhead.
- """
- def __init__(self, descriptor, attr):
- self.attr = attr
- name = 'get_' + IDLToCIdentifier(attr.identifier.name)
- args = [
- Argument('JSContext*', 'cx'),
- Argument('JS::Handle<JSObject*>', 'obj'),
- Argument('%s*' % descriptor.nativeType, 'self'),
- Argument('JSJitGetterCallArgs', 'args')
- ]
- CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args)
- def definition_body(self):
- if self.attr.isMaplikeOrSetlikeAttr():
- # If the interface is maplike/setlike, there will be one getter
- # method for the size property of the backing object. Due to having
- # to unpack the backing object from the slot, this requires its own
- # generator.
- return getMaplikeOrSetlikeSizeGetterBody(self.descriptor, self.attr)
- nativeName = CGSpecializedGetter.makeNativeName(self.descriptor,
- self.attr)
- if self.attr.slotIndices is not None:
- if self.descriptor.hasXPConnectImpls:
- raise TypeError("Interface '%s' has XPConnect impls, so we "
- "can't use our slot for property '%s'!" %
- (self.descriptor.interface.identifier.name,
- self.attr.identifier.name))
- # We're going to store this return value in a slot on some object,
- # to cache it. The question is, which object? For dictionary and
- # sequence return values, we want to use a slot on the Xray expando
- # if we're called via Xrays, and a slot on our reflector otherwise.
- # On the other hand, when dealing with some interfacce types
- # (navigator properties, window.document) we want to avoid calling
- # the getter more than once. In the case of navigator properties
- # that's because the getter actually creates a new object each time.
- # In the case of window.document, it's because the getter can start
- # returning null, which would get hidden in he non-Xray case by the
- # fact that it's [StoreOnSlot], so the cached version is always
- # around.
- #
- # The upshot is that we use the reflector slot for any getter whose
- # type is a gecko interface, whether we're called via Xrays or not.
- # Since [Cached] and [StoreInSlot] cannot be used with "NewObject",
- # we know that in the interface type case the returned object is
- # wrappercached. So creating Xrays to it is reasonable.
- if mayUseXrayExpandoSlots(self.descriptor, self.attr):
- prefix = fill(
- """
- // Have to either root across the getter call or reget after.
- bool isXray;
- JS::Rooted<JSObject*> slotStorage(cx, GetCachedSlotStorageObject(cx, obj, &isXray));
- if (!slotStorage) {
- return false;
- }
- const size_t slotIndex = isXray ? ${xraySlotIndex} : ${slotIndex};
- """,
- xraySlotIndex=memberXrayExpandoReservedSlot(self.attr,
- self.descriptor),
- slotIndex=memberReservedSlot(self.attr, self.descriptor))
- else:
- prefix = fill(
- """
- // Have to either root across the getter call or reget after.
- JS::Rooted<JSObject*> slotStorage(cx, js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false));
- MOZ_ASSERT(IsDOMObject(slotStorage));
- const size_t slotIndex = ${slotIndex};
- """,
- slotIndex=memberReservedSlot(self.attr, self.descriptor))
- prefix += fill(
- """
- MOZ_ASSERT(JSCLASS_RESERVED_SLOTS(js::GetObjectClass(slotStorage)) > slotIndex);
- {
- // Scope for cachedVal
- JS::Value cachedVal = js::GetReservedSlot(slotStorage, slotIndex);
- if (!cachedVal.isUndefined()) {
- args.rval().set(cachedVal);
- // The cached value is in the compartment of slotStorage,
- // so wrap into the caller compartment as needed.
- return ${maybeWrap}(cx, args.rval());
- }
- }
- """,
- maybeWrap=getMaybeWrapValueFuncForType(self.attr.type))
- else:
- prefix = ""
- if self.attr.navigatorObjectGetter:
- cgGetterCall = CGNavigatorGetterCall
- else:
- cgGetterCall = CGGetterCall
- return (prefix +
- cgGetterCall(self.attr.type, nativeName,
- self.descriptor, self.attr).define())
- @staticmethod
- def makeNativeName(descriptor, attr):
- name = attr.identifier.name
- nativeName = MakeNativeName(descriptor.binaryNameFor(name))
- _, resultOutParam, _, _, _ = getRetvalDeclarationForType(attr.type,
- descriptor)
- infallible = ('infallible' in
- descriptor.getExtendedAttributes(attr, getter=True))
- if resultOutParam or attr.type.nullable() or not infallible:
- nativeName = "Get" + nativeName
- return nativeName
- class CGStaticGetter(CGAbstractStaticBindingMethod):
- """
- A class for generating the C++ code for an IDL static attribute getter.
- """
- def __init__(self, descriptor, attr):
- self.attr = attr
- name = 'get_' + IDLToCIdentifier(attr.identifier.name)
- CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
- def generate_code(self):
- nativeName = CGSpecializedGetter.makeNativeName(self.descriptor,
- self.attr)
- return CGGetterCall(self.attr.type, nativeName, self.descriptor,
- self.attr)
- class CGGenericSetter(CGAbstractBindingMethod):
- """
- A class for generating the C++ code for an IDL attribute setter.
- """
- def __init__(self, descriptor, lenientThis=False, allowCrossOriginThis=False):
- if lenientThis:
- name = "genericLenientSetter"
- unwrapFailureCode = dedent("""
- MOZ_ASSERT(!JS_IsExceptionPending(cx));
- if (!ReportLenientThisUnwrappingFailure(cx, &args.callee())) {
- return false;
- }
- args.rval().set(JS::UndefinedValue());
- return true;
- """)
- else:
- if allowCrossOriginThis:
- name = "genericCrossOriginSetter"
- else:
- name = "genericSetter"
- unwrapFailureCode = (
- 'return ThrowInvalidThis(cx, args, %%(securityError)s, "%s");\n' %
- descriptor.interface.identifier.name)
- CGAbstractBindingMethod.__init__(self, descriptor, name, JSNativeArguments(),
- unwrapFailureCode,
- allowCrossOriginThis=allowCrossOriginThis)
- def generate_code(self):
- return CGGeneric(fill(
- """
- if (args.length() == 0) {
- return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${name} attribute setter");
- }
- const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
- MOZ_ASSERT(info->type() == JSJitInfo::Setter);
- JSJitSetterOp setter = info->setter;
- if (!setter(cx, obj, self, JSJitSetterCallArgs(args))) {
- return false;
- }
- args.rval().setUndefined();
- #ifdef DEBUG
- AssertReturnTypeMatchesJitinfo(info, args.rval());
- #endif
- return true;
- """,
- name=self.descriptor.interface.identifier.name))
- class CGSpecializedSetter(CGAbstractStaticMethod):
- """
- A class for generating the code for a specialized attribute setter
- that the JIT can call with lower overhead.
- """
- def __init__(self, descriptor, attr):
- self.attr = attr
- name = 'set_' + IDLToCIdentifier(attr.identifier.name)
- args = [Argument('JSContext*', 'cx'),
- Argument('JS::Handle<JSObject*>', 'obj'),
- Argument('%s*' % descriptor.nativeType, 'self'),
- Argument('JSJitSetterCallArgs', 'args')]
- CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args)
- def definition_body(self):
- nativeName = CGSpecializedSetter.makeNativeName(self.descriptor,
- self.attr)
- return CGSetterCall(self.attr.type, nativeName, self.descriptor,
- self.attr).define()
- @staticmethod
- def makeNativeName(descriptor, attr):
- name = attr.identifier.name
- return "Set" + MakeNativeName(descriptor.binaryNameFor(name))
- class CGStaticSetter(CGAbstractStaticBindingMethod):
- """
- A class for generating the C++ code for an IDL static attribute setter.
- """
- def __init__(self, descriptor, attr):
- self.attr = attr
- name = 'set_' + IDLToCIdentifier(attr.identifier.name)
- CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
- def generate_code(self):
- nativeName = CGSpecializedSetter.makeNativeName(self.descriptor,
- self.attr)
- checkForArg = CGGeneric(fill(
- """
- if (args.length() == 0) {
- return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${name} setter");
- }
- """,
- name=self.attr.identifier.name))
- call = CGSetterCall(self.attr.type, nativeName, self.descriptor,
- self.attr)
- return CGList([checkForArg, call])
- class CGSpecializedForwardingSetter(CGSpecializedSetter):
- """
- A class for generating the code for a specialized attribute setter with
- PutForwards that the JIT can call with lower overhead.
- """
- def __init__(self, descriptor, attr):
- CGSpecializedSetter.__init__(self, descriptor, attr)
- def definition_body(self):
- attrName = self.attr.identifier.name
- forwardToAttrName = self.attr.getExtendedAttribute("PutForwards")[0]
- # JS_GetProperty and JS_SetProperty can only deal with ASCII
- assert all(ord(c) < 128 for c in attrName)
- assert all(ord(c) < 128 for c in forwardToAttrName)
- return fill(
- """
- JS::Rooted<JS::Value> v(cx);
- if (!JS_GetProperty(cx, obj, "${attr}", &v)) {
- return false;
- }
- if (!v.isObject()) {
- return ThrowErrorMessage(cx, MSG_NOT_OBJECT, "${interface}.${attr}");
- }
- JS::Rooted<JSObject*> targetObj(cx, &v.toObject());
- return JS_SetProperty(cx, targetObj, "${forwardToAttrName}", args[0]);
- """,
- attr=attrName,
- interface=self.descriptor.interface.identifier.name,
- forwardToAttrName=forwardToAttrName)
- class CGSpecializedReplaceableSetter(CGSpecializedSetter):
- """
- A class for generating the code for a specialized attribute setter with
- Replaceable that the JIT can call with lower overhead.
- """
- def __init__(self, descriptor, attr):
- CGSpecializedSetter.__init__(self, descriptor, attr)
- def definition_body(self):
- attrName = self.attr.identifier.name
- # JS_DefineProperty can only deal with ASCII
- assert all(ord(c) < 128 for c in attrName)
- return ('return JS_DefineProperty(cx, obj, "%s", args[0], JSPROP_ENUMERATE);\n' %
- attrName)
- class CGSpecializedLenientSetter(CGSpecializedSetter):
- """
- A class for generating the code for a specialized attribute setter with
- LenientSetter that the JIT can call with lower overhead.
- """
- def __init__(self, descriptor, attr):
- CGSpecializedSetter.__init__(self, descriptor, attr)
- def definition_body(self):
- attrName = self.attr.identifier.name
- # JS_DefineProperty can only deal with ASCII
- assert all(ord(c) < 128 for c in attrName)
- return dedent("""
- DeprecationWarning(cx, obj, nsIDocument::eLenientSetter);
- return true;
- """)
- def memberReturnsNewObject(member):
- return member.getExtendedAttribute("NewObject") is not None
- class CGMemberJITInfo(CGThing):
- """
- A class for generating the JITInfo for a property that points to
- our specialized getter and setter.
- """
- def __init__(self, descriptor, member):
- self.member = member
- self.descriptor = descriptor
- def declare(self):
- return ""
- def defineJitInfo(self, infoName, opName, opType, infallible, movable,
- eliminatable, aliasSet, alwaysInSlot, lazilyInSlot,
- slotIndex, returnTypes, args):
- """
- aliasSet is a JSJitInfo::AliasSet value, without the "JSJitInfo::" bit.
- args is None if we don't want to output argTypes for some
- reason (e.g. we have overloads or we're not a method) and
- otherwise an iterable of the arguments for this method.
- """
- assert(not movable or aliasSet != "AliasEverything") # Can't move write-aliasing things
- assert(not alwaysInSlot or movable) # Things always in slots had better be movable
- assert(not eliminatable or aliasSet != "AliasEverything") # Can't eliminate write-aliasing things
- assert(not alwaysInSlot or eliminatable) # Things always in slots had better be eliminatable
- def jitInfoInitializer(isTypedMethod):
- initializer = fill(
- """
- {
- { ${opName} },
- { prototypes::id::${name} },
- { PrototypeTraits<prototypes::id::${name}>::Depth },
- JSJitInfo::${opType},
- JSJitInfo::${aliasSet}, /* aliasSet. Not relevant for setters. */
- ${returnType}, /* returnType. Not relevant for setters. */
- ${isInfallible}, /* isInfallible. False in setters. */
- ${isMovable}, /* isMovable. Not relevant for setters. */
- ${isEliminatable}, /* isEliminatable. Not relevant for setters. */
- ${isAlwaysInSlot}, /* isAlwaysInSlot. Only relevant for getters. */
- ${isLazilyCachedInSlot}, /* isLazilyCachedInSlot. Only relevant for getters. */
- ${isTypedMethod}, /* isTypedMethod. Only relevant for methods. */
- ${slotIndex} /* Reserved slot index, if we're stored in a slot, else 0. */
- }
- """,
- opName=opName,
- name=self.descriptor.name,
- opType=opType,
- aliasSet=aliasSet,
- returnType=reduce(CGMemberJITInfo.getSingleReturnType, returnTypes,
- ""),
- isInfallible=toStringBool(infallible),
- isMovable=toStringBool(movable),
- isEliminatable=toStringBool(eliminatable),
- isAlwaysInSlot=toStringBool(alwaysInSlot),
- isLazilyCachedInSlot=toStringBool(lazilyInSlot),
- isTypedMethod=toStringBool(isTypedMethod),
- slotIndex=slotIndex)
- return initializer.rstrip()
- slotAssert = fill(
- """
- static_assert(${slotIndex} <= JSJitInfo::maxSlotIndex, "We won't fit");
- static_assert(${slotIndex} < ${classReservedSlots}, "There is no slot for us");
- """,
- slotIndex=slotIndex,
- classReservedSlots=INSTANCE_RESERVED_SLOTS + self.descriptor.interface.totalMembersInSlots)
- if args is not None:
- argTypes = "%s_argTypes" % infoName
- args = [CGMemberJITInfo.getJSArgType(arg.type) for arg in args]
- args.append("JSJitInfo::ArgTypeListEnd")
- argTypesDecl = (
- "static const JSJitInfo::ArgType %s[] = { %s };\n" %
- (argTypes, ", ".join(args)))
- return fill(
- """
- $*{argTypesDecl}
- static const JSTypedMethodJitInfo ${infoName} = {
- ${jitInfo},
- ${argTypes}
- };
- $*{slotAssert}
- """,
- argTypesDecl=argTypesDecl,
- infoName=infoName,
- jitInfo=indent(jitInfoInitializer(True)),
- argTypes=argTypes,
- slotAssert=slotAssert)
- return fill(
- """
- static const JSJitInfo ${infoName} = ${jitInfo};
- $*{slotAssert}
- """,
- infoName=infoName,
- jitInfo=jitInfoInitializer(False),
- slotAssert=slotAssert)
- def define(self):
- if self.member.isAttr():
- getterinfo = ("%s_getterinfo" %
- IDLToCIdentifier(self.member.identifier.name))
- # We need the cast here because JSJitGetterOp has a "void* self"
- # while we have the right type.
- getter = ("(JSJitGetterOp)get_%s" %
- IDLToCIdentifier(self.member.identifier.name))
- getterinfal = "infallible" in self.descriptor.getExtendedAttributes(self.member, getter=True)
- movable = self.mayBeMovable() and getterinfal
- eliminatable = self.mayBeEliminatable() and getterinfal
- aliasSet = self.aliasSet()
- getterinfal = getterinfal and infallibleForMember(self.member, self.member.type, self.descriptor)
- isAlwaysInSlot = self.member.getExtendedAttribute("StoreInSlot")
- if self.member.slotIndices is not None:
- assert isAlwaysInSlot or self.member.getExtendedAttribute("Cached")
- isLazilyCachedInSlot = not isAlwaysInSlot
- slotIndex = memberReservedSlot(self.member, self.descriptor)
- # We'll statically assert that this is not too big in
- # CGUpdateMemberSlotsMethod, in the case when
- # isAlwaysInSlot is true.
- else:
- isLazilyCachedInSlot = False
- slotIndex = "0"
- result = self.defineJitInfo(getterinfo, getter, "Getter",
- getterinfal, movable, eliminatable,
- aliasSet, isAlwaysInSlot,
- isLazilyCachedInSlot, slotIndex,
- [self.member.type], None)
- if (not self.member.readonly or
- self.member.getExtendedAttribute("PutForwards") is not None or
- self.member.getExtendedAttribute("Replaceable") is not None or
- self.member.getExtendedAttribute("LenientSetter") is not None):
- setterinfo = ("%s_setterinfo" %
- IDLToCIdentifier(self.member.identifier.name))
- # Actually a JSJitSetterOp, but JSJitGetterOp is first in the
- # union.
- setter = ("(JSJitGetterOp)set_%s" %
- IDLToCIdentifier(self.member.identifier.name))
- # Setters are always fallible, since they have to do a typed unwrap.
- result += self.defineJitInfo(setterinfo, setter, "Setter",
- False, False, False, "AliasEverything",
- False, False, "0",
- [BuiltinTypes[IDLBuiltinType.Types.void]],
- None)
- return result
- if self.member.isMethod():
- methodinfo = ("%s_methodinfo" %
- IDLToCIdentifier(self.member.identifier.name))
- name = CppKeywords.checkMethodName(
- IDLToCIdentifier(self.member.identifier.name))
- if self.member.returnsPromise():
- name = CGMethodPromiseWrapper.makeName(name)
- # Actually a JSJitMethodOp, but JSJitGetterOp is first in the union.
- method = ("(JSJitGetterOp)%s" % name)
- # Methods are infallible if they are infallible, have no arguments
- # to unwrap, and have a return type that's infallible to wrap up for
- # return.
- sigs = self.member.signatures()
- if len(sigs) != 1:
- # Don't handle overloading. If there's more than one signature,
- # one of them must take arguments.
- methodInfal = False
- args = None
- movable = False
- eliminatable = False
- else:
- sig = sigs[0]
- # For methods that affect nothing, it's OK to set movable to our
- # notion of infallible on the C++ side, without considering
- # argument conversions, since argument conversions that can
- # reliably throw would be effectful anyway and the jit doesn't
- # move effectful things.
- hasInfallibleImpl = "infallible" in self.descriptor.getExtendedAttributes(self.member)
- movable = self.mayBeMovable() and hasInfallibleImpl
- eliminatable = self.mayBeEliminatable() and hasInfallibleImpl
- # XXXbz can we move the smarts about fallibility due to arg
- # conversions into the JIT, using our new args stuff?
- if (len(sig[1]) != 0 or
- not infallibleForMember(self.member, sig[0], self.descriptor)):
- # We have arguments or our return-value boxing can fail
- methodInfal = False
- else:
- methodInfal = hasInfallibleImpl
- # For now, only bother to output args if we're side-effect-free.
- if self.member.affects == "Nothing":
- args = sig[1]
- else:
- args = None
- aliasSet = self.aliasSet()
- result = self.defineJitInfo(methodinfo, method, "Method",
- methodInfal, movable, eliminatable,
- aliasSet, False, False, "0",
- [s[0] for s in sigs], args)
- return result
- raise TypeError("Illegal member type to CGPropertyJITInfo")
- def mayBeMovable(self):
- """
- Returns whether this attribute or method may be movable, just
- based on Affects/DependsOn annotations.
- """
- affects = self.member.affects
- dependsOn = self.member.dependsOn
- assert affects in IDLInterfaceMember.AffectsValues
- assert dependsOn in IDLInterfaceMember.DependsOnValues
- # Things that are DependsOn=DeviceState are not movable, because we
- # don't want them coalesced with each other or loop-hoisted, since
- # their return value can change even if nothing is going on from our
- # point of view.
- return (affects == "Nothing" and
- (dependsOn != "Everything" and dependsOn != "DeviceState"))
- def mayBeEliminatable(self):
- """
- Returns whether this attribute or method may be eliminatable, just
- based on Affects/DependsOn annotations.
- """
- # dependsOn shouldn't affect this decision at all, except in jitinfo we
- # have no way to express "Depends on everything, affects nothing",
- # because we only have three alias set values: AliasNone ("depends on
- # nothing, affects nothing"), AliasDOMSets ("depends on DOM sets,
- # affects nothing"), AliasEverything ("depends on everything, affects
- # everything"). So the [Affects=Nothing, DependsOn=Everything] case
- # gets encoded as AliasEverything and defineJitInfo asserts that if our
- # alias state is AliasEverything then we're not eliminatable (because it
- # thinks we might have side-effects at that point). Bug 1155796 is
- # tracking possible solutions for this.
- affects = self.member.affects
- dependsOn = self.member.dependsOn
- assert affects in IDLInterfaceMember.AffectsValues
- assert dependsOn in IDLInterfaceMember.DependsOnValues
- return affects == "Nothing" and dependsOn != "Everything"
- def aliasSet(self):
- """
- Returns the alias set to store in the jitinfo. This may not be the
- effective alias set the JIT uses, depending on whether we have enough
- information about our args to allow the JIT to prove that effectful
- argument conversions won't happen.
- """
- dependsOn = self.member.dependsOn
- assert dependsOn in IDLInterfaceMember.DependsOnValues
- if dependsOn == "Nothing" or dependsOn == "DeviceState":
- assert self.member.affects == "Nothing"
- return "AliasNone"
- if dependsOn == "DOMState":
- assert self.member.affects == "Nothing"
- return "AliasDOMSets"
- return "AliasEverything"
- @staticmethod
- def getJSReturnTypeTag(t):
- if t.nullable():
- # Sometimes it might return null, sometimes not
- return "JSVAL_TYPE_UNKNOWN"
- if t.isVoid():
- # No return, every time
- return "JSVAL_TYPE_UNDEFINED"
- if t.isSequence():
- return "JSVAL_TYPE_OBJECT"
- if t.isRecord():
- return "JSVAL_TYPE_OBJECT"
- if t.isPromise():
- return "JSVAL_TYPE_OBJECT"
- if t.isGeckoInterface():
- return "JSVAL_TYPE_OBJECT"
- if t.isString():
- return "JSVAL_TYPE_STRING"
- if t.isEnum():
- return "JSVAL_TYPE_STRING"
- if t.isCallback():
- return "JSVAL_TYPE_OBJECT"
- if t.isAny():
- # The whole point is to return various stuff
- return "JSVAL_TYPE_UNKNOWN"
- if t.isObject():
- return "JSVAL_TYPE_OBJECT"
- if t.isSpiderMonkeyInterface():
- return "JSVAL_TYPE_OBJECT"
- if t.isUnion():
- u = t.unroll()
- if u.hasNullableType:
- # Might be null or not
- return "JSVAL_TYPE_UNKNOWN"
- return reduce(CGMemberJITInfo.getSingleReturnType,
- u.flatMemberTypes, "")
- if t.isDictionary():
- return "JSVAL_TYPE_OBJECT"
- if t.isDate():
- return "JSVAL_TYPE_OBJECT"
- if not t.isPrimitive():
- raise TypeError("No idea what type " + str(t) + " is.")
- tag = t.tag()
- if tag == IDLType.Tags.bool:
- return "JSVAL_TYPE_BOOLEAN"
- if tag in [IDLType.Tags.int8, IDLType.Tags.uint8,
- IDLType.Tags.int16, IDLType.Tags.uint16,
- IDLType.Tags.int32]:
- return "JSVAL_TYPE_INT32"
- if tag in [IDLType.Tags.int64, IDLType.Tags.uint64,
- IDLType.Tags.unrestricted_float, IDLType.Tags.float,
- IDLType.Tags.unrestricted_double, IDLType.Tags.double]:
- # These all use JS_NumberValue, which can return int or double.
- # But TI treats "double" as meaning "int or double", so we're
- # good to return JSVAL_TYPE_DOUBLE here.
- return "JSVAL_TYPE_DOUBLE"
- if tag != IDLType.Tags.uint32:
- raise TypeError("No idea what type " + str(t) + " is.")
- # uint32 is sometimes int and sometimes double.
- return "JSVAL_TYPE_DOUBLE"
- @staticmethod
- def getSingleReturnType(existingType, t):
- type = CGMemberJITInfo.getJSReturnTypeTag(t)
- if existingType == "":
- # First element of the list; just return its type
- return type
- if type == existingType:
- return existingType
- if ((type == "JSVAL_TYPE_DOUBLE" and
- existingType == "JSVAL_TYPE_INT32") or
- (existingType == "JSVAL_TYPE_DOUBLE" and
- type == "JSVAL_TYPE_INT32")):
- # Promote INT32 to DOUBLE as needed
- return "JSVAL_TYPE_DOUBLE"
- # Different types
- return "JSVAL_TYPE_UNKNOWN"
- @staticmethod
- def getJSArgType(t):
- assert not t.isVoid()
- if t.nullable():
- # Sometimes it might return null, sometimes not
- return "JSJitInfo::ArgType(JSJitInfo::Null | %s)" % CGMemberJITInfo.getJSArgType(t.inner)
- if t.isSequence():
- return "JSJitInfo::Object"
- if t.isPromise():
- return "JSJitInfo::Object"
- if t.isGeckoInterface():
- return "JSJitInfo::Object"
- if t.isString():
- return "JSJitInfo::String"
- if t.isEnum():
- return "JSJitInfo::String"
- if t.isCallback():
- return "JSJitInfo::Object"
- if t.isAny():
- # The whole point is to return various stuff
- return "JSJitInfo::Any"
- if t.isObject():
- return "JSJitInfo::Object"
- if t.isSpiderMonkeyInterface():
- return "JSJitInfo::Object"
- if t.isUnion():
- u = t.unroll()
- type = "JSJitInfo::Null" if u.hasNullableType else ""
- return ("JSJitInfo::ArgType(%s)" %
- reduce(CGMemberJITInfo.getSingleArgType,
- u.flatMemberTypes, type))
- if t.isDictionary():
- return "JSJitInfo::Object"
- if t.isDate():
- return "JSJitInfo::Object"
- if not t.isPrimitive():
- raise TypeError("No idea what type " + str(t) + " is.")
- tag = t.tag()
- if tag == IDLType.Tags.bool:
- return "JSJitInfo::Boolean"
- if tag in [IDLType.Tags.int8, IDLType.Tags.uint8,
- IDLType.Tags.int16, IDLType.Tags.uint16,
- IDLType.Tags.int32]:
- return "JSJitInfo::Integer"
- if tag in [IDLType.Tags.int64, IDLType.Tags.uint64,
- IDLType.Tags.unrestricted_float, IDLType.Tags.float,
- IDLType.Tags.unrestricted_double, IDLType.Tags.double]:
- # These all use JS_NumberValue, which can return int or double.
- # But TI treats "double" as meaning "int or double", so we're
- # good to return JSVAL_TYPE_DOUBLE here.
- return "JSJitInfo::Double"
- if tag != IDLType.Tags.uint32:
- raise TypeError("No idea what type " + str(t) + " is.")
- # uint32 is sometimes int and sometimes double.
- return "JSJitInfo::Double"
- @staticmethod
- def getSingleArgType(existingType, t):
- type = CGMemberJITInfo.getJSArgType(t)
- if existingType == "":
- # First element of the list; just return its type
- return type
- if type == existingType:
- return existingType
- return "%s | %s" % (existingType, type)
- class CGStaticMethodJitinfo(CGGeneric):
- """
- A class for generating the JITInfo for a promise-returning static method.
- """
- def __init__(self, method):
- CGGeneric.__init__(
- self,
- "\n"
- "static const JSJitInfo %s_methodinfo = {\n"
- " { (JSJitGetterOp)%s },\n"
- " { prototypes::id::_ID_Count }, { 0 }, JSJitInfo::StaticMethod,\n"
- " JSJitInfo::AliasEverything, JSVAL_TYPE_MISSING, false, false,\n"
- " false, false, 0\n"
- "};\n" %
- (IDLToCIdentifier(method.identifier.name),
- CppKeywords.checkMethodName(
- IDLToCIdentifier(method.identifier.name))))
- def getEnumValueName(value):
- # Some enum values can be empty strings. Others might have weird
- # characters in them. Deal with the former by returning "_empty",
- # deal with possible name collisions from that by throwing if the
- # enum value is actually "_empty", and throw on any value
- # containing non-ASCII chars for now. Replace all chars other than
- # [0-9A-Za-z_] with '_'.
- if re.match("[^\x20-\x7E]", value):
- raise SyntaxError('Enum value "' + value + '" contains non-ASCII characters')
- if re.match("^[0-9]", value):
- return '_' + value
- value = re.sub(r'[^0-9A-Za-z_]', '_', value)
- if re.match("^_[A-Z]|__", value):
- raise SyntaxError('Enum value "' + value + '" is reserved by the C++ spec')
- if value == "_empty":
- raise SyntaxError('"_empty" is not an IDL enum value we support yet')
- if value == "":
- return "_empty"
- nativeName = MakeNativeName(value)
- if nativeName == "EndGuard_":
- raise SyntaxError('Enum value "' + value + '" cannot be used because it'
- ' collides with our internal EndGuard_ value. Please'
- ' rename our internal EndGuard_ to something else')
- return nativeName
- class CGEnumToJSValue(CGAbstractMethod):
- def __init__(self, enum):
- enumType = enum.identifier.name
- self.stringsArray = enumType + "Values::" + ENUM_ENTRY_VARIABLE_NAME
- CGAbstractMethod.__init__(self, None, "ToJSValue", "bool",
- [Argument("JSContext*", "aCx"),
- Argument(enumType, "aArgument"),
- Argument("JS::MutableHandle<JS::Value>",
- "aValue")])
- def definition_body(self):
- return fill(
- """
- MOZ_ASSERT(uint32_t(aArgument) < ArrayLength(${strings}));
- JSString* resultStr =
- JS_NewStringCopyN(aCx, ${strings}[uint32_t(aArgument)].value,
- ${strings}[uint32_t(aArgument)].length);
- if (!resultStr) {
- return false;
- }
- aValue.setString(resultStr);
- return true;
- """,
- strings=self.stringsArray)
- class CGEnum(CGThing):
- def __init__(self, enum):
- CGThing.__init__(self)
- self.enum = enum
- strings = CGNamespace(
- self.stringsNamespace(),
- CGGeneric(declare=("extern const EnumEntry %s[%d];\n" %
- (ENUM_ENTRY_VARIABLE_NAME, self.nEnumStrings())),
- define=fill(
- """
- extern const EnumEntry ${name}[${count}] = {
- $*{entries}
- { nullptr, 0 }
- };
- """,
- name=ENUM_ENTRY_VARIABLE_NAME,
- count=self.nEnumStrings(),
- entries=''.join('{"%s", %d},\n' % (val, len(val))
- for val in self.enum.values()))))
- toJSValue = CGEnumToJSValue(enum)
- self.cgThings = CGList([strings, toJSValue], "\n")
- def stringsNamespace(self):
- return self.enum.identifier.name + "Values"
- def nEnumStrings(self):
- return len(self.enum.values()) + 1
- def declare(self):
- decl = fill(
- """
- enum class ${name} : uint32_t {
- $*{enums}
- EndGuard_
- };
- """,
- name=self.enum.identifier.name,
- enums=",\n".join(map(getEnumValueName, self.enum.values())) + ",\n")
- strings = CGNamespace(self.stringsNamespace(),
- CGGeneric(declare="extern const EnumEntry %s[%d];\n"
- % (ENUM_ENTRY_VARIABLE_NAME, self.nEnumStrings())))
- return decl + "\n" + self.cgThings.declare()
- def define(self):
- return self.cgThings.define()
- def deps(self):
- return self.enum.getDeps()
- def getUnionAccessorSignatureType(type, descriptorProvider):
- """
- Returns the types that are used in the getter and setter signatures for
- union types
- """
- # Flat member types have already unwrapped nullables.
- assert not type.nullable()
- # Promise types can never appear in unions, because Promise is not
- # distinguishable from anything.
- assert not type.isPromise()
- if type.isSequence() or type.isRecord():
- if type.isSequence():
- wrapperType = "Sequence"
- else:
- wrapperType = "Record"
- # We don't use the returned template here, so it's OK to just pass no
- # sourceDescription.
- elementInfo = getJSToNativeConversionInfo(type.inner,
- descriptorProvider,
- isMember=wrapperType)
- if wrapperType == "Sequence":
- innerType = elementInfo.declType
- else:
- innerType = [recordKeyDeclType(type), elementInfo.declType]
- return CGTemplatedType(wrapperType, innerType,
- isConst=True, isReference=True)
- # Nested unions are unwrapped automatically into our flatMemberTypes.
- assert not type.isUnion()
- if type.isGeckoInterface():
- descriptor = descriptorProvider.getDescriptor(
- type.unroll().inner.identifier.name)
- typeName = CGGeneric(descriptor.nativeType)
- # Allow null pointers for old-binding classes.
- if type.unroll().inner.isExternal():
- typeName = CGWrapper(typeName, post="*")
- else:
- typeName = CGWrapper(typeName, post="&")
- return typeName
- if type.isSpiderMonkeyInterface():
- typeName = CGGeneric(type.name)
- return CGWrapper(typeName, post=" const &")
- if type.isDOMString() or type.isUSVString():
- return CGGeneric("const nsAString&")
- if type.isByteString():
- return CGGeneric("const nsCString&")
- if type.isEnum():
- return CGGeneric(type.inner.identifier.name)
- if type.isCallback():
- return CGGeneric("%s&" % type.unroll().callback.identifier.name)
- if type.isAny():
- return CGGeneric("JS::Value")
- if type.isObject():
- return CGGeneric("JSObject*")
- if type.isDictionary():
- return CGGeneric("const %s&" % type.inner.identifier.name)
- if not type.isPrimitive():
- raise TypeError("Need native type for argument type '%s'" % str(type))
- return CGGeneric(builtinNames[type.tag()])
- def getUnionTypeTemplateVars(unionType, type, descriptorProvider,
- ownsMembers=False):
- name = getUnionMemberName(type)
- holderName = "m" + name + "Holder"
- # By the time tryNextCode is invoked, we're guaranteed the union has been
- # constructed as some type, since we've been trying to convert into the
- # corresponding member.
- prefix = "" if ownsMembers else "mUnion."
- tryNextCode = ("$*{destroyHolder}\n"
- "%sDestroy%s();\n"
- "tryNext = true;\n"
- "return true;\n" % (prefix, name))
- conversionInfo = getJSToNativeConversionInfo(
- type, descriptorProvider, failureCode=tryNextCode,
- isDefinitelyObject=not type.isDictionary(),
- isMember=("OwningUnion" if ownsMembers else None),
- sourceDescription="member of %s" % unionType)
- if conversionInfo.holderType is not None:
- assert not ownsMembers
- destroyHolder = "%s.reset();\n" % holderName
- else:
- destroyHolder = ""
- ctorNeedsCx = conversionInfo.declArgs == "cx"
- ctorArgs = "cx" if ctorNeedsCx else ""
- structType = conversionInfo.declType.define()
- externalType = getUnionAccessorSignatureType(type, descriptorProvider).define()
- if type.isObject():
- if ownsMembers:
- body = dedent("""
- MOZ_ASSERT(mType == eUninitialized);
- mValue.mObject.SetValue(obj);
- mType = eObject;
- """)
- else:
- body = dedent("""
- MOZ_ASSERT(mUnion.mType == mUnion.eUninitialized);
- mUnion.mValue.mObject.SetValue(cx, obj);
- mUnion.mType = mUnion.eObject;
- """)
- # It's a bit sketchy to do the security check after setting the value,
- # but it keeps the code cleaner and lets us avoid rooting |obj| over the
- # call to CallerSubsumes().
- body = body + dedent("""
- if (passedToJSImpl && !CallerSubsumes(obj)) {
- ThrowErrorMessage(cx, MSG_PERMISSION_DENIED_TO_PASS_ARG, "%s");
- return false;
- }
- return true;
- """)
- setter = ClassMethod("SetToObject", "bool",
- [Argument("JSContext*", "cx"),
- Argument("JSObject*", "obj"),
- Argument("bool", "passedToJSImpl", default="false")],
- inline=True, bodyInHeader=True,
- body=body)
- else:
- # Important: we need to not have our declName involve
- # maybe-GCing operations.
- if conversionInfo.holderType is not None:
- holderArgs = conversionInfo.holderArgs
- if holderArgs is None:
- holderArgs = ""
- initHolder = "%s.emplace(%s);\n" % (holderName, holderArgs)
- else:
- initHolder = ""
- jsConversion = fill(
- initHolder + conversionInfo.template,
- val="value",
- maybeMutableVal="value",
- declName="memberSlot",
- holderName=(holderName if ownsMembers else "%s.ref()" % holderName),
- destroyHolder=destroyHolder,
- passedToJSImpl="passedToJSImpl")
- jsConversion = fill(
- """
- tryNext = false;
- { // scope for memberSlot
- ${structType}& memberSlot = RawSetAs${name}(${ctorArgs});
- $*{jsConversion}
- }
- return true;
- """,
- structType=structType,
- name=name,
- ctorArgs=ctorArgs,
- jsConversion=jsConversion)
- if ownsMembers:
- handleType = "JS::Handle<JS::Value>"
- else:
- handleType = "JS::MutableHandle<JS::Value>"
- setter = ClassMethod("TrySetTo" + name, "bool",
- [Argument("JSContext*", "cx"),
- Argument(handleType, "value"),
- Argument("bool&", "tryNext"),
- Argument("bool", "passedToJSImpl", default="false")],
- inline=not ownsMembers,
- bodyInHeader=not ownsMembers,
- body=jsConversion)
- return {
- "name": name,
- "structType": structType,
- "externalType": externalType,
- "setter": setter,
- "holderType": conversionInfo.holderType.define() if conversionInfo.holderType else None,
- "ctorArgs": ctorArgs,
- "ctorArgList": [Argument("JSContext*", "cx")] if ctorNeedsCx else []
- }
- class CGUnionStruct(CGThing):
- def __init__(self, type, descriptorProvider, ownsMembers=False):
- CGThing.__init__(self)
- self.type = type.unroll()
- self.descriptorProvider = descriptorProvider
- self.ownsMembers = ownsMembers
- self.struct = self.getStruct()
- def declare(self):
- return self.struct.declare()
- def define(self):
- return self.struct.define()
- def deps(self):
- return self.type.getDeps()
- def getStruct(self):
- members = [ClassMember("mType", "Type", body="eUninitialized"),
- ClassMember("mValue", "Value")]
- ctor = ClassConstructor([], bodyInHeader=True, visibility="public",
- explicit=True)
- methods = []
- enumValues = ["eUninitialized"]
- toJSValCases = [CGCase("eUninitialized", CGGeneric("return false;\n"))]
- destructorCases = [CGCase("eUninitialized", None)]
- assignmentCases = [
- CGCase("eUninitialized",
- CGGeneric('MOZ_ASSERT(mType == eUninitialized,\n'
- ' "We need to destroy ourselves?");\n'))]
- traceCases = []
- unionValues = []
- if self.type.hasNullableType:
- enumValues.append("eNull")
- methods.append(ClassMethod("IsNull", "bool", [], const=True, inline=True,
- body="return mType == eNull;\n",
- bodyInHeader=True))
- methods.append(ClassMethod("SetNull", "void", [], inline=True,
- body=("Uninit();\n"
- "mType = eNull;\n"),
- bodyInHeader=True))
- destructorCases.append(CGCase("eNull", None))
- assignmentCases.append(CGCase("eNull",
- CGGeneric("MOZ_ASSERT(mType == eUninitialized);\n"
- "mType = eNull;\n")))
- toJSValCases.append(CGCase("eNull", CGGeneric("rval.setNull();\n"
- "return true;\n")))
- hasObjectType = any(t.isObject() for t in self.type.flatMemberTypes)
- for t in self.type.flatMemberTypes:
- vars = getUnionTypeTemplateVars(self.type,
- t, self.descriptorProvider,
- ownsMembers=self.ownsMembers)
- if vars["name"] != "Object" or self.ownsMembers:
- body = fill(
- """
- if (mType == e${name}) {
- return mValue.m${name}.Value();
- }
- %s
- mType = e${name};
- return mValue.m${name}.SetValue(${ctorArgs});
- """,
- **vars)
- # bodyInHeader must be false for return values because they own
- # their union members and we don't want include headers in
- # UnionTypes.h just to call Addref/Release
- methods.append(ClassMethod(
- "RawSetAs" + vars["name"],
- vars["structType"] + "&",
- vars["ctorArgList"],
- bodyInHeader=not self.ownsMembers,
- body=body % "MOZ_ASSERT(mType == eUninitialized);"))
- uninit = "Uninit();"
- if hasObjectType and not self.ownsMembers:
- uninit = 'MOZ_ASSERT(mType != eObject, "This will not play well with Rooted");\n' + uninit
- methods.append(ClassMethod(
- "SetAs" + vars["name"],
- vars["structType"] + "&",
- vars["ctorArgList"],
- bodyInHeader=not self.ownsMembers,
- body=body % uninit))
- if self.ownsMembers:
- methods.append(vars["setter"])
- # Provide a SetStringData() method to support string defaults.
- if t.isByteString():
- methods.append(
- ClassMethod("SetStringData", "void",
- [Argument("const nsCString::char_type*", "aData"),
- Argument("nsCString::size_type", "aLength")],
- inline=True, bodyInHeader=True,
- body="RawSetAs%s().Assign(aData, aLength);\n" % t.name))
- elif t.isString():
- methods.append(
- ClassMethod("SetStringData", "void",
- [Argument("const nsString::char_type*", "aData"),
- Argument("nsString::size_type", "aLength")],
- inline=True, bodyInHeader=True,
- body="RawSetAs%s().Assign(aData, aLength);\n" % t.name))
- body = fill(
- """
- MOZ_ASSERT(Is${name}(), "Wrong type!");
- mValue.m${name}.Destroy();
- mType = eUninitialized;
- """,
- **vars)
- methods.append(ClassMethod("Destroy" + vars["name"],
- "void",
- [],
- visibility="private",
- bodyInHeader=not self.ownsMembers,
- body=body))
- body = fill("return mType == e${name};\n", **vars)
- methods.append(ClassMethod("Is" + vars["name"],
- "bool",
- [],
- const=True,
- bodyInHeader=True,
- body=body))
- body = fill(
- """
- MOZ_ASSERT(Is${name}(), "Wrong type!");
- return mValue.m${name}.Value();
- """,
- **vars)
- # The non-const version of GetAs* returns our internal type
- getterReturnType = "%s&" % vars["structType"]
- methods.append(ClassMethod("GetAs" + vars["name"],
- getterReturnType,
- [],
- bodyInHeader=True,
- body=body))
- # The const version of GetAs* returns our internal type
- # for owning unions, but our external type for non-owning
- # ones.
- if self.ownsMembers:
- getterReturnType = "%s const &" % vars["structType"]
- else:
- getterReturnType = vars["externalType"]
- methods.append(ClassMethod("GetAs" + vars["name"],
- getterReturnType,
- [],
- const=True,
- bodyInHeader=True,
- body=body))
- unionValues.append(
- fill("UnionMember<${structType} > m${name}", **vars))
- enumValues.append("e" + vars["name"])
- skipToJSVal = False
- try:
- toJSValCases.append(
- CGCase("e" + vars["name"],
- self.getConversionToJS(vars, t)))
- except MethodNotNewObjectError:
- # If we can't have a ToJSVal() because one of our members can
- # only be returned from [NewObject] methods, then just skip
- # generating ToJSVal.
- skipToJSVal = True
- destructorCases.append(
- CGCase("e" + vars["name"],
- CGGeneric("Destroy%s();\n" % vars["name"])))
- assignmentCases.append(
- CGCase("e" + vars["name"],
- CGGeneric("SetAs%s() = aOther.GetAs%s();\n" %
- (vars["name"], vars["name"]))))
- if self.ownsMembers and typeNeedsRooting(t):
- if t.isObject():
- traceCases.append(
- CGCase("e" + vars["name"],
- CGGeneric('JS::UnsafeTraceRoot(trc, %s, "%s");\n' %
- ("&mValue.m" + vars["name"] + ".Value()",
- "mValue.m" + vars["name"]))))
- elif t.isDictionary():
- traceCases.append(
- CGCase("e" + vars["name"],
- CGGeneric("mValue.m%s.Value().TraceDictionary(trc);\n" %
- vars["name"])))
- elif t.isSequence():
- traceCases.append(
- CGCase("e" + vars["name"],
- CGGeneric("DoTraceSequence(trc, mValue.m%s.Value());\n" %
- vars["name"])))
- elif t.isRecord():
- traceCases.append(
- CGCase("e" + vars["name"],
- CGGeneric("TraceRecord(trc, mValue.m%s.Value());\n" %
- vars["name"])))
- else:
- assert t.isSpiderMonkeyInterface()
- traceCases.append(
- CGCase("e" + vars["name"],
- CGGeneric("mValue.m%s.Value().TraceSelf(trc);\n" %
- vars["name"])))
- dtor = CGSwitch("mType", destructorCases).define()
- methods.append(ClassMethod("Uninit", "void", [],
- visibility="public", body=dtor,
- bodyInHeader=not self.ownsMembers,
- inline=not self.ownsMembers))
- if not skipToJSVal:
- methods.append(
- ClassMethod(
- "ToJSVal",
- "bool",
- [
- Argument("JSContext*", "cx"),
- Argument("JS::Handle<JSObject*>", "scopeObj"),
- Argument("JS::MutableHandle<JS::Value>", "rval")
- ],
- body=CGSwitch("mType", toJSValCases,
- default=CGGeneric("return false;\n")).define() + "\nreturn false;\n",
- const=True))
- constructors = [ctor]
- selfName = CGUnionStruct.unionTypeName(self.type, self.ownsMembers)
- if self.ownsMembers:
- if traceCases:
- traceBody = CGSwitch("mType", traceCases,
- default=CGGeneric("")).define()
- else:
- traceBody = ""
- methods.append(ClassMethod("TraceUnion", "void",
- [Argument("JSTracer*", "trc")],
- body=traceBody))
- if CGUnionStruct.isUnionCopyConstructible(self.type):
- constructors.append(
- ClassConstructor(
- [Argument("const %s&" % selfName, "aOther")],
- bodyInHeader=True,
- visibility="public",
- explicit=True,
- body="*this = aOther;\n"))
- methods.append(ClassMethod(
- "operator=", "void",
- [Argument("const %s&" % selfName, "aOther")],
- body=CGSwitch("aOther.mType", assignmentCases).define()))
- disallowCopyConstruction = False
- else:
- disallowCopyConstruction = True
- else:
- disallowCopyConstruction = True
- if self.ownsMembers:
- friend = " friend void ImplCycleCollectionUnlink(%s& aUnion);\n" % CGUnionStruct.unionTypeName(self.type, True)
- else:
- friend = " friend class %sArgument;\n" % str(self.type)
- bases = [ClassBase("AllOwningUnionBase")] if self.ownsMembers else []
- return CGClass(selfName,
- bases=bases,
- members=members,
- constructors=constructors,
- methods=methods,
- disallowCopyConstruction=disallowCopyConstruction,
- extradeclarations=friend,
- destructor=ClassDestructor(visibility="public",
- body="Uninit();\n",
- bodyInHeader=True),
- enums=[ClassEnum("Type", enumValues, visibility="private")],
- unions=[ClassUnion("Value", unionValues, visibility="private")])
- def getConversionToJS(self, templateVars, type):
- assert not type.nullable() # flatMemberTypes never has nullable types
- val = "mValue.m%(name)s.Value()" % templateVars
- wrapCode = wrapForType(
- type, self.descriptorProvider,
- {
- "jsvalRef": "rval",
- "jsvalHandle": "rval",
- "obj": "scopeObj",
- "result": val,
- "typedArraysAreStructs": True
- })
- return CGGeneric(wrapCode)
- @staticmethod
- def isUnionCopyConstructible(type):
- return all(isTypeCopyConstructible(t) for t in type.flatMemberTypes)
- @staticmethod
- def unionTypeName(type, ownsMembers):
- """
- Returns a string name for this known union type.
- """
- assert type.isUnion() and not type.nullable()
- return ("Owning" if ownsMembers else "") + type.name
- @staticmethod
- def unionTypeDecl(type, ownsMembers):
- """
- Returns a string for declaring this possibly-nullable union type.
- """
- assert type.isUnion()
- nullable = type.nullable()
- if nullable:
- type = type.inner
- decl = CGGeneric(CGUnionStruct.unionTypeName(type, ownsMembers))
- if nullable:
- decl = CGTemplatedType("Nullable", decl)
- return decl.define()
- class CGUnionConversionStruct(CGThing):
- def __init__(self, type, descriptorProvider):
- CGThing.__init__(self)
- self.type = type.unroll()
- self.descriptorProvider = descriptorProvider
- def declare(self):
- structName = str(self.type)
- members = [ClassMember("mUnion", structName + "&",
- body="const_cast<%s&>(aUnion)" % structName)]
- # Argument needs to be a const ref because that's all Maybe<> allows
- ctor = ClassConstructor([Argument("const %s&" % structName, "aUnion")],
- bodyInHeader=True,
- visibility="public",
- explicit=True)
- methods = []
- if self.type.hasNullableType:
- methods.append(ClassMethod("SetNull", "bool", [],
- body=("MOZ_ASSERT(mUnion.mType == mUnion.eUninitialized);\n"
- "mUnion.mType = mUnion.eNull;\n"
- "return true;\n"),
- inline=True, bodyInHeader=True))
- for t in self.type.flatMemberTypes:
- vars = getUnionTypeTemplateVars(self.type,
- t, self.descriptorProvider)
- methods.append(vars["setter"])
- if vars["name"] != "Object":
- body = fill(
- """
- MOZ_ASSERT(mUnion.mType == mUnion.eUninitialized);
- mUnion.mType = mUnion.e${name};
- return mUnion.mValue.m${name}.SetValue(${ctorArgs});
- """,
- **vars)
- methods.append(ClassMethod("RawSetAs" + vars["name"],
- vars["structType"] + "&",
- vars["ctorArgList"],
- bodyInHeader=True,
- body=body,
- visibility="private"))
- # Provide a SetStringData() method to support string defaults.
- if t.isByteString():
- methods.append(
- ClassMethod("SetStringData", "void",
- [Argument("const nsDependentCString::char_type*", "aData"),
- Argument("nsDependentCString::size_type", "aLength")],
- inline=True, bodyInHeader=True,
- body="RawSetAs%s().Rebind(aData, aLength);\n" % t.name))
- elif t.isString():
- methods.append(
- ClassMethod("SetStringData", "void",
- [Argument("const nsDependentString::char_type*", "aData"),
- Argument("nsDependentString::size_type", "aLength")],
- inline=True, bodyInHeader=True,
- body="RawSetAs%s().Rebind(aData, aLength);\n" % t.name))
- if vars["holderType"] is not None:
- holderType = CGTemplatedType("Maybe",
- CGGeneric(vars["holderType"])).define()
- members.append(ClassMember("m%sHolder" % vars["name"],
- holderType))
- return CGClass(structName + "Argument",
- members=members,
- constructors=[ctor],
- methods=methods,
- disallowCopyConstruction=True).declare()
- def define(self):
- return ""
- def deps(self):
- return set()
- class ClassItem:
- """ Use with CGClass """
- def __init__(self, name, visibility):
- self.name = name
- self.visibility = visibility
- def declare(self, cgClass):
- assert False
- def define(self, cgClass):
- assert False
- class ClassBase(ClassItem):
- def __init__(self, name, visibility='public'):
- ClassItem.__init__(self, name, visibility)
- def declare(self, cgClass):
- return '%s %s' % (self.visibility, self.name)
- def define(self, cgClass):
- # Only in the header
- return ''
- class ClassMethod(ClassItem):
- def __init__(self, name, returnType, args, inline=False, static=False,
- virtual=False, const=False, bodyInHeader=False,
- templateArgs=None, visibility='public', body=None,
- breakAfterReturnDecl="\n",
- breakAfterSelf="\n", override=False):
- """
- override indicates whether to flag the method as override
- """
- assert not override or virtual
- assert not (override and static)
- self.returnType = returnType
- self.args = args
- self.inline = inline or bodyInHeader
- self.static = static
- self.virtual = virtual
- self.const = const
- self.bodyInHeader = bodyInHeader
- self.templateArgs = templateArgs
- self.body = body
- self.breakAfterReturnDecl = breakAfterReturnDecl
- self.breakAfterSelf = breakAfterSelf
- self.override = override
- ClassItem.__init__(self, name, visibility)
- def getDecorators(self, declaring):
- decorators = []
- if self.inline:
- decorators.append('inline')
- if declaring:
- if self.static:
- decorators.append('static')
- if self.virtual:
- decorators.append('virtual')
- if decorators:
- return ' '.join(decorators) + ' '
- return ''
- def getBody(self):
- # Override me or pass a string to constructor
- assert self.body is not None
- return self.body
- def declare(self, cgClass):
- templateClause = ('template <%s>\n' % ', '.join(self.templateArgs)
- if self.bodyInHeader and self.templateArgs else '')
- args = ', '.join([a.declare() for a in self.args])
- if self.bodyInHeader:
- body = indent(self.getBody())
- body = '\n{\n' + body + '}\n'
- else:
- body = ';\n'
- return fill(
- "${templateClause}${decorators}${returnType}${breakAfterReturnDecl}"
- "${name}(${args})${const}${override}${body}"
- "${breakAfterSelf}",
- templateClause=templateClause,
- decorators=self.getDecorators(True),
- returnType=self.returnType,
- breakAfterReturnDecl=self.breakAfterReturnDecl,
- name=self.name,
- args=args,
- const=' const' if self.const else '',
- override=' override' if self.override else '',
- body=body,
- breakAfterSelf=self.breakAfterSelf)
- def define(self, cgClass):
- if self.bodyInHeader:
- return ''
- templateArgs = cgClass.templateArgs
- if templateArgs:
- if cgClass.templateSpecialization:
- templateArgs = \
- templateArgs[len(cgClass.templateSpecialization):]
- if templateArgs:
- templateClause = \
- 'template <%s>\n' % ', '.join([str(a) for a in templateArgs])
- else:
- templateClause = ''
- return fill(
- """
- ${templateClause}${decorators}${returnType}
- ${className}::${name}(${args})${const}
- {
- $*{body}
- }
- """,
- templateClause=templateClause,
- decorators=self.getDecorators(False),
- returnType=self.returnType,
- className=cgClass.getNameString(),
- name=self.name,
- args=', '.join([a.define() for a in self.args]),
- const=' const' if self.const else '',
- body=self.getBody())
- class ClassUsingDeclaration(ClassItem):
- """
- Used for importing a name from a base class into a CGClass
- baseClass is the name of the base class to import the name from
- name is the name to import
- visibility determines the visibility of the name (public,
- protected, private), defaults to public.
- """
- def __init__(self, baseClass, name, visibility='public'):
- self.baseClass = baseClass
- ClassItem.__init__(self, name, visibility)
- def declare(self, cgClass):
- return "using %s::%s;\n\n" % (self.baseClass, self.name)
- def define(self, cgClass):
- return ''
- class ClassConstructor(ClassItem):
- """
- Used for adding a constructor to a CGClass.
- args is a list of Argument objects that are the arguments taken by the
- constructor.
- inline should be True if the constructor should be marked inline.
- bodyInHeader should be True if the body should be placed in the class
- declaration in the header.
- visibility determines the visibility of the constructor (public,
- protected, private), defaults to private.
- explicit should be True if the constructor should be marked explicit.
- baseConstructors is a list of strings containing calls to base constructors,
- defaults to None.
- body contains a string with the code for the constructor, defaults to empty.
- """
- def __init__(self, args, inline=False, bodyInHeader=False,
- visibility="private", explicit=False, constexpr=False, baseConstructors=None,
- body=""):
- assert not (inline and constexpr)
- assert not (bodyInHeader and constexpr)
- self.args = args
- self.inline = inline or bodyInHeader
- self.bodyInHeader = bodyInHeader or constexpr
- self.explicit = explicit
- self.constexpr = constexpr
- self.baseConstructors = baseConstructors or []
- self.body = body
- ClassItem.__init__(self, None, visibility)
- def getDecorators(self, declaring):
- decorators = []
- if self.explicit:
- decorators.append('explicit')
- if self.inline and declaring:
- decorators.append('inline')
- if self.constexpr and declaring:
- decorators.append('constexpr')
- if decorators:
- return ' '.join(decorators) + ' '
- return ''
- def getInitializationList(self, cgClass):
- items = [str(c) for c in self.baseConstructors]
- for m in cgClass.members:
- if not m.static:
- initialize = m.body
- if initialize:
- items.append(m.name + "(" + initialize + ")")
- if len(items) > 0:
- return '\n : ' + ',\n '.join(items)
- return ''
- def getBody(self):
- return self.body
- def declare(self, cgClass):
- args = ', '.join([a.declare() for a in self.args])
- if self.bodyInHeader:
- body = self.getInitializationList(cgClass) + '\n{\n' + indent(self.getBody()) + '}\n'
- else:
- body = ';\n'
- return fill(
- "${decorators}${className}(${args})${body}\n",
- decorators=self.getDecorators(True),
- className=cgClass.getNameString(),
- args=args,
- body=body)
- def define(self, cgClass):
- if self.bodyInHeader:
- return ''
- return fill(
- """
- ${decorators}
- ${className}::${className}(${args})${initializationList}
- {
- $*{body}
- }
- """,
- decorators=self.getDecorators(False),
- className=cgClass.getNameString(),
- args=', '.join([a.define() for a in self.args]),
- initializationList=self.getInitializationList(cgClass),
- body=self.getBody())
- class ClassDestructor(ClassItem):
- """
- Used for adding a destructor to a CGClass.
- inline should be True if the destructor should be marked inline.
- bodyInHeader should be True if the body should be placed in the class
- declaration in the header.
- visibility determines the visibility of the destructor (public,
- protected, private), defaults to private.
- body contains a string with the code for the destructor, defaults to empty.
- virtual determines whether the destructor is virtual, defaults to False.
- """
- def __init__(self, inline=False, bodyInHeader=False,
- visibility="private", body='', virtual=False):
- self.inline = inline or bodyInHeader
- self.bodyInHeader = bodyInHeader
- self.body = body
- self.virtual = virtual
- ClassItem.__init__(self, None, visibility)
- def getDecorators(self, declaring):
- decorators = []
- if self.virtual and declaring:
- decorators.append('virtual')
- if self.inline and declaring:
- decorators.append('inline')
- if decorators:
- return ' '.join(decorators) + ' '
- return ''
- def getBody(self):
- return self.body
- def declare(self, cgClass):
- if self.bodyInHeader:
- body = '\n{\n' + indent(self.getBody()) + '}\n'
- else:
- body = ';\n'
- return fill(
- "${decorators}~${className}()${body}\n",
- decorators=self.getDecorators(True),
- className=cgClass.getNameString(),
- body=body)
- def define(self, cgClass):
- if self.bodyInHeader:
- return ''
- return fill(
- """
- ${decorators}
- ${className}::~${className}()
- {
- $*{body}
- }
- """,
- decorators=self.getDecorators(False),
- className=cgClass.getNameString(),
- body=self.getBody())
- class ClassMember(ClassItem):
- def __init__(self, name, type, visibility="private", static=False,
- body=None, hasIgnoreInitCheckFlag=False):
- self.type = type
- self.static = static
- self.body = body
- self.hasIgnoreInitCheckFlag = hasIgnoreInitCheckFlag;
- ClassItem.__init__(self, name, visibility)
- def declare(self, cgClass):
- return '%s%s%s %s;\n' % ('static ' if self.static else '',
- 'MOZ_INIT_OUTSIDE_CTOR '
- if self.hasIgnoreInitCheckFlag else '',
- self.type, self.name)
- def define(self, cgClass):
- if not self.static:
- return ''
- if self.body:
- body = " = " + self.body
- else:
- body = ""
- return '%s %s::%s%s;\n' % (self.type, cgClass.getNameString(),
- self.name, body)
- class ClassTypedef(ClassItem):
- def __init__(self, name, type, visibility="public"):
- self.type = type
- ClassItem.__init__(self, name, visibility)
- def declare(self, cgClass):
- return 'typedef %s %s;\n' % (self.type, self.name)
- def define(self, cgClass):
- # Only goes in the header
- return ''
- class ClassEnum(ClassItem):
- def __init__(self, name, entries, values=None, visibility="public"):
- self.entries = entries
- self.values = values
- ClassItem.__init__(self, name, visibility)
- def declare(self, cgClass):
- entries = []
- for i in range(0, len(self.entries)):
- if not self.values or i >= len(self.values):
- entry = '%s' % self.entries[i]
- else:
- entry = '%s = %s' % (self.entries[i], self.values[i])
- entries.append(entry)
- name = '' if not self.name else ' ' + self.name
- return 'enum%s\n{\n%s\n};\n' % (name, indent(',\n'.join(entries)))
- def define(self, cgClass):
- # Only goes in the header
- return ''
- class ClassUnion(ClassItem):
- def __init__(self, name, entries, visibility="public"):
- self.entries = [entry + ";\n" for entry in entries]
- ClassItem.__init__(self, name, visibility)
- def declare(self, cgClass):
- return "union %s\n{\n%s\n};\n" % (self.name, indent(''.join(self.entries)))
- def define(self, cgClass):
- # Only goes in the header
- return ''
- class CGClass(CGThing):
- def __init__(self, name, bases=[], members=[], constructors=[],
- destructor=None, methods=[],
- typedefs=[], enums=[], unions=[], templateArgs=[],
- templateSpecialization=[], isStruct=False,
- disallowCopyConstruction=False, indent='',
- decorators='',
- extradeclarations='',
- extradefinitions=''):
- CGThing.__init__(self)
- self.name = name
- self.bases = bases
- self.members = members
- self.constructors = constructors
- # We store our single destructor in a list, since all of our
- # code wants lists of members.
- self.destructors = [destructor] if destructor else []
- self.methods = methods
- self.typedefs = typedefs
- self.enums = enums
- self.unions = unions
- self.templateArgs = templateArgs
- self.templateSpecialization = templateSpecialization
- self.isStruct = isStruct
- self.disallowCopyConstruction = disallowCopyConstruction
- self.indent = indent
- self.defaultVisibility = 'public' if isStruct else 'private'
- self.decorators = decorators
- self.extradeclarations = extradeclarations
- self.extradefinitions = extradefinitions
- def getNameString(self):
- className = self.name
- if self.templateSpecialization:
- className += '<%s>' % ', '.join([str(a)
- for a in self.templateSpecialization])
- return className
- def declare(self):
- result = ''
- if self.templateArgs:
- templateArgs = [a.declare() for a in self.templateArgs]
- templateArgs = templateArgs[len(self.templateSpecialization):]
- result += ('template <%s>\n' %
- ','.join([str(a) for a in templateArgs]))
- type = 'struct' if self.isStruct else 'class'
- if self.templateSpecialization:
- specialization = \
- '<%s>' % ', '.join([str(a) for a in self.templateSpecialization])
- else:
- specialization = ''
- myself = '%s %s%s' % (type, self.name, specialization)
- if self.decorators != '':
- myself += " " + self.decorators
- result += myself
- if self.bases:
- inherit = ' : '
- result += inherit
- # Grab our first base
- baseItems = [CGGeneric(b.declare(self)) for b in self.bases]
- bases = baseItems[:1]
- # Indent the rest
- bases.extend(CGIndenter(b, len(myself) + len(inherit))
- for b in baseItems[1:])
- result += ",\n".join(b.define() for b in bases)
- result += '\n{\n'
- result += self.extradeclarations
- def declareMembers(cgClass, memberList, defaultVisibility):
- members = {'private': [], 'protected': [], 'public': []}
- for member in memberList:
- members[member.visibility].append(member)
- if defaultVisibility == 'public':
- order = ['public', 'protected', 'private']
- else:
- order = ['private', 'protected', 'public']
- result = ''
- lastVisibility = defaultVisibility
- for visibility in order:
- list = members[visibility]
- if list:
- if visibility != lastVisibility:
- result += visibility + ':\n'
- for member in list:
- result += indent(member.declare(cgClass))
- lastVisibility = visibility
- return (result, lastVisibility)
- if self.disallowCopyConstruction:
- class DisallowedCopyConstructor(object):
- def __init__(self):
- self.visibility = "private"
- def declare(self, cgClass):
- name = cgClass.getNameString()
- return ("%s(const %s&) = delete;\n"
- "void operator=(const %s&) = delete;\n" % (name, name, name))
- disallowedCopyConstructors = [DisallowedCopyConstructor()]
- else:
- disallowedCopyConstructors = []
- order = [self.enums, self.unions,
- self.typedefs, self.members,
- self.constructors + disallowedCopyConstructors,
- self.destructors, self.methods]
- lastVisibility = self.defaultVisibility
- pieces = []
- for memberList in order:
- code, lastVisibility = declareMembers(self, memberList, lastVisibility)
- if code:
- code = code.rstrip() + "\n" # remove extra blank lines at the end
- pieces.append(code)
- result += '\n'.join(pieces)
- result += '};\n'
- result = indent(result, len(self.indent))
- return result
- def define(self):
- def defineMembers(cgClass, memberList, itemCount, separator=''):
- result = ''
- for member in memberList:
- if itemCount != 0:
- result = result + separator
- definition = member.define(cgClass)
- if definition:
- # Member variables would only produce empty lines here.
- result += definition
- itemCount += 1
- return (result, itemCount)
- order = [(self.members, ''), (self.constructors, '\n'),
- (self.destructors, '\n'), (self.methods, '\n')]
- result = self.extradefinitions
- itemCount = 0
- for memberList, separator in order:
- memberString, itemCount = defineMembers(self, memberList,
- itemCount, separator)
- result = result + memberString
- return result
- class CGResolveOwnProperty(CGAbstractStaticMethod):
- def __init__(self, descriptor):
- args = [Argument('JSContext*', 'cx'),
- Argument('JS::Handle<JSObject*>', 'wrapper'),
- Argument('JS::Handle<JSObject*>', 'obj'),
- Argument('JS::Handle<jsid>', 'id'),
- Argument('JS::MutableHandle<JS::PropertyDescriptor>', 'desc'),
- ]
- CGAbstractStaticMethod.__init__(self, descriptor, "ResolveOwnProperty",
- "bool", args)
- def definition_body(self):
- return "return js::GetProxyHandler(obj)->getOwnPropertyDescriptor(cx, wrapper, id, desc);\n"
- class CGResolveOwnPropertyViaResolve(CGAbstractBindingMethod):
- """
- An implementation of Xray ResolveOwnProperty stuff for things that have a
- resolve hook.
- """
- def __init__(self, descriptor):
- args = [Argument('JSContext*', 'cx'),
- Argument('JS::Handle<JSObject*>', 'wrapper'),
- Argument('JS::Handle<JSObject*>', 'obj'),
- Argument('JS::Handle<jsid>', 'id'),
- Argument('JS::MutableHandle<JS::PropertyDescriptor>', 'desc')]
- CGAbstractBindingMethod.__init__(self, descriptor,
- "ResolveOwnPropertyViaResolve",
- args, getThisObj="",
- callArgs="")
- def generate_code(self):
- return CGGeneric(dedent("""
- {
- // Since we're dealing with an Xray, do the resolve on the
- // underlying object first. That gives it a chance to
- // define properties on the actual object as needed, and
- // then use the fact that it created the objects as a flag
- // to avoid re-resolving the properties if someone deletes
- // them.
- JSAutoCompartment ac(cx, obj);
- JS::Rooted<JS::PropertyDescriptor> objDesc(cx);
- if (!self->DoResolve(cx, obj, id, &objDesc)) {
- return false;
- }
- // If desc.value() is undefined, then the DoResolve call
- // has already defined the property on the object. Don't
- // try to also define it.
- if (objDesc.object() &&
- !objDesc.value().isUndefined() &&
- !JS_DefinePropertyById(cx, obj, id, objDesc)) {
- return false;
- }
- }
- return self->DoResolve(cx, wrapper, id, desc);
- """))
- class CGEnumerateOwnProperties(CGAbstractStaticMethod):
- def __init__(self, descriptor):
- args = [Argument('JSContext*', 'cx'),
- Argument('JS::Handle<JSObject*>', 'wrapper'),
- Argument('JS::Handle<JSObject*>', 'obj'),
- Argument('JS::AutoIdVector&', 'props')]
- CGAbstractStaticMethod.__init__(self, descriptor,
- "EnumerateOwnProperties", "bool", args)
- def definition_body(self):
- return "return js::GetProxyHandler(obj)->ownPropertyKeys(cx, wrapper, props);\n"
- class CGEnumerateOwnPropertiesViaGetOwnPropertyNames(CGAbstractBindingMethod):
- """
- An implementation of Xray EnumerateOwnProperties stuff for things
- that have a resolve hook.
- """
- def __init__(self, descriptor):
- args = [Argument('JSContext*', 'cx'),
- Argument('JS::Handle<JSObject*>', 'wrapper'),
- Argument('JS::Handle<JSObject*>', 'obj'),
- Argument('JS::AutoIdVector&', 'props')]
- CGAbstractBindingMethod.__init__(self, descriptor,
- "EnumerateOwnPropertiesViaGetOwnPropertyNames",
- args, getThisObj="",
- callArgs="")
- def generate_code(self):
- return CGGeneric(dedent("""
- AutoTArray<nsString, 8> names;
- binding_detail::FastErrorResult rv;
- self->GetOwnPropertyNames(cx, names, rv);
- if (rv.MaybeSetPendingException(cx)) {
- return false;
- }
- // OK to pass null as "proxy" because it's ignored if
- // shadowPrototypeProperties is true
- return AppendNamedPropertyIds(cx, nullptr, names, true, props);
- """))
- class CGPrototypeTraitsClass(CGClass):
- def __init__(self, descriptor, indent=''):
- templateArgs = [Argument('prototypes::ID', 'PrototypeID')]
- templateSpecialization = ['prototypes::id::' + descriptor.name]
- enums = [ClassEnum('', ['Depth'],
- [descriptor.interface.inheritanceDepth()])]
- CGClass.__init__(self, 'PrototypeTraits', indent=indent,
- templateArgs=templateArgs,
- templateSpecialization=templateSpecialization,
- enums=enums, isStruct=True)
- def deps(self):
- return set()
- class CGClassForwardDeclare(CGThing):
- def __init__(self, name, isStruct=False):
- CGThing.__init__(self)
- self.name = name
- self.isStruct = isStruct
- def declare(self):
- type = 'struct' if self.isStruct else 'class'
- return '%s %s;\n' % (type, self.name)
- def define(self):
- # Header only
- return ''
- def deps(self):
- return set()
- class CGProxySpecialOperation(CGPerSignatureCall):
- """
- Base class for classes for calling an indexed or named special operation
- (don't use this directly, use the derived classes below).
- If checkFound is False, will just assert that the prop is found instead of
- checking that it is before wrapping the value.
- resultVar: See the docstring for CGCallGenerator.
- foundVar: For getters and deleters, the generated code can also set a bool
- variable, declared by the caller, if the given indexed or named property
- already existed. If the caller wants this, it should pass the name of the
- bool variable as the foundVar keyword argument to the constructor. The
- caller is responsible for declaring the variable and initializing it to
- false.
- """
- def __init__(self, descriptor, operation, checkFound=True,
- argumentHandleValue=None, resultVar=None, foundVar=None):
- self.checkFound = checkFound
- self.foundVar = foundVar or "found"
- nativeName = MakeNativeName(descriptor.binaryNameFor(operation))
- operation = descriptor.operations[operation]
- assert len(operation.signatures()) == 1
- signature = operation.signatures()[0]
- returnType, arguments = signature
- # We pass len(arguments) as the final argument so that the
- # CGPerSignatureCall won't do any argument conversion of its own.
- CGPerSignatureCall.__init__(self, returnType, arguments, nativeName,
- False, descriptor, operation,
- len(arguments), resultVar=resultVar,
- objectName="proxy")
- if operation.isSetter() or operation.isCreator():
- # arguments[0] is the index or name of the item that we're setting.
- argument = arguments[1]
- info = getJSToNativeConversionInfo(
- argument.type, descriptor,
- treatNullAs=argument.treatNullAs,
- sourceDescription=("value being assigned to %s setter" %
- descriptor.interface.identifier.name))
- if argumentHandleValue is None:
- argumentHandleValue = "desc.value()"
- rootedValue = fill(
- """
- JS::Rooted<JS::Value> rootedValue(cx, ${argumentHandleValue});
- """,
- argumentHandleValue = argumentHandleValue)
- templateValues = {
- "declName": argument.identifier.name,
- "holderName": argument.identifier.name + "_holder",
- "val": argumentHandleValue,
- "maybeMutableVal": "&rootedValue",
- "obj": "obj",
- "passedToJSImpl": "false"
- }
- self.cgRoot.prepend(instantiateJSToNativeConversion(info, templateValues))
- # rootedValue needs to come before the conversion, so we
- # need to prepend it last.
- self.cgRoot.prepend(CGGeneric(rootedValue))
- elif operation.isGetter() or operation.isDeleter():
- if foundVar is None:
- self.cgRoot.prepend(CGGeneric("bool found = false;\n"))
- def getArguments(self):
- args = [(a, a.identifier.name) for a in self.arguments]
- if self.idlNode.isGetter() or self.idlNode.isDeleter():
- args.append((FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean],
- self.idlNode),
- self.foundVar))
- return args
- def wrap_return_value(self):
- if not self.idlNode.isGetter() or self.templateValues is None:
- return ""
- wrap = CGGeneric(wrapForType(self.returnType, self.descriptor, self.templateValues))
- if self.checkFound:
- wrap = CGIfWrapper(wrap, self.foundVar)
- else:
- wrap = CGList([CGGeneric("MOZ_ASSERT(" + self.foundVar + ");\n"), wrap])
- return "\n" + wrap.define()
- class CGProxyIndexedOperation(CGProxySpecialOperation):
- """
- Class to generate a call to an indexed operation.
- If doUnwrap is False, the caller is responsible for making sure a variable
- named 'self' holds the C++ object somewhere where the code we generate
- will see it.
- If checkFound is False, will just assert that the prop is found instead of
- checking that it is before wrapping the value.
- resultVar: See the docstring for CGCallGenerator.
- foundVar: See the docstring for CGProxySpecialOperation.
- """
- def __init__(self, descriptor, name, doUnwrap=True, checkFound=True,
- argumentHandleValue=None, resultVar=None, foundVar=None):
- self.doUnwrap = doUnwrap
- CGProxySpecialOperation.__init__(self, descriptor, name, checkFound,
- argumentHandleValue=argumentHandleValue,
- resultVar=resultVar,
- foundVar=foundVar)
- def define(self):
- # Our first argument is the id we're getting.
- argName = self.arguments[0].identifier.name
- if argName == "index":
- # We already have our index in a variable with that name
- setIndex = ""
- else:
- setIndex = "uint32_t %s = index;\n" % argName
- if self.doUnwrap:
- unwrap = "%s* self = UnwrapProxy(proxy);\n" % self.descriptor.nativeType
- else:
- unwrap = ""
- return (setIndex + unwrap +
- CGProxySpecialOperation.define(self))
- class CGProxyIndexedGetter(CGProxyIndexedOperation):
- """
- Class to generate a call to an indexed getter. If templateValues is not None
- the returned value will be wrapped with wrapForType using templateValues.
- If doUnwrap is False, the caller is responsible for making sure a variable
- named 'self' holds the C++ object somewhere where the code we generate
- will see it.
- If checkFound is False, will just assert that the prop is found instead of
- checking that it is before wrapping the value.
- foundVar: See the docstring for CGProxySpecialOperation.
- """
- def __init__(self, descriptor, templateValues=None, doUnwrap=True,
- checkFound=True, foundVar=None):
- self.templateValues = templateValues
- CGProxyIndexedOperation.__init__(self, descriptor, 'IndexedGetter',
- doUnwrap, checkFound, foundVar=foundVar)
- class CGProxyIndexedPresenceChecker(CGProxyIndexedGetter):
- """
- Class to generate a call that checks whether an indexed property exists.
- For now, we just delegate to CGProxyIndexedGetter
- foundVar: See the docstring for CGProxySpecialOperation.
- """
- def __init__(self, descriptor, foundVar):
- CGProxyIndexedGetter.__init__(self, descriptor, foundVar=foundVar)
- self.cgRoot.append(CGGeneric("(void)result;\n"))
- class CGProxyIndexedSetter(CGProxyIndexedOperation):
- """
- Class to generate a call to an indexed setter.
- """
- def __init__(self, descriptor, argumentHandleValue=None):
- CGProxyIndexedOperation.__init__(self, descriptor, 'IndexedSetter',
- argumentHandleValue=argumentHandleValue)
- class CGProxyNamedOperation(CGProxySpecialOperation):
- """
- Class to generate a call to a named operation.
- 'value' is the jsval to use for the name; None indicates that it should be
- gotten from the property id.
- resultVar: See the docstring for CGCallGenerator.
- foundVar: See the docstring for CGProxySpecialOperation.
- """
- def __init__(self, descriptor, name, value=None, argumentHandleValue=None,
- resultVar=None, foundVar=None):
- CGProxySpecialOperation.__init__(self, descriptor, name,
- argumentHandleValue=argumentHandleValue,
- resultVar=resultVar,
- foundVar=foundVar)
- self.value = value
- def define(self):
- # Our first argument is the id we're getting.
- argName = self.arguments[0].identifier.name
- if argName == "id":
- # deal with the name collision
- decls = "JS::Rooted<jsid> id_(cx, id);\n"
- idName = "id_"
- else:
- decls = ""
- idName = "id"
- decls += "binding_detail::FakeString %s;\n" % argName
- main = fill(
- """
- ${nativeType}* self = UnwrapProxy(proxy);
- $*{op}
- """,
- nativeType=self.descriptor.nativeType,
- op=CGProxySpecialOperation.define(self))
- if self.value is None:
- return fill(
- """
- $*{decls}
- bool isSymbol;
- if (!ConvertIdToString(cx, ${idName}, ${argName}, isSymbol)) {
- return false;
- }
- if (!isSymbol) {
- $*{main}
- }
- """,
- decls=decls,
- idName=idName,
- argName=argName,
- main=main)
- # Sadly, we have to set up nameVal even if we have an atom id,
- # because we don't know for sure, and we can end up needing it
- # so it needs to be higher up the stack. Using a Maybe here
- # seems like probable overkill.
- return fill(
- """
- $*{decls}
- JS::Rooted<JS::Value> nameVal(cx, ${value});
- if (!nameVal.isSymbol()) {
- if (!ConvertJSValueToString(cx, nameVal, eStringify, eStringify,
- ${argName})) {
- return false;
- }
- $*{main}
- }
- """,
- decls=decls,
- value=self.value,
- argName=argName,
- main=main)
- class CGProxyNamedGetter(CGProxyNamedOperation):
- """
- Class to generate a call to an named getter. If templateValues is not None
- the returned value will be wrapped with wrapForType using templateValues.
- 'value' is the jsval to use for the name; None indicates that it should be
- gotten from the property id.
- foundVar: See the docstring for CGProxySpecialOperation.
- """
- def __init__(self, descriptor, templateValues=None, value=None,
- foundVar=None):
- self.templateValues = templateValues
- CGProxyNamedOperation.__init__(self, descriptor, 'NamedGetter', value,
- foundVar=foundVar)
- class CGProxyNamedPresenceChecker(CGProxyNamedGetter):
- """
- Class to generate a call that checks whether a named property exists.
- For now, we just delegate to CGProxyNamedGetter
- foundVar: See the docstring for CGProxySpecialOperation.
- """
- def __init__(self, descriptor, foundVar=None):
- CGProxyNamedGetter.__init__(self, descriptor, foundVar=foundVar)
- self.cgRoot.append(CGGeneric("(void)result;\n"))
- class CGProxyNamedSetter(CGProxyNamedOperation):
- """
- Class to generate a call to a named setter.
- """
- def __init__(self, descriptor, argumentHandleValue=None):
- CGProxyNamedOperation.__init__(self, descriptor, 'NamedSetter',
- argumentHandleValue=argumentHandleValue)
- class CGProxyNamedDeleter(CGProxyNamedOperation):
- """
- Class to generate a call to a named deleter.
- resultVar: See the docstring for CGCallGenerator.
- foundVar: See the docstring for CGProxySpecialOperation.
- """
- def __init__(self, descriptor, resultVar=None, foundVar=None):
- CGProxyNamedOperation.__init__(self, descriptor, 'NamedDeleter',
- resultVar=resultVar,
- foundVar=foundVar)
- class CGProxyIsProxy(CGAbstractMethod):
- def __init__(self, descriptor):
- args = [Argument('JSObject*', 'obj')]
- CGAbstractMethod.__init__(self, descriptor, "IsProxy", "bool", args, alwaysInline=True)
- def declare(self):
- return ""
- def definition_body(self):
- return "return js::IsProxy(obj) && js::GetProxyHandler(obj) == DOMProxyHandler::getInstance();\n"
- class CGProxyUnwrap(CGAbstractMethod):
- def __init__(self, descriptor):
- args = [Argument('JSObject*', 'obj')]
- CGAbstractMethod.__init__(self, descriptor, "UnwrapProxy", descriptor.nativeType + '*', args, alwaysInline=True)
- def declare(self):
- return ""
- def definition_body(self):
- return fill(
- """
- MOZ_ASSERT(js::IsProxy(obj));
- if (js::GetProxyHandler(obj) != DOMProxyHandler::getInstance()) {
- MOZ_ASSERT(xpc::WrapperFactory::IsXrayWrapper(obj));
- obj = js::UncheckedUnwrap(obj);
- }
- MOZ_ASSERT(IsProxy(obj));
- return static_cast<${type}*>(js::GetProxyPrivate(obj).toPrivate());
- """,
- type=self.descriptor.nativeType)
- class CGDOMJSProxyHandler_getOwnPropDescriptor(ClassMethod):
- def __init__(self, descriptor):
- args = [Argument('JSContext*', 'cx'),
- Argument('JS::Handle<JSObject*>', 'proxy'),
- Argument('JS::Handle<jsid>', 'id'),
- Argument('bool', 'ignoreNamedProps'),
- Argument('JS::MutableHandle<JS::PropertyDescriptor>', 'desc')]
- ClassMethod.__init__(self, "getOwnPropDescriptor", "bool", args,
- virtual=True, override=True, const=True)
- self.descriptor = descriptor
- def getBody(self):
- indexedGetter = self.descriptor.operations['IndexedGetter']
- indexedSetter = self.descriptor.operations['IndexedSetter']
- if self.descriptor.supportsIndexedProperties():
- readonly = toStringBool(indexedSetter is None)
- fillDescriptor = "FillPropertyDescriptor(desc, proxy, %s);\nreturn true;\n" % readonly
- templateValues = {
- 'jsvalRef': 'desc.value()',
- 'jsvalHandle': 'desc.value()',
- 'obj': 'proxy',
- 'successCode': fillDescriptor
- }
- getIndexed = fill(
- """
- uint32_t index = GetArrayIndexFromId(cx, id);
- if (IsArrayIndex(index)) {
- $*{callGetter}
- }
- """,
- callGetter=CGProxyIndexedGetter(self.descriptor, templateValues).define())
- else:
- getIndexed = ""
- if self.descriptor.supportsNamedProperties():
- operations = self.descriptor.operations
- readonly = toStringBool(operations['NamedSetter'] is None)
- fillDescriptor = (
- "FillPropertyDescriptor(desc, proxy, %s, %s);\n"
- "return true;\n" %
- (readonly,
- toStringBool(self.descriptor.namedPropertiesEnumerable)))
- templateValues = {'jsvalRef': 'desc.value()', 'jsvalHandle': 'desc.value()',
- 'obj': 'proxy', 'successCode': fillDescriptor}
- computeCondition = dedent("""
- bool hasOnProto;
- if (!HasPropertyOnPrototype(cx, proxy, id, &hasOnProto)) {
- return false;
- }
- callNamedGetter = !hasOnProto;
- """)
- if self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
- computeCondition = fill(
- """
- if (!isXray) {
- callNamedGetter = true;
- } else {
- $*{hasOnProto}
- }
- """,
- hasOnProto=computeCondition)
- outerCondition = "!ignoreNamedProps"
- if self.descriptor.supportsIndexedProperties():
- outerCondition = "!IsArrayIndex(index) && " + outerCondition
- namedGetCode = CGProxyNamedGetter(self.descriptor,
- templateValues).define()
- namedGet = fill("""
- bool callNamedGetter = false;
- if (${outerCondition}) {
- $*{computeCondition}
- }
- if (callNamedGetter) {
- $*{namedGetCode}
- }
- """,
- outerCondition=outerCondition,
- computeCondition=computeCondition,
- namedGetCode=namedGetCode)
- namedGet += "\n"
- else:
- namedGet = ""
- return fill(
- """
- bool isXray = xpc::WrapperFactory::IsXrayWrapper(proxy);
- $*{getIndexed}
- JS::Rooted<JSObject*> expando(cx);
- if (!isXray && (expando = GetExpandoObject(proxy))) {
- if (!JS_GetOwnPropertyDescriptorById(cx, expando, id, desc)) {
- return false;
- }
- if (desc.object()) {
- // Pretend the property lives on the wrapper.
- desc.object().set(proxy);
- return true;
- }
- }
- $*{namedGet}
- desc.object().set(nullptr);
- return true;
- """,
- getIndexed=getIndexed,
- namedGet=namedGet)
- class CGDOMJSProxyHandler_defineProperty(ClassMethod):
- def __init__(self, descriptor):
- # The usual convention is to name the ObjectOpResult out-parameter
- # `result`, but that name is a bit overloaded around here.
- args = [Argument('JSContext*', 'cx'),
- Argument('JS::Handle<JSObject*>', 'proxy'),
- Argument('JS::Handle<jsid>', 'id'),
- Argument('JS::Handle<JS::PropertyDescriptor>', 'desc'),
- Argument('JS::ObjectOpResult&', 'opresult'),
- Argument('bool*', 'defined')]
- ClassMethod.__init__(self, "defineProperty", "bool", args, virtual=True, override=True, const=True)
- self.descriptor = descriptor
- def getBody(self):
- set = ""
- indexedSetter = self.descriptor.operations['IndexedSetter']
- if indexedSetter:
- if self.descriptor.operations['IndexedCreator'] is not indexedSetter:
- raise TypeError("Can't handle creator that's different from the setter")
- set += fill(
- """
- uint32_t index = GetArrayIndexFromId(cx, id);
- if (IsArrayIndex(index)) {
- *defined = true;
- $*{callSetter}
- return opresult.succeed();
- }
- """,
- callSetter=CGProxyIndexedSetter(self.descriptor).define())
- elif self.descriptor.supportsIndexedProperties():
- # We allow untrusted content to prevent Xrays from setting a
- # property if that property is an indexed property and we have no
- # indexed setter. That's how the object would normally behave if
- # you tried to set the property on it. That means we don't need to
- # do anything special for Xrays here.
- set += dedent(
- """
- if (IsArrayIndex(GetArrayIndexFromId(cx, id))) {
- *defined = true;
- return opresult.failNoIndexedSetter();
- }
- """)
- namedSetter = self.descriptor.operations['NamedSetter']
- if namedSetter:
- if self.descriptor.hasUnforgeableMembers:
- raise TypeError("Can't handle a named setter on an interface "
- "that has unforgeables. Figure out how that "
- "should work!")
- if self.descriptor.operations['NamedCreator'] is not namedSetter:
- raise TypeError("Can't handle creator that's different from the setter")
- # If we support indexed properties, we won't get down here for
- # indices, so we can just do our setter unconditionally here.
- set += fill(
- """
- *defined = true;
- $*{callSetter}
- return opresult.succeed();
- """,
- callSetter=CGProxyNamedSetter(self.descriptor).define())
- else:
- # We allow untrusted content to prevent Xrays from setting a
- # property if that property is already a named property on the
- # object and we have no named setter. That's how the object would
- # normally behave if you tried to set the property on it. That
- # means we don't need to do anything special for Xrays here.
- if self.descriptor.supportsNamedProperties():
- set += fill(
- """
- bool found = false;
- $*{presenceChecker}
- if (found) {
- *defined = true;
- return opresult.failNoNamedSetter();
- }
- """,
- presenceChecker=CGProxyNamedPresenceChecker(self.descriptor, foundVar="found").define())
- set += ("return mozilla::dom::DOMProxyHandler::defineProperty(%s);\n" %
- ", ".join(a.name for a in self.args))
- return set
- def getDeleterBody(descriptor, type, foundVar=None):
- """
- type should be "Named" or "Indexed"
- The possible outcomes:
- - an error happened (the emitted code returns false)
- - own property not found (foundVar=false, deleteSucceeded=true)
- - own property found and deleted (foundVar=true, deleteSucceeded=true)
- - own property found but can't be deleted (foundVar=true, deleteSucceeded=false)
- """
- assert type in ("Named", "Indexed")
- deleter = descriptor.operations[type + 'Deleter']
- if deleter:
- assert type == "Named"
- assert foundVar is not None
- if descriptor.hasUnforgeableMembers:
- raise TypeError("Can't handle a deleter on an interface "
- "that has unforgeables. Figure out how "
- "that should work!")
- # See if the deleter method is fallible.
- t = deleter.signatures()[0][0]
- if t.isPrimitive() and not t.nullable() and t.tag() == IDLType.Tags.bool:
- # The deleter method has a boolean return value. When a
- # property is found, the return value indicates whether it
- # was successfully deleted.
- setDS = fill(
- """
- if (!${foundVar}) {
- deleteSucceeded = true;
- }
- """,
- foundVar=foundVar)
- else:
- # No boolean return value: if a property is found,
- # deleting it always succeeds.
- setDS = "deleteSucceeded = true;\n"
- body = (CGProxyNamedDeleter(descriptor,
- resultVar="deleteSucceeded",
- foundVar=foundVar).define() +
- setDS)
- elif getattr(descriptor, "supports%sProperties" % type)():
- presenceCheckerClass = globals()["CGProxy%sPresenceChecker" % type]
- foundDecl = ""
- if foundVar is None:
- foundVar = "found"
- foundDecl = "bool found = false;\n"
- body = fill(
- """
- $*{foundDecl}
- $*{presenceChecker}
- deleteSucceeded = !${foundVar};
- """,
- foundDecl=foundDecl,
- presenceChecker=presenceCheckerClass(descriptor, foundVar=foundVar).define(),
- foundVar=foundVar)
- else:
- body = None
- return body
- class CGDeleteNamedProperty(CGAbstractStaticMethod):
- def __init__(self, descriptor):
- args = [Argument('JSContext*', 'cx'),
- Argument('JS::Handle<JSObject*>', 'xray'),
- Argument('JS::Handle<JSObject*>', 'proxy'),
- Argument('JS::Handle<jsid>', 'id'),
- Argument('JS::ObjectOpResult&', 'opresult')]
- CGAbstractStaticMethod.__init__(self, descriptor, "DeleteNamedProperty",
- "bool", args)
- def definition_body(self):
- return fill(
- """
- MOZ_ASSERT(xpc::WrapperFactory::IsXrayWrapper(xray));
- MOZ_ASSERT(js::IsProxy(proxy));
- MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy));
- JSAutoCompartment ac(cx, proxy);
- bool deleteSucceeded;
- bool found = false;
- $*{namedBody}
- if (!found || deleteSucceeded) {
- return opresult.succeed();
- }
- return opresult.failCantDelete();
- """,
- namedBody=getDeleterBody(self.descriptor, "Named", foundVar="found"))
- class CGDOMJSProxyHandler_delete(ClassMethod):
- def __init__(self, descriptor):
- args = [Argument('JSContext*', 'cx'),
- Argument('JS::Handle<JSObject*>', 'proxy'),
- Argument('JS::Handle<jsid>', 'id'),
- Argument('JS::ObjectOpResult&', 'opresult')]
- ClassMethod.__init__(self, "delete_", "bool", args,
- virtual=True, override=True, const=True)
- self.descriptor = descriptor
- def getBody(self):
- delete = dedent("""
- MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
- "Should not have a XrayWrapper here");
- """)
- indexedBody = getDeleterBody(self.descriptor, "Indexed")
- if indexedBody is not None:
- delete += fill(
- """
- uint32_t index = GetArrayIndexFromId(cx, id);
- if (IsArrayIndex(index)) {
- bool deleteSucceeded;
- $*{indexedBody}
- return deleteSucceeded ? opresult.succeed() : opresult.failCantDelete();
- }
- """,
- indexedBody=indexedBody)
- namedBody = getDeleterBody(self.descriptor, "Named", foundVar="found")
- if namedBody is not None:
- delete += dedent(
- """
- // Try named delete only if the named property visibility
- // algorithm says the property is visible.
- bool tryNamedDelete = true;
- { // Scope for expando
- JS::Rooted<JSObject*> expando(cx, DOMProxyHandler::GetExpandoObject(proxy));
- if (expando) {
- bool hasProp;
- if (!JS_HasPropertyById(cx, expando, id, &hasProp)) {
- return false;
- }
- tryNamedDelete = !hasProp;
- }
- }
- """)
- if not self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
- delete += dedent(
- """
- if (tryNamedDelete) {
- bool hasOnProto;
- if (!HasPropertyOnPrototype(cx, proxy, id, &hasOnProto)) {
- return false;
- }
- tryNamedDelete = !hasOnProto;
- }
- """)
- # We always return above for an index id in the case when we support
- # indexed properties, so we can just treat the id as a name
- # unconditionally here.
- delete += fill(
- """
- if (tryNamedDelete) {
- bool found = false;
- bool deleteSucceeded;
- $*{namedBody}
- if (found) {
- return deleteSucceeded ? opresult.succeed() : opresult.failCantDelete();
- }
- }
- """,
- namedBody=namedBody)
- delete += dedent("""
- return dom::DOMProxyHandler::delete_(cx, proxy, id, opresult);
- """)
- return delete
- class CGDOMJSProxyHandler_ownPropNames(ClassMethod):
- def __init__(self, descriptor, ):
- args = [Argument('JSContext*', 'cx'),
- Argument('JS::Handle<JSObject*>', 'proxy'),
- Argument('unsigned', 'flags'),
- Argument('JS::AutoIdVector&', 'props')]
- ClassMethod.__init__(self, "ownPropNames", "bool", args,
- virtual=True, override=True, const=True)
- self.descriptor = descriptor
- def getBody(self):
- # Per spec, we do indices, then named props, then everything else
- if self.descriptor.supportsIndexedProperties():
- addIndices = dedent("""
- uint32_t length = UnwrapProxy(proxy)->Length();
- MOZ_ASSERT(int32_t(length) >= 0);
- for (int32_t i = 0; i < int32_t(length); ++i) {
- if (!props.append(INT_TO_JSID(i))) {
- return false;
- }
- }
- """)
- else:
- addIndices = ""
- if self.descriptor.supportsNamedProperties():
- if self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
- shadow = "!isXray"
- else:
- shadow = "false"
- addNames = fill(
- """
- nsTArray<nsString> names;
- UnwrapProxy(proxy)->GetSupportedNames(names);
- if (!AppendNamedPropertyIds(cx, proxy, names, ${shadow}, props)) {
- return false;
- }
- """,
- shadow=shadow)
- if not self.descriptor.namedPropertiesEnumerable:
- addNames = CGIfWrapper(CGGeneric(addNames),
- "flags & JSITER_HIDDEN").define()
- addNames = "\n" + addNames
- else:
- addNames = ""
- return fill(
- """
- bool isXray = xpc::WrapperFactory::IsXrayWrapper(proxy);
- $*{addIndices}
- $*{addNames}
- JS::Rooted<JSObject*> expando(cx);
- if (!isXray && (expando = DOMProxyHandler::GetExpandoObject(proxy)) &&
- !js::GetPropertyKeys(cx, expando, flags, &props)) {
- return false;
- }
- return true;
- """,
- addIndices=addIndices,
- addNames=addNames)
- class CGDOMJSProxyHandler_hasOwn(ClassMethod):
- def __init__(self, descriptor):
- args = [Argument('JSContext*', 'cx'),
- Argument('JS::Handle<JSObject*>', 'proxy'),
- Argument('JS::Handle<jsid>', 'id'),
- Argument('bool*', 'bp')]
- ClassMethod.__init__(self, "hasOwn", "bool", args,
- virtual=True, override=True, const=True)
- self.descriptor = descriptor
- def getBody(self):
- if self.descriptor.supportsIndexedProperties():
- indexed = fill(
- """
- uint32_t index = GetArrayIndexFromId(cx, id);
- if (IsArrayIndex(index)) {
- bool found = false;
- $*{presenceChecker}
- *bp = found;
- return true;
- }
- """,
- presenceChecker=CGProxyIndexedPresenceChecker(self.descriptor, foundVar="found").define())
- else:
- indexed = ""
- if self.descriptor.supportsNamedProperties():
- # If we support indexed properties we always return above for index
- # property names, so no need to check for those here.
- named = fill(
- """
- bool found = false;
- $*{presenceChecker}
- *bp = found;
- """,
- presenceChecker=CGProxyNamedPresenceChecker(self.descriptor, foundVar="found").define())
- if not self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
- named = fill(
- """
- bool hasOnProto;
- if (!HasPropertyOnPrototype(cx, proxy, id, &hasOnProto)) {
- return false;
- }
- if (!hasOnProto) {
- $*{protoLacksProperty}
- return true;
- }
- """,
- protoLacksProperty=named)
- named += "*bp = false;\n"
- else:
- named += "\n"
- else:
- named = "*bp = false;\n"
- return fill(
- """
- MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
- "Should not have a XrayWrapper here");
- $*{indexed}
- JS::Rooted<JSObject*> expando(cx, GetExpandoObject(proxy));
- if (expando) {
- bool b = true;
- bool ok = JS_HasPropertyById(cx, expando, id, &b);
- *bp = !!b;
- if (!ok || *bp) {
- return ok;
- }
- }
- $*{named}
- return true;
- """,
- indexed=indexed,
- named=named)
- class CGDOMJSProxyHandler_get(ClassMethod):
- def __init__(self, descriptor):
- args = [Argument('JSContext*', 'cx'),
- Argument('JS::Handle<JSObject*>', 'proxy'),
- Argument('JS::Handle<JS::Value>', 'receiver'),
- Argument('JS::Handle<jsid>', 'id'),
- Argument('JS::MutableHandle<JS::Value>', 'vp')]
- ClassMethod.__init__(self, "get", "bool", args,
- virtual=True, override=True, const=True)
- self.descriptor = descriptor
- def getBody(self):
- getUnforgeableOrExpando = dedent("""
- { // Scope for expando
- JS::Rooted<JSObject*> expando(cx, DOMProxyHandler::GetExpandoObject(proxy));
- if (expando) {
- bool hasProp;
- if (!JS_HasPropertyById(cx, expando, id, &hasProp)) {
- return false;
- }
- if (hasProp) {
- // Forward the get to the expando object, but our receiver is whatever our
- // receiver is.
- return JS_ForwardGetPropertyTo(cx, expando, id, receiver, vp);
- }
- }
- }
- """)
- templateValues = {'jsvalRef': 'vp', 'jsvalHandle': 'vp', 'obj': 'proxy'}
- if self.descriptor.supportsIndexedProperties():
- getIndexedOrExpando = fill(
- """
- uint32_t index = GetArrayIndexFromId(cx, id);
- if (IsArrayIndex(index)) {
- $*{callGetter}
- // Even if we don't have this index, we don't forward the
- // get on to our expando object.
- } else {
- $*{getUnforgeableOrExpando}
- }
- """,
- callGetter=CGProxyIndexedGetter(self.descriptor, templateValues).define(),
- getUnforgeableOrExpando=getUnforgeableOrExpando)
- else:
- getIndexedOrExpando = getUnforgeableOrExpando
- if self.descriptor.supportsNamedProperties():
- getNamed = CGProxyNamedGetter(self.descriptor, templateValues)
- if self.descriptor.supportsIndexedProperties():
- getNamed = CGIfWrapper(getNamed, "!IsArrayIndex(index)")
- getNamed = getNamed.define() + "\n"
- else:
- getNamed = ""
- getOnPrototype = dedent("""
- bool foundOnPrototype;
- if (!GetPropertyOnPrototype(cx, proxy, receiver, id, &foundOnPrototype, vp)) {
- return false;
- }
- if (foundOnPrototype) {
- return true;
- }
- """)
- if self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
- getNamed = getNamed + getOnPrototype
- else:
- getNamed = getOnPrototype + getNamed
- return fill(
- """
- MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
- "Should not have a XrayWrapper here");
- $*{indexedOrExpando}
- $*{named}
- vp.setUndefined();
- return true;
- """,
- indexedOrExpando=getIndexedOrExpando,
- named=getNamed)
- class CGDOMJSProxyHandler_setCustom(ClassMethod):
- def __init__(self, descriptor):
- args = [Argument('JSContext*', 'cx'),
- Argument('JS::Handle<JSObject*>', 'proxy'),
- Argument('JS::Handle<jsid>', 'id'),
- Argument('JS::Handle<JS::Value>', 'v'),
- Argument('bool*', 'done')]
- ClassMethod.__init__(self, "setCustom", "bool", args, virtual=True, override=True, const=True)
- self.descriptor = descriptor
- def getBody(self):
- assertion = ("MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),\n"
- ' "Should not have a XrayWrapper here");\n')
- # Correctness first. If we have a NamedSetter and [OverrideBuiltins],
- # always call the NamedSetter and never do anything else.
- namedSetter = self.descriptor.operations['NamedSetter']
- if (namedSetter is not None and
- self.descriptor.interface.getExtendedAttribute('OverrideBuiltins')):
- # Check assumptions.
- if self.descriptor.supportsIndexedProperties():
- raise ValueError("In interface " + self.descriptor.name + ": " +
- "Can't cope with [OverrideBuiltins] and an indexed getter")
- if self.descriptor.operations['NamedCreator'] is not namedSetter:
- raise ValueError("In interface " + self.descriptor.name + ": " +
- "Can't cope with named setter that is not also a named creator")
- if self.descriptor.hasUnforgeableMembers:
- raise ValueError("In interface " + self.descriptor.name + ": " +
- "Can't cope with [OverrideBuiltins] and unforgeable members")
- callSetter = CGProxyNamedSetter(self.descriptor, argumentHandleValue="v")
- return (assertion +
- callSetter.define() +
- "*done = true;\n"
- "return true;\n")
- # As an optimization, if we are going to call an IndexedSetter, go
- # ahead and call it and have done.
- indexedSetter = self.descriptor.operations['IndexedSetter']
- if indexedSetter is not None:
- if self.descriptor.operations['IndexedCreator'] is not indexedSetter:
- raise ValueError("In interface " + self.descriptor.name + ": " +
- "Can't cope with indexed setter that is not " +
- "also an indexed creator")
- setIndexed = fill(
- """
- uint32_t index = GetArrayIndexFromId(cx, id);
- if (IsArrayIndex(index)) {
- $*{callSetter}
- *done = true;
- return true;
- }
- """,
- callSetter=CGProxyIndexedSetter(self.descriptor,
- argumentHandleValue="v").define())
- else:
- setIndexed = ""
- return (assertion +
- setIndexed +
- "*done = false;\n"
- "return true;\n")
- class CGDOMJSProxyHandler_className(ClassMethod):
- def __init__(self, descriptor):
- args = [Argument('JSContext*', 'cx'),
- Argument('JS::Handle<JSObject*>', 'proxy')]
- ClassMethod.__init__(self, "className", "const char*", args,
- virtual=True, override=True, const=True)
- self.descriptor = descriptor
- def getBody(self):
- return 'return "%s";\n' % self.descriptor.name
- class CGDOMJSProxyHandler_finalizeInBackground(ClassMethod):
- def __init__(self, descriptor):
- args = [Argument('const JS::Value&', 'priv')]
- ClassMethod.__init__(self, "finalizeInBackground", "bool", args,
- virtual=True, override=True, const=True)
- self.descriptor = descriptor
- def getBody(self):
- return "return false;\n"
- class CGDOMJSProxyHandler_finalize(ClassMethod):
- def __init__(self, descriptor):
- args = [Argument('JSFreeOp*', 'fop'), Argument('JSObject*', 'proxy')]
- ClassMethod.__init__(self, "finalize", "void", args,
- virtual=True, override=True, const=True)
- self.descriptor = descriptor
- def getBody(self):
- return (("%s* self = UnwrapPossiblyNotInitializedDOMObject<%s>(proxy);\n" %
- (self.descriptor.nativeType, self.descriptor.nativeType)) +
- finalizeHook(self.descriptor, FINALIZE_HOOK_NAME, self.args[0].name).define())
- class CGDOMJSProxyHandler_getElements(ClassMethod):
- def __init__(self, descriptor):
- assert descriptor.supportsIndexedProperties()
- args = [Argument('JSContext*', 'cx'),
- Argument('JS::Handle<JSObject*>', 'proxy'),
- Argument('uint32_t', 'begin'),
- Argument('uint32_t', 'end'),
- Argument('js::ElementAdder*', 'adder')]
- ClassMethod.__init__(self, "getElements", "bool", args, virtual=True, override=True, const=True)
- self.descriptor = descriptor
- def getBody(self):
- # Just like ownPropertyKeys we'll assume that we have no holes, so
- # we have all properties from 0 to length. If that ever changes
- # (unlikely), we'll need to do something a bit more clever with how we
- # forward on to our ancestor.
- templateValues = {
- 'jsvalRef': 'temp',
- 'jsvalHandle': '&temp',
- 'obj': 'proxy',
- 'successCode': ("if (!adder->append(cx, temp)) return false;\n"
- "continue;\n")
- }
- get = CGProxyIndexedGetter(self.descriptor, templateValues, False, False).define()
- return fill(
- """
- JS::Rooted<JS::Value> temp(cx);
- MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
- "Should not have a XrayWrapper here");
- ${nativeType}* self = UnwrapProxy(proxy);
- uint32_t length = self->Length();
- // Compute the end of the indices we'll get ourselves
- uint32_t ourEnd = std::max(begin, std::min(end, length));
- for (uint32_t index = begin; index < ourEnd; ++index) {
- $*{get}
- }
- if (end > ourEnd) {
- JS::Rooted<JSObject*> proto(cx);
- if (!js::GetObjectProto(cx, proxy, &proto)) {
- return false;
- }
- return js::GetElementsWithAdder(cx, proto, proxy, ourEnd, end, adder);
- }
- return true;
- """,
- nativeType=self.descriptor.nativeType,
- get=get)
- class CGDOMJSProxyHandler_getInstance(ClassMethod):
- def __init__(self):
- ClassMethod.__init__(self, "getInstance", "const DOMProxyHandler*", [], static=True)
- def getBody(self):
- return dedent("""
- static const DOMProxyHandler instance;
- return &instance;
- """)
- class CGDOMJSProxyHandler_getPrototypeIfOrdinary(ClassMethod):
- def __init__(self):
- args = [Argument('JSContext*', 'cx'),
- Argument('JS::Handle<JSObject*>', 'proxy'),
- Argument('bool*', 'isOrdinary'),
- Argument('JS::MutableHandle<JSObject*>', 'proto')]
- ClassMethod.__init__(self, "getPrototypeIfOrdinary", "bool", args,
- virtual=True, override=True, const=True)
- def getBody(self):
- return dedent("""
- *isOrdinary = false;
- return true;
- """)
- class CGDOMJSProxyHandler_call(ClassMethod):
- def __init__(self):
- args = [Argument('JSContext*', 'cx'),
- Argument('JS::Handle<JSObject*>', 'proxy'),
- Argument('const JS::CallArgs&', 'args')]
- ClassMethod.__init__(self, "call", "bool", args, virtual=True, override=True, const=True)
- def getBody(self):
- return fill(
- """
- return js::ForwardToNative(cx, ${legacyCaller}, args);
- """,
- legacyCaller=LEGACYCALLER_HOOK_NAME)
- class CGDOMJSProxyHandler_isCallable(ClassMethod):
- def __init__(self):
- ClassMethod.__init__(self, "isCallable", "bool",
- [Argument('JSObject*', 'obj')],
- virtual=True, override=True, const=True)
- def getBody(self):
- return dedent("""
- return true;
- """)
- class CGDOMJSProxyHandler(CGClass):
- def __init__(self, descriptor):
- assert (descriptor.supportsIndexedProperties() or
- descriptor.supportsNamedProperties() or
- descriptor.hasNonOrdinaryGetPrototypeOf())
- methods = [CGDOMJSProxyHandler_getOwnPropDescriptor(descriptor),
- CGDOMJSProxyHandler_defineProperty(descriptor),
- ClassUsingDeclaration("mozilla::dom::DOMProxyHandler",
- "defineProperty"),
- CGDOMJSProxyHandler_ownPropNames(descriptor),
- CGDOMJSProxyHandler_hasOwn(descriptor),
- CGDOMJSProxyHandler_get(descriptor),
- CGDOMJSProxyHandler_className(descriptor),
- CGDOMJSProxyHandler_finalizeInBackground(descriptor),
- CGDOMJSProxyHandler_finalize(descriptor),
- CGDOMJSProxyHandler_getInstance(),
- CGDOMJSProxyHandler_delete(descriptor)]
- constructors = [
- ClassConstructor(
- [],
- constexpr=True,
- visibility="public",
- explicit=True)
- ]
- if descriptor.supportsIndexedProperties():
- methods.append(CGDOMJSProxyHandler_getElements(descriptor))
- if (descriptor.operations['IndexedSetter'] is not None or
- (descriptor.operations['NamedSetter'] is not None and
- descriptor.interface.getExtendedAttribute('OverrideBuiltins'))):
- methods.append(CGDOMJSProxyHandler_setCustom(descriptor))
- if descriptor.hasNonOrdinaryGetPrototypeOf():
- methods.append(CGDOMJSProxyHandler_getPrototypeIfOrdinary())
- if descriptor.operations['LegacyCaller']:
- methods.append(CGDOMJSProxyHandler_call())
- methods.append(CGDOMJSProxyHandler_isCallable())
- if descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
- parentClass = 'ShadowingDOMProxyHandler'
- else:
- parentClass = 'mozilla::dom::DOMProxyHandler'
- CGClass.__init__(self, 'DOMProxyHandler',
- bases=[ClassBase(parentClass)],
- constructors=constructors,
- methods=methods)
- class CGDOMJSProxyHandlerDeclarer(CGThing):
- """
- A class for declaring a DOMProxyHandler.
- """
- def __init__(self, handlerThing):
- self.handlerThing = handlerThing
- def declare(self):
- # Our class declaration should happen when we're defining
- return ""
- def define(self):
- return self.handlerThing.declare()
- class CGDOMJSProxyHandlerDefiner(CGThing):
- """
- A class for defining a DOMProxyHandler.
- """
- def __init__(self, handlerThing):
- self.handlerThing = handlerThing
- def declare(self):
- return ""
- def define(self):
- return self.handlerThing.define()
- def stripTrailingWhitespace(text):
- tail = '\n' if text.endswith('\n') else ''
- lines = text.splitlines()
- return '\n'.join(line.rstrip() for line in lines) + tail
- class MemberProperties:
- def __init__(self):
- self.isGenericMethod = False
- self.isCrossOriginMethod = False
- self.isPromiseReturningMethod = False
- self.isGenericGetter = False
- self.isLenientGetter = False
- self.isCrossOriginGetter = False
- self.isGenericSetter = False
- self.isLenientSetter = False
- self.isCrossOriginSetter = False
- self.isJsonifier = False
- def memberProperties(m, descriptor):
- props = MemberProperties()
- if m.isMethod():
- if m == descriptor.operations['Jsonifier']:
- props.isGenericMethod = descriptor.needsSpecialGenericOps()
- props.isJsonifier = True
- elif (not m.isIdentifierLess() or m == descriptor.operations['Stringifier']):
- if not m.isStatic() and descriptor.interface.hasInterfacePrototypeObject():
- if descriptor.needsSpecialGenericOps():
- if m.returnsPromise():
- props.isPromiseReturningMethod = True
- else:
- props.isGenericMethod = True
- if m.getExtendedAttribute("CrossOriginCallable"):
- props.isCrossOriginMethod = True
- elif m.isAttr():
- if not m.isStatic() and descriptor.interface.hasInterfacePrototypeObject():
- if m.hasLenientThis():
- props.isLenientGetter = True
- elif m.getExtendedAttribute("CrossOriginReadable"):
- props.isCrossOriginGetter = True
- elif descriptor.needsSpecialGenericOps():
- props.isGenericGetter = True
- if not m.readonly:
- if not m.isStatic() and descriptor.interface.hasInterfacePrototypeObject():
- if m.hasLenientThis():
- props.isLenientSetter = True
- elif IsCrossOriginWritable(m, descriptor):
- props.isCrossOriginSetter = True
- elif descriptor.needsSpecialGenericOps():
- props.isGenericSetter = True
- elif m.getExtendedAttribute("PutForwards"):
- if IsCrossOriginWritable(m, descriptor):
- props.isCrossOriginSetter = True
- elif descriptor.needsSpecialGenericOps():
- props.isGenericSetter = True
- elif (m.getExtendedAttribute("Replaceable") or
- m.getExtendedAttribute("LenientSetter")):
- if descriptor.needsSpecialGenericOps():
- props.isGenericSetter = True
- return props
- class CGDescriptor(CGThing):
- def __init__(self, descriptor):
- CGThing.__init__(self)
- assert not descriptor.concrete or descriptor.interface.hasInterfacePrototypeObject()
- self._deps = descriptor.interface.getDeps()
- cgThings = []
- cgThings.append(CGGeneric(declare="typedef %s NativeType;\n" %
- descriptor.nativeType))
- parent = descriptor.interface.parent
- if parent:
- cgThings.append(CGGeneric("static_assert(IsRefcounted<NativeType>::value == IsRefcounted<%s::NativeType>::value,\n"
- " \"Can't inherit from an interface with a different ownership model.\");\n" %
- toBindingNamespace(descriptor.parentPrototypeName)))
- # These are set to true if at least one non-static
- # method/getter/setter or jsonifier exist on the interface.
- (hasMethod, hasGetter, hasLenientGetter, hasSetter, hasLenientSetter,
- hasPromiseReturningMethod) = False, False, False, False, False, False
- jsonifierMethod = None
- crossOriginMethods, crossOriginGetters, crossOriginSetters = set(), set(), set()
- unscopableNames = list()
- for n in descriptor.interface.namedConstructors:
- cgThings.append(CGClassConstructor(descriptor, n,
- NamedConstructorName(n)))
- for m in descriptor.interface.members:
- if m.isMethod() and m.identifier.name == 'queryInterface':
- continue
- props = memberProperties(m, descriptor)
- if m.isMethod():
- if m.getExtendedAttribute("Unscopable"):
- assert not m.isStatic()
- unscopableNames.append(m.identifier.name)
- if props.isJsonifier:
- jsonifierMethod = m
- elif not m.isIdentifierLess() or m == descriptor.operations['Stringifier']:
- if m.isStatic():
- assert descriptor.interface.hasInterfaceObject()
- cgThings.append(CGStaticMethod(descriptor, m))
- if m.returnsPromise():
- cgThings.append(CGStaticMethodJitinfo(m))
- elif descriptor.interface.hasInterfacePrototypeObject():
- specializedMethod = CGSpecializedMethod(descriptor, m)
- cgThings.append(specializedMethod)
- if m.returnsPromise():
- cgThings.append(CGMethodPromiseWrapper(descriptor, specializedMethod))
- cgThings.append(CGMemberJITInfo(descriptor, m))
- if props.isCrossOriginMethod:
- crossOriginMethods.add(m.identifier.name)
- # If we've hit the maplike/setlike member itself, go ahead and
- # generate its convenience functions.
- elif m.isMaplikeOrSetlike():
- cgThings.append(CGMaplikeOrSetlikeHelperGenerator(descriptor, m))
- elif m.isAttr():
- if m.stringifier:
- raise TypeError("Stringifier attributes not supported yet. "
- "See bug 824857.\n"
- "%s" % m.location)
- if m.getExtendedAttribute("Unscopable"):
- assert not m.isStatic()
- unscopableNames.append(m.identifier.name)
- if m.isStatic():
- assert descriptor.interface.hasInterfaceObject()
- cgThings.append(CGStaticGetter(descriptor, m))
- elif descriptor.interface.hasInterfacePrototypeObject():
- if isNonExposedNavigatorObjectGetter(m, descriptor):
- continue
- cgThings.append(CGSpecializedGetter(descriptor, m))
- if props.isCrossOriginGetter:
- crossOriginGetters.add(m.identifier.name)
- if not m.readonly:
- if m.isStatic():
- assert descriptor.interface.hasInterfaceObject()
- cgThings.append(CGStaticSetter(descriptor, m))
- elif descriptor.interface.hasInterfacePrototypeObject():
- cgThings.append(CGSpecializedSetter(descriptor, m))
- if props.isCrossOriginSetter:
- crossOriginSetters.add(m.identifier.name)
- elif m.getExtendedAttribute("PutForwards"):
- cgThings.append(CGSpecializedForwardingSetter(descriptor, m))
- if props.isCrossOriginSetter:
- crossOriginSetters.add(m.identifier.name)
- elif m.getExtendedAttribute("Replaceable"):
- cgThings.append(CGSpecializedReplaceableSetter(descriptor, m))
- elif m.getExtendedAttribute("LenientSetter"):
- cgThings.append(CGSpecializedLenientSetter(descriptor, m))
- if (not m.isStatic() and
- descriptor.interface.hasInterfacePrototypeObject()):
- cgThings.append(CGMemberJITInfo(descriptor, m))
- hasMethod = hasMethod or props.isGenericMethod
- hasPromiseReturningMethod = (hasPromiseReturningMethod or
- props.isPromiseReturningMethod)
- hasGetter = hasGetter or props.isGenericGetter
- hasLenientGetter = hasLenientGetter or props.isLenientGetter
- hasSetter = hasSetter or props.isGenericSetter
- hasLenientSetter = hasLenientSetter or props.isLenientSetter
- if jsonifierMethod:
- cgThings.append(CGJsonifyAttributesMethod(descriptor))
- cgThings.append(CGJsonifierMethod(descriptor, jsonifierMethod))
- cgThings.append(CGMemberJITInfo(descriptor, jsonifierMethod))
- if hasMethod:
- cgThings.append(CGGenericMethod(descriptor))
- if hasPromiseReturningMethod:
- cgThings.append(CGGenericPromiseReturningMethod(descriptor))
- if len(crossOriginMethods):
- cgThings.append(CGGenericMethod(descriptor,
- allowCrossOriginThis=True))
- if hasGetter:
- cgThings.append(CGGenericGetter(descriptor))
- if hasLenientGetter:
- cgThings.append(CGGenericGetter(descriptor, lenientThis=True))
- if len(crossOriginGetters):
- cgThings.append(CGGenericGetter(descriptor,
- allowCrossOriginThis=True))
- if hasSetter:
- cgThings.append(CGGenericSetter(descriptor))
- if hasLenientSetter:
- cgThings.append(CGGenericSetter(descriptor, lenientThis=True))
- if len(crossOriginSetters):
- cgThings.append(CGGenericSetter(descriptor,
- allowCrossOriginThis=True))
- if descriptor.interface.isNavigatorProperty():
- cgThings.append(CGConstructNavigatorObject(descriptor))
- if descriptor.concrete and not descriptor.proxy:
- if wantsAddProperty(descriptor):
- cgThings.append(CGAddPropertyHook(descriptor))
- # Always have a finalize hook, regardless of whether the class
- # wants a custom hook.
- cgThings.append(CGClassFinalizeHook(descriptor))
- if descriptor.concrete and descriptor.wrapperCache:
- cgThings.append(CGClassObjectMovedHook(descriptor))
- # Generate the _ClearCachedFooValue methods before the property arrays that use them.
- if descriptor.interface.isJSImplemented():
- for m in clearableCachedAttrs(descriptor):
- cgThings.append(CGJSImplClearCachedValueMethod(descriptor, m))
- # Need to output our generated hasinstance bits before
- # PropertyArrays tries to use them.
- if (descriptor.interface.hasInterfaceObject() and
- NeedsGeneratedHasInstance(descriptor)):
- cgThings.append(CGHasInstanceHook(descriptor))
- properties = PropertyArrays(descriptor)
- cgThings.append(CGGeneric(define=str(properties)))
- cgThings.append(CGNativeProperties(descriptor, properties))
- if descriptor.interface.hasInterfaceObject():
- cgThings.append(CGClassConstructor(descriptor,
- descriptor.interface.ctor()))
- cgThings.append(CGInterfaceObjectJSClass(descriptor, properties))
- cgThings.append(CGNamedConstructors(descriptor))
- cgThings.append(CGLegacyCallHook(descriptor))
- if descriptor.interface.getExtendedAttribute("NeedResolve"):
- cgThings.append(CGResolveHook(descriptor))
- cgThings.append(CGMayResolveHook(descriptor))
- cgThings.append(CGEnumerateHook(descriptor))
- if descriptor.hasNamedPropertiesObject:
- cgThings.append(CGGetNamedPropertiesObjectMethod(descriptor))
- if descriptor.interface.hasInterfacePrototypeObject():
- cgThings.append(CGPrototypeJSClass(descriptor, properties))
- if ((descriptor.interface.hasInterfaceObject() or descriptor.interface.isNavigatorProperty()) and
- not descriptor.interface.isExternal() and
- descriptor.isExposedConditionally()):
- cgThings.append(CGConstructorEnabled(descriptor))
- if descriptor.registersGlobalNamesOnWindow:
- cgThings.append(CGDefineDOMInterfaceMethod(descriptor))
- if (descriptor.interface.hasMembersInSlots() and
- descriptor.interface.hasChildInterfaces()):
- raise TypeError("We don't support members in slots on "
- "non-leaf interfaces like %s" %
- descriptor.interface.identifier.name)
- if descriptor.concrete:
- if descriptor.proxy:
- if descriptor.interface.totalMembersInSlots != 0:
- raise TypeError("We can't have extra reserved slots for "
- "proxy interface %s" %
- descriptor.interface.identifier.name)
- cgThings.append(CGGeneric(fill(
- """
- static_assert(IsBaseOf<nsISupports, ${nativeType} >::value,
- "We don't support non-nsISupports native classes for "
- "proxy-based bindings yet");
- """,
- nativeType=descriptor.nativeType)))
- if not descriptor.wrapperCache:
- raise TypeError("We need a wrappercache to support expandos for proxy-based "
- "bindings (" + descriptor.name + ")")
- handlerThing = CGDOMJSProxyHandler(descriptor)
- cgThings.append(CGDOMJSProxyHandlerDeclarer(handlerThing))
- cgThings.append(CGProxyIsProxy(descriptor))
- cgThings.append(CGProxyUnwrap(descriptor))
- cgThings.append(CGDOMJSProxyHandlerDefiner(handlerThing))
- cgThings.append(CGDOMProxyJSClass(descriptor))
- else:
- cgThings.append(CGDOMJSClass(descriptor))
- cgThings.append(CGGetJSClassMethod(descriptor))
- if descriptor.interface.hasMembersInSlots():
- cgThings.append(CGUpdateMemberSlotsMethod(descriptor))
- if descriptor.isGlobal():
- assert descriptor.wrapperCache
- cgThings.append(CGWrapGlobalMethod(descriptor, properties))
- elif descriptor.wrapperCache:
- cgThings.append(CGWrapWithCacheMethod(descriptor, properties))
- cgThings.append(CGWrapMethod(descriptor))
- else:
- cgThings.append(CGWrapNonWrapperCacheMethod(descriptor,
- properties))
- # Set up our Xray callbacks as needed. This needs to come
- # after we have our DOMProxyHandler defined.
- if descriptor.wantsXrays:
- if descriptor.concrete and descriptor.proxy:
- cgThings.append(CGResolveOwnProperty(descriptor))
- cgThings.append(CGEnumerateOwnProperties(descriptor))
- if descriptor.needsXrayNamedDeleterHook():
- cgThings.append(CGDeleteNamedProperty(descriptor))
- elif descriptor.needsXrayResolveHooks():
- cgThings.append(CGResolveOwnPropertyViaResolve(descriptor))
- cgThings.append(CGEnumerateOwnPropertiesViaGetOwnPropertyNames(descriptor))
- if descriptor.wantsXrayExpandoClass:
- cgThings.append(CGXrayExpandoJSClass(descriptor))
- # Now that we have our ResolveOwnProperty/EnumerateOwnProperties stuff
- # done, set up our NativePropertyHooks.
- cgThings.append(CGNativePropertyHooks(descriptor, properties))
- # If we're not wrappercached, we don't know how to clear our
- # cached values, since we can't get at the JSObject.
- if descriptor.wrapperCache:
- cgThings.extend(CGClearCachedValueMethod(descriptor, m) for
- m in clearableCachedAttrs(descriptor))
- haveUnscopables = (len(unscopableNames) != 0 and
- descriptor.interface.hasInterfacePrototypeObject())
- if haveUnscopables:
- cgThings.append(
- CGList([CGGeneric("static const char* const unscopableNames[] = {"),
- CGIndenter(CGList([CGGeneric('"%s"' % name) for
- name in unscopableNames] +
- [CGGeneric("nullptr")], ",\n")),
- CGGeneric("};\n")], "\n"))
- # CGCreateInterfaceObjectsMethod needs to come after our
- # CGDOMJSClass and unscopables, if any.
- cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties,
- haveUnscopables))
- # CGGetProtoObjectMethod and CGGetConstructorObjectMethod need
- # to come after CGCreateInterfaceObjectsMethod.
- if descriptor.interface.hasInterfacePrototypeObject():
- cgThings.append(CGGetProtoObjectHandleMethod(descriptor))
- if descriptor.interface.hasChildInterfaces():
- cgThings.append(CGGetProtoObjectMethod(descriptor))
- if descriptor.interface.hasInterfaceObject():
- cgThings.append(CGGetConstructorObjectHandleMethod(descriptor))
- cgThings.append(CGGetConstructorObjectMethod(descriptor))
- # See whether we need we need to generate an IsPermitted method
- if crossOriginGetters or crossOriginSetters or crossOriginMethods:
- cgThings.append(CGIsPermittedMethod(descriptor,
- crossOriginGetters,
- crossOriginSetters,
- crossOriginMethods))
- cgThings = CGList((CGIndenter(t, declareOnly=True) for t in cgThings), "\n")
- cgThings = CGWrapper(cgThings, pre='\n', post='\n')
- self.cgRoot = CGWrapper(CGNamespace(toBindingNamespace(descriptor.name),
- cgThings),
- post='\n')
- def declare(self):
- return self.cgRoot.declare()
- def define(self):
- return self.cgRoot.define()
- def deps(self):
- return self._deps
- class CGNamespacedEnum(CGThing):
- def __init__(self, namespace, enumName, names, values, comment=""):
- if not values:
- values = []
- # Account for explicit enum values.
- entries = []
- for i in range(0, len(names)):
- if len(values) > i and values[i] is not None:
- entry = "%s = %s" % (names[i], values[i])
- else:
- entry = names[i]
- entries.append(entry)
- # Append a Count.
- entries.append('_' + enumName + '_Count')
- # Indent.
- entries = [' ' + e for e in entries]
- # Build the enum body.
- enumstr = comment + 'enum %s : uint16_t\n{\n%s\n};\n' % (enumName, ',\n'.join(entries))
- curr = CGGeneric(declare=enumstr)
- # Add some whitespace padding.
- curr = CGWrapper(curr, pre='\n', post='\n')
- # Add the namespace.
- curr = CGNamespace(namespace, curr)
- # Add the typedef
- typedef = '\ntypedef %s::%s %s;\n\n' % (namespace, enumName, enumName)
- curr = CGList([curr, CGGeneric(declare=typedef)])
- # Save the result.
- self.node = curr
- def declare(self):
- return self.node.declare()
- def define(self):
- return ""
- def initIdsClassMethod(identifiers, atomCacheName):
- idinit = ['!atomsCache->%s.init(cx, "%s")' %
- (CGDictionary.makeIdName(id),
- id)
- for id in identifiers]
- idinit.reverse()
- body = fill(
- """
- MOZ_ASSERT(!*reinterpret_cast<jsid**>(atomsCache));
- // Initialize these in reverse order so that any failure leaves the first one
- // uninitialized.
- if (${idinit}) {
- return false;
- }
- return true;
- """,
- idinit=" ||\n ".join(idinit))
- return ClassMethod("InitIds", "bool", [
- Argument("JSContext*", "cx"),
- Argument("%s*" % atomCacheName, "atomsCache")
- ], static=True, body=body, visibility="private")
- class CGDictionary(CGThing):
- def __init__(self, dictionary, descriptorProvider):
- self.dictionary = dictionary
- self.descriptorProvider = descriptorProvider
- self.needToInitIds = len(dictionary.members) > 0
- self.memberInfo = [
- (member,
- getJSToNativeConversionInfo(
- member.type,
- descriptorProvider,
- isEnforceRange=member.enforceRange,
- isClamp=member.clamp,
- isMember="Dictionary",
- isOptional=member.canHaveMissingValue(),
- defaultValue=member.defaultValue,
- sourceDescription=self.getMemberSourceDescription(member)))
- for member in dictionary.members]
- # If we have a union member containing something in the same
- # file as us, bail: the C++ includes won't work out.
- for member in dictionary.members:
- type = member.type.unroll()
- if type.isUnion():
- for t in type.flatMemberTypes:
- if (t.isDictionary() and
- CGHeaders.getDeclarationFilename(t.inner) ==
- CGHeaders.getDeclarationFilename(dictionary)):
- raise TypeError(
- "Dictionary contains a union that contains a "
- "dictionary in the same WebIDL file. This won't "
- "compile. Move the inner dictionary to a "
- "different file.\n%s\n%s" %
- (t.location, t.inner.location))
- self.structs = self.getStructs()
- def declare(self):
- return self.structs.declare()
- def define(self):
- return self.structs.define()
- def base(self):
- if self.dictionary.parent:
- return self.makeClassName(self.dictionary.parent)
- return "DictionaryBase"
- def initMethod(self):
- """
- This function outputs the body of the Init() method for the dictionary.
- For the most part, this is some bookkeeping for our atoms so
- we can avoid atomizing strings all the time, then we just spit
- out the getMemberConversion() output for each member,
- separated by newlines.
- """
- body = dedent("""
- // Passing a null JSContext is OK only if we're initing from null,
- // Since in that case we will not have to do any property gets
- MOZ_ASSERT_IF(!cx, val.isNull());
- """)
- if self.needToInitIds:
- body += fill(
- """
- ${dictName}Atoms* atomsCache = nullptr;
- if (cx) {
- atomsCache = GetAtomCache<${dictName}Atoms>(cx);
- if (!*reinterpret_cast<jsid**>(atomsCache) && !InitIds(cx, atomsCache)) {
- return false;
- }
- }
- """,
- dictName=self.makeClassName(self.dictionary))
- if self.dictionary.parent:
- body += fill(
- """
- // Per spec, we init the parent's members first
- if (!${dictName}::Init(cx, val)) {
- return false;
- }
- """,
- dictName=self.makeClassName(self.dictionary.parent))
- else:
- body += dedent(
- """
- { // scope for isConvertible
- bool isConvertible;
- if (!IsConvertibleToDictionary(cx, val, &isConvertible)) {
- return false;
- }
- if (!isConvertible) {
- return ThrowErrorMessage(cx, MSG_NOT_DICTIONARY, sourceDescription);
- }
- }
- """)
- memberInits = [self.getMemberConversion(m).define()
- for m in self.memberInfo]
- if memberInits:
- body += fill(
- """
- bool isNull = val.isNullOrUndefined();
- // We only need these if !isNull, in which case we have |cx|.
- Maybe<JS::Rooted<JSObject *> > object;
- Maybe<JS::Rooted<JS::Value> > temp;
- if (!isNull) {
- MOZ_ASSERT(cx);
- object.emplace(cx, &val.toObject());
- temp.emplace(cx);
- }
- $*{memberInits}
- """,
- memberInits="\n".join(memberInits))
- body += "return true;\n"
- return ClassMethod("Init", "bool", [
- Argument('JSContext*', 'cx'),
- Argument('JS::Handle<JS::Value>', 'val'),
- Argument('const char*', 'sourceDescription', default='"Value"'),
- Argument('bool', 'passedToJSImpl', default='false')
- ], body=body)
- def initFromJSONMethod(self):
- return ClassMethod(
- "Init", "bool",
- [Argument('const nsAString&', 'aJSON')],
- body=dedent("""
- AutoJSAPI jsapi;
- JSObject* cleanGlobal = SimpleGlobalObject::Create(SimpleGlobalObject::GlobalType::BindingDetail);
- if (!cleanGlobal) {
- return false;
- }
- if (!jsapi.Init(cleanGlobal)) {
- return false;
- }
- JSContext* cx = jsapi.cx();
- JS::Rooted<JS::Value> json(cx);
- bool ok = ParseJSON(cx, aJSON, &json);
- NS_ENSURE_TRUE(ok, false);
- return Init(cx, json);
- """))
- def toJSONMethod(self):
- return ClassMethod(
- "ToJSON", "bool",
- [Argument('nsAString&', 'aJSON')],
- body=dedent("""
- AutoJSAPI jsapi;
- jsapi.Init();
- JSContext *cx = jsapi.cx();
- // It's safe to use UnprivilegedJunkScopeOrWorkerGlobal here
- // because we'll only be creating objects, in ways that have no
- // side-effects, followed by a call to JS::ToJSONMaybeSafely,
- // which likewise guarantees no side-effects for the sorts of
- // things we will pass it.
- JSAutoCompartment ac(cx, binding_detail::UnprivilegedJunkScopeOrWorkerGlobal());
- JS::Rooted<JS::Value> val(cx);
- if (!ToObjectInternal(cx, &val)) {
- return false;
- }
- JS::Rooted<JSObject*> obj(cx, &val.toObject());
- return StringifyToJSON(cx, obj, aJSON);
- """), const=True)
- def toObjectInternalMethod(self):
- body = ""
- if self.needToInitIds:
- body += fill(
- """
- ${dictName}Atoms* atomsCache = GetAtomCache<${dictName}Atoms>(cx);
- if (!*reinterpret_cast<jsid**>(atomsCache) && !InitIds(cx, atomsCache)) {
- return false;
- }
- """,
- dictName=self.makeClassName(self.dictionary))
- if self.dictionary.parent:
- body += fill(
- """
- // Per spec, we define the parent's members first
- if (!${dictName}::ToObjectInternal(cx, rval)) {
- return false;
- }
- JS::Rooted<JSObject*> obj(cx, &rval.toObject());
- """,
- dictName=self.makeClassName(self.dictionary.parent))
- else:
- body += dedent(
- """
- JS::Rooted<JSObject*> obj(cx, JS_NewPlainObject(cx));
- if (!obj) {
- return false;
- }
- rval.set(JS::ObjectValue(*obj));
- """)
- if self.memberInfo:
- body += "\n".join(self.getMemberDefinition(m).define()
- for m in self.memberInfo)
- body += "\nreturn true;\n"
- return ClassMethod("ToObjectInternal", "bool", [
- Argument('JSContext*', 'cx'),
- Argument('JS::MutableHandle<JS::Value>', 'rval'),
- ], const=True, body=body)
- def initIdsMethod(self):
- assert self.needToInitIds
- return initIdsClassMethod([m.identifier.name for m in self.dictionary.members],
- "%sAtoms" % self.makeClassName(self.dictionary))
- def traceDictionaryMethod(self):
- body = ""
- if self.dictionary.parent:
- cls = self.makeClassName(self.dictionary.parent)
- body += "%s::TraceDictionary(trc);\n" % cls
- memberTraces = [self.getMemberTrace(m)
- for m in self.dictionary.members
- if typeNeedsRooting(m.type)]
- if memberTraces:
- body += "\n".join(memberTraces)
- return ClassMethod("TraceDictionary", "void", [
- Argument("JSTracer*", "trc"),
- ], body=body)
- def assignmentOperator(self):
- body = CGList([])
- if self.dictionary.parent:
- body.append(CGGeneric(
- "%s::operator=(aOther);\n" %
- self.makeClassName(self.dictionary.parent)))
- for m, _ in self.memberInfo:
- memberName = self.makeMemberName(m.identifier.name)
- if m.canHaveMissingValue():
- memberAssign = CGGeneric(fill(
- """
- ${name}.Reset();
- if (aOther.${name}.WasPassed()) {
- ${name}.Construct(aOther.${name}.Value());
- }
- """,
- name=memberName))
- else:
- memberAssign = CGGeneric(
- "%s = aOther.%s;\n" % (memberName, memberName))
- body.append(memberAssign)
- return ClassMethod(
- "operator=", "void",
- [Argument("const %s&" % self.makeClassName(self.dictionary),
- "aOther")],
- body=body.define())
- def getStructs(self):
- d = self.dictionary
- selfName = self.makeClassName(d)
- members = [ClassMember(self.makeMemberName(m[0].identifier.name),
- self.getMemberType(m),
- visibility="public",
- body=self.getMemberInitializer(m),
- hasIgnoreInitCheckFlag=True)
- for m in self.memberInfo]
- if d.parent:
- # We always want to init our parent with our non-initializing
- # constructor arg, because either we're about to init ourselves (and
- # hence our parent) or we don't want any init happening.
- baseConstructors = [
- "%s(%s)" % (self.makeClassName(d.parent),
- self.getNonInitializingCtorArg())
- ]
- else:
- baseConstructors = None
- ctors = [
- ClassConstructor(
- [],
- visibility="public",
- baseConstructors=baseConstructors,
- body=(
- "// Safe to pass a null context if we pass a null value\n"
- "Init(nullptr, JS::NullHandleValue);\n")),
- ClassConstructor(
- [Argument("const FastDictionaryInitializer&", "")],
- visibility="public",
- baseConstructors=baseConstructors,
- explicit=True,
- bodyInHeader=True,
- body='// Do nothing here; this is used by our "Fast" subclass\n')
- ]
- methods = []
- if self.needToInitIds:
- methods.append(self.initIdsMethod())
- methods.append(self.initMethod())
- canBeRepresentedAsJSON = self.dictionarySafeToJSONify(self.dictionary)
- if canBeRepresentedAsJSON:
- methods.append(self.initFromJSONMethod())
- try:
- methods.append(self.toObjectInternalMethod())
- if canBeRepresentedAsJSON:
- methods.append(self.toJSONMethod())
- except MethodNotNewObjectError:
- # If we can't have a ToObjectInternal() because one of our members
- # can only be returned from [NewObject] methods, then just skip
- # generating ToObjectInternal() and ToJSON (since the latter depens
- # on the former).
- pass
- methods.append(self.traceDictionaryMethod())
- if CGDictionary.isDictionaryCopyConstructible(d):
- disallowCopyConstruction = False
- # Note: no base constructors because our operator= will
- # deal with that.
- ctors.append(ClassConstructor([Argument("const %s&" % selfName,
- "aOther")],
- bodyInHeader=True,
- visibility="public",
- explicit=True,
- body="*this = aOther;\n"))
- methods.append(self.assignmentOperator())
- else:
- disallowCopyConstruction = True
- struct = CGClass(selfName,
- bases=[ClassBase(self.base())],
- members=members,
- constructors=ctors,
- methods=methods,
- isStruct=True,
- disallowCopyConstruction=disallowCopyConstruction)
- fastDictionaryCtor = ClassConstructor(
- [],
- visibility="public",
- bodyInHeader=True,
- baseConstructors=["%s(%s)" %
- (selfName,
- self.getNonInitializingCtorArg())],
- body="// Doesn't matter what int we pass to the parent constructor\n")
- fastStruct = CGClass("Fast" + selfName,
- bases=[ClassBase(selfName)],
- constructors=[fastDictionaryCtor],
- isStruct=True)
- return CGList([struct,
- CGNamespace('binding_detail', fastStruct)],
- "\n")
- def deps(self):
- return self.dictionary.getDeps()
- @staticmethod
- def makeDictionaryName(dictionary):
- return dictionary.identifier.name
- def makeClassName(self, dictionary):
- return self.makeDictionaryName(dictionary)
- @staticmethod
- def makeMemberName(name):
- return "m" + name[0].upper() + IDLToCIdentifier(name[1:])
- def getMemberType(self, memberInfo):
- _, conversionInfo = memberInfo
- # We can't handle having a holderType here
- assert conversionInfo.holderType is None
- declType = conversionInfo.declType
- if conversionInfo.dealWithOptional:
- declType = CGTemplatedType("Optional", declType)
- return declType.define()
- def getMemberConversion(self, memberInfo):
- """
- A function that outputs the initialization of a single dictionary
- member from the given dictionary value.
- We start with our conversionInfo, which tells us how to
- convert a JS::Value to whatever type this member is. We
- substiture the template from the conversionInfo with values
- that point to our "temp" JS::Value and our member (which is
- the C++ value we want to produce). The output is a string of
- code to do the conversion. We store this string in
- conversionReplacements["convert"].
- Now we have three different ways we might use (or skip) this
- string of code, depending on whether the value is required,
- optional with default value, or optional without default
- value. We set up a template in the 'conversion' variable for
- exactly how to do this, then substitute into it from the
- conversionReplacements dictionary.
- """
- member, conversionInfo = memberInfo
- replacements = {
- "val": "temp.ref()",
- "maybeMutableVal": "temp.ptr()",
- "declName": self.makeMemberName(member.identifier.name),
- # We need a holder name for external interfaces, but
- # it's scoped down to the conversion so we can just use
- # anything we want.
- "holderName": "holder",
- "passedToJSImpl": "passedToJSImpl"
- }
- # We can't handle having a holderType here
- assert conversionInfo.holderType is None
- if conversionInfo.dealWithOptional:
- replacements["declName"] = "(" + replacements["declName"] + ".Value())"
- if member.defaultValue:
- replacements["haveValue"] = "!isNull && !temp->isUndefined()"
- propId = self.makeIdName(member.identifier.name)
- propGet = ("JS_GetPropertyById(cx, *object, atomsCache->%s, temp.ptr())" %
- propId)
- conversionReplacements = {
- "prop": self.makeMemberName(member.identifier.name),
- "convert": string.Template(conversionInfo.template).substitute(replacements),
- "propGet": propGet
- }
- # The conversion code will only run where a default value or a value passed
- # by the author needs to get converted, so we can remember if we have any
- # members present here.
- conversionReplacements["convert"] += "mIsAnyMemberPresent = true;\n"
- setTempValue = CGGeneric(dedent(
- """
- if (!${propGet}) {
- return false;
- }
- """))
- conditions = getConditionList(member, "cx", "*object")
- if len(conditions) != 0:
- setTempValue = CGIfElseWrapper(conditions.define(),
- setTempValue,
- CGGeneric("temp->setUndefined();\n"))
- setTempValue = CGIfWrapper(setTempValue, "!isNull")
- conversion = setTempValue.define()
- if member.defaultValue:
- if (member.type.isUnion() and
- (not member.type.nullable() or
- not isinstance(member.defaultValue, IDLNullValue))):
- # Since this has a default value, it might have been initialized
- # already. Go ahead and uninit it before we try to init it
- # again.
- memberName = self.makeMemberName(member.identifier.name)
- if member.type.nullable():
- conversion += fill(
- """
- if (!${memberName}.IsNull()) {
- ${memberName}.Value().Uninit();
- }
- """,
- memberName=memberName)
- else:
- conversion += "%s.Uninit();\n" % memberName
- conversion += "${convert}"
- elif not conversionInfo.dealWithOptional:
- # We're required, but have no default value. Make sure
- # that we throw if we have no value provided.
- conversion += dedent(
- """
- if (!isNull && !temp->isUndefined()) {
- ${convert}
- } else if (cx) {
- // Don't error out if we have no cx. In that
- // situation the caller is default-constructing us and we'll
- // just assume they know what they're doing.
- return ThrowErrorMessage(cx, MSG_MISSING_REQUIRED_DICTIONARY_MEMBER,
- "%s");
- }
- """ % self.getMemberSourceDescription(member))
- conversionReplacements["convert"] = indent(conversionReplacements["convert"]).rstrip()
- else:
- conversion += (
- "if (!isNull && !temp->isUndefined()) {\n"
- " ${prop}.Construct();\n"
- "${convert}"
- "}\n")
- conversionReplacements["convert"] = indent(conversionReplacements["convert"])
- return CGGeneric(
- string.Template(conversion).substitute(conversionReplacements))
- def getMemberDefinition(self, memberInfo):
- member = memberInfo[0]
- declType = memberInfo[1].declType
- memberLoc = self.makeMemberName(member.identifier.name)
- if not member.canHaveMissingValue():
- memberData = memberLoc
- else:
- # The data is inside the Optional<>
- memberData = "%s.InternalValue()" % memberLoc
- # If you have to change this list (which you shouldn't!), make sure it
- # continues to match the list in test_Object.prototype_props.html
- if (member.identifier.name in
- ["constructor", "toSource", "toString", "toLocaleString", "valueOf",
- "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable",
- "__defineGetter__", "__defineSetter__", "__lookupGetter__",
- "__lookupSetter__", "__proto__"]):
- raise TypeError("'%s' member of %s dictionary shadows "
- "a property of Object.prototype, and Xrays to "
- "Object can't handle that.\n"
- "%s" %
- (member.identifier.name,
- self.dictionary.identifier.name,
- member.location))
- propDef = (
- 'JS_DefinePropertyById(cx, obj, atomsCache->%s, temp, JSPROP_ENUMERATE)' %
- self.makeIdName(member.identifier.name))
- innerTemplate = wrapForType(
- member.type, self.descriptorProvider,
- {
- 'result': "currentValue",
- 'successCode': ("if (!%s) {\n"
- " return false;\n"
- "}\n"
- "break;\n" % propDef),
- 'jsvalRef': "temp",
- 'jsvalHandle': "&temp",
- 'returnsNewObject': False,
- # 'obj' can just be allowed to be the string "obj", since that
- # will be our dictionary object, which is presumably itself in
- # the right scope.
- 'typedArraysAreStructs': True
- })
- conversion = CGGeneric(innerTemplate)
- conversion = CGWrapper(conversion,
- pre=("JS::Rooted<JS::Value> temp(cx);\n"
- "%s const & currentValue = %s;\n" %
- (declType.define(), memberData)
- ))
- # Now make sure that our successCode can actually break out of the
- # conversion. This incidentally gives us a scope for 'temp' and
- # 'currentValue'.
- conversion = CGWrapper(
- CGIndenter(conversion),
- pre=("do {\n"
- " // block for our 'break' successCode and scope for 'temp' and 'currentValue'\n"),
- post="} while(0);\n")
- if member.canHaveMissingValue():
- # Only do the conversion if we have a value
- conversion = CGIfWrapper(conversion, "%s.WasPassed()" % memberLoc)
- conditions = getConditionList(member, "cx", "obj")
- if len(conditions) != 0:
- conversion = CGIfWrapper(conversion, conditions.define())
- return conversion
- def getMemberTrace(self, member):
- type = member.type
- assert typeNeedsRooting(type)
- memberLoc = self.makeMemberName(member.identifier.name)
- if not member.canHaveMissingValue():
- memberData = memberLoc
- else:
- # The data is inside the Optional<>
- memberData = "%s.Value()" % memberLoc
- memberName = "%s.%s" % (self.makeClassName(self.dictionary),
- memberLoc)
- if type.isObject():
- trace = CGGeneric('JS::UnsafeTraceRoot(trc, %s, "%s");\n' %
- ("&"+memberData, memberName))
- if type.nullable():
- trace = CGIfWrapper(trace, memberData)
- elif type.isAny():
- trace = CGGeneric('JS::UnsafeTraceRoot(trc, %s, "%s");\n' %
- ("&"+memberData, memberName))
- elif (type.isSequence() or type.isDictionary() or
- type.isSpiderMonkeyInterface() or type.isUnion()):
- if type.nullable():
- memberNullable = memberData
- memberData = "%s.Value()" % memberData
- if type.isSequence():
- trace = CGGeneric('DoTraceSequence(trc, %s);\n' % memberData)
- elif type.isDictionary():
- trace = CGGeneric('%s.TraceDictionary(trc);\n' % memberData)
- elif type.isUnion():
- trace = CGGeneric('%s.TraceUnion(trc);\n' % memberData)
- else:
- assert type.isSpiderMonkeyInterface()
- trace = CGGeneric('%s.TraceSelf(trc);\n' % memberData)
- if type.nullable():
- trace = CGIfWrapper(trace, "!%s.IsNull()" % memberNullable)
- elif type.isRecord():
- # If you implement this, add a record<DOMString, object> to
- # TestInterfaceJSDictionary and test it in test_bug1036214.html
- # to make sure we end up with the correct security properties.
- assert False
- else:
- assert False # unknown type
- if member.canHaveMissingValue():
- trace = CGIfWrapper(trace, "%s.WasPassed()" % memberLoc)
- return trace.define()
- def getMemberInitializer(self, memberInfo):
- """
- Get the right initializer for the member. Most members don't need one,
- but we need to pre-initialize 'any' and 'object' that have a default
- value, so they're safe to trace at all times.
- """
- member, _ = memberInfo
- if member.canHaveMissingValue():
- # Allowed missing value means no need to set it up front, since it's
- # inside an Optional and won't get traced until it's actually set
- # up.
- return None
- type = member.type
- if type.isAny():
- return "JS::UndefinedValue()"
- if type.isObject():
- return "nullptr"
- if type.isDictionary():
- # When we construct ourselves, we don't want to init our member
- # dictionaries. Either we're being constructed-but-not-initialized
- # ourselves (and then we don't want to init them) or we're about to
- # init ourselves and then we'll init them anyway.
- return CGDictionary.getNonInitializingCtorArg()
- return None
- def getMemberSourceDescription(self, member):
- return ("'%s' member of %s" %
- (member.identifier.name, self.dictionary.identifier.name))
- @staticmethod
- def makeIdName(name):
- return IDLToCIdentifier(name) + "_id"
- @staticmethod
- def getNonInitializingCtorArg():
- return "FastDictionaryInitializer()"
- @staticmethod
- def isDictionaryCopyConstructible(dictionary):
- if (dictionary.parent and
- not CGDictionary.isDictionaryCopyConstructible(dictionary.parent)):
- return False
- return all(isTypeCopyConstructible(m.type) for m in dictionary.members)
- @staticmethod
- def typeSafeToJSONify(type):
- """
- Determine whether the given type is safe to convert to JSON. The
- restriction is that this needs to be safe while in a global controlled
- by an adversary, and "safe" means no side-effects when the JS
- representation of this type is converted to JSON. That means that we
- have to be pretty restrictive about what things we can allow. For
- example, "object" is out, because it may have accessor properties on it.
- """
- if type.nullable():
- # Converting null to JSON is always OK.
- return CGDictionary.typeSafeToJSONify(type.inner)
- if type.isSequence():
- # Sequences are arrays we create ourselves, with no holes. They
- # should be safe if their contents are safe, as long as we suppress
- # invocation of .toJSON on objects.
- return CGDictionary.typeSafeToJSONify(type.inner)
- if type.isUnion():
- # OK if everything in it is ok.
- return all(CGDictionary.typeSafeToJSONify(t)
- for t in type.flatMemberTypes)
- if type.isDictionary():
- # OK if the dictionary is OK
- return CGDictionary.dictionarySafeToJSONify(type.inner)
- if type.isString() or type.isEnum():
- # Strings are always OK.
- return True
- if type.isPrimitive():
- # Primitives (numbers and booleans) are ok, as long as
- # they're not unrestricted float/double.
- return not type.isFloat() or not type.isUnrestricted()
- return False
- @staticmethod
- def dictionarySafeToJSONify(dictionary):
- # The dictionary itself is OK, so we're good if all our types are.
- return all(CGDictionary.typeSafeToJSONify(m.type)
- for m in dictionary.members)
- class CGRegisterWorkerBindings(CGAbstractMethod):
- def __init__(self, config):
- CGAbstractMethod.__init__(self, None, 'RegisterWorkerBindings', 'bool',
- [Argument('JSContext*', 'aCx'),
- Argument('JS::Handle<JSObject*>', 'aObj')])
- self.config = config
- def definition_body(self):
- descriptors = self.config.getDescriptors(hasInterfaceObject=True,
- isExposedInAnyWorker=True,
- register=True)
- conditions = []
- for desc in descriptors:
- bindingNS = toBindingNamespace(desc.name)
- condition = "!%s::GetConstructorObject(aCx)" % bindingNS
- if desc.isExposedConditionally():
- condition = (
- "%s::ConstructorEnabled(aCx, aObj) && " % bindingNS
- + condition)
- conditions.append(condition)
- lines = [CGIfWrapper(CGGeneric("return false;\n"), condition) for
- condition in conditions]
- lines.append(CGGeneric("return true;\n"))
- return CGList(lines, "\n").define()
- class CGRegisterWorkerDebuggerBindings(CGAbstractMethod):
- def __init__(self, config):
- CGAbstractMethod.__init__(self, None, 'RegisterWorkerDebuggerBindings', 'bool',
- [Argument('JSContext*', 'aCx'),
- Argument('JS::Handle<JSObject*>', 'aObj')])
- self.config = config
- def definition_body(self):
- descriptors = self.config.getDescriptors(hasInterfaceObject=True,
- isExposedInWorkerDebugger=True,
- register=True)
- conditions = []
- for desc in descriptors:
- bindingNS = toBindingNamespace(desc.name)
- condition = "!%s::GetConstructorObject(aCx)" % bindingNS
- if desc.isExposedConditionally():
- condition = (
- "%s::ConstructorEnabled(aCx, aObj) && " % bindingNS
- + condition)
- conditions.append(condition)
- lines = [CGIfWrapper(CGGeneric("return false;\n"), condition) for
- condition in conditions]
- lines.append(CGGeneric("return true;\n"))
- return CGList(lines, "\n").define()
- class CGRegisterWorkletBindings(CGAbstractMethod):
- def __init__(self, config):
- CGAbstractMethod.__init__(self, None, 'RegisterWorkletBindings', 'bool',
- [Argument('JSContext*', 'aCx'),
- Argument('JS::Handle<JSObject*>', 'aObj')])
- self.config = config
- def definition_body(self):
- descriptors = self.config.getDescriptors(hasInterfaceObject=True,
- isExposedInAnyWorklet=True,
- register=True)
- conditions = []
- for desc in descriptors:
- bindingNS = toBindingNamespace(desc.name)
- condition = "!%s::GetConstructorObject(aCx)" % bindingNS
- if desc.isExposedConditionally():
- condition = (
- "%s::ConstructorEnabled(aCx, aObj) && " % bindingNS
- + condition)
- conditions.append(condition)
- lines = [CGIfWrapper(CGGeneric("return false;\n"), condition) for
- condition in conditions]
- lines.append(CGGeneric("return true;\n"))
- return CGList(lines, "\n").define()
- class CGResolveSystemBinding(CGAbstractMethod):
- def __init__(self, config):
- CGAbstractMethod.__init__(self, None, 'ResolveSystemBinding', 'bool',
- [Argument('JSContext*', 'aCx'),
- Argument('JS::Handle<JSObject*>', 'aObj'),
- Argument('JS::Handle<jsid>', 'aId'),
- Argument('bool*', 'aResolvedp')])
- self.config = config
- def definition_body(self):
- descriptors = self.config.getDescriptors(hasInterfaceObject=True,
- isExposedInSystemGlobals=True,
- register=True)
- def descNameToId(name):
- return "s%s_id" % name
- jsidNames = [descNameToId(desc.name) for desc in descriptors]
- jsidDecls = CGList(CGGeneric("static jsid %s;\n" % name)
- for name in jsidNames)
- jsidInits = CGList(
- (CGIfWrapper(
- CGGeneric("return false;\n"),
- '!AtomizeAndPinJSString(aCx, %s, "%s")' %
- (descNameToId(desc.name), desc.interface.identifier.name))
- for desc in descriptors),
- "\n")
- jsidInits.append(CGGeneric("idsInited = true;\n"))
- jsidInits = CGIfWrapper(jsidInits, "!idsInited")
- jsidInits = CGList([CGGeneric("static bool idsInited = false;\n"),
- jsidInits])
- definitions = CGList([], "\n")
- for desc in descriptors:
- bindingNS = toBindingNamespace(desc.name)
- defineCode = "!%s::GetConstructorObject(aCx)" % bindingNS
- defineCode = CGIfWrapper(CGGeneric("return false;\n"), defineCode)
- defineCode = CGList([defineCode,
- CGGeneric("*aResolvedp = true;\n")])
- condition = "JSID_IS_VOID(aId) || aId == %s" % descNameToId(desc.name)
- if desc.isExposedConditionally():
- condition = "(%s) && %s::ConstructorEnabled(aCx, aObj)" % (condition, bindingNS)
- definitions.append(CGIfWrapper(defineCode, condition))
- return CGList([CGGeneric("MOZ_ASSERT(NS_IsMainThread());\n"),
- jsidDecls,
- jsidInits,
- definitions,
- CGGeneric("return true;\n")],
- "\n").define()
- def getGlobalNames(config):
- names = []
- for desc in config.getDescriptors(registersGlobalNamesOnWindow=True):
- names.append((desc.name, desc))
- names.extend((n.identifier.name, desc) for n in desc.interface.namedConstructors)
- return names
- class CGGlobalNamesString(CGGeneric):
- def __init__(self, config):
- globalNames = getGlobalNames(config)
- currentOffset = 0
- strings = []
- for (name, _) in globalNames:
- strings.append('/* %i */ "%s\\0"' % (currentOffset, name))
- currentOffset += len(name) + 1 # Add trailing null.
- define = fill("""
- const uint32_t WebIDLGlobalNameHash::sCount = ${count};
- const char WebIDLGlobalNameHash::sNames[] =
- $*{strings}
- """,
- count=len(globalNames),
- strings="\n".join(strings) + ";\n")
- CGGeneric.__init__(self, define=define)
- class CGRegisterGlobalNames(CGAbstractMethod):
- def __init__(self, config):
- CGAbstractMethod.__init__(self, None, 'RegisterWebIDLGlobalNames',
- 'void', [])
- self.config = config
- def definition_body(self):
- def getCheck(desc):
- if not desc.isExposedConditionally():
- return "nullptr"
- return "%sBinding::ConstructorEnabled" % desc.name
- define = ""
- currentOffset = 0
- for (name, desc) in getGlobalNames(self.config):
- length = len(name)
- define += "WebIDLGlobalNameHash::Register(%i, %i, %sBinding::DefineDOMInterface, %s);\n" % (currentOffset, length, desc.name, getCheck(desc))
- currentOffset += length + 1 # Add trailing null.
- return define
- def dependencySortObjects(objects, dependencyGetter, nameGetter):
- """
- Sort IDL objects with dependencies on each other such that if A
- depends on B then B will come before A. This is needed for
- declaring C++ classes in the right order, for example. Objects
- that have no dependencies are just sorted by name.
- objects should be something that can produce a set of objects
- (e.g. a set, iterator, list, etc).
- dependencyGetter is something that, given an object, should return
- the set of objects it depends on.
- """
- # XXXbz this will fail if we have two webidl files F1 and F2 such that F1
- # declares an object which depends on an object in F2, and F2 declares an
- # object (possibly a different one!) that depends on an object in F1. The
- # good news is that I expect this to never happen.
- sortedObjects = []
- objects = set(objects)
- while len(objects) != 0:
- # Find the dictionaries that don't depend on anything else
- # anymore and move them over.
- toMove = [o for o in objects if
- len(dependencyGetter(o) & objects) == 0]
- if len(toMove) == 0:
- raise TypeError("Loop in dependency graph\n" +
- "\n".join(o.location for o in objects))
- objects = objects - set(toMove)
- sortedObjects.extend(sorted(toMove, key=nameGetter))
- return sortedObjects
- class ForwardDeclarationBuilder:
- """
- Create a canonical representation of a set of namespaced forward
- declarations.
- """
- def __init__(self):
- """
- The set of declarations is represented as a tree of nested namespaces.
- Each tree node has a set of declarations |decls| and a dict |children|.
- Each declaration is a pair consisting of the class name and a boolean
- that is true iff the class is really a struct. |children| maps the
- names of inner namespaces to the declarations in that namespace.
- """
- self.decls = set()
- self.children = {}
- def _ensureNonTemplateType(self, type):
- if "<" in type:
- # This is a templated type. We don't really know how to
- # forward-declare those, and trying to do it naively is not going to
- # go well (e.g. we may have :: characters inside the type we're
- # templated on!). Just bail out.
- raise TypeError("Attempt to use ForwardDeclarationBuilder on "
- "templated type %s. We don't know how to do that "
- "yet." % type)
- def _listAdd(self, namespaces, name, isStruct=False):
- """
- Add a forward declaration, where |namespaces| is a list of namespaces.
- |name| should not contain any other namespaces.
- """
- if namespaces:
- child = self.children.setdefault(namespaces[0], ForwardDeclarationBuilder())
- child._listAdd(namespaces[1:], name, isStruct)
- else:
- assert '::' not in name
- self.decls.add((name, isStruct))
- def addInMozillaDom(self, name, isStruct=False):
- """
- Add a forward declaration to the mozilla::dom:: namespace. |name| should not
- contain any other namespaces.
- """
- self._ensureNonTemplateType(name);
- self._listAdd(["mozilla", "dom"], name, isStruct)
- def add(self, nativeType, isStruct=False):
- """
- Add a forward declaration, where |nativeType| is a string containing
- the type and its namespaces, in the usual C++ way.
- """
- self._ensureNonTemplateType(nativeType);
- components = nativeType.split('::')
- self._listAdd(components[:-1], components[-1], isStruct)
- def _build(self, atTopLevel):
- """
- Return a codegenerator for the forward declarations.
- """
- decls = []
- if self.decls:
- decls.append(CGList([CGClassForwardDeclare(cname, isStruct)
- for cname, isStruct in sorted(self.decls)]))
- for namespace, child in sorted(self.children.iteritems()):
- decls.append(CGNamespace(namespace, child._build(atTopLevel=False), declareOnly=True))
- cg = CGList(decls, "\n")
- if not atTopLevel and len(decls) + len(self.decls) > 1:
- cg = CGWrapper(cg, pre='\n', post='\n')
- return cg
- def build(self):
- return self._build(atTopLevel=True)
- def forwardDeclareForType(self, t, config):
- t = t.unroll()
- if t.isGeckoInterface():
- name = t.inner.identifier.name
- try:
- desc = config.getDescriptor(name)
- self.add(desc.nativeType)
- except NoSuchDescriptorError:
- pass
- # Note: Spidermonkey interfaces are typedefs, so can't be
- # forward-declared
- elif t.isPromise():
- self.addInMozillaDom("Promise")
- elif t.isCallback():
- self.addInMozillaDom(t.callback.identifier.name)
- elif t.isDictionary():
- self.addInMozillaDom(t.inner.identifier.name, isStruct=True)
- elif t.isCallbackInterface():
- self.addInMozillaDom(t.inner.identifier.name)
- elif t.isUnion():
- # Forward declare both the owning and non-owning version,
- # since we don't know which one we might want
- self.addInMozillaDom(CGUnionStruct.unionTypeName(t, False))
- self.addInMozillaDom(CGUnionStruct.unionTypeName(t, True))
- elif t.isRecord():
- self.forwardDeclareForType(t.inner, config)
- # Don't need to do anything for void, primitive, string, any or object.
- # There may be some other cases we are missing.
- class CGForwardDeclarations(CGWrapper):
- """
- Code generate the forward declarations for a header file.
- additionalDeclarations is a list of tuples containing a classname and a
- boolean. If the boolean is true we will declare a struct, otherwise we'll
- declare a class.
- """
- def __init__(self, config, descriptors, callbacks,
- dictionaries, callbackInterfaces, additionalDeclarations=[]):
- builder = ForwardDeclarationBuilder()
- # Needed for at least Wrap.
- for d in descriptors:
- # If this is a generated iterator interface, we only create these
- # in the generated bindings, and don't need to forward declare.
- if d.interface.isIteratorInterface():
- continue
- builder.add(d.nativeType)
- # If we're an interface and we have a maplike/setlike declaration,
- # we'll have helper functions exposed to the native side of our
- # bindings, which will need to show up in the header. If either of
- # our key/value types are interfaces, they'll be passed as
- # arguments to helper functions, and they'll need to be forward
- # declared in the header.
- if d.interface.maplikeOrSetlikeOrIterable:
- if d.interface.maplikeOrSetlikeOrIterable.hasKeyType():
- builder.forwardDeclareForType(d.interface.maplikeOrSetlikeOrIterable.keyType,
- config)
- if d.interface.maplikeOrSetlikeOrIterable.hasValueType():
- builder.forwardDeclareForType(d.interface.maplikeOrSetlikeOrIterable.valueType,
- config)
- # We just about always need NativePropertyHooks
- builder.addInMozillaDom("NativePropertyHooks", isStruct=True)
- builder.addInMozillaDom("ProtoAndIfaceCache")
- # Add the atoms cache type, even if we don't need it.
- for d in descriptors:
- # Iterators have native types that are template classes, so
- # creating an 'Atoms' cache type doesn't work for them, and is one
- # of the cases where we don't need it anyways.
- if d.interface.isIteratorInterface():
- continue
- builder.add(d.nativeType + "Atoms", isStruct=True)
- for callback in callbacks:
- builder.addInMozillaDom(callback.identifier.name)
- for t in getTypesFromCallback(callback):
- builder.forwardDeclareForType(t, config)
- for d in callbackInterfaces:
- builder.add(d.nativeType)
- builder.add(d.nativeType + "Atoms", isStruct=True)
- for t in getTypesFromDescriptor(d):
- builder.forwardDeclareForType(t, config)
- if d.hasCEReactions():
- builder.addInMozillaDom("DocGroup")
- for d in dictionaries:
- if len(d.members) > 0:
- builder.addInMozillaDom(d.identifier.name + "Atoms", isStruct=True)
- for t in getTypesFromDictionary(d):
- builder.forwardDeclareForType(t, config)
- for className, isStruct in additionalDeclarations:
- builder.add(className, isStruct=isStruct)
- CGWrapper.__init__(self, builder.build())
- class CGBindingRoot(CGThing):
- """
- Root codegen class for binding generation. Instantiate the class, and call
- declare or define to generate header or cpp code (respectively).
- """
- def __init__(self, config, prefix, webIDLFile):
- bindingHeaders = dict.fromkeys((
- 'mozilla/dom/NonRefcountedDOMObject.h',
- ),
- True)
- bindingDeclareHeaders = dict.fromkeys((
- 'mozilla/dom/BindingDeclarations.h',
- 'mozilla/dom/Nullable.h',
- 'mozilla/ErrorResult.h',
- ),
- True)
- descriptors = config.getDescriptors(webIDLFile=webIDLFile,
- hasInterfaceOrInterfacePrototypeObject=True)
- unionTypes = UnionsForFile(config, webIDLFile)
- (unionHeaders, unionImplheaders, unionDeclarations, traverseMethods,
- unlinkMethods, unionStructs) = UnionTypes(unionTypes, config)
- bindingDeclareHeaders.update(dict.fromkeys(unionHeaders, True))
- bindingHeaders.update(dict.fromkeys(unionImplheaders, True))
- bindingDeclareHeaders["mozilla/dom/UnionMember.h"] = len(unionStructs) > 0
- bindingDeclareHeaders["mozilla/dom/FakeString.h"] = len(unionStructs) > 0
- # BindingUtils.h is only needed for SetToObject.
- # If it stops being inlined or stops calling CallerSubsumes
- # both this bit and the bit in UnionTypes can be removed.
- bindingDeclareHeaders["mozilla/dom/BindingUtils.h"] = any(d.isObject() for t in unionTypes
- for d in t.flatMemberTypes)
- bindingDeclareHeaders["mozilla/dom/IterableIterator.h"] = any(d.interface.isIteratorInterface() or
- d.interface.isIterable() for d in descriptors)
- def descriptorHasCrossOriginProperties(desc):
- def hasCrossOriginProperty(m):
- props = memberProperties(m, desc)
- return (props.isCrossOriginMethod or
- props.isCrossOriginGetter or
- props.isCrossOriginSetter)
- return any(hasCrossOriginProperty(m) for m in desc.interface.members)
- bindingDeclareHeaders["jsapi.h"] = any(descriptorHasCrossOriginProperties(d) for d in descriptors)
- bindingDeclareHeaders["jspubtd.h"] = not bindingDeclareHeaders["jsapi.h"]
- bindingDeclareHeaders["js/RootingAPI.h"] = not bindingDeclareHeaders["jsapi.h"]
- def descriptorRequiresPreferences(desc):
- iface = desc.interface
- return any(m.getExtendedAttribute("Pref") for m in iface.members + [iface])
- def descriptorDeprecated(desc):
- iface = desc.interface
- return any(m.getExtendedAttribute("Deprecated") for m in iface.members + [iface])
- bindingHeaders["nsIDocument.h"] = any(
- descriptorDeprecated(d) for d in descriptors)
- bindingHeaders["mozilla/Preferences.h"] = any(
- descriptorRequiresPreferences(d) for d in descriptors)
- bindingHeaders["mozilla/dom/DOMJSProxyHandler.h"] = any(
- d.concrete and d.proxy for d in descriptors)
- hasCEReactions = any(d.hasCEReactions() for d in descriptors)
- bindingHeaders["mozilla/dom/CustomElementRegistry.h"] = hasCEReactions
- bindingHeaders["mozilla/dom/DocGroup.h"] = hasCEReactions
- def descriptorHasChromeOnly(desc):
- ctor = desc.interface.ctor()
- return (any(isChromeOnly(a) or needsContainsHack(a) or
- needsCallerType(a)
- for a in desc.interface.members) or
- desc.interface.getExtendedAttribute("ChromeOnly") is not None or
- # JS-implemented interfaces with an interface object get a
- # chromeonly _create method. And interfaces with an
- # interface object might have a ChromeOnly constructor.
- (desc.interface.hasInterfaceObject() and
- (desc.interface.isJSImplemented() or
- (ctor and isChromeOnly(ctor)))) or
- # JS-implemented interfaces with clearable cached
- # attrs have chromeonly _clearFoo methods.
- (desc.interface.isJSImplemented() and
- any(clearableCachedAttrs(desc))))
- # XXXkhuey ugly hack but this is going away soon.
- bindingHeaders['xpcprivate.h'] = webIDLFile.endswith("EventTarget.webidl")
- hasThreadChecks = any(d.hasThreadChecks() for d in descriptors)
- bindingHeaders["nsThreadUtils.h"] = hasThreadChecks
- dictionaries = config.getDictionaries(webIDLFile)
- def dictionaryHasChromeOnly(dictionary):
- while dictionary:
- if (any(isChromeOnly(m) for m in dictionary.members)):
- return True
- dictionary = dictionary.parent
- return False
- bindingHeaders["nsContentUtils.h"] = (
- any(descriptorHasChromeOnly(d) for d in descriptors) or
- any(dictionaryHasChromeOnly(d) for d in dictionaries))
- hasNonEmptyDictionaries = any(
- len(dict.members) > 0 for dict in dictionaries)
- callbacks = config.getCallbacks(webIDLFile)
- callbackDescriptors = config.getDescriptors(webIDLFile=webIDLFile,
- isCallback=True)
- jsImplemented = config.getDescriptors(webIDLFile=webIDLFile,
- isJSImplemented=True)
- bindingDeclareHeaders["nsWeakReference.h"] = jsImplemented
- bindingHeaders["nsIGlobalObject.h"] = jsImplemented
- bindingHeaders["AtomList.h"] = hasNonEmptyDictionaries or jsImplemented or callbackDescriptors
- def descriptorClearsPropsInSlots(descriptor):
- if not descriptor.wrapperCache:
- return False
- return any(m.isAttr() and m.getExtendedAttribute("StoreInSlot")
- for m in descriptor.interface.members)
- bindingHeaders["nsJSUtils.h"] = any(descriptorClearsPropsInSlots(d) for d in descriptors)
- # Do codegen for all the enums
- enums = config.getEnums(webIDLFile)
- cgthings = [CGEnum(e) for e in enums]
- hasCode = (descriptors or callbackDescriptors or dictionaries or
- callbacks)
- bindingHeaders["mozilla/dom/BindingUtils.h"] = hasCode
- bindingHeaders["mozilla/OwningNonNull.h"] = hasCode
- bindingHeaders["mozilla/dom/BindingDeclarations.h"] = (
- not hasCode and enums)
- bindingHeaders["WrapperFactory.h"] = descriptors
- bindingHeaders["mozilla/dom/DOMJSClass.h"] = descriptors
- bindingHeaders["mozilla/dom/ScriptSettings.h"] = dictionaries # AutoJSAPI
- # Ensure we see our enums in the generated .cpp file, for the ToJSValue
- # method body. Also ensure that we see jsapi.h.
- if enums:
- bindingHeaders[CGHeaders.getDeclarationFilename(enums[0])] = True
- bindingHeaders["jsapi.h"] = True
- # For things that have [UseCounter]
- def descriptorRequiresTelemetry(desc):
- iface = desc.interface
- return any(m.getExtendedAttribute("UseCounter") for m in iface.members)
- bindingHeaders["mozilla/UseCounter.h"] = any(
- descriptorRequiresTelemetry(d) for d in descriptors)
- bindingHeaders["mozilla/dom/SimpleGlobalObject.h"] = any(
- CGDictionary.dictionarySafeToJSONify(d) for d in dictionaries)
- bindingHeaders["XrayWrapper.h"] = any(
- d.wantsXrays and d.wantsXrayExpandoClass for d in descriptors)
- bindingHeaders["mozilla/dom/XrayExpandoClass.h"] = any(
- d.wantsXrays for d in descriptors)
- cgthings.extend(traverseMethods)
- cgthings.extend(unlinkMethods)
- # Do codegen for all the dictionaries. We have to be a bit careful
- # here, because we have to generate these in order from least derived
- # to most derived so that class inheritance works out. We also have to
- # generate members before the dictionary that contains them.
- def getDependenciesFromType(type):
- if type.isDictionary():
- return set([type.unroll().inner])
- if type.isSequence():
- return getDependenciesFromType(type.unroll())
- if type.isUnion():
- return set([type.unroll()])
- return set()
- def getDependencies(unionTypeOrDictionary):
- if isinstance(unionTypeOrDictionary, IDLDictionary):
- deps = set()
- if unionTypeOrDictionary.parent:
- deps.add(unionTypeOrDictionary.parent)
- for member in unionTypeOrDictionary.members:
- deps |= getDependenciesFromType(member.type)
- return deps
- assert unionTypeOrDictionary.isType() and unionTypeOrDictionary.isUnion()
- deps = set()
- for member in unionTypeOrDictionary.flatMemberTypes:
- deps |= getDependenciesFromType(member)
- return deps
- def getName(unionTypeOrDictionary):
- if isinstance(unionTypeOrDictionary, IDLDictionary):
- return unionTypeOrDictionary.identifier.name
- assert unionTypeOrDictionary.isType() and unionTypeOrDictionary.isUnion()
- return unionTypeOrDictionary.name
- for t in dependencySortObjects(dictionaries + unionStructs, getDependencies, getName):
- if t.isDictionary():
- cgthings.append(CGDictionary(t, config))
- else:
- assert t.isUnion()
- cgthings.append(CGUnionStruct(t, config))
- cgthings.append(CGUnionStruct(t, config, True))
- # Do codegen for all the callbacks.
- cgthings.extend(CGCallbackFunction(c, config) for c in callbacks)
- cgthings.extend([CGNamespace('binding_detail', CGFastCallback(c))
- for c in callbacks])
- # Do codegen for all the descriptors
- cgthings.extend([CGDescriptor(x) for x in descriptors])
- # Do codegen for all the callback interfaces.
- cgthings.extend([CGCallbackInterface(x) for x in callbackDescriptors])
- cgthings.extend([CGNamespace('binding_detail',
- CGFastCallback(x.interface))
- for x in callbackDescriptors])
- # Do codegen for JS implemented classes
- def getParentDescriptor(desc):
- if not desc.interface.parent:
- return set()
- return {desc.getDescriptor(desc.interface.parent.identifier.name)}
- for x in dependencySortObjects(jsImplemented, getParentDescriptor,
- lambda d: d.interface.identifier.name):
- cgthings.append(CGCallbackInterface(x, typedArraysAreStructs=True))
- cgthings.append(CGJSImplClass(x))
- # And make sure we have the right number of newlines at the end
- curr = CGWrapper(CGList(cgthings, "\n\n"), post="\n\n")
- # Wrap all of that in our namespaces.
- curr = CGNamespace.build(['mozilla', 'dom'],
- CGWrapper(curr, pre="\n"))
- curr = CGList([CGForwardDeclarations(config, descriptors,
- callbacks,
- dictionaries,
- callbackDescriptors + jsImplemented,
- additionalDeclarations=unionDeclarations),
- curr],
- "\n")
- # Add header includes.
- bindingHeaders = [header
- for header, include in bindingHeaders.iteritems()
- if include]
- bindingDeclareHeaders = [header
- for header, include in bindingDeclareHeaders.iteritems()
- if include]
- curr = CGHeaders(descriptors,
- dictionaries,
- callbacks,
- callbackDescriptors,
- bindingDeclareHeaders,
- bindingHeaders,
- prefix,
- curr,
- config,
- jsImplemented)
- # Add include guards.
- curr = CGIncludeGuard(prefix, curr)
- # Add the auto-generated comment.
- curr = CGWrapper(
- curr,
- pre=(AUTOGENERATED_WITH_SOURCE_WARNING_COMMENT %
- os.path.basename(webIDLFile)))
- # Store the final result.
- self.root = curr
- def declare(self):
- return stripTrailingWhitespace(self.root.declare())
- def define(self):
- return stripTrailingWhitespace(self.root.define())
- def deps(self):
- return self.root.deps()
- class CGNativeMember(ClassMethod):
- def __init__(self, descriptorProvider, member, name, signature, extendedAttrs,
- breakAfter=True, passJSBitsAsNeeded=True, visibility="public",
- typedArraysAreStructs=True, variadicIsSequence=False,
- resultNotAddRefed=False,
- virtual=False,
- override=False):
- """
- If typedArraysAreStructs is false, typed arrays will be passed as
- JS::Handle<JSObject*>. If it's true they will be passed as one of the
- dom::TypedArray subclasses.
- If passJSBitsAsNeeded is false, we don't automatically pass in a
- JSContext* or a JSObject* based on the return and argument types. We
- can still pass it based on 'implicitJSContext' annotations.
- """
- self.descriptorProvider = descriptorProvider
- self.member = member
- self.extendedAttrs = extendedAttrs
- self.resultAlreadyAddRefed = not resultNotAddRefed
- self.passJSBitsAsNeeded = passJSBitsAsNeeded
- self.typedArraysAreStructs = typedArraysAreStructs
- self.variadicIsSequence = variadicIsSequence
- breakAfterSelf = "\n" if breakAfter else ""
- ClassMethod.__init__(self, name,
- self.getReturnType(signature[0], False),
- self.getArgs(signature[0], signature[1]),
- static=member.isStatic(),
- # Mark our getters, which are attrs that
- # have a non-void return type, as const.
- const=(not member.isStatic() and member.isAttr() and
- not signature[0].isVoid()),
- breakAfterReturnDecl=" ",
- breakAfterSelf=breakAfterSelf,
- visibility=visibility,
- virtual=virtual,
- override=override)
- def getReturnType(self, type, isMember):
- return self.getRetvalInfo(type, isMember)[0]
- def getRetvalInfo(self, type, isMember):
- """
- Returns a tuple:
- The first element is the type declaration for the retval
- The second element is a default value that can be used on error returns.
- For cases whose behavior depends on isMember, the second element will be
- None if isMember is true.
- The third element is a template for actually returning a value stored in
- "${declName}" and "${holderName}". This means actually returning it if
- we're not outparam, else assigning to the "retval" outparam. If
- isMember is true, this can be None, since in that case the caller will
- never examine this value.
- """
- if type.isVoid():
- return "void", "", ""
- if type.isPrimitive() and type.tag() in builtinNames:
- result = CGGeneric(builtinNames[type.tag()])
- defaultReturnArg = "0"
- if type.nullable():
- result = CGTemplatedType("Nullable", result)
- defaultReturnArg = ""
- return (result.define(),
- "%s(%s)" % (result.define(), defaultReturnArg),
- "return ${declName};\n")
- if type.isDOMString() or type.isUSVString():
- if isMember:
- # No need for a third element in the isMember case
- return "nsString", None, None
- # Outparam
- return "void", "", "aRetVal = ${declName};\n"
- if type.isByteString():
- if isMember:
- # No need for a third element in the isMember case
- return "nsCString", None, None
- # Outparam
- return "void", "", "aRetVal = ${declName};\n"
- if type.isEnum():
- enumName = type.unroll().inner.identifier.name
- if type.nullable():
- enumName = CGTemplatedType("Nullable",
- CGGeneric(enumName)).define()
- defaultValue = "%s()" % enumName
- else:
- defaultValue = "%s(0)" % enumName
- return enumName, defaultValue, "return ${declName};\n"
- if type.isGeckoInterface() or type.isPromise():
- if type.isGeckoInterface():
- iface = type.unroll().inner
- result = CGGeneric(self.descriptorProvider.getDescriptor(
- iface.identifier.name).prettyNativeType)
- else:
- result = CGGeneric("Promise")
- if self.resultAlreadyAddRefed:
- if isMember:
- holder = "RefPtr"
- else:
- holder = "already_AddRefed"
- if memberReturnsNewObject(self.member) or isMember:
- warning = ""
- else:
- warning = "// Return a raw pointer here to avoid refcounting, but make sure it's safe (the object should be kept alive by the callee).\n"
- result = CGWrapper(result,
- pre=("%s%s<" % (warning, holder)),
- post=">")
- else:
- result = CGWrapper(result, post="*")
- # Since we always force an owning type for callback return values,
- # our ${declName} is an OwningNonNull or RefPtr. So we can just
- # .forget() to get our already_AddRefed.
- return result.define(), "nullptr", "return ${declName}.forget();\n"
- if type.isCallback():
- return ("already_AddRefed<%s>" % type.unroll().callback.identifier.name,
- "nullptr", "return ${declName}.forget();\n")
- if type.isAny():
- if isMember:
- # No need for a third element in the isMember case
- return "JS::Value", None, None
- # Outparam
- return "void", "", "aRetVal.set(${declName});\n"
- if type.isObject():
- if isMember:
- # No need for a third element in the isMember case
- return "JSObject*", None, None
- return "void", "", "aRetVal.set(${declName});\n"
- if type.isSpiderMonkeyInterface():
- if isMember:
- # No need for a third element in the isMember case
- return "JSObject*", None, None
- if type.nullable():
- returnCode = "${declName}.IsNull() ? nullptr : ${declName}.Value().Obj()"
- else:
- returnCode = "${declName}.Obj()"
- return "void", "", "aRetVal.set(%s);\n" % returnCode
- if type.isSequence():
- # If we want to handle sequence-of-sequences return values, we're
- # going to need to fix example codegen to not produce nsTArray<void>
- # for the relevant argument...
- assert not isMember
- # Outparam.
- if type.nullable():
- returnCode = dedent("""
- if (${declName}.IsNull()) {
- aRetVal.SetNull();
- } else {
- aRetVal.SetValue().SwapElements(${declName}.Value());
- }
- """)
- else:
- returnCode = "aRetVal.SwapElements(${declName});\n"
- return "void", "", returnCode
- if type.isRecord():
- # If we want to handle record-of-record return values, we're
- # going to need to fix example codegen to not produce record<void>
- # for the relevant argument...
- assert not isMember
- # In this case we convert directly into our outparam to start with
- return "void", "", ""
- if type.isDate():
- result = CGGeneric("Date")
- if type.nullable():
- result = CGTemplatedType("Nullable", result)
- return (result.define(), "%s()" % result.define(),
- "return ${declName};\n")
- if type.isDictionary():
- if isMember:
- # Only the first member of the tuple matters here, but return
- # bogus values for the others in case someone decides to use
- # them.
- return CGDictionary.makeDictionaryName(type.inner), None, None
- # In this case we convert directly into our outparam to start with
- return "void", "", ""
- if type.isUnion():
- if isMember:
- # Only the first member of the tuple matters here, but return
- # bogus values for the others in case someone decides to use
- # them.
- return CGUnionStruct.unionTypeDecl(type, True), None, None
- # In this case we convert directly into our outparam to start with
- return "void", "", ""
- raise TypeError("Don't know how to declare return value for %s" %
- type)
- def getArgs(self, returnType, argList):
- args = [self.getArg(arg) for arg in argList]
- # Now the outparams
- if returnType.isDOMString() or returnType.isUSVString():
- args.append(Argument("nsString&", "aRetVal"))
- elif returnType.isByteString():
- args.append(Argument("nsCString&", "aRetVal"))
- elif returnType.isSequence():
- nullable = returnType.nullable()
- if nullable:
- returnType = returnType.inner
- # And now the actual underlying type
- elementDecl = self.getReturnType(returnType.inner, True)
- type = CGTemplatedType("nsTArray", CGGeneric(elementDecl))
- if nullable:
- type = CGTemplatedType("Nullable", type)
- args.append(Argument("%s&" % type.define(), "aRetVal"))
- elif returnType.isRecord():
- nullable = returnType.nullable()
- if nullable:
- returnType = returnType.inner
- # And now the actual underlying type
- elementDecl = self.getReturnType(returnType.inner, True)
- type = CGTemplatedType("Record", [recordKeyDeclType(returnType),
- CGGeneric(elementDecl)])
- if nullable:
- type = CGTemplatedType("Nullable", type)
- args.append(Argument("%s&" % type.define(), "aRetVal"))
- elif returnType.isDictionary():
- nullable = returnType.nullable()
- if nullable:
- returnType = returnType.inner
- dictType = CGGeneric(CGDictionary.makeDictionaryName(returnType.inner))
- if nullable:
- dictType = CGTemplatedType("Nullable", dictType)
- args.append(Argument("%s&" % dictType.define(), "aRetVal"))
- elif returnType.isUnion():
- args.append(Argument("%s&" %
- CGUnionStruct.unionTypeDecl(returnType, True),
- "aRetVal"))
- elif returnType.isAny():
- args.append(Argument("JS::MutableHandle<JS::Value>", "aRetVal"))
- elif returnType.isObject() or returnType.isSpiderMonkeyInterface():
- args.append(Argument("JS::MutableHandle<JSObject*>", "aRetVal"))
- # And the nsIPrincipal
- if self.member.getExtendedAttribute('NeedsSubjectPrincipal'):
- # Cheat and assume self.descriptorProvider is a descriptor
- if self.descriptorProvider.interface.isExposedInAnyWorker():
- args.append(Argument("Maybe<nsIPrincipal*>", "aSubjectPrincipal"))
- else:
- args.append(Argument("nsIPrincipal&", "aPrincipal"))
- # And the caller type, if desired.
- if needsCallerType(self.member):
- args.append(Argument("CallerType", "aCallerType"))
- # And the ErrorResult
- if 'infallible' not in self.extendedAttrs:
- # Use aRv so it won't conflict with local vars named "rv"
- args.append(Argument("ErrorResult&", "aRv"))
- # The legacycaller thisval
- if self.member.isMethod() and self.member.isLegacycaller():
- # If it has an identifier, we can't deal with it yet
- assert self.member.isIdentifierLess()
- args.insert(0, Argument("const JS::Value&", "aThisVal"))
- # And jscontext bits.
- if needCx(returnType, argList, self.extendedAttrs,
- self.passJSBitsAsNeeded, self.member.isStatic()):
- args.insert(0, Argument("JSContext*", "cx"))
- if needScopeObject(returnType, argList, self.extendedAttrs,
- self.descriptorProvider.wrapperCache,
- self.passJSBitsAsNeeded,
- self.member.getExtendedAttribute("StoreInSlot")):
- args.insert(1, Argument("JS::Handle<JSObject*>", "obj"))
- # And if we're static, a global
- if self.member.isStatic():
- args.insert(0, Argument("const GlobalObject&", "global"))
- return args
- def doGetArgType(self, type, optional, isMember):
- """
- The main work of getArgType. Returns a string type decl, whether this
- is a const ref, as well as whether the type should be wrapped in
- Nullable as needed.
- isMember can be false or one of the strings "Sequence", "Variadic",
- "Record"
- """
- if type.isSequence():
- nullable = type.nullable()
- if nullable:
- type = type.inner
- elementType = type.inner
- argType = self.getArgType(elementType, False, "Sequence")[0]
- decl = CGTemplatedType("Sequence", argType)
- return decl.define(), True, True
- if type.isRecord():
- nullable = type.nullable()
- if nullable:
- type = type.inner
- elementType = type.inner
- argType = self.getArgType(elementType, False, "Record")[0]
- decl = CGTemplatedType("Record", [recordKeyDeclType(type), argType])
- return decl.define(), True, True
- if type.isUnion():
- # unionTypeDecl will handle nullable types, so return False for
- # auto-wrapping in Nullable
- return CGUnionStruct.unionTypeDecl(type, isMember), True, False
- if type.isPromise():
- assert not type.nullable()
- if optional or isMember:
- typeDecl = "OwningNonNull<Promise>"
- else:
- typeDecl = "Promise&"
- return (typeDecl, False, False)
- if type.isGeckoInterface() and not type.isCallbackInterface():
- iface = type.unroll().inner
- argIsPointer = type.nullable() or iface.isExternal()
- forceOwningType = (iface.isCallback() or isMember)
- if argIsPointer:
- if (optional or isMember) and forceOwningType:
- typeDecl = "RefPtr<%s>"
- else:
- typeDecl = "%s*"
- else:
- if optional or isMember:
- if forceOwningType:
- typeDecl = "OwningNonNull<%s>"
- else:
- typeDecl = "NonNull<%s>"
- else:
- typeDecl = "%s&"
- return ((typeDecl %
- self.descriptorProvider.getDescriptor(iface.identifier.name).prettyNativeType),
- False, False)
- if type.isSpiderMonkeyInterface():
- if not self.typedArraysAreStructs:
- return "JS::Handle<JSObject*>", False, False
- # Unroll for the name, in case we're nullable.
- return type.unroll().name, True, True
- if type.isDOMString() or type.isUSVString():
- if isMember:
- declType = "nsString"
- else:
- declType = "nsAString"
- return declType, True, False
- if type.isByteString():
- declType = "nsCString"
- return declType, True, False
- if type.isEnum():
- return type.unroll().inner.identifier.name, False, True
- if type.isCallback() or type.isCallbackInterface():
- forceOwningType = optional or isMember
- if type.nullable():
- if forceOwningType:
- declType = "RefPtr<%s>"
- else:
- declType = "%s*"
- else:
- if forceOwningType:
- declType = "OwningNonNull<%s>"
- else:
- declType = "%s&"
- if type.isCallback():
- name = type.unroll().callback.identifier.name
- else:
- name = type.unroll().inner.identifier.name
- return declType % name, False, False
- if type.isAny():
- # Don't do the rooting stuff for variadics for now
- if isMember:
- declType = "JS::Value"
- else:
- declType = "JS::Handle<JS::Value>"
- return declType, False, False
- if type.isObject():
- if isMember:
- declType = "JSObject*"
- else:
- declType = "JS::Handle<JSObject*>"
- return declType, False, False
- if type.isDictionary():
- typeName = CGDictionary.makeDictionaryName(type.inner)
- return typeName, True, True
- if type.isDate():
- return "Date", False, True
- assert type.isPrimitive()
- return builtinNames[type.tag()], False, True
- def getArgType(self, type, optional, isMember):
- """
- Get the type of an argument declaration. Returns the type CGThing, and
- whether this should be a const ref.
- isMember can be False, "Sequence", or "Variadic"
- """
- decl, ref, handleNullable = self.doGetArgType(type, optional, isMember)
- decl = CGGeneric(decl)
- if handleNullable and type.nullable():
- decl = CGTemplatedType("Nullable", decl)
- ref = True
- if isMember == "Variadic":
- arrayType = "Sequence" if self.variadicIsSequence else "nsTArray"
- decl = CGTemplatedType(arrayType, decl)
- ref = True
- elif optional:
- # Note: All variadic args claim to be optional, but we can just use
- # empty arrays to represent them not being present.
- decl = CGTemplatedType("Optional", decl)
- ref = True
- return (decl, ref)
- def getArg(self, arg):
- """
- Get the full argument declaration for an argument
- """
- decl, ref = self.getArgType(arg.type, arg.canHaveMissingValue(),
- "Variadic" if arg.variadic else False)
- if ref:
- decl = CGWrapper(decl, pre="const ", post="&")
- return Argument(decl.define(), arg.identifier.name)
- def arguments(self):
- return self.member.signatures()[0][1]
- class CGExampleMethod(CGNativeMember):
- def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True):
- CGNativeMember.__init__(self, descriptor, method,
- CGSpecializedMethod.makeNativeName(descriptor,
- method),
- signature,
- descriptor.getExtendedAttributes(method),
- breakAfter=breakAfter,
- variadicIsSequence=True)
- def declare(self, cgClass):
- assert self.member.isMethod()
- # We skip declaring ourselves if this is a maplike/setlike/iterable
- # method, because those get implemented automatically by the binding
- # machinery, so the implementor of the interface doesn't have to worry
- # about it.
- if self.member.isMaplikeOrSetlikeOrIterableMethod():
- return ''
- return CGNativeMember.declare(self, cgClass);
- def define(self, cgClass):
- return ''
- class CGExampleGetter(CGNativeMember):
- def __init__(self, descriptor, attr):
- CGNativeMember.__init__(self, descriptor, attr,
- CGSpecializedGetter.makeNativeName(descriptor,
- attr),
- (attr.type, []),
- descriptor.getExtendedAttributes(attr,
- getter=True))
- def declare(self, cgClass):
- assert self.member.isAttr()
- # We skip declaring ourselves if this is a maplike/setlike attr (in
- # practice, "size"), because those get implemented automatically by the
- # binding machinery, so the implementor of the interface doesn't have to
- # worry about it.
- if self.member.isMaplikeOrSetlikeAttr():
- return ''
- return CGNativeMember.declare(self, cgClass);
- def define(self, cgClass):
- return ''
- class CGExampleSetter(CGNativeMember):
- def __init__(self, descriptor, attr):
- CGNativeMember.__init__(self, descriptor, attr,
- CGSpecializedSetter.makeNativeName(descriptor,
- attr),
- (BuiltinTypes[IDLBuiltinType.Types.void],
- [FakeArgument(attr.type, attr)]),
- descriptor.getExtendedAttributes(attr,
- setter=True))
- def define(self, cgClass):
- return ''
- class CGBindingImplClass(CGClass):
- """
- Common codegen for generating a C++ implementation of a WebIDL interface
- """
- def __init__(self, descriptor, cgMethod, cgGetter, cgSetter, wantGetParent=True, wrapMethodName="WrapObject", skipStaticMethods=False):
- """
- cgMethod, cgGetter and cgSetter are classes used to codegen methods,
- getters and setters.
- """
- self.descriptor = descriptor
- self._deps = descriptor.interface.getDeps()
- iface = descriptor.interface
- self.methodDecls = []
- def appendMethod(m, isConstructor=False):
- sigs = m.signatures()
- for s in sigs[:-1]:
- # Don't put a blank line after overloads, until we
- # get to the last one.
- self.methodDecls.append(cgMethod(descriptor, m, s,
- isConstructor,
- breakAfter=False))
- self.methodDecls.append(cgMethod(descriptor, m, sigs[-1],
- isConstructor))
- if iface.ctor():
- appendMethod(iface.ctor(), isConstructor=True)
- for n in iface.namedConstructors:
- appendMethod(n, isConstructor=True)
- for m in iface.members:
- if m.isMethod():
- if m.isIdentifierLess():
- continue
- if not m.isStatic() or not skipStaticMethods:
- appendMethod(m)
- elif m.isAttr():
- self.methodDecls.append(cgGetter(descriptor, m))
- if not m.readonly:
- self.methodDecls.append(cgSetter(descriptor, m))
- # Now do the special operations
- def appendSpecialOperation(name, op):
- if op is None:
- return
- if name == "IndexedCreator" or name == "NamedCreator":
- # These are identical to the setters
- return
- assert len(op.signatures()) == 1
- returnType, args = op.signatures()[0]
- # Make a copy of the args, since we plan to modify them.
- args = list(args)
- if op.isGetter() or op.isDeleter():
- # This is a total hack. The '&' belongs with the
- # type, not the name! But it works, and is simpler
- # than trying to somehow make this pretty.
- args.append(FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean],
- op, name="&found"))
- if name == "Stringifier":
- if op.isIdentifierLess():
- # XXXbz I wish we were consistent about our renaming here.
- name = "Stringify"
- else:
- # We already added this method
- return
- if name == "LegacyCaller":
- if op.isIdentifierLess():
- # XXXbz I wish we were consistent about our renaming here.
- name = "LegacyCall"
- else:
- # We already added this method
- return
- if name == "Jsonifier":
- # We already added this method
- return
- self.methodDecls.append(
- CGNativeMember(descriptor, op,
- name,
- (returnType, args),
- descriptor.getExtendedAttributes(op)))
- # Sort things by name so we get stable ordering in the output.
- ops = descriptor.operations.items()
- ops.sort(key=lambda x: x[0])
- for name, op in ops:
- appendSpecialOperation(name, op)
- # If we support indexed properties, then we need a Length()
- # method so we know which indices are supported.
- if descriptor.supportsIndexedProperties():
- # But we don't need it if we already have an infallible
- # "length" attribute, which we often do.
- haveLengthAttr = any(
- m for m in iface.members if m.isAttr() and
- CGSpecializedGetter.makeNativeName(descriptor, m) == "Length")
- if not haveLengthAttr:
- self.methodDecls.append(
- CGNativeMember(descriptor, FakeMember(),
- "Length",
- (BuiltinTypes[IDLBuiltinType.Types.unsigned_long],
- []),
- {"infallible": True}))
- # And if we support named properties we need to be able to
- # enumerate the supported names.
- if descriptor.supportsNamedProperties():
- self.methodDecls.append(
- CGNativeMember(
- descriptor, FakeMember(),
- "GetSupportedNames",
- (IDLSequenceType(None,
- BuiltinTypes[IDLBuiltinType.Types.domstring]),
- []),
- {"infallible": True}))
- wrapArgs = [Argument('JSContext*', 'aCx'),
- Argument('JS::Handle<JSObject*>', 'aGivenProto')]
- if not descriptor.wrapperCache:
- wrapReturnType = "bool"
- wrapArgs.append(Argument('JS::MutableHandle<JSObject*>',
- 'aReflector'))
- else:
- wrapReturnType = "JSObject*"
- self.methodDecls.insert(0,
- ClassMethod(wrapMethodName, wrapReturnType,
- wrapArgs, virtual=descriptor.wrapperCache,
- breakAfterReturnDecl=" ",
- override=descriptor.wrapperCache,
- body=self.getWrapObjectBody()))
- if descriptor.hasCEReactions():
- self.methodDecls.insert(0,
- ClassMethod("GetDocGroup", "DocGroup*", [],
- const=True,
- breakAfterReturnDecl=" ",
- body=self.getGetDocGroupBody()))
- if wantGetParent:
- self.methodDecls.insert(0,
- ClassMethod("GetParentObject",
- self.getGetParentObjectReturnType(),
- [], const=True,
- breakAfterReturnDecl=" ",
- body=self.getGetParentObjectBody()))
- # Invoke CGClass.__init__ in any subclasses afterwards to do the actual codegen.
- def getWrapObjectBody(self):
- return None
- def getGetParentObjectReturnType(self):
- return ("// TODO: return something sensible here, and change the return type\n"
- "%s*" % self.descriptor.nativeType.split('::')[-1])
- def getGetParentObjectBody(self):
- return None
- def getGetDocGroupBody(self):
- return None
- def deps(self):
- return self._deps
- class CGExampleClass(CGBindingImplClass):
- """
- Codegen for the actual example class implementation for this descriptor
- """
- def __init__(self, descriptor):
- CGBindingImplClass.__init__(self, descriptor,
- CGExampleMethod, CGExampleGetter, CGExampleSetter,
- wantGetParent=descriptor.wrapperCache)
- self.parentIface = descriptor.interface.parent
- if self.parentIface:
- self.parentDesc = descriptor.getDescriptor(
- self.parentIface.identifier.name)
- bases = [ClassBase(self.nativeLeafName(self.parentDesc))]
- else:
- bases = [ClassBase("nsISupports /* or NonRefcountedDOMObject if this is a non-refcounted object */")]
- if descriptor.wrapperCache:
- bases.append(ClassBase("nsWrapperCache /* Change wrapperCache in the binding configuration if you don't want this */"))
- destructorVisibility = "protected"
- if self.parentIface:
- extradeclarations = (
- "public:\n"
- " NS_DECL_ISUPPORTS_INHERITED\n"
- " NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(%s, %s)\n"
- "\n" % (self.nativeLeafName(descriptor),
- self.nativeLeafName(self.parentDesc)))
- else:
- extradeclarations = (
- "public:\n"
- " NS_DECL_CYCLE_COLLECTING_ISUPPORTS\n"
- " NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(%s)\n"
- "\n" % self.nativeLeafName(descriptor))
- if descriptor.interface.hasChildInterfaces():
- decorators = ""
- else:
- decorators = "final"
- CGClass.__init__(self, self.nativeLeafName(descriptor),
- bases=bases,
- constructors=[ClassConstructor([],
- visibility="public")],
- destructor=ClassDestructor(visibility=destructorVisibility),
- methods=self.methodDecls,
- decorators=decorators,
- extradeclarations=extradeclarations)
- def define(self):
- # Just override CGClass and do our own thing
- ctordtor = dedent("""
- ${nativeType}::${nativeType}()
- {
- // Add |MOZ_COUNT_CTOR(${nativeType});| for a non-refcounted object.
- }
- ${nativeType}::~${nativeType}()
- {
- // Add |MOZ_COUNT_DTOR(${nativeType});| for a non-refcounted object.
- }
- """)
- if self.parentIface:
- ccImpl = dedent("""
- // Only needed for refcounted objects.
- # error "If you don't have members that need cycle collection,
- # then remove all the cycle collection bits from this
- # implementation and the corresponding header. If you do, you
- # want NS_IMPL_CYCLE_COLLECTION_INHERITED(${nativeType},
- # ${parentType}, your, members, here)"
- NS_IMPL_ADDREF_INHERITED(${nativeType}, ${parentType})
- NS_IMPL_RELEASE_INHERITED(${nativeType}, ${parentType})
- NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType})
- NS_INTERFACE_MAP_END_INHERITING(${parentType})
- """)
- else:
- ccImpl = dedent("""
- // Only needed for refcounted objects.
- NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(${nativeType})
- NS_IMPL_CYCLE_COLLECTING_ADDREF(${nativeType})
- NS_IMPL_CYCLE_COLLECTING_RELEASE(${nativeType})
- NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType})
- NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
- NS_INTERFACE_MAP_ENTRY(nsISupports)
- NS_INTERFACE_MAP_END
- """)
- if self.descriptor.wrapperCache:
- reflectorArg = ""
- reflectorPassArg = ""
- returnType = "JSObject*"
- else:
- reflectorArg = ", JS::MutableHandle<JSObject*> aReflector"
- reflectorPassArg = ", aReflector"
- returnType = "bool"
- classImpl = ccImpl + ctordtor + "\n" + dedent("""
- ${returnType}
- ${nativeType}::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto${reflectorArg})
- {
- return ${ifaceName}Binding::Wrap(aCx, this, aGivenProto${reflectorPassArg});
- }
- """)
- return string.Template(classImpl).substitute(
- ifaceName=self.descriptor.name,
- nativeType=self.nativeLeafName(self.descriptor),
- parentType=self.nativeLeafName(self.parentDesc) if self.parentIface else "",
- returnType=returnType,
- reflectorArg=reflectorArg,
- reflectorPassArg=reflectorPassArg)
- @staticmethod
- def nativeLeafName(descriptor):
- return descriptor.nativeType.split('::')[-1]
- class CGExampleRoot(CGThing):
- """
- Root codegen class for example implementation generation. Instantiate the
- class and call declare or define to generate header or cpp code,
- respectively.
- """
- def __init__(self, config, interfaceName):
- descriptor = config.getDescriptor(interfaceName)
- self.root = CGWrapper(CGExampleClass(descriptor),
- pre="\n", post="\n")
- self.root = CGNamespace.build(["mozilla", "dom"], self.root)
- builder = ForwardDeclarationBuilder()
- if descriptor.hasCEReactions():
- builder.addInMozillaDom("DocGroup")
- for member in descriptor.interface.members:
- if not member.isAttr() and not member.isMethod():
- continue
- if member.isStatic():
- builder.addInMozillaDom("GlobalObject")
- if member.isAttr() and not member.isMaplikeOrSetlikeAttr():
- builder.forwardDeclareForType(member.type, config)
- else:
- assert member.isMethod()
- if not member.isMaplikeOrSetlikeOrIterableMethod():
- for sig in member.signatures():
- builder.forwardDeclareForType(sig[0], config)
- for arg in sig[1]:
- builder.forwardDeclareForType(arg.type, config)
- self.root = CGList([builder.build(),
- self.root], "\n")
- # Throw in our #includes
- self.root = CGHeaders([], [], [], [],
- ["nsWrapperCache.h",
- "nsCycleCollectionParticipant.h",
- "mozilla/Attributes.h",
- "mozilla/ErrorResult.h",
- "mozilla/dom/BindingDeclarations.h",
- "js/TypeDecls.h"],
- ["mozilla/dom/%s.h" % interfaceName,
- ("mozilla/dom/%s" %
- CGHeaders.getDeclarationFilename(descriptor.interface))], "", self.root)
- # And now some include guards
- self.root = CGIncludeGuard(interfaceName, self.root)
- # And our license block comes before everything else
- self.root = CGWrapper(self.root, pre=dedent("""
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* vim:set ts=2 sw=2 sts=2 et cindent: */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- """))
- def declare(self):
- return self.root.declare()
- def define(self):
- return self.root.define()
- def jsImplName(name):
- return name + "JSImpl"
- class CGJSImplMember(CGNativeMember):
- """
- Base class for generating code for the members of the implementation class
- for a JS-implemented WebIDL interface.
- """
- def __init__(self, descriptorProvider, member, name, signature,
- extendedAttrs, breakAfter=True, passJSBitsAsNeeded=True,
- visibility="public", variadicIsSequence=False,
- virtual=False, override=False):
- CGNativeMember.__init__(self, descriptorProvider, member, name,
- signature, extendedAttrs, breakAfter=breakAfter,
- passJSBitsAsNeeded=passJSBitsAsNeeded,
- visibility=visibility,
- variadicIsSequence=variadicIsSequence,
- virtual=virtual,
- override=override)
- self.body = self.getImpl()
- def getArgs(self, returnType, argList):
- args = CGNativeMember.getArgs(self, returnType, argList)
- args.append(Argument("JSCompartment*", "aCompartment", "nullptr"))
- return args
- class CGJSImplMethod(CGJSImplMember):
- """
- Class for generating code for the methods for a JS-implemented WebIDL
- interface.
- """
- def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True):
- virtual = False
- override = False
- if (method.identifier.name == "eventListenerWasAdded" or
- method.identifier.name == "eventListenerWasRemoved"):
- virtual = True
- override = True
- self.signature = signature
- self.descriptor = descriptor
- self.isConstructor = isConstructor
- CGJSImplMember.__init__(self, descriptor, method,
- CGSpecializedMethod.makeNativeName(descriptor,
- method),
- signature,
- descriptor.getExtendedAttributes(method),
- breakAfter=breakAfter,
- variadicIsSequence=True,
- passJSBitsAsNeeded=False,
- virtual=virtual,
- override=override)
- def getArgs(self, returnType, argList):
- if self.isConstructor:
- # Skip the JSCompartment bits for constructors; it's handled
- # manually in getImpl.
- return CGNativeMember.getArgs(self, returnType, argList)
- return CGJSImplMember.getArgs(self, returnType, argList)
- def getImpl(self):
- args = self.getArgs(self.signature[0], self.signature[1])
- if not self.isConstructor:
- return 'return mImpl->%s(%s);\n' % (self.name, ", ".join(arg.name for arg in args))
- assert self.descriptor.interface.isJSImplemented()
- if self.name != 'Constructor':
- raise TypeError("Named constructors are not supported for JS implemented WebIDL. See bug 851287.")
- if len(self.signature[1]) != 0:
- # The first two arguments to the constructor implementation are not
- # arguments to the WebIDL constructor, so don't pass them to __Init()
- assert args[0].argType == 'const GlobalObject&'
- assert args[1].argType == 'JSContext*'
- constructorArgs = [arg.name for arg in args[2:]]
- constructorArgs.append("js::GetObjectCompartment(scopeObj)")
- initCall = fill(
- """
- // Wrap the object before calling __Init so that __DOM_IMPL__ is available.
- JS::Rooted<JSObject*> scopeObj(cx, globalHolder->GetGlobalJSObject());
- MOZ_ASSERT(js::IsObjectInContextCompartment(scopeObj, cx));
- JS::Rooted<JS::Value> wrappedVal(cx);
- if (!GetOrCreateDOMReflector(cx, impl, &wrappedVal)) {
- //XXX Assertion disabled for now, see bug 991271.
- MOZ_ASSERT(true || JS_IsExceptionPending(cx));
- aRv.Throw(NS_ERROR_UNEXPECTED);
- return nullptr;
- }
- // Initialize the object with the constructor arguments.
- impl->mImpl->__Init(${args});
- if (aRv.Failed()) {
- return nullptr;
- }
- """,
- args=", ".join(constructorArgs))
- else:
- initCall = ""
- return genConstructorBody(self.descriptor, initCall)
- def genConstructorBody(descriptor, initCall=""):
- return fill(
- """
- JS::Rooted<JSObject*> jsImplObj(cx);
- nsCOMPtr<nsIGlobalObject> globalHolder =
- ConstructJSImplementation("${contractId}", global, &jsImplObj, aRv);
- if (aRv.Failed()) {
- return nullptr;
- }
- // Build the C++ implementation.
- RefPtr<${implClass}> impl = new ${implClass}(jsImplObj, globalHolder);
- $*{initCall}
- return impl.forget();
- """,
- contractId=descriptor.interface.getJSImplementation(),
- implClass=descriptor.name,
- initCall=initCall)
- # We're always fallible
- def callbackGetterName(attr, descriptor):
- return "Get" + MakeNativeName(
- descriptor.binaryNameFor(attr.identifier.name))
- def callbackSetterName(attr, descriptor):
- return "Set" + MakeNativeName(
- descriptor.binaryNameFor(attr.identifier.name))
- class CGJSImplClearCachedValueMethod(CGAbstractBindingMethod):
- def __init__(self, descriptor, attr):
- if attr.getExtendedAttribute("StoreInSlot"):
- raise TypeError("[StoreInSlot] is not supported for JS-implemented WebIDL. See bug 1056325.")
- CGAbstractBindingMethod.__init__(self, descriptor,
- MakeJSImplClearCachedValueNativeName(attr),
- JSNativeArguments())
- self.attr = attr
- def generate_code(self):
- return CGGeneric(fill(
- """
- ${bindingNamespace}::${fnName}(self);
- args.rval().setUndefined();
- return true;
- """,
- bindingNamespace=toBindingNamespace(self.descriptor.name),
- fnName=MakeClearCachedValueNativeName(self.attr)))
- class CGJSImplGetter(CGJSImplMember):
- """
- Class for generating code for the getters of attributes for a JS-implemented
- WebIDL interface.
- """
- def __init__(self, descriptor, attr):
- CGJSImplMember.__init__(self, descriptor, attr,
- CGSpecializedGetter.makeNativeName(descriptor,
- attr),
- (attr.type, []),
- descriptor.getExtendedAttributes(attr,
- getter=True),
- passJSBitsAsNeeded=False)
- def getImpl(self):
- callbackArgs = [arg.name for arg in self.getArgs(self.member.type, [])]
- return 'return mImpl->%s(%s);\n' % (
- callbackGetterName(self.member, self.descriptorProvider),
- ", ".join(callbackArgs))
- class CGJSImplSetter(CGJSImplMember):
- """
- Class for generating code for the setters of attributes for a JS-implemented
- WebIDL interface.
- """
- def __init__(self, descriptor, attr):
- CGJSImplMember.__init__(self, descriptor, attr,
- CGSpecializedSetter.makeNativeName(descriptor,
- attr),
- (BuiltinTypes[IDLBuiltinType.Types.void],
- [FakeArgument(attr.type, attr)]),
- descriptor.getExtendedAttributes(attr,
- setter=True),
- passJSBitsAsNeeded=False)
- def getImpl(self):
- callbackArgs = [arg.name for arg in self.getArgs(BuiltinTypes[IDLBuiltinType.Types.void],
- [FakeArgument(self.member.type, self.member)])]
- return 'mImpl->%s(%s);\n' % (
- callbackSetterName(self.member, self.descriptorProvider),
- ", ".join(callbackArgs))
- class CGJSImplClass(CGBindingImplClass):
- def __init__(self, descriptor):
- CGBindingImplClass.__init__(self, descriptor, CGJSImplMethod, CGJSImplGetter, CGJSImplSetter, skipStaticMethods=True)
- if descriptor.interface.parent:
- parentClass = descriptor.getDescriptor(
- descriptor.interface.parent.identifier.name).jsImplParent
- baseClasses = [ClassBase(parentClass)]
- isupportsDecl = "NS_DECL_ISUPPORTS_INHERITED\n"
- ccDecl = ("NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(%s, %s)\n" %
- (descriptor.name, parentClass))
- constructorBody = dedent("""
- // Make sure we're an nsWrapperCache already
- MOZ_ASSERT(static_cast<nsWrapperCache*>(this));
- // And that our ancestor has not called SetIsNotDOMBinding()
- MOZ_ASSERT(IsDOMBinding());
- """)
- extradefinitions = fill(
- """
- NS_IMPL_CYCLE_COLLECTION_INHERITED(${ifaceName}, ${parentClass}, mImpl, mParent)
- NS_IMPL_ADDREF_INHERITED(${ifaceName}, ${parentClass})
- NS_IMPL_RELEASE_INHERITED(${ifaceName}, ${parentClass})
- NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(${ifaceName})
- NS_INTERFACE_MAP_END_INHERITING(${parentClass})
- """,
- ifaceName=self.descriptor.name,
- parentClass=parentClass)
- else:
- baseClasses = [ClassBase("nsSupportsWeakReference"),
- ClassBase("nsWrapperCache")]
- isupportsDecl = "NS_DECL_CYCLE_COLLECTING_ISUPPORTS\n"
- ccDecl = ("NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(%s)\n" %
- descriptor.name)
- extradefinitions = fill(
- """
- NS_IMPL_CYCLE_COLLECTION_CLASS(${ifaceName})
- NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(${ifaceName})
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mImpl)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
- NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
- tmp->ClearWeakReferences();
- NS_IMPL_CYCLE_COLLECTION_UNLINK_END
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(${ifaceName})
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImpl)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
- NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(${ifaceName})
- NS_IMPL_CYCLE_COLLECTING_ADDREF(${ifaceName})
- NS_IMPL_CYCLE_COLLECTING_RELEASE(${ifaceName})
- NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${ifaceName})
- NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
- NS_INTERFACE_MAP_ENTRY(nsISupports)
- NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
- NS_INTERFACE_MAP_END
- """,
- ifaceName=self.descriptor.name)
- extradeclarations = fill(
- """
- public:
- $*{isupportsDecl}
- $*{ccDecl}
- private:
- RefPtr<${jsImplName}> mImpl;
- nsCOMPtr<nsIGlobalObject> mParent;
- """,
- isupportsDecl=isupportsDecl,
- ccDecl=ccDecl,
- jsImplName=jsImplName(descriptor.name))
- if descriptor.interface.hasChildInterfaces():
- decorators = ""
- # We need a protected virtual destructor our subclasses can use
- destructor = ClassDestructor(virtual=True, visibility="protected")
- else:
- decorators = "final"
- destructor = ClassDestructor(virtual=False, visibility="private")
- baseConstructors = [
- ("mImpl(new %s(nullptr, aJSImplObject, /* aIncumbentGlobal = */ nullptr))" %
- jsImplName(descriptor.name)),
- "mParent(aParent)"]
- parentInterface = descriptor.interface.parent
- while parentInterface:
- if parentInterface.isJSImplemented():
- baseConstructors.insert(
- 0, "%s(aJSImplObject, aParent)" % parentClass)
- break
- parentInterface = parentInterface.parent
- if not parentInterface and descriptor.interface.parent:
- # We only have C++ ancestors, so only pass along the window
- baseConstructors.insert(0,
- "%s(aParent)" % parentClass)
- constructor = ClassConstructor(
- [Argument("JS::Handle<JSObject*>", "aJSImplObject"),
- Argument("nsIGlobalObject*", "aParent")],
- visibility="public",
- baseConstructors=baseConstructors)
- self.methodDecls.append(
- ClassMethod("_Create",
- "bool",
- JSNativeArguments(),
- static=True,
- body=self.getCreateFromExistingBody()))
- CGClass.__init__(self, descriptor.name,
- bases=baseClasses,
- constructors=[constructor],
- destructor=destructor,
- methods=self.methodDecls,
- decorators=decorators,
- extradeclarations=extradeclarations,
- extradefinitions=extradefinitions)
- def getWrapObjectBody(self):
- return fill(
- """
- JS::Rooted<JSObject*> obj(aCx, ${name}Binding::Wrap(aCx, this, aGivenProto));
- if (!obj) {
- return nullptr;
- }
- // Now define it on our chrome object
- JSAutoCompartment ac(aCx, mImpl->Callback());
- if (!JS_WrapObject(aCx, &obj)) {
- return nullptr;
- }
- if (!JS_DefineProperty(aCx, mImpl->Callback(), "__DOM_IMPL__", obj, 0)) {
- return nullptr;
- }
- return obj;
- """,
- name=self.descriptor.name)
- def getGetParentObjectReturnType(self):
- return "nsISupports*"
- def getGetParentObjectBody(self):
- return "return mParent;\n"
- def getGetDocGroupBody(self):
- return dedent(
- """
- nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mParent);
- if (!window) {
- return nullptr;
- }
- return window->GetDocGroup();
- """)
- def getCreateFromExistingBody(self):
- # XXXbz we could try to get parts of this (e.g. the argument
- # conversions) auto-generated by somehow creating an IDLMethod and
- # adding it to our interface, but we'd still need to special-case the
- # implementation slightly to have it not try to forward to the JS
- # object...
- return fill(
- """
- JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
- if (args.length() < 2) {
- return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${ifaceName}._create");
- }
- if (!args[0].isObject()) {
- return ThrowErrorMessage(cx, MSG_NOT_OBJECT, "Argument 1 of ${ifaceName}._create");
- }
- if (!args[1].isObject()) {
- return ThrowErrorMessage(cx, MSG_NOT_OBJECT, "Argument 2 of ${ifaceName}._create");
- }
- // GlobalObject will go through wrappers as needed for us, and
- // is simpler than the right UnwrapArg incantation.
- GlobalObject global(cx, &args[0].toObject());
- if (global.Failed()) {
- return false;
- }
- nsCOMPtr<nsIGlobalObject> globalHolder = do_QueryInterface(global.GetAsSupports());
- MOZ_ASSERT(globalHolder);
- JS::Rooted<JSObject*> arg(cx, &args[1].toObject());
- RefPtr<${implName}> impl = new ${implName}(arg, globalHolder);
- MOZ_ASSERT(js::IsObjectInContextCompartment(arg, cx));
- return GetOrCreateDOMReflector(cx, impl, args.rval());
- """,
- ifaceName=self.descriptor.interface.identifier.name,
- implName=self.descriptor.name)
- def isJSImplementedDescriptor(descriptorProvider):
- return (isinstance(descriptorProvider, Descriptor) and
- descriptorProvider.interface.isJSImplemented())
- class CGCallback(CGClass):
- def __init__(self, idlObject, descriptorProvider, baseName, methods,
- getters=[], setters=[]):
- self.baseName = baseName
- self._deps = idlObject.getDeps()
- self.idlObject = idlObject
- self.name = idlObject.identifier.name
- if isJSImplementedDescriptor(descriptorProvider):
- self.name = jsImplName(self.name)
- # For our public methods that needThisHandling we want most of the
- # same args and the same return type as what CallbackMember
- # generates. So we want to take advantage of all its
- # CGNativeMember infrastructure, but that infrastructure can't deal
- # with templates and most especially template arguments. So just
- # cheat and have CallbackMember compute all those things for us.
- realMethods = []
- for method in methods:
- if not isinstance(method, CallbackMember) or not method.needThisHandling:
- realMethods.append(method)
- else:
- realMethods.extend(self.getMethodImpls(method))
- realMethods.append(
- ClassMethod("operator==", "bool",
- [Argument("const %s&" % self.name, "aOther")],
- inline=True, bodyInHeader=True,
- const=True,
- body=("return %s::operator==(aOther);\n" % baseName)))
- CGClass.__init__(self, self.name,
- bases=[ClassBase(baseName)],
- constructors=self.getConstructors(),
- methods=realMethods+getters+setters)
- def getConstructors(self):
- if (not self.idlObject.isInterface() and
- not self.idlObject._treatNonObjectAsNull):
- body = "MOZ_ASSERT(JS::IsCallable(mCallback));\n"
- else:
- # Not much we can assert about it, other than not being null, and
- # CallbackObject does that already.
- body = ""
- return [
- ClassConstructor(
- [Argument("JSContext*", "aCx"),
- Argument("JS::Handle<JSObject*>", "aCallback"),
- Argument("nsIGlobalObject*", "aIncumbentGlobal")],
- bodyInHeader=True,
- visibility="public",
- explicit=True,
- baseConstructors=[
- "%s(aCx, aCallback, aIncumbentGlobal)" % self.baseName,
- ],
- body=body),
- ClassConstructor(
- [Argument("JSContext*", "aCx"),
- Argument("JS::Handle<JSObject*>", "aCallback"),
- Argument("nsIGlobalObject*", "aIncumbentGlobal"),
- Argument("const FastCallbackConstructor&", "")],
- bodyInHeader=True,
- visibility="public",
- explicit=True,
- baseConstructors=[
- "%s(aCx, aCallback, aIncumbentGlobal, FastCallbackConstructor())" % self.baseName,
- ],
- body=body),
- ClassConstructor(
- [Argument("JS::Handle<JSObject*>", "aCallback"),
- Argument("JS::Handle<JSObject*>", "aAsyncStack"),
- Argument("nsIGlobalObject*", "aIncumbentGlobal")],
- bodyInHeader=True,
- visibility="public",
- explicit=True,
- baseConstructors=[
- "%s(aCallback, aAsyncStack, aIncumbentGlobal)" % self.baseName,
- ],
- body=body)]
- def getMethodImpls(self, method):
- assert method.needThisHandling
- args = list(method.args)
- # Strip out the JSContext*/JSObject* args
- # that got added.
- assert args[0].name == "cx" and args[0].argType == "JSContext*"
- assert args[1].name == "aThisVal" and args[1].argType == "JS::Handle<JS::Value>"
- args = args[2:]
- # Now remember which index the ErrorResult argument is at;
- # we'll need this below.
- assert args[-1].name == "aRv" and args[-1].argType == "ErrorResult&"
- rvIndex = len(args) - 1
- assert rvIndex >= 0
- # Record the names of all the arguments, so we can use them when we call
- # the private method.
- argnames = [arg.name for arg in args]
- argnamesWithThis = ["s.GetContext()", "thisValJS"] + argnames
- argnamesWithoutThis = ["s.GetContext()", "JS::UndefinedHandleValue"] + argnames
- # Now that we've recorded the argnames for our call to our private
- # method, insert our optional argument for the execution reason.
- args.append(Argument("const char*", "aExecutionReason",
- "nullptr"))
- # Make copies of the arg list for the two "without rv" overloads. Note
- # that those don't need aExceptionHandling or aCompartment arguments
- # because those would make not sense anyway: the only sane thing to do
- # with exceptions in the "without rv" cases is to report them.
- argsWithoutRv = list(args)
- argsWithoutRv.pop(rvIndex)
- argsWithoutThisAndRv = list(argsWithoutRv)
- # Add the potional argument for deciding whether the CallSetup should
- # re-throw exceptions on aRv.
- args.append(Argument("ExceptionHandling", "aExceptionHandling",
- "eReportExceptions"))
- # And the argument for communicating when exceptions should really be
- # rethrown. In particular, even when aExceptionHandling is
- # eRethrowExceptions they won't get rethrown if aCompartment is provided
- # and its principal doesn't subsume either the callback or the
- # exception.
- args.append(Argument("JSCompartment*", "aCompartment", "nullptr"))
- # And now insert our template argument.
- argsWithoutThis = list(args)
- args.insert(0, Argument("const T&", "thisVal"))
- argsWithoutRv.insert(0, Argument("const T&", "thisVal"))
- argnamesWithoutThisAndRv = [arg.name for arg in argsWithoutThisAndRv]
- argnamesWithoutThisAndRv.insert(rvIndex, "rv");
- # If we just leave things like that, and have no actual arguments in the
- # IDL, we will end up trying to call the templated "without rv" overload
- # with "rv" as the thisVal. That's no good. So explicitly append the
- # aExceptionHandling and aCompartment values we need to end up matching
- # the signature of our non-templated "with rv" overload.
- argnamesWithoutThisAndRv.extend(["eReportExceptions", "nullptr"])
- argnamesWithoutRv = [arg.name for arg in argsWithoutRv]
- # Note that we need to insert at rvIndex + 1, since we inserted a
- # thisVal arg at the start.
- argnamesWithoutRv.insert(rvIndex + 1, "rv")
- errorReturn = method.getDefaultRetval()
- setupCall = fill(
- """
- if (!aExecutionReason) {
- aExecutionReason = "${executionReason}";
- }
- CallSetup s(this, aRv, aExecutionReason, aExceptionHandling, aCompartment);
- if (!s.GetContext()) {
- MOZ_ASSERT(aRv.Failed());
- return${errorReturn};
- }
- """,
- errorReturn=errorReturn,
- executionReason=method.getPrettyName())
- bodyWithThis = fill(
- """
- $*{setupCall}
- JS::Rooted<JS::Value> thisValJS(s.GetContext());
- if (!ToJSValue(s.GetContext(), thisVal, &thisValJS)) {
- aRv.Throw(NS_ERROR_FAILURE);
- return${errorReturn};
- }
- return ${methodName}(${callArgs});
- """,
- setupCall=setupCall,
- errorReturn=errorReturn,
- methodName=method.name,
- callArgs=", ".join(argnamesWithThis))
- bodyWithoutThis = fill(
- """
- $*{setupCall}
- return ${methodName}(${callArgs});
- """,
- setupCall=setupCall,
- errorReturn=errorReturn,
- methodName=method.name,
- callArgs=", ".join(argnamesWithoutThis))
- bodyWithThisWithoutRv = fill(
- """
- IgnoredErrorResult rv;
- return ${methodName}(${callArgs});
- """,
- methodName=method.name,
- callArgs=", ".join(argnamesWithoutRv))
- bodyWithoutThisAndRv = fill(
- """
- IgnoredErrorResult rv;
- return ${methodName}(${callArgs});
- """,
- methodName=method.name,
- callArgs=", ".join(argnamesWithoutThisAndRv))
- return [ClassMethod(method.name, method.returnType, args,
- bodyInHeader=True,
- templateArgs=["typename T"],
- body=bodyWithThis),
- ClassMethod(method.name, method.returnType, argsWithoutThis,
- bodyInHeader=True,
- body=bodyWithoutThis),
- ClassMethod(method.name, method.returnType, argsWithoutRv,
- bodyInHeader=True,
- templateArgs=["typename T"],
- body=bodyWithThisWithoutRv),
- ClassMethod(method.name, method.returnType, argsWithoutThisAndRv,
- bodyInHeader=True,
- body=bodyWithoutThisAndRv),
- method]
- def deps(self):
- return self._deps
- class CGCallbackFunction(CGCallback):
- def __init__(self, callback, descriptorProvider):
- self.callback = callback
- CGCallback.__init__(self, callback, descriptorProvider,
- "CallbackFunction",
- methods=[CallCallback(callback, descriptorProvider)])
- def getConstructors(self):
- return CGCallback.getConstructors(self) + [
- ClassConstructor(
- [Argument("CallbackFunction*", "aOther")],
- bodyInHeader=True,
- visibility="public",
- explicit=True,
- baseConstructors=["CallbackFunction(aOther)"])]
- class CGFastCallback(CGClass):
- def __init__(self, idlObject):
- self._deps = idlObject.getDeps()
- baseName = idlObject.identifier.name
- constructor = ClassConstructor(
- [Argument("JSContext*", "aCx"),
- Argument("JS::Handle<JSObject*>", "aCallback"),
- Argument("nsIGlobalObject*", "aIncumbentGlobal")],
- bodyInHeader=True,
- visibility="public",
- explicit=True,
- baseConstructors=[
- "%s(aCx, aCallback, aIncumbentGlobal, FastCallbackConstructor())" %
- baseName,
- ],
- body="")
- traceMethod = ClassMethod("Trace", "void",
- [Argument("JSTracer*", "aTracer")],
- inline=True,
- bodyInHeader=True,
- visibility="public",
- body="%s::Trace(aTracer);\n" % baseName)
- holdMethod = ClassMethod("HoldJSObjectsIfMoreThanOneOwner", "void",
- [],
- inline=True,
- bodyInHeader=True,
- visibility="public",
- body=(
- "%s::HoldJSObjectsIfMoreThanOneOwner();\n" %
- baseName))
- CGClass.__init__(self, "Fast%s" % baseName,
- bases=[ClassBase(baseName)],
- constructors=[constructor],
- methods=[traceMethod, holdMethod])
- def deps(self):
- return self._deps
- class CGCallbackInterface(CGCallback):
- def __init__(self, descriptor, typedArraysAreStructs=False):
- iface = descriptor.interface
- attrs = [m for m in iface.members if m.isAttr() and not m.isStatic()]
- getters = [CallbackGetter(a, descriptor, typedArraysAreStructs)
- for a in attrs]
- setters = [CallbackSetter(a, descriptor, typedArraysAreStructs)
- for a in attrs if not a.readonly]
- methods = [m for m in iface.members
- if m.isMethod() and not m.isStatic() and not m.isIdentifierLess()]
- methods = [CallbackOperation(m, sig, descriptor, typedArraysAreStructs)
- for m in methods for sig in m.signatures()]
- if iface.isJSImplemented() and iface.ctor():
- sigs = descriptor.interface.ctor().signatures()
- if len(sigs) != 1:
- raise TypeError("We only handle one constructor. See bug 869268.")
- methods.append(CGJSImplInitOperation(sigs[0], descriptor))
- if any(m.isAttr() or m.isMethod() for m in iface.members) or (iface.isJSImplemented() and iface.ctor()):
- methods.append(initIdsClassMethod([descriptor.binaryNameFor(m.identifier.name)
- for m in iface.members
- if m.isAttr() or m.isMethod()] +
- (["__init"] if iface.isJSImplemented() and iface.ctor() else []),
- iface.identifier.name + "Atoms"))
- CGCallback.__init__(self, iface, descriptor, "CallbackInterface",
- methods, getters=getters, setters=setters)
- class FakeMember():
- def __init__(self, name=None):
- self.treatNullAs = "Default"
- if name is not None:
- self.identifier = FakeIdentifier(name)
- def isStatic(self):
- return False
- def isAttr(self):
- return False
- def isMethod(self):
- return False
- def getExtendedAttribute(self, name):
- # Claim to be a [NewObject] so we can avoid the "return a raw pointer"
- # comments CGNativeMember codegen would otherwise stick in.
- if name == "NewObject":
- return True
- return None
- class CallbackMember(CGNativeMember):
- # XXXbz It's OK to use CallbackKnownNotGray for wrapScope because
- # CallSetup already handled the unmark-gray bits for us. we don't have
- # anything better to use for 'obj', really...
- def __init__(self, sig, name, descriptorProvider, needThisHandling,
- rethrowContentException=False, typedArraysAreStructs=False,
- wrapScope='CallbackKnownNotGray()'):
- """
- needThisHandling is True if we need to be able to accept a specified
- thisObj, False otherwise.
- """
- assert not rethrowContentException or not needThisHandling
- self.retvalType = sig[0]
- self.originalSig = sig
- args = sig[1]
- self.argCount = len(args)
- if self.argCount > 0:
- # Check for variadic arguments
- lastArg = args[self.argCount-1]
- if lastArg.variadic:
- self.argCountStr = ("(%d - 1) + %s.Length()" %
- (self.argCount, lastArg.identifier.name))
- else:
- self.argCountStr = "%d" % self.argCount
- self.needThisHandling = needThisHandling
- # If needThisHandling, we generate ourselves as private and the caller
- # will handle generating public versions that handle the "this" stuff.
- visibility = "private" if needThisHandling else "public"
- self.rethrowContentException = rethrowContentException
- self.wrapScope = wrapScope
- # We don't care, for callback codegen, whether our original member was
- # a method or attribute or whatnot. Just always pass FakeMember()
- # here.
- CGNativeMember.__init__(self, descriptorProvider, FakeMember(),
- name, (self.retvalType, args),
- extendedAttrs={},
- passJSBitsAsNeeded=False,
- visibility=visibility,
- typedArraysAreStructs=typedArraysAreStructs)
- # We have to do all the generation of our body now, because
- # the caller relies on us throwing if we can't manage it.
- self.exceptionCode = ("aRv.Throw(NS_ERROR_UNEXPECTED);\n"
- "return%s;\n" % self.getDefaultRetval())
- self.body = self.getImpl()
- def getImpl(self):
- setupCall = self.getCallSetup()
- declRval = self.getRvalDecl()
- if self.argCount > 0:
- argvDecl = fill(
- """
- JS::AutoValueVector argv(cx);
- if (!argv.resize(${argCount})) {
- aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
- return${errorReturn};
- }
- """,
- argCount=self.argCountStr,
- errorReturn=self.getDefaultRetval())
- else:
- # Avoid weird 0-sized arrays
- argvDecl = ""
- convertArgs = self.getArgConversions()
- doCall = self.getCall()
- returnResult = self.getResultConversion()
- return setupCall + declRval + argvDecl + convertArgs + doCall + returnResult
- def getResultConversion(self):
- replacements = {
- "val": "rval",
- "holderName": "rvalHolder",
- "declName": "rvalDecl",
- # We actually want to pass in a null scope object here, because
- # wrapping things into our current compartment (that of mCallback)
- # is what we want.
- "obj": "nullptr",
- "passedToJSImpl": "false"
- }
- if isJSImplementedDescriptor(self.descriptorProvider):
- isCallbackReturnValue = "JSImpl"
- else:
- isCallbackReturnValue = "Callback"
- sourceDescription = "return value of %s" % self.getPrettyName()
- convertType = instantiateJSToNativeConversion(
- getJSToNativeConversionInfo(self.retvalType,
- self.descriptorProvider,
- exceptionCode=self.exceptionCode,
- isCallbackReturnValue=isCallbackReturnValue,
- # Allow returning a callback type that
- # allows non-callable objects.
- allowTreatNonCallableAsNull=True,
- sourceDescription=sourceDescription),
- replacements)
- assignRetval = string.Template(
- self.getRetvalInfo(self.retvalType,
- False)[2]).substitute(replacements)
- type = convertType.define()
- return type + assignRetval
- def getArgConversions(self):
- # Just reget the arglist from self.originalSig, because our superclasses
- # just have way to many members they like to clobber, so I can't find a
- # safe member name to store it in.
- argConversions = [self.getArgConversion(i, arg)
- for i, arg in enumerate(self.originalSig[1])]
- if not argConversions:
- return "\n"
- # Do them back to front, so our argc modifications will work
- # correctly, because we examine trailing arguments first.
- argConversions.reverse()
- # Wrap each one in a scope so that any locals it has don't leak out, and
- # also so that we can just "break;" for our successCode.
- argConversions = [CGWrapper(CGIndenter(CGGeneric(c)),
- pre="do {\n",
- post="} while (0);\n")
- for c in argConversions]
- if self.argCount > 0:
- argConversions.insert(0, self.getArgcDecl())
- # And slap them together.
- return CGList(argConversions, "\n").define() + "\n"
- def getArgConversion(self, i, arg):
- argval = arg.identifier.name
- if arg.variadic:
- argval = argval + "[idx]"
- jsvalIndex = "%d + idx" % i
- else:
- jsvalIndex = "%d" % i
- if arg.canHaveMissingValue():
- argval += ".Value()"
- if arg.type.isDOMString():
- # XPConnect string-to-JS conversion wants to mutate the string. So
- # let's give it a string it can mutate
- # XXXbz if we try to do a sequence of strings, this will kinda fail.
- result = "mutableStr"
- prepend = "nsString mutableStr(%s);\n" % argval
- else:
- result = argval
- prepend = ""
- try:
- conversion = prepend + wrapForType(
- arg.type, self.descriptorProvider,
- {
- 'result': result,
- 'successCode': "continue;\n" if arg.variadic else "break;\n",
- 'jsvalRef': "argv[%s]" % jsvalIndex,
- 'jsvalHandle': "argv[%s]" % jsvalIndex,
- 'obj': self.wrapScope,
- 'returnsNewObject': False,
- 'exceptionCode': self.exceptionCode,
- 'typedArraysAreStructs': self.typedArraysAreStructs
- })
- except MethodNotNewObjectError as err:
- raise TypeError("%s being passed as an argument to %s but is not "
- "wrapper cached, so can't be reliably converted to "
- "a JS object." %
- (err.typename, self.getPrettyName()))
- if arg.variadic:
- conversion = fill(
- """
- for (uint32_t idx = 0; idx < ${arg}.Length(); ++idx) {
- $*{conversion}
- }
- break;
- """,
- arg=arg.identifier.name,
- conversion=conversion)
- elif arg.canHaveMissingValue():
- conversion = fill(
- """
- if (${argName}.WasPassed()) {
- $*{conversion}
- } else if (argc == ${iPlus1}) {
- // This is our current trailing argument; reduce argc
- --argc;
- } else {
- argv[${i}].setUndefined();
- }
- """,
- argName=arg.identifier.name,
- conversion=conversion,
- iPlus1=i + 1,
- i=i)
- return conversion
- def getDefaultRetval(self):
- default = self.getRetvalInfo(self.retvalType, False)[1]
- if len(default) != 0:
- default = " " + default
- return default
- def getArgs(self, returnType, argList):
- args = CGNativeMember.getArgs(self, returnType, argList)
- if not self.needThisHandling:
- # Since we don't need this handling, we're the actual method that
- # will be called, so we need an aRethrowExceptions argument.
- if not self.rethrowContentException:
- args.append(Argument("const char*", "aExecutionReason",
- "nullptr"))
- args.append(Argument("ExceptionHandling", "aExceptionHandling",
- "eReportExceptions"))
- args.append(Argument("JSCompartment*", "aCompartment", "nullptr"))
- return args
- # We want to allow the caller to pass in a "this" value, as
- # well as a JSContext.
- return [Argument("JSContext*", "cx"),
- Argument("JS::Handle<JS::Value>", "aThisVal")] + args
- def getCallSetup(self):
- if self.needThisHandling:
- # It's been done for us already
- return ""
- callSetup = "CallSetup s(this, aRv"
- if self.rethrowContentException:
- # getArgs doesn't add the aExceptionHandling argument but does add
- # aCompartment for us.
- callSetup += ', "%s", eRethrowContentExceptions, aCompartment, /* aIsJSImplementedWebIDL = */ ' % self.getPrettyName()
- callSetup += toStringBool(isJSImplementedDescriptor(self.descriptorProvider))
- else:
- callSetup += ', "%s", aExceptionHandling, aCompartment' % self.getPrettyName()
- callSetup += ");\n"
- return fill(
- """
- $*{callSetup}
- JSContext* cx = s.GetContext();
- if (!cx) {
- MOZ_ASSERT(aRv.Failed());
- return${errorReturn};
- }
- """,
- callSetup=callSetup,
- errorReturn=self.getDefaultRetval())
- def getArgcDecl(self):
- return CGGeneric("unsigned argc = %s;\n" % self.argCountStr)
- @staticmethod
- def ensureASCIIName(idlObject):
- type = "attribute" if idlObject.isAttr() else "operation"
- if re.match("[^\x20-\x7E]", idlObject.identifier.name):
- raise SyntaxError('Callback %s name "%s" contains non-ASCII '
- "characters. We can't handle that. %s" %
- (type, idlObject.identifier.name,
- idlObject.location))
- if re.match('"', idlObject.identifier.name):
- raise SyntaxError("Callback %s name '%s' contains "
- "double-quote character. We can't handle "
- "that. %s" %
- (type, idlObject.identifier.name,
- idlObject.location))
- class CallbackMethod(CallbackMember):
- def __init__(self, sig, name, descriptorProvider, needThisHandling,
- rethrowContentException=False, typedArraysAreStructs=False):
- CallbackMember.__init__(self, sig, name, descriptorProvider,
- needThisHandling, rethrowContentException,
- typedArraysAreStructs=typedArraysAreStructs)
- def getRvalDecl(self):
- return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n"
- def getCall(self):
- if self.argCount > 0:
- args = "JS::HandleValueArray::subarray(argv, 0, argc)"
- else:
- args = "JS::HandleValueArray::empty()"
- return fill(
- """
- $*{declCallable}
- $*{declThis}
- if (${callGuard}!JS::Call(cx, ${thisVal}, callable,
- ${args}, &rval)) {
- aRv.NoteJSContextException(cx);
- return${errorReturn};
- }
- """,
- declCallable=self.getCallableDecl(),
- declThis=self.getThisDecl(),
- callGuard=self.getCallGuard(),
- thisVal=self.getThisVal(),
- args=args,
- errorReturn=self.getDefaultRetval())
- class CallCallback(CallbackMethod):
- def __init__(self, callback, descriptorProvider):
- self.callback = callback
- CallbackMethod.__init__(self, callback.signatures()[0], "Call",
- descriptorProvider, needThisHandling=True)
- def getThisDecl(self):
- return ""
- def getThisVal(self):
- return "aThisVal"
- def getCallableDecl(self):
- return "JS::Rooted<JS::Value> callable(cx, JS::ObjectValue(*mCallback));\n"
- def getPrettyName(self):
- return self.callback.identifier.name
- def getCallGuard(self):
- if self.callback._treatNonObjectAsNull:
- return "JS::IsCallable(mCallback) && "
- return ""
- class CallbackOperationBase(CallbackMethod):
- """
- Common class for implementing various callback operations.
- """
- def __init__(self, signature, jsName, nativeName, descriptor,
- singleOperation, rethrowContentException=False,
- typedArraysAreStructs=False):
- self.singleOperation = singleOperation
- self.methodName = descriptor.binaryNameFor(jsName)
- CallbackMethod.__init__(self, signature, nativeName, descriptor,
- singleOperation, rethrowContentException,
- typedArraysAreStructs=typedArraysAreStructs)
- def getThisDecl(self):
- if not self.singleOperation:
- return "JS::Rooted<JS::Value> thisValue(cx, JS::ObjectValue(*mCallback));\n"
- # This relies on getCallableDecl declaring a boolean
- # isCallable in the case when we're a single-operation
- # interface.
- return dedent("""
- JS::Rooted<JS::Value> thisValue(cx, isCallable ? aThisVal.get()
- : JS::ObjectValue(*mCallback));
- """)
- def getThisVal(self):
- return "thisValue"
- def getCallableDecl(self):
- getCallableFromProp = fill(
- """
- ${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx);
- if ((!*reinterpret_cast<jsid**>(atomsCache) && !InitIds(cx, atomsCache)) ||
- !GetCallableProperty(cx, atomsCache->${methodAtomName}, &callable)) {
- aRv.Throw(NS_ERROR_UNEXPECTED);
- return${errorReturn};
- }
- """,
- methodAtomName=CGDictionary.makeIdName(self.methodName),
- atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms",
- errorReturn=self.getDefaultRetval())
- if not self.singleOperation:
- return 'JS::Rooted<JS::Value> callable(cx);\n' + getCallableFromProp
- return fill(
- """
- bool isCallable = JS::IsCallable(mCallback);
- JS::Rooted<JS::Value> callable(cx);
- if (isCallable) {
- callable = JS::ObjectValue(*mCallback);
- } else {
- $*{getCallableFromProp}
- }
- """,
- getCallableFromProp=getCallableFromProp)
- def getCallGuard(self):
- return ""
- class CallbackOperation(CallbackOperationBase):
- """
- Codegen actual WebIDL operations on callback interfaces.
- """
- def __init__(self, method, signature, descriptor, typedArraysAreStructs):
- self.ensureASCIIName(method)
- self.method = method
- jsName = method.identifier.name
- CallbackOperationBase.__init__(self, signature,
- jsName,
- MakeNativeName(descriptor.binaryNameFor(jsName)),
- descriptor, descriptor.interface.isSingleOperationInterface(),
- rethrowContentException=descriptor.interface.isJSImplemented(),
- typedArraysAreStructs=typedArraysAreStructs)
- def getPrettyName(self):
- return "%s.%s" % (self.descriptorProvider.interface.identifier.name,
- self.method.identifier.name)
- class CallbackAccessor(CallbackMember):
- """
- Shared superclass for CallbackGetter and CallbackSetter.
- """
- def __init__(self, attr, sig, name, descriptor, typedArraysAreStructs):
- self.ensureASCIIName(attr)
- self.attrName = attr.identifier.name
- CallbackMember.__init__(self, sig, name, descriptor,
- needThisHandling=False,
- rethrowContentException=descriptor.interface.isJSImplemented(),
- typedArraysAreStructs=typedArraysAreStructs)
- def getPrettyName(self):
- return "%s.%s" % (self.descriptorProvider.interface.identifier.name,
- self.attrName)
- class CallbackGetter(CallbackAccessor):
- def __init__(self, attr, descriptor, typedArraysAreStructs):
- CallbackAccessor.__init__(self, attr,
- (attr.type, []),
- callbackGetterName(attr, descriptor),
- descriptor,
- typedArraysAreStructs)
- def getRvalDecl(self):
- return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n"
- def getCall(self):
- return fill(
- """
- JS::Rooted<JSObject *> callback(cx, mCallback);
- ${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx);
- if ((!*reinterpret_cast<jsid**>(atomsCache) && !InitIds(cx, atomsCache)) ||
- !JS_GetPropertyById(cx, callback, atomsCache->${attrAtomName}, &rval)) {
- aRv.Throw(NS_ERROR_UNEXPECTED);
- return${errorReturn};
- }
- """,
- atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms",
- attrAtomName=CGDictionary.makeIdName(self.descriptorProvider.binaryNameFor(self.attrName)),
- errorReturn=self.getDefaultRetval())
- class CallbackSetter(CallbackAccessor):
- def __init__(self, attr, descriptor, typedArraysAreStructs):
- CallbackAccessor.__init__(self, attr,
- (BuiltinTypes[IDLBuiltinType.Types.void],
- [FakeArgument(attr.type, attr)]),
- callbackSetterName(attr, descriptor),
- descriptor, typedArraysAreStructs)
- def getRvalDecl(self):
- # We don't need an rval
- return ""
- def getCall(self):
- return fill(
- """
- MOZ_ASSERT(argv.length() == 1);
- ${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx);
- if ((!*reinterpret_cast<jsid**>(atomsCache) && !InitIds(cx, atomsCache)) ||
- !JS_SetPropertyById(cx, CallbackKnownNotGray(), atomsCache->${attrAtomName}, argv[0])) {
- aRv.Throw(NS_ERROR_UNEXPECTED);
- return${errorReturn};
- }
- """,
- atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms",
- attrAtomName=CGDictionary.makeIdName(self.descriptorProvider.binaryNameFor(self.attrName)),
- errorReturn=self.getDefaultRetval())
- def getArgcDecl(self):
- return None
- class CGJSImplInitOperation(CallbackOperationBase):
- """
- Codegen the __Init() method used to pass along constructor arguments for JS-implemented WebIDL.
- """
- def __init__(self, sig, descriptor):
- assert sig in descriptor.interface.ctor().signatures()
- CallbackOperationBase.__init__(self, (BuiltinTypes[IDLBuiltinType.Types.void], sig[1]),
- "__init", "__Init", descriptor,
- singleOperation=False,
- rethrowContentException=True,
- typedArraysAreStructs=True)
- def getPrettyName(self):
- return "__init"
- def getMaplikeOrSetlikeErrorReturn(helperImpl):
- """
- Generate return values based on whether a maplike or setlike generated
- method is an interface method (which returns bool) or a helper function
- (which uses ErrorResult).
- """
- if helperImpl:
- return dedent(
- """
- aRv.Throw(NS_ERROR_UNEXPECTED);
- return%s;
- """ % helperImpl.getDefaultRetval())
- return "return false;\n"
- def getMaplikeOrSetlikeBackingObject(descriptor, maplikeOrSetlike, helperImpl=None):
- """
- Generate code to get/create a JS backing object for a maplike/setlike
- declaration from the declaration slot.
- """
- func_prefix = maplikeOrSetlike.maplikeOrSetlikeOrIterableType.title()
- ret = fill(
- """
- JS::Rooted<JSObject*> backingObj(cx);
- bool created = false;
- if (!Get${func_prefix}BackingObject(cx, obj, ${slot}, &backingObj, &created)) {
- $*{errorReturn}
- }
- if (created) {
- PreserveWrapper<${selfType}>(self);
- }
- """,
- slot=memberReservedSlot(maplikeOrSetlike, descriptor),
- func_prefix=func_prefix,
- errorReturn=getMaplikeOrSetlikeErrorReturn(helperImpl),
- selfType=descriptor.nativeType)
- return ret
- def getMaplikeOrSetlikeSizeGetterBody(descriptor, attr):
- """
- Creates the body for the size getter method of maplike/setlike interfaces.
- """
- # We should only have one declaration attribute currently
- assert attr.identifier.name == "size"
- assert attr.isMaplikeOrSetlikeAttr()
- return fill(
- """
- $*{getBackingObj}
- uint32_t result = JS::${funcPrefix}Size(cx, backingObj);
- MOZ_ASSERT(!JS_IsExceptionPending(cx));
- args.rval().setNumber(result);
- return true;
- """,
- getBackingObj=getMaplikeOrSetlikeBackingObject(descriptor,
- attr.maplikeOrSetlike),
- funcPrefix=attr.maplikeOrSetlike.prefix)
- class CGMaplikeOrSetlikeMethodGenerator(CGThing):
- """
- Creates methods for maplike/setlike interfaces. It is expected that all
- methods will be have a maplike/setlike object attached. Unwrapping/wrapping
- will be taken care of by the usual method generation machinery in
- CGMethodCall/CGPerSignatureCall. Functionality is filled in here instead of
- using CGCallGenerator.
- """
- def __init__(self, descriptor, maplikeOrSetlike, methodName,
- helperImpl=None):
- CGThing.__init__(self)
- # True if this will be the body of a C++ helper function.
- self.helperImpl = helperImpl
- self.descriptor = descriptor
- self.maplikeOrSetlike = maplikeOrSetlike
- self.cgRoot = CGList([])
- impl_method_name = methodName
- if impl_method_name[0] == "_":
- # double underscore means this is a js-implemented chrome only rw
- # function. Truncate the double underscore so calling the right
- # underlying JSAPI function still works.
- impl_method_name = impl_method_name[2:]
- self.cgRoot.append(CGGeneric(
- getMaplikeOrSetlikeBackingObject(self.descriptor,
- self.maplikeOrSetlike,
- self.helperImpl)))
- self.returnStmt = getMaplikeOrSetlikeErrorReturn(self.helperImpl)
- # Generates required code for the method. Method descriptions included
- # in definitions below. Throw if we don't have a method to fill in what
- # we're looking for.
- try:
- methodGenerator = getattr(self, impl_method_name)
- except AttributeError:
- raise TypeError("Missing %s method definition '%s'" %
- (self.maplikeOrSetlike.maplikeOrSetlikeType,
- methodName))
- # Method generator returns tuple, containing:
- #
- # - a list of CGThings representing setup code for preparing to call
- # the JS API function
- # - a list of arguments needed for the JS API function we're calling
- # - list of code CGThings needed for return value conversion.
- (setupCode, arguments, setResult) = methodGenerator()
- # Create the actual method call, and then wrap it with the code to
- # return the value if needed.
- funcName = (self.maplikeOrSetlike.prefix +
- MakeNativeName(impl_method_name))
- # Append the list of setup code CGThings
- self.cgRoot.append(CGList(setupCode))
- # Create the JS API call
- self.cgRoot.append(CGWrapper(
- CGGeneric(fill(
- """
- if (!JS::${funcName}(${args})) {
- $*{errorReturn}
- }
- """,
- funcName=funcName,
- args=", ".join(["cx", "backingObj"] + arguments),
- errorReturn=self.returnStmt))))
- # Append result conversion
- self.cgRoot.append(CGList(setResult))
- def mergeTuples(self, a, b):
- """
- Expecting to take 2 tuples were all elements are lists, append the lists in
- the second tuple to the lists in the first.
- """
- return tuple([x + y for x, y in zip(a, b)])
- def appendArgConversion(self, name):
- """
- Generate code to convert arguments to JS::Values, so they can be
- passed into JSAPI functions.
- """
- return CGGeneric(fill(
- """
- JS::Rooted<JS::Value> ${name}Val(cx);
- if (!ToJSValue(cx, ${name}, &${name}Val)) {
- $*{errorReturn}
- }
- """,
- name=name,
- errorReturn=self.returnStmt))
- def appendKeyArgConversion(self):
- """
- Generates the key argument for methods. Helper functions will use
- an AutoValueVector, while interface methods have seperate JS::Values.
- """
- if self.helperImpl:
- return ([], ["argv[0]"], [])
- return ([self.appendArgConversion("arg0")], ["arg0Val"], [])
- def appendKeyAndValueArgConversion(self):
- """
- Generates arguments for methods that require a key and value. Helper
- functions will use an AutoValueVector, while interface methods have
- seperate JS::Values.
- """
- r = self.appendKeyArgConversion()
- if self.helperImpl:
- return self.mergeTuples(r, ([], ["argv[1]"], []))
- return self.mergeTuples(r, ([self.appendArgConversion("arg1")],
- ["arg1Val"],
- []))
- def appendIteratorResult(self):
- """
- Generate code to output JSObject* return values, needed for functions that
- return iterators. Iterators cannot currently be wrapped via Xrays. If
- something that would return an iterator is called via Xray, fail early.
- """
- # TODO: Bug 1173651 - Remove check once bug 1023984 is fixed.
- code = CGGeneric(dedent(
- """
- // TODO (Bug 1173651): Xrays currently cannot wrap iterators. Change
- // after bug 1023984 is fixed.
- if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
- JS_ReportErrorASCII(cx, "Xray wrapping of iterators not supported.");
- return false;
- }
- JS::Rooted<JSObject*> result(cx);
- JS::Rooted<JS::Value> v(cx);
- """))
- arguments = "&v"
- setResult = CGGeneric(dedent(
- """
- result = &v.toObject();
- """))
- return ([code], [arguments], [setResult])
- def appendSelfResult(self):
- """
- Generate code to return the interface object itself.
- """
- code = CGGeneric(dedent(
- """
- JS::Rooted<JSObject*> result(cx);
- """))
- setResult = CGGeneric(dedent(
- """
- result = obj;
- """))
- return ([code], [], [setResult])
- def appendBoolResult(self):
- if self.helperImpl:
- return ([CGGeneric()], ["&aRetVal"], [])
- return ([CGGeneric("bool result;\n")], ["&result"], [])
- def forEach(self):
- """
- void forEach(callback c, any thisval);
- ForEach takes a callback, and a possible value to use as 'this'. The
- callback needs to take value, key, and the interface object
- implementing maplike/setlike. In order to make sure that the third arg
- is our interface object instead of the map/set backing object, we
- create a js function with the callback and original object in its
- storage slots, then use a helper function in BindingUtils to make sure
- the callback is called correctly.
- """
- assert(not self.helperImpl)
- code = [CGGeneric(dedent(
- """
- // Create a wrapper function.
- JSFunction* func = js::NewFunctionWithReserved(cx, ForEachHandler, 3, 0, nullptr);
- if (!func) {
- return false;
- }
- JS::Rooted<JSObject*> funcObj(cx, JS_GetFunctionObject(func));
- JS::Rooted<JS::Value> funcVal(cx, JS::ObjectValue(*funcObj));
- js::SetFunctionNativeReserved(funcObj, FOREACH_CALLBACK_SLOT,
- JS::ObjectValue(*arg0));
- js::SetFunctionNativeReserved(funcObj, FOREACH_MAPLIKEORSETLIKEOBJ_SLOT,
- JS::ObjectValue(*obj));
- """))]
- arguments = ["funcVal", "arg1"]
- return (code, arguments, [])
- def set(self):
- """
- object set(key, value);
- Maplike only function, takes key and sets value to it, returns
- interface object unless being called from a C++ helper.
- """
- assert self.maplikeOrSetlike.isMaplike()
- r = self.appendKeyAndValueArgConversion()
- if self.helperImpl:
- return r
- return self.mergeTuples(r, self.appendSelfResult())
- def add(self):
- """
- object add(value);
- Setlike only function, adds value to set, returns interface object
- unless being called from a C++ helper
- """
- assert self.maplikeOrSetlike.isSetlike()
- r = self.appendKeyArgConversion()
- if self.helperImpl:
- return r
- return self.mergeTuples(r, self.appendSelfResult())
- def get(self):
- """
- type? get(key);
- Retrieves a value from a backing object based on the key. Returns value
- if key is in backing object, undefined otherwise.
- """
- assert self.maplikeOrSetlike.isMaplike()
- r = self.appendKeyArgConversion()
- code = [CGGeneric(dedent(
- """
- JS::Rooted<JS::Value> result(cx);
- """))]
- arguments = ["&result"]
- return self.mergeTuples(r, (code, arguments, []))
- def has(self):
- """
- bool has(key);
- Check if an entry exists in the backing object. Returns true if value
- exists in backing object, false otherwise.
- """
- return self.mergeTuples(self.appendKeyArgConversion(),
- self.appendBoolResult())
- def keys(self):
- """
- object keys();
- Returns new object iterator with all keys from backing object.
- """
- return self.appendIteratorResult()
- def values(self):
- """
- object values();
- Returns new object iterator with all values from backing object.
- """
- return self.appendIteratorResult()
- def entries(self):
- """
- object entries();
- Returns new object iterator with all keys and values from backing
- object. Keys will be null for set.
- """
- return self.appendIteratorResult()
- def clear(self):
- """
- void clear();
- Removes all entries from map/set.
- """
- return ([], [], [])
- def delete(self):
- """
- bool delete(key);
- Deletes an entry from the backing object. Returns true if value existed
- in backing object, false otherwise.
- """
- return self.mergeTuples(self.appendKeyArgConversion(),
- self.appendBoolResult())
- def define(self):
- return self.cgRoot.define()
- class CGMaplikeOrSetlikeHelperFunctionGenerator(CallbackMember):
- """
- Generates code to allow C++ to perform operations on backing objects. Gets
- a context from the binding wrapper, turns arguments into JS::Values (via
- CallbackMember/CGNativeMember argument conversion), then uses
- CGMaplikeOrSetlikeMethodGenerator to generate the body.
- """
- class HelperFunction(CGAbstractMethod):
- """
- Generates context retrieval code and rooted JSObject for interface for
- CGMaplikeOrSetlikeMethodGenerator to use
- """
- def __init__(self, descriptor, name, args, code, needsBoolReturn=False):
- self.code = code
- CGAbstractMethod.__init__(self, descriptor, name,
- "bool" if needsBoolReturn else "void",
- args)
- def definition_body(self):
- return self.code
- def __init__(self, descriptor, maplikeOrSetlike, name, needsKeyArg=False,
- needsValueArg=False, needsBoolReturn=False):
- args = []
- self.maplikeOrSetlike = maplikeOrSetlike
- self.needsBoolReturn = needsBoolReturn
- if needsKeyArg:
- args.append(FakeArgument(maplikeOrSetlike.keyType, None, 'aKey'))
- if needsValueArg:
- assert needsKeyArg
- args.append(FakeArgument(maplikeOrSetlike.valueType, None, 'aValue'))
- # Run CallbackMember init function to generate argument conversion code.
- # wrapScope is set to 'obj' when generating maplike or setlike helper
- # functions, as we don't have access to the CallbackPreserveColor
- # method.
- CallbackMember.__init__(self,
- [BuiltinTypes[IDLBuiltinType.Types.void], args],
- name, descriptor, False,
- wrapScope='obj')
- # Wrap CallbackMember body code into a CGAbstractMethod to make
- # generation easier.
- self.implMethod = CGMaplikeOrSetlikeHelperFunctionGenerator.HelperFunction(
- descriptor, name, self.args, self.body, needsBoolReturn)
- def getCallSetup(self):
- return dedent(
- """
- MOZ_ASSERT(self);
- AutoJSAPI jsapi;
- jsapi.Init();
- JSContext* cx = jsapi.cx();
- // It's safe to use UnprivilegedJunkScopeOrWorkerGlobal here because
- // all we want is to wrap into _some_ scope and then unwrap to find
- // the reflector, and wrapping has no side-effects.
- JSAutoCompartment tempCompartment(cx, binding_detail::UnprivilegedJunkScopeOrWorkerGlobal());
- JS::Rooted<JS::Value> v(cx);
- if(!ToJSValue(cx, self, &v)) {
- aRv.Throw(NS_ERROR_UNEXPECTED);
- return%s;
- }
- // This is a reflector, but due to trying to name things
- // similarly across method generators, it's called obj here.
- JS::Rooted<JSObject*> obj(cx);
- obj = js::UncheckedUnwrap(&v.toObject(), /* stopAtWindowProxy = */ false);
- JSAutoCompartment reflectorCompartment(cx, obj);
- """ % self.getDefaultRetval())
- def getArgs(self, returnType, argList):
- # We don't need the context or the value. We'll generate those instead.
- args = CGNativeMember.getArgs(self, returnType, argList)
- # Prepend a pointer to the binding object onto the arguments
- return [Argument(self.descriptorProvider.nativeType + "*", "self")] + args
- def getResultConversion(self):
- if self.needsBoolReturn:
- return "return aRetVal;\n"
- return "return;\n"
- def getRvalDecl(self):
- if self.needsBoolReturn:
- return "bool aRetVal;\n"
- return ""
- def getArgcDecl(self):
- # Don't need argc for anything.
- return None
- def getDefaultRetval(self):
- if self.needsBoolReturn:
- return " false"
- return ""
- def getCall(self):
- return CGMaplikeOrSetlikeMethodGenerator(self.descriptorProvider,
- self.maplikeOrSetlike,
- self.name.lower(),
- helperImpl=self).define()
- def getPrettyName(self):
- return self.name
- def declare(self):
- return self.implMethod.declare()
- def define(self):
- return self.implMethod.define()
- class CGMaplikeOrSetlikeHelperGenerator(CGNamespace):
- """
- Declares and defines convenience methods for accessing backing objects on
- setlike/maplike interface. Generates function signatures, un/packs
- backing objects from slot, etc.
- """
- def __init__(self, descriptor, maplikeOrSetlike):
- self.descriptor = descriptor
- # Since iterables are folded in with maplike/setlike, make sure we've
- # got the right type here.
- assert maplikeOrSetlike.isMaplike() or maplikeOrSetlike.isSetlike()
- self.maplikeOrSetlike = maplikeOrSetlike
- self.namespace = "%sHelpers" % (self.maplikeOrSetlike.maplikeOrSetlikeOrIterableType.title())
- self.helpers = [
- CGMaplikeOrSetlikeHelperFunctionGenerator(descriptor,
- maplikeOrSetlike,
- "Clear"),
- CGMaplikeOrSetlikeHelperFunctionGenerator(descriptor,
- maplikeOrSetlike,
- "Delete",
- needsKeyArg=True,
- needsBoolReturn=True),
- CGMaplikeOrSetlikeHelperFunctionGenerator(descriptor,
- maplikeOrSetlike,
- "Has",
- needsKeyArg=True,
- needsBoolReturn=True)]
- if self.maplikeOrSetlike.isMaplike():
- self.helpers.append(
- CGMaplikeOrSetlikeHelperFunctionGenerator(descriptor,
- maplikeOrSetlike,
- "Set",
- needsKeyArg=True,
- needsValueArg=True))
- else:
- assert(self.maplikeOrSetlike.isSetlike())
- self.helpers.append(
- CGMaplikeOrSetlikeHelperFunctionGenerator(descriptor,
- maplikeOrSetlike,
- "Add",
- needsKeyArg=True))
- CGNamespace.__init__(self, self.namespace, CGList(self.helpers))
- class CGIterableMethodGenerator(CGGeneric):
- """
- Creates methods for iterable interfaces. Unwrapping/wrapping
- will be taken care of by the usual method generation machinery in
- CGMethodCall/CGPerSignatureCall. Functionality is filled in here instead of
- using CGCallGenerator.
- """
- def __init__(self, descriptor, iterable, methodName):
- if methodName == "forEach":
- CGGeneric.__init__(self, fill(
- """
- if (!JS::IsCallable(arg0)) {
- ThrowErrorMessage(cx, MSG_NOT_CALLABLE, "Argument 1 of ${ifaceName}.forEach");
- return false;
- }
- JS::AutoValueArray<3> callArgs(cx);
- callArgs[2].setObject(*obj);
- JS::Rooted<JS::Value> ignoredReturnVal(cx);
- for (size_t i = 0; i < self->GetIterableLength(); ++i) {
- if (!ToJSValue(cx, self->GetValueAtIndex(i), callArgs[0])) {
- return false;
- }
- if (!ToJSValue(cx, self->GetKeyAtIndex(i), callArgs[1])) {
- return false;
- }
- if (!JS::Call(cx, arg1, arg0, JS::HandleValueArray(callArgs),
- &ignoredReturnVal)) {
- return false;
- }
- }
- """,
- ifaceName=descriptor.interface.identifier.name))
- return
- CGGeneric.__init__(self, fill(
- """
- typedef ${iterClass} itrType;
- RefPtr<itrType> result(new itrType(self,
- itrType::IterableIteratorType::${itrMethod},
- &${ifaceName}IteratorBinding::Wrap));
- """,
- iterClass=iteratorNativeType(descriptor),
- ifaceName=descriptor.interface.identifier.name,
- itrMethod=methodName.title()))
- class GlobalGenRoots():
- """
- Roots for global codegen.
- To generate code, call the method associated with the target, and then
- call the appropriate define/declare method.
- """
- @staticmethod
- def GeneratedAtomList(config):
- # Atom enum
- dictionaries = config.dictionaries
- structs = []
- def memberToAtomCacheMember(binaryNameFor, m):
- binaryMemberName = binaryNameFor(m.identifier.name)
- return ClassMember(CGDictionary.makeIdName(binaryMemberName),
- "PinnedStringId", visibility="public")
- def buildAtomCacheStructure(idlobj, binaryNameFor, members):
- classMembers = [memberToAtomCacheMember(binaryNameFor, m)
- for m in members]
- structName = idlobj.identifier.name + "Atoms"
- return (structName,
- CGWrapper(CGClass(structName,
- bases=None,
- isStruct=True,
- members=classMembers), post='\n'))
- for dict in dictionaries:
- if len(dict.members) == 0:
- continue
- structs.append(buildAtomCacheStructure(dict, lambda x: x, dict.members))
- for d in (config.getDescriptors(isJSImplemented=True) +
- config.getDescriptors(isCallback=True)):
- members = [m for m in d.interface.members if m.isAttr() or m.isMethod()]
- if d.interface.isJSImplemented() and d.interface.ctor():
- # We'll have an __init() method.
- members.append(FakeMember('__init'))
- if len(members) == 0:
- continue
- structs.append(buildAtomCacheStructure(d.interface,
- lambda x: d.binaryNameFor(x),
- members))
- structs.sort()
- generatedStructs = [struct for structName, struct in structs]
- structNames = [structName for structName, struct in structs]
- mainStruct = CGWrapper(CGClass("PerThreadAtomCache",
- bases=[ClassBase(structName) for structName in structNames],
- isStruct=True),
- post='\n')
- structs = CGList(generatedStructs + [mainStruct])
- # Wrap all of that in our namespaces.
- curr = CGNamespace.build(['mozilla', 'dom'],
- CGWrapper(structs, pre='\n'))
- curr = CGWrapper(curr, post='\n')
- # Add include statement for PinnedStringId.
- declareIncludes = ['mozilla/dom/BindingUtils.h']
- curr = CGHeaders([], [], [], [], declareIncludes, [], 'GeneratedAtomList',
- curr)
- # Add include guards.
- curr = CGIncludeGuard('GeneratedAtomList', curr)
- # Add the auto-generated comment.
- curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
- # Done.
- return curr
- @staticmethod
- def GeneratedEventList(config):
- eventList = CGList([])
- for generatedEvent in config.generatedEvents:
- eventList.append(CGGeneric(declare=("GENERATED_EVENT(%s)\n" % generatedEvent)))
- return eventList
- @staticmethod
- def PrototypeList(config):
- # Prototype ID enum.
- descriptorsWithPrototype = config.getDescriptors(hasInterfacePrototypeObject=True)
- protos = [d.name for d in descriptorsWithPrototype]
- idEnum = CGNamespacedEnum('id', 'ID', ['_ID_Start'] + protos,
- [0, '_ID_Start'])
- idEnum = CGList([idEnum])
- def fieldSizeAssert(amount, jitInfoField, message):
- maxFieldValue = "(uint64_t(1) << (sizeof(((JSJitInfo*)nullptr)->%s) * 8))" % jitInfoField
- return CGGeneric(declare="static_assert(%s < %s, \"%s\");\n\n"
- % (amount, maxFieldValue, message))
- idEnum.append(fieldSizeAssert("id::_ID_Count", "protoID",
- "Too many prototypes!"))
- # Wrap all of that in our namespaces.
- idEnum = CGNamespace.build(['mozilla', 'dom', 'prototypes'],
- CGWrapper(idEnum, pre='\n'))
- idEnum = CGWrapper(idEnum, post='\n')
- curr = CGList([CGGeneric(define="#include <stdint.h>\n\n"),
- idEnum])
- # Let things know the maximum length of the prototype chain.
- maxMacroName = "MAX_PROTOTYPE_CHAIN_LENGTH"
- maxMacro = CGGeneric(declare="#define " + maxMacroName + " " + str(config.maxProtoChainLength))
- curr.append(CGWrapper(maxMacro, post='\n\n'))
- curr.append(fieldSizeAssert(maxMacroName, "depth",
- "Some inheritance chain is too long!"))
- # Constructor ID enum.
- constructors = [d.name for d in config.getDescriptors(hasInterfaceObject=True)]
- idEnum = CGNamespacedEnum('id', 'ID', ['_ID_Start'] + constructors,
- ['prototypes::id::_ID_Count', '_ID_Start'])
- # Wrap all of that in our namespaces.
- idEnum = CGNamespace.build(['mozilla', 'dom', 'constructors'],
- CGWrapper(idEnum, pre='\n'))
- idEnum = CGWrapper(idEnum, post='\n')
- curr.append(idEnum)
- # Named properties object enum.
- namedPropertiesObjects = [d.name for d in config.getDescriptors(hasNamedPropertiesObject=True)]
- idEnum = CGNamespacedEnum('id', 'ID', ['_ID_Start'] + namedPropertiesObjects,
- ['constructors::id::_ID_Count', '_ID_Start'])
- # Wrap all of that in our namespaces.
- idEnum = CGNamespace.build(['mozilla', 'dom', 'namedpropertiesobjects'],
- CGWrapper(idEnum, pre='\n'))
- idEnum = CGWrapper(idEnum, post='\n')
- curr.append(idEnum)
- traitsDecls = [CGGeneric(declare=dedent("""
- template <prototypes::ID PrototypeID>
- struct PrototypeTraits;
- """))]
- traitsDecls.extend(CGPrototypeTraitsClass(d) for d in descriptorsWithPrototype)
- ifaceNamesWithProto = [d.interface.identifier.name
- for d in descriptorsWithPrototype]
- traitsDecls.append(CGStringTable("NamesOfInterfacesWithProtos",
- ifaceNamesWithProto))
- traitsDecl = CGNamespace.build(['mozilla', 'dom'],
- CGList(traitsDecls))
- curr.append(traitsDecl)
- # Add include guards.
- curr = CGIncludeGuard('PrototypeList', curr)
- # Add the auto-generated comment.
- curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
- # Done.
- return curr
- @staticmethod
- def RegisterBindings(config):
- curr = CGList([CGGlobalNamesString(config), CGRegisterGlobalNames(config)])
- # Wrap all of that in our namespaces.
- curr = CGNamespace.build(['mozilla', 'dom'],
- CGWrapper(curr, post='\n'))
- curr = CGWrapper(curr, post='\n')
- # Add the includes
- defineIncludes = [CGHeaders.getDeclarationFilename(desc.interface)
- for desc in config.getDescriptors(hasInterfaceObject=True,
- isExposedInWindow=True,
- register=True)]
- defineIncludes.append('mozilla/dom/WebIDLGlobalNameHash.h')
- defineIncludes.extend([CGHeaders.getDeclarationFilename(desc.interface)
- for desc in config.getDescriptors(isNavigatorProperty=True,
- register=True)])
- curr = CGHeaders([], [], [], [], [], defineIncludes, 'RegisterBindings',
- curr)
- # Add include guards.
- curr = CGIncludeGuard('RegisterBindings', curr)
- # Done.
- return curr
- @staticmethod
- def RegisterWorkerBindings(config):
- curr = CGRegisterWorkerBindings(config)
- # Wrap all of that in our namespaces.
- curr = CGNamespace.build(['mozilla', 'dom'],
- CGWrapper(curr, post='\n'))
- curr = CGWrapper(curr, post='\n')
- # Add the includes
- defineIncludes = [CGHeaders.getDeclarationFilename(desc.interface)
- for desc in config.getDescriptors(hasInterfaceObject=True,
- register=True,
- isExposedInAnyWorker=True)]
- curr = CGHeaders([], [], [], [], [], defineIncludes,
- 'RegisterWorkerBindings', curr)
- # Add include guards.
- curr = CGIncludeGuard('RegisterWorkerBindings', curr)
- # Done.
- return curr
- @staticmethod
- def RegisterWorkerDebuggerBindings(config):
- curr = CGRegisterWorkerDebuggerBindings(config)
- # Wrap all of that in our namespaces.
- curr = CGNamespace.build(['mozilla', 'dom'],
- CGWrapper(curr, post='\n'))
- curr = CGWrapper(curr, post='\n')
- # Add the includes
- defineIncludes = [CGHeaders.getDeclarationFilename(desc.interface)
- for desc in config.getDescriptors(hasInterfaceObject=True,
- register=True,
- isExposedInWorkerDebugger=True)]
- curr = CGHeaders([], [], [], [], [], defineIncludes,
- 'RegisterWorkerDebuggerBindings', curr)
- # Add include guards.
- curr = CGIncludeGuard('RegisterWorkerDebuggerBindings', curr)
- # Done.
- return curr
- @staticmethod
- def RegisterWorkletBindings(config):
- curr = CGRegisterWorkletBindings(config)
- # Wrap all of that in our namespaces.
- curr = CGNamespace.build(['mozilla', 'dom'],
- CGWrapper(curr, post='\n'))
- curr = CGWrapper(curr, post='\n')
- # Add the includes
- defineIncludes = [CGHeaders.getDeclarationFilename(desc.interface)
- for desc in config.getDescriptors(hasInterfaceObject=True,
- register=True,
- isExposedInAnyWorklet=True)]
- curr = CGHeaders([], [], [], [], [], defineIncludes,
- 'RegisterWorkletBindings', curr)
- # Add include guards.
- curr = CGIncludeGuard('RegisterWorkletBindings', curr)
- # Done.
- return curr
- @staticmethod
- def ResolveSystemBinding(config):
- curr = CGResolveSystemBinding(config)
- # Wrap all of that in our namespaces.
- curr = CGNamespace.build(['mozilla', 'dom'],
- CGWrapper(curr, post='\n'))
- curr = CGWrapper(curr, post='\n')
- # Add the includes
- defineIncludes = [CGHeaders.getDeclarationFilename(desc.interface)
- for desc in config.getDescriptors(hasInterfaceObject=True,
- register=True,
- isExposedInSystemGlobals=True)]
- defineIncludes.append("nsThreadUtils.h") # For NS_IsMainThread
- defineIncludes.append("js/Id.h") # For jsid
- defineIncludes.append("mozilla/dom/BindingUtils.h") # AtomizeAndPinJSString
- curr = CGHeaders([], [], [], [], [], defineIncludes,
- 'ResolveSystemBinding', curr)
- # Add include guards.
- curr = CGIncludeGuard('ResolveSystemBinding', curr)
- # Done.
- return curr
- @staticmethod
- def UnionTypes(config):
- unionTypes = UnionsForFile(config, None)
- (includes, implincludes, declarations,
- traverseMethods, unlinkMethods,
- unionStructs) = UnionTypes(unionTypes, config)
- unions = CGList(traverseMethods +
- unlinkMethods +
- [CGUnionStruct(t, config) for t in unionStructs] +
- [CGUnionStruct(t, config, True) for t in unionStructs],
- "\n")
- includes.add("mozilla/OwningNonNull.h")
- includes.add("mozilla/dom/UnionMember.h")
- includes.add("mozilla/dom/BindingDeclarations.h")
- # BindingUtils.h is only needed for SetToObject.
- # If it stops being inlined or stops calling CallerSubsumes
- # both this bit and the bit in CGBindingRoot can be removed.
- includes.add("mozilla/dom/BindingUtils.h")
- implincludes.add("mozilla/dom/PrimitiveConversions.h")
- # Wrap all of that in our namespaces.
- curr = CGNamespace.build(['mozilla', 'dom'], unions)
- curr = CGWrapper(curr, post='\n')
- builder = ForwardDeclarationBuilder()
- for className, isStruct in declarations:
- builder.add(className, isStruct=isStruct)
- curr = CGList([builder.build(), curr], "\n")
- curr = CGHeaders([], [], [], [], includes, implincludes, 'UnionTypes',
- curr)
- # Add include guards.
- curr = CGIncludeGuard('UnionTypes', curr)
- # Done.
- return curr
- @staticmethod
- def UnionConversions(config):
- unionTypes = []
- for l in config.unionsPerFilename.itervalues():
- unionTypes.extend(l)
- unionTypes.sort(key=lambda u: u.name)
- headers, unions = UnionConversions(unionTypes,
- config)
- # Wrap all of that in our namespaces.
- curr = CGNamespace.build(['mozilla', 'dom'], unions)
- curr = CGWrapper(curr, post='\n')
- headers.update(["nsDebug.h", "mozilla/dom/UnionTypes.h"])
- curr = CGHeaders([], [], [], [], headers, [], 'UnionConversions', curr)
- # Add include guards.
- curr = CGIncludeGuard('UnionConversions', curr)
- # Done.
- return curr
- # Code generator for simple events
- class CGEventGetter(CGNativeMember):
- def __init__(self, descriptor, attr):
- ea = descriptor.getExtendedAttributes(attr, getter=True)
- CGNativeMember.__init__(self, descriptor, attr,
- CGSpecializedGetter.makeNativeName(descriptor,
- attr),
- (attr.type, []),
- ea,
- resultNotAddRefed=not attr.type.isSequence())
- self.body = self.getMethodBody()
- def getArgs(self, returnType, argList):
- if 'infallible' not in self.extendedAttrs:
- raise TypeError("Event code generator does not support [Throws]!")
- if not self.member.isAttr():
- raise TypeError("Event code generator does not support methods")
- if self.member.isStatic():
- raise TypeError("Event code generators does not support static attributes")
- return CGNativeMember.getArgs(self, returnType, argList)
- def getMethodBody(self):
- type = self.member.type
- memberName = CGDictionary.makeMemberName(self.member.identifier.name)
- if ((type.isPrimitive() and type.tag() in builtinNames) or
- type.isEnum() or
- type.isPromise() or
- type.isGeckoInterface()):
- return "return " + memberName + ";\n"
- if type.isDOMString() or type.isByteString() or type.isUSVString():
- return "aRetVal = " + memberName + ";\n"
- if type.isSpiderMonkeyInterface() or type.isObject():
- return fill(
- """
- if (${memberName}) {
- JS::ExposeObjectToActiveJS(${memberName});
- }
- aRetVal.set(${memberName});
- return;
- """,
- memberName=memberName)
- if type.isAny():
- return fill(
- """
- ${selfName}(aRetVal);
- """,
- selfName=self.name)
- if type.isUnion():
- return "aRetVal = " + memberName + ";\n"
- if type.isSequence():
- return "aRetVal = " + memberName + ";\n"
- raise TypeError("Event code generator does not support this type!")
- def declare(self, cgClass):
- if getattr(self.member, "originatingInterface",
- cgClass.descriptor.interface) != cgClass.descriptor.interface:
- return ""
- return CGNativeMember.declare(self, cgClass)
- def define(self, cgClass):
- if getattr(self.member, "originatingInterface",
- cgClass.descriptor.interface) != cgClass.descriptor.interface:
- return ""
- return CGNativeMember.define(self, cgClass)
- class CGEventSetter(CGNativeMember):
- def __init__(self):
- raise TypeError("Event code generator does not support setters!")
- class CGEventMethod(CGNativeMember):
- def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True):
- self.isInit = False
- CGNativeMember.__init__(self, descriptor, method,
- CGSpecializedMethod.makeNativeName(descriptor,
- method),
- signature,
- descriptor.getExtendedAttributes(method),
- breakAfter=breakAfter,
- variadicIsSequence=True)
- self.originalArgs = list(self.args)
- iface = descriptor.interface
- allowed = isConstructor
- if not allowed and iface.getExtendedAttribute("LegacyEventInit"):
- # Allow it, only if it fits the initFooEvent profile exactly
- # We could check the arg types but it's not worth the effort.
- if (method.identifier.name == "init" + iface.identifier.name and
- signature[1][0].type.isDOMString() and
- signature[1][1].type.isBoolean() and
- signature[1][2].type.isBoolean() and
- # -3 on the left to ignore the type, bubbles, and cancelable parameters
- # -1 on the right to ignore the .trusted property which bleeds through
- # here because it is [Unforgeable].
- len(signature[1]) - 3 == len(filter(lambda x: x.isAttr(), iface.members)) - 1):
- allowed = True
- self.isInit = True
- if not allowed:
- raise TypeError("Event code generator does not support methods!")
- def getArgs(self, returnType, argList):
- args = [self.getArg(arg) for arg in argList]
- return args
- def getArg(self, arg):
- decl, ref = self.getArgType(arg.type,
- arg.canHaveMissingValue(),
- "Variadic" if arg.variadic else False)
- if ref:
- decl = CGWrapper(decl, pre="const ", post="&")
- name = arg.identifier.name
- name = "a" + name[0].upper() + name[1:]
- return Argument(decl.define(), name)
- def declare(self, cgClass):
- if self.isInit:
- constructorForNativeCaller = ""
- else:
- self.args = list(self.originalArgs)
- self.args.insert(0, Argument("mozilla::dom::EventTarget*", "aOwner"))
- constructorForNativeCaller = CGNativeMember.declare(self, cgClass)
- self.args = list(self.originalArgs)
- if needCx(None, self.arguments(), [], considerTypes=True, static=True):
- self.args.insert(0, Argument("JSContext*", "aCx"))
- if not self.isInit:
- self.args.insert(0, Argument("const GlobalObject&", "aGlobal"))
- self.args.append(Argument('ErrorResult&', 'aRv'))
- return constructorForNativeCaller + CGNativeMember.declare(self, cgClass)
- def defineInit(self, cgClass):
- iface = self.descriptorProvider.interface
- members = ""
- while iface.identifier.name != "Event":
- i = 3 # Skip the boilerplate args: type, bubble,s cancelable.
- for m in iface.members:
- if m.isAttr():
- # We need to initialize all the member variables that do
- # not come from Event.
- if getattr(m, "originatingInterface",
- iface).identifier.name == "Event":
- continue
- name = CGDictionary.makeMemberName(m.identifier.name)
- members += "%s = %s;\n" % (name, self.args[i].name)
- i += 1
- iface = iface.parent
- self.body = fill(
- """
- InitEvent(${typeArg}, ${bubblesArg}, ${cancelableArg});
- ${members}
- """,
- typeArg=self.args[0].name,
- bubblesArg=self.args[1].name,
- cancelableArg=self.args[2].name,
- members=members)
- return CGNativeMember.define(self, cgClass)
- def define(self, cgClass):
- self.args = list(self.originalArgs)
- if self.isInit:
- return self.defineInit(cgClass)
- members = ""
- holdJS = ""
- iface = self.descriptorProvider.interface
- while iface.identifier.name != "Event":
- for m in self.descriptorProvider.getDescriptor(iface.identifier.name).interface.members:
- if m.isAttr():
- # We initialize all the other member variables in the
- # Constructor except those ones coming from the Event.
- if getattr(m, "originatingInterface",
- cgClass.descriptor.interface).identifier.name == "Event":
- continue
- name = CGDictionary.makeMemberName(m.identifier.name)
- if m.type.isSequence():
- # For sequences we may not be able to do a simple
- # assignment because the underlying types may not match.
- # For example, the argument can be a
- # Sequence<OwningNonNull<SomeInterface>> while our
- # member is an nsTArray<RefPtr<SomeInterface>>. So
- # use AppendElements, which is actually a template on
- # the incoming type on nsTArray and does the right thing
- # for this case.
- target = name
- source = "%s.%s" % (self.args[1].name, name)
- sequenceCopy = "e->%s.AppendElements(%s);\n"
- if m.type.nullable():
- sequenceCopy = CGIfWrapper(
- CGGeneric(sequenceCopy),
- "!%s.IsNull()" % source).define()
- target += ".SetValue()"
- source += ".Value()"
- members += sequenceCopy % (target, source)
- elif m.type.isSpiderMonkeyInterface():
- srcname = "%s.%s" % (self.args[1].name, name)
- if m.type.nullable():
- members += fill(
- """
- if (${srcname}.IsNull()) {
- e->${varname} = nullptr;
- } else {
- e->${varname} = ${srcname}.Value().Obj();
- }
- """,
- varname=name,
- srcname=srcname)
- else:
- members += fill(
- """
- e->${varname}.set(${srcname}.Obj());
- """,
- varname=name, srcname=srcname)
- else:
- members += "e->%s = %s.%s;\n" % (name, self.args[1].name, name)
- if m.type.isAny() or m.type.isObject() or m.type.isSpiderMonkeyInterface():
- holdJS = "mozilla::HoldJSObjects(e.get());\n"
- iface = iface.parent
- self.body = fill(
- """
- RefPtr<${nativeType}> e = new ${nativeType}(aOwner);
- bool trusted = e->Init(aOwner);
- e->InitEvent(${eventType}, ${eventInit}.mBubbles, ${eventInit}.mCancelable);
- $*{members}
- e->SetTrusted(trusted);
- e->SetComposed(${eventInit}.mComposed);
- $*{holdJS}
- return e.forget();
- """,
- nativeType=self.descriptorProvider.nativeType.split('::')[-1],
- eventType=self.args[0].name,
- eventInit=self.args[1].name,
- members=members,
- holdJS=holdJS)
- self.args.insert(0, Argument("mozilla::dom::EventTarget*", "aOwner"))
- constructorForNativeCaller = CGNativeMember.define(self, cgClass) + "\n"
- self.args = list(self.originalArgs)
- self.body = fill(
- """
- nsCOMPtr<mozilla::dom::EventTarget> owner = do_QueryInterface(aGlobal.GetAsSupports());
- return Constructor(owner, ${arg0}, ${arg1});
- """,
- arg0=self.args[0].name,
- arg1=self.args[1].name)
- if needCx(None, self.arguments(), [], considerTypes=True, static=True):
- self.args.insert(0, Argument("JSContext*", "aCx"))
- self.args.insert(0, Argument("const GlobalObject&", "aGlobal"))
- self.args.append(Argument('ErrorResult&', 'aRv'))
- return constructorForNativeCaller + CGNativeMember.define(self, cgClass)
- class CGEventClass(CGBindingImplClass):
- """
- Codegen for the actual Event class implementation for this descriptor
- """
- def __init__(self, descriptor):
- CGBindingImplClass.__init__(self, descriptor, CGEventMethod, CGEventGetter, CGEventSetter, False, "WrapObjectInternal")
- members = []
- extraMethods = []
- for m in descriptor.interface.members:
- if m.isAttr():
- if m.type.isAny():
- # Add a getter that doesn't need a JSContext. Note that we
- # don't need to do this if our originating interface is not
- # the descriptor's interface, because in that case we
- # wouldn't generate the getter that _does_ need a JSContext
- # either.
- extraMethods.append(
- ClassMethod(
- CGSpecializedGetter.makeNativeName(descriptor, m),
- "void",
- [Argument("JS::MutableHandle<JS::Value>",
- "aRetVal")],
- const=True,
- body=fill(
- """
- JS::ExposeValueToActiveJS(${memberName});
- aRetVal.set(${memberName});
- """,
- memberName=CGDictionary.makeMemberName(m.identifier.name))))
- if getattr(m, "originatingInterface",
- descriptor.interface) != descriptor.interface:
- continue
- nativeType = self.getNativeTypeForIDLType(m.type).define()
- members.append(ClassMember(CGDictionary.makeMemberName(m.identifier.name),
- nativeType,
- visibility="private",
- body="body"))
- parent = self.descriptor.interface.parent
- self.parentType = self.descriptor.getDescriptor(parent.identifier.name).nativeType.split('::')[-1]
- baseDeclarations = fill(
- """
- public:
- NS_DECL_ISUPPORTS_INHERITED
- NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(${nativeType}, ${parentType})
- protected:
- virtual ~${nativeType}();
- explicit ${nativeType}(mozilla::dom::EventTarget* aOwner);
- """,
- nativeType=self.descriptor.nativeType.split('::')[-1],
- parentType=self.parentType)
- className = descriptor.nativeType.split('::')[-1]
- asConcreteTypeMethod = ClassMethod("As%s" % className,
- "%s*" % className,
- [],
- virtual=True,
- body="return this;\n",
- breakAfterReturnDecl=" ",
- override=True)
- extraMethods.append(asConcreteTypeMethod)
- CGClass.__init__(self, className,
- bases=[ClassBase(self.parentType)],
- methods=extraMethods+self.methodDecls,
- members=members,
- extradeclarations=baseDeclarations)
- def getWrapObjectBody(self):
- return "return %sBinding::Wrap(aCx, this, aGivenProto);\n" % self.descriptor.name
- def implTraverse(self):
- retVal = ""
- for m in self.descriptor.interface.members:
- # Unroll the type so we pick up sequences of interfaces too.
- if m.isAttr() and idlTypeNeedsCycleCollection(m.type):
- retVal += (" NS_IMPL_CYCLE_COLLECTION_TRAVERSE(" +
- CGDictionary.makeMemberName(m.identifier.name) +
- ")\n")
- return retVal
- def implUnlink(self):
- retVal = ""
- for m in self.descriptor.interface.members:
- if m.isAttr():
- name = CGDictionary.makeMemberName(m.identifier.name)
- # Unroll the type so we pick up sequences of interfaces too.
- if idlTypeNeedsCycleCollection(m.type):
- retVal += " NS_IMPL_CYCLE_COLLECTION_UNLINK(" + name + ")\n"
- elif m.type.isAny():
- retVal += " tmp->" + name + ".setUndefined();\n"
- elif m.type.isObject() or m.type.isSpiderMonkeyInterface():
- retVal += " tmp->" + name + " = nullptr;\n"
- return retVal
- def implTrace(self):
- retVal = ""
- for m in self.descriptor.interface.members:
- if m.isAttr():
- name = CGDictionary.makeMemberName(m.identifier.name)
- if m.type.isAny() or m.type.isObject() or m.type.isSpiderMonkeyInterface():
- retVal += " NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(" + name + ")\n"
- elif typeNeedsRooting(m.type):
- raise TypeError("Need to implement tracing for event "
- "member of type %s" % m.type)
- return retVal
- def define(self):
- dropJS = ""
- for m in self.descriptor.interface.members:
- if m.isAttr():
- member = CGDictionary.makeMemberName(m.identifier.name)
- if m.type.isAny():
- dropJS += member + " = JS::UndefinedValue();\n"
- elif m.type.isObject() or m.type.isSpiderMonkeyInterface():
- dropJS += member + " = nullptr;\n"
- if dropJS != "":
- dropJS += "mozilla::DropJSObjects(this);\n"
- # Just override CGClass and do our own thing
- nativeType = self.descriptor.nativeType.split('::')[-1]
- ctorParams = ("aOwner, nullptr, nullptr" if self.parentType == "Event"
- else "aOwner")
- classImpl = fill(
- """
- NS_IMPL_CYCLE_COLLECTION_CLASS(${nativeType})
- NS_IMPL_ADDREF_INHERITED(${nativeType}, ${parentType})
- NS_IMPL_RELEASE_INHERITED(${nativeType}, ${parentType})
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(${nativeType}, ${parentType})
- $*{traverse}
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
- NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(${nativeType}, ${parentType})
- $*{trace}
- NS_IMPL_CYCLE_COLLECTION_TRACE_END
- NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(${nativeType}, ${parentType})
- $*{unlink}
- NS_IMPL_CYCLE_COLLECTION_UNLINK_END
- NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(${nativeType})
- NS_INTERFACE_MAP_END_INHERITING(${parentType})
- ${nativeType}::${nativeType}(mozilla::dom::EventTarget* aOwner)
- : ${parentType}(${ctorParams})
- {
- }
- ${nativeType}::~${nativeType}()
- {
- $*{dropJS}
- }
- """,
- ifaceName=self.descriptor.name,
- nativeType=nativeType,
- ctorParams=ctorParams,
- parentType=self.parentType,
- traverse=self.implTraverse(),
- unlink=self.implUnlink(),
- trace=self.implTrace(),
- dropJS=dropJS)
- return classImpl + CGBindingImplClass.define(self)
- def getNativeTypeForIDLType(self, type):
- if type.isPrimitive() and type.tag() in builtinNames:
- nativeType = CGGeneric(builtinNames[type.tag()])
- if type.nullable():
- nativeType = CGTemplatedType("Nullable", nativeType)
- elif type.isEnum():
- nativeType = CGGeneric(type.unroll().inner.identifier.name)
- if type.nullable():
- nativeType = CGTemplatedType("Nullable", nativeType)
- elif type.isDOMString() or type.isUSVString():
- nativeType = CGGeneric("nsString")
- elif type.isByteString():
- nativeType = CGGeneric("nsCString")
- elif type.isPromise():
- nativeType = CGGeneric("RefPtr<Promise>")
- elif type.isGeckoInterface():
- iface = type.unroll().inner
- nativeType = self.descriptor.getDescriptor(
- iface.identifier.name).nativeType
- # Now trim off unnecessary namespaces
- nativeType = nativeType.split("::")
- if nativeType[0] == "mozilla":
- nativeType.pop(0)
- if nativeType[0] == "dom":
- nativeType.pop(0)
- nativeType = CGWrapper(CGGeneric("::".join(nativeType)), pre="RefPtr<", post=">")
- elif type.isAny():
- nativeType = CGGeneric("JS::Heap<JS::Value>")
- elif type.isObject() or type.isSpiderMonkeyInterface():
- nativeType = CGGeneric("JS::Heap<JSObject*>")
- elif type.isUnion():
- nativeType = CGGeneric(CGUnionStruct.unionTypeDecl(type, True))
- elif type.isSequence():
- if type.nullable():
- innerType = type.inner.inner
- else:
- innerType = type.inner
- if (not innerType.isPrimitive() and not innerType.isEnum() and
- not innerType.isDOMString() and not innerType.isByteString() and
- not innerType.isPromise() and not innerType.isGeckoInterface()):
- raise TypeError("Don't know how to properly manage GC/CC for "
- "event member of type %s" %
- type)
- nativeType = CGTemplatedType(
- "nsTArray",
- self.getNativeTypeForIDLType(innerType))
- if type.nullable():
- nativeType = CGTemplatedType("Nullable", nativeType)
- else:
- raise TypeError("Don't know how to declare event member of type %s" %
- type)
- return nativeType
- class CGEventRoot(CGThing):
- def __init__(self, config, interfaceName):
- descriptor = config.getDescriptor(interfaceName)
- self.root = CGWrapper(CGEventClass(descriptor),
- pre="\n", post="\n")
- self.root = CGNamespace.build(["mozilla", "dom"], self.root)
- self.root = CGList([CGClassForwardDeclare("JSContext", isStruct=True),
- self.root])
- parent = descriptor.interface.parent.identifier.name
- # Throw in our #includes
- self.root = CGHeaders(
- [descriptor],
- [],
- [],
- [],
- [
- config.getDescriptor(parent).headerFile,
- "mozilla/Attributes.h",
- "mozilla/ErrorResult.h",
- "mozilla/dom/%sBinding.h" % interfaceName,
- 'mozilla/dom/BindingUtils.h',
- ],
- [
- "%s.h" % interfaceName,
- "js/GCAPI.h",
- 'mozilla/dom/Nullable.h',
- ],
- "", self.root, config)
- # And now some include guards
- self.root = CGIncludeGuard(interfaceName, self.root)
- self.root = CGWrapper(
- self.root,
- pre=(AUTOGENERATED_WITH_SOURCE_WARNING_COMMENT %
- os.path.basename(descriptor.interface.filename())))
- self.root = CGWrapper(self.root, pre=dedent("""
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* vim:set ts=2 sw=2 sts=2 et cindent: */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- """))
- def declare(self):
- return self.root.declare()
- def define(self):
- return self.root.define()
|