cpp.py 226 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880
  1. '''
  2. Functions for generating source code for the C++ bindings.
  3. '''
  4. import io
  5. import os
  6. import pickle
  7. import re
  8. import textwrap
  9. import jlib
  10. from . import classes
  11. from . import csharp
  12. from . import parse
  13. from . import python
  14. from . import rename
  15. from . import state
  16. from . import util
  17. def _make_top_level( text, top_level='::'):
  18. if text == 'string':
  19. # This is a hack; for some reason we often end up with `string` when it
  20. # it should be `std::string`.
  21. text = 'std::string'
  22. initial_prefix = ['']
  23. def handle_prefix( text, prefix):
  24. if text.startswith( prefix):
  25. initial_prefix[0] += prefix
  26. return text[ len(prefix):]
  27. return text
  28. text = handle_prefix( text, 'const ')
  29. text = handle_prefix( text, 'struct ')
  30. if text.startswith( ('fz_', 'pdf_')):
  31. text = f'{top_level}{text}'
  32. text = f'{initial_prefix[0]}{text}'
  33. return text
  34. def declaration_text(
  35. type_,
  36. name,
  37. nest=0,
  38. name_is_simple=True,
  39. verbose=False,
  40. expand_typedef=True,
  41. top_level='::',
  42. ):
  43. '''
  44. Returns text for C++ declaration of <type_> called <name>.
  45. type:
  46. a clang.cindex.Type.
  47. name:
  48. name of type; can be empty.
  49. nest:
  50. for internal diagnostics.
  51. name_is_simple:
  52. true iff <name> is an identifier.
  53. If name_is_simple is false, we surround <name> with (...) if type is a
  54. function.
  55. '''
  56. # clang can give unhelpful spelling for anonymous structs.
  57. assert 'struct (unnamed at ' not in type_.spelling, f'type_.spelling={type_.spelling}'
  58. if verbose:
  59. jlib.log( '{nest=} {name=} {type_.spelling=} {type_.get_declaration().get_usr()=}')
  60. jlib.log( '{type_.kind=} {type_.get_array_size()=} {expand_typedef=}')
  61. array_n = type_.get_array_size()
  62. if verbose:
  63. jlib.log( '{array_n=}')
  64. if array_n >= 0 or type_.kind == state.clang.cindex.TypeKind.INCOMPLETEARRAY:
  65. if verbose: jlib.log( '{array_n=}')
  66. if array_n < 0:
  67. array_n = ''
  68. ret = declaration_text(
  69. type_.get_array_element_type(),
  70. f'{name}[{array_n}]',
  71. nest+1,
  72. name_is_simple,
  73. verbose=verbose,
  74. expand_typedef=expand_typedef,
  75. top_level=top_level,
  76. )
  77. if verbose:
  78. jlib.log( 'returning {ret=}')
  79. return ret
  80. pointee = type_.get_pointee()
  81. if pointee and pointee.spelling:
  82. if type_.kind == state.clang.cindex.TypeKind.LVALUEREFERENCE:
  83. pointee_type = '&'
  84. elif type_.kind == state.clang.cindex.TypeKind.POINTER:
  85. pointee_type = '*'
  86. else:
  87. assert 0, f'Unrecognised pointer kind {type_.kind=}.'
  88. if verbose: jlib.log( '{type_=} {type_.kind=} {pointee.spelling=}')
  89. ret = declaration_text(
  90. pointee,
  91. f'{pointee_type}{name}',
  92. nest+1,
  93. name_is_simple=False,
  94. verbose=verbose,
  95. expand_typedef=expand_typedef,
  96. top_level=top_level,
  97. )
  98. if verbose:
  99. jlib.log( 'returning {ret=}')
  100. return ret
  101. if expand_typedef and type_.get_typedef_name():
  102. if verbose: jlib.log( '{type_.get_typedef_name()=}')
  103. const = 'const ' if type_.is_const_qualified() else ''
  104. ret = f'{const}{_make_top_level(type_.get_typedef_name(), top_level)} {name}'
  105. if verbose:
  106. jlib.log( 'returning {ret=}')
  107. return ret
  108. # On MacOS type `size_t` returns true from get_result() and is
  109. # state.clang.cindex.TypeKind.ELABORATED.
  110. #
  111. if ( type_.get_result().spelling
  112. and type_.kind not in
  113. (
  114. state.clang.cindex.TypeKind.FUNCTIONNOPROTO,
  115. state.clang.cindex.TypeKind.ELABORATED,
  116. )
  117. ):
  118. # <type> is a function. We call ourselves with type=type_.get_result()
  119. # and name=<name>(<args>).
  120. #
  121. assert type_.kind == state.clang.cindex.TypeKind.FUNCTIONPROTO, \
  122. f'{type_.spelling=} {type_.kind=}'
  123. ret = ''
  124. sep = ''
  125. for arg in type_.argument_types():
  126. ret += sep
  127. ret += declaration_text(
  128. arg,
  129. '',
  130. nest+1,
  131. top_level=top_level,
  132. verbose=verbose,
  133. expand_typedef=expand_typedef,
  134. )
  135. sep = ', '
  136. if verbose: jlib.log( '{ret!r=}')
  137. if not name_is_simple:
  138. # If name isn't a simple identifier, put it inside braces, e.g.
  139. # this crudely allows function pointers to work.
  140. name = f'({name})'
  141. ret = f'{name}({ret})'
  142. if verbose: jlib.log( '{type_.get_result()=}')
  143. ret = declaration_text(
  144. type_.get_result(),
  145. ret,
  146. nest+1,
  147. name_is_simple=False,
  148. verbose=verbose,
  149. expand_typedef=expand_typedef,
  150. top_level=top_level,
  151. )
  152. if verbose:
  153. jlib.log( 'returning {ret=}')
  154. return ret
  155. ret = f'{_make_top_level(type_.spelling, top_level)} {name}'
  156. assert not 'struct (unnamed at ' in ret, f'Bad clang name for anonymous struct: {ret}'
  157. if verbose: jlib.log( 'returning {ret=}')
  158. return ret
  159. def write_call_arg(
  160. tu,
  161. arg,
  162. classname,
  163. have_used_this,
  164. out_cpp,
  165. verbose=False,
  166. python=False,
  167. ):
  168. '''
  169. Write an arg of a function call, translating between raw and wrapping
  170. classes as appropriate.
  171. If the required type is a fz_ struct that we wrap, we assume that arg.name
  172. is a reference to an instance of the wrapper class. If the wrapper class
  173. is the same as <classname>, we use 'this->' instead of <name>. We also
  174. generate slightly different code depending on whether the wrapper class is
  175. pod or inline pod.
  176. arg:
  177. Arg from get_args().
  178. classname:
  179. Name of wrapper class available as 'this'.
  180. have_used_this:
  181. If true, we never use 'this->...'.
  182. out_cpp:
  183. .
  184. python:
  185. If true, we write python code, not C.
  186. Returns True if we have used 'this->...', else return <have_used_this>.
  187. '''
  188. assert isinstance( arg, parse.Arg)
  189. assert isinstance( arg.cursor, state.clang.cindex.Cursor)
  190. if not arg.alt:
  191. # Arg is a normal type; no conversion necessary.
  192. if python:
  193. out_cpp.write( arg.name_python)
  194. else:
  195. out_cpp.write( arg.name)
  196. return have_used_this
  197. if verbose:
  198. jlib.log( '{=arg.name arg.alt.spelling classname}')
  199. type_ = state.get_name_canonical( arg.cursor.type)
  200. ptr = '*'
  201. #log( '{=arg.name arg.alt.spelling classname type_.spelling}')
  202. if type_.kind == state.clang.cindex.TypeKind.POINTER:
  203. type_ = state.get_name_canonical( type_.get_pointee())
  204. ptr = ''
  205. #log( '{=arg.name arg.alt.spelling classname type_.spelling}')
  206. extras = parse.get_fz_extras( tu, type_.spelling)
  207. assert extras, f'No extras for type_.spelling={type_.spelling}'
  208. if verbose:
  209. jlib.log( 'param is fz: {type_.spelling=} {extras2.pod=}')
  210. assert extras.pod != 'none' \
  211. 'Cannot pass wrapper for {type_.spelling} as arg because pod is "none" so we cannot recover struct.'
  212. if python:
  213. if extras.pod == 'inline':
  214. out_cpp.write( f'{arg.name_python}.internal()')
  215. elif extras.pod:
  216. out_cpp.write( f'{arg.name_python}.m_internal')
  217. else:
  218. out_cpp.write( f'{arg.name_python}.m_internal')
  219. elif extras.pod == 'inline':
  220. # We use the address of the first class member, casting it to a pointer
  221. # to the wrapped type. Not sure this is guaranteed safe, but should
  222. # work in practise.
  223. name_ = f'{arg.name}.'
  224. if not have_used_this and rename.class_(arg.alt.type.spelling) == classname:
  225. have_used_this = True
  226. name_ = 'this->'
  227. field0 = parse.get_field0(type_).spelling
  228. out_cpp.write( f'{ptr} {name_}internal()')
  229. else:
  230. if verbose:
  231. jlib.log( '{=arg state.get_name_canonical(arg.cursor.type).kind classname extras}')
  232. if extras.pod and state.get_name_canonical( arg.cursor.type).kind == state.clang.cindex.TypeKind.POINTER:
  233. out_cpp.write( '&')
  234. elif not extras.pod and state.get_name_canonical( arg.cursor.type).kind != state.clang.cindex.TypeKind.POINTER:
  235. out_cpp.write( '*')
  236. elif arg.out_param:
  237. out_cpp.write( '&')
  238. if not have_used_this and rename.class_(arg.alt.type.spelling) == classname:
  239. have_used_this = True
  240. out_cpp.write( 'this->')
  241. else:
  242. out_cpp.write( f'{arg.name}.')
  243. out_cpp.write( 'm_internal')
  244. return have_used_this
  245. def make_fncall( tu, cursor, return_type, fncall, out, refcheck_if, trace_if):
  246. '''
  247. Writes a low-level function call to <out>, using fz_context_s from
  248. internal_context_get() and with fz_try...fz_catch that converts to C++
  249. exceptions by calling throw_exception().
  250. return_type:
  251. Text return type of function, e.g. 'void' or 'double'.
  252. fncall:
  253. Text containing function call, e.g. 'function(a, b, 34)'.
  254. out:
  255. Stream to which we write generated code.
  256. '''
  257. uses_fz_context = False;
  258. # Setting this to False is a hack to elide all fz_try/fz_catch code. This
  259. # has a very small effect on mupdfpy test suite performance - e.g. reduce
  260. # time from 548.1s to 543.2s.
  261. #
  262. use_fz_try = True
  263. if cursor.spelling in (
  264. 'pdf_specifics',
  265. ):
  266. # This fn takes a fz_context* but never throws, so we can omit
  267. # `fz_try()...fz_catch()`, which might give a small performance
  268. # improvement.
  269. use_fz_try = False
  270. uses_fz_context = True
  271. else:
  272. for arg in parse.get_args( tu, cursor, include_fz_context=True):
  273. if parse.is_pointer_to( arg.cursor.type, 'fz_context'):
  274. uses_fz_context = True
  275. break
  276. if uses_fz_context:
  277. context_get = rename.internal( 'context_get')
  278. throw_exception = rename.internal( 'throw_exception')
  279. out.write( f' fz_context* auto_ctx = {context_get}();\n')
  280. # Output code that writes diagnostics to std::cerr if $MUPDF_trace is set.
  281. #
  282. def varname_enable():
  283. for t in 'fz_keep_', 'fz_drop_', 'pdf_keep_', 'pdf_drop_':
  284. if cursor.spelling.startswith( t):
  285. return 's_trace_keepdrop'
  286. return 's_trace > 1'
  287. out.write( f' {trace_if}\n')
  288. out.write( f' if ({varname_enable()}) {{\n')
  289. out.write( f' std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): calling {cursor.spelling}():";\n')
  290. for arg in parse.get_args( tu, cursor, include_fz_context=True):
  291. if parse.is_pointer_to( arg.cursor.type, 'fz_context'):
  292. out.write( f' if ({varname_enable()}) std::cerr << " auto_ctx=" << auto_ctx;\n')
  293. elif arg.out_param:
  294. out.write( f' if ({varname_enable()}) std::cerr << " {arg.name}=" << (void*) {arg.name};\n')
  295. elif arg.alt:
  296. # If not a pod, there will not be an operator<<, so just show
  297. # the address of this arg.
  298. #
  299. extras = parse.get_fz_extras( tu, arg.alt.type.spelling)
  300. assert extras.pod != 'none' \
  301. 'Cannot pass wrapper for {type_.spelling} as arg because pod is "none" so we cannot recover struct.'
  302. if extras.pod:
  303. out.write( f' std::cerr << " {arg.name}=" << {arg.name};\n')
  304. elif arg.cursor.type.kind == state.clang.cindex.TypeKind.POINTER:
  305. out.write( f' if ({varname_enable()}) std::cerr << " {arg.name}=" << {arg.name};\n')
  306. elif arg.cursor.type.kind == state.clang.cindex.TypeKind.LVALUEREFERENCE:
  307. out.write( f' if ({varname_enable()}) std::cerr << " &{arg.name}=" << &{arg.name};\n')
  308. else:
  309. out.write( f' std::cerr << " &{arg.name}=" << &{arg.name};\n')
  310. elif parse.is_pointer_to(arg.cursor.type, 'char') and state.get_name_canonical( arg.cursor.type.get_pointee()).is_const_qualified():
  311. # 'const char*' is assumed to be zero-terminated string. But we
  312. # need to protect against trying to write nullptr because this
  313. # appears to kill std::cerr on Linux.
  314. out.write( f' if ({arg.name}) std::cerr << " {arg.name}=\'" << {arg.name} << "\'";\n')
  315. out.write( f' else std::cerr << " {arg.name}:null";\n')
  316. elif parse.is_( arg.cursor.type, 'va_list'):
  317. out.write( f' std::cerr << " {arg.name}:va_list";\n')
  318. elif (0
  319. or parse.is_( arg.cursor.type, 'signed char')
  320. or parse.is_( arg.cursor.type, 'unsigned char')
  321. ):
  322. # Typically used for raw data, so not safe to treat as text.
  323. out.write( f' std::cerr << " {arg.name}=" << ((int) {arg.name});\n')
  324. elif (0
  325. or parse.is_pointer_to(arg.cursor.type, 'signed char')
  326. or parse.is_pointer_to(arg.cursor.type, 'unsigned char')
  327. ):
  328. # Typically used for raw data, so not safe to treat as text.
  329. out.write( f' std::cerr << " {arg.name}=" << ((void*) {arg.name});\n')
  330. elif arg.cursor.type.kind == state.clang.cindex.TypeKind.POINTER:
  331. # Don't assume non-const 'char*' is a zero-terminated string.
  332. out.write( f' if ({varname_enable()}) std::cerr << " {arg.name}=" << (void*) {arg.name};\n')
  333. elif arg.cursor.type.kind == state.clang.cindex.TypeKind.LVALUEREFERENCE:
  334. out.write( f' if ({varname_enable()}) std::cerr << " &{arg.name}=" << &{arg.name};\n')
  335. else:
  336. out.write( f' std::cerr << " {arg.name}=" << {arg.name};\n')
  337. out.write( f' std::cerr << "\\n";\n')
  338. out.write( f' }}\n')
  339. out.write( f' #endif\n')
  340. if uses_fz_context:
  341. out.write( f' {refcheck_if}\n')
  342. out.write( f' long stack0;\n')
  343. out.write( f' if (s_check_error_stack)\n')
  344. out.write( f' {{\n')
  345. out.write( f' stack0 = auto_ctx->error.top - auto_ctx->error.stack_base;\n')
  346. out.write( f' }}\n')
  347. out.write( f' #endif\n')
  348. # Now output the function call.
  349. #
  350. if return_type != 'void':
  351. out.write( f' {return_type} ret;\n')
  352. if cursor.spelling == 'fz_warn':
  353. out.write( ' va_list ap;\n')
  354. out.write( ' fz_var(ap);\n')
  355. indent = ''
  356. if uses_fz_context and use_fz_try:
  357. out.write( f' fz_try(auto_ctx) {{\n')
  358. indent = ' '
  359. if cursor.spelling == 'fz_warn':
  360. out.write( f' {indent}va_start(ap, fmt);\n')
  361. out.write( f' {indent}fz_vwarn(auto_ctx, fmt, ap);\n')
  362. else:
  363. if not uses_fz_context:
  364. out.write( f' /* No fz_context* arg, so no need for fz_try()/fz_catch() to convert MuPDF exceptions into C++ exceptions. */\n')
  365. out.write( f' {indent}')
  366. if return_type != 'void':
  367. out.write( f'ret = ')
  368. out.write( f'{fncall};\n')
  369. if uses_fz_context and use_fz_try:
  370. out.write( f' }}\n')
  371. if cursor.spelling == 'fz_warn':
  372. if use_fz_try:
  373. out.write( f' fz_always(auto_ctx) {{\n')
  374. out.write( f' va_end(ap);\n')
  375. out.write( f' }}\n')
  376. else:
  377. out.write( f' va_end(ap);\n')
  378. if uses_fz_context and use_fz_try:
  379. out.write( f' fz_catch(auto_ctx) {{\n')
  380. out.write( f' {trace_if}\n')
  381. out.write( f' if (s_trace_exceptions) {{\n')
  382. out.write( f' std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): fz_catch() has caught exception.\\n";\n')
  383. out.write( f' }}\n')
  384. out.write( f' #endif\n')
  385. out.write( f' {throw_exception}(auto_ctx);\n')
  386. out.write( f' }}\n')
  387. if uses_fz_context:
  388. out.write( f' {refcheck_if}\n')
  389. out.write( f' if (s_check_error_stack)\n')
  390. out.write( f' {{\n')
  391. out.write( f' long stack1 = auto_ctx->error.top - auto_ctx->error.stack_base;\n')
  392. out.write( f' if (stack1 != stack0)\n')
  393. out.write( f' std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): MuPDF error stack size changed by {cursor.spelling}(): " << stack0 << " -> " << stack1 << "\\n";\n')
  394. out.write( f' }}\n')
  395. out.write( f' #endif\n')
  396. if return_type != 'void':
  397. out.write( f' return ret;\n')
  398. def to_pickle( obj, path):
  399. '''
  400. Pickles <obj> to file <path>.
  401. '''
  402. with open( path, 'wb') as f:
  403. pickle.dump( obj, f)
  404. def from_pickle( path):
  405. '''
  406. Returns contents of file <path> unpickled.
  407. '''
  408. with open( path, 'rb') as f:
  409. return pickle.load( f)
  410. class Generated:
  411. '''
  412. Stores information generated when we parse headers using clang.
  413. '''
  414. def __init__( self):
  415. self.h_files = []
  416. self.cpp_files = []
  417. self.fn_usage_filename = None
  418. self.container_classnames = []
  419. self.to_string_structnames = []
  420. self.fn_usage = dict()
  421. self.output_param_fns = []
  422. self.c_functions = []
  423. self.c_globals = []
  424. self.c_enums = []
  425. self.c_structs = []
  426. self.swig_cpp = io.StringIO()
  427. self.swig_cpp_python = io.StringIO()
  428. self.swig_python = io.StringIO()
  429. self.swig_python_exceptions = io.StringIO()
  430. self.swig_python_set_error_classes = io.StringIO()
  431. self.swig_csharp = io.StringIO()
  432. self.virtual_fnptrs = [] # List of extra wrapper class names with virtual fnptrs.
  433. self.cppyy_extra = ''
  434. def save( self, dirpath):
  435. '''
  436. Saves state to .pickle file, to be loaded later via pickle.load().
  437. '''
  438. to_pickle( self, f'{dirpath}/generated.pickle')
  439. def make_outparam_helper(
  440. tu,
  441. cursor,
  442. fnname,
  443. fnname_wrapper,
  444. generated,
  445. ):
  446. '''
  447. Create extra C++, Python and C# code to make tuple-returning wrapper of
  448. specified function.
  449. We write Python code to generated.swig_python and C++ code to
  450. generated.swig_cpp.
  451. '''
  452. verbose = False
  453. main_name = rename.ll_fn(cursor.spelling)
  454. generated.swig_cpp.write( '\n')
  455. # Write struct.
  456. generated.swig_cpp.write( 'namespace mupdf\n')
  457. generated.swig_cpp.write('{\n')
  458. generated.swig_cpp.write(f' /* Out-params helper class for {cursor.spelling}(). */\n')
  459. generated.swig_cpp.write(f' struct {main_name}_outparams\n')
  460. generated.swig_cpp.write(f' {{\n')
  461. for arg in parse.get_args( tu, cursor):
  462. if not arg.out_param:
  463. continue
  464. decl = declaration_text( arg.cursor.type, arg.name, verbose=verbose)
  465. if verbose:
  466. jlib.log( '{decl=}')
  467. assert arg.cursor.type.kind == state.clang.cindex.TypeKind.POINTER
  468. # We use state.get_name_canonical() here because, for example, it
  469. # converts int64_t to 'long long', which seems to be handled better by
  470. # swig - swig maps int64_t to mupdf.SWIGTYPE_p_int64_t which can't be
  471. # treated or converted to an integer.
  472. #
  473. # We also value-initialise in case the underlying mupdf function also
  474. # reads the supplied value - i.e. treats it as an in-parm as well as an
  475. # out-param; this is particularly important for pointer out-params.
  476. #
  477. pointee = state.get_name_canonical( arg.cursor.type.get_pointee())
  478. generated.swig_cpp.write(f' {declaration_text( pointee, arg.name)} = {{}};\n')
  479. generated.swig_cpp.write(f' }};\n')
  480. generated.swig_cpp.write('\n')
  481. # Write function definition.
  482. name_args = f'{main_name}_outparams_fn('
  483. sep = ''
  484. for arg in parse.get_args( tu, cursor):
  485. if arg.out_param:
  486. continue
  487. name_args += sep
  488. name_args += declaration_text( arg.cursor.type, arg.name, verbose=verbose)
  489. sep = ', '
  490. name_args += f'{sep}{main_name}_outparams* outparams'
  491. name_args += ')'
  492. generated.swig_cpp.write(f' /* Out-params function for {cursor.spelling}(). */\n')
  493. generated.swig_cpp.write(f' {declaration_text( cursor.result_type, name_args)}\n')
  494. generated.swig_cpp.write( ' {\n')
  495. return_void = (cursor.result_type.spelling == 'void')
  496. generated.swig_cpp.write(f' ')
  497. if not return_void:
  498. generated.swig_cpp.write(f'{declaration_text(cursor.result_type, "ret")} = ')
  499. generated.swig_cpp.write(f'{rename.ll_fn(cursor.spelling)}(')
  500. sep = ''
  501. for arg in parse.get_args( tu, cursor):
  502. generated.swig_cpp.write(sep)
  503. if arg.out_param:
  504. generated.swig_cpp.write(f'&outparams->{arg.name}')
  505. else:
  506. generated.swig_cpp.write(f'{arg.name}')
  507. sep = ', '
  508. generated.swig_cpp.write(');\n')
  509. if not return_void:
  510. generated.swig_cpp.write(' return ret;\n')
  511. generated.swig_cpp.write(' }\n')
  512. generated.swig_cpp.write('}\n')
  513. # Write Python wrapper.
  514. python.make_outparam_helper_python(tu, cursor, fnname, fnname_wrapper, generated, main_name)
  515. # Write C# wrapper.
  516. csharp.make_outparam_helper_csharp(tu, cursor, fnname, fnname_wrapper, generated, main_name)
  517. def make_python_class_method_outparam_override(
  518. tu,
  519. cursor,
  520. fnname,
  521. generated,
  522. structname,
  523. classname,
  524. return_type,
  525. ):
  526. '''
  527. Writes Python code to `generated.swig_python` that monkey-patches Python
  528. function or method to make it call the underlying MuPDF function's Python
  529. wrapper, which will return out-params in a tuple.
  530. This is necessary because C++ doesn't support out-params so the C++ API
  531. supports wrapper class out-params by taking references to a dummy wrapper
  532. class instances, whose m_internal is then changed to point to the out-param
  533. struct (with suitable calls to keep/drop to manage the destruction of the
  534. dummy instance).
  535. In Python, we could create dummy wrapper class instances (e.g. passing
  536. nullptr to constructor) and return them, but instead we make our own call
  537. to the underlying MuPDF function and wrap the out-params into wrapper
  538. classes.
  539. '''
  540. out = generated.swig_python
  541. # Underlying fn.
  542. main_name = rename.ll_fn(cursor.spelling)
  543. if structname:
  544. name_new = f'{classname}_{rename.method(structname, cursor.spelling)}_outparams_fn'
  545. else:
  546. name_new = f'{rename.fn(cursor.spelling)}_outparams_fn'
  547. # Define an internal Python function that will become the class method.
  548. #
  549. out.write( f'def {name_new}(')
  550. if structname:
  551. out.write( ' self')
  552. comma = ', '
  553. else:
  554. comma = ''
  555. for arg in parse.get_args( tu, cursor):
  556. if arg.out_param:
  557. continue
  558. if structname and parse.is_pointer_to( arg.cursor.type, structname):
  559. continue
  560. out.write(f'{comma}{arg.name_python}')
  561. comma = ', '
  562. out.write('):\n')
  563. out.write( ' """\n')
  564. if structname:
  565. out.write(f' Helper for out-params of class method {structname}::{main_name}() [{cursor.spelling}()].\n')
  566. else:
  567. out.write(f' Class-aware helper for out-params of {fnname}() [{cursor.spelling}()].\n')
  568. out.write( ' """\n')
  569. # ret, a, b, ... = foo::bar(self.m_internal, p, q, r, ...)
  570. out.write(f' ')
  571. sep = ''
  572. if cursor.result_type.spelling != 'void':
  573. out.write( 'ret')
  574. sep = ', '
  575. for arg in parse.get_args( tu, cursor):
  576. if not arg.out_param:
  577. continue
  578. out.write( f'{sep}{arg.name_python}')
  579. sep = ', '
  580. out.write( f' = {main_name}(')
  581. sep = ''
  582. if structname:
  583. out.write( f' self.m_internal')
  584. sep = ', '
  585. for arg in parse.get_args( tu, cursor):
  586. if arg.out_param:
  587. continue
  588. if structname and parse.is_pointer_to( arg.cursor.type, structname):
  589. continue
  590. out.write( sep)
  591. write_call_arg( tu, arg, classname, have_used_this=False, out_cpp=out, python=True)
  592. sep = ', '
  593. out.write( ')\n')
  594. # return ret, a, b.
  595. #
  596. # We convert returned items to wrapper classes if they are MuPDF types.
  597. #
  598. out.write( ' return ')
  599. sep = ''
  600. if cursor.result_type.spelling != 'void':
  601. if return_type:
  602. #out.write( f'{return_type}(ret)')
  603. # Return type is a class wrapper.
  604. return_ll_type = cursor.result_type
  605. do_keep = False
  606. if cursor.result_type.kind == state.clang.cindex.TypeKind.POINTER:
  607. return_ll_type = return_ll_type.get_pointee()
  608. if parse.has_refs( tu, return_ll_type):
  609. return_ll_type = return_ll_type.spelling
  610. return_ll_type = util.clip( return_ll_type, ('struct ', 'const '))
  611. assert return_ll_type.startswith( ( 'fz_', 'pdf_'))
  612. for prefix in ( 'fz_', 'pdf_'):
  613. if return_ll_type.startswith( prefix):
  614. break
  615. else:
  616. assert 0, f'Unexpected arg type: {return_ll_type}'
  617. return_extra = classes.classextras.get( tu, return_ll_type)
  618. if not function_name_implies_kept_references( fnname):
  619. do_keep = True
  620. else:
  621. if 'char' in return_ll_type.spelling:
  622. jlib.log('### Function returns {cursor.result_type.spelling=} -> {return_ll_type.spelling=}: {fnname}. {function_name_implies_kept_references(fnname)=}')
  623. if do_keep:
  624. keepfn = f'{prefix}keep_{return_ll_type[ len(prefix):]}'
  625. keepfn = rename.ll_fn( keepfn)
  626. out.write( f'{return_type}( {keepfn}( ret))')
  627. else:
  628. out.write( f'{return_type}(ret)')
  629. else:
  630. out.write( 'ret')
  631. sep = ', '
  632. for arg in parse.get_args( tu, cursor):
  633. if not arg.out_param:
  634. continue
  635. if arg.alt:
  636. name = util.clip( arg.alt.type.spelling, ('struct ', 'const '))
  637. for prefix in ( 'fz_', 'pdf_'):
  638. if name.startswith( prefix):
  639. break
  640. else:
  641. assert 0, f'Unexpected arg type: {name}'
  642. if function_name_implies_kept_references( fnname):
  643. out.write( f'{sep}{rename.class_(name)}( {arg.name_python})')
  644. else:
  645. keepfn = f'{prefix}keep_{name[ len(prefix):]}'
  646. keepfn = rename.ll_fn( keepfn)
  647. out.write( f'{sep}{rename.class_(name)}({keepfn}( {arg.name_python}))')
  648. else:
  649. out.write( f'{sep}{arg.name_python}')
  650. sep = ', '
  651. out.write('\n')
  652. out.write('\n')
  653. # foo.bar = foo_bar_outparams_fn
  654. if structname:
  655. out.write(f'{classname}.{rename.method(structname, cursor.spelling)} = {name_new}\n')
  656. else:
  657. out.write(f'{rename.fn( cursor.spelling)} = {name_new}\n')
  658. out.write('\n')
  659. out.write('\n')
  660. def make_wrapper_comment(
  661. tu,
  662. cursor,
  663. fnname,
  664. fnname_wrapper,
  665. indent,
  666. is_method,
  667. is_low_level,
  668. ):
  669. ret = io.StringIO()
  670. def write(text):
  671. text = text.replace('\n', f'\n{indent}')
  672. ret.write( text)
  673. num_out_params = 0
  674. for arg in parse.get_args(
  675. tu,
  676. cursor,
  677. include_fz_context=False,
  678. skip_first_alt=is_method,
  679. ):
  680. if arg.out_param:
  681. num_out_params += 1
  682. if is_low_level:
  683. write( f'Low-level wrapper for `{rename.c_fn(cursor.spelling)}()`.')
  684. else:
  685. write( f'Class-aware wrapper for `{rename.c_fn(cursor.spelling)}()`.')
  686. if num_out_params:
  687. tuple_size = num_out_params
  688. if cursor.result_type.spelling != 'void':
  689. tuple_size += 1
  690. write( f'\n')
  691. write( f'\n')
  692. write( f'This {"method" if is_method else "function"} has out-params. Python/C# wrappers look like:\n')
  693. write( f' `{fnname_wrapper}(')
  694. sep = ''
  695. for arg in parse.get_args( tu, cursor, include_fz_context=False, skip_first_alt=is_method):
  696. if arg.alt or not arg.out_param:
  697. write( f'{sep}{declaration_text( arg.cursor.type, arg.name)}')
  698. sep = ', '
  699. write(')` => ')
  700. if tuple_size > 1:
  701. write( '`(')
  702. sep = ''
  703. if cursor.result_type.spelling != 'void':
  704. write( f'{cursor.result_type.spelling}')
  705. sep = ', '
  706. for arg in parse.get_args( tu, cursor, include_fz_context=False, skip_first_alt=is_method):
  707. if not arg.alt and arg.out_param:
  708. write( f'{sep}{declaration_text( arg.cursor.type.get_pointee(), arg.name)}')
  709. sep = ', '
  710. if tuple_size > 1:
  711. write( ')`')
  712. write( f'\n')
  713. else:
  714. write( ' ')
  715. return ret.getvalue()
  716. def function_wrapper(
  717. tu,
  718. cursor,
  719. fnname,
  720. fnname_wrapper,
  721. out_h,
  722. out_cpp,
  723. generated,
  724. refcheck_if,
  725. trace_if,
  726. ):
  727. '''
  728. Writes low-level C++ wrapper fn, converting any fz_try..fz_catch exception
  729. into a C++ exception.
  730. cursor:
  731. Clang cursor for function to wrap.
  732. fnname:
  733. Name of wrapped function.
  734. fnname_wrapper:
  735. Name of function to create.
  736. out_h:
  737. Stream to which we write header output.
  738. out_cpp:
  739. Stream to which we write cpp output.
  740. generated:
  741. A Generated instance.
  742. refcheck_if:
  743. A '#if*' statement that determines whether extra checks are compiled
  744. in.
  745. trace_if:
  746. A '#if*' statement that determines whether runtime diagnostics are
  747. compiled in.
  748. Example generated function:
  749. fz_band_writer * mupdf_new_band_writer_of_size(fz_context *ctx, size_t size, fz_output *out)
  750. {
  751. fz_band_writer * ret;
  752. fz_try(ctx) {
  753. ret = fz_new_band_writer_of_size(ctx, size, out);
  754. }
  755. fz_catch(ctx) {
  756. mupdf_throw_exception(ctx);
  757. }
  758. return ret;
  759. }
  760. '''
  761. assert cursor.kind == state.clang.cindex.CursorKind.FUNCTION_DECL
  762. if cursor.type.is_function_variadic() and fnname != 'fz_warn':
  763. jlib.log( 'Not writing low-level wrapper because variadic: {fnname=}', 1)
  764. return
  765. verbose = state.state_.show_details( fnname)
  766. if verbose:
  767. jlib.log( 'Wrapping {fnname}')
  768. num_out_params = 0
  769. for arg in parse.get_args( tu, cursor, include_fz_context=True):
  770. if parse.is_pointer_to(arg.cursor.type, 'fz_context'):
  771. continue
  772. if arg.out_param:
  773. num_out_params += 1
  774. # Write first line: <result_type> <fnname_wrapper> (<args>...)
  775. #
  776. comment = make_wrapper_comment( tu, cursor, fnname, fnname_wrapper, indent='', is_method=False, is_low_level=True)
  777. comment = f'/** {comment}*/\n'
  778. for out in out_h, out_cpp:
  779. out.write( comment)
  780. # Copy any comment into .h file before declaration.
  781. if cursor.raw_comment:
  782. # On Windows, carriage returns can appear in cursor.raw_comment on
  783. # due to line ending inconsistencies in our generated extra.cpp and
  784. # extra.h, and can cause spurious differences in our generated C++
  785. # code, which in turn causes unnecessary rebuilds.
  786. #
  787. # It would probably better to fix line endings in our generation of
  788. # extra.*.
  789. raw_comment = cursor.raw_comment.replace('\r', '')
  790. out_h.write(raw_comment)
  791. if not raw_comment.endswith( '\n'):
  792. out_h.write( '\n')
  793. # Write declaration and definition.
  794. name_args_h = f'{fnname_wrapper}('
  795. name_args_cpp = f'{fnname_wrapper}('
  796. comma = ''
  797. for arg in parse.get_args( tu, cursor, include_fz_context=True):
  798. if verbose:
  799. jlib.log( '{arg.cursor=} {arg.name=} {arg.separator=} {arg.alt=} {arg.out_param=}')
  800. if parse.is_pointer_to(arg.cursor.type, 'fz_context'):
  801. continue
  802. decl = declaration_text( arg.cursor.type, arg.name, verbose=verbose)
  803. if verbose:
  804. jlib.log( '{decl=}')
  805. name_args_h += f'{comma}{decl}'
  806. decl = declaration_text( arg.cursor.type, arg.name)
  807. name_args_cpp += f'{comma}{decl}'
  808. comma = ', '
  809. if cursor.type.is_function_variadic():
  810. name_args_h += f'{comma}...'
  811. name_args_cpp += f'{comma}...'
  812. name_args_h += ')'
  813. name_args_cpp += ')'
  814. declaration_h = declaration_text( cursor.result_type, name_args_h, verbose=verbose)
  815. declaration_cpp = declaration_text( cursor.result_type, name_args_cpp, verbose=verbose)
  816. out_h.write( f'FZ_FUNCTION {declaration_h};\n')
  817. out_h.write( '\n')
  818. # Write function definition.
  819. #
  820. out_cpp.write( f'FZ_FUNCTION {declaration_cpp}\n')
  821. out_cpp.write( '{\n')
  822. return_type = cursor.result_type.spelling
  823. fncall = ''
  824. fncall += f'{rename.c_fn(cursor.spelling)}('
  825. for arg in parse.get_args( tu, cursor, include_fz_context=True):
  826. if parse.is_pointer_to( arg.cursor.type, 'fz_context'):
  827. fncall += f'{arg.separator}auto_ctx'
  828. else:
  829. fncall += f'{arg.separator}{arg.name}'
  830. fncall += ')'
  831. make_fncall( tu, cursor, return_type, fncall, out_cpp, refcheck_if, trace_if)
  832. out_cpp.write( '}\n')
  833. out_cpp.write( '\n')
  834. if num_out_params:
  835. make_outparam_helper(
  836. tu,
  837. cursor,
  838. fnname,
  839. fnname_wrapper,
  840. generated,
  841. )
  842. def make_namespace_open( namespace, out):
  843. if namespace:
  844. out.write( '\n')
  845. out.write( f'namespace {namespace}\n')
  846. out.write( '{\n')
  847. def make_namespace_close( namespace, out):
  848. if namespace:
  849. out.write( '\n')
  850. out.write( f'}} /* End of namespace {namespace}. */\n')
  851. # libclang can't always find headers so we define our own `std::string`
  852. # and `std::vector<>` that work well enough for the generation of the
  853. # C++ API.
  854. #
  855. # We also define extra raw functions to aid SWIG-generated code. These
  856. # are implemented in C++, and should be excluded from the generated
  857. # windows_def file later on, otherwise we get link errors on Windows.
  858. #
  859. g_extra_declarations = textwrap.dedent(f'''
  860. #ifdef MUPDF_WRAP_LIBCLANG
  861. namespace std
  862. {{
  863. template<typename T>
  864. struct vector
  865. {{
  866. }};
  867. struct string
  868. {{
  869. }};
  870. }}
  871. #else
  872. #include <string>
  873. #include <vector>
  874. #endif
  875. #include "mupdf/fitz.h"
  876. #include "mupdf/pdf.h"
  877. /**
  878. C++ alternative to `fz_lookup_metadata()` that returns a `std::string`
  879. or calls `fz_throw()` if not found.
  880. */
  881. FZ_FUNCTION std::string fz_lookup_metadata2(fz_context* ctx, fz_document* doc, const char* key);
  882. /**
  883. C++ alternative to `pdf_lookup_metadata()` that returns a `std::string`
  884. or calls `fz_throw()` if not found.
  885. */
  886. FZ_FUNCTION std::string pdf_lookup_metadata2(fz_context* ctx, pdf_document* doc, const char* key);
  887. /**
  888. C++ alternative to `fz_md5_pixmap()` that returns the digest by value.
  889. */
  890. FZ_FUNCTION std::vector<unsigned char> fz_md5_pixmap2(fz_context* ctx, fz_pixmap* pixmap);
  891. /**
  892. C++ alternative to fz_md5_final() that returns the digest by value.
  893. */
  894. FZ_FUNCTION std::vector<unsigned char> fz_md5_final2(fz_md5* md5);
  895. /** */
  896. FZ_FUNCTION long long fz_pixmap_samples_int(fz_context* ctx, fz_pixmap* pixmap);
  897. /**
  898. Provides simple (but slow) access to pixmap data from Python and C#.
  899. */
  900. FZ_FUNCTION int fz_samples_get(fz_pixmap* pixmap, int offset);
  901. /**
  902. Provides simple (but slow) write access to pixmap data from Python and
  903. C#.
  904. */
  905. FZ_FUNCTION void fz_samples_set(fz_pixmap* pixmap, int offset, int value);
  906. /**
  907. C++ alternative to fz_highlight_selection() that returns quads in a
  908. std::vector.
  909. */
  910. FZ_FUNCTION std::vector<fz_quad> fz_highlight_selection2(fz_context* ctx, fz_stext_page* page, fz_point a, fz_point b, int max_quads);
  911. struct fz_search_page2_hit
  912. {{
  913. fz_quad quad;
  914. int mark;
  915. }};
  916. /**
  917. C++ alternative to fz_search_page() that returns information in a std::vector.
  918. */
  919. FZ_FUNCTION std::vector<fz_search_page2_hit> fz_search_page2(fz_context* ctx, fz_document* doc, int number, const char* needle, int hit_max);
  920. /**
  921. C++ alternative to fz_string_from_text_language() that returns information in a std::string.
  922. */
  923. FZ_FUNCTION std::string fz_string_from_text_language2(fz_text_language lang);
  924. /**
  925. C++ alternative to fz_get_glyph_name() that returns information in a std::string.
  926. */
  927. FZ_FUNCTION std::string fz_get_glyph_name2(fz_context* ctx, fz_font* font, int glyph);
  928. /**
  929. Extra struct containing fz_install_load_system_font_funcs()'s args,
  930. which we wrap with virtual_fnptrs set to allow use from Python/C# via
  931. Swig Directors.
  932. */
  933. typedef struct fz_install_load_system_font_funcs_args
  934. {{
  935. fz_load_system_font_fn* f;
  936. fz_load_system_cjk_font_fn* f_cjk;
  937. fz_load_system_fallback_font_fn* f_fallback;
  938. }} fz_install_load_system_font_funcs_args;
  939. /**
  940. Alternative to fz_install_load_system_font_funcs() that takes args in a
  941. struct, to allow use from Python/C# via Swig Directors.
  942. */
  943. FZ_FUNCTION void fz_install_load_system_font_funcs2(fz_context* ctx, fz_install_load_system_font_funcs_args* args);
  944. /** Internal singleton state to allow Swig Director class to find
  945. fz_install_load_system_font_funcs_args class wrapper instance. */
  946. FZ_DATA extern void* fz_install_load_system_font_funcs2_state;
  947. /** Helper for calling `fz_document_handler::open` function pointer via
  948. Swig from Python/C#. */
  949. FZ_FUNCTION fz_document* fz_document_handler_open(fz_context* ctx, const fz_document_handler *handler, fz_stream* stream, fz_stream* accel, fz_archive* dir, void* recognize_state);
  950. /** Helper for calling a `fz_document_handler::recognize` function
  951. pointer via Swig from Python/C#. */
  952. FZ_FUNCTION int fz_document_handler_recognize(fz_context* ctx, const fz_document_handler *handler, const char *magic);
  953. /** Swig-friendly wrapper for pdf_choice_widget_options(), returns the
  954. options directly in a vector. */
  955. FZ_FUNCTION std::vector<std::string> pdf_choice_widget_options2(fz_context* ctx, pdf_annot* tw, int exportval);
  956. /** Swig-friendly wrapper for fz_new_image_from_compressed_buffer(),
  957. uses specified `decode` and `colorkey` if they are not null (in which
  958. case we assert that they have size `2*fz_colorspace_n(colorspace)`). */
  959. FZ_FUNCTION fz_image* fz_new_image_from_compressed_buffer2(
  960. fz_context* ctx,
  961. int w,
  962. int h,
  963. int bpc,
  964. fz_colorspace* colorspace,
  965. int xres,
  966. int yres,
  967. int interpolate,
  968. int imagemask,
  969. const std::vector<float>& decode,
  970. const std::vector<int>& colorkey,
  971. fz_compressed_buffer* buffer,
  972. fz_image* mask
  973. );
  974. /** Swig-friendly wrapper for pdf_rearrange_pages(). */
  975. void pdf_rearrange_pages2(
  976. fz_context* ctx,
  977. pdf_document* doc,
  978. const std::vector<int>& pages,
  979. pdf_clean_options_structure structure
  980. );
  981. /** Swig-friendly wrapper for pdf_subset_fonts(). */
  982. void pdf_subset_fonts2(fz_context *ctx, pdf_document *doc, const std::vector<int>& pages);
  983. /** Swig-friendly and typesafe way to do fz_snprintf(fmt, value). `fmt`
  984. must end with one of 'efg' otherwise we throw an exception. */
  985. std::string fz_format_double(fz_context* ctx, const char* fmt, double value);
  986. struct fz_font_ucs_gid
  987. {{
  988. unsigned long ucs;
  989. unsigned int gid;
  990. }};
  991. /** SWIG-friendly wrapper for fz_enumerate_font_cmap(). */
  992. std::vector<fz_font_ucs_gid> fz_enumerate_font_cmap2(fz_context* ctx, fz_font* font);
  993. /** SWIG-friendly wrapper for pdf_set_annot_callout_line(). */
  994. void pdf_set_annot_callout_line2(fz_context *ctx, pdf_annot *annot, std::vector<fz_point>& callout);
  995. /** SWIG-friendly wrapper for fz_decode_barcode_from_display_list(),
  996. avoiding leak of the returned string. */
  997. std::string fz_decode_barcode_from_display_list2(fz_context *ctx, fz_barcode_type *type, fz_display_list *list, fz_rect subarea, int rotate);
  998. /** SWIG-friendly wrapper for fz_decode_barcode_from_pixmap(), avoiding
  999. leak of the returned string. */
  1000. std::string fz_decode_barcode_from_pixmap2(fz_context *ctx, fz_barcode_type *type, fz_pixmap *pix, int rotate);
  1001. /** SWIG-friendly wrapper for fz_decode_barcode_from_page(), avoiding
  1002. leak of the returned string. */
  1003. std::string fz_decode_barcode_from_page2(fz_context *ctx, fz_barcode_type *type, fz_page *page, fz_rect subarea, int rotate);
  1004. ''')
  1005. g_extra_definitions = textwrap.dedent(f'''
  1006. FZ_FUNCTION std::string fz_lookup_metadata2( fz_context* ctx, fz_document* doc, const char* key)
  1007. {{
  1008. /* Find length first. */
  1009. int e = fz_lookup_metadata(ctx, doc, key, NULL /*buf*/, 0 /*size*/);
  1010. if (e < 0)
  1011. {{
  1012. fz_throw(ctx, FZ_ERROR_GENERIC, "key not found: %s", key);
  1013. }}
  1014. assert(e != 0);
  1015. char* buf = (char*) fz_malloc(ctx, e);
  1016. int e2 = fz_lookup_metadata(ctx, doc, key, buf, e);
  1017. assert(e2 = e);
  1018. std::string ret = buf;
  1019. free(buf);
  1020. return ret;
  1021. }}
  1022. FZ_FUNCTION std::string pdf_lookup_metadata2( fz_context* ctx, pdf_document* doc, const char* key)
  1023. {{
  1024. /* Find length first. */
  1025. int e = pdf_lookup_metadata(ctx, doc, key, NULL /*buf*/, 0 /*size*/);
  1026. if (e < 0)
  1027. {{
  1028. fz_throw(ctx, FZ_ERROR_GENERIC, "key not found: %s", key);
  1029. }}
  1030. assert(e != 0);
  1031. char* buf = (char*) fz_malloc(ctx, e);
  1032. int e2 = pdf_lookup_metadata(ctx, doc, key, buf, e);
  1033. assert(e2 = e);
  1034. std::string ret = buf;
  1035. free(buf);
  1036. return ret;
  1037. }}
  1038. FZ_FUNCTION std::vector<unsigned char> fz_md5_pixmap2(fz_context* ctx, fz_pixmap* pixmap)
  1039. {{
  1040. std::vector<unsigned char> ret(16);
  1041. fz_md5_pixmap( ctx, pixmap, &ret[0]);
  1042. return ret;
  1043. }}
  1044. FZ_FUNCTION long long fz_pixmap_samples_int(fz_context* ctx, fz_pixmap* pixmap)
  1045. {{
  1046. long long ret = (intptr_t) pixmap->samples;
  1047. return ret;
  1048. }}
  1049. FZ_FUNCTION int fz_samples_get(fz_pixmap* pixmap, int offset)
  1050. {{
  1051. return pixmap->samples[offset];
  1052. }}
  1053. FZ_FUNCTION void fz_samples_set(fz_pixmap* pixmap, int offset, int value)
  1054. {{
  1055. pixmap->samples[offset] = value;
  1056. }}
  1057. FZ_FUNCTION std::vector<unsigned char> fz_md5_final2(fz_md5* md5)
  1058. {{
  1059. std::vector<unsigned char> ret(16);
  1060. fz_md5_final( md5, &ret[0]);
  1061. return ret;
  1062. }}
  1063. FZ_FUNCTION std::vector<fz_quad> fz_highlight_selection2(fz_context* ctx, fz_stext_page* page, fz_point a, fz_point b, int max_quads)
  1064. {{
  1065. {{
  1066. std::vector<fz_quad> ret(max_quads);
  1067. int n;
  1068. fz_try(ctx)
  1069. {{
  1070. n = fz_highlight_selection(ctx, page, a, b, &ret[0], max_quads);
  1071. }}
  1072. fz_catch(ctx)
  1073. {{
  1074. n = -1;
  1075. }}
  1076. if (n >= 0)
  1077. {{
  1078. ret.resize(n);
  1079. return ret;
  1080. }}
  1081. }}
  1082. /* We are careful to only call `fz_throw()` after `ret`'s
  1083. destructor has been called. */
  1084. fz_throw(ctx, FZ_ERROR_GENERIC, "fz_highlight_selection() failed");
  1085. }}
  1086. FZ_FUNCTION std::vector<fz_search_page2_hit> fz_search_page2(fz_context* ctx, fz_document* doc, int number, const char* needle, int hit_max)
  1087. {{
  1088. std::vector<fz_quad> quads(hit_max);
  1089. std::vector<int> marks(hit_max);
  1090. int n = fz_search_page_number(ctx, doc, number, needle, &marks[0], &quads[0], hit_max);
  1091. std::vector<fz_search_page2_hit> ret(n);
  1092. for (int i=0; i<n; ++i)
  1093. {{
  1094. ret[i].quad = quads[i];
  1095. ret[i].mark = marks[i];
  1096. }}
  1097. return ret;
  1098. }}
  1099. FZ_FUNCTION std::string fz_string_from_text_language2(fz_text_language lang)
  1100. {{
  1101. char str[8];
  1102. fz_string_from_text_language(str, lang);
  1103. return std::string(str);
  1104. }}
  1105. FZ_FUNCTION std::string fz_get_glyph_name2(fz_context* ctx, fz_font* font, int glyph)
  1106. {{
  1107. char name[32];
  1108. fz_get_glyph_name(ctx, font, glyph, name, sizeof(name));
  1109. return std::string(name);
  1110. }}
  1111. void fz_install_load_system_font_funcs2(fz_context* ctx, fz_install_load_system_font_funcs_args* args)
  1112. {{
  1113. fz_install_load_system_font_funcs(ctx, args->f, args->f_cjk, args->f_fallback);
  1114. }}
  1115. void* fz_install_load_system_font_funcs2_state = nullptr;
  1116. FZ_FUNCTION fz_document* fz_document_handler_open(fz_context* ctx, const fz_document_handler *handler, fz_stream* stream, fz_stream* accel, fz_archive* dir, void* recognize_state)
  1117. {{
  1118. return handler->open(ctx, handler, stream, accel, dir, recognize_state);
  1119. }}
  1120. FZ_FUNCTION int fz_document_handler_recognize(fz_context* ctx, const fz_document_handler *handler, const char *magic)
  1121. {{
  1122. return handler->recognize(ctx, handler, magic);
  1123. }}
  1124. FZ_FUNCTION std::vector<std::string> pdf_choice_widget_options2(fz_context* ctx, pdf_annot* tw, int exportval)
  1125. {{
  1126. int n = pdf_choice_widget_options(ctx, tw, exportval, nullptr);
  1127. std::vector<const char*> opts(n);
  1128. int n2 = pdf_choice_widget_options(ctx, tw, exportval, &opts[0]);
  1129. assert(n2 == n);
  1130. std::vector<std::string> ret(n);
  1131. for (int i=0; i<n; ++i)
  1132. {{
  1133. ret[i] = opts[i];
  1134. }}
  1135. return ret;
  1136. }}
  1137. FZ_FUNCTION fz_image* fz_new_image_from_compressed_buffer2(
  1138. fz_context* ctx,
  1139. int w,
  1140. int h,
  1141. int bpc,
  1142. fz_colorspace* colorspace,
  1143. int xres,
  1144. int yres,
  1145. int interpolate,
  1146. int imagemask,
  1147. const std::vector<float>& decode,
  1148. const std::vector<int>& colorkey,
  1149. fz_compressed_buffer* buffer,
  1150. fz_image* mask
  1151. )
  1152. {{
  1153. int n = fz_colorspace_n(ctx, colorspace);
  1154. assert(decode.empty() || decode.size() == 2 * n);
  1155. assert(colorkey.empty() || colorkey.size() == 2 * n);
  1156. const float* decode2 = decode.empty() ? nullptr : &decode[0];
  1157. const int* colorkey2 = colorkey.empty() ? nullptr : &colorkey[0];
  1158. fz_image* ret = fz_new_image_from_compressed_buffer(
  1159. ctx,
  1160. w,
  1161. h,
  1162. bpc,
  1163. colorspace,
  1164. xres,
  1165. yres,
  1166. interpolate,
  1167. imagemask,
  1168. decode2,
  1169. colorkey2,
  1170. fz_keep_compressed_buffer(ctx, buffer),
  1171. mask
  1172. );
  1173. return ret;
  1174. }}
  1175. void pdf_rearrange_pages2(
  1176. fz_context* ctx,
  1177. pdf_document* doc,
  1178. const std::vector<int>& pages,
  1179. pdf_clean_options_structure structure
  1180. )
  1181. {{
  1182. return pdf_rearrange_pages(ctx, doc, pages.size(), &pages[0], structure);
  1183. }}
  1184. void pdf_subset_fonts2(fz_context *ctx, pdf_document *doc, const std::vector<int>& pages)
  1185. {{
  1186. return pdf_subset_fonts(ctx, doc, pages.size(), &pages[0]);
  1187. }}
  1188. static void s_format_check(fz_context* ctx, const char* fmt, const char* specifiers)
  1189. {{
  1190. int length = strlen(fmt);
  1191. if (!length || !strchr(specifiers, fmt[length-1]))
  1192. {{
  1193. fz_throw(ctx, FZ_ERROR_ARGUMENT, "Incorrect fmt '%s' should end with one of '%s'.", fmt, specifiers);
  1194. }}
  1195. }}
  1196. std::string fz_format_double(fz_context* ctx, const char* fmt, double value)
  1197. {{
  1198. char buffer[256];
  1199. s_format_check(ctx, fmt, "efg");
  1200. fz_snprintf(buffer, sizeof(buffer), fmt, value);
  1201. return buffer;
  1202. }}
  1203. static void fz_enumerate_font_cmap2_cb(fz_context* ctx, void* opaque, unsigned long ucs, unsigned int gid)
  1204. {{
  1205. std::vector<fz_font_ucs_gid>& ret = *(std::vector<fz_font_ucs_gid>*) opaque;
  1206. fz_font_ucs_gid item = {{ucs, gid}};
  1207. ret.push_back(item);
  1208. }}
  1209. std::vector<fz_font_ucs_gid> fz_enumerate_font_cmap2(fz_context* ctx, fz_font* font)
  1210. {{
  1211. std::vector<fz_font_ucs_gid> ret;
  1212. fz_enumerate_font_cmap(ctx, font, fz_enumerate_font_cmap2_cb, &ret);
  1213. return ret;
  1214. }}
  1215. void pdf_set_annot_callout_line2(fz_context *ctx, pdf_annot *annot, std::vector<fz_point>& callout)
  1216. {{
  1217. pdf_set_annot_callout_line(ctx, annot, &callout[0], callout.size());
  1218. }}
  1219. std::string fz_decode_barcode_from_display_list2(fz_context *ctx, fz_barcode_type *type, fz_display_list *list, fz_rect subarea, int rotate)
  1220. {{
  1221. char* ret = fz_decode_barcode_from_display_list(ctx, type, list, subarea, rotate);
  1222. std::string ret2 = ret;
  1223. fz_free(ctx, ret);
  1224. return ret2;
  1225. }}
  1226. std::string fz_decode_barcode_from_pixmap2(fz_context *ctx, fz_barcode_type *type, fz_pixmap *pix, int rotate)
  1227. {{
  1228. char* ret = fz_decode_barcode_from_pixmap(ctx, type, pix, rotate);
  1229. std::string ret2 = ret;
  1230. fz_free(ctx, ret);
  1231. return ret2;
  1232. }}
  1233. std::string fz_decode_barcode_from_page2(fz_context *ctx, fz_barcode_type *type, fz_page *page, fz_rect subarea, int rotate)
  1234. {{
  1235. char* ret = fz_decode_barcode_from_page(ctx, type, page, subarea, rotate);
  1236. std::string ret2 = ret;
  1237. fz_free(ctx, ret);
  1238. return ret2;
  1239. }}
  1240. ''')
  1241. def make_extra( out_extra_h, out_extra_cpp):
  1242. '''
  1243. We write extra abstractions here.
  1244. These are written in C++ but are at the same level of abstraction as MuPDF
  1245. C functions, for example they take `fz_context` args. This is done so that
  1246. we automatically generate wrappers as class methods as well as global
  1247. functions.
  1248. '''
  1249. out_extra_h.write( g_extra_declarations)
  1250. out_extra_cpp.write( textwrap.dedent('''
  1251. #include "mupdf/extra.h"
  1252. '''))
  1253. out_extra_cpp.write( g_extra_definitions)
  1254. def make_internal_functions( namespace, out_h, out_cpp, refcheck_if, trace_if):
  1255. '''
  1256. Writes internal support functions.
  1257. out_h:
  1258. Stream to which we write C++ header text.
  1259. out_cpp:
  1260. Stream to which we write C++ text.
  1261. '''
  1262. out_h.write(
  1263. textwrap.dedent(
  1264. f'''
  1265. #define internal_assert(expression) (expression) ? (void) 0 : internal_assert_fail(__FILE__, __LINE__, __FUNCTION__, #expression)
  1266. FZ_FUNCTION void internal_assert_fail(const char* file, int line, const char* fn, const char* expression);
  1267. /** Internal use only. Looks at environmental variable <name>; returns 0 if unset else int value. */
  1268. FZ_FUNCTION int {rename.internal('env_flag')}(const char* name);
  1269. /** Internal use only. Looks at environmental variable <name>; returns 0 if unset else int value. */
  1270. FZ_FUNCTION int {rename.internal('env_flag_check_unset')}( const char* if_, const char* name);
  1271. /** Internal use only. Returns `fz_context*` for use by current thread. */
  1272. FZ_FUNCTION fz_context* {rename.internal('context_get')}();
  1273. '''
  1274. ))
  1275. out_cpp.write(
  1276. textwrap.dedent(
  1277. '''
  1278. #include "mupdf/exceptions.h"
  1279. #include "mupdf/internal.h"
  1280. #include <iostream>
  1281. #include <thread>
  1282. #include <mutex>
  1283. #include <string.h>
  1284. '''))
  1285. make_namespace_open( namespace, out_cpp)
  1286. state_t = rename.internal( 'state')
  1287. thread_state_t = rename.internal( 'thread_state')
  1288. cpp_text = textwrap.dedent(
  1289. f'''
  1290. FZ_FUNCTION void internal_assert_fail(const char* file, int line, const char* fn, const char* expression)
  1291. {{
  1292. std::cerr << file << ":" << line << ":" << fn << "(): "
  1293. << "MuPDF C++ internal assert failure: " << expression
  1294. << "\\n" << std::flush;
  1295. abort();
  1296. }}
  1297. FZ_FUNCTION int {rename.internal('env_flag')}(const char* name)
  1298. {{
  1299. const char* s = getenv( name);
  1300. if (!s) return 0;
  1301. return atoi( s);
  1302. }}
  1303. FZ_FUNCTION int {rename.internal('env_flag_check_unset')}(const char* if_, const char* name)
  1304. {{
  1305. const char* s = getenv( name);
  1306. if (s) std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():"
  1307. << " Warning: ignoring environmental variable because"
  1308. << " '" << if_ << "' is false: " << name << "\\n";
  1309. return false;
  1310. }}
  1311. {trace_if}
  1312. static const int s_trace = mupdf::internal_env_flag("MUPDF_trace");
  1313. #else
  1314. static const int s_trace = mupdf::internal_env_flag_check_unset("{trace_if}", "MUPDF_trace");
  1315. #endif
  1316. static bool s_state_valid = false;
  1317. struct {rename.internal("state")}
  1318. {{
  1319. /* Constructor. */
  1320. {rename.internal("state")}()
  1321. {{
  1322. m_locks.user = this;
  1323. m_locks.lock = lock;
  1324. m_locks.unlock = unlock;
  1325. m_ctx = nullptr;
  1326. bool multithreaded = true;
  1327. const char* s = getenv( "MUPDF_mt_ctx");
  1328. if ( s && !strcmp( s, "0")) multithreaded = false;
  1329. reinit( multithreaded);
  1330. s_state_valid = true;
  1331. }}
  1332. void reinit( bool multithreaded)
  1333. {{
  1334. if (s_trace)
  1335. {{
  1336. std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): "
  1337. << " calling fz_drop_context()\\n";
  1338. }}
  1339. fz_drop_context( m_ctx);
  1340. m_multithreaded = multithreaded;
  1341. if (s_trace)
  1342. {{
  1343. std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): "
  1344. << " calling fz_new_context()\\n";
  1345. }}
  1346. m_ctx = fz_new_context(NULL /*alloc*/, (multithreaded) ? &m_locks : nullptr, FZ_STORE_DEFAULT);
  1347. if (s_trace)
  1348. {{
  1349. std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): "
  1350. << "fz_new_context() => " << m_ctx << "\\n";
  1351. }}
  1352. if (s_trace)
  1353. {{
  1354. std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): "
  1355. << " calling fz_register_document_handlers()\\n";
  1356. }}
  1357. internal_assert("m_ctx = fz_new_context()" && m_ctx);
  1358. fz_register_document_handlers(m_ctx);
  1359. }}
  1360. static void lock(void *user, int lock)
  1361. {{
  1362. {rename.internal("state")}* self = ({rename.internal("state")}*) user;
  1363. internal_assert( self->m_multithreaded);
  1364. self->m_mutexes[lock].lock();
  1365. }}
  1366. static void unlock(void *user, int lock)
  1367. {{
  1368. {rename.internal("state")}* self = ({rename.internal("state")}*) user;
  1369. internal_assert( self->m_multithreaded);
  1370. self->m_mutexes[lock].unlock();
  1371. }}
  1372. ~{rename.internal("state")}()
  1373. {{
  1374. if (s_trace)
  1375. {{
  1376. std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): "
  1377. << " calling fz_drop_context()\\n";
  1378. }}
  1379. fz_drop_context(m_ctx);
  1380. m_ctx = nullptr;
  1381. s_state_valid = false;
  1382. }}
  1383. bool m_multithreaded;
  1384. fz_context* m_ctx;
  1385. std::mutex m_mutex; /* Serialise access to m_ctx. fixme: not actually necessary. */
  1386. /* Provide thread support to mupdf. */
  1387. std::mutex m_mutexes[FZ_LOCK_MAX];
  1388. fz_locks_context m_locks;
  1389. }};
  1390. static {rename.internal("state")} s_state;
  1391. struct {rename.internal("thread_state")}
  1392. {{
  1393. {rename.internal("thread_state")}()
  1394. :
  1395. m_ctx( nullptr),
  1396. m_constructed( true)
  1397. {{}}
  1398. fz_context* get_context()
  1399. {{
  1400. internal_assert( s_state.m_multithreaded);
  1401. /* The following code checks that we are not being called after
  1402. we have been destructed. This can happen if global mupdf
  1403. wrapper class instances are defined - thread-local objects
  1404. are destructed /before/ globals. */
  1405. if (!m_constructed)
  1406. {{
  1407. std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << ":\\n"
  1408. << "*** Error - undefined behaviour.\\n"
  1409. << "***\\n"
  1410. << "*** Attempt to get thread-local fz_context after destruction\\n"
  1411. << "*** of thread-local fz_context support instance.\\n"
  1412. << "***\\n"
  1413. << "*** This is undefined behaviour.\\n"
  1414. << "***\\n"
  1415. << "*** This can happen if mupdf wrapper class instances are\\n"
  1416. << "*** created as globals, because in C++ global object\\n"
  1417. << "*** destructors are run after thread_local destructors.\\n"
  1418. << "***\\n"
  1419. ;
  1420. }}
  1421. internal_assert( m_constructed);
  1422. if (!m_ctx)
  1423. {{
  1424. /* Make a context for this thread by cloning the global
  1425. context. */
  1426. /* fixme: we don't actually need to take a lock here. */
  1427. std::lock_guard<std::mutex> lock( s_state.m_mutex);
  1428. if (s_trace)
  1429. {{
  1430. std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): "
  1431. << " calling fz_clone_context()\\n";
  1432. }}
  1433. internal_assert(s_state_valid);
  1434. m_ctx = fz_clone_context(s_state.m_ctx);
  1435. if (s_trace)
  1436. {{
  1437. std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): "
  1438. << "fz_clone_context(" << s_state.m_ctx << ") => " << m_ctx << "\\n";
  1439. }}
  1440. internal_assert("m_ctx = fz_clone_context()" && m_ctx);
  1441. }}
  1442. return m_ctx;
  1443. }}
  1444. ~{rename.internal("thread_state")}()
  1445. {{
  1446. if (m_ctx)
  1447. {{
  1448. internal_assert( s_state.m_multithreaded);
  1449. if (s_trace)
  1450. {{
  1451. std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): "
  1452. << " calling fz_drop_context()\\n";
  1453. }}
  1454. fz_drop_context( m_ctx);
  1455. }}
  1456. /* These two statements are an attempt to get useful
  1457. diagnostics in cases of undefined behaviour caused by the
  1458. use of global wrapper class instances, whose destructors
  1459. will be called /after/ destruction of this thread-local
  1460. internal_thread_state instance. See check of m_constructed in
  1461. get_context().
  1462. This probably only works in non-optimised builds -
  1463. optimisation will simply elide both these statements. */
  1464. m_ctx = nullptr;
  1465. m_constructed = false;
  1466. }}
  1467. fz_context* m_ctx;
  1468. bool m_constructed;
  1469. }};
  1470. static thread_local {rename.internal("thread_state")} s_thread_state;
  1471. FZ_FUNCTION fz_context* {rename.internal("context_get")}()
  1472. {{
  1473. if (s_state.m_multithreaded)
  1474. {{
  1475. return s_thread_state.get_context();
  1476. }}
  1477. else
  1478. {{
  1479. /* This gives a small improvement in performance for
  1480. single-threaded use, e.g. from 552.4s to 548.1s. */
  1481. internal_assert(s_state_valid);
  1482. fz_context* ret = s_state.m_ctx;
  1483. internal_assert(ret);
  1484. return ret;
  1485. }}
  1486. }}
  1487. FZ_FUNCTION void reinit_singlethreaded()
  1488. {{
  1489. if (0)
  1490. {{
  1491. std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): Reinitialising as single-threaded.\\n";
  1492. }}
  1493. s_state.reinit( false /*multithreaded*/);
  1494. }}
  1495. ''')
  1496. out_cpp.write( cpp_text)
  1497. make_namespace_close( namespace, out_cpp)
  1498. # Generate code that exposes C++ operator new/delete to Memento.
  1499. #
  1500. # Disabled because our generated code makes very few direct calls
  1501. # to operator new, and Memento ends up catching lots of (presumably
  1502. # false-positive) leaks in the Python interpreter, so isn't very useful.
  1503. #
  1504. if 0:
  1505. out_cpp.write( textwrap.dedent(
  1506. '''
  1507. #ifdef MEMENTO
  1508. void* operator new( size_t size)
  1509. {
  1510. return Memento_cpp_new( size);
  1511. }
  1512. void operator delete( void* pointer)
  1513. {
  1514. Memento_cpp_delete( pointer);
  1515. }
  1516. void* operator new[]( size_t size)
  1517. {
  1518. return Memento_cpp_new_array( size);
  1519. }
  1520. void operator delete[]( void* pointer)
  1521. {
  1522. Memento_cpp_delete_array( pointer);
  1523. }
  1524. #endif
  1525. '''
  1526. ))
  1527. def make_function_wrappers(
  1528. tu,
  1529. namespace,
  1530. out_exceptions_h,
  1531. out_exceptions_cpp,
  1532. out_functions_h,
  1533. out_functions_cpp,
  1534. out_internal_h,
  1535. out_internal_cpp,
  1536. out_functions_h2,
  1537. out_functions_cpp2,
  1538. generated,
  1539. refcheck_if,
  1540. trace_if,
  1541. ):
  1542. '''
  1543. Generates C++ source code containing wrappers for all fz_*() functions.
  1544. We also create a function throw_exception(fz_context* ctx) that throws a
  1545. C++ exception appropriate for the error in ctx.
  1546. If a function has first arg fz_context*, extra code is generated that
  1547. converts fz_try..fz_catch exceptions into C++ exceptions by calling
  1548. throw_exception().
  1549. We remove any fz_context* argument and the implementation calls
  1550. internal_get_context() to get a suitable thread-specific fz_context* to
  1551. use.
  1552. We generate a class for each exception type.
  1553. Returned source is just the raw functions text, e.g. it does not contain
  1554. required #include's.
  1555. Args:
  1556. tu:
  1557. Clang translation unit.
  1558. out_exceptions_h:
  1559. Stream to which we write exception class definitions.
  1560. out_exceptions_cpp:
  1561. Stream to which we write exception class implementation.
  1562. out_functions_h:
  1563. Stream to which we write function declarations.
  1564. out_functions_cpp:
  1565. Stream to which we write function definitions.
  1566. generated:
  1567. A Generated instance.
  1568. '''
  1569. # Look for FZ_ERROR_* enums. We generate an exception class for each of
  1570. # these.
  1571. #
  1572. error_name_prefix = 'FZ_ERROR_'
  1573. fz_error_names = []
  1574. fz_error_names_maxlen = 0 # Used for padding so generated code aligns.
  1575. for cursor in parse.get_children(tu.cursor):
  1576. if cursor.kind == state.clang.cindex.CursorKind.ENUM_DECL:
  1577. #log( 'enum: {cursor.spelling=})
  1578. for child in parse.get_members( cursor):
  1579. #log( 'child:{ child.spelling=})
  1580. if child.spelling.startswith( error_name_prefix):
  1581. name = child.spelling[ len(error_name_prefix):]
  1582. fz_error_names.append( name)
  1583. if len( name) > fz_error_names_maxlen:
  1584. fz_error_names_maxlen = len( name)
  1585. def errors(include_error_base=False):
  1586. '''
  1587. Yields (enum, typename, padding) for each error.
  1588. E.g.:
  1589. enum=FZ_ERROR_SYSTEM
  1590. typename=mupdf_error_memory
  1591. padding=' '
  1592. '''
  1593. names = fz_error_names
  1594. if include_error_base:
  1595. names = ['BASE'] + names
  1596. for name in names:
  1597. enum = f'{error_name_prefix}{name}'
  1598. typename = rename.error_class( enum)
  1599. padding = (fz_error_names_maxlen - len(name)) * ' '
  1600. yield enum, typename, padding
  1601. # Declare base exception class and define its methods.
  1602. #
  1603. base_name = rename.error_class('FZ_ERROR_BASE')
  1604. out_exceptions_h.write( textwrap.dedent(
  1605. f'''
  1606. /** Base class for exceptions. */
  1607. struct {base_name} : std::exception
  1608. {{
  1609. int m_code;
  1610. std::string m_text;
  1611. mutable std::string m_what;
  1612. FZ_FUNCTION const char* what() const throw();
  1613. FZ_FUNCTION {base_name}(int code, const char* text);
  1614. }};
  1615. '''))
  1616. out_exceptions_cpp.write( textwrap.dedent(
  1617. f'''
  1618. FZ_FUNCTION {base_name}::{base_name}(int code, const char* text)
  1619. :
  1620. m_code(code),
  1621. m_text(text)
  1622. {{
  1623. {trace_if}
  1624. if (s_trace_exceptions)
  1625. {{
  1626. std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): {base_name}: " << m_text << "\\n";
  1627. }}
  1628. #endif
  1629. }};
  1630. FZ_FUNCTION const char* {base_name}::what() const throw()
  1631. {{
  1632. m_what = "code=" + std::to_string(m_code) + ": " + m_text;
  1633. return m_what.c_str();
  1634. }};
  1635. '''))
  1636. # Generate SWIG Python code to allow conversion of our error class
  1637. # exceptions into equivalent Python exceptions.
  1638. error_classes_n = 0
  1639. for enum, typename, padding in errors():
  1640. error_classes_n += 1
  1641. error_classes_n += 1 # Extra space for FzErrorBase.
  1642. generated.swig_python_exceptions.write( textwrap.dedent( f'''
  1643. void internal_set_error_classes(PyObject* classes);
  1644. %{{
  1645. /* A Python list of Error classes, [FzErrorNone, FzErrorMemory, FzErrorGeneric, ...]. */
  1646. static PyObject* s_error_classes[{error_classes_n}] = {{}};
  1647. /* Called on startup by mupdf.py, with a list of error classes
  1648. to be copied into s_error_classes. This will allow us to create
  1649. instances of these error classes in SWIG's `%exception ...`, so
  1650. Python code will see exceptions as instances of Python error
  1651. classes. */
  1652. void internal_set_error_classes(PyObject* classes)
  1653. {{
  1654. assert(PyList_Check(classes));
  1655. int n = PyList_Size(classes);
  1656. assert(n == {error_classes_n});
  1657. for (int i=0; i<n; ++i)
  1658. {{
  1659. PyObject* class_ = PyList_GetItem(classes, i);
  1660. s_error_classes[i] = class_;
  1661. }}
  1662. }}
  1663. /* Sets Python exception to a new mupdf.<name> object constructed
  1664. with `text`. */
  1665. void set_exception(PyObject* class_, int code, const std::string& text)
  1666. {{
  1667. PyObject* args = Py_BuildValue("(s)", text.c_str());
  1668. PyObject* instance = PyObject_CallObject(class_, args);
  1669. PyErr_SetObject(class_, instance);
  1670. Py_XDECREF(instance);
  1671. Py_XDECREF(args);
  1672. }}
  1673. /* Exception handler for swig-generated code. Uses internal
  1674. `throw;` to recover the current C++ exception then uses
  1675. `set_exception()` to set the current Python exception. Caller
  1676. should do `SWIG_fail;` after we return. */
  1677. void handle_exception()
  1678. {{
  1679. try
  1680. {{
  1681. throw;
  1682. }}
  1683. '''
  1684. ))
  1685. # Declare exception class for each FZ_ERROR_*. Also append catch blocks for
  1686. # each of these exception classes to `handle_exception()`.
  1687. #
  1688. for i, (enum, typename, padding) in enumerate(errors()):
  1689. out_exceptions_h.write( textwrap.dedent(
  1690. f'''
  1691. /** For `{enum}`. */
  1692. struct {typename} : {base_name}
  1693. {{
  1694. FZ_FUNCTION {typename}(const char* message);
  1695. }};
  1696. '''))
  1697. generated.swig_python_exceptions.write( textwrap.dedent( f'''
  1698. /**/
  1699. catch (mupdf::{typename}& e)
  1700. {{
  1701. if (g_mupdf_trace_exceptions)
  1702. {{
  1703. std::cerr
  1704. << __FILE__ << ':' << __LINE__ << ':'
  1705. #ifndef _WIN32
  1706. << __PRETTY_FUNCTION__ << ':'
  1707. #endif
  1708. << " Converting C++ std::exception mupdf::{typename} ({i=}) into Python exception:\\n"
  1709. << " e.m_code: " << e.m_code << "\\n"
  1710. << " e.m_text: " << e.m_text << "\\n"
  1711. << " e.what(): " << e.what() << "\\n"
  1712. << " typeid(e).name(): " << typeid(e).name() << "\\n"
  1713. << "\\n";
  1714. }}
  1715. set_exception(s_error_classes[{i}], e.m_code, e.m_text);
  1716. }}'''))
  1717. # Append less specific exception handling.
  1718. generated.swig_python_exceptions.write( textwrap.dedent( f'''
  1719. catch (mupdf::FzErrorBase& e)
  1720. {{
  1721. if (g_mupdf_trace_exceptions)
  1722. {{
  1723. std::cerr
  1724. << __FILE__ << ':' << __LINE__ << ':'
  1725. #ifndef _WIN32
  1726. << __PRETTY_FUNCTION__ << ':'
  1727. #endif
  1728. << " Converting C++ std::exception mupdf::FzErrorBase ({error_classes_n-1=}) into Python exception:\\n"
  1729. << " e.m_code: " << e.m_code << "\\n"
  1730. << " e.m_text: " << e.m_text << "\\n"
  1731. << " e.what(): " << e.what() << "\\n"
  1732. << " typeid(e).name(): " << typeid(e).name() << "\\n"
  1733. << "\\n";
  1734. }}
  1735. PyObject* class_ = s_error_classes[{error_classes_n-1}];
  1736. PyObject* args = Py_BuildValue("is", e.m_code, e.m_text.c_str());
  1737. PyObject* instance = PyObject_CallObject(class_, args);
  1738. PyErr_SetObject(class_, instance);
  1739. Py_XDECREF(instance);
  1740. Py_XDECREF(args);
  1741. }}
  1742. catch (std::exception& e)
  1743. {{
  1744. if (g_mupdf_trace_exceptions)
  1745. {{
  1746. std::cerr
  1747. << __FILE__ << ':' << __LINE__ << ':'
  1748. #ifndef _WIN32
  1749. << __PRETTY_FUNCTION__ << ':'
  1750. #endif
  1751. << " Converting C++ std::exception into Python exception: "
  1752. << e.what()
  1753. << " typeid(e).name(): " << typeid(e).name() << "\\n"
  1754. << "\\n";
  1755. }}
  1756. SWIG_Error(SWIG_RuntimeError, e.what());
  1757. }}
  1758. catch (...)
  1759. {{
  1760. if (g_mupdf_trace_exceptions)
  1761. {{
  1762. std::cerr
  1763. << __FILE__ << ':' << __LINE__ << ':'
  1764. #ifndef _WIN32
  1765. << __PRETTY_FUNCTION__ << ':'
  1766. #endif
  1767. << " Converting unknown C++ exception into Python exception."
  1768. << "\\n";
  1769. }}
  1770. SWIG_Error(SWIG_RuntimeError, "Unknown exception");
  1771. }}
  1772. }}
  1773. %}}
  1774. %exception
  1775. {{
  1776. try
  1777. {{
  1778. $action
  1779. }}
  1780. catch (...)
  1781. {{
  1782. handle_exception();
  1783. SWIG_fail;
  1784. }}
  1785. }}
  1786. '''))
  1787. generated.swig_python_set_error_classes.write( f'# Define __str()__ for each error/exception class, to use self.what().\n')
  1788. for enum, typename, padding in errors(include_error_base=1):
  1789. generated.swig_python_set_error_classes.write( f'{typename}.__str__ = lambda self: self.what()\n')
  1790. generated.swig_python_set_error_classes.write( textwrap.dedent( f'''
  1791. # This must be after the declaration of mupdf::FzError*
  1792. # classes in mupdf/exceptions.h and declaration of
  1793. # `internal_set_error_classes()`, otherwise generated code is
  1794. # before the declaration of the Python class or similar. */
  1795. internal_set_error_classes([
  1796. '''))
  1797. for enum, typename, padding in errors():
  1798. generated.swig_python_set_error_classes.write(f' {typename},\n')
  1799. generated.swig_python_set_error_classes.write( textwrap.dedent( f'''
  1800. FzErrorBase,
  1801. ])
  1802. '''))
  1803. # Define constructor for each exception class.
  1804. #
  1805. for enum, typename, padding in errors():
  1806. out_exceptions_cpp.write( textwrap.dedent(
  1807. f'''
  1808. FZ_FUNCTION {typename}::{typename}(const char* text)
  1809. : {base_name}({enum}, text)
  1810. {{
  1811. {trace_if}
  1812. if (s_trace_exceptions)
  1813. {{
  1814. std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): {typename} constructor, text: " << m_text << "\\n";
  1815. }}
  1816. #endif
  1817. }}
  1818. '''))
  1819. # Generate function that throws an appropriate exception from a fz_context.
  1820. #
  1821. throw_exception = rename.internal( 'throw_exception')
  1822. out_exceptions_h.write( textwrap.dedent(
  1823. f'''
  1824. /** Throw exception appropriate for error in `ctx`. */
  1825. FZ_FUNCTION void {throw_exception}(fz_context* ctx);
  1826. '''))
  1827. out_exceptions_cpp.write( textwrap.dedent(
  1828. f'''
  1829. FZ_FUNCTION void {throw_exception}(fz_context* ctx)
  1830. {{
  1831. int code;
  1832. const char* text = fz_convert_error(ctx, &code);
  1833. {trace_if}
  1834. if (s_trace_exceptions)
  1835. {{
  1836. std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): code=" << code << "\\n";
  1837. std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): text=" << text << "\\n";
  1838. }}
  1839. #endif
  1840. '''))
  1841. for enum, typename, padding in errors():
  1842. out_exceptions_cpp.write( f' if (code == {enum}) {padding}throw {typename}{padding}(text);\n')
  1843. out_exceptions_cpp.write( f' throw {base_name}(code, text);\n')
  1844. out_exceptions_cpp.write( f'}}\n')
  1845. out_exceptions_cpp.write( '\n')
  1846. make_internal_functions( namespace, out_internal_h, out_internal_cpp, refcheck_if, trace_if)
  1847. # Generate wrappers for each function that we find.
  1848. #
  1849. functions = []
  1850. for fnname, cursor in state.state_.find_functions_starting_with( tu, ('fz_', 'pdf_'), method=False):
  1851. assert fnname not in state.omit_fns
  1852. #jlib.log( '{fnname=} {cursor.spelling=} {cursor.type.spelling=}')
  1853. if ( cursor.type == state.clang.cindex.TypeKind.FUNCTIONPROTO
  1854. and cursor.type.is_function_variadic()
  1855. ):
  1856. # We don't attempt to wrap variadic functions - would need to find
  1857. # the equivalent function that takes a va_list.
  1858. if 0:
  1859. jlib.log( 'Variadic fn: {cursor.type.spelling=}')
  1860. if fnname != 'fz_warn':
  1861. continue
  1862. if fnname == 'fz_push_try':
  1863. # This is partof implementation of fz_try/catch so doesn't make
  1864. # sense to provide a wrapper. Also it is OS-dependent so including
  1865. # it makes our generated code OS-specific.
  1866. continue
  1867. functions.append( (fnname, cursor))
  1868. jlib.log1( '{len(functions)=}')
  1869. # Sort by function-name to make output easier to read.
  1870. functions.sort()
  1871. for fnname, cursor in functions:
  1872. if state.state_.show_details( fnname):
  1873. jlib.log( 'Looking at {fnname}')
  1874. fnname_wrapper = rename.ll_fn( fnname)
  1875. function_wrapper(
  1876. tu,
  1877. cursor,
  1878. fnname,
  1879. fnname_wrapper,
  1880. out_functions_h,
  1881. out_functions_cpp,
  1882. generated,
  1883. refcheck_if,
  1884. trace_if,
  1885. )
  1886. if not fnname.startswith( ( 'fz_keep_', 'fz_drop_', 'pdf_keep_', 'pdf_drop_')):
  1887. function_wrapper_class_aware(
  1888. tu,
  1889. register_fn_use=None,
  1890. struct_name=None,
  1891. class_name=None,
  1892. fn_cursor=cursor,
  1893. refcheck_if=refcheck_if,
  1894. trace_if=trace_if,
  1895. fnname=fnname,
  1896. out_h=out_functions_h2,
  1897. out_cpp=out_functions_cpp2,
  1898. generated=generated,
  1899. )
  1900. python.cppyy_add_outparams_wrapper( tu, fnname, cursor, state.state_, generated)
  1901. if fnname == "pdf_load_field_name": #(fz_context *ctx, pdf_obj *field);
  1902. # Output wrapper that returns std::string instead of buffer that
  1903. # caller needs to free.
  1904. out_functions_h.write(
  1905. textwrap.dedent(
  1906. f'''
  1907. /** Alternative to `{rename.ll_fn('pdf_load_field_name')}()` that returns a std::string. */
  1908. FZ_FUNCTION std::string {rename.ll_fn('pdf_load_field_name2')}(pdf_obj* field);
  1909. '''))
  1910. out_functions_cpp.write(
  1911. textwrap.dedent(
  1912. f'''
  1913. FZ_FUNCTION std::string {rename.ll_fn('pdf_load_field_name2')}(pdf_obj* field)
  1914. {{
  1915. char* buffer = {rename.ll_fn('pdf_load_field_name')}( field);
  1916. std::string ret( buffer);
  1917. {rename.ll_fn('fz_free')}( buffer);
  1918. return ret;
  1919. }}
  1920. '''))
  1921. out_functions_h2.write(
  1922. textwrap.indent(
  1923. textwrap.dedent(
  1924. f'''
  1925. /** Alternative to `{rename.fn('pdf_load_field_name')}()` that returns a std::string. */
  1926. FZ_FUNCTION std::string {rename.fn('pdf_load_field_name2')}({rename.class_('pdf_obj')}& field);
  1927. '''),
  1928. ' ',
  1929. )
  1930. )
  1931. out_functions_cpp2.write(
  1932. textwrap.dedent(
  1933. f'''
  1934. FZ_FUNCTION std::string {rename.fn('pdf_load_field_name2')}({rename.class_('pdf_obj')}& field)
  1935. {{
  1936. return {rename.ll_fn('pdf_load_field_name2')}( field.m_internal);
  1937. }}
  1938. '''))
  1939. # Output custom wrappers for variadic pdf_dict_getl().
  1940. #
  1941. decl = f'''FZ_FUNCTION pdf_obj* {rename.ll_fn('pdf_dict_getlv')}( pdf_obj* dict, va_list keys)'''
  1942. out_functions_h.write( textwrap.dedent( f'''
  1943. /* Low-level wrapper for `pdf_dict_getl()`. `keys` must be null-terminated list of `pdf_obj*`'s. */
  1944. {decl};
  1945. '''))
  1946. out_functions_cpp.write( textwrap.dedent( f'''
  1947. {decl}
  1948. {{
  1949. pdf_obj *key;
  1950. while (dict != NULL && (key = va_arg(keys, pdf_obj *)) != NULL)
  1951. {{
  1952. dict = {rename.ll_fn('pdf_dict_get')}( dict, key);
  1953. }}
  1954. return dict;
  1955. }}
  1956. '''))
  1957. decl = f'''FZ_FUNCTION pdf_obj* {rename.ll_fn('pdf_dict_getl')}( pdf_obj* dict, ...)'''
  1958. out_functions_h.write( textwrap.dedent( f'''
  1959. /* Low-level wrapper for `pdf_dict_getl()`. `...` must be null-terminated list of `pdf_obj*`'s. */
  1960. {decl};
  1961. '''))
  1962. out_functions_cpp.write( textwrap.dedent( f'''
  1963. {decl}
  1964. {{
  1965. va_list keys;
  1966. va_start(keys, dict);
  1967. try
  1968. {{
  1969. dict = {rename.ll_fn('pdf_dict_getlv')}( dict, keys);
  1970. }}
  1971. catch( std::exception&)
  1972. {{
  1973. va_end(keys);
  1974. throw;
  1975. }}
  1976. va_end(keys);
  1977. return dict;
  1978. }}
  1979. '''))
  1980. decl = f'''FZ_FUNCTION {rename.class_('pdf_obj')} {rename.fn('pdf_dict_getlv')}( {rename.class_('pdf_obj')}& dict, va_list keys)'''
  1981. out_functions_h2.write(
  1982. textwrap.indent(
  1983. textwrap.dedent( f'''
  1984. /* Class-aware wrapper for `pdf_dict_getl()`. `keys` must be null-terminated list of
  1985. `pdf_obj*`'s, not `{rename.class_('pdf_obj')}*`'s, so that conventional
  1986. use with `PDF_NAME()` works. */
  1987. {decl};
  1988. '''),
  1989. ' ',
  1990. )
  1991. )
  1992. out_functions_cpp2.write( textwrap.dedent( f'''
  1993. {decl}
  1994. {{
  1995. pdf_obj* ret = {rename.ll_fn('pdf_dict_getlv')}( dict.m_internal, keys);
  1996. return {rename.class_('pdf_obj')}( {rename.ll_fn('pdf_keep_obj')}( ret));
  1997. }}
  1998. '''))
  1999. decl = f'''FZ_FUNCTION {rename.class_('pdf_obj')} {rename.fn('pdf_dict_getl')}( {rename.class_('pdf_obj')}* dict, ...)'''
  2000. out_functions_h2.write(
  2001. textwrap.indent(
  2002. textwrap.dedent( f'''
  2003. /* Class-aware wrapper for `pdf_dict_getl()`. `...` must be null-terminated list of
  2004. `pdf_obj*`'s, not `{rename.class_('pdf_obj')}*`'s, so that conventional
  2005. use with `PDF_NAME()` works. [We use pointer `dict` arg because variadic
  2006. args do not with with reference args.] */
  2007. {decl};
  2008. '''),
  2009. ' ',
  2010. ),
  2011. )
  2012. out_functions_cpp2.write( textwrap.dedent( f'''
  2013. {decl}
  2014. {{
  2015. va_list keys;
  2016. va_start(keys, dict);
  2017. try
  2018. {{
  2019. {rename.class_('pdf_obj')} ret = {rename.fn('pdf_dict_getlv')}( *dict, keys);
  2020. va_end( keys);
  2021. return ret;
  2022. }}
  2023. catch (std::exception&)
  2024. {{
  2025. va_end( keys);
  2026. throw;
  2027. }}
  2028. }}
  2029. '''))
  2030. def class_add_iterator( tu, struct_cursor, struct_name, classname, extras, refcheck_if, trace_if):
  2031. '''
  2032. Add begin() and end() methods so that this generated class is iterable
  2033. from C++ with:
  2034. for (auto i: foo) {...}
  2035. We modify <extras> to create an iterator class and add begin() and end()
  2036. methods that each return an instance of the iterator class.
  2037. '''
  2038. it_begin, it_end = extras.iterator_next
  2039. # Figure out type of what the iterator returns by looking at type of
  2040. # <it_begin>.
  2041. if it_begin:
  2042. c = parse.find_name( struct_cursor, it_begin)
  2043. assert c.type.kind == state.clang.cindex.TypeKind.POINTER
  2044. it_internal_type = state.get_name_canonical( c.type.get_pointee()).spelling
  2045. it_internal_type = util.clip( it_internal_type, 'struct ')
  2046. it_type = rename.class_( it_internal_type)
  2047. else:
  2048. # The container is also the first item in the linked list.
  2049. it_internal_type = struct_name
  2050. it_type = classname
  2051. # We add to extras.methods_extra().
  2052. #
  2053. check_refs = 1 if parse.has_refs( tu, struct_cursor.type) else 0
  2054. extras.methods_extra.append(
  2055. classes.ExtraMethod( f'{classname}Iterator', 'begin()',
  2056. f'''
  2057. {{
  2058. auto ret = {classname}Iterator({'m_internal->'+it_begin if it_begin else '*this'});
  2059. {refcheck_if}
  2060. #if {check_refs}
  2061. if (s_check_refs)
  2062. {{
  2063. s_{classname}_refs_check.check( this, __FILE__, __LINE__, __FUNCTION__);
  2064. }}
  2065. #endif
  2066. #endif
  2067. return ret;
  2068. }}
  2069. ''',
  2070. f'/* Used for iteration over linked list of {it_type} items starting at {it_internal_type}::{it_begin}. */',
  2071. ),
  2072. )
  2073. extras.methods_extra.append(
  2074. classes.ExtraMethod( f'{classname}Iterator', 'end()',
  2075. f'''
  2076. {{
  2077. auto ret = {classname}Iterator({it_type}());
  2078. {refcheck_if}
  2079. #if {check_refs}
  2080. if (s_check_refs)
  2081. {{
  2082. s_{classname}_refs_check.check( this, __FILE__, __LINE__, __FUNCTION__);
  2083. }}
  2084. #endif
  2085. #endif
  2086. return ret;
  2087. }}
  2088. ''',
  2089. f'/* Used for iteration over linked list of {it_type} items starting at {it_internal_type}::{it_begin}. */',
  2090. ),
  2091. )
  2092. extras.class_bottom += f'\n typedef {classname}Iterator iterator;\n'
  2093. extras.class_pre += f'\nstruct {classname}Iterator;\n'
  2094. extras.class_post += f'''
  2095. struct {classname}Iterator
  2096. {{
  2097. FZ_FUNCTION {classname}Iterator(const {it_type}& item);
  2098. FZ_FUNCTION {classname}Iterator& operator++();
  2099. FZ_FUNCTION bool operator==( const {classname}Iterator& rhs);
  2100. FZ_FUNCTION bool operator!=( const {classname}Iterator& rhs);
  2101. FZ_FUNCTION {it_type} operator*();
  2102. FZ_FUNCTION {it_type}* operator->();
  2103. private:
  2104. {it_type} m_item;
  2105. }};
  2106. '''
  2107. keep_text = ''
  2108. if extras.copyable and extras.copyable != 'default':
  2109. # Our operator++ needs to create it_type from m_item.m_internal->next,
  2110. # so we need to call fz_keep_<it_type>().
  2111. #
  2112. # [Perhaps life would be simpler if our generated constructors always
  2113. # called fz_keep_*() as necessary? In some circumstances this would
  2114. # require us to call fz_drop_*() when constructing an instance, but
  2115. # that might be simpler?]
  2116. #
  2117. base_name = util.clip( struct_name, ('fz_', 'pdf_'))
  2118. if struct_name.startswith( 'fz_'):
  2119. keep_name = f'fz_keep_{base_name}'
  2120. elif struct_name.startswith( 'pdf_'):
  2121. keep_name = f'pdf_keep_{base_name}'
  2122. keep_name = rename.ll_fn(keep_name)
  2123. keep_text = f'{keep_name}(m_item.m_internal->next);'
  2124. extras.extra_cpp += f'''
  2125. FZ_FUNCTION {classname}Iterator::{classname}Iterator(const {it_type}& item)
  2126. : m_item( item)
  2127. {{
  2128. }}
  2129. FZ_FUNCTION {classname}Iterator& {classname}Iterator::operator++()
  2130. {{
  2131. {keep_text}
  2132. m_item = {it_type}(m_item.m_internal->next);
  2133. return *this;
  2134. }}
  2135. FZ_FUNCTION bool {classname}Iterator::operator==( const {classname}Iterator& rhs)
  2136. {{
  2137. return m_item.m_internal == rhs.m_item.m_internal;
  2138. }}
  2139. FZ_FUNCTION bool {classname}Iterator::operator!=( const {classname}Iterator& rhs)
  2140. {{
  2141. return m_item.m_internal != rhs.m_item.m_internal;
  2142. }}
  2143. FZ_FUNCTION {it_type} {classname}Iterator::operator*()
  2144. {{
  2145. return m_item;
  2146. }}
  2147. FZ_FUNCTION {it_type}* {classname}Iterator::operator->()
  2148. {{
  2149. return &m_item;
  2150. }}
  2151. '''
  2152. def class_find_constructor_fns( tu, classname, struct_name, base_name, extras):
  2153. '''
  2154. Returns list of functions that could be used as constructors of the
  2155. specified wrapper class.
  2156. For example we look for functions that return a pointer to <struct_name> or
  2157. return a POD <struct_name> by value.
  2158. tu:
  2159. .
  2160. classname:
  2161. Name of our wrapper class.
  2162. struct_name:
  2163. Name of underlying mupdf struct.
  2164. base_name:
  2165. Name of struct without 'fz_' prefix.
  2166. extras:
  2167. .
  2168. '''
  2169. assert struct_name == f'fz_{base_name}' or struct_name == f'pdf_{base_name}'
  2170. verbose = state.state_.show_details( struct_name)
  2171. constructor_fns = []
  2172. if '-' not in extras.constructor_prefixes:
  2173. # Add default constructor fn prefix.
  2174. if struct_name.startswith( 'fz_'):
  2175. extras.constructor_prefixes.insert( 0, f'fz_new_')
  2176. extras.constructor_prefixes.insert( 0, f'pdf_new_')
  2177. elif struct_name.startswith( 'pdf_'):
  2178. extras.constructor_prefixes.insert( 0, f'pdf_new_')
  2179. for fnprefix in extras.constructor_prefixes:
  2180. if verbose:
  2181. jlib.log('{struct_name=} {fnprefix=}')
  2182. for fnname, cursor in state.state_.find_functions_starting_with( tu, fnprefix, method=True):
  2183. # Check whether this has identical signature to any fn we've
  2184. # already found.
  2185. if verbose:
  2186. jlib.log( '{struct_name=} {fnname=}')
  2187. duplicate_type = None
  2188. duplicate_name = False
  2189. for f, c, is_duplicate in constructor_fns:
  2190. if verbose:
  2191. jlib.log( '{struct_name=} {cursor.spelling=} {c.type.spelling=}')
  2192. if f == fnname:
  2193. if verbose:
  2194. jlib.log('setting duplicate_name to true')
  2195. duplicate_name = True
  2196. break
  2197. if c.type == cursor.type:
  2198. if verbose:
  2199. jlib.log( '{struct_name} wrapper: ignoring candidate constructor {fnname}() because prototype is indistinguishable from {f=}()')
  2200. duplicate_type = f
  2201. break
  2202. if duplicate_name:
  2203. continue
  2204. ok = False
  2205. arg, n = parse.get_first_arg( tu, cursor)
  2206. if arg and n == 1 and parse.is_pointer_to( arg.cursor.type, struct_name):
  2207. # This avoids generation of bogus copy constructor wrapping
  2208. # function fz_new_pixmap_from_alpha_channel() introduced
  2209. # 2021-05-07.
  2210. #
  2211. if verbose:
  2212. jlib.log('ignoring possible constructor because looks like copy constructor: {fnname}')
  2213. elif fnname in extras.constructor_excludes:
  2214. if verbose:
  2215. jlib.log('{fnname=} is in {extras.constructor_excludes=}')
  2216. elif extras.pod and extras.pod != 'none' and state.get_name_canonical( cursor.result_type).spelling == f'{struct_name}':
  2217. # Returns POD struct by value.
  2218. ok = True
  2219. elif not extras.pod and parse.is_pointer_to( cursor.result_type, f'{struct_name}'):
  2220. # Returns pointer to struct.
  2221. ok = True
  2222. if ok:
  2223. if duplicate_type and extras.copyable:
  2224. if verbose:
  2225. jlib.log1( 'adding static method wrapper for {fnname}')
  2226. extras.method_wrappers_static.append( fnname)
  2227. else:
  2228. if duplicate_type:
  2229. if verbose:
  2230. jlib.log( 'not able to provide static factory fn {struct_name}::{fnname} because wrapper class is not copyable.')
  2231. if verbose:
  2232. jlib.log( 'adding constructor wrapper for {fnname}')
  2233. constructor_fns.append( (fnname, cursor, duplicate_type))
  2234. else:
  2235. if verbose:
  2236. jlib.log( 'ignoring possible constructor for {classname=} because does not return required type: {fnname=} -> {cursor.result_type.spelling=}')
  2237. constructor_fns.sort()
  2238. return constructor_fns
  2239. def class_find_destructor_fns( tu, struct_name, base_name):
  2240. '''
  2241. Returns list of functions that could be used by destructor - must be called
  2242. 'fz_drop_<typename>', must take a <struct>* arg, may take a fz_context*
  2243. arg.
  2244. '''
  2245. if struct_name.startswith( 'fz_'):
  2246. destructor_prefix = f'fz_drop_{base_name}'
  2247. elif struct_name.startswith( 'pdf_'):
  2248. destructor_prefix = f'pdf_drop_{base_name}'
  2249. destructor_fns = []
  2250. for fnname, cursor in state.state_.find_functions_starting_with( tu, destructor_prefix, method=True):
  2251. arg_struct = False
  2252. arg_context = False
  2253. args_num = 0
  2254. for arg in parse.get_args( tu, cursor):
  2255. if not arg_struct and parse.is_pointer_to( arg.cursor.type, struct_name):
  2256. arg_struct = True
  2257. elif not arg_context and parse.is_pointer_to( arg.cursor.type, 'fz_context'):
  2258. arg_context = True
  2259. args_num += 1
  2260. if arg_struct:
  2261. if args_num == 1 or (args_num == 2 and arg_context):
  2262. # No params other than <struct>* and fz_context* so this is
  2263. # candidate destructor.
  2264. #log( 'adding candidate destructor: {fnname}')
  2265. destructor_fns.append( (fnname, cursor))
  2266. destructor_fns.sort()
  2267. return destructor_fns
  2268. def num_instances(refcheck_if, delta, name):
  2269. '''
  2270. Returns C++ code to embed in a wrapper class constructor/destructor function
  2271. to update the class static `s_num_instances` variable.
  2272. '''
  2273. ret = ''
  2274. ret += f' {refcheck_if}\n'
  2275. if delta == +1:
  2276. ret += ' ++s_num_instances;\n'
  2277. elif delta == -1:
  2278. ret += ' --s_num_instances;\n'
  2279. else:
  2280. assert 0
  2281. ret += ' #endif\n'
  2282. return ret
  2283. def class_constructor_default(
  2284. tu,
  2285. struct_cursor,
  2286. classname,
  2287. extras,
  2288. out_h,
  2289. out_cpp,
  2290. refcheck_if,
  2291. trace_if,
  2292. ):
  2293. '''
  2294. Generates constructor that sets each member to default value.
  2295. '''
  2296. if extras.pod:
  2297. comment = f'Default constructor, sets each member to default value.'
  2298. else:
  2299. comment = f'Default constructor, sets `m_internal` to null.'
  2300. out_h.write( '\n')
  2301. out_h.write( f' /** {comment} */\n')
  2302. out_h.write( f' FZ_FUNCTION {classname}();\n')
  2303. out_cpp.write( f'/** {comment} */\n')
  2304. out_cpp.write( f'FZ_FUNCTION {classname}::{classname}()\n')
  2305. if not extras.pod:
  2306. out_cpp.write( f': m_internal(nullptr)\n')
  2307. out_cpp.write( f'{{\n')
  2308. if extras.pod == 'none':
  2309. pass
  2310. elif extras.pod:
  2311. for c in parse.get_members(struct_cursor):
  2312. if extras.pod == 'inline':
  2313. c_name = f'this->{c.spelling}'
  2314. else:
  2315. c_name = f'this->m_internal.{c.spelling}'
  2316. if c.type.kind == state.clang.cindex.TypeKind.CONSTANTARRAY:
  2317. out_cpp.write( f' memset(&{c_name}, 0, sizeof({c_name}));\n')
  2318. else:
  2319. out_cpp.write( f' {c_name} = {{}};\n')
  2320. else:
  2321. if parse.has_refs( tu, struct_cursor.type):
  2322. out_cpp.write(f' {refcheck_if}\n')
  2323. out_cpp.write( ' if (s_check_refs)\n')
  2324. out_cpp.write( ' {\n')
  2325. out_cpp.write(f' s_{classname}_refs_check.add( this, __FILE__, __LINE__, __FUNCTION__);\n')
  2326. out_cpp.write( ' }\n')
  2327. out_cpp.write( ' #endif\n')
  2328. out_cpp.write(num_instances(refcheck_if, +1, classname))
  2329. out_cpp.write( f'}};\n')
  2330. def class_copy_constructor(
  2331. tu,
  2332. functions,
  2333. struct_name,
  2334. struct_cursor,
  2335. base_name,
  2336. classname,
  2337. constructor_fns,
  2338. out_h,
  2339. out_cpp,
  2340. refcheck_if,
  2341. trace_if,
  2342. ):
  2343. '''
  2344. Generate a copy constructor and operator= by finding a suitable fz_keep_*()
  2345. function.
  2346. We raise an exception if we can't find one.
  2347. '''
  2348. if struct_name.startswith( 'fz_'):
  2349. keep_name = f'fz_keep_{base_name}'
  2350. drop_name = f'fz_drop_{base_name}'
  2351. elif struct_name.startswith( 'pdf_'):
  2352. keep_name = f'pdf_keep_{base_name}'
  2353. drop_name = f'pdf_drop_{base_name}'
  2354. for name in keep_name, drop_name:
  2355. cursor = state.state_.find_function( tu, name, method=True)
  2356. if not cursor:
  2357. classextra = classes.classextras.get( tu, struct_name)
  2358. if classextra.copyable:
  2359. if 1 or state.state_.show_details( struct_name):
  2360. jlib.log( 'changing to non-copyable because no function {name}(): {struct_name}')
  2361. classextra.copyable = False
  2362. return
  2363. if name == keep_name:
  2364. pvoid = parse.is_pointer_to( cursor.result_type, 'void')
  2365. assert ( pvoid
  2366. or parse.is_pointer_to( cursor.result_type, struct_name)
  2367. ), (
  2368. f'Function {name}(): result_type not void* or pointer to {struct_name}: {cursor.result_type.spelling}'
  2369. )
  2370. arg, n = parse.get_first_arg( tu, cursor)
  2371. assert n == 1, f'should take exactly one arg: {cursor.spelling}()'
  2372. assert parse.is_pointer_to( arg.cursor.type, struct_name), (
  2373. f'arg0 is not pointer to {struct_name}: {cursor.spelling}(): {arg.cursor.spelling} {arg.name}')
  2374. for fnname, cursor, duplicate_type in constructor_fns:
  2375. fnname2 = rename.ll_fn(fnname)
  2376. if fnname2 == keep_name:
  2377. jlib.log( 'not generating copy constructor with {keep_name=} because already used by a constructor.')
  2378. break
  2379. else:
  2380. functions( keep_name)
  2381. comment = f'Copy constructor using `{keep_name}()`.'
  2382. out_h.write( '\n')
  2383. out_h.write( f' /** {comment} */\n')
  2384. out_h.write( f' FZ_FUNCTION {classname}(const {classname}& rhs);\n')
  2385. out_h.write( '\n')
  2386. cast = ''
  2387. if pvoid:
  2388. # Need to cast the void* to the correct type.
  2389. cast = f'(::{struct_name}*) '
  2390. out_cpp.write( f'/** {comment} */\n')
  2391. out_cpp.write( f'FZ_FUNCTION {classname}::{classname}(const {classname}& rhs)\n')
  2392. out_cpp.write( f': m_internal({cast}{rename.ll_fn(keep_name)}(rhs.m_internal))\n')
  2393. out_cpp.write( '{\n')
  2394. # Write trace code.
  2395. out_cpp.write( f' {trace_if}\n')
  2396. out_cpp.write( f' if (s_trace_keepdrop) {{\n')
  2397. out_cpp.write( f' std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():"\n')
  2398. out_cpp.write( f' << " have called {rename.ll_fn(keep_name)}(rhs.m_internal)\\n"\n')
  2399. out_cpp.write( f' ;\n')
  2400. out_cpp.write( f' }}\n')
  2401. out_cpp.write( f' #endif\n')
  2402. if parse.has_refs( tu, struct_cursor.type):
  2403. out_cpp.write(f' {refcheck_if}\n')
  2404. out_cpp.write( ' if (s_check_refs)\n')
  2405. out_cpp.write( ' {\n')
  2406. out_cpp.write(f' s_{classname}_refs_check.add( this, __FILE__, __LINE__, __FUNCTION__);\n')
  2407. out_cpp.write( ' }\n')
  2408. out_cpp.write( ' #endif\n')
  2409. out_cpp.write(num_instances(refcheck_if, +1, classname))
  2410. out_cpp.write( '}\n')
  2411. out_cpp.write( '\n')
  2412. # Make operator=().
  2413. #
  2414. comment = f'operator= using `{keep_name}()` and `{drop_name}()`.'
  2415. out_h.write( f' /** {comment} */\n')
  2416. out_h.write( f' FZ_FUNCTION {classname}& operator=(const {classname}& rhs);\n')
  2417. out_cpp.write( f'/* {comment} */\n')
  2418. out_cpp.write( f'FZ_FUNCTION {classname}& {classname}::operator=(const {classname}& rhs)\n')
  2419. out_cpp.write( '{\n')
  2420. out_cpp.write( f' {trace_if}\n')
  2421. out_cpp.write( f' if (s_trace_keepdrop) {{\n')
  2422. out_cpp.write( f' std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():"\n')
  2423. out_cpp.write( f' << " calling {rename.ll_fn(drop_name)}(this->m_internal)"\n')
  2424. out_cpp.write( f' << " and {rename.ll_fn(keep_name)}(rhs.m_internal)\\n"\n')
  2425. out_cpp.write( f' ;\n')
  2426. out_cpp.write( f' }}\n')
  2427. out_cpp.write( f' #endif\n')
  2428. out_cpp.write( f' {rename.ll_fn(drop_name)}(this->m_internal);\n')
  2429. out_cpp.write( f' {rename.ll_fn(keep_name)}(rhs.m_internal);\n')
  2430. if parse.has_refs( tu, struct_cursor.type):
  2431. out_cpp.write(f' {refcheck_if}\n')
  2432. out_cpp.write( ' if (s_check_refs)\n')
  2433. out_cpp.write( ' {\n')
  2434. out_cpp.write(f' s_{classname}_refs_check.remove( this, __FILE__, __LINE__, __FUNCTION__);\n')
  2435. out_cpp.write( ' }\n')
  2436. out_cpp.write( ' #endif\n')
  2437. out_cpp.write( f' this->m_internal = {cast}rhs.m_internal;\n')
  2438. if parse.has_refs( tu, struct_cursor.type):
  2439. out_cpp.write(f' {refcheck_if}\n')
  2440. out_cpp.write( ' if (s_check_refs)\n')
  2441. out_cpp.write( ' {\n')
  2442. out_cpp.write(f' s_{classname}_refs_check.add( this, __FILE__, __LINE__, __FUNCTION__);\n')
  2443. out_cpp.write( ' }\n')
  2444. out_cpp.write( ' #endif\n')
  2445. out_cpp.write( f' return *this;\n')
  2446. out_cpp.write( '}\n')
  2447. out_cpp.write( '\n')
  2448. def function_name_implies_kept_references( fnname):
  2449. '''
  2450. Returns true if <fnname> implies the function would return kept
  2451. reference(s).
  2452. '''
  2453. if fnname in (
  2454. 'pdf_page_write',
  2455. 'fz_decomp_image_from_stream',
  2456. 'fz_get_pixmap_from_image',
  2457. ):
  2458. return True
  2459. for i in (
  2460. 'add',
  2461. 'convert',
  2462. 'copy',
  2463. 'create',
  2464. 'deep_copy',
  2465. 'find',
  2466. 'graft',
  2467. 'keep',
  2468. 'load',
  2469. 'new',
  2470. 'open',
  2471. 'parse',
  2472. 'read',
  2473. ):
  2474. if fnname.startswith(f'fz_{i}_') or fnname.startswith(f'pdf_{i}_'):
  2475. if state.state_.show_details(fnname):
  2476. jlib.log('Assuming that {fnname=} returns a kept reference.')
  2477. return True
  2478. return False
  2479. def function_wrapper_class_aware_body(
  2480. tu,
  2481. fnname,
  2482. out_cpp,
  2483. struct_name,
  2484. class_name,
  2485. class_static,
  2486. class_constructor,
  2487. extras,
  2488. struct_cursor,
  2489. fn_cursor,
  2490. return_cursor,
  2491. wrap_return,
  2492. refcheck_if,
  2493. trace_if,
  2494. ):
  2495. '''
  2496. Writes function or method body to <out_cpp> that calls a generated C++ wrapper
  2497. function.
  2498. fnname:
  2499. .
  2500. out_cpp:
  2501. .
  2502. struct_name:
  2503. If false, we write a class-aware wrapping function body. Otherwise name
  2504. of struct such as 'fz_rect' and we write method body for the struct's
  2505. wrapper class.
  2506. class_name:
  2507. class_static:
  2508. If true, this is a static class method.
  2509. class_constructor:
  2510. If true, this is a constructor.
  2511. extras:
  2512. .
  2513. struct_cursor:
  2514. .
  2515. fn_cursor:
  2516. Cursor for the underlying MuPDF function.
  2517. return_cursor:
  2518. If not None, the cursor for definition of returned type.
  2519. wrap_return:
  2520. If 'pointer', the underlying function returns a pointer to a struct
  2521. that we wrap.
  2522. If 'value' the underlying function returns, by value, a
  2523. struct that we wrap, so we need to construct our wrapper from the
  2524. address of this value.
  2525. Otherwise we don't wrap the returned value.
  2526. '''
  2527. verbose = state.state_.show_details( fnname)
  2528. out_cpp.write( f'{{\n')
  2529. return_void = (fn_cursor.result_type.spelling == 'void')
  2530. # Write trace code.
  2531. out_cpp.write( f' {trace_if}\n')
  2532. out_cpp.write( f' if (s_trace) {{\n')
  2533. out_cpp.write( f' std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():"\n')
  2534. out_cpp.write( f' << " calling mupdf::{rename.ll_fn(fnname)}()\\n";\n')
  2535. out_cpp.write( f' }}\n')
  2536. out_cpp.write( f' #endif\n')
  2537. if fn_cursor.type.is_function_variadic():
  2538. assert fnname == 'fz_warn', f'{fnname=}'
  2539. out_cpp.write( f' va_list ap;\n')
  2540. out_cpp.write( f' va_start( ap, fmt);\n')
  2541. out_cpp.write( f' {rename.ll_fn("fz_vwarn")}( fmt, ap);\n')
  2542. out_cpp.write( f' va_end( ap);\n')
  2543. elif class_constructor or not struct_name:
  2544. # This code can generate a class method, but we choose to not use this,
  2545. # instead method body simply calls the class-aware function (see below).
  2546. def get_keep_drop(arg):
  2547. name = util.clip( arg.alt.type.spelling, 'struct ')
  2548. if name.startswith('fz_'):
  2549. prefix = 'fz'
  2550. name = name[3:]
  2551. elif name.startswith('pdf_'):
  2552. prefix = 'pdf'
  2553. name = name[4:]
  2554. else:
  2555. assert 0
  2556. return rename.ll_fn(f'{prefix}_keep_{name}'), rename.ll_fn(f'{prefix}_drop_{name}')
  2557. # Handle wrapper-class out-params - need to drop .m_internal and set to
  2558. # null.
  2559. #
  2560. # fixme: maybe instead simply call <arg.name>'s destructor directly?
  2561. #
  2562. for arg in parse.get_args( tu, fn_cursor):
  2563. if arg.alt and arg.out_param:
  2564. if parse.has_refs(tu, arg.alt.type):
  2565. keep_fn, drop_fn = get_keep_drop(arg)
  2566. out_cpp.write( f' /* Out-param {arg.name}.m_internal will be overwritten. */\n')
  2567. out_cpp.write( f' {drop_fn}({arg.name}.m_internal);\n')
  2568. out_cpp.write( f' {arg.name}.m_internal = nullptr;\n')
  2569. # Write function call.
  2570. if class_constructor:
  2571. if extras.pod:
  2572. if extras.pod == 'inline':
  2573. out_cpp.write( f' *(::{struct_name}*) &this->{parse.get_field0(struct_cursor.type).spelling} = ')
  2574. else:
  2575. out_cpp.write( f' this->m_internal = ')
  2576. if fn_cursor.result_type.kind == state.clang.cindex.TypeKind.POINTER:
  2577. out_cpp.write( f'*')
  2578. else:
  2579. out_cpp.write( f' this->m_internal = ')
  2580. if fn_cursor.result_type.kind == state.clang.cindex.TypeKind.POINTER:
  2581. pass
  2582. else:
  2583. assert 0, 'cannot handle underlying fn returning by value when not pod.'
  2584. out_cpp.write( f'{rename.ll_fn(fnname)}(')
  2585. elif wrap_return == 'value':
  2586. out_cpp.write( f' {_make_top_level(return_cursor.spelling)} temp = mupdf::{rename.ll_fn(fnname)}(')
  2587. elif wrap_return == 'pointer':
  2588. out_cpp.write( f' {_make_top_level(return_cursor.spelling)}* temp = mupdf::{rename.ll_fn(fnname)}(')
  2589. elif wrap_return == 'const pointer':
  2590. out_cpp.write( f' const {_make_top_level(return_cursor.spelling)}* temp = mupdf::{rename.ll_fn(fnname)}(')
  2591. elif return_void:
  2592. out_cpp.write( f' mupdf::{rename.ll_fn(fnname)}(')
  2593. else:
  2594. out_cpp.write( f' auto ret = mupdf::{rename.ll_fn(fnname)}(')
  2595. have_used_this = False
  2596. sep = ''
  2597. for arg in parse.get_args( tu, fn_cursor):
  2598. arg_classname = class_name
  2599. if class_static or class_constructor:
  2600. arg_classname = None
  2601. out_cpp.write( sep)
  2602. have_used_this = write_call_arg(
  2603. tu,
  2604. arg,
  2605. arg_classname,
  2606. have_used_this,
  2607. out_cpp,
  2608. state.state_.show_details(fnname),
  2609. )
  2610. sep = ', '
  2611. out_cpp.write( f');\n')
  2612. if state.state_.show_details(fnname):
  2613. jlib.log('{=wrap_return}')
  2614. refcounted_return = False
  2615. if wrap_return in ('pointer', 'const pointer') and parse.has_refs( tu, return_cursor.type):
  2616. refcounted_return = True
  2617. refcounted_return_struct_cursor = return_cursor
  2618. elif class_constructor and parse.has_refs( tu, struct_cursor.type):
  2619. refcounted_return = True
  2620. refcounted_return_struct_cursor = struct_cursor
  2621. if refcounted_return:
  2622. # This MuPDF function returns pointer to a struct which uses reference
  2623. # counting. If the function returns a borrowed reference, we need
  2624. # to increment its reference count before passing it to our wrapper
  2625. # class's constructor.
  2626. #
  2627. #jlib.log('Function returns pointer to {return_cursor=}')
  2628. return_struct_name = util.clip( refcounted_return_struct_cursor.spelling, 'struct ')
  2629. if return_struct_name.startswith('fz_'):
  2630. prefix = 'fz_'
  2631. elif return_struct_name.startswith('pdf_'):
  2632. prefix = 'pdf_'
  2633. else:
  2634. prefix = None
  2635. if state.state_.show_details(fnname):
  2636. jlib.log('{=prefix}')
  2637. if prefix:
  2638. if function_name_implies_kept_references( fnname):
  2639. pass
  2640. #out_cpp.write( f' /* We assume that {fnname} returns a kept reference. */\n')
  2641. else:
  2642. if state.state_.show_details(fnname):
  2643. jlib.log('{=classname fnname constructor} Assuming that {fnname=} returns a borrowed reference.')
  2644. # This function returns a borrowed reference.
  2645. suffix = return_struct_name[ len(prefix):]
  2646. keep_fn = f'{prefix}keep_{suffix}'
  2647. #jlib.log('Function assumed to return borrowed reference: {fnname=} => {return_struct_name=} {keep_fn=}')
  2648. #out_cpp.write( f' /* We assume that {fnname} returns a borrowed reference. */\n')
  2649. if class_constructor:
  2650. out_cpp.write( f' {rename.ll_fn(keep_fn)}(this->m_internal);\n')
  2651. else:
  2652. out_cpp.write( f' {rename.ll_fn(keep_fn)}(temp);\n')
  2653. if wrap_return == 'value':
  2654. out_cpp.write( f' auto ret = {rename.class_(return_cursor.spelling)}(&temp);\n')
  2655. elif wrap_return in ('pointer', 'const pointer'):
  2656. out_cpp.write( f' auto ret = {rename.class_(return_cursor.spelling)}(temp);\n')
  2657. # Handle wrapper-class out-params - need to keep arg.m_internal if
  2658. # fnname implies it will be a borrowed reference.
  2659. for arg in parse.get_args( tu, fn_cursor):
  2660. if arg.alt and arg.out_param:
  2661. if parse.has_refs(tu, arg.alt.type):
  2662. if function_name_implies_kept_references( fnname):
  2663. out_cpp.write( f' /* We assume that out-param {arg.name}.m_internal is a kept reference. */\n')
  2664. else:
  2665. keep_fn, drop_fn = get_keep_drop(arg)
  2666. out_cpp.write( f' /* We assume that out-param {arg.name}.m_internal is a borrowed reference. */\n')
  2667. out_cpp.write( f' {keep_fn}({arg.name}.m_internal);\n')
  2668. else:
  2669. # Class method simply calls the class-aware function, which will have
  2670. # been generated elsewhere.
  2671. out_cpp.write( ' ')
  2672. if not return_void:
  2673. out_cpp.write( 'auto ret = ')
  2674. out_cpp.write( f'mupdf::{rename.fn(fnname)}(')
  2675. sep = ''
  2676. for i, arg in enumerate( parse.get_args( tu, fn_cursor)):
  2677. out_cpp.write( sep)
  2678. if i==0 and not class_static:
  2679. out_cpp.write( '*this')
  2680. else:
  2681. out_cpp.write( f'{arg.name}')
  2682. sep = ', '
  2683. out_cpp.write( ');\n')
  2684. if struct_name and not class_static:
  2685. if parse.has_refs( tu, struct_cursor.type):
  2686. # Write code that does runtime checking of reference counts.
  2687. out_cpp.write( f' {refcheck_if}\n')
  2688. out_cpp.write( f' if (s_check_refs)\n')
  2689. out_cpp.write( f' {{\n')
  2690. if class_constructor:
  2691. out_cpp.write( f' s_{class_name}_refs_check.add( this, __FILE__, __LINE__, __FUNCTION__);\n')
  2692. else:
  2693. out_cpp.write( f' s_{class_name}_refs_check.check( this, __FILE__, __LINE__, __FUNCTION__);\n')
  2694. out_cpp.write( f' }}\n')
  2695. out_cpp.write( f' #endif\n')
  2696. if class_constructor:
  2697. out_cpp.write(num_instances(refcheck_if, +1, class_name))
  2698. if not return_void and not class_constructor:
  2699. out_cpp.write( f' return ret;\n')
  2700. out_cpp.write( f'}}\n')
  2701. out_cpp.write( f'\n')
  2702. def function_wrapper_class_aware(
  2703. tu,
  2704. register_fn_use,
  2705. fnname,
  2706. out_h,
  2707. out_cpp,
  2708. struct_name,
  2709. class_name,
  2710. fn_cursor,
  2711. refcheck_if,
  2712. trace_if,
  2713. class_static=False,
  2714. class_constructor=False,
  2715. extras=None,
  2716. struct_cursor=None,
  2717. duplicate_type=None,
  2718. generated=None,
  2719. debug=None,
  2720. ):
  2721. '''
  2722. Writes a function or class method that calls <fnname>.
  2723. Also appends python and C# code to generated.swig_python and
  2724. generated.swig_csharp if <generated> is not None.
  2725. tu
  2726. .
  2727. register_fn_use
  2728. Callback to keep track of what fz_*() fns have been used.
  2729. fnname
  2730. Name of fz_*() fn to wrap, e.g. fz_concat.
  2731. out_h
  2732. out_cpp
  2733. Where to write generated code.
  2734. struct_name
  2735. If false, we generate class-aware wrapping function. Otherwise name
  2736. of struct such as 'fz_rect' and we create a method in the struct's
  2737. wrapper class.
  2738. class_name
  2739. Ignored if struct_name is false.
  2740. Name of wrapper class, e.g. 'Rect'.
  2741. class_static
  2742. Ignored if struct_name is false.
  2743. If true, we generate a static method.
  2744. Otherwise we generate a normal class method, where first arg that
  2745. is type <struct_name> is omitted from the generated method's
  2746. prototype; in the implementation we use <this>.
  2747. class_constructor
  2748. If true, we write a constructor.
  2749. extras
  2750. None or ClassExtras instance.
  2751. Only used if <constructor> is true.
  2752. struct_cursor
  2753. None or cursor for the struct definition.
  2754. Only used if <constructor> is true.
  2755. duplicate_type:
  2756. If true, we have already generated a method with the same args, so
  2757. this generated method will be commented-out.
  2758. generated:
  2759. If not None and there are one or more out-params, we write
  2760. python code to generated.swig_python that overrides the default
  2761. SWIG-generated method to call our *_outparams_fn() alternative.
  2762. debug
  2763. Show extra diagnostics.
  2764. '''
  2765. verbose = state.state_.show_details( fnname)
  2766. if fn_cursor and fn_cursor.type.is_function_variadic() and fnname != 'fz_warn':
  2767. jlib.log( 'Not writing class-aware wrapper because variadic: {fnname=}', 1)
  2768. return
  2769. if verbose:
  2770. jlib.log( 'Writing class-aware wrapper for {fnname=}')
  2771. if struct_name:
  2772. assert fnname not in state.omit_methods, jlib.log_text( '{=fnname}')
  2773. if debug:
  2774. jlib.log( '{class_name=} {fnname=}')
  2775. assert fnname.startswith( ('fz_', 'pdf_'))
  2776. if not fn_cursor:
  2777. fn_cursor = state.state_.find_function( tu, fnname, method=True)
  2778. if not fn_cursor:
  2779. jlib.log( '*** ignoring {fnname=}')
  2780. return
  2781. if fnname.endswith('_drop'):
  2782. # E.g. fz_concat_push_drop() is not safe (or necessary) for us because
  2783. # we need to manage reference counts ourselves.
  2784. #jlib.log('Ignoring because ends with "_drop": {fnname}')
  2785. return
  2786. if struct_name:
  2787. methodname = rename.method( struct_name, fnname)
  2788. else:
  2789. methodname = rename.fn( fnname)
  2790. if verbose:
  2791. jlib.log( 'Writing class-aware wrapper for {fnname=}')
  2792. # Construct prototype fnname(args).
  2793. #
  2794. if class_constructor:
  2795. assert struct_name
  2796. decl_h = f'{class_name}('
  2797. decl_cpp = f'{class_name}('
  2798. else:
  2799. decl_h = f'{methodname}('
  2800. decl_cpp = f'{methodname}('
  2801. have_used_this = False
  2802. num_out_params = 0
  2803. num_class_wrapper_params = 0
  2804. comma = ''
  2805. this_is_const = False
  2806. debug = state.state_.show_details( fnname)
  2807. for arg in parse.get_args( tu, fn_cursor):
  2808. if debug:
  2809. jlib.log( 'Looking at {struct_name=} {fnname=} {fnname_wrapper} {arg=}', 1)
  2810. decl_h += comma
  2811. decl_cpp += comma
  2812. if arg.out_param:
  2813. num_out_params += 1
  2814. if arg.alt:
  2815. # This parameter is a pointer to a struct that we wrap.
  2816. num_class_wrapper_params += 1
  2817. arg_extras = classes.classextras.get( tu, arg.alt.type.spelling)
  2818. assert arg_extras, jlib.log_text( '{=structname fnname arg.alt.type.spelling}')
  2819. const = ''
  2820. if not arg.out_param and (not arg_extras.pod or arg.cursor.type.kind != state.clang.cindex.TypeKind.POINTER):
  2821. const = 'const '
  2822. if (1
  2823. and struct_name
  2824. and not class_static
  2825. and not class_constructor
  2826. and rename.class_(util.clip( arg.alt.type.spelling, 'struct ')) == class_name
  2827. and not have_used_this
  2828. ):
  2829. assert not arg.out_param
  2830. # Omit this arg from the method's prototype - we'll use <this>
  2831. # when calling the underlying fz_ function.
  2832. have_used_this = True
  2833. if not arg_extras.pod:
  2834. this_is_const = const
  2835. continue
  2836. if arg_extras.pod == 'none':
  2837. jlib.log( 'Not wrapping because {arg=} wrapper has {extras.pod=}', 1)
  2838. return
  2839. text = f'{const}{rename.class_(arg.alt.type.spelling)}& {arg.name}'
  2840. decl_h += text
  2841. decl_cpp += text
  2842. else:
  2843. jlib.logx( '{arg.spelling=}')
  2844. decl_text = declaration_text( arg.cursor.type, arg.name)
  2845. decl_h += decl_text
  2846. decl_cpp += decl_text
  2847. comma = ', '
  2848. if fn_cursor.type.is_function_variadic():
  2849. decl_h += f'{comma}...'
  2850. decl_cpp += f'{comma}...'
  2851. decl_h += ')'
  2852. decl_cpp += ')'
  2853. if this_is_const:
  2854. decl_h += ' const'
  2855. decl_cpp += ' const'
  2856. if verbose:
  2857. jlib.log( '{=struct_name class_constructor}')
  2858. if class_constructor:
  2859. comment = f'Constructor using `{fnname}()`.'
  2860. else:
  2861. comment = make_wrapper_comment(
  2862. tu,
  2863. fn_cursor,
  2864. fnname,
  2865. methodname,
  2866. indent=' ',
  2867. is_method=bool(struct_name),
  2868. is_low_level=False,
  2869. )
  2870. if struct_name and not class_static and not class_constructor:
  2871. assert have_used_this, f'error: wrapper for {struct_name}: {fnname}() is not useful - does not have a {struct_name} arg.'
  2872. if struct_name and not duplicate_type:
  2873. register_fn_use( fnname)
  2874. # If this is true, we explicitly construct a temporary from what the
  2875. # wrapped function returns.
  2876. #
  2877. wrap_return = None
  2878. warning_not_copyable = False
  2879. warning_no_raw_constructor = False
  2880. # Figure out return type for our generated function/method.
  2881. #
  2882. if verbose:
  2883. jlib.log( 'Looking at return type...')
  2884. return_cursor = None
  2885. return_type = None
  2886. return_extras = None
  2887. if class_constructor:
  2888. assert struct_name
  2889. fn_h = f'{decl_h}'
  2890. fn_cpp = f'{class_name}::{decl_cpp}'
  2891. else:
  2892. fn_h = declaration_text( fn_cursor.result_type, decl_h)
  2893. if verbose:
  2894. jlib.log( '{fn_cursor.result_type=}')
  2895. if struct_name:
  2896. fn_cpp = declaration_text( fn_cursor.result_type, f'{class_name}::{decl_cpp}')
  2897. else:
  2898. fn_cpp = declaration_text( fn_cursor.result_type, f'{decl_cpp}')
  2899. # See whether we can convert return type to an instance of a wrapper
  2900. # class.
  2901. #
  2902. if verbose:
  2903. jlib.log( '{fn_cursor.result_type.kind=}')
  2904. if fn_cursor.result_type.kind == state.clang.cindex.TypeKind.POINTER:
  2905. # Function returns a pointer.
  2906. t = state.get_name_canonical( fn_cursor.result_type.get_pointee())
  2907. if verbose:
  2908. jlib.log( '{t.spelling=}')
  2909. return_cursor = parse.find_struct( tu, t.spelling, require_definition=False)
  2910. if verbose:
  2911. jlib.log( '{=t.spelling return_cursor}')
  2912. if return_cursor:
  2913. # Function returns a pointer to a struct.
  2914. return_extras = classes.classextras.get( tu, return_cursor.spelling)
  2915. if return_extras:
  2916. # Function returns a pointer to a struct for which we
  2917. # generate a class wrapper, so change return type to be an
  2918. # instance of the class wrapper.
  2919. return_type = rename.class_(return_cursor.spelling)
  2920. if verbose:
  2921. jlib.log( '{=return_type}')
  2922. if 0 and (state.state_.show_details(return_cursor.type.spelling) or state.state_.show_details(struct_name)):
  2923. jlib.log('{return_cursor.type.spelling=}'
  2924. ' {return_cursor.spelling=}'
  2925. ' {struct_name=} {return_extras.copyable=}'
  2926. ' {return_extras.constructor_raw=}'
  2927. )
  2928. fn_h = f'{return_type} {decl_h}'
  2929. if struct_name:
  2930. fn_cpp = f'{return_type} {class_name}::{decl_cpp}'
  2931. else:
  2932. fn_cpp = f'{return_type} {decl_cpp}'
  2933. if t.is_const_qualified():
  2934. wrap_return = 'const pointer'
  2935. else:
  2936. wrap_return = 'pointer'
  2937. else:
  2938. return_pointee = fn_cursor.result_type.get_pointee()
  2939. if 'char' in return_pointee.spelling:
  2940. if function_name_implies_kept_references(fnname):
  2941. # For now we just output a diagnostic, but eventually
  2942. # we might make C++ wrappers return a std::string here,
  2943. # free()-ing the char* before returning.
  2944. jlib.log1( 'Function name implies kept reference and returns char*:'
  2945. ' {fnname}(): {fn_cursor.result_type.spelling=}'
  2946. ' -> {return_pointee.spelling=}.'
  2947. )
  2948. if verbose:
  2949. jlib.log( '{=warning_not_copyable warning_no_raw_constructor}')
  2950. else:
  2951. # The fz_*() function returns by value. See whether we can convert
  2952. # its return type to an instance of a wrapper class.
  2953. #
  2954. # If so, we will use constructor that takes pointer to the fz_
  2955. # struct. C++ doesn't allow us to use address of temporary, so we
  2956. # generate code like this:
  2957. #
  2958. # fz_quad_s ret = mupdf_snap_selection(...);
  2959. # return Quad(&ret);
  2960. #
  2961. t = state.get_name_canonical( fn_cursor.result_type)
  2962. # 2023-02-09: parse.find_struct() will actually find any definition,
  2963. # and we now prefix Fitz headers with a typedef of size_t on Linux,
  2964. # so we need to avoid calling parse.find_struct() unless `t` is for
  2965. # a MuPDF type.
  2966. #
  2967. if t.spelling.startswith( ('fz_', 'pdf_')):
  2968. return_cursor = parse.find_struct( tu, t.spelling)
  2969. if return_cursor:
  2970. tt = state.get_name_canonical( return_cursor.type)
  2971. if tt.kind == state.clang.cindex.TypeKind.ENUM:
  2972. # For now, we return this type directly with no wrapping.
  2973. pass
  2974. else:
  2975. return_extras = classes.classextras.get( tu, return_cursor.spelling)
  2976. return_type = rename.class_(return_cursor.type.spelling)
  2977. fn_h = f'{return_type} {decl_h}'
  2978. if struct_name:
  2979. fn_cpp = f'{return_type} {class_name}::{decl_cpp}'
  2980. else:
  2981. fn_cpp = f'{return_type} {decl_cpp}'
  2982. wrap_return = 'value'
  2983. if return_extras:
  2984. if not return_extras.copyable:
  2985. out_h.write(
  2986. textwrap.indent(
  2987. textwrap.dedent( f'''
  2988. /* Class-aware wrapper for `{fnname}()`
  2989. is not available because returned wrapper class for `{return_cursor.spelling}`
  2990. is non-copyable. */
  2991. '''
  2992. ),
  2993. ' ',
  2994. )
  2995. )
  2996. if verbose:
  2997. jlib.log( 'Not creating class-aware wrapper because returned wrapper class is non-copyable: {fnname=}.')
  2998. return
  2999. if not return_extras.constructor_raw:
  3000. out_h.write(
  3001. textwrap.indent(
  3002. textwrap.dedent( f'''
  3003. /* Class-aware wrapper for `{fnname}()`
  3004. is not available because returned wrapper class for `{return_cursor.spelling}`
  3005. does not have raw constructor. */
  3006. '''
  3007. ),
  3008. ' ',
  3009. )
  3010. )
  3011. if verbose:
  3012. jlib.log( 'Not creating class-aware wrapper because returned wrapper class does not have raw constructor: {fnname=}.')
  3013. return
  3014. out_h.write( '\n')
  3015. out_h.write( f' /** {comment} */\n')
  3016. # Copy any comment (indented) into class definition above method
  3017. # declaration.
  3018. if fn_cursor.raw_comment:
  3019. raw_comment = fn_cursor.raw_comment.replace('\r', '')
  3020. for line in raw_comment.split( '\n'):
  3021. out_h.write( f' {line}\n')
  3022. if duplicate_type:
  3023. out_h.write( f' /* Disabled because same args as {duplicate_type}.\n')
  3024. out_h.write( f' FZ_FUNCTION {"static " if class_static else ""}{fn_h};\n')
  3025. if duplicate_type:
  3026. out_h.write( f' */\n')
  3027. if not struct_name:
  3028. # Use extra spacing between non-class functions. Class methods are
  3029. # grouped together.
  3030. out_cpp.write( f'\n')
  3031. out_cpp.write( f'/* {comment} */\n')
  3032. if duplicate_type:
  3033. out_cpp.write( f'/* Disabled because same args as {duplicate_type}.\n')
  3034. out_cpp.write( f'FZ_FUNCTION {fn_cpp}\n')
  3035. function_wrapper_class_aware_body(
  3036. tu,
  3037. fnname,
  3038. out_cpp,
  3039. struct_name,
  3040. class_name,
  3041. class_static,
  3042. class_constructor,
  3043. extras,
  3044. struct_cursor,
  3045. fn_cursor,
  3046. return_cursor,
  3047. wrap_return,
  3048. refcheck_if,
  3049. trace_if,
  3050. )
  3051. if struct_name:
  3052. if duplicate_type:
  3053. out_cpp.write( f'*/\n')
  3054. # fixme: the test of `struct_name` means that we don't generate outparam override for
  3055. # class-aware fns which don't have any struct/class args, e.g. fz_lookup_cjk_font().
  3056. #
  3057. if generated and num_out_params:
  3058. make_python_class_method_outparam_override(
  3059. tu,
  3060. fn_cursor,
  3061. fnname,
  3062. generated,
  3063. struct_name,
  3064. class_name,
  3065. return_type,
  3066. )
  3067. def class_custom_method(
  3068. tu,
  3069. register_fn_use,
  3070. struct_cursor,
  3071. classname,
  3072. extramethod,
  3073. out_h,
  3074. out_cpp,
  3075. refcheck_if,
  3076. trace_if,
  3077. ):
  3078. '''
  3079. Writes custom method as specified by <extramethod>.
  3080. tu
  3081. .
  3082. register_fn_use
  3083. Callable taking single <fnname> arg.
  3084. struct_cursor
  3085. Cursor for definition of MuPDF struct.
  3086. classname
  3087. Name of wrapper class for <struct_cursor>.
  3088. extramethod
  3089. An ExtraMethod or ExtraConstructor instance.
  3090. out_h
  3091. out_cpp
  3092. Where to write generated code.
  3093. '''
  3094. assert isinstance( extramethod, ( classes.ExtraMethod, classes.ExtraConstructor)), f'{type(extramethod)}'
  3095. is_constructor = False
  3096. is_destructor = False
  3097. is_begin_end = False
  3098. if extramethod.return_:
  3099. name_args = extramethod.name_args
  3100. return_space = f'{extramethod.return_} '
  3101. comment = 'Custom method.'
  3102. if name_args.startswith( 'begin(') or name_args.startswith( 'end('):
  3103. is_begin_end = True
  3104. elif extramethod.name_args == '~()':
  3105. # Destructor.
  3106. name_args = f'~{classname}{extramethod.name_args[1:]}'
  3107. return_space = ''
  3108. comment = 'Custom destructor.'
  3109. is_destructor = True
  3110. elif extramethod.name_args.startswith('operator '):
  3111. name_args = extramethod.name_args
  3112. comment = 'Custom operator.'
  3113. return_space = ''
  3114. else:
  3115. # Constructor.
  3116. assert extramethod.name_args.startswith( '('), f'bad constructor/destructor in {classname=}: {extramethod.name_args=}'
  3117. name_args = f'{classname}{extramethod.name_args}'
  3118. return_space = ''
  3119. comment = 'Custom constructor.'
  3120. is_constructor = True
  3121. out_h.write( f'\n')
  3122. if extramethod.comment:
  3123. for i, line in enumerate( extramethod.comment.strip().split('\n')):
  3124. line = line.replace( '/* ', '/** ')
  3125. out_h.write( f' {line}\n')
  3126. else:
  3127. out_h.write( f' /** {comment} */\n')
  3128. out_h.write( f' FZ_FUNCTION {return_space}{name_args};\n')
  3129. out_cpp.write( f'/** {comment} */\n')
  3130. # Remove any default arg values from <name_args>.
  3131. name_args_no_defaults = re.sub('= *[^(][^),]*', '', name_args)
  3132. if name_args_no_defaults != name_args:
  3133. jlib.log('have changed {name_args=} to {name_args_no_defaults=}', 1)
  3134. out_cpp.write( f'FZ_FUNCTION {return_space}{classname}::{name_args_no_defaults}')
  3135. body = textwrap.dedent(extramethod.body)
  3136. end = body.rfind('}')
  3137. assert end >= 0
  3138. out_cpp.write( body[:end])
  3139. if is_constructor and parse.has_refs( tu, struct_cursor.type):
  3140. # Insert ref checking code into end of custom constructor body.
  3141. out_cpp.write( f' {refcheck_if}\n')
  3142. out_cpp.write( f' if (s_check_refs)\n')
  3143. out_cpp.write( f' {{\n')
  3144. out_cpp.write( f' s_{classname}_refs_check.add( this, __FILE__, __LINE__, __FUNCTION__);\n')
  3145. out_cpp.write( f' }}\n')
  3146. out_cpp.write( f' #endif\n')
  3147. if is_constructor:
  3148. out_cpp.write( num_instances(refcheck_if, +1, classname))
  3149. if is_destructor:
  3150. out_cpp.write( num_instances(refcheck_if, -1, classname))
  3151. out_cpp.write( body[end:])
  3152. out_cpp.write( f'\n')
  3153. if 1: # lgtm [py/constant-conditional-expression]
  3154. # Register calls of all fz_* functions. Not necessarily helpful - we
  3155. # might only be interested in calls of fz_* functions that are directly
  3156. # available to uses of class.
  3157. #
  3158. for fnname in re.findall( '(mupdf::[a-zA-Z0-9_]+) *[(]', extramethod.body):
  3159. fnname = util.clip( fnname, 'mupdf::')
  3160. if not fnname.startswith( 'pdf_'):
  3161. fnname = 'fz_' + fnname
  3162. #log( 'registering use of {fnname} in extramethod {classname}::{name_args}')
  3163. register_fn_use( fnname)
  3164. return is_constructor, is_destructor, is_begin_end
  3165. def class_raw_constructor(
  3166. tu,
  3167. register_fn_use,
  3168. classname,
  3169. struct_cursor,
  3170. struct_name,
  3171. base_name,
  3172. extras,
  3173. constructor_fns,
  3174. out_h,
  3175. out_cpp,
  3176. refcheck_if,
  3177. trace_if,
  3178. ):
  3179. '''
  3180. Create a raw constructor - a constructor taking a pointer to underlying
  3181. struct. This raw constructor assumes that it already owns the pointer so it
  3182. does not call fz_keep_*(); the class's destructor will call fz_drop_*().
  3183. '''
  3184. #jlib.log( 'Creating raw constructor {classname=} {struct_name=} {extras.pod=} {extras.constructor_raw=} {fnname=}')
  3185. comment = f'/** Constructor using raw copy of pre-existing `::{struct_name}`. */'
  3186. if extras.pod:
  3187. constructor_decl = f'{classname}(const ::{struct_name}* internal)'
  3188. else:
  3189. constructor_decl = f'{classname}(::{struct_name}* internal)'
  3190. out_h.write( '\n')
  3191. out_h.write( f' {comment}\n')
  3192. explicit = ''
  3193. if parse.has_refs( tu, struct_cursor.type):
  3194. # Don't allow implicit construction from low-level struct, because our
  3195. # destructor will drop it without a prior balancing keep.
  3196. explicit = f'explicit '
  3197. out_h.write(
  3198. f' /* This constructor is marked as `explicit` because wrapper classes do not\n'
  3199. f' call `keep`in constructors, but do call `drop` in destructors. So\n'
  3200. f' automatic construction from a {struct_name}* will generally cause an\n'
  3201. f' unbalanced `drop` resulting in errors such as SEGV. */\n'
  3202. )
  3203. if extras.constructor_raw == 'default':
  3204. out_h.write( f' FZ_FUNCTION {explicit}{classname}(::{struct_name}* internal=NULL);\n')
  3205. else:
  3206. out_h.write( f' FZ_FUNCTION {explicit}{constructor_decl};\n')
  3207. if extras.constructor_raw != 'declaration_only':
  3208. out_cpp.write( f'FZ_FUNCTION {classname}::{constructor_decl}\n')
  3209. if extras.pod == 'inline':
  3210. pass
  3211. elif extras.pod:
  3212. out_cpp.write( ': m_internal(*internal)\n')
  3213. else:
  3214. out_cpp.write( ': m_internal(internal)\n')
  3215. out_cpp.write( '{\n')
  3216. if extras.pod == 'inline':
  3217. assert struct_cursor, f'cannot form raw constructor for inline pod {classname} without cursor for underlying {struct_name}'
  3218. out_cpp.write( f' assert( internal);\n')
  3219. for c in parse.get_members(struct_cursor):
  3220. if c.type.kind == state.clang.cindex.TypeKind.CONSTANTARRAY:
  3221. out_cpp.write( f' memcpy(this->{c.spelling}, internal->{c.spelling}, sizeof(this->{c.spelling}));\n')
  3222. else:
  3223. out_cpp.write( f' this->{c.spelling} = internal->{c.spelling};\n')
  3224. if parse.has_refs( tu, struct_cursor.type):
  3225. out_cpp.write( f' {refcheck_if}\n')
  3226. out_cpp.write( f' if (s_check_refs)\n')
  3227. out_cpp.write( f' {{\n')
  3228. out_cpp.write( f' s_{classname}_refs_check.add( this, __FILE__, __LINE__, __FUNCTION__);\n')
  3229. out_cpp.write( f' }}\n')
  3230. out_cpp.write( f' #endif\n')
  3231. out_cpp.write(num_instances(refcheck_if, +1, classname))
  3232. out_cpp.write( '}\n')
  3233. out_cpp.write( '\n')
  3234. if extras.pod == 'inline':
  3235. # Write second constructor that takes underlying struct by value.
  3236. #
  3237. assert not parse.has_refs( tu, struct_cursor.type)
  3238. constructor_decl = f'{classname}(const ::{struct_name} internal)'
  3239. out_h.write( '\n')
  3240. out_h.write( f' {comment}\n')
  3241. out_h.write( f' FZ_FUNCTION {constructor_decl};\n')
  3242. if extras.constructor_raw != 'declaration_only':
  3243. out_cpp.write( f'FZ_FUNCTION {classname}::{constructor_decl}\n')
  3244. out_cpp.write( '{\n')
  3245. for c in parse.get_members(struct_cursor):
  3246. if c.type.kind == state.clang.cindex.TypeKind.CONSTANTARRAY:
  3247. out_cpp.write( f' memcpy(this->{c.spelling}, &internal.{c.spelling}, sizeof(this->{c.spelling}));\n')
  3248. else:
  3249. out_cpp.write( f' this->{c.spelling} = internal.{c.spelling};\n')
  3250. out_cpp.write(num_instances(refcheck_if, +1, classname))
  3251. out_cpp.write( '}\n')
  3252. out_cpp.write( '\n')
  3253. # Write accessor for inline state.state_.
  3254. #
  3255. for const in False, True:
  3256. space_const = ' const' if const else ''
  3257. const_space = 'const ' if const else ''
  3258. out_h.write( '\n')
  3259. out_h.write( f' /** Access as underlying struct. */\n')
  3260. out_h.write( f' FZ_FUNCTION {const_space}::{struct_name}* internal(){space_const};\n')
  3261. out_cpp.write( f'{comment}\n')
  3262. out_cpp.write( f'FZ_FUNCTION {const_space}::{struct_name}* {classname}::internal(){space_const}\n')
  3263. out_cpp.write( '{\n')
  3264. field0 = parse.get_field0(struct_cursor.canonical).spelling
  3265. out_cpp.write( f' auto ret = ({const_space}::{struct_name}*) &this->{field0};\n')
  3266. if parse.has_refs( tu, struct_cursor.type):
  3267. out_cpp.write( f' {refcheck_if}\n')
  3268. out_cpp.write( f' if (s_check_refs)\n')
  3269. out_cpp.write( f' {{\n')
  3270. out_cpp.write( f' s_{classname}_refs_check.add( this, __FILE__, __LINE__, __FUNCTION__);\n')
  3271. out_cpp.write( f' }}\n')
  3272. out_cpp.write( f' #endif\n')
  3273. out_cpp.write( ' return ret;\n')
  3274. out_cpp.write( '}\n')
  3275. out_cpp.write( '\n')
  3276. def class_accessors(
  3277. tu,
  3278. register_fn_use,
  3279. classname,
  3280. struct_cursor,
  3281. struct_name,
  3282. extras,
  3283. out_h,
  3284. out_cpp,
  3285. ):
  3286. '''
  3287. Writes accessor functions for member data.
  3288. '''
  3289. if not extras.pod:
  3290. jlib.logx( 'creating accessor for non-pod class {classname=} wrapping {struct_name}')
  3291. n = 0
  3292. for cursor in parse.get_members(struct_cursor):
  3293. n += 1
  3294. #jlib.log( 'accessors: {cursor.spelling=} {cursor.type.spelling=}')
  3295. # We set this to fz_keep_<type>() function to call, if we return a
  3296. # wrapper class constructed from raw pointer to underlying fz_* struct.
  3297. keep_function = None
  3298. # Set <decl> to be prototype with %s where the name is, e.g. 'int
  3299. # %s()'; later on we use python's % operator to replace the '%s'
  3300. # with the name we want.
  3301. #
  3302. if cursor.type.kind == state.clang.cindex.TypeKind.POINTER:
  3303. decl = 'const ' + declaration_text( cursor.type, '%s()')
  3304. pointee_type = state.get_name_canonical( cursor.type.get_pointee()).spelling
  3305. pointee_type = util.clip( pointee_type, 'const ')
  3306. pointee_type = util.clip( pointee_type, 'struct ')
  3307. #if 'fz_' in pointee_type:
  3308. # jlib.log( '{pointee_type=}')
  3309. # We don't attempt to make accessors to function pointers.
  3310. if state.get_name_canonical( cursor.type.get_pointee()).kind == state.clang.cindex.TypeKind.FUNCTIONPROTO:
  3311. jlib.logx( 'ignoring {cursor.spelling=} because pointer to FUNCTIONPROTO')
  3312. continue
  3313. elif pointee_type.startswith( ('fz_', 'pdf_')):
  3314. extras2 = parse.get_fz_extras( tu, pointee_type)
  3315. if extras2:
  3316. # Make this accessor return an instance of the wrapping
  3317. # class by value.
  3318. #
  3319. classname2 = rename.class_( pointee_type)
  3320. decl = f'{classname2} %s()'
  3321. # If there's a fz_keep_() function, we must call it on the
  3322. # internal data before returning the wrapper class.
  3323. pointee_type_base = util.clip( pointee_type, ('fz_', 'pdf_'))
  3324. keep_function = f'{parse.prefix(pointee_type)}keep_{pointee_type_base}'
  3325. if state.state_.find_function( tu, keep_function, method=False):
  3326. jlib.logx( 'using {keep_function=}')
  3327. else:
  3328. jlib.log( 'cannot find {keep_function=}')
  3329. keep_function = None
  3330. elif cursor.type.kind == state.clang.cindex.TypeKind.FUNCTIONPROTO:
  3331. jlib.log( 'ignoring {cursor.spelling=} because FUNCTIONPROTO')
  3332. continue
  3333. else:
  3334. if 0 and extras.pod: # lgtm [py/unreachable-statement]
  3335. # Return reference so caller can modify. Unfortunately SWIG
  3336. # converts non-const references to pointers, so generated
  3337. # python isn't useful.
  3338. fn_args = '& %s()'
  3339. else:
  3340. fn_args = '%s()'
  3341. if cursor.type.get_array_size() >= 0:
  3342. if 0: # lgtm [py/unreachable-statement]
  3343. # Return reference to the array; we need to put fn name
  3344. # and args inside (...) to allow the declaration syntax
  3345. # to work - we end up with things like:
  3346. #
  3347. # char (& media_class())[64];
  3348. #
  3349. # Unfortunately SWIG doesn't seem to be able to cope
  3350. # with this.
  3351. decl = declaration_text( cursor.type, '(%s)' % fn_args)
  3352. else:
  3353. # Return pointer to the first element of the array, so
  3354. # that SWIG can cope.
  3355. fn_args = '* %s()'
  3356. type_ = cursor.type.get_array_element_type()
  3357. decl = declaration_text( type_, fn_args)
  3358. else:
  3359. if ( cursor.type.kind == state.clang.cindex.TypeKind.TYPEDEF
  3360. and cursor.type.get_typedef_name() in ('uint8_t', 'int8_t')
  3361. ):
  3362. # Don't let accessor return uint8_t because SWIG thinks it
  3363. # is a char*, leading to memory errors. Instead return int.
  3364. #
  3365. jlib.logx('Changing from {cursor.type.get_typedef_name()=} {cursor.type=} to int')
  3366. decl = f'int {fn_args}'
  3367. else:
  3368. decl = declaration_text( cursor.type, fn_args)
  3369. # todo: if return type is uint8_t or int8_t, maybe return as <int>
  3370. # so SWIG doesn't think it is a string? This would fix errors with
  3371. # fz_image::n and fz_image::bpc.
  3372. out_h.write( f' FZ_FUNCTION {decl % cursor.spelling};\n')
  3373. out_cpp.write( 'FZ_FUNCTION %s\n' % (decl % ( f'{classname}::{cursor.spelling}')))
  3374. out_cpp.write( '{\n')
  3375. if keep_function:
  3376. out_cpp.write( f' {rename.ll_fn(keep_function)}(m_internal->{cursor.spelling});\n')
  3377. out_cpp.write( f' return ({classname2}) m_internal->{cursor.spelling};\n')
  3378. else:
  3379. if extras.pod:
  3380. out_cpp.write( f' return m_internal.{cursor.spelling};\n')
  3381. else:
  3382. out_cpp.write( f' return m_internal->{cursor.spelling};\n')
  3383. out_cpp.write( '}\n')
  3384. out_cpp.write( '\n')
  3385. assert n, f'No fields found for {struct_cursor.spelling}.'
  3386. def class_destructor(
  3387. tu,
  3388. register_fn_use,
  3389. classname,
  3390. extras,
  3391. struct_cursor,
  3392. destructor_fns,
  3393. out_h,
  3394. out_cpp,
  3395. refcheck_if,
  3396. trace_if,
  3397. ):
  3398. if len(destructor_fns) > 1:
  3399. # Use function with shortest name.
  3400. if 0: # lgtm [py/unreachable-statement]
  3401. jlib.log( 'Multiple possible destructor fns for {classname=}')
  3402. for fnname, cursor in destructor_fns:
  3403. jlib.log( ' {fnname=} {cursor.spelling=}')
  3404. shortest = None
  3405. for i in destructor_fns:
  3406. if shortest is None or len(i[0]) < len(shortest[0]):
  3407. shortest = i
  3408. #jlib.log( 'Using: {shortest[0]=}')
  3409. destructor_fns = [shortest]
  3410. if len(destructor_fns):
  3411. fnname, cursor = destructor_fns[0]
  3412. register_fn_use( cursor.spelling)
  3413. out_h.write( f' /** Destructor using {cursor.spelling}(). */\n')
  3414. out_h.write( f' FZ_FUNCTION ~{classname}();\n')
  3415. out_cpp.write( f'FZ_FUNCTION {classname}::~{classname}()\n')
  3416. out_cpp.write( '{\n')
  3417. out_cpp.write( f' {rename.ll_fn(fnname)}(m_internal);\n')
  3418. if parse.has_refs( tu, struct_cursor.type):
  3419. out_cpp.write( f' {refcheck_if}\n')
  3420. out_cpp.write( f' if (s_check_refs)\n')
  3421. out_cpp.write( ' {\n')
  3422. out_cpp.write( f' s_{classname}_refs_check.remove( this, __FILE__, __LINE__, __FUNCTION__);\n')
  3423. out_cpp.write( ' }\n')
  3424. out_cpp.write( f' #endif\n')
  3425. out_cpp.write(num_instances(refcheck_if, -1, classname))
  3426. out_cpp.write( '}\n')
  3427. out_cpp.write( '\n')
  3428. else:
  3429. out_h.write(f' {refcheck_if}\n')
  3430. out_h.write(f' /** Destructor only decrements s_num_instances. */\n')
  3431. out_h.write(f' FZ_FUNCTION ~{classname}();\n')
  3432. out_h.write( ' #else\n')
  3433. out_h.write( ' /** We use default destructor. */\n')
  3434. out_h.write( ' #endif\n')
  3435. out_cpp.write( f'{refcheck_if}\n')
  3436. out_cpp.write( f'FZ_FUNCTION {classname}::~{classname}()\n')
  3437. out_cpp.write( '{\n')
  3438. out_cpp.write(num_instances(refcheck_if, -1, classname))
  3439. out_cpp.write( '}\n')
  3440. out_cpp.write( '#endif\n')
  3441. out_cpp.write( '\n')
  3442. def pod_class_members(
  3443. tu,
  3444. classname,
  3445. struct_cursor,
  3446. struct_name,
  3447. extras,
  3448. out_h,
  3449. out_cpp,
  3450. ):
  3451. '''
  3452. Writes code for wrapper class's to_string() member function.
  3453. '''
  3454. out_h.write( f'\n')
  3455. out_h.write( f' /** Returns string containing our members, labelled and inside (...), using operator<<. */\n')
  3456. out_h.write( f' FZ_FUNCTION std::string to_string();\n')
  3457. out_h.write( f'\n')
  3458. out_h.write( f' /** Comparison method. */\n')
  3459. out_h.write( f' FZ_FUNCTION bool operator==(const {classname}& rhs);\n')
  3460. out_h.write( f'\n')
  3461. out_h.write( f' /** Comparison method. */\n')
  3462. out_h.write( f' FZ_FUNCTION bool operator!=(const {classname}& rhs);\n')
  3463. out_cpp.write( f'FZ_FUNCTION std::string {classname}::to_string()\n')
  3464. out_cpp.write( f'{{\n')
  3465. out_cpp.write( f' std::ostringstream buffer;\n')
  3466. out_cpp.write( f' buffer << *this;\n')
  3467. out_cpp.write( f' return buffer.str();\n')
  3468. out_cpp.write( f'}}\n')
  3469. out_cpp.write( f'\n')
  3470. out_cpp.write( f'FZ_FUNCTION bool {classname}::operator==(const {classname}& rhs)\n')
  3471. out_cpp.write( f'{{\n')
  3472. out_cpp.write( f' return ::operator==( *this, rhs);\n')
  3473. out_cpp.write( f'}}\n')
  3474. out_cpp.write( f'\n')
  3475. out_cpp.write( f'FZ_FUNCTION bool {classname}::operator!=(const {classname}& rhs)\n')
  3476. out_cpp.write( f'{{\n')
  3477. out_cpp.write( f' return ::operator!=( *this, rhs);\n')
  3478. out_cpp.write( f'}}\n')
  3479. out_cpp.write( f'\n')
  3480. def struct_to_string_fns(
  3481. tu,
  3482. struct_cursor,
  3483. struct_name,
  3484. extras,
  3485. out_h,
  3486. out_cpp,
  3487. ):
  3488. '''
  3489. Writes functions for text representation of struct/wrapper-class members.
  3490. '''
  3491. out_h.write( f'\n')
  3492. out_h.write( f'/** Returns string containing a {struct_name}\'s members, labelled and inside (...), using operator<<. */\n')
  3493. out_h.write( f'FZ_FUNCTION std::string to_string_{struct_name}(const ::{struct_name}& s);\n')
  3494. out_h.write( f'\n')
  3495. out_h.write( f'/** Returns string containing a {struct_name}\'s members, labelled and inside (...), using operator<<.\n')
  3496. out_h.write( f'(Convenience overload). */\n')
  3497. out_h.write( f'FZ_FUNCTION std::string to_string(const ::{struct_name}& s);\n')
  3498. out_cpp.write( f'\n')
  3499. out_cpp.write( f'FZ_FUNCTION std::string to_string_{struct_name}(const ::{struct_name}& s)\n')
  3500. out_cpp.write( f'{{\n')
  3501. out_cpp.write( f' std::ostringstream buffer;\n')
  3502. out_cpp.write( f' buffer << s;\n')
  3503. out_cpp.write( f' return buffer.str();\n')
  3504. out_cpp.write( f'}}\n')
  3505. out_cpp.write( f'\n')
  3506. out_cpp.write( f'FZ_FUNCTION std::string to_string(const ::{struct_name}& s)\n')
  3507. out_cpp.write( f'{{\n')
  3508. out_cpp.write( f' return to_string_{struct_name}(s);\n')
  3509. out_cpp.write( f'}}\n')
  3510. def pod_struct_fns(
  3511. tu,
  3512. namespace,
  3513. struct_cursor,
  3514. struct_name,
  3515. extras,
  3516. out_h,
  3517. out_cpp,
  3518. ):
  3519. '''
  3520. Writes extra fns for POD structs - operator<<(), operator==(), operator!=().
  3521. '''
  3522. # Write operator<< functions for streaming text representation of C struct
  3523. # members. We should be at top-level in out_h and out_cpp, i.e. not inside
  3524. # 'namespace mupdf {...}'.
  3525. out_h.write( f'\n')
  3526. out_h.write( f'/** {struct_name}: writes members, labelled and inside (...), to a stream. */\n')
  3527. out_h.write( f'FZ_FUNCTION std::ostream& operator<< (std::ostream& out, const ::{struct_name}& rhs);\n')
  3528. out_cpp.write( f'\n')
  3529. out_cpp.write( f'FZ_FUNCTION std::ostream& operator<< (std::ostream& out, const ::{struct_name}& rhs)\n')
  3530. out_cpp.write( f'{{\n')
  3531. i = 0
  3532. out_cpp.write( f' out\n')
  3533. out_cpp.write( f' << "("\n');
  3534. for cursor in parse.get_members(struct_cursor):
  3535. out_cpp.write( f' << ');
  3536. if i:
  3537. out_cpp.write( f'" {cursor.spelling}="')
  3538. else:
  3539. out_cpp.write( f' "{cursor.spelling}="')
  3540. out_cpp.write( f' << rhs.{cursor.spelling}\n')
  3541. i += 1
  3542. out_cpp.write( f' << ")"\n');
  3543. out_cpp.write( f' ;\n')
  3544. out_cpp.write( f' return out;\n')
  3545. out_cpp.write( f'}}\n')
  3546. # Write comparison fns.
  3547. out_h.write( f'\n')
  3548. out_h.write( f'/** {struct_name}: comparison function. */\n')
  3549. out_h.write( f'FZ_FUNCTION bool operator==( const ::{struct_name}& lhs, const ::{struct_name}& rhs);\n')
  3550. out_h.write( f'\n')
  3551. out_h.write( f'/** {struct_name}: comparison function. */\n')
  3552. out_h.write( f'FZ_FUNCTION bool operator!=( const ::{struct_name}& lhs, const ::{struct_name}& rhs);\n')
  3553. out_cpp.write( f'\n')
  3554. out_cpp.write( f'FZ_FUNCTION bool operator==( const ::{struct_name}& lhs, const ::{struct_name}& rhs)\n')
  3555. out_cpp.write( f'{{\n')
  3556. for cursor in parse.get_members(struct_cursor):
  3557. out_cpp.write( f' if (lhs.{cursor.spelling} != rhs.{cursor.spelling}) return false;\n')
  3558. out_cpp.write( f' return true;\n')
  3559. out_cpp.write( f'}}\n')
  3560. out_cpp.write( f'FZ_FUNCTION bool operator!=( const ::{struct_name}& lhs, const ::{struct_name}& rhs)\n')
  3561. out_cpp.write( f'{{\n')
  3562. out_cpp.write( f' return !(lhs == rhs);\n')
  3563. out_cpp.write( f'}}\n')
  3564. def pod_class_fns(
  3565. tu,
  3566. classname,
  3567. struct_cursor,
  3568. struct_name,
  3569. extras,
  3570. out_h,
  3571. out_cpp,
  3572. ):
  3573. '''
  3574. Writes extra fns for wrappers for POD structs - operator<<(), operator==(),
  3575. operator!=().
  3576. '''
  3577. # Write functions for text representation of wrapper-class members. These
  3578. # functions make use of the corresponding struct functions created by
  3579. # struct_to_string_fns().
  3580. #
  3581. assert extras.pod != 'none'
  3582. classname = f'mupdf::{classname}'
  3583. out_h.write( f'\n')
  3584. out_h.write( f'/** {classname}: writes underlying {struct_name}\'s members, labelled and inside (...), to a stream. */\n')
  3585. out_h.write( f'FZ_FUNCTION std::ostream& operator<< (std::ostream& out, const {classname}& rhs);\n')
  3586. out_cpp.write( f'\n')
  3587. out_cpp.write( f'FZ_FUNCTION std::ostream& operator<< (std::ostream& out, const {classname}& rhs)\n')
  3588. out_cpp.write( f'{{\n')
  3589. if extras.pod == 'inline':
  3590. out_cpp.write( f' return out << *rhs.internal();\n')
  3591. elif extras.pod:
  3592. out_cpp.write( f' return out << rhs.m_internal;\n')
  3593. else:
  3594. out_cpp.write( f' return out << " " << *rhs.m_internal;\n')
  3595. out_cpp.write( f'}}\n')
  3596. # Write comparison fns, using comparison of underlying MuPDF struct.
  3597. out_h.write( f'\n')
  3598. out_h.write( f'/** {classname}: comparison function. */\n')
  3599. out_h.write( f'FZ_FUNCTION bool operator==( const {classname}& lhs, const {classname}& rhs);\n')
  3600. out_h.write( f'\n')
  3601. out_h.write( f'/** {classname}: comparison function. */\n')
  3602. out_h.write( f'FZ_FUNCTION bool operator!=( const {classname}& lhs, const {classname}& rhs);\n')
  3603. out_cpp.write( f'\n')
  3604. out_cpp.write( f'FZ_FUNCTION bool operator==( const {classname}& lhs, const {classname}& rhs)\n')
  3605. out_cpp.write( f'{{\n')
  3606. if extras.pod == 'inline':
  3607. out_cpp.write( f' return *lhs.internal() == *rhs.internal();\n')
  3608. else:
  3609. out_cpp.write( f' return lhs.m_internal == rhs.m_internal;\n')
  3610. out_cpp.write( f'}}\n')
  3611. out_cpp.write( f'\n')
  3612. out_cpp.write( f'FZ_FUNCTION bool operator!=( const {classname}& lhs, const {classname}& rhs)\n')
  3613. out_cpp.write( f'{{\n')
  3614. if extras.pod == 'inline':
  3615. out_cpp.write( f' return *lhs.internal() != *rhs.internal();\n')
  3616. else:
  3617. out_cpp.write( f' return lhs.m_internal != rhs.m_internal;\n')
  3618. out_cpp.write( f'}}\n')
  3619. def get_struct_fnptrs( cursor_struct, shallow_typedef_expansion=False, verbose=False):
  3620. '''
  3621. Yields (cursor, fnptr_type) for function-pointer members of struct defined
  3622. at cusor, where <cursor> is the cursor of the member and <fntr_type> is the
  3623. type.
  3624. cursor_struct:
  3625. Cursor for definition of struct; this can be a typedef.
  3626. shallow_typedef_expansion:
  3627. If true, the returned <fnptr_type> has any top-level typedefs resolved
  3628. so will be a clang.cindex.TypeKind.FUNCTIONPROTO, but typedefs within
  3629. the function args are not resolved, e.g. they can be size_t. This can
  3630. be useful when generating code that will be compiled on different
  3631. platforms with differing definitions of size_t.
  3632. '''
  3633. if verbose:
  3634. jlib.log('Looking for fnptrs in {cursor_struct.spelling=}')
  3635. for cursor in parse.get_members(cursor_struct):
  3636. t = cursor.type
  3637. if verbose:
  3638. jlib.log('{t.kind=} {cursor.spelling=}')
  3639. if t.kind == state.clang.cindex.TypeKind.POINTER:
  3640. t = cursor.type.get_pointee()
  3641. if t.kind in (state.clang.cindex.TypeKind.TYPEDEF, state.clang.cindex.TypeKind.ELABORATED):
  3642. t_cursor = t.get_declaration()
  3643. t = t_cursor.underlying_typedef_type
  3644. if t.kind == state.clang.cindex.TypeKind.FUNCTIONPROTO:
  3645. if shallow_typedef_expansion:
  3646. if verbose:
  3647. jlib.log('Not calling state.get_name_canonical() for {t.spelling=}. {cursor.spelling=}.')
  3648. else:
  3649. tt = state.get_name_canonical( t)
  3650. if verbose:
  3651. jlib.log('{tt.spelling=}')
  3652. if (0
  3653. or 'struct (unnamed at ' in tt.spelling
  3654. or 'unnamed struct at ' in tt.spelling
  3655. ):
  3656. # This is clang giving an unhelpful name to an
  3657. # anonymous struct.
  3658. if verbose:
  3659. jlib.log( 'Avoiding clang anonymous struct placeholder: {tt.spelling=}')
  3660. else:
  3661. t = tt
  3662. if verbose:
  3663. jlib.log('Yielding: {cursor.spelling=} {t.spelling=}')
  3664. yield cursor, t
  3665. def class_wrapper_virtual_fnptrs(
  3666. tu,
  3667. struct_cursor,
  3668. struct_name,
  3669. classname,
  3670. extras,
  3671. out_h,
  3672. out_cpp,
  3673. out_h_end,
  3674. generated,
  3675. refcheck_if,
  3676. trace_if,
  3677. ):
  3678. '''
  3679. Generate extra wrapper class if struct contains function pointers, for
  3680. use as a SWIG Director class so that the function pointers can be made to
  3681. effectively point to Python or C# code.
  3682. '''
  3683. if not extras.virtual_fnptrs:
  3684. return
  3685. verbose = state.state_.show_details( struct_name)
  3686. generated.virtual_fnptrs.append( f'{classname}2')
  3687. self_ = extras.virtual_fnptrs.pop( 'self_')
  3688. self_n = extras.virtual_fnptrs.pop( 'self_n', 1)
  3689. alloc = extras.virtual_fnptrs.pop( 'alloc')
  3690. free = extras.virtual_fnptrs.pop( 'free', None)
  3691. comment = extras.virtual_fnptrs.pop( 'comment', None)
  3692. assert not extras.virtual_fnptrs, f'Unused items in virtual_fnptrs: {extras.virtual_fnptrs}'
  3693. # Class definition beginning.
  3694. #
  3695. out_h.write( '\n')
  3696. out_h.write( f'/** Wrapper class for struct {struct_name} with virtual fns for each fnptr; this is for use as a SWIG Director class. */\n')
  3697. if comment:
  3698. out_h.write(comment)
  3699. out_h.write( f'struct {classname}2 : {classname}\n')
  3700. out_h.write( '{\n')
  3701. out_cpp.write( f'/* Implementation of methods for `{classname}2`, virtual_fnptrs wrapper for `{struct_name}`). */\n')
  3702. out_cpp.write( '\n')
  3703. def get_fnptrs( shallow_typedef_expansion=False):
  3704. for i in get_struct_fnptrs( struct_cursor, shallow_typedef_expansion, verbose=verbose):
  3705. yield i
  3706. # Constructor
  3707. #
  3708. out_h.write( '\n')
  3709. out_h.write( ' /** == Constructor. */\n')
  3710. out_h.write(f' FZ_FUNCTION {classname}2();\n')
  3711. out_cpp.write('\n')
  3712. out_cpp.write(f'FZ_FUNCTION {classname}2::{classname}2()\n')
  3713. out_cpp.write( '{\n')
  3714. alloc = [''] + alloc.split('\n')
  3715. alloc = '\n '.join(alloc)
  3716. out_cpp.write(f'{alloc}\n')
  3717. out_cpp.write(f' {trace_if}\n')
  3718. out_cpp.write(f' if (s_trace_director)\n')
  3719. out_cpp.write( ' {\n')
  3720. out_cpp.write(f' std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << ": {classname}2::{classname}2(): this=" << this << "\\n";\n')
  3721. if not extras.pod:
  3722. out_cpp.write(f' std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << ": {classname}2::{classname}2(): m_internal=" << m_internal << "\\n";\n')
  3723. out_cpp.write(f' {classname}2* self = {self_("m_internal")};\n')
  3724. out_cpp.write(f' std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << ": {classname}2::{classname}2(): self=" << self << "\\n";\n')
  3725. out_cpp.write(' }\n')
  3726. out_cpp.write(' #endif\n')
  3727. out_cpp.write( '}\n')
  3728. # Destructor. This needs to be virtual with an empty implementation,
  3729. # because instances will generally be derived classes.
  3730. out_h.write( '\n')
  3731. out_h.write( ' /** == Destructor. */\n')
  3732. out_h.write(f' FZ_FUNCTION virtual ~{classname}2();\n')
  3733. out_cpp.write('\n')
  3734. out_cpp.write(f'FZ_FUNCTION {classname}2::~{classname}2()\n')
  3735. out_cpp.write( '{\n')
  3736. out_cpp.write(f' {trace_if}\n')
  3737. out_cpp.write(f' if (s_trace_director)\n')
  3738. out_cpp.write( ' {\n')
  3739. out_cpp.write(f' std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << ": ~{classname}2(): this=" << this << "\\n";\n')
  3740. if not extras.pod:
  3741. out_cpp.write( f' std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << ": ~{classname}2(): m_internal=" << m_internal << "\\n";\n')
  3742. out_cpp.write( ' }\n')
  3743. out_cpp.write(f' #endif\n')
  3744. if free:
  3745. out_cpp.write(f' {free}\n')
  3746. out_cpp.write( '}\n')
  3747. def write(text):
  3748. out_h.write(text)
  3749. out_cpp.write(text)
  3750. # Define static callback for each fnptr. It's important that these
  3751. # functions do not resolve function parameter typedefs such as size_t to
  3752. # the underlying types such as long int, because:
  3753. #
  3754. # * Our generated code can be compiled on different machines where types
  3755. # such as size_t can be typedef-ed differently.
  3756. #
  3757. # * Elsewhere, code that we generate will assign our static callback
  3758. # functions to MuPDF's function pointers (which use things like size_t).
  3759. #
  3760. # * These assignments will fail if the types don't match exactly.
  3761. #
  3762. # For example fz_output has a member:
  3763. # fz_output_write_fn *write;
  3764. #
  3765. # This typedef is:
  3766. # void (fz_output_write_fn)(fz_context *ctx, void *state, const void *data, size_t n);
  3767. #
  3768. # We generate a static function called Output2_s_write() and we will be
  3769. # setting a fz_output's write member to point to Output2_s_write(), which
  3770. # only works if the types match exactly.
  3771. #
  3772. # So we need to resolve the outer 'typedef fz_output_write_fn', but not
  3773. # the inner 'size_t' typedef for the <n> arg. This is slightly tricky with
  3774. # clang-python - it provide a Type.get_canonical() method that resolves all
  3775. # typedefs, but to resolve just one level of typedefs requires a bit more
  3776. # work. See get_struct_fnptrs() for details.
  3777. #
  3778. # [Usually our generated code deliberately resolves typedefs such as size_t
  3779. # to long int etc, because SWIG-generated code for size_t etc does not
  3780. # always work properly due to SWIG having its own definitions of things
  3781. # like size_t in Python/C#. But in this case the generated static function
  3782. # is not seen by SWIG so it's ok to make it use size_t etc.]
  3783. #
  3784. for cursor, fnptr_type in get_fnptrs( shallow_typedef_expansion=True):
  3785. # Write static callback.
  3786. return_type = _make_top_level(fnptr_type.get_result().spelling)
  3787. out_cpp.write(f'/* Static callback, calls self->{cursor.spelling}(). */\n')
  3788. out_cpp.write(f'static {return_type} {classname}2_s_{cursor.spelling}')
  3789. out_cpp.write('(')
  3790. sep = ''
  3791. for i, arg_type in enumerate( fnptr_type.argument_types()):
  3792. name = f'arg_{i}'
  3793. out_cpp.write(sep)
  3794. out_cpp.write( declaration_text( arg_type, name, expand_typedef=False))
  3795. sep = ', '
  3796. out_cpp.write(')')
  3797. out_cpp.write('\n')
  3798. out_cpp.write('{\n')
  3799. self_expression = self_() if self_n is None else self_( f'arg_{self_n}')
  3800. out_cpp.write(f' {classname}2* self = {self_expression};\n')
  3801. out_cpp.write(f' {trace_if}\n')
  3802. out_cpp.write(f' if (s_trace_director)\n')
  3803. out_cpp.write( ' {\n')
  3804. out_cpp.write(f' std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << ": {classname}2_s_{cursor.spelling}(): arg_0=" << arg_0 << " arg_1=" << arg_1 << " self=" << self << "\\n";\n')
  3805. out_cpp.write( ' }\n')
  3806. out_cpp.write( ' #endif\n')
  3807. out_cpp.write( ' {\n')
  3808. out_cpp.write( ' char error_message[256] = "";\n')
  3809. out_cpp.write( ' try\n')
  3810. out_cpp.write( ' {\n')
  3811. out_cpp.write(f' return self->{cursor.spelling}(')
  3812. sep = ''
  3813. for i, arg_type in enumerate( fnptr_type.argument_types()):
  3814. if i == self_n:
  3815. # This is the void* from which we found `self` so ignore
  3816. # here. Note that we still pass the fz_context to the virtual
  3817. # fn.
  3818. continue
  3819. name = f'arg_{i}'
  3820. out_cpp.write( f'{sep}{name}')
  3821. sep = ', '
  3822. out_cpp.write(');\n')
  3823. out_cpp.write(' }\n')
  3824. # todo: catch our different exception types and map to FZ_ERROR_*.
  3825. out_cpp.write( ' catch (std::exception& e)\n')
  3826. out_cpp.write( ' {\n')
  3827. out_cpp.write(f' {trace_if}\n')
  3828. out_cpp.write( ' if (s_trace_director)\n')
  3829. out_cpp.write( ' {\n')
  3830. out_cpp.write(f' std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << ": {classname}2_s_{cursor.spelling}(): converting std::exception to fz_throw(): " << e.what() << "\\n";\n')
  3831. out_cpp.write( ' }\n')
  3832. out_cpp.write( ' #endif\n')
  3833. out_cpp.write( ' fz_strlcpy(error_message, e.what(), sizeof(error_message));\n')
  3834. out_cpp.write( ' }\n')
  3835. out_cpp.write( ' /* We defer fz_throw() to here, to ensure that `std::exception& e` has been destructed. */\n')
  3836. out_cpp.write( ' fz_throw(arg_0, FZ_ERROR_GENERIC, "%s", error_message);\n')
  3837. if return_type != 'void':
  3838. out_cpp.write(f' /* Keep compiler happy. */\n')
  3839. out_cpp.write(f' {return_type} ret;\n')
  3840. out_cpp.write(f' return ret;\n')
  3841. out_cpp.write( ' }\n')
  3842. out_cpp.write('}\n')
  3843. # Define use_virtual_<name>( bool use) method for each fnptr.
  3844. #
  3845. out_h.write(f'\n')
  3846. # Using a Doxygen-style `/**` comment prefix here can break swig with
  3847. # `Error: Syntax error in input(3).` if there are no following method
  3848. # declarations.
  3849. out_h.write(f' /** These methods set the function pointers in *m_internal\n')
  3850. out_h.write(f' to point to internal callbacks that call our virtual methods. */\n')
  3851. for cursor, fnptr_type in get_fnptrs():
  3852. out_h.write(f' FZ_FUNCTION void use_virtual_{cursor.spelling}( bool use=true);\n')
  3853. out_cpp.write(f'FZ_FUNCTION void {classname}2::use_virtual_{cursor.spelling}( bool use)\n')
  3854. out_cpp.write( '{\n')
  3855. out_cpp.write(f' {trace_if}\n')
  3856. out_cpp.write(f' if (s_trace_director)\n')
  3857. out_cpp.write( ' {\n')
  3858. out_cpp.write(f' std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << ": {classname}2::use_virtual_{cursor.spelling}(): this=" << this << " use=" << use << "\\n";\n')
  3859. out_cpp.write( ' }\n')
  3860. out_cpp.write( ' #endif\n')
  3861. if extras.pod == 'inline':
  3862. # Fnptr (in {classname}2) and virtual function (in {classname})
  3863. # have same name, so we need qualify the fnptr with {classname} to
  3864. # ensure we distinguish between the two.
  3865. out_cpp.write(f' {classname}::{cursor.spelling} = (use) ? {classname}2_s_{cursor.spelling} : nullptr;\n')
  3866. elif extras.pod:
  3867. out_cpp.write(f' m_internal.{cursor.spelling} = (use) ? {classname}2_s_{cursor.spelling} : nullptr;\n')
  3868. else:
  3869. out_cpp.write(f' m_internal->{cursor.spelling} = (use) ? {classname}2_s_{cursor.spelling} : nullptr;\n')
  3870. out_cpp.write( '}\n')
  3871. # Write virtual fn default implementations.
  3872. #
  3873. out_h.write(f'\n')
  3874. # Using a Doxygen-style `/**` comment prefix here can break swig with
  3875. # `Error: Syntax error in input(3).` if there are no following method
  3876. # declarations.
  3877. out_h.write(f' /** Default virtual method implementations; these all throw an exception. */\n')
  3878. for cursor, fnptr_type in get_fnptrs():
  3879. out_h.write(f' FZ_FUNCTION virtual {_make_top_level(fnptr_type.get_result().spelling)} {cursor.spelling}(')
  3880. out_cpp.write(f'/* Default implementation of virtual method. */\n')
  3881. out_cpp.write(f'FZ_FUNCTION {_make_top_level(fnptr_type.get_result().spelling)} {classname}2::{cursor.spelling}(')
  3882. sep = ''
  3883. for i, arg_type in enumerate( fnptr_type.argument_types()):
  3884. if i == self_n:
  3885. # This is the void* from which we found `self` so ignore
  3886. # here. Note that we still pass the fz_context to the virtual
  3887. # fn.
  3888. continue
  3889. name = f'arg_{i}'
  3890. write(f'{sep}')
  3891. decl_text = declaration_text(arg_type, name, verbose=0)
  3892. write(decl_text)
  3893. sep = ', '
  3894. out_h.write( ');\n')
  3895. out_cpp.write( ')\n')
  3896. out_cpp.write( '{\n')
  3897. out_cpp.write(f' std::cerr << "Unexpected call of unimplemented virtual_fnptrs fn {classname}2::{cursor.spelling}(): this=" << this << ".\\n";\n')
  3898. out_cpp.write(f' throw std::runtime_error( "Unexpected call of unimplemented virtual_fnptrs fn {classname}2::{cursor.spelling}()");\n')
  3899. out_cpp.write( '}\n')
  3900. out_h.write( '};\n')
  3901. def class_wrapper(
  3902. tu,
  3903. register_fn_use,
  3904. struct_cursor,
  3905. struct_name,
  3906. classname,
  3907. extras,
  3908. out_h,
  3909. out_cpp,
  3910. out_h_end,
  3911. out_cpp2,
  3912. out_h2,
  3913. generated,
  3914. refcheck_if,
  3915. trace_if,
  3916. ):
  3917. '''
  3918. Creates source for a class called <classname> that wraps <struct_name>,
  3919. with methods that call selected fz_*() functions. Writes to out_h and
  3920. out_cpp.
  3921. Created source is just the per-class code, e.g. it does not contain
  3922. #include's.
  3923. Args:
  3924. tu:
  3925. Clang translation unit.
  3926. struct_cursor:
  3927. Cursor for struct to wrap.
  3928. struct_name:
  3929. Name of struct to wrap.
  3930. classname:
  3931. Name of wrapper class to create.
  3932. out_h:
  3933. Stream to which we write class definition.
  3934. out_cpp:
  3935. Stream to which we write method implementations.
  3936. out_h_end:
  3937. Stream for text that should be put at the end of the generated
  3938. header text.
  3939. generated:
  3940. We write extra python and C# code to generated.out_swig_python and
  3941. generated.out_swig_csharp for use in the swig .i file.
  3942. Returns (is_container, has_to_string). <is_container> is true if generated
  3943. class has custom begin() and end() methods; <has_to_string> is true if we
  3944. have created a to_string() method.
  3945. '''
  3946. assert extras, f'extras is None for {struct_name}'
  3947. if extras.iterator_next:
  3948. class_add_iterator( tu, struct_cursor, struct_name, classname, extras, refcheck_if, trace_if)
  3949. if extras.class_pre:
  3950. out_h.write( textwrap.dedent( extras.class_pre))
  3951. base_name = util.clip( struct_name, ('fz_', 'pdf_'))
  3952. constructor_fns = class_find_constructor_fns( tu, classname, struct_name, base_name, extras)
  3953. for fnname in extras.constructors_wrappers:
  3954. cursor = state.state_.find_function( tu, fnname, method=True)
  3955. assert cursor, f'No cursor for constructor wrapper fnname={fnname}'
  3956. constructor_fns.append( (fnname, cursor, None))
  3957. destructor_fns = class_find_destructor_fns( tu, struct_name, base_name)
  3958. # Class definition beginning.
  3959. #
  3960. out_h.write( '\n')
  3961. if extras.copyable:
  3962. out_h.write( f'/** Wrapper class for struct `{struct_name}`. */\n')
  3963. else:
  3964. out_h.write( f'/** Wrapper class for struct `{struct_name}`. Not copyable or assignable. */\n')
  3965. if struct_cursor.raw_comment:
  3966. raw_comment = struct_cursor.raw_comment.replace('\r', '')
  3967. out_h.write(raw_comment)
  3968. if not raw_comment.endswith( '\n'):
  3969. out_h.write( '\n')
  3970. out_h.write( f'struct {classname}\n{{')
  3971. out_cpp.write( '\n')
  3972. out_cpp.write( f'/* Implementation of methods for {classname} (wrapper for {struct_name}). */\n')
  3973. out_cpp.write( '\n')
  3974. refs = parse.has_refs( tu, struct_cursor.type)
  3975. if refs:
  3976. refs_name, refs_size = refs
  3977. out_cpp.write( f'{refcheck_if}\n')
  3978. if isinstance(refs_name, int):
  3979. # <refs_name> is offset of .refs in the struct.
  3980. allow_int_this = ', true /*allow_int_this*/' if struct_name == 'pdf_obj' else ''
  3981. out_cpp.write( f'static RefsCheck<::{struct_name}, {classname}{allow_int_this}> s_{classname}_refs_check({refs_name}, {refs_size});\n')
  3982. else:
  3983. # <refs_name> is name of .refs in the struct.
  3984. out_cpp.write( f'static RefsCheck<::{struct_name}, {classname}> s_{classname}_refs_check(offsetof(::{struct_name}, {refs_name}), {refs_size});\n')
  3985. out_cpp.write( f'#endif\n')
  3986. out_cpp.write( '\n')
  3987. # Trailing text in header, e.g. typedef for iterator.
  3988. #
  3989. if extras.class_top:
  3990. # Strip leading blank line to avoid slightly odd-looking layout.
  3991. text = util.clip( extras.class_top, '\n')
  3992. text = textwrap.dedent( text)
  3993. text = textwrap.indent( text, ' ')
  3994. out_h.write( '\n')
  3995. out_h.write( text)
  3996. # Constructors
  3997. #
  3998. num_constructors = 0
  3999. have_created_default_constructor = False
  4000. if constructor_fns:
  4001. out_h.write( '\n')
  4002. out_h.write( ' /** == Constructors. */\n')
  4003. num_constructors += len(constructor_fns)
  4004. for fnname, cursor, duplicate_type in constructor_fns:
  4005. # clang-6 appears not to be able to handle fn args that are themselves
  4006. # function pointers, so for now we allow function_wrapper() to fail,
  4007. # so we need to use temporary buffers, otherwise out_functions_h and
  4008. # out_functions_cpp can get partial text written.
  4009. #
  4010. assert cursor, f'No cursor for constructor function. fnname={fnname} duplicate_type={duplicate_type}'
  4011. temp_out_h = io.StringIO()
  4012. temp_out_cpp = io.StringIO()
  4013. if state.state_.show_details(fnname):
  4014. jlib.log('Creating constructor for {=classname fnname}')
  4015. if parse.get_first_arg( tu, cursor) == (None, 0):
  4016. have_created_default_constructor = True
  4017. try:
  4018. function_wrapper_class_aware(
  4019. tu,
  4020. register_fn_use,
  4021. fnname,
  4022. temp_out_h,
  4023. temp_out_cpp,
  4024. struct_name,
  4025. classname,
  4026. cursor,
  4027. refcheck_if,
  4028. trace_if,
  4029. class_static=False,
  4030. class_constructor=True,
  4031. extras=extras,
  4032. struct_cursor=struct_cursor,
  4033. duplicate_type=duplicate_type,
  4034. )
  4035. except Clang6FnArgsBug as e:
  4036. jlib.log( 'Unable to wrap function {fnname} because: {e}')
  4037. else:
  4038. out_h.write( temp_out_h.getvalue())
  4039. out_cpp.write( temp_out_cpp.getvalue())
  4040. # Custom constructors.
  4041. #
  4042. for extra_constructor in extras.constructors_extra:
  4043. if extra_constructor.name_args == '()':
  4044. have_created_default_constructor = True
  4045. class_custom_method(
  4046. tu,
  4047. register_fn_use,
  4048. struct_cursor,
  4049. classname,
  4050. extra_constructor,
  4051. out_h,
  4052. out_cpp,
  4053. refcheck_if,
  4054. trace_if,
  4055. )
  4056. num_constructors += 1
  4057. # Look for function that can be used by copy constructor and operator=.
  4058. #
  4059. if refs:
  4060. assert extras.copyable != 'default', f'Non-POD class for {struct_name=} has refs, so must not use copyable="default"'
  4061. if not extras.pod and extras.copyable and extras.copyable != 'default':
  4062. class_copy_constructor(
  4063. tu,
  4064. register_fn_use,
  4065. struct_name,
  4066. struct_cursor,
  4067. base_name,
  4068. classname,
  4069. constructor_fns,
  4070. out_h,
  4071. out_cpp,
  4072. refcheck_if,
  4073. trace_if,
  4074. )
  4075. elif extras.copyable:
  4076. out_h.write( '\n')
  4077. out_h.write( ' /** We use default copy constructor and operator=. */\n')
  4078. if extras.constructor_default:
  4079. if have_created_default_constructor:
  4080. if 0:
  4081. jlib.log( 'Not creating default constructor because default custom constructor. {struct_name=}')
  4082. elif extras.constructor_raw == 'default':
  4083. if 0:
  4084. jlib.log( 'Not creating default constructor because default raw constructor. {struct_name=}')
  4085. else:
  4086. class_constructor_default(
  4087. tu,
  4088. struct_cursor,
  4089. classname,
  4090. extras,
  4091. out_h,
  4092. out_cpp,
  4093. refcheck_if,
  4094. trace_if,
  4095. )
  4096. num_constructors += 1
  4097. # Auto-add all methods that take <struct_name> as first param, but
  4098. # skip methods that are already wrapped in extras.method_wrappers or
  4099. # extras.methods_extra etc.
  4100. #
  4101. for fnname in parse.find_wrappable_function_with_arg0_type( tu, struct_name):
  4102. if state.state_.show_details(fnname):
  4103. jlib.log('{struct_name=}: looking at potential method wrapping {fnname=}')
  4104. if fnname in extras.method_wrappers:
  4105. #log( 'auto-detected fn already in {struct_name} method_wrappers: {fnname}')
  4106. # Omit this function, because there is an extra method with the
  4107. # same name. (We could probably include both as they will generally
  4108. # have different args so overloading will distinguish them, but
  4109. # extra methods are usually defined to be used in preference.)
  4110. pass
  4111. elif fnname.startswith( 'fz_new_draw_device'):
  4112. # fz_new_draw_device*() functions take first arg fz_matrix, but
  4113. # aren't really appropriate for the fz_matrix wrapper class.
  4114. #
  4115. pass
  4116. elif isinstance( fnname, list):
  4117. assert 0
  4118. else:
  4119. for extramethod in extras.methods_extra:
  4120. if not extramethod.overload:
  4121. if extramethod.name_args.startswith( f'{rename.method( struct_name, fnname)}('):
  4122. jlib.log1( 'Omitting default method because same name as extramethod: {extramethod.name_args}')
  4123. break
  4124. else:
  4125. #log( 'adding to extras.method_wrappers: {fnname}')
  4126. extras.method_wrappers.append( fnname)
  4127. # Extra static methods.
  4128. #
  4129. if extras.method_wrappers_static:
  4130. out_h.write( '\n')
  4131. out_h.write( ' /* == Static methods. */\n')
  4132. for fnname in extras.method_wrappers_static:
  4133. function_wrapper_class_aware(
  4134. tu,
  4135. register_fn_use,
  4136. fnname,
  4137. out_h,
  4138. out_cpp,
  4139. struct_name,
  4140. classname,
  4141. fn_cursor=None,
  4142. refcheck_if=refcheck_if,
  4143. trace_if=trace_if,
  4144. class_static=True,
  4145. struct_cursor=struct_cursor,
  4146. generated=generated,
  4147. )
  4148. # Extra methods that wrap fz_*() fns.
  4149. #
  4150. if extras.method_wrappers or extras.methods_extra:
  4151. out_h.write( '\n')
  4152. out_h.write( ' /* == Methods. */')
  4153. out_h.write( '\n')
  4154. extras.method_wrappers.sort()
  4155. for fnname in extras.method_wrappers:
  4156. function_wrapper_class_aware(
  4157. tu,
  4158. register_fn_use,
  4159. fnname,
  4160. out_h,
  4161. out_cpp,
  4162. struct_name,
  4163. classname,
  4164. None, #fn_cursor
  4165. refcheck_if,
  4166. trace_if,
  4167. struct_cursor=struct_cursor,
  4168. generated=generated,
  4169. debug=state.state_.show_details(fnname),
  4170. )
  4171. # Custom methods.
  4172. #
  4173. is_container = 0
  4174. custom_destructor = False
  4175. for extramethod in extras.methods_extra:
  4176. is_constructor, is_destructor, is_begin_end = class_custom_method(
  4177. tu,
  4178. register_fn_use,
  4179. struct_cursor,
  4180. classname,
  4181. extramethod,
  4182. out_h,
  4183. out_cpp,
  4184. refcheck_if,
  4185. trace_if,
  4186. )
  4187. if is_constructor:
  4188. num_constructors += 1
  4189. if is_destructor:
  4190. custom_destructor = True
  4191. if is_begin_end:
  4192. is_container += 1
  4193. assert is_container==0 or is_container==2, f'struct_name={struct_name} is_container={is_container}' # Should be begin()+end() or neither.
  4194. if is_container:
  4195. pass
  4196. #jlib.log( 'Generated class has begin() and end(): {classname=}')
  4197. if num_constructors == 0 or extras.constructor_raw:
  4198. if state.state_.show_details(struct_name):
  4199. jlib.log('calling class_raw_constructor(). {struct_name=}')
  4200. class_raw_constructor(
  4201. tu,
  4202. register_fn_use,
  4203. classname,
  4204. struct_cursor,
  4205. struct_name,
  4206. base_name,
  4207. extras,
  4208. constructor_fns,
  4209. out_h,
  4210. out_cpp,
  4211. refcheck_if,
  4212. trace_if,
  4213. )
  4214. # Accessor methods to POD data.
  4215. #
  4216. if extras.accessors and extras.pod == 'inline':
  4217. jlib.log( 'ignoring {extras.accessors=} for {struct_name=} because {extras.pod=}.')
  4218. elif extras.accessors:
  4219. out_h.write( f'\n')
  4220. out_h.write( f' /* == Accessors to members of ::{struct_name} m_internal. */\n')
  4221. out_h.write( '\n')
  4222. class_accessors(
  4223. tu,
  4224. register_fn_use,
  4225. classname,
  4226. struct_cursor,
  4227. struct_name,
  4228. extras,
  4229. out_h,
  4230. out_cpp,
  4231. )
  4232. # Destructor.
  4233. #
  4234. if not custom_destructor:
  4235. out_h.write( f'\n')
  4236. class_destructor(
  4237. tu,
  4238. register_fn_use,
  4239. classname,
  4240. extras,
  4241. struct_cursor,
  4242. destructor_fns,
  4243. out_h,
  4244. out_cpp,
  4245. refcheck_if,
  4246. trace_if,
  4247. )
  4248. # If class has '{structname}* m_internal;', provide access to m_iternal as
  4249. # an integer, for use by python etc, and provide `operator bool()`.
  4250. if not extras.pod:
  4251. class_custom_method(
  4252. tu,
  4253. register_fn_use,
  4254. struct_cursor,
  4255. classname,
  4256. classes.ExtraMethod(
  4257. 'long long',
  4258. 'm_internal_value()',
  4259. '''
  4260. {
  4261. return (uintptr_t) m_internal;
  4262. }
  4263. ''',
  4264. '/** Return numerical value of .m_internal; helps with Python debugging. */',
  4265. ),
  4266. out_h,
  4267. out_cpp,
  4268. refcheck_if,
  4269. trace_if,
  4270. )
  4271. class_custom_method(
  4272. tu,
  4273. register_fn_use,
  4274. struct_cursor,
  4275. classname,
  4276. classes.ExtraMethod(
  4277. '',
  4278. 'operator bool()',
  4279. f'''
  4280. {{
  4281. {trace_if}
  4282. if (s_trace)
  4283. {{
  4284. std::cerr << __FILE__ << ":" << __LINE__ << ":"
  4285. << " {classname}::operator bool() called,"
  4286. << " m_internal=" << m_internal << "."
  4287. << "\\n";
  4288. }}
  4289. #endif
  4290. return m_internal ? true : false;
  4291. }}
  4292. ''',
  4293. '/** Return true iff `m_internal` is not null. */',
  4294. ),
  4295. out_h,
  4296. out_cpp,
  4297. refcheck_if,
  4298. trace_if,
  4299. )
  4300. # Class members.
  4301. #
  4302. out_h.write( '\n')
  4303. out_h.write( ' /* == Member data. */\n')
  4304. out_h.write( '\n')
  4305. if extras.pod == 'none':
  4306. pass
  4307. elif extras.pod == 'inline':
  4308. out_h.write( f' /* These members are the same as the members of ::{struct_name}. */\n')
  4309. for c in parse.get_members(struct_cursor):
  4310. out_h.write( f' {declaration_text(c.type, c.spelling)};\n')
  4311. elif extras.pod:
  4312. out_h.write( f' ::{struct_cursor.spelling} m_internal; /** Wrapped data is held by value. */\n')
  4313. else:
  4314. # Putting this double-asterix comment on same line as m_internal breaks
  4315. # swig-4.02 with "Error: Syntax error in input(3).".
  4316. out_h.write( f' /** Pointer to wrapped data. */\n')
  4317. out_h.write( f' ::{struct_name}* m_internal;\n')
  4318. # Declare static `num_instances` variable.
  4319. out_h.write( '\n')
  4320. out_h.write(f' /* Ideally this would be in `{refcheck_if}...#endif`, but Swig will\n')
  4321. out_h.write(f' generate code regardless so we always need to have this available. */\n')
  4322. out_h.write(f' FZ_DATA static int s_num_instances;\n')
  4323. out_cpp.write(f'/* Ideally this would be in `{refcheck_if}...#endif`, but Swig will\n')
  4324. out_cpp.write(f'generate code regardless so we always need to have this available. */\n')
  4325. out_cpp.write(f'int {classname}::s_num_instances = 0;\n')
  4326. out_cpp.write(f'\n')
  4327. # Make operator<< (std::ostream&, ...) for POD classes.
  4328. #
  4329. has_to_string = False
  4330. if extras.pod and extras.pod != 'none':
  4331. has_to_string = True
  4332. pod_class_members(
  4333. tu,
  4334. classname,
  4335. struct_cursor,
  4336. struct_name,
  4337. extras,
  4338. out_h,
  4339. out_cpp,
  4340. )
  4341. # Trailing text in header, e.g. typedef for iterator.
  4342. #
  4343. if extras.class_bottom:
  4344. out_h.write( textwrap.indent( textwrap.dedent( extras.class_bottom), ' '))
  4345. # Private copy constructor if not copyable.
  4346. #
  4347. if not extras.copyable:
  4348. out_h.write( '\n')
  4349. out_h.write( ' private:\n')
  4350. out_h.write( '\n')
  4351. out_h.write( ' /** This class is not copyable or assignable. */\n')
  4352. out_h.write( f' {classname}(const {classname}& rhs);\n')
  4353. out_h.write( f' {classname}& operator=(const {classname}& rhs);\n')
  4354. # Class definition end.
  4355. #
  4356. out_h.write( '};\n')
  4357. if extras.class_post:
  4358. out_h_end.write( textwrap.dedent( extras.class_post))
  4359. if extras.extra_cpp:
  4360. out_cpp.write( f'/* .extra_cpp for {struct_name}. */\n')
  4361. out_cpp.write( textwrap.dedent( extras.extra_cpp))
  4362. class_wrapper_virtual_fnptrs(
  4363. tu,
  4364. struct_cursor,
  4365. struct_name,
  4366. classname,
  4367. extras,
  4368. out_h,
  4369. out_cpp,
  4370. out_h_end,
  4371. generated,
  4372. refcheck_if,
  4373. trace_if,
  4374. )
  4375. return is_container, has_to_string
  4376. def header_guard( name, out):
  4377. '''
  4378. Writes header guard for <name> to stream <out>.
  4379. '''
  4380. m = ''
  4381. for c in name:
  4382. m += c.upper() if c.isalnum() else '_'
  4383. out.write( f'#ifndef {m}\n')
  4384. out.write( f'#define {m}\n')
  4385. out.write( '\n')
  4386. def tabify( filename, text):
  4387. '''
  4388. Returns <text> with leading multiples of 4 spaces converted to tab
  4389. characters.
  4390. '''
  4391. ret = ''
  4392. linenum = 0
  4393. for line in text.split( '\n'):
  4394. linenum += 1
  4395. i = 0
  4396. while 1:
  4397. if i == len(line):
  4398. break
  4399. if line[i] != ' ':
  4400. break
  4401. i += 1
  4402. if i % 4:
  4403. if line[ int(i/4)*4:].startswith( ' *'):
  4404. # This can happen in comments.
  4405. pass
  4406. else:
  4407. jlib.log( '*** {filename}:{linenum}: {i=} {line!r=} indentation is not a multiple of 4')
  4408. num_tabs = int(i / 4)
  4409. ret += num_tabs * '\t' + line[ num_tabs*4:] + '\n'
  4410. # We use [:-1] here because split() always returns extra last item '', so
  4411. # we will have appended an extra '\n'.
  4412. #
  4413. return ret[:-1]
  4414. def refcount_check_code( out, refcheck_if):
  4415. '''
  4416. Writes reference count checking code to <out>.
  4417. '''
  4418. out.write( textwrap.dedent(
  4419. f'''
  4420. /* Support for checking that reference counts of underlying
  4421. MuPDF structs are not smaller than the number of wrapper class
  4422. instances. Enable at runtime by setting environmental variable
  4423. MUPDF_check_refs to "1". */
  4424. static const bool s_check_refs = internal_env_flag("MUPDF_check_refs");
  4425. /* For each MuPDF struct that has an 'int refs' member, we create
  4426. a static instance of this class template with T set to our wrapper
  4427. class, for example:
  4428. static RefsCheck<fz_document, FzDocument> s_FzDocument_refs_check;
  4429. Then if s_check_refs is true, each constructor function calls
  4430. .add(), the destructor calls .remove() and other class functions
  4431. call .check(). This ensures that we check reference counting after
  4432. each class operation.
  4433. If <allow_int_this> is true, we allow _this->m_internal to be
  4434. an invalid pointer less than 4096, in which case we don't try
  4435. to check refs. This is used for pdf_obj because in Python the
  4436. enums PDF_ENUM_NAME_* are converted to mupdf.PdfObj's contain
  4437. .m_internal's which are the enum values cast to (for_pdf_obj*), so
  4438. that they can be used directly.
  4439. If m_size is -1, we don't attempt any checking; this is for fz_xml
  4440. which is reference counted but does not have a simple .refs member.
  4441. */
  4442. {refcheck_if}
  4443. template<typename Struct, typename ClassWrapper, bool allow_int_this=false>
  4444. struct RefsCheck
  4445. {{
  4446. std::mutex m_mutex;
  4447. int m_offset;
  4448. int m_size;
  4449. std::map<Struct*, int> m_this_to_num;
  4450. RefsCheck(int offset, int size)
  4451. : m_offset(offset), m_size(size)
  4452. {{
  4453. assert(offset >= 0 && offset < 1000);
  4454. assert(m_size == 32 || m_size == 16 || m_size == 8 || m_size == -1);
  4455. }}
  4456. void change( const ClassWrapper* this_, const char* file, int line, const char* fn, int delta)
  4457. {{
  4458. assert( s_check_refs);
  4459. if (m_size == -1)
  4460. {{
  4461. /* No well-defined .refs member for us to check, e.g. fz_xml. */
  4462. return;
  4463. }}
  4464. if (!this_->m_internal) return;
  4465. if (allow_int_this)
  4466. {{
  4467. #if 0 // Historic diagnostics, might still be useful.
  4468. std::cerr << __FILE__ << ":" << __LINE__
  4469. << " " << file << ":" << line << ":" << fn << ":"
  4470. << " this_->m_internal=" << this_->m_internal
  4471. << "\\n";
  4472. #endif
  4473. if ((intptr_t) this_->m_internal < 4096)
  4474. {{
  4475. #if 0 // Historic diagnostics, might still be useful.
  4476. std::cerr << __FILE__ << ":" << __LINE__
  4477. << " " << file << ":" << line << ":" << fn << ":"
  4478. << " Ignoring this_->m_internal=" << this_->m_internal
  4479. << "\\n";
  4480. #endif
  4481. return;
  4482. }}
  4483. }}
  4484. std::lock_guard< std::mutex> lock( m_mutex);
  4485. /* Our lock doesn't make our access to
  4486. this_->m_internal->refs thead-safe - other threads
  4487. could be modifying it via fz_keep_<Struct>() or
  4488. fz_drop_<Struct>(). But hopefully our read will be atomic
  4489. in practise anyway? */
  4490. void* refs_ptr = (char*) this_->m_internal + m_offset;
  4491. int refs;
  4492. if (m_size == 32) refs = *(int32_t*) refs_ptr;
  4493. if (m_size == 16) refs = *(int16_t*) refs_ptr;
  4494. if (m_size == 8) refs = *(int8_t* ) refs_ptr;
  4495. int& n = m_this_to_num[ this_->m_internal];
  4496. int n_prev = n;
  4497. assert( n >= 0);
  4498. n += delta;
  4499. #if 0 // Historic diagnostics, might still be useful.
  4500. std::cerr << file << ":" << line << ":" << fn << "():"
  4501. // << " " << typeid(ClassWrapper).name() << ":"
  4502. << " this_=" << this_
  4503. << " this_->m_internal=" << this_->m_internal
  4504. << " refs=" << refs
  4505. << " n: " << n_prev << " => " << n
  4506. << "\\n";
  4507. #endif
  4508. if ( n < 0)
  4509. {{
  4510. #if 0 // Historic diagnostics, might still be useful.
  4511. std::cerr << file << ":" << line << ":" << fn << "():"
  4512. // << " " << typeid(ClassWrapper).name() << ":"
  4513. << " this_=" << this_
  4514. << " this_->m_internal=" << this_->m_internal
  4515. << " bad n: " << n_prev << " => " << n
  4516. << "\\n";
  4517. #endif
  4518. abort();
  4519. }}
  4520. if ( n && refs < n)
  4521. {{
  4522. #if 0 // Historic diagnostics, might still be useful.
  4523. std::cerr << file << ":" << line << ":" << fn << "():"
  4524. // << " " << typeid(ClassWrapper).name() << ":"
  4525. << " this_=" << this_
  4526. << " this_->m_internal=" << this_->m_internal
  4527. << " refs=" << refs
  4528. << " n: " << n_prev << " => " << n
  4529. << " refs mismatch (refs<n):"
  4530. << "\\n";
  4531. #endif
  4532. abort();
  4533. }}
  4534. if (n && ::abs( refs - n) > 1000)
  4535. {{
  4536. /* This traps case where n > 0 but underlying struct is
  4537. freed and .ref is set to bogus value by fz_free() or
  4538. similar. */
  4539. #if 0 // Historic diagnostics, might still be useful.
  4540. std::cerr << file << ":" << line << ":" << fn << "(): " << ": " << typeid(ClassWrapper).name()
  4541. << " bad change to refs."
  4542. << " this_=" << this_
  4543. << " refs=" << refs
  4544. << " n: " << n_prev << " => " << n
  4545. << "\\n";
  4546. #endif
  4547. abort();
  4548. }}
  4549. if (n == 0) m_this_to_num.erase( this_->m_internal);
  4550. }}
  4551. void add( const ClassWrapper* this_, const char* file, int line, const char* fn)
  4552. {{
  4553. change( this_, file, line, fn, +1);
  4554. }}
  4555. void remove( const ClassWrapper* this_, const char* file, int line, const char* fn)
  4556. {{
  4557. change( this_, file, line, fn, -1);
  4558. }}
  4559. void check( const ClassWrapper* this_, const char* file, int line, const char* fn)
  4560. {{
  4561. change( this_, file, line, fn, 0);
  4562. }}
  4563. }};
  4564. #endif
  4565. '''
  4566. ))
  4567. def cpp_source(
  4568. dir_mupdf,
  4569. namespace,
  4570. base,
  4571. header_git,
  4572. generated,
  4573. check_regress,
  4574. clang_info_version,
  4575. refcheck_if,
  4576. trace_if,
  4577. debug,
  4578. ):
  4579. '''
  4580. Generates all .h and .cpp files.
  4581. Args:
  4582. dir_mupdf:
  4583. Location of mupdf checkout.
  4584. namespace:
  4585. C++ namespace to use.
  4586. base:
  4587. Directory in which all generated files are placed.
  4588. header_git:
  4589. If true we include git info in the file comment that is written
  4590. into all generated files.
  4591. generated:
  4592. A Generated instance.
  4593. check_regress:
  4594. If true, we raise exception if generated content differs from what
  4595. is in existing files.
  4596. refcheck_if:
  4597. `#if ... ' text for enabling reference-checking code. For example
  4598. `#if 1` to always enable, `#ifndef NDEBUG` to only enable in debug
  4599. builds, `#if 0` to always disable.
  4600. refcheck_if:
  4601. `#if ... ' text for enabling optional runtime diagnostic, for
  4602. example by setting `MuPDF_trace=1` runtime. For example `#if 1` to
  4603. always enable, `#ifndef NDEBUG` to only enable in debug builds,
  4604. `#if 0` to always disable.
  4605. debug:
  4606. True if debug build.
  4607. Updates <generated> and returns <tu> from clang..
  4608. '''
  4609. assert isinstance(generated, Generated)
  4610. assert not dir_mupdf.endswith( '/')
  4611. assert not base.endswith( '/')
  4612. # Do initial setting up of generated files before parse, because we include extra.h in our parse input.
  4613. doit = True
  4614. if doit:
  4615. class File:
  4616. def __init__( self, filename, tabify=True):
  4617. self.filename = filename
  4618. self.tabify = tabify
  4619. self.file = io.StringIO()
  4620. self.line_begin = True
  4621. self.regressions = True
  4622. self.closed = False
  4623. def write( self, text, fileline=False):
  4624. # Do not allow writes after .close().
  4625. assert not self.closed, f'File.write() called after .close(). {self.filename=}'
  4626. if fileline:
  4627. # Generate #line <line> "<filename>" for our caller's
  4628. # location. This makes any compiler warnings refer to their
  4629. # python code rather than the generated C++ code.
  4630. tb = traceback.extract_stack( None)
  4631. filename, line, function, source = tb[0]
  4632. if self.line_begin:
  4633. self.file.write( f'#line {line} "{filename}"\n')
  4634. self.file.write( text)
  4635. self.line_begin = text.endswith( '\n')
  4636. def close( self):
  4637. if self.closed:
  4638. # Allow multiple calls to .close().
  4639. return
  4640. self.closed = True
  4641. if self.filename:
  4642. # Overwrite if contents differ.
  4643. text = self.get()
  4644. if self.tabify:
  4645. text = tabify( self.filename, text)
  4646. cr = check_regress
  4647. jlib.log('calling util.update_file_regress() check_regress={cr}: {self.filename=}', 1)
  4648. e = util.update_file_regress( text, self.filename, check_regression=cr)
  4649. jlib.log('util.update_file_regress() returned => {e}', 1)
  4650. if e:
  4651. jlib.log('util.update_file_regress() => {e=}', 1)
  4652. self.regressions = True
  4653. jlib.log(f'File updated: {os.path.relpath(self.filename)}')
  4654. else:
  4655. jlib.log(f'File unchanged: {os.path.relpath(self.filename)}')
  4656. def get( self):
  4657. return self.file.getvalue()
  4658. else:
  4659. class File:
  4660. def __init__( self, filename):
  4661. pass
  4662. def write( self, text, fileline=False):
  4663. pass
  4664. def close( self):
  4665. pass
  4666. class Outputs:
  4667. '''
  4668. A set of output files.
  4669. For convenience, after outputs.add( 'foo', 'foo.c'), outputs.foo is a
  4670. python stream that writes to 'foo.c'.
  4671. '''
  4672. def __init__( self):
  4673. self.items = []
  4674. def add( self, name, filename):
  4675. '''
  4676. Sets self.<name> to file opened for writing on <filename>.
  4677. '''
  4678. file = File( filename)
  4679. self.items.append( (name, filename, file))
  4680. setattr( self, name, file)
  4681. def get( self):
  4682. '''
  4683. Returns list of (name, filename, file) tuples.
  4684. '''
  4685. return self.items
  4686. def close( self):
  4687. for name, filename, file in self.items:
  4688. file.close()
  4689. out_cpps = Outputs()
  4690. out_hs = Outputs()
  4691. for name in (
  4692. 'classes',
  4693. 'classes2',
  4694. 'exceptions',
  4695. 'functions',
  4696. 'internal',
  4697. 'extra',
  4698. ):
  4699. out_hs.add( name, f'{base}/include/mupdf/{name}.h')
  4700. out_cpps.add( name, f'{base}/implementation/{name}.cpp')
  4701. # Make text of header comment for all generated file.
  4702. #
  4703. header_text = textwrap.dedent(
  4704. f'''
  4705. /**
  4706. This file was auto-generated by mupdfwrap.py.
  4707. ''')
  4708. if header_git:
  4709. git_id = jlib.get_git_id( dir_mupdf, allow_none=True)
  4710. if git_id:
  4711. git_id = git_id.split('\n', 1)
  4712. header_text += textwrap.dedent(
  4713. f'''
  4714. mupdf checkout:
  4715. {git_id[0]}'
  4716. ''')
  4717. header_text += '*/\n'
  4718. header_text += '\n'
  4719. header_text = header_text[1:] # Strip leading \n.
  4720. for _, _, file in out_cpps.get() + out_hs.get():
  4721. file.write( header_text)
  4722. os.makedirs( f'{base}/include/mupdf', exist_ok=True)
  4723. os.makedirs( f'{base}/implementation', exist_ok=True)
  4724. num_regressions = 0
  4725. # Create extra File that writes to internal buffer rather than an actual
  4726. # file, which we will append to out_h.
  4727. #
  4728. out_h_classes_end = File( None)
  4729. # Write multiple-inclusion guards into headers:
  4730. #
  4731. for name, filename, file in out_hs.get():
  4732. prefix = f'{base}/include/'
  4733. assert filename.startswith( prefix)
  4734. name = filename[ len(prefix):]
  4735. header_guard( name, file)
  4736. # We need to write to out_hs.extra here before we do the parse
  4737. # because out_hs.extra will be part of the input text passed to the
  4738. # clang parser.
  4739. #
  4740. make_extra(out_hs.extra, out_cpps.extra)
  4741. out_hs.extra.write( textwrap.dedent('''
  4742. #endif
  4743. '''))
  4744. out_hs.extra.close()
  4745. out_cpps.extra.close()
  4746. # Now parse.
  4747. #
  4748. try:
  4749. index = state.clang.cindex.Index.create()
  4750. except Exception as e:
  4751. raise Exception(f'libclang does not appear to be installed') from e
  4752. header = f'{dir_mupdf}/include/mupdf/fitz.h'
  4753. assert os.path.isfile( header), f'header={header}'
  4754. # Get clang to parse mupdf/fitz.h and mupdf/pdf.h and mupdf/extra.h.
  4755. #
  4756. # It might be possible to use index.parse()'s <unsaved_files> arg to
  4757. # specify these multiple files, but i couldn't get that to work.
  4758. #
  4759. # So instead we write some #include's to a temporary file and ask clang to
  4760. # parse it.
  4761. #
  4762. temp_h = f'_mupdfwrap_temp.cpp'
  4763. try:
  4764. with open( temp_h, 'w') as f:
  4765. if state.state_.linux or state.state_.macos:
  4766. jlib.log1('Prefixing Fitz headers with `typedef unsigned long size_t;`'
  4767. ' because size_t not available to clang on Linux/MacOS.')
  4768. # On Linux, size_t is defined internally in gcc (e.g. not even
  4769. # in /usr/include/stdint.h) and so not visible to clang.
  4770. #
  4771. # If we don't define it, clang complains about C99 not
  4772. # supporting implicit int and appears to variously expand
  4773. # size_t as different function pointers, e.g. `int (int *)` and
  4774. # `int (*)(int *)`.
  4775. #
  4776. f.write( textwrap.dedent('''
  4777. /*
  4778. Workaround on Linux/MacOS. size_t is defined internally in
  4779. gcc (e.g. not even in /usr/include/stdint.h) and so not visible to clang.
  4780. */
  4781. typedef unsigned long size_t;
  4782. '''))
  4783. if state.state_.macos:
  4784. f.write( textwrap.dedent('''
  4785. /*
  4786. Workaround on MacOS: we need to define fixed-size int types
  4787. and FILE and va_list, similarly as with size_t above.
  4788. */
  4789. typedef signed char int8_t;
  4790. typedef short int16_t;
  4791. typedef int int32_t;
  4792. typedef long long int64_t;
  4793. typedef unsigned char uint8_t;
  4794. typedef unsigned short uint16_t;
  4795. typedef unsigned int uint32_t;
  4796. typedef unsigned long long uint64_t;
  4797. typedef struct FILE FILE;
  4798. typedef struct va_list va_list;
  4799. '''))
  4800. f.write( textwrap.dedent('''
  4801. #include "mupdf/extra.h"
  4802. #include "mupdf/fitz.h"
  4803. #include "mupdf/pdf.h"
  4804. '''))
  4805. # libclang often doesn't have access to system headers so we define
  4806. # MUPDF_WRAP_LIBCLANG so that extra.h can use dummy definition of
  4807. # std::vector.
  4808. #
  4809. args = [
  4810. '-I', f'{dir_mupdf}/include',
  4811. '-I', f'{dir_mupdf}/platform/c++/include',
  4812. '-D', 'MUPDF_WRAP_LIBCLANG',
  4813. '-D', 'FZ_FUNCTION=',
  4814. ]
  4815. tu = index.parse(
  4816. temp_h,
  4817. args = args,
  4818. options = 0
  4819. | state.clang.cindex.TranslationUnit.PARSE_INCOMPLETE
  4820. | state.clang.cindex.TranslationUnit.PARSE_SKIP_FUNCTION_BODIES
  4821. ,
  4822. )
  4823. # Show warnings/errors from the parse. Failure to include stddef.h
  4824. # appears to be harmless on Linux, but other failures seem to cause
  4825. # more problems.
  4826. #
  4827. def show_clang_diagnostic( diagnostic, depth=0):
  4828. for diagnostic2 in diagnostic.children:
  4829. show_clang_diagnostic( diagnostic2, depth + 1)
  4830. jlib.log1( '{" "*4*depth}{diagnostic}')
  4831. if tu.diagnostics:
  4832. jlib.log1( 'tu.diagnostics():')
  4833. for diagnostic in tu.diagnostics:
  4834. show_clang_diagnostic(diagnostic, 1)
  4835. finally:
  4836. if os.path.isfile( temp_h):
  4837. os.remove( temp_h)
  4838. # Write required #includes into .h files:
  4839. #
  4840. out_hs.exceptions.write( textwrap.dedent(
  4841. '''
  4842. #include <stdexcept>
  4843. #include <string>
  4844. #include "mupdf/fitz.h"
  4845. '''))
  4846. out_hs.internal.write( textwrap.dedent(
  4847. '''
  4848. #include <iostream>
  4849. '''))
  4850. out_hs.functions.write( textwrap.dedent(
  4851. '''
  4852. #include "mupdf/extra.h"
  4853. #include "mupdf/fitz.h"
  4854. #include "mupdf/pdf.h"
  4855. #include <iostream>
  4856. #include <string>
  4857. #include <vector>
  4858. '''))
  4859. out_hs.classes.write( textwrap.dedent(
  4860. '''
  4861. #include "mupdf/fitz.h"
  4862. #include "mupdf/functions.h"
  4863. #include "mupdf/pdf.h"
  4864. #include <map>
  4865. #include <string>
  4866. #include <vector>
  4867. '''))
  4868. out_hs.classes2.write( textwrap.dedent(
  4869. '''
  4870. #include "classes.h"
  4871. '''))
  4872. # Write required #includes into .cpp files:
  4873. #
  4874. out_cpps.exceptions.write( textwrap.dedent(
  4875. f'''
  4876. #include "mupdf/exceptions.h"
  4877. #include "mupdf/fitz.h"
  4878. #include "mupdf/internal.h"
  4879. #include <iostream>
  4880. #include <string.h>
  4881. {trace_if}
  4882. static const bool s_trace_exceptions = mupdf::internal_env_flag("MUPDF_trace_exceptions");
  4883. #else
  4884. static const bool s_trace_exceptions_dummy = mupdf::internal_env_flag_check_unset("{trace_if}", "MUPDF_trace_exceptions");
  4885. #endif
  4886. '''))
  4887. out_cpps.functions.write( textwrap.dedent(
  4888. '''
  4889. #include "mupdf/exceptions.h"
  4890. #include "mupdf/functions.h"
  4891. #include "mupdf/internal.h"
  4892. #include "mupdf/extra.h"
  4893. #include <assert.h>
  4894. #include <sstream>
  4895. #include <string.h>
  4896. '''))
  4897. out_cpps.classes.write(
  4898. textwrap.dedent(
  4899. f'''
  4900. #include "mupdf/classes.h"
  4901. #include "mupdf/classes2.h"
  4902. #include "mupdf/exceptions.h"
  4903. #include "mupdf/internal.h"
  4904. #include "mupdf/fitz/geometry.h"
  4905. #include <algorithm>
  4906. #include <map>
  4907. #include <mutex>
  4908. #include <sstream>
  4909. #include <string.h>
  4910. #include <thread>
  4911. #include <string.h>
  4912. {trace_if}
  4913. static const int s_trace = mupdf::internal_env_flag("MUPDF_trace");
  4914. static const bool s_trace_keepdrop = mupdf::internal_env_flag("MUPDF_trace_keepdrop");
  4915. static const bool s_trace_director = mupdf::internal_env_flag("MUPDF_trace_director");
  4916. #else
  4917. static const int s_trace = mupdf::internal_env_flag_check_unset("{trace_if}", "MUPDF_trace");
  4918. static const bool s_trace_keepdrop = mupdf::internal_env_flag_check_unset("{trace_if}", "MUPDF_trace_keepdrop");
  4919. static const bool s_trace_director = mupdf::internal_env_flag_check_unset("{trace_if}", "MUPDF_trace_director");
  4920. #endif
  4921. '''))
  4922. out_cpps.classes2.write(
  4923. textwrap.dedent(
  4924. f'''
  4925. #include "mupdf/classes2.h"
  4926. #include "mupdf/exceptions.h"
  4927. #include "mupdf/internal.h"
  4928. #include "mupdf/fitz/geometry.h"
  4929. #include <map>
  4930. #include <mutex>
  4931. #include <sstream>
  4932. #include <string.h>
  4933. #include <thread>
  4934. #include <string.h>
  4935. {trace_if}
  4936. static const int s_trace = mupdf::internal_env_flag("MUPDF_trace");
  4937. #else
  4938. static const int s_trace = mupdf::internal_env_flag_check_unset("{trace_if}", "MUPDF_trace");
  4939. #endif
  4940. '''))
  4941. namespace = 'mupdf'
  4942. for _, _, file in out_cpps.get() + out_hs.get():
  4943. if file in (out_cpps.internal, out_cpps.extra, out_hs.extra):
  4944. continue
  4945. make_namespace_open( namespace, file)
  4946. # Write reference counting check code to out_cpps.classes.
  4947. refcount_check_code( out_cpps.classes, refcheck_if)
  4948. # Write declaration and definition for metadata_keys global.
  4949. #
  4950. out_hs.functions.write(
  4951. textwrap.dedent(
  4952. '''
  4953. /*
  4954. The keys that are defined for fz_lookup_metadata().
  4955. */
  4956. FZ_DATA extern const std::vector<std::string> metadata_keys;
  4957. '''))
  4958. out_cpps.functions.write(
  4959. textwrap.dedent(
  4960. f'''
  4961. FZ_FUNCTION const std::vector<std::string> metadata_keys = {{
  4962. "format",
  4963. "encryption",
  4964. "info:Title",
  4965. "info:Author",
  4966. "info:Subject",
  4967. "info:Keywords",
  4968. "info:Creator",
  4969. "info:Producer",
  4970. "info:CreationDate",
  4971. "info:ModDate",
  4972. }};
  4973. {trace_if}
  4974. static const int s_trace = internal_env_flag("MUPDF_trace");
  4975. static const bool s_trace_keepdrop = internal_env_flag("MUPDF_trace_keepdrop");
  4976. static const bool s_trace_exceptions = internal_env_flag("MUPDF_trace_exceptions");
  4977. static const bool s_check_error_stack = internal_env_flag("MUPDF_check_error_stack");
  4978. #else
  4979. static const int s_trace = internal_env_flag_check_unset("{trace_if}", "MUPDF_trace");
  4980. static const bool s_trace_keepdrop = internal_env_flag_check_unset("{trace_if}", "MUPDF_trace_keepdrop");
  4981. static const bool s_trace_exceptions = internal_env_flag_check_unset("{trace_if}", "MUPDF_trace_exceptions");
  4982. static const bool s_check_error_stack = internal_env_flag_check_unset("{trace_if}", "MUPDF_check_error_stack");
  4983. #endif
  4984. '''))
  4985. # Write source code for exceptions and wrapper functions.
  4986. #
  4987. jlib.log( 'Creating wrapper functions...')
  4988. make_function_wrappers(
  4989. tu,
  4990. namespace,
  4991. out_hs.exceptions,
  4992. out_cpps.exceptions,
  4993. out_hs.functions,
  4994. out_cpps.functions,
  4995. out_hs.internal,
  4996. out_cpps.internal,
  4997. out_hs.classes2,
  4998. out_cpps.classes2,
  4999. generated,
  5000. refcheck_if,
  5001. trace_if,
  5002. )
  5003. fn_usage = dict()
  5004. functions_unrecognised = set()
  5005. for fnname, cursor in state.state_.find_functions_starting_with( tu, '', method=True):
  5006. fn_usage[ fnname] = [0, cursor]
  5007. generated.c_functions.append(fnname)
  5008. for structname, cursor in state.state_.structs[ tu].items():
  5009. generated.c_structs.append( structname)
  5010. # Create windows_mupdf.def, containing explicit exports for all MuPDF
  5011. # global data and functions. We do this instead of explicitly prefixing
  5012. # everything with FZ_FUNCTION or FZ_DATA in the MuPDF header files.
  5013. #
  5014. windows_def_path = os.path.relpath(f'{base}/windows_mupdf.def')
  5015. windows_def = ''
  5016. windows_def += 'EXPORTS\n'
  5017. for name, cursor in state.state_.find_global_data_starting_with( tu, ('fz_', 'pdf_')):
  5018. if state.state_.show_details(name):
  5019. jlib.log('global: {name=}')
  5020. generated.c_globals.append(name)
  5021. windows_def += f' {name} DATA\n'
  5022. for fnname, cursor in state.state_.find_functions_starting_with( tu, ('fz_', 'pdf_', 'FT_'), method=False):
  5023. if cursor.storage_class == state.clang.cindex.StorageClass.STATIC:
  5024. # These fns do not work in windows.def, probably because they are
  5025. # usually inline?
  5026. #
  5027. jlib.log('Not adding to windows_def because static: {fnname}()', 1)
  5028. elif os.path.abspath(cursor.extent.start.file.name) == os.path.abspath(out_hs.extra.filename):
  5029. # Items defined in out_hs.extra are C++ so we would need to use the
  5030. # mangled name if we added them to windows_def. Instead they are
  5031. # explicitly prefixed with `FZ_FUNCTION`.
  5032. #
  5033. # (We use os.path.abspath() to avoid problems with back and forward
  5034. # slashes in cursor.extent.start.file.name on Windows.)
  5035. #
  5036. jlib.log1('Not adding to {windows_def_path} because defined in {os.path.relpath(out_hs.extra.filename)}: {cursor.spelling}')
  5037. else:
  5038. windows_def += f' {fnname}\n'
  5039. # Add some internal fns that PyMuPDF requires.
  5040. for fnname in (
  5041. 'FT_Get_First_Char',
  5042. 'FT_Get_Next_Char',
  5043. ):
  5044. windows_def += f' {fnname}\n'
  5045. if debug:
  5046. # In debug builds these are real fns, not macros, and we need to
  5047. # make them exported.
  5048. windows_def += f' fz_lock_debug_lock\n'
  5049. windows_def += f' fz_lock_debug_unlock\n'
  5050. jlib.fs_update( windows_def, windows_def_path)
  5051. def register_fn_use( name):
  5052. assert name.startswith( ('fz_', 'pdf_'))
  5053. if name in fn_usage:
  5054. fn_usage[ name][0] += 1
  5055. else:
  5056. functions_unrecognised.add( name)
  5057. # Write source code for wrapper classes.
  5058. #
  5059. jlib.log( 'Creating wrapper classes...')
  5060. # Find all classes that we can create.
  5061. #
  5062. classes_ = []
  5063. for cursor in parse.get_children(tu.cursor):
  5064. if not cursor.spelling.startswith( ('fz_', 'pdf_')):
  5065. continue
  5066. if cursor.kind != state.clang.cindex.CursorKind.TYPEDEF_DECL:
  5067. continue;
  5068. type_ = state.get_name_canonical( cursor.underlying_typedef_type)
  5069. if type_.kind not in (state.clang.cindex.TypeKind.RECORD, state.clang.cindex.TypeKind.ELABORATED):
  5070. continue
  5071. if type_.kind == state.clang.cindex.TypeKind.ELABORATED:
  5072. jlib.log( 'state.clang.cindex.TypeKind.ELABORATED: {type_.spelling=}')
  5073. if not cursor.is_definition():
  5074. # Handle abstract type only if we have an ClassExtra for it.
  5075. extras = classes.classextras.get( tu, cursor.spelling)
  5076. if extras and extras.opaque:
  5077. pass
  5078. #log( 'Creating wrapper for opaque struct: {cursor.spelling=}')
  5079. else:
  5080. continue
  5081. #struct_name = type_.spelling
  5082. struct_name = cursor.spelling
  5083. struct_name = util.clip( struct_name, 'struct ')
  5084. if cursor.spelling != struct_name:
  5085. jlib.log('{type_.spelling=} {struct_name=} {cursor.spelling=}')
  5086. classname = rename.class_( struct_name)
  5087. # For some reason after updating mupdf 2020-04-13, clang-python is
  5088. # returning two locations for struct fz_buffer_s, both STRUCT_DECL. One
  5089. # is 'typedef struct fz_buffer_s fz_buffer;', the other is the full
  5090. # struct definition.
  5091. #
  5092. # No idea why this is happening. Using .canonical doesn't seem to
  5093. # affect things.
  5094. #
  5095. for cl, cu, s in classes_:
  5096. if cl == classname:
  5097. jlib.logx( 'ignoring duplicate STRUCT_DECL for {struct_name=}')
  5098. break
  5099. else:
  5100. classes_.append( (classname, cursor, struct_name))
  5101. classes_.sort()
  5102. # Write forward declarations - this is required because some class
  5103. # methods take pointers to other classes.
  5104. #
  5105. out_hs.classes.write( '\n')
  5106. out_hs.classes.write( '/* Forward declarations of all classes that we define. */\n')
  5107. for classname, struct_cursor, struct_name in classes_:
  5108. out_hs.classes.write( f'struct {classname};\n')
  5109. out_hs.classes.write( '\n')
  5110. # Create each class.
  5111. #
  5112. for classname, struct_cursor, struct_name in classes_:
  5113. #jlib.log( 'creating wrapper {classname} for {cursor.spelling}')
  5114. extras = classes.classextras.get( tu, struct_name)
  5115. assert extras, f'struct_name={struct_name}'
  5116. if extras.pod:
  5117. struct_to_string_fns(
  5118. tu,
  5119. struct_cursor,
  5120. struct_name,
  5121. extras,
  5122. out_hs.functions,
  5123. out_cpps.functions,
  5124. )
  5125. with jlib.LogPrefixScope( f'{struct_name}: '):
  5126. is_container, has_to_string = class_wrapper(
  5127. tu,
  5128. register_fn_use,
  5129. struct_cursor,
  5130. struct_name,
  5131. classname,
  5132. extras,
  5133. out_hs.classes,
  5134. out_cpps.classes,
  5135. out_h_classes_end,
  5136. out_cpps.classes2,
  5137. out_hs.classes2,
  5138. generated,
  5139. refcheck_if,
  5140. trace_if,
  5141. )
  5142. if is_container:
  5143. generated.container_classnames.append( classname)
  5144. if has_to_string:
  5145. generated.to_string_structnames.append( struct_name)
  5146. out_hs.functions.write( textwrap.dedent( '''
  5147. /** Reinitializes the MuPDF context for single-threaded use, which
  5148. is slightly faster when calling code is single threaded.
  5149. This should be called before any other use of MuPDF.
  5150. */
  5151. FZ_FUNCTION void reinit_singlethreaded();
  5152. '''))
  5153. # Generate num_instances diagnostic fn.
  5154. out_hs.classes.write('\n')
  5155. out_hs.classes.write('/** Returns map from class name (for example FzDocument) to s_num_instances. */\n')
  5156. out_hs.classes.write('FZ_FUNCTION std::map<std::string, int> num_instances();\n')
  5157. out_cpps.classes.write('FZ_FUNCTION std::map<std::string, int> num_instances()\n')
  5158. out_cpps.classes.write('{\n')
  5159. out_cpps.classes.write(' std::map<std::string, int> ret;\n')
  5160. for classname, struct_cursor, struct_name in classes_:
  5161. out_cpps.classes.write(f' ret["{classname}"] = {classname}::s_num_instances;\n')
  5162. out_cpps.classes.write(' \n')
  5163. out_cpps.classes.write(' return ret;\n')
  5164. out_cpps.classes.write('}\n')
  5165. # Write close of namespace.
  5166. out_hs.classes.write( out_h_classes_end.get())
  5167. for _, _, file in out_cpps.get() + out_hs.get():
  5168. if file in (out_cpps.internal, out_cpps.extra, out_hs.extra):
  5169. continue
  5170. make_namespace_close( namespace, file)
  5171. # Write pod struct fns such as operator<<(), operator==() - these need to
  5172. # be outside the namespace.
  5173. #
  5174. for classname, struct_cursor, struct_name in classes_:
  5175. extras = classes.classextras.get( tu, struct_name)
  5176. if extras.pod:
  5177. # Make operator<<(), operator==(), operator!=() for POD struct.
  5178. #
  5179. pod_struct_fns(
  5180. tu,
  5181. namespace,
  5182. struct_cursor,
  5183. struct_name,
  5184. extras,
  5185. out_hs.functions,
  5186. out_cpps.functions,
  5187. )
  5188. if extras.pod != 'none':
  5189. # Make operator<<(), operator==(), operator!=() for POD class
  5190. # wrappers.
  5191. #
  5192. pod_class_fns(
  5193. tu,
  5194. classname,
  5195. struct_cursor,
  5196. struct_name,
  5197. extras,
  5198. out_hs.classes,
  5199. out_cpps.classes,
  5200. )
  5201. # Terminate multiple-inclusion guards in headers:
  5202. #
  5203. for name, _, file in out_hs.get():
  5204. if name != 'extra':
  5205. file.write( '\n#endif\n')
  5206. out_hs.close()
  5207. out_cpps.close()
  5208. generated.h_files = [filename for _, filename, _ in out_hs.get()]
  5209. generated.cpp_files = [filename for _, filename, _ in out_cpps.get()]
  5210. if 0: # lgtm [py/unreachable-statement]
  5211. jlib.log( 'Have created:')
  5212. for filename in filenames_h + filenames_cpp:
  5213. jlib.log( ' {filename}')
  5214. # Output usage information.
  5215. #
  5216. fn_usage_filename = f'{base}/fn_usage.txt'
  5217. out_fn_usage = File( fn_usage_filename, tabify=False)
  5218. functions_unused = 0
  5219. functions_used = 0
  5220. for fnname in sorted( fn_usage.keys()):
  5221. n, cursor = fn_usage[ fnname]
  5222. exclude_reasons = parse.find_wrappable_function_with_arg0_type_excluded_cache.get( fnname, [])
  5223. if n:
  5224. functions_used += 1
  5225. else:
  5226. functions_unused += 1
  5227. if n and not exclude_reasons:
  5228. continue
  5229. out_fn_usage.write( f'Functions not wrapped by class methods:\n')
  5230. out_fn_usage.write( '\n')
  5231. for fnname in sorted( fn_usage.keys()):
  5232. n, cursor = fn_usage[ fnname]
  5233. exclude_reasons = parse.find_wrappable_function_with_arg0_type_excluded_cache.get( fnname, [])
  5234. if not exclude_reasons:
  5235. continue
  5236. if n:
  5237. continue
  5238. num_interesting_reasons = 0
  5239. for t, description in exclude_reasons:
  5240. if t == parse.MethodExcludeReason_FIRST_ARG_NOT_STRUCT:
  5241. continue
  5242. if t == parse.MethodExcludeReason_VARIADIC:
  5243. continue
  5244. num_interesting_reasons += 1
  5245. if num_interesting_reasons:
  5246. try:
  5247. out_fn_usage.write( f' {declaration_text( cursor.type, cursor.spelling)}\n')
  5248. except Clang6FnArgsBug as e:
  5249. out_fn_usage.write( f' {cursor.spelling} [full prototype not available due to known clang-6 issue]\n')
  5250. for t, description in exclude_reasons:
  5251. if t == parse.MethodExcludeReason_FIRST_ARG_NOT_STRUCT:
  5252. continue
  5253. out_fn_usage.write( f' {description}\n')
  5254. out_fn_usage.write( '\n')
  5255. out_fn_usage.write( f'\n')
  5256. out_fn_usage.write( f'Functions used more than once:\n')
  5257. for fnname in sorted( fn_usage.keys()):
  5258. n, cursor = fn_usage[ fnname]
  5259. if n > 1:
  5260. out_fn_usage.write( f' n={n}: {declaration_text( cursor.type, cursor.spelling)}\n')
  5261. out_fn_usage.write( f'\n')
  5262. out_fn_usage.write( f'Number of wrapped functions: {len(fn_usage)}\n')
  5263. out_fn_usage.write( f'Number of wrapped functions used by wrapper classes: {functions_used}\n')
  5264. out_fn_usage.write( f'Number of wrapped functions not used by wrapper classes: {functions_unused}\n')
  5265. out_fn_usage.close()
  5266. generated.c_enums = state.state_.enums[ tu]
  5267. if num_regressions:
  5268. raise Exception( f'There were {num_regressions} regressions')
  5269. return tu
  5270. def test():
  5271. '''
  5272. Place to experiment with clang-python.
  5273. '''
  5274. text = ''
  5275. if state.state_.linux:
  5276. text += textwrap.dedent('''
  5277. /*
  5278. Workaround on Linux. size_t is defined internally in gcc. It isn't
  5279. even in stdint.h.
  5280. */
  5281. typedef unsigned long size_t;
  5282. ''')
  5283. text += textwrap.dedent('''
  5284. #include "mupdf/fitz.h"
  5285. #include "mupdf/pdf.h"
  5286. ''')
  5287. path = 'wrap-test.c'
  5288. jlib.fs_update( text, path)
  5289. index = state.clang.cindex.Index.create()
  5290. tu = index.parse( path, '-I /usr/include -I include'.split(' '))
  5291. path2 = 'wrap-test.c.c'
  5292. tu.save(path2)
  5293. jlib.log( 'Have saved to: {path2}')
  5294. parse.dump_ast( tu.cursor, 'ast')
  5295. for diagnostic in tu.diagnostics:
  5296. jlib.log('{diagnostic=}')
  5297. for cursor in parse.get_members( tu.cursor):
  5298. if 'cpp_test_' in cursor.spelling:
  5299. parse.dump_ast(cursor, out=jlib.log)