1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826 |
- {
- This file is part of the Free Component Library (FCL)
- Copyright (c) 2014 by Michael Van Canneyt
- Pascal to Javascript converter class.
- See the file COPYING.FPC, included in this distribution,
- for details about the copyright.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- **********************************************************************
- }(*
- Abstract:
- Converts TPasElements into TJSElements.
- Works:
- - units, programs
- - unit interface function
- - uses list
- - use $impl for implementation declarations, can be disabled
- - interface vars
- - only double, no other float type
- - only string, no other string type
- - modifier public to protect from removing by optimizer
- - implementation vars
- - external vars
- - initialization section
- - option to add "use strict";
- - procedures
- - params
- - local vars
- - default values
- - function results
- - modifier external 'name'
- - local const: declare in singleton parent function as local var
- - give procedure overloads in module unique names by appending $1, $2, ...
- - give nested procedure overloads unique names by appending $1, $2, ...
- - untyped parameter
- - varargs
- - modifier public to protect from removing by optimizer
- - assign statements
- - char
- - literals
- - ord(char) -> char.charCodeAt()
- - chr(integer) -> String.fromCharCode(integer)
- - string
- - literals
- - setlength(s,newlen) -> s.length == newlen
- - read and write char aString[]
- - allow only String, no ShortString, AnsiString, UnicodeString,...
- - allow type casting string to external class name 'String'
- - for loop
- - if loopvar is used afterwards append if($loopend>i)i--;
- - repeat..until
- - while..do
- - try..finally
- - try..except, try..except on else
- - raise, raise E
- - asm..end
- - assembler; asm..end;
- - break
- - continue
- - procedure str, function str
- - type alias
- - inc/dec to += -=
- - case-of
- - convert "a div b" to "Math.floor(a / b)"
- - and, or, xor, not: logical and bitwise
- - typecast boolean to integer and back
- - rename name conflicts with js identifiers: apply, bind, call, prototype, ...
- - record
- - types and vars
- - assign
- - clone record member
- - clone set member
- - clone when passing as argument
- - equal, not equal
- - classes
- - declare using createClass
- - constructor
- - destructor
- - vars, init on create, clear references on destroy
- - class vars
- - ancestor
- - virtual, override, abstract
- - "is" operator
- - "as" operator
- - call inherited "inherited;", "inherited funcname;"
- - call class method
- - read/write class var
- - property
- - param list
- - property of type array
- - class property
- - accessors non static
- - Assigned()
- - default property
- - type casts
- - overloads, reintroduce append $1, $2, ...
- - reintroduced variables
- - external vars and methods
- - const
- - dynamic arrays
- - arrays can be null
- - init as "arr = []" so typeof works
- - SetLength(arr,len) becomes arr = SetLength(arr,len,defaultvalue)
- - length(), low(), high(), assigned(), concat()
- - assign nil -> [] so typeof works
- - read, write element arr[index]
- - multi dimensional [index1,index2] -> [index1][index2]
- - array of record
- - equal, unequal nil -> rtl.length(array)==0 or >0
- - when passing nil to an array argument, pass []
- - allow type casting array to external class name 'Array'
- - type cast array to array of same dimensions and compatible element type
- - function copy(array,start=0,count=max): array
- - procedure insert(item,var array,const position)
- - procedure delete(var array,const start,count)
- - static arrays
- - range: enumtype
- - init as arr = rtl.arrayNewMultiDim([dim1,dim2,...],value)
- - init with expression
- - length(1-dim array)
- - low(1-dim array), high(1-dim array)
- - open arrays
- - as dynamic arrays
- - enums
- - type with values and names
- - option to write numbers instead of variables
- - ord(), low(), high(), pred(), succ()
- - type cast alias to enumtype
- - type cast number to enumtype
- - sets
- - set of enum
- - include, exclude, clone when referenced
- - assign := set state referenced
- - constant set: enums, enum vars, ranges
- - set operators +, -, *, ><, =, <>, >=, <=
- - in-operator
- - low(), high()
- - when passing as argument set state referenced
- - with-do using local var
- - with record do i:=v;
- - with classinstance do begin create; i:=v; f(); i:=a[]; end;
- - pass by reference
- - pass local var to a var/out parameter
- - pass variable to a var/out parameter
- - pass reference to a var/out parameter
- - pass array element to a var/out parameter
- - procedure types
- - implemented as immutable wrapper function
- - assign := nil, proctype (not clone), @function, @method
- - call explicit and implicit
- - compare equal and notequal with nil, proctype, address, function
- - assigned(proctype)
- - pass as argument
- - methods
- - mode delphi: proctype:=proc
- - mode delphi: functype=funcresulttype
- - nested functions
- - class-of
- - assign := nil, var
- - call class method
- - call constructor
- - operators =, <>
- - class var, property, method
- - Self in class method
- - typecast
- - class external
- - JS object or function as ancestor
- - does not descend from TObject
- - all members become external. case sensitive
- - has no hidden values like $class, $ancestor, $unitname, $init, $final
- - can be ancestor of a pascal class (not descend from TObject).
- - pascal class descendant can override methods
- - property works as normal, replaced by getter and setter
- - class-of
- - class var/function: works as in JS.
- - is and as operators
- - destructor forbidden
- - constructor must not be virtual
- - constructor 'new' -> new extclass(params)
- - identifiers are renamed to avoid clashes with external names
- - call inherited
- - Pascal descendant can override newinstance
- - any class can be typecasted to any root class
- - class instances cannot access external class members (e.g. static class functions)
- - external class bracket accessor, getter/setter has external name '[]'
- - external class 'Array' bracket operator [integer] type jsvalue
- - external class 'Object' bracket operator [string] type jsvalue
- - jsvalue
- - init as undefined
- - assign to jsvalue := integer, string, boolean, double, char
- - type cast base types to jsvalue
- - type cast jsvalue to base type
- integer: Math.floor(jsvalue) may return NaN
- boolean: !(jsvalue == false) works for numbers too 0==false
- double: rtl.getNumber(jsvalue) typeof(n)=="number"?n:NaN;
- string: ""+jsvalue
- char: rtl.getChar(jsvalue) ((typeof(c)!="string") && (c.length==1)) ? c : ""
- - enums: assign to jsvalue, typecast jsvalue to enum
- - class instance: assign to jsvalue, typecast jsvalue to a class
- - class of: assign to jsvalue, typecast jsvalue to a class-of
- - array of jsvalue,
- allow to assign any array to an array of jsvalue
- allow type casting to any array
- - parameter, result type, assign from/to untyped
- - operators equal, not equal
- - callback: assign to jsvalue, equal, not equal
- - ECMAScript6:
- - use 0b for binary literals
- - use 0o for octal literals
- ToDos:
- - -Jirtl.js-
- - make -Jirtl.js default for -Jc and -Tnodejs, needs #IFDEF in cfg
- - remove 'Object' array workaround
- - FuncName:= (instead of Result:=)
- - ord(s[i]) -> s.charCodeAt(i)
- - $modeswitch -> define <modeswitch>
- - $modeswitch- -> turn off
- - integer range
- - @@ compare method in delphi mode
- - make records more lightweight
- - dotted unit names, namespaces
- - type alias type
- - RTTI
- - enumeration for..in..do
- - pointer of record
- - nested types in class
- - asm: pas() - useful for overloads and protect an identifier from optimization
- - source maps
- Not in Version 1.0:
- - write, writeln
- - arrays
- - static array: non 0 start index, length
- - array of static array: setlength
- - array range char, char range, integer range, enum range
- - array of const
- - sets
- - set of char, boolean, integer range, char range, enum range
- - set of (enum,enum2) - anonymous enumtype
- - call array of proc element without ()
- - record const
- - enums with custom values
- - library
- - option typecast checking
- - option verify method calls -CR
- - option range checking -Cr
- - option overflow checking -Co
- - optimizations:
- - set operators on literals without temporary arrays, a in [b], [a]*b<>[]
- - use a number for small sets
- -O1 insert local/unit vars for global type references:
- at start of intf var $r1;
- at end of impl: $r1=path;
- -O1 insert unit vars for complex literals
- -O1 no function Result var when assigned only once
- - SetLength(scope.a,l) -> read scope only once, same for
- Include, Exclude, Inc, Dec
- -O1 replace constant expression with result
- -O1 pass array element by ref: when index is constant, use that directly
- - objects, interfaces, advanced records
- - class helpers, type helpers, record helpers,
- - generics
- - operator overloading
- - inline
- - anonymous functions
- Compile flags for debugging: -d<x>
- VerbosePas2JS
- *)
- unit fppas2js;
- {$mode objfpc}{$H+}
- {$inline on}
- interface
- uses
- Classes, SysUtils, math, contnrs, jsbase, jstree, PasTree, PScanner,
- PasResolver;
- // message numbers
- const
- nPasElementNotSupported = 4001;
- nIdentifierNotFound = 4002;
- nUnaryOpcodeNotSupported = 4003;
- nBinaryOpcodeNotSupported = 4004;
- nInvalidNumber = 4005;
- nInitializedArraysNotSupported = 4006;
- nMemberExprMustBeIdentifier = 4007;
- nCantWriteSetLiteral = 4008;
- nVariableIdentifierExpected = 4009;
- nExpectedXButFoundY = 4010;
- nInvalidFunctionReference = 4011;
- nMissingExternalName = 4012;
- nVirtualMethodNameMustMatchExternal = 4013;
- nInvalidVariableModifier = 4014;
- nNoArgumentsAllowedForExternalObjectConstructor = 4015;
- nNewInstanceFunctionMustBeVirtual = 4016;
- nNewInstanceFunctionMustHaveTwoParameters = 4017;
- nNewInstanceFunctionMustNotHaveOverloads = 4018;
- nBracketAccessorOfExternalClassMustHaveOneParameter = 4019;
- // resourcestring patterns of messages
- resourcestring
- sPasElementNotSupported = 'Pascal element not supported: %s';
- sIdentifierNotFound = 'Identifier not found "%s"';
- sUnaryOpcodeNotSupported = 'Unary OpCode not yet supported "%s"';
- sBinaryOpcodeNotSupported = 'Binary OpCode not yet supported "%s"';
- sInvalidNumber = 'Invalid number "%s"';
- sInitializedArraysNotSupported = 'Initialized array variables not yet supported';
- sMemberExprMustBeIdentifier = 'Member expression must be an identifier';
- sCantWriteSetLiteral = 'Cannot write set literal';
- sVariableIdentifierExpected = 'Variable identifier expected';
- sExpectedXButFoundY = 'Expected %s, but found %s';
- sInvalidFunctionReference = 'Invalid function reference';
- sMissingExternalName = 'Missing external name';
- sVirtualMethodNameMustMatchExternal = 'Virtual method name must match external';
- sInvalidVariableModifier = 'Invalid variable modifier "%s"';
- sNoArgumentsAllowedForExternalObjectConstructor = 'no arguments allowed for external object constructor';
- sNewInstanceFunctionMustBeVirtual = 'NewInstance function must be virtual';
- sNewInstanceFunctionMustHaveTwoParameters = 'NewInstance function must have two parameters';
- sNewInstanceFunctionMustNotHaveOverloads = 'NewInstance function must not have overloads';
- sBracketAccessorOfExternalClassMustHaveOneParameter = 'Bracket accessor of external class must have one parameter';
- const
- ExtClassBracketAccessor = '[]'; // external name '[]' marks the array param getter/setter
- type
- TPas2JSBuiltInName = (
- pbifnArray_Concat,
- pbifnArray_Copy,
- pbifnArray_Length,
- pbifnArray_NewMultiDim,
- pbifnArray_SetLength,
- pbifnAs,
- pbifnAsExt,
- pbifnClassInstanceFree,
- pbifnClassInstanceNew,
- pbifnCreateClass,
- pbifnCreateClassExt,
- pbifnGetChar,
- pbifnGetNumber,
- pbifnGetObject,
- pbifnIs,
- pbifnIsExt,
- pbifnProcType_Create,
- pbifnProcType_Equal,
- pbifnProgramMain,
- pbifnRecordEqual,
- pbifnSetCharAt,
- pbifnSet_Clone,
- pbifnSet_Create,
- pbifnSet_Difference,
- pbifnSet_Equal,
- pbifnSet_Exclude,
- pbifnSet_GreaterEqual,
- pbifnSet_Include,
- pbifnSet_Intersect,
- pbifnSet_LowerEqual,
- pbifnSet_NotEqual,
- pbifnSet_Reference,
- pbifnSet_SymDiffSet,
- pbifnSet_Union,
- pbifnSpaceLeft,
- pbifnUnitInit,
- pbivnExceptObject,
- pbivnImplementation,
- pbivnLoopEnd,
- pbivnModules,
- pbivnPtrClass,
- pbivnRTL,
- pbivnWith
- );
- const
- Pas2JSBuiltInNames: array[TPas2JSBuiltInName] of string = (
- 'arrayConcat', // rtl.arrayConcat
- 'arrayCopy', // rtl.arrayCopy
- 'length', // rtl.length
- 'arrayNewMultiDim', // rtl.arrayNewMultiDim
- 'arraySetLength', // rtl.arraySetLength
- 'as', // rtl.as
- 'asExt', // rtl.asExt
- '$destroy',
- '$create',
- 'createClass', // rtl.createClass
- 'createClassExt', // rtl.createClassExt
- 'getChar', // rtl.getChar
- 'getNumber', // rtl.getNumber
- 'getObject', // rtl.getObject
- 'is', // rtl.is
- 'isExt', // rtl.isExt
- 'createCallback', // rtl.createCallback
- 'eqCallback', // rtl.eqCallback
- '$main',
- '$equal',
- 'setCharAt', // rtl.setCharAt
- 'cloneSet', // rtl.cloneSet
- 'createSet', // rtl.createSet [...]
- 'diffSet', // rtl.diffSet -
- 'eqSet', // rtl.eqSet =
- 'excludeSet', // rtl.excludeSet
- 'geSet', // rtl.geSet superset >=
- 'includeSet', // rtl.includeSet
- 'intersectSet', // rtl.intersectSet *
- 'leSet', // rtl.leSet subset <=
- 'neSet', // rtl.neSet <>
- 'refSet', // rtl.refSet
- 'symDiffSet', // rtl.symDiffSet >< (symmetrical difference)
- 'unionSet', // rtl.unionSet +
- 'spaceLeft', // rtl.spaceLeft
- '$init',
- '$e',
- '$impl',
- '$loopend',
- 'pas',
- '$class',
- 'rtl',
- '$with'
- );
- JSReservedWords: array[0..106] of string = (
- // keep sorted, first uppercase, then lowercase !
- 'Array',
- 'ArrayBuffer',
- 'Boolean',
- 'DataView',
- 'Date',
- 'Error',
- 'EvalError',
- 'Float32Array',
- 'Float64Array',
- 'Generator',
- 'GeneratorFunction',
- 'Infinity',
- 'Int16Array',
- 'Int32Array',
- 'Int8Array',
- 'InternalError',
- 'JSON',
- 'Map',
- 'Math',
- 'NaN',
- 'Number',
- 'Object',
- 'Promise',
- 'Proxy',
- 'RangeError',
- 'ReferenceError',
- 'Reflect',
- 'RegExp',
- 'Set',
- 'String',
- 'Symbol',
- 'SyntaxError',
- 'TypeError',
- 'URIError',
- 'Uint16Array',
- 'Uint32Array',
- 'Uint8Array',
- 'Uint8ClampedArray',
- 'WeakMap',
- 'WeakSet',
- '__extends',
- '_super',
- 'anonymous',
- 'apply',
- 'arguments',
- 'array',
- 'await',
- 'bind',
- 'break',
- 'call',
- 'case',
- 'catch',
- 'class',
- 'constructor',
- 'continue',
- 'decodeURI',
- 'decodeURIComponent',
- 'default',
- 'delete',
- 'do',
- 'each',
- 'else',
- 'encodeURI',
- 'encodeURIComponent',
- 'enum',
- 'escape',
- 'eval',
- 'export',
- 'extends',
- 'false',
- 'for',
- 'function',
- 'getPrototypeOf',
- 'if',
- 'implements',
- 'import',
- 'in',
- 'instanceof',
- 'interface',
- 'isFinite',
- 'isNaN',
- 'isPrototypeOf',
- 'let',
- 'new',
- 'null',
- 'package',
- 'parseFloat',
- 'parseInt',
- 'private',
- 'protected',
- 'prototype',
- 'public',
- 'return',
- 'static',
- 'super',
- 'switch',
- 'this',
- 'throw',
- 'true',
- 'try',
- 'undefined',
- 'unescape',
- 'uneval',
- 'var',
- 'while',
- 'with',
- 'yield'
- );
- const
- ClassVarModifiersType = [vmClass,vmStatic];
- LowJSInteger = -$10000000000000;
- HighJSInteger = $fffffffffffff;
- LowJSBoolean = false;
- HighJSBoolean = true;
- Type
- { EPas2JS }
- EPas2JS = Class(Exception)
- public
- PasElement: TPasElement;
- MsgNumber: integer;
- Args: TMessageArgs;
- Id: int64;
- MsgType: TMessageType;
- end;
- //------------------------------------------------------------------------------
- // Pas2js built-in types
- type
- TPas2jsBaseType = (
- pbtNone,
- pbtJSValue
- );
- TPas2jsBaseTypes = set of TPas2jsBaseType;
- const
- Pas2jsBaseTypeNames: array[TPas2jsBaseType] of string = (
- 'None',
- 'JSValue'
- );
- btAllJSValueSrcTypes = [btNil,btUntyped]+btAllInteger
- +btAllStringAndChars+btAllFloats+btAllBooleans;
- btAllJSValueTypeCastTo = btAllInteger
- +btAllStringAndChars+btAllFloats+btAllBooleans;
- //------------------------------------------------------------------------------
- // Element CustomData
- type
- { TPas2JsElementData }
- TPas2JsElementData = Class(TPasElementBase)
- private
- FElement: TPasElement;
- procedure SetElement(const AValue: TPasElement);
- public
- Owner: TObject; // e.g. a TPasToJSConverter
- Next: TPas2JsElementData; // TPasToJSConverter uses this for its memory chain
- constructor Create; virtual;
- destructor Destroy; override;
- property Element: TPasElement read FElement write SetElement; // can be TPasElement
- end;
- TPas2JsElementDataClass = class of TPas2JsElementData;
- { TP2JConstExprData - CustomData of a const TPasExpr }
- TP2JConstExprData = Class(TPas2JsElementData)
- public
- // Element is TPasExpr
- Value: TJSValue;
- destructor Destroy; override;
- end;
- TPas2JSClassScope = class(TPasClassScope)
- public
- NewInstanceFunction: TPasClassFunction;
- end;
- { TPas2JSWithExprScope }
- TPas2JSWithExprScope = class(TPasWithExprScope)
- public
- WithVarName: string;
- end;
- { TResElDataPas2JSBaseType - CustomData for compiler built-in types (TPasUnresolvedSymbolRef), e.g. jsvalue }
- TResElDataPas2JSBaseType = class(TResElDataBaseType)
- public
- JSBaseType: TPas2jsBaseType;
- end;
- //------------------------------------------------------------------------------
- // TPas2JSResolver
- const
- DefaultPasResolverOptions = [
- proFixCaseOfOverrides,
- proClassPropertyNonStatic,
- proPropertyAsVarParam,
- proClassOfIs,
- proExtClassInstanceNoTypeMembers,
- proOpenAsDynArrays,
- proProcTypeWithoutIsNested
- ];
- type
- TPas2JSResolver = class(TPasResolver)
- private
- FJSBaseTypes: array[TPas2jsBaseType] of TPasUnresolvedSymbolRef;
- FExternalNames: TFPHashList; // list of list of TPasIdentifier
- FFirstElementData, FLastElementData: TPas2JsElementData;
- function GetJSBaseTypes(aBaseType: TPas2jsBaseType): TPasUnresolvedSymbolRef; inline;
- procedure InternalAdd(Item: TPasIdentifier);
- procedure OnClearHashItem(Item, Dummy: pointer);
- protected
- FOverloadScopes: TFPList; // list of TPasIdentifierScope
- function GetOverloadIndex(Identifier: TPasIdentifier;
- StopAt: TPasElement): integer;
- function GetOverloadIndex(El: TPasElement): integer;
- function RenameOverload(El: TPasElement): boolean;
- procedure RenameOverloadsInSection(aSection: TPasSection);
- procedure RenameOverloads(DeclEl: TPasElement; Declarations: TFPList);
- procedure RenameSubOverloads(Declarations: TFPList);
- procedure PushOverloadScope(Scope: TPasIdentifierScope);
- procedure PopOverloadScope;
- procedure ResolveImplAsm(El: TPasImplAsmStatement); override;
- procedure FinishModule(CurModule: TPasModule); override;
- procedure FinishClassType(El: TPasClassType); override;
- procedure FinishVariable(El: TPasVariable); override;
- procedure FinishProcedureType(El: TPasProcedureType); override;
- procedure FinishPropertyOfClass(PropEl: TPasProperty); override;
- procedure CheckNewInstanceFunction(ClassScope: TPas2JSClassScope); virtual;
- function AddExternalName(const aName: string; El: TPasElement): TPasIdentifier; virtual;
- function FindExternalName(const aName: String): TPasIdentifier; virtual;
- procedure AddExternalPath(aName: string; El: TPasElement);
- procedure ClearElementData; virtual;
- protected
- // additional base types
- function AddJSBaseType(const aName: string; Typ: TPas2jsBaseType): TResElDataPas2JSBaseType;
- function IsJSBaseType(TypeEl: TPasType; Typ: TPas2jsBaseType): boolean;
- function IsJSBaseType(const TypeResolved: TPasResolverResult;
- Typ: TPas2jsBaseType; HasValue: boolean = false): boolean;
- function CheckAssignCompatibilityCustom(const LHS,
- RHS: TPasResolverResult; ErrorEl: TPasElement;
- RaiseOnIncompatible: boolean; var Handled: boolean): integer; override;
- function CheckTypeCastClassInstanceToClass(const FromClassRes,
- ToClassRes: TPasResolverResult; ErrorEl: TPasElement): integer; override;
- function CheckEqualCompatibilityCustomType(const LHS,
- RHS: TPasResolverResult; ErrorEl: TPasElement;
- RaiseOnIncompatible: boolean): integer; override;
- function ResolveBracketOperatorClass(Params: TParamsExpr;
- const ResolvedValue: TPasResolverResult; ClassScope: TPasClassScope;
- Access: TResolvedRefAccess): boolean; override;
- procedure ComputeArrayParams_Class(Params: TParamsExpr; var
- ResolvedEl: TPasResolverResult; ClassScope: TPasClassScope;
- Flags: TPasResolverComputeFlags; StartEl: TPasElement); override;
- public
- constructor Create;
- destructor Destroy; override;
- // base types
- procedure AddObjFPCBuiltInIdentifiers(
- const TheBaseTypes: TResolveBaseTypes=btAllStandardTypes;
- const TheBaseProcs: TResolverBuiltInProcs=bfAllStandardProcs); override;
- function CheckTypeCastRes(const FromResolved,
- ToResolved: TPasResolverResult; ErrorEl: TPasElement;
- RaiseOnError: boolean): integer; override;
- property JSBaseTypes[aBaseType: TPas2jsBaseType]: TPasUnresolvedSymbolRef read GetJSBaseTypes;
- // compute literals and constants
- function ExtractPasStringLiteral(El: TPasElement; const S: String): TJSString; virtual;
- function ComputeConst(Expr: TPasExpr; StoreCustomData: boolean): TJSValue; virtual;
- function ComputeConstString(Expr: TPasExpr; StoreCustomData, NotEmpty: boolean): String; virtual;
- function IsExternalBracketAccessor(El: TPasElement): boolean;
- // CustomData
- function GetElementData(El: TPasElementBase;
- DataClass: TPas2JsElementDataClass): TPas2JsElementData; virtual;
- procedure AddElementData(Data: TPas2JsElementData); virtual;
- function CreateElementData(DataClass: TPas2JsElementDataClass;
- El: TPasElement): TPas2JsElementData; virtual;
- end;
- //------------------------------------------------------------------------------
- // TConvertContext
- type
- TCtxJSElementKind = (
- cjkRoot,
- cjkObject,
- cjkFunction,
- cjkArray,
- cjkDot);
- TCtxAccess = (
- caRead, // normal read
- caAssign, // needs setter
- caByReference // needs path, getter and setter
- );
- TFunctionContext = Class;
- { TConvertContext }
- TConvertContextClass = Class of TConvertContext;
- TConvertContext = Class(TObject)
- public
- PasElement: TPasElement;
- JSElement: TJSElement;
- Resolver: TPas2JSResolver;
- Parent: TConvertContext;
- Kind: TCtxJSElementKind;
- IsSingleton: boolean;
- Access: TCtxAccess;
- AccessContext: TConvertContext;
- TmpVarCount: integer;
- constructor Create(PasEl: TPasElement; JSEl: TJSElement; aParent: TConvertContext); virtual;
- function GetRootModule: TPasModule;
- function GetThis: TPasElement;
- function GetThisContext: TFunctionContext;
- function GetContextOfType(aType: TConvertContextClass): TConvertContext;
- function CreateLocalIdentifier(const Prefix: string): string;
- function CurrentModeswitches: TModeSwitches;
- function GetSingletonFunc: TFunctionContext;
- end;
- { TRootContext }
- TRootContext = Class(TConvertContext)
- public
- constructor Create(PasEl: TPasElement; JSEl: TJSElement; aParent: TConvertContext); override;
- end;
- { TFunctionContext }
- TFunctionContext = Class(TConvertContext)
- public
- This: TPasElement;
- constructor Create(PasEl: TPasElement; JSEl: TJSElement; aParent: TConvertContext); override;
- end;
- { TObjectContext }
- TObjectContext = Class(TConvertContext)
- public
- constructor Create(PasEl: TPasElement; JSEl: TJSElement; aParent: TConvertContext); override;
- end;
- { TInterfaceContext }
- TInterfaceContext = Class(TFunctionContext)
- public
- constructor Create(PasEl: TPasElement; JSEl: TJSElement; aParent: TConvertContext); override;
- end;
- { TDotContext - used for converting eopSubIdent }
- TDotContext = Class(TConvertContext)
- public
- LeftResolved: TPasResolverResult;
- constructor Create(PasEl: TPasElement; JSEl: TJSElement; aParent: TConvertContext); override;
- end;
- { TAssignContext - used for left side of an assign statement }
- TAssignContext = Class(TConvertContext)
- public
- // set when creating:
- LeftResolved: TPasResolverResult;
- RightResolved: TPasResolverResult;
- RightSide: TJSElement;
- // created by ConvertElement:
- PropertyEl: TPasProperty;
- Setter: TPasElement;
- Call: TJSCallExpression;
- constructor Create(PasEl: TPasElement; JSEl: TJSElement; aParent: TConvertContext); override;
- end;
- { TParamContext }
- TParamContext = Class(TConvertContext)
- public
- // set when creating:
- Arg: TPasArgument;
- Expr: TPasExpr;
- ResolvedExpr: TPasResolverResult;
- // created by ConvertElement:
- Getter: TJSElement;
- Setter: TJSElement;
- ReusingReference: boolean; // true = result is a reference, do not create another
- constructor Create(PasEl: TPasElement; JSEl: TJSElement; aParent: TConvertContext); override;
- end;
- //------------------------------------------------------------------------------
- // TPasToJSConverter
- type
- TPasToJsConverterOption = (
- coLowerCase, // lowercase all identifiers, except conflicts with JS reserved words
- coSwitchStatement, // convert case-of into switch instead of if-then-else
- coEnumNumbers, // use enum numbers instead of names
- coUseStrict // insert 'use strict'
- );
- TPasToJsConverterOptions = set of TPasToJsConverterOption;
- TPas2JSIsElementUsedEvent = function(Sender: TObject; El: TPasElement): boolean of object;
- TPasToJsPlatform = (
- PlatformBrowser,
- PlatformNodeJS
- );
- TPasToJsPlatforms = set of TPasToJsPlatform;
- const
- PasToJsPlatformNames: array[TPasToJsPlatform] of string = (
- 'Browser',
- 'NodeJS'
- );
- type
- TPasToJsProcessor = (
- ProcessorECMAScript5,
- ProcessorECMAScript6
- );
- TPasToJsProcessors = set of TPasToJsProcessor;
- const
- PasToJsProcessorNames: array[TPasToJsProcessor] of string = (
- 'ECMAScript5',
- 'ECMAScript6'
- );
- type
- TJSReservedWordList = array of String;
- TRefPathKind = (
- rpkPath, // e.g. "TObject"
- rpkPathWithDot, // e.g. "TObject."
- rpkPathAndName // e.g. "TObject.ClassName"
- );
- { TPasToJSConverter }
- TPasToJSConverter = Class(TObject)
- private
- // inline at top, only functions declared after the inline implementation actually use it
- function GetUseEnumNumbers: boolean; inline;
- function GetUseLowerCase: boolean; inline;
- function GetUseSwitchStatement: boolean; inline;
- private
- type
- TForLoopFindData = record
- ForLoop: TPasImplForLoop;
- LoopVar: TPasElement;
- FoundLoop: boolean;
- LoopVarWrite: boolean; // true if first acces of LoopVar after loop is a write
- LoopVarRead: boolean; // true if first acces of LoopVar after loop is a read
- end;
- PForLoopFindData = ^TForLoopFindData;
- procedure ForLoop_OnProcBodyElement(El: TPasElement; arg: pointer);
- private
- type
- TTryExceptFindData = record
- HasRaiseWithoutObject: boolean;
- end;
- PTryExceptFindData = ^TTryExceptFindData;
- procedure TryExcept_OnElement(El: TPasElement; arg: pointer);
- private
- FBuiltInNames: array[TPas2JSBuiltInName] of string;
- FOnIsElementUsed: TPas2JSIsElementUsedEvent;
- FOptions: TPasToJsConverterOptions;
- FPreservedWords: TJSReservedWordList; // sorted with CompareStr
- FTargetPlatform: TPasToJsPlatform;
- FTargetProcessor: TPasToJsProcessor;
- Function CreateBuiltInIdentifierExpr(AName: string): TJSPrimaryExpressionIdent;
- Function CreateDeclNameExpression(El: TPasElement; const Name: string;
- AContext: TConvertContext): TJSPrimaryExpressionIdent;
- Function CreateIdentifierExpr(AName: string; El: TPasElement; AContext: TConvertContext): TJSPrimaryExpressionIdent;
- Function CreateSwitchStatement(El: TPasImplCaseOf; AContext: TConvertContext): TJSElement;
- Function CreateTypeDecl(El: TPasType; AContext: TConvertContext): TJSElement;
- Function CreateVarDecl(El: TPasVariable; AContext: TConvertContext): TJSElement;
- Procedure AddToSourceElements(Src: TJSSourceElements; El: TJSElement);
- procedure RemoveFromSourceElements(Src: TJSSourceElements;
- El: TJSElement);
- function GetBuildInNames(bin: TPas2JSBuiltInName): string;
- procedure SetBuildInNames(bin: TPas2JSBuiltInName; const AValue: string);
- procedure SetPreservedWords(const AValue: TJSReservedWordList);
- procedure SetUseEnumNumbers(const AValue: boolean);
- procedure SetUseLowerCase(const AValue: boolean);
- procedure SetUseSwitchStatement(const AValue: boolean);
- protected
- // Error functions
- Procedure DoError(Id: int64; Const Msg : String);
- Procedure DoError(Id: int64; Const Msg : String; Const Args : Array of Const);
- Procedure DoError(Id: int64; MsgNumber: integer; const MsgPattern: string; Const Args : Array of Const; El: TPasElement);
- procedure RaiseNotSupported(El: TPasElement; AContext: TConvertContext; Id: int64; const Msg: string = '');
- procedure RaiseIdentifierNotFound(Identifier: string; El: TPasElement; Id: int64);
- procedure RaiseInconsistency(Id: int64);
- // Computation, value conversions
- Function GetExpressionValueType(El: TPasExpr; AContext: TConvertContext ): TJSType; virtual;
- Function GetPasIdentValueType(AName: String; AContext: TConvertContext): TJSType; virtual;
- Function ComputeConstString(Expr: TPasExpr; AContext: TConvertContext; NotEmpty: boolean): String; virtual;
- Function IsExternalClassConstructor(El: TPasElement): boolean;
- // Name mangling
- Function TransformVariableName(El: TPasElement; Const AName: String; AContext : TConvertContext): String; virtual;
- Function TransformVariableName(El: TPasElement; AContext : TConvertContext) : String; virtual;
- Function TransformModuleName(El: TPasModule; AContext : TConvertContext) : String; virtual;
- Function IsPreservedWord(const aName: string): boolean; virtual;
- // Never create an element manually, always use the below functions
- Function IsElementUsed(El: TPasElement): boolean; virtual;
- Function CreateElement(C: TJSElementClass; Src: TPasElement): TJSElement; virtual;
- Function CreateFreeOrNewInstanceExpr(Ref: TResolvedReference;
- AContext : TConvertContext): TJSCallExpression; virtual;
- Function CreateFunction(El: TPasElement; WithBody: boolean = true): TJSFunctionDeclarationStatement;
- Procedure CreateProcedureCall(var Call: TJSCallExpression; Args: TParamsExpr;
- TargetProc: TPasProcedureType; AContext: TConvertContext); virtual;
- Procedure CreateProcedureCallArgs(Elements: TJSArrayLiteralElements;
- Args: TParamsExpr; TargetProc: TPasProcedureType; AContext: TConvertContext); virtual;
- Function CreateProcCallArg(El: TPasExpr; TargetArg: TPasArgument;
- AContext: TConvertContext): TJSElement; virtual;
- Function CreateProcCallArgRef(El: TPasExpr; ResolvedEl: TPasResolverResult;
- TargetArg: TPasArgument; AContext: TConvertContext): TJSElement; virtual;
- Function CreateUnary(Members: array of string; E: TJSElement): TJSUnary;
- Function CreateMemberExpression(Members: array of string): TJSDotMemberExpression;
- Function CreateCallExpression(El: TPasElement): TJSCallExpression;
- Function CreateUsesList(UsesSection: TPasSection; AContext : TConvertContext): TJSArrayLiteral;
- Procedure AddToStatementList(var First, Last: TJSStatementList;
- Add: TJSElement; Src: TPasElement);
- Function CreateValInit(PasType: TPasType; Expr: TPasElement; El: TPasElement; AContext: TConvertContext): TJSElement; virtual;
- Function CreateVarInit(El: TPasVariable; AContext: TConvertContext): TJSElement; virtual;
- Function CreateLiteralNumber(El: TPasElement; const n: TJSNumber): TJSLiteral; virtual;
- Function CreateLiteralString(El: TPasElement; const s: string): TJSLiteral; virtual;
- Function CreateLiteralJSString(El: TPasElement; const s: TJSString): TJSLiteral; virtual;
- Function CreateLiteralBoolean(El: TPasElement; b: boolean): TJSLiteral; virtual;
- Function CreateLiteralNull(El: TPasElement): TJSLiteral; virtual;
- Function CreateLiteralUndefined(El: TPasElement): TJSLiteral; virtual;
- Function CreateRecordInit(aRecord: TPasRecordType; Expr: TPasElement;
- El: TPasElement; AContext: TConvertContext): TJSElement; virtual;
- Function CreateArrayInit(ArrayType: TPasArrayType; Expr: TPasElement;
- El: TPasElement; AContext: TConvertContext): TJSElement; virtual;
- Function CreateCmpArrayWithNil(El: TPasElement; JSArray: TJSElement; OpCode: TExprOpCode): TJSElement; virtual;
- Function CreateReferencePath(El: TPasElement; AContext : TConvertContext;
- Kind: TRefPathKind; Full: boolean = false; Ref: TResolvedReference = nil): string; virtual;
- Function CreateReferencePathExpr(El: TPasElement; AContext : TConvertContext; Full: boolean = false; Ref: TResolvedReference = nil): TJSPrimaryExpressionIdent; virtual;
- Function CreateImplementationSection(El: TPasModule; Src: TJSSourceElements; AContext: TConvertContext): TJSElement;
- Procedure CreateInitSection(El: TPasModule; Src: TJSSourceElements; AContext: TConvertContext);
- Function CreateDotExpression(aParent: TPasElement; Left, Right: TJSElement): TJSElement; virtual;
- Function CreateReferencedSet(El: TPasElement; SetExpr: TJSElement): TJSElement; virtual;
- Function CreateCloneRecord(El: TPasElement; ResolvedEl: TPasResolverResult;
- RecordExpr: TJSElement; AContext: TConvertContext): TJSElement; virtual;
- Function CreateCallback(El: TPasElement; ResolvedEl: TPasResolverResult;
- AContext: TConvertContext): TJSElement; virtual;
- Function CreateAssignStatement(LeftEl: TPasElement; AssignContext: TAssignContext): TJSElement; virtual;
- // Statements
- Function ConvertImplBlockElements(El: TPasImplBlock; AContext: TConvertContext; NilIfEmpty: boolean): TJSElement; virtual;
- Function ConvertBeginEndStatement(El: TPasImplBeginBlock; AContext: TConvertContext; NilIfEmpty: boolean): TJSElement; virtual;
- Function ConvertStatement(El: TPasImplStatement; AContext: TConvertContext ): TJSElement; virtual;
- Function ConvertAssignStatement(El: TPasImplAssign; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertRaiseStatement(El: TPasImplRaise; AContext: TConvertContext ): TJSElement; virtual;
- Function ConvertIfStatement(El: TPasImplIfElse; AContext: TConvertContext ): TJSElement; virtual;
- Function ConvertWhileStatement(El: TPasImplWhileDo; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertRepeatStatement(El: TPasImplRepeatUntil; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertForStatement(El: TPasImplForLoop; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertFinalizationSection(El: TFinalizationSection; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertInitializationSection(El: TInitializationSection; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertSimpleStatement(El: TPasImplSimple; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertWithStatement(El: TPasImplWithDo; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertTryStatement(El: TPasImplTry; AContext: TConvertContext ): TJSElement; virtual;
- Function ConvertExceptOn(El: TPasImplExceptOn; AContext: TConvertContext): TJSElement;
- Function ConvertCaseOfStatement(El: TPasImplCaseOf; AContext: TConvertContext): TJSElement;
- Function ConvertAsmStatement(El: TPasImplAsmStatement; AContext: TConvertContext): TJSElement;
- // Expressions
- Function ConvertArrayValues(El: TArrayValues; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertInheritedExpression(El: TInheritedExpr; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertNilExpr(El: TNilExpr; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertParamsExpression(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertArrayParams(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertFuncParams(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertExternalConstructor(Left: TPasElement;
- Ref: TResolvedReference; ParamsExpr: TParamsExpr;
- AContext : TConvertContext): TJSElement; virtual;
- Function ConvertTypeCastToBaseType(El: TParamsExpr; AContext: TConvertContext; BaseTypeData: TResElDataBaseType): TJSElement; virtual;
- Function ConvertSetLiteral(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertOpenArrayParam(ElType: TPasType; El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertBuiltInLength(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertBuiltInSetLength(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertBuiltInExcludeInclude(El: TParamsExpr; AContext: TConvertContext; IsInclude: boolean): TJSElement; virtual;
- Function ConvertBuiltInContinue(El: TPasExpr; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertBuiltInBreak(El: TPasExpr; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertBuiltInExit(El: TPasExpr; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertBuiltInIncDec(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertBuiltInAssigned(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertBuiltInChr(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertBuiltInOrd(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertBuiltInLow(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertBuiltInHigh(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertBuiltInPred(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertBuiltInSucc(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertBuiltInStrProc(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertBuiltInStrFunc(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertBuiltInStrParam(El: TPasExpr; AContext: TConvertContext; IsStrFunc, IsFirst: boolean): TJSElement; virtual;
- Function ConvertBuiltInConcatArray(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertBuiltInCopyArray(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertBuiltInInsertArray(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertBuiltInDeleteArray(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertRecordValues(El: TRecordValues; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertSelfExpression(El: TSelfExpr; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertBinaryExpression(El: TBinaryExpr; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertBinaryExpressionRes(El: TBinaryExpr; AContext: TConvertContext;
- const LeftResolved, RightResolved: TPasResolverResult; var A,B: TJSElement): TJSElement; virtual;
- Function ConvertSubIdentExpression(El: TBinaryExpr; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertBoolConstExpression(El: TBoolConstExpr; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertPrimitiveExpression(El: TPrimitiveExpr; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertIdentifierExpr(El: TPrimitiveExpr; AContext : TConvertContext): TJSElement; virtual;
- Function ConvertUnaryExpression(El: TUnaryExpr; AContext: TConvertContext): TJSElement; virtual;
- // Convert declarations
- Function ConvertElement(El : TPasElement; AContext: TConvertContext) : TJSElement; virtual;
- Function ConvertProperty(El: TPasProperty; AContext: TConvertContext ): TJSElement; virtual;
- Function ConvertCommand(El: TPasImplCommand; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertCommands(El: TPasImplCommands; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertConst(El: TPasConst; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertDeclarations(El: TPasDeclarations; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertExportSymbol(El: TPasExportSymbol; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertExpression(El: TPasExpr; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertImplBlock(El: TPasImplBlock; AContext: TConvertContext ): TJSElement; virtual;
- Function ConvertLabelMark(El: TPasImplLabelMark; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertLabels(El: TPasLabels; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertModule(El: TPasModule; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertPackage(El: TPasPackage; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertProcedure(El: TPasProcedure; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertResString(El: TPasResString; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertVariable(El: TPasVariable; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertRecordType(El: TPasRecordType; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertClassType(El: TPasClassType; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertClassExternalType(El: TPasClassType; AContext: TConvertContext): TJSElement; virtual;
- Function ConvertEnumType(El: TPasEnumType; AContext: TConvertContext): TJSElement; virtual;
- Public
- Constructor Create;
- destructor Destroy; override;
- Function ConvertPasElement(El: TPasElement; Resolver: TPas2JSResolver) : TJSElement;
- // options
- Property Options: TPasToJsConverterOptions read FOptions write FOptions;
- Property TargetPlatform: TPasToJsPlatform read FTargetPlatform write FTargetPlatform;
- Property TargetProcessor: TPasToJsProcessor read FTargetProcessor write FTargetProcessor;
- Property UseLowerCase: boolean read GetUseLowerCase write SetUseLowerCase default true;
- Property UseSwitchStatement: boolean read GetUseSwitchStatement write SetUseSwitchStatement;// default false, because slower than "if" in many engines
- Property UseEnumNumbers: boolean read GetUseEnumNumbers write SetUseEnumNumbers; // default false
- Property OnIsElementUsed: TPas2JSIsElementUsedEvent read FOnIsElementUsed write FOnIsElementUsed;
- Property PreservedWords: TJSReservedWordList read FPreservedWords write SetPreservedWords;
- // names
- Property BuildInNames[bin: TPas2JSBuiltInName]: string read GetBuildInNames write SetBuildInNames;
- end;
- var
- JSTypeCaptions: array[TJSType] of string = (
- 'undefined',
- 'null',
- 'boolean',
- 'number',
- 'string',
- 'object',
- 'reference',
- 'completion'
- );
- function CodePointToJSString(u: cardinal): TJSString;
- function PosLast(c: char; const s: string): integer;
- implementation
- const
- TempRefObjGetterName = 'get';
- TempRefObjSetterName = 'set';
- TempRefObjSetterArgName = 'v';
- function CodePointToJSString(u: cardinal): TJSString;
- begin
- if u < $10000 then
- // Note: codepoints $D800 - $DFFF are reserved
- Result:=WideChar(u)
- else
- Result:=WideChar($D800+((u - $10000) shr 10))+WideChar($DC00+((u - $10000) and $3ff));
- end;
- function PosLast(c: char; const s: string): integer;
- begin
- Result:=length(s);
- while (Result>0) and (s[Result]<>c) do dec(Result);
- end;
- { TPas2JSResolver }
- // inline
- function TPas2JSResolver.GetJSBaseTypes(aBaseType: TPas2jsBaseType
- ): TPasUnresolvedSymbolRef;
- begin
- Result:=TPasUnresolvedSymbolRef(FJSBaseTypes[aBaseType]);
- end;
- procedure TPas2JSResolver.InternalAdd(Item: TPasIdentifier);
- var
- Index: Integer;
- OldItem: TPasIdentifier;
- aName: ShortString;
- begin
- aName:=Item.Identifier;
- Index:=FExternalNames.FindIndexOf(aName);
- {$IFDEF VerbosePasResolver}
- if Item.Owner<>nil then
- raise Exception.Create('20170322235419');
- Item.Owner:=Self;
- {$ENDIF}
- //writeln(' Index=',Index);
- if Index>=0 then
- begin
- // insert LIFO - last in, first out
- OldItem:=TPasIdentifier(FExternalNames.List^[Index].Data);
- {$IFDEF VerbosePasResolver}
- if OldItem.Identifier<>aName then
- raise Exception.Create('20170322235429');
- {$ENDIF}
- Item.NextSameIdentifier:=OldItem;
- FExternalNames.List^[Index].Data:=Item;
- end
- else
- begin
- FExternalNames.Add(aName, Item);
- {$IFDEF VerbosePasResolver}
- if FindExternalName(Item.Identifier)<>Item then
- raise Exception.Create('20170322235433');
- {$ENDIF}
- end;
- end;
- procedure TPas2JSResolver.OnClearHashItem(Item, Dummy: pointer);
- var
- PasIdentifier: TPasIdentifier absolute Item;
- Ident: TPasIdentifier;
- begin
- if Dummy=nil then ;
- //writeln('TPas2JSResolver.OnClearItem ',PasIdentifier.Identifier+':'+PasIdentifier.ClassName);
- while PasIdentifier<>nil do
- begin
- Ident:=PasIdentifier;
- PasIdentifier:=PasIdentifier.NextSameIdentifier;
- Ident.Free;
- end;
- end;
- function TPas2JSResolver.GetOverloadIndex(Identifier: TPasIdentifier;
- StopAt: TPasElement): integer;
- // if not found return number of overloads
- // if found return index in overloads
- var
- El: TPasElement;
- ProcScope: TPasProcedureScope;
- C: TClass;
- begin
- Result:=0;
- // iterate from last added to first added
- while Identifier<>nil do
- begin
- El:=Identifier.Element;
- Identifier:=Identifier.NextSameIdentifier;
- if El=StopAt then
- begin
- Result:=0;
- continue;
- end;
- C:=El.ClassType;
- if C=TPasClassType then
- begin
- if TPasClassType(El).IsForward then
- continue;
- end
- else if C.InheritsFrom(TPasProcedure) then
- begin
- if TPasProcedure(El).IsOverride then
- continue;
- // Note: external proc pollute the name space
- ProcScope:=TPasProcedureScope(El.CustomData);
- if ProcScope.DeclarationProc<>nil then
- // implementation proc -> only count the header -> skip
- continue;
- end;
- inc(Result);
- end;
- end;
- function TPas2JSResolver.GetOverloadIndex(El: TPasElement): integer;
- var
- i: Integer;
- Identifier: TPasIdentifier;
- begin
- Result:=0;
- for i:=FOverloadScopes.Count-1 downto 0 do
- begin
- // find last added
- Identifier:=TPasIdentifierScope(FOverloadScopes[i]).FindLocalIdentifier(El.Name);
- // add count or index
- inc(Result,GetOverloadIndex(Identifier,El));
- end;
- // find in external names
- Identifier:=FindExternalName(El.Name);
- // add count or index
- inc(Result,GetOverloadIndex(Identifier,El));
- end;
- function TPas2JSResolver.RenameOverload(El: TPasElement): boolean;
- var
- OverloadIndex: Integer;
- NewName: String;
- begin
- // => count overloads in this section
- OverloadIndex:=GetOverloadIndex(El);
- if OverloadIndex=0 then
- exit(false); // there is no overload
- if (El.ClassType=TPasClassFunction)
- and (TPas2JSClassScope(TPasClassType(El.Parent).CustomData).NewInstanceFunction=El) then
- RaiseMsg(20170324234324,nNewInstanceFunctionMustNotHaveOverloads,
- sNewInstanceFunctionMustNotHaveOverloads,[],El);
- NewName:=El.Name+'$'+IntToStr(OverloadIndex);
- {$IFDEF VerbosePas2JS}
- writeln('TPas2JSResolver.RenameOverload "',El.Name,'" has overload. NewName="',NewName,'"');
- {$ENDIF}
- El.Name:=NewName;
- Result:=true;
- end;
- procedure TPas2JSResolver.RenameOverloadsInSection(aSection: TPasSection);
- var
- ImplSection: TImplementationSection;
- SectionClass: TClass;
- begin
- if aSection=nil then exit;
- PushOverloadScope(aSection.CustomData as TPasIdentifierScope);
- RenameOverloads(aSection,aSection.Declarations);
- SectionClass:=aSection.ClassType;
- if SectionClass=TInterfaceSection then
- begin
- // unit interface
- // first rename all overloads in interface and implementation
- ImplSection:=(aSection.Parent as TPasModule).ImplementationSection;
- if ImplSection<>nil then
- begin
- PushOverloadScope(ImplSection.CustomData as TPasIdentifierScope);
- RenameOverloads(ImplSection,ImplSection.Declarations);
- end;
- // and then rename all nested overloads (e.g. methods)
- // Important: nested overloads must check both interface and implementation
- RenameSubOverloads(aSection.Declarations);
- if ImplSection<>nil then
- begin
- RenameSubOverloads(ImplSection.Declarations);
- PopOverloadScope;
- end;
- end
- else
- begin
- // program or library
- RenameSubOverloads(aSection.Declarations);
- end;
- PopOverloadScope;
- {$IFDEF VerbosePas2JS}
- writeln('TPas2JSResolver.RenameOverloadsInSection END ',GetObjName(aSection));
- {$ENDIF}
- end;
- procedure TPas2JSResolver.RenameOverloads(DeclEl: TPasElement;
- Declarations: TFPList);
- var
- i: Integer;
- El: TPasElement;
- Proc: TPasProcedure;
- ProcScope: TPasProcedureScope;
- begin
- //IsExternalClass:=(DeclEl is TPasClassType) and (TPasClassType(DeclEl).IsExternal);
- if DeclEl=nil then;
- for i:=0 to Declarations.Count-1 do
- begin
- El:=TPasElement(Declarations[i]);
- if (El is TPasProcedure) then
- begin
- Proc:=TPasProcedure(El);
- if Proc.IsOverride or Proc.IsExternal then
- continue;
- // Note: Pascal names of external procs are not in the JS, so no need to rename them
- ProcScope:=Proc.CustomData as TPasProcedureScope;
- //writeln('TPas2JSResolver.RenameOverloads Proc=',Proc.Name,' DeclarationProc=',GetObjName(ProcScope.DeclarationProc),' ImplProc=',GetObjName(ProcScope.ImplProc),' ClassScope=',GetObjName(ProcScope.ClassScope));
- if ProcScope.DeclarationProc<>nil then
- begin
- if ProcScope.ImplProc<>nil then
- RaiseInternalError(20170221110853);
- // proc implementation (not forward) -> skip
- continue;
- end;
- // proc declaration (header, not body)
- if RenameOverload(Proc) then
- if ProcScope.ImplProc<>nil then
- ProcScope.ImplProc.Name:=Proc.Name;
- end;
- end;
- {$IFDEF VerbosePas2JS}
- writeln('TPas2JSResolver.RenameOverloads END ',GetObjName(DeclEl));
- {$ENDIF}
- end;
- procedure TPas2JSResolver.RenameSubOverloads(Declarations: TFPList);
- var
- i, OldScopeCount: Integer;
- El: TPasElement;
- Proc, ImplProc: TPasProcedure;
- ProcScope: TPasProcedureScope;
- ClassScope, aScope: TPasClassScope;
- ClassEl: TPasClassType;
- C: TClass;
- begin
- for i:=0 to Declarations.Count-1 do
- begin
- El:=TPasElement(Declarations[i]);
- C:=El.ClassType;
- if C.InheritsFrom(TPasProcedure) then
- begin
- Proc:=TPasProcedure(El);
- if Proc.IsAbstract or Proc.IsExternal then continue;
- ProcScope:=Proc.CustomData as TPasProcedureScope;
- {$IFDEF VerbosePas2JS}
- writeln('TPas2JSResolver.RenameSubOverloads Proc=',Proc.Name,' DeclarationProc=',GetObjName(ProcScope.DeclarationProc),' ImplProc=',GetObjName(ProcScope.ImplProc),' ClassScope=',GetObjName(ProcScope.ClassScope));
- {$ENDIF}
- if ProcScope.DeclarationProc<>nil then
- // proc implementation (not forward) -> skip
- continue;
- ImplProc:=Proc;
- if ProcScope.ImplProc<>nil then
- begin
- // this proc has a separate implementation
- // -> switch to implementation
- ImplProc:=ProcScope.ImplProc;
- ProcScope:=ImplProc.CustomData as TPasProcedureScope;
- end;
- PushOverloadScope(ProcScope);
- // first rename all overloads on this level
- RenameOverloads(ImplProc.Body,ImplProc.Body.Declarations);
- // then process nested procedures
- RenameSubOverloads(ImplProc.Body.Declarations);
- PopOverloadScope;
- end
- else if C=TPasClassType then
- begin
- ClassEl:=TPasClassType(El);
- if ClassEl.IsForward then continue;
- ClassScope:=El.CustomData as TPas2JSClassScope;
- OldScopeCount:=FOverloadScopes.Count;
- // add class and ancestors scopes
- aScope:=ClassScope;
- repeat
- PushOverloadScope(aScope);
- aScope:=aScope.AncestorScope;
- until aScope=nil;
- // first rename all overloads on this level
- RenameOverloads(ClassEl,ClassEl.Members);
- // then process nested procedures
- RenameSubOverloads(ClassEl.Members);
- while FOverloadScopes.Count>OldScopeCount do
- PopOverloadScope;
- end
- else if C=TPasConst then
- RenameOverload(El)
- else if C.InheritsFrom(TPasVariable) and (El.Parent.ClassType=TPasClassType) then
- RenameOverload(El);
- end;
- {$IFDEF VerbosePas2JS}
- writeln('TPas2JSResolver.RenameSubOverloads END');
- {$ENDIF}
- end;
- procedure TPas2JSResolver.PushOverloadScope(Scope: TPasIdentifierScope);
- begin
- FOverloadScopes.Add(Scope);
- end;
- procedure TPas2JSResolver.PopOverloadScope;
- begin
- FOverloadScopes.Delete(FOverloadScopes.Count-1);
- end;
- procedure TPas2JSResolver.ResolveImplAsm(El: TPasImplAsmStatement);
- {type
- TAsmToken = (
- atNone,
- atWord,
- atDot,
- atRoundBracketOpen,
- atRoundBracketClose
- );
- procedure Next;
- begin
- end;}
- var
- Lines: TStrings;
- begin
- Lines:=El.Tokens;
- if Lines=nil then exit;
- end;
- procedure TPas2JSResolver.FinishModule(CurModule: TPasModule);
- var
- ModuleClass: TClass;
- begin
- inherited FinishModule(CurModule);
- FOverloadScopes:=TFPList.Create;
- try
- ModuleClass:=CurModule.ClassType;
- if ModuleClass=TPasModule then
- begin
- RenameOverloadsInSection(CurModule.InterfaceSection);
- // Note: ImplementationSection is child of InterfaceSection
- end
- else if ModuleClass=TPasProgram then
- RenameOverloadsInSection(TPasProgram(CurModule).ProgramSection)
- else if CurModule.ClassType=TPasLibrary then
- RenameOverloadsInSection(TPasLibrary(CurModule).LibrarySection)
- else
- RaiseNotYetImplemented(20170221000032,CurModule);
- finally
- FOverloadScopes.Free;
- end;
- end;
- procedure TPas2JSResolver.FinishClassType(El: TPasClassType);
- begin
- inherited FinishClassType(El);
- if El.IsExternal then
- begin
- if El.ExternalName='' then
- RaiseMsg(20170321151109,nMissingExternalName,sMissingExternalName,[],El);
- AddExternalPath(El.ExternalName,El);
- end;
- end;
- procedure TPas2JSResolver.FinishVariable(El: TPasVariable);
- const
- ClassFieldModifiersAllowed = [vmClass,vmStatic,vmExternal,vmPublic];
- RecordVarModifiersAllowed = [];
- LocalVarModifiersAllowed = [];
- ImplementationVarModifiersAllowed = [];
- SectionVarModifiersAllowed = [vmExternal,vmPublic];
- procedure RaiseVarModifierNotSupported(const Allowed: TVariableModifiers);
- var
- s: String;
- m: TVariableModifier;
- begin
- s:='';
- for m in TVariableModifiers do
- if (m in El.VarModifiers) and not (m in Allowed) then
- begin
- str(m,s);
- RaiseMsg(20170322134418,nInvalidVariableModifier,
- sInvalidVariableModifier,[VariableModifierNames[m]],El);
- end;
- end;
- var
- ExtName: String;
- ParentC: TClass;
- begin
- inherited FinishVariable(El);
- ParentC:=El.Parent.ClassType;
- if (ParentC=TPasClassType) then
- begin
- // class member
- RaiseVarModifierNotSupported(ClassFieldModifiersAllowed);
- if TPasClassType(El.Parent).IsExternal then
- begin
- // external class -> make variable external
- if not (vmExternal in El.VarModifiers) then
- begin
- if (El.ClassType=TPasVariable) or (El.ClassType=TPasConst) then
- begin
- if El.ExportName<>nil then
- RaiseMsg(20170322134321,nInvalidVariableModifier,
- sInvalidVariableModifier,['export name'],El.ExportName);
- El.ExportName:=TPrimitiveExpr.Create(El,pekString,''''+El.Name+'''');
- end;
- Include(El.VarModifiers,vmExternal);
- end;
- end;
- end
- else if ParentC=TPasRecordType then
- // record member
- RaiseVarModifierNotSupported(RecordVarModifiersAllowed)
- else if ParentC=TProcedureBody then
- // local var
- RaiseVarModifierNotSupported(LocalVarModifiersAllowed)
- else if ParentC=TImplementationSection then
- // implementation var
- RaiseVarModifierNotSupported(ImplementationVarModifiersAllowed)
- else if ParentC.InheritsFrom(TPasSection) then
- begin
- // interface/program/library var
- RaiseVarModifierNotSupported(SectionVarModifiersAllowed);
- end
- else
- begin
- {$IFDEF VerbosePas2JS}
- writeln('TPas2JSResolver.FinishVariable ',GetObjName(El),' Parent=',GetObjName(El.Parent));
- {$ENDIF}
- RaiseNotYetImplemented(20170324151259,El);
- end;
- if vmExternal in El.VarModifiers then
- begin
- // compute constant
- if El.LibraryName<>nil then
- RaiseMsg(20170227094227,nPasElementNotSupported,sPasElementNotSupported,
- ['library'],El.ExportName);
- if El.ExportName=nil then
- RaiseMsg(20170227100750,nMissingExternalName,sMissingExternalName,[],El);
- ExtName:=ComputeConstString(El.ExportName,true,true);
- // add external name to FExternalNames
- if (El.Parent is TPasSection)
- or ((El.ClassType=TPasConst) and (El.Parent is TPasProcedure)) then
- AddExternalPath(ExtName,El.ExportName);
- end;
- end;
- procedure TPas2JSResolver.FinishProcedureType(El: TPasProcedureType);
- var
- Proc: TPasProcedure;
- pm: TProcedureModifier;
- ExtName: String;
- C: TClass;
- AClass: TPasClassType;
- ClassScope: TPas2JSClassScope;
- begin
- inherited FinishProcedureType(El);
- if El.Parent is TPasProcedure then
- begin
- Proc:=TPasProcedure(El.Parent);
- // calling convention
- if Proc.CallingConvention<>ccDefault then
- RaiseMsg(20170211214731,nPasElementNotSupported,sPasElementNotSupported,
- [cCallingConventions[Proc.CallingConvention]],Proc);
- for pm in TProcedureModifiers do
- if (pm in Proc.Modifiers)
- and (not (pm in [pmVirtual, pmAbstract, pmOverride,
- pmOverload, pmReintroduce,
- pmAssembler, pmVarargs, pmPublic,
- pmExternal, pmForward])) then
- RaiseNotYetImplemented(20170208142159,El,'modifier '+ModifierNames[pm]);
- // check pmPublic
- if [pmPublic,pmExternal]<=Proc.Modifiers then
- RaiseMsg(20170324150149,nInvalidXModifierY,
- sInvalidXModifierY,[Proc.ElementTypeName,'public, external'],Proc);
- if (Proc.PublicName<>nil) then
- RaiseMsg(20170324150417,nPasElementNotSupported,sPasElementNotSupported,
- ['public name'],Proc.PublicName);
- if (Proc.Parent.ClassType=TPasClassType) then
- begin
- // class member
- AClass:=TPasClassType(Proc.Parent);
- ClassScope:=AClass.CustomData as TPas2JSClassScope;
- if AClass.IsExternal then
- begin
- // external class -> make method external
- if not (pmExternal in Proc.Modifiers) then
- begin
- if Proc.LibrarySymbolName<>nil then
- RaiseMsg(20170322142158,nInvalidXModifierY,
- sInvalidXModifierY,[Proc.ElementTypeName,'symbol name'],Proc.LibrarySymbolName);
- Proc.Modifiers:=Proc.Modifiers+[pmExternal];
- Proc.LibrarySymbolName:=TPrimitiveExpr.Create(El,pekString,''''+Proc.Name+'''');
- end;
- C:=Proc.ClassType;
- if (C=TPasProcedure) or (C=TPasFunction)
- or (C=TPasClassProcedure) or (C=TPasClassFunction) then
- // ok
- else if C=TPasConstructor then
- begin
- if Proc.IsVirtual then
- // constructor of external class can't be overriden -> forbid virtual
- RaiseMsg(20170323100447,nInvalidXModifierY,sInvalidXModifierY,
- [Proc.ElementTypeName,'virtual,external'],Proc);
- if CompareText(Proc.Name,'new')=0 then
- begin
- ExtName:=ComputeConstString(Proc.LibrarySymbolName,true,true);
- if ExtName<>Proc.Name then
- RaiseMsg(20170323083511,nVirtualMethodNameMustMatchExternal,
- sVirtualMethodNameMustMatchExternal,[],Proc.LibrarySymbolName);
- end
- else if El.Args.Count>0 then
- RaiseMsg(20170322164357,nNoArgumentsAllowedForExternalObjectConstructor,
- sNoArgumentsAllowedForExternalObjectConstructor,[],TPasArgument(El.Args[0]));
- if pmVirtual in Proc.Modifiers then
- RaiseMsg(20170322183141,nInvalidXModifierY,sInvalidXModifierY,
- [Proc.ElementTypeName,'virtual'],Proc.ProcType);
- end
- else
- RaiseMsg(20170322163210,nPasElementNotSupported,sPasElementNotSupported,
- [Proc.ElementTypeName],Proc);
- end
- else
- begin
- // Pascal class
- if (ClassScope.NewInstanceFunction=nil)
- and (ClassScope.AncestorScope<>nil)
- and (TPasClassType(ClassScope.AncestorScope.Element).IsExternal)
- and (Proc.ClassType=TPasClassFunction)
- and (Proc.Visibility in [visProtected,visPublic,visPublished])
- and (TPasClassFunction(Proc).FuncType.ResultEl.ResultType=AClass)
- and ([pmOverride,pmExternal]*Proc.Modifiers=[]) then
- begin
- // The first non private class function in a Pascal class descending
- // from an external class
- // -> this is the NewInstance function
- ClassScope.NewInstanceFunction:=TPasClassFunction(Proc);
- CheckNewInstanceFunction(ClassScope);
- end;
- end;
- end;
- if pmExternal in Proc.Modifiers then
- begin
- // external proc
- // external override -> unneeded information, probably a bug
- if Proc.IsOverride then
- RaiseMsg(20170321101715,nInvalidXModifierY,sInvalidXModifierY,
- [Proc.ElementTypeName,'override,external'],Proc);
- if Proc.LibraryExpr<>nil then
- RaiseMsg(20170211220712,nPasElementNotSupported,sPasElementNotSupported,
- ['library'],Proc.LibraryExpr);
- if Proc.LibrarySymbolName=nil then
- RaiseMsg(20170227095454,nMissingExternalName,sMissingExternalName,
- ['missing external name'],Proc);
- for pm in [pmAssembler,pmForward,pmNoReturn,pmInline] do
- if pm in Proc.Modifiers then
- RaiseMsg(20170323100842,nInvalidXModifierY,sInvalidXModifierY,
- [Proc.ElementTypeName,ModifierNames[pm]],Proc);
- // compute external name
- ExtName:=ComputeConstString(Proc.LibrarySymbolName,true,true);
- // a virtual must have the external name, so that override works
- if Proc.IsVirtual and (Proc.Name<>ExtName) then
- RaiseMsg(20170321090049,nVirtualMethodNameMustMatchExternal,
- sVirtualMethodNameMustMatchExternal,[],Proc.LibrarySymbolName);
- if Proc.Parent is TPasSection then
- AddExternalPath(ExtName,Proc.LibrarySymbolName);
- exit;
- end;
- end;
- end;
- procedure TPas2JSResolver.FinishPropertyOfClass(PropEl: TPasProperty);
- var
- Getter, Setter: TPasElement;
- GetterIsBracketAccessor, SetterIsBracketAccessor: Boolean;
- Arg: TPasArgument;
- ArgResolved: TPasResolverResult;
- begin
- inherited FinishPropertyOfClass(PropEl);
- Getter:=GetPasPropertyGetter(PropEl);
- GetterIsBracketAccessor:=IsExternalBracketAccessor(Getter);
- Setter:=GetPasPropertySetter(PropEl);
- SetterIsBracketAccessor:=IsExternalBracketAccessor(Setter);
- if GetterIsBracketAccessor then
- begin
- if PropEl.Args.Count<>1 then
- RaiseMsg(20170403001743,nBracketAccessorOfExternalClassMustHaveOneParameter,
- sBracketAccessorOfExternalClassMustHaveOneParameter,
- [],PropEl);
- end;
- if SetterIsBracketAccessor then
- begin
- if PropEl.Args.Count<>1 then
- RaiseMsg(20170403001806,nBracketAccessorOfExternalClassMustHaveOneParameter,
- sBracketAccessorOfExternalClassMustHaveOneParameter,
- [],PropEl);
- end;
- if GetterIsBracketAccessor or SetterIsBracketAccessor then
- begin
- Arg:=TPasArgument(PropEl.Args[0]);
- if not (Arg.Access in [argDefault,argConst]) then
- RaiseMsg(20170403090225,nXExpectedButYFound,sXExpectedButYFound,
- ['default or "const"',AccessNames[Arg.Access]],PropEl);
- ComputeElement(Arg,ArgResolved,[rcType],Arg);
- if not (ArgResolved.BaseType in (btAllInteger+btAllStringAndChars+btAllBooleans+btAllFloats)) then
- RaiseMsg(20170403090628,nIncompatibleTypesGotExpected,
- sIncompatibleTypesGotExpected,
- [GetResolverResultDescription(ArgResolved,true),'string'],Arg);
- end;
- end;
- procedure TPas2JSResolver.CheckNewInstanceFunction(ClassScope: TPas2JSClassScope
- );
- var
- Proc: TPasClassFunction;
- Args: TFPList;
- Arg: TPasArgument;
- ResolvedArg: TPasResolverResult;
- begin
- Proc:=ClassScope.NewInstanceFunction;
- // proc modifiers override and external were already checked
- // visibility was already checked
- // function result type was already checked
- if not Proc.IsVirtual then
- RaiseMsg(20170324231040,nNewInstanceFunctionMustBeVirtual,
- sNewInstanceFunctionMustBeVirtual,[],Proc);
- Args:=Proc.ProcType.Args;
- if Args.Count<2 then
- RaiseMsg(20170324232247,nNewInstanceFunctionMustHaveTwoParameters,
- sNewInstanceFunctionMustHaveTwoParameters,[],Proc.ProcType);
- // first param must be a string
- Arg:=TPasArgument(Args[0]);
- if Arg.Access<>argDefault then
- RaiseMsg(20170324232655,nIncompatibleTypeArgNo,sIncompatibleTypeArgNo,
- ['1',AccessNames[Arg.Access],'default (none)'],Arg);
- if Arg.ArgType=nil then
- RaiseMsg(20170324233201,nIncompatibleTypeArgNo,sIncompatibleTypeArgNo,
- ['1','untyped','String'],Arg);
- ComputeElement(Arg.ArgType,ResolvedArg,[rcType]);
- if ResolvedArg.BaseType<>btString then
- RaiseMsg(20170324233348,nIncompatibleTypeArgNo,sIncompatibleTypeArgNo,
- ['1',GetResolverResultDescription(ResolvedArg),'String'],Arg);
- // second param must be const untyped
- Arg:=TPasArgument(Args[1]);
- if Arg.Access<>argConst then
- RaiseMsg(20170324233457,nIncompatibleTypeArgNo,sIncompatibleTypeArgNo,
- ['2',AccessNames[Arg.Access],'const'],Arg);
- if Arg.ArgType<>nil then
- RaiseMsg(20170324233508,nIncompatibleTypeArgNo,sIncompatibleTypeArgNo,
- ['2','type','untyped'],Arg);
- end;
- function TPas2JSResolver.AddExternalName(const aName: string; El: TPasElement
- ): TPasIdentifier;
- var
- Item: TPasIdentifier;
- begin
- //writeln('TPas2JSResolver.AddExternalIdentifier Name="',aName,'" El=',GetObjName(El));
- Item:=TPasIdentifier.Create;
- Item.Identifier:=aName;
- Item.Element:=El;
- InternalAdd(Item);
- //writeln('TPas2JSResolver.AddExternalIdentifier END');
- Result:=Item;
- end;
- function TPas2JSResolver.FindExternalName(const aName: String
- ): TPasIdentifier;
- begin
- Result:=TPasIdentifier(FExternalNames.Find(aName));
- {$IFDEF VerbosePasResolver}
- if (Result<>nil) and (Result.Owner<>Self) then
- begin
- writeln('TPas2JSResolver.FindExternalName Result.Owner<>Self Owner='+GetObjName(Result.Owner));
- raise Exception.Create('20170322235814');
- end;
- {$ENDIF}
- end;
- procedure TPas2JSResolver.AddExternalPath(aName: string; El: TPasElement);
- // add aName and the first identifier of aName
- var
- p: PChar;
- l: integer;
- begin
- aName:=Trim(aName);
- if aName='' then exit;
- AddExternalName(aName,El);
- p:=PChar(aName);
- while p^ in ['a'..'z','A'..'Z','0'..'9','_','$'] do inc(p);
- l:=p-PChar(aName);
- if l=length(aName) then exit;
- AddExternalName(LeftStr(aName,l),El);
- end;
- procedure TPas2JSResolver.ClearElementData;
- var
- Data, Next: TPas2JsElementData;
- begin
- Data:=FFirstElementData;
- while Data<>nil do
- begin
- Next:=Data.Next;
- Data.Free;
- Data:=Next;
- end;
- FFirstElementData:=nil;
- FLastElementData:=nil;
- FExternalNames.ForEachCall(@OnClearHashItem,nil);
- FExternalNames.Clear;
- end;
- function TPas2JSResolver.AddJSBaseType(const aName: string; Typ: TPas2jsBaseType
- ): TResElDataPas2JSBaseType;
- var
- El: TPasUnresolvedSymbolRef;
- begin
- El:=AddCustomBaseType(aName,TResElDataPas2JSBaseType);
- if Typ<>pbtNone then
- FJSBaseTypes[Typ]:=El;
- Result:=TResElDataPas2JSBaseType(El.CustomData);
- Result.JSBaseType:=Typ;
- end;
- function TPas2JSResolver.IsJSBaseType(TypeEl: TPasType; Typ: TPas2jsBaseType
- ): boolean;
- begin
- Result:=(TypeEl is TPasUnresolvedSymbolRef)
- and (CompareText(TypeEl.Name,Pas2jsBaseTypeNames[Typ])=0)
- and (TypeEl.CustomData is TResElDataPas2JSBaseType);
- end;
- function TPas2JSResolver.IsJSBaseType(const TypeResolved: TPasResolverResult;
- Typ: TPas2jsBaseType; HasValue: boolean): boolean;
- begin
- if (TypeResolved.BaseType<>btCustom) or not IsJSBaseType(TypeResolved.TypeEl,Typ) then
- exit(false);
- if HasValue and not (rrfReadable in TypeResolved.Flags) then
- exit(false);
- Result:=true;
- end;
- function TPas2JSResolver.CheckAssignCompatibilityCustom(const LHS,
- RHS: TPasResolverResult; ErrorEl: TPasElement; RaiseOnIncompatible: boolean;
- var Handled: boolean): integer;
- var
- LeftBaseType: TPas2jsBaseType;
- LArray: TPasArrayType;
- ElTypeResolved: TPasResolverResult;
- begin
- Result:=cIncompatible;
- if LHS.BaseType=btCustom then
- begin
- if not (LHS.TypeEl is TPasUnresolvedSymbolRef) then
- begin
- {$IFDEF VerbosePas2JS}
- writeln('TPas2JSResolver.CheckAssignCompatibilityCustomBaseType LHS=',GetResolverResultDesc(LHS));
- {$ENDIF}
- RaiseInternalError(20170325114554);
- end;
- if not (LHS.TypeEl.CustomData is TResElDataPas2JSBaseType) then
- exit;
- Handled:=true;
- LeftBaseType:=TResElDataPas2JSBaseType(LHS.TypeEl.CustomData).JSBaseType;
- if LeftBaseType=pbtJSValue then
- begin
- // assign to a JSValue
- if rrfReadable in RHS.Flags then
- begin
- // RHS is a value
- if (RHS.BaseType in btAllJSValueSrcTypes) then
- Result:=cExact+1 // type cast to JSValue
- else if RHS.BaseType=btCustom then
- begin
- if IsJSBaseType(RHS,pbtJSValue) then
- Result:=cExact;
- end
- else if RHS.BaseType=btContext then
- Result:=cExact+1;
- end
- else if RHS.BaseType=btContext then
- begin
- // RHS is not a value
- if RHS.IdentEl<>nil then
- begin
- if RHS.IdentEl.ClassType=TPasClassType then
- Result:=cExact+1; // RHS is a class
- end;
- end;
- end;
- end
- else if (LHS.BaseType=btContext) and (LHS.TypeEl.ClassType=TPasArrayType)
- and (rrfReadable in RHS.Flags) then
- begin
- LArray:=TPasArrayType(LHS.TypeEl);
- if length(LArray.Ranges)>0 then
- exit;
- if (RHS.BaseType<>btContext) or (RHS.TypeEl.ClassType<>TPasArrayType) then
- exit;
- ComputeElement(LArray.ElType,ElTypeResolved,[rcType]);
- if IsJSBaseType(ElTypeResolved,pbtJSValue) then
- begin
- // array of jsvalue := array
- Handled:=true;
- Result:=cExact+1;
- end;
- end;
- if RaiseOnIncompatible then ;
- if ErrorEl=nil then ;
- end;
- function TPas2JSResolver.CheckTypeCastClassInstanceToClass(const FromClassRes,
- ToClassRes: TPasResolverResult; ErrorEl: TPasElement): integer;
- var
- ToClass: TPasClassType;
- ClassScope: TPasClassScope;
- begin
- if FromClassRes.BaseType=btNil then exit(cExact);
- ToClass:=(ToClassRes.TypeEl as TPasClassType);
- ClassScope:=ToClass.CustomData as TPasClassScope;
- if ClassScope.AncestorScope=nil then
- // type cast to root class
- Result:=cExact+1
- else
- Result:=cIncompatible;
- if ErrorEl=nil then ;
- end;
- function TPas2JSResolver.CheckEqualCompatibilityCustomType(const LHS,
- RHS: TPasResolverResult; ErrorEl: TPasElement; RaiseOnIncompatible: boolean
- ): integer;
- var
- LeftBaseType: TPas2jsBaseType;
- begin
- Result:=cIncompatible;
- if LHS.BaseType=btCustom then
- begin
- if not (LHS.TypeEl is TPasUnresolvedSymbolRef) then
- begin
- {$IFDEF VerbosePas2JS}
- writeln('TPas2JSResolver.CheckEqualCompatibilityCustomType LHS=',GetResolverResultDesc(LHS));
- {$ENDIF}
- RaiseInternalError(20170330005841);
- end;
- if not (LHS.TypeEl.CustomData is TResElDataPas2JSBaseType) then
- exit;
- LeftBaseType:=TResElDataPas2JSBaseType(LHS.TypeEl.CustomData).JSBaseType;
- if LeftBaseType=pbtJSValue then
- begin
- if (rrfReadable in LHS.Flags) then
- begin
- if (rrfReadable in RHS.Flags) then
- begin
- if RHS.BaseType in btAllJSValueSrcTypes then
- Result:=cExact
- else if RHS.BaseType=btCustom then
- begin
- if IsJSBaseType(RHS,pbtJSValue) then
- Result:=cExact;
- end
- else if RHS.BaseType=btContext then
- Result:=cExact+1;
- end
- else if RHS.BaseType=btContext then
- begin
- // right side is not a value
- if RHS.IdentEl<>nil then
- begin
- if RHS.IdentEl.ClassType=TPasClassType then
- Result:=cExact+1; // RHS is a class
- end;
- end;
- end;
- end;
- end
- else if RHS.BaseType=btCustom then
- exit(CheckEqualCompatibilityCustomType(RHS,LHS,ErrorEl,RaiseOnIncompatible))
- else
- RaiseInternalError(20170330005725);
- end;
- function TPas2JSResolver.ResolveBracketOperatorClass(Params: TParamsExpr;
- const ResolvedValue: TPasResolverResult; ClassScope: TPasClassScope;
- Access: TResolvedRefAccess): boolean;
- var
- ParamResolved: TPasResolverResult;
- Param: TPasExpr;
- aClass: TPasClassType;
- begin
- if ClassScope.DefaultProperty=nil then
- begin
- aClass:=TPasClassType(ClassScope.Element);
- if IsExternalClassName(aClass,'Array') then
- begin
- if ResolvedValue.IdentEl is TPasType then
- RaiseMsg(20170402194000,nIllegalQualifier,sIllegalQualifier,['['],Params);
- if length(Params.Params)<>1 then
- RaiseMsg(20170402194059,nWrongNumberOfParametersForArray,
- sWrongNumberOfParametersForArray,[],Params);
- // check first param is an integer value
- Param:=Params.Params[0];
- ComputeElement(Param,ParamResolved,[]);
- if (not (rrfReadable in ParamResolved.Flags))
- or not (ParamResolved.BaseType in btAllInteger) then
- CheckRaiseTypeArgNo(20170402194221,1,Param,ParamResolved,'integer',true);
- AccessExpr(Param,rraRead);
- exit(true);
- end
- else if IsExternalClassName(aClass,'Object') then
- begin
- if ResolvedValue.IdentEl is TPasType then
- RaiseMsg(20170402194453,nIllegalQualifier,sIllegalQualifier,['['],Params);
- if length(Params.Params)<>1 then
- RaiseMsg(20170402194456,nWrongNumberOfParametersForArray,
- sWrongNumberOfParametersForArray,[],Params);
- // check first param is a string value
- Param:=Params.Params[0];
- ComputeElement(Param,ParamResolved,[]);
- if (not (rrfReadable in ParamResolved.Flags))
- or not (ParamResolved.BaseType in btAllStringAndChars) then
- CheckRaiseTypeArgNo(20170402194511,1,Param,ParamResolved,'string',true);
- AccessExpr(Param,rraRead);
- exit(true);
- end;
- end;
- Result:=inherited ResolveBracketOperatorClass(Params, ResolvedValue, ClassScope, Access);
- end;
- procedure TPas2JSResolver.ComputeArrayParams_Class(Params: TParamsExpr;
- var ResolvedEl: TPasResolverResult; ClassScope: TPasClassScope;
- Flags: TPasResolverComputeFlags; StartEl: TPasElement);
- var
- aClass: TPasClassType;
- OrigResolved: TPasResolverResult;
- begin
- aClass:=TPasClassType(ClassScope.Element);
- if IsExternalClassName(aClass,'Array') or IsExternalClassName(aClass,'Object') then
- begin
- if [rcConstant,rcType]*Flags<>[] then
- RaiseConstantExprExp(20170402202137,Params);
- OrigResolved:=ResolvedEl;
- SetResolverTypeExpr(ResolvedEl,btCustom,JSBaseTypes[pbtJSValue],[rrfReadable,rrfWritable]);
- // identifier and value is the array/object itself
- ResolvedEl.IdentEl:=OrigResolved.IdentEl;
- ResolvedEl.ExprEl:=OrigResolved.ExprEl;
- ResolvedEl.Flags:=OrigResolved.Flags+[rrfReadable,rrfWritable];
- exit;
- end;
- inherited ComputeArrayParams_Class(Params, ResolvedEl, ClassScope, Flags,
- StartEl);
- end;
- constructor TPas2JSResolver.Create;
- var
- bt: TPas2jsBaseType;
- begin
- inherited;
- FExternalNames:=TFPHashList.Create;
- StoreSrcColumns:=true;
- Options:=Options+DefaultPasResolverOptions;
- ScopeClass_Class:=TPas2JSClassScope;
- ScopeClass_WithExpr:=TPas2JSWithExprScope;
- for bt in [pbtJSValue] do
- AddJSBaseType(Pas2jsBaseTypeNames[bt],bt);
- end;
- destructor TPas2JSResolver.Destroy;
- begin
- ClearElementData;
- FreeAndNil(FExternalNames);
- inherited Destroy;
- end;
- procedure TPas2JSResolver.AddObjFPCBuiltInIdentifiers(
- const TheBaseTypes: TResolveBaseTypes;
- const TheBaseProcs: TResolverBuiltInProcs);
- begin
- inherited AddObjFPCBuiltInIdentifiers(
- TheBaseTypes
- -btAllStrings+[btString] // allow only String
- -btAllFloats+[btDouble] // allow only Double
- ,TheBaseProcs);
- end;
- function TPas2JSResolver.CheckTypeCastRes(const FromResolved,
- ToResolved: TPasResolverResult; ErrorEl: TPasElement; RaiseOnError: boolean
- ): integer;
- var
- JSBaseType: TPas2jsBaseType;
- C: TClass;
- ToClass: TPasClassType;
- begin
- Result:=cIncompatible;
- {$IFDEF VerbosePas2JS}
- writeln('TPas2JSResolver.CheckTypeCastCustomBaseType To=',GetResolverResultDesc(ToResolved),' From=',GetResolverResultDesc(FromResolved));
- {$ENDIF}
- if rrfReadable in FromResolved.Flags then
- begin
- if (ToResolved.BaseType=btCustom) then
- begin
- if not (ToResolved.TypeEl is TPasUnresolvedSymbolRef) then
- RaiseInternalError(20170325142826);
- if (ToResolved.TypeEl.CustomData is TResElDataPas2JSBaseType) then
- begin
- // type cast to pas2js type, e.g. JSValue(V)
- JSBaseType:=TResElDataPas2JSBaseType(ToResolved.TypeEl.CustomData).JSBaseType;
- if JSBaseType=pbtJSValue then
- begin
- if rrfReadable in FromResolved.Flags then
- begin
- if (FromResolved.BaseType in btAllJSValueSrcTypes) then
- Result:=cExact+1 // type cast to JSValue
- else if FromResolved.BaseType=btCustom then
- begin
- if IsJSBaseType(FromResolved,pbtJSValue) then
- Result:=cExact;
- end
- else if FromResolved.BaseType=btContext then
- Result:=cExact+1;
- end;
- end;
- exit;
- end;
- end
- else if FromResolved.BaseType=btCustom then
- begin
- if not (FromResolved.TypeEl is TPasUnresolvedSymbolRef) then
- RaiseInternalError(20170325143016);
- if (FromResolved.TypeEl.CustomData is TResElDataPas2JSBaseType) then
- begin
- // type cast a pas2js value, e.g. T(jsvalue)
- if not (rrfReadable in FromResolved.Flags) then
- exit;
- JSBaseType:=TResElDataPas2JSBaseType(FromResolved.TypeEl.CustomData).JSBaseType;
- if JSBaseType=pbtJSValue then
- begin
- if (ToResolved.BaseType in btAllJSValueTypeCastTo) then
- Result:=cExact+1 // type cast JSValue to simple base type
- else if ToResolved.BaseType=btContext then
- begin
- C:=ToResolved.TypeEl.ClassType;
- if (C=TPasClassType)
- or (C=TPasClassOfType)
- or (C=TPasEnumType) then
- Result:=cExact+1;
- end;
- end;
- exit;
- end;
- end
- else if ToResolved.BaseType=btContext then
- begin
- C:=ToResolved.TypeEl.ClassType;
- if C=TPasClassType then
- begin
- ToClass:=TPasClassType(ToResolved.TypeEl);
- if ToClass.IsExternal then
- begin
- if IsExternalClassName(ToClass,'String')
- and (FromResolved.BaseType in btAllStringAndChars) then
- exit(cExact);
- if IsExternalClassName(ToClass,'Array')
- and ((FromResolved.BaseType=btArray)
- or (FromResolved.BaseType=btContext)) then
- exit(cExact);
- end;
- end
- else if C=TPasArrayType then
- begin
- if (FromResolved.BaseType=btContext)
- and (FromResolved.TypeEl.ClassType=TPasClassType)
- and TPasClassType(FromResolved.TypeEl).IsExternal
- and IsExternalClassName(TPasClassType(FromResolved.TypeEl),'Array') then
- begin
- // type cast external Array to an array
- exit(cExact+1);
- end;
- end;
- end;
- end;
- Result:=inherited CheckTypeCastRes(FromResolved,ToResolved,ErrorEl,RaiseOnError);
- end;
- function TPas2JSResolver.ExtractPasStringLiteral(El: TPasElement;
- const S: String): TJSString;
- { Extracts the value from a Pascal string literal
- S is a Pascal string literal e.g. 'Line'#10
- '' empty string
- '''' => "'"
- #decimal
- #$hex
- ^l l is a letter a-z
- }
- var
- p, StartP: PChar;
- c: Char;
- i: Integer;
- begin
- Result:='';
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.ExtractPasStringLiteral "',S,'"');
- {$ENDIF}
- if S='' then
- RaiseInternalError(20170207154543);
- p:=PChar(S);
- repeat
- case p^ of
- #0: break;
- '''':
- begin
- inc(p);
- StartP:=p;
- repeat
- c:=p^;
- case c of
- #0:
- RaiseInternalError(20170207155120);
- '''':
- begin
- if p>StartP then
- Result:=Result+TJSString(copy(S,StartP-PChar(S)+1,p-StartP));
- inc(p);
- StartP:=p;
- if p^<>'''' then
- break;
- Result:=Result+'''';
- inc(p);
- StartP:=p;
- end;
- else
- inc(p);
- end;
- until false;
- if p>StartP then
- Result:=Result+TJSString(copy(S,StartP-PChar(S)+1,p-StartP));
- end;
- '#':
- begin
- inc(p);
- if p^='$' then
- begin
- // #$hexnumber
- inc(p);
- StartP:=p;
- i:=0;
- repeat
- c:=p^;
- case c of
- #0: break;
- '0'..'9': i:=i*16+ord(c)-ord('0');
- 'a'..'f': i:=i*16+ord(c)-ord('a')+10;
- 'A'..'F': i:=i*16+ord(c)-ord('A')+10;
- else break;
- end;
- if i>$10ffff then
- RaiseNotYetImplemented(20170207164657,El,'maximum codepoint is $10ffff');
- inc(p);
- until false;
- if p=StartP then
- RaiseInternalError(20170207164956);
- Result:=Result+CodePointToJSString(i);
- end
- else
- begin
- // #decimalnumber
- StartP:=p;
- i:=0;
- repeat
- c:=p^;
- case c of
- #0: break;
- '0'..'9': i:=i*10+ord(c)-ord('0');
- else break;
- end;
- if i>$10ffff then
- RaiseNotYetImplemented(20170207171140,El,'maximum codepoint is $10ffff');
- inc(p);
- until false;
- if p=StartP then
- RaiseInternalError(20170207171148);
- Result:=Result+CodePointToJSString(i);
- end;
- end;
- '^':
- begin
- // ^A is #1
- inc(p);
- c:=p^;
- case c of
- 'a'..'z': Result:=Result+TJSChar(ord(c)-ord('a')+1);
- 'A'..'Z': Result:=Result+TJSChar(ord(c)-ord('A')+1);
- else RaiseInternalError(20170207160412);
- end;
- inc(p);
- end;
- else
- RaiseNotYetImplemented(20170207154653,El,'ord='+IntToStr(ord(p^)));
- end;
- until false;
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.ExtractPasStringLiteral Result="',Result,'"');
- {$ENDIF}
- end;
- function TPas2JSResolver.ComputeConst(Expr: TPasExpr; StoreCustomData: boolean
- ): TJSValue;
- var
- Prim: TPrimitiveExpr;
- V: TJSValue;
- ConstData: TP2JConstExprData;
- begin
- Result:=nil;
- if Expr=nil then
- RaiseInternalError(20170215123600);
- if StoreCustomData and (Expr.CustomData is TPasElementBase) then
- begin
- ConstData:=TP2JConstExprData(GetElementData(
- TPasElementBase(Expr.CustomData),TP2JConstExprData));
- if ConstData<>nil then
- begin
- // use stored result
- Result:=ConstData.Value;
- exit;
- end;
- end;
- V:=nil;
- try
- if Expr.ClassType=TPrimitiveExpr then
- begin
- Prim:=TPrimitiveExpr(Expr);
- if Prim.Kind=pekString then
- V:=TJSValue.Create(ExtractPasStringLiteral(Prim,Prim.Value))
- else
- RaiseNotYetImplemented(20170215124733,Prim);
- end
- else
- RaiseNotYetImplemented(20170215124746,Expr);
- Result:=V;
- if StoreCustomData then
- begin
- // store result
- ConstData:=TP2JConstExprData(CreateElementData(TP2JConstExprData,Expr));
- ConstData.Value:=V;
- end;
- finally
- if Result=nil then
- V.Free;
- end;
- end;
- function TPas2JSResolver.ComputeConstString(Expr: TPasExpr; StoreCustomData,
- NotEmpty: boolean): String;
- var
- V: TJSValue;
- begin
- V:=ComputeConst(Expr,StoreCustomData);
- if V.ValueType<>jsbase.jstString then
- RaiseNotYetImplemented(20170320220728,Expr,'expected string constant');
- if V.ValueType<>jstString then
- RaiseMsg(20170211221121,nExpectedXButFoundY,sExpectedXButFoundY,['string literal',JSTypeCaptions[V.ValueType]],Expr);
- if NotEmpty and (V.AsString='') then
- RaiseMsg(20170321085318,nExpectedXButFoundY,sExpectedXButFoundY,['string literal','empty'],Expr);
- Result:=String(V.AsString);
- end;
- function TPas2JSResolver.IsExternalBracketAccessor(El: TPasElement): boolean;
- var
- ExtName: String;
- begin
- if (not (El is TPasProcedure)) or (TPasProcedure(El).LibrarySymbolName=nil) then
- exit(false);
- ExtName:=ComputeConstString(TPasProcedure(El).LibrarySymbolName,false,false);
- Result:=ExtName=ExtClassBracketAccessor;
- end;
- function TPas2JSResolver.GetElementData(El: TPasElementBase;
- DataClass: TPas2JsElementDataClass): TPas2JsElementData;
- begin
- Result:=nil;
- repeat
- if El.InheritsFrom(DataClass) then
- exit(TPas2JsElementData(El));
- if El.CustomData=nil then exit;
- El:=El.CustomData as TPasElementBase;
- until false;
- end;
- procedure TPas2JSResolver.AddElementData(Data: TPas2JsElementData);
- begin
- Data.Owner:=Self;
- if FFirstElementData<>nil then
- begin
- FLastElementData.Next:=Data;
- FLastElementData:=Data;
- end
- else
- begin
- FFirstElementData:=Data;
- FLastElementData:=Data;
- end;
- end;
- function TPas2JSResolver.CreateElementData(DataClass: TPas2JsElementDataClass;
- El: TPasElement): TPas2JsElementData;
- begin
- Result:=DataClass.Create;
- Result.Element:=El;
- AddElementData(Result);
- end;
- { TP2JConstExprData }
- destructor TP2JConstExprData.Destroy;
- begin
- FreeAndNil(Value);
- inherited Destroy;
- end;
- { TParamContext }
- constructor TParamContext.Create(PasEl: TPasElement; JSEl: TJSElement;
- aParent: TConvertContext);
- begin
- inherited Create(PasEl, JSEl, aParent);
- Access:=caAssign;
- AccessContext:=Self;
- end;
- { TPas2JsElementData }
- procedure TPas2JsElementData.SetElement(const AValue: TPasElement);
- var
- Data: TPasElementBase;
- begin
- if FElement=AValue then Exit;
- if FElement<>nil then
- begin
- Data:=FElement;
- while Data.CustomData<>Self do
- if Data.CustomData is TPasElementBase then
- Data:=TPasElementBase(Data.CustomData)
- else
- begin
- {$IFDEF VerbosePas2JS}
- writeln('TPas2JsElementData.SetElement REMOVE ',ClassName);
- writeln(' ',GetObjName(Data.CustomData));
- {$ENDIF}
- raise EPas2JS.Create('');
- end;
- Data.CustomData:=CustomData;
- TPasElement(FElement).Release;
- end;
- FElement:=AValue;
- if FElement<>nil then
- begin
- TPasElement(FElement).AddRef;
- Data:=FElement;
- while Data.CustomData is TPasElementBase do
- Data:=TPasElementBase(Data.CustomData);
- if Data.CustomData<>nil then
- begin
- {$IFDEF VerbosePas2JS}
- writeln('TPas2JsElementData.SetElement INSERT ',ClassName);
- writeln(' ',GetObjName(Data.CustomData));
- {$ENDIF}
- raise EPas2JS.Create('');
- end;
- Data.CustomData:=Self;
- end;
- end;
- constructor TPas2JsElementData.Create;
- begin
- end;
- destructor TPas2JsElementData.Destroy;
- begin
- Element:=nil;
- Next:=nil;
- Owner:=nil;
- inherited Destroy;
- end;
- { TAssignContext }
- constructor TAssignContext.Create(PasEl: TPasElement; JSEl: TJSElement;
- aParent: TConvertContext);
- begin
- inherited Create(PasEl, JSEl, aParent);
- Access:=caAssign;
- AccessContext:=Self;
- end;
- { TDotContext }
- constructor TDotContext.Create(PasEl: TPasElement; JSEl: TJSElement;
- aParent: TConvertContext);
- begin
- inherited Create(PasEl, JSEl, aParent);
- Kind:=cjkDot;
- end;
- { TInterfaceContext }
- constructor TInterfaceContext.Create(PasEl: TPasElement; JSEl: TJSElement;
- aParent: TConvertContext);
- begin
- inherited;
- IsSingleton:=true;
- end;
- { TObjectContext }
- constructor TObjectContext.Create(PasEl: TPasElement; JSEl: TJSElement;
- aParent: TConvertContext);
- begin
- inherited;
- Kind:=cjkObject;
- end;
- { TFunctionContext }
- constructor TFunctionContext.Create(PasEl: TPasElement; JSEl: TJSElement;
- aParent: TConvertContext);
- begin
- inherited;
- Kind:=cjkFunction;
- end;
- { TRootContext }
- constructor TRootContext.Create(PasEl: TPasElement; JSEl: TJSElement;
- aParent: TConvertContext);
- begin
- inherited;
- Kind:=cjkRoot;
- end;
- { TConvertContext }
- constructor TConvertContext.Create(PasEl: TPasElement; JSEl: TJSElement;
- aParent: TConvertContext);
- begin
- PasElement:=PasEl;
- JSElement:=JsEl;
- Parent:=aParent;
- if Parent<>nil then
- begin
- Resolver:=Parent.Resolver;
- Access:=aParent.Access;
- AccessContext:=aParent.AccessContext;
- end;
- end;
- function TConvertContext.GetRootModule: TPasModule;
- var
- aContext: TConvertContext;
- begin
- aContext:=Self;
- while aContext.Parent<>nil do
- aContext:=aContext.Parent;
- if aContext.PasElement is TPasModule then
- Result:=TPasModule(aContext.PasElement)
- else
- Result:=nil;
- end;
- function TConvertContext.GetThis: TPasElement;
- var
- ctx: TFunctionContext;
- begin
- ctx:=GetThisContext;
- if ctx<>nil then
- Result:=ctx.This
- else
- Result:=nil;
- end;
- function TConvertContext.GetThisContext: TFunctionContext;
- begin
- Result:=TFunctionContext(GetContextOfType(TFunctionContext));
- end;
- function TConvertContext.GetContextOfType(aType: TConvertContextClass
- ): TConvertContext;
- var
- ctx: TConvertContext;
- begin
- Result:=nil;
- ctx:=Self;
- repeat
- if ctx is aType then
- exit(ctx);
- ctx:=ctx.Parent;
- until ctx=nil;
- end;
- function TConvertContext.CreateLocalIdentifier(const Prefix: string): string;
- begin
- inc(TmpVarCount);
- Result:=Prefix+IntToStr(TmpVarCount);
- end;
- function TConvertContext.CurrentModeswitches: TModeSwitches;
- begin
- if Resolver=nil then
- Result:=OBJFPCModeSwitches
- else
- Result:=Resolver.CurrentParser.CurrentModeswitches;
- end;
- function TConvertContext.GetSingletonFunc: TFunctionContext;
- var
- Ctx: TConvertContext;
- begin
- Ctx:=Self;
- while (Ctx<>nil) do
- begin
- if Ctx.IsSingleton and (Ctx.JSElement<>nil) and (Ctx is TFunctionContext) then
- exit(TFunctionContext(Ctx));
- Ctx:=Ctx.Parent;
- end;
- end;
- { TPasToJSConverter }
- // inline
- function TPasToJSConverter.GetUseEnumNumbers: boolean;
- begin
- Result:=coEnumNumbers in FOptions;
- end;
- // inline
- function TPasToJSConverter.GetUseLowerCase: boolean;
- begin
- Result:=coLowerCase in FOptions;
- end;
- // inline
- function TPasToJSConverter.GetUseSwitchStatement: boolean;
- begin
- Result:=coSwitchStatement in FOptions;
- end;
- procedure TPasToJSConverter.AddToSourceElements(Src: TJSSourceElements;
- El: TJSElement);
- Var
- List : TJSStatementList;
- AddEl : TJSElement;
- begin
- While El<>nil do
- begin
- if El is TJSStatementList then
- begin
- List:=El as TJSStatementList;
- // List.A is first statement, List.B is next in list, chained.
- // -> add A, continue with B and free List
- AddEl:=List.A;
- El:=List.B;
- List.A:=Nil;
- List.B:=Nil;
- FreeAndNil(List);
- end
- else
- begin
- AddEl:=El;
- El:=Nil;
- end;
- Src.Statements.AddNode.Node:=AddEl;
- end;
- end;
- procedure TPasToJSConverter.RemoveFromSourceElements(Src: TJSSourceElements;
- El: TJSElement);
- var
- Statements: TJSElementNodes;
- i: Integer;
- begin
- Statements:=Src.Statements;
- for i:=Statements.Count-1 downto 0 do
- if Statements[i].Node=El then
- Statements.Delete(i);
- end;
- function TPasToJSConverter.GetBuildInNames(bin: TPas2JSBuiltInName): string;
- begin
- Result:=FBuiltInNames[bin];
- end;
- procedure TPasToJSConverter.SetBuildInNames(bin: TPas2JSBuiltInName;
- const AValue: string);
- begin
- FBuiltInNames[bin]:=AValue;
- end;
- procedure TPasToJSConverter.SetPreservedWords(const AValue: TJSReservedWordList
- );
- var
- i: Integer;
- begin
- if FPreservedWords=AValue then Exit;
- for i:=0 to length(AValue)-2 do
- if CompareStr(AValue[i],AValue[i+1])>=0 then
- raise Exception.Create('TPasToJSConverter.SetPreservedWords "'+AValue[i]+'" >= "'+AValue[i+1]+'"');
- FPreservedWords:=AValue;
- end;
- function TPasToJSConverter.ConvertModule(El: TPasModule;
- AContext: TConvertContext): TJSElement;
- (* Format:
- rtl.module('<unitname>',
- [<interface uses1>,<uses2>, ...],
- function(){
- <interface>
- <implementation>
- this.$init=function(){
- <initialization>
- };
- },
- [<implementation uses1>,<uses2>, ...]);
- *)
- Var
- OuterSrc , Src: TJSSourceElements;
- RegModuleCall: TJSCallExpression;
- ArgArray: TJSArguments;
- UsesList: TFPList;
- FunDef: TJSFuncDef;
- FunBody: TJSFunctionBody;
- FunDecl: TJSFunctionDeclarationStatement;
- UsesSection: TPasSection;
- ModuleName: String;
- IntfContext: TInterfaceContext;
- ImplVarSt: TJSVariableStatement;
- VarDecl: TJSVarDeclaration;
- ImplAssignSt: TJSSimpleAssignStatement;
- ImplDecl: TJSElement;
- begin
- Result:=Nil;
- OuterSrc:=TJSSourceElements(CreateElement(TJSSourceElements, El));
- Result:=OuterSrc;
- // create 'rtl.module(...)'
- RegModuleCall:=CreateCallExpression(El);
- AddToSourceElements(OuterSrc,RegModuleCall);
- RegModuleCall.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],'module']);
- ArgArray := RegModuleCall.Args;
- RegModuleCall.Args:=ArgArray;
- // add unitname parameter: unitname
- ModuleName:=TransformModuleName(El,AContext);
- ArgArray.Elements.AddElement.Expr:=CreateLiteralString(El,ModuleName);
- // add interface-uses-section parameter: [<interface uses1>,<uses2>, ...]
- UsesSection:=nil;
- if (El is TPasProgram) then
- UsesSection:=TPasProgram(El).ProgramSection
- else if (El is TPasLibrary) then
- UsesSection:=TPasLibrary(El).LibrarySection
- else
- UsesSection:=El.InterfaceSection;
- ArgArray.Elements.AddElement.Expr:=CreateUsesList(UsesSection,AContext);
- // add interface parameter: function(){}
- FunDecl:=TJSFunctionDeclarationStatement.Create(0,0);
- ArgArray.Elements.AddElement.Expr:=FunDecl;
- FunDef:=TJSFuncDef.Create;
- FunDecl.AFunction:=FunDef;
- FunDef.Name:='';
- FunBody:=TJSFunctionBody.Create(0,0);
- FunDef.Body:=FunBody;
- Src:=TJSSourceElements(CreateElement(TJSSourceElements, El));
- FunBody.A:=Src;
- if coUseStrict in Options then
- AddToSourceElements(Src,CreateLiteralString(El,'use strict'));
- IntfContext:=TInterfaceContext.Create(El,Src,AContext);
- try
- IntfContext.This:=El;
- if (El is TPasProgram) then
- begin // program
- if Assigned(TPasProgram(El).ProgramSection) then
- AddToSourceElements(Src,ConvertDeclarations(TPasProgram(El).ProgramSection,IntfContext));
- CreateInitSection(El,Src,IntfContext);
- end
- else if El is TPasLibrary then
- begin // library
- if Assigned(TPasLibrary(El).LibrarySection) then
- AddToSourceElements(Src,ConvertDeclarations(TPasLibrary(El).LibrarySection,IntfContext));
- CreateInitSection(El,Src,IntfContext);
- end
- else
- begin // unit
- // add implementation object at top, so the interface elemwnts can add stuff
- if (FBuiltInNames[pbivnImplementation]<>'') and Assigned(El.ImplementationSection) then
- begin
- // add 'var $impl = {};'
- ImplVarSt:=TJSVariableStatement(CreateElement(TJSVariableStatement,El));
- AddToSourceElements(Src,ImplVarSt);
- VarDecl:=TJSVarDeclaration(CreateElement(TJSVarDeclaration,El));
- ImplVarSt.A:=VarDecl;
- VarDecl.Name:=FBuiltInNames[pbivnImplementation];
- VarDecl.Init:=TJSEmptyBlockStatement(CreateElement(TJSEmptyBlockStatement,El.ImplementationSection));
- // add 'this.$impl = $impl;'
- ImplAssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El));
- AddToSourceElements(Src,ImplAssignSt);
- ImplAssignSt.LHS:=CreateBuiltInIdentifierExpr('this.'+FBuiltInNames[pbivnImplementation]);
- ImplAssignSt.Expr:=CreateBuiltInIdentifierExpr(FBuiltInNames[pbivnImplementation]);
- end
- else
- begin
- ImplVarSt:=nil;
- ImplAssignSt:=nil;
- end;
- if Assigned(El.InterfaceSection) then
- AddToSourceElements(Src,ConvertDeclarations(El.InterfaceSection,IntfContext));
- if ImplVarSt<>nil then
- begin
- ImplDecl:=CreateImplementationSection(El,Src,IntfContext);
- if ImplDecl=nil then
- begin
- RemoveFromSourceElements(Src,ImplVarSt);
- RemoveFromSourceElements(Src,ImplAssignSt);
- end;
- end;
- CreateInitSection(El,Src,IntfContext);
- // add optional implementation uses list: [<implementation uses1>,<uses2>, ...]
- if Assigned(El.ImplementationSection) then
- begin
- UsesList:=El.ImplementationSection.UsesList;
- if (UsesList<>nil) and (UsesList.Count>0) then
- ArgArray.Elements.AddElement.Expr:=CreateUsesList(El.ImplementationSection,AContext);
- end;
- end;
- finally
- IntfContext.Free;
- end;
- end;
- function TPasToJSConverter.CreateElement(C: TJSElementClass; Src: TPasElement
- ): TJSElement;
- var
- Line, Col: Integer;
- begin
- if Assigned(Src) then
- begin
- TPasResolver.UnmangleSourceLineNumber(Src.SourceLinenumber,Line,Col);
- Result:=C.Create(Line,Col,Src.SourceFilename);
- end
- else
- Result:=C.Create(0,0);
- end;
- function TPasToJSConverter.CreateFreeOrNewInstanceExpr(Ref: TResolvedReference;
- AContext: TConvertContext): TJSCallExpression;
- // create "$create("funcname");"
- var
- ok: Boolean;
- C: TJSCallExpression;
- Proc: TPasProcedure;
- ProcScope: TPasProcedureScope;
- ClassScope: TPasClassScope;
- aClass: TPasElement;
- ArgEx: TJSLiteral;
- ArgElems: TJSArrayLiteralElements;
- FunName: String;
- begin
- Result:=nil;
- //writeln('TPasToJSConverter.CreateNewInstanceStatement Ref.Declaration=',GetObjName(Ref.Declaration));
- Proc:=Ref.Declaration as TPasProcedure;
- if Proc.Name='' then
- RaiseInconsistency(20170125191914);
- //writeln('TPasToJSConverter.CreateNewInstanceStatement Proc.Name=',Proc.Name);
- ProcScope:=Proc.CustomData as TPasProcedureScope;
- //writeln('TPasToJSConverter.CreateNewInstanceStatement ProcScope.Element=',GetObjName(ProcScope.Element),' ProcScope.ClassScope=',GetObjName(ProcScope.ClassScope),' ProcScope.DeclarationProc=',GetObjName(ProcScope.DeclarationProc),' ProcScope.ImplProc=',GetObjName(ProcScope.ImplProc),' ProcScope.CustomData=',GetObjName(ProcScope.CustomData));
- ClassScope:=ProcScope.ClassScope;
- aClass:=ClassScope.Element;
- if aClass.Name='' then
- RaiseInconsistency(20170125191923);
- //writeln('TPasToJSConverter.CreateNewInstanceStatement aClass.Name=',aClass.Name);
- C:=CreateCallExpression(Ref.Element);
- ok:=false;
- try
- // add "$create()"
- if rrfNewInstance in Ref.Flags then
- FunName:=FBuiltInNames[pbifnClassInstanceNew]
- else
- FunName:=FBuiltInNames[pbifnClassInstanceFree];
- FunName:=CreateReferencePath(Proc,AContext,rpkPathWithDot,false,Ref)+FunName;
- C.Expr:=CreateBuiltInIdentifierExpr(FunName);
- ArgElems:=C.Args.Elements;
- // parameter: "funcname"
- ArgEx := CreateLiteralString(Ref.Element,TransformVariableName(Proc,AContext));
- ArgElems.AddElement.Expr:=ArgEx;
- ok:=true;
- finally
- if not ok then
- C.Free;
- end;
- Result:=C;
- end;
- function TPasToJSConverter.CreateFunction(El: TPasElement; WithBody: boolean
- ): TJSFunctionDeclarationStatement;
- var
- FuncDef: TJSFuncDef;
- FuncSt: TJSFunctionDeclarationStatement;
- begin
- FuncSt:=TJSFunctionDeclarationStatement(CreateElement(TJSFunctionDeclarationStatement,El));
- Result:=FuncSt;
- FuncDef:=TJSFuncDef.Create;
- FuncSt.AFunction:=FuncDef;
- if WithBody then
- FuncDef.Body:=TJSFunctionBody(CreateElement(TJSFunctionBody,El));
- end;
- function TPasToJSConverter.ConvertUnaryExpression(El: TUnaryExpr;
- AContext: TConvertContext): TJSElement;
- procedure NotSupported;
- begin
- DoError(20170215134950,nUnaryOpcodeNotSupported,sUnaryOpcodeNotSupported,
- [OpcodeStrings[El.OpCode]],El);
- end;
- Var
- U : TJSUnaryExpression;
- E : TJSElement;
- ResolvedOp, ResolvedEl: TPasResolverResult;
- BitwiseNot: Boolean;
- begin
- if AContext=nil then ;
- Result:=Nil;
- U:=nil;
- Case El.OpCode of
- eopAdd:
- begin
- E:=ConvertElement(El.Operand,AContext);
- U:=TJSUnaryPlusExpression(CreateElement(TJSUnaryPlusExpression,El));
- U.A:=E;
- end;
- eopSubtract:
- begin
- E:=ConvertElement(El.Operand,AContext);
- U:=TJSUnaryMinusExpression(CreateElement(TJSUnaryMinusExpression,El));
- U.A:=E;
- end;
- eopNot:
- begin
- E:=ConvertElement(El.Operand,AContext);
- BitwiseNot:=true;
- if AContext.Resolver<>nil then
- begin
- AContext.Resolver.ComputeElement(El.Operand,ResolvedOp,[]);
- BitwiseNot:=ResolvedOp.BaseType in btAllInteger;
- end;
- if BitwiseNot then
- U:=TJSUnaryInvExpression(CreateElement(TJSUnaryInvExpression,El))
- else
- U:=TJSUnaryNotExpression(CreateElement(TJSUnaryNotExpression,El));
- U.A:=E;
- end;
- eopAddress:
- begin
- if AContext.Resolver=nil then
- NotSupported;
- AContext.Resolver.ComputeElement(El.Operand,ResolvedEl,[rcNoImplicitProc]);
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.ConvertUnaryExpression ',GetResolverResultDesc(ResolvedEl));
- {$ENDIF}
- if ResolvedEl.BaseType=btProc then
- begin
- if ResolvedEl.IdentEl is TPasProcedure then
- begin
- Result:=CreateCallback(El.Operand,ResolvedEl,AContext);
- exit;
- end;
- end;
- end;
- end;
- if U=nil then
- NotSupported;
- Result:=U;
- end;
- function TPasToJSConverter.GetExpressionValueType(El: TPasExpr;
- AContext: TConvertContext): TJSType;
- Function CombineValueType(A,B : TJSType) : TJSType;
- begin
- If (A=jstUNDEFINED) then
- Result:=B
- else if (B=jstUNDEFINED) then
- Result:=A
- else
- Result:=A; // pick the first
- end;
- Var
- A,B : TJSType;
- begin
- if (El is TBoolConstExpr) then
- Result:=jstBoolean
- else if (El is TPrimitiveExpr) then
- begin
- Case El.Kind of
- pekIdent : Result:=GetPasIdentValueType(El.Name,AContext);
- pekNumber : Result:=jstNumber;
- pekString : Result:=jstString;
- pekSet : Result:=jstUNDEFINED;
- pekNil : Result:=jstNull;
- pekBoolConst : Result:=jstBoolean;
- pekRange : Result:=jstUNDEFINED;
- pekFuncParams : Result:=jstUNDEFINED;
- pekArrayParams : Result:=jstUNDEFINED;
- pekListOfExp : Result:=jstUNDEFINED;
- pekInherited : Result:=jstUNDEFINED;
- pekSelf : Result:=jstObject;
- end
- end
- else if (El is TUnaryExpr) then
- Result:=GetExpressionValueType(TUnaryExpr(El).Operand,AContext)
- else if (El is TBinaryExpr) then
- begin
- A:=GetExpressionValueType(TBinaryExpr(El).Left,AContext);
- B:=GetExpressionValueType(TBinaryExpr(El).Right,AContext);
- Result:=CombineValueType(A,B);
- end
- else
- result:=jstUndefined
- end;
- function TPasToJSConverter.GetPasIdentValueType(AName: String;
- AContext: TConvertContext): TJSType;
- begin
- if AContext=nil then ;
- if AName='' then ;
- Result:=jstUNDEFINED;
- end;
- function TPasToJSConverter.ComputeConstString(Expr: TPasExpr;
- AContext: TConvertContext; NotEmpty: boolean): String;
- var
- Prim: TPrimitiveExpr;
- begin
- if AContext.Resolver<>nil then
- Result:=AContext.Resolver.ComputeConstString(Expr,false,NotEmpty)
- else
- begin
- // fall back:
- Result:='';
- if Expr is TPrimitiveExpr then
- begin
- Prim:=TPrimitiveExpr(Expr);
- if Prim.Kind=pekString then
- Result:=Prim.Value
- else
- RaiseNotSupported(Prim,AContext,20170215124733);
- end
- else
- RaiseNotSupported(Expr,AContext,20170322121331);
- end;
- end;
- function TPasToJSConverter.IsExternalClassConstructor(El: TPasElement): boolean;
- var
- P: TPasElement;
- begin
- if (El.ClassType=TPasConstructor)
- and (pmExternal in TPasConstructor(El).Modifiers) then
- begin
- P:=El.Parent;
- if (P<>nil) and (P.ClassType=TPasClassType) and TPasClassType(P).IsExternal then
- exit(true);
- end;
- Result:=false;
- end;
- function TPasToJSConverter.ConvertBinaryExpression(El: TBinaryExpr;
- AContext: TConvertContext): TJSElement;
- Const
- BinClasses : Array [TExprOpCode] of TJSBinaryClass = (
- Nil, //eopEmpty,
- TJSAdditiveExpressionPlus, // +
- TJSAdditiveExpressionMinus, // -
- TJSMultiplicativeExpressionMul, // *
- TJSMultiplicativeExpressionDiv, // /
- TJSMultiplicativeExpressionDiv, // div
- TJSMultiplicativeExpressionMod, // mod
- Nil, //eopPower
- TJSURShiftExpression, // shr
- TJSLShiftExpression, // shl
- Nil, // Not
- Nil, // And
- Nil, // Or
- Nil, // XOr
- TJSEqualityExpressionEQ,
- TJSEqualityExpressionNE,
- TJSRelationalExpressionLT,
- TJSRelationalExpressionGT,
- TJSRelationalExpressionLE,
- TJSRelationalExpressionGE,
- Nil, // In
- TJSRelationalExpressionInstanceOf, // is
- Nil, // As
- Nil, // Symmetrical diff
- Nil, // Address,
- Nil, // Deref
- Nil // SubIndent,
- );
- Var
- R : TJSBinary;
- C : TJSBinaryClass;
- A,B: TJSElement;
- UseBitwiseOp: Boolean;
- Call: TJSCallExpression;
- LeftResolved, RightResolved: TPasResolverResult;
- Flags: TPasResolverComputeFlags;
- ModeSwitches: TModeSwitches;
- begin
- Result:=Nil;
- case El.OpCode of
- eopSubIdent:
- begin
- Result:=ConvertSubIdentExpression(El,AContext);
- exit;
- end;
- eopNone:
- if El.left is TInheritedExpr then
- begin
- Result:=ConvertInheritedExpression(TInheritedExpr(El.left),AContext);
- exit;
- end;
- end;
- if AContext.Access<>caRead then
- DoError(20170209152633,nVariableIdentifierExpected,sVariableIdentifierExpected,[],El);
- Call:=nil;
- A:=ConvertElement(El.left,AContext);
- B:=nil;
- try
- B:=ConvertElement(El.right,AContext);
- if AContext.Resolver<>nil then
- begin
- ModeSwitches:=AContext.CurrentModeswitches;
- // compute left
- Flags:=[];
- if El.OpCode in [eopEqual,eopNotEqual] then
- if not (msDelphi in ModeSwitches) then
- Flags:=[rcNoImplicitProcType];
- AContext.Resolver.ComputeElement(El.left,LeftResolved,Flags);
- // compute right
- Flags:=[];
- if (El.OpCode in [eopEqual,eopNotEqual])
- and not (msDelphi in ModeSwitches) then
- begin
- if LeftResolved.BaseType=btNil then
- Flags:=[rcNoImplicitProcType]
- else if AContext.Resolver.IsProcedureType(LeftResolved,true) then
- Flags:=[rcNoImplicitProcType]
- else
- Flags:=[];
- end;
- AContext.Resolver.ComputeElement(El.right,RightResolved,Flags);
- Result:=ConvertBinaryExpressionRes(El,AContext,LeftResolved,RightResolved,A,B);
- if Result<>nil then exit;
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.ConvertBinaryExpression Left=',GetResolverResultDesc(LeftResolved),' Right=',GetResolverResultDesc(RightResolved));
- {$ENDIF}
- end;
- C:=BinClasses[El.OpCode];
- if C=nil then
- Case El.OpCode of
- eopAs :
- begin
- // "A as B"
- Call:=CreateCallExpression(El);
- if (RightResolved.TypeEl is TPasClassType) and TPasClassType(RightResolved.TypeEl).IsExternal then
- // B is external class -> "rtl.asExt(A,B)"
- Call.Expr:=CreateBuiltInIdentifierExpr(FBuiltInNames[pbivnRTL]+'.'+FBuiltInNames[pbifnAsExt])
- else
- // otherwise -> "rtl.as(A,B)"
- Call.Expr:=CreateBuiltInIdentifierExpr(FBuiltInNames[pbivnRTL]+'.'+FBuiltInNames[pbifnAs]);
- Call.Args.Elements.AddElement.Expr:=A;
- Call.Args.Elements.AddElement.Expr:=B;
- Result:=Call;
- exit;
- end;
- eopAnd,
- eopOr,
- eopXor:
- begin
- if AContext.Resolver<>nil then
- UseBitwiseOp:=((LeftResolved.BaseType in btAllInteger)
- or (RightResolved.BaseType in btAllInteger))
- else
- UseBitwiseOp:=(GetExpressionValueType(El.left,AContext)=jstNumber)
- or (GetExpressionValueType(El.right,AContext)=jstNumber);
- if UseBitwiseOp then
- Case El.OpCode of
- eopAnd : C:=TJSBitwiseAndExpression;
- eopOr : C:=TJSBitwiseOrExpression;
- eopXor : C:=TJSBitwiseXOrExpression;
- end
- else
- Case El.OpCode of
- eopAnd : C:=TJSLogicalAndExpression;
- eopOr : C:=TJSLogicalOrExpression;
- else
- DoError(20161024191234,nBinaryOpcodeNotSupported,sBinaryOpcodeNotSupported,['logical XOR'],El);
- end;
- end;
- else
- if C=nil then
- DoError(20161024191244,nBinaryOpcodeNotSupported,sBinaryOpcodeNotSupported,[OpcodeStrings[El.OpCode]],El);
- end;
- if (Result=Nil) and (C<>Nil) then
- begin
- R:=TJSBinary(CreateElement(C,El));
- R.A:=A; A:=nil;
- R.B:=B; B:=nil;
- Result:=R;
- if El.OpCode=eopDiv then
- begin
- // convert "a div b" to "Math.floor(a/b)"
- Call:=CreateCallExpression(El);
- Call.Args.Elements.AddElement.Expr:=R;
- Call.Expr:=CreateBuiltInIdentifierExpr('Math.floor');
- Result:=Call;
- end;
- end;
- finally
- if Result=nil then
- begin
- A.Free;
- B.Free;
- end;
- end;
- end;
- function TPasToJSConverter.ConvertBinaryExpressionRes(El: TBinaryExpr;
- AContext: TConvertContext; const LeftResolved,
- RightResolved: TPasResolverResult; var A, B: TJSElement): TJSElement;
- function CreateEqualCallback: TJSElement;
- var
- Call: TJSCallExpression;
- NotEl: TJSUnaryNotExpression;
- begin
- // convert "proctypeA = proctypeB" to "rtl.eqCallback(proctypeA,proctypeB)"
- Call:=CreateCallExpression(El);
- Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnProcType_Equal]]);
- Call.Args.Elements.AddElement.Expr:=A;
- A:=nil;
- Call.Args.Elements.AddElement.Expr:=B;
- B:=nil;
- if El.OpCode=eopNotEqual then
- begin
- // convert "proctypeA <> proctypeB" to "!rtl.eqCallback(proctypeA,proctypeB)"
- NotEl:=TJSUnaryNotExpression(CreateElement(TJSUnaryNotExpression,El));
- NotEl.A:=Call;
- Result:=NotEl;
- end
- else
- Result:=Call;
- end;
- var
- FunName: String;
- Call: TJSCallExpression;
- Bracket: TJSBracketMemberExpression;
- DotExpr: TJSDotMemberExpression;
- NotEl: TJSUnaryNotExpression;
- begin
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.ConvertBinaryExpressionRes OpCode="',OpcodeStrings[El.OpCode],'" Left=',GetResolverResultDesc(LeftResolved),' Right=',GetResolverResultDesc(RightResolved));
- {$ENDIF}
- Result:=nil;
- if LeftResolved.BaseType=btSet then
- begin
- // set operators -> rtl.operatorfunction(a,b)
- case El.OpCode of
- eopAdd: FunName:=FBuiltInNames[pbifnSet_Union];
- eopSubtract: FunName:=FBuiltInNames[pbifnSet_Difference];
- eopMultiply: FunName:=FBuiltInNames[pbifnSet_Intersect];
- eopSymmetricaldifference: FunName:=FBuiltInNames[pbifnSet_SymDiffSet];
- eopEqual: FunName:=FBuiltInNames[pbifnSet_Equal];
- eopNotEqual: FunName:=FBuiltInNames[pbifnSet_NotEqual];
- eopGreaterThanEqual: FunName:=FBuiltInNames[pbifnSet_GreaterEqual];
- eopLessthanEqual: FunName:=FBuiltInNames[pbifnSet_LowerEqual];
- else
- DoError(20170209151300,nBinaryOpcodeNotSupported,sBinaryOpcodeNotSupported,[OpcodeStrings[El.OpCode]],El);
- end;
- Call:=CreateCallExpression(El);
- Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FunName]);
- Call.Args.Elements.AddElement.Expr:=A;
- A:=nil;
- Call.Args.Elements.AddElement.Expr:=B;
- B:=nil;
- Result:=Call;
- exit;
- end
- else if (RightResolved.BaseType=btSet) and (El.OpCode=eopIn) then
- begin
- // a in b -> b[a]
- Bracket:=TJSBracketMemberExpression(CreateElement(TJSBracketMemberExpression,El));
- Bracket.MExpr:=B;
- B:=nil;
- Bracket.Name:=A;
- A:=nil;
- Result:=Bracket;
- exit;
- end
- else if (El.OpCode=eopIs) then
- begin
- // "A is B"
- Call:=CreateCallExpression(El);
- Result:=Call;
- Call.Args.Elements.AddElement.Expr:=A; A:=nil;
- if RightResolved.IdentEl is TPasClassOfType then
- begin
- // "A is class-of-type" -> "A is class"
- FreeAndNil(B);
- B:=CreateReferencePathExpr(TPasClassOfType(RightResolved.IdentEl).DestType,AContext);
- end;
- if (RightResolved.TypeEl is TPasClassType) and TPasClassType(RightResolved.TypeEl).IsExternal then
- begin
- // B is an external class -> "rtl.isExt(A,B)"
- Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnIsExt]]);
- Call.Args.Elements.AddElement.Expr:=B; B:=nil;
- end
- else if LeftResolved.TypeEl is TPasClassOfType then
- begin
- // A is a TPasClassOfType -> "rtl.is(A,B)"
- Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnIs]]);
- Call.Args.Elements.AddElement.Expr:=B; B:=nil;
- end
- else
- begin
- // use directly "B.isPrototypeOf(A)"
- DotExpr:=TJSDotMemberExpression(CreateElement(TJSDotMemberExpression,El));
- DotExpr.MExpr:=B; B:=nil;
- DotExpr.Name:='isPrototypeOf';
- Call.Expr:=DotExpr;
- end;
- exit;
- end
- else if (El.OpCode in [eopEqual,eopNotEqual]) then
- begin
- if AContext.Resolver.IsProcedureType(LeftResolved,true) then
- begin
- if RightResolved.BaseType=btNil then
- else if AContext.Resolver.IsProcedureType(RightResolved,true)
- or AContext.Resolver.IsJSBaseType(RightResolved,pbtJSValue,true) then
- exit(CreateEqualCallback);
- end
- else if AContext.Resolver.IsProcedureType(RightResolved,true) then
- begin
- if LeftResolved.BaseType=btNil then
- else if AContext.Resolver.IsJSBaseType(LeftResolved,pbtJSValue,true) then
- exit(CreateEqualCallback);
- end
- else if LeftResolved.TypeEl is TPasRecordType then
- begin
- // convert "recordA = recordB" to "recordA.$equal(recordB)"
- Call:=CreateCallExpression(El);
- Call.Expr:=CreateDotExpression(El,A,CreateBuiltInIdentifierExpr(FBuiltInNames[pbifnRecordEqual]));
- A:=nil;
- Call.Args.Elements.AddElement.Expr:=B;
- B:=nil;
- if El.OpCode=eopNotEqual then
- begin
- // convert "recordA = recordB" to "!recordA.$equal(recordB)"
- NotEl:=TJSUnaryNotExpression(CreateElement(TJSUnaryNotExpression,El));
- NotEl.A:=Call;
- Result:=NotEl;
- end
- else
- Result:=Call;
- exit;
- end
- else if LeftResolved.TypeEl is TPasArrayType then
- begin
- if RightResolved.BaseType=btNil then
- begin
- // convert "array = nil" to "rtl.length(array) > 0"
- FreeAndNil(B);
- Result:=CreateCmpArrayWithNil(El,A,El.OpCode);
- A:=nil;
- exit;
- end;
- end
- else if RightResolved.TypeEl is TPasArrayType then
- begin
- if LeftResolved.BaseType=btNil then
- begin
- // convert "nil = array" to "0 < rtl.length(array)"
- FreeAndNil(A);
- Result:=CreateCmpArrayWithNil(El,B,El.OpCode);
- B:=nil;
- exit;
- end;
- end;
- end;
- end;
- function TPasToJSConverter.ConvertSubIdentExpression(El: TBinaryExpr;
- AContext: TConvertContext): TJSElement;
- // connect El.left and El.right with a dot.
- var
- Left, Right: TJSElement;
- DotContext: TDotContext;
- OldAccess: TCtxAccess;
- LeftResolved: TPasResolverResult;
- RightRef: TResolvedReference;
- ParamsExpr: TParamsExpr;
- RightEl: TPasExpr;
- begin
- Result:=nil;
- ParamsExpr:=nil;;
- RightEl:=El.right;
- while RightEl.ClassType=TParamsExpr do
- begin
- ParamsExpr:=TParamsExpr(RightEl);
- RightEl:=ParamsExpr.Value;
- end;
- if (RightEl.ClassType=TPrimitiveExpr)
- and (RightEl.CustomData is TResolvedReference) then
- begin
- RightRef:=TResolvedReference(RightEl.CustomData);
- if IsExternalClassConstructor(RightRef.Declaration) then
- begin
- if ParamsExpr<>nil then
- begin
- // left side is done in ConvertFuncParams
- Result:=ConvertParamsExpression(El.right as TParamsExpr,AContext);
- end
- else
- Result:=ConvertExternalConstructor(El.left,RightRef,nil,AContext);
- exit;
- end;
- end;
- if AContext.Resolver<>nil then
- begin
- AContext.Resolver.ComputeElement(El.left,LeftResolved,[]);
- if LeftResolved.BaseType=btModule then
- begin
- // e.g. System.ExitCode
- // unit prefix is automatically created -> omit
- Result:=ConvertElement(El.right,AContext);
- exit;
- end;
- end;
- // convert left side
- OldAccess:=AContext.Access;
- AContext.Access:=caRead;
- Left:=ConvertElement(El.left,AContext);
- if Left=nil then
- RaiseInconsistency(20170201140821);
- AContext.Access:=OldAccess;
- // convert right side
- DotContext:=TDotContext.Create(El,Left,AContext);
- Right:=nil;
- try
- DotContext.LeftResolved:=LeftResolved;
- Right:=ConvertElement(El.right,DotContext);
- finally
- DotContext.Free;
- if Right=nil then
- Left.Free;
- end;
- // connect via dot
- Result:=CreateDotExpression(El,Left,Right);
- end;
- function TPasToJSConverter.CreateIdentifierExpr(AName: string; El: TPasElement;
- AContext: TConvertContext): TJSPrimaryExpressionIdent;
- Var
- I : TJSPrimaryExpressionIdent;
- begin
- I:=TJSPrimaryExpressionIdent(CreateElement(TJSPrimaryExpressionIdent,El));
- AName:=TransformVariableName(El,AName,AContext);
- I.Name:=TJSString(AName);
- Result:=I;
- end;
- function TPasToJSConverter.CreateDeclNameExpression(El: TPasElement;
- const Name: string; AContext: TConvertContext): TJSPrimaryExpressionIdent;
- var
- CurName: String;
- begin
- CurName:=TransformVariableName(El,Name,AContext);
- if (FBuiltInNames[pbivnImplementation]<>'') and (El.Parent.ClassType=TImplementationSection) then
- CurName:=FBuiltInNames[pbivnImplementation]+'.'+CurName
- else
- CurName:='this.'+CurName;
- Result:=TJSPrimaryExpressionIdent(CreateElement(TJSPrimaryExpressionIdent,El));
- Result.Name:=TJSString(CurName);
- end;
- function TPasToJSConverter.ConvertPrimitiveExpression(El: TPrimitiveExpr;
- AContext: TConvertContext): TJSElement;
- Var
- L : TJSLiteral;
- Number : TJSNumber;
- ConversionError : Integer;
- i: Int64;
- S: String;
- begin
- {$IFDEF VerbosePas2JS}
- str(El.Kind,S);
- writeln('TPasToJSConverter.ConvertPrimitiveExpression El=',GetObjName(El),' Context=',GetObjName(AContext),' El.Kind=',S);
- {$ENDIF}
- Result:=Nil;
- case El.Kind of
- pekString:
- begin
- if AContext.Resolver<>nil then
- Result:=CreateLiteralJSString(El,
- AContext.Resolver.ExtractPasStringLiteral(El,El.Value))
- else
- begin
- S:=AnsiDequotedStr(El.Value,'''');
- Result:=CreateLiteralString(El,S);
- end;
- //writeln('TPasToJSConverter.ConvertPrimitiveExpression Result="',TJSLiteral(Result).Value.AsString,'" ',GetObjName(AContext.Resolver));
- end;
- pekNumber:
- begin
- case El.Value[1] of
- '0'..'9':
- begin
- Val(El.Value,Number,ConversionError);
- if ConversionError<>0 then
- DoError(20161024191248,nInvalidNumber,sInvalidNumber,[El.Value],El);
- L:=CreateLiteralNumber(El,Number);
- if El.Value[1] in ['0'..'9'] then
- L.Value.CustomValue:=TJSString(El.Value);
- end;
- '$','&','%':
- begin
- i:=StrToInt64Def(El.Value,-1);
- if i<0 then
- DoError(20161024224442,nInvalidNumber,sInvalidNumber,[El.Value],El);
- Number:=i;
- if Number<>i then
- // number was rounded -> we lost precision
- DoError(20161024230812,nInvalidNumber,sInvalidNumber,[El.Value],El);
- L:=CreateLiteralNumber(El,Number);
- S:=copy(El.Value,2,length(El.Value));
- case El.Value[1] of
- '$': S:='0x'+S;
- '&': if TargetProcessor=ProcessorECMAScript5 then
- S:='0'+S
- else
- S:='0o'+S;
- '%': if TargetProcessor=ProcessorECMAScript5 then
- S:=''
- else
- S:='0b'+S;
- end;
- L.Value.CustomValue:=TJSString(S);
- end;
- else
- DoError(20161024223232,nInvalidNumber,sInvalidNumber,[El.Value],El);
- end;
- Result:=L;
- end;
- pekIdent:
- Result:=ConvertIdentifierExpr(El,AContext);
- else
- RaiseNotSupported(El,AContext,20161024222543);
- end;
- end;
- function TPasToJSConverter.ConvertIdentifierExpr(El: TPrimitiveExpr;
- AContext: TConvertContext): TJSElement;
- var
- Decl: TPasElement;
- Name: String;
- Ref: TResolvedReference;
- Call: TJSCallExpression;
- BuiltInProc: TResElDataBuiltInProc;
- Prop: TPasProperty;
- ImplicitCall: Boolean;
- AssignContext: TAssignContext;
- Arg: TPasArgument;
- ParamContext: TParamContext;
- ResolvedEl: TPasResolverResult;
- ProcType: TPasProcedureType;
- begin
- Result:=nil;
- if AContext=nil then ;
- if El.Kind<>pekIdent then
- RaiseInconsistency(20161024191255);
- if El.CustomData is TResolvedReference then
- begin
- Ref:=TResolvedReference(El.CustomData);
- Decl:=Ref.Declaration;
- if IsExternalClassConstructor(Decl) then
- begin
- // create external object/function
- Result:=ConvertExternalConstructor(nil,Ref,nil,AContext);
- exit;
- end;
- if [rrfNewInstance,rrfFreeInstance]*Ref.Flags<>[] then
- begin
- // call constructor, destructor
- Result:=CreateFreeOrNewInstanceExpr(Ref,AContext);
- exit;
- end;
- Prop:=nil;
- AssignContext:=nil;
- ImplicitCall:=rrfImplicitCallWithoutParams in Ref.Flags;
- if Decl.ClassType=TPasProperty then
- begin
- // Decl is a property -> redirect to getter/setter
- Prop:=TPasProperty(Decl);
- case AContext.Access of
- caAssign:
- begin
- Decl:=AContext.Resolver.GetPasPropertySetter(Prop);
- if Decl is TPasProcedure then
- begin
- AssignContext:=AContext.AccessContext as TAssignContext;
- if AssignContext.Call<>nil then
- RaiseNotSupported(El,AContext,20170206000310);
- AssignContext.PropertyEl:=Prop;
- AssignContext.Setter:=Decl;
- // Setter
- Call:=CreateCallExpression(El);
- AssignContext.Call:=Call;
- Call.Expr:=CreateReferencePathExpr(Decl,AContext,false,Ref);
- Call.Args.Elements.AddElement.Expr:=AssignContext.RightSide;
- AssignContext.RightSide:=nil;
- Result:=Call;
- exit;
- end;
- end;
- caRead:
- begin
- Decl:=AContext.Resolver.GetPasPropertyGetter(Prop);
- if (Decl is TPasFunction) and (Prop.Args.Count=0) then
- ImplicitCall:=true;
- end;
- else
- RaiseNotSupported(El,AContext,20170213212623);
- end;
- end
- else if Decl.ClassType=TPasArgument then
- begin
- Arg:=TPasArgument(Decl);
- if Arg.Access in [argVar,argOut] then
- begin
- // Arg is a reference object
- case AContext.Access of
- caRead:
- begin
- // create arg.get()
- Call:=CreateCallExpression(El);
- Call.Expr:=CreateDotExpression(El,
- CreateIdentifierExpr(Arg.Name,Arg,AContext),
- CreateBuiltInIdentifierExpr(TempRefObjGetterName));
- Result:=Call;
- exit;
- end;
- caAssign:
- begin
- // create arg.set(RHS)
- AssignContext:=AContext.AccessContext as TAssignContext;
- if AssignContext.Call<>nil then
- RaiseNotSupported(El,AContext,20170214120606);
- Call:=CreateCallExpression(El);
- AssignContext.Call:=Call;
- Call.Expr:=CreateDotExpression(El,
- CreateIdentifierExpr(Arg.Name,Arg,AContext),
- CreateBuiltInIdentifierExpr(TempRefObjSetterName));
- Call.Args.Elements.AddElement.Expr:=AssignContext.RightSide;
- AssignContext.RightSide:=nil;
- Result:=Call;
- exit;
- end;
- caByReference:
- begin
- // simply pass the reference
- ParamContext:=AContext.AccessContext as TParamContext;
- ParamContext.ReusingReference:=true;
- Result:=CreateIdentifierExpr(Arg.Name,Arg,AContext);
- exit;
- end;
- else
- RaiseNotSupported(El,AContext,20170214120739);
- end;
- end;
- end;
- //writeln('TPasToJSConverter.ConvertPrimitiveExpression pekIdent TResolvedReference ',GetObjName(Ref.Declaration),' ',GetObjName(Ref.Declaration.CustomData));
- if Decl.CustomData is TResElDataBuiltInProc then
- begin
- BuiltInProc:=TResElDataBuiltInProc(Decl.CustomData);
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.ConvertPrimitiveExpression ',Decl.Name,' ',ResolverBuiltInProcNames[BuiltInProc.BuiltIn]);
- {$ENDIF}
- case BuiltInProc.BuiltIn of
- bfBreak: Result:=ConvertBuiltInBreak(El,AContext);
- bfContinue: Result:=ConvertBuiltInContinue(El,AContext);
- bfExit: Result:=ConvertBuiltInExit(El,AContext);
- else
- RaiseNotSupported(El,AContext,20161130164955,'built in proc '+ResolverBuiltInProcNames[BuiltInProc.BuiltIn]);
- end;
- if Result=nil then
- RaiseInconsistency(20170214120048);
- exit;
- end;
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.ConvertIdentifierExpr ',GetObjName(El),' Decl=',GetObjName(Decl),' Decl.Parent=',GetObjName(Decl.Parent));
- {$ENDIF}
- if Decl is TPasModule then
- Name:=FBuiltInNames[pbivnModules]+'.'+TransformModuleName(TPasModule(Decl),AContext)
- else if (Decl is TPasFunctionType) and (CompareText(ResolverResultVar,El.Value)=0) then
- Name:=ResolverResultVar
- else if Decl.ClassType=TPasEnumValue then
- begin
- if UseEnumNumbers then
- begin
- Result:=CreateLiteralNumber(El,(Decl.Parent as TPasEnumType).Values.IndexOf(Decl));
- exit;
- end
- else
- begin
- // enums always need the full path
- Name:=CreateReferencePath(Decl,AContext,rpkPathAndName,true);
- end;
- end
- else
- Name:=CreateReferencePath(Decl,AContext,rpkPathAndName,false,Ref);
- if Result=nil then
- Result:=CreateBuiltInIdentifierExpr(Name);
- if ImplicitCall then
- begin
- // create a call with default parameters
- ProcType:=nil;
- if Decl is TPasProcedure then
- ProcType:=TPasProcedure(Decl).ProcType
- else
- begin
- AContext.Resolver.ComputeElement(El,ResolvedEl,[rcNoImplicitProc]);
- if ResolvedEl.TypeEl is TPasProcedureType then
- ProcType:=TPasProcedureType(ResolvedEl.TypeEl)
- else
- RaiseNotSupported(El,AContext,20170217005025);
- end;
- Call:=nil;
- try
- CreateProcedureCall(Call,nil,ProcType,AContext);
- Call.Expr:=Result;
- Result:=Call;
- finally
- if Result<>Call then
- Call.Free;
- end;
- end;
- end
- else if AContext.Resolver<>nil then
- RaiseIdentifierNotFound(El.Value,El,20161024191306)
- else
- // simple mode
- Result:=CreateIdentifierExpr(El.Value,El,AContext);
- end;
- function TPasToJSConverter.ConvertBoolConstExpression(El: TBoolConstExpr;
- AContext: TConvertContext): TJSElement;
- begin
- if AContext=nil then ;
- Result:=CreateLiteralBoolean(El,El.Value);
- end;
- function TPasToJSConverter.ConvertNilExpr(El: TNilExpr;
- AContext: TConvertContext): TJSElement;
- begin
- if AContext=nil then ;
- Result:=CreateLiteralNull(El);
- end;
- function TPasToJSConverter.ConvertInheritedExpression(El: TInheritedExpr;
- AContext: TConvertContext): TJSElement;
- function CreateAncestorCall(ParentEl: TPasElement; Apply: boolean;
- AncestorProc: TPasProcedure; ParamsExpr: TParamsExpr): TJSElement;
- var
- FunName: String;
- Call: TJSCallExpression;
- ThisContext: TFunctionContext;
- Proc: TPasProcedure;
- ProcScope: TPasProcedureScope;
- ClassScope, AncestorScope: TPasClassScope;
- AncestorClass: TPasClassType;
- begin
- Result:=nil;
- if (AncestorProc.Parent is TPasClassType)
- and TPasClassType(AncestorProc.Parent).IsExternal then
- begin
- // ancestor is in an external class
- // They could be overriden, without a Pascal declaration
- // -> use the direct ancestor class of the current proc
- ThisContext:=AContext.GetThisContext;
- Proc:=ThisContext.PasElement as TPasProcedure;
- ProcScope:=TPasProcedureScope(Proc.CustomData);
- ClassScope:=ProcScope.ClassScope;
- if ClassScope=nil then
- RaiseInconsistency(20170323111252);
- AncestorScope:=ClassScope.AncestorScope;
- if AncestorScope=nil then
- RaiseInconsistency(20170323111306);
- AncestorClass:=AncestorScope.Element as TPasClassType;
- FunName:=CreateReferencePath(AncestorClass,AContext,rpkPathAndName,true)
- +'.'+TransformVariableName(AncestorProc,AContext);
- end
- else
- FunName:=CreateReferencePath(AncestorProc,AContext,rpkPathAndName,true);
- if Apply then
- // create "ancestor.funcname.apply(this,arguments)"
- FunName:=FunName+'.apply'
- else
- // create "ancestor.funcname.call(this,param1,param2,...)"
- FunName:=FunName+'.call';
- Call:=nil;
- try
- Call:=CreateCallExpression(ParentEl);
- Call.Expr:=CreateBuiltInIdentifierExpr(FunName);
- Call.Args.Elements.AddElement.Expr:=CreateBuiltInIdentifierExpr('this');
- if Apply then
- Call.Args.Elements.AddElement.Expr:=CreateBuiltInIdentifierExpr('arguments')
- else
- CreateProcedureCall(Call,ParamsExpr,AncestorProc.ProcType,AContext);
- Result:=Call;
- finally
- if Result=nil then
- Call.Free;
- end;
- end;
- var
- Right: TPasExpr;
- Ref: TResolvedReference;
- PrimExpr: TPrimitiveExpr;
- AncestorProc: TPasProcedure;
- ParamsExpr: TParamsExpr;
- begin
- Result:=nil;
- if (El.Parent is TBinaryExpr) and (TBinaryExpr(El.Parent).OpCode=eopNone)
- and (TBinaryExpr(El.Parent).left=El) then
- begin
- // "inherited <name>"
- AncestorProc:=nil;
- ParamsExpr:=nil;
- Right:=TBinaryExpr(El.Parent).right;
- if Right.ClassType=TPrimitiveExpr then
- begin
- PrimExpr:=TPrimitiveExpr(Right);
- Ref:=PrimExpr.CustomData as TResolvedReference;
- if rrfImplicitCallWithoutParams in Ref.Flags then
- begin
- // inherited <function>
- // -> create "AncestorProc.call(this,defaultargs)"
- AncestorProc:=Ref.Declaration as TPasProcedure;
- end
- else
- begin
- // inherited <varname>
- // all variables have unique names -> simply access it
- Result:=ConvertPrimitiveExpression(PrimExpr,AContext);
- exit;
- end;
- end
- else if Right.ClassType=TParamsExpr then
- begin
- ParamsExpr:=TParamsExpr(Right);
- if ParamsExpr.Kind=pekFuncParams then
- begin
- if ParamsExpr.Value is TPrimitiveExpr then
- begin
- // inherited <function>(args)
- // -> create "AncestorProc.call(this,args,defaultargs)"
- PrimExpr:=TPrimitiveExpr(ParamsExpr.Value);
- Ref:=PrimExpr.CustomData as TResolvedReference;
- AncestorProc:=Ref.Declaration as TPasProcedure;
- end;
- end
- else
- begin
- // inherited <varname>[]
- // all variables have unique names -> simply access it
- Result:=ConvertElement(Right,AContext);
- exit;
- end;
- end;
- if AncestorProc=nil then
- begin
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.ConvertInheritedExpression Right=',GetObjName(Right));
- {$ENDIF}
- RaiseNotSupported(El,AContext,20170201190824);
- end;
- //writeln('TPasToJSConverter.ConvertInheritedExpression Func=',GetObjName(FuncContext.PasElement));
- Result:=CreateAncestorCall(Right,false,AncestorProc,ParamsExpr);
- end
- else
- begin
- // "inherited;"
- if El.CustomData=nil then
- exit; // "inherited;" when there is no AncestorProc proc -> silently ignore
- // create "AncestorProc.apply(this,arguments)"
- Ref:=TResolvedReference(El.CustomData);
- AncestorProc:=Ref.Declaration as TPasProcedure;
- Result:=CreateAncestorCall(El,true,AncestorProc,nil);
- end;
- end;
- function TPasToJSConverter.ConvertSelfExpression(El: TSelfExpr;
- AContext: TConvertContext): TJSElement;
- begin
- if AContext=nil then ;
- Result:=TJSPrimaryExpressionThis(CreateElement(TJSPrimaryExpressionThis,El));
- end;
- function TPasToJSConverter.ConvertParamsExpression(El: TParamsExpr;
- AContext: TConvertContext): TJSElement;
- begin
- Result:=Nil;
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.ConvertParamsExpression ',GetObjName(El),' El.Kind=',ExprKindNames[El.Kind]);
- {$ENDIF}
- Case El.Kind of
- pekFuncParams:
- Result:=ConvertFuncParams(El,AContext);
- pekArrayParams:
- Result:=ConvertArrayParams(El,AContext);
- pekSet:
- Result:=ConvertSetLiteral(El,AContext);
- else
- RaiseNotSupported(El,AContext,20170209103235,ExprKindNames[El.Kind]);
- end;
- end;
- function TPasToJSConverter.ConvertArrayParams(El: TParamsExpr;
- AContext: TConvertContext): TJSElement;
- var
- ArgContext: TConvertContext;
- function GetValueReference: TResolvedReference;
- var
- Value: TPasExpr;
- begin
- Result:=nil;
- Value:=El.Value;
- if (Value.ClassType=TPrimitiveExpr)
- and (Value.CustomData is TResolvedReference) then
- exit(TResolvedReference(Value.CustomData));
- end;
- procedure ConvertStringBracket;
- var
- Call: TJSCallExpression;
- Param: TPasExpr;
- Expr: TJSAdditiveExpressionMinus;
- DotExpr: TJSDotMemberExpression;
- AssignContext: TAssignContext;
- Elements: TJSArrayLiteralElements;
- AssignSt: TJSSimpleAssignStatement;
- OldAccess: TCtxAccess;
- begin
- Param:=El.Params[0];
- case AContext.Access of
- caAssign:
- begin
- // s[index] := value -> s = rtl.setCharAt(s,index,value)
- AssignContext:=AContext.AccessContext as TAssignContext;
- AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El));
- try
- OldAccess:=AContext.Access;
- AContext.Access:=caRead;
- AssignSt.LHS:=ConvertElement(El.Value,AContext);
- // rtl.setCharAt
- Call:=CreateCallExpression(El);
- AssignContext.Call:=Call;
- AssignSt.Expr:=Call;
- Elements:=Call.Args.Elements;
- Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnSetCharAt]]);
- // first param s
- Elements.AddElement.Expr:=ConvertElement(El.Value,AContext);
- AContext.Access:=OldAccess;
- // second param index
- Elements.AddElement.Expr:=ConvertElement(Param,ArgContext);
- // third param value
- Elements.AddElement.Expr:=AssignContext.RightSide;
- AssignContext.RightSide:=nil;
- Result:=AssignSt
- finally
- if Result=nil then
- AssignSt.Free;
- end;
- end;
- caRead:
- begin
- Call:=CreateCallExpression(El);
- Elements:=Call.Args.Elements;
- try
- // s[index] -> s.charAt(index-1)
- // add string accessor
- DotExpr:=TJSDotMemberExpression(CreateElement(TJSDotMemberExpression,El));
- Call.Expr:=DotExpr;
- DotExpr.MExpr:=ConvertElement(El.Value,AContext);
- DotExpr.Name:='charAt';
- // add parameter "index-1"
- Expr:=TJSAdditiveExpressionMinus(CreateElement(TJSAdditiveExpressionMinus,Param));
- Elements.AddElement.Expr:=Expr;
- Expr.A:=ConvertElement(Param,ArgContext);
- Expr.B:=CreateLiteralNumber(Param,1);
- Result:=Call;
- finally
- if Result=nil then
- Call.Free;
- end;
- end;
- else
- RaiseNotSupported(El,AContext,20170213213101);
- end;
- end;
- procedure ConvertArray(ArrayEl: TPasArrayType);
- var
- B, Sub: TJSBracketMemberExpression;
- i, ArgNo: Integer;
- Arg: TJSElement;
- OldAccess: TCtxAccess;
- begin
- B:=TJSBracketMemberExpression(CreateElement(TJSBracketMemberExpression,El));
- try
- // add read accessor
- OldAccess:=AContext.Access;
- AContext.Access:=caRead;
- B.MExpr:=ConvertElement(El.Value,AContext);
- AContext.Access:=OldAccess;
- Result:=B;
- ArgNo:=0;
- repeat
- // Note: dynamic array has length(ArrayEl.Ranges)=0
- for i:=1 to Max(length(ArrayEl.Ranges),1) do
- begin
- // add parameter
- ArgContext.Access:=caRead;
- Arg:=ConvertElement(El.Params[ArgNo],ArgContext);
- ArgContext.Access:=OldAccess;
- if B.Name<>nil then
- begin
- Sub:=B;
- B:=TJSBracketMemberExpression(CreateElement(TJSBracketMemberExpression,El));
- B.MExpr:=Sub;
- end;
- B.Name:=Arg;
- inc(ArgNo);
- if ArgNo>length(El.Params) then
- RaiseInconsistency(20170206180553);
- end;
- if ArgNo=length(El.Params) then
- break;
- // continue in sub array
- ArrayEl:=AContext.Resolver.ResolveAliasType(ArrayEl.ElType) as TPasArrayType;
- until false;
- Result:=B;
- finally
- if Result=nil then
- B.Free;
- end;
- end;
- procedure ConvertJSObject;
- var
- B: TJSBracketMemberExpression;
- OldAccess: TCtxAccess;
- begin
- B:=TJSBracketMemberExpression(CreateElement(TJSBracketMemberExpression,El));
- try
- // add read accessor
- OldAccess:=AContext.Access;
- AContext.Access:=caRead;
- B.MExpr:=ConvertElement(El.Value,AContext);
- AContext.Access:=OldAccess;
- // add parameter
- ArgContext.Access:=caRead;
- B.Name:=ConvertElement(El.Params[0],ArgContext);
- ArgContext.Access:=OldAccess;
- Result:=B;
- finally
- if Result=nil then
- B.Free;
- end;
- end;
- function IsJSBracketAccessorAndConvert(Prop: TPasProperty;
- AccessEl: TPasElement;
- AContext: TConvertContext; ChompPropName: boolean): boolean;
- // If El.Value contains property name set ChompPropName = true
- var
- Bracket: TJSBracketMemberExpression;
- OldAccess: TCtxAccess;
- PathEl: TPasExpr;
- Ref: TResolvedReference;
- Path: String;
- begin
- if not AContext.Resolver.IsExternalBracketAccessor(AccessEl) then
- exit(false);
- Result:=true;
- // bracket accessor of external class
- if Prop.Args.Count<>1 then
- RaiseInconsistency(20170403003753);
- // bracket accessor of external class -> create PathEl[param]
- Bracket:=TJSBracketMemberExpression(CreateElement(TJSBracketMemberExpression,Prop));
- try
- PathEl:=El.Value;
- if ChompPropName then
- begin
- if (PathEl is TPrimitiveExpr)
- and (TPrimitiveExpr(PathEl).Kind=pekIdent)
- and (PathEl.CustomData is TResolvedReference) then
- begin
- // propname without path, e.g. propname[param]
- Ref:=TResolvedReference(PathEl.CustomData);
- Path:=CreateReferencePath(Prop,AContext,rpkPath,false,Ref);
- if Path<>'' then
- Bracket.MExpr:=CreateBuiltInIdentifierExpr(Path);
- PathEl:=nil;
- end
- else if (PathEl is TBinaryExpr)
- and (TBinaryExpr(PathEl).OpCode=eopSubIdent)
- and (TBinaryExpr(PathEl).right is TPrimitiveExpr)
- and (TPrimitiveExpr(TBinaryExpr(PathEl).right).Kind=pekIdent) then
- begin
- // instance.propname[param] -> instance[param]
- PathEl:=TBinaryExpr(PathEl).left;
- end
- else
- RaiseNotSupported(El.Value,AContext,20170402225050);
- end;
- if (PathEl<>nil) and (Bracket.MExpr=nil) then
- begin
- OldAccess:=AContext.Access;
- AContext.Access:=caRead;
- Bracket.MExpr:=ConvertElement(PathEl,AContext);
- AContext.Access:=OldAccess;
- end;
- OldAccess:=ArgContext.Access;
- ArgContext.Access:=caRead;
- Bracket.Name:=ConvertElement(El.Params[0],AContext);
- ArgContext.Access:=OldAccess;
- ConvertArrayParams:=Bracket;
- Bracket:=nil;
- finally
- Bracket.Free;
- end;
- end;
- procedure ConvertIndexProperty(Prop: TPasProperty; AContext: TConvertContext);
- var
- Call: TJSCallExpression;
- i: Integer;
- TargetArg: TPasArgument;
- Elements: TJSArrayLiteralElements;
- Arg: TJSElement;
- AccessEl: TPasElement;
- AssignContext: TAssignContext;
- OldAccess: TCtxAccess;
- begin
- Result:=nil;
- AssignContext:=nil;
- Call:=CreateCallExpression(El);
- try
- case AContext.Access of
- caAssign:
- begin
- AccessEl:=AContext.Resolver.GetPasPropertySetter(Prop);
- if IsJSBracketAccessorAndConvert(Prop,AccessEl,AContext,true) then
- exit;
- AssignContext:=AContext.AccessContext as TAssignContext;
- AssignContext.PropertyEl:=Prop;
- AssignContext.Setter:=AccessEl;
- AssignContext.Call:=Call;
- end;
- caRead:
- begin
- AccessEl:=AContext.Resolver.GetPasPropertyGetter(Prop);
- if IsJSBracketAccessorAndConvert(Prop,AccessEl,AContext,true) then
- exit;
- end
- else
- RaiseNotSupported(El,AContext,20170213213317);
- end;
- Call.Expr:=CreateReferencePathExpr(AccessEl,AContext,false,GetValueReference);
- Elements:=Call.Args.Elements;
- OldAccess:=ArgContext.Access;
- // add params
- i:=0;
- while i<Prop.Args.Count do
- begin
- TargetArg:=TPasArgument(Prop.Args[i]);
- Arg:=CreateProcCallArg(El.Params[i],TargetArg,ArgContext);
- Elements.AddElement.Expr:=Arg;
- inc(i);
- end;
- // fill up default values
- while i<Prop.Args.Count do
- begin
- TargetArg:=TPasArgument(Prop.Args[i]);
- if TargetArg.ValueExpr=nil then
- begin
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.ConvertArrayParams.ConvertIndexProperty missing default value: Prop=',Prop.Name,' i=',i);
- {$ENDIF}
- RaiseInconsistency(20170206185126);
- end;
- AContext.Access:=caRead;
- Arg:=ConvertElement(TargetArg.ValueExpr,ArgContext);
- Elements.AddElement.Expr:=Arg;
- inc(i);
- end;
- // finally add as last parameter the value
- if AssignContext<>nil then
- begin
- Elements.AddElement.Expr:=AssignContext.RightSide;
- AssignContext.RightSide:=nil;
- end;
- ArgContext.Access:=OldAccess;
- Result:=Call;
- finally
- if Result=nil then
- begin
- if (AssignContext<>nil) and (AssignContext.Call=Call) then
- AssignContext.Call:=nil;
- Call.Free;
- end;
- end;
- end;
- procedure ConvertDefaultProperty(const ResolvedEl: TPasResolverResult;
- Prop: TPasProperty);
- var
- DotContext: TDotContext;
- Left, Right: TJSElement;
- OldAccess: TCtxAccess;
- AccessEl, SetAccessEl: TPasElement;
- begin
- case AContext.Access of
- caAssign:
- begin
- AccessEl:=AContext.Resolver.GetPasPropertySetter(Prop);
- if IsJSBracketAccessorAndConvert(Prop,AccessEl,AContext,false) then
- exit;
- end;
- caRead:
- begin
- AccessEl:=AContext.Resolver.GetPasPropertyGetter(Prop);
- if IsJSBracketAccessorAndConvert(Prop,AccessEl,AContext,false) then
- exit;
- end;
- caByReference:
- begin
- //ParamContext:=AContext.AccessContext as TParamContext;
- AccessEl:=AContext.Resolver.GetPasPropertyGetter(Prop);
- SetAccessEl:=AContext.Resolver.GetPasPropertySetter(Prop);
- if AContext.Resolver.IsExternalBracketAccessor(AccessEl) then
- begin
- if AContext.Resolver.IsExternalBracketAccessor(SetAccessEl) then
- begin
- // read and write are brackets -> easy
- if not IsJSBracketAccessorAndConvert(Prop,AccessEl,AContext,false) then
- RaiseNotSupported(El,AContext,20170405090845);
- exit;
- end;
- end;
- RaiseNotSupported(El,AContext,20170403000550);
- end;
- else
- RaiseNotSupported(El,AContext,20170402233834);
- end;
- DotContext:=nil;
- Left:=nil;
- Right:=nil;
- try
- OldAccess:=AContext.Access;
- AContext.Access:=caRead;
- Left:=ConvertElement(El.Value,AContext);
- AContext.Access:=OldAccess;
- DotContext:=TDotContext.Create(El.Value,Left,AContext);
- DotContext.LeftResolved:=ResolvedEl;
- ConvertIndexProperty(Prop,DotContext);
- Right:=Result;
- Result:=nil;
- finally
- DotContext.Free;
- if Right=nil then
- Left.Free;
- end;
- Result:=CreateDotExpression(El,Left,Right);
- end;
- Var
- ResolvedEl: TPasResolverResult;
- TypeEl: TPasType;
- ClassScope: TPas2JSClassScope;
- B: TJSBracketMemberExpression;
- OldAccess: TCtxAccess;
- aClass: TPasClassType;
- begin
- if El.Kind<>pekArrayParams then
- RaiseInconsistency(20170209113713);
- ArgContext:=AContext;
- while ArgContext is TDotContext do
- ArgContext:=ArgContext.Parent;
- if AContext.Resolver=nil then
- begin
- // without Resolver
- if Length(El.Params)<>1 then
- RaiseNotSupported(El,AContext,20170207151325,'Cannot convert 2-dim arrays');
- B:=TJSBracketMemberExpression(CreateElement(TJSBracketMemberExpression,El));
- try
- // add reference
- OldAccess:=AContext.Access;
- AContext.Access:=caRead;
- B.MExpr:=ConvertElement(El.Value,AContext);
- // add parameter
- OldAccess:=ArgContext.Access;
- ArgContext.Access:=caRead;
- B.Name:=ConvertElement(El.Params[0],ArgContext);
- ArgContext.Access:=OldAccess;
- Result:=B;
- finally
- if Result=nil then
- B.Free;
- end;
- exit;
- end;
- // has Resolver
- AContext.Resolver.ComputeElement(El.Value,ResolvedEl,[]);
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.ConvertArrayParams Value=',GetResolverResultDesc(ResolvedEl));
- {$ENDIF}
- if ResolvedEl.BaseType in btAllStrings then
- ConvertStringBracket
- else if (ResolvedEl.IdentEl is TPasProperty)
- and (TPasProperty(ResolvedEl.IdentEl).Args.Count>0) then
- ConvertIndexProperty(TPasProperty(ResolvedEl.IdentEl),AContext)
- else if ResolvedEl.BaseType=btContext then
- begin
- TypeEl:=ResolvedEl.TypeEl;
- if TypeEl.ClassType=TPasClassType then
- begin
- aClass:=TPasClassType(TypeEl);
- ClassScope:=TypeEl.CustomData as TPas2JSClassScope;
- if ClassScope.DefaultProperty<>nil then
- ConvertDefaultProperty(ResolvedEl,ClassScope.DefaultProperty)
- else if AContext.Resolver.IsExternalClassName(aClass,'Array')
- or AContext.Resolver.IsExternalClassName(aClass,'Object') then
- ConvertJSObject
- else
- RaiseInconsistency(20170206180448);
- end
- else if TypeEl.ClassType=TPasClassOfType then
- begin
- ClassScope:=TPasClassOfType(TypeEl).DestType.CustomData as TPas2JSClassScope;
- if ClassScope.DefaultProperty=nil then
- RaiseInconsistency(20170206180503);
- ConvertDefaultProperty(ResolvedEl,ClassScope.DefaultProperty);
- end
- else if TypeEl.ClassType=TPasArrayType then
- ConvertArray(TPasArrayType(TypeEl))
- else
- RaiseNotSupported(El,AContext,20170206181220,GetResolverResultDesc(ResolvedEl));
- end
- else
- RaiseNotSupported(El,AContext,20170206180222);
- end;
- function TPasToJSConverter.ConvertFuncParams(El: TParamsExpr;
- AContext: TConvertContext): TJSElement;
- var
- Ref: TResolvedReference;
- Decl, Left: TPasElement;
- BuiltInProc: TResElDataBuiltInProc;
- TargetProcType: TPasProcedureType;
- Call: TJSCallExpression;
- Elements: TJSArrayLiteralElements;
- E: TJSArrayLiteral;
- OldAccess: TCtxAccess;
- DeclResolved, ParamResolved: TPasResolverResult;
- Param: TPasExpr;
- JSBaseType: TPas2jsBaseType;
- C: TClass;
- begin
- Result:=nil;
- if El.Kind<>pekFuncParams then
- RaiseInconsistency(20170209113515);
- //writeln('TPasToJSConverter.ConvertFuncParams START pekFuncParams ',GetObjName(El.CustomData),' ',GetObjName(El.Value.CustomData));
- Call:=nil;
- Elements:=nil;
- TargetProcType:=nil;
- if El.Value.CustomData is TResolvedReference then
- begin
- Ref:=TResolvedReference(El.Value.CustomData);
- Decl:=Ref.Declaration;
- if Decl is TPasType then
- Decl:=AContext.Resolver.ResolveAliasType(TPasType(Decl));
- //writeln('TPasToJSConverter.ConvertFuncParams pekFuncParams TResolvedReference ',GetObjName(Ref.Declaration),' ',GetObjName(Ref.Declaration.CustomData));
- C:=Decl.ClassType;
- if C=TPasUnresolvedSymbolRef then
- begin
- if Decl.CustomData is TResElDataBuiltInProc then
- begin
- BuiltInProc:=TResElDataBuiltInProc(Decl.CustomData);
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.ConvertFuncParams ',Decl.Name,' ',ResolverBuiltInProcNames[BuiltInProc.BuiltIn]);
- {$ENDIF}
- case BuiltInProc.BuiltIn of
- bfLength: Result:=ConvertBuiltInLength(El,AContext);
- bfSetLength: Result:=ConvertBuiltInSetLength(El,AContext);
- bfInclude: Result:=ConvertBuiltInExcludeInclude(El,AContext,true);
- bfExclude: Result:=ConvertBuiltInExcludeInclude(El,AContext,false);
- bfExit: Result:=ConvertBuiltInExit(El,AContext);
- bfInc,
- bfDec: Result:=ConvertBuiltInIncDec(El,AContext);
- bfAssigned: Result:=ConvertBuiltInAssigned(El,AContext);
- bfChr: Result:=ConvertBuiltInChr(El,AContext);
- bfOrd: Result:=ConvertBuiltInOrd(El,AContext);
- bfLow: Result:=ConvertBuiltInLow(El,AContext);
- bfHigh: Result:=ConvertBuiltInHigh(El,AContext);
- bfPred: Result:=ConvertBuiltInPred(El,AContext);
- bfSucc: Result:=ConvertBuiltInSucc(El,AContext);
- bfStrProc: Result:=ConvertBuiltInStrProc(El,AContext);
- bfStrFunc: Result:=ConvertBuiltInStrFunc(El,AContext);
- bfConcatArray: Result:=ConvertBuiltInConcatArray(El,AContext);
- bfCopyArray: Result:=ConvertBuiltInCopyArray(El,AContext);
- bfInsertArray: Result:=ConvertBuiltInInsertArray(El,AContext);
- bfDeleteArray: Result:=ConvertBuiltInDeleteArray(El,AContext);
- else
- RaiseNotSupported(El,AContext,20161130164955,'built in proc '+ResolverBuiltInProcNames[BuiltInProc.BuiltIn]);
- end;
- if Result=nil then
- RaiseInconsistency(20170210121932);
- exit;
- end
- else if Decl.CustomData is TResElDataBaseType then
- begin
- Result:=ConvertTypeCastToBaseType(El,AContext,TResElDataBaseType(Decl.CustomData));
- exit;
- end
- else
- RaiseNotSupported(El,AContext,20170325160624);
- end
- else if IsExternalClassConstructor(Decl) then
- begin
- // create external object/function
- // -> check if there is complex left side, e.g. TExtA.Create(params)
- Left:=El;
- while (Left.Parent.ClassType=TParamsExpr) do
- Left:=Left.Parent;
- if (Left.Parent.ClassType=TBinaryExpr) and (TBinaryExpr(Left.Parent).right=Left) then
- Left:=TBinaryExpr(Left.Parent).Left
- else
- Left:=nil;
- Result:=ConvertExternalConstructor(Left,Ref,El,AContext);
- exit;
- end
- else if C.InheritsFrom(TPasProcedure) then
- TargetProcType:=TPasProcedure(Decl).ProcType
- else if (C=TPasClassType)
- or (C=TPasClassOfType)
- or (C=TPasEnumType)
- or (C=TPasArrayType) then
- begin
- // typecast
- // default is to simply replace "aType(value)" with "value"
- Param:=El.Params[0];
- AContext.Resolver.ComputeElement(Param,ParamResolved,[]);
- Result:=ConvertElement(Param,AContext);
- if (ParamResolved.BaseType=btCustom)
- and (ParamResolved.TypeEl.CustomData is TResElDataPas2JSBaseType) then
- begin
- JSBaseType:=TResElDataPas2JSBaseType(ParamResolved.TypeEl.CustomData).JSBaseType;
- if JSBaseType=pbtJSValue then
- begin
- if (C=TPasClassType)
- or (C=TPasClassOfType) then
- begin
- // TObject(jsvalue) -> rtl.getObject(jsvalue)
- Call:=CreateCallExpression(El);
- Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnGetObject]]);
- Call.Args.Elements.AddElement.Expr:=Result;
- Result:=Call;
- end;
- end;
- end;
- exit;
- end
- else if C.InheritsFrom(TPasVariable) then
- begin
- AContext.Resolver.ComputeElement(Decl,DeclResolved,[rcType]);
- if DeclResolved.TypeEl is TPasProcedureType then
- TargetProcType:=TPasProcedureType(DeclResolved.TypeEl)
- else
- RaiseNotSupported(El,AContext,20170217115244);
- end
- else if (C=TPasArgument) then
- begin
- AContext.Resolver.ComputeElement(Decl,DeclResolved,[rcType]);
- if DeclResolved.TypeEl is TPasProcedureType then
- TargetProcType:=TPasProcedureType(DeclResolved.TypeEl)
- else
- RaiseNotSupported(El,AContext,20170328224020);
- end
- else if (C=TPasProcedureType)
- or (C=TPasFunctionType) then
- begin
- TargetProcType:=TPasProcedureType(Decl);
- end
- else
- begin
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.ConvertFuncParams El=',GetObjName(El),' Decl=',GetObjName(Decl));
- {$ENDIF}
- RaiseNotSupported(El,AContext,20170215114337);
- end;
- if [rrfNewInstance,rrfFreeInstance]*Ref.Flags<>[] then
- // call constructor, destructor
- Call:=CreateFreeOrNewInstanceExpr(Ref,AContext);
- end;
- if Call=nil then
- begin
- Call:=CreateCallExpression(El);
- Elements:=Call.Args.Elements;
- end;
- OldAccess:=AContext.Access;
- try
- AContext.Access:=caRead;
- if Call.Expr=nil then
- Call.Expr:=ConvertElement(El.Value,AContext);
- if Call.Args=nil then
- begin
- // append ()
- Call.Args:=TJSArguments(CreateElement(TJSArguments,El));
- Elements:=Call.Args.Elements;
- end
- else if Elements=nil then
- begin
- // insert array parameter [], e.g. this.TObject.$create("create",[])
- Elements:=Call.Args.Elements;
- E:=TJSArrayLiteral(CreateElement(TJSArrayLiteral,El));
- Elements.AddElement.Expr:=E;
- Elements:=TJSArrayLiteral(E).Elements;
- end;
- CreateProcedureCallArgs(Elements,El,TargetProcType,AContext);
- if Elements.Count=0 then
- begin
- Call.Args.Free;
- Call.Args:=nil;
- end;
- Result:=Call;
- finally
- AContext.Access:=OldAccess;
- if Result=nil then
- Call.Free;
- end;
- end;
- function TPasToJSConverter.ConvertExternalConstructor(Left: TPasElement;
- Ref: TResolvedReference; ParamsExpr: TParamsExpr; AContext: TConvertContext
- ): TJSElement;
- var
- Proc: TPasConstructor;
- ExtName: String;
- NewExpr: TJSNewMemberExpression;
- Call: TJSCallExpression;
- LeftResolved: TPasResolverResult;
- OldAccess: TCtxAccess;
- ExtNameEl: TJSElement;
- WithData: TPas2JSWithExprScope;
- begin
- Result:=nil;
- NewExpr:=nil;
- Call:=nil;
- ExtNameEl:=nil;
- try
- Proc:=Ref.Declaration as TPasConstructor;
- ExtNameEl:=nil;
- if Left<>nil then
- begin
- if AContext.Resolver<>nil then
- begin
- AContext.Resolver.ComputeElement(Left,LeftResolved,[]);
- if LeftResolved.BaseType=btModule then
- begin
- // e.g. Unit.TExtA
- // ExtName is global -> omit unit
- Left:=nil;
- end
- else ;
- end;
- if Left<>nil then
- begin
- // convert left side
- OldAccess:=AContext.Access;
- AContext.Access:=caRead;
- ExtNameEl:=ConvertElement(Left,AContext);
- AContext.Access:=OldAccess;
- end;
- end;
- if ExtNameEl=nil then
- begin
- if Ref.WithExprScope<>nil then
- begin
- // using local WITH var
- WithData:=Ref.WithExprScope as TPas2JSWithExprScope;
- ExtName:=WithData.WithVarName;
- end
- else
- // use external class name
- ExtName:=(Proc.Parent as TPasClassType).ExternalName;
- ExtNameEl:=CreateBuiltInIdentifierExpr(ExtName);
- end;
- if CompareText(Proc.Name,'new')=0 then
- begin
- // create 'new ExtName(params)'
- NewExpr:=TJSNewMemberExpression(CreateElement(TJSNewMemberExpression,Ref.Element));
- NewExpr.MExpr:=ExtNameEl;
- NewExpr.Args:=TJSArguments(CreateElement(TJSArguments,Ref.Element));
- ExtNameEl:=nil;
- if ParamsExpr<>nil then
- CreateProcedureCallArgs(NewExpr.Args.Elements,ParamsExpr,Proc.ProcType,AContext);
- Result:=NewExpr;
- NewExpr:=nil;
- end
- else
- RaiseInconsistency(20170323083214);
- finally
- ExtNameEl.Free;
- NewExpr.Free;
- Call.Free;
- end;
- end;
- function TPasToJSConverter.ConvertTypeCastToBaseType(El: TParamsExpr;
- AContext: TConvertContext; BaseTypeData: TResElDataBaseType): TJSElement;
- var
- bt: TResolverBaseType;
- Param: TPasExpr;
- ParamResolved: TPasResolverResult;
- NotEqual: TJSEqualityExpressionNE;
- CondExpr: TJSConditionalExpression;
- JSBaseType: TPas2jsBaseType;
- Call: TJSCallExpression;
- NotExpr: TJSUnaryNotExpression;
- AddExpr: TJSAdditiveExpressionPlus;
- JSBaseTypeData: TResElDataPas2JSBaseType;
- TypeEl: TPasType;
- C: TClass;
- function IsParamPas2JSBaseType: boolean;
- var
- TypeEl: TPasType;
- begin
- if ParamResolved.BaseType<>btCustom then exit(false);
- TypeEl:=ParamResolved.TypeEl;
- if TypeEl.ClassType<>TPasUnresolvedSymbolRef then exit(false);
- if not (TypeEl.CustomData is TResElDataPas2JSBaseType) then exit(false);
- Result:=true;
- JSBaseTypeData:=TResElDataPas2JSBaseType(TypeEl.CustomData);
- JSBaseType:=JSBaseTypeData.JSBaseType;
- end;
- begin
- Result:=nil;
- Param:=El.Params[0];
- AContext.Resolver.ComputeElement(Param,ParamResolved,[]);
- JSBaseTypeData:=nil;
- JSBaseType:=pbtNone;
- bt:=BaseTypeData.BaseType;
- if bt in btAllInteger then
- begin
- if ParamResolved.BaseType in btAllInteger then
- begin
- // integer to integer -> value
- Result:=ConvertElement(Param,AContext);
- exit;
- end
- else if ParamResolved.BaseType in btAllBooleans then
- begin
- // boolean to integer -> value?1:0
- Result:=ConvertElement(Param,AContext);
- // Note: convert value first in case it raises an exception
- CondExpr:=TJSConditionalExpression(CreateElement(TJSConditionalExpression,El));
- CondExpr.A:=Result;
- CondExpr.B:=CreateLiteralNumber(El,1);
- CondExpr.C:=CreateLiteralNumber(El,0);
- Result:=CondExpr;
- exit;
- end
- else if IsParamPas2JSBaseType then
- begin
- if JSBaseType=pbtJSValue then
- begin
- // convert jsvalue to integer -> Math.floor(value)
- Result:=ConvertElement(Param,AContext);
- // Note: convert value first in case it raises an exception
- Call:=CreateCallExpression(El);
- Call.Expr:=CreateMemberExpression(['Math','floor']);
- Call.Args.Elements.AddElement.Expr:=Result;
- Result:=Call;
- exit;
- end;
- end;
- end
- else if bt in btAllBooleans then
- begin
- if ParamResolved.BaseType in btAllBooleans then
- begin
- // boolean to boolean -> value
- Result:=ConvertElement(Param,AContext);
- exit;
- end
- else if ParamResolved.BaseType in btAllInteger then
- begin
- // integer to boolean -> value!=0
- Result:=ConvertElement(Param,AContext);
- // Note: convert value first in case it raises an exception
- NotEqual:=TJSEqualityExpressionNE(CreateElement(TJSEqualityExpressionNE,El));
- NotEqual.A:=Result;
- NotEqual.B:=CreateLiteralNumber(El,0);
- Result:=NotEqual;
- exit;
- end
- else if IsParamPas2JSBaseType then
- begin
- if JSBaseType=pbtJSValue then
- begin
- // convert jsvalue to boolean -> !(value==false)
- Result:=ConvertElement(Param,AContext);
- // Note: convert value first in case it raises an exception
- NotExpr:=TJSUnaryNotExpression(CreateElement(TJSUnaryNotExpression,El));
- NotExpr.A:=TJSEqualityExpressionEQ(CreateElement(TJSEqualityExpressionEQ,El));
- TJSEqualityExpressionEQ(NotExpr.A).A:=Result;
- TJSEqualityExpressionEQ(NotExpr.A).B:=CreateLiteralBoolean(El,false);
- Result:=NotExpr;
- exit;
- end;
- end;
- end
- else if bt in btAllFloats then
- begin
- if ParamResolved.BaseType in (btAllFloats+btAllInteger) then
- begin
- // double to double -> value
- Result:=ConvertElement(Param,AContext);
- exit;
- end
- else if IsParamPas2JSBaseType then
- begin
- if JSBaseType=pbtJSValue then
- begin
- // convert jsvalue to double -> rtl.getNumber(value)
- Result:=ConvertElement(Param,AContext);
- // Note: convert value first in case it raises an exception
- Call:=CreateCallExpression(El);
- Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnGetNumber]]);
- Call.Args.Elements.AddElement.Expr:=Result;
- Result:=Call;
- exit;
- end;
- end;
- end
- else if bt in btAllStrings then
- begin
- if ParamResolved.BaseType in btAllStringAndChars then
- begin
- // string or char to string -> value
- Result:=ConvertElement(Param,AContext);
- exit;
- end
- else if IsParamPas2JSBaseType then
- begin
- if JSBaseType=pbtJSValue then
- begin
- // convert jsvalue to string -> ""+value
- Result:=ConvertElement(Param,AContext);
- // Note: convert value first in case it raises an exception
- AddExpr:=TJSAdditiveExpressionPlus(CreateElement(TJSAdditiveExpressionPlus,El));
- AddExpr.A:=CreateLiteralString(El,'');
- AddExpr.B:=Result;
- Result:=AddExpr;
- exit;
- end;
- end;
- end
- else if bt=btChar then
- begin
- if ParamResolved.BaseType=btChar then
- begin
- // char to char
- Result:=ConvertElement(Param,AContext);
- exit;
- end
- else if IsParamPas2JSBaseType then
- begin
- if JSBaseType=pbtJSValue then
- begin
- // convert jsvalue to char -> rtl.getChar(value)
- Result:=ConvertElement(Param,AContext);
- // Note: convert value first in case it raises an exception
- Call:=CreateCallExpression(El);
- Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnGetChar]]);
- Call.Args.Elements.AddElement.Expr:=Result;
- Result:=Call;
- exit;
- end;
- end;
- end
- else if (bt=btCustom) and (BaseTypeData is TResElDataPas2JSBaseType) then
- begin
- JSBaseType:=TResElDataPas2JSBaseType(BaseTypeData).JSBaseType;
- if JSBaseType=pbtJSValue then
- begin
- // type cast to jsvalue
- Result:=ConvertElement(Param,AContext);
- // Note: convert value first in case it raises an exception
- if ParamResolved.BaseType=btContext then
- begin
- TypeEl:=AContext.Resolver.ResolveAliasType(ParamResolved.TypeEl);
- C:=TypeEl.ClassType;
- if C=TPasClassType then
- begin
- // TObject(vsvalue) -> rtl.getObject(vsvalue)
- Call:=CreateCallExpression(El);
- Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnGetObject]]);
- Call.Args.Elements.AddElement.Expr:=Result;
- Result:=Call;
- end;
- end;
- exit;
- end;
- end;
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.ConvertTypeCastToBaseType BaseTypeData=',BaseTypeNames[bt],' ParamResolved=',GetResolverResultDesc(ParamResolved));
- {$ENDIF}
- RaiseNotSupported(El,AContext,20170325161150);
- end;
- function TPasToJSConverter.ConvertSetLiteral(El: TParamsExpr;
- AContext: TConvertContext): TJSElement;
- var
- Call: TJSCallExpression;
- ArgContext: TConvertContext;
- i: Integer;
- Arg: TJSElement;
- ArgEl: TPasExpr;
- begin
- if El.Kind<>pekSet then
- RaiseInconsistency(20170209112737);
- if AContext.Access<>caRead then
- DoError(20170209112926,nCantWriteSetLiteral,sCantWriteSetLiteral,[],El);
- if length(El.Params)=0 then
- Result:=TJSObjectLiteral(CreateElement(TJSObjectLiteral,El))
- else
- begin
- Result:=nil;
- ArgContext:=AContext;
- while ArgContext is TDotContext do
- ArgContext:=ArgContext.Parent;
- Call:=CreateCallExpression(El);
- try
- Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnSet_Create]]);
- for i:=0 to length(El.Params)-1 do
- begin
- ArgEl:=El.Params[i];
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.ConvertSetLiteral ',i,' El.Params[i]=',GetObjName(ArgEl));
- {$ENDIF}
- if (ArgEl.ClassType=TBinaryExpr) and (TBinaryExpr(ArgEl).Kind=pekRange) then
- begin
- // range -> add three parameters: null,left,right
- // ToDo: error if left>right
- // add null
- Call.Args.Elements.AddElement.Expr:=CreateLiteralNull(ArgEl);
- // add left
- Arg:=ConvertElement(TBinaryExpr(ArgEl).left,ArgContext);
- Call.Args.Elements.AddElement.Expr:=Arg;
- // add right
- Arg:=ConvertElement(TBinaryExpr(ArgEl).right,ArgContext);
- Call.Args.Elements.AddElement.Expr:=Arg;
- end
- else
- begin
- Arg:=ConvertElement(ArgEl,ArgContext);
- Call.Args.Elements.AddElement.Expr:=Arg;
- end;
- end;
- Result:=Call;
- finally
- if Result=nil then
- Call.Free;
- end;
- end;
- end;
- function TPasToJSConverter.ConvertOpenArrayParam(ElType: TPasType;
- El: TParamsExpr; AContext: TConvertContext): TJSElement;
- var
- ArrLit: TJSArrayLiteral;
- i: Integer;
- NestedElType: TPasType;
- Param: TPasExpr;
- JSParam: TJSElement;
- begin
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.ConvertOpenArrayParam ',GetObjName(ElType));
- {$ENDIF}
- Result:=nil;
- try
- NestedElType:=nil;
- if ElType is TPasArrayType then
- NestedElType:=TPasArrayType(ElType).ElType;
- ArrLit:=TJSArrayLiteral(CreateElement(TJSArrayLiteral,El));
- for i:=0 to length(El.Params)-1 do
- begin
- Param:=El.Params[i];
- if (NestedElType<>nil)
- and (Param is TParamsExpr) and (TParamsExpr(Param).Kind=pekSet) then
- JSParam:=ConvertOpenArrayParam(NestedElType,TParamsExpr(Param),AContext)
- else
- JSParam:=ConvertElement(Param,AContext);
- ArrLit.Elements.AddElement.Expr:=JSParam;
- end;
- Result:=ArrLit;
- finally
- if Result=nil then
- ArrLit.Free;
- end;
- end;
- function TPasToJSConverter.ConvertBuiltInLength(El: TParamsExpr;
- AContext: TConvertContext): TJSElement;
- var
- Arg: TJSElement;
- Param: TPasExpr;
- ParamResolved, RangeResolved: TPasResolverResult;
- Ranges: TPasExprArray;
- Call: TJSCallExpression;
- begin
- Result:=nil;
- Param:=El.Params[0];
- AContext.Resolver.ComputeElement(Param,ParamResolved,[]);
- if ParamResolved.BaseType=btContext then
- begin
- if ParamResolved.TypeEl is TPasArrayType then
- begin
- Ranges:=TPasArrayType(ParamResolved.TypeEl).Ranges;
- if length(Ranges)>0 then
- begin
- // static array -> number literal
- if length(Ranges)>1 then
- RaiseNotSupported(El,AContext,20170223131042);
- AContext.Resolver.ComputeElement(Ranges[0],RangeResolved,[rcConstant]);
- if RangeResolved.BaseType=btContext then
- begin
- if RangeResolved.IdentEl is TPasEnumType then
- begin
- Result:=CreateLiteralNumber(El,TPasEnumType(RangeResolved.IdentEl).Values.Count);
- exit;
- end;
- end
- else if RangeResolved.BaseType=btBoolean then
- begin
- Result:=CreateLiteralNumber(El,2);
- exit;
- end;
- end
- else
- begin
- // dynamic array -> rtl.length(array)
- Result:=ConvertElement(El.Params[0],AContext);
- // Note: convert param first, it may raise an exception
- Call:=CreateCallExpression(El);
- Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnArray_Length]]);
- Call.Args.Elements.AddElement.Expr:=Result;
- Result:=Call;
- exit;
- end;
- end;
- end;
- // default: Param.length
- Arg:=ConvertElement(Param,AContext);
- Result:=CreateDotExpression(El,Arg,CreateBuiltInIdentifierExpr('length'));
- end;
- function TPasToJSConverter.ConvertBuiltInSetLength(El: TParamsExpr;
- AContext: TConvertContext): TJSElement;
- // convert "SetLength(a,Len)" to "a = rtl.arraySetLength(a,Len)"
- var
- Param0: TPasExpr;
- ResolvedParam0: TPasResolverResult;
- ArrayType: TPasArrayType;
- Call: TJSCallExpression;
- ValInit, Arg: TJSElement;
- AssignSt: TJSSimpleAssignStatement;
- AssignContext: TAssignContext;
- ElType: TPasType;
- begin
- Result:=nil;
- Param0:=El.Params[0];
- if AContext.Access<>caRead then
- RaiseInconsistency(20170213213621);
- AContext.Resolver.ComputeElement(Param0,ResolvedParam0,[rcNoImplicitProc]);
- {$IFDEF VerbosePasResolver}
- writeln('TPasToJSConverter.ConvertBuiltInSetLength ',GetResolverResultDesc(ResolvedParam0));
- {$ENDIF}
- if ResolvedParam0.TypeEl is TPasArrayType then
- begin
- // SetLength(AnArray,newlength)
- ArrayType:=TPasArrayType(ResolvedParam0.TypeEl);
- {$IFDEF VerbosePasResolver}
- writeln('TPasToJSConverter.ConvertBuiltInSetLength array');
- {$ENDIF}
- // -> AnArray = rtl.setArrayLength(AnArray,newlength,initvalue)
- AssignContext:=TAssignContext.Create(El,nil,AContext);
- try
- AContext.Resolver.ComputeElement(Param0,AssignContext.LeftResolved,[rcNoImplicitProc]);
- AssignContext.RightResolved:=ResolvedParam0;
- // create right side
- // rtl.setArrayLength()
- Call:=CreateCallExpression(El);
- AssignContext.RightSide:=Call;
- Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnArray_SetLength]]);
- // 1st param: AnArray
- Call.Args.Elements.AddElement.Expr:=ConvertElement(Param0,AContext);
- // 2nd param: newlength
- Call.Args.Elements.AddElement.Expr:=ConvertElement(El.Params[1],AContext);
- // 3rd param: default value
- ElType:=AContext.Resolver.ResolveAliasType(ArrayType.ElType);
- if ElType.ClassType=TPasRecordType then
- ValInit:=CreateReferencePathExpr(ElType,AContext)
- else
- ValInit:=CreateValInit(ElType,nil,Param0,AContext);
- Call.Args.Elements.AddElement.Expr:=ValInit;
- // create left side: array =
- Result:=CreateAssignStatement(Param0,AssignContext);
- finally
- AssignContext.RightSide.Free;
- AssignContext.Free;
- end;
- end
- else if ResolvedParam0.BaseType=btString then
- begin
- // convert "SetLength(string,NewLen);" to "string.length == NewLen;"
- {$IFDEF VerbosePasResolver}
- writeln('TPasToJSConverter.ConvertBuiltInSetLength string');
- {$ENDIF}
- AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El));
- try
- Arg:=ConvertElement(Param0,AContext);
- // left side: string.length
- AssignSt.LHS:=CreateDotExpression(El,Arg,CreateBuiltInIdentifierExpr('length'));
- // right side: newlength
- AssignSt.Expr:=ConvertElement(El.Params[1],AContext);
- Result:=AssignSt;
- finally
- if Result=nil then
- AssignSt.Free;
- end;
- end
- else
- RaiseNotSupported(El.Value,AContext,20170130141026,'setlength '+GetResolverResultDesc(ResolvedParam0));
- end;
- function TPasToJSConverter.ConvertBuiltInExcludeInclude(El: TParamsExpr;
- AContext: TConvertContext; IsInclude: boolean): TJSElement;
- // convert "Include(aSet,Enum)" to "aSet=rtl.includeSet(aSet,Enum)"
- var
- Call: TJSCallExpression;
- Param0: TPasExpr;
- AssignContext: TAssignContext;
- FunName: String;
- begin
- Result:=nil;
- Param0:=El.Params[0];
- AssignContext:=TAssignContext.Create(El,nil,AContext);
- try
- AContext.Resolver.ComputeElement(Param0,AssignContext.LeftResolved,[rcNoImplicitProc]);
- AssignContext.RightResolved:=AssignContext.LeftResolved;
- // create right side rtl.includeSet(aSet,Enum)
- Call:=CreateCallExpression(El);
- AssignContext.RightSide:=Call;
- if IsInclude then
- FunName:=FBuiltInNames[pbifnSet_Include]
- else
- FunName:=FBuiltInNames[pbifnSet_Exclude];
- Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FunName]);
- Call.Args.Elements.AddElement.Expr:=ConvertElement(Param0,AContext);
- Call.Args.Elements.AddElement.Expr:=ConvertElement(El.Params[1],AContext);
- Result:=CreateAssignStatement(Param0,AssignContext);
- finally
- AssignContext.RightSide.Free;
- AssignContext.Free;
- end;
- end;
- function TPasToJSConverter.ConvertBuiltInContinue(El: TPasExpr;
- AContext: TConvertContext): TJSElement;
- begin
- if AContext=nil then;
- Result:=TJSContinueStatement(CreateElement(TJSContinueStatement,El));
- end;
- function TPasToJSConverter.ConvertBuiltInBreak(El: TPasExpr;
- AContext: TConvertContext): TJSElement;
- begin
- if AContext=nil then;
- Result:=TJSBreakStatement(CreateElement(TJSBreakStatement,El));
- end;
- function TPasToJSConverter.ConvertBuiltInExit(El: TPasExpr;
- AContext: TConvertContext): TJSElement;
- // convert "exit;" -> in a function: "return result;" in a procedure: "return;"
- // convert "exit(param);" -> "return param;"
- var
- ProcEl: TPasElement;
- begin
- Result:=TJSReturnStatement(CreateElement(TJSReturnStatement,El));
- if (El is TParamsExpr) and (length(TParamsExpr(El).Params)>0) then
- begin
- // with parameter. convert "exit(param);" -> "return param;"
- TJSReturnStatement(Result).Expr:=ConvertExpression(TParamsExpr(El).Params[0],AContext);
- end
- else
- begin
- // without parameter.
- ProcEl:=El.Parent;
- while not (ProcEl is TPasProcedure) do ProcEl:=ProcEl.Parent;
- if ProcEl is TPasFunction then
- // in a function, "return result;"
- TJSReturnStatement(Result).Expr:=CreateBuiltInIdentifierExpr(ResolverResultVar)
- else
- ; // in a procedure, "return;" which means "return undefined;"
- end;
- end;
- function TPasToJSConverter.ConvertBuiltInIncDec(El: TParamsExpr;
- AContext: TConvertContext): TJSElement;
- // convert inc(a,b) to a+=b
- // convert dec(a,b) to a-=b
- var
- AssignSt: TJSAssignStatement;
- begin
- if CompareText((El.Value as TPrimitiveExpr).Value,'inc')=0 then
- AssignSt:=TJSAddEqAssignStatement(CreateElement(TJSAddEqAssignStatement,El))
- else
- AssignSt:=TJSSubEqAssignStatement(CreateElement(TJSSubEqAssignStatement,El));
- Result:=AssignSt;
- AssignSt.LHS:=ConvertExpression(El.Params[0],AContext);
- if length(El.Params)=1 then
- AssignSt.Expr:=CreateLiteralNumber(El,1)
- else
- AssignSt.Expr:=ConvertExpression(El.Params[1],AContext);
- end;
- function TPasToJSConverter.ConvertBuiltInAssigned(El: TParamsExpr;
- AContext: TConvertContext): TJSElement;
- var
- NE: TJSEqualityExpressionNE;
- Param: TPasExpr;
- ParamResolved: TPasResolverResult;
- C: TClass;
- GT: TJSRelationalExpressionGT;
- Call: TJSCallExpression;
- begin
- Result:=nil;
- if AContext.Resolver=nil then
- RaiseInconsistency(20170210105235);
- Param:=El.Params[0];
- AContext.Resolver.ComputeElement(Param,ParamResolved,[rcNoImplicitProcType]);
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.ConvertBuiltInAssigned ParamResolved=',GetResolverResultDesc(ParamResolved));
- {$ENDIF}
- if ParamResolved.BaseType=btContext then
- begin
- C:=ParamResolved.TypeEl.ClassType;
- if (C=TPasClassType)
- or (C=TPasClassOfType)
- or C.InheritsFrom(TPasProcedureType) then
- begin
- // convert Assigned(value) -> value!=null
- Result:=ConvertElement(Param,AContext);
- // Note: convert Param first, it may raise an exception
- NE:=TJSEqualityExpressionNE(CreateElement(TJSEqualityExpressionNE,El));
- NE.A:=Result;
- NE.B:=CreateLiteralNull(El);
- Result:=NE;
- end
- else if C=TPasArrayType then
- begin
- // convert Assigned(value) -> rtl.length(value)>0
- Result:=ConvertElement(Param,AContext);
- // Note: convert Param first, it may raise an exception
- GT:=TJSRelationalExpressionGT(CreateElement(TJSRelationalExpressionGT,El));
- Call:=CreateCallExpression(El);
- Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnArray_Length]]);
- Call.Args.Elements.AddElement.Expr:=Result;
- GT.A:=Call;
- GT.B:=CreateLiteralNumber(El,0);
- Result:=GT;
- end
- else
- RaiseNotSupported(El,AContext,20170328124606);
- end;
- end;
- function TPasToJSConverter.ConvertBuiltInChr(El: TParamsExpr;
- AContext: TConvertContext): TJSElement;
- var
- ParamResolved: TPasResolverResult;
- Param: TPasExpr;
- Call: TJSCallExpression;
- begin
- Result:=nil;
- if AContext.Resolver=nil then
- RaiseInconsistency(20170325185847);
- Param:=El.Params[0];
- AContext.Resolver.ComputeElement(Param,ParamResolved,[]);
- if ParamResolved.BaseType in btAllInteger then
- begin
- // chr(integer) -> String.fromCharCode(integer)
- Result:=ConvertElement(Param,AContext);
- // Note: convert Param first, as it might raise an exception
- Call:=CreateCallExpression(El);
- Call.Expr:=CreateMemberExpression(['String','fromCharCode']);
- Call.Args.Elements.AddElement.Expr:=Result;
- Result:=Call;
- exit;
- end;
- DoError(20170325185906,nExpectedXButFoundY,sExpectedXButFoundY,['integer',GetResolverResultDescription(ParamResolved)],Param);
- end;
- function TPasToJSConverter.ConvertBuiltInOrd(El: TParamsExpr;
- AContext: TConvertContext): TJSElement;
- var
- ParamResolved: TPasResolverResult;
- Param: TPasExpr;
- Call: TJSCallExpression;
- begin
- Result:=nil;
- if AContext.Resolver=nil then
- RaiseInconsistency(20170210105235);
- Param:=El.Params[0];
- AContext.Resolver.ComputeElement(Param,ParamResolved,[]);
- if ParamResolved.BaseType=btChar then
- begin
- // ord(aChar) -> aChar.charCodeAt()
- Result:=ConvertElement(Param,AContext);
- // Note: convert Param first, as it might raise an exception
- Call:=CreateCallExpression(El);
- Call.Expr:=CreateDotExpression(El,Result,CreateBuiltInIdentifierExpr('charCodeAt'));
- Result:=Call;
- exit;
- end
- else if ParamResolved.BaseType=btContext then
- begin
- if ParamResolved.TypeEl.ClassType=TPasEnumType then
- begin
- // ord(enum) -> enum
- Result:=ConvertElement(Param,AContext);
- exit;
- end;
- end;
- DoError(20170210105339,nExpectedXButFoundY,sExpectedXButFoundY,['enum',GetResolverResultDescription(ParamResolved)],Param);
- end;
- function TPasToJSConverter.ConvertBuiltInLow(El: TParamsExpr;
- AContext: TConvertContext): TJSElement;
- // low(enumtype) -> first enumvalue
- // low(set var) -> first enumvalue
- // low(settype) -> first enumvalue
- // low(array var) -> first index
- procedure CreateEnumValue(TypeEl: TPasEnumType);
- var
- EnumValue: TPasEnumValue;
- begin
- EnumValue:=TPasEnumValue(TypeEl.Values[0]);
- Result:=CreateReferencePathExpr(EnumValue,AContext);
- end;
- var
- ResolvedEl, RangeResolved: TPasResolverResult;
- Param: TPasExpr;
- TypeEl: TPasType;
- Ranges: TPasExprArray;
- begin
- Result:=nil;
- if AContext.Resolver=nil then
- RaiseInconsistency(20170210120659);
- Param:=El.Params[0];
- AContext.Resolver.ComputeElement(Param,ResolvedEl,[]);
- case ResolvedEl.BaseType of
- btContext:
- begin
- TypeEl:=ResolvedEl.TypeEl;
- if TypeEl.ClassType=TPasEnumType then
- begin
- CreateEnumValue(TPasEnumType(TypeEl));
- exit;
- end
- else if (TypeEl.ClassType=TPasSetType) then
- begin
- if TPasSetType(TypeEl).EnumType<>nil then
- begin
- TypeEl:=TPasSetType(TypeEl).EnumType;
- CreateEnumValue(TPasEnumType(TypeEl));
- exit;
- end;
- end
- else if TypeEl.ClassType=TPasArrayType then
- begin
- Ranges:=TPasArrayType(TypeEl).Ranges;
- if length(Ranges)=0 then
- begin
- Result:=CreateLiteralNumber(El,0);
- exit;
- end
- else if length(Ranges)=1 then
- begin
- AContext.Resolver.ComputeElement(Ranges[0],RangeResolved,[rcConstant]);
- if RangeResolved.BaseType=btContext then
- begin
- if RangeResolved.IdentEl is TPasEnumType then
- begin
- CreateEnumValue(TPasEnumType(RangeResolved.IdentEl));
- exit;
- end;
- end
- else if RangeResolved.BaseType=btBoolean then
- begin
- Result:=CreateLiteralBoolean(El,LowJSBoolean);
- exit;
- end;
- end;
- RaiseNotSupported(El,AContext,20170222231008);
- end;
- end;
- btChar,
- btWideChar:
- begin
- Result:=CreateLiteralJSString(El,#0);
- exit;
- end;
- btBoolean:
- begin
- Result:=CreateLiteralBoolean(El,LowJSBoolean);
- exit;
- end;
- btSet:
- begin
- TypeEl:=ResolvedEl.TypeEl;
- if TypeEl.ClassType=TPasEnumType then
- begin
- CreateEnumValue(TPasEnumType(TypeEl));
- exit;
- end;
- end;
- end;
- DoError(20170210110717,nExpectedXButFoundY,sExpectedXButFoundY,['enum or array',GetResolverResultDescription(ResolvedEl)],Param);
- end;
- function TPasToJSConverter.ConvertBuiltInHigh(El: TParamsExpr;
- AContext: TConvertContext): TJSElement;
- // high(enumtype) -> last enumvalue
- // high(set var) -> last enumvalue
- // high(settype) -> last enumvalue
- // high(dynamic array) -> array.length-1
- // high(static array) -> last index
- procedure CreateEnumValue(TypeEl: TPasEnumType);
- var
- EnumValue: TPasEnumValue;
- begin
- EnumValue:=TPasEnumValue(TypeEl.Values[TypeEl.Values.Count-1]);
- Result:=CreateReferencePathExpr(EnumValue,AContext);
- end;
- var
- ResolvedEl, RangeResolved: TPasResolverResult;
- Param, Range: TPasExpr;
- TypeEl: TPasType;
- MinusExpr: TJSAdditiveExpressionMinus;
- Call: TJSCallExpression;
- begin
- Result:=nil;
- if AContext.Resolver=nil then
- RaiseInconsistency(20170210120653);
- Param:=El.Params[0];
- AContext.Resolver.ComputeElement(Param,ResolvedEl,[]);
- case ResolvedEl.BaseType of
- btContext:
- begin
- TypeEl:=ResolvedEl.TypeEl;
- if TypeEl.ClassType=TPasEnumType then
- begin
- CreateEnumValue(TPasEnumType(TypeEl));
- exit;
- end
- else if (TypeEl.ClassType=TPasSetType) then
- begin
- if TPasSetType(TypeEl).EnumType<>nil then
- begin
- TypeEl:=TPasSetType(TypeEl).EnumType;
- CreateEnumValue(TPasEnumType(TypeEl));
- exit;
- end;
- end
- else if TypeEl.ClassType=TPasArrayType then
- begin
- if length(TPasArrayType(TypeEl).Ranges)=0 then
- begin
- // dynamic array -> rtl.length(Param)-1
- Result:=ConvertElement(Param,AContext);
- // Note: convert Param first, it may raise an exception
- Call:=CreateCallExpression(El);
- Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnArray_Length]]);
- Call.Args.Elements.AddElement.Expr:=Result;
- MinusExpr:=TJSAdditiveExpressionMinus(CreateElement(TJSAdditiveExpressionMinus,El));
- MinusExpr.A:=Call;
- MinusExpr.B:=CreateLiteralNumber(El,1);
- Result:=MinusExpr;
- exit;
- end
- else if length(TPasArrayType(TypeEl).Ranges)=1 then
- begin
- // static array
- Range:=TPasArrayType(TypeEl).Ranges[0];
- AContext.Resolver.ComputeElement(Range,RangeResolved,[rcConstant]);
- if RangeResolved.BaseType=btContext then
- begin
- if RangeResolved.IdentEl is TPasEnumType then
- begin
- CreateEnumValue(TPasEnumType(RangeResolved.IdentEl));
- exit;
- end;
- end
- else if RangeResolved.BaseType=btBoolean then
- begin
- Result:=CreateLiteralBoolean(Param,HighJSBoolean);
- exit;
- end;
- end;
- RaiseNotSupported(El,AContext,20170222231101);
- end;
- end;
- btBoolean:
- begin
- Result:=CreateLiteralBoolean(Param,HighJSBoolean);
- exit;
- end;
- btSet:
- begin
- TypeEl:=ResolvedEl.TypeEl;
- if TypeEl.ClassType=TPasEnumType then
- begin
- CreateEnumValue(TPasEnumType(TypeEl));
- exit;
- end;
- end;
- end;
- DoError(20170210114139,nExpectedXButFoundY,sExpectedXButFoundY,['enum or array',GetResolverResultDescription(ResolvedEl)],Param);
- end;
- function TPasToJSConverter.ConvertBuiltInPred(El: TParamsExpr;
- AContext: TConvertContext): TJSElement;
- // pred(enumvalue) -> enumvalue-1
- var
- ResolvedEl: TPasResolverResult;
- Param: TPasExpr;
- V: TJSElement;
- Expr: TJSAdditiveExpressionMinus;
- begin
- Result:=nil;
- if AContext.Resolver=nil then
- RaiseInconsistency(20170210120648);
- Param:=El.Params[0];
- AContext.Resolver.ComputeElement(Param,ResolvedEl,[]);
- if (ResolvedEl.BaseType=btContext)
- and (ResolvedEl.TypeEl.ClassType=TPasEnumType) then
- begin
- V:=ConvertElement(Param,AContext);
- Expr:=TJSAdditiveExpressionMinus(CreateElement(TJSAdditiveExpressionMinus,El));
- Expr.A:=V;
- Expr.B:=CreateLiteralNumber(El,1);
- Result:=Expr;
- exit;
- end;
- DoError(20170210120039,nExpectedXButFoundY,sExpectedXButFoundY,['enum',GetResolverResultDescription(ResolvedEl)],Param);
- end;
- function TPasToJSConverter.ConvertBuiltInSucc(El: TParamsExpr;
- AContext: TConvertContext): TJSElement;
- // succ(enumvalue) -> enumvalue+1
- var
- ResolvedEl: TPasResolverResult;
- Param: TPasExpr;
- V: TJSElement;
- Expr: TJSAdditiveExpressionPlus;
- begin
- Result:=nil;
- if AContext.Resolver=nil then
- RaiseInconsistency(20170210120645);
- Param:=El.Params[0];
- AContext.Resolver.ComputeElement(Param,ResolvedEl,[]);
- if (ResolvedEl.BaseType=btContext)
- and (ResolvedEl.TypeEl.ClassType=TPasEnumType) then
- begin
- V:=ConvertElement(Param,AContext);
- Expr:=TJSAdditiveExpressionPlus(CreateElement(TJSAdditiveExpressionPlus,El));
- Expr.A:=V;
- Expr.B:=CreateLiteralNumber(El,1);
- Result:=Expr;
- exit;
- end;
- DoError(20170210120626,nExpectedXButFoundY,sExpectedXButFoundY,['enum',GetResolverResultDescription(ResolvedEl)],Param);
- end;
- function TPasToJSConverter.ConvertBuiltInStrProc(El: TParamsExpr;
- AContext: TConvertContext): TJSElement;
- // convert 'str(value,aString)' to 'aString = <string>'
- // for the conversion see ConvertBuiltInStrFunc
- var
- AssignContext: TAssignContext;
- StrVar: TPasExpr;
- begin
- Result:=nil;
- AssignContext:=TAssignContext.Create(El,nil,AContext);
- try
- StrVar:=El.Params[1];
- AContext.Resolver.ComputeElement(StrVar,AssignContext.LeftResolved,[rcNoImplicitProc]);
- // create right side
- AssignContext.RightSide:=ConvertBuiltInStrParam(El.Params[0],AContext,false,true);
- SetResolverValueExpr(AssignContext.RightResolved,btString,
- AContext.Resolver.BaseTypes[btString],El,[rrfReadable]);
- // create 'StrVar = rightside'
- Result:=CreateAssignStatement(StrVar,AssignContext);
- finally
- AssignContext.RightSide.Free;
- AssignContext.Free;
- end;
- end;
- function TPasToJSConverter.ConvertBuiltInStrFunc(El: TParamsExpr;
- AContext: TConvertContext): TJSElement;
- // convert 'str(boolean)' to '""+boolean'
- // convert 'str(integer)' to '""+integer'
- // convert 'str(float)' to '""+float'
- // convert 'str(float:width)' to rtl.spaceLeft('""+float,width)'
- // convert 'str(float:width:precision)' to 'rtl.spaceLeft(float.toFixed(precision),width)'
- var
- i: Integer;
- Param: TPasExpr;
- Sum, Add: TJSElement;
- AddEl: TJSAdditiveExpressionPlus;
- begin
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.ConvertBuiltInStrFunc Count=',length(El.Params));
- {$ENDIF}
- Result:=nil;
- Sum:=nil;
- Add:=nil;
- try
- for i:=0 to length(El.Params)-1 do
- begin
- Param:=El.Params[i];
- Add:=ConvertBuiltInStrParam(Param,AContext,true,i=0);
- if Sum=nil then
- Sum:=Add
- else
- begin
- AddEl:=TJSAdditiveExpressionPlus(CreateElement(TJSAdditiveExpressionPlus,Param));
- AddEl.A:=Sum;
- AddEl.B:=Add;
- Sum:=AddEl;
- end;
- Add:=nil;
- end;
- Result:=Sum;
- finally
- Add.Free;
- if Result=nil then
- Sum.Free;
- end;
- end;
- function TPasToJSConverter.ConvertBuiltInStrParam(El: TPasExpr;
- AContext: TConvertContext; IsStrFunc, IsFirst: boolean): TJSElement;
- var
- ResolvedEl: TPasResolverResult;
- NeedStrLit: Boolean;
- Add: TJSElement;
- Call: TJSCallExpression;
- PlusEl: TJSAdditiveExpressionPlus;
- Bracket: TJSBracketMemberExpression;
- procedure PrependStrLit;
- begin
- PlusEl:=TJSAdditiveExpressionPlus(CreateElement(TJSAdditiveExpressionPlus,El));
- PlusEl.A:=CreateLiteralString(El,'');
- PlusEl.B:=Add;
- Add:=PlusEl;
- end;
- begin
- Result:=nil;
- AContext.Resolver.ComputeElement(El,ResolvedEl,[]);
- Add:=nil;
- Call:=nil;
- Bracket:=nil;
- try
- NeedStrLit:=false;
- if ResolvedEl.BaseType in (btAllBooleans+btAllInteger) then
- begin
- NeedStrLit:=true;
- Add:=ConvertElement(El,AContext);
- end
- else if ResolvedEl.BaseType in btAllFloats then
- begin
- NeedStrLit:=true;
- Add:=ConvertElement(El,AContext);
- if El.format2<>nil then
- begin
- // precision -> rtl El.toFixed(precision);
- NeedStrLit:=false;
- Call:=CreateCallExpression(El);
- Call.Expr:=CreateDotExpression(El,Add,CreateBuiltInIdentifierExpr('toFixed'));
- Call.Args.Elements.AddElement.Expr:=ConvertElement(El.format2,AContext);
- Add:=Call;
- Call:=nil;
- end;
- end
- else if IsStrFunc and (ResolvedEl.BaseType in btAllStringAndChars) then
- Add:=ConvertElement(El,AContext)
- else if ResolvedEl.BaseType=btContext then
- begin
- if ResolvedEl.TypeEl.ClassType=TPasEnumType then
- begin
- // create enumtype[enumvalue]
- Bracket:=TJSBracketMemberExpression(CreateElement(TJSBracketMemberExpression,El));
- Bracket.MExpr:=CreateReferencePathExpr(TPasEnumType(ResolvedEl.TypeEl),AContext);
- Bracket.Name:=ConvertElement(El,AContext);
- Add:=Bracket;
- Bracket:=nil;
- end
- else
- RaiseNotSupported(El,AContext,20170320123827);
- end
- else
- RaiseNotSupported(El,AContext,20170320093001);
- if El.format1<>nil then
- begin
- // width -> leading spaces
- if NeedStrLit then
- PrependStrLit;
- // create 'rtl.spaceLeft(add,width)'
- Call:=CreateCallExpression(El);
- Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnSpaceLeft]]);
- Call.Args.Elements.AddElement.Expr:=Add;
- Add:=nil;
- Call.Args.Elements.AddElement.Expr:=ConvertElement(El.format1,AContext);
- Add:=Call;
- Call:=nil;
- end
- else if IsFirst and NeedStrLit then
- PrependStrLit;
- Result:=Add;
- finally
- Call.Free;
- Bracket.Free;
- if Result=nil then
- Add.Free;
- end;
- end;
- function TPasToJSConverter.ConvertBuiltInConcatArray(El: TParamsExpr;
- AContext: TConvertContext): TJSElement;
- // concat(array1, array2)
- var
- Param0Resolved, ElTypeResolved: TPasResolverResult;
- Param0: TPasExpr;
- ArrayType: TPasArrayType;
- Call: TJSCallExpression;
- i: Integer;
- begin
- if length(El.Params)<1 then
- RaiseInconsistency(20170331000332);
- if length(El.Params)=1 then
- begin
- // concat(array1) -> array1
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.ConvertBuiltInConcatArray Count=',length(El.Params));
- {$ENDIF}
- Result:=ConvertElement(El.Params[0],AContext);
- end
- else
- begin
- // concat(array1,array2,...)
- Param0:=El.Params[0];
- AContext.Resolver.ComputeElement(Param0,Param0Resolved,[]);
- if Param0Resolved.BaseType<>btContext then
- RaiseNotSupported(Param0,AContext,20170331000819);
- if Param0Resolved.TypeEl.ClassType<>TPasArrayType then
- RaiseNotSupported(Param0,AContext,20170331000846);
- ArrayType:=TPasArrayType(Param0Resolved.TypeEl);
- if length(ArrayType.Ranges)>0 then
- RaiseNotSupported(Param0,AContext,20170331001021);
- AContext.Resolver.ComputeElement(ArrayType.ElType,ElTypeResolved,[rcType]);
- Call:=CreateCallExpression(El);
- try
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.ConvertBuiltInConcatArray Count=',length(El.Params),' ElType=',GetResolverResultDesc(ElTypeResolved));
- {$ENDIF}
- if ElTypeResolved.BaseType=btContext then
- begin
- if ElTypeResolved.TypeEl.ClassType=TPasRecordType then
- begin
- // record: rtl.arrayConcat(RecordType,array1,array2,...)
- Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnArray_Concat]]);
- Call.Args.Elements.AddElement.Expr:=CreateReferencePathExpr(
- ElTypeResolved.TypeEl,AContext);
- end;
- end
- else if ElTypeResolved.BaseType=btSet then
- begin
- // set: rtl.arrayConcat("refSet",array1,array2,...)
- Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnArray_Concat]]);
- Call.Args.Elements.AddElement.Expr:=CreateLiteralString(El,FBuiltInNames[pbifnSet_Reference]);
- end;
- if Call.Expr=nil then
- // default: array1.concat(array2,...)
- Call.Expr:=CreateDotExpression(El,ConvertElement(Param0,AContext),
- CreateBuiltInIdentifierExpr('concat'));
- for i:=1 to length(El.Params)-1 do
- Call.Args.Elements.AddElement.Expr:=ConvertElement(El.Params[i],AContext);
- Result:=Call;
- finally
- if Result=nil then
- Call.Free;
- end;
- end;
- end;
- function TPasToJSConverter.ConvertBuiltInCopyArray(El: TParamsExpr;
- AContext: TConvertContext): TJSElement;
- var
- Param: TPasExpr;
- ParamResolved, ElTypeResolved: TPasResolverResult;
- C: TClass;
- TypeParam: TJSElement;
- Call: TJSCallExpression;
- ArrayType: TPasArrayType;
- begin
- Result:=nil;
- Call:=nil;
- try
- Param:=El.Params[0];
- AContext.Resolver.ComputeElement(El,ParamResolved,[]);
- if ParamResolved.BaseType<>btContext then
- RaiseInconsistency(20170401003242);
- if ParamResolved.TypeEl.ClassType<>TPasArrayType then
- RaiseInconsistency(20170401003256);
- ArrayType:=TPasArrayType(ParamResolved.TypeEl);
- AContext.Resolver.ComputeElement(ArrayType.ElType,ElTypeResolved,[rcType]);
- // rtl.arrayCopy(type,src,start,count)
- TypeParam:=nil;
- if ElTypeResolved.BaseType=btContext then
- begin
- C:=ElTypeResolved.TypeEl.ClassType;
- if C=TPasRecordType then
- TypeParam:=CreateReferencePathExpr(TPasRecordType(ElTypeResolved.TypeEl),AContext);
- end
- else if ElTypeResolved.BaseType=btSet then
- TypeParam:=CreateLiteralString(El,FBuiltInNames[pbifnSet_Reference]);
- if TypeParam=nil then
- TypeParam:=CreateLiteralNumber(El,0);
- Call:=CreateCallExpression(El);
- // rtl.arrayCopy
- Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnArray_Copy]]);
- // param: type
- Call.Args.Elements.AddElement.Expr:=TypeParam;
- // param: src
- Call.Args.Elements.AddElement.Expr:=ConvertElement(Param,AContext);
- // param: start
- if length(El.Params)=1 then
- Call.Args.Elements.AddElement.Expr:=CreateLiteralNumber(El,0)
- else
- Call.Args.Elements.AddElement.Expr:=ConvertElement(El.Params[1],AContext);
- // param: count
- if length(El.Params)>=3 then
- Call.Args.Elements.AddElement.Expr:=ConvertElement(El.Params[2],AContext);
- Result:=Call;
- finally
- if Result=nil then
- Call.Free;
- end;
- if El=nil then ;
- if AContext=nil then;
- end;
- function TPasToJSConverter.ConvertBuiltInInsertArray(El: TParamsExpr;
- AContext: TConvertContext): TJSElement;
- // procedure insert(item,var array,const position)
- // -> array.splice(position,1,item);
- var
- ArrEl: TJSElement;
- Call: TJSCallExpression;
- begin
- Result:=nil;
- Call:=nil;
- try
- Call:=CreateCallExpression(El);
- ArrEl:=ConvertElement(El.Params[1],AContext);
- Call.Expr:=CreateDotExpression(El,ArrEl,CreateBuiltInIdentifierExpr('splice'));
- Call.Args.Elements.AddElement.Expr:=ConvertElement(El.Params[2],AContext);
- Call.Args.Elements.AddElement.Expr:=CreateLiteralNumber(El,1);
- Call.Args.Elements.AddElement.Expr:=ConvertElement(El.Params[0],AContext);
- Result:=Call;
- finally
- if Result=nil then
- Call.Free;
- end;
- end;
- function TPasToJSConverter.ConvertBuiltInDeleteArray(El: TParamsExpr;
- AContext: TConvertContext): TJSElement;
- // proc delete(var array,const start,count)
- // -> array.splice(start,count)
- var
- ArrEl: TJSElement;
- Call: TJSCallExpression;
- begin
- Result:=nil;
- Call:=nil;
- try
- Call:=CreateCallExpression(El);
- ArrEl:=ConvertElement(El.Params[0],AContext);
- Call.Expr:=CreateDotExpression(El,ArrEl,CreateBuiltInIdentifierExpr('splice'));
- Call.Args.Elements.AddElement.Expr:=ConvertElement(El.Params[1],AContext);
- Call.Args.Elements.AddElement.Expr:=ConvertElement(El.Params[2],AContext);
- Result:=Call;
- finally
- if Result=nil then
- Call.Free;
- end;
- end;
- function TPasToJSConverter.ConvertRecordValues(El: TRecordValues;
- AContext: TConvertContext): TJSElement;
- Var
- R : TJSObjectLiteral;
- I : Integer;
- It : TRecordValuesItem;
- rel : TJSObjectLiteralElement;
- begin
- R:=TJSObjectLiteral(CreateElement(TJSObjectLiteral,El));
- For I:=0 to Length(El.Fields)-1 do
- begin
- it:=El.Fields[i];
- Rel:=R.Elements.AddElement;
- Rel.Name:=TJSString(it.Name);
- Rel.Expr:=ConvertElement(it.ValueExp,AContext);
- end;
- Result:=R;
- end;
- function TPasToJSConverter.ConvertArrayValues(El: TArrayValues;
- AContext: TConvertContext): TJSElement;
- Var
- R : TJSArrayLiteral;
- I : Integer;
- rel : TJSArrayLiteralElement;
- begin
- R:=TJSArrayLiteral(CreateElement(TJSObjectLiteral,El));
- For I:=0 to Length(El.Values)-1 do
- begin
- Rel:=R.Elements.AddElement;
- Rel.ElementIndex:=i;
- Rel.Expr:=ConvertElement(El.Values[i],AContext);
- end;
- Result:=R;
- end;
- function TPasToJSConverter.ConvertExpression(El: TPasExpr;
- AContext: TConvertContext): TJSElement;
- begin
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.ConvertExpression El=',GetObjName(El),' Context=',GetObjName(AContext));
- {$ENDIF}
- Result:=Nil;
- if (El.ClassType=TUnaryExpr) then
- Result:=ConvertUnaryExpression(TUnaryExpr(El),AContext)
- else if (El.ClassType=TBinaryExpr) then
- Result:=ConvertBinaryExpression(TBinaryExpr(El),AContext)
- else if (El.ClassType=TPrimitiveExpr) then
- Result:=ConvertPrimitiveExpression(TPrimitiveExpr(El),AContext)
- else if (El.ClassType=TBoolConstExpr) then
- Result:=ConvertBoolConstExpression(TBoolConstExpr(El),AContext)
- else if (El.ClassType=TNilExpr) then
- Result:=ConvertNilExpr(TNilExpr(El),AContext)
- else if (El.ClassType=TInheritedExpr) then
- Result:=ConvertInheritedExpression(TInheritedExpr(El),AContext)
- else if (El.ClassType=TSelfExpr) then
- Result:=ConvertSelfExpression(TSelfExpr(El),AContext)
- else if (El.ClassType=TParamsExpr) then
- Result:=ConvertParamsExpression(TParamsExpr(El),AContext)
- else if (El.ClassType=TRecordValues) then
- Result:=ConvertRecordValues(TRecordValues(El),AContext)
- else
- RaiseNotSupported(El,AContext,20161024191314);
- end;
- function TPasToJSConverter.CreateBuiltInIdentifierExpr(AName: string
- ): TJSPrimaryExpressionIdent;
- var
- Ident: TJSPrimaryExpressionIdent;
- begin
- if AName='' then
- RaiseInconsistency(20170402230134);
- Ident:=TJSPrimaryExpressionIdent.Create(0,0);
- // do not lowercase
- Ident.Name:=TJSString(AName);
- Result:=Ident;
- end;
- function TPasToJSConverter.CreateTypeDecl(El: TPasType;
- AContext: TConvertContext): TJSElement;
- var
- ElClass: TClass;
- begin
- Result:=Nil;
- ElClass:=El.ClassType;
- if ElClass=TPasClassType then
- Result := ConvertClassType(TPasClassType(El), AContext)
- else if ElClass=TPasRecordType then
- Result := ConvertRecordType(TPasRecordType(El), AContext)
- else if ElClass=TPasEnumType then
- Result := ConvertEnumType(TPasEnumType(El), AContext)
- else if (ElClass=TPasSetType) then
- begin
- if TPasSetType(El).IsPacked then
- DoError(20170222231613,nPasElementNotSupported,sPasElementNotSupported,
- ['packed'],El);
- end
- else if (ElClass=TPasAliasType)
- or (ElClass=TPasClassOfType) then
- else if (ElClass=TPasProcedureType)
- or (ElClass=TPasFunctionType) then
- begin
- if TPasProcedureType(El).IsNested then
- DoError(20170222231636,nPasElementNotSupported,sPasElementNotSupported,
- ['is nested'],El);
- if TPasProcedureType(El).CallingConvention<>ccDefault then
- DoError(20170222231532,nPasElementNotSupported,sPasElementNotSupported,
- [cCallingConventions[TPasProcedureType(El).CallingConvention]],El);
- end
- else if (ElClass=TPasArrayType) then
- begin
- if TPasArrayType(El).PackMode<>pmNone then
- DoError(20170222231648,nPasElementNotSupported,sPasElementNotSupported,
- ['packed'],El);
- end
- else
- begin
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.CreateTypeDecl El=',GetObjName(El));
- {$ENDIF}
- RaiseNotSupported(El,AContext,20170208144053);
- end;
- end;
- function TPasToJSConverter.CreateVarDecl(El: TPasVariable;
- AContext: TConvertContext): TJSElement;
- Var
- C : TJSElement;
- V : TJSVariableStatement;
- AssignSt: TJSSimpleAssignStatement;
- Obj: TJSObjectLiteral;
- ObjLit: TJSObjectLiteralElement;
- begin
- Result:=nil;
- if vmExternal in El.VarModifiers then
- begin
- // external: do not add a declaration
- exit;
- end;
- if AContext is TObjectContext then
- begin
- // create 'A: initvalue'
- Obj:=TObjectContext(AContext).JSElement as TJSObjectLiteral;
- ObjLit:=Obj.Elements.AddElement;
- ObjLit.Name:=TJSString(TransformVariableName(El,AContext));
- ObjLit.Expr:=CreateVarInit(El,AContext);
- end
- else if AContext.IsSingleton then
- begin
- // create 'this.A=initvalue'
- AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El));
- Result:=AssignSt;
- AssignSt.LHS:=CreateDeclNameExpression(El,El.Name,AContext);
- AssignSt.Expr:=CreateVarInit(El,AContext);
- end
- else
- begin
- // create 'var A=initvalue'
- C:=ConvertVariable(El,AContext);
- V:=TJSVariableStatement(CreateElement(TJSVariableStatement,El));
- V.A:=C;
- Result:=V;
- end;
- end;
- function TPasToJSConverter.CreateSwitchStatement(El: TPasImplCaseOf;
- AContext: TConvertContext): TJSElement;
- var
- SwitchEl: TJSSwitchStatement;
- JSCaseEl: TJSCaseElement;
- SubEl: TPasImplElement;
- St: TPasImplCaseStatement;
- ok: Boolean;
- i, j: Integer;
- BreakSt: TJSBreakStatement;
- BodySt: TJSElement;
- StList: TJSStatementList;
- Expr: TPasExpr;
- begin
- Result:=nil;
- SwitchEl:=TJSSwitchStatement(CreateElement(TJSSwitchStatement,El));
- ok:=false;
- try
- SwitchEl.Cond:=ConvertExpression(El.CaseExpr,AContext);
- for i:=0 to El.Elements.Count-1 do
- begin
- SubEl:=TPasImplElement(El.Elements[i]);
- if not (SubEl is TPasImplCaseStatement) then
- continue;
- St:=TPasImplCaseStatement(SubEl);
- JSCaseEl:=nil;
- for j:=0 to St.Expressions.Count-1 do
- begin
- Expr:=TPasExpr(St.Expressions[j]);
- JSCaseEl:=SwitchEl.Cases.AddCase;
- JSCaseEl.Expr:=ConvertExpression(Expr,AContext);
- end;
- BodySt:=nil;
- if St.Body<>nil then
- BodySt:=ConvertElement(St.Body,AContext);
- // add break
- BreakSt:=TJSBreakStatement(CreateElement(TJSBreakStatement,St));
- if BodySt=nil then
- // no Pascal statement -> add only one 'break;'
- BodySt:=BreakSt
- else
- begin
- if (BodySt is TJSStatementList) then
- begin
- // list of statements -> append 'break;' to end
- StList:=TJSStatementList(BodySt);
- AddToStatementList(TJSStatementList(BodySt),StList,BreakSt,St);
- end
- else
- begin
- // single statement -> create list of old and 'break;'
- StList:=TJSStatementList(CreateElement(TJSStatementList,St));
- StList.A:=BodySt;
- StList.B:=BreakSt;
- BodySt:=StList;
- end;
- end;
- JSCaseEl.Body:=BodySt;
- end;
- if El.ElseBranch<>nil then
- begin
- JSCaseEl:=SwitchEl.Cases.AddCase;
- JSCaseEl.Body:=ConvertImplBlockElements(El.ElseBranch,AContext,false);
- SwitchEl.TheDefault:=JSCaseEl;
- end;
- ok:=true;
- finally
- if not ok then
- SwitchEl.Free;
- end;
- Result:=SwitchEl;
- end;
- function TPasToJSConverter.ConvertDeclarations(El: TPasDeclarations;
- AContext: TConvertContext): TJSElement;
- Var
- E : TJSElement;
- SLFirst, SLLast: TJSStatementList;
- P: TPasElement;
- IsProcBody, IsFunction, IsAssembler: boolean;
- I : Integer;
- PasProc: TPasProcedure;
- ProcScope: TPasProcedureScope;
- ProcBody: TPasImplBlock;
- Procedure Add(NewEl: TJSElement);
- begin
- if AContext is TObjectContext then
- begin
- // NewEl is already added
- end
- else
- begin
- AddToStatementList(SLFirst,SLLast,NewEl,El);
- ConvertDeclarations:=SLFirst;
- end;
- end;
- Procedure AddFunctionResultInit;
- var
- VarSt: TJSVariableStatement;
- AssignSt: TJSSimpleAssignStatement;
- PasFun: TPasFunction;
- FunType: TPasFunctionType;
- ResultEl: TPasResultElement;
- begin
- PasFun:=El.Parent as TPasFunction;
- FunType:=PasFun.FuncType;
- ResultEl:=FunType.ResultEl;
- // add 'var result=initvalue'
- VarSt:=TJSVariableStatement(CreateElement(TJSVariableStatement,El));
- Add(VarSt);
- Result:=SLFirst;
- AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El));
- VarSt.A:=AssignSt;
- AssignSt.LHS:=CreateBuiltInIdentifierExpr(ResolverResultVar);
- AssignSt.Expr:=CreateValInit(ResultEl.ResultType,nil,El,aContext);
- end;
- Procedure AddFunctionResultReturn;
- var
- RetSt: TJSReturnStatement;
- begin
- RetSt:=TJSReturnStatement(CreateElement(TJSReturnStatement,El));
- RetSt.Expr:=CreateBuiltInIdentifierExpr(ResolverResultVar);
- Add(RetSt);
- end;
- begin
- Result:=nil;
- {
- TPasDeclarations = class(TPasElement)
- TPasSection = class(TPasDeclarations)
- TInterfaceSection = class(TPasSection)
- TImplementationSection = class(TPasSection)
- TProgramSection = class(TImplementationSection)
- TLibrarySection = class(TImplementationSection)
- TProcedureBody = class(TPasDeclarations)
- }
- SLFirst:=nil;
- SLLast:=nil;
- IsProcBody:=(El is TProcedureBody) and (TProcedureBody(El).Body<>nil);
- IsFunction:=IsProcBody and (El.Parent is TPasFunction);
- IsAssembler:=IsProcBody and (TProcedureBody(El).Body is TPasImplAsmStatement);
- if IsFunction and not IsAssembler then
- AddFunctionResultInit;
- For I:=0 to El.Declarations.Count-1 do
- begin
- P:=TPasElement(El.Declarations[i]);
- //writeln('TPasToJSConverter.ConvertDeclarations El[',i,']=',GetObjName(P));
- if not IsElementUsed(P) then continue;
- E:=Nil;
- if P.ClassType=TPasConst then
- E:=ConvertConst(TPasConst(P),aContext) // can be nil
- else if P.ClassType=TPasVariable then
- E:=CreateVarDecl(TPasVariable(P),aContext) // can be nil
- else if P is TPasType then
- E:=CreateTypeDecl(TPasType(P),aContext) // can be nil
- else if P is TPasProcedure then
- begin
- PasProc:=TPasProcedure(P);
- if PasProc.IsForward then continue; // JavaScript does not need the forward
- ProcScope:=TPasProcedureScope(PasProc.CustomData);
- if (ProcScope.DeclarationProc<>nil)
- and (not ProcScope.DeclarationProc.IsForward) then
- continue; // this proc was already converted in interface or class
- if ProcScope.DeclarationProc<>nil then
- PasProc:=ProcScope.DeclarationProc;
- E:=ConvertProcedure(PasProc,aContext);
- end
- else
- RaiseNotSupported(P as TPasElement,AContext,20161024191434);
- Add(E);
- end;
- if IsProcBody then
- begin
- ProcBody:=TProcedureBody(El).Body;
- if (ProcBody.Elements.Count>0) or IsAssembler then
- begin
- E:=ConvertElement(TProcedureBody(El).Body,aContext);
- Add(E);
- end;
- end;
- if IsFunction and not IsAssembler then
- AddFunctionResultReturn;
- end;
- function TPasToJSConverter.ConvertClassType(El: TPasClassType;
- AContext: TConvertContext): TJSElement;
- (*
- type
- TMyClass = class(Ancestor)
- i: longint;
- end;
- rtl.createClass(this,"TMyClass",Ancestor,function(){
- this.i = 0;
- });
- *)
- type
- TMemberFunc = (mfInit, mfFinalize);
- const
- MemberFuncName: array[TMemberFunc] of string = (
- '$init',
- '$final'
- );
- var
- IsTObject, AncestorIsExternal: boolean;
- function IsMemberNeeded(aMember: TPasElement): boolean;
- begin
- if IsElementUsed(aMember) then exit(true);
- if IsTObject then
- begin
- if aMember is TPasProcedure then
- begin
- if (CompareText(aMember.Name,'AfterConstruction')=0)
- or (CompareText(aMember.Name,'BeforeDestruction')=0) then
- exit(true);
- end;
- end;
- Result:=false;
- end;
- procedure AddCallAncestorMemberFunction(ClassContext: TConvertContext;
- Ancestor: TPasType; Src: TJSSourceElements; Kind: TMemberFunc);
- var
- Call: TJSCallExpression;
- AncestorPath: String;
- begin
- if (Ancestor=nil) or AncestorIsExternal then
- exit;
- Call:=CreateCallExpression(El);
- AncestorPath:=CreateReferencePath(Ancestor,ClassContext,rpkPathAndName);
- Call.Expr:=CreateBuiltInIdentifierExpr(AncestorPath+'.'+MemberFuncName[Kind]+'.call');
- Call.Args.Elements.AddElement.Expr:=CreateBuiltInIdentifierExpr('this');
- AddToSourceElements(Src,Call);
- end;
- procedure AddInstanceMemberFunction(Src: TJSSourceElements;
- ClassContext: TConvertContext; Ancestor: TPasType; Kind: TMemberFunc);
- // add instance initialization function:
- // this.$init = function(){
- // ancestor.$init();
- // ... init variables ...
- // }
- // or add instance finalization function:
- // this.$final = function(){
- // ... clear references ...
- // ancestor.$final();
- // }
- var
- FuncVD: TJSVarDeclaration;
- New_Src: TJSSourceElements;
- New_FuncContext: TFunctionContext;
- I: Integer;
- P: TPasElement;
- NewEl: TJSElement;
- Func: TJSFunctionDeclarationStatement;
- VarType: TPasType;
- AssignSt: TJSSimpleAssignStatement;
- begin
- // add instance members
- New_Src:=TJSSourceElements(CreateElement(TJSSourceElements, El));
- New_FuncContext:=TFunctionContext.Create(El,New_Src,ClassContext);
- try
- New_FuncContext.This:=El;
- New_FuncContext.IsSingleton:=true;
- // add class members
- For I:=0 to El.Members.Count-1 do
- begin
- P:=TPasElement(El.Members[i]);
- if not IsMemberNeeded(P) then continue;
- NewEl:=nil;
- if (P.ClassType=TPasVariable)
- and (ClassVarModifiersType*TPasVariable(P).VarModifiers=[]) then
- begin
- if Kind=mfInit then
- // mfInit: init var
- NewEl:=CreateVarDecl(TPasVariable(P),New_FuncContext) // can be nil
- else
- begin
- // mfFinalize: clear reference
- if vmExternal in TPasVariable(P).VarModifiers then continue;
- VarType:=ClassContext.Resolver.ResolveAliasType(TPasVariable(P).VarType);
- if (VarType.ClassType=TPasRecordType)
- or (VarType.ClassType=TPasClassType)
- or (VarType.ClassType=TPasClassOfType)
- or (VarType.ClassType=TPasSetType)
- or (VarType.ClassType=TPasProcedureType)
- or (VarType.ClassType=TPasFunctionType)
- or (VarType.ClassType=TPasArrayType) then
- begin
- AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El));
- NewEl:=AssignSt;
- AssignSt.LHS:=CreateDeclNameExpression(P,P.Name,New_FuncContext);
- AssignSt.Expr:=CreateLiteralUndefined(El);
- end;
- end;
- end;
- if NewEl=nil then continue;
- if (Kind=mfInit) and (New_Src.Statements.Count=0) then
- // add call ancestor.$init.call(this)
- AddCallAncestorMemberFunction(ClassContext,Ancestor,New_Src,Kind);
- AddToSourceElements(New_Src,NewEl);
- end;
- if (Kind=mfFinalize) and (New_Src.Statements.Count>0) then
- // call ancestor.$final.call(this)
- AddCallAncestorMemberFunction(ClassContext,Ancestor,New_Src,Kind);
- if (Ancestor<>nil) and (not AncestorIsExternal)
- and (New_Src.Statements.Count=0) then
- exit; // descendent does not need $init/$final
- FuncVD:=TJSVarDeclaration(CreateElement(TJSVarDeclaration,El));
- AddToSourceElements(Src,FuncVD);
- FuncVD.Name:='this.'+MemberFuncName[Kind];
- Func:=CreateFunction(El);
- FuncVD.Init:=Func;
- Func.AFunction.Body.A:=New_Src;
- New_Src:=nil;
- finally
- New_Src.Free;
- New_FuncContext.Free;
- end;
- end;
- var
- Call: TJSCallExpression;
- FunDecl: TJSFunctionDeclarationStatement;
- FunDef: TJSFuncDef;
- Src: TJSSourceElements;
- ArgEx: TJSLiteral;
- FuncContext: TFunctionContext;
- i: Integer;
- NewEl: TJSElement;
- P: TPasElement;
- Scope: TPas2JSClassScope;
- Ancestor: TPasType;
- AncestorPath: String;
- C: TClass;
- begin
- Result:=nil;
- if El.IsForward then
- exit(nil);
- if El.IsExternal then exit;
- if El.CustomData is TPas2JSClassScope then
- Scope:=TPas2JSClassScope(El.CustomData)
- else
- Scope:=nil;
- IsTObject:=CompareText(El.Name,'TObject')=0;
- if (Scope<>nil) and (Scope.AncestorScope<>nil) then
- Ancestor:=Scope.AncestorScope.Element as TPasType
- else
- Ancestor:=El.AncestorType;
- // create call 'rtl.createClass('
- Call:=CreateCallExpression(El);
- try
- AncestorIsExternal:=(Ancestor is TPasClassType) and TPasClassType(Ancestor).IsExternal;
- if AncestorIsExternal then
- Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnCreateClassExt]])
- else
- Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnCreateClass]]);
- // add parameter: owner. 'this' for top level class.
- Call.Args.Elements.AddElement.Expr:=CreateBuiltInIdentifierExpr('this');
- // add parameter: string constant '"classname"'
- ArgEx := CreateLiteralString(El,TransformVariableName(El,AContext));
- Call.Args.Elements.AddElement.Expr:=ArgEx;
- // add parameter: ancestor
- if Ancestor=nil then
- AncestorPath:='null'
- else if AncestorIsExternal then
- AncestorPath:=TPasClassType(Ancestor).ExternalName
- else
- AncestorPath:=CreateReferencePath(Ancestor,AContext,rpkPathAndName);
- Call.Args.Elements.AddElement.Expr:=CreateBuiltInIdentifierExpr(AncestorPath);
- if AncestorIsExternal then
- begin
- // add the name of the NewInstance function
- if Scope.NewInstanceFunction<>nil then
- Call.Args.Elements.AddElement.Expr:=CreateLiteralString(
- Scope.NewInstanceFunction,Scope.NewInstanceFunction.Name)
- else
- Call.Args.Elements.AddElement.Expr:=CreateLiteralString(El,'');
- end;
- // add parameter: class initialize function 'function(){...}'
- FunDecl:=TJSFunctionDeclarationStatement.Create(0,0);
- Call.Args.Elements.AddElement.Expr:=FunDecl;
- FunDef:=TJSFuncDef.Create;
- FunDecl.AFunction:=FunDef;
- FunDef.Name:='';
- FunDef.Body:=TJSFunctionBody.Create(0,0);
- Src:=TJSSourceElements(CreateElement(TJSSourceElements, El));
- FunDef.Body.A:=Src;
- // add members
- FuncContext:=TFunctionContext.Create(El,Src,AContext);
- try
- FuncContext.IsSingleton:=true;
- FuncContext.This:=El;
- // add class members: types and class vars
- For i:=0 to El.Members.Count-1 do
- begin
- P:=TPasElement(El.Members[i]);
- //writeln('TPasToJSConverter.ConvertClassType class El[',i,']=',GetObjName(P));
- if not IsMemberNeeded(P) then continue;
- C:=P.ClassType;
- NewEl:=nil;
- if C=TPasVariable then
- begin
- if ClassVarModifiersType*TPasVariable(P).VarModifiers<>[] then
- begin
- NewEl:=CreateVarDecl(TPasVariable(P),FuncContext); // can be nil
- if NewEl=nil then continue;
- end
- else
- continue;
- end
- else if C=TPasConst then
- NewEl:=ConvertConst(TPasConst(P),aContext)
- else if C=TPasProperty then
- begin
- NewEl:=ConvertProperty(TPasProperty(P),AContext);
- if NewEl=nil then continue;
- end
- else if C.InheritsFrom(TPasType) then
- NewEl:=CreateTypeDecl(TPasType(P),aContext)
- else if C.InheritsFrom(TPasProcedure) then
- continue
- else
- RaiseNotSupported(P,FuncContext,20161221233338);
- if NewEl=nil then
- RaiseNotSupported(P,FuncContext,20170204223922);
- AddToSourceElements(Src,NewEl);
- end;
- // instance initialization function
- AddInstanceMemberFunction(Src,FuncContext,Ancestor,mfInit);
- // instance finalization function
- AddInstanceMemberFunction(Src,FuncContext,Ancestor,mfFinalize);
- // add methods
- For i:=0 to El.Members.Count-1 do
- begin
- P:=TPasElement(El.Members[i]);
- //writeln('TPasToJSConverter.ConvertClassType class El[',i,']=',GetObjName(P));
- if not IsMemberNeeded(P) then continue;
- if P is TPasProcedure then
- NewEl:=ConvertProcedure(TPasProcedure(P),aContext)
- else
- continue;
- if NewEl=nil then
- continue; // e.g. abstract or external proc
- AddToSourceElements(Src,NewEl);
- end;
- finally
- FuncContext.Free;
- end;
- Result:=Call;
- finally
- if Result<>Call then
- Call.Free;
- end;
- end;
- function TPasToJSConverter.ConvertClassExternalType(El: TPasClassType;
- AContext: TConvertContext): TJSElement;
- function IsMemberNeeded(aMember: TPasElement): boolean;
- begin
- Result:=IsElementUsed(aMember);
- end;
- var
- i: Integer;
- P: TPasElement;
- C: TClass;
- Proc: TPasProcedure;
- begin
- Result:=nil;
- if El.IsForward then exit;
- // add class members: types and class vars
- For i:=0 to El.Members.Count-1 do
- begin
- P:=TPasElement(El.Members[i]);
- //writeln('TPasToJSConverter.ConvertClassExternalType class El[',i,']=',GetObjName(P));
- if not IsMemberNeeded(P) then continue;
- C:=P.ClassType;
- if (C=TPasVariable) or (C=TPasConst) then
- begin
- if not (vmExternal in TPasVariable(P).VarModifiers) then
- DoError(20170321150737,nMissingExternalName,sMissingExternalName,[],P);
- end
- else if C=TPasProperty then
- // is replaced with Getter/Setter -> nothing to do here
- else if C.InheritsFrom(TPasProcedure) then
- begin
- Proc:=TPasProcedure(P);
- if Proc.IsExternal then
- // external, nothing to do here
- else
- DoError(20170321152209,nMissingExternalName,sMissingExternalName,[],P);
- end
- else
- RaiseNotSupported(P,AContext,20170321151727);
- end;
- end;
- function TPasToJSConverter.ConvertEnumType(El: TPasEnumType;
- AContext: TConvertContext): TJSElement;
- // TMyEnum = (red, green)
- // convert to
- // this.TMyEnum = {
- // "0":"red",
- // "red":0,
- // "0":"green",
- // "green":0,
- // }
- var
- ObjectContect: TObjectContext;
- i: Integer;
- EnumValue: TPasEnumValue;
- ParentObj, Obj: TJSObjectLiteral;
- ObjLit: TJSObjectLiteralElement;
- AssignSt: TJSSimpleAssignStatement;
- JSName: TJSString;
- begin
- Result:=nil;
- for i:=0 to El.Values.Count-1 do
- begin
- EnumValue:=TPasEnumValue(El.Values[i]);
- if EnumValue.Value<>nil then
- RaiseNotSupported(EnumValue.Value,AContext,20170208145221,'enum constant');
- end;
- ObjectContect:=nil;
- try
- Obj:=TJSObjectLiteral(CreateElement(TJSObjectLiteral,El));
- if AContext is TObjectContext then
- begin
- // add 'TypeName: function(){}'
- ParentObj:=TObjectContext(AContext).JSElement as TJSObjectLiteral;
- ObjLit:=ParentObj.Elements.AddElement;
- ObjLit.Name:=TJSString(TransformVariableName(El,AContext));
- ObjLit.Expr:=Obj;
- Result:=Obj;
- end
- else
- begin
- // add 'this.TypeName = function(){}'
- AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El));
- AssignSt.LHS:=CreateDeclNameExpression(El,El.Name,AContext);
- AssignSt.Expr:=Obj;
- Result:=AssignSt;
- end;
- ObjectContect:=TObjectContext.Create(El,Obj,AContext);
- for i:=0 to El.Values.Count-1 do
- begin
- EnumValue:=TPasEnumValue(El.Values[i]);
- JSName:=TJSString(TransformVariableName(EnumValue,AContext));
- // add "0":"value"
- ObjLit:=Obj.Elements.AddElement;
- ObjLit.Name:=TJSString(IntToStr(i));
- ObjLit.Expr:=CreateLiteralJSString(El,JSName);
- // add value:0
- ObjLit:=Obj.Elements.AddElement;
- ObjLit.Name:=JSName;
- ObjLit.Expr:=CreateLiteralNumber(El,i);
- end;
- finally
- ObjectContect.Free;
- end;
- end;
- procedure TPasToJSConverter.ForLoop_OnProcBodyElement(El: TPasElement;
- arg: pointer);
- // Called by ConvertForStatement on each element of the current proc body
- // Check each element that lies behind the loop if it is reads the LoopVar
- var
- Data: PForLoopFindData absolute arg;
- begin
- if El.HasParent(Data^.ForLoop) then
- Data^.FoundLoop:=true
- else if Data^.FoundLoop and (not Data^.LoopVarWrite) and (not Data^.LoopVarRead) then
- begin
- // El comes after loop and LoopVar was not yet accessed
- if (El.CustomData is TResolvedReference)
- and (TResolvedReference(El.CustomData).Declaration=Data^.LoopVar) then
- begin
- // El refers the LoopVar
- // ToDo: check write only access
- Data^.LoopVarRead:=true;
- end;
- end;
- end;
- procedure TPasToJSConverter.TryExcept_OnElement(El: TPasElement; arg: pointer);
- var
- Data: PTryExceptFindData absolute arg;
- begin
- if (El is TPasImplRaise) and (TPasImplRaise(El).ExceptObject=nil) then
- Data^.HasRaiseWithoutObject:=true;
- end;
- procedure TPasToJSConverter.SetUseEnumNumbers(const AValue: boolean);
- begin
- if AValue then
- Include(FOptions,coEnumNumbers)
- else
- Exclude(FOptions,coEnumNumbers);
- end;
- procedure TPasToJSConverter.SetUseLowerCase(const AValue: boolean);
- begin
- if AValue then
- Include(FOptions,coLowerCase)
- else
- Exclude(FOptions,coLowerCase);
- end;
- procedure TPasToJSConverter.SetUseSwitchStatement(const AValue: boolean);
- begin
- if AValue then
- Include(FOptions,coSwitchStatement)
- else
- Exclude(FOptions,coSwitchStatement);
- end;
- constructor TPasToJSConverter.Create;
- var
- n: TPas2JSBuiltInName;
- begin
- FOptions:=[coLowerCase];
- for n in TPas2JSBuiltInName do
- FBuiltInNames[n]:=Pas2JSBuiltInNames[n];
- end;
- destructor TPasToJSConverter.Destroy;
- begin
- inherited Destroy;
- end;
- function TPasToJSConverter.ConvertProcedure(El: TPasProcedure;
- AContext: TConvertContext): TJSElement;
- Var
- FS : TJSFunctionDeclarationStatement;
- FD : TJSFuncDef;
- n:Integer;
- AssignSt: TJSSimpleAssignStatement;
- FuncContext: TFunctionContext;
- ProcScope: TPasProcedureScope;
- Arg: TPasArgument;
- ImplProc: TPasProcedure;
- begin
- Result:=nil;
- if El.IsAbstract then exit;
- if El.IsExternal then exit;
- ProcScope:=TPasProcedureScope(El.CustomData);
- if ProcScope.DeclarationProc<>nil then
- exit;
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.ConvertProcedure "',El.Name,'" ',El.Parent.ClassName);
- {$ENDIF}
- ImplProc:=El;
- if ProcScope.ImplProc<>nil then
- ImplProc:=ProcScope.ImplProc;
- AssignSt:=nil;
- if AContext.IsSingleton then
- begin
- AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El));
- Result:=AssignSt;
- AssignSt.LHS:=CreateDeclNameExpression(El,El.Name,AContext);
- end;
- FS:=CreateFunction(El,ImplProc.Body<>nil);
- FD:=FS.AFunction;
- if AssignSt<>nil then
- AssignSt.Expr:=FS
- else
- begin
- // local/nested function
- Result:=FS;
- FD.Name:=TJSString(TransformVariableName(El,AContext));
- end;
- for n := 0 to El.ProcType.Args.Count - 1 do
- begin
- Arg:=TPasArgument(El.ProcType.Args[n]);
- FD.Params.Add(TransformVariableName(Arg,AContext));
- end;
- if ImplProc.Body<>nil then
- begin
- FuncContext:=TFunctionContext.Create(ImplProc,FD.Body,AContext);
- try
- if ProcScope.ClassScope<>nil then
- FuncContext.This:=ProcScope.ClassScope.Element
- else
- FuncContext.This:=AContext.GetThis;
- FD.Body.A:=ConvertDeclarations(ImplProc.Body,FuncContext);
- finally
- FuncContext.Free;
- end;
- end;
- {
- TPasProcedureBase = class(TPasElement)
- TPasOverloadedProc = class(TPasProcedureBase)
- TPasProcedure = class(TPasProcedureBase)
- TPasFunction = class(TPasProcedure)
- TPasOperator = class(TPasProcedure)
- TPasConstructor = class(TPasProcedure)
- TPasDestructor = class(TPasProcedure)
- TPasClassProcedure = class(TPasProcedure)
- TPasClassFunction = class(TPasProcedure)
- }
- end;
- function TPasToJSConverter.ConvertBeginEndStatement(El: TPasImplBeginBlock;
- AContext: TConvertContext; NilIfEmpty: boolean): TJSElement;
- begin
- Result:=ConvertImplBlockElements(El,AContext,NilIfEmpty);
- end;
- function TPasToJSConverter.ConvertImplBlockElements(El: TPasImplBlock;
- AContext: TConvertContext; NilIfEmpty: boolean): TJSElement;
- var
- First, Last: TJSStatementList;
- I : Integer;
- PasImpl: TPasImplElement;
- JSImpl : TJSElement;
- begin
- if Not (Assigned(El.Elements) and (El.Elements.Count>0)) then
- begin
- if NilIfEmpty then
- Result:=nil
- else
- Result:=TJSEmptyBlockStatement(CreateElement(TJSEmptyBlockStatement,El));
- end
- else
- begin
- First:=nil;
- Result:=First;
- Last:=First;
- //writeln('TPasToJSConverter.ConvertImplBlockElements START El.Elements.Count=',El.Elements.Count);
- For I:=0 to El.Elements.Count-1 do
- begin
- PasImpl:=TPasImplElement(El.Elements[i]);
- JSImpl:=ConvertElement(PasImpl,AContext);
- if JSImpl=nil then
- continue; // e.g. "inherited;" when there is no ancestor proc
- //writeln('TPasToJSConverter.ConvertImplBlockElements ',i,' ',JSImpl.ClassName);
- AddToStatementList(First,Last,JSImpl,PasImpl);
- Result:=First;
- end;
- end;
- end;
- function TPasToJSConverter.ConvertInitializationSection(
- El: TInitializationSection; AContext: TConvertContext): TJSElement;
- var
- FDS: TJSFunctionDeclarationStatement;
- FunName: String;
- IsMain, ok: Boolean;
- AssignSt: TJSSimpleAssignStatement;
- FuncContext: TFunctionContext;
- Body: TJSFunctionBody;
- begin
- // create: 'this.$init=function(){}'
- IsMain:=(El.Parent<>nil) and (El.Parent is TPasProgram);
- if IsMain then
- FunName:=FBuiltInNames[pbifnProgramMain]
- else
- FunName:=FBuiltInNames[pbifnUnitInit];
- AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El));
- Result:=AssignSt;
- FuncContext:=nil;
- ok:=false;
- try
- AssignSt.LHS:=CreateMemberExpression(['this',FunName]);
- FDS:=CreateFunction(El,El.Elements.Count>0);
- AssignSt.Expr:=FDS;
- if El.Elements.Count>0 then
- begin
- Body:=FDS.AFunction.Body;
- FuncContext:=TFunctionContext.Create(El,Body,AContext);
- FuncContext.This:=AContext.GetThis;
- Body.A:=ConvertImplBlockElements(El,FuncContext,false);
- end;
- ok:=true;
- finally
- FuncContext.Free;
- if not ok then FreeAndNil(Result);
- end;
- end;
- function TPasToJSConverter.ConvertFinalizationSection(El: TFinalizationSection;
- AContext: TConvertContext): TJSElement;
- begin
- Result:=nil;
- RaiseNotSupported(El,AContext,20161024192519);
- end;
- function TPasToJSConverter.ConvertTryStatement(El: TPasImplTry;
- AContext: TConvertContext): TJSElement;
- function NeedExceptObject: boolean;
- var
- Data: TTryExceptFindData;
- begin
- Result:=false;
- if El.FinallyExcept.Elements.Count=0 then exit;
- if TPasElement(El.FinallyExcept.Elements[0]) is TPasImplExceptOn then
- exit(true);
- Data:=Default(TTryExceptFindData);
- El.FinallyExcept.ForEachCall(@TryExcept_OnElement,@Data);
- Result:=Data.HasRaiseWithoutObject;
- end;
- Var
- T : TJSTryStatement;
- ExceptBlock: TPasImplTryHandler;
- i: Integer;
- ExceptOn: TPasImplExceptOn;
- IfSt, Last: TJSIfStatement;
- begin
- Result:=nil;
- T:=nil;
- try
- if El.FinallyExcept is TPasImplTryFinally then
- begin
- T:=TJSTryFinallyStatement(CreateElement(TJSTryFinallyStatement,El));
- T.Block:=ConvertImplBlockElements(El,AContext,true);
- T.BFinally:=ConvertImplBlockElements(El.FinallyExcept,AContext,true);
- end
- else
- begin
- T:=TJSTryCatchStatement(CreateElement(TJSTryCatchStatement,El));
- T.Block:=ConvertImplBlockElements(El,AContext,true);
- if NeedExceptObject then
- T.Ident:=TJSString(FBuiltInNames[pbivnExceptObject]);
- //T.BCatch:=ConvertElement(El.FinallyExcept,AContext);
- ExceptBlock:=El.FinallyExcept;
- if (ExceptBlock.Elements.Count>0)
- and (TPasImplElement(ExceptBlock.Elements[0]) is TPasImplExceptOn) then
- begin
- Last:=nil;
- for i:=0 to ExceptBlock.Elements.Count-1 do
- begin
- ExceptOn:=TObject(ExceptBlock.Elements[i]) as TPasImplExceptOn;
- IfSt:=ConvertExceptOn(ExceptOn,AContext) as TJSIfStatement;
- if Last=nil then
- T.BCatch:=IfSt
- else
- Last.BFalse:=IfSt;
- Last:=IfSt;
- end;
- if El.ElseBranch<>nil then
- Last.BFalse:=ConvertImplBlockElements(El.ElseBranch,AContext,true)
- else
- begin
- // default else: throw exceptobject
- Last.BFalse:=TJSThrowStatement(CreateElement(TJSThrowStatement,El));
- TJSThrowStatement(Last.BFalse).A:=
- CreateBuiltInIdentifierExpr(FBuiltInNames[pbivnExceptObject]);
- end;
- end
- else
- begin
- if El.ElseBranch<>nil then
- RaiseNotSupported(El.ElseBranch,AContext,20170205003014);
- T.BCatch:=ConvertImplBlockElements(ExceptBlock,AContext,true);
- end;
- end;
- Result:=T;
- finally
- if Result=nil then
- T.Free;
- end;
- end;
- function TPasToJSConverter.ConvertCaseOfStatement(El: TPasImplCaseOf;
- AContext: TConvertContext): TJSElement;
- var
- SubEl: TPasImplElement;
- St: TPasImplCaseStatement;
- ok: Boolean;
- i, j: Integer;
- JSExpr: TJSElement;
- StList: TJSStatementList;
- Expr: TPasExpr;
- IfSt, LastIfSt: TJSIfStatement;
- TmpVarName: String;
- VarDecl: TJSVarDeclaration;
- VarSt: TJSVariableStatement;
- JSOrExpr: TJSLogicalOrExpression;
- JSAndExpr: TJSLogicalAndExpression;
- JSLEExpr: TJSRelationalExpressionLE;
- JSGEExpr: TJSRelationalExpressionGE;
- JSEQExpr: TJSEqualityExpressionEQ;
- begin
- Result:=nil;
- if UseSwitchStatement then
- begin
- // convert to switch statement
- // switch does not support ranges -> check
- ok:=true;
- for i:=0 to El.Elements.Count-1 do
- begin
- SubEl:=TPasImplElement(El.Elements[i]);
- if not (SubEl is TPasImplCaseStatement) then
- continue;
- St:=TPasImplCaseStatement(SubEl);
- for j:=0 to St.Expressions.Count-1 do
- begin
- Expr:=TPasExpr(St.Expressions[j]);
- if (Expr is TBinaryExpr) and (TBinaryExpr(Expr).Kind=pekRange) then
- begin
- ok:=false;
- break;
- end;
- end;
- if not ok then break;
- end;
- if ok then
- begin
- Result:=CreateSwitchStatement(El,AContext);
- exit;
- end;
- end;
- // convert to if statements
- StList:=TJSStatementList(CreateElement(TJSStatementList,El));
- ok:=false;
- try
- // create var $tmp=CaseExpr;
- TmpVarName:=AContext.CreateLocalIdentifier('$tmp');
- VarSt:=TJSVariableStatement(CreateElement(TJSVariableStatement,El.CaseExpr));
- StList.A:=VarSt;
- VarDecl:=TJSVarDeclaration(CreateElement(TJSVarDeclaration,El.CaseExpr));
- VarSt.A:=VarDecl;
- VarDecl.Name:=TmpVarName;
- VarDecl.Init:=ConvertExpression(El.CaseExpr,AContext);
- LastIfSt:=nil;
- for i:=0 to El.Elements.Count-1 do
- begin
- SubEl:=TPasImplElement(El.Elements[i]);
- if SubEl is TPasImplCaseStatement then
- begin
- St:=TPasImplCaseStatement(SubEl);
- // create for example "if (tmp==expr) || ((tmp>=expr) && (tmp<=expr)){}"
- IfSt:=TJSIfStatement(CreateElement(TJSIfStatement,SubEl));
- if LastIfSt=nil then
- StList.B:=IfSt
- else
- LastIfSt.BFalse:=IfSt;
- LastIfSt:=IfSt;
- for j:=0 to St.Expressions.Count-1 do
- begin
- Expr:=TPasExpr(St.Expressions[j]);
- if (Expr is TBinaryExpr) and (TBinaryExpr(Expr).Kind=pekRange) then
- begin
- // range -> create "(tmp>=left) && (tmp<=right)"
- // create "() && ()"
- JSAndExpr:=TJSLogicalAndExpression(CreateElement(TJSLogicalAndExpression,Expr));
- JSExpr:=JSAndExpr;
- // create "tmp>=left"
- JSGEExpr:=TJSRelationalExpressionGE(CreateElement(TJSRelationalExpressionGE,Expr));
- JSAndExpr.A:=JSGEExpr;
- JSGEExpr.A:=CreateIdentifierExpr(TmpVarName,El.CaseExpr,AContext);
- JSGEExpr.B:=ConvertExpression(TBinaryExpr(Expr).left,AContext);
- // create "tmp<=right"
- JSLEExpr:=TJSRelationalExpressionLE(CreateElement(TJSRelationalExpressionLE,Expr));
- JSAndExpr.B:=JSLEExpr;
- JSLEExpr.A:=CreateIdentifierExpr(TmpVarName,El.CaseExpr,AContext);
- JSLEExpr.B:=ConvertExpression(TBinaryExpr(Expr).right,AContext);
- end
- else
- begin
- // value -> create (tmp==Expr)
- JSEQExpr:=TJSEqualityExpressionEQ(CreateElement(TJSEqualityExpressionEQ,Expr));
- JSExpr:=JSEQExpr;
- JSEQExpr.A:=CreateIdentifierExpr(TmpVarName,El.CaseExpr,AContext);
- JSEQExpr.B:=ConvertExpression(Expr,AContext);
- end;
- if IfSt.Cond=nil then
- // first expression
- IfSt.Cond:=JSExpr
- else
- begin
- // multi expression -> append with OR
- JSOrExpr:=TJSLogicalOrExpression(CreateElement(TJSLogicalOrExpression,St));
- JSOrExpr.A:=IfSt.Cond;
- JSOrExpr.B:=JSExpr;
- IfSt.Cond:=JSOrExpr;
- end;
- end;
- // convert statement
- if St.Body<>nil then
- IfSt.BTrue:=ConvertElement(St.Body,AContext)
- else
- IfSt.BTrue:=TJSEmptyStatement(CreateElement(TJSEmptyStatement,St));
- end
- else if SubEl is TPasImplCaseElse then
- begin
- // Pascal 'else' or 'otherwise' -> create JS "else{}"
- if LastIfSt=nil then
- RaiseNotSupported(SubEl,AContext,20161128120802,'case-of needs at least one case');
- LastIfSt.BFalse:=ConvertImplBlockElements(El.ElseBranch,AContext,true);
- end
- else
- RaiseNotSupported(SubEl,AContext,20161128113055);
- end;
- ok:=true;
- finally
- if not ok then
- StList.Free;
- end;
- Result:=StList;
- end;
- function TPasToJSConverter.ConvertAsmStatement(El: TPasImplAsmStatement;
- AContext: TConvertContext): TJSElement;
- var
- s: String;
- L: TJSLiteral;
- begin
- if AContext=nil then ;
- s:=Trim(El.Tokens.Text);
- if (s<>'') and (s[length(s)]=';') then
- Delete(s,length(s),1);
- if s='' then
- Result:=TJSEmptyStatement(CreateElement(TJSEmptyStatement,El))
- else begin
- L:=TJSLiteral(CreateElement(TJSLiteral,El));
- L.Value.CustomValue:=TJSString(s);
- Result:=L;
- end;
- end;
- function TPasToJSConverter.CreateImplementationSection(El: TPasModule;
- Src: TJSSourceElements; AContext: TConvertContext): TJSElement;
- var
- Section: TImplementationSection;
- begin
- Result:=nil;
- if not Assigned(El.ImplementationSection) then
- exit;
- Section:=El.ImplementationSection;
- // add implementation section
- // merge interface and implementation
- Result:=ConvertDeclarations(Section,AContext);
- AddToSourceElements(Src,Result);
- end;
- procedure TPasToJSConverter.CreateInitSection(El: TPasModule;
- Src: TJSSourceElements; AContext: TConvertContext);
- begin
- // add initialization section
- if Assigned(El.InitializationSection) then
- AddToSourceElements(Src,ConvertInitializationSection(El.InitializationSection,AContext));
- // finalization: not supported
- if Assigned(El.FinalizationSection) then
- raise Exception.Create('TPasToJSConverter.ConvertInitializationSection: finalization section is not supported');
- end;
- function TPasToJSConverter.CreateDotExpression(aParent: TPasElement; Left,
- Right: TJSElement): TJSElement;
- var
- Dot: TJSDotMemberExpression;
- RightParent: TJSElement;
- ok: Boolean;
- begin
- Result:=nil;
- if Left=nil then
- RaiseInconsistency(20170201140827);
- if Right=nil then
- RaiseInconsistency(20170211192018);
- ok:=false;
- try
- // create a TJSDotMemberExpression of Left and the left-most identifier of Right
- // Left becomes the new left-most element of Right.
- Result:=Right;
- RightParent:=nil;
- repeat
- if (Right.ClassType=TJSCallExpression) then
- begin
- RightParent:=Right;
- Right:=TJSCallExpression(Right).Expr;
- if Right=nil then
- begin
- // left-most is nil -> insert Left
- TJSCallExpression(RightParent).Expr:=Left;
- ok:=true;
- exit;
- end;
- end
- else if (Right.ClassType=TJSBracketMemberExpression) then
- begin
- RightParent:=Right;
- Right:=TJSBracketMemberExpression(Right).MExpr;
- if Right=nil then
- begin
- // left-most is nil -> insert Left
- TJSBracketMemberExpression(RightParent).MExpr:=Left;
- ok:=true;
- exit;
- end;
- end
- else if (Right.ClassType=TJSDotMemberExpression) then
- begin
- RightParent:=Right;
- Right:=TJSDotMemberExpression(Right).MExpr;
- if Right=nil then
- begin
- // left-most is nil -> insert Left
- TJSDotMemberExpression(RightParent).MExpr:=Left;
- ok:=true;
- exit;
- end;
- end
- else if (Right.ClassType=TJSPrimaryExpressionIdent) then
- begin
- // left-most identifier found
- // -> replace it
- Dot := TJSDotMemberExpression(CreateElement(TJSDotMemberExpression, aParent));
- if Result=Right then
- Result:=Dot
- else if RightParent is TJSBracketMemberExpression then
- TJSBracketMemberExpression(RightParent).MExpr:=Dot
- else if RightParent is TJSCallExpression then
- TJSCallExpression(RightParent).Expr:=Dot
- else if RightParent is TJSDotMemberExpression then
- TJSDotMemberExpression(RightParent).MExpr:=Dot
- else
- begin
- Dot.Free;
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.CreateDotExpression Right=',GetObjName(Right),' RightParent=',GetObjName(RightParent),' Result=',GetObjName(Result));
- {$ENDIF}
- RaiseInconsistency(20170129141307);
- end;
- Dot.MExpr := Left;
- Dot.Name := TJSPrimaryExpressionIdent(Right).Name;
- FreeAndNil(Right);
- break;
- end
- else
- begin
- {$IFDEF VerbosePas2JS}
- writeln('CreateDotExpression Right=',Right.ClassName);
- {$ENDIF}
- DoError(20161024191240,nMemberExprMustBeIdentifier,sMemberExprMustBeIdentifier,[],aParent);
- end;
- until false;
- ok:=true;
- finally
- if not ok then
- begin
- Left.Free;
- FreeAndNil(Result);
- end;
- end;
- end;
- function TPasToJSConverter.CreateReferencedSet(El: TPasElement; SetExpr: TJSElement
- ): TJSElement;
- var
- Call: TJSCallExpression;
- begin
- Call:=CreateCallExpression(El);
- Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnSet_Reference]]);
- Call.Args.Elements.AddElement.Expr:=SetExpr;
- Result:=Call;
- end;
- function TPasToJSConverter.CreateCloneRecord(El: TPasElement;
- ResolvedEl: TPasResolverResult; RecordExpr: TJSElement;
- AContext: TConvertContext): TJSElement;
- // create "new RecordType(RecordExpr)
- var
- NewExpr: TJSNewMemberExpression;
- begin
- if not (ResolvedEl.TypeEl is TPasRecordType) then
- RaiseInconsistency(20170212155956);
- NewExpr:=TJSNewMemberExpression(CreateElement(TJSNewMemberExpression,El));
- NewExpr.MExpr:=CreateReferencePathExpr(ResolvedEl.TypeEl,AContext);
- NewExpr.Args:=TJSArguments(CreateElement(TJSArguments,El));
- NewExpr.Args.Elements.AddElement.Expr:=RecordExpr;
- Result:=NewExpr;
- end;
- function TPasToJSConverter.CreateCallback(El: TPasElement;
- ResolvedEl: TPasResolverResult; AContext: TConvertContext): TJSElement;
- var
- Call: TJSCallExpression;
- Scope: TJSElement;
- DotExpr: TJSDotMemberExpression;
- Prim: TJSPrimaryExpressionIdent;
- aName: String;
- DotPos: SizeInt;
- FunName: String;
- begin
- // create "rtl.createCallback(scope,func)"
- Result:=nil;
- if not (ResolvedEl.IdentEl is TPasProcedure) then
- RaiseInconsistency(20170215140756);
- Call:=nil;
- Scope:=nil;
- try
- Call:=CreateCallExpression(El);
- // "rtl.createCallback"
- Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnProcType_Create]]);
- // add parameters
- Scope:=ConvertElement(El,AContext);
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.CreateCallback ',GetObjName(Scope));
- {$ENDIF}
- FunName:='';
- // the last element of Scope is the proc, chomp that off
- if Scope.ClassType=TJSDotMemberExpression then
- begin
- // chomp dot member -> rtl.createCallback(scope,"FunName")
- DotExpr:=TJSDotMemberExpression(Scope);
- FunName:=String(DotExpr.Name);
- DotPos:=PosLast('.',FunName);
- if DotPos>0 then
- begin
- // e.g. path dot $class.funname
- // keep DotExpr, chomp funname
- DotExpr.Name:=TJSString(LeftStr(FunName,DotPos-1));
- FunName:=copy(FunName,DotPos+1);
- if not IsValidJSIdentifier(DotExpr.Name) then
- begin
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.CreateCallback ',GetObjName(Scope),' DotExpr.Name="',DotExpr.Name,'"');
- {$ENDIF}
- DoError(20170215161802,nInvalidFunctionReference,sInvalidFunctionReference,[],El);
- end;
- end
- else
- begin
- // e.g. path dot funname
- // delete DotExpr
- Scope:=DotExpr.MExpr;
- DotExpr.MExpr:=nil;
- FreeAndNil(DotExpr);
- end;
- if not IsValidJSIdentifier(TJSString(FunName)) then
- begin
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.CreateCallback ',GetObjName(Scope),' FunName="',FunName,'"');
- {$ENDIF}
- DoError(20170215161802,nInvalidFunctionReference,sInvalidFunctionReference,[],El);
- end;
- Call.Args.Elements.AddElement.Expr:=Scope;
- // add function name as parameter
- Call.Args.Elements.AddElement.Expr:=CreateLiteralString(El,FunName);
- end
- else if Scope.ClassType=TJSPrimaryExpressionIdent then
- begin
- Prim:=TJSPrimaryExpressionIdent(Scope);
- aName:=String(Prim.Name);
- DotPos:=PosLast('.',aName);
- if DotPos>0 then
- begin
- // chomp dotted identifier -> rtl.createCallback(scope,"FunName")
- FunName:=copy(aName,DotPos+1);
- Prim.Name:=TJSString(LeftStr(aName,DotPos-1));
- Call.Args.Elements.AddElement.Expr:=Prim;
- // add function name as parameter
- Call.Args.Elements.AddElement.Expr:=CreateLiteralString(El,FunName);
- end
- else
- begin
- // nested proc -> rtl.createCallback(this,FunName)
- FunName:=aName;
- Prim.Name:='this';
- Call.Args.Elements.AddElement.Expr:=Prim;
- // add function as parameter
- Call.Args.Elements.AddElement.Expr:=CreateBuiltInIdentifierExpr(FunName);
- end;
- end
- else
- begin
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.CreateCallback invalid Scope=',GetObjName(Scope));
- {$ENDIF}
- RaiseNotSupported(El,AContext,20170215161210);
- end;
- Result:=Call;
- finally
- if Result=nil then
- begin
- Scope.Free;
- Call.Free;
- end;
- end;
- end;
- function TPasToJSConverter.CreateAssignStatement(LeftEl: TPasElement;
- AssignContext: TAssignContext): TJSElement;
- var
- LHS: TJSElement;
- AssignSt: TJSSimpleAssignStatement;
- begin
- Result:=nil;
- LHS:=ConvertElement(LeftEl,AssignContext);
- if AssignContext.Call<>nil then
- begin
- // has a setter -> right side was already added as parameter
- if AssignContext.RightSide<>nil then
- begin
- LHS.Free;
- RaiseInconsistency(20170207215447);
- end;
- Result:=LHS;
- end
- else
- begin
- AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,AssignContext.PasElement));
- AssignSt.LHS:=LHS;
- AssignSt.Expr:=AssignContext.RightSide;
- AssignContext.RightSide:=nil;
- Result:=AssignSt;
- end;
- end;
- function TPasToJSConverter.ConvertImplBlock(El: TPasImplBlock;
- AContext: TConvertContext): TJSElement;
- begin
- Result:=Nil;
- if (El is TPasImplStatement) then
- Result:=ConvertStatement(TPasImplStatement(El),AContext)
- else if (El.ClassType=TPasImplIfElse) then
- Result:=ConvertIfStatement(TPasImplIfElse(El),AContext)
- else if (El.ClassType=TPasImplRepeatUntil) then
- Result:=ConvertRepeatStatement(TPasImplRepeatUntil(El),AContext)
- else if (El.ClassType=TPasImplBeginBlock) then
- Result:=ConvertBeginEndStatement(TPasImplBeginBlock(El),AContext,true)
- else if (El.ClassType=TInitializationSection) then
- Result:=ConvertInitializationSection(TInitializationSection(El),AContext)
- else if (El.ClassType=TFinalizationSection) then
- Result:=ConvertFinalizationSection(TFinalizationSection(El),AContext)
- else if (El.ClassType=TPasImplTry) then
- Result:=ConvertTryStatement(TPasImplTry(El),AContext)
- else if (El.ClassType=TPasImplCaseOf) then
- Result:=ConvertCaseOfStatement(TPasImplCaseOf(El),AContext)
- else
- RaiseNotSupported(El,AContext,20161024192156);
- (*
- TPasImplBlock = class(TPasImplElement)
- TPasImplCaseOf = class(TPasImplBlock)
- TPasImplStatement = class(TPasImplBlock)
- TPasImplCaseElse = class(TPasImplBlock)
- TPasImplTry = class(TPasImplBlock)
- TPasImplTryHandler = class(TPasImplBlock)
- TPasImplTryFinally = class(TPasImplTryHandler)
- TPasImplTryExcept = class(TPasImplTryHandler)
- TPasImplTryExceptElse = class(TPasImplTryHandler)
- *)
- end;
- function TPasToJSConverter.ConvertPackage(El: TPasPackage;
- AContext: TConvertContext): TJSElement;
- begin
- RaiseNotSupported(El,AContext,20161024192555);
- Result:=Nil;
- // ToDo TPasPackage = class(TPasElement)
- end;
- function TPasToJSConverter.ConvertResString(El: TPasResString;
- AContext: TConvertContext): TJSElement;
- begin
- RaiseNotSupported(El,AContext,20161024192604);
- Result:=Nil;
- // ToDo: TPasResString
- end;
- function TPasToJSConverter.ConvertVariable(El: TPasVariable;
- AContext: TConvertContext): TJSElement;
- Var
- V : TJSVarDeclaration;
- vm: TVariableModifier;
- begin
- for vm in TVariableModifier do
- if (vm in El.VarModifiers) and (not (vm in [vmClass,vmExternal])) then
- RaiseNotSupported(El,AContext,20170208141622,'modifier '+VariableModifierNames[vm]);
- if El.LibraryName<>nil then
- RaiseNotSupported(El,AContext,20170208141844,'library name');
- if El.AbsoluteLocation<>'' then
- RaiseNotSupported(El,AContext,20170208141926,'absolute');
- V:=TJSVarDeclaration(CreateElement(TJSVarDeclaration,El));
- V.Name:=TransformVariableName(El,AContext);
- V.Init:=CreateVarInit(El,AContext);
- Result:=V;
- end;
- function TPasToJSConverter.ConvertProperty(El: TPasProperty;
- AContext: TConvertContext): TJSElement;
- begin
- Result:=Nil;
- if El.IndexExpr<>nil then
- RaiseNotSupported(El.IndexExpr,AContext,20170215103010,'property index expression');
- if El.ImplementsFunc<>nil then
- RaiseNotSupported(El.ImplementsFunc,AContext,20170215102923,'property implements function');
- if El.DispIDExpr<>nil then
- RaiseNotSupported(El.DispIDExpr,AContext,20170215103029,'property dispid expression');
- if El.DefaultExpr<>nil then
- RaiseNotSupported(El.DefaultExpr,AContext,20170215103129,'property default modifier');
- if El.StoredAccessor<>nil then
- RaiseNotSupported(El.StoredAccessor,AContext,20170215121145,'property stored accessor');
- if El.StoredAccessorName<>'' then
- RaiseNotSupported(El,AContext,20170215121248,'property stored accessor');
- // does not need any declaration. Access is redirected to getter/setter.
- end;
- function TPasToJSConverter.ConvertExportSymbol(El: TPasExportSymbol;
- AContext: TConvertContext): TJSElement;
- begin
- RaiseNotSupported(El,AContext,20161024192650);
- Result:=Nil;
- // ToDo: TPasExportSymbol
- end;
- function TPasToJSConverter.ConvertLabels(El: TPasLabels;
- AContext: TConvertContext): TJSElement;
- begin
- RaiseNotSupported(El,AContext,20161024192701);
- Result:=Nil;
- // ToDo: TPasLabels = class(TPasImplElement)
- end;
- function TPasToJSConverter.ConvertRaiseStatement(El: TPasImplRaise;
- AContext: TConvertContext): TJSElement;
- Var
- E : TJSElement;
- T : TJSThrowStatement;
- begin
- if El.ExceptObject<>Nil then
- E:=ConvertElement(El.ExceptObject,AContext)
- else
- E:=CreateBuiltInIdentifierExpr(FBuiltInNames[pbivnExceptObject]);
- T:=TJSThrowStatement(CreateElement(TJSThrowStatement,El));
- T.A:=E;
- Result:=T;
- end;
- function TPasToJSConverter.ConvertAssignStatement(El: TPasImplAssign;
- AContext: TConvertContext): TJSElement;
- Var
- LHS: TJSElement;
- T: TJSAssignStatement;
- AssignContext: TAssignContext;
- Flags: TPasResolverComputeFlags;
- LeftIsProcType: Boolean;
- begin
- Result:=nil;
- LHS:=nil;
- AssignContext:=TAssignContext.Create(El,nil,AContext);
- try
- if AContext.Resolver<>nil then
- begin
- AContext.Resolver.ComputeElement(El.left,AssignContext.LeftResolved,[rcNoImplicitProc]);
- Flags:=[];
- LeftIsProcType:=AContext.Resolver.IsProcedureType(AssignContext.LeftResolved,true);
- if LeftIsProcType then
- begin
- if msDelphi in AContext.CurrentModeswitches then
- Include(Flags,rcNoImplicitProc)
- else
- Include(Flags,rcNoImplicitProcType);
- end;
- AContext.Resolver.ComputeElement(El.right,AssignContext.RightResolved,Flags);
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.ConvertAssignStatement Left={',GetResolverResultDesc(AssignContext.LeftResolved),'} Right={',GetResolverResultDesc(AssignContext.RightResolved),'}');
- {$ENDIF}
- if LeftIsProcType and (msDelphi in AContext.CurrentModeswitches)
- and (AssignContext.RightResolved.BaseType=btProc) then
- begin
- // Delphi allows assigning a proc without @: proctype:=proc
- AssignContext.RightSide:=CreateCallback(El.right,AssignContext.RightResolved,AContext);
- end
- else if AssignContext.RightResolved.BaseType=btNil then
- begin
- if AContext.Resolver.IsArrayType(AssignContext.LeftResolved) then
- begin
- // array:=nil -> array:=[]
- AssignContext.RightSide:=TJSArrayLiteral(CreateElement(TJSArrayLiteral,El.right));
- end;
- end;
- end;
- if AssignContext.RightSide=nil then
- AssignContext.RightSide:=ConvertElement(El.right,AContext);
- if (AssignContext.RightResolved.BaseType=btSet)
- and (AssignContext.RightResolved.IdentEl<>nil) then
- begin
- // right side is a set variable -> create reference
- {$IFDEF VerbosePas2JS}
- //writeln('TPasToJSConverter.ConvertAssignStatement SET variable Right={',GetResolverResultDesc(AssignContext.RightResolved),'} AssignContext.RightResolved.IdentEl=',GetObjName(AssignContext.RightResolved.IdentEl));
- {$ENDIF}
- // create rtl.refSet(right)
- AssignContext.RightSide:=CreateReferencedSet(El.right,AssignContext.RightSide);
- end
- else if AssignContext.RightResolved.BaseType=btContext then
- begin
- if AssignContext.RightResolved.TypeEl.ClassType=TPasRecordType then
- begin
- // right side is a record -> clone
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.ConvertAssignStatement RECORD variable Right={',GetResolverResultDesc(AssignContext.RightResolved),'} AssignContext.RightResolved.IdentEl=',GetObjName(AssignContext.RightResolved.IdentEl));
- {$ENDIF}
- // create "new RightRecordType(RightRecord)"
- AssignContext.RightSide:=CreateCloneRecord(El.right,
- AssignContext.RightResolved,AssignContext.RightSide,AContext);
- end;
- end;
- LHS:=ConvertElement(El.left,AssignContext);
- if AssignContext.Call<>nil then
- begin
- // left side is a Setter -> RightSide was already inserted as parameter
- if AssignContext.RightSide<>nil then
- RaiseInconsistency(20170207215544);
- Result:=LHS;
- end
- else
- begin
- // left side is a variable -> create normal assign statement
- case El.Kind of
- akDefault: T:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El));
- akAdd: T:=TJSAddEqAssignStatement(CreateElement(TJSAddEqAssignStatement,El));
- akMinus: T:=TJSSubEqAssignStatement(CreateElement(TJSSubEqAssignStatement,El));
- akMul: T:=TJSMulEqAssignStatement(CreateElement(TJSMulEqAssignStatement,El));
- akDivision: T:=TJSDivEqAssignStatement(CreateElement(TJSDivEqAssignStatement,El));
- else RaiseNotSupported(El,AContext,20161107221807);
- end;
- T.Expr:=AssignContext.RightSide;
- AssignContext.RightSide:=nil;
- T.LHS:=LHS;
- Result:=T;
- end;
- finally
- if Result=nil then
- LHS.Free;
- AssignContext.RightSide.Free;
- AssignContext.Free;
- end;
- end;
- function TPasToJSConverter.ConvertCommand(El: TPasImplCommand;
- AContext: TConvertContext): TJSElement;
- begin
- RaiseNotSupported(El,AContext,20161024192705);
- Result:=Nil;
- // ToDo: TPasImplCommand = class(TPasImplElement)
- end;
- function TPasToJSConverter.ConvertIfStatement(El: TPasImplIfElse;
- AContext: TConvertContext): TJSElement;
- Var
- C,BThen,BElse : TJSElement;
- T : TJSIfStatement;
- ok: Boolean;
- begin
- if AContext=nil then ;
- C:=Nil;
- BThen:=Nil;
- BElse:=Nil;
- ok:=false;
- try
- C:=ConvertElement(El.ConditionExpr,AContext);
- if Assigned(El.IfBranch) then
- BThen:=ConvertElement(El.IfBranch,AContext);
- if Assigned(El.ElseBranch) then
- BElse:=ConvertElement(El.ElseBranch,AContext);
- ok:=true;
- finally
- if not ok then
- begin
- FreeAndNil(C);
- FreeAndNil(BThen);
- FreeAndNil(BElse);
- end;
- end;
- T:=TJSIfStatement(CreateElement(TJSIfStatement,El));
- T.Cond:=C;
- T.BTrue:=BThen;
- T.BFalse:=BElse;
- Result:=T;
- end;
- function TPasToJSConverter.ConvertWhileStatement(El: TPasImplWhileDo;
- AContext: TConvertContext): TJSElement;
- Var
- C : TJSElement;
- B : TJSElement;
- W : TJSWhileStatement;
- ok: Boolean;
- begin
- Result:=Nil;
- C:=Nil;
- B:=Nil;
- ok:=false;
- try
- C:=ConvertElement(EL.ConditionExpr,AContext);
- if Assigned(EL.Body) then
- B:=ConvertElement(EL.Body,AContext)
- else
- B:=TJSEmptyBlockStatement(CreateElement(TJSEmptyBlockStatement,El));
- ok:=true;
- finally
- if not ok then
- begin
- FreeAndNil(B);
- FreeAndNil(C);
- end;
- end;
- W:=TJSWhileStatement(CreateElement(TJSWhileStatement,El));
- W.Cond:=C;
- W.Body:=B;
- Result:=W;
- end;
- function TPasToJSConverter.ConvertRepeatStatement(El: TPasImplRepeatUntil;
- AContext: TConvertContext): TJSElement;
- Var
- C : TJSElement;
- N : TJSUnaryNotExpression;
- W : TJSDoWhileStatement;
- B : TJSElement;
- ok: Boolean;
- begin
- Result:=Nil;
- C:=Nil;
- B:=Nil;
- ok:=false;
- try
- C:=ConvertElement(EL.ConditionExpr,AContext);
- N:=TJSUnaryNotExpression(CreateElement(TJSUnaryNotExpression,EL.ConditionExpr));
- N.A:=C;
- B:=ConvertImplBlockElements(El,AContext,false);
- ok:=true;
- finally
- if not ok then
- begin
- FreeAndNil(B);
- FreeAndNil(C);
- end;
- end;
- W:=TJSDoWhileStatement(CreateElement(TJSDoWhileStatement,El));
- W.Cond:=N;
- W.Body:=B;
- Result:=W;
- end;
- function TPasToJSConverter.ConvertForStatement(El: TPasImplForLoop;
- AContext: TConvertContext): TJSElement;
- // Creates the following code:
- // var $loopend=<EndExpr>;
- // for(LoopVar=<StartExpr>; LoopVar<=$loopend; LoopVar++){}
- // if(LoopVar>$loopend)LoopVar--; // this line is only added if LoopVar is read later
- //
- // The StartExpr must be executed exactly once at beginning.
- // The EndExpr must be executed exactly once at beginning.
- // LoopVar can be a varname or programname.varname
- Var
- ForSt : TJSForStatement;
- List, ListEnd: TJSStatementList;
- SimpleAss : TJSSimpleAssignStatement;
- VarDecl : TJSVarDeclaration;
- Incr, Decr : TJSUNaryExpression;
- BinExp : TJSBinaryExpression;
- VarStat: TJSVariableStatement;
- IfSt: TJSIfStatement;
- GTExpr: TJSRelationalExpression;
- CurLoopEndVarName: String;
- FuncContext: TConvertContext;
- ResolvedVar: TPasResolverResult;
- function NeedDecrAfterLoop: boolean;
- var
- ResolvedVar: TPasResolverResult;
- aParent: TPasElement;
- ProcBody: TProcedureBody;
- FindData: TForLoopFindData;
- begin
- Result:=true;
- if AContext.Resolver=nil then exit(false);
- AContext.Resolver.ComputeElement(El.VariableName,ResolvedVar,[rcNoImplicitProc]);
- if ResolvedVar.IdentEl=nil then
- exit;
- if ResolvedVar.IdentEl.Parent is TProcedureBody then
- begin
- // loopvar is a local var
- ProcBody:=TProcedureBody(ResolvedVar.IdentEl.Parent);
- aParent:=El;
- while true do
- begin
- aParent:=aParent.Parent;
- if aParent=nil then exit;
- if aParent is TProcedureBody then
- begin
- if aParent<>ProcBody then exit;
- break;
- end;
- end;
- // loopvar is a local var of the same function as where the loop is
- // -> check if it is read after the loop
- FindData:=Default(TForLoopFindData);
- FindData.ForLoop:=El;
- FindData.LoopVar:=ResolvedVar.IdentEl;
- ProcBody.Body.ForEachCall(@ForLoop_OnProcBodyElement,@FindData);
- if not FindData.LoopVarRead then
- exit(false);
- end;
- end;
- begin
- Result:=Nil;
- BinExp:=Nil;
- if AContext.Access<>caRead then
- RaiseInconsistency(20170213213740);
- // get function context
- FuncContext:=AContext;
- while (FuncContext.Parent<>nil) and (not (FuncContext is TFunctionContext)) do
- FuncContext:=FuncContext.Parent;
- // create unique loopend var name
- CurLoopEndVarName:=FuncContext.CreateLocalIdentifier(FBuiltInNames[pbivnLoopEnd]);
- // loopvar:=
- // for (statementlist...
- List:=TJSStatementList(CreateElement(TJSStatementList,El));
- ListEnd:=List;
- try
- // add "var $loopend=<EndExpr>"
- VarStat:=TJSVariableStatement(CreateElement(TJSVariableStatement,El));
- List.A:=VarStat;
- VarDecl:=TJSVarDeclaration(CreateElement(TJSVarDeclaration,El));
- VarStat.A:=VarDecl;
- VarDecl.Name:=CurLoopEndVarName;
- VarDecl.Init:=ConvertElement(El.EndExpr,AContext);
- // add "for()"
- ForSt:=TJSForStatement(CreateElement(TJSForStatement,El));
- List.B:=ForSt;
- // add "LoopVar=<StartExpr>;"
- SimpleAss:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El.StartExpr));
- ForSt.Init:=SimpleAss;
- if AContext.Resolver<>nil then
- begin
- AContext.Resolver.ComputeElement(El.VariableName,ResolvedVar,[rcNoImplicitProc]);
- if not (ResolvedVar.IdentEl is TPasVariable) then
- DoError(20170213214404,nExpectedXButFoundY,sExpectedXButFoundY,['var',GetResolverResultDescription(ResolvedVar)],El);
- end;
- SimpleAss.LHS:=ConvertElement(El.VariableName,AContext);
- SimpleAss.Expr:=ConvertElement(El.StartExpr,AContext);
- // add "LoopVar<=$loopend"
- if El.Down then
- BinExp:=TJSRelationalExpressionGE(CreateElement(TJSRelationalExpressionGE,El.EndExpr))
- else
- BinExp:=TJSRelationalExpressionLE(CreateElement(TJSRelationalExpressionLE,El.EndExpr));
- ForSt.Cond:=BinExp;
- BinExp.A:=ConvertElement(El.VariableName,AContext);
- BinExp.B:=CreateIdentifierExpr(CurLoopEndVarName,El.EndExpr,AContext);
- // add "LoopVar++"
- if El.Down then
- Incr:=TJSUnaryPostMinusMinusExpression(CreateElement(TJSUnaryPostMinusMinusExpression,El))
- else
- Incr:=TJSUnaryPostPlusPlusExpression(CreateElement(TJSUnaryPostPlusPlusExpression,El));
- ForSt.Incr:=Incr;
- Incr.A:=ConvertElement(El.VariableName,AContext);
- // add body
- if El.Body<>nil then
- ForSt.Body:=ConvertElement(El.Body,AContext);
- if NeedDecrAfterLoop then
- begin
- // add "if(LoopVar>$loopend)LoopVar--;"
- // add "if()"
- IfSt:=TJSIfStatement(CreateElement(TJSIfStatement,El));
- AddToStatementList(List,ListEnd,IfSt,El);
- // add "LoopVar>$loopend"
- if El.Down then
- GTExpr:=TJSRelationalExpressionLT(CreateElement(TJSRelationalExpressionLT,El))
- else
- GTExpr:=TJSRelationalExpressionGT(CreateElement(TJSRelationalExpressionGT,El));
- IfSt.Cond:=GTExpr;
- GTExpr.A:=ConvertElement(El.VariableName,AContext);
- GTExpr.B:=CreateIdentifierExpr(CurLoopEndVarName,El.EndExpr,AContext);
- // add "LoopVar--"
- if El.Down then
- Decr:=TJSUnaryPostPlusPlusExpression(CreateElement(TJSUnaryPostPlusPlusExpression,El))
- else
- Decr:=TJSUnaryPostMinusMinusExpression(CreateElement(TJSUnaryPostMinusMinusExpression,El));
- IfSt.BTrue:=Decr;
- Decr.A:=ConvertElement(El.VariableName,AContext);
- end;
- Result:=List;
- finally
- if Result=nil then
- List.Free;
- end;
- end;
- function TPasToJSConverter.ConvertSimpleStatement(El: TPasImplSimple;
- AContext: TConvertContext): TJSElement;
- Var
- E : TJSElement;
- begin
- E:=ConvertElement(EL.Expr,AContext);
- if E=nil then
- exit(nil); // e.g. "inherited;" without ancestor proc
- Result:=TJSExpressionStatement(CreateElement(TJSExpressionStatement,El));
- TJSExpressionStatement(Result).A:=E;
- end;
- function TPasToJSConverter.ConvertWithStatement(El: TPasImplWithDo;
- AContext: TConvertContext): TJSElement;
- Var
- B,E , Expr: TJSElement;
- W,W2 : TJSWithStatement;
- I : Integer;
- ok: Boolean;
- PasExpr: TPasElement;
- V: TJSVariableStatement;
- VarDecl: TJSVarDeclaration;
- FuncContext: TFunctionContext;
- FirstSt, LastSt: TJSStatementList;
- WithScope: TPasWithScope;
- WithExprScope: TPas2JSWithExprScope;
- begin
- Result:=nil;
- if AContext.Resolver<>nil then
- begin
- // with Resolver:
- // Insert for each expression a local var. Example:
- // with aPoint do X:=3;
- // convert to
- // var $with1 = aPoint;
- // $with1.X = 3;
- FuncContext:=TFunctionContext(AContext.GetContextOfType(TFunctionContext));
- if FuncContext=nil then
- RaiseInconsistency(20170212003759);
- FirstSt:=nil;
- LastSt:=nil;
- try
- WithScope:=El.CustomData as TPasWithScope;
- for i:=0 to El.Expressions.Count-1 do
- begin
- PasExpr:=TPasElement(El.Expressions[i]);
- Expr:=ConvertElement(PasExpr,AContext);
- // create unique local var name
- WithExprScope:=WithScope.ExpressionScopes[i] as TPas2JSWithExprScope;
- WithExprScope.WithVarName:=FuncContext.CreateLocalIdentifier(FBuiltInNames[pbivnWith]);
- // create local "var $with1 = expr;"
- V:=TJSVariableStatement(CreateElement(TJSVariableStatement,PasExpr));
- VarDecl:=TJSVarDeclaration(CreateElement(TJSVarDeclaration,PasExpr));
- V.A:=VarDecl;
- VarDecl.Name:=WithExprScope.WithVarName;
- VarDecl.Init:=Expr;
- AddToStatementList(FirstSt,LastSt,V,PasExpr);
- end;
- if Assigned(El.Body) then
- begin
- B:=ConvertElement(El.Body,AContext);
- AddToStatementList(FirstSt,LastSt,B,El.Body);
- end;
- Result:=FirstSt;
- finally
- if Result=nil then
- FreeAndNil(FirstSt);
- end;
- end
- else
- begin
- // without Resolver use as fallback the JavaScript with(){}
- W:=Nil;
- if Assigned(El.Body) then
- B:=ConvertElement(El.Body,AContext)
- else
- B:=TJSEmptyBlockStatement(CreateElement(TJSEmptyBlockStatement,El));
- ok:=false;
- try
- For I:=0 to El.Expressions.Count-1 do
- begin
- E:=ConvertElement(TPasElement(El.Expressions[i]),AContext);
- W2:=TJSWithStatement(CreateElement(TJSWithStatement,TPasElement(El.Expressions[i])));
- if Not Assigned(Result) then // result is the first
- Result:=W2;
- if Assigned(W) then // Chain
- W.B:=W2;
- W:=W2; // W is the last
- W.A:=E;
- end;
- ok:=true;
- finally
- if not ok then
- begin
- FreeAndNil(E);
- FreeAndNil(Result);
- end;
- end;
- W.B:=B;
- end;
- end;
- function TPasToJSConverter.IsElementUsed(El: TPasElement): boolean;
- begin
- if Assigned(OnIsElementUsed) then
- Result:=OnIsElementUsed(Self,El)
- else
- Result:=true;
- end;
- procedure TPasToJSConverter.RaiseInconsistency(Id: int64);
- begin
- raise Exception.Create('TPasToJSConverter.RaiseInconsistency['+IntToStr(Id)+']: you found a bug');
- end;
- function TPasToJSConverter.CreateUnary(Members: array of string; E: TJSElement): TJSUnary;
- var
- unary: TJSUnary;
- asi: TJSSimpleAssignStatement;
- begin
- unary := TJSUnary.Create(0, 0, '');
- asi := TJSSimpleAssignStatement.Create(0, 0, '');
- unary.A := asi;
- asi.Expr := E;
- asi.LHS := CreateMemberExpression(Members);
- Result := unary;
- end;
- function TPasToJSConverter.CreateMemberExpression(Members: array of string): TJSDotMemberExpression;
- var
- pex: TJSPrimaryExpressionIdent;
- MExpr: TJSDotMemberExpression;
- LastMExpr: TJSDotMemberExpression;
- k: integer;
- begin
- if Length(Members) < 2 then
- DoError(20161024192715,'internal error: member expression with less than two members');
- LastMExpr := nil;
- for k:=High(Members) downto Low(Members)+1 do
- begin
- MExpr := TJSDotMemberExpression.Create(0, 0, '');
- MExpr.Name := TJSString(Members[k]);
- if LastMExpr=nil then
- Result := MExpr
- else
- LastMExpr.MExpr := MExpr;
- LastMExpr := MExpr;
- end;
- pex := TJSPrimaryExpressionIdent.Create(0, 0, '');
- pex.Name := TJSString(Members[Low(Members)]);
- LastMExpr.MExpr := pex;
- end;
- function TPasToJSConverter.CreateCallExpression(El: TPasElement
- ): TJSCallExpression;
- begin
- Result:=TJSCallExpression(CreateElement(TJSCallExpression,El));
- Result.Args:=TJSArguments(CreateElement(TJSArguments,El));
- end;
- function TPasToJSConverter.CreateUsesList(UsesSection: TPasSection;
- AContext: TConvertContext): TJSArrayLiteral;
- var
- ArgArray: TJSArrayLiteral;
- k: Integer;
- El: TPasElement;
- anUnitName: String;
- ArgEx: TJSLiteral;
- UsesList: TFPList;
- begin
- UsesList:=UsesSection.UsesList;
- ArgArray:=TJSArrayLiteral.Create(0,0);
- if UsesList<>nil then
- for k:=0 to UsesList.Count-1 do
- begin
- El:=TPasElement(UsesList[k]);
- if not (El is TPasModule) then continue;
- if (not IsElementUsed(El)) and (CompareText('system',El.Name)<>0) then
- continue;
- anUnitName := TransformVariableName(TPasModule(El),AContext);
- ArgEx := CreateLiteralString(UsesSection,anUnitName);
- ArgArray.Elements.AddElement.Expr := ArgEx;
- end;
- Result:=ArgArray;
- end;
- procedure TPasToJSConverter.AddToStatementList(var First,
- Last: TJSStatementList; Add: TJSElement; Src: TPasElement);
- var
- SL2: TJSStatementList;
- begin
- if not Assigned(Add) then exit;
- if Add is TJSStatementList then
- begin
- // add list
- if TJSStatementList(Add).A=nil then
- begin
- // empty list -> skip
- if TJSStatementList(Add).B<>nil then
- raise Exception.Create('internal error: AddToStatementList add list A=nil, B<>nil, B='+TJSStatementList(Add).B.ClassName);
- FreeAndNil(Add);
- end
- else if Last=nil then
- begin
- // our list is not yet started -> simply take the extra list
- Last:=TJSStatementList(Add);
- First:=Last;
- end
- else
- begin
- // merge lists (append)
- if Last.B<>nil then
- begin
- // add a nil to the end of chain
- SL2:=TJSStatementList(CreateElement(TJSStatementList,Src));
- SL2.A:=Last.B;
- Last.B:=SL2;
- Last:=SL2;
- // Last.B is now nil
- end;
- Last.B:=Add;
- while Last.B is TJSStatementList do
- Last:=TJSStatementList(Last.B);
- end;
- end
- else
- begin
- if Last=nil then
- begin
- // start list
- Last:=TJSStatementList(CreateElement(TJSStatementList,Src));
- First:=Last;
- Last.A:=Add;
- end
- else if Last.B=nil then
- // second element
- Last.B:=Add
- else
- begin
- // add to chain
- while Last.B is TJSStatementList do
- Last:=TJSStatementList(Last.B);
- SL2:=TJSStatementList(CreateElement(TJSStatementList,Src));
- SL2.A:=Last.B;
- Last.B:=SL2;
- Last:=SL2;
- Last.B:=Add;
- end;
- end;
- end;
- function TPasToJSConverter.CreateValInit(PasType: TPasType; Expr: TPasElement;
- El: TPasElement; AContext: TConvertContext): TJSElement;
- var
- T: TPasType;
- Lit: TJSLiteral;
- bt: TResolverBaseType;
- JSBaseType: TPas2jsBaseType;
- begin
- T:=PasType;
- if AContext.Resolver<>nil then
- T:=AContext.Resolver.ResolveAliasType(T);
- if (T is TPasArrayType) then
- Result:=CreateArrayInit(TPasArrayType(T),Expr,El,AContext)
- else if T is TPasRecordType then
- Result:=CreateRecordInit(TPasRecordType(T),Expr,El,AContext)
- else if Assigned(Expr) then
- Result:=ConvertElement(Expr,AContext)
- else if T is TPasSetType then
- Result:=TJSObjectLiteral(CreateElement(TJSObjectLiteral,El))
- else
- begin
- // always init with a default value to create a typed variable (faster and more readable)
- Lit:=TJSLiteral(CreateElement(TJSLiteral,El));
- Result:=Lit;
- if T=nil then
- Lit.Value.IsUndefined:=true
- else if (T.ClassType=TPasPointerType)
- or (T.ClassType=TPasClassType)
- or (T.ClassType=TPasClassOfType)
- or (T.ClassType=TPasProcedureType)
- or (T.ClassType=TPasFunctionType) then
- Lit.Value.IsNull:=true
- else if T.ClassType=TPasStringType then
- Lit.Value.AsString:=''
- else if T.ClassType=TPasEnumType then
- Lit.Value.AsNumber:=0
- else if T.ClassType=TPasUnresolvedSymbolRef then
- begin
- if T.CustomData is TResElDataBaseType then
- begin
- bt:=TResElDataBaseType(T.CustomData).BaseType;
- if bt in btAllInteger then
- Lit.Value.AsNumber:=0
- else if bt in btAllFloats then
- Lit.Value.CustomValue:='0.0'
- else if bt in btAllStringAndChars then
- Lit.Value.AsString:=''
- else if bt in btAllBooleans then
- Lit.Value.AsBoolean:=false
- else if bt in [btNil,btPointer,btProc] then
- Lit.Value.IsNull:=true
- else if (bt=btCustom) and (T.CustomData is TResElDataPas2JSBaseType) then
- begin
- JSBaseType:=TResElDataPas2JSBaseType(T.CustomData).JSBaseType;
- if JSBaseType=pbtJSValue then
- Lit.Value.IsUndefined:=true;
- end
- else
- begin
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.CreateVarInit unknown PasType T=',GetObjName(T),' basetype=',BaseTypeNames[bt]);
- {$ENDIF}
- RaiseNotSupported(PasType,AContext,20170208162121);
- end;
- end
- else if (CompareText(T.Name,'longint')=0)
- or (CompareText(T.Name,'int64')=0)
- or (CompareText(T.Name,'real')=0)
- or (CompareText(T.Name,'double')=0)
- or (CompareText(T.Name,'single')=0) then
- Lit.Value.AsNumber:=0.0
- else if (CompareText(T.Name,'boolean')=0) then
- Lit.Value.AsBoolean:=false
- else if (CompareText(T.Name,'string')=0)
- or (CompareText(T.Name,'char')=0)
- then
- Lit.Value.AsString:=''
- else
- begin
- Lit.Value.IsUndefined:=true;
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.CreateVarInit unknown PasType class=',T.ClassName,' name=',T.Name);
- {$ENDIF}
- end;
- end
- else
- begin
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.CreateValInit unknown PasType ',GetObjName(T));
- {$ENDIF}
- RaiseNotSupported(PasType,AContext,20170208161506);
- end;
- end;
- end;
- function TPasToJSConverter.CreateVarInit(El: TPasVariable;
- AContext: TConvertContext): TJSElement;
- begin
- Result:=CreateValInit(El.VarType,El.Expr,El,AContext);
- end;
- function TPasToJSConverter.CreateLiteralNumber(El: TPasElement;
- const n: TJSNumber): TJSLiteral;
- begin
- Result:=TJSLiteral(CreateElement(TJSLiteral,El));
- Result.Value.AsNumber:=n;
- end;
- function TPasToJSConverter.CreateLiteralString(El: TPasElement; const s: string
- ): TJSLiteral;
- begin
- Result:=TJSLiteral(CreateElement(TJSLiteral,El));
- Result.Value.AsString:=TJSString(s);
- end;
- function TPasToJSConverter.CreateLiteralJSString(El: TPasElement;
- const s: TJSString): TJSLiteral;
- begin
- Result:=TJSLiteral(CreateElement(TJSLiteral,El));
- Result.Value.AsString:=s;
- end;
- function TPasToJSConverter.CreateLiteralBoolean(El: TPasElement; b: boolean
- ): TJSLiteral;
- begin
- Result:=TJSLiteral(CreateElement(TJSLiteral,El));
- Result.Value.AsBoolean:=b;
- end;
- function TPasToJSConverter.CreateLiteralNull(El: TPasElement): TJSLiteral;
- begin
- Result:=TJSLiteral(CreateElement(TJSLiteral,El));
- Result.Value.IsNull:=true;
- end;
- function TPasToJSConverter.CreateLiteralUndefined(El: TPasElement): TJSLiteral;
- begin
- Result:=TJSLiteral(CreateElement(TJSLiteral,El));
- Result.Value.IsUndefined:=true;
- end;
- function TPasToJSConverter.CreateRecordInit(aRecord: TPasRecordType;
- Expr: TPasElement; El: TPasElement; AContext: TConvertContext): TJSElement;
- // new recordtype()
- var
- NewMemE: TJSNewMemberExpression;
- begin
- if Expr<>nil then
- RaiseNotSupported(Expr,AContext,20161024192747);
- NewMemE:=TJSNewMemberExpression(CreateElement(TJSNewMemberExpression,El));
- Result:=NewMemE;
- NewMemE.MExpr:=CreateReferencePathExpr(aRecord,AContext);
- end;
- function TPasToJSConverter.CreateArrayInit(ArrayType: TPasArrayType;
- Expr: TPasElement; El: TPasElement; AContext: TConvertContext): TJSElement;
- var
- Call: TJSCallExpression;
- DimArray, ArrLit: TJSArrayLiteral;
- i, DimSize: Integer;
- RangeResolved, ElTypeResolved, ExprResolved: TPasResolverResult;
- Range: TPasExpr;
- Lit: TJSLiteral;
- CurArrayType: TPasArrayType;
- DefaultValue: TJSElement;
- ArrayValues: TPasExprArray;
- begin
- if Assigned(Expr) then
- begin
- // init array with constant(s)
- if AContext.Resolver=nil then
- DoError(20161024192739,nInitializedArraysNotSupported,sInitializedArraysNotSupported,[],ArrayType);
- ArrLit:=TJSArrayLiteral(CreateElement(TJSArrayLiteral,El));
- try
- AContext.Resolver.ComputeElement(Expr,ExprResolved,[rcConstant]);
- if (ExprResolved.BaseType=btArray)
- and (ExprResolved.ExprEl is TArrayValues) then
- begin
- ArrayValues:=TArrayValues(ExprResolved.ExprEl).Values;
- for i:=0 to length(ArrayValues)-1 do
- ArrLit.Elements.AddElement.Expr:=ConvertElement(ArrayValues[i],AContext);
- end
- else
- RaiseNotSupported(Expr,AContext,20170223133034);
- Result:=ArrLit;
- finally
- if Result=nil then
- ArrLit.Free;
- end;
- end
- else if length(ArrayType.Ranges)=0 then
- begin
- // empty dynamic array: []
- Result:=TJSArrayLiteral(CreateElement(TJSArrayLiteral,El));
- end
- else
- begin
- // static array
- // create "rtl.arrayNewMultiDim([dim1,dim2,...],defaultvalue)"
- if AContext.Resolver=nil then
- RaiseNotSupported(El,AContext,20170223113050,'');
- Result:=nil;
- try
- Call:=CreateCallExpression(El);
- Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnArray_NewMultiDim]]);
- // add parameter [dim1,dim2,...]
- DimArray:=TJSArrayLiteral(CreateElement(TJSArrayLiteral,El));
- Call.Args.Elements.AddElement.Expr:=DimArray;
- CurArrayType:=ArrayType;
- while true do
- begin
- for i:=0 to length(CurArrayType.Ranges)-1 do
- begin
- Range:=CurArrayType.Ranges[i];
- // compute size of this dimension
- AContext.Resolver.ComputeElement(Range,RangeResolved,[rcConstant]);
- DimSize:=AContext.Resolver.GetRangeLength(RangeResolved);
- if DimSize=0 then
- RaiseNotSupported(Range,AContext,20170223113318);
- Lit:=CreateLiteralNumber(El,DimSize);
- DimArray.Elements.AddElement.Expr:=Lit;
- end;
- AContext.Resolver.ComputeElement(CurArrayType.ElType,ElTypeResolved,[rcType]);
- if (ElTypeResolved.TypeEl is TPasArrayType) then
- begin
- CurArrayType:=TPasArrayType(ElTypeResolved.TypeEl);
- if length(CurArrayType.Ranges)>0 then
- begin
- // nested static array
- continue;
- end;
- end;
- break;
- end;
- // add parameter defaultvalue
- DefaultValue:=CreateValInit(ElTypeResolved.TypeEl,nil,El,AContext);
- Call.Args.Elements.AddElement.Expr:=DefaultValue;
- Result:=Call;
- finally
- if Result=nil then
- Call.Free;
- end;
- end;
- end;
- function TPasToJSConverter.CreateCmpArrayWithNil(El: TPasElement;
- JSArray: TJSElement; OpCode: TExprOpCode): TJSElement;
- var
- Call: TJSCallExpression;
- BinExpr: TJSBinaryExpression;
- begin
- if not (OpCode in [eopEqual,eopNotEqual]) then
- RaiseInconsistency(20170401184819);
- Call:=CreateCallExpression(El);
- Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnArray_Length]]);
- Call.Args.Elements.AddElement.Expr:=JSArray;
- if OpCode=eopEqual then
- BinExpr:=TJSEqualityExpressionEQ(CreateElement(TJSEqualityExpressionEQ,El))
- else
- BinExpr:=TJSRelationalExpressionGT(CreateElement(TJSRelationalExpressionGT,El));
- BinExpr.A:=Call;
- BinExpr.B:=CreateLiteralNumber(El,0);
- Result:=BinExpr;
- end;
- function TPasToJSConverter.CreateReferencePath(El: TPasElement;
- AContext: TConvertContext; Kind: TRefPathKind; Full: boolean;
- Ref: TResolvedReference): string;
- { Notes:
- - local var, argument or result variable, even higher lvl does not need a reference path
- local vars are also argument, result var, result variable
- - 'this':
- - in interface function (even nested) 'this' is the interface,
- - in implementation function (even nested) 'this' is the implementation,
- - in initialization 'this' is interface
- - in method body 'this' is the instance
- - in class method body 'this' is the class
- - with context uses the local $withnnn var
- otherwise use absolute path
- }
- function GetReferenceEl: TPasElement;
- begin
- if Ref<>nil then
- Result:=Ref.Element
- else
- Result:=El;
- end;
- function IsLocalVar: boolean;
- begin
- Result:=false;
- if El.ClassType=TPasArgument then
- exit(true);
- if El.ClassType=TPasResultElement then
- exit(true);
- if AContext.Resolver=nil then
- exit(true);
- if El.Parent=nil then
- RaiseNotSupported(El,AContext,20170203121306,GetObjName(El));
- if El.Parent.ClassType=TPasImplExceptOn then
- exit(true);
- if not (El.Parent is TProcedureBody) then exit;
- Result:=true;
- end;
- procedure Prepend(var aPath: string; Prefix: string);
- begin
- if aPath<>'' then
- aPath:='.'+aPath;
- aPath:=Prefix+aPath;
- end;
- function IsClassFunction(Proc: TPasElement): boolean;
- var
- C: TClass;
- begin
- if Proc=nil then exit(false);
- C:=Proc.ClassType;
- Result:=(C=TPasClassFunction) or (C=TPasClassProcedure)
- or (C=TPasClassConstructor) or (C=TPasClassDestructor);
- end;
- procedure Append_GetClass(Member: TPasElement);
- begin
- if (Member.Parent as TPasClassType).IsExternal then
- exit;
- if Result<>'' then
- Result:=Result+'.'+FBuiltInNames[pbivnPtrClass]
- else
- Result:=FBuiltInNames[pbivnPtrClass];
- end;
- var
- FoundModule: TPasModule;
- This, ParentEl: TPasElement;
- Dot: TDotContext;
- ThisContext: TFunctionContext;
- WithData: TPas2JSWithExprScope;
- ProcScope: TPasProcedureScope;
- begin
- Result:='';
- //writeln('TPasToJSConverter.CreateReferencePath START El=',GetObjName(El),' Parent=',GetObjName(El.Parent),' Context=',GetObjName(AContext));
- if AContext is TDotContext then
- begin
- Dot:=TDotContext(AContext);
- if Dot.Resolver<>nil then
- begin
- if El is TPasVariable then
- begin
- //writeln('TPasToJSConverter.CreateReferencePath Left=',GetResolverResultDesc(Dot.LeftResolved),' Right=class var ',GetObjName(El));
- if (ClassVarModifiersType*TPasVariable(El).VarModifiers<>[])
- and (Dot.Access=caAssign)
- and Dot.Resolver.ResolvedElIsClassInstance(Dot.LeftResolved) then
- begin
- // writing a class var
- Append_GetClass(El);
- end;
- end
- else if IsClassFunction(El) then
- begin
- if Dot.Resolver.ResolvedElIsClassInstance(Dot.LeftResolved) then
- // accessing a class method from an object, 'this' must be the class
- Append_GetClass(El);
- end;
- end;
- end
- else if (Ref<>nil) and (Ref.WithExprScope<>nil) then
- begin
- // using local WITH var
- WithData:=Ref.WithExprScope as TPas2JSWithExprScope;
- Prepend(Result,WithData.WithVarName);
- end
- else if IsLocalVar then
- begin
- // El is local var -> does not need path
- end
- else if (El is TPasProcedure) and (TPasProcedure(El).LibrarySymbolName<>nil)
- and not (El.Parent is TPasClassType) then
- begin
- // an external function -> use the literal
- if Kind=rpkPathAndName then
- Result:=ComputeConstString(TPasProcedure(El).LibrarySymbolName,AContext,true)
- else
- Result:='';
- exit;
- end
- else if (El is TPasVariable) and (TPasVariable(El).ExportName<>nil)
- and not (El.Parent is TPasClassType) then
- begin
- // an external var -> use the literal
- if Kind=rpkPathAndName then
- Result:=ComputeConstString(TPasVariable(El).ExportName,AContext,true)
- else
- Result:='';
- exit;
- end
- else if (El is TPasClassType) and TPasClassType(El).IsExternal then
- begin
- Result:=TPasClassType(El).ExternalName;
- exit;
- end
- else
- begin
- // need full path
- if El.Parent=nil then
- RaiseNotSupported(El,AContext,20170201172141,GetObjName(El));
- if (El.CustomData is TPasProcedureScope) then
- begin
- ProcScope:=TPasProcedureScope(El.CustomData);
- if ProcScope.DeclarationProc<>nil then
- El:=ProcScope.DeclarationProc;
- end;
- ThisContext:=AContext.GetThisContext;
- if ThisContext<>nil then
- This:=ThisContext.GetThis
- else
- This:=nil;
- ParentEl:=El.Parent;
- while ParentEl<>nil do
- begin
- if (ParentEl.CustomData is TPasProcedureScope) then
- begin
- ProcScope:=TPasProcedureScope(ParentEl.CustomData);
- if ProcScope.DeclarationProc<>nil then
- ParentEl:=ProcScope.DeclarationProc;
- end;
- if ParentEl.ClassType=TImplementationSection then
- begin
- // element is in an implementation section
- if ParentEl=This then
- Prepend(Result,'this')
- else
- begin
- FoundModule:=El.GetModule;
- if FoundModule=nil then
- RaiseInconsistency(20161024192755);
- if AContext.GetRootModule=FoundModule then
- // in same unit -> use '$impl'
- Prepend(Result,FBuiltInNames[pbivnImplementation])
- else
- // in other unit -> use pas.unitname.$impl
- Prepend(Result,FBuiltInNames[pbivnModules]
- +'.'+TransformModuleName(FoundModule,AContext)
- +'.'+FBuiltInNames[pbivnImplementation]);
- end;
- break;
- end
- else if ParentEl is TPasModule then
- begin
- // element is in an unit interface or program/library section
- if ParentEl=This then
- Prepend(Result,'this')
- else
- Prepend(Result,FBuiltInNames[pbivnModules]
- +'.'+TransformModuleName(TPasModule(ParentEl),AContext));
- break;
- end
- else if (ParentEl.ClassType=TPasClassType)
- or (ParentEl.ClassType=TPasRecordType) then
- begin
- // element is a class or record
- if Full then
- Prepend(Result,ParentEl.Name)
- else
- begin
- // Pascal and JS have similar scoping rules, so we can use 'this'.
- Result:='this';
- if (ThisContext<>nil) and (not IsClassFunction(ThisContext.PasElement)) then
- begin
- // 'this' is an class instance
- if El is TPasVariable then
- begin
- //writeln('TPasToJSConverter.CreateReferencePath class var ',GetObjName(El),' This=',GetObjName(This));
- if (ClassVarModifiersType*TPasVariable(El).VarModifiers<>[])
- and (AContext.Access=caAssign) then
- begin
- Append_GetClass(El); // writing a class var
- end;
- end
- else if IsClassFunction(El) then
- Append_GetClass(El); // accessing a class function
- end;
- break;
- end;
- end
- else if ParentEl.ClassType=TPasEnumType then
- Prepend(Result,ParentEl.Name);
- ParentEl:=ParentEl.Parent;
- end;
- end;
- if (Result<>'') and (Kind in [rpkPathWithDot,rpkPathAndName]) then
- Result:=Result+'.';
- if Kind=rpkPathAndName then
- Result:=Result+TransformVariableName(El,AContext);
- end;
- function TPasToJSConverter.CreateReferencePathExpr(El: TPasElement;
- AContext: TConvertContext; Full: boolean; Ref: TResolvedReference
- ): TJSPrimaryExpressionIdent;
- var
- Name: String;
- begin
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.CreateReferencePathExpr El="',GetObjName(El),'" El.Parent=',GetObjName(El.Parent));
- {$ENDIF}
- Name:=CreateReferencePath(El,AContext,rpkPathAndName,Full,Ref);
- Result:=CreateBuiltInIdentifierExpr(Name);
- end;
- procedure TPasToJSConverter.CreateProcedureCall(var Call: TJSCallExpression;
- Args: TParamsExpr; TargetProc: TPasProcedureType; AContext: TConvertContext);
- // create a call, adding call by reference and default values
- begin
- if Call=nil then
- Call:=TJSCallExpression(CreateElement(TJSCallExpression,Args));
- if ((Args=nil) or (length(Args.Params)=0))
- and ((TargetProc=nil) or (TargetProc.Args.Count=0)) then
- exit;
- if Call.Args=nil then
- Call.Args:=TJSArguments(CreateElement(TJSArguments,Args));
- CreateProcedureCallArgs(Call.Args.Elements,Args,TargetProc,AContext);
- end;
- procedure TPasToJSConverter.CreateProcedureCallArgs(
- Elements: TJSArrayLiteralElements; Args: TParamsExpr;
- TargetProc: TPasProcedureType; AContext: TConvertContext);
- // Add call arguments. Handle call by reference and default values
- var
- ArgContext: TConvertContext;
- i: Integer;
- Arg: TJSElement;
- TargetArgs: TFPList;
- TargetArg: TPasArgument;
- OldAccess: TCtxAccess;
- begin
- // get context
- ArgContext:=AContext;
- while ArgContext is TDotContext do
- ArgContext:=ArgContext.Parent;
- i:=0;
- OldAccess:=ArgContext.Access;
- if TargetProc<>nil then
- TargetArgs:=TargetProc.Args
- else
- TargetArgs:=nil;
- // add params
- if Args<>nil then
- while i<length(Args.Params) do
- begin
- if (TargetArgs<>nil) and (i<TargetArgs.Count) then
- TargetArg:=TPasArgument(TargetArgs[i])
- else
- TargetArg:=nil;
- Arg:=CreateProcCallArg(Args.Params[i],TargetArg,ArgContext);
- Elements.AddElement.Expr:=Arg;
- inc(i);
- end;
- // fill up default values
- if TargetProc<>nil then
- begin
- while i<TargetArgs.Count do
- begin
- TargetArg:=TPasArgument(TargetArgs[i]);
- if TargetArg.ValueExpr=nil then
- begin
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.CreateProcedureCallArgs missing default value: TargetProc=',TargetProc.Name,' i=',i);
- {$ENDIF}
- RaiseNotSupported(Args,AContext,20170201193601);
- end;
- AContext.Access:=caRead;
- Arg:=ConvertElement(TargetArg.ValueExpr,ArgContext);
- Elements.AddElement.Expr:=Arg;
- inc(i);
- end;
- end;
- ArgContext.Access:=OldAccess;
- end;
- function TPasToJSConverter.CreateProcCallArg(El: TPasExpr;
- TargetArg: TPasArgument; AContext: TConvertContext): TJSElement;
- var
- ExprResolved, ArgResolved: TPasResolverResult;
- ExprFlags: TPasResolverComputeFlags;
- NeedVar: Boolean;
- begin
- Result:=nil;
- if TargetArg=nil then
- begin
- // simple conversion
- AContext.Access:=caRead;
- Result:=ConvertElement(El,AContext);
- exit;
- end;
- if not (TargetArg.Access in [argDefault,argVar,argOut,argConst]) then
- DoError(20170213220927,nPasElementNotSupported,sPasElementNotSupported,
- [AccessNames[TargetArg.Access]],El);
- NeedVar:=TargetArg.Access in [argVar,argOut];
- AContext.Resolver.ComputeElement(TargetArg,ArgResolved,[]);
- ExprFlags:=[];
- if NeedVar then
- Include(ExprFlags,rcNoImplicitProc)
- else if AContext.Resolver.IsProcedureType(ArgResolved,true) then
- Include(ExprFlags,rcNoImplicitProcType);
- if (ArgResolved.TypeEl is TPasArrayType)
- and (El is TParamsExpr) and (TParamsExpr(El).Kind=pekSet) then
- begin
- // passing a set to an open array
- if NeedVar then
- RaiseNotSupported(El,AContext,20170326213042);
- Result:=ConvertOpenArrayParam(AContext.Resolver.ResolveAliasType(ArgResolved.TypeEl),
- TParamsExpr(El),AContext);
- exit;
- end;
- AContext.Resolver.ComputeElement(El,ExprResolved,ExprFlags);
- // consider TargetArg access
- if NeedVar then
- Result:=CreateProcCallArgRef(El,ExprResolved,TargetArg,AContext)
- else
- begin
- // pass as default, const or constref
- AContext.Access:=caRead;
- if (ExprResolved.BaseType=btNil) and (ArgResolved.TypeEl is TPasArrayType) then
- begin
- // arrays must never be null -> pass []
- Result:=TJSArrayLiteral(CreateElement(TJSArrayLiteral,El));
- exit;
- end;
- Result:=ConvertElement(El,AContext);
- if TargetArg.Access=argDefault then
- begin
- if (ExprResolved.BaseType=btSet) and (ExprResolved.IdentEl<>nil) then
- begin
- // right side is a set variable -> create reference
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.CreateProcedureCallArg create reference of SET variable Right={',GetResolverResultDesc(ExprResolved),'} AssignContext.RightResolved.IdentEl=',GetObjName(ExprResolved.IdentEl));
- {$ENDIF}
- // create rtl.refSet(right)
- Result:=CreateReferencedSet(El,Result);
- exit;
- end
- else if ExprResolved.BaseType=btContext then
- begin
- if ExprResolved.TypeEl.ClassType=TPasRecordType then
- begin
- // right side is a record -> clone
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.CreateProcedureCallArg clone RECORD variable Right={',GetResolverResultDesc(ExprResolved),'} AssignContext.RightResolved.IdentEl=',GetObjName(ExprResolved.IdentEl));
- {$ENDIF}
- // create "new RightRecordType(RightRecord)"
- Result:=CreateCloneRecord(El,ExprResolved,Result,AContext);
- exit;
- end;
- end;
- end;
- end;
- end;
- function TPasToJSConverter.CreateProcCallArgRef(El: TPasExpr;
- ResolvedEl: TPasResolverResult; TargetArg: TPasArgument;
- AContext: TConvertContext): TJSElement;
- const
- GetPathName = 'p';
- SetPathName = 's';
- ParamName = 'a';
- var
- Obj: TJSObjectLiteral;
- procedure AddVar(const aName: string; var Expr: TJSElement);
- var
- ObjLit: TJSObjectLiteralElement;
- begin
- if Expr=nil then exit;
- ObjLit:=Obj.Elements.AddElement;
- ObjLit.Name:=TJSString(aName);
- ObjLit.Expr:=Expr;
- Expr:=nil;
- end;
- var
- ParamContext: TParamContext;
- FullGetter, GetPathExpr, SetPathExpr, GetExpr, SetExpr, ParamExpr: TJSElement;
- AssignSt: TJSSimpleAssignStatement;
- ObjLit: TJSObjectLiteralElement;
- FuncSt: TJSFunctionDeclarationStatement;
- RetSt: TJSReturnStatement;
- GetDotPos, SetDotPos: Integer;
- GetPath, SetPath: String;
- BracketExpr: TJSBracketMemberExpression;
- DotExpr: TJSDotMemberExpression;
- begin
- // pass reference -> create a temporary JS object with a FullGetter and setter
- Obj:=nil;
- FullGetter:=nil;
- ParamContext:=TParamContext.Create(El,nil,AContext);
- GetPathExpr:=nil;
- SetPathExpr:=nil;
- GetExpr:=nil;
- SetExpr:=nil;
- try
- // create FullGetter and setter
- ParamContext.Access:=caByReference;
- ParamContext.Arg:=TargetArg;
- ParamContext.Expr:=El;
- ParamContext.ResolvedExpr:=ResolvedEl;
- FullGetter:=ConvertElement(El,ParamContext);
- // FullGetter is now a full JS expression to retrieve the value.
- if ParamContext.ReusingReference then
- begin
- // result is already a reference
- Result:=FullGetter;
- exit;
- end;
- // if ParamContext.Getter is set then
- // ParamContext.Getter is the last part of the FullGetter
- // FullSetter is created from FullGetter by replacing the Getter with the Setter
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.CreateProcedureCallArg VAR FullGetter=',GetObjName(FullGetter),' Getter=',GetObjName(ParamContext.Getter),' Setter=',GetObjName(ParamContext.Setter));
- {$ENDIF}
- if (ParamContext.Getter=nil)<>(ParamContext.Setter=nil) then
- begin
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.CreateProcedureCallArg FullGetter=',GetObjName(FullGetter),' Getter=',GetObjName(ParamContext.Getter),' Setter=',GetObjName(ParamContext.Setter));
- {$ENDIF}
- RaiseInconsistency(20170213222941);
- end;
- // create "{p:Result,get:function(){return this.p.Getter},set:function(v){this.p.Setter(v);}}"
- Obj:=TJSObjectLiteral(CreateElement(TJSObjectLiteral,El));
- if FullGetter.ClassType=TJSPrimaryExpressionIdent then
- begin
- // create "{get:function(){return FullGetter;},set:function(v){FullGetter=v;}}"
- if (ParamContext.Getter<>nil) and (ParamContext.Getter<>FullGetter) then
- RaiseInconsistency(20170213224339);
- GetPath:=String(TJSPrimaryExpressionIdent(FullGetter).Name);
- GetDotPos:=PosLast('.',GetPath);
- if GetDotPos>0 then
- begin
- // e.g. path1.path2.readvar
- // create
- // GetPathExpr: path1.path2
- // GetExpr: this.p.readvar
- // Will create "{p:GetPathExpr, get:function(){return GetExpr;},
- // set:function(v){GetExpr = v;}}"
- GetPathExpr:=CreateBuiltInIdentifierExpr(LeftStr(GetPath,GetDotPos-1));
- GetExpr:=CreateDotExpression(El,CreateBuiltInIdentifierExpr('this.'+GetPathName),
- CreateBuiltInIdentifierExpr(copy(GetPath,GetDotPos+1)));
- if ParamContext.Setter=nil then
- SetExpr:=CreateDotExpression(El,CreateBuiltInIdentifierExpr('this.'+GetPathName),
- CreateBuiltInIdentifierExpr(copy(GetPath,GetDotPos+1)));
- end
- else
- begin
- // local var
- GetExpr:=FullGetter;
- FullGetter:=nil;
- if ParamContext.Setter=nil then
- SetExpr:=CreateBuiltInIdentifierExpr(GetPath);
- end;
- if ParamContext.Setter<>nil then
- begin
- // custom Setter
- SetExpr:=ParamContext.Setter;
- ParamContext.Setter:=nil;
- if SetExpr.ClassType=TJSPrimaryExpressionIdent then
- begin
- SetPath:=String(TJSPrimaryExpressionIdent(SetExpr).Name);
- SetDotPos:=PosLast('.',SetPath);
- FreeAndNil(SetExpr);
- if LeftStr(GetPath,GetDotPos)=LeftStr(SetPath,SetDotPos) then
- begin
- // use GetPathExpr for setter
- SetExpr:=CreateDotExpression(El,CreateBuiltInIdentifierExpr('this.'+GetPathName),
- CreateBuiltInIdentifierExpr(copy(SetPath,GetDotPos+1)));
- end
- else
- begin
- // setter needs its own SetPathExpr
- SetPathExpr:=CreateBuiltInIdentifierExpr(LeftStr(SetPath,SetDotPos-1));
- SetExpr:=CreateDotExpression(El,CreateBuiltInIdentifierExpr('this.'+SetPathName),
- CreateBuiltInIdentifierExpr(copy(SetPath,GetDotPos+1)));
- end;
- end;
- end;
- end
- else if FullGetter.ClassType=TJSDotMemberExpression then
- begin
- if ParamContext.Setter<>nil then
- RaiseNotSupported(El,AContext,20170214231900);
- // convert this.r.i to
- // {p:this.r,
- // get:function{return this.p.i;},
- // set:function(v){this.p.i=v;}
- // }
- // GetPathExpr: this.r
- // GetExpr: this.p.i
- // SetExpr: this.p.i
- DotExpr:=TJSDotMemberExpression(FullGetter);
- GetPathExpr:=DotExpr.MExpr;
- DotExpr.MExpr:=CreateBuiltInIdentifierExpr('this.'+GetPathName);
- GetExpr:=DotExpr;
- FullGetter:=nil;
- SetExpr:=CreateDotExpression(El,
- CreateBuiltInIdentifierExpr('this.'+GetPathName),
- CreateBuiltInIdentifierExpr(String(DotExpr.Name)));
- end
- else if FullGetter.ClassType=TJSBracketMemberExpression then
- begin
- if ParamContext.Setter<>nil then
- RaiseNotSupported(El,AContext,20170214215150);
- // convert this.arr[value] to
- // {a:value,
- // p:this.arr,
- // get:function{return this.p[this.a];},
- // set:function(v){this.p[this.a]=v;}
- // }
- BracketExpr:=TJSBracketMemberExpression(FullGetter);
- ParamExpr:=BracketExpr.Name;
- // create "a:value"
- BracketExpr.Name:=CreateBuiltInIdentifierExpr('this.'+ParamName);
- AddVar(ParamName,ParamExpr);
- // create GetPathExpr "this.arr"
- GetPathExpr:=BracketExpr.MExpr;
- BracketExpr.MExpr:=CreateBuiltInIdentifierExpr('this.'+GetPathName);
- // GetExpr "this.p[this.a]"
- GetExpr:=BracketExpr;
- FullGetter:=nil;
- // SetExpr "this.p[this.a]"
- BracketExpr:=TJSBracketMemberExpression(CreateElement(TJSBracketMemberExpression,El));
- SetExpr:=BracketExpr;
- BracketExpr.MExpr:=CreateBuiltInIdentifierExpr('this.'+GetPathName);
- BracketExpr.Name:=CreateBuiltInIdentifierExpr('this.'+ParamName);
- end
- else
- begin
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.CreateProcedureCallArg FullGetter=',GetObjName(FullGetter),' Getter=',GetObjName(ParamContext.Getter),' Setter=',GetObjName(ParamContext.Setter));
- {$ENDIF}
- RaiseNotSupported(El,AContext,20170213230336);
- end;
- if (SetExpr.ClassType=TJSPrimaryExpressionIdent)
- or (SetExpr.ClassType=TJSDotMemberExpression)
- or (SetExpr.ClassType=TJSBracketMemberExpression) then
- begin
- // create SetExpr = v;
- AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El));
- AssignSt.LHS:=SetExpr;
- AssignSt.Expr:=CreateBuiltInIdentifierExpr(TempRefObjSetterArgName);
- SetExpr:=AssignSt;
- end
- else if (SetExpr.ClassType=TJSCallExpression) then
- // has already the form Func(v)
- else
- RaiseInconsistency(20170213225940);
- // add p:GetPathExpr
- AddVar(GetPathName,GetPathExpr);
- // add get:function(){ return GetExpr; }
- ObjLit:=Obj.Elements.AddElement;
- ObjLit.Name:=TempRefObjGetterName;
- FuncSt:=CreateFunction(El);
- ObjLit.Expr:=FuncSt;
- RetSt:=TJSReturnStatement(CreateElement(TJSReturnStatement,El));
- FuncSt.AFunction.Body.A:=RetSt;
- RetSt.Expr:=GetExpr;
- GetExpr:=nil;
- // add s:GetPathExpr
- AddVar(SetPathName,SetPathExpr);
- // add set:function(v){ SetExpr }
- ObjLit:=Obj.Elements.AddElement;
- ObjLit.Name:=TempRefObjSetterName;
- FuncSt:=CreateFunction(El);
- ObjLit.Expr:=FuncSt;
- FuncSt.AFunction.Params.Add(TempRefObjSetterArgName);
- FuncSt.AFunction.Body.A:=SetExpr;
- SetExpr:=nil;
- Result:=Obj;
- finally
- if Result=nil then
- begin
- GetPathExpr.Free;
- SetPathExpr.Free;
- GetExpr.Free;
- SetExpr.Free;
- Obj.Free;
- ParamContext.Setter.Free;
- FullGetter.Free;
- end;
- ParamContext.Free;
- end;
- end;
- function TPasToJSConverter.ConvertExceptOn(El: TPasImplExceptOn;
- AContext: TConvertContext): TJSElement;
- // convert "on T do ;" to "if(T.isPrototypeOf(exceptObject)){}"
- // convert "on E:T do ;" to "if(T.isPrototypeOf(exceptObject)){ var E=exceptObject; }"
- Var
- IfSt : TJSIfStatement;
- ListFirst , ListLast: TJSStatementList;
- DotExpr: TJSDotMemberExpression;
- Call: TJSCallExpression;
- V: TJSVariableStatement;
- VarDecl: TJSVarDeclaration;
- begin
- Result:=nil;
- // create "if()"
- IfSt:=TJSIfStatement(CreateElement(TJSIfStatement,El));
- try
- // create "T.isPrototypeOf"
- DotExpr:=TJSDotMemberExpression(CreateElement(TJSDotMemberExpression,El));
- DotExpr.MExpr:=CreateReferencePathExpr(El.TypeEl,AContext);
- DotExpr.Name:='isPrototypeOf';
- // create "T.isPrototypeOf(exceptObject)"
- Call:=CreateCallExpression(El);
- Call.Expr:=DotExpr;
- Call.Args.Elements.AddElement.Expr:=CreateBuiltInIdentifierExpr(FBuiltInNames[pbivnExceptObject]);
- IfSt.Cond:=Call;
- if El.VarEl<>nil then
- begin
- // add "var E=exceptObject;"
- ListFirst:=TJSStatementList(CreateElement(TJSStatementList,El.Body));
- ListLast:=ListFirst;
- IfSt.BTrue:=ListFirst;
- V:=TJSVariableStatement(CreateElement(TJSVariableStatement,El));
- ListFirst.A:=V;
- VarDecl:=TJSVarDeclaration(CreateElement(TJSVarDeclaration,El));
- V.A:=VarDecl;
- VarDecl.Name:=TransformVariableName(El,El.VariableName,AContext);
- VarDecl.Init:=CreateBuiltInIdentifierExpr(FBuiltInNames[pbivnExceptObject]);
- // add statements
- AddToStatementList(ListFirst,ListLast,ConvertElement(El.Body,AContext),El);
- end
- else if El.Body<>nil then
- // add statements
- IfSt.BTrue:=ConvertElement(El.Body,AContext);
- Result:=IfSt;
- finally
- if Result=nil then
- IfSt.Free;
- end;
- end;
- function TPasToJSConverter.ConvertStatement(El: TPasImplStatement;
- AContext: TConvertContext): TJSElement;
- begin
- Result:=Nil;
- if (El is TPasImplRaise) then
- Result:=ConvertRaiseStatement(TPasImplRaise(El),AContext)
- else if (El is TPasImplAssign) then
- Result:=ConvertAssignStatement(TPasImplAssign(El),AContext)
- else if (El is TPasImplWhileDo) then
- Result:=ConvertWhileStatement(TPasImplWhileDo(El),AContext)
- else if (El is TPasImplSimple) then
- Result:=ConvertSimpleStatement(TPasImplSimple(El),AContext)
- else if (El is TPasImplWithDo) then
- Result:=ConvertWithStatement(TPasImplWithDo(El),AContext)
- else if (El is TPasImplExceptOn) then
- Result:=ConvertExceptOn(TPasImplExceptOn(El),AContext)
- else if (El is TPasImplForLoop) then
- Result:=ConvertForStatement(TPasImplForLoop(El),AContext)
- else if (El is TPasImplAsmStatement) then
- Result:=ConvertAsmStatement(TPasImplAsmStatement(El),AContext)
- else
- RaiseNotSupported(El,AContext,20161024192759);
- {
- TPasImplCaseStatement = class(TPasImplStatement)
- }
- end;
- function TPasToJSConverter.ConvertCommands(El: TPasImplCommands;
- AContext: TConvertContext): TJSElement;
- begin
- RaiseNotSupported(El,AContext,20161024192806);
- Result:=Nil;
- // ToDo: TPasImplCommands = class(TPasImplElement)
- end;
- function TPasToJSConverter.ConvertConst(El: TPasConst; AContext: TConvertContext
- ): TJSElement;
- // Important: returns nil if const was added to higher context
- Var
- AssignSt: TJSSimpleAssignStatement;
- Obj: TJSObjectLiteral;
- ObjLit: TJSObjectLiteralElement;
- ConstContext: TFunctionContext;
- C: TJSElement;
- V: TJSVariableStatement;
- Src: TJSSourceElements;
- begin
- Result:=nil;
- if not AContext.IsSingleton then
- begin
- // local const are stored in interface/implementation
- ConstContext:=AContext.GetSingletonFunc;
- if not (ConstContext.JSElement is TJSSourceElements) then
- begin
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.CreateConstDecl ConstContext=',GetObjName(ConstContext),' JSElement=',GetObjName(ConstContext.JSElement));
- {$ENDIF}
- RaiseNotSupported(El,AContext,20170220153216);
- end;
- Src:=TJSSourceElements(ConstContext.JSElement);
- C:=ConvertVariable(El,AContext);
- V:=TJSVariableStatement(CreateElement(TJSVariableStatement,El));
- V.A:=C;
- AddToSourceElements(Src,V);
- end
- else if AContext is TObjectContext then
- begin
- // create 'A: initvalue'
- Obj:=TObjectContext(AContext).JSElement as TJSObjectLiteral;
- ObjLit:=Obj.Elements.AddElement;
- ObjLit.Name:=TJSString(TransformVariableName(El,AContext));
- ObjLit.Expr:=CreateVarInit(El,AContext);
- end
- else
- begin
- // create 'this.A=initvalue'
- AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El));
- Result:=AssignSt;
- AssignSt.LHS:=CreateDeclNameExpression(El,El.Name,AContext);
- AssignSt.Expr:=CreateVarInit(El,AContext);
- end;
- end;
- function TPasToJSConverter.ConvertLabelMark(El: TPasImplLabelMark;
- AContext: TConvertContext): TJSElement;
- begin
- RaiseNotSupported(El,AContext,20161024192857);
- Result:=Nil;
- // ToDo: TPasImplLabelMark = class(TPasImplLabelMark) then
- end;
- function TPasToJSConverter.ConvertElement(El: TPasElement;
- AContext: TConvertContext): TJSElement;
- var
- C: TClass;
- begin
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.ConvertElement El=',GetObjName(El),' Context=',GetObjName(AContext));
- {$ENDIF}
- if El=nil then
- begin
- Result:=nil;
- RaiseInconsistency(20161024190203);
- end;
- C:=El.ClassType;
- If (C=TPasPackage) then
- Result:=ConvertPackage(TPasPackage(El),AContext)
- else if (C=TPasResString) then
- Result:=ConvertResString(TPasResString(El),AContext)
- else if (C=TPasConst) then
- Result:=ConvertConst(TPasConst(El),AContext)
- else if (C=TPasProperty) then
- Result:=ConvertProperty(TPasProperty(El),AContext)
- else if (C=TPasVariable) then
- Result:=ConvertVariable(TPasVariable(El),AContext)
- else if (C=TPasExportSymbol) then
- Result:=ConvertExportSymbol(TPasExportSymbol(El),AContext)
- else if (C=TPasLabels) then
- Result:=ConvertLabels(TPasLabels(El),AContext)
- else if (C=TPasImplCommand) then
- Result:=ConvertCommand(TPasImplCommand(El),AContext)
- else if (C=TPasImplCommands) then
- Result:=ConvertCommands(TPasImplCommands(El),AContext)
- else if (C=TPasImplLabelMark) then
- Result:=ConvertLabelMark(TPasImplLabelMark(El),AContext)
- else if C.InheritsFrom(TPasExpr) then
- Result:=ConvertExpression(TPasExpr(El),AContext)
- else if C.InheritsFrom(TPasDeclarations) then
- Result:=ConvertDeclarations(TPasDeclarations(El),AContext)
- else if C.InheritsFrom(TPasProcedure) then
- Result:=ConvertProcedure(TPasProcedure(El),AContext)
- else if C.InheritsFrom(TPasImplBlock) then
- Result:=ConvertImplBlock(TPasImplBlock(El),AContext)
- else if C.InheritsFrom(TPasModule) then
- Result:=ConvertModule(TPasModule(El),AContext)
- else
- begin
- Result:=nil;
- RaiseNotSupported(El, AContext, 20161024190449);
- end;
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.ConvertElement END ',GetObjName(El));
- {$ENDIF}
- end;
- function TPasToJSConverter.ConvertRecordType(El: TPasRecordType;
- AContext: TConvertContext): TJSElement;
- (*
- type
- TMyRecord = record
- i: longint;
- s: string;
- d: double;
- r: TOtherRecord;
- end;
- this.TMyRecord=function(s) {
- if (s){
- this.i = s.i;
- this.s = s.s;
- this.d = s.d;
- this.r = new this.TOtherRecord(s.r);
- } else {
- this.i = 0;
- this.s = "";
- this.d = 0.0;
- this.r = new this.TOtherRecord();
- };
- this.$equal = function(b){
- return (this.i == b.i) && (this.s == b.s) && (this.d == b.d)
- && (this.r.$equal(b.r))
- };
- };
- *)
- const
- SrcParamName = 's';
- EqualParamName = 'b';
- procedure AddCloneStatements(IfSt: TJSIfStatement;
- FuncContext: TFunctionContext);
- var
- i: Integer;
- PasVar: TPasVariable;
- VarAssignSt: TJSSimpleAssignStatement;
- First, Last: TJSStatementList;
- VarDotExpr: TJSDotMemberExpression;
- PasVarType: TPasType;
- ResolvedPasVar: TPasResolverResult;
- begin
- // init members with s
- First:=nil;
- Last:=nil;
- for i:=0 to El.Members.Count-1 do
- begin
- PasVar:=TPasVariable(El.Members[i]);
- if not IsElementUsed(PasVar) then continue;
- // create 'this.A = s.A;'
- VarAssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,PasVar));
- AddToStatementList(First,Last,VarAssignSt,PasVar);
- if i=0 then IfSt.BTrue:=First;
- VarAssignSt.LHS:=CreateDeclNameExpression(PasVar,PasVar.Name,FuncContext);
- VarDotExpr:=TJSDotMemberExpression(CreateElement(TJSDotMemberExpression,PasVar));
- VarAssignSt.Expr:=VarDotExpr;
- VarDotExpr.MExpr:=CreateBuiltInIdentifierExpr(SrcParamName);
- VarDotExpr.Name:=TJSString(TransformVariableName(PasVar,FuncContext));
- if (AContext.Resolver<>nil) then
- begin
- PasVarType:=AContext.Resolver.ResolveAliasType(PasVar.VarType);
- if PasVarType.ClassType=TPasRecordType then
- begin
- SetResolverIdentifier(ResolvedPasVar,btContext,PasVar,PasVarType,[rrfReadable,rrfWritable]);
- VarAssignSt.Expr:=CreateCloneRecord(PasVar,ResolvedPasVar,VarDotExpr,FuncContext);
- continue;
- end
- else if PasVarType.ClassType=TPasSetType then
- begin
- VarAssignSt.Expr:=CreateReferencedSet(PasVar,VarDotExpr);
- continue;
- end
- end;
- end;
- end;
- procedure AddInitDefaultStatements(IfSt: TJSIfStatement;
- FuncContext: TFunctionContext);
- var
- i: Integer;
- PasVar: TPasVariable;
- JSVar: TJSElement;
- First, Last: TJSStatementList;
- begin
- // the "else" part:
- // when there is no s parameter, init members with default value
- First:=nil;
- Last:=nil;
- for i:=0 to El.Members.Count-1 do
- begin
- PasVar:=TPasVariable(El.Members[i]);
- if not IsElementUsed(PasVar) then continue;
- JSVar:=CreateVarDecl(PasVar,FuncContext);
- AddToStatementList(First,Last,JSVar,PasVar);
- if IfSt.BFalse=nil then
- IfSt.BFalse:=First;
- end;
- end;
- procedure Add_AndExpr_ToReturnSt(RetSt: TJSReturnStatement;
- PasVar: TPasVariable; var LastAndExpr: TJSLogicalAndExpression;
- Expr: TJSElement);
- var
- AndExpr: TJSLogicalAndExpression;
- begin
- if RetSt.Expr=nil then
- RetSt.Expr:=Expr
- else
- begin
- AndExpr:=TJSLogicalAndExpression(CreateElement(TJSLogicalAndExpression,PasVar));
- if LastAndExpr=nil then
- begin
- AndExpr.A:=RetSt.Expr;
- RetSt.Expr:=AndExpr;
- end
- else
- begin
- AndExpr.A:=LastAndExpr.B;
- LastAndExpr.B:=AndExpr;
- end;
- AndExpr.B:=Expr;
- LastAndExpr:=AndExpr;
- end;
- end;
- procedure AddEqualFunction(var BodyFirst, BodyLast: TJSStatementList;
- FuncContext: TFunctionContext);
- // add equal function:
- // this.$equal = function(b){
- // return (this.member1 == b.member1);
- // };
- var
- AssignSt: TJSSimpleAssignStatement;
- FD: TJSFuncDef;
- RetSt: TJSReturnStatement;
- i: Integer;
- PasVar: TPasVariable;
- FDS: TJSFunctionDeclarationStatement;
- EqExpr: TJSEqualityExpressionEQ;
- LastAndExpr: TJSLogicalAndExpression;
- VarType: TPasType;
- Call: TJSCallExpression;
- VarName: String;
- begin
- // add "this.$equal ="
- AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El));
- AssignSt.LHS:=CreateMemberExpression(['this',FBuiltInNames[pbifnRecordEqual]]);
- AddToStatementList(BodyFirst,BodyLast,AssignSt,El);
- // add "function(b){"
- FDS:=CreateFunction(El);
- AssignSt.Expr:=FDS;
- FD:=FDS.AFunction;
- FD.Params.Add(EqualParamName);
- FD.Body:=TJSFunctionBody(CreateElement(TJSFunctionBody,El));
- // add "return "
- RetSt:=TJSReturnStatement(CreateElement(TJSReturnStatement,El));
- FD.Body.A:=RetSt;
- LastAndExpr:=nil;
- for i:=0 to El.Members.Count-1 do
- begin
- PasVar:=TPasVariable(El.Members[i]);
- if not IsElementUsed(PasVar) then continue;
- // "this.member = b.member;"
- VarType:=PasVar.VarType;
- if FuncContext.Resolver<>nil then
- VarType:=FuncContext.Resolver.ResolveAliasType(VarType);
- VarName:=TransformVariableName(PasVar,FuncContext);
- if VarType.ClassType=TPasRecordType then
- begin
- // record
- // add "this.member.$equal(b.member)"
- Call:=CreateCallExpression(PasVar);
- Add_AndExpr_ToReturnSt(RetSt,PasVar,LastAndExpr,Call);
- Call.Expr:=CreateMemberExpression(['this',VarName,FBuiltInNames[pbifnRecordEqual]]);
- Call.Args.Elements.AddElement.Expr:=CreateMemberExpression([EqualParamName,VarName]);
- end
- else if VarType.ClassType=TPasSetType then
- begin
- // set
- // add "rtl.eqSet(this.member,b.member)"
- Call:=CreateCallExpression(PasVar);
- Add_AndExpr_ToReturnSt(RetSt,PasVar,LastAndExpr,Call);
- Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnSet_Equal]]);
- Call.Args.Elements.AddElement.Expr:=CreateMemberExpression(['this',VarName]);
- Call.Args.Elements.AddElement.Expr:=CreateMemberExpression([EqualParamName,VarName]);
- end
- else if VarType is TPasProcedureType then
- begin
- // proc type
- // add "rtl.eqCallback(this.member,b.member)"
- Call:=CreateCallExpression(PasVar);
- Add_AndExpr_ToReturnSt(RetSt,PasVar,LastAndExpr,Call);
- Call.Expr:=CreateMemberExpression([FBuiltInNames[pbivnRTL],FBuiltInNames[pbifnProcType_Equal]]);
- Call.Args.Elements.AddElement.Expr:=CreateMemberExpression(['this',VarName]);
- Call.Args.Elements.AddElement.Expr:=CreateMemberExpression([EqualParamName,VarName]);
- end
- else
- begin
- // default: use simple equal "=="
- EqExpr:=TJSEqualityExpressionEQ(CreateElement(TJSEqualityExpressionEQ,PasVar));
- Add_AndExpr_ToReturnSt(RetSt,PasVar,LastAndExpr,EqExpr);
- EqExpr.A:=CreateMemberExpression(['this',VarName]);
- EqExpr.B:=CreateMemberExpression([EqualParamName,VarName]);
- end;
- end;
- end;
- var
- AssignSt: TJSSimpleAssignStatement;
- FDS: TJSFunctionDeclarationStatement;
- FD: TJSFuncDef;
- BodyFirst, BodyLast: TJSStatementList;
- FuncContext: TFunctionContext;
- Obj: TJSObjectLiteral;
- ObjLit: TJSObjectLiteralElement;
- IfSt: TJSIfStatement;
- begin
- Result:=nil;
- FuncContext:=nil;
- AssignSt:=nil;
- try
- FDS:=CreateFunction(El);
- if AContext is TObjectContext then
- begin
- // add 'TypeName: function(){}'
- Obj:=TObjectContext(AContext).JSElement as TJSObjectLiteral;
- ObjLit:=Obj.Elements.AddElement;
- ObjLit.Name:=TJSString(TransformVariableName(El,AContext));
- ObjLit.Expr:=FDS;
- end
- else
- begin
- // add 'this.TypeName = function(){}'
- AssignSt:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El));
- AssignSt.LHS:=CreateDeclNameExpression(El,El.Name,AContext);
- AssignSt.Expr:=FDS;
- end;
- FD:=FDS.AFunction;
- // add param s
- FD.Params.Add(SrcParamName);
- // create function body
- FuncContext:=TFunctionContext.Create(El,FD.Body,AContext);
- FuncContext.This:=El;
- FuncContext.IsSingleton:=true;
- if El.Members.Count>0 then
- begin
- BodyFirst:=nil;
- BodyLast:=nil;
- // add if(s)
- IfSt:=TJSIfStatement(CreateElement(TJSIfStatement,El));
- AddToStatementList(BodyFirst,BodyLast,IfSt,El);
- FD.Body.A:=BodyFirst;
- IfSt.Cond:=CreateBuiltInIdentifierExpr(SrcParamName);
- // add clone statements
- AddCloneStatements(IfSt,FuncContext);
- // add init default statements
- AddInitDefaultStatements(IfSt,FuncContext);
- // add equal function
- AddEqualFunction(BodyFirst,BodyLast,FuncContext);
- end;
- Result:=AssignSt;
- finally
- FuncContext.Free;
- if Result=nil then AssignSt.Free;
- end;
- end;
- procedure TPasToJSConverter.DoError(Id: int64; const Msg: String);
- var
- E: EPas2JS;
- begin
- E:=EPas2JS.Create(Msg);
- E.Id:=Id;
- E.MsgType:=mtError;
- Raise E;
- end;
- procedure TPasToJSConverter.DoError(Id: int64; const Msg: String;
- const Args: array of const);
- var
- E: EPas2JS;
- begin
- E:=EPas2JS.CreateFmt(Msg,Args);
- E.Id:=Id;
- E.MsgType:=mtError;
- Raise E;
- end;
- procedure TPasToJSConverter.DoError(Id: int64; MsgNumber: integer;
- const MsgPattern: string; const Args: array of const; El: TPasElement);
- var
- E: EPas2JS;
- begin
- E:=EPas2JS.CreateFmt(MsgPattern,Args);
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.DoError ',id,' ',El.FullName,':',El.ClassName,' Msg="',E.Message,'"');
- {$ENDIF}
- E.PasElement:=El;
- E.MsgNumber:=MsgNumber;
- E.Id:=Id;
- E.MsgType:=mtError;
- CreateMsgArgs(E.Args,Args);
- raise E;
- end;
- procedure TPasToJSConverter.RaiseNotSupported(El: TPasElement;
- AContext: TConvertContext; Id: int64; const Msg: string);
- var
- E: EPas2JS;
- begin
- {$IFDEF VerbosePas2JS}
- writeln('TPasToJSConverter.RaiseNotSupported ',id,' ',El.FullName,':',El.ClassName,' Msg="',Msg,'"');
- {$ENDIF}
- if AContext=nil then ;
- E:=EPas2JS.CreateFmt(sPasElementNotSupported,[GetObjName(El)]);
- if Msg<>'' then
- E.Message:=E.Message+': '+Msg;
- E.PasElement:=El;
- E.MsgNumber:=nPasElementNotSupported;
- SetLength(E.Args,1);
- E.Args[0]:=El.ClassName;
- E.Id:=Id;
- E.MsgType:=mtError;
- raise E;
- end;
- procedure TPasToJSConverter.RaiseIdentifierNotFound(Identifier: string;
- El: TPasElement; Id: int64);
- var
- E: EPas2JS;
- begin
- E:=EPas2JS.CreateFmt(sIdentifierNotFound,[Identifier]);
- E.PasElement:=El;
- E.MsgNumber:=nIdentifierNotFound;
- SetLength(E.Args,1);
- E.Args[0]:=Identifier;
- E.Id:=Id;
- E.MsgType:=mtError;
- raise E;
- end;
- function TPasToJSConverter.TransformVariableName(El: TPasElement;
- const AName: String; AContext: TConvertContext): String;
- var
- i: Integer;
- c: Char;
- begin
- if AContext=nil then ;
- if Pos('.',AName)>0 then
- RaiseInconsistency(20170203164711);
- if UseLowerCase then
- Result:=LowerCase(AName)
- else
- Result:=AName;
- if not IsPreservedWord(Result) then
- exit;
- for i:=1 to length(Result) do
- begin
- c:=Result[i];
- case c of
- 'a'..'z','A'..'Z':
- begin
- Result[i]:=chr(ord(c) xor 32);
- if not IsPreservedWord(Result) then
- exit;
- end;
- end;
- end;
- RaiseNotSupported(El,AContext,20170203131832);
- end;
- function TPasToJSConverter.TransformVariableName(El: TPasElement;
- AContext: TConvertContext): String;
- begin
- if (El is TPasProcedure) and (TPasProcedure(El).LibrarySymbolName<>nil) then
- Result:=ComputeConstString(TPasProcedure(El).LibrarySymbolName,AContext,true)
- else if (El is TPasVariable) and (TPasVariable(El).ExportName<>nil) then
- Result:=ComputeConstString(TPasVariable(El).ExportName,AContext,true)
- else
- Result:=TransformVariableName(El,El.Name,AContext);
- end;
- function TPasToJSConverter.TransformModuleName(El: TPasModule;
- AContext: TConvertContext): String;
- begin
- if El is TPasProgram then
- Result:='program'
- else
- Result:=TransformVariableName(El,AContext);
- end;
- function TPasToJSConverter.IsPreservedWord(const aName: string): boolean;
- var
- l, r, m, cmp: Integer;
- begin
- Result:=true;
- if aName=FBuiltInNames[pbivnModules] then exit;
- if aName=FBuiltInNames[pbivnRTL] then exit;
- // search default list
- l:=low(JSReservedWords);
- r:=high(JSReservedWords);
- while l<=r do
- begin
- m:=(l+r) div 2;
- cmp:=CompareStr(aName,JSReservedWords[m]);
- //writeln('TPasToJSConverter.IsPreservedWord Name="',aName,'" l=',l,' r=',r,' m=',m,' JSReservedWords[m]=',JSReservedWords[m],' cmp=',cmp);
- if cmp>0 then
- l:=m+1
- else if cmp<0 then
- r:=m-1
- else
- exit;
- end;
- // search user list
- l:=0;
- r:=length(FPreservedWords)-1;
- while l<=r do
- begin
- m:=(l+r) div 2;
- cmp:=CompareStr(aName,FPreservedWords[m]);
- //writeln('TPasToJSConverter.IsPreservedWord Name="',aName,'" l=',l,' r=',r,' m=',m,' FReservedWords[m]=',FReservedWords[m],' cmp=',cmp);
- if cmp>0 then
- l:=m+1
- else if cmp<0 then
- r:=m-1
- else
- exit;
- end;
- Result:=false;
- end;
- function TPasToJSConverter.ConvertPasElement(El: TPasElement;
- Resolver: TPas2JSResolver): TJSElement;
- var
- aContext: TRootContext;
- begin
- aContext:=TRootContext.Create(El,nil,nil);
- try
- aContext.Resolver:=Resolver;
- if (El.ClassType=TPasImplBeginBlock) then
- Result:=ConvertBeginEndStatement(TPasImplBeginBlock(El),AContext,false)
- else
- Result:=ConvertElement(El,aContext);
- finally
- FreeAndNil(aContext);
- end;
- end;
- var
- i: integer;
- initialization
- for i:=low(JSReservedWords) to High(JSReservedWords)-1 do
- if CompareStr(JSReservedWords[i],JSReservedWords[i+1])>=0 then
- raise Exception.Create('20170203135442 '+JSReservedWords[i]+' >= '+JSReservedWords[i+1]);
- end.
|