pas2jscompiler.pp 175 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702
  1. { Author: Mattias Gaertner 2021 [email protected]
  2. Abstract:
  3. TPas2jsCompiler is the wheel boss of the pas2js compiler.
  4. It can be used in a command line program or compiled into an application.
  5. TPas2jsCompiler does not have understanding of the file system.
  6. DO NOT ADD filesystem related calls to this unit.
  7. The file system is abstracted out in TPas2JSFS (unit pas2jsfs)
  8. Add high-level calls to TPas2JSFS instead or create virtual methods that can be overridden.
  9. FileSystem specific things should go in Pas2JSFileCache and Pas2JSFSCompiler.
  10. Compiler-ToDos:
  11. Warn if -Ju and -Fu intersect
  12. -Fa<x>[,y] (for a program) load units <x> and [y] before uses is parsed
  13. Add Windows macros, see InitMacros.
  14. }
  15. unit Pas2jsCompiler;
  16. {$mode objfpc}{$H+}
  17. {$I pas2js_defines.inc}
  18. {$IF defined(VerboseUnitQueue) or defined(VerbosePCUFiler)}
  19. {$DEFINE ReallyVerbose}
  20. {$ENDIF}
  21. interface
  22. uses
  23. {$IFDEF Pas2js}
  24. JS,
  25. {$ELSE}
  26. RtlConsts,
  27. {$ENDIF}
  28. // !! No filesystem units here.
  29. Classes, SysUtils, contnrs,
  30. jsbase, jstree, jswriter, JSSrcMap, fpjson,
  31. PScanner, PParser, PasTree, PasResolver, PasResolveEval, PasUseAnalyzer,
  32. Pas2JSUtils,
  33. pas2jsresstrfile, pas2jsresources, pas2jshtmlresources, pas2jsjsresources,
  34. FPPas2Js, FPPJsSrcMap, Pas2jsLogger, Pas2jsFS, Pas2jsPParser, Pas2jsUseAnalyzer;
  35. const
  36. VersionMajor = 2;
  37. VersionMinor = 3;
  38. VersionRelease = 1;
  39. VersionExtra = '';
  40. DefaultConfigFile = 'pas2js.cfg';
  41. //------------------------------------------------------------------------------
  42. // Messages
  43. const
  44. nOptionIsEnabled = 101; sOptionIsEnabled = 'Option "%s" is %s';
  45. nSyntaxModeIs = 102; sSyntaxModeIs = 'Syntax mode is %s';
  46. nModeswitchXisY = 103; sModeswitchXisY = 'Modeswitch %s is %s';
  47. // 104 in unit Pas2JSFS
  48. // 105 in unit Pas2JSFS
  49. nNameValue = 106; sNameValue = '%s: %s';
  50. nReadingOptionsFromFile = 107; sReadingOptionsFromFile = 'Reading options from file %s';
  51. nEndOfReadingConfigFile = 108; sEndOfReadingConfigFile = 'End of reading config file %s';
  52. nInterpretingFileOption = 109; sInterpretingFileOption = 'interpreting file option %s';
  53. nSourceFileNotFound = 110; sSourceFileNotFound = 'source file not found %s';
  54. nFileIsFolder = 111; sFileIsFolder = 'expected file, but found directory %s';
  55. nConfigFileSearch = 112; sConfigFileSearch = 'Configfile search: %s';
  56. nHandlingOption = 113; sHandlingOption = 'handling option %s';
  57. nQuickHandlingOption = 114; sQuickHandlingOption = 'quick handling option %s';
  58. nOutputDirectoryNotFound = 115; sOutputDirectoryNotFound = 'output directory not found: %s';
  59. nUnableToWriteFile = 116; sUnableToWriteFile = 'Unable to write file %s';
  60. nWritingFile = 117; sWritingFile = 'Writing file %s ...';
  61. nCompilationAborted = 118; sCompilationAborted = 'Compilation aborted';
  62. nCfgDirective = 119; sCfgDirective = 'cfg directive %s: %s';
  63. nUnitCycle = 120; sUnitCycle = 'Unit cycle found %s';
  64. nOptionForbidsCompile = 121; sOptionForbidsCompile = 'Option -Ju forbids to compile unit "%s"';
  65. nUnitNeedsCompileDueToUsedUnit = 122; sUnitsNeedCompileDueToUsedUnit = 'Unit "%s" needs compile due to used unit "%s"';
  66. nUnitNeedsCompileDueToOption = 123; sUnitsNeedCompileDueToOption = 'Unit "%s" needs compile due to option "%s"';
  67. nUnitNeedsCompileJSMissing = 124; sUnitsNeedCompileJSMissing = 'Unit "%s" needs compile, js file missing "%s"';
  68. nUnitNeedsCompilePasHasChanged = 125; sUnitsNeedCompilePasHasChanged = 'Unit "%s" needs compile, Pascal file has changed, js is %s';
  69. nParsingFile = 126; sParsingFile = 'Parsing %s ...';
  70. nCompilingFile = 127; sCompilingFile = 'Compiling %s ...';
  71. nExpectedButFound = 128; sExpectedButFound = 'Illegal unit name: Expected "%s", but found "%s"';
  72. nLinesInFilesCompiled = 129; sLinesInFilesCompiled = '%s lines in %s files compiled, %s sec%s';
  73. nTargetPlatformIs = 130; sTargetPlatformIs = 'Target platform is %s';
  74. nTargetProcessorIs = 131; sTargetProcessorIs = 'Target processor is %s';
  75. nMessageEncodingIs = 132; sMessageEncodingIs = 'Message encoding is %s';
  76. nUnableToTranslatePathToDir = 133; sUnableToTranslatePathToDir = 'Unable to translate path %s to directory %s';
  77. nSrcMapSourceRootIs = 134; sSrcMapSourceRootIs = 'source map "sourceRoot" is %s';
  78. nSrcMapBaseDirIs = 135; sSrcMapBaseDirIs = 'source map "local base directory" is %s';
  79. nUnitFileNotFound = 136; sUnitFileNotFound = 'unit file not found %s';
  80. nClassInterfaceStyleIs = 137; sClassInterfaceStyleIs = 'Class interface style is %s';
  81. nHandlingEnvOpts = 138; sHandlingEnvOpts = 'handling environment options %s';
  82. nPostProcessorInfoX = 139; sPostProcessorInfoX = 'Post processor: %s';
  83. nPostProcessorRunX = 140; sPostProcessorRunX = 'Run post processor: %s';
  84. nPostProcessorFailX = 141; sPostProcessorFailX = 'Post processor failed: %s';
  85. nPostProcessorWarnX = 142; sPostProcessorWarnX = 'Post processor: %s';
  86. nPostProcessorFinished = 143; sPostProcessorFinished = 'Post processor finished';
  87. nRTLIdentifierChanged = 144; sRTLIdentifierChanged = 'RTL identifier %s changed from %s to %s';
  88. nSkipNoConstResourcestring = 145; sSkipNoConstResourcestring = 'Resource string %s is not a constant, not adding to resourcestrings file.';
  89. nUnknownOptimizationOption = 146; sUnknownOptimizationOption = 'unknown -Oo option %s';
  90. // Note: error numbers 201+ are used by Pas2jsFileCache
  91. //------------------------------------------------------------------------------
  92. // Options
  93. type
  94. TP2jsCompilerOption = (
  95. coSkipDefaultConfigs,
  96. coBuildAll,
  97. // verbosity
  98. coShowLogo,
  99. coShowErrors,
  100. coShowWarnings,
  101. coShowNotes,
  102. coShowHints,
  103. coShowInfos,
  104. coShowLineNumbers,
  105. coShowTriedUsedFiles,
  106. coShowConditionals,
  107. coShowUsedTools,
  108. coShowDebug,
  109. coShowMessageNumbers, // not in "show all"
  110. // checks
  111. coOverflowChecks,
  112. coRangeChecks,
  113. coObjectChecks,
  114. coAssertions,
  115. // features
  116. coAllowCAssignments,
  117. coAllowMacros,
  118. coWriteableConst,
  119. // output
  120. coLowerCase,
  121. coUseStrict,
  122. coWriteDebugLog,
  123. coWriteMsgToStdErr,
  124. coPrecompile, // create precompile file
  125. // optimizations
  126. coEnumValuesAsNumbers, // -O1
  127. coKeepNotUsedPrivates, // -O-
  128. coKeepNotUsedDeclarationsWPO, // -O-
  129. coShortRefGlobals, // -O2
  130. coObfuscateLocalIdentifiers, // -O2
  131. // source map
  132. coSourceMapCreate,
  133. coSourceMapInclude,
  134. coSourceMapFilenamesAbsolute,
  135. coSourceMapXSSIHeader
  136. );
  137. TP2jsCompilerOptions = set of TP2jsCompilerOption;
  138. TP2jsOptimization = coEnumValuesAsNumbers..coKeepNotUsedDeclarationsWPO;
  139. TP2jsRTLVersionCheck = (
  140. rvcNone,
  141. rvcMain,
  142. rvcSystem,
  143. rvcUnit
  144. );
  145. TP2JSResourceStringFile = (rsfNone,rsfUnit,rsfProgram);
  146. TResourceMode = (Skip,rmNone,rmHTML,rmJS);
  147. const
  148. DefaultP2jsCompilerOptions = [coShowErrors,coWriteableConst,coUseStrict,coSourceMapXSSIHeader];
  149. DefaultP2JSResourceStringFile = rsfProgram;
  150. DefaultP2jsRTLVersionCheck = rvcNone;
  151. DefaultResourceMode = rmHTML;
  152. coShowAll = [coShowErrors..coShowDebug];
  153. coAllOptimizations = [coEnumValuesAsNumbers..coObfuscateLocalIdentifiers];
  154. coO0 = [coKeepNotUsedPrivates,coKeepNotUsedDeclarationsWPO];
  155. coO1 = [coEnumValuesAsNumbers];
  156. coO2 = coO1+[coShortRefGlobals
  157. {$IFDEF EnableObfuscateIdentifiers},coObfuscateLocalIdentifiers{$ENDIF}];
  158. p2jscoCaption: array[TP2jsCompilerOption] of string = (
  159. // only used by experts or programs parsing the pas2js output, no need for resourcestrings
  160. 'Skip default configs',
  161. 'Build all',
  162. 'Show logo',
  163. 'Show errors',
  164. 'Show warnings',
  165. 'Show notes',
  166. 'Show hints',
  167. 'Show infos',
  168. 'Show line numbers',
  169. 'Show tried/used files',
  170. 'Show conditionals',
  171. 'Show used tools',
  172. 'Show debug',
  173. 'Show message numbers',
  174. 'Overflow checking',
  175. 'Range checking',
  176. 'Method call checking',
  177. 'Assertions',
  178. 'Allow C assignments',
  179. 'Allow macros',
  180. 'Allows typed constants to be writeable',
  181. 'Lowercase identifiers',
  182. 'Use strict',
  183. 'Write pas2jsdebug.log',
  184. 'Write messages to StdErr',
  185. 'Create precompiled units',
  186. 'Enum values as numbers',
  187. 'Keep not used private declarations',
  188. 'Keep not used declarations (WPO)',
  189. 'Create short local variables for globals',
  190. 'Obfuscate local identifiers',
  191. 'Create source map',
  192. 'Include Pascal sources in source map',
  193. 'Do not shorten filenames in source map',
  194. 'Prepend XSSI protection )]} to source map'
  195. );
  196. //------------------------------------------------------------------------------
  197. // $mode and $modeswitches
  198. type
  199. TP2jsMode = (
  200. p2jmObjFPC,
  201. p2jmDelphi
  202. );
  203. TP2jsModes = set of TP2jsMode;
  204. const
  205. p2jscModeNames: array[TP2jsMode] of string = (
  206. 'ObjFPC',
  207. 'Delphi'
  208. );
  209. p2jsMode_SwitchSets: array[TP2jsMode] of TModeSwitches = (
  210. OBJFPCModeSwitches*msAllPas2jsModeSwitches+msAllPas2jsModeSwitchesReadOnly,
  211. DelphiModeSwitches*msAllPas2jsModeSwitches+msAllPas2jsModeSwitchesReadOnly
  212. );
  213. //------------------------------------------------------------------------------
  214. // param macros
  215. type
  216. EPas2jsMacro = class(Exception);
  217. TOnSubstituteMacro = function(Sender: TObject; var Params: string; Lvl: integer): boolean of object;
  218. { TPas2jsMacro }
  219. TPas2jsMacro = class
  220. public
  221. Name: string;
  222. Description: string;
  223. Value: string;
  224. CanHaveParams: boolean;
  225. OnSubstitute: TOnSubstituteMacro;
  226. end;
  227. { TPas2jsMacroEngine }
  228. TPas2jsMacroEngine = class
  229. private
  230. fMacros: TObjectList; // list of TPas2jsMacro
  231. FMaxLevel: integer;
  232. function GetMacros(Index: integer): TPas2jsMacro;
  233. public
  234. constructor Create;
  235. destructor Destroy; override;
  236. function Count: integer;
  237. function AddValue(const aName, aDescription, aValue: string): TPas2jsMacro;
  238. function AddFunction(const aName, aDescription: string;
  239. const OnSubstitute: TOnSubstituteMacro; CanHaveParams: boolean): TPas2jsMacro;
  240. function IndexOf(const aName: string): integer;
  241. procedure Delete(Index: integer);
  242. function FindMacro(const aName: string): TPas2jsMacro;
  243. procedure Substitute(var s: string; Sender: TObject = nil; Lvl: integer = 0);
  244. property Macros[Index: integer]: TPas2jsMacro read GetMacros; default;
  245. property MaxLevel: integer read FMaxLevel write FMaxLevel;
  246. end;
  247. //------------------------------------------------------------------------------
  248. // Module file
  249. type
  250. ECompilerTerminate = class(Exception);
  251. TPas2jsCompiler = class;
  252. TPas2JSCompilerFile = Class;
  253. TUsedBySection = (
  254. ubMainSection,
  255. ubImplSection
  256. );
  257. TPas2jsReaderState = (
  258. prsNone,
  259. prsReading,
  260. prsWaitingForUsedUnits,
  261. prsCanContinue,
  262. prsFinished,
  263. prsError
  264. );
  265. { TPCUSupport }
  266. TPCUSupport = Class(TObject)
  267. private
  268. FFile: TPas2JSCompilerFile;
  269. Protected
  270. procedure RaiseInternalError(id: TMaxPrecInt; Msg: string);
  271. Procedure SetPasModule(aModule: TPasModule);
  272. Procedure SetReaderState(aReaderState: TPas2JSReaderState);
  273. Procedure SetPCUFileName(Const FN: String);
  274. public
  275. constructor Create(aCompilerFile: TPas2JSCompilerFile);
  276. function HandleException(E: Exception): Boolean; virtual; abstract;
  277. procedure CreatePCUReader; virtual; abstract;
  278. function HasReader: Boolean; virtual; abstract;
  279. function ReadContinue: Boolean; virtual; abstract;
  280. function ReadCanContinue: Boolean; virtual; abstract;
  281. function FindPCU(const UseUnitName: string): string; virtual; abstract;
  282. procedure SetInitialCompileFlags; virtual; abstract;
  283. procedure WritePCU; virtual; abstract;
  284. procedure ReadUnit; virtual; abstract;
  285. property MyFile: TPas2JSCompilerFile Read FFile;
  286. end;
  287. { TFindUnitInfo }
  288. TFindUnitInfo = Record
  289. FileName: String;
  290. UnitName: String;
  291. isPCU: Boolean;
  292. isForeign: Boolean;
  293. end;
  294. { TLoadUnitInfo }
  295. TLoadUnitInfo = Record
  296. UseFilename, // pas or pcu filename, see IsPCU
  297. UseUnitname,
  298. InFilename: String; // can be ''
  299. NameExpr, InFileExpr: TPasExpr; // can be nil
  300. UseIsForeign: boolean;
  301. IsPCU: Boolean;
  302. end;
  303. { TPas2JSCompilerSupport }
  304. TPas2JSCompilerSupport = Class
  305. private
  306. FCompiler: TPas2JSCompiler;
  307. Public
  308. Constructor Create(aCompiler: TPas2JSCompiler); virtual;
  309. Property Compiler: TPas2JSCompiler read FCompiler;
  310. end;
  311. { TPas2jsCompilerFile }
  312. TPas2jsCompilerFile = class(TPas2JSCompilerSupport)
  313. private
  314. FConverter: TPasToJSConverter;
  315. FFileResolver: TPas2jsFSResolver;
  316. FIsForeign: boolean;
  317. FIsMainFile: boolean;
  318. FJSFilename: string;
  319. FJSModule: TJSElement;
  320. FLog: TPas2jsLogger;
  321. FNeedBuild: Boolean;
  322. FParser: TPas2jsPasParser;
  323. FPasFileName: String;
  324. FPasModule: TPasModule;
  325. FPasResolver: TPas2jsCompilerResolver;
  326. FPasUnitName: string;
  327. FPCUFilename: string;
  328. FPCUSupport: TPCUSupport;
  329. FReaderState: TPas2jsReaderState;
  330. FResourceHandler: TPas2jsResourceHandler;
  331. FScanner: TPas2jsPasScanner;
  332. FShowDebug: boolean;
  333. FUnitFilename: string;
  334. FUseAnalyzer: TPas2JSAnalyzer;
  335. FUsedBy: array[TUsedBySection] of TFPList; // list of TPas2jsCompilerFile
  336. function GetUsedBy(Section: TUsedBySection; Index: integer): TPas2jsCompilerFile;
  337. function GetUsedByCount(Section: TUsedBySection): integer;
  338. procedure HandleResources(Sender: TObject; const aFileName: String; aOptions: TStrings);
  339. function OnConverterIsElementUsed(Sender: TObject; El: TPasElement): boolean;
  340. function OnConverterIsTypeInfoUsed(Sender: TObject; El: TPasElement): boolean;
  341. procedure OnPasResolverLog(Sender: TObject; const Msg: String);
  342. procedure OnParserLog(Sender: TObject; const Msg: String);
  343. procedure OnScannerLog(Sender: TObject; const Msg: String);
  344. procedure OnUseAnalyzerMessage(Sender: TObject; Msg: TPAMessage);
  345. procedure HandleEParserError(E: EParserError);
  346. procedure HandleEPasResolve(E: EPasResolve);
  347. procedure HandleEPas2JS(E: EPas2JS);
  348. procedure HandleUnknownException(E: Exception);
  349. procedure HandleException(E: Exception);
  350. {$IFDEF Pas2js}
  351. procedure HandleJSException(Msg: string; E: jsvalue);
  352. {$ENDIF}
  353. procedure DoLogMsgAtEl(MsgType: TMessageType; const Msg: string;
  354. MsgNumber: integer; El: TPasElement);
  355. procedure RaiseInternalError(id: TMaxPrecInt; Msg: string);
  356. procedure ReaderFinished;
  357. public
  358. constructor Create(aCompiler: TPas2jsCompiler;
  359. const aPasFilename, aPCUFilename: string); reintroduce;
  360. destructor Destroy; override;
  361. Function CreatePCUSupport: TPCUSupport; virtual;
  362. function GetInitialModeSwitches: TModeSwitches;
  363. function IsUnitReadFromPCU: Boolean; // unit was read from pcu
  364. function GetInitialBoolSwitches: TBoolSwitches;
  365. function GetInitialConverterOptions: TPasToJsConverterOptions;
  366. procedure CreateScannerAndParser(aFileResolver: TPas2jsFSResolver);
  367. procedure CreateConverter;
  368. function OnResolverFindModule(const UseUnitName, InFilename: String; NameExpr,
  369. InFileExpr: TPasExpr): TPasModule;
  370. procedure OnResolverCheckSrcName(const Element: TPasElement);
  371. procedure OpenFile(aFilename: string);// beware: this changes FileResolver.BaseDirectory
  372. procedure ReadUnit;
  373. function ReadContinue: boolean; // true=finished
  374. function ReaderState: TPas2jsReaderState;
  375. procedure CreateJS;
  376. procedure EmitModuleHints;
  377. function GetPasFirstSection: TPasSection;
  378. function GetPasImplSection: TPasSection;
  379. function GetPasMainUsesClause: TPasUsesClause;
  380. function GetPasImplUsesClause: TPasUsesClause;
  381. function GetCurPasModule: TPasModule;
  382. function GetModuleName: string;
  383. class function GetFile(aModule: TPasModule): TPas2jsCompilerFile;
  384. public
  385. Property ResourceHandler : TPas2jsResourceHandler Read FResourceHandler Write FResourceHandler;
  386. property PasFileName: String Read FPasFileName;
  387. property PasUnitName: string read FPasUnitName write FPasUnitName;// unit name in source, initialized from UnitFilename
  388. property Converter: TPasToJSConverter read FConverter;
  389. property FileResolver: TPas2jsFSResolver read FFileResolver;
  390. property IsForeign: boolean read FIsForeign write FIsForeign;// true = do not build
  391. property IsMainFile: boolean read FIsMainFile write FIsMainFile;
  392. property JSFilename: string read FJSFilename write FJSFilename;
  393. property JSModule: TJSElement read FJSModule;
  394. property Log: TPas2jsLogger read FLog;
  395. property NeedBuild: Boolean read FNeedBuild write FNeedBuild;
  396. property Parser: TPas2jsPasParser read FParser;
  397. property PascalResolver: TPas2jsCompilerResolver read FPasResolver;
  398. property PasModule: TPasModule read FPasModule;
  399. property PCUFilename: string read FPCUFilename;
  400. property PCUSupport: TPCUSupport Read FPCUSupport;
  401. property Scanner: TPas2jsPasScanner read FScanner;
  402. property ShowDebug: boolean read FShowDebug write FShowDebug;
  403. property UnitFilename: string read FUnitFilename;
  404. property UseAnalyzer: TPas2JSAnalyzer read FUseAnalyzer; // unit analysis
  405. property UsedByCount[Section: TUsedBySection]: integer read GetUsedByCount;
  406. property UsedBy[Section: TUsedBySection; Index: integer]: TPas2jsCompilerFile read GetUsedBy;
  407. end;
  408. { TPas2JSCompilerSupport }
  409. TPas2JSPostProcessorSupport = Class(TPas2JSCompilerSupport)
  410. Public
  411. Procedure WriteUsedTools; virtual; abstract;
  412. Procedure Clear; virtual; abstract;
  413. Procedure AddPostProcessor(Const Cmd: String); virtual; abstract;
  414. Procedure CallPostProcessors(Const JSFileName: String; aWriter: TPas2JSMapper); virtual; abstract;
  415. end;
  416. { TPas2JSConfigSupport }
  417. TPas2JSConfigSupport = Class(TPas2JSCompilerSupport)
  418. private
  419. FConditionEval: TCondDirectiveEvaluator;
  420. FCurrentCfgFilename: string;
  421. FCurrentCfgLineNumber: integer;
  422. Protected
  423. procedure CfgSyntaxError(const Msg: string);
  424. function ConditionEvalVariable(Sender: TCondDirectiveEvaluator; aName: String; out Value: string): boolean;
  425. procedure ConditionEvalLog(Sender: TCondDirectiveEvaluator; Args: array of const);
  426. property ConditionEvaluator: TCondDirectiveEvaluator read FConditionEval;
  427. property CurrentCfgFilename: string read FCurrentCfgFilename;
  428. property CurrentCfgLineNumber: integer read FCurrentCfgLineNumber;
  429. Protected
  430. // These must be overridden in descendents
  431. function FindDefaultConfig: String; virtual; abstract;
  432. function GetReader(aFileName: string): TSourceLineReader; virtual; abstract;
  433. Public
  434. constructor Create(aCompiler: TPas2jsCompiler); override;
  435. destructor Destroy; override;
  436. procedure LoadDefaultConfig;
  437. procedure LoadConfig(Const aFileName: String);virtual;
  438. property Compiler: TPas2jsCompiler Read FCompiler;
  439. end;
  440. { TPas2jsCompiler }
  441. TPas2jsCompiler = class
  442. private
  443. FAllJSIntoMainJS: Boolean;
  444. FCompilerExe: string;
  445. FConfigSupport: TPas2JSConfigSupport;
  446. FConverterGlobals: TPasToJSConverterGlobals;
  447. FDefines: TStrings; // Objects can be TMacroDef
  448. FFiles: TPasAnalyzerKeySet; // set of TPas2jsCompilerFile, key is UnitFilename
  449. FFS: TPas2jsFS;
  450. FHasShownEncoding: boolean;
  451. FHasShownLogo: boolean;
  452. FInsertFilenames: TStringList;
  453. FAppendFilenames: TStringList;
  454. FInterfaceType: TPasClassInterfaceType;
  455. FLog: TPas2jsLogger;
  456. FMainFile: TPas2jsCompilerFile;
  457. FMainJSFile: String;
  458. FMainJSFileIsResolved: Boolean;
  459. FMainJSFileResolved: String;
  460. FMainSrcFile: String;
  461. FModeSwitches: TModeSwitches;
  462. FNamespaces: TStringList;
  463. FNamespacesFromCmdLine: integer;
  464. FOptions: TP2jsCompilerOptions;
  465. FOwnsFS: boolean;
  466. FParamMacros: TPas2jsMacroEngine;
  467. FPostProcessorSupport: TPas2JSPostProcessorSupport;
  468. FPrecompileGUID: TGUID;
  469. FReadingModules: TFPList; // list of TPas2jsCompilerFile ordered by uses sections
  470. FResolverHub: TPas2JSResolverHub;
  471. FRTLVersionCheck: TP2jsRTLVersionCheck;
  472. FSrcMapBaseDir: string;
  473. FSrcMapSourceRoot: string;
  474. FUnits: TPasAnalyzerKeySet; // set of TPas2jsCompilerFile, key is PasUnitName
  475. FWPOAnalyzer: TPas2JSAnalyzer;
  476. FResourceMode : TResourceMode;
  477. FResources : TPas2JSResourceHandler;
  478. FResourceStrings : TResourceStringsFile;
  479. FResourceStringFile : TP2JSResourceStringFile;
  480. FImports : TJSSourceElements;
  481. procedure AddInsertJSFilename(const aFilename: string);
  482. procedure AddAppendJSFilename(const aFilename: string);
  483. Procedure AddNamespaces(const Paths: string; FromCmdLine: boolean);
  484. procedure AddUnitResourceStrings(aFile: TPas2jsCompilerFile);
  485. function CreateFileWriter(aFile: TPas2jsCompilerFile; const aFilename: string): TPas2JSMapper;
  486. procedure EmitJavaScript(aFile: TPas2jsCompilerFile; aFileWriter: TPas2JSMapper);
  487. function GetDefaultNamespace: String;
  488. function GetFileCount: integer;
  489. function GetResolvedMainJSFile: string;
  490. function GetShowDebug: boolean;
  491. function GetShowFullPaths: boolean;
  492. function GetShowLogo: Boolean; inline;
  493. function GetShowTriedUsedFiles: boolean;
  494. function GetShowUsedTools: boolean; inline;
  495. function GetSkipDefaultConfig: Boolean; inline;
  496. function GetSrcMapEnable: boolean;
  497. function GetSrcMapInclude: boolean;
  498. function GetSrcMapFilenamesAbsolute: boolean;
  499. function GetSrcMapXSSIHeader: boolean;
  500. function GetTargetPlatform: TPasToJsPlatform;
  501. function GetTargetProcessor: TPasToJsProcessor;
  502. function GetWriteDebugLog: boolean;
  503. function GetWriteMsgToStdErr: boolean;
  504. function IndexOfInsertJSFilename(const aFilename: string): integer;
  505. function IndexOfAppendJSFilename(const aFilename: string): integer;
  506. procedure InsertCustomJSFiles(aWriter: TPas2JSMapper);
  507. procedure AppendCustomJSFiles(aWriter: TPas2JSMapper);
  508. procedure InsertImportSection(aFileWriter: TPas2JSMapper);
  509. function LoadUsedUnit(Info: TLoadUnitInfo; Context: TPas2jsCompilerFile): TPas2jsCompilerFile;
  510. function OnMacroCfgDir(Sender: TObject; var Params: string; Lvl: integer): boolean;
  511. procedure RemoveInsertJSFilename(const aFilename: string);
  512. procedure RemoveAppendJSFilename(const aFilename: string);
  513. function ResolvedMainJSFile: string;
  514. procedure SetAllJSIntoMainJS(AValue: Boolean);
  515. procedure SetConverterGlobals(const AValue: TPasToJSConverterGlobals);
  516. procedure SetCompilerExe(AValue: string);
  517. procedure SetFS(AValue: TPas2jsFS);
  518. procedure SetMode(AValue: TP2jsMode);
  519. procedure SetModeSwitches(const AValue: TModeSwitches);
  520. procedure SetOptions(AValue: TP2jsCompilerOptions);
  521. procedure SetShowDebug(AValue: boolean);
  522. procedure SetShowFullPaths(AValue: boolean);
  523. procedure SetShowLogo(AValue: Boolean);
  524. procedure SetShowTriedUsedFiles(AValue: boolean);
  525. procedure SetShowUsedTools(AValue: boolean);
  526. procedure SetSkipDefaultConfig(AValue: Boolean);
  527. procedure SetSrcMapBaseDir(const AValue: string);
  528. procedure SetSrcMapEnable(const AValue: boolean);
  529. procedure SetSrcMapInclude(const AValue: boolean);
  530. procedure SetSrcMapFilenamesAbsolute(const AValue: boolean);
  531. procedure SetSrcMapXSSIHeader(const AValue: boolean);
  532. procedure SetTargetPlatform(const AValue: TPasToJsPlatform);
  533. procedure SetTargetProcessor(const AValue: TPasToJsProcessor);
  534. procedure SetWriteDebugLog(const AValue: boolean);
  535. procedure SetWriteMsgToStdErr(const AValue: boolean);
  536. procedure WriteJSToFile(const MapFileName: string; aFileWriter: TPas2JSMapper);
  537. procedure WriteResourceStrings(aFileName: String);
  538. procedure WriteResources(aFileName: String);
  539. procedure WriteSrcMap(const MapFileName: string; aFileWriter: TPas2JSMapper);
  540. private
  541. procedure AddDefinesForTargetPlatform;
  542. procedure AddDefinesForTargetProcessor;
  543. procedure AddReadingModule(aFile: TPas2jsCompilerFile);
  544. procedure RemoveReadingModule(aFile: TPas2jsCompilerFile);
  545. procedure RegisterMessages;
  546. private
  547. // params, cfg files
  548. FCurParam: string;
  549. FResourceOutputFile: String;
  550. procedure LoadConfig(CfgFilename: string);
  551. procedure ReadEnvironment;
  552. procedure ReadParam(Param: string; Quick, FromCmdLine: boolean);
  553. procedure ReadSingleLetterOptions(const Param: string; p: integer;
  554. const Allowed: string; out Enabled, Disabled: string);
  555. procedure ReadCodeGenerationFlags(Param: String; p: integer);
  556. procedure ReadSyntaxFlags(Param: String; p: integer);
  557. procedure ReadVerbosityFlags(Param: String; p: integer);
  558. protected
  559. // Create various other classes. Virtual so they can be overridden in descendents
  560. function CreateImportList : TJSSourceElements;
  561. function CreateImportStatement : TJSImportStatement;
  562. function CreateJSMapper: TPas2JSMapper; virtual;
  563. function CreateJSWriter(aFileWriter: TPas2JSMapper): TJSWriter; virtual;
  564. function CreateLog: TPas2jsLogger; virtual;
  565. function CreateMacroEngine: TPas2jsMacroEngine;virtual;
  566. function CreateSrcMap(const aFileName: String): TPas2JSSrcMap; virtual;
  567. function CreateOptimizer: TPas2JSAnalyzer;
  568. function CreateSetOfCompilerFiles(keyType: TKeyCompareType): TPasAnalyzerKeySet; virtual; abstract;// mandatory !
  569. function CreateFS: TPas2JSFS; virtual; abstract; // mandatory !
  570. function FormatPath(Const aPath: String): String;
  571. function FullFormatPath(Const aPath: String): String;
  572. procedure WritePrecompiledFormats; virtual;
  573. procedure WriteHelpLine(S: String);
  574. function LoadFile(Filename: string; Binary: boolean = false): TPas2jsFile;
  575. // Override these for PCU format
  576. procedure HandleLinkLibStatement(Sender: TObject; const aLibName, aLibAlias, aLibOptions: String; var Handled: boolean);
  577. function CreateCompilerFile(const PasFileName, PCUFilename: String): TPas2jsCompilerFile; virtual;
  578. // Command-line option handling
  579. procedure HandleOptionPCUFormat(aValue: String); virtual;
  580. function HandleOptionPaths(C: Char; aValue: String; FromCmdLine: Boolean): Boolean; virtual;
  581. function HandleOptionJ(C: Char; aValue: String; Quick,FromCmdLine: Boolean): Boolean; virtual;
  582. function HandleOptionM(aValue: String; Quick: Boolean): Boolean; virtual;
  583. procedure HandleOptionConfigFile(aPos: Integer; const aFileName: string); virtual;
  584. procedure HandleOptionInfo(aValue: string);
  585. function HandleOptionOptimization(C: Char; aValue: String): Boolean;
  586. // DoWriteJSFile: return false to use the default write function.
  587. function DoWriteJSFile(const DestFilename, MapFilename: String; aWriter: TPas2JSMapper): Boolean; virtual;
  588. procedure Compile(StartTime: TDateTime);
  589. procedure ProcessQueue;
  590. function MarkNeedBuilding(aFile: TPas2jsCompilerFile;
  591. Checked: TPasAnalyzerKeySet { set of TPas2jsCompilerFile, key is UnitFilename };
  592. var SrcFileCount: integer): boolean;
  593. procedure OptimizeProgram(aFile: TPas2jsCompilerFile); virtual;
  594. procedure CreateJavaScript(aFile: TPas2jsCompilerFile;
  595. Checked: TPasAnalyzerKeySet { set of TPas2jsCompilerFile, key is UnitFilename });
  596. procedure FinishSrcMap(SrcMap: TPas2JSSrcMap); virtual;
  597. // WriteSingleJSFile does not recurse
  598. procedure WriteSingleJSFile(aFile: TPas2jsCompilerFile; CombinedFileWriter: TPas2JSMapper);
  599. // WriteJSFiles recurses uses clause
  600. procedure WriteJSFiles(aFile: TPas2jsCompilerFile;
  601. CombinedFileWriter: TPas2JSMapper;
  602. Checked: TPasAnalyzerKeySet { set of TPas2jsCompilerFile, key is UnitFilename });
  603. procedure InitParamMacros;virtual;
  604. procedure ClearDefines;
  605. procedure RaiseInternalError(id: TMaxPrecInt; Msg: string);
  606. {$IFDEF Pas2js}
  607. procedure HandleJSException(Msg: string; E: jsvalue; TerminateInternal: boolean = true);
  608. {$ENDIF}
  609. function GetExitCode: Longint; virtual;
  610. procedure SetExitCode(Value: Longint); virtual;
  611. Procedure SetWorkingDir(const aDir: String); virtual;
  612. Procedure CreateResourceSupport; virtual;
  613. public
  614. constructor Create; virtual;
  615. destructor Destroy; override;
  616. procedure Reset; virtual;
  617. procedure ParamFatal(Msg: string);
  618. procedure Run(
  619. aCompilerExe: string; // needed for default config and help
  620. aWorkingDir: string;
  621. ParamList: TStrings;
  622. DoReset: boolean = true);
  623. procedure Terminate(TheExitCode: integer);
  624. class function GetVersion(ShortVersion: boolean): string;
  625. procedure WriteHelp;
  626. procedure WriteLogo;
  627. procedure WriteEncoding;
  628. procedure WriteVersionLine;
  629. procedure WriteOptions;
  630. procedure WriteDefines;
  631. procedure WriteUsedTools;
  632. procedure WriteFoldersAndSearchPaths;
  633. procedure WriteInfo;
  634. function GetShownMsgTypes: TMessageTypes;
  635. procedure AddDefine(const aName: String);
  636. procedure AddDefine(const aName, Value: String);
  637. procedure RemoveDefine(const aName: String);
  638. function IsDefined(const aName: String): boolean;
  639. procedure SetOption(Flag: TP2jsCompilerOption; Enable: boolean);
  640. function GetUnitInfo(UseUnitName, InFileName, ModuleDir: String;
  641. PCUSupport: TPCUSupport): TFindUnitInfo;
  642. procedure CheckUnitAlias(var UseUnitName: string); virtual;
  643. function FindFileWithUnitFilename(UnitFilename: string): TPas2jsCompilerFile;
  644. procedure LoadModuleFile(UnitFilename, UseUnitName: string;
  645. out aFile: TPas2jsCompilerFile; isPCU: Boolean);
  646. Function FindUnitJSFileName(aFileName: String): String;
  647. function FindLoadedUnit(const TheUnitName: string): TPas2jsCompilerFile;
  648. procedure AddUsedUnit(aFile: TPas2jsCompilerFile);
  649. function ExpandFileName(const Filename: string): string;
  650. public
  651. property CompilerExe: string read FCompilerExe write SetCompilerExe;
  652. property DefaultNamespace: String read GetDefaultNamespace;
  653. property Defines: TStrings read FDefines;
  654. property FS: TPas2jsFS read FFS write SetFS;
  655. property OwnsFS: boolean read FOwnsFS write FOwnsFS; // true = auto free FS when compiler is freed
  656. property FileCount: integer read GetFileCount;
  657. property InterfaceType: TPasClassInterfaceType read FInterfaceType write FInterfaceType; // default interface type
  658. property Log: TPas2jsLogger read FLog;
  659. property MainFile: TPas2jsCompilerFile read FMainFile;
  660. property ModeSwitches: TModeSwitches read FModeSwitches write SetModeSwitches;
  661. property Options: TP2jsCompilerOptions read FOptions write SetOptions;
  662. property ConverterGlobals: TPasToJSConverterGlobals read FConverterGlobals write SetConverterGlobals;
  663. property ResolverHub: TPas2JSResolverHub read FResolverHub;
  664. property ParamMacros: TPas2jsMacroEngine read FParamMacros;
  665. property PrecompileGUID: TGUID read FPrecompileGUID write FPrecompileGUID;
  666. property RTLVersionCheck: TP2jsRTLVersionCheck read FRTLVersionCheck write FRTLVersionCheck;
  667. property SrcMapEnable: boolean read GetSrcMapEnable write SetSrcMapEnable;
  668. property SrcMapSourceRoot: string read FSrcMapSourceRoot write FSrcMapSourceRoot;
  669. property SrcMapInclude: boolean read GetSrcMapInclude write SetSrcMapInclude;
  670. property SrcMapXSSIHeader: boolean read GetSrcMapXSSIHeader write SetSrcMapXSSIHeader;
  671. property SrcMapFilenamesAbsolute: boolean read GetSrcMapFilenamesAbsolute write SetSrcMapFilenamesAbsolute;
  672. property ShowDebug: boolean read GetShowDebug write SetShowDebug;
  673. property ShowFullPaths: boolean read GetShowFullPaths write SetShowFullPaths;
  674. property ShowLogo: Boolean read GetShowLogo write SetShowLogo;
  675. property ShowTriedUsedFiles: boolean read GetShowTriedUsedFiles write SetShowTriedUsedFiles;
  676. property ShowUsedTools: boolean read GetShowUsedTools write SetShowUsedTools;
  677. property SkipDefaultConfig: Boolean read GetSkipDefaultConfig write SetSkipDefaultConfig;
  678. property TargetPlatform: TPasToJsPlatform read GetTargetPlatform write SetTargetPlatform;
  679. property TargetProcessor: TPasToJsProcessor read GetTargetProcessor write SetTargetProcessor;
  680. property WPOAnalyzer: TPas2JSAnalyzer read FWPOAnalyzer; // Whole Program Optimization
  681. property WriteDebugLog: boolean read GetWriteDebugLog write SetWriteDebugLog;
  682. property WriteMsgToStdErr: boolean read GetWriteMsgToStdErr write SetWriteMsgToStdErr;
  683. property AllJSIntoMainJS: Boolean Read FAllJSIntoMainJS Write SetAllJSIntoMainJS;
  684. property ExitCode: longint read GetExitCode write SetExitCode;
  685. property InsertFilenames : TStringList read FInsertFilenames;
  686. Property AppendFileNames : TStringList read FAppendFileNames;
  687. property MainJSFile: String Read FMainJSFile Write FMainJSFile;
  688. property MainSrcFile: String Read FMainSrcFile Write FMainSrcFile;
  689. property SrcMapBaseDir: string read FSrcMapBaseDir write SetSrcMapBaseDir; // includes trailing pathdelim
  690. property Namespaces: TStringList read FNamespaces;
  691. property NamespacesFromCmdLine: integer read FNamespacesFromCmdLine;
  692. Property ResourceMode : TResourceMode Read FResourceMode Write FResourceMode;
  693. Property ResourceOutputFile : String Read FResourceOutputFile Write FResourceOutputFile;
  694. // can be set optionally, will be freed by compiler
  695. property ConfigSupport: TPas2JSConfigSupport Read FConfigSupport Write FConfigSupport;
  696. property PostProcessorSupport: TPas2JSPostProcessorSupport Read FPostProcessorSupport Write FPostProcessorSupport;
  697. end;
  698. function GetCompiledDate: string;
  699. function GetCompiledVersion: string;
  700. function GetCompiledTargetOS: string;
  701. function GetCompiledTargetCPU: string;
  702. implementation
  703. // !! No filesystem units here.
  704. //uses ;
  705. function GetCompiledDate: string;
  706. begin
  707. Result:={$I %Date%};
  708. end;
  709. function GetCompiledVersion: string;
  710. begin
  711. Result:={$I %FPCVERSION%};
  712. end;
  713. function GetCompiledTargetOS: string;
  714. begin
  715. Result:=lowerCase({$I %FPCTARGETOS%});
  716. end;
  717. function GetCompiledTargetCPU: string;
  718. begin
  719. Result:=lowerCase({$I %FPCTARGETCPU%});
  720. end;
  721. { TPas2JSCompilerSupport }
  722. constructor TPas2JSCompilerSupport.Create(aCompiler: TPas2JSCompiler);
  723. begin
  724. FCompiler:=aCompiler;
  725. end;
  726. { TPas2JSConfigSupport }
  727. constructor TPas2JSConfigSupport.Create(aCompiler: TPas2jsCompiler);
  728. begin
  729. Inherited Create(aCompiler);
  730. FConditionEval:=TCondDirectiveEvaluator.Create;
  731. FConditionEval.OnLog:=@ConditionEvalLog;
  732. FConditionEval.OnEvalVariable:=@ConditionEvalVariable;
  733. end;
  734. destructor TPas2JSConfigSupport.Destroy;
  735. begin
  736. FreeAndNil(FConditionEval);
  737. inherited Destroy;
  738. end;
  739. { TPCUSupport }
  740. procedure TPCUSupport.RaiseInternalError(id: TMaxPrecInt; Msg: string);
  741. begin
  742. MyFile.RaiseInternalError(id,msg);
  743. end;
  744. procedure TPCUSupport.SetPasModule(aModule: TPasModule);
  745. begin
  746. MyFile.FPasModule:=aModule;
  747. end;
  748. procedure TPCUSupport.SetReaderState(aReaderState: TPas2JSReaderState);
  749. begin
  750. MyFile.FReaderState:=aReaderState;
  751. end;
  752. procedure TPCUSupport.SetPCUFileName(const FN: String);
  753. begin
  754. FFile.FPCUFilename:=FN;
  755. end;
  756. constructor TPCUSupport.Create(aCompilerFile: TPas2JSCompilerFile);
  757. begin
  758. FFile:=aCompilerFile;
  759. end;
  760. { TPas2jsMacroEngine }
  761. function TPas2jsMacroEngine.GetMacros(Index: integer): TPas2jsMacro;
  762. begin
  763. Result:=TPas2jsMacro(fMacros[Index]);
  764. end;
  765. constructor TPas2jsMacroEngine.Create;
  766. begin
  767. fMacros:=TObjectList.Create(true);
  768. FMaxLevel:=10;
  769. end;
  770. destructor TPas2jsMacroEngine.Destroy;
  771. begin
  772. FreeAndNil(fMacros);
  773. inherited Destroy;
  774. end;
  775. function TPas2jsMacroEngine.Count: integer;
  776. begin
  777. Result:=fMacros.Count;
  778. end;
  779. function TPas2jsMacroEngine.AddValue(const aName, aDescription, aValue: string
  780. ): TPas2jsMacro;
  781. begin
  782. if not IsValidIdent(aName) then
  783. raise EPas2jsMacro.Create('invalid macro name "'+aName+'"');
  784. if IndexOf(aName)>=0 then
  785. raise EPas2jsMacro.Create('duplicate macro name "'+aName+'"');
  786. Result:=TPas2jsMacro.Create;
  787. Result.Name:=aName;
  788. Result.Description:=aDescription;
  789. Result.Value:=aValue;
  790. fMacros.Add(Result);
  791. end;
  792. function TPas2jsMacroEngine.AddFunction(const aName, aDescription: string;
  793. const OnSubstitute: TOnSubstituteMacro; CanHaveParams: boolean): TPas2jsMacro;
  794. begin
  795. if not IsValidIdent(aName) then
  796. raise EPas2jsMacro.Create('invalid macro name "'+aName+'"');
  797. if IndexOf(aName)>=0 then
  798. raise EPas2jsMacro.Create('duplicate macro name "'+aName+'"');
  799. Result:=TPas2jsMacro.Create;
  800. Result.Name:=aName;
  801. Result.Description:=aDescription;
  802. Result.CanHaveParams:=CanHaveParams;
  803. Result.OnSubstitute:=OnSubstitute;
  804. fMacros.Add(Result);
  805. end;
  806. function TPas2jsMacroEngine.IndexOf(const aName: string): integer;
  807. var
  808. i: Integer;
  809. begin
  810. for i:=0 to Count-1 do
  811. if CompareText(Macros[i].Name,aName)=0 then
  812. exit(i);
  813. Result:=-1;
  814. end;
  815. procedure TPas2jsMacroEngine.Delete(Index: integer);
  816. begin
  817. fMacros.Delete(Index);
  818. end;
  819. function TPas2jsMacroEngine.FindMacro(const aName: string): TPas2jsMacro;
  820. var
  821. i: Integer;
  822. begin
  823. i:=IndexOf(aName);
  824. if i>=0 then
  825. Result:=Macros[i]
  826. else
  827. Result:=nil;
  828. end;
  829. procedure TPas2jsMacroEngine.Substitute(var s: string; Sender: TObject;
  830. Lvl: integer);
  831. // Rules:
  832. // $macro or $macro$
  833. // if Macro.OnSubstitute is set then optional brackets are allowed: $macro(params)
  834. var
  835. p, StartP, BracketLvl, ParamStartP: Integer;
  836. MacroName, NewValue: String;
  837. Macro: TPas2jsMacro;
  838. begin
  839. if Lvl>=MaxLevel then
  840. raise EPas2jsMacro.Create('macro cycle detected: "'+s+'"');
  841. p:=1;
  842. while p<length(s) do begin
  843. if (s[p]='$') and (s[p+1] in ['_','a'..'z','A'..'Z']) then
  844. begin
  845. StartP:=p;
  846. inc(p,2);
  847. while (p<=length(s)) and (s[p] in ['_','a'..'z','A'..'Z','0'..'9']) do
  848. inc(p);
  849. MacroName:=copy(s,StartP+1,p-StartP-1);
  850. Macro:=FindMacro(MacroName);
  851. if Macro=nil then
  852. raise EPas2jsMacro.Create('macro not found "'+MacroName+'" in "'+s+'"');
  853. NewValue:='';
  854. if Macro.CanHaveParams and (p<=length(s)) and (s[p]='(') then
  855. begin
  856. // read NewValue
  857. inc(p);
  858. ParamStartP:=p;
  859. BracketLvl:=1;
  860. repeat
  861. if p>length(s) then
  862. raise EPas2jsMacro.Create('missing closing bracket ) in "'+s+'"');
  863. case s[p] of
  864. '(': inc(BracketLvl);
  865. ')':
  866. if BracketLvl=1 then
  867. begin
  868. NewValue:=copy(s,ParamStartP,p-ParamStartP);
  869. break;
  870. end else begin
  871. dec(BracketLvl);
  872. end;
  873. end;
  874. until false;
  875. end else if (p<=length(s)) and (s[p]='$') then
  876. inc(p);
  877. if Assigned(Macro.OnSubstitute) then
  878. begin
  879. if not Macro.OnSubstitute(Sender,NewValue,Lvl+1) then
  880. raise EPas2jsMacro.Create('macro "'+MacroName+'" failed in "'+s+'"');
  881. end else
  882. NewValue:=Macro.Value;
  883. s:=LeftStr(s,StartP-1)+NewValue+copy(s,p,length(s));
  884. p:=StartP;
  885. end;
  886. inc(p);
  887. end;
  888. end;
  889. { TPas2jsCompilerFile }
  890. constructor TPas2jsCompilerFile.Create(aCompiler: TPas2jsCompiler;
  891. const aPasFilename, aPCUFilename: string);
  892. var
  893. ub: TUsedBySection;
  894. begin
  895. inherited Create(aCompiler);
  896. FPasFileName:=aPasFilename;
  897. FPCUFilename:=aPCUFilename;
  898. if FPasFileName<>'' then
  899. FUnitFilename:=FPasFileName
  900. else
  901. FUnitFilename:=FPCUFilename;
  902. FLog:=Compiler.Log;
  903. FPasResolver:=TPas2jsCompilerResolver.Create;
  904. FPasResolver.Owner:=Self;
  905. FPasResolver.OnFindModule:=@OnResolverFindModule;
  906. FPasResolver.OnCheckSrcName:=@OnResolverCheckSrcName;
  907. FPasResolver.OnLog:=@OnPasResolverLog;
  908. FPasResolver.Log:=Log;
  909. FPasResolver.Hub:=aCompiler.ResolverHub;
  910. FPasResolver.AddObjFPCBuiltInIdentifiers(btAllJSBaseTypes,bfAllJSBaseProcs);
  911. FIsMainFile:=Compiler.FS.SameFileName(Compiler.MainSrcFile,PasFilename);
  912. for ub in TUsedBySection do
  913. FUsedBy[ub]:=TFPList.Create;
  914. FUseAnalyzer:=TPas2JSAnalyzer.Create;
  915. FUseAnalyzer.OnMessage:=@OnUseAnalyzerMessage;
  916. FUseAnalyzer.Resolver:=FPasResolver;
  917. FPCUSupport:=CreatePCUSupport;
  918. end;
  919. destructor TPas2jsCompilerFile.Destroy;
  920. var
  921. ub: TUsedBySection;
  922. begin
  923. FreeAndNil(FPCUSupport);
  924. FreeAndNil(FUseAnalyzer);
  925. for ub in TUsedBySection do
  926. FreeAndNil(FUsedBy[ub]);
  927. FreeAndNil(FJSModule);
  928. FreeAndNil(FConverter);
  929. FreeAndNil(FParser);
  930. FreeAndNil(FScanner);
  931. FreeAndNil(FFileResolver);
  932. FreeAndNil(FPasResolver);
  933. {$IFDEF EnablePasTreeFree}
  934. FPasModule:=nil;
  935. {$ELSE}
  936. if FPasModule<>nil then
  937. FPasModule.ReleaseUsedUnits;
  938. ReleaseAndNil(TPasElement(FPasModule){$IFDEF CheckPasTreeRefCount},'CreateElement'{$ENDIF});
  939. {$ENDIF}
  940. inherited Destroy;
  941. end;
  942. function TPas2jsCompilerFile.CreatePCUSupport: TPCUSupport;
  943. begin
  944. Result:=Nil;
  945. end;
  946. function TPas2jsCompilerFile.GetInitialModeSwitches: TModeSwitches;
  947. begin
  948. Result:=Compiler.ModeSwitches;
  949. end;
  950. function TPas2jsCompilerFile.GetInitialBoolSwitches: TBoolSwitches;
  951. var
  952. bs: TBoolSwitches;
  953. begin
  954. bs:=[bsLongStrings,bsWriteableConst];
  955. if coShowHints in Compiler.Options then
  956. Include(bs,bsHints);
  957. if coShowNotes in Compiler.Options then
  958. Include(bs,bsNotes);
  959. if coShowWarnings in Compiler.Options then
  960. Include(bs,bsWarnings);
  961. if coOverflowChecks in Compiler.Options then
  962. Include(bs,bsOverflowChecks);
  963. if coRangeChecks in Compiler.Options then
  964. Include(bs,bsRangeChecks);
  965. if coObjectChecks in Compiler.Options then
  966. Include(bs,bsObjectChecks);
  967. if coAssertions in Compiler.Options then
  968. Include(bs,bsAssertions);
  969. if coAllowMacros in Compiler.Options then
  970. Include(bs,bsMacro);
  971. if not (coWriteableConst in Compiler.Options) then
  972. Exclude(bs,bsWriteableConst);
  973. Result:=bs;
  974. end;
  975. function TPas2jsCompilerFile.
  976. GetInitialConverterOptions: TPasToJsConverterOptions;
  977. begin
  978. Result:=DefaultPasToJSOptions;
  979. if coUseStrict in Compiler.Options then
  980. Include(Result,fppas2js.coUseStrict)
  981. else
  982. Exclude(Result,fppas2js.coUseStrict);
  983. if coEnumValuesAsNumbers in Compiler.Options then
  984. Include(Result,fppas2js.coEnumNumbers);
  985. if (coShortRefGlobals in Compiler.Options) or IsUnitReadFromPCU then
  986. Include(Result,fppas2js.coShortRefGlobals);
  987. if coObfuscateLocalIdentifiers in Compiler.Options then
  988. Include(Result,fppas2js.coObfuscateLocalIdentifiers);
  989. if coLowerCase in Compiler.Options then
  990. Include(Result,fppas2js.coLowerCase)
  991. else
  992. Exclude(Result,fppas2js.coLowerCase);
  993. case Compiler.RTLVersionCheck of
  994. rvcNone: ;
  995. rvcMain: Include(Result,fppas2js.coRTLVersionCheckMain);
  996. rvcSystem: Include(Result,fppas2js.coRTLVersionCheckSystem);
  997. rvcUnit: Include(Result,fppas2js.coRTLVersionCheckUnit);
  998. end;
  999. end;
  1000. procedure TPas2jsCompilerFile.CreateScannerAndParser(aFileResolver: TPas2jsFSResolver);
  1001. var
  1002. aUnitName: String;
  1003. i: Integer;
  1004. M: TMacroDef;
  1005. begin
  1006. FFileResolver:=aFileResolver;
  1007. // scanner
  1008. if FScanner<>nil then
  1009. RaiseInternalError(20180707193258,UnitFilename);
  1010. FScanner := TPas2jsPasScanner.Create(FileResolver);
  1011. Scanner.LogEvents:=PascalResolver.ScannerLogEvents;
  1012. Scanner.OnLog:=@OnScannerLog;
  1013. Scanner.OnFormatPath:[email protected];
  1014. Scanner.RegisterResourceHandler('*',@HandleResources);
  1015. // create parser (Note: this sets some scanner options to defaults)
  1016. FParser := TPas2jsPasParser.Create(Scanner, FileResolver, PascalResolver);
  1017. // set options
  1018. Scanner.Options:=Scanner.Options+[po_StopOnErrorDirective];
  1019. Scanner.AllowedModeSwitches:=msAllPas2jsModeSwitches;
  1020. Scanner.ReadOnlyModeSwitches:=msAllPas2jsModeSwitchesReadOnly;
  1021. Scanner.CurrentModeSwitches:=GetInitialModeSwitches;
  1022. Scanner.AllowedBoolSwitches:=bsAllPas2jsBoolSwitches;
  1023. Scanner.ReadOnlyBoolSwitches:=bsAllPas2jsBoolSwitchesReadOnly;
  1024. Scanner.CurrentBoolSwitches:=GetInitialBoolSwitches;
  1025. Scanner.AllowedValueSwitches:=vsAllPas2jsValueSwitches;
  1026. Scanner.ReadOnlyValueSwitches:=vsAllPas2jsValueSwitchesReadOnly;
  1027. Scanner.CurrentValueSwitch[vsInterfaces]:=InterfaceTypeNames[Compiler.InterfaceType];
  1028. if coAllowCAssignments in Compiler.Options then
  1029. Scanner.Options:=Scanner.Options+[po_cassignments];
  1030. if Compiler.ResourceMode=rmNone then
  1031. Scanner.Options:= Scanner.Options+[po_DisableResources];
  1032. // Note: some Scanner.Options are set by TPasResolver
  1033. for i:=0 to Compiler.Defines.Count-1 do
  1034. begin
  1035. M:=TMacroDef(Compiler.Defines.Objects[i]);
  1036. if M=nil then
  1037. Scanner.AddDefine(Compiler.Defines[i])
  1038. else
  1039. Scanner.AddMacro(M.Name,M.Value);
  1040. end;
  1041. Scanner.CompilerVersion:=Compiler.GetVersion(true);
  1042. Scanner.TargetPlatform:=Compiler.TargetPlatform;
  1043. Scanner.TargetProcessor:=Compiler.TargetProcessor;
  1044. Scanner.Resolver:=PascalResolver;
  1045. // parser
  1046. Parser.LogEvents:=PascalResolver.ParserLogEvents;
  1047. Parser.OnLog:=@OnParserLog;
  1048. Parser.Log:=Log;
  1049. PascalResolver.P2JParser:=Parser;
  1050. if not IsMainFile then
  1051. begin
  1052. aUnitName:=ExtractFilenameOnly(UnitFilename);
  1053. if CompareText(aUnitName,'system')=0 then
  1054. Parser.ImplicitUses.Clear;
  1055. end;
  1056. end;
  1057. procedure TPas2jsCompilerFile.CreateConverter;
  1058. begin
  1059. if FConverter<>nil then exit;
  1060. FConverter:=TPasToJSConverter.Create;
  1061. FConverter.Globals:=Compiler.ConverterGlobals;
  1062. FConverter.Options:=GetInitialConverterOptions;
  1063. end;
  1064. procedure TPas2jsCompilerFile.OnResolverCheckSrcName(const Element: TPasElement);
  1065. var
  1066. SrcName, ExpectedSrcName: String;
  1067. begin
  1068. //writeln('TPas2jsCompilerFile.OnPasTreeCheckSrcName ',UnitFilename,' Name=',Element.Name,' IsMainFile=',IsMainFile);
  1069. if (Element.ClassType=TPasUnitModule) or (Element.ClassType=TPasModule) then
  1070. begin
  1071. SrcName:=Element.Name;
  1072. if IsMainFile then
  1073. begin
  1074. // main source is an unit
  1075. if PasUnitName='' then
  1076. begin
  1077. {$IFDEF VerboseSetPasUnitName}
  1078. writeln('TPas2jsCompilerFile.OnPasTreeCheckSrcName ',UnitFilename,' Name=',Element.Name,' IsMainFile=',IsMainFile);
  1079. {$ENDIF}
  1080. PasUnitName:=SrcName;
  1081. Compiler.AddUsedUnit(Self);
  1082. end;
  1083. end else begin
  1084. // an unit name must fit its filename
  1085. ExpectedSrcName:=ExtractFilenameOnly(UnitFilename);
  1086. if CompareText(SrcName,ExpectedSrcName)=0 then
  1087. exit; // ok
  1088. Parser.RaiseParserError(nExpectedButFound,[ExpectedSrcName,SrcName]);
  1089. end;
  1090. end;
  1091. end;
  1092. function TPas2jsCompilerFile.GetUsedBy(Section: TUsedBySection; Index: integer
  1093. ): TPas2jsCompilerFile;
  1094. begin
  1095. Result:=TPas2jsCompilerFile(FUsedBy[Section][Index]);
  1096. end;
  1097. function TPas2jsCompilerFile.GetUsedByCount(Section: TUsedBySection): integer;
  1098. begin
  1099. Result:=FUsedBy[Section].Count;
  1100. end;
  1101. procedure TPas2jsCompilerFile.HandleResources(Sender: TObject; const aFileName: String; aOptions: TStrings);
  1102. begin
  1103. // maybe emit message ?
  1104. FResourceHandler.StartUnit(PasModule.Name);
  1105. FResourceHandler.HandleResource(aFileName,aOptions);
  1106. if Sender=nil then ;
  1107. end;
  1108. function TPas2jsCompilerFile.OnConverterIsElementUsed(Sender: TObject;
  1109. El: TPasElement): boolean;
  1110. begin
  1111. if (Compiler.WPOAnalyzer<>nil)
  1112. and not (coKeepNotUsedDeclarationsWPO in Compiler.Options) then
  1113. Result:=Compiler.WPOAnalyzer.IsUsed(El)
  1114. else if not (coKeepNotUsedPrivates in Compiler.Options) then
  1115. Result:=UseAnalyzer.IsUsed(El)
  1116. else
  1117. Result:=true;
  1118. if Sender=nil then ;
  1119. end;
  1120. function TPas2jsCompilerFile.OnConverterIsTypeInfoUsed(Sender: TObject;
  1121. El: TPasElement): boolean;
  1122. begin
  1123. if (Compiler.WPOAnalyzer<>nil)
  1124. and not (coKeepNotUsedDeclarationsWPO in Compiler.Options) then
  1125. Result:=Compiler.WPOAnalyzer.IsTypeInfoUsed(El)
  1126. else if not (coKeepNotUsedPrivates in Compiler.Options) then
  1127. Result:=UseAnalyzer.IsTypeInfoUsed(El)
  1128. else
  1129. Result:=true;
  1130. if Sender=nil then ;
  1131. end;
  1132. procedure TPas2jsCompilerFile.OnPasResolverLog(Sender: TObject; const Msg: String);
  1133. var
  1134. aResolver: TPasResolver;
  1135. begin
  1136. if Msg='' then ; // ignore standard formatted message
  1137. aResolver:=TPasResolver(Sender);
  1138. DoLogMsgAtEl(aResolver.LastMsgType,aResolver.LastMsg,aResolver.LastMsgNumber,
  1139. aResolver.LastElement);
  1140. if Sender=nil then ;
  1141. end;
  1142. procedure TPas2jsCompilerFile.OnParserLog(Sender: TObject; const Msg: String);
  1143. var
  1144. aParser: TPasParser;
  1145. aScanner: TPascalScanner;
  1146. begin
  1147. if Msg='' then ; // ignore standard formatted message
  1148. aParser:=TPasParser(Sender);
  1149. aScanner:=aParser.Scanner;
  1150. Log.Log(aParser.LastMsgType,aParser.LastMsg,aParser.LastMsgNumber,
  1151. aScanner.CurFilename,aScanner.CurRow,aScanner.CurColumn);
  1152. if Sender=nil then ;
  1153. end;
  1154. procedure TPas2jsCompilerFile.OnScannerLog(Sender: TObject; const Msg: String);
  1155. var
  1156. aScanner: TPas2jsPasScanner;
  1157. begin
  1158. if Msg='' then ; // ignore standard formatted message
  1159. aScanner:=TPas2jsPasScanner(Sender);
  1160. Log.Log(aScanner.LastMsgType,aScanner.LastMsg,aScanner.LastMsgNumber,
  1161. aScanner.CurFilename,aScanner.CurRow,aScanner.CurColumn);
  1162. if Sender=nil then ;
  1163. end;
  1164. procedure TPas2jsCompilerFile.OnUseAnalyzerMessage(Sender: TObject;
  1165. Msg: TPAMessage);
  1166. begin
  1167. Log.Log(Msg.MsgType,Msg.MsgText,Msg.MsgNumber,Msg.Filename,Msg.Row,Msg.Col);
  1168. if Sender=nil then ;
  1169. end;
  1170. procedure TPas2jsCompilerFile.HandleEParserError(E: EParserError);
  1171. begin
  1172. Log.Log(Parser.LastMsgType,Parser.LastMsg,Parser.LastMsgNumber,
  1173. E.Filename,E.Row,E.Column);
  1174. Compiler.Terminate(ExitCodeSyntaxError);
  1175. end;
  1176. procedure TPas2jsCompilerFile.HandleEPasResolve(E: EPasResolve);
  1177. var
  1178. aFilename: String;
  1179. aRow, aColumn: integer;
  1180. begin
  1181. if E.PasElement<>nil then
  1182. begin
  1183. aFilename:=E.PasElement.SourceFilename;
  1184. PascalResolver.UnmangleSourceLineNumber(E.PasElement.SourceLinenumber,aRow,aColumn);
  1185. end else begin
  1186. aFilename:=Scanner.CurFilename;
  1187. aRow:=Scanner.CurRow;
  1188. aColumn:=Scanner.CurColumn;
  1189. end;
  1190. Log.Log(E.MsgType,E.Message,E.MsgNumber,aFilename,aRow,aColumn);
  1191. Compiler.Terminate(ExitCodeSyntaxError);
  1192. end;
  1193. procedure TPas2jsCompilerFile.HandleEPas2JS(E: EPas2JS);
  1194. var
  1195. aFilename: String;
  1196. aRow, aColumn: integer;
  1197. begin
  1198. if E.PasElement<>nil then
  1199. begin
  1200. aFilename:=E.PasElement.SourceFilename;
  1201. PascalResolver.UnmangleSourceLineNumber(E.PasElement.SourceLinenumber,aRow,aColumn);
  1202. Log.Log(E.MsgType,E.Message,E.MsgNumber,aFilename,aRow,aColumn);
  1203. end else begin
  1204. Log.Log(E.MsgType,E.Message,E.MsgNumber);
  1205. end;
  1206. Compiler.Terminate(ExitCodeConverterError);
  1207. end;
  1208. procedure TPas2jsCompilerFile.HandleUnknownException(E: Exception);
  1209. begin
  1210. if not (E is ECompilerTerminate) then
  1211. Log.Log(mtFatal,'bug: uncaught '+E.ClassName+': '+E.Message,0); // must use on E:ECompilerTerminate do raise;
  1212. Log.Log(mtFatal,E.ClassName+': '+E.Message,0);
  1213. Compiler.Terminate(ExitCodeErrorInternal);
  1214. // Note: a "raise E" is not allowed by caught exceptions, try..except will free it
  1215. end;
  1216. procedure TPas2jsCompilerFile.HandleException(E: Exception);
  1217. begin
  1218. {$IFDEF ReallyVerbose}
  1219. writeln('TPas2jsCompilerFile.HandleException ',E.ClassName,' ',E.Message);
  1220. {$ENDIF}
  1221. if ShowDebug then
  1222. Log.LogExceptionBackTrace(E);
  1223. if E is EScannerError then
  1224. begin
  1225. Log.Log(Scanner.LastMsgType,Scanner.LastMsg,Scanner.LastMsgNumber,
  1226. Scanner.CurFilename,Scanner.CurRow,Scanner.CurColumn);
  1227. Compiler.Terminate(ExitCodeSyntaxError);
  1228. end else if E is EParserError then
  1229. HandleEParserError(EParserError(E))
  1230. else if E is EPasResolve then
  1231. HandleEPasResolve(EPasResolve(E))
  1232. else if E is EPas2JS then
  1233. HandleEPas2JS(EPas2JS(E))
  1234. else if E is EFileNotFoundError then
  1235. begin
  1236. if (E.Message<>'') or (Log.LastMsgType<>mtFatal) then
  1237. Log.Log(mtFatal,E.Message);
  1238. Compiler.Terminate(ExitCodeFileNotFound);
  1239. end
  1240. else if E is EPas2jsFS then
  1241. begin
  1242. Log.Log(mtFatal,E.Message);
  1243. Compiler.Terminate(ExitCodeFileNotFound);
  1244. end
  1245. else if Assigned(PCUSupport) and PCUSupport.HandleException(E) then
  1246. else
  1247. HandleUnknownException(E);
  1248. end;
  1249. {$IFDEF Pas2js}
  1250. procedure TPas2jsCompilerFile.HandleJSException(Msg: string; E: jsvalue);
  1251. begin
  1252. Compiler.HandleJSException(Msg,E,true);
  1253. end;
  1254. {$ENDIF}
  1255. procedure TPas2jsCompilerFile.DoLogMsgAtEl(MsgType: TMessageType;
  1256. const Msg: string; MsgNumber: integer; El: TPasElement);
  1257. var
  1258. Line, Col: integer;
  1259. Filename: String;
  1260. begin
  1261. if (El<>nil) then
  1262. begin
  1263. Filename:=El.SourceFilename;
  1264. TPasResolver.UnmangleSourceLineNumber(El.SourceLinenumber,Line,Col);
  1265. end else begin
  1266. Filename:='';
  1267. Line:=0;
  1268. Col:=0;
  1269. end;
  1270. Log.Log(MsgType,Msg,MsgNumber,Filename,Line,Col);
  1271. end;
  1272. procedure TPas2jsCompilerFile.RaiseInternalError(id: TMaxPrecInt; Msg: string);
  1273. begin
  1274. Compiler.RaiseInternalError(id,Msg);
  1275. end;
  1276. function TPas2jsCompilerFile.IsUnitReadFromPCU: Boolean;
  1277. begin
  1278. Result:=Assigned(PCUSupport) and PCUSupport.HasReader;
  1279. end;
  1280. procedure TPas2jsCompilerFile.ReaderFinished;
  1281. begin
  1282. FReaderState:=prsFinished;
  1283. try
  1284. Compiler.RemoveReadingModule(Self);
  1285. if coWriteDebugLog in Compiler.Options then
  1286. begin
  1287. Log.DebugLogWriteLn('Pas-Module:');
  1288. Log.DebugLogWriteLn(PasModule.GetDeclaration(true));
  1289. end;
  1290. if Assigned(PCUSupport) and not PCUSupport.HasReader then
  1291. UseAnalyzer.Options:=UseAnalyzer.Options+[paoImplReferences];
  1292. {$IFDEF VerboseUnitQueue}
  1293. writeln('TPas2jsCompilerFile.ReaderFinished analyzing ',UnitFilename,' ...');
  1294. {$ENDIF}
  1295. UseAnalyzer.AnalyzeModule(FPasModule);
  1296. {$IFDEF ReallyVerbose}
  1297. writeln('TPas2jsCompilerFile.ReaderFinished analyzed ',UnitFilename,' ScopeModule=',GetObjName(UseAnalyzer.ScopeModule));
  1298. {$ENDIF}
  1299. if Assigned(PCUSupport) and Not PCUSupport.HasReader
  1300. and (coPrecompile in Compiler.Options) then
  1301. PCUSupport.WritePCU;
  1302. except
  1303. on E: ECompilerTerminate do
  1304. raise;
  1305. on E: Exception do
  1306. HandleException(E);
  1307. {$IFDEF pas2js}
  1308. else
  1309. HandleJSException('[20181031190529] TPas2jsCompilerFile.ReaderFinished File="'+UnitFilename+'"',
  1310. JSExceptValue);
  1311. {$ENDIF}
  1312. end;
  1313. end;
  1314. procedure TPas2jsCompilerFile.OpenFile(aFilename: string);
  1315. begin
  1316. FPasFilename:=aFilename;
  1317. try
  1318. Scanner.OpenFile(PasFilename);
  1319. except
  1320. on E: ECompilerTerminate do
  1321. raise;
  1322. on E: Exception do
  1323. HandleException(E);
  1324. {$IFDEF pas2js}
  1325. else HandleJSException('[20181031190536] TPas2jsCompilerFile.OpenFile "'+aFilename+'"',JSExceptValue);
  1326. {$ENDIF}
  1327. end;
  1328. end;
  1329. procedure TPas2jsCompilerFile.ReadUnit;
  1330. begin
  1331. if ShowDebug then
  1332. Log.LogMsg(nParsingFile,[QuoteStr(UnitFilename)]);
  1333. if FPasModule<>nil then
  1334. Compiler.RaiseInternalError(20180305190321,UnitFilename);
  1335. FReaderState:=prsReading;
  1336. try
  1337. {$IFDEF VerboseUnitQueue}
  1338. writeln('TPas2jsCompilerFile.ReadUnit ',UnitFilename,' START');
  1339. {$ENDIF}
  1340. Compiler.AddReadingModule(Self);
  1341. PascalResolver.InterfaceOnly:=IsForeign;
  1342. if IsUnitReadFromPCU then
  1343. PCUSupport.ReadUnit
  1344. else
  1345. begin
  1346. if IsMainFile then
  1347. Parser.ParseMain(FPasModule)
  1348. else
  1349. Parser.ParseSubModule(FPasModule);
  1350. if Parser.CurModule=nil then
  1351. ReaderFinished
  1352. else
  1353. FReaderState:=prsWaitingForUsedUnits;
  1354. end;
  1355. {$IFDEF VerboseUnitQueue}
  1356. writeln('TPas2jsCompilerFile.ReadUnit ',UnitFilename,' ReaderState=',ReaderState);
  1357. {$ENDIF}
  1358. except
  1359. on E: ECompilerTerminate do
  1360. raise;
  1361. on E: Exception do
  1362. HandleException(E);
  1363. {$IFDEF pas2js}
  1364. else
  1365. HandleJSException('[20181031190541] TPas2jsCompilerFile.ReadUnit File="'+UnitFilename+'"',
  1366. JSExceptValue);
  1367. {$ENDIF}
  1368. end;
  1369. if FReaderState=prsReading then
  1370. FReaderState:=prsError;
  1371. if (PasModule<>nil) and (PasModule.CustomData=nil) then
  1372. PasModule.CustomData:=Self;
  1373. end;
  1374. function TPas2jsCompilerFile.ReadContinue: boolean;
  1375. begin
  1376. Result:=true;
  1377. if ShowDebug then
  1378. Log.LogPlain(['Debug: Continue reading unit "',UnitFilename,'"...']);
  1379. if FPasModule=nil then
  1380. Compiler.RaiseInternalError(20180305190338,UnitFilename);
  1381. FReaderState:=prsReading;
  1382. try
  1383. {$IFDEF VerboseUnitQueue}
  1384. writeln('TPas2jsCompilerFile.ReadContinue ',UnitFilename);
  1385. {$ENDIF}
  1386. if Assigned(PCUSupport) and PCUSupport.HasReader then
  1387. Result:=PCUSupport.ReadContinue
  1388. else
  1389. begin
  1390. Parser.ParseContinue;
  1391. Result:=Parser.CurModule=nil;
  1392. end;
  1393. {$IFDEF VerboseUnitQueue}
  1394. writeln('TPas2jsCompilerFile.ReadContinue ',UnitFilename,' finished=',Result);
  1395. {$ENDIF}
  1396. if Result then
  1397. ReaderFinished
  1398. else
  1399. FReaderState:=prsWaitingForUsedUnits;
  1400. except
  1401. on E: ECompilerTerminate do
  1402. raise;
  1403. on E: Exception do
  1404. HandleException(E);
  1405. {$IFDEF pas2js}
  1406. else
  1407. HandleJSException('[20181031190545] TPas2jsCompilerFile.ReadContinue File="'+UnitFilename+'"',
  1408. JSExceptValue);
  1409. {$ENDIF}
  1410. end;
  1411. if FReaderState=prsReading then
  1412. FReaderState:=prsError;
  1413. end;
  1414. function TPas2jsCompilerFile.ReaderState: TPas2jsReaderState;
  1415. var
  1416. Section: TPasSection;
  1417. begin
  1418. Result:=FReaderState;
  1419. if Result=prsWaitingForUsedUnits then
  1420. begin
  1421. if Assigned(PCUSupport) and PCUSupport.HasReader then
  1422. begin
  1423. If PCUSupport.ReadCanContinue then
  1424. Result:=prsCanContinue;
  1425. end
  1426. else
  1427. begin
  1428. if Parser.CanParseContinue(Section) then
  1429. Result:=prsCanContinue;
  1430. end;
  1431. end;
  1432. end;
  1433. procedure TPas2jsCompilerFile.CreateJS;
  1434. begin
  1435. //writeln('TPas2jsCompilerFile.CreateJS START ',UnitFilename,' JS=',GetObjName(FJSModule));
  1436. try
  1437. // convert
  1438. CreateConverter;
  1439. Converter.OnIsElementUsed:=@OnConverterIsElementUsed;
  1440. Converter.OnIsTypeInfoUsed:=@OnConverterIsTypeInfoUsed;
  1441. FJSModule:=Converter.ConvertPasElement(PasModule,PascalResolver);
  1442. if FResourceHandler.Outputmode=romJS then
  1443. FJSModule:=FResourceHandler.WriteJS(PasUnitName,FJSModule);
  1444. except
  1445. on E: ECompilerTerminate do
  1446. raise;
  1447. on E: Exception do
  1448. HandleException(E);
  1449. {$IFDEF pas2js}
  1450. else
  1451. HandleJSException('[20181031190549] TPas2jsCompilerFile.CreateJS File="'+UnitFilename+'"',
  1452. JSExceptValue);
  1453. {$ENDIF}
  1454. end;
  1455. //writeln('TPas2jsCompilerFile.CreateJS END ',UnitFilename,' JS=',GetObjName(FJSModule));
  1456. end;
  1457. procedure TPas2jsCompilerFile.EmitModuleHints;
  1458. begin
  1459. try
  1460. // show hints only for units with sources
  1461. if (PCUSupport=nil) or not PCUSupport.HasReader then
  1462. begin
  1463. //writeln('TPas2jsCompilerFile.EmitModuleHints ',UnitFilename);
  1464. UseAnalyzer.EmitModuleHints(PasModule);
  1465. end;
  1466. except
  1467. on E: ECompilerTerminate do
  1468. raise;
  1469. on E: Exception do
  1470. HandleException(E);
  1471. {$IFDEF pas2js}
  1472. else
  1473. HandleJSException('[20190226183324] TPas2jsCompilerFile.EmitModuleHints File="'+UnitFilename+'"',
  1474. JSExceptValue);
  1475. {$ENDIF}
  1476. end;
  1477. end;
  1478. function TPas2jsCompilerFile.GetPasFirstSection: TPasSection;
  1479. var
  1480. aModule: TPasModule;
  1481. begin
  1482. aModule:=GetCurPasModule;
  1483. if aModule=nil then exit;
  1484. if aModule.ClassType=TPasProgram then
  1485. Result:=TPasProgram(aModule).ProgramSection
  1486. else if aModule.ClassType=TPasLibrary then
  1487. Result:=TPasLibrary(aModule).LibrarySection
  1488. else
  1489. Result:=aModule.InterfaceSection;
  1490. end;
  1491. function TPas2jsCompilerFile.GetPasImplSection: TPasSection;
  1492. var
  1493. aModule: TPasModule;
  1494. begin
  1495. Result:=nil;
  1496. aModule:=GetCurPasModule;
  1497. if aModule=nil then exit;
  1498. Result:=aModule.ImplementationSection;
  1499. end;
  1500. function TPas2jsCompilerFile.GetPasMainUsesClause: TPasUsesClause;
  1501. var
  1502. aModule: TPasModule;
  1503. IntfSection: TInterfaceSection;
  1504. PrgSection: TProgramSection;
  1505. LibSection: TLibrarySection;
  1506. begin
  1507. Result:=nil;
  1508. aModule:=GetCurPasModule;
  1509. if aModule=nil then exit;
  1510. if aModule.ClassType=TPasModule then
  1511. begin
  1512. IntfSection:=TPasModule(aModule).InterfaceSection;
  1513. if IntfSection<>nil then
  1514. Result:=IntfSection.UsesClause;
  1515. end else if aModule.ClassType=TPasProgram then
  1516. begin
  1517. PrgSection:=TPasProgram(aModule).ProgramSection;
  1518. if PrgSection<>nil then
  1519. Result:=PrgSection.UsesClause;
  1520. end else if aModule.ClassType=TPasLibrary then
  1521. begin
  1522. LibSection:=TPasLibrary(aModule).LibrarySection;
  1523. if LibSection<>nil then
  1524. Result:=LibSection.UsesClause;
  1525. end;
  1526. end;
  1527. function TPas2jsCompilerFile.GetPasImplUsesClause: TPasUsesClause;
  1528. var
  1529. aModule: TPasModule;
  1530. begin
  1531. Result:=nil;
  1532. aModule:=GetCurPasModule;
  1533. if aModule=nil then exit;
  1534. if aModule.ImplementationSection<>nil then
  1535. Result:=aModule.ImplementationSection.UsesClause;
  1536. end;
  1537. function TPas2jsCompilerFile.GetCurPasModule: TPasModule;
  1538. begin
  1539. if PasModule<>nil then
  1540. Result:=PasModule
  1541. else if (PascalResolver<>nil) and (PascalResolver.RootElement<>nil) then
  1542. Result:=PascalResolver.RootElement
  1543. else if Parser<>nil then
  1544. Result:=Parser.CurModule
  1545. else
  1546. Result:=nil;
  1547. end;
  1548. function TPas2jsCompilerFile.GetModuleName: string;
  1549. var
  1550. aModule: TPasModule;
  1551. begin
  1552. aModule:=GetCurPasModule;
  1553. if aModule<>nil then
  1554. Result:=aModule.Name
  1555. else
  1556. Result:='';
  1557. if Result='' then
  1558. Result:=ExtractFilenameOnly(UnitFilename);
  1559. end;
  1560. class function TPas2jsCompilerFile.GetFile(aModule: TPasModule
  1561. ): TPas2jsCompilerFile;
  1562. var
  1563. Scope: TPasModuleScope;
  1564. Resolver: TPas2jsCompilerResolver;
  1565. begin
  1566. Result:=nil;
  1567. if (aModule=nil) or (aModule.CustomData=nil) then exit;
  1568. if aModule.CustomData is TPas2jsCompilerFile then
  1569. Result:=TPas2jsCompilerFile(aModule.CustomData)
  1570. else if aModule.CustomData is TPasModuleScope then
  1571. begin
  1572. Scope:=TPasModuleScope(aModule.CustomData);
  1573. Resolver:=NoNil(Scope.Owner) as TPas2jsCompilerResolver;
  1574. Result:=Resolver.Owner as TPas2jsCompilerFile;
  1575. end;
  1576. end;
  1577. function TPas2jsCompilerFile.OnResolverFindModule(const UseUnitName,
  1578. InFilename: String; NameExpr, InFileExpr: TPasExpr): TPasModule;
  1579. var
  1580. aFile: TPas2jsCompilerFile;
  1581. UnitInfo: TFindUnitInfo;
  1582. LoadInfo: TLoadUnitInfo;
  1583. ModuleDir: String;
  1584. begin
  1585. Result:=nil;
  1586. aFile:=Nil;
  1587. // check duplicate identifier or unit cycle
  1588. if CompareText(ExtractFilenameOnly(UnitFilename),UseUnitname)=0 then
  1589. Parser.RaiseParserError(nUnitCycle,[UseUnitname]);
  1590. ModuleDir:=ExtractFilePath(PasFileName);
  1591. UnitInfo:=Compiler.GetUnitInfo(UseUnitName,InFileName,ModuleDir,PCUSupport);
  1592. if UnitInfo.FileName<>'' then
  1593. begin
  1594. LoadInfo.UseFilename:=UnitInfo.FileName;
  1595. LoadInfo.UseUnitname:=UnitInfo.UnitName;
  1596. LoadInfo.NameExpr:=NameExpr;
  1597. LoadInfo.IsPCU:=UnitInfo.isPCU;
  1598. if UnitInfo.isPCU then
  1599. begin
  1600. LoadInfo.InFilename:='';
  1601. LoadInfo.InFileExpr:=Nil;
  1602. LoadInfo.UseIsForeign:=False;
  1603. end
  1604. else
  1605. begin
  1606. LoadInfo.InFilename:=InFileName;
  1607. LoadInfo.InFileExpr:=InFileExpr;
  1608. LoadInfo.UseIsForeign:=UnitInfo.isForeign;
  1609. end;
  1610. aFile:=Compiler.LoadUsedUnit(LoadInfo,Self);
  1611. end;
  1612. if aFile<>nil then
  1613. Result:=aFile.PasModule;
  1614. // if Result=nil resolver will give a nice error position, so don't do it here
  1615. end;
  1616. { TPas2JSConfigSupport }
  1617. procedure TPas2JSConfigSupport.CfgSyntaxError(const Msg: string);
  1618. begin
  1619. Compiler.Log.Log(mtError,Msg,0,CurrentCfgFilename,CurrentCfgLineNumber,0);
  1620. Compiler.Terminate(ExitCodeErrorInConfig);
  1621. end;
  1622. procedure TPas2JSConfigSupport.LoadConfig(Const aFileName: String);
  1623. type
  1624. TSkip = (
  1625. skipNone,
  1626. skipIf,
  1627. skipElse
  1628. );
  1629. const
  1630. IdentChars = ['a'..'z','A'..'Z','_','0'..'9'];
  1631. var
  1632. Line: String;
  1633. l, p, StartP: integer;
  1634. function GetWord: String;
  1635. begin
  1636. StartP:=p;
  1637. while (p<=l) and ((Line[p] in IdentChars) or (Line[p]>#127)) do inc(p);
  1638. Result:=copy(Line,StartP,p-StartP);
  1639. while (p<=l) and (Line[p] in [' ',#9]) do inc(p);
  1640. end;
  1641. procedure DebugCfgDirective(const s: string);
  1642. begin
  1643. Compiler.Log.LogMsg(nCfgDirective,[QuoteStr(Line),s],CurrentCfgFilename,CurrentCfgLineNumber,1,false);
  1644. end;
  1645. var
  1646. OldCfgFilename, Directive, aName, Expr: String;
  1647. aFile: TSourceLineReader;
  1648. IfLvl, SkipLvl, OldCfgLineNumber: Integer;
  1649. Skip: TSkip;
  1650. begin
  1651. if Compiler.ShowDebug or Compiler.ShowTriedUsedFiles then
  1652. Compiler.Log.LogMsgIgnoreFilter(nReadingOptionsFromFile,[QuoteStr(aFilename)]);
  1653. IfLvl:=0;
  1654. SkipLvl:=0;
  1655. Skip:=skipNone;
  1656. aFile:=nil;
  1657. try
  1658. OldCfgFilename:=FCurrentCfgFilename;
  1659. FCurrentCfgFilename:=aFilename;
  1660. OldCfgLineNumber:=FCurrentCfgLineNumber;
  1661. aFile:=GetReader(aFileName);
  1662. while not aFile.IsEOF do begin
  1663. Line:=aFile.ReadLine;
  1664. FCurrentCfgLineNumber:=aFile.LineNumber;
  1665. if Compiler.ShowDebug then
  1666. Compiler.Log.LogMsgIgnoreFilter(nInterpretingFileOption,[QuoteStr(Line)]);
  1667. if Line='' then continue;
  1668. l:=length(Line);
  1669. p:=1;
  1670. while (p<=l) and (Line[p] in [' ',#9]) do inc(p);
  1671. if p>l then continue; // empty line
  1672. if (p<=l) and (Line[p]='#') then
  1673. begin
  1674. // cfg directive
  1675. inc(p);
  1676. if (p>l) or (Line[p] in [#0,#9,' ','-']) then continue; // comment
  1677. Directive:=lowercase(GetWord);
  1678. case Directive of
  1679. 'ifdef','ifndef':
  1680. begin
  1681. inc(IfLvl);
  1682. if Skip=skipNone then
  1683. begin
  1684. aName:=GetWord;
  1685. if Compiler.IsDefined(aName)=(Directive='ifdef') then
  1686. begin
  1687. // execute block
  1688. if Compiler.ShowDebug then
  1689. DebugCfgDirective('true -> execute');
  1690. end else begin
  1691. // skip block
  1692. if Compiler.ShowDebug then
  1693. DebugCfgDirective('false -> skip');
  1694. SkipLvl:=IfLvl;
  1695. Skip:=skipIf;
  1696. end;
  1697. end;
  1698. end;
  1699. 'if':
  1700. begin
  1701. inc(IfLvl);
  1702. if Skip=skipNone then
  1703. begin
  1704. Expr:=copy(Line,p,length(Line));
  1705. if ConditionEvaluator.Eval(Expr) then
  1706. begin
  1707. // execute block
  1708. if Compiler.ShowDebug then
  1709. DebugCfgDirective('true -> execute');
  1710. end else begin
  1711. // skip block
  1712. if Compiler.ShowDebug then
  1713. DebugCfgDirective('false -> skip');
  1714. SkipLvl:=IfLvl;
  1715. Skip:=skipIf;
  1716. end;
  1717. end;
  1718. end;
  1719. 'else':
  1720. begin
  1721. if IfLvl=0 then
  1722. CfgSyntaxError('"'+Directive+'" without #ifdef');
  1723. if (Skip=skipElse) and (IfLvl=SkipLvl) then
  1724. CfgSyntaxError('"there was already an #else');
  1725. if (Skip=skipIf) and (IfLvl=SkipLvl) then
  1726. begin
  1727. // if-block was skipped -> execute else block
  1728. if Compiler.ShowDebug then
  1729. DebugCfgDirective('execute');
  1730. SkipLvl:=0;
  1731. Skip:=skipNone;
  1732. end else if Skip=skipNone then
  1733. begin
  1734. // if-block was executed -> skip else block
  1735. if Compiler.ShowDebug then
  1736. DebugCfgDirective('skip');
  1737. Skip:=skipElse;
  1738. SkipLvl:=IfLvl;
  1739. end;
  1740. end;
  1741. 'elseif':
  1742. begin
  1743. if IfLvl=0 then
  1744. CfgSyntaxError('"'+Directive+'" without #ifdef');
  1745. if (Skip=skipIf) and (IfLvl=SkipLvl) then
  1746. begin
  1747. // if-block was skipped -> try this elseif
  1748. Expr:=copy(Line,p,length(Line));
  1749. if ConditionEvaluator.Eval(Expr) then
  1750. begin
  1751. // execute elseif block
  1752. if Compiler.ShowDebug then
  1753. DebugCfgDirective('true -> execute');
  1754. SkipLvl:=0;
  1755. Skip:=skipNone;
  1756. end else begin
  1757. // skip elseif block
  1758. if Compiler.ShowDebug then
  1759. DebugCfgDirective('false -> skip');
  1760. end;
  1761. end else if Skip=skipNone then
  1762. begin
  1763. // if-block was executed -> skip without test
  1764. if Compiler.ShowDebug then
  1765. DebugCfgDirective('no test -> skip');
  1766. Skip:=skipIf;
  1767. end;
  1768. end;
  1769. 'endif':
  1770. begin
  1771. if IfLvl=0 then
  1772. CfgSyntaxError('"'+Directive+'" without #ifdef');
  1773. dec(IfLvl);
  1774. if IfLvl<SkipLvl then
  1775. begin
  1776. // end block
  1777. if Compiler.ShowDebug then
  1778. DebugCfgDirective('end block');
  1779. SkipLvl:=0;
  1780. Skip:=skipNone;
  1781. end;
  1782. end;
  1783. 'error':
  1784. Compiler.ParamFatal('user defined: '+copy(Line,p,length(Line)))
  1785. else
  1786. if Skip=skipNone then
  1787. CfgSyntaxError('unknown directive "#'+Directive+'"')
  1788. else
  1789. DebugCfgDirective('skipping unknown directive');
  1790. end;
  1791. end else if Skip=skipNone then
  1792. begin
  1793. // option line
  1794. Line:=copy(Line,p,length(Line));
  1795. Compiler.ReadParam(Line,false,false);
  1796. end;
  1797. end;
  1798. finally
  1799. FCurrentCfgFilename:=OldCfgFilename;
  1800. FCurrentCfgLineNumber:=OldCfgLineNumber;
  1801. aFile.Free;
  1802. end;
  1803. if Compiler.ShowDebug or Compiler.ShowTriedUsedFiles then
  1804. Compiler.Log.LogMsgIgnoreFilter(nEndOfReadingConfigFile,[QuoteStr(aFilename)]);
  1805. end;
  1806. procedure TPas2JSConfigSupport.LoadDefaultConfig;
  1807. var
  1808. aFileName: string;
  1809. begin
  1810. aFileName:=FindDefaultConfig;
  1811. if aFileName<>'' then
  1812. LoadConfig(aFilename);
  1813. end;
  1814. procedure TPas2JSConfigSupport.ConditionEvalLog(Sender: TCondDirectiveEvaluator;
  1815. Args: array of const);
  1816. begin
  1817. CfgSyntaxError(SafeFormat(Sender.MsgPattern,Args));
  1818. end;
  1819. function TPas2JSConfigSupport.ConditionEvalVariable(Sender: TCondDirectiveEvaluator;
  1820. aName: String; out Value: string): boolean;
  1821. var
  1822. i: Integer;
  1823. M: TMacroDef;
  1824. ms: TModeSwitch;
  1825. begin
  1826. // check defines
  1827. i:=Compiler.Defines.IndexOf(aName);
  1828. if i>=0 then
  1829. begin
  1830. M:=TMacroDef(Compiler.Defines.Objects[i]);
  1831. if M=nil then
  1832. Value:=CondDirectiveBool[true]
  1833. else
  1834. Value:=M.Value;
  1835. exit(true);
  1836. end;
  1837. // check modeswitches
  1838. ms:=StrToModeSwitch(aName);
  1839. if (ms<>msNone) and (ms in Compiler.ModeSwitches) then
  1840. begin
  1841. Value:=CondDirectiveBool[true];
  1842. exit(true);
  1843. end;
  1844. if Sender=nil then ;
  1845. Result:=false;
  1846. end;
  1847. { TPas2jsCompiler }
  1848. procedure TPas2jsCompiler.SetFS(AValue: TPas2jsFS);
  1849. begin
  1850. if FFS=AValue then Exit;
  1851. FOwnsFS:=false;
  1852. FFS:=AValue;
  1853. end;
  1854. function TPas2jsCompiler.GetFileCount: integer;
  1855. begin
  1856. Result:=FFiles.Count;
  1857. end;
  1858. function TPas2jsCompiler.GetDefaultNamespace: String;
  1859. var
  1860. C: TClass;
  1861. begin
  1862. Result:='';
  1863. if FMainFile=nil then exit;
  1864. if FMainFile.PasModule=nil then exit;
  1865. C:=FMainFile.PasModule.ClassType;
  1866. if (C=TPasProgram) or (C=TPasLibrary) or (C=TPasPackage) then
  1867. Result:=FMainFile.PascalResolver.DefaultNameSpace;
  1868. end;
  1869. procedure TPas2jsCompiler.Compile(StartTime: TDateTime);
  1870. var
  1871. Checked: TPasAnalyzerKeySet;
  1872. CombinedFileWriter: TPas2JSMapper;
  1873. SrcFileCount: integer;
  1874. Seconds: TDateTime;
  1875. ok: Boolean;
  1876. begin
  1877. if FMainFile<>nil then
  1878. RaiseInternalError(20170504192137,'');
  1879. Checked:=nil;
  1880. CombinedFileWriter:=nil;
  1881. SrcFileCount:=0;
  1882. CreateGUID(FPrecompileGUID);
  1883. ok:=false;
  1884. try
  1885. // load main Pascal file
  1886. LoadModuleFile(MainSrcFile,'',FMainFile,False);
  1887. if MainFile=nil then exit;
  1888. // parse and load Pascal files recursively
  1889. FMainFile.ReadUnit;
  1890. ProcessQueue;
  1891. // whole program optimization
  1892. if MainFile.PasModule is TPasProgram then
  1893. OptimizeProgram(MainFile);
  1894. // check what files need building
  1895. Checked:=CreateSetOfCompilerFiles(kcFilename);
  1896. MarkNeedBuilding(MainFile,Checked,SrcFileCount);
  1897. SrcFileCount:=Checked.Count;// all modules, including skipped modules
  1898. FreeAndNil(Checked);
  1899. // convert all Pascal to JavaScript
  1900. Checked:=CreateSetOfCompilerFiles(kcFilename);
  1901. CreateJavaScript(MainFile,Checked);
  1902. FreeAndNil(Checked);
  1903. // write .js files
  1904. Checked:=CreateSetOfCompilerFiles(kcFilename);
  1905. WriteJSFiles(MainFile,Nil,Checked);
  1906. FreeAndNil(Checked);
  1907. // write success message
  1908. if ExitCode=0 then
  1909. begin
  1910. Seconds:=(Now-StartTime)*86400;
  1911. Log.LogMsgIgnoreFilter(nLinesInFilesCompiled,
  1912. [IntToStr(FS.ReadLineCounter),IntToStr(SrcFileCount),
  1913. FormatFloat('0.0',Seconds),'s']);
  1914. ok:=true;
  1915. end;
  1916. finally
  1917. Checked.Free;
  1918. if not Ok then
  1919. Log.LogMsgIgnoreFilter(nCompilationAborted,[]);
  1920. CombinedFileWriter.Free;
  1921. end;
  1922. end;
  1923. procedure TPas2jsCompiler.ProcessQueue;
  1924. var
  1925. i: Integer;
  1926. aFile: TPas2jsCompilerFile;
  1927. Found: Boolean;
  1928. Section: TPasSection;
  1929. begin
  1930. // parse til exception or all modules have finished
  1931. repeat
  1932. {$IF defined(VerbosePasResolver) or defined(VerboseUnitQueue)}
  1933. writeln('TPas2jsCompiler.ProcessQueue FParsingModules.Count=',FReadingModules.Count);
  1934. {$ENDIF}
  1935. Found:=false;
  1936. for i:=FReadingModules.Count-1 downto 0 do
  1937. begin
  1938. aFile:=TPas2jsCompilerFile(FReadingModules[i]);
  1939. if aFile.ReaderState<>prsCanContinue then
  1940. begin
  1941. {$IF defined(VerbosePasResolver) or defined(VerboseUnitQueue)}
  1942. writeln('TPas2jsCompiler.ProcessQueue aFile=',aFile.UnitFilename,' NOT YET READY');
  1943. {$ENDIF}
  1944. if (not aFile.IsUnitReadFromPCU) and (aFile.Parser.CurModule=nil) then
  1945. RaiseInternalError(20180306111410,'File='+aFile.UnitFilename+' Parser.CurModule=nil');
  1946. continue;
  1947. end;
  1948. Found:=true;
  1949. {$IF defined(VerbosePasResolver) or defined(VerboseUnitQueue)}
  1950. writeln('TPas2jsCompiler.ProcessQueue aFile=',aFile.UnitFilename);
  1951. {$ENDIF}
  1952. aFile.ReadContinue;
  1953. if aFile.ReaderState=prsCanContinue then
  1954. begin
  1955. {$IF defined(VerbosePasResolver) or defined(VerboseUnitQueue)}
  1956. writeln('TPas2jsCompiler.ProcessQueue aFile=',aFile.UnitFilename,' ReadContinue buggy');
  1957. {$ENDIF}
  1958. RaiseInternalError(20180313130300,'File='+aFile.UnitFilename+' ReadContinue buggy');
  1959. end;
  1960. break;
  1961. end;
  1962. until not Found;
  1963. {$IF defined(VerbosePasResolver) or defined(VerboseUnitQueue)}
  1964. writeln('TPas2jsCompiler.ProcessQueue END FParsingModules.Count=',FReadingModules.Count);
  1965. {$ENDIF}
  1966. // check consistency
  1967. for i:=0 to FReadingModules.Count-1 do
  1968. begin
  1969. aFile:=TPas2jsCompilerFile(FReadingModules[i]);
  1970. if aFile.PascalResolver=nil then
  1971. RaiseInternalError(20180313124125,aFile.UnitFilename);
  1972. if (Not aFile.IsUnitReadFromPCU) and (aFile.Parser.CurModule<>nil) then
  1973. begin
  1974. {$IF defined(VerbosePasResolver) or defined(VerboseUnitQueue)}
  1975. writeln('TPas2jsCompiler.ProcessQueue aFile=',aFile.UnitFilename,' was not finished');
  1976. {$ENDIF}
  1977. RaiseInternalError(20180305185342,aFile.UnitFilename);
  1978. end;
  1979. Section:=aFile.PascalResolver.GetLastSection;
  1980. if Section=nil then
  1981. RaiseInternalError(20180313124207,aFile.UnitFilename);
  1982. if Section.PendingUsedIntf<>nil then
  1983. RaiseInternalError(20180313124226,aFile.UnitFilename+' '+GetObjName(Section)+' PendingUsedIntf='+GetObjName(Section.PendingUsedIntf));
  1984. end;
  1985. end;
  1986. function TPas2jsCompiler.MarkNeedBuilding(aFile: TPas2jsCompilerFile;
  1987. Checked: TPasAnalyzerKeySet; var SrcFileCount: integer): boolean;
  1988. procedure Mark(MsgNumber: integer;
  1989. Args: array of const);
  1990. begin
  1991. if aFile.NeedBuild then exit;
  1992. aFile.NeedBuild:=true;
  1993. inc(SrcFileCount);
  1994. if ShowDebug or ShowTriedUsedFiles then
  1995. Log.LogMsg(MsgNumber,Args,'',0,0,false);
  1996. end;
  1997. procedure CheckUsesClause(UsesClause: TPasUsesClause);
  1998. var
  1999. i: Integer;
  2000. UsedFile: TPas2jsCompilerFile;
  2001. aModule: TPasModule;
  2002. begin
  2003. if length(UsesClause)=0 then exit;
  2004. for i:=0 to length(UsesClause)-1 do begin
  2005. aModule:=UsesClause[i].Module as TPasModule;
  2006. UsedFile:=TPas2jsCompilerFile.GetFile(aModule);
  2007. if UsedFile=nil then
  2008. RaiseInternalError(20171214121631,aModule.Name);
  2009. if MarkNeedBuilding(UsedFile,Checked,SrcFileCount) then
  2010. begin
  2011. if not aFile.NeedBuild then
  2012. Mark(nUnitNeedsCompileDueToUsedUnit,
  2013. [aFile.GetModuleName,UsedFile.GetModuleName]);
  2014. end;
  2015. end;
  2016. end;
  2017. begin
  2018. Result:=false;
  2019. //writeln('TPas2jsCompiler.MarkNeedBuilding ',aFile.UnitFilename);
  2020. // check each file only once
  2021. if Checked.FindItem(aFile)<>nil then
  2022. exit(aFile.NeedBuild);
  2023. Checked.Add(aFile);
  2024. if AllJSIntoMainJS and (WPOAnalyzer<>nil)
  2025. and not WPOAnalyzer.IsUsed(aFile.PasModule) then
  2026. begin
  2027. {$IFDEF REALLYVERBOSE}
  2028. writeln('TPas2jsCompiler.MarkNeedBuilding module not used by WPO: ',aFile.UnitFilename);
  2029. {$ENDIF}
  2030. exit(false);
  2031. end;
  2032. // check dependencies
  2033. //writeln('TPas2jsCompiler.MarkNeedBuilding CheckUsesClause ',aFile.UnitFilename,' MainUses');
  2034. CheckUsesClause(aFile.GetPasMainUsesClause);
  2035. //writeln('TPas2jsCompiler.MarkNeedBuilding CheckUsesClause ',aFile.UnitFilename,' ImplUses');
  2036. CheckUsesClause(aFile.GetPasImplUsesClause);
  2037. if (not aFile.NeedBuild) and (not aFile.IsForeign) then
  2038. begin
  2039. // this unit can be compiled
  2040. if aFile.IsMainFile then
  2041. Mark(nUnitNeedsCompileDueToOption,[aFile.GetModuleName,'<main source file>'])
  2042. else if coBuildAll in Options then
  2043. Mark(nUnitNeedsCompileDueToOption,[aFile.GetModuleName,'-B'])
  2044. else if AllJSIntoMainJS then
  2045. Mark(nUnitNeedsCompileDueToOption,[aFile.GetModuleName,'-Jc'])
  2046. else if (aFile.JSFilename<>'') and (not FS.FileExists(aFile.JSFilename)) then
  2047. Mark(nUnitNeedsCompileJSMissing,[aFile.GetModuleName,FormatPath(aFile.JSFilename)])
  2048. else if (aFile.JSFilename<>'')
  2049. and FS.File1IsNewer(aFile.UnitFilename,aFile.JSFilename) then
  2050. begin
  2051. Mark(nUnitNeedsCompilePasHasChanged,[aFile.GetModuleName,FullFormatPath(aFile.JSFilename)])
  2052. end;
  2053. end;
  2054. if aFile.NeedBuild then
  2055. begin
  2056. // unit needs compile
  2057. if aFile.IsForeign then
  2058. begin
  2059. // ... but is forbidden to compile
  2060. Log.LogMsg(nOptionForbidsCompile,[aFile.GetModuleName]);
  2061. Terminate(ExitCodeWriteError);
  2062. end;
  2063. end;
  2064. Result:=aFile.NeedBuild;
  2065. end;
  2066. function TPas2jsCompiler.CreateOptimizer: TPas2JSAnalyzer;
  2067. begin
  2068. Result:=TPas2JSAnalyzer.Create;
  2069. end;
  2070. procedure TPas2jsCompiler.OptimizeProgram(aFile: TPas2jsCompilerFile);
  2071. begin
  2072. if not AllJSIntoMainJS then exit;
  2073. if coKeepNotUsedDeclarationsWPO in Options then exit;
  2074. if not (aFile.PasModule is TPasProgram) then exit;
  2075. FWPOAnalyzer:=CreateOptimizer;
  2076. FWPOAnalyzer.Resolver:=aFile.PascalResolver;
  2077. FWPOAnalyzer.Options:=FWPOAnalyzer.Options+[paoOnlyExports];
  2078. FWPOAnalyzer.AnalyzeWholeProgram(TPasProgram(aFile.PasModule));
  2079. end;
  2080. procedure TPas2jsCompiler.CreateJavaScript(aFile: TPas2jsCompilerFile;
  2081. Checked: TPasAnalyzerKeySet);
  2082. procedure CheckUsesClause(UsesClause: TPasUsesClause);
  2083. var
  2084. i: Integer;
  2085. UsedFile: TPas2jsCompilerFile;
  2086. aModule: TPasModule;
  2087. begin
  2088. if length(UsesClause)=0 then exit;
  2089. for i:=0 to length(UsesClause)-1 do begin
  2090. aModule:=UsesClause[i].Module as TPasModule;
  2091. UsedFile:=TPas2jsCompilerFile.GetFile(aModule);
  2092. if UsedFile=nil then
  2093. RaiseInternalError(20171214121720,aModule.Name);
  2094. CreateJavaScript(UsedFile,Checked);
  2095. end;
  2096. end;
  2097. begin
  2098. //writeln('TPas2jsCompiler.CreateJavaScript ',aFile.UnitFilename,' JS=',GetObjName(aFile.JSModule),' Need=',aFile.NeedBuild);
  2099. if aFile.JSModule<>nil then exit; // already created
  2100. // check each file only once
  2101. if Checked.ContainsItem(aFile) then exit;
  2102. Checked.Add(aFile);
  2103. // emit module hints
  2104. aFile.EmitModuleHints;
  2105. if not aFile.NeedBuild then exit;
  2106. Log.LogMsg(nCompilingFile,[FullFormatPath(aFile.UnitFilename)],'',0,0,
  2107. not (coShowLineNumbers in Options));
  2108. // convert dependencies
  2109. CheckUsesClause(aFile.GetPasMainUsesClause);
  2110. CheckUsesClause(aFile.GetPasImplUsesClause);
  2111. aFile.CreateJS;
  2112. end;
  2113. procedure TPas2jsCompiler.FinishSrcMap(SrcMap: TPas2JSSrcMap);
  2114. var
  2115. LocalFilename, MapFilename, BaseDir: String;
  2116. aFile: TPas2jsFile;
  2117. i: Integer;
  2118. begin
  2119. if SrcMapBaseDir<>'' then
  2120. BaseDir:=SrcMapBaseDir
  2121. else
  2122. BaseDir:=ExtractFilePath(ExtractFilePath(SrcMap.LocalFilename));
  2123. for i:=0 to SrcMap.SourceCount-1 do begin
  2124. LocalFilename:=SrcMap.SourceFiles[i];
  2125. if LocalFilename='' then continue;
  2126. if SrcMapInclude and FS.FileExists(LocalFilename) then
  2127. begin
  2128. // include source in SrcMap
  2129. aFile:=LoadFile(LocalFilename);
  2130. SrcMap.SourceContents[i]:=aFile.Source;
  2131. end;
  2132. // translate local file name
  2133. MapFilename:=LocalFilename;
  2134. if (BaseDir<>'') and not SrcMapFilenamesAbsolute then
  2135. begin
  2136. if not FS.TryCreateRelativePath(LocalFilename,BaseDir,true,false,MapFilename) then
  2137. begin
  2138. // e.g. file is on another partition
  2139. if not SrcMapInclude then
  2140. begin
  2141. Log.Log(mtError,
  2142. SafeFormat(sUnableToTranslatePathToDir,[QuoteStr(LocalFilename),QuoteStr(BaseDir)]),
  2143. nUnableToTranslatePathToDir);
  2144. Terminate(ExitCodeConverterError);
  2145. end;
  2146. // the source is included, do not translate the filename
  2147. MapFilename:=LocalFilename;
  2148. end;
  2149. end;
  2150. if FilenameIsAbsolute(MapFilename)
  2151. and SameText(SrcMapSourceRoot,'file://') then
  2152. begin
  2153. // Firefox needs the "file://" schema with every file
  2154. MapFilename:='file://'+MapFilename;
  2155. end;
  2156. {$IFNDEF Unix}
  2157. // use / as PathDelim
  2158. if PathDelim<>'/' then
  2159. MapFilename:=StringReplace(MapFilename,PathDelim,'/',[rfReplaceAll]);
  2160. {$ENDIF}
  2161. if LocalFilename<>MapFilename then
  2162. SrcMap.SourceTranslatedFiles[i]:=MapFilename;
  2163. end;
  2164. end;
  2165. function TPas2jsCompiler.DoWriteJSFile(const DestFilename, MapFilename: String;
  2166. aWriter: TPas2JSMapper): Boolean;
  2167. begin
  2168. Result:=False;
  2169. if DestFilename='' then ;
  2170. if MapFilename='' then ;
  2171. if aWriter=nil then ;
  2172. end;
  2173. function TPas2jsCompiler.CreateJSWriter(aFileWriter: TPas2JSMapper): TJSWriter;
  2174. begin
  2175. Result:=TJSWriter.Create(aFileWriter);
  2176. end;
  2177. function TPas2jsCompiler.CreateJSMapper: TPas2JSMapper;
  2178. begin
  2179. Result:=TPas2JSMapper.Create(4096);
  2180. end;
  2181. function TPas2jsCompiler.CreateSrcMap(const aFileName: String): TPas2JSSrcMap;
  2182. begin
  2183. Result:=TPas2JSSrcMap.Create(aFileName);
  2184. end;
  2185. function TPas2jsCompiler.CreateFileWriter(aFile: TPas2jsCompilerFile;
  2186. const aFilename: string): TPas2JSMapper;
  2187. var
  2188. SrcMap: TPas2JSSrcMap;
  2189. DestFileName : String;
  2190. begin
  2191. DestFileName:=AFileName;
  2192. if DestFileName='' then
  2193. DestFileName:=aFile.JSFilename;
  2194. Result:=CreateJSMapper;
  2195. Result.DestFileName:=DestFileName;
  2196. if SrcMapEnable then
  2197. begin
  2198. SrcMap:=CreateSrcMap(ExtractFilename(DestFilename));
  2199. Result.SrcMap:=SrcMap;
  2200. SrcMap.Release;// release the refcount from the Create
  2201. SrcMap.SourceRoot:=SrcMapSourceRoot;
  2202. SrcMap.LocalFilename:=aFile.JSFilename;
  2203. if SrcMapXSSIHeader then
  2204. SrcMap.Options:=SrcMap.Options+[smoSafetyHeader]
  2205. else
  2206. SrcMap.Options:=SrcMap.Options-[smoSafetyHeader];
  2207. SrcMap.Options:=SrcMap.Options+[smoAllowSrcLine0];
  2208. end;
  2209. end;
  2210. procedure TPas2jsCompiler.HandleLinkLibStatement(Sender: TObject; const aLibName, aLibAlias, aLibOptions: String;
  2211. var Handled: boolean);
  2212. Var
  2213. Imp : TJSImportStatement;
  2214. PasLib : TJSSimpleAssignStatement;
  2215. dmAlias,dmimp : TJSDotMemberExpression;
  2216. pePas,peAlias : TJSPrimaryExpressionIdent;
  2217. LibModuleName : String;
  2218. begin
  2219. Handled:=true;
  2220. if aLibOptions<>'' then
  2221. ParamFatal('[20210919141030] linklib options not supported');
  2222. Imp:=CreateImportStatement;
  2223. Imp.NameSpaceImport:=TJSString(aLibAlias);
  2224. LibModuleName:=aLibName;
  2225. if ExtractFileExt(LibModuleName)='' then
  2226. LibModuleName:=LibModuleName+'.js';
  2227. Imp.ModuleName:=TJSString(LibModuleName);
  2228. // pas.$imports.libalias:=libalias
  2229. // LHS
  2230. pePas:=TJSPrimaryExpressionIdent.Create(0,0,'');
  2231. pePas.Name:='pas';
  2232. dmImp:=TJSDotMemberExpression.Create(0,0,'');
  2233. dmImp.Name:='$libimports';
  2234. dmImp.MExpr:=pePas;
  2235. dmAlias:=TJSDotMemberExpression.Create(0,0,'');
  2236. dmAlias.Name:=TJSString(alibAlias);
  2237. dmAlias.MExpr:=dmImp;
  2238. peAlias:=TJSPrimaryExpressionIdent.Create(0,0,'');
  2239. peAlias.Name:=TJSString(aLibAlias);
  2240. // Put all together
  2241. PasLib:=TJSSimpleAssignStatement.Create(0,0,'');
  2242. PasLib.LHS:=dmAlias;
  2243. pasLib.Expr:=peAlias;
  2244. // Add to statements
  2245. FImports.Statements.AddNode.Node:=Imp;
  2246. FImports.Statements.AddNode.Node:=PasLib;
  2247. end;
  2248. procedure TPas2jsCompiler.EmitJavaScript(aFile: TPas2jsCompilerFile;
  2249. aFileWriter: TPas2JSMapper);
  2250. var
  2251. aJSWriter: TJSWriter;
  2252. begin
  2253. // write JavaScript
  2254. aJSWriter:=CreateJSWriter(aFileWriter);
  2255. try
  2256. aJSWriter.Options:=DefaultJSWriterOptions;
  2257. aJSWriter.IndentSize:=2;
  2258. try
  2259. aJSWriter.WriteJS(aFile.JSModule);
  2260. except
  2261. on E: Exception do begin
  2262. if ShowDebug then
  2263. Log.LogExceptionBackTrace(E);
  2264. Log.LogPlain('[20180204193420] Error while creating JavaScript '+FullFormatPath(aFileWriter.DestFilename)+': '+E.Message);
  2265. Terminate(ExitCodeErrorInternal);
  2266. end
  2267. {$IFDEF Pas2js}
  2268. else HandleJSException('[20181031190520] TPas2jsCompiler.WriteJSFiles Error while creating JavaScript',JSExceptValue);
  2269. {$ENDIF}
  2270. end;
  2271. Finally
  2272. aJSWriter.Free;
  2273. end;
  2274. end;
  2275. procedure TPas2jsCompiler.WriteJSToFile(const MapFileName: string;
  2276. aFileWriter: TPas2JSMapper);
  2277. Var
  2278. {$IFDEF Pas2js}
  2279. buf: TJSArray;
  2280. {$ELSE}
  2281. buf: TMemoryStream;
  2282. {$ENDIF}
  2283. WithUTF8BOM: Boolean;
  2284. begin
  2285. // write js
  2286. try
  2287. {$IFDEF Pas2js}
  2288. buf:=TJSArray.new;
  2289. {$ELSE}
  2290. buf:=TMemoryStream.Create;
  2291. {$ENDIF}
  2292. try
  2293. WithUTF8BOM:=(Log.Encoding='') or (Log.Encoding='utf8');
  2294. aFileWriter.SaveJSToStream(WithUTF8BOM,ExtractFilename(MapFilename),buf);
  2295. {$IFDEF Pas2js}
  2296. {$ELSE}
  2297. buf.Position:=0;
  2298. {$ENDIF}
  2299. FS.SaveToFile(buf,aFileWriter.DestFilename);
  2300. finally
  2301. {$IFDEF Pas2js}
  2302. buf:=nil;
  2303. {$ELSE}
  2304. buf.Free;
  2305. {$ENDIF}
  2306. end;
  2307. except
  2308. on E: Exception do begin
  2309. if ShowDebug then
  2310. Log.LogExceptionBackTrace(E);
  2311. {$IFDEF FPC}
  2312. if E.Message<>SafeFormat(SFCreateError,[aFileWriter.DestFileName]) then
  2313. {$ENDIF}
  2314. Log.LogPlain('Error: '+E.Message);
  2315. Log.LogMsg(nUnableToWriteFile,[FullFormatPath(aFileWriter.DestFilename)]);
  2316. Terminate(ExitCodeWriteError);
  2317. end
  2318. {$IFDEF Pas2js}
  2319. else HandleJSException('[20181031190637] TPas2jsCompiler.WriteJSFiles',JSExceptValue,true);
  2320. {$ENDIF}
  2321. end;
  2322. end;
  2323. procedure TPas2jsCompiler.WriteSrcMap(const MapFileName: string;
  2324. aFileWriter: TPas2JSMapper);
  2325. Var
  2326. {$IFDEF Pas2js}
  2327. buf: TJSArray;
  2328. {$ELSE}
  2329. buf: TMemoryStream;
  2330. {$ENDIF}
  2331. begin
  2332. Log.LogMsg(nWritingFile,[FullFormatPath(MapFilename)],'',0,0,
  2333. not (coShowLineNumbers in Options));
  2334. FinishSrcMap(aFileWriter.SrcMap);
  2335. try
  2336. {$IFDEF Pas2js}
  2337. buf:=TJSArray.new;
  2338. {$ELSE}
  2339. buf:=TMemoryStream.Create;
  2340. {$ENDIF}
  2341. try
  2342. // Note: No UTF-8 BOM in source map, Chrome 59 gives an error
  2343. aFileWriter.SrcMap.SaveToStream(buf);
  2344. {$IFDEF Pas2js}
  2345. {$ELSE}
  2346. buf.Position:=0;
  2347. {$ENDIF}
  2348. FS.SaveToFile(buf,MapFilename);
  2349. finally
  2350. {$IFDEF Pas2js}
  2351. buf:=nil;
  2352. {$ELSE}
  2353. buf.Free;
  2354. {$ENDIF}
  2355. end;
  2356. except
  2357. on E: Exception do begin
  2358. if ShowDebug then
  2359. Log.LogExceptionBackTrace(E);
  2360. {$IFDEF FPC}
  2361. if E.Message<>SafeFormat(SFCreateError,[aFileWriter.DestFileName]) then
  2362. {$ENDIF}
  2363. Log.LogPlain('Error: '+E.Message);
  2364. Log.LogMsg(nUnableToWriteFile,[FullFormatPath(MapFilename)]);
  2365. Terminate(ExitCodeWriteError);
  2366. end
  2367. {$IFDEF Pas2js}
  2368. else HandleJSException('[20181031190737] TPas2jsCompiler.WriteJSFiles',JSExceptValue);
  2369. {$ENDIF}
  2370. end;
  2371. end;
  2372. procedure TPas2jsCompiler.AddUnitResourceStrings(aFile : TPas2jsCompilerFile);
  2373. Var
  2374. ResList : TFPList;
  2375. Procedure AddToList(aList : TFPList);
  2376. var
  2377. I : integer;
  2378. begin
  2379. For I:=0 to aList.Count-1 do
  2380. ResList.Add(aList[i]);
  2381. end;
  2382. Procedure AddUsedToList(aList : TFPList);
  2383. var
  2384. I : integer;
  2385. begin
  2386. For I:=0 to aList.Count-1 do
  2387. if aFile.UseAnalyzer.IsUsed(TPasElement(aList[i])) then
  2388. ResList.Add(aList[i]);
  2389. end;
  2390. Procedure CheckSection(aSection : TPasSection);
  2391. begin
  2392. if not (Assigned(aSection) and Assigned(aSection.ResStrings)) then
  2393. exit;
  2394. if FResourceStringFile=rsfProgram then
  2395. AddUsedToList(aSection.ResStrings)
  2396. else
  2397. AddToList(aSection.ResStrings);
  2398. end;
  2399. Var
  2400. I : Integer;
  2401. Res : TPasResString;
  2402. aValue : TResEvalValue;
  2403. begin
  2404. if FResourceStringFile=rsfUnit then
  2405. FResourceStrings.Clear;
  2406. ResList:=TFPList.Create;
  2407. try
  2408. // Program ?
  2409. if aFile.pasModule is TPasProgram then
  2410. CheckSection(TPasProgram(aFile.pasModule).ProgramSection)
  2411. else if aFile.pasModule is TPasLibrary then // Library ?
  2412. CheckSection(TPasLibrary(aFile.pasModule).LibrarySection)
  2413. else
  2414. begin
  2415. // Interface
  2416. CheckSection(aFile.PasModule.InterfaceSection);
  2417. // Implementation
  2418. CheckSection(aFile.PasModule.ImplementationSection);
  2419. end;
  2420. // Now add to file
  2421. if ResList.Count>0 then
  2422. begin
  2423. FResourceStrings.StartUnit(aFile.GetModuleName);
  2424. For I:=0 to ResList.Count-1 do
  2425. begin
  2426. Res:=TPasResString(ResList[i]);
  2427. aValue:=aFile.PascalResolver.Eval(Res.Expr,[refConst],False);
  2428. case aValue.Kind of
  2429. {$IFDEF FPC_HAS_CPSTRING}
  2430. revkString:
  2431. FResourceStrings.AddString(Res.Name,TResEvalString(aValue).S);
  2432. {$ENDIF}
  2433. revkUnicodeString:
  2434. FResourceStrings.AddString(Res.Name,TJSONStringType(TResEvalUTF16(aValue).S))
  2435. else
  2436. Log.Log(mtNote,sSkipNoConstResourcestring,nSkipNoConstResourcestring,aFile.PasFileName);
  2437. end;
  2438. ReleaseEvalValue(aValue);
  2439. end;
  2440. end;
  2441. finally
  2442. ResList.Free;
  2443. end;
  2444. end;
  2445. procedure TPas2jsCompiler.WriteResourceStrings(aFileName : String);
  2446. Var
  2447. {$IFDEF Pas2js}
  2448. buf: TJSArray;
  2449. {$ELSE}
  2450. buf: TMemoryStream;
  2451. {$ENDIF}
  2452. S : TJSONStringType;
  2453. begin
  2454. Log.LogMsg(nWritingFile,[FullFormatPath(aFilename)],'',0,0,False);
  2455. try
  2456. {$IFDEF Pas2js}
  2457. buf:=TJSArray.new;
  2458. {$ELSE}
  2459. buf:=TMemoryStream.Create;
  2460. {$ENDIF}
  2461. try
  2462. // Note: No UTF-8 BOM in source map, Chrome 59 gives an error
  2463. S:=FResourceStrings.AsString;
  2464. {$ifdef pas2js}
  2465. buf.push(S);
  2466. {$else}
  2467. buf.Write(S[1],length(S));
  2468. {$endif}
  2469. FS.SaveToFile(buf,aFilename);
  2470. finally
  2471. {$IFDEF Pas2js}
  2472. buf:=nil;
  2473. {$ELSE}
  2474. buf.Free;
  2475. {$ENDIF}
  2476. end;
  2477. except
  2478. on E: Exception do begin
  2479. if ShowDebug then
  2480. Log.LogExceptionBackTrace(E);
  2481. {$IFDEF FPC}
  2482. if E.Message<>SafeFormat(SFCreateError,[aFileName]) then
  2483. {$ENDIF}
  2484. Log.LogPlain('Error: '+E.Message);
  2485. Log.LogMsg(nUnableToWriteFile,[FullFormatPath(aFilename)]);
  2486. Terminate(ExitCodeWriteError);
  2487. end
  2488. {$IFDEF Pas2js}
  2489. else HandleJSException('[20181031190737] TPas2jsCompiler.WriteJSFiles',JSExceptValue);
  2490. {$ENDIF}
  2491. end;
  2492. end;
  2493. procedure TPas2jsCompiler.WriteResources(aFileName: String);
  2494. Var
  2495. {$IFDEF Pas2js}
  2496. buf: TJSArray;
  2497. {$ELSE}
  2498. buf: TMemoryStream;
  2499. {$ENDIF}
  2500. S : TJSONStringType;
  2501. begin
  2502. Log.LogMsg(nWritingFile,[FullFormatPath(aFilename)],'',0,0,False);
  2503. try
  2504. {$IFDEF Pas2js}
  2505. buf:=TJSArray.new;
  2506. {$ELSE}
  2507. buf:=TMemoryStream.Create;
  2508. {$ENDIF}
  2509. try
  2510. S:=FResources.AsString;
  2511. {$ifdef pas2js}
  2512. buf.push(S);
  2513. {$else}
  2514. buf.Write(S[1],length(S));
  2515. {$endif}
  2516. FS.SaveToFile(buf,aFilename);
  2517. finally
  2518. {$IFDEF Pas2js}
  2519. buf:=nil;
  2520. {$ELSE}
  2521. buf.Free;
  2522. {$ENDIF}
  2523. end;
  2524. except
  2525. on E: Exception do begin
  2526. if ShowDebug then
  2527. Log.LogExceptionBackTrace(E);
  2528. {$IFDEF FPC}
  2529. if E.Message<>SafeFormat(SFCreateError,[aFileName]) then
  2530. {$ENDIF}
  2531. Log.LogPlain('Error: '+E.Message);
  2532. Log.LogMsg(nUnableToWriteFile,[FullFormatPath(aFilename)]);
  2533. Terminate(ExitCodeWriteError);
  2534. end
  2535. {$IFDEF Pas2js}
  2536. else HandleJSException('[20181031190737] TPas2jsCompiler.WriteJSFiles',JSExceptValue);
  2537. {$ENDIF}
  2538. end;
  2539. end;
  2540. procedure TPas2jsCompiler.WriteSingleJSFile(aFile: TPas2jsCompilerFile; CombinedFileWriter: TPas2JSMapper);
  2541. Procedure WriteToStandardOutput(aFileWriter : TPas2JSMapper);
  2542. begin
  2543. // write to stdout
  2544. {$IFDEF HasStdErr}
  2545. Log.WriteMsgToStdErr:=false;
  2546. {$ENDIF}
  2547. try
  2548. Log.LogRaw(aFileWriter.AsString);
  2549. finally
  2550. {$IFDEF HasStdErr}
  2551. Log.WriteMsgToStdErr:=coWriteMsgToStdErr in Options;
  2552. {$ENDIF}
  2553. end;
  2554. end;
  2555. Procedure CheckOutputDir(Const DestFileName : String);
  2556. Var
  2557. DestDir : String;
  2558. begin
  2559. // check output directory
  2560. DestDir:=ChompPathDelim(ExtractFilePath(DestFilename));
  2561. if (DestDir<>'') and not FS.DirectoryExists(DestDir) then
  2562. begin
  2563. Log.LogMsg(nOutputDirectoryNotFound,[FullFormatPath(DestDir)]);
  2564. Terminate(ExitCodeFileNotFound);
  2565. end;
  2566. if FS.DirectoryExists(DestFilename) then
  2567. begin
  2568. Log.LogMsg(nFileIsFolder,[FullFormatPath(DestFilename)]);
  2569. Terminate(ExitCodeWriteError);
  2570. end;
  2571. end;
  2572. Var
  2573. aFileWriter: TPas2JSMapper;
  2574. isSingleFile, JSFileWritten: Boolean;
  2575. ResFileName,MapFilename: String;
  2576. begin
  2577. aFileWriter:=CombinedFileWriter;
  2578. try
  2579. isSingleFile:=aFileWriter=nil;
  2580. if isSingleFile then
  2581. // create local writer for this file
  2582. begin
  2583. aFileWriter:=CreateFileWriter(aFile,'');
  2584. if aFile.IsMainFile and Not AllJSIntoMainJS then
  2585. begin
  2586. InsertCustomJSFiles(aFileWriter);
  2587. if FResources.OutputMode=romExtraJS then
  2588. aFileWriter.WriteFile(FResources.AsString,GetResolvedMainJSFile);
  2589. end;
  2590. end;
  2591. if FResourceStringFile<>rsfNone then
  2592. AddUnitResourceStrings(aFile);
  2593. FResources.DoneUnit(aFile.isMainFile);
  2594. EmitJavaScript(aFile,aFileWriter);
  2595. if isSingleFile or aFile.isMainFile then
  2596. begin
  2597. if aFile.IsMainFile then
  2598. AppendCustomJSFiles(aFileWriter);
  2599. if Assigned(PostProcessorSupport) then
  2600. PostProcessorSupport.CallPostProcessors(aFile.JSFilename,aFileWriter);
  2601. if SrcMapEnable then
  2602. MapFilename:=aFileWriter.DestFilename+'.map'
  2603. else
  2604. MapFilename:='';
  2605. CheckOutputDir(aFileWriter.DestFileName);
  2606. // Give chance to descendants to write file
  2607. JSFileWritten:=DoWriteJSFile(aFile.JSFilename,MapFilename,aFileWriter);
  2608. if (aFile.JSFilename='') and (MainJSFile='.') then
  2609. WriteToStandardOutput(aFileWriter);
  2610. //writeln('TPas2jsCompiler.WriteJSFiles ',aFile.UnitFilename,' ',aFile.JSFilename);
  2611. Log.LogMsg(nWritingFile,[FullFormatPath(aFileWriter.DestFilename)],'',0,0, not (coShowLineNumbers in Options));
  2612. if not JSFileWritten then
  2613. WriteJSToFile(MapFileName,aFileWriter);
  2614. if (FResourceStringFile=rsfUnit) or (aFile.IsMainFile and (FResourceStringFile<>rsfNone)) then
  2615. if FResourceStrings.StringsCount>0 then
  2616. WriteResourceStrings(ChangeFileExt(aFileWriter.DestFileName,'.jrs'));
  2617. // Writeln('IsSingleFile ',isSingleFile,' mainfile: ',aFile.IsMainFile,' filename: ', aFileWriter.DestFileName);
  2618. if aFile.isMainFile and (FResources.OutputMode=romFile) and (FResources.ResourceCount>0) then
  2619. begin
  2620. ResFileName:=FResourceOutputFile;
  2621. if ResFileName='' then
  2622. // default is projectname-res.ext, to avoid projectname.html, used in web projects in Lazarus IDE
  2623. ResFileName:=ChangeFileExt(aFileWriter.DestFileName,'-res'+FResources.OutputFileExtension);
  2624. WriteResources(ResFileName);
  2625. end;
  2626. // write source map
  2627. if aFileWriter.SrcMap<>nil then
  2628. WriteSrcMap(MapFileName,aFileWriter);
  2629. end;
  2630. finally
  2631. if isSingleFile then
  2632. aFileWriter.Free;
  2633. end;
  2634. end;
  2635. procedure TPas2jsCompiler.InsertImportSection(aFileWriter : TPas2JSMapper);
  2636. var
  2637. aJSWriter: TJSWriter;
  2638. begin
  2639. if FImports.Statements.Count=0 then
  2640. exit;
  2641. // write JavaScript
  2642. aJSWriter:=CreateJSWriter(aFileWriter);
  2643. try
  2644. aJSWriter.Options:=DefaultJSWriterOptions;
  2645. aJSWriter.IndentSize:=2;
  2646. try
  2647. aJSWriter.WriteJS(FImports);
  2648. except
  2649. on E: Exception do begin
  2650. if ShowDebug then
  2651. Log.LogExceptionBackTrace(E);
  2652. Log.LogPlain('[20210911104700] Error while creating JavaScript '+FullFormatPath(aFileWriter.DestFilename)+': '+E.Message);
  2653. Terminate(ExitCodeErrorInternal);
  2654. end
  2655. {$IFDEF Pas2js}
  2656. else HandleJSException('[20210911104700] TPas2jsCompiler.WriteJSFiles Error while creating JavaScript',JSExceptValue);
  2657. {$ENDIF}
  2658. end;
  2659. Finally
  2660. aJSWriter.Free;
  2661. end;
  2662. end;
  2663. procedure TPas2jsCompiler.WriteJSFiles(aFile: TPas2jsCompilerFile; CombinedFileWriter: TPas2JSMapper; Checked: TPasAnalyzerKeySet);
  2664. procedure CheckUsesClause(aFileWriter: TPas2JSMapper; UsesClause: TPasUsesClause);
  2665. var
  2666. i: Integer;
  2667. UsedFile: TPas2jsCompilerFile;
  2668. aModule: TPasModule;
  2669. begin
  2670. if length(UsesClause)=0 then exit;
  2671. for i:=0 to length(UsesClause)-1 do begin
  2672. aModule:=UsesClause[i].Module as TPasModule;
  2673. UsedFile:=TPas2jsCompilerFile.GetFile(aModule);
  2674. if UsedFile=nil then
  2675. RaiseInternalError(20171214121720,aModule.Name);
  2676. WriteJSFiles(UsedFile,aFileWriter,Checked);
  2677. end;
  2678. end;
  2679. Var
  2680. aFileWriter : TPas2JSMapper;
  2681. begin
  2682. // writeln('TPas2jsCompiler.WriteJSFiles START ',aFile.UnitFilename,' Need=',aFile.NeedBuild,' Checked=',Checked.ContainsItem(aFile),' JSModule=',GetObjName(aFile.JSModule));
  2683. if (aFile.JSModule=nil) or (not aFile.NeedBuild) then exit;
  2684. // check each file only once
  2685. if Checked.ContainsItem(aFile) then exit;
  2686. Checked.Add(aFile);
  2687. aFileWriter:=CombinedFileWriter;
  2688. if AllJSIntoMainJS and (aFileWriter=nil) then
  2689. begin
  2690. // create CombinedFileWriter
  2691. aFileWriter:=CreateFileWriter(aFile,GetResolvedMainJSFile);
  2692. InsertCustomJSFiles(aFileWriter);
  2693. if TargetPlatform in [PlatformNodeJS,PlatformModule] then
  2694. InsertImportSection(aFileWriter);
  2695. if FResources.OutputMode=romExtraJS then
  2696. aFileWriter.WriteFile(FResources.AsString,GetResolvedMainJSFile);
  2697. end;
  2698. Try
  2699. // convert dependencies
  2700. CheckUsesClause(aFileWriter,aFile.GetPasMainUsesClause);
  2701. CheckUsesClause(aFileWriter,aFile.GetPasImplUsesClause);
  2702. // Write me...
  2703. WriteSingleJSFile(aFile,aFileWriter);
  2704. finally
  2705. if aFileWriter<>CombinedFileWriter then
  2706. aFileWriter.Free;
  2707. end;
  2708. end;
  2709. procedure TPas2jsCompiler.InitParamMacros;
  2710. begin
  2711. ParamMacros.AddValue('Pas2jsFullVersion','major.minor.release<extra>',GetVersion(false));
  2712. ParamMacros.AddValue('Pas2jsVersion','major.minor.release',GetVersion(true));
  2713. ParamMacros.AddFunction('CfgDir','Use within a config file. The directory of this config file',@OnMacroCfgDir,false);
  2714. // Additionally, under windows the following special variables are recognized:
  2715. { ToDo:
  2716. LOCAL_APPDATA
  2717. Usually the directory ”Local settings/Application Data” under the user’s home directory.
  2718. APPDATA
  2719. Usually the directory ”Application Data” under the user’s home directory.
  2720. COMMON_APPDATA
  2721. Usually the directory ”Application Data” under the ’All users’ directory.
  2722. PERSONAL
  2723. Usually the ”My documents” directory of the user.
  2724. PROGRAM_FILES
  2725. Usually ”program files” directory on the system drive
  2726. PROGRAM_FILES_COMMON
  2727. Usually the ”Common files” directory under the program files directory.
  2728. PROFILE
  2729. The user’s home directory. }
  2730. end;
  2731. procedure TPas2jsCompiler.ClearDefines;
  2732. var
  2733. i: Integer;
  2734. M: TMacroDef;
  2735. begin
  2736. for i:=0 to FDefines.Count-1 do
  2737. begin
  2738. M:=TMacroDef(FDefines.Objects[i]);
  2739. M.Free;
  2740. end;
  2741. FDefines.Clear;
  2742. end;
  2743. procedure TPas2jsCompiler.RaiseInternalError(id: TMaxPrecInt; Msg: string);
  2744. begin
  2745. Log.LogPlain('['+IntToStr(id)+'] '+Msg);
  2746. raise Exception.Create(Msg);
  2747. end;
  2748. {$IFDEF Pas2js}
  2749. procedure TPas2jsCompiler.HandleJSException(Msg: string; E: jsvalue;
  2750. TerminateInternal: boolean);
  2751. var
  2752. obj: JS.TJSObject;
  2753. Exc: Exception;
  2754. begin
  2755. if isObject(E) then
  2756. begin
  2757. obj:=js.TJSObject(E);
  2758. if isExt(obj,TJSError) then
  2759. begin
  2760. {AllowWriteln}
  2761. if obj['stack'] then
  2762. writeln(obj['stack']);
  2763. {AllowWriteln-}
  2764. Log.Log(mtFatal,Msg+': '+String(obj['message']));
  2765. end else if isExt(obj,TObject) then
  2766. begin
  2767. if TObject(obj) is Exception then
  2768. begin
  2769. Exc:=Exception(TObject(obj));
  2770. {$ifdef NodeJS}
  2771. {AllowWriteln}
  2772. if Exc.NodeJSError<>nil then
  2773. writeln(Exc.NodeJSError.stack);
  2774. {AllowWriteln-}
  2775. {$endif}
  2776. Log.Log(mtFatal,Msg+': ('+Exc.ClassName+') '+Exc.Message);
  2777. end else begin
  2778. Log.Log(mtFatal,Msg+': ('+TObject(obj).ClassName+')');
  2779. end;
  2780. end else
  2781. Log.Log(mtFatal,Msg+': '+String(E));
  2782. end else begin
  2783. Log.Log(mtFatal,Msg+': '+String(E));
  2784. end;
  2785. if TerminateInternal then
  2786. Terminate(ExitCodeErrorInternal);
  2787. end;
  2788. {$ENDIF}
  2789. function TPas2jsCompiler.GetExitCode: Longint;
  2790. begin
  2791. Result:=System.ExitCode;
  2792. end;
  2793. procedure TPas2jsCompiler.SetExitCode(Value: Longint);
  2794. begin
  2795. System.ExitCode:=Value;
  2796. end;
  2797. procedure TPas2jsCompiler.SetWorkingDir(const aDir: String);
  2798. begin
  2799. // Do nothing
  2800. if aDir='' then ;
  2801. end;
  2802. procedure TPas2jsCompiler.CreateResourceSupport;
  2803. begin
  2804. Case FResourceMode of
  2805. rmNone : FResources:=TNoResources.Create(FS);
  2806. rmHTML : FResources:=THTMLResourceLinkHandler.Create(FS);
  2807. rmJS : FResources:=TJSResourceHandler.Create(FS);
  2808. end;
  2809. end;
  2810. procedure TPas2jsCompiler.Terminate(TheExitCode: integer);
  2811. begin
  2812. ExitCode:=TheExitCode;
  2813. if Log<>nil then Log.Flush;
  2814. raise ECompilerTerminate.Create('TPas2jsCompiler.Terminate');
  2815. end;
  2816. function TPas2jsCompiler.GetShowDebug: boolean;
  2817. begin
  2818. Result:=coShowDebug in Options;
  2819. end;
  2820. function TPas2jsCompiler.GetShowFullPaths: boolean;
  2821. begin
  2822. Result:=FS.ShowFullPaths;
  2823. end;
  2824. function TPas2jsCompiler.GetShowLogo: Boolean;
  2825. begin
  2826. Result:=coShowLogo in FOptions;
  2827. end;
  2828. function TPas2jsCompiler.GetShowTriedUsedFiles: boolean;
  2829. begin
  2830. Result:=coShowTriedUsedFiles in FOptions;
  2831. end;
  2832. function TPas2jsCompiler.GetShowUsedTools: boolean;
  2833. begin
  2834. Result:=coShowUsedTools in Options;
  2835. end;
  2836. function TPas2jsCompiler.GetSkipDefaultConfig: Boolean;
  2837. begin
  2838. Result:=coSkipDefaultConfigs in FOptions;
  2839. end;
  2840. function TPas2jsCompiler.GetSrcMapEnable: boolean;
  2841. begin
  2842. Result:=coSourceMapCreate in FOptions;
  2843. end;
  2844. function TPas2jsCompiler.GetSrcMapInclude: boolean;
  2845. begin
  2846. Result:=coSourceMapInclude in FOptions;
  2847. end;
  2848. function TPas2jsCompiler.GetSrcMapFilenamesAbsolute: boolean;
  2849. begin
  2850. Result:=coSourceMapFilenamesAbsolute in FOptions;
  2851. end;
  2852. function TPas2jsCompiler.GetSrcMapXSSIHeader: boolean;
  2853. begin
  2854. Result:=coSourceMapXSSIHeader in FOptions;
  2855. end;
  2856. function TPas2jsCompiler.GetTargetPlatform: TPasToJsPlatform;
  2857. begin
  2858. Result:=FConverterGlobals.TargetPlatform;
  2859. end;
  2860. function TPas2jsCompiler.GetTargetProcessor: TPasToJsProcessor;
  2861. begin
  2862. Result:=FConverterGlobals.TargetProcessor;
  2863. end;
  2864. function TPas2jsCompiler.GetWriteDebugLog: boolean;
  2865. begin
  2866. Result:=coWriteDebugLog in FOptions;
  2867. end;
  2868. function TPas2jsCompiler.GetWriteMsgToStdErr: boolean;
  2869. begin
  2870. Result:=coWriteMsgToStdErr in FOptions;
  2871. end;
  2872. procedure TPas2jsCompiler.SetCompilerExe(AValue: string);
  2873. begin
  2874. if AValue<>'' then
  2875. AValue:=ExpandFileName(AValue);
  2876. if FCompilerExe=AValue then Exit;
  2877. FCompilerExe:=AValue;
  2878. end;
  2879. procedure TPas2jsCompiler.SetMode(AValue: TP2jsMode);
  2880. begin
  2881. SetModeSwitches(p2jsMode_SwitchSets[AValue]);
  2882. case AValue of
  2883. p2jmObjFPC: Options:=Options-[coAllowCAssignments];
  2884. p2jmDelphi: Options:=Options-[coAllowCAssignments];
  2885. end;
  2886. end;
  2887. procedure TPas2jsCompiler.SetModeSwitches(const AValue: TModeSwitches);
  2888. begin
  2889. if FModeSwitches=AValue then Exit;
  2890. FModeSwitches:=AValue;
  2891. end;
  2892. procedure TPas2jsCompiler.SetOptions(AValue: TP2jsCompilerOptions);
  2893. begin
  2894. if FOptions=AValue then Exit;
  2895. FOptions:=AValue;
  2896. Log.ShowMsgNumbers:=coShowMessageNumbers in FOptions;
  2897. Log.ShowMsgTypes:=GetShownMsgTypes;
  2898. FS.ShowTriedUsedFiles:=coShowTriedUsedFiles in FOptions;
  2899. end;
  2900. procedure TPas2jsCompiler.SetShowDebug(AValue: boolean);
  2901. begin
  2902. if AValue then
  2903. FOptions:=FOptions+[coShowNotes,coShowInfos,coShowDebug]
  2904. else
  2905. Exclude(FOptions,coShowNotes);
  2906. end;
  2907. procedure TPas2jsCompiler.SetShowFullPaths(AValue: boolean);
  2908. begin
  2909. FS.ShowFullPaths:=AValue;
  2910. end;
  2911. procedure TPas2jsCompiler.SetShowLogo(AValue: Boolean);
  2912. begin
  2913. SetOption(coShowLogo,AValue);
  2914. end;
  2915. procedure TPas2jsCompiler.SetShowTriedUsedFiles(AValue: boolean);
  2916. begin
  2917. FS.ShowTriedUsedFiles:=AValue;
  2918. SetOption(coShowTriedUsedFiles,AValue);
  2919. end;
  2920. procedure TPas2jsCompiler.SetShowUsedTools(AValue: boolean);
  2921. begin
  2922. SetOption(coShowUsedTools,AValue);
  2923. end;
  2924. procedure TPas2jsCompiler.SetSkipDefaultConfig(AValue: Boolean);
  2925. begin
  2926. SetOption(coSkipDefaultConfigs,AValue);
  2927. end;
  2928. procedure TPas2jsCompiler.SetSrcMapBaseDir(const AValue: string);
  2929. var
  2930. NewValue: String;
  2931. begin
  2932. NewValue:=FS.ExpandDirectory(AValue);
  2933. if FSrcMapBaseDir=NewValue then Exit;
  2934. FSrcMapBaseDir:=NewValue;
  2935. end;
  2936. procedure TPas2jsCompiler.SetSrcMapEnable(const AValue: boolean);
  2937. begin
  2938. SetOption(coSourceMapCreate,AValue);
  2939. end;
  2940. procedure TPas2jsCompiler.SetSrcMapInclude(const AValue: boolean);
  2941. begin
  2942. SetOption(coSourceMapInclude,AValue);
  2943. end;
  2944. procedure TPas2jsCompiler.SetSrcMapFilenamesAbsolute(const AValue: boolean);
  2945. begin
  2946. SetOption(coSourceMapFilenamesAbsolute,AValue);
  2947. end;
  2948. procedure TPas2jsCompiler.SetSrcMapXSSIHeader(const AValue: boolean);
  2949. begin
  2950. SetOption(coSourceMapXSSIHeader,AValue);
  2951. end;
  2952. procedure TPas2jsCompiler.SetTargetPlatform(const AValue: TPasToJsPlatform);
  2953. var
  2954. OldPlatform: TPasToJsPlatform;
  2955. begin
  2956. OldPlatform:=FConverterGlobals.TargetPlatform;
  2957. if OldPlatform=AValue then Exit;
  2958. RemoveDefine(PasToJsPlatformNames[OldPlatform]);
  2959. FConverterGlobals.TargetPlatform:=AValue;
  2960. if AValue in [PlatformNodeJS,PlatformModule] then
  2961. AllJSIntoMainJS:=true;
  2962. AddDefinesForTargetPlatform;
  2963. end;
  2964. procedure TPas2jsCompiler.SetTargetProcessor(const AValue: TPasToJsProcessor);
  2965. var
  2966. OldTargetProcessor: TPasToJsProcessor;
  2967. begin
  2968. OldTargetProcessor:=FConverterGlobals.TargetProcessor;
  2969. if OldTargetProcessor=AValue then Exit;
  2970. RemoveDefine(PasToJsProcessorNames[OldTargetProcessor]);
  2971. FConverterGlobals.TargetProcessor:=AValue;
  2972. AddDefinesForTargetProcessor;
  2973. end;
  2974. procedure TPas2jsCompiler.SetWriteDebugLog(const AValue: boolean);
  2975. begin
  2976. SetOption(coWriteDebugLog,AValue);
  2977. end;
  2978. procedure TPas2jsCompiler.SetWriteMsgToStdErr(const AValue: boolean);
  2979. begin
  2980. SetOption(coWriteMsgToStdErr,AValue);
  2981. {$IFDEF HasStdErr}
  2982. Log.WriteMsgToStdErr:=AValue;
  2983. {$ENDIF}
  2984. end;
  2985. procedure TPas2jsCompiler.AddDefinesForTargetPlatform;
  2986. begin
  2987. AddDefine(PasToJsPlatformNames[TargetPlatform]);
  2988. AddDefine('Pas2JSTargetOS',PasToJsPlatformNames[TargetPlatform]);
  2989. end;
  2990. procedure TPas2jsCompiler.AddDefinesForTargetProcessor;
  2991. begin
  2992. AddDefine(PasToJsProcessorNames[TargetProcessor]);
  2993. AddDefine('Pas2JSTargetCPU',PasToJsProcessorNames[TargetProcessor]);
  2994. case TargetProcessor of
  2995. ProcessorECMAScript5: AddDefine('ECMAScript', '5');
  2996. ProcessorECMAScript6: AddDefine('ECMAScript', '6');
  2997. end;
  2998. end;
  2999. procedure TPas2jsCompiler.AddReadingModule(aFile: TPas2jsCompilerFile);
  3000. begin
  3001. if FReadingModules.IndexOf(aFile)>=0 then
  3002. exit;
  3003. FReadingModules.Add(aFile);
  3004. end;
  3005. procedure TPas2jsCompiler.RemoveReadingModule(aFile: TPas2jsCompilerFile);
  3006. begin
  3007. FReadingModules.Remove(aFile);
  3008. end;
  3009. procedure TPas2jsCompiler.RegisterMessages;
  3010. var
  3011. LastMsgNumber: integer;
  3012. procedure r(MsgType: TMessageType; MsgNumber: integer; const MsgPattern: string);
  3013. var
  3014. s: String;
  3015. begin
  3016. if (LastMsgNumber>=0) and (MsgNumber<>LastMsgNumber+1) then
  3017. begin
  3018. if MsgNumber>LastMsgNumber+1 then
  3019. s:='TPas2jsCompiler.RegisterMessages: gap in registered message numbers: '+IntToStr(LastMsgNumber+1)+' '+IntToStr(MsgNumber)
  3020. else
  3021. s:='TPas2jsCompiler.RegisterMessages: not ascending order in registered message numbers: Last='+IntToStr(LastMsgNumber)+' New='+IntToStr(MsgNumber);
  3022. RaiseInternalError(20170504161422,s);
  3023. end;
  3024. Log.RegisterMsg(MsgType,MsgNumber,MsgPattern);
  3025. LastMsgNumber:=MsgNumber;
  3026. end;
  3027. begin
  3028. LastMsgNumber:=-1;
  3029. r(mtInfo,nOptionIsEnabled,sOptionIsEnabled);
  3030. r(mtInfo,nSyntaxModeIs,sSyntaxModeIs);
  3031. r(mtInfo,nModeswitchXisY,sModeswitchXisY);
  3032. LastMsgNumber:=-1; // was nMacroDefined 103
  3033. r(mtInfo,nUsingPath,sUsingPath);
  3034. r(mtNote,nFolderNotFound,sFolderNotFound);
  3035. r(mtInfo,nNameValue,sNameValue);
  3036. r(mtInfo,nReadingOptionsFromFile,sReadingOptionsFromFile);
  3037. r(mtInfo,nEndOfReadingConfigFile,sEndOfReadingConfigFile);
  3038. r(mtDebug,nInterpretingFileOption,sInterpretingFileOption);
  3039. r(mtFatal,nSourceFileNotFound,sSourceFileNotFound);
  3040. r(mtFatal,nFileIsFolder,sFileIsFolder);
  3041. r(mtInfo,nConfigFileSearch,sConfigFileSearch);
  3042. r(mtDebug,nHandlingOption,sHandlingOption);
  3043. r(mtDebug,nQuickHandlingOption,sQuickHandlingOption);
  3044. r(mtFatal,nOutputDirectoryNotFound,sOutputDirectoryNotFound);
  3045. r(mtError,nUnableToWriteFile,sUnableToWriteFile);
  3046. r(mtInfo,nWritingFile,sWritingFile);
  3047. r(mtFatal,nCompilationAborted,sCompilationAborted);
  3048. r(mtDebug,nCfgDirective,sCfgDirective);
  3049. r(mtError,nUnitCycle,sUnitCycle);
  3050. r(mtError,nOptionForbidsCompile,sOptionForbidsCompile);
  3051. r(mtInfo,nUnitNeedsCompileDueToUsedUnit,sUnitsNeedCompileDueToUsedUnit);
  3052. r(mtInfo,nUnitNeedsCompileDueToOption,sUnitsNeedCompileDueToOption);
  3053. r(mtInfo,nUnitNeedsCompileJSMissing,sUnitsNeedCompileJSMissing);
  3054. r(mtInfo,nUnitNeedsCompilePasHasChanged,sUnitsNeedCompilePasHasChanged);
  3055. r(mtInfo,nParsingFile,sParsingFile);
  3056. r(mtInfo,nCompilingFile,sCompilingFile);
  3057. r(mtError,nExpectedButFound,sExpectedButFound);
  3058. r(mtInfo,nLinesInFilesCompiled,sLinesInFilesCompiled);
  3059. r(mtInfo,nTargetPlatformIs,sTargetPlatformIs);
  3060. r(mtInfo,nTargetProcessorIs,sTargetProcessorIs);
  3061. r(mtInfo,nMessageEncodingIs,sMessageEncodingIs);
  3062. r(mtError,nUnableToTranslatePathToDir,sUnableToTranslatePathToDir);
  3063. r(mtInfo,nSrcMapSourceRootIs,sSrcMapSourceRootIs);
  3064. r(mtInfo,nSrcMapBaseDirIs,sSrcMapBaseDirIs);
  3065. r(mtFatal,nUnitFileNotFound,sUnitFileNotFound);
  3066. r(mtInfo,nClassInterfaceStyleIs,sClassInterfaceStyleIs);
  3067. r(mtInfo,nHandlingEnvOpts,sHandlingEnvOpts);
  3068. r(mtInfo,nPostProcessorInfoX,sPostProcessorInfoX);
  3069. r(mtInfo,nPostProcessorRunX,sPostProcessorRunX);
  3070. r(mtError,nPostProcessorFailX,sPostProcessorFailX);
  3071. r(mtWarning,nPostProcessorWarnX,sPostProcessorWarnX);
  3072. r(mtInfo,nPostProcessorFinished,sPostProcessorFinished);
  3073. r(mtInfo,nRTLIdentifierChanged,sRTLIdentifierChanged);
  3074. r(mtNote,nSkipNoConstResourcestring,sSkipNoConstResourcestring);
  3075. r(mtWarning,nUnknownOptimizationOption,sUnknownOptimizationOption);
  3076. Pas2jsPParser.RegisterMessages(Log);
  3077. end;
  3078. procedure TPas2jsCompiler.LoadConfig(CfgFilename: string);
  3079. begin
  3080. ConfigSupport.LoadConfig(CfgFileName);
  3081. end;
  3082. procedure TPas2jsCompiler.ReadEnvironment;
  3083. var
  3084. s: String;
  3085. List: TStrings;
  3086. begin
  3087. s:=GetEnvironmentVariable('PAS2JS_OPTS');
  3088. if s='' then exit;
  3089. if ShowDebug then
  3090. Log.LogMsgIgnoreFilter(nHandlingEnvOpts,['PAS2JS_OPTS=['+s+']']);
  3091. List:=TStringList.Create;
  3092. try
  3093. SplitCmdLineParams(s,List);
  3094. for s in List do
  3095. if s<>'' then
  3096. ReadParam(s,false,false);
  3097. finally
  3098. List.Free;
  3099. end;
  3100. end;
  3101. procedure TPas2jsCompiler.ParamFatal(Msg: string);
  3102. begin
  3103. if FCurParam<>'' then
  3104. Msg:='parameter '+FCurParam+': '+Msg;
  3105. if Assigned(ConfigSupport) and (ConfigSupport.CurrentCfgFilename<>'') then
  3106. Log.Log(mtFatal,Msg,0,ConfigSupport.CurrentCfgFilename,ConfigSupport.CurrentCfgLineNumber,0)
  3107. else
  3108. Log.LogPlain(['Fatal: ',Msg]);
  3109. Terminate(ExitCodeErrorInParams);
  3110. end;
  3111. procedure TPas2jsCompiler.HandleOptionPCUFormat(aValue: String);
  3112. begin
  3113. ParamFatal('No support in this compiler for precompiled format '+aValue);
  3114. end;
  3115. function TPas2jsCompiler.HandleOptionPaths(C: Char; aValue: String;
  3116. FromCmdLine: Boolean): Boolean;
  3117. Var
  3118. ErrorMsg: String;
  3119. begin
  3120. Result:=True;
  3121. case c of
  3122. 'N': AddNamespaces(aValue,FromCmdLine);
  3123. 'r': Log.Log(mtNote,'-Fr not yet implemented');
  3124. 'e': Log.OutputFilename:=aValue;
  3125. else
  3126. ErrorMsg:=FS.HandleOptionPaths(C,aValue,FromCmdLine);
  3127. if ErrorMsg<>'' then
  3128. ParamFatal(ErrorMsg);
  3129. end;
  3130. end;
  3131. function TPas2jsCompiler.HandleOptionJ(C: Char; aValue: String;
  3132. Quick, FromCmdLine: Boolean): Boolean;
  3133. Var
  3134. S, ErrorMsg, aName: String;
  3135. i: Integer;
  3136. enable: Boolean;
  3137. pbi: TPas2JSBuiltInName;
  3138. begin
  3139. Result:=True;
  3140. case c of
  3141. 'c': // -Jc concatenate
  3142. begin
  3143. if aValue='' then
  3144. AllJSIntoMainJS:=true
  3145. else if (AValue='-') then
  3146. AllJSIntoMainJS:=false
  3147. else
  3148. ParamFatal('invalid value (-Jc) "'+aValue+'"');
  3149. end;
  3150. 'e': // -Je<encoding>
  3151. begin
  3152. S:=NormalizeEncoding(aValue);
  3153. case S of
  3154. {$IFDEF FPC_HAS_CPSTRING}
  3155. 'console','system',
  3156. {$ENDIF}
  3157. 'utf8', 'json':
  3158. if Log.Encoding<>S then begin
  3159. Log.Encoding:=S;
  3160. if FHasShownEncoding then begin
  3161. FHasShownEncoding:=false;
  3162. WriteEncoding;
  3163. end;
  3164. end;
  3165. else ParamFatal('invalid encoding (-Je) "'+aValue+'"');
  3166. end;
  3167. end;
  3168. 'a', // -Ja<js-file>
  3169. 'i': // -Ji<js-file>
  3170. if aValue='' then
  3171. if c='a' then
  3172. ParamFatal('missing append file "'+aValue+'"')
  3173. else
  3174. ParamFatal('missing insertion file "'+aValue+'"')
  3175. else if not Quick then
  3176. begin
  3177. if aValue='' then
  3178. Result:=false
  3179. else if aValue[length(aValue)]='-' then
  3180. begin
  3181. Delete(aValue,length(aValue),1);
  3182. if aValue='' then
  3183. Result:=False
  3184. else if c='i' then
  3185. RemoveInsertJSFilename(aValue)
  3186. else
  3187. RemoveAppendJSFileName(aValue);
  3188. end
  3189. else
  3190. if C='i' then
  3191. AddInsertJSFilename(aValue)
  3192. else
  3193. addAppendJSFileName(aValue)
  3194. end;
  3195. 'l': // -Jl
  3196. SetOption(coLowercase,aValue<>'-');
  3197. 'm': // -Jm source map options
  3198. if aValue='' then
  3199. SrcMapEnable:=true
  3200. else if aValue[1]='-' then
  3201. begin
  3202. if aValue<>'-' then
  3203. Result:=False
  3204. else
  3205. SrcMapEnable:=false;
  3206. end else
  3207. begin
  3208. case aValue of
  3209. 'include':
  3210. SrcMapInclude:=true;
  3211. 'include-':
  3212. SrcMapInclude:=false;
  3213. 'absolute':
  3214. SrcMapFilenamesAbsolute:=true;
  3215. 'absolute-':
  3216. SrcMapFilenamesAbsolute:=false;
  3217. 'xssiheader':
  3218. SrcMapXSSIHeader:=true;
  3219. 'xssiheader-':
  3220. SrcMapXSSIHeader:=false;
  3221. else
  3222. begin
  3223. i:=Pos('=',aValue);
  3224. if i<1 then
  3225. ParamFatal('unknown -Jm parameter "'+aValue+'"')
  3226. else
  3227. begin
  3228. S:=LeftStr(aValue,i-1);
  3229. Delete(aValue,1,i);
  3230. Case s of
  3231. 'sourceroot': SrcMapSourceRoot:=aValue;
  3232. 'basedir': SrcMapBaseDir:=aValue;
  3233. else
  3234. ParamFatal('unknown -Jm parameter "'+s+'"')
  3235. end;
  3236. end;
  3237. end;
  3238. end;
  3239. // enable source maps when setting any -Jm<x> option
  3240. SrcMapEnable:=true;
  3241. end;
  3242. 'o': // -Jo<flag>
  3243. begin
  3244. S:=aValue;
  3245. if aValue='' then
  3246. ParamFatal('missing value of -Jo option');
  3247. if SameText(LeftStr(S,4),'rtl-') then
  3248. begin
  3249. // -Jortl-<name>=<value> set rtl identifier
  3250. i:=5;
  3251. while (i<=length(S)) and (S[i] in ['a'..'z','A'..'Z','0'..'9','_']) do
  3252. inc(i);
  3253. if (i>length(S)) or (S[i]<>'=') then
  3254. ParamFatal('expected -Jortl-name=value');
  3255. aName:='pbi'+copy(S,5,i-5);
  3256. S:=copy(S,i+1,255);
  3257. val(aName,pbi,i);
  3258. if i<>0 then
  3259. ParamFatal('unknown rtl identifier "'+aName+'"');
  3260. if IsValidJSIdentifier(TJSString(ConverterGlobals.BuiltInNames[pbi]))
  3261. and not IsValidJSIdentifier(TJSString(S)) then
  3262. ParamFatal('JavaScript identifier expected');
  3263. if not Quick then
  3264. ConverterGlobals.BuiltInNames[pbi]:=S;
  3265. end else begin
  3266. Enable:=true;
  3267. c:=S[length(S)];
  3268. if c in ['+','-'] then
  3269. begin
  3270. Enable:=c='+';
  3271. Delete(S,length(S),1);
  3272. end;
  3273. Case lowercase(S) of
  3274. 'searchlikefpc': FS.SearchLikeFPC:=Enable;
  3275. 'usestrict': SetOption(coUseStrict,Enable);
  3276. 'checkversion=main': RTLVersionCheck:=rvcMain;
  3277. 'checkversion=system': RTLVersionCheck:=rvcSystem;
  3278. 'checkversion=unit': RTLVersionCheck:=rvcUnit;
  3279. else
  3280. Result:=False;
  3281. end;
  3282. end;
  3283. end;
  3284. 'p': // -Jp<...>
  3285. begin
  3286. if not Assigned(PostProcessorSupport) then
  3287. ParamFatal('-Jp: No postprocessor support available');
  3288. Result:=copy(aValue,1,3)='cmd';
  3289. if Result then
  3290. begin
  3291. delete(aValue,1,3);
  3292. if not Quick then
  3293. PostProcessorSupport.AddPostProcessor(aValue);
  3294. end;
  3295. end;
  3296. 'r': // -Jr<...>
  3297. begin
  3298. S:=aValue;
  3299. if aValue='' then
  3300. ParamFatal('missing value for -Jr option')
  3301. else if (S='none') then
  3302. FResourceStringFile:=rsfNone
  3303. else if (S='unit') then
  3304. FResourceStringFile:=rsfunit
  3305. else if (S='program') then
  3306. FResourceStringFile:=rsfProgram
  3307. else
  3308. ParamFatal('invalid resource string file format (-Jr) "'+aValue+'"');
  3309. end;
  3310. 'R': // -JR<...>
  3311. begin
  3312. I:=Pos('=',aValue);
  3313. if I=0 then
  3314. I:=Length(aValue)+1;
  3315. S:=Copy(aValue,1,I-1);
  3316. if S='' then
  3317. ParamFatal('missing value for -JR option')
  3318. else if (S='none') then
  3319. FResourceMode:=rmNone
  3320. else if (S='js') then
  3321. FResourceMode:= rmJS
  3322. else if (S='html') then
  3323. begin
  3324. FResourceMode:=rmHTML;
  3325. S:=Copy(aValue,I+1,Length(aValue)-I);
  3326. FResourceOutputFile:=S;
  3327. if (FResourceOutputFile<>'') and (ExtractFileExt(FResourceOutputFile)='') then
  3328. FResourceOutputFile:=FResourceOutputFile+'.html';
  3329. end;
  3330. end;
  3331. 'u': // -Ju<foreign path>
  3332. if not Quick then
  3333. begin
  3334. ErrorMsg:=FS.AddForeignUnitPath(aValue,FromCmdLine);
  3335. if ErrorMsg<>'' then
  3336. ParamFatal('invalid foreign unit path (-Ju) "'+ErrorMsg+'"');
  3337. end;
  3338. 'U': // -JU...
  3339. HandleOptionPCUFormat(aValue);
  3340. else
  3341. Result:=False;
  3342. end;
  3343. end;
  3344. function TPas2jsCompiler.HandleOptionM(aValue: String; Quick: Boolean): Boolean;
  3345. var
  3346. Negated: Boolean;
  3347. ms: TModeSwitch;
  3348. begin
  3349. Result:=True;
  3350. if aValue='' then
  3351. ParamFatal('invalid syntax mode (-M<x>) "'+aValue+'"');
  3352. if Quick then exit;
  3353. case lowerCase(aValue) of
  3354. 'delphi': SetMode(p2jmDelphi);
  3355. 'objfpc': SetMode(p2jmObjFPC);
  3356. else
  3357. if aValue[length(aValue)]='-' then
  3358. begin
  3359. aValue:=LeftStr(aValue,length(aValue)-1);
  3360. Negated:=true;
  3361. end else
  3362. Negated:=false;
  3363. for ms in TModeSwitch do
  3364. if (ms in msAllPas2jsModeSwitches)
  3365. and SameText(SModeSwitchNames[ms],aValue) then
  3366. begin
  3367. if (ms in ModeSwitches)<>Negated then
  3368. begin
  3369. // already set
  3370. exit;
  3371. end else if ms in msAllPas2jsModeSwitchesReadOnly then
  3372. ParamFatal('modeswitch is read only -M"'+aValue+'"')
  3373. else begin
  3374. // switch
  3375. if Negated then
  3376. ModeSwitches:=ModeSwitches-[ms]
  3377. else
  3378. ModeSwitches:=ModeSwitches+[ms];
  3379. exit;
  3380. end;
  3381. end;
  3382. ParamFatal('invalid syntax mode (-M) "'+aValue+'"');
  3383. end;
  3384. end;
  3385. procedure TPas2jsCompiler.HandleOptionConfigFile(aPos: Integer; const aFileName: string);
  3386. Var
  3387. FN: String;
  3388. begin
  3389. // load extra config file
  3390. if aFilename='' then
  3391. ParamFatal('invalid config file at param position '+IntToStr(aPos));
  3392. FN:=ExpandFileName(aFilename);
  3393. if not FS.FileExists(FN) then
  3394. ParamFatal('config file not found: "'+aFileName+'"');
  3395. LoadConfig(FN);
  3396. end;
  3397. procedure TPas2jsCompiler.HandleOptionInfo(aValue: string);
  3398. Var
  3399. InfoMsg: String;
  3400. procedure AppendInfo(Add: string);
  3401. begin
  3402. if InfoMsg<>'' then
  3403. InfoMsg:=InfoMsg+' ';
  3404. InfoMsg:=InfoMsg+Add;
  3405. end;
  3406. Var
  3407. P,L: integer;
  3408. C,c2: Char;
  3409. pr: TPasToJsProcessor;
  3410. pl: TPasToJsPlatform;
  3411. s: string;
  3412. pbi: TPas2JSBuiltInName;
  3413. ms: TModeSwitch;
  3414. begin
  3415. // write information and halt
  3416. InfoMsg:='';
  3417. if aValue='' then
  3418. begin
  3419. WriteInfo;
  3420. Terminate(0);
  3421. exit;
  3422. end;
  3423. P:=1;
  3424. L:=Length(aValue);
  3425. while p<=l do
  3426. begin
  3427. C:=aValue[P];
  3428. case C of
  3429. 'D': // wite compiler date
  3430. AppendInfo(GetCompiledDate);
  3431. 'V': // write short version
  3432. AppendInfo(GetVersion(true));
  3433. 'W': // write long version
  3434. AppendInfo(GetVersion(false));
  3435. 'S':
  3436. begin
  3437. inc(p);
  3438. if p>l then
  3439. ParamFatal('missing info option after S in "'+aValue+'".');
  3440. C2:=aValue[p];
  3441. case C2 of
  3442. 'O': // write source OS
  3443. AppendInfo(GetCompiledTargetOS);
  3444. 'P': // write source processor
  3445. AppendInfo(GetCompiledTargetCPU);
  3446. else
  3447. ParamFatal('unknown info option S"'+C2+'" in "'+aValue+'".');
  3448. end;
  3449. end;
  3450. 'T':
  3451. begin
  3452. inc(p);
  3453. if p>l then
  3454. ParamFatal('missing info option after T in "'+aValue+'".');
  3455. C2:=aValue[p];
  3456. case C2 of
  3457. 'O': // write target platform
  3458. AppendInfo(PasToJsPlatformNames[TargetPlatform]);
  3459. 'P': // write target processor
  3460. AppendInfo(PasToJsProcessorNames[TargetProcessor]);
  3461. else
  3462. ParamFatal('unknown info option S"'+C2+'" in "'+aValue+'".');
  3463. end;
  3464. end;
  3465. 'c':
  3466. // write list of supported JS processors
  3467. for pr in TPasToJsProcessor do
  3468. Log.LogPlain(PasToJsProcessorNames[pr]);
  3469. 'm':
  3470. begin
  3471. // write list of supported modeswitches
  3472. for ms in (msAllPas2jsModeSwitches-AllLanguageModes) do
  3473. Log.LogPlain(SModeSwitchNames[ms]);
  3474. end;
  3475. 'o':
  3476. begin
  3477. // write list of optimizations
  3478. Log.LogPlain('EnumNumbers');
  3479. Log.LogPlain('RemoveNotUsedPrivates');
  3480. Log.LogPlain('RemoveNotUsedDeclarations');
  3481. Log.LogPlain('ShortRefGlobals');
  3482. end;
  3483. 't':
  3484. // write list of supported targets
  3485. for pl in TPasToJsPlatform do
  3486. Log.LogPlain(PasToJsPlatformNames[pl]);
  3487. 'J':
  3488. // write list of RTL identifiers
  3489. begin
  3490. Log.LogPlain('-JoRTL-<x> identifiers:');
  3491. for pbi in TPas2JSBuiltInName do
  3492. begin
  3493. str(pbi,s);
  3494. Delete(s,1,3);
  3495. Log.LogPlain('-JoRTL-'+s+'='+Pas2JSBuiltInNames[pbi]);
  3496. end;
  3497. end
  3498. else
  3499. ParamFatal('unknown info option "'+C+'" in "'+aValue+'".');
  3500. end;
  3501. inc(p);
  3502. end;
  3503. if InfoMsg<>'' then
  3504. Log.LogPlain(InfoMsg);
  3505. end;
  3506. function TPas2jsCompiler.HandleOptionOptimization(C: Char; aValue: String): Boolean;
  3507. Var
  3508. Enable: Boolean;
  3509. begin
  3510. Result:=True;
  3511. case C of
  3512. '-': Options:=Options-coAllOptimizations+coO0;
  3513. '1': Options:=Options-coAllOptimizations+coO1;
  3514. '2': Options:=Options-coAllOptimizations+coO2;
  3515. 'o':
  3516. begin
  3517. if aValue='' then
  3518. ParamFatal('missing -Oo option');
  3519. Enable:=true;
  3520. c:=aValue[length(aValue)];
  3521. if c in ['+','-'] then
  3522. begin
  3523. Enable:=c='+';
  3524. Delete(aValue,length(aValue),1);
  3525. end
  3526. else if lowercase(LeftStr(aValue,2))='no' then begin
  3527. Enable:=false;
  3528. Delete(aValue,1,2);
  3529. end;
  3530. Case LowerCase(aValue) of
  3531. 'enumnumbers': SetOption(coEnumValuesAsNumbers,Enable);
  3532. 'removenotusedprivates': SetOption(coKeepNotUsedPrivates,not Enable);
  3533. 'removenotuseddeclarations': SetOption(coKeepNotUsedDeclarationsWPO,not Enable);
  3534. 'shortrefglobals': SetOption(coShortRefGlobals,Enable);
  3535. 'obfuscatelocalidentifiers': SetOption(coObfuscateLocalIdentifiers,Enable);
  3536. else
  3537. Log.LogMsgIgnoreFilter(nUnknownOptimizationOption,[QuoteStr(aValue)]);
  3538. end;
  3539. end;
  3540. else
  3541. Result:=False;
  3542. end;
  3543. end;
  3544. procedure TPas2jsCompiler.ReadParam(Param: string; Quick, FromCmdLine: boolean);
  3545. procedure UnknownParam;
  3546. begin
  3547. ParamFatal('unknown parameter "'+Param+'". Use -h for help.');
  3548. end;
  3549. var
  3550. EnabledFlags, DisabledFlags, Identifier, aValue: string;
  3551. p, l, i: Integer;
  3552. c: Char;
  3553. aProc: TPasToJsProcessor;
  3554. aPlatform: TPasToJsPlatform;
  3555. begin
  3556. //writeln('TPas2jsCompiler.ReadParam ',Param,' ',Quick,' ',FromCmdLine);
  3557. if ShowDebug then
  3558. if Quick then
  3559. Log.LogMsgIgnoreFilter(nQuickHandlingOption,[QuoteStr(Param)])
  3560. else
  3561. Log.LogMsgIgnoreFilter(nHandlingOption,[QuoteStr(Param)]);
  3562. if Param='' then exit;
  3563. FCurParam:=Param;
  3564. ParamMacros.Substitute(Param,Self);
  3565. if Param='' then exit;
  3566. if Quick and ((Param='-h') or (Param='-?') or (Param='--help')) then
  3567. begin
  3568. WriteHelp;
  3569. Terminate(0);
  3570. end;
  3571. l:=length(Param);
  3572. p:=1;
  3573. case Param[p] of
  3574. '-':
  3575. begin
  3576. inc(p);
  3577. if p>l then
  3578. UnknownParam;
  3579. aValue:=Copy(Param,P+1,Length(Param));
  3580. case Param[p] of
  3581. 'i':
  3582. begin
  3583. HandleOptionInfo(aValue);
  3584. Terminate(0);
  3585. end;
  3586. 'B','l','n':
  3587. begin
  3588. ReadSingleLetterOptions(Param,p,'Bln',EnabledFlags,DisabledFlags);
  3589. for i:=1 to length(EnabledFlags) do begin
  3590. case EnabledFlags[i] of
  3591. 'B': Options:=Options+[coBuildAll];
  3592. 'l': ShowLogo:=true;
  3593. 'n': SkipDefaultConfig:=true;
  3594. end;
  3595. end;
  3596. for i:=1 to length(DisabledFlags) do begin
  3597. case DisabledFlags[i] of
  3598. 'B': Options:=Options-[coBuildAll];
  3599. 'l': ShowLogo:=false;
  3600. 'n': SkipDefaultConfig:=false;
  3601. end;
  3602. end;
  3603. end;
  3604. 'C': // code generation
  3605. ReadCodeGenerationFlags(aValue,1);
  3606. 'd': // define
  3607. if not Quick then
  3608. begin
  3609. Identifier:=aValue;
  3610. i:=Pos(':=',Identifier);
  3611. if i>0 then
  3612. begin
  3613. aValue:=copy(Identifier,i+2,length(Identifier));
  3614. Identifier:=LeftStr(Identifier,i-1);
  3615. if not IsValidIdent(Identifier) then
  3616. ParamFatal('invalid define name (-d): "'+Param+'"');
  3617. AddDefine(Identifier,aValue);
  3618. end else begin
  3619. if not IsValidIdent(Identifier) then
  3620. ParamFatal('invalid define (-d): "'+Param+'"');
  3621. AddDefine(Identifier);
  3622. end;
  3623. end;
  3624. 'F': // folders and search paths
  3625. begin
  3626. if aValue='' then
  3627. UnknownParam;
  3628. c:=aValue[1];
  3629. Delete(aValue,1,1);
  3630. if not HandleOptionPaths(c,aValue,fromCmdLine) then
  3631. UnknownParam;
  3632. end;
  3633. 'I': // include path, same as -Fi
  3634. if not Quick then
  3635. begin
  3636. if not HandleOptionPaths('i',aValue,fromCmdLine) then
  3637. UnknownParam;
  3638. end;
  3639. 'J': // extra pas2js options
  3640. begin
  3641. if aValue='' then
  3642. UnknownParam;
  3643. c:=aValue[1];
  3644. Delete(aValue,1,1);
  3645. if not HandleOptionJ(c,aValue,Quick,FromCmdLine) then
  3646. UnknownParam;
  3647. end;
  3648. 'M': // syntax mode
  3649. if not HandleOptionM(aValue,Quick) then
  3650. UnknownParam;
  3651. 'N':
  3652. begin
  3653. if aValue='' then
  3654. UnknownParam;
  3655. case aValue[1] of
  3656. 'S':
  3657. begin
  3658. Log.Log(mtWarning,'obsolete option -NS, use -FN instead');
  3659. Delete(aValue,1,1);
  3660. HandleOptionPaths('N',aValue,FromCmdLine);
  3661. end;
  3662. else UnknownParam;
  3663. end;
  3664. end;
  3665. 'o': // output file, main JavaScript file
  3666. begin
  3667. if aValue='' then
  3668. ParamFatal('invalid empty output file (-o)')
  3669. else if aValue='..' then
  3670. ParamFatal('invalid output file (-o) "'+aValue+'"')
  3671. else if aValue='.' then
  3672. // ok, stdout
  3673. else
  3674. aValue:=ExpandFileName(aValue);
  3675. MainJSFile:=aValue;
  3676. end;
  3677. 'O': // optimizations
  3678. begin
  3679. if aValue='' then
  3680. UnknownParam;
  3681. C:=aValue[1];
  3682. Delete(aValue,1,1);
  3683. if not HandleOptionOptimization(C,aValue) then
  3684. UnknownParam;
  3685. end;
  3686. 'P': // target processor
  3687. begin
  3688. for aProc in TPasToJsProcessor do
  3689. if SameText(aValue,PasToJsProcessorNames[aProc]) then
  3690. begin
  3691. TargetProcessor:=aProc;
  3692. aValue:='';
  3693. break;
  3694. end;
  3695. if aValue<>'' then
  3696. ParamFatal('invalid target processor (-P) "'+aValue+'"');
  3697. end;
  3698. 'S': // Syntax
  3699. begin
  3700. inc(p);
  3701. if (p<=l) and (Param[p]='I') then
  3702. begin
  3703. Identifier:=copy(Param,p,length(Param));
  3704. if SameText(Identifier,'com') then
  3705. InterfaceType:=citCom
  3706. else if SameText(Identifier,'corba') then
  3707. InterfaceType:=citCorba
  3708. else
  3709. ParamFatal('invalid interface style (-SI) "'+Identifier+'"');
  3710. end
  3711. else
  3712. ReadSyntaxFlags(Param,p);
  3713. end;
  3714. 'T': // target platform
  3715. begin
  3716. inc(p);
  3717. Identifier:=copy(Param,p,length(Param));
  3718. for aPlatform in TPasToJsPlatform do
  3719. if SameText(Identifier,PasToJsPlatformNames[aPlatform]) then
  3720. begin
  3721. TargetPlatform:=aPlatform;
  3722. Identifier:='';
  3723. break;
  3724. end;
  3725. if Identifier<>'' then
  3726. ParamFatal('invalid target platform (-T) "'+Identifier+'"');
  3727. end;
  3728. 'u': // undefine
  3729. if not Quick then
  3730. begin
  3731. if not IsValidIdent(aValue) then
  3732. ParamFatal('invalid undefine (-u): "'+aValue+'"');
  3733. RemoveDefine(aValue);
  3734. end;
  3735. 'v': // verbose
  3736. begin
  3737. inc(p);
  3738. ReadVerbosityFlags(Param,p);
  3739. end;
  3740. else
  3741. UnknownParam;
  3742. end;
  3743. end;
  3744. '@':
  3745. if not Quick then
  3746. HandleOptionConfigFile(i,copy(Param,2,length(Param)));
  3747. else
  3748. // filename
  3749. if (not Quick) then
  3750. begin
  3751. if not FromCmdLine then
  3752. ConfigSupport.CfgSyntaxError('invalid parameter');
  3753. if MainSrcFile<>'' then
  3754. ParamFatal('Only one Pascal file is supported, but got "'+MainSrcFile+'" and "'+Param+'".');
  3755. MainSrcFile:=ExpandFileName(Param);
  3756. end;
  3757. end;
  3758. end;
  3759. procedure TPas2jsCompiler.ReadSingleLetterOptions(const Param: string;
  3760. p: integer; const Allowed: string; out Enabled, Disabled: string);
  3761. // e.g. 'B' 'lB' 'l-' 'l+B-'
  3762. var
  3763. Letter: Char;
  3764. i, l: Integer;
  3765. begin
  3766. l:=length(Param);
  3767. if p>l then
  3768. ParamFatal('Invalid option "'+Param+'"');
  3769. Enabled:='';
  3770. Disabled:='';
  3771. while p<=l do
  3772. begin
  3773. Letter:=Param[p];
  3774. if Letter='-' then
  3775. ParamFatal('Invalid option "'+Param+'"');
  3776. if Pos(Letter,Allowed)<1 then
  3777. ParamFatal('unknown option "'+Param+'". Use -h for help.');
  3778. inc(p);
  3779. if (p<=l) and (Param[p]='-') then
  3780. begin
  3781. // disable
  3782. if Pos(Letter,Disabled)<1 then Disabled+=Letter;
  3783. i:=Pos(Letter,Enabled);
  3784. if i>0 then Delete(Enabled,i,1);
  3785. inc(p);
  3786. end else begin
  3787. // enable
  3788. if Pos(Letter,Enabled)<1 then Enabled+=Letter;
  3789. i:=Pos(Letter,Disabled);
  3790. if i>0 then Delete(Disabled,i,1);
  3791. if (p<=l) and (Param[p]='+') then inc(p);
  3792. end;
  3793. end;
  3794. end;
  3795. procedure TPas2jsCompiler.ReadCodeGenerationFlags(Param: String; p: integer);
  3796. var
  3797. Enabled, Disabled: string;
  3798. i: Integer;
  3799. begin
  3800. ReadSingleLetterOptions(Param,p,'orR',Enabled,Disabled);
  3801. for i:=1 to length(Enabled) do begin
  3802. case Enabled[i] of
  3803. 'o': Options:=Options+[coOverflowChecks];
  3804. 'r': Options:=Options+[coRangeChecks];
  3805. 'R': Options:=Options+[coObjectChecks];
  3806. end;
  3807. end;
  3808. for i:=1 to length(Disabled) do begin
  3809. case Disabled[i] of
  3810. 'o': Options:=Options-[coOverflowChecks];
  3811. 'r': Options:=Options-[coRangeChecks];
  3812. 'R': Options:=Options-[coObjectChecks];
  3813. end;
  3814. end;
  3815. end;
  3816. procedure TPas2jsCompiler.ReadSyntaxFlags(Param: String; p: integer);
  3817. var
  3818. Enabled, Disabled: string;
  3819. i: Integer;
  3820. begin
  3821. ReadSingleLetterOptions(Param,p,'2acdmj',Enabled,Disabled);
  3822. for i:=1 to length(Enabled) do begin
  3823. case Enabled[i] of
  3824. '2': SetMode(p2jmObjFPC);
  3825. 'a': Options:=Options+[coAssertions];
  3826. 'c': Options:=Options+[coAllowCAssignments];
  3827. 'd': SetMode(p2jmDelphi);
  3828. 'm': Options:=Options+[coAllowMacros];
  3829. 'j': Options:=Options+[coWriteableConst];
  3830. end;
  3831. end;
  3832. for i:=1 to length(Disabled) do begin
  3833. case Disabled[i] of
  3834. '2': ;
  3835. 'a': Options:=Options-[coAssertions];
  3836. 'c': Options:=Options-[coAllowCAssignments];
  3837. 'd': ;
  3838. 'm': Options:=Options-[coAllowMacros];
  3839. 'j': Options:=Options-[coWriteableConst];
  3840. end;
  3841. end;
  3842. end;
  3843. procedure TPas2jsCompiler.ReadVerbosityFlags(Param: String; p: integer);
  3844. var
  3845. Enabled, Disabled: string;
  3846. i, l: Integer;
  3847. begin
  3848. l:=length(Param);
  3849. if p>l then exit;
  3850. if Param[p]='m' then
  3851. begin
  3852. // read m-flags
  3853. repeat
  3854. inc(p);
  3855. if (p>l) or not (Param[p] in ['0'..'9']) then
  3856. ParamFatal('missing number in "'+Param+'"');
  3857. i:=0;
  3858. while (p<=l) and (Param[p] in ['0'..'9']) do
  3859. begin
  3860. i:=i*10+ord(Param[p])-ord('0');
  3861. if i>99999 then
  3862. ParamFatal('Invalid -vm parameter in "'+Param+'"');
  3863. inc(p);
  3864. end;
  3865. if (p<=l) and (Param[p]='-') then
  3866. begin
  3867. inc(p);
  3868. Log.MsgNumberDisabled[i]:=false;
  3869. end else
  3870. Log.MsgNumberDisabled[i]:=true;
  3871. if p>l then break;
  3872. if Param[p]<>',' then
  3873. ParamFatal('Invalid option "'+Param+'"');
  3874. until false;
  3875. exit;
  3876. end;
  3877. // read other flags
  3878. ReadSingleLetterOptions(Param,p,'ewnhila0bctdqxvz',Enabled,Disabled);
  3879. for i:=1 to length(Enabled) do begin
  3880. case Enabled[i] of
  3881. 'e': Options:=Options+[coShowErrors];
  3882. 'w': Options:=Options+[coShowWarnings];
  3883. 'n': Options:=Options+[coShowNotes];
  3884. 'h': Options:=Options+[coShowHints];
  3885. 'i': Options:=Options+[coShowInfos];
  3886. 'l': Options:=Options+[coShowLineNumbers];
  3887. 'a': Options:=Options+coShowAll;
  3888. '0': Options:=Options-coShowAll+[coShowErrors];
  3889. 'b': ShowFullPaths:=true;
  3890. 'c': Options:=Options+[coShowConditionals,coShowInfos];
  3891. 't': ShowTriedUsedFiles:=true;
  3892. 'd': ShowDebug:=true;
  3893. 'q': Options:=Options+[coShowMessageNumbers];
  3894. 'x': Options:=Options+[coShowUsedTools];
  3895. 'v': Options:=Options+[coWriteDebugLog];
  3896. 'z': WriteMsgToStdErr:=true;
  3897. end;
  3898. end;
  3899. for i:=1 to length(Disabled) do begin
  3900. case Disabled[i] of
  3901. 'e': Options:=Options-[coShowErrors];
  3902. 'w': Options:=Options-[coShowWarnings];
  3903. 'n': Options:=Options-[coShowNotes];
  3904. 'h': Options:=Options-[coShowHints];
  3905. 'i': Options:=Options-[coShowInfos];
  3906. 'l': Options:=Options-[coShowLineNumbers];
  3907. 'a': ;
  3908. '0': ;
  3909. 'b': ShowFullPaths:=false;
  3910. 'c': Options:=Options-[coShowConditionals];
  3911. 't': ShowTriedUsedFiles:=false;
  3912. 'd': ShowDebug:=false;
  3913. 'q': Options:=Options-[coShowMessageNumbers];
  3914. 'x': Options:=Options-[coShowUsedTools];
  3915. 'v': Options:=Options+[coWriteDebugLog];
  3916. 'z': WriteMsgToStdErr:=false;
  3917. end;
  3918. end;
  3919. end;
  3920. function TPas2jsCompiler.CreateImportList: TJSSourceElements;
  3921. begin
  3922. Result:=TJSSourceElements.Create(0,0,'');
  3923. end;
  3924. function TPas2jsCompiler.CreateImportStatement: TJSImportStatement;
  3925. begin
  3926. Result:=TJSImportStatement.Create(0,0,'');
  3927. end;
  3928. procedure TPas2jsCompiler.SetAllJSIntoMainJS(AValue: Boolean);
  3929. begin
  3930. if FAllJSIntoMainJS=AValue then Exit;
  3931. if aValue then
  3932. FMainJSFileIsResolved:=False;
  3933. FAllJSIntoMainJS:=AValue;
  3934. end;
  3935. procedure TPas2jsCompiler.SetConverterGlobals(
  3936. const AValue: TPasToJSConverterGlobals);
  3937. begin
  3938. if AValue=FConverterGlobals then exit;
  3939. if (FConverterGlobals<>nil) and (FConverterGlobals.Owner=Self) then
  3940. FreeAndNil(FConverterGlobals);
  3941. FConverterGlobals:=AValue;
  3942. end;
  3943. function TPas2jsCompiler.FormatPath(const aPath: String): String;
  3944. begin
  3945. Result:=FS.FormatPath(aPath);
  3946. end;
  3947. function TPas2jsCompiler.FullFormatPath(const aPath: String): String;
  3948. begin
  3949. Result:=QuoteStr(FormatPath(aPath));
  3950. end;
  3951. function TPas2jsCompiler.CreateMacroEngine: TPas2jsMacroEngine;
  3952. begin
  3953. Result:=TPas2jsMacroEngine.Create;
  3954. end;
  3955. function TPas2jsCompiler.CreateLog: TPas2jsLogger;
  3956. begin
  3957. Result:=TPas2jsLogger.Create;
  3958. end;
  3959. constructor TPas2jsCompiler.Create;
  3960. begin
  3961. FOptions:=DefaultP2jsCompilerOptions;
  3962. FConverterGlobals:=TPasToJSConverterGlobals.Create(Self);
  3963. FResolverHub:=TPas2JSResolverHub.Create(Self);
  3964. FNamespaces:=TStringList.Create;
  3965. FDefines:=TStringList.Create;
  3966. FInsertFilenames:=TStringList.Create;
  3967. FAppendFileNames:=TStringList.Create;
  3968. FImports:=CreateImportList;
  3969. FLog:=CreateLog;
  3970. FLog.OnFormatPath:=@FormatPath;
  3971. FParamMacros:=CreateMacroEngine;
  3972. RegisterMessages;
  3973. FS:=CreateFS;
  3974. FOwnsFS:=true;
  3975. // Done by Reset: TStringList(FDefines).Sorted:=True;
  3976. // Done by Reset: TStringList(FDefines).Duplicates:=dupError;
  3977. //FConditionEval.OnEvalFunction:=@ConditionEvalFunction;
  3978. FFiles:=CreateSetOfCompilerFiles(kcFilename);
  3979. FUnits:=CreateSetOfCompilerFiles(kcUnitName);
  3980. FResourceMode:=DefaultResourceMode;
  3981. FResourceStrings:=TResourceStringsFile.Create;
  3982. FReadingModules:=TFPList.Create;
  3983. InitParamMacros;
  3984. Reset;
  3985. end;
  3986. destructor TPas2jsCompiler.Destroy;
  3987. procedure FreeStuff;
  3988. begin
  3989. FreeAndNil(FImports);
  3990. FreeAndNil(FResourceStrings);
  3991. FreeAndNil(FNamespaces);
  3992. FreeAndNil(FWPOAnalyzer);
  3993. FreeAndNil(FInsertFilenames);
  3994. FreeAndNil(FAppendFilenames);
  3995. FMainFile:=nil;
  3996. FreeAndNil(FUnits);
  3997. FreeAndNil(FReadingModules);
  3998. FFiles.FreeItems;
  3999. FreeAndNil(FFiles);
  4000. FreeAndNil(FPostProcessorSupport);
  4001. FreeAndNil(FConfigSupport);
  4002. ConverterGlobals:=nil;
  4003. FreeAndNil(FResolverHub);
  4004. ClearDefines;
  4005. FreeAndNil(FDefines);
  4006. FLog.OnFormatPath:=nil;
  4007. if FOwnsFS then
  4008. FreeAndNil(FFS)
  4009. else
  4010. FFS:=nil;
  4011. FreeAndNil(FParamMacros);
  4012. end;
  4013. begin
  4014. if ShowDebug then
  4015. try
  4016. FreeStuff;
  4017. except
  4018. on E: Exception do
  4019. begin
  4020. Log.LogExceptionBackTrace(E);
  4021. end
  4022. {$IFDEF Pas2js}
  4023. else HandleJSException('[20181031190818] TPas2jsCompiler.Destroy',JSExceptValue);
  4024. {$ENDIF}
  4025. end
  4026. else
  4027. FreeStuff;
  4028. FreeAndNil(FLog);
  4029. inherited Destroy;
  4030. end;
  4031. function TPas2jsCompiler.OnMacroCfgDir(Sender: TObject; var Params: string;
  4032. Lvl: integer): boolean;
  4033. begin
  4034. if Lvl=0 then ;
  4035. if Sender=nil then ;
  4036. Params:=ExtractFilePath(ConfigSupport.CurrentCfgFilename);
  4037. Result:=true;
  4038. end;
  4039. procedure TPas2jsCompiler.AddDefine(const aName: String);
  4040. begin
  4041. if FDefines.IndexOf(aName)>=0 then exit;
  4042. FDefines.Add(aName);
  4043. end;
  4044. procedure TPas2jsCompiler.AddDefine(const aName, Value: String);
  4045. var
  4046. Index: Integer;
  4047. M: TMacroDef;
  4048. begin
  4049. Index:=FDefines.IndexOf(aName);
  4050. If (Index<0) then
  4051. FDefines.AddObject(aName,TMacroDef.Create(aName,Value))
  4052. else begin
  4053. M:=TMacroDef(FDefines.Objects[Index]);
  4054. if M=nil then
  4055. FDefines.Objects[Index]:=TMacroDef.Create(aName,Value)
  4056. else
  4057. M.Value:=Value;
  4058. end;
  4059. end;
  4060. procedure TPas2jsCompiler.RemoveDefine(const aName: String);
  4061. var
  4062. i: Integer;
  4063. M: TMacroDef;
  4064. begin
  4065. i:=FDefines.IndexOf(aName);
  4066. if (i<>-1) then
  4067. begin
  4068. M:=TMacroDef(FDefines.Objects[i]);
  4069. M.Free;
  4070. FDefines.Delete(i);
  4071. end;
  4072. end;
  4073. function TPas2jsCompiler.IsDefined(const aName: String): boolean;
  4074. begin
  4075. Result:=FDefines.IndexOf(aName)>=0;
  4076. end;
  4077. class function TPas2jsCompiler.GetVersion(ShortVersion: boolean): string;
  4078. begin
  4079. Result:=IntToStr(VersionMajor)+'.'+IntToStr(VersionMinor)+'.'+IntToStr(VersionRelease);
  4080. if not ShortVersion then
  4081. Result+=VersionExtra;
  4082. end;
  4083. procedure TPas2jsCompiler.WritePrecompiledFormats;
  4084. begin
  4085. WriteHelpLine(' -JU: This pas2js does not support PCU files');
  4086. end;
  4087. procedure TPas2jsCompiler.AddNamespaces(const Paths: string;
  4088. FromCmdLine: boolean);
  4089. // cmd line paths are added in front of the cfg paths
  4090. // cmd line paths are added in order, cfg paths are added in reverse order
  4091. // multi paths separated by semicolon are added in order
  4092. // duplicates are removed
  4093. var
  4094. Added: Integer;
  4095. function Add(aPath: string): boolean;
  4096. var
  4097. Remove: Boolean;
  4098. i: Integer;
  4099. begin
  4100. Remove:=false;
  4101. // search duplicate
  4102. if aPath[length(aPath)]='-' then
  4103. begin
  4104. Delete(aPath,length(aPath),1);
  4105. Remove:=true;
  4106. end;
  4107. if not IsValidIdent(aPath,true,true) then
  4108. exit(False);
  4109. i:=Namespaces.Count-1;
  4110. while (i>=0) and (CompareText(aPath,NameSpaces[i])<>0) do dec(i);
  4111. if Remove then
  4112. begin
  4113. // remove
  4114. if i>=0 then
  4115. begin
  4116. NameSpaces.Delete(i);
  4117. if NamespacesFromCmdLine>i then dec(FNamespacesFromCmdLine);
  4118. end;
  4119. exit(true);
  4120. end;
  4121. if FromCmdLine then
  4122. begin
  4123. // from cmdline: append in order to the cmdline params, in front of cfg params
  4124. if i>=0 then
  4125. begin
  4126. if i<=NamespacesFromCmdLine then exit(true);
  4127. NameSpaces.Delete(i);
  4128. end;
  4129. NameSpaces.Insert(NamespacesFromCmdLine,aPath);
  4130. inc(FNamespacesFromCmdLine);
  4131. end else begin
  4132. // from cfg: append in reverse order to the cfg params, behind cmdline params
  4133. if i>=0 then
  4134. begin
  4135. if i<=FNamespacesFromCmdLine+Added then exit(true);
  4136. NameSpaces.Delete(i);
  4137. end;
  4138. NameSpaces.Insert(FNamespacesFromCmdLine+Added,aPath);
  4139. inc(Added);
  4140. end;
  4141. Result:=true;
  4142. end;
  4143. var
  4144. aPath: String;
  4145. p: integer;
  4146. begin
  4147. p:=1;
  4148. Added:=0;
  4149. while p<=length(Paths) do
  4150. begin
  4151. aPath:=GetNextDelimitedItem(Paths,';',p);
  4152. if aPath='' then
  4153. continue;
  4154. if not Add(aPath) then
  4155. exit;
  4156. end;
  4157. end;
  4158. procedure TPas2jsCompiler.Reset;
  4159. begin
  4160. FResolverHub.Reset;
  4161. FreeAndNil(FWPOAnalyzer);
  4162. FPrecompileGUID:=default(TGUID);
  4163. FNamespaces.Clear;
  4164. FNamespacesFromCmdLine:=0;
  4165. FMainFile:=nil;
  4166. FUnits.Clear;
  4167. FReadingModules.Clear;
  4168. FFiles.FreeItems;
  4169. FInsertFilenames.Clear;
  4170. FAppendFIleNames.Clear;
  4171. if Assigned(FPostProcessorSupport) then
  4172. FPostProcessorSupport.Clear;
  4173. FCompilerExe:='';
  4174. FSrcMapBaseDir:='';
  4175. FMainSrcFile:='';
  4176. FOptions:=DefaultP2jsCompilerOptions;
  4177. FRTLVersionCheck:=DefaultP2jsRTLVersionCheck;
  4178. FModeSwitches:=p2jsMode_SwitchSets[p2jmObjFPC];
  4179. FConverterGlobals.Reset;
  4180. FConverterGlobals.RTLVersion:=(VersionMajor*100+VersionMinor)*100+VersionRelease;
  4181. FConverterGlobals.TargetPlatform:=PlatformBrowser;
  4182. FConverterGlobals.TargetProcessor:=ProcessorECMAScript5;
  4183. FMainJSFileIsResolved:=False;
  4184. Log.Reset;
  4185. Log.ShowMsgTypes:=GetShownMsgTypes;
  4186. ClearDefines;
  4187. TStringList(FDefines).Sorted:=True;
  4188. {$IFDEF FPC}
  4189. TStringList(FDefines).Duplicates:=dupError;
  4190. {$ENDIF}
  4191. AddDefine('PAS2JS');
  4192. AddDefine('PAS2JS_FULLVERSION',IntToStr((VersionMajor*100+VersionMinor)*100+VersionRelease));
  4193. AddDefinesForTargetPlatform;
  4194. AddDefinesForTargetProcessor;
  4195. // add FPC compatibility flags
  4196. AddDefine('FPC_HAS_FEATURE_CLASSES');
  4197. AddDefine('FPC_HAS_FEATURE_INIT');
  4198. AddDefine('FPC_HAS_FEATURE_DYNARRAYS');
  4199. AddDefine('FPC_HAS_FEATURE_EXCEPTIONS');
  4200. AddDefine('FPC_HAS_FEATURE_EXITCODE');
  4201. AddDefine('FPC_HAS_FEATURE_INITFINAL');
  4202. AddDefine('FPC_HAS_FEATURE_RTTI');
  4203. AddDefine('FPC_HAS_FEATURE_SUPPORT');
  4204. AddDefine('FPC_HAS_FEATURE_UNICODESTRINGS');
  4205. AddDefine('FPC_HAS_FEATURE_WIDESTRINGS');
  4206. AddDefine('FPC_HAS_TYPE_DOUBLE');
  4207. AddDefine('FPC_HAS_UNICODESTRING');
  4208. AddDefine('FPC_UNICODESTRINGS');
  4209. AddDefine('FPC_WIDESTRING_EQUAL_UNICODESTRING');
  4210. AddDefine('STR_CONCAT_PROCS');
  4211. AddDefine('UNICODE');
  4212. FHasShownLogo:=false;
  4213. FHasShownEncoding:=false;
  4214. FFS.Reset;
  4215. end;
  4216. procedure TPas2jsCompiler.Run(aCompilerExe: string; aWorkingDir: string;
  4217. ParamList: TStrings; DoReset: boolean);
  4218. var
  4219. i: Integer;
  4220. StartTime: TDateTime;
  4221. begin
  4222. StartTime:=Now;
  4223. if DoReset then Reset;
  4224. if FileCount>0 then
  4225. RaiseInternalError(20170504161340,'internal error: TPas2jsCompiler.Run FileCount>0');
  4226. try
  4227. // set working directory, needed by all relative filenames
  4228. SetWorkingDir(aWorkingDir);
  4229. CompilerExe:=aCompilerExe; // maybe needed to find the default config
  4230. // quick check command line params
  4231. for i:=0 to ParamList.Count-1 do
  4232. ReadParam(ParamList[i],true,true);
  4233. if WriteDebugLog then
  4234. Log.OpenDebugLog;
  4235. if ShowLogo then
  4236. WriteLogo;
  4237. // read default config
  4238. if Assigned(ConfigSupport) and not SkipDefaultConfig then
  4239. ConfigSupport.LoadDefaultConfig;
  4240. // read env PAS2JS_OPTS
  4241. ReadEnvironment;
  4242. // read command line parameters
  4243. for i:=0 to ParamList.Count-1 do
  4244. ReadParam(ParamList[i],false,true);
  4245. // At this point we know what kind of resources we must use
  4246. CreateResourceSupport;
  4247. // now we know, if the logo can be displayed
  4248. if ShowLogo then
  4249. WriteLogo;
  4250. // show debug info
  4251. if ShowDebug then
  4252. begin
  4253. WriteOptions;
  4254. WriteDefines;
  4255. end;
  4256. if ShowDebug or ShowUsedTools then
  4257. WriteUsedTools;
  4258. if ShowDebug or ShowTriedUsedFiles then
  4259. WriteFoldersAndSearchPaths;
  4260. if MainSrcFile='' then
  4261. ParamFatal('No source file name in command line');
  4262. if not FS.FileExists(MainSrcFile) then
  4263. ParamFatal('Pascal file not found: "'+MainSrcFile+'"');
  4264. // compile
  4265. Compile(StartTime);
  4266. except
  4267. on E: ECompilerTerminate do
  4268. begin
  4269. end;
  4270. on E: Exception do begin
  4271. if ShowDebug then
  4272. Log.LogExceptionBackTrace(E);
  4273. Log.Log(mtFatal,E.Message);
  4274. raise; // reraise unexpected exception
  4275. end else begin
  4276. if ShowDebug then
  4277. Log.LogExceptionBackTrace(nil);
  4278. {$IFDEF Pas2js}
  4279. HandleJSException('[20181031190933] TPas2jsCompiler.Run',JSExceptValue,false);
  4280. {$ENDIF}
  4281. raise; // reraise unexpected exception
  4282. end;
  4283. end;
  4284. end;
  4285. procedure TPas2jsCompiler.WriteHelpLine(S: String);
  4286. const
  4287. MaxLineLen = 78;
  4288. Indent = 12;
  4289. var
  4290. l, p, LastCharStart, WordBreak: integer;
  4291. Len: integer;
  4292. CodePointCount: Integer;
  4293. procedure InitLine;
  4294. begin
  4295. l:=length(s);
  4296. p:=1;
  4297. LastCharStart:=p;
  4298. WordBreak:=0;
  4299. CodePointCount:=0;
  4300. end;
  4301. begin
  4302. if length(s)<=MaxLineLen then
  4303. begin
  4304. Log.LogRaw(s);
  4305. exit;
  4306. end;
  4307. InitLine;
  4308. while p<=l do
  4309. begin
  4310. case s[p] of
  4311. 'a'..'z','A'..'Z','0'..'9','_','-','.',',','"','''','`',
  4312. #128..high(char) :
  4313. begin
  4314. LastCharStart:=p;
  4315. {$IFDEF FPC_HAS_CPSTRING}
  4316. Len:=UTF8CharacterStrictLength(@s[p]);
  4317. if Len=0 then Len:=1;
  4318. inc(p,Len);
  4319. {$ELSE}
  4320. if (p<l) and (s[p] in [#$DC00..#$DFFF]) then
  4321. inc(p,2)
  4322. else
  4323. inc(p,1);
  4324. {$ENDIF}
  4325. end;
  4326. else
  4327. LastCharStart:=p;
  4328. WordBreak:=p;
  4329. inc(p);
  4330. end;
  4331. inc(CodePointCount);
  4332. if CodePointCount>=MaxLineLen then
  4333. begin
  4334. if (WordBreak=0)
  4335. or (WordBreak<MaxLineLen div {$IFDEF FPC_HAS_CPSTRING}3{$ELSE}2{$ENDIF}) then
  4336. WordBreak:=LastCharStart;
  4337. Len:=WordBreak-1;
  4338. Log.LogRaw(LeftStr(s,Len));
  4339. Delete(s,1,len);
  4340. s:=StringOfChar(' ',Indent)+Trim(s);
  4341. InitLine;
  4342. end;
  4343. end;
  4344. Log.LogRaw(s);
  4345. end;
  4346. function TPas2jsCompiler.LoadFile(Filename: string; Binary: boolean
  4347. ): TPas2jsFile;
  4348. begin
  4349. try
  4350. Result:=FS.LoadFile(Filename,Binary);
  4351. except
  4352. on E: Exception do
  4353. begin
  4354. Log.Log(mtError,E.Message);
  4355. Terminate(ExitCodeFileNotFound);
  4356. end
  4357. end;
  4358. end;
  4359. procedure TPas2jsCompiler.WriteHelp;
  4360. procedure w(s: string); inline;
  4361. begin
  4362. WriteHelpLine(S);
  4363. end;
  4364. var
  4365. i: Integer;
  4366. ParamMacro: TPas2jsMacro;
  4367. begin
  4368. WriteLogo;
  4369. Log.LogLn;
  4370. if CompilerExe<>'' then
  4371. begin
  4372. w('Usage: '+CompilerExe+' <your.pas>');
  4373. end else begin
  4374. w('Usage: pas2js <your.pas>');
  4375. end;
  4376. Log.LogLn;
  4377. w('Options:');
  4378. w('Put + after a boolean switch option to enable it, - to disable it');
  4379. w(' @<x> : Read compiler options from file <x> in addition to the default '+DefaultConfigFile);
  4380. w(' -B : Rebuild all');
  4381. w(' -d<x> : Defines the symbol <x>. Optional: -d<x>:=<value>');
  4382. w(' -i<x> : Write information and halt. <x> is a combination of the following:');
  4383. w(' -iD : Write compiler date');
  4384. w(' -iSO : Write compiler OS');
  4385. w(' -iSP : Write compiler host processor');
  4386. w(' -iTO : Write target platform');
  4387. w(' -iTP : Write target processor');
  4388. w(' -iV : Write short compiler version');
  4389. w(' -iW : Write full compiler version');
  4390. w(' -ic : Write list of supported JS processors usable by -P<x>');
  4391. w(' -im : Write list of supported modeswitches usable by -M<x>');
  4392. w(' -io : Write list of supported optimizations usable by -Oo<x>');
  4393. w(' -it : Write list of supported targets usable by -T<x>');
  4394. w(' -iJ : Write list of supported JavaScript identifiers -JoRTL-<x>');
  4395. w(' -C<x> : Code generation options. <x> is a combination of the following letters:');
  4396. // -C3 Turn on ieee error checking for constants
  4397. w(' o : Overflow checking of integer operations');
  4398. // -CO Check for possible overflow of integer operations
  4399. w(' r : Range checking');
  4400. w(' R : Object checks. Verify method calls and object type casts.');
  4401. w(' -F... Set file names and paths:');
  4402. w(' -Fe<x>: Redirect output to file <x>. UTF-8 encoded.');
  4403. w(' -FE<x>: Set main output path to <x>');
  4404. w(' -Fi<x>: Add <x> to include paths');
  4405. w(' -FN<x>: add <x> to namespaces. Namespaces with trailing - are removed.');
  4406. w(' Delphi calls this flag "unit scope names".');
  4407. //w(' -Fr<x>: Load error message file <x>');
  4408. w(' -Fu<x>: Add <x> to unit paths');
  4409. w(' -FU<x>: Set unit output path to <x>');
  4410. w(' -I<x> : Add <x> to include paths, same as -Fi');
  4411. w(' -J... Extra options of pas2js');
  4412. w(' -Ja<x>: Append JS file <x> to main JS file. E.g. -Jamap.js. Can be given multiple times. To remove a file name append a minus, e.g. -Jamap.js-.');
  4413. w(' -Jc : Write all JavaScript concatenated into the output file');
  4414. w(' -Je<x>: Encode messages as <x>.');
  4415. w(' -Jeconsole: Console codepage. This is the default.');
  4416. w(' -Jesystem : System codepage. On non Windows console and system are the same.');
  4417. w(' -Jeutf-8 : Unicode UTF-8. Default when using -Fe.');
  4418. w(' -JeJSON : Output compiler messages as JSON. Logo etc are outputted as-is.');
  4419. w(' -Ji<x>: Insert JS file <x> into main JS file. E.g. -Jirtl.js. Can be given multiple times. To remove a file name append a minus, e.g. -Jirtl.js-.');
  4420. w(' -Jl : lower case identifiers');
  4421. w(' -Jm : generate source maps');
  4422. w(' -Jmsourceroot=<x>: use x as "sourceRoot", prefix URL for source file names.');
  4423. w(' -Jmbasedir=<x>: write source file names relative to directory x, default is map file folder.');
  4424. w(' -Jminclude: include Pascal sources in source map.');
  4425. w(' -Jmabsolute: store absolute filenames, not relative.');
  4426. w(' -Jmxssiheader: start source map with XSSI protection )]}'', default.');
  4427. w(' -Jm-: disable generating source maps');
  4428. w(' -Jo<x>: Enable or disable extra option. The x is case insensitive:');
  4429. w(' -JoSearchLikeFPC: search source files like FPC, default: search case insensitive.');
  4430. w(' -JoUseStrict: add "use strict" to modules, default.');
  4431. w(' -JoCheckVersion-: do not add rtl version check, default.');
  4432. w(' -JoCheckVersion=main: insert rtl version check into main.');
  4433. w(' -JoCheckVersion=system: insert rtl version check into system unit init.');
  4434. w(' -JoCheckVersion=unit: insert rtl version check into every unit init.');
  4435. w(' -JoRTL-<y>=<z>: set RTL identifier y to value z. See -iJ.');
  4436. w(' -Jr<x> Control writing of resource string file');
  4437. w(' -Jrnone: Do not write resource string file');
  4438. w(' -Jrunit: Write resource string file per unit with all resource strings');
  4439. w(' -Jrprogram: Write resource string file per program with all used resource strings in program');
  4440. w(' -JR<x> Control writing of linked resources');
  4441. w(' -JRnone: Skip resource directives');
  4442. w(' -JRjs: Write resources in Javascript structure');
  4443. w(' -JRhtml[=filename] : Write resources as preload links in HTML file (default is projectfile-res.html)');
  4444. w(' -Jpcmd<command>: Run postprocessor. For each generated js execute command passing the js as stdin and read the new js from stdout. This option can be added multiple times to call several postprocessors in succession.');
  4445. w(' -Ju<x>: Add <x> to foreign unit paths. Foreign units are not compiled.');
  4446. WritePrecompiledFormats;
  4447. w(' -l : Write logo');
  4448. w(' -M<x> : Set language mode or enable/disable a modeswitch');
  4449. w(' -MDelphi: Delphi 7 compatibility mode');
  4450. w(' -MObjFPC: FPC''s Object Pascal compatibility mode (default)');
  4451. w(' -M<x> : enable or disable modeswitch, see option -im');
  4452. w(' Each mode (as listed above) enables its default set of modeswitches.');
  4453. w(' Other modeswitches are disabled and need to be enabled one by another.');
  4454. w(' -NS<x> : obsolete: add <x> to namespaces. Same as -FN<x>');
  4455. w(' -n : Do not read the default config files');
  4456. w(' -o<x> : Change main JavaScript file to <x>, "." means stdout');
  4457. w(' -O<x> : Optimizations:');
  4458. w(' -O- : Disable optimizations');
  4459. w(' -O1 : Level 1 optimizations (quick and debugger friendly)');
  4460. w(' -O2 : Level 2 optimizations (Level 1 + not debugger friendly)');
  4461. w(' -Oo<x>: Enable or disable optimization. The x is case insensitive:');
  4462. w(' -OoEnumNumbers[-]: write enum value as number instead of name. Default in -O1.');
  4463. w(' -OoRemoveNotUsedPrivates[-]: Default is enabled');
  4464. w(' -OoRemoveNotUsedDeclarations[-]: Default enabled for programs with -Jc');
  4465. w(' -OoRemoveNotUsedPublished[-] : Default is disabled');
  4466. w(' -OoShortRefGlobals[-]: Insert JS local var for types, modules and static functions. Default enabled in -O2');
  4467. {$IFDEF EnableObfuscateIdentifiers}
  4468. w(' -OoObfuscateLocalIdentifiers[-]: Use auto generated names for private and local Pascal identifiers. Default enabled in -O2');
  4469. {$ENDIF}
  4470. w(' -P<x> : Set target processor. Case insensitive:');
  4471. w(' -Pecmascript5: default');
  4472. w(' -Pecmascript6');
  4473. w(' -S<x> : Syntax options. <x> is a combination of the following letters:');
  4474. w(' 2 : Same as -Mobjfpc (default)');
  4475. w(' a : Turn on assertions');
  4476. w(' c : Support operators like C (*=,+=,/= and -=)');
  4477. w(' d : Same as -Mdelphi');
  4478. w(' j : Allows typed constants to be writeable (default)');
  4479. w(' m : Enables macro replacements');
  4480. w(' -SI<x> : Set interface style to <x>');
  4481. w(' -SIcom : COM, reference counted interface (default)');
  4482. w(' -SIcorba: CORBA interface');
  4483. w(' -T<x> : Set target platform');
  4484. w(' -Tbrowser: default');
  4485. w(' -Tnodejs : add pas.run(), includes -Jc');
  4486. w(' -Telectron: experimental');
  4487. w(' -Tmodule : add pas.run(), includes -Jc');
  4488. w(' -u<x> : Undefines the symbol <x>');
  4489. w(' -v<x> : Be verbose. <x> is a combination of the following letters:');
  4490. w(' e : Show errors (default)');
  4491. w(' w : Show warnings');
  4492. w(' n : Show notes');
  4493. w(' h : Show hints');
  4494. w(' i : Show info');
  4495. w(' l : Show line numbers, needs -vi');
  4496. w(' a : Show everything');
  4497. w(' 0 : Show nothing (except errors)');
  4498. w(' b : Show file names with full path');
  4499. w(' c : Show conditionals');
  4500. w(' t : Show tried/used files');
  4501. w(' d : Show debug notes and info, enables -vni');
  4502. w(' q : Show message numbers');
  4503. w(' x : Show used tools');
  4504. w(' v : Write pas2jsdebug.log with lots of debugging info');
  4505. w(' z : Write messages to stderr, -o. still uses stdout.');
  4506. w(' -vm<x>,<y>: Do not show messages numbered <x> and <y>.');
  4507. w(' -? : Show this help');
  4508. w(' -h : Show this help');
  4509. Log.LogLn;
  4510. w('Environment variable PAS2JS_OPTS is parsed after default config and before command line parameters.');
  4511. Log.LogLn;
  4512. w('Macros: Format is $Name, $Name$ or $Name()');
  4513. for i:=0 to ParamMacros.Count-1 do begin
  4514. ParamMacro:=ParamMacros[i];
  4515. Log.LogRaw([' $',ParamMacro.Name,BoolToStr(ParamMacro.CanHaveParams,'()',''),': ',ParamMacro.Description]);
  4516. end;
  4517. end;
  4518. procedure TPas2jsCompiler.WriteLogo;
  4519. begin
  4520. if FHasShownLogo then exit;
  4521. FHasShownLogo:=true;
  4522. WriteVersionLine;
  4523. Log.LogPlain('Copyright (c) 2021 Free Pascal team.');
  4524. if coShowInfos in Options then
  4525. WriteEncoding;
  4526. end;
  4527. procedure TPas2jsCompiler.WriteEncoding;
  4528. begin
  4529. if FHasShownEncoding then exit;
  4530. FHasShownEncoding:=true;
  4531. Log.LogMsgIgnoreFilter(nMessageEncodingIs,[Log.GetEncodingCaption]);
  4532. end;
  4533. procedure TPas2jsCompiler.WriteVersionLine;
  4534. var
  4535. s: String;
  4536. begin
  4537. s:='Pas2JS Compiler version '+GetVersion(false);
  4538. s:=s+' ['+{$i %Date%}+'] for '+{$i %FPCTargetOS%}+' '+{$i %FPCTargetCPU%};
  4539. Log.LogPlain(s);
  4540. if coShowInfos in Options then
  4541. WriteEncoding;
  4542. end;
  4543. procedure TPas2jsCompiler.WriteOptions;
  4544. var
  4545. co: TP2jsCompilerOption;
  4546. fco: TP2jsFSOption;
  4547. ms: TModeSwitch;
  4548. begin
  4549. // message encoding
  4550. WriteEncoding;
  4551. // target platform
  4552. Log.LogMsgIgnoreFilter(nTargetPlatformIs,[PasToJsPlatformNames[TargetPlatform]]);
  4553. Log.LogMsgIgnoreFilter(nTargetProcessorIs,[PasToJsProcessorNames[TargetProcessor]]);
  4554. // syntax mode
  4555. for ms in msAllPas2jsModeSwitches do
  4556. case ms of
  4557. msObjfpc:
  4558. if ms in ModeSwitches then
  4559. Log.LogMsgIgnoreFilter(nSyntaxModeIs,[p2jscModeNames[p2jmObjFPC]]);
  4560. msDelphi:
  4561. if ms in ModeSwitches then
  4562. Log.LogMsgIgnoreFilter(nSyntaxModeIs,[p2jscModeNames[p2jmDelphi]]);
  4563. else
  4564. Log.LogMsgIgnoreFilter(nModeswitchXisY,[SModeSwitchNames[ms],BoolToStr(ms in ModeSwitches,'on','off')]);
  4565. end;
  4566. Log.LogMsgIgnoreFilter(nClassInterfaceStyleIs,[InterfaceTypeNames[InterfaceType]]);
  4567. // boolean options
  4568. for co in TP2jsCompilerOption do
  4569. Log.LogMsgIgnoreFilter(nOptionIsEnabled,
  4570. [p2jscoCaption[co],BoolToStr(co in Options,'enabled','disabled')]);
  4571. for fco in TP2jsFSOption do
  4572. Log.LogMsgIgnoreFilter(nOptionIsEnabled,
  4573. [p2jsfcoCaption[fco],BoolToStr(fco in FS.Options,'enabled','disabled')]);
  4574. // source map options
  4575. if SrcMapEnable then
  4576. begin
  4577. Log.LogMsgIgnoreFilter(nSrcMapSourceRootIs,[QuoteStr(SrcMapSourceRoot)]);
  4578. Log.LogMsgIgnoreFilter(nSrcMapBaseDirIs,[QuoteStr(SrcMapBaseDir)]);
  4579. end;
  4580. end;
  4581. procedure TPas2jsCompiler.WriteDefines;
  4582. var
  4583. i: Integer;
  4584. S: String;
  4585. M: TMacroDef;
  4586. pbi: TPas2JSBuiltInName;
  4587. begin
  4588. for i:=0 to Defines.Count-1 do
  4589. begin
  4590. S:=Defines[i];
  4591. M:=TMacroDef(Defines.Objects[i]);
  4592. if M<>nil then
  4593. Log.Log(mtInfo,SafeFormat(SLogMacroXSetToY,[S,QuoteStr(M.Value)]),nLogMacroXSetToY,'',0,0,false)
  4594. else
  4595. Log.Log(mtInfo,SafeFormat(SLogMacroDefined,[S]),nLogMacroDefined,'',0,0,false)
  4596. end;
  4597. for pbi in TPas2JSBuiltInName do
  4598. if Pas2JSBuiltInNames[pbi]<>ConverterGlobals.BuiltInNames[pbi] then
  4599. begin
  4600. WriteStr(S,pbi);
  4601. S:=copy(S,4,255);
  4602. Log.LogMsgIgnoreFilter(nRTLIdentifierChanged,[QuoteStr(S),
  4603. QuoteStr(Pas2JSBuiltInNames[pbi]),QuoteStr(ConverterGlobals.BuiltInNames[pbi])]);
  4604. end;
  4605. end;
  4606. procedure TPas2jsCompiler.WriteUsedTools;
  4607. begin
  4608. if Assigned(FPostProcessorSupport) then
  4609. FPostProcessorSupport.WriteUsedTools;
  4610. end;
  4611. procedure TPas2jsCompiler.WriteFoldersAndSearchPaths;
  4612. var
  4613. I: integer;
  4614. begin
  4615. Log.LogMsgIgnoreFilter(nNameValue,['Compiler exe',QuoteStr(CompilerExe)]);
  4616. FS.WriteFoldersAndSearchPaths;
  4617. for i:=0 to Namespaces.Count-1 do
  4618. Log.LogMsgIgnoreFilter(nUsingPath,['Unit scope',Namespaces[i]]);
  4619. Log.LogMsgIgnoreFilter(nNameValue,['Output file',QuoteStr(MainJSFile)]);
  4620. end;
  4621. procedure TPas2jsCompiler.WriteInfo;
  4622. var
  4623. Flags: string;
  4624. procedure AppendFlag(const s: string);
  4625. begin
  4626. if s='' then exit;
  4627. if Flags='' then
  4628. Flags:=StringOfChar(' ',Log.Indent)
  4629. else
  4630. Flags:=Flags+',';
  4631. if length(Flags)+length(s)>Log.LineLen then
  4632. begin
  4633. Log.LogPlain(Flags);
  4634. Flags:=StringOfChar(' ',Log.Indent);
  4635. end;
  4636. Flags:=Flags+s;
  4637. end;
  4638. procedure FlushFlags;
  4639. begin
  4640. if Flags='' then exit;
  4641. Log.LogPlain(Flags);
  4642. Flags:='';
  4643. end;
  4644. var
  4645. ms: TModeSwitch;
  4646. begin
  4647. WriteVersionLine;
  4648. Log.LogLn;
  4649. Log.LogPlain('Compiler date : '+GetCompiledDate);
  4650. Log.LogPlain('Compiler CPU target: '+GetCompiledTargetCPU);
  4651. Log.LogLn;
  4652. Log.LogPlain('Supported targets (targets marked with ''{*}'' are under development):');
  4653. Log.LogPlain([' ',PasToJsPlatformNames[PlatformBrowser],': webbrowser']);
  4654. Log.LogPlain([' ',PasToJsPlatformNames[PlatformNodeJS],': Node.js']);
  4655. Log.LogPlain([' ',PasToJsPlatformNames[PlatformElectron],': Electron app']);
  4656. Log.LogLn;
  4657. Log.LogPlain('Supported CPU instruction sets:');
  4658. Log.LogPlain(' ECMAScript5, ECMAScript6');
  4659. Log.LogLn;
  4660. Log.LogPlain('Recognized compiler and RTL features:');
  4661. Flags:='';
  4662. AppendFlag('INITFINAL');
  4663. AppendFlag('RTTI');
  4664. AppendFlag('CLASSES');
  4665. AppendFlag('EXCEPTIONS');
  4666. AppendFlag('EXITCODE');
  4667. AppendFlag('WIDESTRINGS');
  4668. AppendFlag('RANDOM');
  4669. AppendFlag('DYNARRAYS');
  4670. AppendFlag('COMMANDARGS');
  4671. AppendFlag('RESOURCES');
  4672. AppendFlag('UNICODESTRINGS');
  4673. FlushFlags;
  4674. Log.LogLn;
  4675. Log.LogPlain('Recognized modeswitches:');
  4676. Flags:='';
  4677. for ms in (msAllPas2jsModeSwitches-AllLanguageModes) do
  4678. AppendFlag(SModeSwitchNames[ms]);
  4679. FlushFlags;
  4680. Log.LogLn;
  4681. Log.LogPlain('Supported Optimizations:');
  4682. Log.LogPlain(' EnumNumbers');
  4683. Log.LogPlain(' RemoveNotUsedPrivates');
  4684. Log.LogPlain(' ShortRefGlobals');
  4685. Log.LogLn;
  4686. Log.LogPlain('Supported Whole Program Optimizations:');
  4687. Log.LogPlain(' RemoveNotUsedDeclarations');
  4688. Log.LogLn;
  4689. Log.LogPlain('This program comes under the Library GNU General Public License');
  4690. Log.LogPlain('For more information read COPYING.FPC, included in this distribution');
  4691. Log.LogLn;
  4692. Log.LogPlain('Please report bugs in our bug tracker on:');
  4693. Log.LogPlain(' http://bugs.freepascal.org');
  4694. Log.LogLn;
  4695. Log.LogPlain('More information may be found on our WWW pages (including directions');
  4696. Log.LogPlain('for mailing lists useful for asking questions or discussing potential');
  4697. Log.LogPlain('new features, etc.):');
  4698. Log.LogPlain(' http://www.freepascal.org');
  4699. end;
  4700. function TPas2jsCompiler.GetShownMsgTypes: TMessageTypes;
  4701. begin
  4702. Result:=[mtFatal];
  4703. if coShowErrors in FOptions then Include(Result,mtError);
  4704. if coShowWarnings in FOptions then Include(Result,mtWarning);
  4705. if coShowNotes in FOptions then Include(Result,mtNote);
  4706. if coShowHints in FOptions then Include(Result,mtHint);
  4707. if coShowInfos in FOptions then Include(Result,mtInfo);
  4708. if coShowDebug in FOptions then Include(Result,mtDebug);
  4709. end;
  4710. procedure TPas2jsCompiler.SetOption(Flag: TP2jsCompilerOption; Enable: boolean);
  4711. begin
  4712. if Enable then
  4713. Options:=Options+[Flag]
  4714. else
  4715. Options:=Options-[Flag];
  4716. end;
  4717. function TPas2jsCompiler.FindFileWithUnitFilename(UnitFilename: string): TPas2jsCompilerFile;
  4718. begin
  4719. if UnitFilename='' then exit(nil);
  4720. Result:=TPas2jsCompilerFile(FFiles.FindKey(Pointer(UnitFilename)));
  4721. end;
  4722. function TPas2jsCompiler.CreateCompilerFile(const PasFileName,
  4723. PCUFilename: String): TPas2jsCompilerFile;
  4724. begin
  4725. Result:=TPas2jsCompilerFile.Create(Self,PasFileName,PCUFilename);
  4726. end;
  4727. procedure TPas2jsCompiler.LoadModuleFile(UnitFilename, UseUnitName: string; out
  4728. aFile: TPas2jsCompilerFile; isPCU: Boolean);
  4729. // Creates aFile and opens the file, ready for parsing
  4730. // Note: aFile must be an out parameter and not a function result, so it is
  4731. // already set while running
  4732. var
  4733. aPasTree: TPas2jsCompilerResolver;
  4734. ExpUnitFilename: String;
  4735. begin
  4736. aFile:=nil;
  4737. Log.LogMsg(nParsingFile,[FormatPath(UnitFilename)],'',0,0,not (coShowLineNumbers in Options));
  4738. ExpUnitFilename:=UnitFilename;
  4739. if ExpUnitFilename<>'' then
  4740. ExpUnitFilename:=ExpandFileName(ExpUnitFilename);
  4741. aFile:=FindFileWithUnitFilename(ExpUnitFilename);
  4742. if aFile<>nil then exit;
  4743. if (ExpUnitFilename='') or not FS.FileExists(ExpUnitFilename) then
  4744. begin
  4745. if isPCU then
  4746. Log.LogMsg(nUnitFileNotFound,[QuoteStr(UnitFilename)])
  4747. else
  4748. Log.LogMsg(nSourceFileNotFound,[QuoteStr(UnitFilename)]);
  4749. Terminate(ExitCodeFileNotFound);
  4750. end;
  4751. if FS.DirectoryExists(ExpUnitFilename) then
  4752. begin
  4753. Log.LogMsg(nFileIsFolder,[QuoteStr(UnitFilename)]);
  4754. Terminate(ExitCodeFileNotFound);
  4755. end;
  4756. if isPCU then
  4757. aFile:=CreateCompilerFile('',ExpUnitFilename)
  4758. else
  4759. aFile:=CreateCompilerFile(ExpUnitFilename,'');
  4760. if UseUnitName<>'' then
  4761. begin
  4762. {$IFDEF VerboseSetPasUnitName}
  4763. writeln('TPas2jsCompiler.LoadModuleFile File="',aFile.UnitFilename,'" UseUnit="',UseUnitName,'"');
  4764. {$ENDIF}
  4765. if CompareText(ExtractFilenameOnly(UnitFilename),UseUnitName)=0 then
  4766. aFile.PasUnitName:=UseUnitName // e.g. when searching Unit1, found UNIT1.pas, use Unit1
  4767. else
  4768. aFile.PasUnitName:=ExtractFilenameOnly(UnitFilename);
  4769. end;
  4770. FFiles.Add(aFile);
  4771. // do not add here aFile to FUnits
  4772. aFile.ShowDebug:=ShowDebug;
  4773. if aFile.IsMainFile then
  4774. aFile.JSFilename:=GetResolvedMainJSFile;
  4775. // pastree (engine)
  4776. aPasTree:=aFile.PascalResolver;
  4777. if coShowLineNumbers in Options then
  4778. aPasTree.ScannerLogEvents:=aPasTree.ScannerLogEvents+[sleLineNumber];
  4779. if coShowConditionals in Options then
  4780. aPasTree.ScannerLogEvents:=aPasTree.ScannerLogEvents+[sleConditionals];
  4781. if [coShowLineNumbers,coShowInfos,coShowDebug]*Options<>[] then
  4782. aPasTree.ParserLogEvents:=aPasTree.ParserLogEvents+[pleInterface,pleImplementation];
  4783. // scanner
  4784. aFile.ResourceHandler:=FResources;;
  4785. aFile.CreateScannerAndParser(FS.CreateResolver);
  4786. aFile.Scanner.OnLinkLib:=@HandleLinkLibStatement;
  4787. if ShowDebug then
  4788. Log.LogPlain(['Debug: Opening file "',UnitFilename,'"...']);
  4789. if IsPCU then
  4790. begin
  4791. aFile.FileResolver.BaseDirectory:=ExtractFilePath(UnitFilename);
  4792. aFile.PCUSupport.CreatePCUReader;
  4793. end
  4794. else
  4795. begin
  4796. // open file (beware: this changes FileResolver.BaseDirectory)
  4797. aFile.OpenFile(UnitFilename);
  4798. end;
  4799. end;
  4800. function TPas2jsCompiler.FindUnitJSFileName(aFileName: String): String;
  4801. begin
  4802. if AllJSIntoMainJS then
  4803. Result:=GetResolvedMainJSFile
  4804. else
  4805. Result:=FS.FindUnitJSFileName(aFilename);
  4806. end;
  4807. function TPas2jsCompiler.FindLoadedUnit(const TheUnitName: string
  4808. ): TPas2jsCompilerFile;
  4809. begin
  4810. if not IsValidIdent(TheUnitName,true) then exit(nil);
  4811. Result:=TPas2jsCompilerFile(FUnits.FindKey(Pointer(TheUnitName)));
  4812. end;
  4813. procedure TPas2jsCompiler.AddUsedUnit(aFile: TPas2jsCompilerFile);
  4814. var
  4815. OldFile: TPas2jsCompilerFile;
  4816. begin
  4817. if aFile.PasUnitName='' then
  4818. RaiseInternalError(20170504161347,'missing PasUnitName "'+aFile.UnitFilename+'"');
  4819. OldFile:=FindLoadedUnit(aFile.PasUnitName);
  4820. if OldFile<>nil then
  4821. begin
  4822. if OldFile<>aFile then
  4823. RaiseInternalError(20170504161354,'duplicate unit "'+OldFile.PasUnitName+'" "'+aFile.UnitFilename+'" "'+OldFile.UnitFilename+'"');
  4824. end else begin
  4825. FUnits.Add(aFile);
  4826. end;
  4827. end;
  4828. function TPas2jsCompiler.ExpandFileName(const Filename: string): string;
  4829. begin
  4830. Result:=FS.ExpandFileName(Filename);
  4831. end;
  4832. procedure TPas2jsCompiler.InsertCustomJSFiles(aWriter: TPas2JSMapper);
  4833. var
  4834. i: Integer;
  4835. Filename: String;
  4836. FileResolver: TPas2jsFSResolver;
  4837. aFile: TPas2jsFile;
  4838. begin
  4839. if InsertFilenames.Count=0 then exit;
  4840. FileResolver:=FS.CreateResolver;
  4841. try
  4842. for i:=0 to InsertFilenames.Count-1 do begin
  4843. Filename:=FS.FindCustomJSFileName(InsertFilenames[i]);
  4844. if Filename='' then
  4845. begin
  4846. Log.LogMsg(nCustomJSFileNotFound,[InsertFilenames[i]]);
  4847. Terminate(ExitCodeFileNotFound);
  4848. end;
  4849. aFile:=LoadFile(Filename);
  4850. if aFile.Source='' then continue;
  4851. aWriter.WriteFile(aFile.Source,Filename);
  4852. end
  4853. finally
  4854. FileResolver.Free;
  4855. end;
  4856. end;
  4857. procedure TPas2jsCompiler.AppendCustomJSFiles(aWriter: TPas2JSMapper);
  4858. var
  4859. i: Integer;
  4860. Filename: String;
  4861. FileResolver: TPas2jsFSResolver;
  4862. aFile: TPas2jsFile;
  4863. begin
  4864. if AppendFilenames.Count=0 then exit;
  4865. FileResolver:=FS.CreateResolver;
  4866. try
  4867. for i:=0 to AppendFilenames.Count-1 do begin
  4868. Filename:=FS.FindCustomJSFileName(AppendFilenames[i]);
  4869. if Filename='' then
  4870. begin
  4871. Log.LogMsg(nCustomJSFileNotFound,[AppendFilenames[i]]);
  4872. Terminate(ExitCodeFileNotFound);
  4873. end;
  4874. aFile:=LoadFile(Filename);
  4875. if aFile.Source='' then continue;
  4876. aWriter.WriteFile(aFile.Source,Filename);
  4877. end
  4878. finally
  4879. FileResolver.Free;
  4880. end;
  4881. end;
  4882. function TPas2jsCompiler.IndexOfInsertJSFilename(const aFilename: string
  4883. ): integer;
  4884. var
  4885. i: Integer;
  4886. begin
  4887. for i:=0 to FInsertFilenames.Count-1 do
  4888. if FS.SameFileName(aFilename,InsertFilenames[i]) then
  4889. exit(i);
  4890. Result:=-1;
  4891. end;
  4892. procedure TPas2jsCompiler.AddInsertJSFilename(const aFilename: string);
  4893. begin
  4894. if IndexOfInsertJSFilename(aFilename)<0 then
  4895. InsertFilenames.Add(aFilename);
  4896. end;
  4897. procedure TPas2jsCompiler.RemoveInsertJSFilename(const aFilename: string);
  4898. var
  4899. i: Integer;
  4900. begin
  4901. i:=IndexOfInsertJSFilename(aFilename);
  4902. if i>=0 then
  4903. InsertFilenames.Delete(i);
  4904. end;
  4905. function TPas2jsCompiler.IndexOfAppendJSFilename(const aFilename: string
  4906. ): integer;
  4907. var
  4908. i: Integer;
  4909. begin
  4910. for i:=0 to AppendFilenames.Count-1 do
  4911. if FS.SameFileName(aFilename,AppendFilenames[i]) then
  4912. exit(i);
  4913. Result:=-1;
  4914. end;
  4915. procedure TPas2jsCompiler.RemoveAppendJSFilename(const aFilename: string);
  4916. var
  4917. i: Integer;
  4918. begin
  4919. i:=IndexOfAppendJSFilename(aFilename);
  4920. if i>=0 then
  4921. AppendFilenames.Delete(i);
  4922. end;
  4923. procedure TPas2jsCompiler.AddAppendJSFilename(const aFilename: string);
  4924. begin
  4925. if IndexOfAppendJSFilename(aFilename)<0 then
  4926. AppendFilenames.Add(aFilename);
  4927. end;
  4928. function TPas2jsCompiler.GetResolvedMainJSFile: string;
  4929. begin
  4930. if not FMainJSFileIsResolved then
  4931. begin
  4932. FMainJSFileResolved:=ResolvedMainJSFile;
  4933. FMainJSFileIsResolved:=True;
  4934. end;
  4935. Result:=FMainJSFileResolved;
  4936. end;
  4937. function TPas2jsCompiler.GetUnitInfo(UseUnitName, InFileName,
  4938. ModuleDir: String; PCUSupport: TPCUSupport): TFindUnitInfo;
  4939. var
  4940. FoundPasFilename, FoundPasUnitName: string;
  4941. FoundPasIsForeign: Boolean;
  4942. FoundPCUFilename, FoundPCUUnitName: string;
  4943. function TryUnitName(const TestUnitName: string): boolean;
  4944. var
  4945. aFile: TPas2jsCompilerFile;
  4946. begin
  4947. if FoundPasFilename='' then
  4948. begin
  4949. // search loaded units
  4950. aFile:=FindLoadedUnit(TestUnitName);
  4951. if aFile<>nil then
  4952. begin
  4953. if aFile.PasFilename<>'' then
  4954. begin
  4955. FoundPasFilename:=aFile.PasFilename;
  4956. FoundPasUnitName:=TestUnitName;
  4957. end else if Assigned(PCUSupport) and (aFile.PCUFilename<>'')
  4958. and (FoundPCUFilename='') then
  4959. begin
  4960. FoundPCUFilename:=aFile.PCUFilename;
  4961. FoundPCUUnitName:=TestUnitName;
  4962. end;
  4963. end else begin
  4964. // search pas in unit path
  4965. FoundPasFilename:=FS.FindUnitFileName(TestUnitName,'',ModuleDir,FoundPasIsForeign);
  4966. if FoundPasFilename<>'' then
  4967. FoundPasUnitName:=TestUnitName;
  4968. end;
  4969. end;
  4970. if Assigned(PCUSupport) and (FoundPCUFilename='')
  4971. and (FoundPasFilename='') // for now: search pcu only if there is no pas
  4972. then
  4973. begin
  4974. FoundPCUFilename:=PCUSupport.FindPCU(TestUnitName);
  4975. if FoundPCUFilename<>'' then
  4976. FoundPCUUnitName:=TestUnitName;
  4977. end;
  4978. Result:=(FoundPasFilename<>'')
  4979. and (not Assigned(PCUSupport) or (FoundPCUFilename<>''));
  4980. end;
  4981. var
  4982. aNameSpace, DefNameSpace: String;
  4983. i: Integer;
  4984. begin
  4985. //writeln('TPas2jsCompiler.GetUnitInfo ',UseUnitName,' in=',InFileName,' ',GetObjName(PCUSupport));
  4986. Result:=Default(TFindUnitInfo);
  4987. FoundPasFilename:='';
  4988. FoundPasIsForeign:=false;
  4989. FoundPasUnitName:='';
  4990. FoundPCUFilename:='';
  4991. FoundPCUUnitName:='';
  4992. if InFilename='' then
  4993. begin
  4994. CheckUnitAlias(UseUnitName);
  4995. // first search with name as written in module
  4996. if not TryUnitName(UseUnitname) then
  4997. begin
  4998. if Pos('.',UseUnitname)<1 then
  4999. begin
  5000. // generic unit name -> search with namespaces
  5001. // first the cmdline namespaces
  5002. for i:=0 to Namespaces.Count-1 do
  5003. begin
  5004. aNameSpace:=Namespaces[i];
  5005. if aNameSpace='' then continue;
  5006. if TryUnitName(aNameSpace+'.'+UseUnitname) then break;
  5007. end;
  5008. if (FoundPasFilename='') or (FoundPCUFilename='') then
  5009. begin
  5010. // then the default program namespace
  5011. DefNameSpace:=GetDefaultNamespace;
  5012. if DefNameSpace<>'' then
  5013. begin
  5014. i:=Namespaces.Count-1;
  5015. while (i>=0) and not SameText(Namespaces[i],DefNameSpace) do dec(i);
  5016. if i<0 then
  5017. TryUnitName(DefNameSpace+'.'+UseUnitname);
  5018. end;
  5019. end;
  5020. end;
  5021. end
  5022. end else begin
  5023. // search Pascal file with InFilename
  5024. FoundPasFilename:=FS.FindUnitFileName(UseUnitname,InFilename,ModuleDir,FoundPasIsForeign);
  5025. if FoundPasFilename<>'' then
  5026. FoundPasUnitName:=ExtractFilenameOnly(InFilename);
  5027. // Note: at the moment if there is a source do not search for pcu
  5028. // Eventually search for both, load pcu and if that fails unload pcu and load source
  5029. if (FoundPasFilename='') and Assigned(PCUSupport) and (FoundPCUFilename='') then
  5030. begin
  5031. // no pas file -> search pcu
  5032. FoundPCUFilename:=PCUSupport.FindPCU(UseUnitName);
  5033. if FoundPCUFilename<>'' then
  5034. begin
  5035. FoundPCUUnitName:=UseUnitName;
  5036. end;
  5037. end;
  5038. end;
  5039. if (FoundPasFilename='') and (FoundPCUFilename<>'') then
  5040. begin
  5041. Result.FileName:=FoundPCUFilename;
  5042. Result.UnitName:=FoundPCUUnitName;
  5043. Result.isPCU:=True;
  5044. Result.isForeign:=False;
  5045. end else if (FoundPasFileName<>'') then
  5046. begin
  5047. Result.FileName:=FoundPasFilename;
  5048. Result.UnitName:=FoundPasUnitName;
  5049. Result.isPCU:=False;
  5050. Result.isForeign:=FoundPasIsForeign;
  5051. end;
  5052. end;
  5053. procedure TPas2jsCompiler.CheckUnitAlias(var UseUnitName: string);
  5054. begin
  5055. if UseUnitName='' then ;
  5056. end;
  5057. function TPas2jsCompiler.LoadUsedUnit(Info: TLoadUnitInfo;
  5058. Context: TPas2jsCompilerFile): TPas2jsCompilerFile;
  5059. function FindCycle(aFile, SearchFor: TPas2jsCompilerFile;
  5060. var Cycle: TFPList): boolean;
  5061. // Note: when traversing, add every search file to Cycle, to avoid running in circles.
  5062. // When a cycle is detected, clear the Cycle list and build the cycle path
  5063. var
  5064. i: Integer;
  5065. aParent: TPas2jsCompilerFile;
  5066. begin
  5067. Cycle.Add(aFile);
  5068. for i:=0 to aFile.UsedByCount[ubMainSection]-1 do begin
  5069. aParent:=aFile.UsedBy[ubMainSection,i];
  5070. if aParent=SearchFor then
  5071. begin
  5072. // unit cycle found
  5073. Cycle.Clear;
  5074. Cycle.Add(aParent);
  5075. Cycle.Add(aFile);
  5076. exit(true);
  5077. end;
  5078. if Cycle.IndexOf(aParent)>=0 then
  5079. continue;// already searched
  5080. if FindCycle(aParent,SearchFor,Cycle) then
  5081. begin
  5082. Cycle.Add(aFile);
  5083. exit(true);
  5084. end;
  5085. end;
  5086. Result:=false;
  5087. end;
  5088. var
  5089. aFile: TPas2jsCompilerFile;
  5090. procedure CheckCycle;
  5091. var
  5092. i: Integer;
  5093. Cycle: TFPList;
  5094. CyclePath: String;
  5095. begin
  5096. if Context.PasModule.ImplementationSection=nil then
  5097. begin
  5098. // main uses section (e.g. interface or program, not implementation)
  5099. // -> check for cycles
  5100. aFile.FUsedBy[ubMainSection].Add(Context);
  5101. Cycle:=TFPList.Create;
  5102. try
  5103. if FindCycle(aFile,aFile,Cycle) then
  5104. begin
  5105. CyclePath:='';
  5106. for i:=0 to Cycle.Count-1 do begin
  5107. if i>0 then CyclePath+=',';
  5108. CyclePath+=TPas2jsCompilerFile(Cycle[i]).GetModuleName;
  5109. end;
  5110. Context.PascalResolver.RaiseMsg(20180223141537,nUnitCycle,sUnitCycle,[CyclePath],Info.NameExpr);
  5111. end;
  5112. finally
  5113. Cycle.Free;
  5114. end;
  5115. end else begin
  5116. // implementation uses section
  5117. aFile.FUsedBy[ubImplSection].Add(Context);
  5118. end;
  5119. end;
  5120. var
  5121. UseJSFilename: String;
  5122. OtherFile: TPas2jsCompilerFile;
  5123. begin
  5124. Result:=nil;
  5125. aFile:=FindFileWithUnitFilename(Info.UseFilename);
  5126. if aFile<>nil then
  5127. begin
  5128. // known unit
  5129. if (aFile.PasUnitName<>'') and (CompareText(aFile.PasUnitName,Info.UseUnitname)<>0) then
  5130. begin
  5131. Log.LogPlain(['Debug: TPas2jsPasTree.FindUnit unitname MISMATCH aFile.PasUnitname="',aFile.PasUnitName,'"',
  5132. ' Self=',Context.FileResolver.FS.FormatPath(Context.UnitFilename),
  5133. ' Uses=',Info.UseUnitname,
  5134. ' IsForeign=',Context.IsForeign]);
  5135. RaiseInternalError(20170504161412,'TPas2jsPasTree.FindUnit unit name mismatch');
  5136. end;
  5137. CheckCycle;
  5138. end else begin
  5139. // new unit
  5140. if Info.InFilename<>'' then
  5141. begin
  5142. aFile:=FindLoadedUnit(Info.UseUnitname);
  5143. if aFile<>nil then
  5144. begin
  5145. {$IF defined(VerbosePasResolver) or defined(VerbosePas2JS)}
  5146. writeln('TPas2jsCompilerFile.FindUnit in-file unit name duplicate: New=',Info.UseFilename,' Old=',aFile.UnitFilename);
  5147. {$ENDIF}
  5148. Context.PascalResolver.RaiseMsg(20180223141323,nDuplicateFileFound,sDuplicateFileFound,
  5149. [Info.UseFilename,aFile.UnitFilename],Info.InFileExpr);
  5150. end;
  5151. end;
  5152. UseJSFilename:='';
  5153. if (not Context.IsForeign) then
  5154. UseJSFilename:=FindUnitJSFileName(Info.UseFilename);
  5155. // Log.LogPlain(['Debug: TPas2jsPasTree.FindUnit Self=',FileResolver.Cache.FormatPath(UnitFilename),
  5156. // ' Uses=',ActualUnitname,' Found="',FileResolver.Cache.FormatPath(UseFilename),'"',
  5157. // ' IsForeign=',IsForeign,' JSFile="',FileResolver.Cache.FormatPath(useJSFilename),'"']);
  5158. // load Pascal or PCU file
  5159. LoadModuleFile(Info.UseFilename,Info.UseUnitname,aFile,Info.IsPCU);
  5160. // consistency checks
  5161. if aFile.PasUnitName<>Info.UseUnitname then
  5162. RaiseInternalError(20170922143329,'aFile.PasUnitName='+aFile.PasUnitName+' UseUnitname='+Info.UseUnitname);
  5163. if Info.isPCU then
  5164. begin
  5165. if Not FS.SameFileName(aFile.PCUFilename,Info.UseFilename) then
  5166. RaiseInternalError(20180312122331,'aFile.PCUFilename='+aFile.PCUFilename+' UseFilename='+Info.UseFilename);
  5167. end else
  5168. begin
  5169. if Not FS.SameFileName(aFile.UnitFilename,Info.UseFilename) then
  5170. RaiseInternalError(20170922143330,'aFile.UnitFilename='+aFile.UnitFilename+' UseFilename='+Info.UseFilename);
  5171. end;
  5172. if aFile=Context then
  5173. begin
  5174. // unit uses itself -> cycle
  5175. Context.Parser.RaiseParserError(nUnitCycle,[Info.UseUnitname]);
  5176. end;
  5177. // add file to trees
  5178. AddUsedUnit(aFile);
  5179. // consistency checks
  5180. OtherFile:=FindLoadedUnit(Info.UseUnitname);
  5181. if aFile<>OtherFile then
  5182. begin
  5183. if OtherFile=nil then
  5184. RaiseInternalError(20170922143405,'UseUnitname='+Info.UseUnitname)
  5185. else
  5186. RaiseInternalError(20170922143511,'UseUnitname='+Info.UseUnitname+' Found='+OtherFile.PasUnitName);
  5187. end;
  5188. OtherFile:=FindFileWithUnitFilename(Info.UseFilename);
  5189. if aFile<>OtherFile then
  5190. begin
  5191. if OtherFile=nil then
  5192. RaiseInternalError(20180224094625,'UseFilename='+Info.UseFilename)
  5193. else
  5194. RaiseInternalError(20180224094627,'UseFilename='+Info.UseFilename+' Found='+OtherFile.UnitFilename);
  5195. end;
  5196. CheckCycle;
  5197. aFile.JSFilename:=UseJSFilename;
  5198. aFile.IsForeign:=Info.UseIsForeign;
  5199. // read
  5200. aFile.ReadUnit;
  5201. // beware: the parser may not yet have finished
  5202. end;
  5203. Result:=aFile;
  5204. end;
  5205. function TPas2jsCompiler.ResolvedMainJSFile: string;
  5206. Var
  5207. OP,UP: String;
  5208. begin
  5209. OP:=FS.MainOutputPath;
  5210. UP:=FS.UnitOutputPath;
  5211. if MainJSFile='.' then
  5212. Result:=''
  5213. else begin
  5214. Result:=MainJSFile;
  5215. if Result<>'' then
  5216. begin
  5217. // has option -o
  5218. if ExtractFilePath(Result)='' then
  5219. begin
  5220. // -o<FileWithoutPath>
  5221. if OP<>'' then
  5222. Result:=OP+Result
  5223. else if UP<>'' then
  5224. Result:=UP+Result;
  5225. end;
  5226. end else begin
  5227. // no option -o
  5228. Result:=ChangeFileExt(MainSrcFile,'.js');
  5229. if OP<>'' then
  5230. begin
  5231. // option -FE and no -o => put into MainOutputPath
  5232. Result:=OP+ExtractFilename(Result)
  5233. end else if UP<>'' then
  5234. begin
  5235. // option -FU and no -o and no -FE => put into UnitOutputPath
  5236. Result:=UP+ExtractFilename(Result)
  5237. end else begin
  5238. // no -FU, no -FE and no -o => put into source directory
  5239. end;
  5240. end;
  5241. end;
  5242. end;
  5243. end.