| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936 |
- /**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file interfaceMakerPythonNative.cxx
- */
- #include "interfaceMakerPythonNative.h"
- #include "interrogateBuilder.h"
- #include "interrogate.h"
- #include "functionRemap.h"
- #include "parameterRemapUnchanged.h"
- #include "typeManager.h"
- #include "pnotify.h" // For nout
- #include "interrogateDatabase.h"
- #include "interrogateType.h"
- #include "interrogateFunction.h"
- #include "cppArrayType.h"
- #include "cppConstType.h"
- #include "cppEnumType.h"
- #include "cppFunctionType.h"
- #include "cppFunctionGroup.h"
- #include "cppPointerType.h"
- #include "cppTypeDeclaration.h"
- #include "cppTypedefType.h"
- #include "cppSimpleType.h"
- #include "cppStructType.h"
- #include "cppExpression.h"
- #include "vector"
- #include "cppParameterList.h"
- #include "algorithm"
- #include "lineStream.h"
- #include <set>
- #include <map>
- using std::dec;
- using std::hex;
- using std::max;
- using std::min;
- using std::oct;
- using std::ostream;
- using std::ostringstream;
- using std::set;
- using std::string;
- extern InterrogateType dummy_type;
- extern std::string EXPORT_IMPORT_PREFIX;
- #define CLASS_PREFIX "Dtool_"
- // Name Remapper... Snagged from ffi py code....
- struct RenameSet {
- const char *_from;
- const char *_to;
- int function_type;
- };
- RenameSet methodRenameDictionary[] = {
- { "operator ==" , "__eq__", 0 },
- { "operator !=" , "__ne__", 0 },
- { "operator << " , "__lshift__", 0 },
- { "operator >>" , "__rshift__", 0 },
- { "operator <" , "__lt__", 0 },
- { "operator >" , "__gt__", 0 },
- { "operator <=" , "__le__", 0 },
- { "operator >=" , "__ge__", 0 },
- { "operator =" , "assign", 0 },
- { "operator ()" , "__call__", 0 },
- { "operator []" , "__getitem__", 0 },
- { "operator ++unary", "increment", 0 },
- { "operator ++" , "increment", 0 },
- { "operator --unary", "decrement", 0 },
- { "operator --" , "decrement", 0 },
- { "operator ^" , "__xor__", 0 },
- { "operator %" , "__mod__", 0 },
- { "operator !" , "logicalNot", 0 },
- { "operator ~unary", "__invert__", 0 },
- { "operator &" , "__and__", 0 },
- { "operator &&" , "logicalAnd", 0 },
- { "operator |" , "__or__", 0 },
- { "operator ||" , "logicalOr", 0 },
- { "operator +" , "__add__", 0 },
- { "operator -" , "__sub__", 0 },
- { "operator -unary", "__neg__", 0 },
- { "operator *" , "__mul__", 0 },
- { "operator /" , "__div__", 0 },
- { "operator +=" , "__iadd__", 1 },
- { "operator -=" , "__isub__", 1 },
- { "operator *=" , "__imul__", 1 },
- { "operator /=" , "__idiv__", 1 },
- { "operator ," , "concatenate", 0 },
- { "operator |=" , "__ior__", 1 },
- { "operator &=" , "__iand__", 1 },
- { "operator ^=" , "__ixor__", 1 },
- { "operator ~=" , "bitwiseNotEqual", 0 },
- { "operator ->" , "dereference", 0 },
- { "operator <<=" , "__ilshift__", 1 },
- { "operator >>=" , "__irshift__", 1 },
- { "operator typecast bool", "__nonzero__", 0 },
- { "__nonzero__" , "__nonzero__", 0 },
- { "__reduce__" , "__reduce__", 0 },
- { "__reduce_persist__", "__reduce_persist__", 0 },
- { "__copy__" , "__copy__", 0 },
- { "__deepcopy__" , "__deepcopy__", 0 },
- { "print" , "Cprint", 0 },
- { "CInterval.set_t", "_priv__cSetT", 0 },
- { nullptr, nullptr, -1 }
- };
- const char *pythonKeywords[] = {
- "and",
- "as",
- "assert",
- "break",
- "class",
- "continue",
- "def",
- "del",
- "elif",
- "else",
- "except",
- "exec",
- "finally",
- "for",
- "from",
- "global",
- "if",
- "import",
- "in",
- "is",
- "lambda",
- "nonlocal",
- "not",
- "or",
- "pass",
- "print",
- "raise",
- "return",
- "try",
- "while",
- "with",
- "yield",
- nullptr
- };
- std::string
- checkKeyword(std::string &cppName) {
- for (int x = 0; pythonKeywords[x] != nullptr; x++) {
- if (cppName == pythonKeywords[x]) {
- return std::string("_") + cppName;
- }
- }
- return cppName;
- }
- std::string
- classNameFromCppName(const std::string &cppName, bool mangle) {
- if (!mangle_names) {
- mangle = false;
- }
- // # initialize to empty string
- std::string className = "";
- // # These are the characters we want to strip out of the name
- const std::string badChars("!@#$%^&*()<>,.-=+~{}? ");
- bool nextCap = false;
- bool nextUscore = false;
- bool firstChar = true && mangle;
- for (std::string::const_iterator chr = cppName.begin();
- chr != cppName.end(); ++chr) {
- if ((*chr == '_' || *chr == ' ') && mangle) {
- nextCap = true;
- } else if (badChars.find(*chr) != std::string::npos) {
- nextUscore = !mangle;
- } else if (nextCap || firstChar) {
- className += toupper(*chr);
- nextCap = false;
- firstChar = false;
- } else if (nextUscore) {
- className += '_';
- nextUscore = false;
- className += *chr;
- } else {
- className += *chr;
- }
- }
- if (className.empty()) {
- std::string text = "** ERROR ** Renaming class: " + cppName + " to empty string";
- printf("%s", text.c_str());
- }
- className = checkKeyword(className);
- // # FFIConstants.notify.debug('Renaming class: ' + cppName + ' to: ' +
- // className)
- return className;
- }
- std::string
- methodNameFromCppName(const std::string &cppName, const std::string &className, bool mangle) {
- if (!mangle_names) {
- mangle = false;
- }
- std::string origName = cppName;
- if (origName.substr(0, 6) == "__py__") {
- // By convention, a leading prefix of "__py__" is stripped. This
- // indicates a Python-specific variant of a particular method.
- origName = origName.substr(6);
- }
- std::string methodName;
- const std::string badChars("!@#$%^&*()<>,.-=+~{}? ");
- bool nextCap = false;
- for (std::string::const_iterator chr = origName.begin();
- chr != origName.end();
- chr++) {
- if ((*chr == '_' || *chr == ' ') && mangle) {
- nextCap = true;
- } else if (badChars.find(*chr) != std::string::npos) {
- if (!mangle) {
- methodName += '_';
- }
- } else if (nextCap) {
- methodName += toupper(*chr);
- nextCap = false;
- } else {
- methodName += *chr;
- }
- }
- for (int x = 0; methodRenameDictionary[x]._from != nullptr; x++) {
- if (origName == methodRenameDictionary[x]._from) {
- methodName = methodRenameDictionary[x]._to;
- }
- }
- // # Mangle names that happen to be python keywords so they are not anymore
- methodName = checkKeyword(methodName);
- return methodName;
- }
- std::string methodNameFromCppName(InterfaceMaker::Function *func, const std::string &className, bool mangle) {
- std::string cppName = func->_ifunc.get_name();
- if (func->_ifunc.is_unary_op()) {
- cppName += "unary";
- }
- return methodNameFromCppName(cppName, className, mangle);
- }
- std::string methodNameFromCppName(FunctionRemap *remap, const std::string &className, bool mangle) {
- std::string cppName = remap->_cppfunc->get_local_name();
- if (remap->_ftype->_flags & CPPFunctionType::F_unary_op) {
- cppName += "unary";
- }
- return methodNameFromCppName(cppName, className, mangle);
- }
- /**
- * Determines whether this method should be mapped to one of Python's special
- * slotted functions, those hard-coded functions that are assigned to
- * particular function pointers within the object structure, for special
- * functions like __getitem__ and __len__.
- *
- * Returns true if it has such a mapping, false if it is just a normal method.
- * If it returns true, the SlottedFunctionDef structure is filled in with the
- * important details.
- */
- bool InterfaceMakerPythonNative::
- get_slotted_function_def(Object *obj, Function *func, FunctionRemap *remap,
- SlottedFunctionDef &def) {
- if (obj == nullptr) {
- // Only methods may be slotted.
- return false;
- }
- def._answer_location = string();
- def._wrapper_type = WT_none;
- def._min_version = 0;
- def._keep_method = false;
- string method_name = func->_ifunc.get_name();
- bool is_unary_op = func->_ifunc.is_unary_op();
- if (method_name == "operator +") {
- def._answer_location = "nb_add";
- def._wrapper_type = WT_binary_operator;
- return true;
- }
- if (method_name == "operator -" && is_unary_op) {
- def._answer_location = "nb_negative";
- def._wrapper_type = WT_no_params;
- return true;
- }
- if (method_name == "operator -") {
- def._answer_location = "nb_subtract";
- def._wrapper_type = WT_binary_operator;
- return true;
- }
- if (method_name == "operator *") {
- def._answer_location = "nb_multiply";
- def._wrapper_type = WT_binary_operator;
- return true;
- }
- if (method_name == "operator /") {
- def._answer_location = "nb_divide";
- def._wrapper_type = WT_binary_operator;
- return true;
- }
- if (method_name == "operator %") {
- def._answer_location = "nb_remainder";
- def._wrapper_type = WT_binary_operator;
- return true;
- }
- if (method_name == "operator <<") {
- def._answer_location = "nb_lshift";
- def._wrapper_type = WT_binary_operator;
- return true;
- }
- if (method_name == "operator >>") {
- def._answer_location = "nb_rshift";
- def._wrapper_type = WT_binary_operator;
- return true;
- }
- if (method_name == "operator ^") {
- def._answer_location = "nb_xor";
- def._wrapper_type = WT_binary_operator;
- return true;
- }
- if (method_name == "operator ~" && is_unary_op) {
- def._answer_location = "nb_invert";
- def._wrapper_type = WT_no_params;
- return true;
- }
- if (method_name == "operator &") {
- def._answer_location = "nb_and";
- def._wrapper_type = WT_binary_operator;
- return true;
- }
- if (method_name == "operator |") {
- def._answer_location = "nb_or";
- def._wrapper_type = WT_binary_operator;
- return true;
- }
- if (method_name == "__pow__") {
- def._answer_location = "nb_power";
- def._wrapper_type = WT_ternary_operator;
- return true;
- }
- if (method_name == "operator +=") {
- def._answer_location = "nb_inplace_add";
- def._wrapper_type = WT_inplace_binary_operator;
- return true;
- }
- if (method_name == "operator -=") {
- def._answer_location = "nb_inplace_subtract";
- def._wrapper_type = WT_inplace_binary_operator;
- return true;
- }
- if (method_name == "operator *=") {
- def._answer_location = "nb_inplace_multiply";
- def._wrapper_type = WT_inplace_binary_operator;
- return true;
- }
- if (method_name == "operator /=") {
- def._answer_location = "nb_inplace_divide";
- def._wrapper_type = WT_inplace_binary_operator;
- return true;
- }
- if (method_name == "operator %=") {
- def._answer_location = "nb_inplace_remainder";
- def._wrapper_type = WT_inplace_binary_operator;
- return true;
- }
- if (method_name == "operator <<=") {
- def._answer_location = "nb_inplace_lshift";
- def._wrapper_type = WT_inplace_binary_operator;
- return true;
- }
- if (method_name == "operator >>=") {
- def._answer_location = "nb_inplace_rshift";
- def._wrapper_type = WT_inplace_binary_operator;
- return true;
- }
- if (method_name == "operator &=") {
- def._answer_location = "nb_inplace_and";
- def._wrapper_type = WT_inplace_binary_operator;
- return true;
- }
- if (method_name == "operator ^=") {
- def._answer_location = "nb_inplace_xor";
- def._wrapper_type = WT_inplace_binary_operator;
- return true;
- }
- if (method_name == "__ipow__") {
- def._answer_location = "nb_inplace_power";
- def._wrapper_type = WT_inplace_ternary_operator;
- return true;
- }
- if (obj->_protocol_types & Object::PT_sequence) {
- if (remap->_flags & FunctionRemap::F_getitem_int) {
- def._answer_location = "sq_item";
- def._wrapper_type = WT_sequence_getitem;
- return true;
- }
- if (remap->_flags & FunctionRemap::F_setitem_int ||
- remap->_flags & FunctionRemap::F_delitem_int) {
- def._answer_location = "sq_ass_item";
- def._wrapper_type = WT_sequence_setitem;
- return true;
- }
- if (remap->_flags & FunctionRemap::F_size) {
- def._answer_location = "sq_length";
- def._wrapper_type = WT_sequence_size;
- return true;
- }
- }
- if (obj->_protocol_types & Object::PT_mapping) {
- if (remap->_flags & FunctionRemap::F_getitem) {
- def._answer_location = "mp_subscript";
- def._wrapper_type = WT_one_param;
- return true;
- }
- if (remap->_flags & FunctionRemap::F_setitem ||
- remap->_flags & FunctionRemap::F_delitem) {
- def._answer_location = "mp_ass_subscript";
- def._wrapper_type = WT_mapping_setitem;
- return true;
- }
- if (remap->_flags & FunctionRemap::F_size) {
- def._answer_location = "mp_length";
- def._wrapper_type = WT_sequence_size;
- return true;
- }
- }
- if (obj->_protocol_types & Object::PT_iter) {
- if (method_name == "__iter__") {
- def._answer_location = "tp_iter";
- def._wrapper_type = WT_no_params;
- return true;
- }
- if (method_name == "next" || method_name == "__next__") {
- def._answer_location = "tp_iternext";
- def._wrapper_type = WT_iter_next;
- return true;
- }
- }
- if (method_name == "__await__") {
- def._answer_location = "am_await";
- def._wrapper_type = WT_no_params;
- return true;
- }
- if (method_name == "__aiter__") {
- def._answer_location = "am_aiter";
- def._wrapper_type = WT_no_params;
- return true;
- }
- if (method_name == "__anext__") {
- def._answer_location = "am_anext";
- def._wrapper_type = WT_no_params;
- return true;
- }
- if (method_name == "operator ()") {
- def._answer_location = "tp_call";
- def._wrapper_type = WT_none;
- return true;
- }
- if (method_name == "__getattribute__") {
- // Like __getattr__, but is called unconditionally, ie. does not try
- // PyObject_GenericGetAttr first.
- def._answer_location = "tp_getattro";
- def._wrapper_type = WT_one_param;
- return true;
- }
- if (method_name == "__getattr__") {
- def._answer_location = "tp_getattro";
- def._wrapper_type = WT_getattr;
- return true;
- }
- if (method_name == "__setattr__") {
- def._answer_location = "tp_setattro";
- def._wrapper_type = WT_setattr;
- return true;
- }
- if (method_name == "__delattr__") {
- // __delattr__ shares the slot with __setattr__, except that it takes only
- // one argument.
- def._answer_location = "tp_setattro";
- def._wrapper_type = WT_setattr;
- return true;
- }
- if (method_name == "__nonzero__" || method_name == "__bool__") {
- // Python 2 named it nb_nonzero, Python 3 nb_bool. We refer to it just as
- // nb_bool.
- def._answer_location = "nb_bool";
- def._wrapper_type = WT_inquiry;
- return true;
- }
- if (method_name == "__getbuffer__") {
- def._answer_location = "bf_getbuffer";
- def._wrapper_type = WT_getbuffer;
- return true;
- }
- if (method_name == "__releasebuffer__") {
- def._answer_location = "bf_releasebuffer";
- def._wrapper_type = WT_releasebuffer;
- return true;
- }
- if (method_name == "__traverse__") {
- def._answer_location = "tp_traverse";
- def._wrapper_type = WT_traverse;
- return true;
- }
- if (method_name == "__clear__") {
- def._answer_location = "tp_clear";
- def._wrapper_type = WT_inquiry;
- return true;
- }
- if (method_name == "__repr__") {
- def._answer_location = "tp_repr";
- def._wrapper_type = WT_no_params;
- return true;
- }
- if (method_name == "__str__") {
- def._answer_location = "tp_str";
- def._wrapper_type = WT_no_params;
- return true;
- }
- if (method_name == "__cmp__" || (remap->_flags & FunctionRemap::F_compare_to) != 0) {
- def._answer_location = "tp_compare";
- def._wrapper_type = WT_compare;
- def._keep_method = (method_name != "__cmp__");
- return true;
- }
- if (method_name == "__hash__" || (remap->_flags & FunctionRemap::F_hash) != 0) {
- def._answer_location = "tp_hash";
- def._wrapper_type = WT_hash;
- def._keep_method = (method_name != "__hash__");
- return true;
- }
- if (remap->_type == FunctionRemap::T_typecast_method) {
- // A typecast operator. Check for a supported low-level typecast type.
- if (TypeManager::is_bool(remap->_return_type->get_orig_type())) {
- // If it's a bool type, then we wrap it with the __nonzero__ slot
- // method.
- def._answer_location = "nb_bool";
- def._wrapper_type = WT_inquiry;
- return true;
- } else if (TypeManager::is_integer(remap->_return_type->get_orig_type())) {
- // An integer type.
- def._answer_location = "nb_int";
- def._wrapper_type = WT_no_params;
- return true;
- } else if (TypeManager::is_float(remap->_return_type->get_orig_type())) {
- // A floating-point (or double) type.
- def._answer_location = "nb_float";
- def._wrapper_type = WT_no_params;
- return true;
- } else if (remap->_return_type->new_type_is_atomic_string()) {
- // A string type.
- def._answer_location = "tp_str";
- def._wrapper_type = WT_no_params;
- return true;
- }
- }
- return false;
- }
- /**
- * Determines whether the slot occurs in the map of slotted functions, and if
- * so, writes out a pointer to its wrapper. If not, writes out def (usually
- * 0).
- */
- void InterfaceMakerPythonNative::
- write_function_slot(ostream &out, int indent_level, const SlottedFunctions &slots,
- const string &slot, const string &default_) {
- SlottedFunctions::const_iterator rfi = slots.find(slot);
- if (rfi == slots.end()) {
- indent(out, indent_level) << default_ << ",";
- if (default_ == "0") {
- out << " // " << slot;
- }
- out << "\n";
- return;
- }
- const SlottedFunctionDef &def = rfi->second;
- // Add an #ifdef if there is a specific version requirement on this
- // function.
- if (def._min_version > 0) {
- out << "#if PY_VERSION_HEX >= 0x" << hex << def._min_version << dec << "\n";
- }
- indent(out, indent_level) << "&" << def._wrapper_name << ",\n";
- if (def._min_version > 0) {
- out << "#else\n";
- indent(out, indent_level) << default_ << ",\n";
- out << "#endif\n";
- }
- }
- void InterfaceMakerPythonNative::
- get_valid_child_classes(std::map<std::string, CastDetails> &answer, CPPStructType *inclass, const std::string &upcast_seed, bool can_downcast) {
- if (inclass == nullptr) {
- return;
- }
- for (const CPPStructType::Base &base : inclass->_derivation) {
- // if (base._vis <= V_public) can_downcast = false;
- CPPStructType *base_type = TypeManager::resolve_type(base._base)->as_struct_type();
- if (base_type != nullptr) {
- std::string scoped_name = base_type->get_local_name(&parser);
- if (answer.find(scoped_name) == answer.end()) {
- answer[scoped_name]._can_downcast = can_downcast;
- answer[scoped_name]._to_class_name = scoped_name;
- answer[scoped_name]._structType = base_type;
- if (base._is_virtual) {
- answer[scoped_name]._can_downcast = false;
- }
- std::string local_upcast("(");
- local_upcast += scoped_name + " *)"+ upcast_seed +"";
- answer[scoped_name]._up_cast_string = local_upcast;
- answer[scoped_name]._is_legal_py_class = is_cpp_type_legal(base_type);
- } else {
- answer[scoped_name]._can_downcast = false;
- }
- get_valid_child_classes(answer, base_type, answer[scoped_name]._up_cast_string, answer[scoped_name]._can_downcast);
- }
- }
- }
- /**
- */
- void InterfaceMakerPythonNative::
- write_python_instance(ostream &out, int indent_level, const string &return_expr,
- bool owns_memory, const InterrogateType &itype, bool is_const) {
- out << std::boolalpha;
- if (!isExportThisRun(itype._cpptype)) {
- _external_imports.insert(TypeManager::resolve_type(itype._cpptype));
- }
- string class_name = itype.get_scoped_name();
- // We don't handle final classes via DTool_CreatePyInstanceTyped since we
- // know it can't be of a subclass type, so we don't need to do the downcast.
- CPPStructType *struct_type = itype._cpptype->as_struct_type();
- if (IsPandaTypedObject(struct_type) && !struct_type->is_final()) {
- // We can't let DTool_CreatePyInstanceTyped do the NULL check since we
- // will be grabbing the type index (which would obviously crash when
- // called on a NULL pointer), so we do it here.
- indent(out, indent_level)
- << "if (" << return_expr << " == nullptr) {\n";
- indent(out, indent_level)
- << " Py_INCREF(Py_None);\n";
- indent(out, indent_level)
- << " return Py_None;\n";
- indent(out, indent_level)
- << "} else {\n";
- // Special exception if we are returning TypedWritable, which might
- // actually be a derived class that inherits from ReferenceCount.
- if (!owns_memory && !is_const && class_name == "TypedWritable") {
- indent(out, indent_level)
- << " ReferenceCount *rc = " << return_expr << "->as_reference_count();\n";
- indent(out, indent_level)
- << " bool is_refcount = (rc != nullptr);\n";
- indent(out, indent_level)
- << " if (is_refcount) {\n";
- indent(out, indent_level)
- << " rc->ref();\n";
- indent(out, indent_level)
- << " }\n";
- indent(out, indent_level)
- << " return DTool_CreatePyInstanceTyped((void *)" << return_expr
- << ", *Dtool_Ptr_" << make_safe_name(class_name) << ", is_refcount, "
- << is_const << ", " << return_expr
- << "->get_type_index());\n";
- } else {
- indent(out, indent_level)
- << " return DTool_CreatePyInstanceTyped((void *)" << return_expr
- << ", *Dtool_Ptr_" << make_safe_name(class_name) << ", "
- << owns_memory << ", " << is_const << ", "
- << return_expr << "->as_typed_object()->get_type_index());\n";
- }
- indent(out, indent_level)
- << "}\n";
- } else {
- // DTool_CreatePyInstance will do the NULL check.
- indent(out, indent_level)
- << "return "
- << "DTool_CreatePyInstance((void *)" << return_expr << ", "
- << "*Dtool_Ptr_" << make_safe_name(class_name) << ", "
- << owns_memory << ", " << is_const << ");\n";
- }
- }
- /**
- *
- */
- InterfaceMakerPythonNative::
- InterfaceMakerPythonNative(InterrogateModuleDef *def) :
- InterfaceMakerPython(def)
- {
- }
- /**
- *
- */
- InterfaceMakerPythonNative::
- ~InterfaceMakerPythonNative() {
- }
- /**
- * Generates the list of function prototypes corresponding to the functions
- * that will be output in write_functions().
- */
- void InterfaceMakerPythonNative::
- write_prototypes(ostream &out_code, ostream *out_h) {
- if (out_h != nullptr) {
- *out_h << "#include \"py_panda.h\"\n\n";
- }
- /*
- for (Function *func : _functions) {
- if (!func->_itype.is_global() && is_function_legal(func)) {
- write_prototype_for(out_code, func);
- }
- }
- */
- Objects::iterator oi;
- for (oi = _objects.begin(); oi != _objects.end(); ++oi) {
- Object *object = (*oi).second;
- if (object->_itype.is_class() || object->_itype.is_struct()) {
- if (is_cpp_type_legal(object->_itype._cpptype)) {
- if (isExportThisRun(object->_itype._cpptype)) {
- write_prototypes_class(out_code, out_h, object);
- } else {
- // write_prototypes_class_external(out_code, object);
- // _external_imports.insert(object->_itype._cpptype);
- }
- }
- } else if (object->_itype.is_scoped_enum() && isExportThisRun(object->_itype._cpptype)) {
- // Forward declare where we will put the scoped enum type.
- string class_name = object->_itype._cpptype->get_local_name(&parser);
- string safe_name = make_safe_name(class_name);
- out_code << "static PyTypeObject *Dtool_Ptr_" << safe_name << " = nullptr;\n";
- }
- }
- out_code << "/**\n";
- out_code << " * Declarations for exported classes\n";
- out_code << " */\n";
- out_code << "static const Dtool_TypeDef exports[] = {\n";
- for (oi = _objects.begin(); oi != _objects.end(); ++oi) {
- Object *object = (*oi).second;
- if (object->_itype.is_class() || object->_itype.is_struct()) {
- CPPType *type = object->_itype._cpptype;
- if (isExportThisRun(type) && is_cpp_type_legal(type)) {
- string class_name = type->get_local_name(&parser);
- string safe_name = make_safe_name(class_name);
- out_code << " {\"" << class_name << "\", &Dtool_" << safe_name << "},\n";
- }
- }
- }
- out_code << " {nullptr, nullptr},\n";
- out_code << "};\n\n";
- out_code << "/**\n";
- out_code << " * Extern declarations for imported classes\n";
- out_code << " */\n";
- // Write out a table of the externally imported types that will be filled in
- // upon module initialization.
- if (!_external_imports.empty()) {
- out_code << "#ifndef LINK_ALL_STATIC\n";
- out_code << "static Dtool_TypeDef imports[] = {\n";
- int idx = 0;
- for (CPPType *type : _external_imports) {
- string class_name = type->get_local_name(&parser);
- string safe_name = make_safe_name(class_name);
- out_code << " {\"" << class_name << "\", nullptr},\n";
- out_code << "#define Dtool_Ptr_" << safe_name << " (imports[" << idx << "].type)\n";
- ++idx;
- }
- out_code << " {nullptr, nullptr},\n";
- out_code << "};\n";
- out_code << "#endif\n\n";
- }
- for (CPPType *type : _external_imports) {
- string class_name = type->get_local_name(&parser);
- string safe_name = make_safe_name(class_name);
- out_code << "// " << class_name << "\n";
- out_code << "#ifndef LINK_ALL_STATIC\n";
- // out_code << "IMPORT_THIS struct Dtool_PyTypedObject Dtool_" <<
- // safe_name << ";\n";
- //if (has_get_class_type_function(type)) {
- // out_code << "static struct Dtool_PyTypedObject *Dtool_Ptr_" << safe_name << ";\n";
- //}
- // out_code << "#define Dtool_Ptr_" << safe_name << " &Dtool_" <<
- // safe_name << "\n"; out_code << "IMPORT_THIS void
- // Dtool_PyModuleClassInit_" << safe_name << "(PyObject *module);\n";
- // This is some really ugly code, because we have to store a pointer with
- // a function of a signature that differs from class to class. If someone
- // can think of an elegant way to do this without sacrificing perf, let me
- // know.
- int has_coerce = has_coerce_constructor(type->as_struct_type());
- if (has_coerce > 0) {
- if (TypeManager::is_reference_count(type)) {
- out_code
- << "inline static bool Dtool_ConstCoerce_" << safe_name << "(PyObject *args, CPT(" << class_name << ") &coerced) {\n"
- << " nassertr(Dtool_Ptr_" << safe_name << " != nullptr, false);\n"
- << " nassertr(Dtool_Ptr_" << safe_name << "->_Dtool_ConstCoerce != nullptr, false);\n"
- << " return ((bool (*)(PyObject *, CPT(" << class_name << ") &))Dtool_Ptr_" << safe_name << "->_Dtool_ConstCoerce)(args, coerced);\n"
- << "}\n";
- if (has_coerce > 1) {
- out_code
- << "inline static bool Dtool_Coerce_" << safe_name << "(PyObject *args, PT(" << class_name << ") &coerced) {\n"
- << " nassertr(Dtool_Ptr_" << safe_name << " != nullptr, false);\n"
- << " nassertr(Dtool_Ptr_" << safe_name << "->_Dtool_Coerce != nullptr, false);\n"
- << " return ((bool (*)(PyObject *, PT(" << class_name << ") &))Dtool_Ptr_" << safe_name << "->_Dtool_Coerce)(args, coerced);\n"
- << "}\n";
- }
- } else {
- out_code
- << "inline static " << class_name << " *Dtool_Coerce_" << safe_name << "(PyObject *args, " << class_name << " &coerced) {\n"
- << " nassertr(Dtool_Ptr_" << safe_name << " != nullptr, nullptr);\n"
- << " nassertr(Dtool_Ptr_" << safe_name << "->_Dtool_Coerce != nullptr, nullptr);\n"
- << " return ((" << class_name << " *(*)(PyObject *, " << class_name << " &))Dtool_Ptr_" << safe_name << "->_Dtool_Coerce)(args, coerced);\n"
- << "}\n";
- }
- }
- out_code << "#else\n";
- out_code << "extern struct Dtool_PyTypedObject Dtool_" << safe_name << ";\n";
- out_code << "static struct Dtool_PyTypedObject *const Dtool_Ptr_" << safe_name << " = &Dtool_" << safe_name << ";\n";
- if (has_coerce > 0) {
- if (TypeManager::is_reference_count(type)) {
- out_code << "extern bool Dtool_ConstCoerce_" << safe_name << "(PyObject *args, CPT(" << class_name << ") &coerced);\n";
- if (has_coerce > 1) {
- out_code << "extern bool Dtool_Coerce_" << safe_name << "(PyObject *args, PT(" << class_name << ") &coerced);\n";
- }
- } else {
- out_code << "extern " << class_name << " *Dtool_Coerce_" << safe_name << "(PyObject *args, " << class_name << " &coerced);\n";
- }
- }
- out_code << "#endif\n";
- }
- }
- /**
- * Output enough enformation to a declartion of a externally generated dtool
- * type object
- */
- void InterfaceMakerPythonNative::
- write_prototypes_class_external(ostream &out, Object *obj) {
- std::string class_name = make_safe_name(obj->_itype.get_scoped_name());
- std::string c_class_name = obj->_itype.get_true_name();
- std::string preferred_name = obj->_itype.get_name();
- out << "/**\n";
- out << " * Forward declaration of class " << class_name << "\n";
- out << " */\n";
- // This typedef is necessary for class templates since we can't pass a comma
- // to a macro function.
- out << "typedef " << c_class_name << " " << class_name << "_localtype;\n";
- out << "Define_Module_Class_Forward(" << _def->module_name << ", " << class_name << ", " << class_name << "_localtype, " << classNameFromCppName(preferred_name, false) << ");\n";
- }
- /**
- */
- void InterfaceMakerPythonNative::
- write_prototypes_class(ostream &out_code, ostream *out_h, Object *obj) {
- std::string ClassName = make_safe_name(obj->_itype.get_scoped_name());
- out_code << "/**\n";
- out_code << " * Forward declarations for top-level class " << ClassName << "\n";
- out_code << " */\n";
- /*
- for (Function *func : obj->_methods) {
- write_prototype_for(out_code, func);
- }
- */
- /*
- for (Function *func : obj->_constructors) {
- std::string fname = "int Dtool_Init_" + ClassName + "(PyObject *self, PyObject *args, PyObject *kwds)";
- write_prototype_for_name(out_code, obj, func, fname);
- }
- */
- write_class_declarations(out_code, out_h, obj);
- }
- /**
- * Generates the list of functions that are appropriate for this interface.
- * This function is called *before* write_prototypes(), above.
- */
- void InterfaceMakerPythonNative::
- write_functions(ostream &out) {
- out << "/**\n";
- out << " * Python wrappers for global functions\n" ;
- out << " */\n";
- FunctionsByIndex::iterator fi;
- for (fi = _functions.begin(); fi != _functions.end(); ++fi) {
- Function *func = (*fi).second;
- if (!func->_itype.is_global() && is_function_legal(func)) {
- write_function_for_top(out, nullptr, func);
- }
- }
- Objects::iterator oi;
- for (oi = _objects.begin(); oi != _objects.end(); ++oi) {
- Object *object = (*oi).second;
- if (object->_itype.is_class() || object->_itype.is_struct()) {
- if (is_cpp_type_legal(object->_itype._cpptype)) {
- if (isExportThisRun(object->_itype._cpptype)) {
- write_class_details(out, object);
- }
- }
- }
- }
- // Objects::iterator oi;
- for (oi = _objects.begin(); oi != _objects.end(); ++oi) {
- Object *object = (*oi).second;
- if (!object->_itype.get_outer_class()) {
- if (object->_itype.is_class() || object->_itype.is_struct()) {
- if (is_cpp_type_legal(object->_itype._cpptype)) {
- if (isExportThisRun(object->_itype._cpptype)) {
- write_module_class(out, object);
- }
- }
- }
- }
- }
- }
- /**
- * Writes out all of the wrapper methods necessary to export the given object.
- * This is called by write_functions.
- */
- void InterfaceMakerPythonNative::
- write_class_details(ostream &out, Object *obj) {
- // std::string cClassName = obj->_itype.get_scoped_name();
- std::string ClassName = make_safe_name(obj->_itype.get_scoped_name());
- std::string cClassName = obj->_itype.get_true_name();
- out << "/**\n";
- out << " * Python wrappers for functions of class " << cClassName << "\n" ;
- out << " */\n";
- // First write out all the wrapper functions for the methods.
- for (Function *func : obj->_methods) {
- if (func) {
- // Write the definition of the generic wrapper function for this
- // function.
- write_function_for_top(out, obj, func);
- }
- }
- // Now write out generated getters and setters for the properties.
- for (Property *property : obj->_properties) {
- write_getset(out, obj, property);
- }
- // Write the constructors.
- std::string fname = "static int Dtool_Init_" + ClassName + "(PyObject *self, PyObject *args, PyObject *kwds)";
- for (Function *func : obj->_constructors) {
- string expected_params;
- write_function_for_name(out, obj, func->_remaps, fname, expected_params, true, AT_keyword_args, RF_int);
- }
- if (obj->_constructors.size() == 0) {
- // We still need to write a dummy constructor to prevent inheriting the
- // constructor from a base class.
- out << fname << " {\n"
- " Dtool_Raise_TypeError(\"cannot init abstract class\");\n"
- " return -1;\n"
- "}\n\n";
- }
- CPPType *cpptype = TypeManager::resolve_type(obj->_itype._cpptype);
- // If we have "coercion constructors", write a single wrapper to consolidate
- // those.
- int has_coerce = has_coerce_constructor(cpptype->as_struct_type());
- if (has_coerce > 0) {
- write_coerce_constructor(out, obj, true);
- if (has_coerce > 1 && TypeManager::is_reference_count(obj->_itype._cpptype)) {
- write_coerce_constructor(out, obj, false);
- }
- }
- // Write make seqs: generated methods that return a sequence of items.
- for (MakeSeq *make_seq : obj->_make_seqs) {
- if (is_function_legal(make_seq->_length_getter) &&
- is_function_legal(make_seq->_element_getter)) {
- write_make_seq(out, obj, ClassName, cClassName, make_seq);
- } else {
- if (!is_function_legal(make_seq->_length_getter)) {
- std::cerr << "illegal length function for MAKE_SEQ: " << make_seq->_length_getter->_name << "\n";
- }
- if (!is_function_legal(make_seq->_element_getter)) {
- std::cerr << "illegal element function for MAKE_SEQ: " << make_seq->_element_getter->_name << "\n";
- }
- }
- }
- // Determine which external imports we will need.
- std::map<string, CastDetails> details;
- std::map<string, CastDetails>::iterator di;
- builder.get_type(TypeManager::unwrap(cpptype), false);
- get_valid_child_classes(details, cpptype->as_struct_type());
- for (di = details.begin(); di != details.end(); di++) {
- // InterrogateType ptype =idb->get_type(di->first);
- if (di->second._is_legal_py_class && !isExportThisRun(di->second._structType)) {
- _external_imports.insert(TypeManager::resolve_type(di->second._structType));
- }
- // out << "IMPORT_THIS struct Dtool_PyTypedObject Dtool_" <<
- // make_safe_name(di->second._to_class_name) << ";\n";
- }
- // Write support methods to cast from and to pointers of this type.
- {
- out << "static void *Dtool_UpcastInterface_" << ClassName << "(PyObject *self, Dtool_PyTypedObject *requested_type) {\n";
- out << " Dtool_PyTypedObject *type = DtoolInstance_TYPE(self);\n";
- out << " if (type != &Dtool_" << ClassName << ") {\n";
- out << " printf(\"" << ClassName << " ** Bad Source Type-- Requesting Conversion from %s to %s\\n\", Py_TYPE(self)->tp_name, requested_type->_PyType.tp_name); fflush(nullptr);\n";;
- out << " return nullptr;\n";
- out << " }\n";
- out << "\n";
- out << " " << cClassName << " *local_this = (" << cClassName << " *)DtoolInstance_VOID_PTR(self);\n";
- out << " if (requested_type == &Dtool_" << ClassName << ") {\n";
- out << " return local_this;\n";
- out << " }\n";
- for (di = details.begin(); di != details.end(); di++) {
- if (di->second._is_legal_py_class) {
- out << " if (requested_type == Dtool_Ptr_" << make_safe_name(di->second._to_class_name) << ") {\n";
- out << " return " << di->second._up_cast_string << " local_this;\n";
- out << " }\n";
- }
- }
- // Are there any implicit cast operators that can cast this object to our
- // desired pointer?
- for (Function *func : obj->_methods) {
- for (FunctionRemap *remap : func->_remaps) {
- if (remap->_type == FunctionRemap::T_typecast_method &&
- is_remap_legal(remap) &&
- !remap->_return_type->return_value_needs_management() &&
- (remap->_cppfunc->_storage_class & CPPInstance::SC_explicit) == 0 &&
- TypeManager::is_pointer(remap->_return_type->get_new_type())) {
- CPPType *cast_type = remap->_return_type->get_orig_type();
- CPPType *obj_type = TypeManager::unwrap(TypeManager::resolve_type(remap->_return_type->get_new_type()));
- string return_expr = "(" + cast_type->get_local_name(&parser) + ")*local_this";
- out << " // " << *remap->_cppfunc << "\n";
- out << " if (requested_type == Dtool_Ptr_" << make_safe_name(obj_type->get_local_name(&parser)) << ") {\n";
- out << " return (void *)(" << remap->_return_type->get_return_expr(return_expr) << ");\n";
- out << " }\n";
- }
- }
- }
- out << " return nullptr;\n";
- out << "}\n\n";
- out << "static void *Dtool_DowncastInterface_" << ClassName << "(void *from_this, Dtool_PyTypedObject *from_type) {\n";
- out << " if (from_this == nullptr || from_type == nullptr) {\n";
- out << " return nullptr;\n";
- out << " }\n";
- out << " if (from_type == Dtool_Ptr_" << ClassName << ") {\n";
- out << " return from_this;\n";
- out << " }\n";
- for (di = details.begin(); di != details.end(); di++) {
- if (di->second._can_downcast && di->second._is_legal_py_class) {
- out << " if (from_type == Dtool_Ptr_" << make_safe_name(di->second._to_class_name) << ") {\n";
- out << " " << di->second._to_class_name << "* other_this = (" << di->second._to_class_name << "*)from_this;\n" ;
- out << " return (" << cClassName << "*)other_this;\n";
- out << " }\n";
- }
- }
- out << " return nullptr;\n";
- out << "}\n\n";
- }
- }
- /**
- */
- void InterfaceMakerPythonNative::
- write_class_declarations(ostream &out, ostream *out_h, Object *obj) {
- const InterrogateType &itype = obj->_itype;
- std::string class_name = make_safe_name(obj->_itype.get_scoped_name());
- std::string c_class_name = obj->_itype.get_true_name();
- std::string preferred_name = itype.get_name();
- std::string class_struct_name = std::string(CLASS_PREFIX) + class_name;
- CPPType *type = obj->_itype._cpptype;
- // This typedef is necessary for class templates since we can't pass a comma
- // to a macro function.
- out << "typedef " << c_class_name << " " << class_name << "_localtype;\n";
- if (obj->_itype.has_destructor() ||
- obj->_itype.destructor_is_inherited() ||
- obj->_itype.destructor_is_implicit()) {
- if (TypeManager::is_reference_count(type)) {
- out << "Define_Module_ClassRef";
- } else {
- out << "Define_Module_Class";
- }
- } else {
- if (TypeManager::is_reference_count(type)) {
- out << "Define_Module_ClassRef_Private";
- } else {
- out << "Define_Module_Class_Private";
- }
- }
- out << "(" << _def->module_name << ", " << class_name << ", " << class_name << "_localtype, " << classNameFromCppName(preferred_name, false) << ");\n";
- out << "static struct Dtool_PyTypedObject *const Dtool_Ptr_" << class_name << " = &Dtool_" << class_name << ";\n";
- out << "static void Dtool_PyModuleClassInit_" << class_name << "(PyObject *module);\n";
- int has_coerce = has_coerce_constructor(type->as_struct_type());
- if (has_coerce > 0) {
- if (TypeManager::is_reference_count(type)) {
- out << "bool Dtool_ConstCoerce_" << class_name << "(PyObject *args, CPT(" << c_class_name << ") &coerced);\n";
- if (has_coerce > 1) {
- out << "bool Dtool_Coerce_" << class_name << "(PyObject *args, PT(" << c_class_name << ") &coerced);\n";
- }
- } else {
- out << "" << c_class_name << " *Dtool_Coerce_" << class_name << "(PyObject *args, " << c_class_name << " &coerced);\n";
- }
- }
- out << "\n";
- if (out_h != nullptr) {
- *out_h << "extern \"C\" " << EXPORT_IMPORT_PREFIX << " struct Dtool_PyTypedObject Dtool_" << class_name << ";\n";
- }
- }
- /**
- * Generates whatever additional code is required to support a module file.
- */
- void InterfaceMakerPythonNative::
- write_sub_module(ostream &out, Object *obj) {
- // Object * obj = _objects[_embeded_index] ;
- string class_name = make_safe_name(obj->_itype.get_scoped_name());
- string class_ptr;
- if (!obj->_itype.is_typedef()) {
- out << " // " << *(obj->_itype._cpptype) << "\n";
- out << " Dtool_PyModuleClassInit_" << class_name << "(module);\n";
- class_ptr = "&Dtool_" + class_name;
- } else {
- // Unwrap typedefs.
- TypeIndex wrapped = obj->_itype._wrapped_type;
- while (interrogate_type_is_typedef(wrapped)) {
- wrapped = interrogate_type_wrapped_type(wrapped);
- }
- InterrogateDatabase *idb = InterrogateDatabase::get_ptr();
- const InterrogateType &wrapped_itype = idb->get_type(wrapped);
- class_name = make_safe_name(wrapped_itype.get_scoped_name());
- out << " // typedef " << wrapped_itype.get_scoped_name()
- << " " << *(obj->_itype._cpptype) << "\n";
- if (!isExportThisRun(wrapped_itype._cpptype)) {
- _external_imports.insert(TypeManager::resolve_type(wrapped_itype._cpptype));
- class_ptr = "Dtool_Ptr_" + class_name;
- out << " assert(" << class_ptr << " != nullptr);\n";
- } else {
- class_ptr = "&Dtool_" + class_name;
- // If this is a typedef to a class defined in the same module, make sure
- // that the class is initialized before we try to define the typedef.
- out << " Dtool_PyModuleClassInit_" << class_name << "(module);\n";
- }
- }
- std::string export_class_name = classNameFromCppName(obj->_itype.get_name(), false);
- std::string export_class_name2 = classNameFromCppName(obj->_itype.get_name(), true);
- class_ptr = "(PyObject *)" + class_ptr;
- // Note: PyModule_AddObject steals a reference, so we have to call Py_INCREF
- // for every but the first time we add it to the module.
- if (obj->_itype.is_typedef()) {
- out << " Py_INCREF(" << class_ptr << ");\n";
- }
- out << " PyModule_AddObject(module, \"" << export_class_name << "\", " << class_ptr << ");\n";
- if (export_class_name != export_class_name2) {
- out << " Py_INCREF(Dtool_Ptr_" << class_name << ");\n";
- out << " PyModule_AddObject(module, \"" << export_class_name2 << "\", " << class_ptr << ");\n";
- }
- }
- /**
- */
- void InterfaceMakerPythonNative::
- write_module_support(ostream &out, ostream *out_h, InterrogateModuleDef *def) {
- out << "/**\n";
- out << " * Module Object Linker ..\n";
- out << " */\n";
- Objects::iterator oi;
- out << "void Dtool_" << def->library_name << "_RegisterTypes() {\n"
- " TypeRegistry *registry = TypeRegistry::ptr();\n"
- " nassertv(registry != nullptr);\n";
- for (oi = _objects.begin(); oi != _objects.end(); ++oi) {
- Object *object = (*oi).second;
- if (object->_itype.is_class() || object->_itype.is_struct()) {
- CPPType *type = object->_itype._cpptype;
- if (is_cpp_type_legal(type) && isExportThisRun(type)) {
- string class_name = object->_itype.get_scoped_name();
- string safe_name = make_safe_name(class_name);
- bool is_typed = has_get_class_type_function(type);
- if (is_typed) {
- out << " {\n";
- if (has_init_type_function(type)) {
- // Call the init_type function. This isn't necessary for all
- // types as many of them are automatically initialized at static
- // init type, but for some extension classes it's useful.
- out << " " << type->get_local_name(&parser)
- << "::init_type();\n";
- }
- out << " TypeHandle handle = " << type->get_local_name(&parser)
- << "::get_class_type();\n";
- out << " Dtool_" << safe_name << "._type = handle;\n";
- out << " registry->record_python_type(handle, "
- "(PyObject *)&Dtool_" << safe_name << ");\n";
- out << " }\n";
- } else {
- if (IsPandaTypedObject(type->as_struct_type())) {
- nout << object->_itype.get_scoped_name() << " derives from TypedObject, "
- << "but does not define a get_class_type() function.\n";
- }
- }
- }
- }
- }
- out << "}\n\n";
- out << "void Dtool_" << def->library_name << "_BuildInstants(PyObject *module) {\n";
- out << " (void) module;\n";
- for (oi = _objects.begin(); oi != _objects.end(); ++oi) {
- Object *object = (*oi).second;
- if (object->_itype.is_enum() && !object->_itype.is_nested() &&
- isExportThisRun(object->_itype._cpptype)) {
- int enum_count = object->_itype.number_of_enum_values();
- if (object->_itype.is_scoped_enum()) {
- // Convert as Python 3.4-style enum.
- string class_name = object->_itype._cpptype->get_local_name(&parser);
- string safe_name = make_safe_name(class_name);
- CPPType *underlying_type = TypeManager::unwrap_const(object->_itype._cpptype->as_enum_type()->get_underlying_type());
- string cast_to = underlying_type->get_local_name(&parser);
- out << " // enum class " << object->_itype.get_scoped_name() << "\n";
- out << " {\n";
- out << " PyObject *members = PyTuple_New(" << enum_count << ");\n";
- out << " PyObject *member;\n";
- for (int xx = 0; xx < enum_count; xx++) {
- out << " member = PyTuple_New(2);\n"
- "#if PY_MAJOR_VERSION >= 3\n"
- " PyTuple_SET_ITEM(member, 0, PyUnicode_FromString(\""
- << object->_itype.get_enum_value_name(xx) << "\"));\n"
- "#else\n"
- " PyTuple_SET_ITEM(member, 0, PyString_FromString(\""
- << object->_itype.get_enum_value_name(xx) << "\"));\n"
- "#endif\n"
- " PyTuple_SET_ITEM(member, 1, Dtool_WrapValue(("
- << cast_to << ")" << object->_itype.get_scoped_name() << "::"
- << object->_itype.get_enum_value_name(xx) << "));\n"
- " PyTuple_SET_ITEM(members, " << xx << ", member);\n";
- }
- out << " Dtool_Ptr_" << safe_name << " = Dtool_EnumType_Create(\""
- << object->_itype.get_name() << "\", members, \""
- << _def->module_name << "\");\n";
- out << " PyModule_AddObject(module, \"" << object->_itype.get_name()
- << "\", (PyObject *)Dtool_Ptr_" << safe_name << ");\n";
- out << " }\n";
- } else {
- out << " // enum " << object->_itype.get_scoped_name() << "\n";
- for (int xx = 0; xx < enum_count; xx++) {
- string name1 = classNameFromCppName(object->_itype.get_enum_value_name(xx), false);
- string name2 = classNameFromCppName(object->_itype.get_enum_value_name(xx), true);
- string enum_value = "::" + object->_itype.get_enum_value_name(xx);
- out << " PyModule_AddObject(module, \"" << name1 << "\", Dtool_WrapValue(" << enum_value << "));\n";
- if (name1 != name2) {
- // Also write the mangled name, for historical purposes.
- out << " PyModule_AddObject(module, \"" << name2 << "\", Dtool_WrapValue(" << enum_value << "));\n";
- }
- }
- }
- }
- }
- InterrogateDatabase *idb = InterrogateDatabase::get_ptr();
- int num_manifests = idb->get_num_global_manifests();
- for (int mi = 0; mi < num_manifests; mi++) {
- ManifestIndex manifest_index = idb->get_global_manifest(mi);
- const InterrogateManifest &iman = idb->get_manifest(manifest_index);
- if (iman.has_getter()) {
- FunctionIndex func_index = iman.get_getter();
- record_function(dummy_type, func_index);
- }
- string name1 = classNameFromCppName(iman.get_name(), false);
- string name2 = classNameFromCppName(iman.get_name(), true);
- if (iman.has_int_value()) {
- int value = iman.get_int_value();
- out << " PyModule_AddIntConstant(module, \"" << name1 << "\", " << value << ");\n";
- if (name1 != name2) {
- // Also write the mangled name, for historical purposes.
- out << " PyModule_AddIntConstant(module, \"" << name2 << "\", " << value << ");\n";
- }
- } else {
- string value = iman.get_definition();
- out << " PyModule_AddStringConstant(module, \"" << name1 << "\", \"" << value << "\");\n";
- if (name1 != name2) {
- out << " PyModule_AddStringConstant(module, \"" << name2 << "\", \"" << value << "\");\n";
- }
- }
- }
- for (oi = _objects.begin(); oi != _objects.end(); ++oi) {
- Object *object = (*oi).second;
- if (!object->_itype.get_outer_class()) {
- if (object->_itype.is_class() ||
- object->_itype.is_struct() ||
- object->_itype.is_typedef()) {
- if (is_cpp_type_legal(object->_itype._cpptype)) {
- if (isExportThisRun(object->_itype._cpptype)) {
- write_sub_module(out, object);
- }
- }
- }
- }
- }
- out << "}\n\n";
- bool force_base_functions = true;
- out << "static PyMethodDef python_simple_funcs[] = {\n";
- FunctionsByIndex::iterator fi;
- for (fi = _functions.begin(); fi != _functions.end(); ++fi) {
- Function *func = (*fi).second;
- if (!func->_itype.is_global() && is_function_legal(func)) {
- string name1 = methodNameFromCppName(func, "", false);
- string name2 = methodNameFromCppName(func, "", true);
- string flags;
- string fptr = "&" + func->_name;
- switch (func->_args_type) {
- case AT_keyword_args:
- flags = "METH_VARARGS | METH_KEYWORDS";
- fptr = "(PyCFunction) " + fptr;
- break;
- case AT_varargs:
- flags = "METH_VARARGS";
- break;
- case AT_single_arg:
- flags = "METH_O";
- break;
- default:
- flags = "METH_NOARGS";
- break;
- }
- // Note: we shouldn't add METH_STATIC here, since both METH_STATIC and
- // METH_CLASS are illegal for module-level functions.
- out << " {\"" << name1 << "\", " << fptr
- << ", " << flags << ", (const char *)" << func->_name << "_comment},\n";
- if (name1 != name2) {
- out << " {\"" << name2 << "\", " << fptr
- << ", " << flags << ", (const char *)" << func->_name << "_comment},\n";
- }
- }
- }
- if (force_base_functions) {
- out << " // Support Function For Dtool_types ... for now in each module ??\n";
- out << " {\"Dtool_BorrowThisReference\", &Dtool_BorrowThisReference, METH_VARARGS, \"Used to borrow 'this' pointer (to, from)\\nAssumes no ownership.\"},\n";
- //out << " {\"Dtool_AddToDictionary\", &Dtool_AddToDictionary, METH_VARARGS, \"Used to add items into a tp_dict\"},\n";
- }
- out << " {nullptr, nullptr, 0, nullptr}\n" << "};\n\n";
- out << "extern const struct LibraryDef " << def->library_name << "_moddef = {python_simple_funcs, exports, ";
- if (_external_imports.empty()) {
- out << "nullptr};\n";
- } else {
- out << "imports};\n";
- }
- if (out_h != nullptr) {
- *out_h << "extern const struct LibraryDef " << def->library_name << "_moddef;\n";
- }
- }
- /**
- */
- void InterfaceMakerPythonNative::
- write_module(ostream &out, ostream *out_h, InterrogateModuleDef *def) {
- InterfaceMakerPython::write_module(out, out_h, def);
- Objects::iterator oi;
- out << "/**\n";
- out << " * Module initialization functions for Python module \"" << def->module_name << "\"\n";
- out << " */\n";
- out << "#if PY_MAJOR_VERSION >= 3\n"
- << "static struct PyModuleDef python_native_module = {\n"
- << " PyModuleDef_HEAD_INIT,\n"
- << " \"" << def->module_name << "\",\n"
- << " nullptr,\n"
- << " -1,\n"
- << " nullptr,\n"
- << " nullptr, nullptr, nullptr, nullptr\n"
- << "};\n"
- << "\n"
- << "#ifdef _WIN32\n"
- << "extern \"C\" __declspec(dllexport) PyObject *PyInit_" << def->module_name << "();\n"
- << "#elif __GNUC__ >= 4\n"
- << "extern \"C\" __attribute__((visibility(\"default\"))) PyObject *PyInit_" << def->module_name << "();\n"
- << "#else\n"
- << "extern \"C\" PyObject *PyInit_" << def->module_name << "();\n"
- << "#endif\n"
- << "\n"
- << "PyObject *PyInit_" << def->module_name << "() {\n"
- << " LibraryDef *refs[] = {&" << def->library_name << "_moddef, nullptr};\n"
- << " PyObject *module = Dtool_PyModuleInitHelper(refs, &python_native_module);\n"
- << " Dtool_" << def->library_name << "_BuildInstants(module);\n"
- << " return module;\n"
- << "}\n"
- << "\n"
- << "#else // Python 2 case\n"
- << "\n"
- << "#ifdef _WIN32\n"
- << "extern \"C\" __declspec(dllexport) void init" << def->module_name << "();\n"
- << "#elif __GNUC__ >= 4\n"
- << "extern \"C\" __attribute__((visibility(\"default\"))) void init" << def->module_name << "();\n"
- << "#else\n"
- << "extern \"C\" void init" << def->module_name << "();\n"
- << "#endif\n"
- << "\n"
- << "void init" << def->module_name << "() {\n"
- << " LibraryDef *refs[] = {&" << def->library_name << "_moddef, nullptr};\n"
- << " PyObject *module = Dtool_PyModuleInitHelper(refs, \"" << def->module_name << "\");\n"
- << " Dtool_" << def->library_name << "_BuildInstants(module);\n"
- << "}\n"
- << "\n"
- << "#endif\n"
- << "\n";
- }
- /**
- */
- void InterfaceMakerPythonNative::
- write_module_class(ostream &out, Object *obj) {
- bool has_local_repr = false;
- bool has_local_str = false;
- bool has_local_richcompare = false;
- bool has_local_getbuffer = false;
- {
- int num_nested = obj->_itype.number_of_nested_types();
- for (int ni = 0; ni < num_nested; ni++) {
- TypeIndex nested_index = obj->_itype.get_nested_type(ni);
- if (_objects.count(nested_index) == 0) {
- // Illegal type.
- continue;
- }
- Object *nested_obj = _objects[nested_index];
- assert(nested_obj != nullptr);
- if (nested_obj->_itype.is_class() || nested_obj->_itype.is_struct()) {
- write_module_class(out, nested_obj);
- }
- }
- }
- InterrogateDatabase *idb = InterrogateDatabase::get_ptr();
- std::string ClassName = make_safe_name(obj->_itype.get_scoped_name());
- std::string cClassName = obj->_itype.get_true_name();
- std::string export_class_name = classNameFromCppName(obj->_itype.get_name(), false);
- bool is_runtime_typed = IsPandaTypedObject(obj->_itype._cpptype->as_struct_type());
- if (!is_runtime_typed && has_get_class_type_function(obj->_itype._cpptype)) {
- is_runtime_typed = true;
- }
- out << "/**\n";
- out << " * Python method tables for " << ClassName << " (" << export_class_name << ")\n" ;
- out << " */\n";
- out << "static PyMethodDef Dtool_Methods_" << ClassName << "[] = {\n";
- SlottedFunctions slots;
- // function Table
- bool got_copy = false;
- bool got_deepcopy = false;
- for (Function *func : obj->_methods) {
- if (func->_name == "__copy__") {
- got_copy = true;
- } else if (func->_name == "__deepcopy__") {
- got_deepcopy = true;
- }
- string name1 = methodNameFromCppName(func, export_class_name, false);
- string name2 = methodNameFromCppName(func, export_class_name, true);
- string flags;
- string fptr = "&" + func->_name;
- switch (func->_args_type) {
- case AT_keyword_args:
- flags = "METH_VARARGS | METH_KEYWORDS";
- fptr = "(PyCFunction) " + fptr;
- break;
- case AT_varargs:
- flags = "METH_VARARGS";
- break;
- case AT_single_arg:
- flags = "METH_O";
- break;
- default:
- flags = "METH_NOARGS";
- break;
- }
- if (!func->_has_this) {
- flags += " | METH_STATIC";
- }
- bool has_nonslotted = false;
- for (FunctionRemap *remap : func->_remaps) {
- if (!is_remap_legal(remap)) {
- continue;
- }
- SlottedFunctionDef slotted_def;
- if (get_slotted_function_def(obj, func, remap, slotted_def)) {
- const string &key = slotted_def._answer_location;
- if (slotted_def._wrapper_type == WT_none) {
- slotted_def._wrapper_name = func->_name;
- } else {
- slotted_def._wrapper_name = func->_name + "_" + key;
- }
- if (slots.count(key)) {
- slots[key]._remaps.insert(remap);
- } else {
- slots[key] = slotted_def;
- slots[key]._remaps.insert(remap);
- }
- if (slotted_def._keep_method) {
- has_nonslotted = true;
- }
- // Python 3 doesn't support nb_divide. It has nb_true_divide and also
- // nb_floor_divide, but they have different semantics than in C++.
- // Ugh. Make special slots to store the nb_divide members that take a
- // float. We'll use this to build up nb_true_divide, so that we can
- // still properly divide float vector types.
- if (remap->_flags & FunctionRemap::F_divide_float) {
- string true_key;
- if (key == "nb_inplace_divide") {
- true_key = "nb_inplace_true_divide";
- } else {
- true_key = "nb_true_divide";
- }
- if (slots.count(true_key) == 0) {
- SlottedFunctionDef def;
- def._answer_location = true_key;
- def._wrapper_type = slotted_def._wrapper_type;
- def._min_version = 0x03000000;
- def._wrapper_name = func->_name + "_" + true_key;
- slots[true_key] = def;
- }
- slots[true_key]._remaps.insert(remap);
- }
- } else {
- has_nonslotted = true;
- }
- }
- if (has_nonslotted) {
- // This is a bit of a hack, as these methods should probably be going
- // through the slotted function system. But it's kind of pointless to
- // write these out, and a waste of space.
- string fname = func->_ifunc.get_name();
- if (fname == "operator <" ||
- fname == "operator <=" ||
- fname == "operator ==" ||
- fname == "operator !=" ||
- fname == "operator >" ||
- fname == "operator >=") {
- continue;
- }
- // This method has non-slotted remaps, so write it out into the function
- // table.
- out << " {\"" << name1 << "\", " << fptr
- << ", " << flags << ", (const char *)" << func->_name << "_comment},\n";
- if (name1 != name2) {
- out << " {\"" << name2 << "\", " << fptr
- << ", " << flags << ", (const char *)" << func->_name << "_comment},\n";
- }
- }
- }
- if (obj->_protocol_types & Object::PT_make_copy) {
- if (!got_copy) {
- out << " {\"__copy__\", ©_from_make_copy, METH_NOARGS, nullptr},\n";
- got_copy = true;
- }
- } else if (obj->_protocol_types & Object::PT_copy_constructor) {
- if (!got_copy) {
- out << " {\"__copy__\", ©_from_copy_constructor, METH_NOARGS, nullptr},\n";
- got_copy = true;
- }
- }
- if (got_copy && !got_deepcopy) {
- out << " {\"__deepcopy__\", &map_deepcopy_to_copy, METH_VARARGS, nullptr},\n";
- }
- for (MakeSeq *make_seq : obj->_make_seqs) {
- if (!is_function_legal(make_seq->_length_getter) ||
- !is_function_legal(make_seq->_element_getter)) {
- continue;
- }
- string seq_name = make_seq->_imake_seq.get_name();
- string flags = "METH_NOARGS";
- if (!make_seq->_length_getter->_has_this &&
- !make_seq->_element_getter->_has_this) {
- flags += " | METH_STATIC";
- }
- string name1 = methodNameFromCppName(seq_name, export_class_name, false);
- string name2 = methodNameFromCppName(seq_name, export_class_name, true);
- out << " {\"" << name1
- << "\", (PyCFunction) &" << make_seq->_name << ", " << flags << ", nullptr},\n";
- if (name1 != name2) {
- out << " { \"" << name2
- << "\", (PyCFunction) &" << make_seq->_name << ", " << flags << ", nullptr},\n";
- }
- }
- out << " {nullptr, nullptr, 0, nullptr}\n"
- << "};\n\n";
- int num_derivations = obj->_itype.number_of_derivations();
- int di;
- for (di = 0; di < num_derivations; di++) {
- TypeIndex d_type_Index = obj->_itype.get_derivation(di);
- if (!interrogate_type_is_unpublished(d_type_Index)) {
- const InterrogateType &d_itype = idb->get_type(d_type_Index);
- if (is_cpp_type_legal(d_itype._cpptype)) {
- if (!isExportThisRun(d_itype._cpptype)) {
- _external_imports.insert(TypeManager::resolve_type(d_itype._cpptype));
- // out << "IMPORT_THIS struct Dtool_PyTypedObject Dtool_" <<
- // make_safe_name(d_itype.get_scoped_name().c_str()) << ";\n";
- }
- }
- }
- }
- std::vector<CPPType*> bases;
- for (di = 0; di < num_derivations; di++) {
- TypeIndex d_type_Index = obj->_itype.get_derivation(di);
- if (!interrogate_type_is_unpublished(d_type_Index)) {
- const InterrogateType &d_itype = idb->get_type(d_type_Index);
- if (is_cpp_type_legal(d_itype._cpptype)) {
- bases.push_back(d_itype._cpptype);
- }
- }
- }
- {
- SlottedFunctions::iterator rfi;
- for (rfi = slots.begin(); rfi != slots.end(); rfi++) {
- const SlottedFunctionDef &def = rfi->second;
- // This is just for reporting. There might be remaps from multiple
- // functions with different names mapped to the same slot.
- string fname;
- if (def._remaps.size() > 0) {
- const FunctionRemap *first_remap = *def._remaps.begin();
- fname = first_remap->_cppfunc->get_simple_name();
- }
- if (def._min_version > 0) {
- out << "#if PY_VERSION_HEX >= 0x" << hex << def._min_version << dec << "\n";
- }
- switch (rfi->second._wrapper_type) {
- case WT_no_params:
- case WT_iter_next:
- // PyObject *func(PyObject *self)
- {
- out << "//////////////////\n";
- out << "// A wrapper function to satisfy Python's internal calling conventions.\n";
- out << "// " << ClassName << " slot " << rfi->second._answer_location << " -> " << fname << "\n";
- out << "//////////////////\n";
- out << "static PyObject *" << def._wrapper_name << "(PyObject *self) {\n";
- out << " " << cClassName << " *local_this = nullptr;\n";
- out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n";
- out << " return nullptr;\n";
- out << " }\n\n";
- int return_flags = RF_pyobject | RF_err_null;
- if (rfi->second._wrapper_type == WT_iter_next) {
- // If the function returns NULL, we should return NULL to indicate
- // a StopIteration, rather than returning None.
- return_flags |= RF_preserve_null;
- }
- string expected_params;
- write_function_forset(out, def._remaps, 0, 0, expected_params, 2, true, true,
- AT_no_args, return_flags, false);
- out << " if (!_PyErr_OCCURRED()) {\n";
- out << " return Dtool_Raise_BadArgumentsError(\n";
- output_quoted(out, 6, expected_params);
- out << ");\n";
- out << " }\n";
- out << " return nullptr;\n";
- out << "}\n\n";
- }
- break;
- case WT_one_param:
- case WT_binary_operator:
- case WT_inplace_binary_operator:
- // PyObject *func(PyObject *self, PyObject *one)
- {
- int return_flags = RF_err_null;
- if (rfi->second._wrapper_type == WT_inplace_binary_operator) {
- return_flags |= RF_self;
- } else {
- return_flags |= RF_pyobject;
- }
- out << "//////////////////\n";
- out << "// A wrapper function to satisfy Python's internal calling conventions.\n";
- out << "// " << ClassName << " slot " << rfi->second._answer_location << " -> " << fname << "\n";
- out << "//////////////////\n";
- out << "static PyObject *" << def._wrapper_name << "(PyObject *self, PyObject *arg) {\n";
- out << " " << cClassName << " *local_this = nullptr;\n";
- if (rfi->second._wrapper_type != WT_one_param) {
- // WT_binary_operator means we must return NotImplemented, instead
- // of raising an exception, if the this pointer doesn't match.
- // This is for things like __sub__, which Python likes to call on
- // the wrong-type objects.
- out << " DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **)&local_this);\n";
- out << " if (local_this == nullptr) {\n";
- out << " Py_INCREF(Py_NotImplemented);\n";
- out << " return Py_NotImplemented;\n";
- } else {
- out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n";
- out << " return nullptr;\n";
- }
- out << " }\n";
- string expected_params;
- write_function_forset(out, def._remaps, 1, 1, expected_params, 2, true, true,
- AT_single_arg, return_flags, false);
- if (rfi->second._wrapper_type != WT_one_param) {
- out << " Py_INCREF(Py_NotImplemented);\n";
- out << " return Py_NotImplemented;\n";
- } else {
- out << " if (!_PyErr_OCCURRED()) {\n";
- out << " return Dtool_Raise_BadArgumentsError(\n";
- output_quoted(out, 6, expected_params);
- out << ");\n";
- out << " }\n";
- out << " return nullptr;\n";
- }
- out << "}\n\n";
- }
- break;
- case WT_setattr:
- // int func(PyObject *self, PyObject *one, PyObject *two = NULL)
- {
- out << "//////////////////\n";
- out << "// A wrapper function to satisfy Python's internal calling conventions.\n";
- out << "// " << ClassName << " slot " << rfi->second._answer_location << " -> " << fname << "\n";
- out << "//////////////////\n";
- out << "static int " << def._wrapper_name << "(PyObject *self, PyObject *arg, PyObject *arg2) {\n";
- out << " " << cClassName << " *local_this = nullptr;\n";
- out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n";
- out << " return -1;\n";
- out << " }\n\n";
- set<FunctionRemap*> setattr_remaps;
- set<FunctionRemap*> delattr_remaps;
- // This function handles both delattr and setattr. Fish out the
- // remaps for both types.
- for (FunctionRemap *remap : def._remaps) {
- if (remap->_cppfunc->get_simple_name() == "__delattr__" && remap->_parameters.size() == 2) {
- delattr_remaps.insert(remap);
- } else if (remap->_cppfunc->get_simple_name() == "__setattr__" && remap->_parameters.size() == 3) {
- setattr_remaps.insert(remap);
- }
- }
- out << " // Determine whether to call __setattr__ or __delattr__.\n";
- out << " if (arg2 != nullptr) { // __setattr__\n";
- if (!setattr_remaps.empty()) {
- out << " PyObject *args = PyTuple_Pack(2, arg, arg2);\n";
- string expected_params;
- write_function_forset(out, setattr_remaps, 2, 2, expected_params, 4,
- true, true, AT_varargs, RF_int | RF_decref_args, true);
- out << " Py_DECREF(args);\n";
- out << " if (!_PyErr_OCCURRED()) {\n";
- out << " Dtool_Raise_BadArgumentsError(\n";
- output_quoted(out, 8, expected_params);
- out << ");\n";
- out << " }\n";
- } else {
- out << " PyErr_Format(PyExc_TypeError,\n";
- out << " \"can't set attributes of built-in/extension type '%s'\",\n";
- out << " Py_TYPE(self)->tp_name);\n";
- }
- out << " return -1;\n\n";
- out << " } else { // __delattr__\n";
- if (!delattr_remaps.empty()) {
- string expected_params;
- write_function_forset(out, delattr_remaps, 1, 1, expected_params, 4,
- true, true, AT_single_arg, RF_int, true);
- out << " if (!_PyErr_OCCURRED()) {\n";
- out << " Dtool_Raise_BadArgumentsError(\n";
- output_quoted(out, 8, expected_params);
- out << ");\n";
- out << " }\n";
- } else {
- out << " PyErr_Format(PyExc_TypeError,\n";
- out << " \"can't delete attributes of built-in/extension type '%s'\",\n";
- out << " Py_TYPE(self)->tp_name);\n";
- }
- out << " return -1;\n";
- out << " }\n";
- out << "}\n\n";
- }
- break;
- case WT_getattr:
- // PyObject *func(PyObject *self, PyObject *one) Specifically to
- // implement __getattr__. First calls PyObject_GenericGetAttr(), and
- // only calls the wrapper if it returns NULL. If one wants to override
- // this completely, one should define __getattribute__ instead.
- {
- out << "//////////////////\n";
- out << "// A wrapper function to satisfy Python's internal calling conventions.\n";
- out << "// " << ClassName << " slot " << rfi->second._answer_location << " -> " << fname << "\n";
- out << "//////////////////\n";
- out << "static PyObject *" << def._wrapper_name << "(PyObject *self, PyObject *arg) {\n";
- out << " PyObject *res = PyObject_GenericGetAttr(self, arg);\n";
- out << " if (res != nullptr) {\n";
- out << " return res;\n";
- out << " }\n";
- out << " if (_PyErr_OCCURRED() != PyExc_AttributeError) {\n";
- out << " return nullptr;\n";
- out << " }\n";
- out << " PyErr_Clear();\n\n";
- out << " " << cClassName << " *local_this = nullptr;\n";
- out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n";
- out << " return nullptr;\n";
- out << " }\n\n";
- string expected_params;
- write_function_forset(out, def._remaps, 1, 1, expected_params, 2,
- true, true, AT_single_arg,
- RF_pyobject | RF_err_null, true);
- // out << " PyErr_Clear();\n";
- out << " return nullptr;\n";
- out << "}\n\n";
- }
- break;
- case WT_sequence_getitem:
- // PyObject *func(PyObject *self, Py_ssize_t index)
- {
- out << "//////////////////\n";
- out << "// A wrapper function to satisfy Python's internal calling conventions.\n";
- out << "// " << ClassName << " slot " << rfi->second._answer_location << " -> " << fname << "\n";
- out << "//////////////////\n";
- out << "static PyObject *" << def._wrapper_name << "(PyObject *self, Py_ssize_t index) {\n";
- out << " " << cClassName << " *local_this = nullptr;\n";
- out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n";
- out << " return nullptr;\n";
- out << " }\n\n";
- // This is a getitem or setitem of a sequence type. This means we
- // *need* to raise IndexError if we're out of bounds. We have to
- // assume the bounds are 0 .. this->size() (this is the same
- // assumption that Python makes).
- out << " if (index < 0 || index >= (Py_ssize_t) local_this->size()) {\n";
- out << " PyErr_SetString(PyExc_IndexError, \"" << ClassName << " index out of range\");\n";
- out << " return nullptr;\n";
- out << " }\n";
- string expected_params;
- write_function_forset(out, def._remaps, 1, 1, expected_params, 2, true, true,
- AT_no_args, RF_pyobject | RF_err_null, false, true, "index");
- out << " if (!_PyErr_OCCURRED()) {\n";
- out << " return Dtool_Raise_BadArgumentsError(\n";
- output_quoted(out, 6, expected_params);
- out << ");\n";
- out << " }\n";
- out << " return nullptr;\n";
- out << "}\n\n";
- }
- break;
- case WT_sequence_setitem:
- // int_t func(PyObject *self, Py_ssize_t index, PyObject *value)
- {
- out << "//////////////////\n";
- out << "// A wrapper function to satisfy Python's internal calling conventions.\n";
- out << "// " << ClassName << " slot " << rfi->second._answer_location << " -> " << fname << "\n";
- out << "//////////////////\n";
- out << "static int " << def._wrapper_name << "(PyObject *self, Py_ssize_t index, PyObject *arg) {\n";
- out << " " << cClassName << " *local_this = nullptr;\n";
- out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n";
- out << " return -1;\n";
- out << " }\n\n";
- out << " if (index < 0 || index >= (Py_ssize_t) local_this->size()) {\n";
- out << " PyErr_SetString(PyExc_IndexError, \"" << ClassName << " index out of range\");\n";
- out << " return -1;\n";
- out << " }\n";
- set<FunctionRemap*> setitem_remaps;
- set<FunctionRemap*> delitem_remaps;
- // This function handles both delitem and setitem. Fish out the
- // remaps for either one.
- for (FunctionRemap *remap : def._remaps) {
- if (remap->_flags & FunctionRemap::F_setitem_int) {
- setitem_remaps.insert(remap);
- } else if (remap->_flags & FunctionRemap::F_delitem_int) {
- delitem_remaps.insert(remap);
- }
- }
- string expected_params;
- out << " if (arg != nullptr) { // __setitem__\n";
- write_function_forset(out, setitem_remaps, 2, 2, expected_params, 4,
- true, true, AT_single_arg, RF_int, false, true, "index");
- out << " } else { // __delitem__\n";
- write_function_forset(out, delitem_remaps, 1, 1, expected_params, 4,
- true, true, AT_single_arg, RF_int, false, true, "index");
- out << " }\n\n";
- out << " if (!_PyErr_OCCURRED()) {\n";
- out << " Dtool_Raise_BadArgumentsError(\n";
- output_quoted(out, 6, expected_params);
- out << ");\n";
- out << " }\n";
- out << " return -1;\n";
- out << "}\n\n";
- }
- break;
- case WT_sequence_size:
- // Py_ssize_t func(PyObject *self)
- {
- out << "//////////////////\n";
- out << "// A wrapper function to satisfy Python's internal calling conventions.\n";
- out << "// " << ClassName << " slot " << rfi->second._answer_location << " -> " << fname << "\n";
- out << "//////////////////\n";
- out << "static Py_ssize_t " << def._wrapper_name << "(PyObject *self) {\n";
- out << " " << cClassName << " *local_this = nullptr;\n";
- out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n";
- out << " return -1;\n";
- out << " }\n\n";
- // This is a cheap cheat around all of the overhead of calling the
- // wrapper function.
- out << " return (Py_ssize_t) local_this->" << fname << "();\n";
- out << "}\n\n";
- }
- break;
- case WT_mapping_setitem:
- // int func(PyObject *self, PyObject *one, PyObject *two)
- {
- out << "//////////////////\n";
- out << "// A wrapper function to satisfy Python's internal calling conventions.\n";
- out << "// " << ClassName << " slot " << rfi->second._answer_location << " -> " << fname << "\n";
- out << "//////////////////\n";
- out << "static int " << def._wrapper_name << "(PyObject *self, PyObject *arg, PyObject *arg2) {\n";
- out << " " << cClassName << " *local_this = nullptr;\n";
- out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n";
- out << " return -1;\n";
- out << " }\n\n";
- set<FunctionRemap*> setitem_remaps;
- set<FunctionRemap*> delitem_remaps;
- // This function handles both delitem and setitem. Fish out the
- // remaps for either one.
- for (FunctionRemap *remap : def._remaps) {
- if (remap->_flags & FunctionRemap::F_setitem) {
- setitem_remaps.insert(remap);
- } else if (remap->_flags & FunctionRemap::F_delitem) {
- delitem_remaps.insert(remap);
- }
- }
- string expected_params;
- out << " if (arg2 != nullptr) { // __setitem__\n";
- out << " PyObject *args = PyTuple_Pack(2, arg, arg2);\n";
- write_function_forset(out, setitem_remaps, 2, 2, expected_params, 4,
- true, true, AT_varargs, RF_int | RF_decref_args, false);
- out << " Py_DECREF(args);\n";
- out << " } else { // __delitem__\n";
- write_function_forset(out, delitem_remaps, 1, 1, expected_params, 4,
- true, true, AT_single_arg, RF_int, false);
- out << " }\n\n";
- out << " if (!_PyErr_OCCURRED()) {\n";
- out << " Dtool_Raise_BadArgumentsError(\n";
- output_quoted(out, 6, expected_params);
- out << ");\n";
- out << " }\n";
- out << " return -1;\n";
- out << "}\n\n";
- }
- break;
- case WT_inquiry:
- // int func(PyObject *self)
- {
- out << "//////////////////\n";
- out << "// A wrapper function to satisfy Python's internal calling conventions.\n";
- out << "// " << ClassName << " slot " << rfi->second._answer_location << " -> " << fname << "\n";
- out << "//////////////////\n";
- out << "static int " << def._wrapper_name << "(PyObject *self) {\n";
- // Find the remap. There should be only one.
- FunctionRemap *remap = *def._remaps.begin();
- const char *container = "";
- if (remap->_has_this) {
- out << " " << cClassName << " *local_this = nullptr;\n";
- out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n";
- out << " return -1;\n";
- out << " }\n\n";
- container = "local_this";
- }
- vector_string params;
- out << " return (int) " << remap->call_function(out, 4, false, container, params) << ";\n";
- out << "}\n\n";
- }
- break;
- case WT_getbuffer:
- // int __getbuffer__(PyObject *self, Py_buffer *buffer, int flags) We
- // map this directly, and assume that the arguments match. The whole
- // point of this is to be fast, and we don't want to negate that by
- // first wrapping and then unwrapping the arguments again. We also
- // want to guarantee const correctness, since that will determine
- // whether a read-only buffer is given.
- {
- has_local_getbuffer = true;
- out << "//////////////////\n";
- out << "// A wrapper function to satisfy Python's internal calling conventions.\n";
- out << "// " << ClassName << " slot " << rfi->second._answer_location << " -> " << fname << "\n";
- out << "//////////////////\n";
- out << "static int " << def._wrapper_name << "(PyObject *self, Py_buffer *buffer, int flags) {\n";
- out << " " << cClassName << " *local_this = nullptr;\n";
- out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n";
- out << " return -1;\n";
- out << " }\n\n";
- vector_string params_const(1);
- vector_string params_nonconst(1);
- FunctionRemap *remap_const = nullptr;
- FunctionRemap *remap_nonconst = nullptr;
- // Iterate through the remaps to find the one that matches our
- // parameters.
- for (FunctionRemap *remap : def._remaps) {
- if (remap->_const_method) {
- if ((remap->_flags & FunctionRemap::F_explicit_self) == 0) {
- params_const.push_back("self");
- }
- remap_const = remap;
- } else {
- if ((remap->_flags & FunctionRemap::F_explicit_self) == 0) {
- params_nonconst.push_back("self");
- }
- remap_nonconst = remap;
- }
- }
- params_const.push_back("buffer");
- params_const.push_back("flags");
- params_nonconst.push_back("buffer");
- params_nonconst.push_back("flags");
- // We have to distinguish properly between const and nonconst,
- // because the function may depend on it to decide whether to
- // provide a writable buffer or a readonly buffer.
- const string const_this = "(const " + cClassName + " *)local_this";
- if (remap_const != nullptr && remap_nonconst != nullptr) {
- out << " if (!DtoolInstance_IS_CONST(self)) {\n";
- out << " return " << remap_nonconst->call_function(out, 4, false, "local_this", params_nonconst) << ";\n";
- out << " } else {\n";
- out << " return " << remap_const->call_function(out, 4, false, const_this, params_const) << ";\n";
- out << " }\n";
- } else if (remap_nonconst != nullptr) {
- out << " if (!DtoolInstance_IS_CONST(self)) {\n";
- out << " return " << remap_nonconst->call_function(out, 4, false, "local_this", params_nonconst) << ";\n";
- out << " } else {\n";
- out << " Dtool_Raise_TypeError(\"Cannot call " << ClassName << ".__getbuffer__() on a const object.\");\n";
- out << " return -1;\n";
- out << " }\n";
- } else if (remap_const != nullptr) {
- out << " return " << remap_const->call_function(out, 4, false, const_this, params_const) << ";\n";
- } else {
- nout << ClassName << "::__getbuffer__ does not match the required signature.\n";
- out << " return -1;\n";
- }
- out << "}\n\n";
- }
- break;
- case WT_releasebuffer:
- // void __releasebuffer__(PyObject *self, Py_buffer *buffer) Same
- // story as __getbuffer__ above.
- {
- out << "//////////////////\n";
- out << "// A wrapper function to satisfy Python's internal calling conventions.\n";
- out << "// " << ClassName << " slot " << rfi->second._answer_location << " -> " << fname << "\n";
- out << "//////////////////\n";
- out << "static void " << def._wrapper_name << "(PyObject *self, Py_buffer *buffer) {\n";
- out << " " << cClassName << " *local_this = nullptr;\n";
- out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n";
- out << " return;\n";
- out << " }\n\n";
- vector_string params_const(1);
- vector_string params_nonconst(1);
- FunctionRemap *remap_const = nullptr;
- FunctionRemap *remap_nonconst = nullptr;
- // Iterate through the remaps to find the one that matches our
- // parameters.
- for (FunctionRemap *remap : def._remaps) {
- if (remap->_const_method) {
- if ((remap->_flags & FunctionRemap::F_explicit_self) == 0) {
- params_const.push_back("self");
- }
- remap_const = remap;
- } else {
- if ((remap->_flags & FunctionRemap::F_explicit_self) == 0) {
- params_nonconst.push_back("self");
- }
- remap_nonconst = remap;
- }
- }
- params_const.push_back("buffer");
- params_nonconst.push_back("buffer");
- string return_expr;
- const string const_this = "(const " + cClassName + " *)local_this";
- if (remap_const != nullptr && remap_nonconst != nullptr) {
- out << " if (!DtoolInstance_IS_CONST(self)) {\n";
- return_expr = remap_nonconst->call_function(out, 4, false, "local_this", params_nonconst);
- if (!return_expr.empty()) {
- out << " " << return_expr << ";\n";
- }
- out << " } else {\n";
- return_expr = remap_const->call_function(out, 4, false, const_this, params_const);
- if (!return_expr.empty()) {
- out << " " << return_expr << ";\n";
- }
- out << " }\n";
- } else if (remap_nonconst != nullptr) {
- // Doesn't matter if there's no const version. We *have* to call
- // it or else we could leak memory.
- return_expr = remap_nonconst->call_function(out, 2, false, "local_this", params_nonconst);
- if (!return_expr.empty()) {
- out << " " << return_expr << ";\n";
- }
- } else if (remap_const != nullptr) {
- return_expr = remap_const->call_function(out, 2, false, const_this, params_const);
- if (!return_expr.empty()) {
- out << " " << return_expr << ";\n";
- }
- } else {
- nout << ClassName << "::__releasebuffer__ does not match the required signature.\n";
- out << " return;\n";
- }
- out << "}\n\n";
- }
- break;
- case WT_ternary_operator:
- case WT_inplace_ternary_operator:
- // PyObject *func(PyObject *self, PyObject *one, PyObject *two)
- {
- int return_flags = RF_err_null;
- if (rfi->second._wrapper_type == WT_inplace_ternary_operator) {
- return_flags |= RF_self;
- } else {
- return_flags |= RF_pyobject;
- }
- out << "//////////////////\n";
- out << "// A wrapper function to satisfy Python's internal calling conventions.\n";
- out << "// " << ClassName << " slot " << rfi->second._answer_location << " -> " << fname << "\n";
- out << "//////////////////\n";
- out << "static PyObject *" << def._wrapper_name << "(PyObject *self, PyObject *arg, PyObject *arg2) {\n";
- out << " " << cClassName << " *local_this = nullptr;\n";
- out << " DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **)&local_this);\n";
- out << " if (local_this == nullptr) {\n";
- // WT_ternary_operator means we must return NotImplemented, instead
- // of raising an exception, if the this pointer doesn't match. This
- // is for things like __pow__, which Python likes to call on the
- // wrong-type objects.
- out << " Py_INCREF(Py_NotImplemented);\n";
- out << " return Py_NotImplemented;\n";
- out << " }\n";
- set<FunctionRemap*> one_param_remaps;
- set<FunctionRemap*> two_param_remaps;
- for (FunctionRemap *remap : def._remaps) {
- if (remap->_parameters.size() == 2) {
- one_param_remaps.insert(remap);
- } else if (remap->_parameters.size() == 3) {
- two_param_remaps.insert(remap);
- }
- }
- string expected_params;
- out << " if (arg2 != nullptr && arg2 != Py_None) {\n";
- out << " PyObject *args = PyTuple_Pack(2, arg, arg2);\n";
- write_function_forset(out, two_param_remaps, 2, 2, expected_params, 4,
- true, true, AT_varargs, RF_pyobject | RF_err_null | RF_decref_args, true);
- out << " Py_DECREF(args);\n";
- out << " } else {\n";
- write_function_forset(out, one_param_remaps, 1, 1, expected_params, 4,
- true, true, AT_single_arg, RF_pyobject | RF_err_null, true);
- out << " }\n\n";
- out << " if (!_PyErr_OCCURRED()) {\n";
- out << " return Dtool_Raise_BadArgumentsError(\n";
- output_quoted(out, 6, expected_params);
- out << ");\n";
- out << " }\n";
- out << " return nullptr;\n";
- out << "}\n\n";
- }
- break;
- case WT_traverse:
- // int __traverse__(PyObject *self, visitproc visit, void *arg) This
- // is a low-level function. Overloads are not supported.
- {
- out << "//////////////////\n";
- out << "// A wrapper function to satisfy Python's internal calling conventions.\n";
- out << "// " << ClassName << " slot " << rfi->second._answer_location << " -> " << fname << "\n";
- out << "//////////////////\n";
- out << "static int " << def._wrapper_name << "(PyObject *self, visitproc visit, void *arg) {\n";
- // Find the remap. There should be only one.
- FunctionRemap *remap = *def._remaps.begin();
- const char *container = "";
- if (remap->_has_this) {
- out << " " << cClassName << " *local_this = nullptr;\n";
- out << " DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **) &local_this);\n";
- out << " if (local_this == nullptr) {\n";
- out << " return 0;\n";
- out << " }\n\n";
- container = "local_this";
- }
- vector_string params((int)remap->_has_this);
- params.push_back("visit");
- params.push_back("arg");
- out << " return " << remap->call_function(out, 2, false, container, params) << ";\n";
- out << "}\n\n";
- }
- break;
- case WT_compare:
- // int func(PyObject *self, Py_ssize_t index)
- {
- out << "//////////////////\n";
- out << "// A wrapper function to satisfy Python's internal calling conventions.\n";
- out << "// " << ClassName << " slot " << rfi->second._answer_location << " -> " << fname << "\n";
- out << "//////////////////\n";
- out << "static int " << def._wrapper_name << "(PyObject *self, PyObject *arg) {\n";
- out << " " << cClassName << " *local_this = nullptr;\n";
- out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n";
- out << " return -1;\n";
- out << " }\n\n";
- string expected_params;
- write_function_forset(out, def._remaps, 1, 1, expected_params, 2, true, true,
- AT_single_arg, RF_compare, false, true);
- out << " if (!_PyErr_OCCURRED()) {\n";
- out << " Dtool_Raise_BadArgumentsError(\n";
- output_quoted(out, 6, expected_params);
- out << ");\n";
- out << " }\n";
- out << " return -1;\n";
- out << "}\n\n";
- }
- break;
- case WT_hash:
- // Py_hash_t func(PyObject *self)
- {
- out << "//////////////////\n";
- out << "// A wrapper function to satisfy Python's internal calling conventions.\n";
- out << "// " << ClassName << " slot " << rfi->second._answer_location << " -> " << fname << "\n";
- out << "//////////////////\n";
- out << "static Py_hash_t " << def._wrapper_name << "(PyObject *self) {\n";
- out << " " << cClassName << " *local_this = nullptr;\n";
- out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n";
- out << " return -1;\n";
- out << " }\n\n";
- FunctionRemap *remap = *def._remaps.begin();
- vector_string params;
- out << " return (Py_hash_t) " << remap->call_function(out, 4, false, "local_this", params) << ";\n";
- out << "}\n\n";
- }
- break;
- case WT_none:
- // Nothing special about the wrapper function: just write it normally.
- string fname = "static PyObject *" + def._wrapper_name + "(PyObject *self, PyObject *args, PyObject *kwds)\n";
- std::vector<FunctionRemap *> remaps;
- remaps.insert(remaps.end(), def._remaps.begin(), def._remaps.end());
- string expected_params;
- write_function_for_name(out, obj, remaps, fname, expected_params, true, AT_keyword_args, RF_pyobject | RF_err_null);
- break;
- }
- if (def._min_version > 0) {
- out << "#endif // PY_VERSION_HEX >= 0x" << hex << def._min_version << dec << "\n";
- }
- }
- int need_repr = 0;
- if (slots.count("tp_repr") == 0) {
- need_repr = NeedsAReprFunction(obj->_itype);
- }
- if (need_repr > 0) {
- out << "//////////////////\n";
- out << "// A __repr__ function\n";
- out << "// " << ClassName << "\n";
- out << "//////////////////\n";
- out << "static PyObject *Dtool_Repr_" << ClassName << "(PyObject *self) {\n";
- out << " " << cClassName << " *local_this = nullptr;\n";
- out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n";
- out << " return nullptr;\n";
- out << " }\n\n";
- out << " std::ostringstream os;\n";
- if (need_repr == 3) {
- out << " invoke_extension(local_this).python_repr(os, \""
- << classNameFromCppName(ClassName, false) << "\");\n";
- } else if (need_repr == 2) {
- out << " local_this->output(os);\n";
- } else {
- out << " local_this->python_repr(os, \""
- << classNameFromCppName(ClassName, false) << "\");\n";
- }
- out << " std::string ss = os.str();\n";
- out << " return Dtool_WrapValue(ss);\n";
- out << "}\n\n";
- has_local_repr = true;
- }
- int need_str = 0;
- if (slots.count("tp_str") == 0) {
- need_str = NeedsAStrFunction(obj->_itype);
- }
- if (need_str > 0) {
- out << "//////////////////\n";
- out << "// A __str__ function\n";
- out << "// " << ClassName << "\n";
- out << "//////////////////\n";
- out << "static PyObject *Dtool_Str_" << ClassName << "(PyObject *self) {\n";
- out << " " << cClassName << " *local_this = nullptr;\n";
- out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n";
- out << " return nullptr;\n";
- out << " }\n\n";
- out << " std::ostringstream os;\n";
- if (need_str == 2) {
- out << " local_this->write(os, 0);\n";
- } else {
- out << " local_this->write(os);\n";
- }
- out << " std::string ss = os.str();\n";
- out << " return Dtool_WrapValue(ss);\n";
- out << "}\n\n";
- has_local_str = true;
- }
- }
- if (NeedsARichCompareFunction(obj->_itype) || slots.count("tp_compare")) {
- out << "//////////////////\n";
- out << "// A rich comparison function\n";
- out << "// " << ClassName << "\n";
- out << "//////////////////\n";
- out << "static PyObject *Dtool_RichCompare_" << ClassName << "(PyObject *self, PyObject *arg, int op) {\n";
- out << " " << cClassName << " *local_this = nullptr;\n";
- out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n";
- out << " return nullptr;\n";
- out << " }\n\n";
- for (Function *func : obj->_methods) {
- std::set<FunctionRemap*> remaps;
- if (!func) {
- continue;
- }
- // We only accept comparison operators that take one parameter (besides
- // 'this').
- for (FunctionRemap *remap : func->_remaps) {
- if (is_remap_legal(remap) && remap->_has_this && (remap->_args_type == AT_single_arg)) {
- remaps.insert(remap);
- }
- }
- const string &fname = func->_ifunc.get_name();
- const char *op_type;
- if (fname == "operator <") {
- op_type = "Py_LT";
- } else if (fname == "operator <=") {
- op_type = "Py_LE";
- } else if (fname == "operator ==") {
- op_type = "Py_EQ";
- } else if (fname == "operator !=") {
- op_type = "Py_NE";
- } else if (fname == "operator >") {
- op_type = "Py_GT";
- } else if (fname == "operator >=") {
- op_type = "Py_GE";
- } else {
- continue;
- }
- if (!has_local_richcompare) {
- out << " switch (op) {\n";
- has_local_richcompare = true;
- }
- out << " case " << op_type << ":\n";
- out << " {\n";
- string expected_params;
- write_function_forset(out, remaps, 1, 1, expected_params, 6, true, false,
- AT_single_arg, RF_pyobject | RF_err_null, false);
- out << " break;\n";
- out << " }\n";
- }
- if (has_local_richcompare) {
- // End of switch block
- out << " }\n\n";
- out << " if (_PyErr_OCCURRED()) {\n";
- out << " PyErr_Clear();\n";
- out << " }\n\n";
- }
- if (slots.count("tp_compare")) {
- // A lot of Panda code depends on comparisons being done via the
- // compare_to function, which is mapped to the tp_compare slot, which
- // Python 3 no longer has. So, we'll write code to fall back to that if
- // no matching comparison operator was found.
- out << " // All is not lost; we still have the compare_to function to fall back onto.\n";
- out << " int cmpval = " << slots["tp_compare"]._wrapper_name << "(self, arg);\n";
- out << " if (cmpval == -1 && _PyErr_OCCURRED()) {\n";
- out << " if (PyErr_ExceptionMatches(PyExc_TypeError)) {\n";
- out << " PyErr_Clear();\n";
- out << " } else {\n";
- out << " return nullptr;\n";
- out << " }\n";
- out << " }\n";
- out << " switch (op) {\n";
- out << " case Py_LT:\n";
- out << " return PyBool_FromLong(cmpval < 0);\n";
- out << " case Py_LE:\n";
- out << " return PyBool_FromLong(cmpval <= 0);\n";
- out << " case Py_EQ:\n";
- out << " return PyBool_FromLong(cmpval == 0);\n";
- out << " case Py_NE:\n";
- out << " return PyBool_FromLong(cmpval != 0);\n";
- out << " case Py_GT:\n";
- out << " return PyBool_FromLong(cmpval > 0);\n";
- out << " case Py_GE:\n";
- out << " return PyBool_FromLong(cmpval >= 0);\n";
- out << " }\n";
- has_local_richcompare = true;
- }
- out << " Py_INCREF(Py_NotImplemented);\n";
- out << " return Py_NotImplemented;\n";
- out << "}\n\n";
- }
- int num_getset = 0;
- if (obj->_properties.size() > 0) {
- // Write out the array of properties, telling Python which getter and
- // setter to call when they are assigned or queried in Python code.
- for (Property *property : obj->_properties) {
- const InterrogateElement &ielem = property->_ielement;
- if (!property->_has_this || property->_getter_remaps.empty()) {
- continue;
- }
- if (num_getset == 0) {
- out << "static PyGetSetDef Dtool_Properties_" << ClassName << "[] = {\n";
- }
- ++num_getset;
- string name1 = methodNameFromCppName(ielem.get_name(), "", false);
- // string name2 = methodNameFromCppName(ielem.get_name(), "", true);
- string getter = "&Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter";
- string setter = "nullptr";
- if (!ielem.is_sequence() && !ielem.is_mapping() && !property->_setter_remaps.empty()) {
- setter = "&Dtool_" + ClassName + "_" + ielem.get_name() + "_Setter";
- }
- out << " {(char *)\"" << name1 << "\", " << getter << ", " << setter;
- if (ielem.has_comment()) {
- out << ", (char *)\n";
- output_quoted(out, 4, ielem.get_comment());
- out << ",\n ";
- } else {
- out << ", nullptr, ";
- }
- // Extra void* argument; we don't make use of it.
- out << "nullptr},\n";
- /*if (name1 != name2 && name1 != "__dict__") {
- // Add alternative spelling.
- out << " {(char *)\"" << name2 << "\", " << getter << ", " << setter
- << ", (char *)\n"
- << " \"Alias of " << name1 << ", for consistency with old naming conventions.\",\n"
- << " NULL},\n";
- }*/
- }
- if (num_getset != 0) {
- out << " {nullptr},\n";
- out << "};\n\n";
- }
- }
- // These fields are inherited together. We should either write all of them
- // or none of them so that they are inherited from DTOOL_SUPER_BASE.
- bool has_hash_compare = (slots.count("tp_hash") != 0 ||
- slots.count("tp_compare") != 0 ||
- has_local_richcompare);
- bool has_parent_class = (obj->_itype.number_of_derivations() != 0);
- // Output the type slot tables.
- out << "static PyNumberMethods Dtool_NumberMethods_" << ClassName << " = {\n";
- write_function_slot(out, 2, slots, "nb_add");
- write_function_slot(out, 2, slots, "nb_subtract");
- write_function_slot(out, 2, slots, "nb_multiply");
- out << "#if PY_MAJOR_VERSION < 3\n";
- // Note: nb_divide does not exist in Python 3. We will probably need some
- // smart mechanism for dispatching to either floor_divide or true_divide.
- write_function_slot(out, 2, slots, "nb_divide");
- out << "#endif\n";
- write_function_slot(out, 2, slots, "nb_remainder");
- write_function_slot(out, 2, slots, "nb_divmod");
- write_function_slot(out, 2, slots, "nb_power");
- write_function_slot(out, 2, slots, "nb_negative");
- write_function_slot(out, 2, slots, "nb_positive");
- write_function_slot(out, 2, slots, "nb_absolute");
- write_function_slot(out, 2, slots, "nb_bool");
- write_function_slot(out, 2, slots, "nb_invert");
- write_function_slot(out, 2, slots, "nb_lshift");
- write_function_slot(out, 2, slots, "nb_rshift");
- write_function_slot(out, 2, slots, "nb_and");
- write_function_slot(out, 2, slots, "nb_xor");
- write_function_slot(out, 2, slots, "nb_or");
- out << "#if PY_MAJOR_VERSION < 3\n";
- write_function_slot(out, 2, slots, "nb_coerce");
- out << "#endif\n";
- write_function_slot(out, 2, slots, "nb_int");
- out << " nullptr, // nb_long\n"; // removed in Python 3
- write_function_slot(out, 2, slots, "nb_float");
- out << "#if PY_MAJOR_VERSION < 3\n";
- write_function_slot(out, 2, slots, "nb_oct");
- write_function_slot(out, 2, slots, "nb_hex");
- out << "#endif\n";
- write_function_slot(out, 2, slots, "nb_inplace_add");
- write_function_slot(out, 2, slots, "nb_inplace_subtract");
- write_function_slot(out, 2, slots, "nb_inplace_multiply");
- out << "#if PY_MAJOR_VERSION < 3\n";
- write_function_slot(out, 2, slots, "nb_inplace_divide");
- out << "#endif\n";
- write_function_slot(out, 2, slots, "nb_inplace_remainder");
- write_function_slot(out, 2, slots, "nb_inplace_power");
- write_function_slot(out, 2, slots, "nb_inplace_lshift");
- write_function_slot(out, 2, slots, "nb_inplace_rshift");
- write_function_slot(out, 2, slots, "nb_inplace_and");
- write_function_slot(out, 2, slots, "nb_inplace_xor");
- write_function_slot(out, 2, slots, "nb_inplace_or");
- write_function_slot(out, 2, slots, "nb_floor_divide");
- write_function_slot(out, 2, slots, "nb_true_divide");
- write_function_slot(out, 2, slots, "nb_inplace_floor_divide");
- write_function_slot(out, 2, slots, "nb_inplace_true_divide");
- out << "#if PY_VERSION_HEX >= 0x02050000\n";
- write_function_slot(out, 2, slots, "nb_index");
- out << "#endif\n";
- out << "#if PY_VERSION_HEX >= 0x03050000\n";
- write_function_slot(out, 2, slots, "nb_matrix_multiply");
- write_function_slot(out, 2, slots, "nb_inplace_matrix_multiply");
- out << "#endif\n";
- out << "};\n\n";
- // NB: it's tempting not to write this table when a class doesn't have them.
- // But then Python won't inherit them from base classes either! So we
- // always write this table for now even if it will be full of 0's, unless
- // this type has no base classes at all.
- if (has_parent_class || (obj->_protocol_types & Object::PT_sequence) != 0) {
- out << "static PySequenceMethods Dtool_SequenceMethods_" << ClassName << " = {\n";
- write_function_slot(out, 2, slots, "sq_length");
- write_function_slot(out, 2, slots, "sq_concat");
- write_function_slot(out, 2, slots, "sq_repeat");
- write_function_slot(out, 2, slots, "sq_item");
- out << " nullptr, // sq_slice\n"; // removed in Python 3
- write_function_slot(out, 2, slots, "sq_ass_item");
- out << " nullptr, // sq_ass_slice\n"; // removed in Python 3
- write_function_slot(out, 2, slots, "sq_contains");
- write_function_slot(out, 2, slots, "sq_inplace_concat");
- write_function_slot(out, 2, slots, "sq_inplace_repeat");
- out << "};\n\n";
- }
- // Same note applies as for the SequenceMethods.
- if (has_parent_class || (obj->_protocol_types & Object::PT_mapping) != 0) {
- out << "static PyMappingMethods Dtool_MappingMethods_" << ClassName << " = {\n";
- write_function_slot(out, 2, slots, "mp_length");
- write_function_slot(out, 2, slots, "mp_subscript");
- write_function_slot(out, 2, slots, "mp_ass_subscript");
- out << "};\n\n";
- }
- // Same note applies as above.
- if (has_parent_class || has_local_getbuffer) {
- out << "static PyBufferProcs Dtool_BufferProcs_" << ClassName << " = {\n";
- out << "#if PY_MAJOR_VERSION < 3\n";
- write_function_slot(out, 2, slots, "bf_getreadbuffer");
- write_function_slot(out, 2, slots, "bf_getwritebuffer");
- write_function_slot(out, 2, slots, "bf_getsegcount");
- write_function_slot(out, 2, slots, "bf_getcharbuffer");
- out << "#endif\n";
- out << "#if PY_VERSION_HEX >= 0x02060000\n";
- write_function_slot(out, 2, slots, "bf_getbuffer");
- write_function_slot(out, 2, slots, "bf_releasebuffer");
- out << "#endif\n";
- out << "};\n\n";
- }
- bool have_async = false;
- if (has_parent_class || slots.count("am_await") != 0 ||
- slots.count("am_aiter") != 0 ||
- slots.count("am_anext") != 0) {
- out << "#if PY_VERSION_HEX >= 0x03050000\n";
- out << "static PyAsyncMethods Dtool_AsyncMethods_" << ClassName << " = {\n";
- write_function_slot(out, 2, slots, "am_await");
- write_function_slot(out, 2, slots, "am_aiter");
- write_function_slot(out, 2, slots, "am_anext");
- out << "};\n";
- out << "#endif\n\n";
- have_async = true;
- }
- // Output the actual PyTypeObject definition.
- out << "struct Dtool_PyTypedObject Dtool_" << ClassName << " = {\n";
- out << " {\n";
- out << " PyVarObject_HEAD_INIT(nullptr, 0)\n";
- // const char *tp_name;
- out << " \"" << _def->module_name << "." << export_class_name << "\",\n";
- // Py_ssize_t tp_basicsize;
- out << " sizeof(Dtool_PyInstDef),\n";
- // Py_ssize_t tp_itemsize;
- out << " 0, // tp_itemsize\n";
- // destructor tp_dealloc;
- out << " &Dtool_FreeInstance_" << ClassName << ",\n";
- // printfunc tp_print;
- write_function_slot(out, 4, slots, "tp_print");
- // getattrfunc tp_getattr;
- write_function_slot(out, 4, slots, "tp_getattr");
- // setattrfunc tp_setattr;
- write_function_slot(out, 4, slots, "tp_setattr");
- // cmpfunc tp_compare; (reserved in Python 3)
- out << "#if PY_VERSION_HEX >= 0x03050000\n";
- if (have_async) {
- out << " &Dtool_AsyncMethods_" << ClassName << ",\n";
- } else {
- out << " nullptr, // tp_as_async\n";
- }
- out << "#elif PY_MAJOR_VERSION >= 3\n";
- out << " nullptr, // tp_reserved\n";
- out << "#else\n";
- if (has_hash_compare) {
- write_function_slot(out, 4, slots, "tp_compare",
- "&DtoolInstance_ComparePointers");
- } else {
- out << " nullptr, // tp_compare\n";
- }
- out << "#endif\n";
- // reprfunc tp_repr;
- if (has_local_repr) {
- out << " &Dtool_Repr_" << ClassName << ",\n";
- } else {
- write_function_slot(out, 4, slots, "tp_repr");
- }
- // PyNumberMethods *tp_as_number;
- out << " &Dtool_NumberMethods_" << ClassName << ",\n";
- // PySequenceMethods *tp_as_sequence;
- if (has_parent_class || (obj->_protocol_types & Object::PT_sequence) != 0) {
- out << " &Dtool_SequenceMethods_" << ClassName << ",\n";
- } else {
- out << " nullptr, // tp_as_sequence\n";
- }
- // PyMappingMethods *tp_as_mapping;
- if (has_parent_class || (obj->_protocol_types & Object::PT_mapping) != 0) {
- out << " &Dtool_MappingMethods_" << ClassName << ",\n";
- } else {
- out << " nullptr, // tp_as_mapping\n";
- }
- // hashfunc tp_hash;
- if (has_hash_compare) {
- write_function_slot(out, 4, slots, "tp_hash", "&DtoolInstance_HashPointer");
- } else {
- out << " nullptr, // tp_hash\n";
- }
- // ternaryfunc tp_call;
- write_function_slot(out, 4, slots, "tp_call");
- // reprfunc tp_str;
- if (has_local_str) {
- out << " &Dtool_Str_" << ClassName << ",\n";
- } else if (has_local_repr) {
- out << " &Dtool_Repr_" << ClassName << ",\n";
- } else {
- write_function_slot(out, 4, slots, "tp_str");
- }
- // getattrofunc tp_getattro;
- write_function_slot(out, 4, slots, "tp_getattro");
- // setattrofunc tp_setattro;
- write_function_slot(out, 4, slots, "tp_setattro");
- // PyBufferProcs *tp_as_buffer;
- if (has_parent_class || has_local_getbuffer) {
- out << " &Dtool_BufferProcs_" << ClassName << ",\n";
- } else {
- out << " nullptr, // tp_as_buffer\n";
- }
- string gcflag;
- if (obj->_protocol_types & Object::PT_python_gc) {
- gcflag = " | Py_TPFLAGS_HAVE_GC";
- }
- // long tp_flags;
- if (has_local_getbuffer) {
- out << "#if PY_VERSION_HEX >= 0x02060000 && PY_VERSION_HEX < 0x03000000\n";
- out << " Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES | Py_TPFLAGS_HAVE_NEWBUFFER" << gcflag << ",\n";
- out << "#else\n";
- out << " Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES" << gcflag << ",\n";
- out << "#endif\n";
- } else {
- out << " Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES" << gcflag << ",\n";
- }
- // const char *tp_doc;
- if (obj->_itype.has_comment()) {
- out << "#ifdef NDEBUG\n";
- out << " 0,\n";
- out << "#else\n";
- output_quoted(out, 4, obj->_itype.get_comment());
- out << ",\n";
- out << "#endif\n";
- } else {
- out << " nullptr, // tp_doc\n";
- }
- // traverseproc tp_traverse;
- out << " nullptr, // tp_traverse\n";
- //write_function_slot(out, 4, slots, "tp_traverse");
- // inquiry tp_clear;
- out << " nullptr, // tp_clear\n";
- //write_function_slot(out, 4, slots, "tp_clear");
- // richcmpfunc tp_richcompare;
- if (has_local_richcompare) {
- out << " &Dtool_RichCompare_" << ClassName << ",\n";
- } else if (has_hash_compare) {
- // All hashable types need to be comparable.
- out << "#if PY_MAJOR_VERSION >= 3\n";
- out << " &DtoolInstance_RichComparePointers,\n";
- out << "#else\n";
- out << " nullptr, // tp_richcompare\n";
- out << "#endif\n";
- } else {
- out << " nullptr, // tp_richcompare\n";
- }
- // Py_ssize_t tp_weaklistoffset;
- out << " 0, // tp_weaklistoffset\n";
- // getiterfunc tp_iter;
- write_function_slot(out, 4, slots, "tp_iter");
- // iternextfunc tp_iternext;
- write_function_slot(out, 4, slots, "tp_iternext");
- // struct PyMethodDef *tp_methods;
- out << " Dtool_Methods_" << ClassName << ",\n";
- // struct PyMemberDef *tp_members;
- out << " nullptr, // tp_members\n";
- // struct PyGetSetDef *tp_getset;
- if (num_getset > 0) {
- out << " Dtool_Properties_" << ClassName << ",\n";
- } else {
- out << " nullptr, // tp_getset\n";
- }
- // struct _typeobject *tp_base;
- out << " nullptr, // tp_base\n";
- // PyObject *tp_dict;
- out << " nullptr, // tp_dict\n";
- // descrgetfunc tp_descr_get;
- write_function_slot(out, 4, slots, "tp_descr_get");
- // descrsetfunc tp_descr_set;
- write_function_slot(out, 4, slots, "tp_descr_set");
- // Py_ssize_t tp_dictoffset;
- out << " 0, // tp_dictoffset\n";
- // initproc tp_init;
- out << " Dtool_Init_" << ClassName << ",\n";
- // allocfunc tp_alloc;
- out << " PyType_GenericAlloc,\n";
- // newfunc tp_new;
- out << " Dtool_new_" << ClassName << ",\n";
- // freefunc tp_free;
- if (obj->_protocol_types & Object::PT_python_gc) {
- out << " PyObject_GC_Del,\n";
- } else {
- out << " PyObject_Del,\n";
- }
- // inquiry tp_is_gc;
- out << " nullptr, // tp_is_gc\n";
- // PyObject *tp_bases;
- out << " nullptr, // tp_bases\n";
- // PyObject *tp_mro;
- out << " nullptr, // tp_mro\n";
- // PyObject *tp_cache;
- out << " nullptr, // tp_cache\n";
- // PyObject *tp_subclasses;
- out << " nullptr, // tp_subclasses\n";
- // PyObject *tp_weaklist;
- out << " nullptr, // tp_weaklist\n";
- // destructor tp_del;
- out << " nullptr, // tp_del\n";
- // unsigned int tp_version_tag
- out << "#if PY_VERSION_HEX >= 0x02060000\n";
- out << " 0, // tp_version_tag\n";
- out << "#endif\n";
- // destructor tp_finalize
- out << "#if PY_VERSION_HEX >= 0x03040000\n";
- out << " nullptr, // tp_finalize\n";
- out << "#endif\n";
- out << " },\n";
- // It's tempting to initialize the type handle here, but this causes static
- // init ordering issues; this may run before init_type is called.
- out << " TypeHandle::none(),\n";
- out << " Dtool_PyModuleClassInit_" << ClassName << ",\n";
- out << " Dtool_UpcastInterface_" << ClassName << ",\n";
- out << " Dtool_DowncastInterface_" << ClassName << ",\n";
- int has_coerce = has_coerce_constructor(obj->_itype._cpptype->as_struct_type());
- if (has_coerce > 0) {
- if (TypeManager::is_reference_count(obj->_itype._cpptype)) {
- out << " (CoerceFunction)Dtool_ConstCoerce_" << ClassName << ",\n";
- if (has_coerce > 1) {
- out << " (CoerceFunction)Dtool_Coerce_" << ClassName << ",\n";
- } else {
- out << " nullptr,\n";
- }
- } else {
- out << " nullptr,\n";
- out << " (CoerceFunction)Dtool_Coerce_" << ClassName << ",\n";
- }
- } else {
- out << " nullptr,\n";
- out << " nullptr,\n";
- }
- out << "};\n\n";
- out << "static void Dtool_PyModuleClassInit_" << ClassName << "(PyObject *module) {\n";
- out << " (void) module; // Unused\n";
- out << " static bool initdone = false;\n";
- out << " if (!initdone) {\n";
- out << " initdone = true;\n";
- // Add bases.
- out << " // Dependent objects\n";
- if (bases.size() > 0) {
- string baseargs;
- for (CPPType *base : bases) {
- string safe_name = make_safe_name(base->get_local_name(&parser));
- if (isExportThisRun(base)) {
- baseargs += ", (PyTypeObject *)&Dtool_" + safe_name;
- out << " Dtool_PyModuleClassInit_" << safe_name << "(nullptr);\n";
- } else {
- baseargs += ", (PyTypeObject *)Dtool_Ptr_" + safe_name;
- out << " assert(Dtool_Ptr_" << safe_name << " != nullptr);\n"
- << " assert(Dtool_Ptr_" << safe_name << "->_Dtool_ModuleClassInit != nullptr);\n"
- << " Dtool_Ptr_" << safe_name << "->_Dtool_ModuleClassInit(nullptr);\n";
- }
- }
- out << " Dtool_" << ClassName << "._PyType.tp_bases = PyTuple_Pack(" << bases.size() << baseargs << ");\n";
- } else {
- out << " Dtool_" << ClassName << "._PyType.tp_base = (PyTypeObject *)&Dtool_DTOOL_SUPER_BASE;\n";
- }
- int num_nested = obj->_itype.number_of_nested_types();
- int num_dict_items = 1;
- // Go through once to estimate the number of elements the dict will hold.
- for (int ni = 0; ni < num_nested; ni++) {
- TypeIndex nested_index = obj->_itype.get_nested_type(ni);
- if (_objects.count(nested_index) == 0) {
- continue;
- }
- Object *nested_obj = _objects[nested_index];
- assert(nested_obj != nullptr);
- if (nested_obj->_itype.is_class() || nested_obj->_itype.is_struct()) {
- num_dict_items += 2;
- } else if (nested_obj->_itype.is_typedef()) {
- ++num_dict_items;
- } else if (nested_obj->_itype.is_enum() && !nested_obj->_itype.is_scoped_enum()) {
- CPPEnumType *enum_type = nested_obj->_itype._cpptype->as_enum_type();
- num_dict_items += 2 * enum_type->_elements.size();
- }
- }
- // Build type dictionary. The size is just an estimation.
- if (num_dict_items > 5) {
- out << " PyObject *dict = _PyDict_NewPresized(" << num_dict_items << ");\n";
- } else {
- out << " PyObject *dict = PyDict_New();\n";
- }
- out << " Dtool_" << ClassName << "._PyType.tp_dict = dict;\n";
- out << " PyDict_SetItemString(dict, \"DtoolClassDict\", dict);\n";
- // Now go through the nested types again to actually add the dict items.
- for (int ni = 0; ni < num_nested; ni++) {
- TypeIndex nested_index = obj->_itype.get_nested_type(ni);
- if (_objects.count(nested_index) == 0) {
- // Illegal type.
- continue;
- }
- Object *nested_obj = _objects[nested_index];
- assert(nested_obj != nullptr);
- if (nested_obj->_itype.is_class() || nested_obj->_itype.is_struct()) {
- std::string ClassName1 = make_safe_name(nested_obj->_itype.get_scoped_name());
- std::string ClassName2 = make_safe_name(nested_obj->_itype.get_name());
- out << " // Nested Object " << ClassName1 << ";\n";
- out << " Dtool_PyModuleClassInit_" << ClassName1 << "(nullptr);\n";
- string name1 = classNameFromCppName(ClassName2, false);
- string name2 = classNameFromCppName(ClassName2, true);
- out << " PyDict_SetItemString(dict, \"" << name1 << "\", (PyObject *)&Dtool_" << ClassName1 << ");\n";
- if (name1 != name2) {
- out << " PyDict_SetItemString(dict, \"" << name2 << "\", (PyObject *)&Dtool_" << ClassName1 << ");\n";
- }
- } else if (nested_obj->_itype.is_typedef()) {
- // Unwrap typedefs.
- TypeIndex wrapped = nested_obj->_itype._wrapped_type;
- while (interrogate_type_is_typedef(wrapped)) {
- wrapped = interrogate_type_wrapped_type(wrapped);
- }
- // Er, we can only export typedefs to structs.
- if (!interrogate_type_is_struct(wrapped)) {
- continue;
- }
- string ClassName1 = make_safe_name(interrogate_type_scoped_name(wrapped));
- string ClassName2 = make_safe_name(interrogate_type_name(wrapped));
- string name1 = classNameFromCppName(ClassName2, false);
- out << " PyDict_SetItemString(dict, \"" << name1 << "\", (PyObject *)&Dtool_" << ClassName1 << ");\n";
- // No need to support mangled names for nested typedefs; we only added
- // support recently.
- } else if (nested_obj->_itype.is_scoped_enum()) {
- // Convert enum class as Python 3.4-style enum.
- string class_name = nested_obj->_itype._cpptype->get_local_name(&parser);
- string safe_name = make_safe_name(class_name);
- int enum_count = nested_obj->_itype.number_of_enum_values();
- CPPType *underlying_type = TypeManager::unwrap_const(nested_obj->_itype._cpptype->as_enum_type()->get_underlying_type());
- string cast_to = underlying_type->get_local_name(&parser);
- out << " // enum class " << nested_obj->_itype.get_scoped_name() << ";\n";
- out << " {\n";
- out << " PyObject *members = PyTuple_New(" << enum_count << ");\n";
- out << " PyObject *member;\n";
- for (int xx = 0; xx < enum_count; xx++) {
- out << " member = PyTuple_New(2);\n"
- "#if PY_MAJOR_VERSION >= 3\n"
- " PyTuple_SET_ITEM(member, 0, PyUnicode_FromString(\""
- << nested_obj->_itype.get_enum_value_name(xx) << "\"));\n"
- "#else\n"
- " PyTuple_SET_ITEM(member, 0, PyString_FromString(\""
- << nested_obj->_itype.get_enum_value_name(xx) << "\"));\n"
- "#endif\n"
- " PyTuple_SET_ITEM(member, 1, Dtool_WrapValue(("
- << cast_to << ")" << nested_obj->_itype.get_scoped_name() << "::"
- << nested_obj->_itype.get_enum_value_name(xx) << "));\n"
- " PyTuple_SET_ITEM(members, " << xx << ", member);\n";
- }
- out << " Dtool_Ptr_" << safe_name << " = Dtool_EnumType_Create(\""
- << nested_obj->_itype.get_name() << "\", members, \""
- << _def->module_name << "\");\n";
- out << " PyDict_SetItemString(dict, \"" << nested_obj->_itype.get_name()
- << "\", (PyObject *)Dtool_Ptr_" << safe_name << ");\n";
- out << " }\n";
- } else if (nested_obj->_itype.is_enum()) {
- out << " // enum " << nested_obj->_itype.get_scoped_name() << ";\n";
- CPPEnumType *enum_type = nested_obj->_itype._cpptype->as_enum_type();
- CPPEnumType::Elements::const_iterator ei;
- for (ei = enum_type->_elements.begin(); ei != enum_type->_elements.end(); ++ei) {
- string name1 = classNameFromCppName((*ei)->get_simple_name(), false);
- string name2;
- if (nested_obj->_itype.has_true_name()) {
- name2 = classNameFromCppName((*ei)->get_simple_name(), true);
- } else {
- // Don't generate the alternative syntax for anonymous enums, since
- // we added support for those after we started deprecating the
- // alternative syntax.
- name2 = name1;
- }
- string enum_value = obj->_itype.get_scoped_name() + "::" + (*ei)->get_simple_name();
- out << " PyDict_SetItemString(dict, \"" << name1 << "\", Dtool_WrapValue(" << enum_value << "));\n";
- if (name1 != name2) {
- out << " PyDict_SetItemString(dict, \"" << name2 << "\", Dtool_WrapValue(" << enum_value << "));\n";
- }
- }
- }
- }
- // Also add the static properties, which can't be added via getset.
- for (Property *property : obj->_properties) {
- const InterrogateElement &ielem = property->_ielement;
- if (property->_has_this || property->_getter_remaps.empty()) {
- continue;
- }
- string name1 = methodNameFromCppName(ielem.get_name(), "", false);
- // string name2 = methodNameFromCppName(ielem.get_name(), "", true);
- string getter = "&Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter";
- string setter = "nullptr";
- if (!ielem.is_sequence() && !ielem.is_mapping() && !property->_setter_remaps.empty()) {
- setter = "&Dtool_" + ClassName + "_" + ielem.get_name() + "_Setter";
- }
- out << " static const PyGetSetDef def_" << name1 << " = {(char *)\"" << name1 << "\", " << getter << ", " << setter;
- if (ielem.has_comment()) {
- out << ", (char *)\n";
- output_quoted(out, 4, ielem.get_comment());
- out << ",\n ";
- } else {
- out << ", nullptr, ";
- }
- // Extra void* argument; we don't make use of it.
- out << "nullptr};\n";
- out << " PyDict_SetItemString(dict, \"" << name1 << "\", Dtool_NewStaticProperty(&Dtool_" << ClassName << "._PyType, &def_" << name1 << "));\n";
- /* Alternative spelling:
- out << " PyDict_SetItemString(\"" << name2 << "\", &def_" << name1 << ");\n";
- */
- }
- out << " if (PyType_Ready((PyTypeObject *)&Dtool_" << ClassName << ") < 0) {\n"
- " Dtool_Raise_TypeError(\"PyType_Ready(" << ClassName << ")\");\n"
- " return;\n"
- " }\n"
- " Py_INCREF((PyTypeObject *)&Dtool_" << ClassName << ");\n"
- " }\n";
- /*
- * Also write out the explicit alternate names. int num_alt_names =
- * obj->_itype.get_num_alt_names(); for (int i = 0; i < num_alt_names; ++i) {
- * string alt_name = make_safe_name(obj->_itype.get_alt_name(i)); if
- * (export_class_name != alt_name) { out << " PyModule_AddObject(module,
- * \"" << alt_name << "\", (PyObject *)&Dtool_" << ClassName <<
- * ".As_PyTypeObject());\n"; } }
- */
- // out << " }\n";
- out << "}\n\n";
- }
- /**
- * This method should be overridden and redefined to return true for
- * interfaces that require the implicit "this" parameter, if present, to be
- * passed as the first parameter to any wrapper functions.
- */
- bool InterfaceMakerPythonNative::
- synthesize_this_parameter() {
- return true;
- }
- /**
- * This method should be overridden and redefined to return true for
- * interfaces that require overloaded instances of a function to be defined as
- * separate functions (each with its own hashed name), or false for interfaces
- * that can support overloading natively, and thus only require one wrapper
- * function per each overloaded input function.
- */
- bool InterfaceMakerPythonNative::
- separate_overloading() {
- // We used to return true here. Nowadays, some of the default arguments are
- // handled in the PyArg_ParseTuple code, and some are still being considered
- // as separate overloads (this depends on a bunch of factors, see
- // collapse_default_remaps). This is all handled elsewhere.
- return false;
- }
- /**
- * Returns the prefix string used to generate wrapper function names.
- */
- string InterfaceMakerPythonNative::
- get_wrapper_prefix() {
- return "Dtool_";
- }
- /**
- * Returns the prefix string used to generate unique symbolic names, which are
- * not necessarily C-callable function names.
- */
- string InterfaceMakerPythonNative::
- get_unique_prefix() {
- return "Dtool_";
- }
- /**
- * Associates the function wrapper with its function in the appropriate
- * structures in the database.
- */
- void InterfaceMakerPythonNative::
- record_function_wrapper(InterrogateFunction &ifunc, FunctionWrapperIndex wrapper_index) {
- ifunc._python_wrappers.push_back(wrapper_index);
- }
- /**
- * Writes the prototype for the indicated function.
- */
- void InterfaceMakerPythonNative::
- write_prototype_for(ostream &out, InterfaceMaker::Function *func) {
- std::string fname = "PyObject *" + func->_name + "(PyObject *self, PyObject *args)";
- write_prototype_for_name(out, func, fname);
- }
- /**
- *
- */
- void InterfaceMakerPythonNative::
- write_prototype_for_name(ostream &out, InterfaceMaker::Function *func, const std::string &function_namename) {
- // Function::Remaps::const_iterator ri;
- // for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) {
- // FunctionRemap *remap = (*ri);
- if (!output_function_names) {
- // If we're not saving the function names, don't export it from the
- // library.
- out << "static ";
- } else {
- out << "extern \"C\" ";
- }
- out << function_namename << ";\n";
- // }
- }
- /**
- * Writes the definition for a function that will call the indicated C++
- * function or method.
- */
- void InterfaceMakerPythonNative::
- write_function_for_top(ostream &out, InterfaceMaker::Object *obj, InterfaceMaker::Function *func) {
- // First check if this function has non-slotted and legal remaps, ie. if we
- // should even write it.
- bool has_remaps = false;
- for (FunctionRemap *remap : func->_remaps) {
- if (!is_remap_legal(remap)) {
- continue;
- }
- SlottedFunctionDef slotted_def;
- if (!get_slotted_function_def(obj, func, remap, slotted_def) || slotted_def._keep_method) {
- // It has a non-slotted remap, so we should write it.
- has_remaps = true;
- break;
- }
- }
- if (!has_remaps) {
- // Nope.
- return;
- }
- // This is a bit of a hack, as these methods should probably be going
- // through the slotted function system. But it's kind of pointless to write
- // these out, and a waste of space.
- string fname = func->_ifunc.get_name();
- if (fname == "operator <" ||
- fname == "operator <=" ||
- fname == "operator ==" ||
- fname == "operator !=" ||
- fname == "operator >" ||
- fname == "operator >=") {
- return;
- }
- if (func->_ifunc.is_unary_op()) {
- assert(func->_args_type == AT_no_args);
- }
- string prototype = "static PyObject *" + func->_name + "(PyObject *";
- // This will be NULL for static funcs, so prevent code from using it.
- if (func->_has_this) {
- prototype += "self";
- }
- switch (func->_args_type) {
- case AT_keyword_args:
- prototype += ", PyObject *args, PyObject *kwds";
- break;
- case AT_varargs:
- prototype += ", PyObject *args";
- break;
- case AT_single_arg:
- prototype += ", PyObject *arg";
- break;
- default:
- prototype += ", PyObject *";
- break;
- }
- prototype += ")";
- string expected_params;
- write_function_for_name(out, obj, func->_remaps, prototype, expected_params, true, func->_args_type, RF_pyobject | RF_err_null);
- // Now synthesize a variable for the docstring.
- ostringstream comment;
- if (!expected_params.empty()) {
- comment << "C++ Interface:\n"
- << expected_params;
- }
- if (func->_ifunc._comment.size() > 2) {
- if (!expected_params.empty()) {
- comment << "\n";
- }
- comment << func->_ifunc._comment;
- }
- out << "#ifndef NDEBUG\n";
- out << "static const char *" << func->_name << "_comment =\n";
- output_quoted(out, 2, comment.str());
- out << ";\n";
- out << "#else\n";
- out << "static const char *" << func->_name << "_comment = nullptr;\n";
- out << "#endif\n\n";
- }
- /**
- * Writes the definition for a function that will call the indicated C++
- * function or method.
- */
- void InterfaceMakerPythonNative::
- write_function_for_name(ostream &out, Object *obj,
- const Function::Remaps &remaps,
- const string &function_name,
- string &expected_params,
- bool coercion_allowed,
- ArgsType args_type, int return_flags) {
- std::map<int, std::set<FunctionRemap *> > map_sets;
- std::map<int, std::set<FunctionRemap *> >::iterator mii;
- std::set<FunctionRemap *>::iterator sii;
- bool has_this = false;
- Function::Remaps::const_iterator ri;
- FunctionRemap *remap = nullptr;
- int max_required_args = 0;
- bool all_nonconst = true;
- bool has_keywords = false;
- out << "/**\n * Python function wrapper for:\n";
- for (ri = remaps.begin(); ri != remaps.end(); ++ri) {
- remap = (*ri);
- if (is_remap_legal(remap)) {
- int min_num_args = remap->get_min_num_args();
- int max_num_args = remap->get_max_num_args();
- if (remap->_has_this) {
- has_this = true;
- }
- if (!remap->_has_this || remap->_const_method) {
- all_nonconst = false;
- }
- if (remap->_args_type == AT_keyword_args) {
- has_keywords = true;
- }
- max_required_args = max(max_num_args, max_required_args);
- for (int i = min_num_args; i <= max_num_args; ++i) {
- map_sets[i].insert(remap);
- }
- out << " * ";
- remap->write_orig_prototype(out, 0, false, (max_num_args - min_num_args));
- out << "\n";
- } else {
- out << " * Rejected Remap [";
- remap->write_orig_prototype(out, 0);
- out << "]\n";
- }
- }
- out << " */\n";
- if (has_this && obj == nullptr) {
- assert(obj != nullptr);
- }
- out << function_name << " {\n";
- if (has_this) {
- std::string ClassName = make_safe_name(obj->_itype.get_scoped_name());
- std::string cClassName = obj->_itype.get_true_name();
- // string class_name = remap->_cpptype->get_simple_name();
- // Extract pointer from 'self' parameter.
- out << " " << cClassName << " *local_this = nullptr;\n";
- if (all_nonconst) {
- // All remaps are non-const. Also check that this object isn't const.
- out << " if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", "
- << "(void **)&local_this, \"" << classNameFromCppName(cClassName, false)
- << "." << methodNameFromCppName(remap, cClassName, false) << "\")) {\n";
- } else {
- out << " if (!DtoolInstance_GetPointer(self, local_this, Dtool_" << ClassName << ")) {\n";
- }
- error_return(out, 4, return_flags);
- out << " }\n";
- }
- if (map_sets.empty()) {
- error_return(out, 2, return_flags);
- out << "}\n\n";
- return;
- }
- if (args_type == AT_keyword_args && !has_keywords) {
- // We don't actually take keyword arguments. Make sure we didn't get any.
- out << " if (kwds != nullptr && PyDict_Size(kwds) > 0) {\n";
- out << "#ifdef NDEBUG\n";
- error_raise_return(out, 4, return_flags, "TypeError", "function takes no keyword arguments");
- out << "#else\n";
- error_raise_return(out, 4, return_flags, "TypeError",
- methodNameFromCppName(remap, "", false) + "() takes no keyword arguments");
- out << "#endif\n";
- out << " }\n";
- args_type = AT_varargs;
- }
- if (args_type == AT_keyword_args || args_type == AT_varargs) {
- max_required_args = collapse_default_remaps(map_sets, max_required_args);
- }
- if (remap->_flags & FunctionRemap::F_explicit_args) {
- // We have a remap that wants to handle the wrapper itself.
- string expected_params;
- write_function_instance(out, remap, 0, 0, expected_params, 2, true, true,
- args_type, return_flags);
- } else if (map_sets.size() > 1 && (args_type == AT_varargs || args_type == AT_keyword_args)) {
- // We have more than one remap.
- switch (args_type) {
- case AT_keyword_args:
- indent(out, 2) << "int parameter_count = (int)PyTuple_Size(args);\n";
- indent(out, 2) << "if (kwds != nullptr) {\n";
- indent(out, 2) << " parameter_count += (int)PyDict_Size(kwds);\n";
- indent(out, 2) << "}\n";
- break;
- case AT_varargs:
- indent(out, 2) << "int parameter_count = (int)PyTuple_Size(args);\n";
- break;
- case AT_single_arg:
- // It shouldn't get here, but we'll handle these cases nonetheless.
- indent(out, 2) << "const int parameter_count = 1;\n";
- break;
- default:
- indent(out, 2) << "const int parameter_count = 0;\n";
- break;
- }
- // Keep track of how many args this function actually takes for the error
- // message. We add one to the parameter count for "self", following the
- // Python convention.
- int add_self = has_this ? 1 : 0;
- set<int> num_args;
- indent(out, 2) << "switch (parameter_count) {\n";
- for (mii = map_sets.begin(); mii != map_sets.end(); ++mii) {
- int max_args = mii->first;
- int min_args = min(max_required_args, max_args);
- for (int i = min_args; i <= max_args; ++i) {
- indent(out, 2) << "case " << i << ":\n";
- num_args.insert(i + add_self);
- }
- num_args.insert(max_args + add_self);
- bool strip_keyword_args = false;
- // Check whether any remap actually takes keyword arguments. If not,
- // then we don't have to bother checking that for every remap.
- if (args_type == AT_keyword_args && max_args > 0) {
- strip_keyword_args = true;
- std::set<FunctionRemap *>::iterator sii;
- for (sii = mii->second.begin(); sii != mii->second.end(); ++sii) {
- remap = (*sii);
- size_t first_param = remap->_has_this ? 1u : 0u;
- for (size_t i = first_param; i < remap->_parameters.size(); ++i) {
- if (remap->_parameters[i]._has_name) {
- strip_keyword_args = false;
- break;
- }
- }
- }
- }
- if (strip_keyword_args) {
- // None of the remaps take any keyword arguments, so let's check that
- // we take none. This saves some checks later on.
- indent(out, 4) << "if (kwds == nullptr || PyDict_GET_SIZE(kwds) == 0) {\n";
- if (min_args == 1 && min_args == 1) {
- indent(out, 4) << " PyObject *arg = PyTuple_GET_ITEM(args, 0);\n";
- write_function_forset(out, mii->second, min_args, max_args, expected_params, 6,
- coercion_allowed, true, AT_single_arg, return_flags, true, !all_nonconst);
- } else {
- write_function_forset(out, mii->second, min_args, max_args, expected_params, 6,
- coercion_allowed, true, AT_varargs, return_flags, true, !all_nonconst);
- }
- } else if (min_args == 1 && max_args == 1 && args_type == AT_varargs) {
- // We already checked that the args tuple has only one argument, so
- // we might as well extract that from the tuple now.
- indent(out, 4) << "{\n";
- indent(out, 4) << " PyObject *arg = PyTuple_GET_ITEM(args, 0);\n";
- write_function_forset(out, mii->second, min_args, max_args, expected_params, 6,
- coercion_allowed, true, AT_single_arg, return_flags, true, !all_nonconst);
- } else {
- indent(out, 4) << "{\n";
- write_function_forset(out, mii->second, min_args, max_args, expected_params, 6,
- coercion_allowed, true, args_type, return_flags, true, !all_nonconst);
- }
- indent(out, 4) << "}\n";
- indent(out, 4) << "break;\n";
- }
- // In NDEBUG case, fall through to the error at end of function.
- out << "#ifndef NDEBUG\n";
- indent(out, 2) << "default:\n";
- // Format an error saying how many arguments we actually take. So much
- // logic for such a silly matter. Sheesh.
- ostringstream msg;
- msg << methodNameFromCppName(remap, "", false) << "() takes ";
- set<int>::iterator si = num_args.begin();
- msg << *si;
- if (num_args.size() == 2) {
- msg << " or " << *(++si);
- } else if (num_args.size() > 2) {
- ++si;
- while (si != num_args.end()) {
- int num = *si;
- if ((++si) == num_args.end()) {
- msg << " or " << num;
- } else {
- msg << ", " << num;
- }
- }
- }
- msg << " arguments (%d given)";
- string count_var = "parameter_count";
- if (add_self) {
- count_var += " + 1";
- }
- error_raise_return(out, 4, return_flags, "TypeError",
- msg.str(), count_var);
- out << "#endif\n";
- indent(out, 2) << "}\n";
- out << " if (!_PyErr_OCCURRED()) {\n"
- << " ";
- if ((return_flags & ~RF_pyobject) == RF_err_null) {
- out << "return ";
- }
- out << "Dtool_Raise_BadArgumentsError(\n";
- output_quoted(out, 6, expected_params);
- out << ");\n"
- << " }\n";
- error_return(out, 2, return_flags);
- } else {
- mii = map_sets.begin();
- // If no parameters are accepted, we do need to check that the argument
- // count is indeed 0, since we won't check that in
- // write_function_instance.
- if (mii->first == 0 && args_type != AT_no_args) {
- switch (args_type) {
- case AT_keyword_args:
- out << " if (!Dtool_CheckNoArgs(args, kwds)) {\n";
- out << " int parameter_count = (int)PyTuple_Size(args);\n";
- out << " if (kwds != nullptr) {\n";
- out << " parameter_count += (int)PyDict_Size(kwds);\n";
- out << " }\n";
- break;
- case AT_varargs:
- out << " if (!Dtool_CheckNoArgs(args)) {\n";
- out << " const int parameter_count = (int)PyTuple_GET_SIZE(args);\n";
- break;
- case AT_single_arg:
- // Shouldn't happen, but let's handle this case nonetheless.
- out << " {\n";
- out << " const int parameter_count = 1;\n";
- break;
- case AT_no_args:
- break;
- case AT_unknown:
- break;
- }
- out << "#ifdef NDEBUG\n";
- error_raise_return(out, 4, return_flags, "TypeError", "function takes no arguments");
- out << "#else\n";
- error_raise_return(out, 4, return_flags, "TypeError",
- methodNameFromCppName(remap, "", false) + "() takes no arguments (%d given)",
- "parameter_count");
- out << "#endif\n";
- out << " }\n";
- } else if (args_type == AT_keyword_args && max_required_args == 1 && mii->first == 1) {
- // Check this to be sure, as we handle the case of only 1 keyword arg in
- // write_function_forset (not using ParseTupleAndKeywords).
- out << " int parameter_count = (int)PyTuple_Size(args);\n"
- " if (kwds != nullptr) {\n"
- " parameter_count += (int)PyDict_Size(kwds);\n"
- " }\n"
- " if (parameter_count != 1) {\n"
- "#ifdef NDEBUG\n";
- error_raise_return(out, 4, return_flags, "TypeError",
- "function takes exactly 1 argument");
- out << "#else\n";
- error_raise_return(out, 4, return_flags, "TypeError",
- methodNameFromCppName(remap, "", false) + "() takes exactly 1 argument (%d given)",
- "parameter_count");
- out << "#endif\n";
- out << " }\n";
- }
- int min_args = min(max_required_args, mii->first);
- write_function_forset(out, mii->second, min_args, mii->first, expected_params, 2,
- coercion_allowed, true, args_type, return_flags, true, !all_nonconst);
- // This block is often unreachable for many functions... maybe we can
- // figure out a way in the future to better determine when it will be and
- // won't be necessary to write this out.
- if (args_type != AT_no_args) {
- out << " if (!_PyErr_OCCURRED()) {\n"
- << " ";
- if ((return_flags & ~RF_pyobject) == RF_err_null) {
- out << "return ";
- }
- out << "Dtool_Raise_BadArgumentsError(\n";
- output_quoted(out, 6, expected_params);
- out << ");\n"
- << " }\n";
- error_return(out, 2, return_flags);
- }
- }
- out << "}\n\n";
- }
- /**
- * Writes the definition for a coerce constructor: a special constructor that
- * is called to implicitly cast a tuple or other type to a desired type. This
- * is done by calling the appropriate constructor or static make() function.
- * Constructors marked with the "explicit" keyword aren't considered, just
- * like in C++.
- *
- * There are usually two coerce constructors: one for const pointers, one for
- * non-const pointers. This is due to the possibility that a static make()
- * function may return a const pointer.
- *
- * There are two variants of this: if the class in question is a
- * ReferenceCount, the coerce constructor takes a reference to a PointerTo or
- * ConstPointerTo to store the converted pointer in. Otherwise, it is a
- * regular pointer, and an additional boolean indicates whether the caller is
- * supposed to call "delete" on the coerced pointer or not.
- *
- * In all cases, the coerce constructor returns a bool indicating whether the
- * conversion was possible. It does not raise exceptions when none of the
- * constructors matched, but just returns false.
- */
- void InterfaceMakerPythonNative::
- write_coerce_constructor(ostream &out, Object *obj, bool is_const) {
- std::map<int, std::set<FunctionRemap *> > map_sets;
- std::map<int, std::set<FunctionRemap *> >::iterator mii;
- int max_required_args = 0;
- // Go through the methods and find appropriate static make() functions.
- for (Function *func : obj->_methods) {
- for (FunctionRemap *remap : func->_remaps) {
- if (is_remap_legal(remap) && remap->_flags & FunctionRemap::F_coerce_constructor) {
- nassertd(!remap->_has_this) continue;
- // It's a static make() function.
- CPPType *return_type = remap->_return_type->get_new_type();
- if (!is_const && TypeManager::is_const_pointer_or_ref(return_type)) {
- // If we're making the non-const coerce constructor, reject this
- // remap if it returns a const pointer.
- continue;
- }
- int min_num_args = remap->get_min_num_args();
- int max_num_args = remap->get_max_num_args();
- // Coerce constructor should take at least one argument.
- nassertd(max_num_args > 0) continue;
- min_num_args = max(min_num_args, 1);
- max_required_args = max(max_num_args, max_required_args);
- for (int i = min_num_args; i <= max_num_args; ++i) {
- map_sets[i].insert(remap);
- }
- size_t parameter_size = remap->_parameters.size();
- map_sets[parameter_size].insert(remap);
- }
- }
- }
- // Now go through the constructors that are suitable for coercion. This
- // excludes copy constructors and ones marked "explicit".
- for (Function *func : obj->_constructors) {
- for (FunctionRemap *remap : func->_remaps) {
- if (is_remap_legal(remap) && remap->_flags & FunctionRemap::F_coerce_constructor) {
- nassertd(!remap->_has_this) continue;
- int min_num_args = remap->get_min_num_args();
- int max_num_args = remap->get_max_num_args();
- // Coerce constructor should take at least one argument.
- nassertd(max_num_args > 0) continue;
- min_num_args = max(min_num_args, 1);
- max_required_args = max(max_num_args, max_required_args);
- for (int i = min_num_args; i <= max_num_args; ++i) {
- map_sets[i].insert(remap);
- }
- size_t parameter_size = remap->_parameters.size();
- map_sets[parameter_size].insert(remap);
- }
- }
- }
- std::string ClassName = make_safe_name(obj->_itype.get_scoped_name());
- std::string cClassName = obj->_itype.get_true_name();
- int return_flags = RF_coerced;
- if (TypeManager::is_reference_count(obj->_itype._cpptype)) {
- // The coercion works slightly different for reference counted types,
- // since we can handle those a bit more nicely by taking advantage of the
- // refcount instead of having to use a boolean to indicate that it should
- // be managed.
- if (is_const) {
- out << "bool Dtool_ConstCoerce_" << ClassName << "(PyObject *args, CPT(" << cClassName << ") &coerced) {\n";
- } else {
- out << "bool Dtool_Coerce_" << ClassName << "(PyObject *args, PT(" << cClassName << ") &coerced) {\n";
- }
- // Note: this relies on the PT() being initialized to NULL. This is
- // currently the case in all invocations, but this may not be true in the
- // future.
- out << " if (DtoolInstance_GetPointer(args, coerced.cheat(), Dtool_" << ClassName << ")) {\n";
- out << " // The argument is already of matching type, no need to coerce.\n";
- if (!is_const) {
- out << " if (!DtoolInstance_IS_CONST(args)) {\n";
- out << " // A non-const instance is required, which this is.\n";
- out << " coerced->ref();\n";
- out << " return true;\n";
- out << " }\n";
- } else {
- out << " coerced->ref();\n";
- out << " return true;\n";
- }
- return_flags |= RF_err_false;
- } else {
- out << cClassName << " *Dtool_Coerce_" << ClassName << "(PyObject *args, " << cClassName << " &coerced) {\n";
- out << " " << cClassName << " *local_this;\n";
- out << " if (DtoolInstance_GetPointer(args, local_this, Dtool_" << ClassName << ")) {\n";
- out << " if (DtoolInstance_IS_CONST(args)) {\n";
- out << " // This is a const object. Make a copy.\n";
- out << " coerced = *(const " << cClassName << " *)local_this;\n";
- out << " return &coerced;\n";
- out << " }\n";
- out << " return local_this;\n";
- return_flags |= RF_err_null;
- }
- out << " }\n\n";
- if (map_sets.empty()) {
- error_return(out, 2, return_flags);
- out << "}\n\n";
- return;
- }
- // Coercion constructors are special cases in that they can take either a
- // single value or a tuple. (They never, however, take a tuple containing a
- // single value.)
- string expected_params;
- mii = map_sets.find(1);
- if (mii != map_sets.end()) {
- out << " if (!PyTuple_Check(args)) {\n";
- out << " PyObject *arg = args;\n";
- write_function_forset(out, mii->second, mii->first, mii->first, expected_params, 4, false, false,
- AT_single_arg, return_flags, true, false);
- if (map_sets.size() == 1) {
- out << " }\n";
- // out << " PyErr_Clear();\n";
- error_return(out, 2, return_flags);
- out << "}\n\n";
- return;
- }
- // We take this one out of the map sets. There's not much value in
- // coercing tuples containing just one value.
- map_sets.erase(mii);
- out << " } else {\n";
- } else {
- out << " if (PyTuple_Check(args)) {\n";
- }
- max_required_args = collapse_default_remaps(map_sets, max_required_args);
- if (map_sets.size() > 1) {
- indent(out, 4) << "switch (PyTuple_GET_SIZE(args)) {\n";
- for (mii = map_sets.begin(); mii != map_sets.end(); ++mii) {
- int max_args = mii->first;
- int min_args = min(max_required_args, max_args);
- // This is not called for tuples containing just one value or no values
- // at all, so we should never have to consider that case.
- if (min_args < 2) {
- min_args = 2;
- }
- nassertd(max_args >= min_args) continue;
- for (int i = min_args; i < max_args; ++i) {
- if (i != 1) {
- indent(out, 6) << "case " << i << ":\n";
- }
- }
- indent(out, 6) << "case " << max_args << ": {\n";
- write_function_forset(out, mii->second, min_args, max_args, expected_params, 8, false, false,
- AT_varargs, return_flags, true, false);
- indent(out, 8) << "break;\n";
- indent(out, 6) << "}\n";
- }
- indent(out, 4) << "}\n";
- } else {
- mii = map_sets.begin();
- int max_args = mii->first;
- int min_args = min(max_required_args, max_args);
- // This is not called for tuples containing just one value or no values at
- // all, so we should never have to consider that case.
- if (min_args < 2) {
- min_args = 2;
- }
- nassertv(max_args >= min_args);
- if (min_args == max_args) {
- indent(out, 4) << "if (PyTuple_GET_SIZE(args) == " << mii->first << ") {\n";
- } else {
- indent(out, 4) << "Py_ssize_t size = PyTuple_GET_SIZE(args);\n";
- // Not sure if this check really does any good. I guess it's a useful
- // early-fail test.
- indent(out, 4) << "if (size >= " << min_args << " && size <= " << max_args << ") {\n";
- }
- write_function_forset(out, mii->second, min_args, max_args, expected_params, 6, false, false,
- AT_varargs, return_flags, true, false);
- indent(out, 4) << "}\n";
- }
- out << " }\n\n";
- // out << " PyErr_Clear();\n";
- error_return(out, 2, return_flags);
- out << "}\n\n";
- }
- /**
- * Special case optimization: if the last map is a subset of the map before
- * it, and the last parameter is only a simple parameter type (that we have
- * special default argument handling for), we can merge the cases. When this
- * happens, we can make use of a special feature of PyArg_ParseTuple for
- * handling of these last few default arguments. This doesn't work well for
- * all types of default expressions, though, hence the need for this elaborate
- * checking mechanism down here, which goes in parallel with the actual
- * optional arg handling logic in write_function_instance.
- *
- * This isn't just to help reduce the amount of generated code; it also
- * enables arbitrary selection of keyword arguments for many functions, ie.
- * for this function:
- *
- * int func(int a=0, int b=0, bool c=false, string d="");
- *
- * Thanks to this mechanism, we can call it like so:
- *
- * func(c=True, d=".")
- *
- * The return value is the minimum of the number of maximum arguments.
- *
- * Sorry, let me try that again: it returns the largest number of arguments
- * for which the overloads will be separated out rather than handled via the
- * special default handling mechanism. Or something.
- *
- * Please don't hate me.
- */
- int InterfaceMakerPythonNative::
- collapse_default_remaps(std::map<int, std::set<FunctionRemap *> > &map_sets,
- int max_required_args) {
- if (map_sets.size() < 1) {
- return max_required_args;
- }
- std::map<int, std::set<FunctionRemap *> >::reverse_iterator rmi, rmi_next;
- rmi = map_sets.rbegin();
- rmi_next = rmi;
- for (++rmi_next; rmi_next != map_sets.rend();) {
- if (std::includes(rmi_next->second.begin(), rmi_next->second.end(),
- rmi->second.begin(), rmi->second.end())) {
- // Check if the nth argument is something we can easily create a default
- // for.
- std::set<FunctionRemap *>::iterator sii;
- for (sii = rmi->second.begin(); sii != rmi->second.end(); ++sii) {
- FunctionRemap *remap = (*sii);
- size_t pn = (size_t)rmi->first;
- if (!remap->_has_this || remap->_type == FunctionRemap::T_constructor) {
- --pn;
- }
- nassertd(pn < remap->_parameters.size()) goto abort_iteration;
- ParameterRemap *param = remap->_parameters[pn]._remap;
- CPPType *type = param->get_new_type();
- if (param->new_type_is_atomic_string()) {
- CPPType *orig_type = param->get_orig_type();
- if (TypeManager::is_char_pointer(orig_type)) {
- } else if (TypeManager::is_wchar_pointer(orig_type)) {
- goto abort_iteration;
- } else if (TypeManager::is_wstring(orig_type)) {
- goto abort_iteration;
- } else if (TypeManager::is_const_ptr_to_basic_string_wchar(orig_type)) {
- goto abort_iteration;
- } else {
- // Regular strings are OK if the default argument is a string
- // literal or the default string constructor, since those are
- // trivial to handle. This actually covers almost all of the
- // cases of default string args.
- CPPExpression::Type expr_type = param->get_default_value()->_type;
- if (expr_type != CPPExpression::T_default_construct &&
- expr_type != CPPExpression::T_string) {
- goto abort_iteration;
- }
- }
- } else if (TypeManager::is_integer(type)) {
- } else if (TypeManager::is_float(type)) {
- } else if (TypeManager::is_const_char_pointer(type)) {
- } else if (TypeManager::is_pointer_to_PyTypeObject(type)) {
- } else if (TypeManager::is_pointer_to_PyStringObject(type)) {
- } else if (TypeManager::is_pointer_to_PyUnicodeObject(type)) {
- } else if (TypeManager::is_pointer_to_PyObject(type)) {
- } else if (TypeManager::is_pointer_to_Py_buffer(type)) {
- goto abort_iteration;
- } else if (TypeManager::is_pointer_to_simple(type)) {
- goto abort_iteration;
- } else if (TypeManager::is_pointer(type)) {
- // I'm allowing other pointer types, but only if the expression
- // happens to evaluate to a numeric constant (which will likely only
- // be NULL). There are too many issues to resolve right now with
- // allowing more complex default expressions, including issues in
- // the C++ parser (but the reader is welcome to give it a try!)
- CPPExpression::Result res = param->get_default_value()->evaluate();
- if (res._type != CPPExpression::RT_integer &&
- res._type != CPPExpression::RT_pointer) {
- goto abort_iteration;
- }
- } else {
- goto abort_iteration;
- }
- }
- // rmi_next has a superset of the remaps in rmi, and we are going to
- // erase rmi_next, so put all the remaps in rmi. rmi->second =
- // rmi_next->second;
- max_required_args = rmi_next->first;
- rmi = rmi_next;
- ++rmi_next;
- } else {
- break;
- }
- }
- abort_iteration:
- // Now erase the other remap sets. Reverse iterators are weird, we first
- // need to get forward iterators and decrement them by one.
- std::map<int, std::set<FunctionRemap *> >::iterator erase_begin, erase_end;
- erase_begin = rmi.base();
- erase_end = map_sets.rbegin().base();
- --erase_begin;
- --erase_end;
- if (erase_begin == erase_end) {
- return max_required_args;
- }
- // We're never erasing the map set with the highest number of args.
- nassertr(erase_end != map_sets.end(), max_required_args);
- // We know erase_begin is a superset of erase_end, but we want all the
- // remaps in erase_end (which we aren't erasing). if (rmi ==
- // map_sets.rbegin()) {
- erase_end->second = erase_begin->second;
- // }
- map_sets.erase(erase_begin, erase_end);
- assert(map_sets.size() >= 1);
- return max_required_args;
- }
- /**
- */
- int get_type_sort(CPPType *type) {
- int answer = 0;
- // printf(" %s\n",type->get_local_name().c_str());
- // The highest numbered one will be checked first.
- if (TypeManager::is_nullptr(type)) {
- return 15;
- } else if (TypeManager::is_pointer_to_Py_buffer(type)) {
- return 14;
- } else if (TypeManager::is_pointer_to_PyTypeObject(type)) {
- return 13;
- } else if (TypeManager::is_pointer_to_PyObject(type)) {
- return 12;
- } else if (TypeManager::is_wstring(type)) {
- return 11;
- } else if (TypeManager::is_wchar_pointer(type)) {
- return 10;
- } else if (TypeManager::is_string(type)) {
- return 9;
- } else if (TypeManager::is_char_pointer(type)) {
- return 8;
- } else if (TypeManager::is_unsigned_longlong(type)) {
- return 7;
- } else if (TypeManager::is_longlong(type)) {
- return 6;
- } else if (TypeManager::is_integer(type) && !TypeManager::is_bool(type)) {
- return 5;
- } else if (TypeManager::is_double(type)) {
- return 4;
- } else if (TypeManager::is_float(type)) {
- return 3;
- } else if (TypeManager::is_pointer_to_simple(type)) {
- return 2;
- } else if (TypeManager::is_bool(type)) {
- return 1;
- } else if (TypeManager::is_pointer(type) ||
- TypeManager::is_reference(type) ||
- TypeManager::is_struct(type)) {
- answer = 20;
- int deepest = 0;
- TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(type)), false);
- InterrogateDatabase *idb = InterrogateDatabase::get_ptr();
- const InterrogateType &itype = idb->get_type(type_index);
- if (itype.is_class() || itype.is_struct()) {
- int num_derivations = itype.number_of_derivations();
- for (int di = 0; di < num_derivations; di++) {
- TypeIndex d_type_Index = itype.get_derivation(di);
- const InterrogateType &d_itype = idb->get_type(d_type_Index);
- int this_one = get_type_sort(d_itype._cpptype);
- if (this_one > deepest) {
- deepest = this_one;
- }
- }
- }
- answer += deepest;
- // printf(" Class Name %s %d\n",itype.get_name().c_str(),answer);
- }
- // printf(" Class Name %s %d\n",itype.get_name().c_str(),answer);
- return answer;
- }
- // The Core sort function for remap calling orders..
- bool RemapCompareLess(FunctionRemap *in1, FunctionRemap *in2) {
- assert(in1 != nullptr);
- assert(in2 != nullptr);
- if (in1->_const_method != in2->_const_method) {
- // Non-const methods should come first.
- return in2->_const_method;
- }
- if (in1->_parameters.size() != in2->_parameters.size()) {
- return (in1->_parameters.size() > in2->_parameters.size());
- }
- int pcount = in1->_parameters.size();
- for (int x = 0; x < pcount; x++) {
- CPPType *orig_type1 = in1->_parameters[x]._remap->get_orig_type();
- CPPType *orig_type2 = in2->_parameters[x]._remap->get_orig_type();
- int pd1 = get_type_sort(orig_type1);
- int pd2 = get_type_sort(orig_type2);
- if (pd1 != pd2) {
- return (pd1 > pd2);
- }
- }
- // ok maybe something to do with return strength..
- return false;
- }
- /**
- * Writes out a set of function wrappers that handle all instances of a
- * particular function with the same number of parameters. (Actually, in some
- * cases relating to default argument handling, this may be called with remaps
- * taking a range of parameters.)
- *
- * min_num_args and max_num_args are the range of parameter counts to respect
- * for these functions. This is important for default argument handling.
- *
- * expected_params is a reference to a string that will be filled in with a
- * list of overloads that this function takes, for displaying in the doc
- * string and error messages.
- *
- * If coercion_allowed is true, it will attempt to convert arguments to the
- * appropriate parameter type using the appropriate Dtool_Coerce function.
- * This means it may write some remaps twice: once without coercion, and then
- * it may go back and write it a second time to try parameter coercion.
- *
- * If report_errors is true, it will print an error and exit when one has
- * occurred, instead of falling back to the next overload. This is
- * automatically disabled when more than one function is passed.
- *
- * args_type indicates whether this function takes no args, a single PyObject*
- * arg, an args tuple, or an args tuple and kwargs dictionary.
- *
- * return_flags indicates which value should be returned from the wrapper
- * function and what should be returned on error.
- *
- * If check_exceptions is false, it will not check if the function raised an
- * exception, except if it took PyObject* arguments. This should NEVER be
- * false for C++ functions that call Python code, since that would block a
- * meaningful exception like SystemExit or KeyboardInterrupt.
- *
- * If verify_const is set, it will write out a check to make sure that non-
- * const functions aren't called for a const "this". This is usually only
- * false when write_function_for_name has already done this check (which it
- * does when *all* remaps are non-const).
- *
- * If first_pexpr is not empty, it represents the preconverted value of the
- * first parameter. This is a special-case hack for one of the slot
- * functions.
- */
- void InterfaceMakerPythonNative::
- write_function_forset(ostream &out,
- const std::set<FunctionRemap *> &remapsin,
- int min_num_args, int max_num_args,
- string &expected_params, int indent_level,
- bool coercion_allowed, bool report_errors,
- ArgsType args_type, int return_flags,
- bool check_exceptions, bool verify_const,
- const string &first_pexpr) {
- if (remapsin.empty()) {
- return;
- }
- FunctionRemap *remap = nullptr;
- std::set<FunctionRemap *>::iterator sii;
- bool all_nonconst = false;
- if (verify_const) {
- // Check if all of the remaps are non-const. If so, we only have to check
- // the constness of the self pointer once, rather than per remap.
- all_nonconst = true;
- for (sii = remapsin.begin(); sii != remapsin.end(); ++sii) {
- remap = (*sii);
- if (!remap->_has_this || remap->_const_method) {
- all_nonconst = false;
- break;
- }
- }
- if (all_nonconst) {
- // Yes, they do. Check that the parameter has the required constness.
- indent(out, indent_level)
- << "if (!DtoolInstance_IS_CONST(self)) {\n";
- indent_level += 2;
- verify_const = false;
- }
- }
- string first_param_name;
- bool same_first_param = false;
- // If there's only one arg and all remaps have the same parameter name, we
- // extract it from the dictionary, so we don't have to call
- // ParseTupleAndKeywords.
- if (first_pexpr.empty() && min_num_args == 1 && max_num_args == 1 &&
- args_type == AT_keyword_args) {
- sii = remapsin.begin();
- remap = (*sii);
- if (remap->_parameters[(int)remap->_has_this]._has_name) {
- first_param_name = remap->_parameters[(int)remap->_has_this]._name;
- same_first_param = true;
- for (++sii; sii != remapsin.end(); ++sii) {
- remap = (*sii);
- if (remap->_parameters[(int)remap->_has_this]._name != first_param_name) {
- same_first_param = false;
- break;
- }
- }
- }
- }
- if (same_first_param) {
- // Yes, they all have the same argument name (or there is only one remap).
- // Extract it from the dict so we don't have to call
- // ParseTupleAndKeywords.
- indent(out, indent_level) << "PyObject *arg;\n";
- indent(out, indent_level) << "if (Dtool_ExtractArg(&arg, args, kwds, \"" << first_param_name << "\")) {\n";
- indent_level += 2;
- args_type = AT_single_arg;
- }
- if (remapsin.size() > 1) {
- // There are multiple different overloads for this number of parameters.
- // Sort them all into order from most-specific to least-specific, then try
- // them one at a time.
- std::vector<FunctionRemap *> remaps (remapsin.begin(), remapsin.end());
- std::sort(remaps.begin(), remaps.end(), RemapCompareLess);
- std::vector<FunctionRemap *>::const_iterator sii;
- int num_coercion_possible = 0;
- sii = remaps.begin();
- while (sii != remaps.end()) {
- remap = *(sii++);
- if (coercion_allowed && is_remap_coercion_possible(remap)) {
- if (++num_coercion_possible == 1 && sii == remaps.end()) {
- // This is the last remap, and it happens to be the only one with
- // coercion possible. So we might as well just break off now, and
- // let this case be handled by the coercion loop, below. BUG: this
- // remap doesn't get listed in expected_params.
- break;
- }
- }
- if (verify_const && (remap->_has_this && !remap->_const_method)) {
- // If it's a non-const method, we only allow a non-const this.
- indent(out, indent_level)
- << "if (!DtoolInstance_IS_CONST(self)) {\n";
- } else {
- indent(out, indent_level)
- << "{\n";
- }
- indent(out, indent_level) << " // -2 ";
- remap->write_orig_prototype(out, 0, false, (max_num_args - min_num_args));
- out << "\n";
- // NB. We don't pass on report_errors here because we want it to
- // silently drop down to the next overload.
- write_function_instance(out, remap, min_num_args, max_num_args,
- expected_params, indent_level + 2,
- false, false, args_type, return_flags,
- check_exceptions, first_pexpr);
- indent(out, indent_level) << "}\n\n";
- }
- // Go through one more time, but allow coercion this time.
- if (coercion_allowed) {
- for (sii = remaps.begin(); sii != remaps.end(); sii ++) {
- remap = (*sii);
- if (!is_remap_coercion_possible(remap)) {
- indent(out, indent_level)
- << "// No coercion possible: ";
- remap->write_orig_prototype(out, 0, false, (max_num_args - min_num_args));
- out << "\n";
- continue;
- }
- if (verify_const && (remap->_has_this && !remap->_const_method)) {
- indent(out, indent_level)
- << "if (!DtoolInstance_IS_CONST(self)) {\n";
- } else {
- indent(out, indent_level)
- << "{\n";
- }
- indent(out, indent_level) << " // -2 ";
- remap->write_orig_prototype(out, 0, false, (max_num_args - min_num_args));
- out << "\n";
- string ignore_expected_params;
- write_function_instance(out, remap, min_num_args, max_num_args,
- ignore_expected_params, indent_level + 2,
- true, false, args_type, return_flags,
- check_exceptions, first_pexpr);
- indent(out, indent_level) << "}\n\n";
- }
- }
- } else {
- // There is only one possible overload with this number of parameters.
- // Just call it.
- sii = remapsin.begin();
- remap = (*sii);
- indent(out, indent_level)
- << "// 1-" ;
- remap->write_orig_prototype(out, 0, false, (max_num_args - min_num_args));
- out << "\n";
- write_function_instance(out, remap, min_num_args, max_num_args,
- expected_params, indent_level,
- coercion_allowed, report_errors,
- args_type, return_flags,
- check_exceptions, first_pexpr);
- }
- // Close the brace we opened earlier.
- if (same_first_param) {
- indent_level -= 2;
- indent(out, indent_level) << "}\n";
- }
- // If we did a const check earlier, and we were asked to report errors,
- // write out an else case raising an exception.
- if (all_nonconst) {
- if (report_errors) {
- indent(out, indent_level - 2)
- << "} else {\n";
- string class_name = remap->_cpptype->get_simple_name();
- ostringstream msg;
- msg << "Cannot call "
- << classNameFromCppName(class_name, false)
- << "." << methodNameFromCppName(remap, class_name, false)
- << "() on a const object.";
- out << "#ifdef NDEBUG\n";
- error_raise_return(out, indent_level, return_flags, "TypeError",
- "non-const method called on const object");
- out << "#else\n";
- error_raise_return(out, indent_level, return_flags, "TypeError", msg.str());
- out << "#endif\n";
- }
- indent_level -= 2;
- indent(out, indent_level) << "}\n";
- }
- }
- /**
- * Writes out the code to handle a a single instance of an overloaded
- * function. This will convert all of the arguments from PyObject* to the
- * appropriate C++ type, call the C++ function, possibly check for errors, and
- * construct a Python wrapper for the return value.
- *
- * return_flags indicates which value should be returned from the wrapper
- * function and what should be returned on error.
- *
- * If coercion_possible is true, it will attempt to convert arguments to the
- * appropriate parameter type using the appropriate Dtool_Coerce function.
- *
- * If report_errors is true, it will print an error and exit when one has
- * occurred, instead of falling back to the next overload. This should be
- * done if it is the only overload.
- *
- * If check_exceptions is false, it will not check if the function raised an
- * exception, except if it took PyObject* arguments. This should NEVER be
- * false for C++ functions that call Python code, since that would block a
- * meaningful exception like SystemExit or KeyboardInterrupt.
- *
- * If first_pexpr is not empty, it represents the preconverted value of the
- * first parameter. This is a special-case hack for one of the slot
- * functions.
- */
- void InterfaceMakerPythonNative::
- write_function_instance(ostream &out, FunctionRemap *remap,
- int min_num_args, int max_num_args,
- string &expected_params, int indent_level,
- bool coercion_possible, bool report_errors,
- ArgsType args_type, int return_flags,
- bool check_exceptions,
- const string &first_pexpr) {
- string format_specifiers;
- string keyword_list;
- string parameter_list;
- string container;
- string type_check;
- string param_name;
- bool has_keywords = false;
- vector_string pexprs;
- LineStream extra_convert;
- ostringstream extra_param_check;
- LineStream extra_cleanup;
- int min_version = 0;
- // This will be set if the function itself is suspected of possibly raising
- // a TypeError.
- bool may_raise_typeerror = false;
- // This will be set to true if one of the things we're about to do *might*
- // raise a TypeError that we may have to clear.
- bool clear_error = false;
- bool is_constructor = (remap->_type == FunctionRemap::T_constructor);
- InterrogateDatabase *idb = InterrogateDatabase::get_ptr();
- // Make one pass through the parameter list. We will output a one-line
- // temporary variable definition for each parameter, while simultaneously
- // building the ParseTuple() function call and also the parameter expression
- // list for call_function().
- expected_params += methodNameFromCppName(remap, "", false);
- expected_params += "(";
- int num_params = 0;
- if ((remap->_flags & FunctionRemap::F_explicit_args) == 0) {
- num_params = max_num_args;
- if (remap->_has_this) {
- num_params += 1;
- }
- if (num_params > (int)remap->_parameters.size()) {
- // Limit to how many parameters this remap actually has.
- num_params = (int)remap->_parameters.size();
- max_num_args = num_params;
- if (remap->_has_this) {
- --max_num_args;
- }
- }
- nassertv(num_params <= (int)remap->_parameters.size());
- }
- bool only_pyobjects = true;
- int pn = 0;
- if (remap->_has_this) {
- // The first parameter is the 'this' parameter.
- string expected_class_name = classNameFromCppName(remap->_cpptype->get_simple_name(), false);
- if (remap->_const_method) {
- expected_params += expected_class_name + " self";
- string class_name = remap->_cpptype->get_local_name(&parser);
- container = "(const " + class_name + "*)local_this";
- } else {
- expected_params += "const " + expected_class_name + " self";
- container = "local_this";
- }
- pexprs.push_back(container);
- ++pn;
- }
- if (!first_pexpr.empty()) {
- if (pn >= num_params) {
- // first_pexpr was passed even though the function takes no arguments.
- nassert_raise("pn < num_params");
- } else {
- // The first actual argument was already converted.
- if (pn > 0) {
- expected_params += ", ";
- }
- expected_params += first_pexpr;
- pexprs.push_back(first_pexpr);
- ++pn;
- }
- }
- if (remap->_flags & FunctionRemap::F_explicit_args) {
- // The function handles the arguments by itself.
- expected_params += "*args";
- pexprs.push_back("args");
- if (args_type == AT_keyword_args) {
- expected_params += ", **kwargs";
- pexprs.push_back("kwds");
- }
- num_params = 0;
- }
- // Now convert (the rest of the) actual arguments, one by one.
- for (; pn < num_params; ++pn) {
- ParameterRemap *param = remap->_parameters[pn]._remap;
- CPPType *orig_type = param->get_orig_type();
- CPPType *type = param->get_new_type();
- CPPExpression *default_value = param->get_default_value();
- param_name = remap->get_parameter_name(pn);
- if (!is_cpp_type_legal(orig_type)) {
- // We can't wrap this. We sometimes get here for default arguments.
- // Just skip this parameter.
- continue;
- }
- // Has this remap been selected to consider optional arguments for this
- // parameter? We can do that by adding a vertical bar to the
- // PyArg_ParseTuple format string, coupled with some extra logic in the
- // argument handling, below.
- bool is_optional = false;
- if (remap->_has_this && !is_constructor) {
- if (pn > min_num_args) {
- is_optional = true;
- if ((pn - 1) == min_num_args) {
- format_specifiers += "|";
- }
- }
- } else {
- if (pn >= min_num_args) {
- is_optional = true;
- if (pn == min_num_args) {
- format_specifiers += "|";
- }
- }
- }
- if (pn > 0) {
- expected_params += ", ";
- }
- // This is the string to convert our local variable to the appropriate C++
- // type. Normally this is just a cast.
- string pexpr_string =
- "(" + orig_type->get_local_name(&parser) + ")" + param_name;
- string default_expr;
- if (is_optional) {
- // If this is an optional argument, PyArg_ParseTuple will leave the
- // variable unchanged if it has been omitted, so we have to initialize
- // it to the desired default expression. Format it.
- ostringstream default_expr_str;
- default_expr_str << " = ";
- default_value->output(default_expr_str, 0, &parser, false);
- default_expr = default_expr_str.str();
- // We should only ever have to consider optional arguments for functions
- // taking a variable number of arguments.
- nassertv(args_type == AT_varargs || args_type == AT_keyword_args);
- }
- string reported_name = remap->_parameters[pn]._name;
- if (!keyword_list.empty()) {
- keyword_list += ", \"" + reported_name + "\"";
- } else {
- keyword_list = "\"" + reported_name + "\"";
- }
- if (remap->_parameters[pn]._has_name) {
- has_keywords = true;
- }
- if (param->new_type_is_atomic_string()) {
- if (TypeManager::is_char_pointer(orig_type)) {
- indent(out, indent_level) << "char ";
- if (TypeManager::is_const_char_pointer(orig_type)) {
- out << "const ";
- }
- out << "*" << param_name << default_expr << ";\n";
- format_specifiers += "z";
- parameter_list += ", &" + param_name;
- expected_params += "str";
- } else if (TypeManager::is_wchar_pointer(orig_type)) {
- indent(out, indent_level) << "#if PY_VERSION_HEX >= 0x03020000\n";
- indent(out, indent_level) << "PyObject *" << param_name << ";\n";
- indent(out, indent_level) << "#else\n";
- indent(out, indent_level) << "PyUnicodeObject *" << param_name << ";\n";
- indent(out, indent_level) << "#endif\n";
- format_specifiers += "U";
- parameter_list += ", &" + param_name;
- extra_convert
- << "#if PY_VERSION_HEX >= 0x03030000\n"
- << "wchar_t *" << param_name << "_str = PyUnicode_AsWideCharString(" << param_name << ", nullptr);\n"
- << "#else"
- << "Py_ssize_t " << param_name << "_len = PyUnicode_GET_SIZE(" << param_name << ");\n"
- << "wchar_t *" << param_name << "_str = (wchar_t *)alloca(sizeof(wchar_t) * (" + param_name + "_len + 1));\n"
- << "PyUnicode_AsWideChar(" << param_name << ", " << param_name << "_str, " << param_name << "_len);\n"
- << param_name << "_str[" << param_name << "_len] = 0;\n"
- << "#endif\n";
- pexpr_string = param_name + "_str";
- extra_cleanup
- << "#if PY_VERSION_HEX >= 0x03030000\n"
- << "PyMem_Free(" << param_name << "_str);\n"
- << "#endif\n";
- expected_params += "unicode";
- } else if (TypeManager::is_wstring(orig_type)) {
- indent(out, indent_level) << "#if PY_VERSION_HEX >= 0x03020000\n";
- indent(out, indent_level) << "PyObject *" << param_name << ";\n";
- indent(out, indent_level) << "#else\n";
- indent(out, indent_level) << "PyUnicodeObject *" << param_name << ";\n";
- indent(out, indent_level) << "#endif\n";
- format_specifiers += "U";
- parameter_list += ", &" + param_name;
- extra_convert
- << "#if PY_VERSION_HEX >= 0x03030000\n"
- << "Py_ssize_t " << param_name << "_len;\n"
- << "wchar_t *" << param_name << "_str = PyUnicode_AsWideCharString("
- << param_name << ", &" << param_name << "_len);\n"
- << "#else\n"
- << "Py_ssize_t " << param_name << "_len = PyUnicode_GET_SIZE(" << param_name << ");\n"
- << "wchar_t *" << param_name << "_str = (wchar_t *)alloca(sizeof(wchar_t) * (" + param_name + "_len + 1));\n"
- << "PyUnicode_AsWideChar(" << param_name << ", " << param_name << "_str, " << param_name << "_len);\n"
- << "#endif\n";
- pexpr_string = param_name + "_str, " + param_name + "_len";
- extra_cleanup
- << "#if PY_VERSION_HEX >= 0x03030000\n"
- << "PyMem_Free(" << param_name << "_str);\n"
- << "#endif\n";
- expected_params += "unicode";
- } else if (TypeManager::is_const_ptr_to_basic_string_wchar(orig_type)) {
- indent(out, indent_level) << "#if PY_VERSION_HEX >= 0x03020000\n";
- indent(out, indent_level) << "PyObject *" << param_name << ";\n";
- indent(out, indent_level) << "#else\n";
- indent(out, indent_level) << "PyUnicodeObject *" << param_name << ";\n";
- indent(out, indent_level) << "#endif\n";
- format_specifiers += "U";
- parameter_list += ", &" + param_name;
- extra_convert
- << "#if PY_VERSION_HEX >= 0x03030000\n"
- << "Py_ssize_t " << param_name << "_len;\n"
- << "wchar_t *" << param_name << "_str = PyUnicode_AsWideCharString("
- << param_name << ", &" << param_name << "_len);\n"
- << "#else\n"
- << "Py_ssize_t " << param_name << "_len = PyUnicode_GET_SIZE(" << param_name << ");\n"
- << "wchar_t *" << param_name << "_str = (wchar_t *)alloca(sizeof(wchar_t) * (" + param_name + "_len + 1));\n"
- << "PyUnicode_AsWideChar(" << param_name << ", " << param_name << "_str, " << param_name << "_len);\n"
- << "#endif\n";
- pexpr_string = param_name + "_str, " + param_name + "_len";
- extra_cleanup
- << "#if PY_VERSION_HEX >= 0x03030000\n"
- << "PyMem_Free(" << param_name << "_str);\n"
- << "#endif\n";
- expected_params += "unicode";
- } else { // A regular string.
- if (is_optional) {
- CPPExpression::Type expr_type = default_value->_type;
- if (expr_type == CPPExpression::T_default_construct) {
- // The default string constructor yields an empty string.
- indent(out, indent_level) << "const char *" << param_name << "_str = \"\";\n";
- indent(out, indent_level) << "Py_ssize_t " << param_name << "_len = 0;\n";
- } else {
- // We only get here for string literals, so this should be fine
- indent(out, indent_level) << "const char *" << param_name << "_str"
- << default_expr << ";\n";
- indent(out, indent_level) << "Py_ssize_t " << param_name << "_len = "
- << default_value->_str.size() << ";\n";
- }
- } else {
- indent(out, indent_level) << "const char *" << param_name << "_str = nullptr;\n";
- indent(out, indent_level) << "Py_ssize_t " << param_name << "_len;\n";
- }
- if (args_type == AT_single_arg) {
- out << "#if PY_MAJOR_VERSION >= 3\n";
- indent(out, indent_level)
- << param_name << "_str = PyUnicode_AsUTF8AndSize(arg, &"
- << param_name << "_len);\n";
- out << "#else\n"; // NB. PyString_AsStringAndSize also accepts a PyUnicode.
- indent(out, indent_level) << "if (PyString_AsStringAndSize(arg, (char **)&"
- << param_name << "_str, &" << param_name << "_len) == -1) {\n";
- indent(out, indent_level + 2) << param_name << "_str = nullptr;\n";
- indent(out, indent_level) << "}\n";
- out << "#endif\n";
- extra_param_check << " && " << param_name << "_str != nullptr";
- } else {
- format_specifiers += "s#";
- parameter_list += ", &" + param_name
- + "_str, &" + param_name + "_len";
- }
- //if (TypeManager::is_const_ptr_to_basic_string_char(orig_type)) {
- // pexpr_string = "&std::string(" + param_name + "_str, " + param_name + "_len)";
- //} else {
- pexpr_string = param_name + "_str, " + param_name + "_len";
- //}
- expected_params += "str";
- }
- // Remember to clear the TypeError that any of the above methods raise.
- clear_error = true;
- only_pyobjects = false;
- } else if (TypeManager::is_vector_unsigned_char(type)) {
- indent(out, indent_level) << "unsigned char *" << param_name << "_str = nullptr;\n";
- indent(out, indent_level) << "Py_ssize_t " << param_name << "_len;\n";
- if (args_type == AT_single_arg) {
- extra_param_check << " && PyBytes_AsStringAndSize(arg, (char **)&"
- << param_name << "_str, &" << param_name << "_len) >= 0";
- } else {
- format_specifiers += "\" FMTCHAR_BYTES \"#";
- parameter_list += ", &" + param_name + "_str, &" + param_name + "_len";
- }
- pexpr_string = type->get_local_name(&parser);
- pexpr_string += "(" + param_name + "_str, " + param_name + "_str + " + param_name + "_len" + ")";
- expected_params += "bytes";
- // Remember to clear the TypeError that any of the above methods raise.
- clear_error = true;
- only_pyobjects = false;
- } else if (TypeManager::is_scoped_enum(type)) {
- if (args_type == AT_single_arg) {
- param_name = "arg";
- } else {
- indent(out, indent_level) << "PyObject *" << param_name;
- if (default_value != nullptr) {
- out << " = nullptr";
- }
- out << ";\n";
- format_specifiers += "O";
- parameter_list += ", &" + param_name;
- }
- CPPEnumType *enum_type = (CPPEnumType *)TypeManager::unwrap(type);
- CPPType *underlying_type = enum_type->get_underlying_type();
- underlying_type = TypeManager::unwrap_const(underlying_type);
- //indent(out, indent_level);
- //underlying_type->output_instance(out, param_name + "_val", &parser);
- //out << default_expr << ";\n";
- extra_convert << "long " << param_name << "_val";
- if (default_value != nullptr) {
- extra_convert << " = (long)";
- default_value->output(extra_convert, 0, &parser, false);
- extra_convert <<
- ";\nif (" << param_name << " != nullptr) {\n"
- " " << param_name << "_val = Dtool_EnumValue_AsLong(" + param_name + ");\n"
- "}";
- } else {
- extra_convert
- << ";\n"
- << param_name << "_val = Dtool_EnumValue_AsLong(" + param_name + ");\n";
- }
- pexpr_string = "(" + enum_type->get_local_name(&parser) + ")" + param_name + "_val";
- expected_params += classNameFromCppName(enum_type->get_simple_name(), false);
- extra_param_check << " && " << param_name << "_val != -1";
- clear_error = true;
- } else if (TypeManager::is_bool(type)) {
- if (args_type == AT_single_arg) {
- param_name = "arg";
- } else {
- indent(out, indent_level) << "PyObject *" << param_name;
- if (is_optional) {
- CPPExpression::Result res = default_value->evaluate();
- if (res._type != CPPExpression::RT_error) {
- // It's a compile-time constant. Write Py_True or Py_False.
- out << " = " << (res.as_boolean() ? "Py_True" : "Py_False");
- } else {
- // Select Py_True or Py_False at runtime.
- out << " = (";
- default_value->output(out, 0, &parser, false);
- out << ") ? Py_True : Py_False";
- }
- }
- out << ";\n";
- format_specifiers += "O";
- parameter_list += ", &" + param_name;
- }
- pexpr_string = "(PyObject_IsTrue(" + param_name + ") != 0)";
- expected_params += "bool";
- } else if (TypeManager::is_nullptr(type)) {
- if (args_type == AT_single_arg) {
- type_check = "arg == Py_None";
- param_name = "arg";
- } else {
- indent(out, indent_level) << "PyObject *" << param_name << default_expr << ";\n";
- extra_param_check << " && " << param_name << " == Py_None";
- format_specifiers += "O";
- parameter_list += ", &" + param_name;
- }
- pexpr_string = "nullptr";
- expected_params += "NoneType";
- } else if (TypeManager::is_char(type)) {
- indent(out, indent_level) << "char " << param_name << default_expr << ";\n";
- format_specifiers += "c";
- parameter_list += ", &" + param_name;
- // extra_param_check << " && isascii(" << param_name << ")";
- pexpr_string = "(char) " + param_name;
- expected_params += "char";
- only_pyobjects = false;
- } else if (TypeManager::is_wchar(type)) {
- indent(out, indent_level) << "#if PY_VERSION_HEX >= 0x03020000\n";
- indent(out, indent_level) << "PyObject *" << param_name << ";\n";
- indent(out, indent_level) << "#else\n";
- indent(out, indent_level) << "PyUnicodeObject *" << param_name << ";\n";
- indent(out, indent_level) << "#endif\n";
- format_specifiers += "U";
- parameter_list += ", &" + param_name;
- // We tell it to copy 2 characters, but make sure it only copied one, as
- // a trick to check for the proper length in one go.
- extra_convert << "wchar_t " << param_name << "_chars[2];\n";
- extra_param_check << " && PyUnicode_AsWideChar(" << param_name << ", " << param_name << "_chars, 2) == 1";
- pexpr_string = param_name + "_chars[0]";
- expected_params += "unicode char";
- only_pyobjects = false;
- clear_error = true;
- } else if (TypeManager::is_ssize(type)) {
- indent(out, indent_level) << "Py_ssize_t " << param_name << default_expr << ";\n";
- format_specifiers += "n";
- parameter_list += ", &" + param_name;
- expected_params += "int";
- only_pyobjects = false;
- } else if (TypeManager::is_size(type)) {
- if (args_type == AT_single_arg) {
- type_check = "PyLongOrInt_Check(arg)";
- extra_convert <<
- "size_t arg_val = PyLongOrInt_AsSize_t(arg);\n"
- "#ifndef NDEBUG\n"
- "if (arg_val == (size_t)-1 && _PyErr_OCCURRED()) {\n";
- error_return(extra_convert, 2, return_flags);
- extra_convert <<
- "}\n"
- "#endif\n";
- pexpr_string = "arg_val";
- } else {
- // It certainly isn't the exact same thing as size_t, but Py_ssize_t
- // should at least be the same size. The problem with mapping this to
- // unsigned int is that that doesn't work well on 64-bit systems, on
- // which size_t is a 64-bit integer.
- indent(out, indent_level) << "Py_ssize_t " << param_name << default_expr << ";\n";
- format_specifiers += "n";
- parameter_list += ", &" + param_name;
- extra_convert
- << "#ifndef NDEBUG\n"
- << "if (" << param_name << " < 0) {\n";
- error_raise_return(extra_convert, 2, return_flags, "OverflowError",
- "can't convert negative value %zd to size_t",
- param_name);
- extra_convert
- << "}\n"
- << "#endif\n";
- }
- expected_params += "int";
- only_pyobjects = false;
- } else if (TypeManager::is_longlong(type)) {
- // It's not trivial to do overflow checking for a long long, so we
- // simply don't do it.
- if (TypeManager::is_unsigned_longlong(type)) {
- indent(out, indent_level) << "unsigned PY_LONG_LONG " << param_name << default_expr << ";\n";
- format_specifiers += "K";
- } else {
- indent(out, indent_level) << "PY_LONG_LONG " << param_name << default_expr << ";\n";
- format_specifiers += "L";
- }
- parameter_list += ", &" + param_name;
- expected_params += "long";
- only_pyobjects = false;
- } else if (TypeManager::is_unsigned_short(type) ||
- TypeManager::is_unsigned_char(type) || TypeManager::is_signed_char(type)) {
- if (args_type == AT_single_arg) {
- type_check = "PyLongOrInt_Check(arg)";
- extra_convert
- << "long " << param_name << " = PyLongOrInt_AS_LONG(arg);\n";
- pexpr_string = "(" + type->get_local_name(&parser) + ")" + param_name;
- } else {
- indent(out, indent_level) << "long " << param_name << default_expr << ";\n";
- format_specifiers += "l";
- parameter_list += ", &" + param_name;
- }
- // The "H" format code, unlike "h", does not do overflow checking, so we
- // have to do it ourselves (except in release builds).
- extra_convert
- << "#ifndef NDEBUG\n";
- if (TypeManager::is_unsigned_short(type)) {
- extra_convert << "if (" << param_name << " < 0 || " << param_name << " > USHRT_MAX) {\n";
- error_raise_return(extra_convert, 2, return_flags, "OverflowError",
- "value %ld out of range for unsigned short integer",
- param_name);
- } else if (TypeManager::is_unsigned_char(type)) {
- extra_convert << "if (" << param_name << " < 0 || " << param_name << " > UCHAR_MAX) {\n";
- error_raise_return(extra_convert, 2, return_flags, "OverflowError",
- "value %ld out of range for unsigned byte",
- param_name);
- } else {
- extra_convert << "if (" << param_name << " < SCHAR_MIN || " << param_name << " > SCHAR_MAX) {\n";
- error_raise_return(extra_convert, 2, return_flags, "OverflowError",
- "value %ld out of range for signed byte",
- param_name);
- }
- extra_convert
- << "}\n"
- << "#endif\n";
- expected_params += "int";
- only_pyobjects = false;
- } else if (TypeManager::is_short(type)) {
- if (args_type == AT_single_arg) {
- type_check = "PyLongOrInt_Check(arg)";
- // Perform overflow checking in debug builds.
- extra_convert
- << "long arg_val = PyLongOrInt_AS_LONG(arg);\n"
- << "#ifndef NDEBUG\n"
- << "if (arg_val < SHRT_MIN || arg_val > SHRT_MAX) {\n";
- error_raise_return(extra_convert, 2, return_flags, "OverflowError",
- "value %ld out of range for signed short integer",
- "arg_val");
- extra_convert
- << "}\n"
- << "#endif\n";
- pexpr_string = "(" + type->get_local_name(&parser) + ")arg_val";
- } else {
- indent(out, indent_level) << "short " << param_name << default_expr << ";\n";
- format_specifiers += "h";
- parameter_list += ", &" + param_name;
- }
- expected_params += "int";
- only_pyobjects = false;
- } else if (TypeManager::is_unsigned_integer(type)) {
- if (args_type == AT_single_arg) {
- // Windows has 32-bit longs, and Python 2 stores a C long for PyInt
- // internally, so a PyInt wouldn't cover the whole range; that's why
- // we have to accept PyLong as well here.
- type_check = "PyLongOrInt_Check(arg)";
- extra_convert
- << "unsigned long " << param_name << " = PyLong_AsUnsignedLong(arg);\n";
- pexpr_string = "(" + type->get_local_name(&parser) + ")" + param_name;
- } else {
- indent(out, indent_level) << "unsigned long " << param_name << default_expr << ";\n";
- format_specifiers += "k";
- parameter_list += ", &" + param_name;
- }
- // The "I" format code, unlike "i", does not do overflow checking, so we
- // have to do it ourselves (in debug builds). Note that Python 2 stores
- // longs internally, for ints, so we don't do it for Python 2 on
- // Windows, where longs are the same size as ints. BUG: does not catch
- // negative values on Windows when going through the PyArg_ParseTuple
- // case.
- if (!TypeManager::is_long(type)) {
- extra_convert
- << "#if (SIZEOF_LONG > SIZEOF_INT) && !defined(NDEBUG)\n"
- << "if (" << param_name << " > UINT_MAX) {\n";
- error_raise_return(extra_convert, 2, return_flags, "OverflowError",
- "value %lu out of range for unsigned integer",
- param_name);
- extra_convert
- << "}\n"
- << "#endif\n";
- }
- expected_params += "int";
- only_pyobjects = false;
- } else if (TypeManager::is_long(type)) {
- // Signed longs are equivalent to Python's int type.
- if (args_type == AT_single_arg) {
- pexpr_string = "PyLongOrInt_AS_LONG(arg)";
- type_check = "PyLongOrInt_Check(arg)";
- } else {
- indent(out, indent_level) << "long " << param_name << default_expr << ";\n";
- format_specifiers += "l";
- parameter_list += ", &" + param_name;
- }
- expected_params += "int";
- only_pyobjects = false;
- } else if (TypeManager::is_integer(type)) {
- if (args_type == AT_single_arg) {
- type_check = "PyLongOrInt_Check(arg)";
- // Perform overflow checking in debug builds. Note that Python 2
- // stores longs internally, for ints, so we don't do it on Windows,
- // where longs are the same size as ints.
- extra_convert
- << "long arg_val = PyLongOrInt_AS_LONG(arg);\n"
- << "#if (SIZEOF_LONG > SIZEOF_INT) && !defined(NDEBUG)\n"
- << "if (arg_val < INT_MIN || arg_val > INT_MAX) {\n";
- error_raise_return(extra_convert, 2, return_flags, "OverflowError",
- "value %ld out of range for signed integer",
- "arg_val");
- extra_convert
- << "}\n"
- << "#endif\n";
- pexpr_string = "(" + type->get_local_name(&parser) + ")arg_val";
- } else {
- indent(out, indent_level) << "int " << param_name << default_expr << ";\n";
- format_specifiers += "i";
- parameter_list += ", &" + param_name;
- }
- expected_params += "int";
- only_pyobjects = false;
- } else if (TypeManager::is_double(type)) {
- if (args_type == AT_single_arg) {
- pexpr_string = "PyFloat_AsDouble(arg)";
- type_check = "PyNumber_Check(arg)";
- } else {
- indent(out, indent_level) << "double " << param_name << default_expr << ";\n";
- format_specifiers += "d";
- parameter_list += ", &" + param_name;
- }
- expected_params += "double";
- only_pyobjects = false;
- } else if (TypeManager::is_float(type)) {
- if (args_type == AT_single_arg) {
- pexpr_string = "(" + type->get_local_name(&parser) + ")PyFloat_AsDouble(arg)";
- type_check = "PyNumber_Check(arg)";
- } else {
- indent(out, indent_level) << "float " << param_name << default_expr << ";\n";
- format_specifiers += "f";
- parameter_list += ", &" + param_name;
- }
- expected_params += "float";
- only_pyobjects = false;
- } else if (TypeManager::is_const_char_pointer(type)) {
- indent(out, indent_level) << "const char *" << param_name << default_expr << ";\n";
- format_specifiers += "z";
- parameter_list += ", &" + param_name;
- expected_params += "buffer";
- only_pyobjects = false;
- } else if (TypeManager::is_pointer_to_PyTypeObject(type)) {
- if (args_type == AT_single_arg) {
- param_name = "arg";
- } else {
- indent(out, indent_level) << "PyObject *" << param_name << default_expr << ";\n";
- format_specifiers += "O";
- parameter_list += ", &" + param_name;
- pexpr_string = param_name;
- }
- extra_param_check << " && PyType_Check(" << param_name << ")";
- pexpr_string = "(PyTypeObject *)" + param_name;
- expected_params += "type";
- // It's reasonable to assume that a function taking a PyTypeObject might
- // also throw a TypeError if the type is incorrect.
- may_raise_typeerror = true;
- } else if (TypeManager::is_pointer_to_PyStringObject(type)) {
- if (args_type == AT_single_arg) {
- // This is a single-arg function, so there's no need to convert
- // anything.
- param_name = "arg";
- type_check = "PyString_Check(arg)";
- pexpr_string = "(PyStringObject *)" + param_name;
- } else {
- indent(out, indent_level) << "PyStringObject *" << param_name << default_expr << ";\n";
- format_specifiers += "S";
- parameter_list += ", &" + param_name;
- pexpr_string = param_name;
- }
- expected_params += "string";
- } else if (TypeManager::is_pointer_to_PyUnicodeObject(type)) {
- if (args_type == AT_single_arg) {
- // This is a single-arg function, so there's no need to convert
- // anything.
- param_name = "arg";
- type_check = "PyUnicode_Check(arg)";
- pexpr_string = "(PyUnicodeObject *)" + param_name;
- } else {
- indent(out, indent_level) << "PyUnicodeObject *" << param_name << default_expr << ";\n";
- format_specifiers += "U";
- parameter_list += ", &" + param_name;
- pexpr_string = param_name;
- }
- expected_params += "unicode";
- } else if (TypeManager::is_pointer_to_PyObject(type)) {
- if (args_type == AT_single_arg) {
- // This is a single-arg function, so there's no need to convert
- // anything.
- param_name = "arg";
- } else {
- indent(out, indent_level) << "PyObject *" << param_name << default_expr << ";\n";
- format_specifiers += "O";
- parameter_list += ", &" + param_name;
- }
- pexpr_string = param_name;
- expected_params += "object";
- // It's reasonable to assume that a function taking a PyObject might
- // also throw a TypeError if the type is incorrect.
- may_raise_typeerror = true;
- } else if (TypeManager::is_pointer_to_Py_buffer(type)) {
- min_version = 0x02060000; // Only support this remap in version 2.6+.
- if (args_type == AT_single_arg) {
- param_name = "arg";
- } else {
- indent(out, indent_level) << "PyObject *" << param_name << ";\n";
- format_specifiers += "O";
- parameter_list += ", &" + param_name;
- }
- indent(out, indent_level) << "Py_buffer " << param_name << "_view;\n";
- extra_param_check << " && PyObject_GetBuffer("
- << param_name << ", &"
- << param_name << "_view, PyBUF_FULL) == 0";
- pexpr_string = "&" + param_name + "_view";
- extra_cleanup << "PyBuffer_Release(&" << param_name << "_view);\n";
- expected_params += "buffer";
- may_raise_typeerror = true;
- clear_error = true;
- } else if (TypeManager::is_pointer_to_simple(type)) {
- if (args_type == AT_single_arg) {
- param_name = "arg";
- } else {
- indent(out, indent_level) << "PyObject *" << param_name << ";\n";
- format_specifiers += "O";
- parameter_list += ", &" + param_name;
- }
- indent(out, indent_level) << "Py_buffer " << param_name << "_view;\n";
- // Unravel the type to determine its properties.
- int array_len = -1;
- bool is_const = true;
- CPPSimpleType *simple = nullptr;
- CPPType *unwrap = TypeManager::unwrap_const_reference(type);
- if (unwrap != nullptr) {
- CPPArrayType *array_type = unwrap->as_array_type();
- CPPPointerType *pointer_type = unwrap->as_pointer_type();
- if (array_type != nullptr) {
- if (array_type->_bounds != nullptr) {
- array_len = array_type->_bounds->evaluate().as_integer();
- }
- unwrap = array_type->_element_type;
- } else if (pointer_type != nullptr) {
- unwrap = pointer_type->_pointing_at;
- }
- CPPConstType *const_type = unwrap->as_const_type();
- if (const_type != nullptr) {
- unwrap = const_type->_wrapped_around;
- } else {
- is_const = false;
- }
- while (unwrap->get_subtype() == CPPDeclaration::ST_typedef) {
- unwrap = unwrap->as_typedef_type()->_type;
- }
- simple = unwrap->as_simple_type();
- }
- // Determine the format, so we can check the type of the buffer we get.
- char format_chr = 'B';
- switch (simple->_type) {
- case CPPSimpleType::T_char:
- if (simple->_flags & CPPSimpleType::F_unsigned) {
- format_chr = 'B';
- } else if (simple->_flags & CPPSimpleType::F_signed) {
- format_chr = 'b';
- } else {
- format_chr = 'c';
- }
- break;
- case CPPSimpleType::T_int:
- if (simple->_flags & CPPSimpleType::F_longlong) {
- format_chr = 'q';
- } else if (simple->_flags & CPPSimpleType::F_long) {
- format_chr = 'l';
- } else if (simple->_flags & CPPSimpleType::F_short) {
- format_chr = 'h';
- } else {
- format_chr = 'i';
- }
- if (simple->_flags & CPPSimpleType::F_unsigned) {
- format_chr &= 0x5f; // Uppercase
- }
- break;
- case CPPSimpleType::T_float:
- format_chr = 'f';
- break;
- case CPPSimpleType::T_double:
- format_chr = 'd';
- break;
- default:
- nout << "Warning: cannot determine buffer format string for type "
- << type->get_local_name(&parser)
- << " (simple type " << *simple << ")\n";
- extra_param_check << " && false";
- }
- const char *flags;
- if (format_chr == 'B') {
- if (is_const) {
- flags = "PyBUF_SIMPLE";
- } else {
- flags = "PyBUF_WRITABLE";
- }
- } else if (is_const) {
- flags = "PyBUF_FORMAT";
- } else {
- flags = "PyBUF_FORMAT | PyBUF_WRITABLE";
- }
- extra_param_check << " && PyObject_GetBuffer(" << param_name << ", &"
- << param_name << "_view, " << flags << ") == 0";
- if (format_chr != 'B') {
- extra_param_check
- << " && " << param_name << "_view.format[0] == '" << format_chr << "'"
- << " && " << param_name << "_view.format[1] == 0";
- }
- if (array_len != -1) {
- extra_param_check
- << " && " << param_name << "_view.len == " << array_len;
- }
- pexpr_string = "(" + simple->get_local_name(&parser) + " *)" +
- param_name + "_view.buf";
- extra_cleanup << "PyBuffer_Release(&" << param_name << "_view);\n";
- expected_params += "buffer";
- clear_error = true;
- } else if (TypeManager::is_pointer(type)) {
- CPPType *obj_type = TypeManager::unwrap(TypeManager::resolve_type(type));
- bool const_ok = !TypeManager::is_non_const_pointer_or_ref(orig_type);
- if (TypeManager::is_const_pointer_or_ref(orig_type)) {
- expected_params += "const ";
- // } else { expected_params += "non-const ";
- }
- string expected_class_name = classNameFromCppName(obj_type->get_simple_name(), false);
- expected_params += expected_class_name;
- if (args_type == AT_single_arg) {
- param_name = "arg";
- } else {
- indent(out, indent_level) << "PyObject *" << param_name;
- if (is_optional) {
- out << " = nullptr";
- }
- out << ";\n";
- format_specifiers += "O";
- parameter_list += ", &" + param_name;
- }
- // If the default value is NULL, we also accept a None value.
- bool maybe_none = false;
- if (default_value != nullptr && (return_flags & RF_coerced) == 0) {
- CPPExpression::Result res = param->get_default_value()->evaluate();
- if (res._type == CPPExpression::RT_integer ||
- res._type == CPPExpression::RT_pointer) {
- if (res.as_integer() == 0) {
- maybe_none = true;
- }
- }
- }
- string class_name = obj_type->get_local_name(&parser);
- // need to a forward scope for this class..
- if (!isExportThisRun(obj_type)) {
- _external_imports.insert(TypeManager::resolve_type(obj_type));
- }
- string this_class_name;
- string method_prefix;
- if (remap->_cpptype) {
- this_class_name = remap->_cpptype->get_simple_name();
- method_prefix = classNameFromCppName(this_class_name, false) + string(".");
- }
- if (coercion_possible &&
- has_coerce_constructor(obj_type->as_struct_type())) {
- // Call the coercion function directly, which will try to extract the
- // pointer directly before trying coercion.
- string coerce_call;
- if (TypeManager::is_reference_count(obj_type)) {
- // We use a PointerTo to handle the management here. It's cleaner
- // that way.
- if (default_expr == " = 0" || default_expr == " = nullptr") {
- default_expr.clear();
- }
- if (TypeManager::is_const_pointer_to_anything(type)) {
- extra_convert
- << "CPT(" << class_name << ") " << param_name << "_this"
- << default_expr << ";\n";
- coerce_call = "Dtool_ConstCoerce_" + make_safe_name(class_name) +
- "(" + param_name + ", " + param_name + "_this)";
- } else {
- extra_convert
- << "PT(" << class_name << ") " << param_name << "_this"
- << default_expr << ";\n";
- coerce_call = "Dtool_Coerce_" + make_safe_name(class_name) +
- "(" + param_name + ", " + param_name + "_this)";
- }
- // Use move constructor when available for functions that take an
- // actual PointerTo. This eliminates an unref()ref() pair.
- pexpr_string = "std::move(" + param_name + "_this)";
- } else {
- // This is a move-assignable type, such as TypeHandle or LVecBase4.
- obj_type->output_instance(extra_convert, param_name + "_local", &parser);
- extra_convert << ";\n";
- type->output_instance(extra_convert, param_name + "_this", &parser);
- if (is_optional && maybe_none) {
- extra_convert
- << default_expr << ";\n"
- << "if (" << param_name << " != nullptr && " << param_name << " != Py_None) {\n"
- << " " << param_name << "_this";
- } else if (is_optional) {
- extra_convert
- << default_expr << ";\n"
- << "if (" << param_name << " != nullptr) {\n"
- << " " << param_name << "_this";
- } else if (maybe_none) {
- extra_convert
- << " = nullptr;\n"
- << "if (" << param_name << " != Py_None) {\n"
- << " " << param_name << "_this";
- }
- extra_convert << " = Dtool_Coerce_" + make_safe_name(class_name) +
- "(" + param_name + ", " + param_name + "_local);\n";
- if (is_optional || maybe_none) {
- extra_convert << "}\n";
- }
- coerce_call = "(" + param_name + "_this != nullptr)";
- pexpr_string = param_name + "_this";
- }
- if (report_errors) {
- // We were asked to report any errors. Let's do it.
- if (is_optional && maybe_none) {
- extra_convert << "if (" << param_name << " != nullptr && " << param_name << " != Py_None && !" << coerce_call << ") {\n";
- } else if (is_optional) {
- extra_convert << "if (" << param_name << " != nullptr && !" << coerce_call << ") {\n";
- } else if (maybe_none) {
- extra_convert << "if (" << param_name << " != Py_None && !" << coerce_call << ") {\n";
- } else {
- extra_convert << "if (!" << coerce_call << ") {\n";
- }
- // Display error like: Class.func() argument 0 must be A, not B
- if ((return_flags & ~RF_pyobject) == RF_err_null) {
- // Dtool_Raise_ArgTypeError returns NULL already
- extra_convert << " return ";
- } else {
- extra_convert << " ";
- }
- extra_convert
- << "Dtool_Raise_ArgTypeError(" << param_name << ", "
- << pn << ", \"" << method_prefix
- << methodNameFromCppName(remap, this_class_name, false)
- << "\", \"" << expected_class_name << "\");\n";
- if ((return_flags & ~RF_pyobject) != RF_err_null) {
- error_return(extra_convert, 2, return_flags);
- }
- extra_convert << "}\n";
- } else if (is_optional && maybe_none) {
- extra_param_check << " && (" << param_name << " == nullptr || " << param_name << " == Py_None || " << coerce_call << ")";
- } else if (is_optional) {
- extra_param_check << " && (" << param_name << " == nullptr || " << coerce_call << ")";
- } else if (maybe_none) {
- extra_param_check << " && (" << param_name << " == Py_None || " << coerce_call << ")";
- } else {
- extra_param_check << " && " << coerce_call;
- }
- } else { // The regular, non-coercion case.
- type->output_instance(extra_convert, param_name + "_this", &parser);
- if (is_optional && maybe_none) {
- extra_convert
- << default_expr << ";\n"
- << "if (" << param_name << " != nullptr && " << param_name << " != Py_None) {\n"
- << " " << param_name << "_this";
- } else if (is_optional) {
- extra_convert
- << default_expr << ";\n"
- << "if (" << param_name << " != nullptr) {\n"
- << " " << param_name << "_this";
- } else if (maybe_none) {
- extra_convert
- << " = nullptr;\n"
- << "if (" << param_name << " != Py_None) {\n"
- << " " << param_name << "_this";
- }
- if (const_ok && !report_errors) {
- // This function does the same thing in this case and is slightly
- // simpler. But maybe we should just reorganize these functions
- // entirely?
- extra_convert << " = nullptr;\n";
- int indent_level = (is_optional || maybe_none) ? 2 : 0;
- indent(extra_convert, indent_level)
- << "DtoolInstance_GetPointer(" << param_name
- << ", " << param_name << "_this"
- << ", *Dtool_Ptr_" << make_safe_name(class_name)
- << ");\n";
- } else {
- extra_convert << std::boolalpha
- << " = (" << class_name << " *)"
- << "DTOOL_Call_GetPointerThisClass(" << param_name
- << ", Dtool_Ptr_" << make_safe_name(class_name)
- << ", " << pn << ", \""
- << method_prefix << methodNameFromCppName(remap, this_class_name, false)
- << "\", " << const_ok << ", " << report_errors << ");\n";
- }
- if (is_optional && maybe_none) {
- extra_convert << "}\n";
- extra_param_check << " && (" << param_name << " == nullptr || " << param_name << " == Py_None || " << param_name << "_this != nullptr)";
- } else if (is_optional) {
- extra_convert << "}\n";
- extra_param_check << " && (" << param_name << " == nullptr || " << param_name << "_this != nullptr)";
- } else if (maybe_none) {
- extra_convert << "}\n";
- extra_param_check << " && (" << param_name << " == Py_None || " << param_name << "_this != nullptr)";
- } else {
- extra_param_check << " && " << param_name << "_this != nullptr";
- }
- pexpr_string = param_name + "_this";
- }
- } else {
- // Ignore a parameter.
- if (args_type == AT_single_arg) {
- param_name = "arg";
- } else {
- indent(out, indent_level) << "PyObject *" << param_name << ";\n";
- format_specifiers += "O";
- parameter_list += ", &" + param_name;
- }
- expected_params += "any";
- }
- if (!reported_name.empty()) {
- expected_params += " " + reported_name;
- }
- pexprs.push_back(pexpr_string);
- }
- expected_params += ")\n";
- if (min_version > 0) {
- out << "#if PY_VERSION_HEX >= 0x" << hex << min_version << dec << "\n";
- }
- // Track how many curly braces we've opened.
- short open_scopes = 0;
- if (!type_check.empty() && args_type == AT_single_arg) {
- indent(out, indent_level)
- << "if (" << type_check << ") {\n";
- ++open_scopes;
- indent_level += 2;
- } else if (!format_specifiers.empty()) {
- string method_name = methodNameFromCppName(remap, "", false);
- switch (args_type) {
- case AT_keyword_args:
- // Wrapper takes a varargs tuple and a keyword args dict.
- if (has_keywords) {
- if (only_pyobjects && max_num_args == 1) {
- // But we are only expecting one object arg, which is an easy common
- // case we have implemented ourselves.
- if (min_num_args == 1) {
- indent(out, indent_level)
- << "if (Dtool_ExtractArg(&" << param_name << ", args, kwds, " << keyword_list << ")) {\n";
- } else {
- indent(out, indent_level)
- << "if (Dtool_ExtractOptionalArg(&" << param_name << ", args, kwds, " << keyword_list << ")) {\n";
- }
- } else {
- // We have to use the more expensive PyArg_ParseTupleAndKeywords.
- clear_error = true;
- indent(out, indent_level)
- << "static const char *keyword_list[] = {" << keyword_list << ", nullptr};\n";
- indent(out, indent_level)
- << "if (PyArg_ParseTupleAndKeywords(args, kwds, \""
- << format_specifiers << ":" << method_name
- << "\", (char **)keyword_list" << parameter_list << ")) {\n";
- }
- } else if (only_pyobjects) {
- // This function actually has no named parameters, so let's not take
- // any keyword arguments.
- if (max_num_args == 1) {
- if (min_num_args == 1) {
- indent(out, indent_level)
- << "if (Dtool_ExtractArg(&" << param_name << ", args, kwds)) {\n";
- } else {
- indent(out, indent_level)
- << "if (Dtool_ExtractOptionalArg(&" << param_name << ", args, kwds)) {\n";
- }
- } else if (max_num_args == 0) {
- indent(out, indent_level)
- << "if (Dtool_CheckNoArgs(args, kwds)) {\n";
- } else {
- clear_error = true;
- indent(out, indent_level)
- << "if ((kwds == nullptr || PyDict_Size(kwds) == 0) && PyArg_UnpackTuple(args, \""
- << methodNameFromCppName(remap, "", false)
- << "\", " << min_num_args << ", " << max_num_args
- << parameter_list << ")) {\n";
- }
- } else {
- clear_error = true;
- indent(out, indent_level)
- << "if ((kwds == nullptr || PyDict_Size(kwds) == 0) && PyArg_ParseTuple(args, \""
- << format_specifiers << ":" << method_name
- << "\"" << parameter_list << ")) {\n";
- }
- ++open_scopes;
- indent_level += 2;
- break;
- case AT_varargs:
- // Wrapper takes a varargs tuple.
- if (only_pyobjects) {
- // All parameters are PyObject*, so we can use the slightly more
- // efficient PyArg_UnpackTuple function instead.
- if (min_num_args == 1 && max_num_args == 1) {
- indent(out, indent_level)
- << "if (PyTuple_GET_SIZE(args) == 1) {\n";
- indent(out, indent_level + 2)
- << param_name << " = PyTuple_GET_ITEM(args, 0);\n";
- } else {
- clear_error = true;
- indent(out, indent_level)
- << "if (PyArg_UnpackTuple(args, \""
- << methodNameFromCppName(remap, "", false)
- << "\", " << min_num_args << ", " << max_num_args
- << parameter_list << ")) {\n";
- }
- } else {
- clear_error = true;
- indent(out, indent_level)
- << "if (PyArg_ParseTuple(args, \""
- << format_specifiers << ":" << method_name
- << "\"" << parameter_list << ")) {\n";
- }
- ++open_scopes;
- indent_level += 2;
- break;
- case AT_single_arg:
- // Single argument. If not a PyObject*, use PyArg_Parse.
- if (!only_pyobjects && format_specifiers != "O") {
- indent(out, indent_level)
- << "if (PyArg_Parse(arg, \"" << format_specifiers << ":"
- << method_name << "\"" << parameter_list << ")) {\n";
- ++open_scopes;
- clear_error = true;
- indent_level += 2;
- }
- default:
- break;
- }
- }
- while (extra_convert.is_text_available()) {
- string line = extra_convert.get_line();
- if (line.size() == 0 || line[0] == '#') {
- out << line << "\n";
- } else {
- indent(out, indent_level) << line << "\n";
- }
- }
- string extra_param_check_str = extra_param_check.str();
- if (!extra_param_check_str.empty()) {
- indent(out, indent_level)
- << "if (" << extra_param_check_str.substr(4) << ") {\n";
- ++open_scopes;
- indent_level += 2;
- }
- if (is_constructor && !remap->_has_this &&
- (remap->_flags & FunctionRemap::F_explicit_self) != 0) {
- // If we'll be passing "self" to the constructor, we need to pre-
- // initialize it here. Unfortunately, we can't pre-load the "this"
- // pointer, but the constructor itself can do this.
- CPPType *orig_type = remap->_return_type->get_orig_type();
- TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(orig_type)), false);
- const InterrogateType &itype = idb->get_type(type_index);
- indent(out, indent_level)
- << "// Pre-initialize self for the constructor\n";
- if (!is_constructor || (return_flags & RF_int) == 0) {
- // This is not a constructor, but somehow we landed up here at a static
- // method requiring a 'self' pointer. This happens in coercion
- // constructors in particular. We'll have to create a temporary
- // PyObject instance to pass to it.
- indent(out, indent_level)
- << "PyObject *self = Dtool_new_"
- << make_safe_name(itype.get_scoped_name()) << "(&"
- << CLASS_PREFIX << make_safe_name(itype.get_scoped_name())
- << "._PyType, nullptr, nullptr);\n";
- extra_cleanup << "PyObject_Del(self);\n";
- } else {
- // XXX rdb: this isn't needed, is it, because tp_new already initializes
- // the instance?
- indent(out, indent_level)
- << "DTool_PyInit_Finalize(self, nullptr, &"
- << CLASS_PREFIX << make_safe_name(itype.get_scoped_name())
- << ", false, false);\n";
- }
- }
- string return_expr;
- if (remap->_blocking) {
- // With SIMPLE_THREADS, it's important that we never release the
- // interpreter lock.
- out << "#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)\n";
- indent(out, indent_level)
- << "PyThreadState *_save;\n";
- indent(out, indent_level)
- << "Py_UNBLOCK_THREADS\n";
- out << "#endif // HAVE_THREADS && !SIMPLE_THREADS\n";
- }
- if (track_interpreter) {
- indent(out, indent_level) << "in_interpreter = 0;\n";
- }
- // If the function returns a pointer that we may need to manage, we store it
- // in a temporary return_value variable and set this to true.
- bool manage_return = false;
- if (remap->_return_type->new_type_is_atomic_string()) {
- // Treat strings as a special case. We don't want to format the return
- // expression.
- return_expr = remap->call_function(out, indent_level, false, container, pexprs);
- CPPType *type = remap->_return_type->get_orig_type();
- indent(out, indent_level);
- type->output_instance(out, "return_value", &parser);
- out << " = " << return_expr << ";\n";
- manage_return = remap->_return_value_needs_management;
- return_expr = "return_value";
- } else if ((return_flags & RF_coerced) != 0 && !TypeManager::is_reference_count(remap->_cpptype)) {
- // Another special case is the coerce constructor for a trivial type. We
- // don't want to invoke "operator new" unnecessarily.
- if (is_constructor && remap->_extension) {
- // Extension constructors are a special case, as usual.
- indent(out, indent_level)
- << remap->get_call_str("&coerced", pexprs) << ";\n";
- } else {
- indent(out, indent_level)
- << "coerced = " << remap->get_call_str(container, pexprs) << ";\n";
- }
- return_expr = "&coerced";
- } else {
- // The general case; an ordinary constructor or function.
- return_expr = remap->call_function(out, indent_level, true, container, pexprs);
- if (return_flags & RF_self) {
- // We won't be using the return value, anyway.
- return_expr.clear();
- }
- if (!return_expr.empty()) {
- manage_return = remap->_return_value_needs_management;
- CPPType *type = remap->_return_type->get_temporary_type();
- indent(out, indent_level);
- type->output_instance(out, "return_value", &parser);
- out << " = " << return_expr << ";\n";
- return_expr = "return_value";
- }
- }
- // Clean up any memory we might have allocate for parsing the parameters.
- while (extra_cleanup.is_text_available()) {
- string line = extra_cleanup.get_line();
- if (line.size() == 0 || line[0] == '#') {
- out << line << "\n";
- } else {
- indent(out, indent_level) << line << "\n";
- }
- }
- if (track_interpreter) {
- indent(out, indent_level) << "in_interpreter = 1;\n";
- }
- if (remap->_blocking) {
- out << "#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)\n";
- indent(out, indent_level)
- << "Py_BLOCK_THREADS\n";
- out << "#endif // HAVE_THREADS && !SIMPLE_THREADS\n";
- }
- if (manage_return) {
- // If a constructor returns NULL, that means allocation failed.
- if (remap->_return_type->return_value_needs_management()) {
- indent(out, indent_level) << "if (return_value == nullptr) {\n";
- if ((return_flags & ~RF_pyobject) == RF_err_null) {
- // PyErr_NoMemory returns NULL, so allow tail call elimination.
- indent(out, indent_level) << " return PyErr_NoMemory();\n";
- } else {
- indent(out, indent_level) << " PyErr_NoMemory();\n";
- error_return(out, indent_level + 2, return_flags);
- }
- indent(out, indent_level) << "}\n";
- }
- return_expr = manage_return_value(out, indent_level, remap, "return_value");
- return_expr = remap->_return_type->temporary_to_return(return_expr);
- }
- // How could we raise a TypeError if we don't take any args?
- if (args_type == AT_no_args || max_num_args == 0) {
- may_raise_typeerror = false;
- }
- // If a function takes a PyObject* argument, it would be a good idea to
- // always check for exceptions.
- if (may_raise_typeerror) {
- check_exceptions = true;
- }
- // Generated getters and setters don't raise exceptions or asserts since
- // they don't contain any code.
- if (remap->_type == FunctionRemap::T_getter ||
- remap->_type == FunctionRemap::T_setter) {
- check_exceptions = false;
- }
- // The most common case of the below logic is consolidated in a single
- // function, as another way to reduce code bloat. Sigh.
- if (check_exceptions && (!may_raise_typeerror || report_errors) &&
- watch_asserts && (return_flags & RF_coerced) == 0) {
- if (return_flags & RF_decref_args) {
- indent(out, indent_level) << "Py_DECREF(args);\n";
- return_flags &= ~RF_decref_args;
- }
- // An even specialer special case for functions with void return or bool
- // return. We have our own functions that do all this in a single
- // function call, so it should reduce the amount of code output while not
- // being any slower.
- bool return_null = (return_flags & RF_pyobject) != 0 &&
- (return_flags & RF_err_null) != 0;
- if (return_null && return_expr.empty()) {
- indent(out, indent_level)
- << "return Dtool_Return_None();\n";
- // Reset the return value bit so that the code below doesn't generate
- // the return statement a second time.
- return_flags &= ~RF_pyobject;
- } else if (return_null && TypeManager::is_bool(remap->_return_type->get_new_type())) {
- indent(out, indent_level)
- << "return Dtool_Return_Bool(" << return_expr << ");\n";
- return_flags &= ~RF_pyobject;
- } else if (return_null && TypeManager::is_pointer_to_PyObject(remap->_return_type->get_new_type())) {
- indent(out, indent_level)
- << "return Dtool_Return(" << return_expr << ");\n";
- return_flags &= ~RF_pyobject;
- } else {
- indent(out, indent_level)
- << "if (Dtool_CheckErrorOccurred()) {\n";
- if (manage_return) {
- delete_return_value(out, indent_level + 2, remap, return_expr);
- }
- error_return(out, indent_level + 2, return_flags);
- indent(out, indent_level) << "}\n";
- }
- } else {
- if (check_exceptions) {
- // Check if a Python exception has occurred. We only do this when
- // check_exception is set. If report_errors is set, this method must
- // terminate on error.
- if (!may_raise_typeerror || report_errors) {
- indent(out, indent_level)
- << "if (_PyErr_OCCURRED()) {\n";
- } else {
- // If a method is some extension method that takes a PyObject*, and it
- // raised a TypeError, continue. The documentation tells us not to
- // compare the result of PyErr_Occurred against a specific exception
- // type. However, in our case, this seems okay because we know that
- // the TypeError we want to catch here is going to be generated by a
- // PyErr_SetString call, not by user code.
- indent(out, indent_level)
- << "PyObject *exception = _PyErr_OCCURRED();\n";
- indent(out, indent_level)
- << "if (exception == PyExc_TypeError) {\n";
- indent(out, indent_level)
- << " // TypeError raised; continue to next overload type.\n";
- indent(out, indent_level)
- << "} else if (exception != nullptr) {\n";
- }
- if (manage_return) {
- delete_return_value(out, indent_level + 2, remap, return_expr);
- }
- error_return(out, indent_level + 2, return_flags);
- indent(out, indent_level)
- << "} else {\n";
- ++open_scopes;
- indent_level += 2;
- }
- if (return_flags & RF_decref_args) {
- indent(out, indent_level) << "Py_DECREF(args);\n";
- return_flags &= ~RF_decref_args;
- }
- // Outputs code to check to see if an assertion has failed while the C++
- // code was executing, and report this failure back to Python. Don't do
- // this for coercion constructors since they are called by other wrapper
- // functions which already check this on their own. Generated getters
- // obviously can't raise asserts.
- if (watch_asserts && (return_flags & (RF_coerced | RF_raise_keyerror)) == 0 &&
- remap->_type != FunctionRemap::T_getter &&
- remap->_type != FunctionRemap::T_setter) {
- out << "#ifndef NDEBUG\n";
- indent(out, indent_level)
- << "Notify *notify = Notify::ptr();\n";
- indent(out, indent_level)
- << "if (UNLIKELY(notify->has_assert_failed())) {\n";
- if (manage_return) {
- // Output code to delete any temporary object we may have allocated.
- delete_return_value(out, indent_level + 2, remap, return_expr);
- }
- if (return_flags & RF_err_null) {
- // This function returns NULL, so we can pass it on.
- indent(out, indent_level + 2)
- << "return Dtool_Raise_AssertionError();\n";
- } else {
- indent(out, indent_level + 2)
- << "Dtool_Raise_AssertionError();\n";
- error_return(out, indent_level + 2, return_flags);
- }
- indent(out, indent_level)
- << "}\n";
- out << "#endif\n";
- }
- }
- // Okay, we're past all the error conditions and special cases. Now return
- // the return type in the way that was requested.
- if ((return_flags & RF_int) != 0 && (return_flags & RF_raise_keyerror) == 0) {
- CPPType *orig_type = remap->_return_type->get_orig_type();
- if (is_constructor) {
- // Special case for constructor.
- TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(orig_type)), false);
- const InterrogateType &itype = idb->get_type(type_index);
- indent(out, indent_level)
- << "return DTool_PyInit_Finalize(self, (void *)" << return_expr << ", &" << CLASS_PREFIX << make_safe_name(itype.get_scoped_name()) << ", true, false);\n";
- } else if (TypeManager::is_bool(orig_type)) {
- // It's an error return boolean, I guess. Return 0 on success.
- indent(out, indent_level) << "return (" << return_expr << ") ? 0 : -1;\n";
- } else if (TypeManager::is_integer(orig_type)) {
- if ((return_flags & RF_compare) == RF_compare) {
- // Make sure it returns -1, 0, or 1, or Python complains with:
- // RuntimeWarning: tp_compare didn't return -1, 0 or 1
- indent(out, indent_level) << "return (int)(" << return_expr << " > 0) - (int)(" << return_expr << " < 0);\n";
- } else {
- indent(out, indent_level) << "return " << return_expr << ";\n";
- }
- } else if (TypeManager::is_void(orig_type)) {
- indent(out, indent_level) << "return 0;\n";
- } else {
- nout << "Warning: function has return type " << *orig_type
- << ", expected int or void:\n" << expected_params << "\n";
- indent(out, indent_level) << "// Don't know what to do with return type "
- << *orig_type << ".\n";
- indent(out, indent_level) << "return 0;\n";
- }
- } else if (return_flags & RF_self) {
- indent(out, indent_level) << "Py_INCREF(self);\n";
- indent(out, indent_level) << "return self;\n";
- } else if (return_flags & RF_pyobject) {
- if (return_expr.empty()) {
- indent(out, indent_level) << "Py_INCREF(Py_None);\n";
- indent(out, indent_level) << "return Py_None;\n";
- } else if (return_flags & RF_preserve_null) {
- indent(out, indent_level) << "if (" << return_expr << " == nullptr) {\n";
- indent(out, indent_level) << " return nullptr;\n";
- indent(out, indent_level) << "} else {\n";
- pack_return_value(out, indent_level + 2, remap, return_expr, return_flags);
- indent(out, indent_level) << "}\n";
- } else {
- pack_return_value(out, indent_level, remap, return_expr, return_flags);
- }
- } else if (return_flags & RF_coerced) {
- // We were asked to assign the result to a "coerced" reference.
- CPPType *return_type = remap->_cpptype;
- CPPType *orig_type = remap->_return_type->get_orig_type();
- // Special case for static make function that returns a pointer: cast the
- // pointer to the right pointer type.
- if (!is_constructor && (remap->_flags & FunctionRemap::F_coerce_constructor) != 0 &&
- (TypeManager::is_pointer(orig_type) || TypeManager::is_pointer_to_base(orig_type))) {
- CPPType *new_type = remap->_return_type->get_new_type();
- if (TypeManager::is_const_pointer_to_anything(new_type)) {
- return_type = CPPType::new_type(new CPPConstType(return_type));
- }
- if (IsPandaTypedObject(return_type->as_struct_type())) {
- return_expr = "DCAST("
- + return_type->get_local_name(&parser)
- + ", " + return_expr + ")";
- } else {
- return_type = CPPType::new_type(new CPPPointerType(return_type));
- return_expr = "(" + return_type->get_local_name(&parser) +
- ") " + return_expr;
- }
- }
- if (return_expr == "coerced") {
- // We already did this earlier...
- indent(out, indent_level) << "return true;\n";
- } else if (TypeManager::is_reference_count(remap->_cpptype)) {
- indent(out, indent_level) << "coerced = std::move(" << return_expr << ");\n";
- indent(out, indent_level) << "return true;\n";
- } else {
- indent(out, indent_level) << "return &coerced;\n";
- }
- } else if (return_flags & RF_raise_keyerror) {
- CPPType *orig_type = remap->_return_type->get_orig_type();
- if (TypeManager::is_bool(orig_type) || TypeManager::is_pointer(orig_type)) {
- indent(out, indent_level) << "if (!" << return_expr << ") {\n";
- } else if (TypeManager::is_unsigned_integer(orig_type)) {
- indent(out, indent_level) << "if ((int)" << return_expr << " == -1) {\n";
- } else if (TypeManager::is_integer(orig_type)) {
- indent(out, indent_level) << "if (" << return_expr << " < 0) {\n";
- } else {
- indent(out, indent_level) << "if (false) {\n";
- }
- if (args_type == AT_single_arg) {
- indent(out, indent_level) << " PyErr_SetObject(PyExc_KeyError, arg);\n";
- } else {
- indent(out, indent_level) << " PyErr_SetObject(PyExc_KeyError, key);\n";
- }
- error_return(out, indent_level + 2, return_flags);
- indent(out, indent_level) << "}\n";
- }
- // Close the extra braces opened earlier.
- while (open_scopes > 0) {
- indent_level -= 2;
- indent(out, indent_level) << "}\n";
- --open_scopes;
- }
- if (clear_error && !report_errors) {
- // We were asked not to report errors, so clear the active exception if
- // this overload might have raised a TypeError.
- indent(out, indent_level) << "PyErr_Clear();\n";
- }
- if (min_version > 0) {
- // Close the #if PY_VERSION_HEX check.
- out << "#endif\n";
- }
- }
- /**
- * Outputs the correct return statement that should be used in case of error
- * based on the ReturnFlags.
- */
- void InterfaceMakerPythonNative::
- error_return(ostream &out, int indent_level, int return_flags) {
- // if (return_flags & RF_coerced) { indent(out, indent_level) << "coerced =
- // NULL;\n"; }
- if (return_flags & RF_decref_args) {
- indent(out, indent_level) << "Py_DECREF(args);\n";
- }
- if (return_flags & RF_int) {
- indent(out, indent_level) << "return -1;\n";
- } else if (return_flags & RF_err_notimplemented) {
- indent(out, indent_level) << "Py_INCREF(Py_NotImplemented);\n";
- indent(out, indent_level) << "return Py_NotImplemented;\n";
- } else if (return_flags & RF_err_null) {
- indent(out, indent_level) << "return nullptr;\n";
- } else if (return_flags & RF_err_false) {
- indent(out, indent_level) << "return false;\n";
- }
- }
- /**
- * Similar to error_return, except raises an exception before returning. If
- * format_args are not the empty string, uses PyErr_Format instead of
- * PyErr_SetString.
- */
- void InterfaceMakerPythonNative::
- error_raise_return(ostream &out, int indent_level, int return_flags,
- const string &exc_type, const string &message,
- const string &format_args) {
- if (return_flags & RF_decref_args) {
- indent(out, indent_level) << "Py_DECREF(args);\n";
- return_flags &= ~RF_decref_args;
- }
- if (format_args.empty()) {
- if (exc_type == "TypeError") {
- if ((return_flags & RF_err_null) != 0) {
- // This is probably an over-optimization, but why the heck not.
- indent(out, indent_level) << "return Dtool_Raise_TypeError(";
- output_quoted(out, indent_level + 29, message, false);
- out << ");\n";
- return;
- } else {
- indent(out, indent_level) << "Dtool_Raise_TypeError(";
- output_quoted(out, indent_level + 22, message, false);
- out << ");\n";
- }
- } else {
- indent(out, indent_level) << "PyErr_SetString(PyExc_" << exc_type << ",\n";
- output_quoted(out, indent_level + 16, message);
- out << ");\n";
- }
- } else if ((return_flags & RF_err_null) != 0 &&
- (return_flags & RF_pyobject) != 0) {
- // PyErr_Format always returns NULL. Passing it on directly allows the
- // compiler to make a tiny optimization, so why not.
- indent(out, indent_level) << "return PyErr_Format(PyExc_" << exc_type << ",\n";
- output_quoted(out, indent_level + 20, message);
- out << ",\n";
- indent(out, indent_level + 20) << format_args << ");\n";
- return;
- } else {
- indent(out, indent_level) << "PyErr_Format(PyExc_" << exc_type << ",\n";
- output_quoted(out, indent_level + 13, message);
- out << ",\n";
- indent(out, indent_level + 13) << format_args << ");\n";
- }
- error_return(out, indent_level, return_flags);
- }
- /**
- * Outputs a command to pack the indicated expression, of the return_type
- * type, as a Python return value.
- */
- void InterfaceMakerPythonNative::
- pack_return_value(ostream &out, int indent_level, FunctionRemap *remap,
- string return_expr, int return_flags) {
- ParameterRemap *return_type = remap->_return_type;
- CPPType *orig_type = return_type->get_orig_type();
- CPPType *type = return_type->get_new_type();
- if (TypeManager::is_scoped_enum(type)) {
- InterrogateDatabase *idb = InterrogateDatabase::get_ptr();
- TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(orig_type)), false);
- const InterrogateType &itype = idb->get_type(type_index);
- string safe_name = make_safe_name(itype.get_scoped_name());
- indent(out, indent_level)
- << "return PyObject_CallFunction((PyObject *)Dtool_Ptr_" << safe_name;
- CPPType *underlying_type = ((CPPEnumType *)itype._cpptype)->get_underlying_type();
- if (TypeManager::is_unsigned_integer(underlying_type)) {
- out << ", \"k\", (unsigned long)";
- } else {
- out << ", \"l\", (long)";
- }
- out << "(" << return_expr << "));\n";
- } else if (return_type->new_type_is_atomic_string() ||
- TypeManager::is_simple(type) ||
- TypeManager::is_char_pointer(type) ||
- TypeManager::is_wchar_pointer(type) ||
- TypeManager::is_pointer_to_PyObject(type) ||
- TypeManager::is_pointer_to_Py_buffer(type) ||
- TypeManager::is_vector_unsigned_char(type)) {
- // Most types are now handled by the many overloads of Dtool_WrapValue,
- // defined in py_panda.h.
- indent(out, indent_level)
- << "return Dtool_WrapValue(" << return_expr << ");\n";
- } else if (TypeManager::is_pointer(type)) {
- bool is_const = TypeManager::is_const_pointer_to_anything(type);
- bool owns_memory = remap->_return_value_needs_management;
- // Note, we don't check for NULL here any more. This is now done by the
- // appropriate CreateInstance(Typed) function.
- if (manage_reference_counts && TypeManager::is_pointer_to_base(orig_type)) {
- // Use a trick to transfer the reference count to avoid a pair of
- // unnecessary ref() and unref() calls. Ideally we'd use move
- // semantics, but py_panda.cxx cannot make use of PointerTo.
- indent(out, indent_level) << "// Transfer ownership of return_value.\n";
- indent(out, indent_level);
- type->output_instance(out, "return_ptr", &parser);
- out << " = " << return_expr << ";\n";
- indent(out, indent_level) << "return_value.cheat() = nullptr;\n";
- return_expr = "return_ptr";
- }
- InterrogateDatabase *idb = InterrogateDatabase::get_ptr();
- if (TypeManager::is_struct(orig_type) || TypeManager::is_ref_to_anything(orig_type)) {
- if (TypeManager::is_ref_to_anything(orig_type) || remap->_manage_reference_count) {
- TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(type)),false);
- const InterrogateType &itype = idb->get_type(type_index);
- write_python_instance(out, indent_level, return_expr, owns_memory, itype, is_const);
- } else {
- TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(orig_type)),false);
- const InterrogateType &itype = idb->get_type(type_index);
- write_python_instance(out, indent_level, return_expr, owns_memory, itype, is_const);
- }
- } else if (TypeManager::is_struct(orig_type->remove_pointer())) {
- TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(orig_type)),false);
- const InterrogateType &itype = idb->get_type(type_index);
- write_python_instance(out, indent_level, return_expr, owns_memory, itype, is_const);
- } else {
- indent(out, indent_level) << "Should Never Reach This InterfaceMakerPythonNative::pack_python_value";
- // << "return Dtool_Integer((int) " << return_expr << ");\n";
- }
- } else {
- // Return None.
- indent(out, indent_level)
- << "return Py_BuildValue(\"\"); // Don't know how to wrap type.\n";
- }
- }
- /**
- * Generates the synthetic method described by the MAKE_SEQ() macro.
- */
- void InterfaceMakerPythonNative::
- write_make_seq(ostream &out, Object *obj, const std::string &ClassName,
- const std::string &cClassName, MakeSeq *make_seq) {
- out << "/*\n"
- " * Python make_seq wrapper\n"
- " */\n";
- out << "static PyObject *" << make_seq->_name + "(PyObject *self, PyObject *) {\n";
- // This used to return a list. But it should really be a tuple, I think,
- // because it probably makes more sense for it to be immutable (as changes
- // to it won't be visible on the C++ side anyway).
- FunctionRemap *remap = make_seq->_length_getter->_remaps.front();
- vector_string pexprs;
- if (make_seq->_length_getter->_has_this) {
- out <<
- " " << cClassName << " *local_this = nullptr;\n"
- " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"
- " return nullptr;\n"
- " }\n"
- " Py_ssize_t count = (Py_ssize_t)" << remap->get_call_str("local_this", pexprs) << ";\n";
- } else {
- out << " Py_ssize_t count = (Py_ssize_t)" << remap->get_call_str("", pexprs) << ";\n";
- }
- Function *elem_getter = make_seq->_element_getter;
- if ((elem_getter->_args_type & AT_varargs) == AT_varargs) {
- // Fast way to create a temporary tuple to hold only a single item, under
- // the assumption that the called method doesn't do anything with this
- // tuple other than unpack it (which is a fairly safe assumption to make).
- out << " PyTupleObject args;\n";
- out << " (void)PyObject_INIT_VAR((PyVarObject *)&args, &PyTuple_Type, 1);\n";
- }
- out <<
- " PyObject *tuple = PyTuple_New(count);\n"
- "\n"
- " for (Py_ssize_t i = 0; i < count; ++i) {\n"
- " PyObject *index = Dtool_WrapValue(i);\n";
- switch (elem_getter->_args_type) {
- case AT_keyword_args:
- out << " PyTuple_SET_ITEM(&args, 0, index);\n"
- " PyObject *value = " << elem_getter->_name << "(self, (PyObject *)&args, nullptr);\n";
- break;
- case AT_varargs:
- out << " PyTuple_SET_ITEM(&args, 0, index);\n"
- " PyObject *value = " << elem_getter->_name << "(self, (PyObject *)&args);\n";
- break;
- case AT_single_arg:
- out << " PyObject *value = " << elem_getter->_name << "(self, index);\n";
- break;
- default:
- out << " PyObject *value = " << elem_getter->_name << "(self, nullptr);\n";
- break;
- }
- out <<
- " PyTuple_SET_ITEM(tuple, i, value);\n"
- " Py_DECREF(index);\n"
- " }\n"
- "\n";
- if ((elem_getter->_args_type & AT_varargs) == AT_varargs) {
- out << " _Py_ForgetReference((PyObject *)&args);\n";
- }
- out <<
- " if (Dtool_CheckErrorOccurred()) {\n"
- " Py_DECREF(tuple);\n"
- " return nullptr;\n"
- " }\n"
- " return tuple;\n"
- "}\n"
- "\n";
- }
- /**
- * Generates the synthetic method described by the MAKE_PROPERTY() macro.
- */
- void InterfaceMakerPythonNative::
- write_getset(ostream &out, Object *obj, Property *property) {
- // We keep around this empty vector for passing to get_call_str.
- const vector_string pexprs;
- string ClassName = make_safe_name(obj->_itype.get_scoped_name());
- std::string cClassName = obj->_itype.get_true_name();
- const InterrogateElement &ielem = property->_ielement;
- FunctionRemap *len_remap = nullptr;
- if (property->_length_function != nullptr) {
- assert(!property->_length_function->_remaps.empty());
- // This is actually a sequence. Wrap this with a special class.
- len_remap = property->_length_function->_remaps.front();
- out << "/**\n"
- " * sequence length function for property " << ielem.get_scoped_name() << "\n"
- " */\n"
- "static Py_ssize_t Dtool_" + ClassName + "_" + ielem.get_name() + "_Len(PyObject *self) {\n";
- if (property->_length_function->_has_this) {
- out <<
- " " << cClassName << " *local_this = nullptr;\n"
- " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"
- " return -1;\n"
- " }\n"
- " return (Py_ssize_t)" << len_remap->get_call_str("local_this", pexprs) << ";\n";
- } else {
- out << " return (Py_ssize_t)" << len_remap->get_call_str("", pexprs) << ";\n";
- }
- out << "}\n\n";
- }
- if (property->_getter_remaps.empty()) {
- return;
- }
- if (ielem.is_sequence()) {
- assert(len_remap != nullptr);
- out <<
- "/**\n"
- " * sequence getter for property " << ielem.get_scoped_name() << "\n"
- " */\n"
- "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Sequence_Getitem(PyObject *self, Py_ssize_t index) {\n";
- if (property->_has_this) {
- out <<
- " " << cClassName << " *local_this = nullptr;\n"
- " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"
- " return nullptr;\n"
- " }\n";
- }
- // This is a getitem of a sequence type. This means we *need* to raise
- // IndexError if we're out of bounds.
- out << " if (index < 0 || index >= (Py_ssize_t)"
- << len_remap->get_call_str("local_this", pexprs) << ") {\n";
- out << " PyErr_SetString(PyExc_IndexError, \"" << ClassName << "." << ielem.get_name() << "[] index out of range\");\n";
- out << " return nullptr;\n";
- out << " }\n";
- /*if (property->_has_function != NULL) {
- out << " if (!local_this->" << property->_has_function->_ifunc.get_name() << "(index)) {\n"
- << " Py_INCREF(Py_None);\n"
- << " return Py_None;\n"
- << " }\n";
- }*/
- std::set<FunctionRemap*> remaps;
- // Extract only the getters that take one integral argument.
- for (FunctionRemap *remap : property->_getter_remaps) {
- int min_num_args = remap->get_min_num_args();
- int max_num_args = remap->get_max_num_args();
- if (min_num_args <= 1 && max_num_args >= 1 &&
- TypeManager::is_integer(remap->_parameters[(size_t)remap->_has_this]._remap->get_new_type())) {
- remaps.insert(remap);
- }
- }
- string expected_params;
- write_function_forset(out, remaps, 1, 1, expected_params, 2, true, true,
- AT_no_args, RF_pyobject | RF_err_null, false, true, "index");
- out << " if (!_PyErr_OCCURRED()) {\n";
- out << " return Dtool_Raise_BadArgumentsError(\n";
- output_quoted(out, 6, expected_params);
- out << ");\n"
- " }\n"
- "}\n\n";
- // Write out a setitem if this is not a read-only property.
- if (!property->_setter_remaps.empty()) {
- out << "static int Dtool_" + ClassName + "_" + ielem.get_name() + "_Sequence_Setitem(PyObject *self, Py_ssize_t index, PyObject *arg) {\n";
- if (property->_has_this) {
- out << " " << cClassName << " *local_this = nullptr;\n";
- out << " if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", (void **)&local_this, \""
- << classNameFromCppName(cClassName, false) << "." << ielem.get_name() << "\")) {\n";
- out << " return -1;\n";
- out << " }\n\n";
- }
- out << " if (index < 0 || index >= (Py_ssize_t)"
- << len_remap->get_call_str("local_this", pexprs) << ") {\n";
- out << " PyErr_SetString(PyExc_IndexError, \"" << ClassName << "." << ielem.get_name() << "[] index out of range\");\n";
- out << " return -1;\n";
- out << " }\n";
- out << " if (arg == nullptr) {\n";
- if (property->_deleter != nullptr) {
- if (property->_deleter->_has_this) {
- out << " local_this->" << property->_deleter->_ifunc.get_name() << "(index);\n";
- } else {
- out << " " << cClassName << "::" << property->_deleter->_ifunc.get_name() << "(index);\n";
- }
- out << " return 0;\n";
- } else {
- out << " Dtool_Raise_TypeError(\"can't delete " << ielem.get_name() << "[] attribute\");\n"
- " return -1;\n";
- }
- out << " }\n";
- if (property->_clear_function != nullptr) {
- out << " if (arg == Py_None) {\n";
- if (property->_clear_function->_has_this) {
- out << " local_this->" << property->_clear_function->_ifunc.get_name() << "(index);\n";
- } else {
- out << " " << cClassName << "::" << property->_clear_function->_ifunc.get_name() << "(index);\n";
- }
- out << " return 0;\n"
- << " }\n";
- }
- std::set<FunctionRemap*> remaps;
- // Extract only the setters that take two arguments.
- for (FunctionRemap *remap : property->_setter_remaps) {
- int min_num_args = remap->get_min_num_args();
- int max_num_args = remap->get_max_num_args();
- if (min_num_args <= 2 && max_num_args >= 2 &&
- TypeManager::is_integer(remap->_parameters[1]._remap->get_new_type())) {
- remaps.insert(remap);
- }
- }
- string expected_params;
- write_function_forset(out, remaps, 2, 2,
- expected_params, 2, true, true, AT_single_arg,
- RF_int, false, false, "index");
- out << " if (!_PyErr_OCCURRED()) {\n";
- out << " Dtool_Raise_BadArgumentsError(\n";
- output_quoted(out, 6, expected_params);
- out << ");\n";
- out << " }\n";
- out << " return -1;\n";
- out << "}\n\n";
- }
- // Finally, add the inserter, if one exists.
- if (property->_inserter != nullptr) {
- out << "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Sequence_insert(PyObject *self, size_t index, PyObject *arg) {\n";
- if (property->_has_this) {
- out << " " << cClassName << " *local_this = nullptr;\n";
- out << " if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", (void **)&local_this, \""
- << classNameFromCppName(cClassName, false) << "." << ielem.get_name() << "\")) {\n";
- out << " return nullptr;\n";
- out << " }\n\n";
- }
- std::set<FunctionRemap*> remaps;
- remaps.insert(property->_inserter->_remaps.begin(),
- property->_inserter->_remaps.end());
- string expected_params;
- write_function_forset(out, remaps, 2, 2,
- expected_params, 2, true, true, AT_single_arg,
- RF_pyobject | RF_err_null, false, false, "index");
- out << " if (!_PyErr_OCCURRED()) {\n";
- out << " Dtool_Raise_BadArgumentsError(\n";
- output_quoted(out, 6, expected_params);
- out << ");\n";
- out << " }\n";
- out << " return nullptr;\n";
- out << "}\n\n";
- }
- }
- // Write the getitem functions.
- if (ielem.is_mapping()) {
- out <<
- "/**\n"
- " * mapping getitem for property " << ielem.get_scoped_name() << "\n"
- " */\n"
- "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Mapping_Getitem(PyObject *self, PyObject *arg) {\n";
- // Before we do the has_function: if this is also a sequence, then we have
- // to also handle the case here that we were passed an index.
- if (ielem.is_sequence()) {
- out <<
- "#if PY_MAJOR_VERSION >= 3\n"
- " if (PyLong_CheckExact(arg)) {\n"
- "#else\n"
- " if (PyLong_CheckExact(arg) || PyInt_CheckExact(arg)) {\n"
- "#endif\n"
- " return Dtool_" << ClassName << "_" << ielem.get_name() << "_Sequence_Getitem(self, PyLongOrInt_AsSize_t(arg));\n"
- " }\n\n";
- }
- if (property->_has_this) {
- out <<
- " " << cClassName << " *local_this = nullptr;\n"
- " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"
- " return nullptr;\n"
- " }\n";
- }
- if (property->_has_function != nullptr) {
- std::set<FunctionRemap*> remaps;
- remaps.insert(property->_has_function->_remaps.begin(),
- property->_has_function->_remaps.end());
- out << " {\n";
- string expected_params;
- write_function_forset(out, remaps, 1, 1, expected_params, 4, true, true,
- AT_single_arg, RF_raise_keyerror | RF_err_null, false, true);
- out << " }\n";
- }
- std::set<FunctionRemap*> remaps;
- // Extract only the getters that take one argument. Fish out the ones
- // already taken by the sequence getter.
- for (FunctionRemap *remap : property->_getter_remaps) {
- int min_num_args = remap->get_min_num_args();
- int max_num_args = remap->get_max_num_args();
- if (min_num_args <= 1 && max_num_args >= 1 &&
- (!ielem.is_sequence() || !TypeManager::is_integer(remap->_parameters[(size_t)remap->_has_this]._remap->get_new_type()))) {
- remaps.insert(remap);
- }
- }
- string expected_params;
- write_function_forset(out, remaps, 1, 1, expected_params, 2, true, true,
- AT_single_arg, RF_pyobject | RF_err_null, false, true);
- out << " if (!_PyErr_OCCURRED()) {\n";
- out << " return Dtool_Raise_BadArgumentsError(\n";
- output_quoted(out, 6, expected_params);
- out << ");\n"
- " }\n"
- " return nullptr;\n"
- "}\n\n";
- // Write out a setitem if this is not a read-only property.
- if (!property->_setter_remaps.empty()) {
- out <<
- "/**\n"
- " * mapping setitem for property " << ielem.get_scoped_name() << "\n"
- " */\n"
- "static int Dtool_" + ClassName + "_" + ielem.get_name() + "_Mapping_Setitem(PyObject *self, PyObject *key, PyObject *value) {\n";
- if (property->_has_this) {
- out <<
- " " << cClassName << " *local_this = nullptr;\n"
- " if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", (void **)&local_this, \""
- << classNameFromCppName(cClassName, false) << "." << ielem.get_name() << "\")) {\n"
- " return -1;\n"
- " }\n\n";
- }
- out << " if (value == nullptr) {\n";
- if (property->_deleter != nullptr) {
- out << " PyObject *arg = key;\n";
- if (property->_has_function != nullptr) {
- std::set<FunctionRemap*> remaps;
- remaps.insert(property->_has_function->_remaps.begin(),
- property->_has_function->_remaps.end());
- out << " {\n";
- string expected_params;
- write_function_forset(out, remaps, 1, 1, expected_params, 6, true, true,
- AT_single_arg, RF_raise_keyerror | RF_int, false, true);
- out << " }\n";
- }
- std::set<FunctionRemap*> remaps;
- remaps.insert(property->_deleter->_remaps.begin(),
- property->_deleter->_remaps.end());
- string expected_params;
- write_function_forset(out, remaps, 1, 1,
- expected_params, 4, true, true, AT_single_arg,
- RF_int, false, false);
- out << " return -1;\n";
- } else {
- out << " Dtool_Raise_TypeError(\"can't delete " << ielem.get_name() << "[] attribute\");\n"
- " return -1;\n";
- }
- out << " }\n";
- if (property->_clear_function != nullptr) {
- out << " if (value == Py_None) {\n"
- << " local_this->" << property->_clear_function->_ifunc.get_name() << "(key);\n"
- << " return 0;\n"
- << " }\n";
- }
- std::set<FunctionRemap*> remaps;
- remaps.insert(property->_setter_remaps.begin(),
- property->_setter_remaps.end());
- // We have to create an args tuple only to unpack it later, ugh.
- out << " PyObject *args = PyTuple_New(2);\n"
- << " PyTuple_SET_ITEM(args, 0, key);\n"
- << " PyTuple_SET_ITEM(args, 1, value);\n"
- << " Py_INCREF(key);\n"
- << " Py_INCREF(value);\n";
- string expected_params;
- write_function_forset(out, remaps, 2, 2,
- expected_params, 2, true, true, AT_varargs,
- RF_int | RF_decref_args, false, false);
- out << " if (!_PyErr_OCCURRED()) {\n";
- out << " Dtool_Raise_BadArgumentsError(\n";
- output_quoted(out, 6, expected_params);
- out << ");\n";
- out << " }\n";
- out << " Py_DECREF(args);\n";
- out << " return -1;\n";
- out << "}\n\n";
- }
- if (property->_getkey_function != nullptr) {
- out <<
- "/**\n"
- " * mapping key-getter for property " << ielem.get_scoped_name() << "\n"
- " */\n"
- "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Mapping_Getkey(PyObject *self, Py_ssize_t index) {\n";
- if (property->_has_this) {
- out <<
- " " << cClassName << " *local_this = nullptr;\n"
- " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n"
- " return nullptr;\n"
- " }\n";
- }
- // We need to raise IndexError if we're out of bounds.
- if (len_remap != nullptr) {
- out << " if (index < 0 || index >= (Py_ssize_t)"
- << len_remap->get_call_str("local_this", pexprs) << ") {\n";
- out << " PyErr_SetString(PyExc_IndexError, \"" << ClassName << "." << ielem.get_name() << "[] index out of range\");\n";
- out << " return nullptr;\n";
- out << " }\n";
- }
- std::set<FunctionRemap*> remaps;
- // Extract only the getters that take one integral argument.
- for (FunctionRemap *remap : property->_getkey_function->_remaps) {
- int min_num_args = remap->get_min_num_args();
- int max_num_args = remap->get_max_num_args();
- if (min_num_args <= 1 && max_num_args >= 1 &&
- TypeManager::is_integer(remap->_parameters[(size_t)remap->_has_this]._remap->get_new_type())) {
- remaps.insert(remap);
- }
- }
- string expected_params;
- write_function_forset(out, remaps, 1, 1, expected_params, 2, true, true,
- AT_no_args, RF_pyobject | RF_err_null, false, true, "index");
- out << " if (!_PyErr_OCCURRED()) {\n";
- out << " return Dtool_Raise_BadArgumentsError(\n";
- output_quoted(out, 6, expected_params);
- out << ");\n"
- " }\n"
- "}\n\n";
- }
- }
- // Now write the actual getter wrapper. It will be a different wrapper
- // depending on whether it's a mapping or a sequence.
- if (ielem.is_mapping()) {
- out << "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter(PyObject *self, void *) {\n";
- if (property->_has_this) {
- out << " nassertr(self != nullptr, nullptr);\n";
- }
- if (property->_setter_remaps.empty()) {
- out << " Dtool_MappingWrapper *wrap = Dtool_NewMappingWrapper(self, \"" << ClassName << "." << ielem.get_name() << "\");\n";
- } else {
- out << " Dtool_MappingWrapper *wrap = Dtool_NewMutableMappingWrapper(self, \"" << ClassName << "." << ielem.get_name() << "\");\n";
- }
- out << " if (wrap != nullptr) {\n"
- " wrap->_getitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Mapping_Getitem;\n";
- if (!property->_setter_remaps.empty()) {
- out << " if (!DtoolInstance_IS_CONST(self)) {\n";
- out << " wrap->_setitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Mapping_Setitem;\n";
- out << " }\n";
- }
- if (property->_length_function != nullptr) {
- out << " wrap->_keys._len_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Len;\n";
- if (property->_getkey_function != nullptr) {
- out << " wrap->_keys._getitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Mapping_Getkey;\n";
- }
- }
- out << " }\n"
- " return (PyObject *)wrap;\n"
- "}\n\n";
- } else if (ielem.is_sequence()) {
- out << "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter(PyObject *self, void *) {\n";
- if (property->_has_this) {
- out << " nassertr(self != nullptr, nullptr);\n";
- }
- if (property->_setter_remaps.empty()) {
- out <<
- " Dtool_SequenceWrapper *wrap = Dtool_NewSequenceWrapper(self, \"" << ClassName << "." << ielem.get_name() << "\");\n"
- " if (wrap != nullptr) {\n"
- " wrap->_len_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Len;\n"
- " wrap->_getitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Sequence_Getitem;\n";
- } else {
- out <<
- " Dtool_MutableSequenceWrapper *wrap = Dtool_NewMutableSequenceWrapper(self, \"" << ClassName << "." << ielem.get_name() << "\");\n"
- " if (wrap != nullptr) {\n"
- " wrap->_len_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Len;\n"
- " wrap->_getitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Sequence_Getitem;\n";
- if (!property->_setter_remaps.empty()) {
- out << " if (!DtoolInstance_IS_CONST(self)) {\n";
- out << " wrap->_setitem_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Sequence_Setitem;\n";
- if (property->_inserter != nullptr) {
- out << " wrap->_insert_func = &Dtool_" << ClassName << "_" << ielem.get_name() << "_Sequence_insert;\n";
- }
- out << " }\n";
- }
- }
- out << " }\n"
- " return (PyObject *)wrap;\n"
- "}\n\n";
- } else {
- // Write out a regular, unwrapped getter.
- out << "static PyObject *Dtool_" + ClassName + "_" + ielem.get_name() + "_Getter(PyObject *self, void *) {\n";
- FunctionRemap *remap = property->_getter_remaps.front();
- if (remap->_has_this) {
- if (remap->_const_method) {
- out << " const " << cClassName << " *local_this = nullptr;\n";
- out << " if (!Dtool_Call_ExtractThisPointer(self, Dtool_" << ClassName << ", (void **)&local_this)) {\n";
- } else {
- out << " " << cClassName << " *local_this = nullptr;\n";
- out << " if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", (void **)&local_this, \""
- << classNameFromCppName(cClassName, false) << "." << ielem.get_name() << "\")) {\n";
- }
- out << " return nullptr;\n";
- out << " }\n\n";
- }
- if (property->_has_function != nullptr) {
- if (remap->_has_this) {
- out << " if (!local_this->" << property->_has_function->_ifunc.get_name() << "()) {\n";
- } else {
- out << " if (!" << cClassName << "::" << property->_has_function->_ifunc.get_name() << "()) {\n";
- }
- out << " Py_INCREF(Py_None);\n"
- << " return Py_None;\n"
- << " }\n";
- }
- std::set<FunctionRemap*> remaps;
- remaps.insert(remap);
- string expected_params;
- write_function_forset(out, remaps, 0, 0,
- expected_params, 2, false, true, AT_no_args,
- RF_pyobject | RF_err_null, false, false);
- out << "}\n\n";
- // Write out a setter if this is not a read-only property.
- if (!property->_setter_remaps.empty()) {
- out << "static int Dtool_" + ClassName + "_" + ielem.get_name() + "_Setter(PyObject *self, PyObject *arg, void *) {\n";
- if (remap->_has_this) {
- out << " " << cClassName << " *local_this = nullptr;\n";
- out << " if (!Dtool_Call_ExtractThisPointer_NonConst(self, Dtool_" << ClassName << ", (void **)&local_this, \""
- << classNameFromCppName(cClassName, false) << "." << ielem.get_name() << "\")) {\n";
- out << " return -1;\n";
- out << " }\n\n";
- }
- out << " if (arg == nullptr) {\n";
- if (property->_deleter != nullptr && remap->_has_this) {
- out << " local_this->" << property->_deleter->_ifunc.get_name() << "();\n"
- << " return 0;\n";
- } else if (property->_deleter != nullptr) {
- out << " " << cClassName << "::" << property->_deleter->_ifunc.get_name() << "();\n"
- << " return 0;\n";
- } else {
- out << " Dtool_Raise_TypeError(\"can't delete " << ielem.get_name() << " attribute\");\n"
- " return -1;\n";
- }
- out << " }\n";
- if (property->_clear_function != nullptr) {
- out << " if (arg == Py_None) {\n";
- if (remap->_has_this) {
- out << " local_this->" << property->_clear_function->_ifunc.get_name() << "();\n";
- } else {
- out << " " << cClassName << "::" << property->_clear_function->_ifunc.get_name() << "();\n";
- }
- out << " return 0;\n"
- << " }\n";
- }
- std::set<FunctionRemap*> remaps;
- // Extract only the setters that take one argument.
- for (FunctionRemap *remap : property->_setter_remaps) {
- int min_num_args = remap->get_min_num_args();
- int max_num_args = remap->get_max_num_args();
- if (min_num_args <= 1 && max_num_args >= 1) {
- remaps.insert(remap);
- }
- }
- string expected_params;
- write_function_forset(out, remaps, 1, 1,
- expected_params, 2, true, true, AT_single_arg,
- RF_int, false, false);
- out << " if (!_PyErr_OCCURRED()) {\n";
- out << " Dtool_Raise_BadArgumentsError(\n";
- output_quoted(out, 6, expected_params);
- out << ");\n";
- out << " }\n";
- out << " return -1;\n";
- out << "}\n\n";
- }
- }
- }
- /**
- * Records the indicated type, which may be a struct type, along with all of
- * its associated methods, if any.
- */
- InterfaceMaker::Object *InterfaceMakerPythonNative::
- record_object(TypeIndex type_index) {
- if (type_index == 0) {
- return nullptr;
- }
- Objects::iterator oi = _objects.find(type_index);
- if (oi != _objects.end()) {
- return (*oi).second;
- }
- InterrogateDatabase *idb = InterrogateDatabase::get_ptr();
- const InterrogateType &itype = idb->get_type(type_index);
- if (!is_cpp_type_legal(itype._cpptype)) {
- return nullptr;
- }
- Object *object = new Object(itype);
- bool inserted = _objects.insert(Objects::value_type(type_index, object)).second;
- assert(inserted);
- Function *function;
- int num_constructors = itype.number_of_constructors();
- for (int ci = 0; ci < num_constructors; ci++) {
- function = record_function(itype, itype.get_constructor(ci));
- if (is_function_legal(function)) {
- object->_constructors.push_back(function);
- }
- }
- int num_methods = itype.number_of_methods();
- int mi;
- for (mi = 0; mi < num_methods; mi++) {
- function = record_function(itype, itype.get_method(mi));
- if (is_function_legal(function)) {
- object->_methods.push_back(function);
- }
- }
- int num_casts = itype.number_of_casts();
- for (mi = 0; mi < num_casts; mi++) {
- function = record_function(itype, itype.get_cast(mi));
- if (is_function_legal(function)) {
- object->_methods.push_back(function);
- }
- }
- int num_derivations = itype.number_of_derivations();
- for (int di = 0; di < num_derivations; di++) {
- TypeIndex d_type_Index = itype.get_derivation(di);
- idb->get_type(d_type_Index);
- if (!interrogate_type_is_unpublished(d_type_Index)) {
- if (itype.derivation_has_upcast(di)) {
- function = record_function(itype, itype.derivation_get_upcast(di));
- if (is_function_legal(function)) {
- object->_methods.push_back(function);
- }
- }
- /*if (itype.derivation_has_downcast(di)) {
- // Downcasts are methods of the base class, not the child class.
- TypeIndex base_type_index = itype.get_derivation(di);
- const InterrogateType &base_type = idb->get_type(base_type_index);
- function = record_function(base_type, itype.derivation_get_downcast(di));
- if (is_function_legal(function)) {
- Object *pobject = record_object(base_type_index);
- if (pobject != NULL) {
- pobject->_methods.push_back(function);
- }
- }
- }*/
- }
- }
- int num_elements = itype.number_of_elements();
- for (int ei = 0; ei < num_elements; ei++) {
- ElementIndex element_index = itype.get_element(ei);
- Property *property = record_property(itype, element_index);
- if (property != nullptr) {
- object->_properties.push_back(property);
- } else {
- // No use exporting a property without a getter.
- delete property;
- }
- }
- int num_make_seqs = itype.number_of_make_seqs();
- for (int msi = 0; msi < num_make_seqs; msi++) {
- MakeSeqIndex make_seq_index = itype.get_make_seq(msi);
- const InterrogateMakeSeq &imake_seq = idb->get_make_seq(make_seq_index);
- string class_name = itype.get_scoped_name();
- string clean_name = InterrogateBuilder::clean_identifier(class_name);
- string wrapper_name = "MakeSeq_" + clean_name + "_" + imake_seq.get_name();
- MakeSeq *make_seq = new MakeSeq(wrapper_name, imake_seq);
- make_seq->_length_getter = record_function(itype, imake_seq.get_length_getter());
- make_seq->_element_getter = record_function(itype, imake_seq.get_element_getter());
- object->_make_seqs.push_back(make_seq);
- }
- object->check_protocols();
- int num_nested = itype.number_of_nested_types();
- for (int ni = 0; ni < num_nested; ni++) {
- TypeIndex nested_index = itype.get_nested_type(ni);
- record_object(nested_index);
- }
- return object;
- }
- /**
- *
- */
- InterfaceMaker::Property *InterfaceMakerPythonNative::
- record_property(const InterrogateType &itype, ElementIndex element_index) {
- InterrogateDatabase *idb = InterrogateDatabase::get_ptr();
- const InterrogateElement &ielement = idb->get_element(element_index);
- if (!ielement.has_getter()) {
- // A property needs at the very least a getter.
- return nullptr;
- }
- Property *property;
- {
- FunctionIndex func_index = ielement.get_getter();
- if (func_index != 0) {
- const InterrogateFunction &ifunc = idb->get_function(func_index);
- property = new Property(ielement);
- InterrogateFunction::Instances::const_iterator ii;
- for (ii = ifunc._instances->begin(); ii != ifunc._instances->end(); ++ii) {
- CPPInstance *cppfunc = (*ii).second;
- FunctionRemap *remap =
- make_function_remap(itype, ifunc, cppfunc, 0);
- if (remap != nullptr && is_remap_legal(remap)) {
- property->_getter_remaps.push_back(remap);
- property->_has_this |= remap->_has_this;
- }
- }
- } else {
- return nullptr;
- }
- }
- if (ielement.has_setter()) {
- FunctionIndex func_index = ielement.get_setter();
- if (func_index != 0) {
- const InterrogateFunction &ifunc = idb->get_function(func_index);
- InterrogateFunction::Instances::const_iterator ii;
- for (ii = ifunc._instances->begin(); ii != ifunc._instances->end(); ++ii) {
- CPPInstance *cppfunc = (*ii).second;
- FunctionRemap *remap =
- make_function_remap(itype, ifunc, cppfunc, 0);
- if (remap != nullptr && is_remap_legal(remap)) {
- property->_setter_remaps.push_back(remap);
- property->_has_this |= remap->_has_this;
- }
- }
- }
- }
- if (ielement.has_has_function()) {
- FunctionIndex func_index = ielement.get_has_function();
- Function *has_function = record_function(itype, func_index);
- if (is_function_legal(has_function)) {
- property->_has_function = has_function;
- property->_has_this |= has_function->_has_this;
- }
- }
- if (ielement.has_clear_function()) {
- FunctionIndex func_index = ielement.get_clear_function();
- Function *clear_function = record_function(itype, func_index);
- if (is_function_legal(clear_function)) {
- property->_clear_function = clear_function;
- property->_has_this |= clear_function->_has_this;
- }
- }
- if (ielement.has_del_function()) {
- FunctionIndex func_index = ielement.get_del_function();
- Function *del_function = record_function(itype, func_index);
- if (is_function_legal(del_function)) {
- property->_deleter = del_function;
- property->_has_this |= del_function->_has_this;
- }
- }
- if (ielement.is_sequence() || ielement.is_mapping()) {
- FunctionIndex func_index = ielement.get_length_function();
- if (func_index != 0) {
- property->_length_function = record_function(itype, func_index);
- }
- }
- if (ielement.is_sequence() && ielement.has_insert_function()) {
- FunctionIndex func_index = ielement.get_insert_function();
- Function *insert_function = record_function(itype, func_index);
- if (is_function_legal(insert_function)) {
- property->_inserter = insert_function;
- property->_has_this |= insert_function->_has_this;
- }
- }
- if (ielement.is_mapping() && ielement.has_getkey_function()) {
- FunctionIndex func_index = ielement.get_getkey_function();
- assert(func_index != 0);
- Function *getkey_function = record_function(itype, func_index);
- if (is_function_legal(getkey_function)) {
- property->_getkey_function = getkey_function;
- property->_has_this |= getkey_function->_has_this;
- }
- }
- return property;
- }
- /**
- * Walks through the set of functions in the database and generates wrappers
- * for each function, storing these in the database. No actual code should be
- * output yet; this just updates the database with the wrapper information.
- */
- void InterfaceMakerPythonNative::
- generate_wrappers() {
- InterrogateDatabase *idb = InterrogateDatabase::get_ptr();
- // We use a while loop rather than a simple for loop, because we might
- // increase the number of types recursively during the traversal.
- int ti = 0;
- while (ti < idb->get_num_all_types()) {
- TypeIndex type_index = idb->get_all_type(ti);
- record_object(type_index);
- ++ti;
- }
- int num_global_elements = idb->get_num_global_elements();
- for (int gi = 0; gi < num_global_elements; ++gi) {
- TypeIndex type_index = idb->get_global_element(gi);
- record_object(type_index);
- }
- int num_functions = idb->get_num_global_functions();
- for (int fi = 0; fi < num_functions; fi++) {
- FunctionIndex func_index = idb->get_global_function(fi);
- record_function(dummy_type, func_index);
- }
- int num_manifests = idb->get_num_global_manifests();
- for (int mi = 0; mi < num_manifests; mi++) {
- ManifestIndex manifest_index = idb->get_global_manifest(mi);
- const InterrogateManifest &iman = idb->get_manifest(manifest_index);
- if (iman.has_getter()) {
- FunctionIndex func_index = iman.get_getter();
- record_function(dummy_type, func_index);
- }
- }
- int num_elements = idb->get_num_global_elements();
- for (int ei = 0; ei < num_elements; ei++) {
- ElementIndex element_index = idb->get_global_element(ei);
- const InterrogateElement &ielement = idb->get_element(element_index);
- if (ielement.has_getter()) {
- FunctionIndex func_index = ielement.get_getter();
- record_function(dummy_type, func_index);
- }
- if (ielement.has_setter()) {
- FunctionIndex func_index = ielement.get_setter();
- record_function(dummy_type, func_index);
- }
- }
- }
- /**
- */
- bool InterfaceMakerPythonNative::
- is_cpp_type_legal(CPPType *in_ctype) {
- if (in_ctype == nullptr) {
- return false;
- }
- string name = in_ctype->get_local_name(&parser);
- if (builder.in_ignoretype(name)) {
- return false;
- }
- if (builder.in_forcetype(name)) {
- return true;
- }
- // bool answer = false;
- CPPType *type = TypeManager::resolve_type(in_ctype);
- if (TypeManager::is_rvalue_reference(type)) {
- return false;
- }
- type = TypeManager::unwrap(type);
- if (TypeManager::is_void(type)) {
- return true;
- } else if (TypeManager::is_basic_string_char(type)) {
- return true;
- } else if (TypeManager::is_basic_string_wchar(type)) {
- return true;
- } else if (TypeManager::is_vector_unsigned_char(in_ctype)) {
- return true;
- } else if (TypeManager::is_simple(type)) {
- return true;
- } else if (TypeManager::is_pointer_to_simple(type)) {
- return true;
- } else if (builder.in_forcetype(type->get_local_name(&parser))) {
- return true;
- } else if (TypeManager::is_exported(type)) {
- return true;
- } else if (TypeManager::is_pointer_to_PyObject(in_ctype)) {
- return true;
- } else if (TypeManager::is_pointer_to_Py_buffer(in_ctype)) {
- return true;
- }
- // if (answer == false) printf(" -------------------- Bad Type ??
- // %s\n",type->get_local_name().c_str());
- return false;
- }
- /**
- */
- bool InterfaceMakerPythonNative::
- isExportThisRun(CPPType *ctype) {
- if (builder.in_forcetype(ctype->get_local_name(&parser))) {
- return true;
- }
- if (!TypeManager::is_exported(ctype)) {
- return false;
- }
- if (TypeManager::is_local(ctype)) {
- return true;
- }
- return false;
- }
- /**
- */
- bool InterfaceMakerPythonNative::
- isExportThisRun(Function *func) {
- if (func == nullptr || !is_function_legal(func)) {
- return false;
- }
- for (FunctionRemap *remap : func->_remaps) {
- return isExportThisRun(remap->_cpptype);
- }
- return false;
- }
- /**
- */
- bool InterfaceMakerPythonNative::
- is_remap_legal(FunctionRemap *remap) {
- if (remap == nullptr) {
- return false;
- }
- // return must be legal and managable..
- if (!is_cpp_type_legal(remap->_return_type->get_orig_type())) {
- // printf(" is_remap_legal Return Is Bad %s\n",remap->_return_type->get_orig_
- // type()->get_fully_scoped_name().c_str());
- return false;
- }
- // We don't currently support returning pointers, but we accept them as
- // function parameters. But const char * is an exception.
- if (!remap->_return_type->new_type_is_atomic_string() &&
- TypeManager::is_pointer_to_simple(remap->_return_type->get_orig_type())) {
- return false;
- }
- // ouch .. bad things will happen here .. do not even try..
- if (remap->_ForcedVoidReturn) {
- return false;
- }
- // all non-optional params must be legal
- for (size_t pn = 0; pn < remap->_parameters.size(); pn++) {
- ParameterRemap *param = remap->_parameters[pn]._remap;
- CPPType *orig_type = param->get_orig_type();
- if (param->get_default_value() == nullptr && !is_cpp_type_legal(orig_type)) {
- return false;
- }
- }
- // ok all looks ok.
- return true;
- }
- /**
- */
- int InterfaceMakerPythonNative::
- has_coerce_constructor(CPPStructType *type) {
- if (type == nullptr) {
- return 0;
- }
- // It is convenient to set default-constructability and move-assignability
- // as requirement for non-reference-counted objects, since it simplifies the
- // implementation and it holds for all classes we need it for.
- if (!TypeManager::is_reference_count(type) &&
- (!type->is_default_constructible() || !type->is_move_assignable())) {
- return 0;
- }
- CPPScope *scope = type->get_scope();
- if (scope == nullptr) {
- return 0;
- }
- int result = 0;
- CPPScope::Functions::iterator fgi;
- for (fgi = scope->_functions.begin(); fgi != scope->_functions.end(); ++fgi) {
- CPPFunctionGroup *fgroup = fgi->second;
- for (CPPInstance *inst : fgroup->_instances) {
- CPPFunctionType *ftype = inst->_type->as_function_type();
- if (ftype == nullptr) {
- continue;
- }
- if (inst->_storage_class & CPPInstance::SC_explicit) {
- // Skip it if it is marked not to allow coercion.
- continue;
- }
- if (inst->_vis > min_vis) {
- // Not published.
- continue;
- }
- CPPParameterList::Parameters ¶ms = ftype->_parameters->_parameters;
- if (params.size() == 0) {
- // It's useless if it doesn't take any parameters.
- continue;
- }
- if (ftype->_flags & CPPFunctionType::F_constructor) {
- if (ftype->_flags & (CPPFunctionType::F_copy_constructor |
- CPPFunctionType::F_move_constructor)) {
- // Skip a copy and move constructor.
- continue;
- } else {
- return 2;
- }
- } else if (fgroup->_name == "make" && (inst->_storage_class & CPPInstance::SC_static) != 0) {
- if (TypeManager::is_const_pointer_or_ref(ftype->_return_type)) {
- result = 1;
- } else {
- return 2;
- }
- }
- }
- }
- return result;
- }
- /**
- */
- bool InterfaceMakerPythonNative::
- is_remap_coercion_possible(FunctionRemap *remap) {
- if (remap == nullptr) {
- return false;
- }
- size_t pn = 0;
- if (remap->_has_this) {
- // Skip the "this" parameter. It's never coercible.
- ++pn;
- }
- while (pn < remap->_parameters.size()) {
- CPPType *type = remap->_parameters[pn]._remap->get_new_type();
- if (TypeManager::is_char_pointer(type)) {
- } else if (TypeManager::is_wchar_pointer(type)) {
- } else if (TypeManager::is_pointer_to_PyObject(type)) {
- } else if (TypeManager::is_pointer_to_Py_buffer(type)) {
- } else if (TypeManager::is_pointer_to_simple(type)) {
- } else if (TypeManager::is_pointer(type)) {
- // This is a pointer to an object, so we might be able to coerce a
- // parameter to it.
- CPPType *obj_type = TypeManager::unwrap(TypeManager::resolve_type(type));
- if (has_coerce_constructor(obj_type->as_struct_type()) > 0) {
- // It has a coercion constructor, so go for it.
- return true;
- }
- }
- ++pn;
- }
- return false;
- }
- /**
- */
- bool InterfaceMakerPythonNative::
- is_function_legal(Function *func) {
- for (FunctionRemap *remap : func->_remaps) {
- if (is_remap_legal(remap)) {
- // printf(" Function Is Marked Legal %s\n",func->_name.c_str());
- return true;
- }
- }
- // printf(" Function Is Marked Illegal %s\n",func->_name.c_str());
- return false;
- }
- /**
- */
- bool InterfaceMakerPythonNative::
- IsRunTimeTyped(const InterrogateType &itype) {
- TypeIndex ptype_id = itype.get_outer_class();
- if (ptype_id > 0) {
- InterrogateDatabase *idb = InterrogateDatabase::get_ptr();
- InterrogateType ptype = idb->get_type(ptype_id);
- return IsRunTimeTyped(ptype);
- }
- if (itype.get_name() == "TypedObject") {
- return true;
- }
- return false;
- }
- /**
- */
- bool InterfaceMakerPythonNative::
- DoesInheritFromIsClass(const CPPStructType *inclass, const std::string &name) {
- if (inclass == nullptr) {
- return false;
- }
- std::string scoped_name = inclass->get_fully_scoped_name();
- if (scoped_name == name) {
- return true;
- }
- for (const CPPStructType::Base &base : inclass->_derivation) {
- CPPStructType *base_type = TypeManager::resolve_type(base._base)->as_struct_type();
- if (base_type != nullptr) {
- if (DoesInheritFromIsClass(base_type, name)) {
- return true;
- }
- }
- }
- return false;
- }
- /**
- */
- bool InterfaceMakerPythonNative::
- has_get_class_type_function(CPPType *type) {
- while (type->get_subtype() == CPPDeclaration::ST_typedef) {
- type = type->as_typedef_type()->_type;
- }
- CPPStructType *struct_type = type->as_struct_type();
- if (struct_type == nullptr) {
- return false;
- }
- CPPScope *scope = struct_type->get_scope();
- return scope->_functions.find("get_class_type") != scope->_functions.end();
- }
- /**
- *
- */
- bool InterfaceMakerPythonNative::
- has_init_type_function(CPPType *type) {
- while (type->get_subtype() == CPPDeclaration::ST_typedef) {
- type = type->as_typedef_type()->_type;
- }
- CPPStructType *struct_type = type->as_struct_type();
- if (struct_type == nullptr) {
- return false;
- }
- CPPScope *scope = struct_type->get_scope();
- CPPScope::Functions::const_iterator it = scope->_functions.find("init_type");
- if (it == scope->_functions.end()) {
- return false;
- }
- const CPPFunctionGroup *group = it->second;
- for (const CPPInstance *cppinst : group->_instances) {
- const CPPFunctionType *cppfunc = cppinst->_type->as_function_type();
- if (cppfunc != nullptr &&
- cppfunc->_parameters != nullptr &&
- cppfunc->_parameters->_parameters.size() == 0 &&
- (cppinst->_storage_class & CPPInstance::SC_static) != 0) {
- return true;
- }
- }
- return false;
- }
- /**
- * Returns -1 if the class does not define write() (and therefore cannot
- * support a __str__ function).
- *
- * Returns 1 if the class defines write(ostream).
- *
- * Returns 2 if the class defines write(ostream, int).
- *
- * Note that if you want specific behavior for Python str(), you should just
- * define a __str__ function, which maps directly to the appropriate type
- * slot.
- */
- int InterfaceMakerPythonNative::
- NeedsAStrFunction(const InterrogateType &itype_class) {
- InterrogateDatabase *idb = InterrogateDatabase::get_ptr();
- int num_methods = itype_class.number_of_methods();
- int mi;
- for (mi = 0; mi < num_methods; ++mi) {
- FunctionIndex func_index = itype_class.get_method(mi);
- const InterrogateFunction &ifunc = idb->get_function(func_index);
- if (ifunc.get_name() == "write") {
- if (ifunc._instances != nullptr) {
- InterrogateFunction::Instances::const_iterator ii;
- for (ii = ifunc._instances->begin();
- ii != ifunc._instances->end();
- ++ii) {
- CPPInstance *cppinst = (*ii).second;
- CPPFunctionType *cppfunc = cppinst->_type->as_function_type();
- if (cppfunc != nullptr) {
- if (cppfunc->_parameters != nullptr &&
- cppfunc->_return_type != nullptr &&
- TypeManager::is_void(cppfunc->_return_type)) {
- if (cppfunc->_parameters->_parameters.size() == 1) {
- CPPInstance *inst1 = cppfunc->_parameters->_parameters[0];
- if (TypeManager::is_pointer_to_ostream(inst1->_type)) {
- // write(ostream)
- return 1;
- }
- }
- if (cppfunc->_parameters->_parameters.size() == 2) {
- CPPInstance *inst1 = cppfunc->_parameters->_parameters[0];
- if (TypeManager::is_pointer_to_ostream(inst1->_type)) {
- inst1 = cppfunc->_parameters->_parameters[1];
- if (inst1->_initializer != nullptr) {
- // write(ostream, int = 0)
- return 1;
- }
- if (TypeManager::is_integer(inst1->_type)) {
- // write(ostream, int)
- return 2;
- }
- }
- }
- }
- }
- }
- }
- }
- }
- return -1;
- }
- /**
- * Returns -1 if the class does not define output() or python_repr() (and
- * therefore cannot support a __repr__ function).
- *
- * Returns 1 if the class defines python_repr(ostream, string).
- *
- * Returns 2 if the class defines output(ostream).
- *
- * Returns 3 if the class defines an extension function for
- * python_repr(ostream, string).
- *
- * Note that defining python_repr is deprecated in favor of defining a
- * __repr__ that returns a string, which maps directly to the appropriate type
- * slot.
- */
- int InterfaceMakerPythonNative::
- NeedsAReprFunction(const InterrogateType &itype_class) {
- InterrogateDatabase *idb = InterrogateDatabase::get_ptr();
- int num_methods = itype_class.number_of_methods();
- int mi;
- for (mi = 0; mi < num_methods; ++mi) {
- FunctionIndex func_index = itype_class.get_method(mi);
- const InterrogateFunction &ifunc = idb->get_function(func_index);
- if (ifunc.get_name() == "python_repr") {
- if (ifunc._instances != nullptr) {
- InterrogateFunction::Instances::const_iterator ii;
- for (ii = ifunc._instances->begin();
- ii != ifunc._instances->end();
- ++ii) {
- CPPInstance *cppinst = (*ii).second;
- CPPFunctionType *cppfunc = cppinst->_type->as_function_type();
- if (cppfunc != nullptr) {
- if (cppfunc->_parameters != nullptr &&
- cppfunc->_return_type != nullptr &&
- TypeManager::is_void(cppfunc->_return_type)) {
- if (cppfunc->_parameters->_parameters.size() == 2) {
- CPPInstance *inst1 = cppfunc->_parameters->_parameters[0];
- if (TypeManager::is_pointer_to_ostream(inst1->_type)) {
- inst1 = cppfunc->_parameters->_parameters[1];
- if (TypeManager::is_string(inst1->_type) ||
- TypeManager::is_char_pointer(inst1->_type)) {
- // python_repr(ostream, string)
- if ((cppinst->_storage_class & CPPInstance::SC_extension) != 0) {
- return 3;
- } else {
- return 1;
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
- for (mi = 0; mi < num_methods; ++mi) {
- FunctionIndex func_index = itype_class.get_method(mi);
- const InterrogateFunction &ifunc = idb->get_function(func_index);
- if (ifunc.get_name() == "output") {
- if (ifunc._instances != nullptr) {
- InterrogateFunction::Instances::const_iterator ii;
- for (ii = ifunc._instances->begin();
- ii != ifunc._instances->end();
- ++ii) {
- CPPInstance *cppinst = (*ii).second;
- CPPFunctionType *cppfunc = cppinst->_type->as_function_type();
- if (cppfunc != nullptr) {
- if (cppfunc->_parameters != nullptr &&
- cppfunc->_return_type != nullptr &&
- TypeManager::is_void(cppfunc->_return_type)) {
- if (cppfunc->_parameters->_parameters.size() == 1) {
- CPPInstance *inst1 = cppfunc->_parameters->_parameters[0];
- if (TypeManager::is_pointer_to_ostream(inst1->_type)) {
- // output(ostream)
- return 2;
- }
- }
- if (cppfunc->_parameters->_parameters.size() >= 2) {
- CPPInstance *inst1 = cppfunc->_parameters->_parameters[0];
- if (TypeManager::is_pointer_to_ostream(inst1->_type)) {
- inst1 = cppfunc->_parameters->_parameters[1];
- if (inst1->_initializer != nullptr) {
- // output(ostream, foo = bar, ...)
- return 2;
- }
- }
- }
- }
- }
- }
- }
- }
- }
- return -1;
- }
- /**
- * Returns true if the class defines a rich comparison operator.
- */
- bool InterfaceMakerPythonNative::
- NeedsARichCompareFunction(const InterrogateType &itype_class) {
- InterrogateDatabase *idb = InterrogateDatabase::get_ptr();
- int num_methods = itype_class.number_of_methods();
- int mi;
- for (mi = 0; mi < num_methods; ++mi) {
- FunctionIndex func_index = itype_class.get_method(mi);
- const InterrogateFunction &ifunc = idb->get_function(func_index);
- if (ifunc.get_name() == "operator <") {
- return true;
- }
- if (ifunc.get_name() == "operator <=") {
- return true;
- }
- if (ifunc.get_name() == "operator ==") {
- return true;
- }
- if (ifunc.get_name() == "operator !=") {
- return true;
- }
- if (ifunc.get_name() == "operator >") {
- return true;
- }
- if (ifunc.get_name() == "operator >=") {
- return true;
- }
- }
- return false;
- }
- /**
- * Outputs the indicated string as a single quoted, multi-line string to the
- * generated C++ source code. The output point is left on the last line of
- * the string, following the trailing quotation mark.
- */
- void InterfaceMakerPythonNative::
- output_quoted(ostream &out, int indent_level, const std::string &str,
- bool first_line) {
- indent(out, (first_line ? indent_level : 0))
- << '"';
- std::string::const_iterator si;
- for (si = str.begin(); si != str.end();) {
- switch (*si) {
- case '"':
- case '\\':
- out << '\\' << *si;
- break;
- case '\n':
- out << "\\n\"";
- if (++si == str.end()) {
- return;
- }
- out << "\n";
- indent(out, indent_level)
- << '"';
- continue;
- default:
- if (!isprint(*si)) {
- out << "\\" << oct << std::setw(3) << std::setfill('0') << (unsigned int)(*si)
- << dec;
- } else {
- out << *si;
- }
- }
- ++si;
- }
- out << '"';
- }
|