pas2jscompiler.pp 104 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304
  1. { Author: Mattias Gaertner 2017 [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. Compiler-ToDos:
  6. Warn if -Ju and -Fu intersect
  7. -Fa<x>[,y] (for a program) load units <x> and [y] before uses is parsed
  8. Add Windows macros, see InitMacros.
  9. add options for names of globals like 'pas' and 'rtl'
  10. FileCache:
  11. uses 'in'
  12. }
  13. unit Pas2jsCompiler;
  14. {$mode objfpc}{$H+}
  15. {$inline on}
  16. interface
  17. uses
  18. Classes, SysUtils, AVL_Tree, contnrs,
  19. PScanner, PParser, PasTree, PasResolver, PasUseAnalyzer, PasResolveEval,
  20. jstree, jswriter, FPPas2Js, FPPJsSrcMap,
  21. Pas2jsFileUtils, Pas2jsLogger, Pas2jsFileCache, Pas2jsPParser;
  22. const
  23. VersionMajor = 0;
  24. VersionMinor = 8;
  25. VersionRelease = 41;
  26. VersionExtra = '+beta';
  27. DefaultConfigFile = 'pas2js.cfg';
  28. //------------------------------------------------------------------------------
  29. // Messages
  30. const
  31. nOptionIsEnabled = 101; sOptionIsEnabled = 'Option "%s" is %s';
  32. nSyntaxModeIs = 102; sSyntaxModeIs = 'Syntax mode is %s';
  33. nMacroDefined = 103; sMacroDefined = 'Macro defined: %s';
  34. nUsingPath = 104; sUsingPath = 'Using %s: "%s"';
  35. nFolderNotFound = 105; sFolderNotFound = '%s not found: "%s"';
  36. nNameValue = 106; sNameValue = '%s: "%s"';
  37. nReadingOptionsFromFile = 107; sReadingOptionsFromFile = 'Reading options from file "%s"';
  38. nEndOfReadingConfigFile = 108; sEndOfReadingConfigFile = 'End of reading config file "%s"';
  39. nInterpretingFileOption = 109; sInterpretingFileOption = 'interpreting file option "%s"';
  40. nSourceFileNotFound = 110; sSourceFileNotFound = 'source file not found "%s"';
  41. nFileIsFolder = 111; sFileIsFolder = 'expected file, but found directory "%s"';
  42. nConfigFileSearch = 112; sConfigFileSearch = 'Configfile search: %s';
  43. nHandlingOption = 113; sHandlingOption = 'handling option "%s"';
  44. nQuickHandlingOption = 114; sQuickHandlingOption = 'quick handling option "%s"';
  45. nOutputDirectoryNotFound = 115; sOutputDirectoryNotFound = 'output directory not found: "%s"';
  46. nUnableToWriteFile = 116; sUnableToWriteFile = 'Unable to write file "%s"';
  47. nWritingFile = 117; sWritingFile = 'Writing file "%s" ...';
  48. nCompilationAborted = 118; sCompilationAborted = 'Compilation aborted';
  49. nCfgDirective = 119; sCfgDirective = 'cfg directive "%s": %s';
  50. nUnitCycle = 120; sUnitCycle = 'Unit cycle found %s';
  51. nOptionForbidsCompile = 121; sOptionForbidsCompile = 'Option -Ju forbids to compile unit "%s"';
  52. nUnitNeedsCompileDueToUsedUnit = 122; sUnitsNeedCompileDueToUsedUnit = 'Unit "%s" needs compile due to used unit "%s"';
  53. nUnitNeedsCompileDueToOption = 123; sUnitsNeedCompileDueToOption = 'Unit "%s" needs compile due to option "%s"';
  54. nUnitNeedsCompileJSMissing = 124; sUnitsNeedCompileJSMissing = 'Unit "%s" needs compile, js file missing "%s"';
  55. nUnitNeedsCompilePasHasChanged = 125; sUnitsNeedCompilePasHasChanged = 'Unit "%s" needs compile, Pascal file has changed, js is "%s"';
  56. nParsingFile = 126; sParsingFile = 'Parsing "%s" ...';
  57. nCompilingFile = 127; sCompilingFile = 'Compiling "%s" ...';
  58. nExpectedButFound = 128; sExpectedButFound = 'Illegal unit name: Expected "%s", but found "%s"';
  59. nLinesInFilesCompiled = 129; sLinesInFilesCompiled = '%s lines in %s files compiled, %s sec';
  60. nTargetPlatformIs = 130; sTargetPlatformIs = 'Target platform is %s';
  61. nTargetProcessorIs = 131; sTargetProcessorIs = 'Target processor is %s';
  62. nMessageEncodingIs = 132; sMessageEncodingIs = 'Message encoding is %s';
  63. nUnableToTranslatePathToDir = 133; sUnableToTranslatePathToDir = 'Unable to translate path "%s" to directory "%s"';
  64. nSrcMapSourceRootIs = 134; sSrcMapSourceRootIs = 'source map "sourceRoot" is %s';
  65. nSrcMapBaseDirIs = 135; sSrcMapBaseDirIs = 'source map "local base directory" is %s';
  66. //------------------------------------------------------------------------------
  67. // Options
  68. type
  69. TP2jsCompilerOption = (
  70. coSkipDefaultConfigs,
  71. coBuildAll,
  72. coShowLogo,
  73. coShowErrors,
  74. coShowWarnings,
  75. coShowNotes,
  76. coShowHints,
  77. coShowInfos,
  78. coShowLineNumbers,
  79. coShowConditionals,
  80. coShowUsedTools,
  81. coShowMessageNumbers, // not in "show all"
  82. coShowDebug, // not in "show all"
  83. coAllowCAssignments,
  84. coLowerCase,
  85. coEnumValuesAsNumbers,
  86. coKeepNotUsedPrivates,
  87. coKeepNotUsedDeclarationsWPO,
  88. coSourceMapCreate,
  89. coSourceMapInclude
  90. );
  91. TP2jsCompilerOptions = set of TP2jsCompilerOption;
  92. const
  93. DefaultP2jsCompilerOptions = [coShowErrors];
  94. coShowAll = [coShowErrors..coShowUsedTools];
  95. coO1Enable = [coEnumValuesAsNumbers];
  96. coO1Disable = [coKeepNotUsedPrivates,coKeepNotUsedDeclarationsWPO];
  97. p2jscoCaption: array[TP2jsCompilerOption] of string = (
  98. // only used by experts, no need for resourcestrings
  99. 'Skip default configs',
  100. 'Build all',
  101. 'Show logo',
  102. 'Show errors',
  103. 'Show warnings',
  104. 'Show notes',
  105. 'Show hints',
  106. 'Show infos',
  107. 'Show line numbers',
  108. 'Show conditionals',
  109. 'Show used tools',
  110. 'Show message numbers',
  111. 'Show debug',
  112. 'Allow C assignments',
  113. 'Lowercase identifiers',
  114. 'Enum values as numbers',
  115. 'Keep not used private declarations',
  116. 'Keep not used declarations (WPO)',
  117. 'Create source map',
  118. 'Include Pascal sources in source map'
  119. );
  120. //------------------------------------------------------------------------------
  121. // $mode and $modeswitches
  122. type
  123. TP2jsMode = (
  124. p2jmObjFPC,
  125. p2jmDelphi
  126. );
  127. TP2jsModes = set of TP2jsMode;
  128. const
  129. p2jscModeNames: array[TP2jsMode] of string = (
  130. 'ObjFPC',
  131. 'Delphi'
  132. );
  133. p2jsMode_SwitchSets: array[TP2jsMode] of TModeSwitches = (
  134. OBJFPCModeSwitches*msAllPas2jsModeSwitches+msAllPas2jsModeSwitchesReadOnly,
  135. DelphiModeSwitches*msAllPas2jsModeSwitches+msAllPas2jsModeSwitchesReadOnly
  136. );
  137. //------------------------------------------------------------------------------
  138. // param macros
  139. type
  140. EPas2jsMacro = class(Exception);
  141. TOnSubstituteMacro = function(Sender: TObject; var Params: string; Lvl: integer): boolean of object;
  142. { TPas2jsMacro }
  143. TPas2jsMacro = class
  144. public
  145. Name: string;
  146. Description: string;
  147. Value: string;
  148. CanHaveParams: boolean;
  149. OnSubstitute: TOnSubstituteMacro;
  150. end;
  151. { TPas2jsMacroEngine }
  152. TPas2jsMacroEngine = class
  153. private
  154. fMacros: TObjectList; // list of TPas2jsMacro
  155. FMaxLevel: integer;
  156. function GetMacros(Index: integer): TPas2jsMacro;
  157. public
  158. constructor Create;
  159. destructor Destroy; override;
  160. function Count: integer;
  161. function AddValue(const aName, aDescription, aValue: string): TPas2jsMacro;
  162. function AddFunction(const aName, aDescription: string;
  163. const OnSubstitute: TOnSubstituteMacro; CanHaveParams: boolean): TPas2jsMacro;
  164. function IndexOf(const aName: string): integer;
  165. procedure Delete(Index: integer);
  166. function FindMacro(const aName: string): TPas2jsMacro;
  167. procedure Substitute(var s: string; Sender: TObject = nil; Lvl: integer = 0);
  168. property Macros[Index: integer]: TPas2jsMacro read GetMacros; default;
  169. property MaxLevel: integer read FMaxLevel write FMaxLevel;
  170. end;
  171. //------------------------------------------------------------------------------
  172. // Module file
  173. type
  174. ECompilerTerminate = class(Exception);
  175. TPas2jsCompiler = class;
  176. TUsedBySection = (
  177. ubMainSection,
  178. ubImplSection
  179. );
  180. { TPas2jsCompilerFile }
  181. TPas2jsCompilerFile = class
  182. private
  183. FCompiler: TPas2jsCompiler;
  184. FConverter: TPasToJSConverter;
  185. FFileResolver: TPas2jsFileResolver;
  186. FIsForeign: boolean;
  187. FIsMainFile: boolean;
  188. FJSFilename: string;
  189. FJSModule: TJSElement;
  190. FLog: TPas2jsLogger;
  191. FNeedBuild: Boolean;
  192. FParser: TPas2jsPasParser;
  193. FPasFilename: String;
  194. FPasModule: TPasModule;
  195. FPasResolver: TPas2jsCompilerResolver;
  196. FPasUnitName: string;
  197. FScanner: TPascalScanner;
  198. FShowDebug: boolean;
  199. FUseAnalyzer: TPasAnalyzer;
  200. FUsedBy: array[TUsedBySection] of TFPList; // list of TPas2jsCompilerFile
  201. procedure FPasResolverContinueParsing(Sender: TObject);
  202. function GetUsedBy(Section: TUsedBySection; Index: integer): TPas2jsCompilerFile;
  203. function GetUsedByCount(Section: TUsedBySection): integer;
  204. function OnConverterIsElementUsed(Sender: TObject; El: TPasElement): boolean;
  205. function OnConverterIsTypeInfoUsed(Sender: TObject; El: TPasElement): boolean;
  206. procedure OnPasResolverLog(Sender: TObject; const Msg: String);
  207. procedure OnParserLog(Sender: TObject; const Msg: String);
  208. procedure OnScannerLog(Sender: TObject; const Msg: String);
  209. procedure OnUseAnalyzerMessage(Sender: TObject; Msg: TPAMessage);
  210. procedure SetJSFilename(AValue: string);
  211. procedure HandleEParserError(E: EParserError);
  212. procedure HandleEPasResolve(E: EPasResolve);
  213. procedure HandleEPas2JS(E: EPas2JS);
  214. procedure HandleUnknownException(E: Exception);
  215. procedure HandleException(E: Exception);
  216. procedure DoLogMsgAtEl(MsgType: TMessageType; const Msg: string;
  217. MsgNumber: integer; El: TPasElement);
  218. procedure RaiseInternalError(id: int64; Msg: string);
  219. procedure ParserFinished;
  220. public
  221. constructor Create(aCompiler: TPas2jsCompiler; const aPasFilename: string);
  222. destructor Destroy; override;
  223. procedure CreateScannerAndParser(aFileResolver: TPas2jsFileResolver);
  224. function OnPasTreeFindModule(const UseUnitname: String): TPasModule;
  225. function FindUnit(const UseUnitname: String): TPasModule;
  226. procedure OnPasTreeCheckSrcName(const Element: TPasElement);
  227. procedure OpenFile(aFilename: string);// beware: this changes FileResolver.BaseDirectory
  228. procedure ParsePascal;
  229. procedure CreateJS;
  230. function GetPasFirstSection: TPasSection;
  231. function GetPasImplSection: TPasSection;
  232. function GetPasMainUsesClause: TPasUsesClause;
  233. function GetPasImplUsesClause: TPasUsesClause;
  234. function GetCurPasModule: TPasModule;
  235. function GetModuleName: string;
  236. class function GetFile(aModule: TPasModule): TPas2jsCompilerFile;
  237. public
  238. property Compiler: TPas2jsCompiler read FCompiler;
  239. property Converter: TPasToJSConverter read FConverter;
  240. property FileResolver: TPas2jsFileResolver read FFileResolver;
  241. property IsForeign: boolean read FIsForeign write FIsForeign;// true = do not build
  242. property IsMainFile: boolean read FIsMainFile write FIsMainFile;
  243. property JSFilename: string read FJSFilename write SetJSFilename;
  244. property JSModule: TJSElement read FJSModule;
  245. property Log: TPas2jsLogger read FLog;
  246. property NeedBuild: Boolean read FNeedBuild write FNeedBuild;
  247. property Parser: TPas2jsPasParser read FParser;
  248. property PascalResolver: TPas2jsCompilerResolver read FPasResolver;
  249. property PasFilename: String read FPasFilename;
  250. property PasModule: TPasModule read FPasModule;
  251. property PasUnitName: string read FPasUnitName write FPasUnitName;// unit name in program
  252. property Scanner: TPascalScanner read FScanner;
  253. property ShowDebug: boolean read FShowDebug write FShowDebug;
  254. property UseAnalyzer: TPasAnalyzer read FUseAnalyzer; // unit analysis
  255. property UsedByCount[Section: TUsedBySection]: integer read GetUsedByCount;
  256. property UsedBy[Section: TUsedBySection; Index: integer]: TPas2jsCompilerFile read GetUsedBy;
  257. end;
  258. { TPas2JSWPOptimizer }
  259. TPas2JSWPOptimizer = class(TPasAnalyzer)
  260. public
  261. end;
  262. { TPas2jsCompiler }
  263. TPas2jsCompiler = class
  264. private
  265. FCompilerExe: string;
  266. FConditionEval: TCondDirectiveEvaluator;
  267. FCurrentCfgFilename: string;
  268. FCurrentCfgLineNumber: integer;
  269. FDefines: TStrings; // Objects can be TMacroDef
  270. FFileCache: TPas2jsFilesCache;
  271. FFileCacheAutoFree: boolean;
  272. FFiles: TAVLTree; // tree of TPas2jsCompilerFile sorted for PasFilename
  273. FHasShownLogo: boolean;
  274. FLog: TPas2jsLogger;
  275. FMainFile: TPas2jsCompilerFile;
  276. FMode: TP2jsMode;
  277. FOptions: TP2jsCompilerOptions;
  278. FParamMacros: TPas2jsMacroEngine;
  279. FSrcMapSourceRoot: string;
  280. FTargetPlatform: TPasToJsPlatform;
  281. FTargetProcessor: TPasToJsProcessor;
  282. FUnits: TAVLTree; // tree of TPas2jsCompilerFile sorted for UnitName
  283. FWPOAnalyzer: TPas2JSWPOptimizer;
  284. function ConditionEvalVariable(Sender: TCondDirectiveEvaluator;
  285. aName: String; out Value: string): boolean;
  286. function GetDefaultNamespace: String;
  287. function GetFileCount: integer;
  288. function GetShowDebug: boolean; inline;
  289. function GetShowFullPaths: boolean;
  290. function GetShowLogo: Boolean; inline;
  291. function GetShowTriedUsedFiles: boolean; inline;
  292. function GetShowUsedTools: boolean; inline;
  293. function GetSkipDefaultConfig: Boolean; inline;
  294. function GetSrcMapBaseDir: string;
  295. function GetSrcMapEnable: boolean;
  296. function GetSrcMapInclude: boolean;
  297. function OnMacroCfgDir(Sender: TObject; var Params: string; Lvl: integer
  298. ): boolean;
  299. function OnMacroEnv(Sender: TObject; var Params: string; Lvl: integer
  300. ): boolean;
  301. procedure AddDefinesForTargetPlatform;
  302. procedure AddDefinesForTargetProcessor;
  303. procedure CfgSyntaxError(const Msg: string);
  304. procedure ConditionEvalLog(Sender: TCondDirectiveEvaluator;
  305. Args: array of const);
  306. procedure LoadConfig(CfgFilename: string);
  307. procedure LoadDefaultConfig;
  308. procedure ParamFatal(Msg: string);
  309. procedure ReadParam(Param: string; Quick, FromCmdLine: boolean);
  310. procedure ReadSingleLetterOptions(const Param: string; p: PChar;
  311. const Allowed: string; out Enabled, Disabled: string);
  312. procedure ReadSyntaxFlags(Param: String; p: PChar);
  313. procedure ReadVerbosityFlags(Param: String; p: PChar);
  314. procedure RegisterMessages;
  315. procedure SetCompilerExe(AValue: string);
  316. procedure SetFileCache(AValue: TPas2jsFilesCache);
  317. procedure SetMode(AValue: TP2jsMode);
  318. procedure SetOptions(AValue: TP2jsCompilerOptions);
  319. procedure SetShowDebug(AValue: boolean);
  320. procedure SetShowFullPaths(AValue: boolean);
  321. procedure SetShowLogo(AValue: Boolean);
  322. procedure SetShowTriedUsedFiles(AValue: boolean);
  323. procedure SetShowUsedTools(AValue: boolean);
  324. procedure SetSkipDefaultConfig(AValue: Boolean);
  325. procedure SetSrcMapBaseDir(const AValue: string);
  326. procedure SetSrcMapEnable(const AValue: boolean);
  327. procedure SetSrcMapInclude(const AValue: boolean);
  328. procedure SetTargetPlatform(const AValue: TPasToJsPlatform);
  329. procedure SetTargetProcessor(const AValue: TPasToJsProcessor);
  330. protected
  331. // If this function returns true, the compiler assumes the file was written.
  332. // If false, the compiler will attempt to write the file itself.
  333. function DoWriteJSFile(const DestFilename: String; aWriter: TPas2JSMapper): Boolean; virtual;
  334. procedure Compile(StartTime: TDateTime);
  335. function MarkNeedBuilding(aFile: TPas2jsCompilerFile; Checked: TAVLTree;
  336. var SrcFileCount: integer): boolean;
  337. procedure OptimizeProgram(aFile: TPas2jsCompilerFile); virtual;
  338. procedure CreateJavaScript(aFile: TPas2jsCompilerFile; Checked: TAVLTree);
  339. procedure FinishSrcMap(SrcMap: TPas2JSSrcMap); virtual;
  340. procedure WriteJSFiles(aFile: TPas2jsCompilerFile;
  341. var CombinedFileWriter: TPas2JSMapper; Checked: TAVLTree);
  342. procedure InitParamMacros;
  343. procedure ClearDefines;
  344. procedure RaiseInternalError(id: int64; Msg: string);
  345. public
  346. constructor Create; virtual;
  347. destructor Destroy; override;
  348. procedure Reset;
  349. procedure Run(
  350. aCompilerExe: string; // needed for default config and help
  351. aWorkingDir: string;
  352. ParamList: TStrings;
  353. DoReset: boolean = true);
  354. procedure Terminate(TheExitCode: integer);
  355. class function GetVersion(ShortVersion: boolean): string;
  356. procedure WriteHelp;
  357. procedure WriteLogo;
  358. procedure WriteVersionLine;
  359. procedure WriteOptions;
  360. procedure WriteDefines;
  361. procedure WriteFoldersAndSearchPaths;
  362. procedure WriteInfo;
  363. function GetShownMsgTypes: TMessageTypes;
  364. procedure AddDefine(const aName: String);
  365. procedure AddDefine(const aName, Value: String);
  366. procedure RemoveDefine(const aName: String);
  367. function IsDefined(const aName: String): boolean;
  368. procedure SetOption(Flag: TP2jsCompilerOption; Enable: boolean);
  369. function FindPasFile(PasFilename: string): TPas2jsCompilerFile;
  370. procedure LoadPasFile(PasFilename, UseUnitName: string; out aFile: TPas2jsCompilerFile);
  371. function FindUsedUnit(const TheUnitName: string): TPas2jsCompilerFile;
  372. procedure AddUsedUnit(aFile: TPas2jsCompilerFile);
  373. public
  374. property CompilerExe: string read FCompilerExe write SetCompilerExe;
  375. property ConditionEvaluator: TCondDirectiveEvaluator read FConditionEval;
  376. property CurrentCfgFilename: string read FCurrentCfgFilename;
  377. property CurrentCfgLineNumber: integer read FCurrentCfgLineNumber;
  378. property DefaultNamespace: String read GetDefaultNamespace;
  379. property Defines: TStrings read FDefines;
  380. property FileCache: TPas2jsFilesCache read FFileCache write SetFileCache;
  381. property FileCacheAutoFree: boolean read FFileCacheAutoFree write FFileCacheAutoFree;
  382. property FileCount: integer read GetFileCount;
  383. property Log: TPas2jsLogger read FLog;
  384. property MainFile: TPas2jsCompilerFile read FMainFile;
  385. property Mode: TP2jsMode read FMode write SetMode;
  386. property Options: TP2jsCompilerOptions read FOptions write SetOptions;
  387. property ParamMacros: TPas2jsMacroEngine read FParamMacros;
  388. property SrcMapEnable: boolean read GetSrcMapEnable write SetSrcMapEnable;
  389. property SrcMapSourceRoot: string read FSrcMapSourceRoot write FSrcMapSourceRoot;
  390. property SrcMapBaseDir: string read GetSrcMapBaseDir write SetSrcMapBaseDir;
  391. property SrcMapInclude: boolean read GetSrcMapInclude write SetSrcMapInclude;
  392. property ShowDebug: boolean read GetShowDebug write SetShowDebug;
  393. property ShowFullPaths: boolean read GetShowFullPaths write SetShowFullPaths;
  394. property ShowLogo: Boolean read GetShowLogo write SetShowLogo;
  395. property ShowTriedUsedFiles: boolean read GetShowTriedUsedFiles write SetShowTriedUsedFiles;
  396. property ShowUsedTools: boolean read GetShowUsedTools write SetShowUsedTools;
  397. property SkipDefaultConfig: Boolean read GetSkipDefaultConfig write SetSkipDefaultConfig;
  398. property TargetPlatform: TPasToJsPlatform read FTargetPlatform write SetTargetPlatform;
  399. property TargetProcessor: TPasToJsProcessor read FTargetProcessor write SetTargetProcessor;
  400. property WPOAnalyzer: TPas2JSWPOptimizer read FWPOAnalyzer; // Whole Program Optimization
  401. end;
  402. function CompareCompilerFilesPasFile(Item1, Item2: Pointer): integer;
  403. function CompareFileAndCompilerFilePasFile(Filename, Item: Pointer): integer;
  404. function CompareCompilerFilesPasUnitname(Item1, Item2: Pointer): integer;
  405. function CompareUnitnameAndCompilerFile(TheUnitname, Item: Pointer): integer;
  406. function GetCompiledDate: string;
  407. function GetCompiledFPCVersion: string;
  408. function GetCompiledTargetOS: string;
  409. function GetCompiledTargetCPU: string;
  410. implementation
  411. function CompareCompilerFilesPasFile(Item1, Item2: Pointer): integer;
  412. var
  413. File1: TPas2jsCompilerFile absolute Item1;
  414. File2: TPas2jsCompilerFile absolute Item2;
  415. begin
  416. Result:=CompareFilenames(File1.PasFilename,File2.PasFilename);
  417. end;
  418. function CompareFileAndCompilerFilePasFile(Filename, Item: Pointer): integer;
  419. var
  420. aFile: TPas2jsCompilerFile absolute Item;
  421. aFilename: String;
  422. begin
  423. aFilename:=AnsiString(Filename);
  424. Result:=CompareFilenames(aFilename,aFile.PasFilename);
  425. end;
  426. function CompareCompilerFilesPasUnitname(Item1, Item2: Pointer): integer;
  427. var
  428. File1: TPas2jsCompilerFile absolute Item1;
  429. File2: TPas2jsCompilerFile absolute Item2;
  430. begin
  431. Result:=CompareText(File1.PasUnitName,File2.PasUnitName);
  432. end;
  433. function CompareUnitnameAndCompilerFile(TheUnitname, Item: Pointer): integer;
  434. var
  435. aFile: TPas2jsCompilerFile absolute Item;
  436. anUnitname: String;
  437. begin
  438. anUnitname:=AnsiString(TheUnitname);
  439. Result:=CompareText(anUnitname,aFile.PasUnitName);
  440. end;
  441. function GetCompiledDate: string;
  442. begin
  443. Result:={$I %Date%};
  444. end;
  445. function GetCompiledFPCVersion: string;
  446. begin
  447. Result:={$I %FPCVERSION%};
  448. end;
  449. function GetCompiledTargetOS: string;
  450. begin
  451. Result:=lowerCase({$I %FPCTARGETOS%});
  452. end;
  453. function GetCompiledTargetCPU: string;
  454. begin
  455. Result:=lowerCase({$I %FPCTARGETCPU%});
  456. end;
  457. { TPas2jsMacroEngine }
  458. function TPas2jsMacroEngine.GetMacros(Index: integer): TPas2jsMacro;
  459. begin
  460. Result:=TPas2jsMacro(fMacros[Index]);
  461. end;
  462. constructor TPas2jsMacroEngine.Create;
  463. begin
  464. fMacros:=TObjectList.Create(true);
  465. FMaxLevel:=10;
  466. end;
  467. destructor TPas2jsMacroEngine.Destroy;
  468. begin
  469. FreeAndNil(fMacros);
  470. inherited Destroy;
  471. end;
  472. function TPas2jsMacroEngine.Count: integer;
  473. begin
  474. Result:=fMacros.Count;
  475. end;
  476. function TPas2jsMacroEngine.AddValue(const aName, aDescription, aValue: string
  477. ): TPas2jsMacro;
  478. begin
  479. if not IsValidIdent(aName) then
  480. raise EPas2jsMacro.Create('invalid macro name "'+aName+'"');
  481. if IndexOf(aName)>=0 then
  482. raise EPas2jsMacro.Create('duplicate macro name "'+aName+'"');
  483. Result:=TPas2jsMacro.Create;
  484. Result.Name:=aName;
  485. Result.Description:=aDescription;
  486. Result.Value:=aValue;
  487. fMacros.Add(Result);
  488. end;
  489. function TPas2jsMacroEngine.AddFunction(const aName, aDescription: string;
  490. const OnSubstitute: TOnSubstituteMacro; CanHaveParams: boolean): TPas2jsMacro;
  491. begin
  492. if not IsValidIdent(aName) then
  493. raise EPas2jsMacro.Create('invalid macro name "'+aName+'"');
  494. if IndexOf(aName)>=0 then
  495. raise EPas2jsMacro.Create('duplicate macro name "'+aName+'"');
  496. Result:=TPas2jsMacro.Create;
  497. Result.Name:=aName;
  498. Result.Description:=aDescription;
  499. Result.CanHaveParams:=CanHaveParams;
  500. Result.OnSubstitute:=OnSubstitute;
  501. fMacros.Add(Result);
  502. end;
  503. function TPas2jsMacroEngine.IndexOf(const aName: string): integer;
  504. var
  505. i: Integer;
  506. begin
  507. for i:=0 to Count-1 do
  508. if CompareText(Macros[i].Name,aName)=0 then
  509. exit(i);
  510. Result:=-1;
  511. end;
  512. procedure TPas2jsMacroEngine.Delete(Index: integer);
  513. begin
  514. fMacros.Delete(Index);
  515. end;
  516. function TPas2jsMacroEngine.FindMacro(const aName: string): TPas2jsMacro;
  517. var
  518. i: Integer;
  519. begin
  520. i:=IndexOf(aName);
  521. if i>=0 then
  522. Result:=Macros[i]
  523. else
  524. Result:=nil;
  525. end;
  526. procedure TPas2jsMacroEngine.Substitute(var s: string; Sender: TObject;
  527. Lvl: integer);
  528. // Rules:
  529. // $macro or $macro$
  530. // if Macro.OnSubstitute is set then optional brackets are allowed: $macro(params)
  531. var
  532. p, StartP, BracketLvl, ParamStartP: Integer;
  533. MacroName, NewValue: String;
  534. Macro: TPas2jsMacro;
  535. begin
  536. if Lvl>=MaxLevel then
  537. raise EPas2jsMacro.Create('macro cycle detected: "'+s+'"');
  538. p:=1;
  539. while p<length(s) do begin
  540. if (s[p]='$') and (s[p+1] in ['_','a'..'z','A'..'Z']) then begin
  541. StartP:=p;
  542. inc(p,2);
  543. while (p<=length(s)) and (s[p] in ['_','a'..'z','A'..'Z','0'..'9']) do
  544. inc(p);
  545. MacroName:=copy(s,StartP+1,p-StartP-1);
  546. Macro:=FindMacro(MacroName);
  547. if Macro=nil then
  548. raise EPas2jsMacro.Create('macro not found "'+MacroName+'" in "'+s+'"');
  549. NewValue:='';
  550. if Macro.CanHaveParams and (p<=length(s)) and (s[p]='(') then begin
  551. // read NewValue
  552. inc(p);
  553. ParamStartP:=p;
  554. BracketLvl:=1;
  555. repeat
  556. if p>length(s) then
  557. raise EPas2jsMacro.Create('missing closing bracket ) in "'+s+'"');
  558. case s[p] of
  559. '(': inc(BracketLvl);
  560. ')':
  561. if BracketLvl=1 then begin
  562. NewValue:=copy(s,ParamStartP,p-ParamStartP);
  563. break;
  564. end else begin
  565. dec(BracketLvl);
  566. end;
  567. end;
  568. until false;
  569. end else if (p<=length(s)) and (s[p]='$') then
  570. inc(p);
  571. if Assigned(Macro.OnSubstitute) then begin
  572. if not Macro.OnSubstitute(Sender,NewValue,Lvl+1) then
  573. raise EPas2jsMacro.Create('macro "'+MacroName+'" failed in "'+s+'"');
  574. end else
  575. NewValue:=Macro.Value;
  576. s:=LeftStr(s,StartP-1)+NewValue+copy(s,p,length(s));
  577. p:=StartP;
  578. end;
  579. inc(p);
  580. end;
  581. end;
  582. { TPas2jsCompilerFile }
  583. constructor TPas2jsCompilerFile.Create(aCompiler: TPas2jsCompiler;
  584. const aPasFilename: string);
  585. var
  586. ub: TUsedBySection;
  587. begin
  588. FCompiler:=aCompiler;
  589. FLog:=Compiler.Log;
  590. FPasFilename:=aPasFilename;
  591. FPasResolver:=TPas2jsCompilerResolver.Create;
  592. FPasResolver.Owner:=Self;
  593. FPasResolver.OnContinueParsing:=@FPasResolverContinueParsing;
  594. FPasResolver.OnFindModule:=@OnPasTreeFindModule;
  595. FPasResolver.OnCheckSrcName:=@OnPasTreeCheckSrcName;
  596. FPasResolver.OnLog:=@OnPasResolverLog;
  597. FPasResolver.Log:=Log;
  598. FPasResolver.AddObjFPCBuiltInIdentifiers(btAllJSBaseTypes,bfAllJSBaseProcs);
  599. FIsMainFile:=CompareFilenames(aCompiler.FileCache.MainSrcFile,aPasFilename)=0;
  600. for ub in TUsedBySection do
  601. FUsedBy[ub]:=TFPList.Create;
  602. FUseAnalyzer:=TPasAnalyzer.Create;
  603. FUseAnalyzer.OnMessage:=@OnUseAnalyzerMessage;
  604. FUseAnalyzer.Resolver:=FPasResolver;
  605. end;
  606. destructor TPas2jsCompilerFile.Destroy;
  607. var
  608. ub: TUsedBySection;
  609. begin
  610. FreeAndNil(FUseAnalyzer);
  611. for ub in TUsedBySection do
  612. FreeAndNil(FUsedBy[ub]);
  613. FreeAndNil(FJSModule);
  614. FreeAndNil(FConverter);
  615. if FPasModule<>nil then begin
  616. FPasModule.Release;
  617. FPasModule:=nil;
  618. end;
  619. FreeAndNil(FParser);
  620. FreeAndNil(FScanner);
  621. FreeAndNil(FFileResolver);
  622. FreeAndNil(FPasResolver);
  623. inherited Destroy;
  624. end;
  625. procedure TPas2jsCompilerFile.CreateScannerAndParser(aFileResolver: TPas2jsFileResolver);
  626. var
  627. aUnitName: String;
  628. i: Integer;
  629. M: TMacroDef;
  630. begin
  631. FFileResolver:=aFileResolver;
  632. // scanner
  633. FScanner := TPascalScanner.Create(FileResolver);
  634. Scanner.LogEvents:=PascalResolver.ScannerLogEvents;
  635. Scanner.OnLog:=@OnScannerLog;
  636. Scanner.OnFormatPath:[email protected];
  637. // create parser (Note: this sets some scanner options to defaults)
  638. FParser := TPas2jsPasParser.Create(Scanner, FileResolver, PascalResolver);
  639. // set options
  640. Scanner.AllowedModeSwitches:=msAllPas2jsModeSwitches;
  641. Scanner.ReadOnlyModeSwitches:=msAllPas2jsModeSwitchesReadOnly;
  642. Scanner.CurrentModeSwitches:=p2jsMode_SwitchSets[Compiler.Mode];
  643. // Note: some Scanner.Options are set by TPasResolver
  644. for i:=0 to Compiler.Defines.Count-1 do
  645. begin
  646. M:=TMacroDef(Compiler.Defines.Objects[i]);
  647. if M=nil then
  648. Scanner.AddDefine(Compiler.Defines[i])
  649. else
  650. Scanner.AddMacro(M.Name,M.Value);
  651. end;
  652. if coAllowCAssignments in Compiler.Options then
  653. Scanner.Options:=Scanner.Options+[po_cassignments];
  654. if Compiler.Mode=p2jmDelphi then
  655. Scanner.Options:=Scanner.Options+[po_delphi];
  656. // parser
  657. Parser.LogEvents:=PascalResolver.ParserLogEvents;
  658. Parser.OnLog:=@OnParserLog;
  659. Parser.Log:=Log;
  660. PascalResolver.P2JParser:=Parser;
  661. if not IsMainFile then begin
  662. aUnitName:=ExtractFilenameOnly(PasFilename);
  663. if CompareText(aUnitName,'system')=0 then
  664. Parser.ImplicitUses.Clear;
  665. end;
  666. end;
  667. procedure TPas2jsCompilerFile.OnPasTreeCheckSrcName(const Element: TPasElement);
  668. var
  669. SrcName, ExpectedSrcName: String;
  670. begin
  671. //writeln('TPas2jsCompilerFile.OnPasTreeCheckSrcName ',PasFilename,' Name=',Element.Name,' IsMainFile=',IsMainFile);
  672. if (Element.ClassType=TPasUnitModule) or (Element.ClassType=TPasModule) then
  673. begin
  674. SrcName:=Element.Name;
  675. if IsMainFile then begin
  676. // main source is an unit
  677. if PasUnitName='' then begin
  678. {$IFDEF VerboseSetPasUnitName}
  679. writeln('TPas2jsCompilerFile.OnPasTreeCheckSrcName ',PasFilename,' Name=',Element.Name,' IsMainFile=',IsMainFile);
  680. {$ENDIF}
  681. PasUnitName:=SrcName;
  682. Compiler.AddUsedUnit(Self);
  683. end;
  684. end else begin
  685. // an unit name must fit its filename
  686. ExpectedSrcName:=ExtractFilenameOnly(PasFilename);
  687. if CompareText(SrcName,ExpectedSrcName)=0 then
  688. exit; // ok
  689. Parser.RaiseParserError(nExpectedButFound,[ExpectedSrcName,SrcName]);
  690. end;
  691. end;
  692. end;
  693. function TPas2jsCompilerFile.GetUsedBy(Section: TUsedBySection; Index: integer
  694. ): TPas2jsCompilerFile;
  695. begin
  696. Result:=TPas2jsCompilerFile(FUsedBy[Section][Index]);
  697. end;
  698. procedure TPas2jsCompilerFile.FPasResolverContinueParsing(Sender: TObject);
  699. begin
  700. try
  701. Parser.ParseContinueImplementation;
  702. except
  703. on E: Exception do
  704. HandleException(E);
  705. end;
  706. ParserFinished;
  707. end;
  708. function TPas2jsCompilerFile.GetUsedByCount(Section: TUsedBySection): integer;
  709. begin
  710. Result:=FUsedBy[Section].Count;
  711. end;
  712. function TPas2jsCompilerFile.OnConverterIsElementUsed(Sender: TObject;
  713. El: TPasElement): boolean;
  714. begin
  715. if (Compiler.WPOAnalyzer<>nil)
  716. and not (coKeepNotUsedDeclarationsWPO in Compiler.Options) then
  717. Result:=Compiler.WPOAnalyzer.IsUsed(El)
  718. else if not (coKeepNotUsedPrivates in Compiler.Options) then
  719. Result:=UseAnalyzer.IsUsed(El)
  720. else
  721. Result:=true;
  722. end;
  723. function TPas2jsCompilerFile.OnConverterIsTypeInfoUsed(Sender: TObject;
  724. El: TPasElement): boolean;
  725. begin
  726. if (Compiler.WPOAnalyzer<>nil)
  727. and not (coKeepNotUsedDeclarationsWPO in Compiler.Options) then
  728. Result:=Compiler.WPOAnalyzer.IsTypeInfoUsed(El)
  729. else if not (coKeepNotUsedPrivates in Compiler.Options) then
  730. Result:=UseAnalyzer.IsTypeInfoUsed(El)
  731. else
  732. Result:=true;
  733. end;
  734. procedure TPas2jsCompilerFile.OnPasResolverLog(Sender: TObject; const Msg: String);
  735. var
  736. aResolver: TPasResolver;
  737. begin
  738. if Msg='' then ; // ignore standard formatted message
  739. aResolver:=TPasResolver(Sender);
  740. DoLogMsgAtEl(aResolver.LastMsgType,aResolver.LastMsg,aResolver.LastMsgNumber,
  741. aResolver.LastElement);
  742. end;
  743. procedure TPas2jsCompilerFile.OnParserLog(Sender: TObject; const Msg: String);
  744. var
  745. aParser: TPasParser;
  746. aScanner: TPascalScanner;
  747. begin
  748. if Msg='' then ; // ignore standard formatted message
  749. aParser:=TPasParser(Sender);
  750. aScanner:=aParser.Scanner;
  751. Log.Log(aParser.LastMsgType,aParser.LastMsg,aParser.LastMsgNumber,
  752. aScanner.CurFilename,aScanner.CurRow,aScanner.CurColumn);
  753. end;
  754. procedure TPas2jsCompilerFile.OnScannerLog(Sender: TObject; const Msg: String);
  755. var
  756. aScanner: TPascalScanner;
  757. begin
  758. if Msg='' then ; // ignore standard formatted message
  759. aScanner:=TPascalScanner(Sender);
  760. Log.Log(aScanner.LastMsgType,aScanner.LastMsg,aScanner.LastMsgNumber,
  761. aScanner.CurFilename,aScanner.CurRow,aScanner.CurColumn);
  762. end;
  763. procedure TPas2jsCompilerFile.OnUseAnalyzerMessage(Sender: TObject;
  764. Msg: TPAMessage);
  765. begin
  766. Log.Log(Msg.MsgType,Msg.MsgText,Msg.MsgNumber,Msg.Filename,Msg.Row,Msg.Col);
  767. end;
  768. procedure TPas2jsCompilerFile.SetJSFilename(AValue: string);
  769. begin
  770. if FJSFilename=AValue then Exit;
  771. FJSFilename:=AValue;
  772. end;
  773. procedure TPas2jsCompilerFile.HandleEParserError(E: EParserError);
  774. begin
  775. Log.Log(Parser.LastMsgType,Parser.LastMsg,Parser.LastMsgNumber,
  776. E.Filename,E.Row,E.Column);
  777. Compiler.Terminate(ExitCodeSyntaxError);
  778. end;
  779. procedure TPas2jsCompilerFile.HandleEPasResolve(E: EPasResolve);
  780. var
  781. aFilename: String;
  782. aRow, aColumn: integer;
  783. begin
  784. if E.PasElement<>nil then begin
  785. aFilename:=E.PasElement.SourceFilename;
  786. PascalResolver.UnmangleSourceLineNumber(E.PasElement.SourceLinenumber,aRow,aColumn);
  787. end else begin
  788. aFilename:=Scanner.CurFilename;
  789. aRow:=Scanner.CurRow;
  790. aColumn:=Scanner.CurColumn;
  791. end;
  792. Log.Log(E.MsgType,E.Message,E.MsgNumber,aFilename,aRow,aColumn);
  793. Compiler.Terminate(ExitCodeSyntaxError);
  794. end;
  795. procedure TPas2jsCompilerFile.HandleEPas2JS(E: EPas2JS);
  796. var
  797. aFilename: String;
  798. aRow, aColumn: integer;
  799. begin
  800. if E.PasElement<>nil then begin
  801. aFilename:=E.PasElement.SourceFilename;
  802. PascalResolver.UnmangleSourceLineNumber(E.PasElement.SourceLinenumber,aRow,aColumn);
  803. Log.Log(E.MsgType,E.Message,E.MsgNumber,aFilename,aRow,aColumn);
  804. end else begin
  805. Log.Log(E.MsgType,E.Message,E.MsgNumber);
  806. end;
  807. Compiler.Terminate(ExitCodeConverterError);
  808. end;
  809. procedure TPas2jsCompilerFile.HandleUnknownException(E: Exception);
  810. begin
  811. if not (E is ECompilerTerminate) then
  812. Log.Log(mtFatal,E.ClassName+': '+E.Message,0);
  813. Compiler.Terminate(ExitCodeErrorInternal);
  814. end;
  815. procedure TPas2jsCompilerFile.HandleException(E: Exception);
  816. begin
  817. if E is EScannerError then begin
  818. Log.Log(Scanner.LastMsgType,Scanner.LastMsg,Scanner.LastMsgNumber,
  819. Scanner.CurFilename,Scanner.CurRow,Scanner.CurColumn);
  820. Compiler.Terminate(ExitCodeSyntaxError);
  821. end else if E is EParserError then
  822. HandleEParserError(EParserError(E))
  823. else if E is EPasResolve then
  824. HandleEPasResolve(EPasResolve(E))
  825. else if E is EPas2JS then
  826. HandleEPas2JS(EPas2JS(E))
  827. else
  828. HandleUnknownException(E);
  829. end;
  830. procedure TPas2jsCompilerFile.DoLogMsgAtEl(MsgType: TMessageType;
  831. const Msg: string; MsgNumber: integer; El: TPasElement);
  832. var
  833. Line, Col: integer;
  834. Filename: String;
  835. begin
  836. if (El<>nil) then begin
  837. Filename:=El.SourceFilename;
  838. TPasResolver.UnmangleSourceLineNumber(El.SourceLinenumber,Line,Col);
  839. end else begin
  840. Filename:='';
  841. Line:=0;
  842. Col:=0;
  843. end;
  844. Log.Log(MsgType,Msg,MsgNumber,Filename,Line,Col);
  845. end;
  846. procedure TPas2jsCompilerFile.RaiseInternalError(id: int64; Msg: string);
  847. begin
  848. Compiler.RaiseInternalError(id,Msg);
  849. end;
  850. procedure TPas2jsCompilerFile.ParserFinished;
  851. begin
  852. try
  853. if ShowDebug then begin
  854. Log.LogRaw('Pas-Module:');
  855. Log.LogRaw(PasModule.GetDeclaration(true));
  856. end;
  857. // analyze
  858. UseAnalyzer.AnalyzeModule(FPasModule);
  859. except
  860. on E: Exception do
  861. HandleException(E);
  862. end;
  863. end;
  864. procedure TPas2jsCompilerFile.OpenFile(aFilename: string);
  865. begin
  866. FPasFilename:=aFilename;
  867. try
  868. Scanner.OpenFile(PasFilename);
  869. except
  870. on E: EScannerError do begin
  871. Log.Log(Scanner.LastMsgType,Scanner.LastMsg,Scanner.LastMsgNumber,
  872. Scanner.CurFilename,Scanner.CurRow,Scanner.CurColumn);
  873. Compiler.Terminate(ExitCodeSyntaxError);
  874. end;
  875. end;
  876. end;
  877. procedure TPas2jsCompilerFile.ParsePascal;
  878. begin
  879. if ShowDebug then
  880. Log.LogRaw(['Debug: Parsing Pascal "',PasFilename,'"...']);
  881. try
  882. // parse Pascal
  883. PascalResolver.InterfaceOnly:=IsForeign;
  884. if IsMainFile then
  885. Parser.ParseMain(FPasModule)
  886. else
  887. Parser.ParseSubModule(FPasModule);
  888. if PasModule.CustomData=nil then
  889. PasModule.CustomData:=Self;
  890. if (FPasModule.ImplementationSection<>nil)
  891. and (FPasModule.ImplementationSection.PendingUsedIntf<>nil) then
  892. exit;
  893. ParserFinished;
  894. except
  895. on E: Exception do
  896. HandleException(E);
  897. end;
  898. if (PasModule<>nil) and (PasModule.CustomData=nil) then
  899. PasModule.CustomData:=Self;
  900. end;
  901. procedure TPas2jsCompilerFile.CreateJS;
  902. begin
  903. try
  904. // show hints only for units that are actually converted
  905. UseAnalyzer.EmitModuleHints(PasModule);
  906. // convert
  907. FConverter:=TPasToJSConverter.Create;
  908. FConverter.Options:=FConverter.Options+[coUseStrict];
  909. if coEnumValuesAsNumbers in Compiler.Options then
  910. FConverter.Options:=FConverter.Options+[fppas2js.coEnumNumbers];
  911. FConverter.UseLowerCase:=coLowerCase in Compiler.Options;
  912. FConverter.TargetPlatform:=Compiler.TargetPlatform;
  913. FConverter.TargetProcessor:=Compiler.TargetProcessor;
  914. FConverter.OnIsElementUsed:=@OnConverterIsElementUsed;
  915. FConverter.OnIsTypeInfoUsed:=@OnConverterIsTypeInfoUsed;
  916. FJSModule:=Converter.ConvertPasElement(PasModule,PascalResolver);
  917. except
  918. on E: Exception do
  919. HandleException(E);
  920. end;
  921. end;
  922. function TPas2jsCompilerFile.GetPasFirstSection: TPasSection;
  923. var
  924. aModule: TPasModule;
  925. begin
  926. aModule:=GetCurPasModule;
  927. if aModule=nil then exit;
  928. if aModule.ClassType=TPasUnitModule then
  929. Result:=TPasUnitModule(aModule).InterfaceSection
  930. else if aModule.ClassType=TPasProgram then
  931. Result:=TPasProgram(aModule).ProgramSection
  932. else if aModule.ClassType=TPasLibrary then
  933. Result:=TPasLibrary(aModule).LibrarySection
  934. else
  935. Result:=nil;
  936. end;
  937. function TPas2jsCompilerFile.GetPasImplSection: TPasSection;
  938. var
  939. aModule: TPasModule;
  940. begin
  941. Result:=nil;
  942. aModule:=GetCurPasModule;
  943. if aModule=nil then exit;
  944. Result:=aModule.ImplementationSection;
  945. end;
  946. function TPas2jsCompilerFile.GetPasMainUsesClause: TPasUsesClause;
  947. var
  948. aModule: TPasModule;
  949. IntfSection: TInterfaceSection;
  950. PrgSection: TProgramSection;
  951. LibSection: TLibrarySection;
  952. begin
  953. Result:=nil;
  954. aModule:=GetCurPasModule;
  955. if aModule=nil then exit;
  956. if aModule.ClassType=TPasModule then begin
  957. IntfSection:=TPasModule(aModule).InterfaceSection;
  958. if IntfSection<>nil then
  959. Result:=IntfSection.UsesClause;
  960. end else if aModule.ClassType=TPasProgram then begin
  961. PrgSection:=TPasProgram(aModule).ProgramSection;
  962. if PrgSection<>nil then
  963. Result:=PrgSection.UsesClause;
  964. end else if aModule.ClassType=TPasLibrary then begin
  965. LibSection:=TPasLibrary(aModule).LibrarySection;
  966. if LibSection<>nil then
  967. Result:=LibSection.UsesClause;
  968. end;
  969. end;
  970. function TPas2jsCompilerFile.GetPasImplUsesClause: TPasUsesClause;
  971. var
  972. aModule: TPasModule;
  973. begin
  974. Result:=nil;
  975. aModule:=GetCurPasModule;
  976. if aModule=nil then exit;
  977. if aModule.ImplementationSection<>nil then
  978. Result:=aModule.ImplementationSection.UsesClause;
  979. end;
  980. function TPas2jsCompilerFile.GetCurPasModule: TPasModule;
  981. begin
  982. if PasModule<>nil then
  983. Result:=PasModule
  984. else if Parser<>nil then
  985. Result:=Parser.CurModule
  986. else
  987. Result:=nil;
  988. end;
  989. function TPas2jsCompilerFile.GetModuleName: string;
  990. var
  991. aModule: TPasModule;
  992. begin
  993. aModule:=GetCurPasModule;
  994. if aModule<>nil then
  995. Result:=aModule.Name
  996. else
  997. Result:='';
  998. if Result='' then
  999. Result:=ExtractFilenameOnly(PasFilename);
  1000. end;
  1001. class function TPas2jsCompilerFile.GetFile(aModule: TPasModule
  1002. ): TPas2jsCompilerFile;
  1003. var
  1004. Scope: TPasModuleScope;
  1005. Resolver: TPas2jsCompilerResolver;
  1006. begin
  1007. Result:=nil;
  1008. if (aModule=nil) or (aModule.CustomData=nil) then exit;
  1009. if aModule.CustomData is TPas2jsCompilerFile then
  1010. Result:=TPas2jsCompilerFile(aModule.CustomData)
  1011. else if aModule.CustomData is TPasModuleScope then begin
  1012. Scope:=TPasModuleScope(aModule.CustomData);
  1013. Resolver:=NoNil(Scope.Owner) as TPas2jsCompilerResolver;
  1014. Result:=Resolver.Owner as TPas2jsCompilerFile;
  1015. end;
  1016. end;
  1017. function TPas2jsCompilerFile.OnPasTreeFindModule(const UseUnitname: String): TPasModule;
  1018. var
  1019. aNameSpace: String;
  1020. LastEl: TPasElement;
  1021. i: Integer;
  1022. begin
  1023. Result:=nil;
  1024. if CompareText(ExtractFilenameOnly(PasFilename),UseUnitname)=0 then begin
  1025. // duplicate identifier or unit cycle
  1026. Parser.RaiseParserError(nUnitCycle,[UseUnitname]);
  1027. end;
  1028. LastEl:=PascalResolver.LastElement;
  1029. if (LastEl<>nil)
  1030. and ((LastEl is TPasSection) or (LastEl.ClassType=TPasUsesUnit)
  1031. or (LastEl.Parent is TPasSection)) then
  1032. // ok
  1033. else
  1034. RaiseInternalError(20170504161408,'internal error TPas2jsCompilerFile.FindModule PasTree.LastElement='+GetObjName(LastEl)+' '+GetObjName(LastEl.Parent));
  1035. if (Pos('.',UseUnitname)<1) then begin
  1036. // generic unit -> search with namespaces
  1037. // first the default program namespace
  1038. aNameSpace:=Compiler.GetDefaultNamespace;
  1039. if aNameSpace<>'' then begin
  1040. Result:=FindUnit(aNameSpace+'.'+UseUnitname);
  1041. if Result<>nil then exit;
  1042. end;
  1043. // then the cmdline namespaces
  1044. for i:=0 to Compiler.FileCache.Namespaces.Count-1 do begin
  1045. aNameSpace:=Compiler.FileCache.Namespaces[i];
  1046. if aNameSpace='' then continue;
  1047. Result:=FindUnit(aNameSpace+'.'+UseUnitname);
  1048. if Result<>nil then exit;
  1049. end
  1050. end;
  1051. // search in unitpath
  1052. Result:=FindUnit(UseUnitname);
  1053. // if nil resolver will give a nice error position
  1054. end;
  1055. function TPas2jsCompilerFile.FindUnit(const UseUnitname: String): TPasModule;
  1056. function FindCycle(aFile, SearchFor: TPas2jsCompilerFile;
  1057. var Cycle: TFPList): boolean;
  1058. var
  1059. i: Integer;
  1060. aParent: TPas2jsCompilerFile;
  1061. begin
  1062. for i:=0 to aFile.UsedByCount[ubMainSection]-1 do begin
  1063. aParent:=aFile.UsedBy[ubMainSection,i];
  1064. if aParent=SearchFor then begin
  1065. // unit cycle found
  1066. Cycle:=TFPList.Create;
  1067. Cycle.Add(aParent);
  1068. Cycle.Add(aFile);
  1069. exit(true);
  1070. end;
  1071. if FindCycle(aParent,SearchFor,Cycle) then begin
  1072. Cycle.Add(aFile);
  1073. exit(true);
  1074. end;
  1075. end;
  1076. Result:=false;
  1077. end;
  1078. var
  1079. aFile: TPas2jsCompilerFile;
  1080. procedure CheckCycle;
  1081. var
  1082. i: Integer;
  1083. Cycle: TFPList;
  1084. CyclePath: String;
  1085. begin
  1086. if Parser.CurModule.ImplementationSection=nil then begin
  1087. // main uses section (e.g. interface or program, not implementation)
  1088. // -> check for cycles
  1089. aFile.FUsedBy[ubMainSection].Add(Self);
  1090. Cycle:=nil;
  1091. try
  1092. if FindCycle(aFile,aFile,Cycle) then begin
  1093. CyclePath:='';
  1094. for i:=0 to Cycle.Count-1 do begin
  1095. if i>0 then CyclePath+=',';
  1096. CyclePath+=TPas2jsCompilerFile(Cycle[i]).GetModuleName;
  1097. end;
  1098. Parser.RaiseParserError(nUnitCycle,[CyclePath]);
  1099. end;
  1100. finally
  1101. Cycle.Free;
  1102. end;
  1103. end else begin
  1104. // implementation uses section
  1105. aFile.FUsedBy[ubImplSection].Add(Self);
  1106. end;
  1107. end;
  1108. var
  1109. UsePasFilename, InFilename, UseJSFilename: String;
  1110. UseIsForeign: boolean;
  1111. begin
  1112. Result:=nil;
  1113. InFilename:='';
  1114. // first try registered units
  1115. aFile:=Compiler.FindUsedUnit(UseUnitname);
  1116. if aFile<>nil then begin
  1117. // known unit
  1118. if (aFile.PasUnitName<>'') and (CompareText(aFile.PasUnitName,UseUnitname)<>0) then
  1119. begin
  1120. Log.LogRaw(['Debug: TPas2jsPasTree.FindUnit unitname MISMATCH aFile.PasUnitname="',aFile.PasUnitName,'"',
  1121. ' Self=',FileResolver.Cache.FormatPath(PasFilename),
  1122. ' Uses=',UseUnitname,
  1123. ' IsForeign=',IsForeign]);
  1124. RaiseInternalError(20170504161412,'TPas2jsPasTree.FindUnit unit name mismatch');
  1125. end;
  1126. CheckCycle;
  1127. end else begin
  1128. // new unit -> search
  1129. // search Pascal file
  1130. UsePasFilename:=FileResolver.FindUnitFileName(UseUnitname,InFilename,UseIsForeign);
  1131. if UsePasFilename='' then begin
  1132. // can't find unit
  1133. exit;
  1134. end;
  1135. UseJSFilename:='';
  1136. if (not IsForeign) then
  1137. UseJSFilename:=FileResolver.FindUnitJSFileName(UsePasFilename);
  1138. // Log.LogRaw(['Debug: TPas2jsPasTree.FindUnit Self=',FileResolver.Cache.FormatPath(PasFilename),
  1139. // ' Uses=',UseUnitname,' Found="',FileResolver.Cache.FormatPath(UsePasFilename),'"',
  1140. // ' IsForeign=',IsForeign,' JSFile="',FileResolver.Cache.FormatPath(useJSFilename),'"']);
  1141. // load Pascal file
  1142. Compiler.LoadPasFile(UsePasFilename,UseUnitname,aFile);
  1143. if aFile=Self then begin
  1144. // unit uses itself -> cycle
  1145. Parser.RaiseParserError(nUnitCycle,[UseUnitname]);
  1146. end;
  1147. if aFile.PasUnitName<>UseUnitname then
  1148. RaiseInternalError(20170922143329,'aFile.PasUnitName='+aFile.PasUnitName+' UseUnitname='+UseUnitname);
  1149. Compiler.AddUsedUnit(aFile);
  1150. if aFile<>Compiler.FindUsedUnit(UseUnitname) then
  1151. begin
  1152. if Compiler.FindUsedUnit(UseUnitname)=nil then
  1153. RaiseInternalError(20170922143405,'UseUnitname='+UseUnitname)
  1154. else
  1155. RaiseInternalError(20170922143511,'UseUnitname='+UseUnitname+' Found='+Compiler.FindUsedUnit(UseUnitname).PasUnitName);
  1156. end;
  1157. CheckCycle;
  1158. aFile.JSFilename:=UseJSFilename;
  1159. aFile.IsForeign:=UseIsForeign;
  1160. // parse Pascal
  1161. aFile.ParsePascal;
  1162. // beware: the parser may not yet have finished due to unit cycles
  1163. end;
  1164. Result:=aFile.PasModule;
  1165. end;
  1166. { TPas2jsCompiler }
  1167. procedure TPas2jsCompiler.SetFileCache(AValue: TPas2jsFilesCache);
  1168. begin
  1169. if FFileCache=AValue then Exit;
  1170. FFileCacheAutoFree:=false;
  1171. FFileCache:=AValue;
  1172. end;
  1173. procedure TPas2jsCompiler.CfgSyntaxError(const Msg: string);
  1174. begin
  1175. Log.Log(mtError,Msg,0,CurrentCfgFilename,CurrentCfgLineNumber,0);
  1176. Terminate(ExitCodeErrorInConfig);
  1177. end;
  1178. function TPas2jsCompiler.GetFileCount: integer;
  1179. begin
  1180. Result:=FFiles.Count;
  1181. end;
  1182. function TPas2jsCompiler.GetDefaultNamespace: String;
  1183. var
  1184. C: TClass;
  1185. begin
  1186. Result:='';
  1187. if FMainFile=nil then exit;
  1188. if FMainFile.PasModule=nil then exit;
  1189. C:=FMainFile.PasModule.ClassType;
  1190. if (C=TPasProgram) or (C=TPasLibrary) or (C=TPasPackage) then
  1191. Result:=FMainFile.PascalResolver.DefaultNameSpace;
  1192. end;
  1193. procedure TPas2jsCompiler.AddDefinesForTargetProcessor;
  1194. begin
  1195. AddDefine(PasToJsProcessorNames[TargetProcessor]);
  1196. case TargetProcessor of
  1197. ProcessorECMAScript5: AddDefine('ECMAScript', '5');
  1198. ProcessorECMAScript6: AddDefine('ECMAScript', '6');
  1199. end;
  1200. end;
  1201. procedure TPas2jsCompiler.ConditionEvalLog(Sender: TCondDirectiveEvaluator;
  1202. Args: array of const);
  1203. begin
  1204. CfgSyntaxError(SafeFormat(Sender.MsgPattern,Args));
  1205. end;
  1206. function TPas2jsCompiler.ConditionEvalVariable(Sender: TCondDirectiveEvaluator;
  1207. aName: String; out Value: string): boolean;
  1208. var
  1209. i: Integer;
  1210. M: TMacroDef;
  1211. ms: TModeSwitch;
  1212. begin
  1213. // check defines
  1214. i:=FDefines.IndexOf(aName);
  1215. if i>=0 then begin
  1216. M:=TMacroDef(FDefines.Objects[i]);
  1217. if M=nil then
  1218. Value:=CondDirectiveBool[true]
  1219. else
  1220. Value:=M.Value;
  1221. exit(true);
  1222. end;
  1223. // check modeswitches
  1224. ms:=StrToModeSwitch(aName);
  1225. if (ms<>msNone) and (ms in p2jsMode_SwitchSets[Mode]) then begin
  1226. Value:=CondDirectiveBool[true];
  1227. exit(true);
  1228. end;
  1229. end;
  1230. procedure TPas2jsCompiler.AddDefinesForTargetPlatform;
  1231. begin
  1232. AddDefine(PasToJsPlatformNames[TargetPlatform]);
  1233. end;
  1234. procedure TPas2jsCompiler.Compile(StartTime: TDateTime);
  1235. var
  1236. Checked: TAVLTree;
  1237. CombinedFileWriter: TPas2JSMapper;
  1238. SrcFileCount: integer;
  1239. Seconds: TDateTime;
  1240. begin
  1241. if FMainFile<>nil then
  1242. RaiseInternalError(20170504192137,'');
  1243. Checked:=nil;
  1244. CombinedFileWriter:=nil;
  1245. SrcFileCount:=0;
  1246. try
  1247. // load main Pascal file
  1248. LoadPasFile(FileCache.MainSrcFile,'',FMainFile);
  1249. if MainFile=nil then exit;
  1250. // parse and load Pascal files recursively
  1251. FMainFile.ParsePascal;
  1252. // whole program optimization
  1253. if MainFile.PasModule is TPasProgram then
  1254. OptimizeProgram(MainFile);
  1255. // check what files need building
  1256. Checked:=TAVLTree.Create;
  1257. MarkNeedBuilding(MainFile,Checked,SrcFileCount);
  1258. FreeAndNil(Checked);
  1259. // convert all Pascal to JavaScript
  1260. Checked:=TAVLTree.Create;
  1261. CreateJavaScript(MainFile,Checked);
  1262. FreeAndNil(Checked);
  1263. // write .js files
  1264. Checked:=TAVLTree.Create;
  1265. WriteJSFiles(MainFile,CombinedFileWriter,Checked);
  1266. FreeAndNil(Checked);
  1267. // write success message
  1268. if ExitCode=0 then begin
  1269. Seconds:=(Now-StartTime)*86400;
  1270. Log.LogMsgIgnoreFilter(nLinesInFilesCompiled,
  1271. [IntToStr(FileCache.ReadLineCounter),IntToStr(SrcFileCount),
  1272. FormatFloat('0.0',Seconds)]);
  1273. end;
  1274. finally
  1275. Checked.Free;
  1276. if ExitCode<>0 then
  1277. Log.LogMsgIgnoreFilter(nCompilationAborted,[]);
  1278. CombinedFileWriter.Free;
  1279. end;
  1280. end;
  1281. function TPas2jsCompiler.MarkNeedBuilding(aFile: TPas2jsCompilerFile;
  1282. Checked: TAVLTree; var SrcFileCount: integer): boolean;
  1283. procedure Mark(MsgNumber: integer; Args: array of const);
  1284. begin
  1285. if aFile.NeedBuild then exit;
  1286. aFile.NeedBuild:=true;
  1287. inc(SrcFileCount);
  1288. if ShowDebug or ShowTriedUsedFiles then
  1289. Log.LogMsg(MsgNumber,Args,'',0,0,false);
  1290. end;
  1291. procedure CheckUsesClause(UsesClause: TPasUsesClause);
  1292. var
  1293. i: Integer;
  1294. UsedFile: TPas2jsCompilerFile;
  1295. aModule: TPasModule;
  1296. begin
  1297. if length(UsesClause)=0 then exit;
  1298. for i:=0 to length(UsesClause)-1 do begin
  1299. aModule:=UsesClause[i].Module as TPasModule;
  1300. UsedFile:=TPas2jsCompilerFile.GetFile(aModule);
  1301. if UsedFile=nil then
  1302. RaiseInternalError(20171214121631,aModule.Name);
  1303. if MarkNeedBuilding(UsedFile,Checked,SrcFileCount) then begin
  1304. if not aFile.NeedBuild then
  1305. Mark(nUnitNeedsCompileDueToUsedUnit,
  1306. [aFile.GetModuleName,UsedFile.GetModuleName]);
  1307. end;
  1308. end;
  1309. end;
  1310. begin
  1311. Result:=false;
  1312. // check each file only once
  1313. if Checked.Find(aFile)<>nil then
  1314. exit(aFile.NeedBuild);
  1315. Checked.Add(aFile);
  1316. if FileCache.AllJSIntoMainJS and (WPOAnalyzer<>nil)
  1317. and not WPOAnalyzer.IsUsed(aFile.PasModule) then
  1318. exit(false);
  1319. // check dependencies
  1320. CheckUsesClause(aFile.GetPasMainUsesClause);
  1321. CheckUsesClause(aFile.GetPasImplUsesClause);
  1322. if (not aFile.NeedBuild) and (not aFile.IsForeign) then begin
  1323. // this unit can be compiled
  1324. if aFile.IsMainFile then
  1325. Mark(nUnitNeedsCompileDueToOption,[aFile.GetModuleName,'<main source file>'])
  1326. else if coBuildAll in Options then
  1327. Mark(nUnitNeedsCompileDueToOption,[aFile.GetModuleName,'-B'])
  1328. else if FileCache.AllJSIntoMainJS then
  1329. Mark(nUnitNeedsCompileDueToOption,[aFile.GetModuleName,'-Jc'])
  1330. else if (aFile.JSFilename<>'') and (not FileExists(aFile.JSFilename)) then
  1331. Mark(nUnitNeedsCompileJSMissing,[aFile.GetModuleName,FileCache.FormatPath(aFile.JSFilename)])
  1332. else if (aFile.JSFilename<>'')
  1333. and (FileAge(aFile.PasFilename)>FileAge(aFile.JSFilename)) then begin
  1334. // ToDo: replace FileAge with checksum
  1335. Mark(nUnitNeedsCompilePasHasChanged,[aFile.GetModuleName,FileCache.FormatPath(aFile.JSFilename)])
  1336. end;
  1337. end;
  1338. if aFile.NeedBuild then begin
  1339. // unit needs compile
  1340. if aFile.IsForeign then begin
  1341. // ... but is forbidden to compile
  1342. Log.LogMsg(nOptionForbidsCompile,[aFile.GetModuleName]);
  1343. Terminate(ExitCodeWriteError);
  1344. end;
  1345. end;
  1346. Result:=aFile.NeedBuild;
  1347. end;
  1348. procedure TPas2jsCompiler.OptimizeProgram(aFile: TPas2jsCompilerFile);
  1349. begin
  1350. if not FileCache.AllJSIntoMainJS then exit;
  1351. if coKeepNotUsedDeclarationsWPO in Options then exit;
  1352. if not (aFile.PasModule is TPasProgram) then exit;
  1353. FWPOAnalyzer:=TPas2JSWPOptimizer.Create;
  1354. FWPOAnalyzer.Resolver:=aFile.PascalResolver;
  1355. FWPOAnalyzer.Options:=FWPOAnalyzer.Options+[paoOnlyExports];
  1356. FWPOAnalyzer.AnalyzeWholeProgram(TPasProgram(aFile.PasModule));
  1357. end;
  1358. procedure TPas2jsCompiler.CreateJavaScript(aFile: TPas2jsCompilerFile;
  1359. Checked: TAVLTree);
  1360. procedure CheckUsesClause(UsesClause: TPasUsesClause);
  1361. var
  1362. i: Integer;
  1363. UsedFile: TPas2jsCompilerFile;
  1364. aModule: TPasModule;
  1365. begin
  1366. if length(UsesClause)=0 then exit;
  1367. for i:=0 to length(UsesClause)-1 do begin
  1368. aModule:=UsesClause[i].Module as TPasModule;
  1369. UsedFile:=TPas2jsCompilerFile.GetFile(aModule);
  1370. if UsedFile=nil then
  1371. RaiseInternalError(20171214121720,aModule.Name);
  1372. CreateJavaScript(UsedFile,Checked);
  1373. end;
  1374. end;
  1375. begin
  1376. if (aFile.JSModule<>nil) or (not aFile.NeedBuild) then exit;
  1377. // check each file only once
  1378. if Checked.Find(aFile)<>nil then exit;
  1379. Checked.Add(aFile);
  1380. Log.LogMsg(nCompilingFile,[FileCache.FormatPath(aFile.PasFilename)],'',0,0,
  1381. not (coShowLineNumbers in Options));
  1382. // convert dependencies
  1383. CheckUsesClause(aFile.GetPasMainUsesClause);
  1384. CheckUsesClause(aFile.GetPasImplUsesClause);
  1385. aFile.CreateJS;
  1386. end;
  1387. procedure TPas2jsCompiler.FinishSrcMap(SrcMap: TPas2JSSrcMap);
  1388. var
  1389. LocalFilename, MapFilename, BaseDir: String;
  1390. aFile: TPas2jsCachedFile;
  1391. i: Integer;
  1392. begin
  1393. if SrcMapBaseDir<>'' then
  1394. BaseDir:=SrcMapBaseDir
  1395. else
  1396. BaseDir:=ExtractFilePath(ExtractFilePath(SrcMap.LocalFilename));
  1397. for i:=0 to SrcMap.SourceCount-1 do begin
  1398. LocalFilename:=SrcMap.SourceFiles[i];
  1399. if LocalFilename='' then continue;
  1400. if SrcMapInclude then begin
  1401. // include source in SrcMap
  1402. aFile:=FileCache.LoadTextFile(LocalFilename);
  1403. SrcMap.SourceContents[i]:=aFile.Source;
  1404. end;
  1405. // translate local file name
  1406. if BaseDir<>'' then begin
  1407. if not TryCreateRelativePath(LocalFilename,BaseDir,true,MapFilename)
  1408. then begin
  1409. // e.g. file is on another partition
  1410. if not SrcMapInclude then begin
  1411. Log.Log(mtError,
  1412. SafeFormat(sUnableToTranslatePathToDir,[LocalFilename,BaseDir]),
  1413. nUnableToTranslatePathToDir);
  1414. Terminate(ExitCodeConverterError);
  1415. end;
  1416. // the source is included, do not translate the filename
  1417. MapFilename:=LocalFilename;
  1418. end;
  1419. {$IFNDEF Unix}
  1420. // use / as PathDelim
  1421. MapFilename:=StringReplace(MapFilename,PathDelim,'/',[rfReplaceAll]);
  1422. {$ENDIF}
  1423. SrcMap.SourceTranslatedFiles[i]:=MapFilename;
  1424. end;
  1425. end;
  1426. end;
  1427. function TPas2jsCompiler.DoWriteJSFile(const DestFilename: String;
  1428. aWriter: TPas2JSMapper): Boolean;
  1429. begin
  1430. Result:=False;
  1431. if DestFilename='' then ;
  1432. if aWriter=nil then ;
  1433. end;
  1434. procedure TPas2jsCompiler.WriteJSFiles(aFile: TPas2jsCompilerFile;
  1435. var CombinedFileWriter: TPas2JSMapper; Checked: TAVLTree);
  1436. procedure CheckUsesClause(UsesClause: TPasUsesClause);
  1437. var
  1438. i: Integer;
  1439. UsedFile: TPas2jsCompilerFile;
  1440. aModule: TPasModule;
  1441. begin
  1442. if length(UsesClause)=0 then exit;
  1443. for i:=0 to length(UsesClause)-1 do begin
  1444. aModule:=UsesClause[i].Module as TPasModule;
  1445. UsedFile:=TPas2jsCompilerFile.GetFile(aModule);
  1446. if UsedFile=nil then
  1447. RaiseInternalError(20171214121720,aModule.Name);
  1448. WriteJSFiles(UsedFile,CombinedFileWriter,Checked);
  1449. end;
  1450. end;
  1451. var
  1452. aFileWriter: TPas2JSMapper;
  1453. FreeWriter: Boolean;
  1454. procedure CreateFileWriter(aFilename: string);
  1455. var
  1456. SrcMap: TPas2JSSrcMap;
  1457. begin
  1458. aFileWriter:=TPas2JSMapper.Create(4096);
  1459. FreeWriter:=true;
  1460. if SrcMapEnable then begin
  1461. SrcMap:=TPas2JSSrcMap.Create(ExtractFilename(aFilename));
  1462. aFileWriter.SrcMap:=SrcMap;
  1463. SrcMap.Release;// release the refcount from the Create
  1464. SrcMap.SourceRoot:=SrcMapSourceRoot;
  1465. SrcMap.LocalFilename:=aFile.JSFilename;
  1466. end;
  1467. end;
  1468. var
  1469. DestFilename, DestDir, Src, MapFilename: String;
  1470. aJSWriter: TJSWriter;
  1471. fs: TFileStream;
  1472. ms: TMemoryStream;
  1473. begin
  1474. //writeln('TPas2jsCompiler.WriteJSFiles ',aFile.PasFilename,' Need=',aFile.NeedBuild,' Checked=',Checked.Find(aFile)<>nil);
  1475. if (aFile.JSModule=nil) or (not aFile.NeedBuild) then exit;
  1476. // check each file only once
  1477. if Checked.Find(aFile)<>nil then exit;
  1478. Checked.Add(aFile);
  1479. FreeWriter:=false;
  1480. if FileCache.AllJSIntoMainJS and (CombinedFileWriter=nil) then begin
  1481. // create CombinedFileWriter
  1482. DestFilename:=FileCache.GetResolvedMainJSFile;
  1483. CreateFileWriter(DestFilename);
  1484. CombinedFileWriter:=aFileWriter;
  1485. FileCache.InsertCustomJSFiles(CombinedFileWriter);
  1486. end else begin
  1487. DestFilename:=aFile.JSFilename;
  1488. end;
  1489. // convert dependencies
  1490. CheckUsesClause(aFile.GetPasMainUsesClause);
  1491. CheckUsesClause(aFile.GetPasImplUsesClause);
  1492. aJSWriter:=nil;
  1493. aFileWriter:=CombinedFileWriter;
  1494. try
  1495. if aFileWriter=nil then begin
  1496. // create writer for this file
  1497. CreateFileWriter(DestFilename);
  1498. if aFile.IsMainFile and not FileCache.AllJSIntoMainJS then
  1499. FileCache.InsertCustomJSFiles(aFileWriter);
  1500. end;
  1501. // write JavaScript
  1502. aJSWriter:=TJSWriter.Create(aFileWriter);
  1503. aJSWriter.Options:=[woUseUTF8,woCompactArrayLiterals,woCompactObjectLiterals,woCompactArguments];
  1504. aJSWriter.IndentSize:=2;
  1505. aJSWriter.WriteJS(aFile.JSModule);
  1506. if aFile.IsMainFile and (TargetPlatform=PlatformNodeJS) then
  1507. aFileWriter.WriteFile('rtl.run();'+LineEnding,aFile.PasFilename);
  1508. // Give chance to descendants to write file
  1509. if DoWriteJSFile(aFile.JSFilename,aFileWriter) then
  1510. exit;// descendant has written -> finished
  1511. if (aFile.JSFilename='') and (FileCache.MainJSFile='.') then begin
  1512. // write to stdout
  1513. Log.LogRaw(aFileWriter.AsAnsistring);
  1514. end else if FreeWriter then begin
  1515. // write to file
  1516. //writeln('TPas2jsCompiler.WriteJSFiles ',aFile.PasFilename,' ',aFile.JSFilename);
  1517. Log.LogMsg(nWritingFile,[FileCache.FormatPath(DestFilename)],'',0,0,
  1518. not (coShowLineNumbers in Options));
  1519. // check output directory
  1520. DestDir:=ChompPathDelim(ExtractFilePath(DestFilename));
  1521. if (DestDir<>'') and not DirectoryExists(DestDir) then begin
  1522. Log.LogMsg(nOutputDirectoryNotFound,[FileCache.FormatPath(DestDir)]);
  1523. Terminate(ExitCodeFileNotFound);
  1524. end;
  1525. if DirectoryExists(DestFilename) then begin
  1526. Log.LogMsg(nFileIsFolder,[FileCache.FormatPath(DestFilename)]);
  1527. Terminate(ExitCodeWriteError);
  1528. end;
  1529. MapFilename:=DestFilename+'.map';
  1530. // write js
  1531. try
  1532. fs:=TFileStream.Create(DestFilename,fmCreate);
  1533. try
  1534. // UTF8-BOM
  1535. if (Log.Encoding='') or (Log.Encoding='utf8') then begin
  1536. Src:=String(UTF8BOM);
  1537. fs.Write(Src[1],length(Src));
  1538. end;
  1539. // JS source
  1540. fs.Write(aFileWriter.Buffer^,aFileWriter.BufferLength);
  1541. // source map comment
  1542. if aFileWriter.SrcMap<>nil then begin
  1543. Src:='//# sourceMappingURL='+ExtractFilename(MapFilename)+LineEnding;
  1544. fs.Write(Src[1],length(Src));
  1545. end;
  1546. finally
  1547. fs.Free;
  1548. end;
  1549. except
  1550. on E: Exception do begin
  1551. Log.LogRaw('Error: '+E.Message);
  1552. Log.LogMsg(nUnableToWriteFile,[FileCache.FormatPath(DestFilename)]);
  1553. Terminate(ExitCodeWriteError);
  1554. end;
  1555. end;
  1556. // write source map
  1557. if aFileWriter.SrcMap<>nil then begin
  1558. Log.LogMsg(nWritingFile,[FileCache.FormatPath(MapFilename)],'',0,0,
  1559. not (coShowLineNumbers in Options));
  1560. FinishSrcMap(aFileWriter.SrcMap);
  1561. try
  1562. ms:=TMemoryStream.Create;
  1563. try
  1564. // Note: No UTF-8 BOM in source map, Chrome 59 gives an error
  1565. aFileWriter.SrcMap.SaveToStream(ms);
  1566. ms.Position:=0;
  1567. ms.SaveToFile(MapFilename);
  1568. finally
  1569. ms.Free;
  1570. end;
  1571. except
  1572. on E: Exception do begin
  1573. Log.LogRaw('Error: '+E.Message);
  1574. Log.LogMsg(nUnableToWriteFile,[FileCache.FormatPath(MapFilename)]);
  1575. Terminate(ExitCodeWriteError);
  1576. end;
  1577. end;
  1578. end;
  1579. end;
  1580. finally
  1581. if FreeWriter then begin
  1582. if CombinedFileWriter=aFileWriter then
  1583. CombinedFileWriter:=nil;
  1584. aFileWriter.Free
  1585. end;
  1586. aJSWriter.Free;
  1587. end;
  1588. end;
  1589. procedure TPas2jsCompiler.InitParamMacros;
  1590. begin
  1591. ParamMacros.AddValue('Pas2jsFullVersion','major.minor.release<extra>',GetVersion(false));
  1592. ParamMacros.AddValue('Pas2jsVersion','major.minor.release',GetVersion(true));
  1593. ParamMacros.AddFunction('Env','environment variable, e.g. $Env(HOME)',@OnMacroEnv,true);
  1594. ParamMacros.AddFunction('CfgDir','Use within a config file. The directory of this config file',@OnMacroCfgDir,false);
  1595. // Additionally, under windows the following special variables are recognized:
  1596. { ToDo:
  1597. LOCAL_APPDATA
  1598. Usually the directory ”Local settings/Application Data” under the user’s home directory.
  1599. APPDATA
  1600. Usually the directory ”Application Data” under the user’s home directory.
  1601. COMMON_APPDATA
  1602. Usually the directory ”Application Data” under the ’All users’ directory.
  1603. PERSONAL
  1604. Usually the ”My documents” directory of the user.
  1605. PROGRAM_FILES
  1606. Usually ”program files” directory on the system drive
  1607. PROGRAM_FILES_COMMON
  1608. Usually the ”Common files” directory under the program files directory.
  1609. PROFILE
  1610. The user’s home directory. }
  1611. end;
  1612. procedure TPas2jsCompiler.ClearDefines;
  1613. var
  1614. i: Integer;
  1615. M: TMacroDef;
  1616. begin
  1617. for i:=0 to FDefines.Count-1 do
  1618. begin
  1619. M:=TMacroDef(FDefines.Objects[i]);
  1620. M.Free;
  1621. end;
  1622. FDefines.Clear;
  1623. end;
  1624. procedure TPas2jsCompiler.RaiseInternalError(id: int64; Msg: string);
  1625. begin
  1626. Log.LogRaw('['+IntToStr(id)+'] '+Msg);
  1627. raise Exception.Create(Msg);
  1628. end;
  1629. procedure TPas2jsCompiler.Terminate(TheExitCode: integer);
  1630. begin
  1631. ExitCode:=TheExitCode;
  1632. if Log<>nil then Log.Flush;
  1633. raise ECompilerTerminate.Create('');
  1634. end;
  1635. function TPas2jsCompiler.GetShowDebug: boolean;
  1636. begin
  1637. Result:=coShowDebug in Options;
  1638. end;
  1639. function TPas2jsCompiler.GetShowFullPaths: boolean;
  1640. begin
  1641. Result:=FileCache.ShowFullPaths;
  1642. end;
  1643. function TPas2jsCompiler.GetShowLogo: Boolean;
  1644. begin
  1645. Result:=coShowLogo in FOptions;
  1646. end;
  1647. function TPas2jsCompiler.GetShowTriedUsedFiles: boolean;
  1648. begin
  1649. Result:=FileCache.ShowTriedUsedFiles;
  1650. end;
  1651. function TPas2jsCompiler.GetShowUsedTools: boolean;
  1652. begin
  1653. Result:=coShowUsedTools in Options;
  1654. end;
  1655. function TPas2jsCompiler.GetSkipDefaultConfig: Boolean;
  1656. begin
  1657. Result:=coSkipDefaultConfigs in FOptions;
  1658. end;
  1659. function TPas2jsCompiler.GetSrcMapBaseDir: string;
  1660. begin
  1661. Result:=FileCache.SrcMapBaseDir;
  1662. end;
  1663. function TPas2jsCompiler.GetSrcMapEnable: boolean;
  1664. begin
  1665. Result:=coSourceMapCreate in FOptions;
  1666. end;
  1667. function TPas2jsCompiler.GetSrcMapInclude: boolean;
  1668. begin
  1669. Result:=coSourceMapInclude in FOptions;
  1670. end;
  1671. procedure TPas2jsCompiler.LoadConfig(CfgFilename: string);
  1672. type
  1673. TSkip = (
  1674. skipNone,
  1675. skipIf,
  1676. skipElse
  1677. );
  1678. const
  1679. IdentChars = ['a'..'z','A'..'Z','_','0'..'9'];
  1680. var
  1681. Line: String;
  1682. p, StartP: PChar;
  1683. function GetWord: String;
  1684. begin
  1685. StartP:=p;
  1686. while (p^ in IdentChars) or (p^>#127) do inc(p);
  1687. Result:=copy(Line,StartP-PChar(Line)+1,p-StartP);
  1688. while p^ in [' ',#9] do inc(p);
  1689. end;
  1690. procedure DebugCfgDirective(const s: string);
  1691. begin
  1692. Log.LogMsg(nCfgDirective,[Line,s],CurrentCfgFilename,CurrentCfgLineNumber,1,false);
  1693. end;
  1694. var
  1695. OldCfgFilename, Directive, aName, Expr: String;
  1696. aFile: TPas2jsFileLineReader;
  1697. IfLvl, SkipLvl, OldCfgLineNumber: Integer;
  1698. Skip: TSkip;
  1699. CacheFile: TPas2jsCachedFile;
  1700. begin
  1701. if ShowTriedUsedFiles then
  1702. Log.LogMsgIgnoreFilter(nReadingOptionsFromFile,[CfgFilename]);
  1703. IfLvl:=0;
  1704. SkipLvl:=0;
  1705. Skip:=skipNone;
  1706. aFile:=nil;
  1707. try
  1708. OldCfgFilename:=FCurrentCfgFilename;
  1709. FCurrentCfgFilename:=CfgFilename;
  1710. OldCfgLineNumber:=FCurrentCfgLineNumber;
  1711. CacheFile:=FileCache.LoadTextFile(CfgFilename);
  1712. aFile:=CacheFile.CreateLineReader(true);
  1713. while not aFile.IsEOF do begin
  1714. Line:=aFile.ReadLine;
  1715. FCurrentCfgLineNumber:=aFile.LineNumber;
  1716. if ShowDebug then
  1717. Log.LogMsgIgnoreFilter(nInterpretingFileOption,[Line]);
  1718. if Line='' then continue;
  1719. p:=PChar(Line);
  1720. while (p^ in [' ',#9]) do inc(p);
  1721. if p^=#0 then continue; // empty line
  1722. if p^='#' then begin
  1723. // cfg directive
  1724. inc(p);
  1725. if p^ in [#0,#9,' ','-'] then continue; // comment
  1726. Directive:=lowercase(GetWord);
  1727. case Directive of
  1728. 'ifdef','ifndef':
  1729. begin
  1730. inc(IfLvl);
  1731. if Skip=skipNone then begin
  1732. aName:=GetWord;
  1733. if IsDefined(aName)=(Directive='ifdef') then begin
  1734. // execute block
  1735. if ShowDebug then
  1736. DebugCfgDirective('true -> execute');
  1737. end else begin
  1738. // skip block
  1739. if ShowDebug then
  1740. DebugCfgDirective('false -> skip');
  1741. SkipLvl:=IfLvl;
  1742. Skip:=skipIf;
  1743. end;
  1744. end;
  1745. end;
  1746. 'if':
  1747. begin
  1748. inc(IfLvl);
  1749. if Skip=skipNone then begin
  1750. Expr:=copy(Line,p-PChar(Line)+1,length(Line));
  1751. if ConditionEvaluator.Eval(Expr) then begin
  1752. // execute block
  1753. if ShowDebug then
  1754. DebugCfgDirective('true -> execute');
  1755. end else begin
  1756. // skip block
  1757. if ShowDebug then
  1758. DebugCfgDirective('false -> skip');
  1759. SkipLvl:=IfLvl;
  1760. Skip:=skipIf;
  1761. end;
  1762. end;
  1763. end;
  1764. 'else':
  1765. begin
  1766. if IfLvl=0 then
  1767. CfgSyntaxError('"'+Directive+'" without ifdef');
  1768. if (Skip=skipElse) and (IfLvl=SkipLvl) then
  1769. CfgSyntaxError('"there was already an $else');;
  1770. if (Skip=skipIf) and (IfLvl=SkipLvl) then begin
  1771. // if-block was skipped -> execute else block
  1772. if ShowDebug then
  1773. DebugCfgDirective('execute');
  1774. SkipLvl:=0;
  1775. Skip:=skipNone;
  1776. end else if Skip=skipNone then begin
  1777. // if-block was executed -> skip else block
  1778. if ShowDebug then
  1779. DebugCfgDirective('skip');
  1780. Skip:=skipElse;
  1781. end;
  1782. end;
  1783. 'elseif':
  1784. begin
  1785. if IfLvl=0 then
  1786. CfgSyntaxError('"'+Directive+'" without ifdef');
  1787. if (Skip=skipIf) and (IfLvl=SkipLvl) then begin
  1788. // if-block was skipped -> try this elseif
  1789. Expr:=copy(Line,p-PChar(Line)+1,length(Line));
  1790. if ConditionEvaluator.Eval(Expr) then begin
  1791. // execute elseif block
  1792. if ShowDebug then
  1793. DebugCfgDirective('true -> execute');
  1794. SkipLvl:=0;
  1795. Skip:=skipNone;
  1796. end else begin
  1797. // skip elseif block
  1798. if ShowDebug then
  1799. DebugCfgDirective('false -> skip');
  1800. end;
  1801. end else if Skip=skipNone then begin
  1802. // if-block was executed -> skip without test
  1803. if ShowDebug then
  1804. DebugCfgDirective('no test -> skip');
  1805. Skip:=skipIf;
  1806. end;
  1807. end;
  1808. 'endif':
  1809. begin
  1810. if IfLvl=0 then
  1811. CfgSyntaxError('"'+Directive+'" without ifdef');
  1812. dec(IfLvl);
  1813. if IfLvl<SkipLvl then begin
  1814. // end block
  1815. if ShowDebug then
  1816. DebugCfgDirective('end block');
  1817. SkipLvl:=0;
  1818. Skip:=skipNone;
  1819. end;
  1820. end;
  1821. 'error':
  1822. ParamFatal('user defined: '+copy(Line,p-PChar(Line)+1,length(Line)))
  1823. else
  1824. if Skip=skipNone then
  1825. CfgSyntaxError('unknown directive "'+Directive+'"')
  1826. else
  1827. DebugCfgDirective('skipping unknown directive');
  1828. end;
  1829. end else if Skip=skipNone then begin
  1830. // option line
  1831. Line:=String(p);
  1832. ReadParam(Line,false,false);
  1833. end;
  1834. end;
  1835. finally
  1836. FCurrentCfgFilename:=OldCfgFilename;
  1837. FCurrentCfgLineNumber:=OldCfgLineNumber;
  1838. aFile.Free;
  1839. end;
  1840. if ShowTriedUsedFiles then
  1841. Log.LogMsgIgnoreFilter(nEndOfReadingConfigFile,[CfgFilename]);
  1842. end;
  1843. procedure TPas2jsCompiler.LoadDefaultConfig;
  1844. function TryConfig(aFilename: string): boolean;
  1845. begin
  1846. Result:=false;
  1847. if aFilename='' then exit;
  1848. aFilename:=ExpandFileNameUTF8(aFilename);
  1849. if ShowTriedUsedFiles then
  1850. Log.LogMsgIgnoreFilter(nConfigFileSearch,[aFilename]);
  1851. if not FileExists(aFilename) then exit;
  1852. Result:=true;
  1853. LoadConfig(aFilename);
  1854. end;
  1855. var
  1856. aFilename: String;
  1857. begin
  1858. // first try HOME directory
  1859. aFilename:=ChompPathDelim(GetEnvironmentVariableUTF8('HOME'));
  1860. if aFilename<>'' then
  1861. if TryConfig(aFilename+PathDelim+DefaultConfigFile) then exit;
  1862. // then try compiler directory
  1863. if (CompilerExe<>'') then begin
  1864. aFilename:=ExtractFilePath(CompilerExe);
  1865. if aFilename<>'' then begin
  1866. aFilename:=IncludeTrailingPathDelimiter(aFilename)+DefaultConfigFile;
  1867. if TryConfig(aFilename) then exit;
  1868. end;
  1869. end;
  1870. // finally try global directory
  1871. {$IFDEF Unix}
  1872. if TryConfig('/etc/'+DefaultConfigFile) then exit;
  1873. {$ENDIF}
  1874. end;
  1875. procedure TPas2jsCompiler.ParamFatal(Msg: string);
  1876. begin
  1877. Log.LogRaw(['Fatal: ',Msg]);
  1878. Terminate(ExitCodeErrorInParams);
  1879. end;
  1880. procedure TPas2jsCompiler.ReadParam(Param: string; Quick, FromCmdLine: boolean);
  1881. procedure UnknownParam;
  1882. begin
  1883. ParamFatal('unknown parameter "'+Param+'". Use -h for help.');
  1884. end;
  1885. procedure AppendInfo(var Value: string; Add: string);
  1886. begin
  1887. if Value<>'' then
  1888. Value:=Value+' ';
  1889. Value:=Value+Add;
  1890. end;
  1891. var
  1892. p: PChar;
  1893. EnabledFlags, DisabledFlags, Identifier, Value, aFilename, ErrorMsg: string;
  1894. i: Integer;
  1895. c: Char;
  1896. aProc: TPasToJsProcessor;
  1897. Enable: Boolean;
  1898. aPlatform: TPasToJsPlatform;
  1899. begin
  1900. if ShowDebug then
  1901. if Quick then
  1902. Log.LogMsgIgnoreFilter(nQuickHandlingOption,[Param])
  1903. else
  1904. Log.LogMsgIgnoreFilter(nHandlingOption,[Param]);
  1905. if Param='' then exit;
  1906. ParamMacros.Substitute(Param,Self);
  1907. if Param='' then exit;
  1908. if Quick and ((Param='-h') or (Param='-?') or (Param='--help')) then begin
  1909. WriteHelp;
  1910. Terminate(0);
  1911. end;
  1912. p:=PChar(Param);
  1913. case p^ of
  1914. '-':
  1915. begin
  1916. inc(p);
  1917. case p^ of
  1918. 'i':
  1919. begin
  1920. // write information and halt
  1921. inc(p);
  1922. Value:='';
  1923. repeat
  1924. case p^ of
  1925. #0:
  1926. if p-PChar(Param)=length(Param) then
  1927. begin
  1928. if length(Param)=2 then
  1929. WriteInfo;
  1930. break;
  1931. end;
  1932. 'D': // wite compiler date
  1933. AppendInfo(Value,GetCompiledDate);
  1934. 'V': // write short version
  1935. AppendInfo(Value,GetVersion(true));
  1936. 'W': // write long version
  1937. AppendInfo(Value,GetVersion(false));
  1938. 'S':
  1939. begin
  1940. inc(p);
  1941. case p^ of
  1942. #0:
  1943. ParamFatal('missing info option after S in "'+Param+'".');
  1944. 'O': // write source OS
  1945. AppendInfo(Value,GetCompiledTargetOS);
  1946. 'P': // write source processor
  1947. AppendInfo(Value,GetCompiledTargetCPU);
  1948. else
  1949. ParamFatal('unknown info option S"'+p^+'" in "'+Param+'".');
  1950. end;
  1951. end;
  1952. 'T':
  1953. begin
  1954. inc(p);
  1955. case p^ of
  1956. #0:
  1957. ParamFatal('missing info option after T in "'+Param+'".');
  1958. 'O': // write target platform
  1959. AppendInfo(Value,PasToJsPlatformNames[TargetPlatform]);
  1960. 'P': // write target processor
  1961. AppendInfo(Value,PasToJsProcessorNames[TargetProcessor]);
  1962. else
  1963. ParamFatal('unknown info option S"'+p^+'" in "'+Param+'".');
  1964. end;
  1965. end;
  1966. else
  1967. ParamFatal('unknown info option "'+p^+'" in "'+Param+'".');
  1968. end;
  1969. inc(p);
  1970. until false;
  1971. Log.LogRaw(Value);
  1972. Terminate(0);
  1973. end;
  1974. 'B','l','n':
  1975. begin
  1976. ReadSingleLetterOptions(Param,p,'Bln',EnabledFlags,DisabledFlags);
  1977. for i:=1 to length(EnabledFlags) do begin
  1978. case EnabledFlags[i] of
  1979. 'B': Options:=Options+[coBuildAll];
  1980. 'l': ShowLogo:=true;
  1981. 'n': SkipDefaultConfig:=true;
  1982. end;
  1983. end;
  1984. for i:=1 to length(DisabledFlags) do begin
  1985. case DisabledFlags[i] of
  1986. 'B': Options:=Options-[coBuildAll];
  1987. 'l': ShowLogo:=false;
  1988. 'n': SkipDefaultConfig:=false;
  1989. end;
  1990. end;
  1991. end;
  1992. 'd': // define
  1993. if not Quick then begin
  1994. Identifier:=copy(Param,3,length(Param));
  1995. i:=Pos(':=',Identifier);
  1996. if i>0 then begin
  1997. Value:=copy(Identifier,i+2,length(Identifier));
  1998. Identifier:=LeftStr(Identifier,i-1);
  1999. if not IsValidIdent(Identifier) then
  2000. ParamFatal('invalid define: "'+Param+'"');
  2001. AddDefine(Identifier,Value);
  2002. end else begin
  2003. if not IsValidIdent(Identifier) then
  2004. ParamFatal('invalid define: "'+Param+'"');
  2005. AddDefine(Identifier);
  2006. end;
  2007. end;
  2008. 'F': // folders and search paths
  2009. begin
  2010. inc(p);
  2011. c:=p^;
  2012. inc(p);
  2013. case c of
  2014. 'e': Log.OutputFilename:=String(p);
  2015. 'i': if not FileCache.AddIncludePaths(String(p),FromCmdLine,ErrorMsg) then
  2016. ParamFatal('invalid include path "'+ErrorMsg+'"');
  2017. 'u': if not FileCache.AddUnitPaths(String(p),FromCmdLine,ErrorMsg) then
  2018. ParamFatal('invalid unit path "'+ErrorMsg+'"');
  2019. 'U': FileCache.UnitOutputPath:=String(p);
  2020. else UnknownParam;
  2021. end;
  2022. end;
  2023. 'I': // include path, same as -Fi
  2024. if not Quick then begin
  2025. inc(p);
  2026. if not FileCache.AddIncludePaths(String(p),FromCmdLine,ErrorMsg) then
  2027. ParamFatal('invalid include path "'+ErrorMsg+'"');
  2028. end;
  2029. 'J': // extra pas2js options
  2030. begin
  2031. inc(p);
  2032. c:=p^;
  2033. inc(p);
  2034. case c of
  2035. 'c': FileCache.AllJSIntoMainJS:=p^<>'-';
  2036. 'i':
  2037. if p^=#0 then
  2038. ParamFatal('missing insertion file: '+Param)
  2039. else if not Quick then begin
  2040. aFilename:=String(p);
  2041. if aFilename='' then
  2042. UnknownParam;
  2043. if aFilename[length(aFilename)]='-' then begin
  2044. Delete(aFilename,length(aFilename),1);
  2045. if aFilename='' then
  2046. UnknownParam;
  2047. FileCache.RemoveInsertFilename(aFilename);
  2048. end else
  2049. FileCache.AddInsertFilename(aFilename);
  2050. end;
  2051. 'l': SetOption(coLowerCase,p^<>'-');
  2052. 'm':
  2053. // source map options
  2054. if p^=#0 then
  2055. SrcMapEnable:=true
  2056. else if p^='-' then
  2057. begin
  2058. if p[1]<>#0 then
  2059. UnknownParam;
  2060. SrcMapEnable:=false;
  2061. end
  2062. else
  2063. begin
  2064. Value:=String(p);
  2065. if Value='include' then
  2066. SrcMapInclude:=true
  2067. else if Value='include-' then
  2068. SrcMapInclude:=false
  2069. else
  2070. begin
  2071. i:=Pos('=',Value);
  2072. if i<1 then
  2073. UnknownParam;
  2074. Identifier:=LeftStr(Value,i-1);
  2075. Delete(Value,1,i);
  2076. if Identifier='sourceroot' then
  2077. SrcMapSourceRoot:=Value
  2078. else if Identifier='basedir' then
  2079. SrcMapBaseDir:=Value
  2080. else
  2081. UnknownParam;
  2082. end;
  2083. // enable source maps when setting any -Jm<x> option
  2084. SrcMapEnable:=true;
  2085. end;
  2086. 'u':
  2087. if not Quick then
  2088. if not FileCache.AddSrcUnitPaths(String(p),FromCmdLine,ErrorMsg) then
  2089. ParamFatal('invalid foreign unit path "'+ErrorMsg+'"');
  2090. 'e':
  2091. begin
  2092. Identifier:=NormalizeEncoding(String(p));
  2093. case Identifier of
  2094. 'console','system','utf8': Log.Encoding:=Identifier;
  2095. else ParamFatal('invalid encoding "'+String(p)+'"');
  2096. end;
  2097. end
  2098. else UnknownParam;
  2099. end;
  2100. end;
  2101. 'M': // syntax mode
  2102. begin
  2103. inc(p);
  2104. Identifier:=String(p);
  2105. if CompareText(Identifier,'delphi')=0 then Mode:=p2jmDelphi
  2106. else if CompareText(Identifier,'objfpc')=0 then Mode:=p2jmObjFPC
  2107. else ParamFatal('invalid syntax mode "'+Identifier+'"');
  2108. end;
  2109. 'N':
  2110. begin
  2111. inc(p);
  2112. case p^ of
  2113. 'S': if not FileCache.AddNamespaces(String(p+1),FromCmdLine,ErrorMsg) then
  2114. ParamFatal('invalid namespace "'+ErrorMsg+'"');
  2115. else UnknownParam;
  2116. end;
  2117. end;
  2118. 'o': // output file, main JavaScript file
  2119. begin
  2120. inc(p);
  2121. FileCache.MainJSFile:=String(p);
  2122. end;
  2123. 'O': // optimizations
  2124. begin
  2125. inc(p);
  2126. case p^ of
  2127. '-':
  2128. begin
  2129. inc(p);
  2130. Options:=Options-coO1Enable+coO1Disable;
  2131. end;
  2132. '1':
  2133. begin
  2134. inc(p);
  2135. Options:=Options+coO1Enable-coO1Disable;
  2136. end;
  2137. 'o':
  2138. begin
  2139. inc(p);
  2140. Identifier:=String(p);
  2141. if Identifier='' then UnknownParam;
  2142. inc(p,length(Identifier));
  2143. Enable:=true;
  2144. c:=Identifier[length(Identifier)];
  2145. if c in ['+','-'] then begin
  2146. Enable:=c='+';
  2147. Delete(Identifier,length(Identifier),1);
  2148. end;
  2149. if CompareText(Identifier,'EnumNumbers')=0 then
  2150. SetOption(coEnumValuesAsNumbers,Enable)
  2151. else if CompareText(Identifier,'RemoveNotUsedPrivates')=0 then
  2152. SetOption(coKeepNotUsedPrivates,not Enable)
  2153. else if CompareText(Identifier,'RemoveNotUsedDeclarations')=0 then
  2154. SetOption(coKeepNotUsedDeclarationsWPO,not Enable)
  2155. else
  2156. UnknownParam;
  2157. end;
  2158. else
  2159. UnknownParam;
  2160. end;
  2161. if p-PChar(Param)<length(Param) then
  2162. UnknownParam;
  2163. end;
  2164. 'P': // target processor
  2165. begin
  2166. inc(p);
  2167. Identifier:=String(p);
  2168. for aProc in TPasToJsProcessor do
  2169. if CompareText(Identifier,PasToJsProcessorNames[aProc])=0 then
  2170. begin
  2171. TargetProcessor:=aProc;
  2172. Identifier:='';
  2173. break;
  2174. end;
  2175. if Identifier<>'' then
  2176. ParamFatal('invalid target processor "'+Identifier+'"');
  2177. end;
  2178. 'S': // Syntax
  2179. begin
  2180. inc(p);
  2181. ReadSyntaxFlags(Param,p);
  2182. end;
  2183. 'T': // target platform
  2184. begin
  2185. inc(p);
  2186. Identifier:=String(p);
  2187. for aPlatform in TPasToJsPlatform do
  2188. if CompareText(Identifier,PasToJsPlatformNames[aPlatform])=0 then
  2189. begin
  2190. TargetPlatform:=aPlatform;
  2191. Identifier:='';
  2192. break;
  2193. end;
  2194. if Identifier<>'' then
  2195. ParamFatal('invalid target platform "'+Identifier+'"');
  2196. end;
  2197. 'u': // undefine
  2198. if not Quick then begin
  2199. Identifier:=copy(Param,3,length(Param));
  2200. if not IsValidIdent(Identifier) then
  2201. ParamFatal('-u: invalid undefine: "'+Param+'"');
  2202. RemoveDefine(Identifier);
  2203. end;
  2204. 'v': // verbose
  2205. begin
  2206. inc(p);
  2207. ReadVerbosityFlags(Param,p);
  2208. end;
  2209. else
  2210. UnknownParam;
  2211. end;
  2212. end;
  2213. '@':
  2214. if not Quick then begin
  2215. // load extra config file
  2216. aFilename:=copy(Param,2,length(Param));
  2217. if aFilename='' then
  2218. ParamFatal('invalid config file at param position '+IntToStr(i));
  2219. aFilename:=ExpandFileNameUTF8(aFilename);
  2220. if not FileExists(aFilename) then
  2221. ParamFatal('config file not found: "'+copy(Param,2,length(Param))+'"');
  2222. LoadConfig(aFilename);
  2223. end;
  2224. else
  2225. // filename
  2226. if (not Quick) then begin
  2227. if not FromCmdLine then
  2228. CfgSyntaxError('invalid parameter');
  2229. if FileCache.MainSrcFile<>'' then
  2230. ParamFatal('Two Pascal files. Only one Pascal file is supported.');
  2231. aFilename:=ExpandFileNameUTF8(Param);
  2232. if not FileExists(aFilename) then
  2233. ParamFatal('Pascal file not found: "'+Param+'"');
  2234. FileCache.MainSrcFile:=aFilename;
  2235. end;
  2236. end;
  2237. end;
  2238. procedure TPas2jsCompiler.ReadVerbosityFlags(Param: String; p: PChar);
  2239. var
  2240. Enabled, Disabled: string;
  2241. i: Integer;
  2242. begin
  2243. if p^='m' then begin
  2244. // read m-flags
  2245. repeat
  2246. inc(p);
  2247. if not (p^ in ['0'..'9']) then
  2248. ParamFatal('missing number in "'+Param+'"');
  2249. i:=0;
  2250. while p^ in ['0'..'9'] do begin
  2251. i:=i*10+ord(p^)-ord('0');
  2252. if i>99999 then
  2253. ParamFatal('Invalid -vm parameter in "'+Param+'"');
  2254. inc(p);
  2255. end;
  2256. Log.MsgNumberDisabled[i]:=p^<>'-';
  2257. if p^='-' then inc(p);
  2258. if p^=#0 then break;
  2259. if p^<>',' then
  2260. ParamFatal('Invalid option "'+Param+'"');
  2261. until false;
  2262. exit;
  2263. end;
  2264. // read other flags
  2265. ReadSingleLetterOptions(Param,p,'ewnhila0bctdqxz',Enabled,Disabled);
  2266. for i:=1 to length(Enabled) do begin
  2267. case Enabled[i] of
  2268. 'e': Options:=Options+[coShowErrors];
  2269. 'w': Options:=Options+[coShowWarnings];
  2270. 'n': Options:=Options+[coShowNotes];
  2271. 'h': Options:=Options+[coShowHints];
  2272. 'i': Options:=Options+[coShowInfos];
  2273. 'l': Options:=Options+[coShowLineNumbers];
  2274. 'a': Options:=Options+coShowAll;
  2275. '0': Options:=Options-coShowAll+[coShowErrors];
  2276. 'b': ShowFullPaths:=true;
  2277. 'c': Options:=Options+[coShowConditionals,coShowInfos];
  2278. 't': ShowTriedUsedFiles:=true;
  2279. 'd': ShowDebug:=true;
  2280. 'q': Options:=Options+[coShowMessageNumbers];
  2281. 'x': Options:=Options+[coShowUsedTools];
  2282. end;
  2283. end;
  2284. for i:=1 to length(Disabled) do begin
  2285. case Disabled[i] of
  2286. 'e': Options:=Options-[coShowErrors];
  2287. 'w': Options:=Options-[coShowWarnings];
  2288. 'n': Options:=Options-[coShowNotes];
  2289. 'h': Options:=Options-[coShowHints];
  2290. 'i': Options:=Options-[coShowInfos];
  2291. 'l': Options:=Options-[coShowLineNumbers];
  2292. 'a': ;
  2293. '0': ;
  2294. 'b': ShowFullPaths:=false;
  2295. 'c': Options:=Options-[coShowConditionals];
  2296. 't': ShowTriedUsedFiles:=false;
  2297. 'd': ShowDebug:=false;
  2298. 'q': Options:=Options-[coShowMessageNumbers];
  2299. 'x': Options:=Options-[coShowUsedTools];
  2300. end;
  2301. end;
  2302. end;
  2303. procedure TPas2jsCompiler.ReadSyntaxFlags(Param: String; p: PChar);
  2304. var
  2305. Enabled, Disabled: string;
  2306. i: Integer;
  2307. begin
  2308. ReadSingleLetterOptions(Param,p,'c',Enabled,Disabled);
  2309. for i:=1 to length(Enabled) do begin
  2310. case Enabled[i] of
  2311. '2': Mode:=p2jmObjFPC;
  2312. 'c': Options:=Options+[coAllowCAssignments];
  2313. 'd': Mode:=p2jmDelphi;
  2314. end;
  2315. end;
  2316. for i:=1 to length(Disabled) do begin
  2317. case Disabled[i] of
  2318. '2': ;
  2319. 'c': Options:=Options-[coAllowCAssignments];
  2320. 'd': ;
  2321. end;
  2322. end;
  2323. end;
  2324. procedure TPas2jsCompiler.ReadSingleLetterOptions(const Param: string; p: PChar;
  2325. const Allowed: string; out Enabled, Disabled: string);
  2326. // e.g. 'B' 'lB' 'l-' 'l+B-'
  2327. var
  2328. Letter: Char;
  2329. i: SizeInt;
  2330. begin
  2331. if p^=#0 then
  2332. ParamFatal('Invalid option "'+Param+'"');
  2333. Enabled:='';
  2334. Disabled:='';
  2335. repeat
  2336. Letter:=p^;
  2337. if Letter='-' then
  2338. ParamFatal('Invalid option "'+Param+'"');
  2339. if Pos(Letter,Allowed)<1 then
  2340. ParamFatal('unknown option "'+Param+'". Use -h for help.');
  2341. inc(p);
  2342. if p^='-' then begin
  2343. // disable
  2344. if Pos(Letter,Disabled)<1 then Disabled+=Letter;
  2345. i:=Pos(Letter,Enabled);
  2346. if i>0 then Delete(Enabled,i,1);
  2347. inc(p);
  2348. end else begin
  2349. // enable
  2350. if Pos(Letter,Enabled)<1 then Enabled+=Letter;
  2351. i:=Pos(Letter,Disabled);
  2352. if i>0 then Delete(Disabled,i,1);
  2353. if p^='+' then inc(p);
  2354. end;
  2355. until p^=#0;
  2356. end;
  2357. procedure TPas2jsCompiler.RegisterMessages;
  2358. var
  2359. LastMsgNumber: integer;
  2360. procedure r(MsgType: TMessageType; MsgNumber: integer; const MsgPattern: string);
  2361. var
  2362. s: String;
  2363. begin
  2364. if (LastMsgNumber>=0) and (MsgNumber<>LastMsgNumber+1) then
  2365. begin
  2366. s:='TPas2jsCompiler.RegisterMessages: gap in registered message numbers: '+IntToStr(LastMsgNumber)+' '+IntToStr(MsgNumber);
  2367. RaiseInternalError(20170504161422,s);
  2368. end;
  2369. Log.RegisterMsg(MsgType,MsgNumber,MsgPattern);
  2370. LastMsgNumber:=MsgNumber;
  2371. end;
  2372. begin
  2373. LastMsgNumber:=-1;
  2374. r(mtInfo,nOptionIsEnabled,sOptionIsEnabled);
  2375. r(mtInfo,nSyntaxModeIs,sSyntaxModeIs);
  2376. r(mtInfo,nMacroDefined,sMacroDefined);
  2377. r(mtInfo,nUsingPath,sUsingPath);
  2378. r(mtNote,nFolderNotFound,sFolderNotFound);
  2379. r(mtInfo,nNameValue,sNameValue);
  2380. r(mtInfo,nReadingOptionsFromFile,sReadingOptionsFromFile);
  2381. r(mtInfo,nEndOfReadingConfigFile,sEndOfReadingConfigFile);
  2382. r(mtDebug,nInterpretingFileOption,sInterpretingFileOption);
  2383. r(mtFatal,nSourceFileNotFound,sSourceFileNotFound);
  2384. r(mtFatal,nFileIsFolder,sFileIsFolder);
  2385. r(mtInfo,nConfigFileSearch,sConfigFileSearch);
  2386. r(mtDebug,nHandlingOption,sHandlingOption);
  2387. r(mtDebug,nQuickHandlingOption,sQuickHandlingOption);
  2388. r(mtFatal,nOutputDirectoryNotFound,sOutputDirectoryNotFound);
  2389. r(mtInfo,nUnableToWriteFile,sUnableToWriteFile);
  2390. r(mtInfo,nWritingFile,sWritingFile);
  2391. r(mtFatal,nCompilationAborted,sCompilationAborted);
  2392. r(mtDebug,nCfgDirective,sCfgDirective);
  2393. r(mtError,nUnitCycle,sUnitCycle);
  2394. r(mtError,nOptionForbidsCompile,sOptionForbidsCompile);
  2395. r(mtInfo,nUnitNeedsCompileDueToUsedUnit,sUnitsNeedCompileDueToUsedUnit);
  2396. r(mtInfo,nUnitNeedsCompileDueToOption,sUnitsNeedCompileDueToOption);
  2397. r(mtInfo,nUnitNeedsCompileJSMissing,sUnitsNeedCompileJSMissing);
  2398. r(mtInfo,nUnitNeedsCompilePasHasChanged,sUnitsNeedCompilePasHasChanged);
  2399. r(mtInfo,nParsingFile,sParsingFile);
  2400. r(mtInfo,nCompilingFile,sCompilingFile);
  2401. r(mtError,nExpectedButFound,sExpectedButFound);
  2402. r(mtInfo,nLinesInFilesCompiled,sLinesInFilesCompiled);
  2403. r(mtInfo,nTargetPlatformIs,sTargetPlatformIs);
  2404. r(mtInfo,nTargetProcessorIs,sTargetProcessorIs);
  2405. r(mtInfo,nMessageEncodingIs,sMessageEncodingIs);
  2406. r(mtError,nUnableToTranslatePathToDir,sUnableToTranslatePathToDir);
  2407. r(mtInfo,nSrcMapSourceRootIs,sSrcMapSourceRootIs);
  2408. r(mtInfo,nSrcMapBaseDirIs,sSrcMapBaseDirIs);
  2409. Pas2jsPParser.RegisterMessages(Log);
  2410. end;
  2411. procedure TPas2jsCompiler.SetCompilerExe(AValue: string);
  2412. begin
  2413. if AValue<>'' then
  2414. AValue:=ExpandFileNameUTF8(AValue);
  2415. if FCompilerExe=AValue then Exit;
  2416. FCompilerExe:=AValue;
  2417. end;
  2418. procedure TPas2jsCompiler.SetMode(AValue: TP2jsMode);
  2419. begin
  2420. if FMode=AValue then Exit;
  2421. FMode:=AValue;
  2422. case FMode of
  2423. p2jmObjFPC: Options:=Options-[coAllowCAssignments];
  2424. p2jmDelphi: Options:=Options-[coAllowCAssignments];
  2425. end;
  2426. end;
  2427. procedure TPas2jsCompiler.SetOptions(AValue: TP2jsCompilerOptions);
  2428. begin
  2429. if FOptions=AValue then Exit;
  2430. FOptions:=AValue;
  2431. Log.ShowMsgNumbers:=coShowMessageNumbers in FOptions;
  2432. Log.ShowMsgTypes:=GetShownMsgTypes;
  2433. end;
  2434. procedure TPas2jsCompiler.SetShowDebug(AValue: boolean);
  2435. begin
  2436. if AValue then
  2437. FOptions:=FOptions+[coShowNotes,coShowInfos,coShowDebug]
  2438. else
  2439. Exclude(FOptions,coShowNotes);
  2440. end;
  2441. procedure TPas2jsCompiler.SetShowFullPaths(AValue: boolean);
  2442. begin
  2443. FileCache.ShowFullPaths:=AValue;
  2444. end;
  2445. procedure TPas2jsCompiler.SetShowLogo(AValue: Boolean);
  2446. begin
  2447. SetOption(coShowLogo,AValue);
  2448. end;
  2449. procedure TPas2jsCompiler.SetShowTriedUsedFiles(AValue: boolean);
  2450. begin
  2451. FileCache.ShowTriedUsedFiles:=AValue;
  2452. end;
  2453. procedure TPas2jsCompiler.SetShowUsedTools(AValue: boolean);
  2454. begin
  2455. SetOption(coShowUsedTools,AValue);
  2456. end;
  2457. procedure TPas2jsCompiler.SetSkipDefaultConfig(AValue: Boolean);
  2458. begin
  2459. SetOption(coSkipDefaultConfigs,AValue);
  2460. end;
  2461. procedure TPas2jsCompiler.SetSrcMapBaseDir(const AValue: string);
  2462. begin
  2463. FileCache.SrcMapBaseDir:=AValue;
  2464. end;
  2465. procedure TPas2jsCompiler.SetSrcMapEnable(const AValue: boolean);
  2466. begin
  2467. SetOption(coSourceMapCreate,AValue);
  2468. end;
  2469. procedure TPas2jsCompiler.SetSrcMapInclude(const AValue: boolean);
  2470. begin
  2471. SetOption(coSourceMapInclude,AValue);
  2472. end;
  2473. procedure TPas2jsCompiler.SetTargetPlatform(const AValue: TPasToJsPlatform);
  2474. begin
  2475. if FTargetPlatform=AValue then Exit;
  2476. RemoveDefine(PasToJsPlatformNames[TargetPlatform]);
  2477. FTargetPlatform:=AValue;
  2478. if FTargetPlatform=PlatformNodeJS then
  2479. FileCache.AllJSIntoMainJS:=true;
  2480. AddDefinesForTargetPlatform;
  2481. end;
  2482. procedure TPas2jsCompiler.SetTargetProcessor(const AValue: TPasToJsProcessor);
  2483. begin
  2484. if FTargetProcessor=AValue then Exit;
  2485. RemoveDefine(PasToJsProcessorNames[TargetProcessor]);
  2486. FTargetProcessor:=AValue;
  2487. AddDefinesForTargetProcessor;
  2488. end;
  2489. constructor TPas2jsCompiler.Create;
  2490. begin
  2491. FOptions:=DefaultP2jsCompilerOptions;
  2492. FLog:=TPas2jsLogger.Create;
  2493. FParamMacros:=TPas2jsMacroEngine.Create;
  2494. RegisterMessages;
  2495. FFileCache:=TPas2jsFilesCache.Create(Log);
  2496. FFileCacheAutoFree:=true;
  2497. FLog.OnFormatPath:[email protected];
  2498. FDefines:=TStringList.Create;
  2499. // Done by Reset: TStringList(FDefines).Sorted:=True;
  2500. // Done by Reset: TStringList(FDefines).Duplicates:=dupError;
  2501. FConditionEval:=TCondDirectiveEvaluator.Create;
  2502. FConditionEval.OnLog:=@ConditionEvalLog;
  2503. FConditionEval.OnEvalVariable:=@ConditionEvalVariable;
  2504. //FConditionEval.OnEvalFunction:=@ConditionEvalFunction;
  2505. FFiles:=TAVLTree.Create(@CompareCompilerFilesPasFile);
  2506. FUnits:=TAVLTree.Create(@CompareCompilerFilesPasUnitname);
  2507. InitParamMacros;
  2508. Reset;
  2509. end;
  2510. destructor TPas2jsCompiler.Destroy;
  2511. begin
  2512. FreeAndNil(FWPOAnalyzer);
  2513. FMainFile:=nil;
  2514. FreeAndNil(FUnits);
  2515. FFiles.FreeAndClear;
  2516. FreeAndNil(FFiles);
  2517. ClearDefines;
  2518. FreeAndNil(FDefines);
  2519. FreeAndNil(FConditionEval);
  2520. FLog.OnFormatPath:=nil;
  2521. if FFileCacheAutoFree then
  2522. FreeAndNil(FFileCache)
  2523. else
  2524. FFileCache:=nil;
  2525. FreeAndNil(FParamMacros);
  2526. FreeAndNil(FLog);
  2527. inherited Destroy;
  2528. end;
  2529. function TPas2jsCompiler.OnMacroCfgDir(Sender: TObject; var Params: string;
  2530. Lvl: integer): boolean;
  2531. begin
  2532. if Lvl=0 then ;
  2533. Params:=ExtractFilePath(CurrentCfgFilename);
  2534. Result:=true;
  2535. end;
  2536. function TPas2jsCompiler.OnMacroEnv(Sender: TObject; var Params: string;
  2537. Lvl: integer): boolean;
  2538. begin
  2539. if Lvl=0 then ;
  2540. Params:=GetEnvironmentVariableUTF8(Params);
  2541. Result:=true;
  2542. end;
  2543. procedure TPas2jsCompiler.AddDefine(const aName: String);
  2544. begin
  2545. if FDefines.IndexOf(aName)>=0 then exit;
  2546. FDefines.Add(aName);
  2547. end;
  2548. procedure TPas2jsCompiler.AddDefine(const aName, Value: String);
  2549. var
  2550. Index: Integer;
  2551. M: TMacroDef;
  2552. begin
  2553. Index:=FDefines.IndexOf(aName);
  2554. If (Index<0) then
  2555. FDefines.AddObject(aName,TMacroDef.Create(aName,Value))
  2556. else begin
  2557. M:=TMacroDef(FDefines.Objects[Index]);
  2558. if M=nil then
  2559. FDefines.Objects[Index]:=TMacroDef.Create(aName,Value)
  2560. else
  2561. M.Value:=Value;
  2562. end;
  2563. end;
  2564. procedure TPas2jsCompiler.RemoveDefine(const aName: String);
  2565. var
  2566. i: Integer;
  2567. M: TMacroDef;
  2568. begin
  2569. i:=FDefines.IndexOf(aName);
  2570. if (i<>-1) then begin
  2571. M:=TMacroDef(FDefines.Objects[i]);
  2572. M.Free;
  2573. FDefines.Delete(i);
  2574. end;
  2575. end;
  2576. function TPas2jsCompiler.IsDefined(const aName: String): boolean;
  2577. begin
  2578. Result:=FDefines.IndexOf(aName)>=0;
  2579. end;
  2580. class function TPas2jsCompiler.GetVersion(ShortVersion: boolean): string;
  2581. begin
  2582. Result:=IntToStr(VersionMajor)+'.'+IntToStr(VersionMinor)+'.'+IntToStr(VersionRelease);
  2583. if not ShortVersion then
  2584. Result+=VersionExtra;
  2585. end;
  2586. procedure TPas2jsCompiler.Reset;
  2587. begin
  2588. FreeAndNil(FWPOAnalyzer);
  2589. FMainFile:=nil;
  2590. FUnits.Clear;
  2591. FFiles.FreeAndClear;
  2592. FCompilerExe:='';
  2593. FOptions:=DefaultP2jsCompilerOptions;
  2594. FMode:=p2jmObjFPC;
  2595. FTargetPlatform:=PlatformBrowser;
  2596. FTargetProcessor:=ProcessorECMAScript5;
  2597. Log.Reset;
  2598. Log.ShowMsgTypes:=GetShownMsgTypes;
  2599. ClearDefines;
  2600. TStringList(FDefines).Sorted:=True;
  2601. TStringList(FDefines).Duplicates:=dupError;
  2602. AddDefine('PAS2JS');
  2603. AddDefine('PAS2JS_FULLVERSION',IntToStr((VersionMajor*100+VersionMinor)*100+VersionRelease));
  2604. AddDefinesForTargetPlatform;
  2605. AddDefinesForTargetProcessor;
  2606. // add FPC compatibility flags
  2607. AddDefine('FPC_HAS_FEATURE_CLASSES');
  2608. AddDefine('FPC_HAS_FEATURE_DYNARRAYS');
  2609. AddDefine('FPC_HAS_FEATURE_EXCEPTIONS');
  2610. AddDefine('FPC_HAS_FEATURE_EXITCODE');
  2611. AddDefine('FPC_HAS_FEATURE_INITFINAL');
  2612. AddDefine('FPC_HAS_FEATURE_RTTI');
  2613. AddDefine('FPC_HAS_FEATURE_SUPPORT');
  2614. AddDefine('FPC_HAS_FEATURE_UNICODESTRINGS');
  2615. AddDefine('FPC_HAS_FEATURE_WIDESTRINGS');
  2616. AddDefine('FPC_HAS_TYPE_DOUBLE');
  2617. AddDefine('FPC_HAS_UNICODESTRING');
  2618. AddDefine('FPC_UNICODESTRINGS');
  2619. AddDefine('FPC_WIDESTRING_EQUAL_UNICODESTRING');
  2620. AddDefine('STR_CONCAT_PROCS');
  2621. AddDefine('UNICODE');
  2622. FHasShownLogo:=false;
  2623. FFileCache.Reset;
  2624. end;
  2625. procedure TPas2jsCompiler.Run(aCompilerExe: string; aWorkingDir: string;
  2626. ParamList: TStrings; DoReset: boolean);
  2627. var
  2628. i: Integer;
  2629. StartTime: TDateTime;
  2630. begin
  2631. StartTime:=Now;
  2632. if DoReset then Reset;
  2633. if FileCount>0 then
  2634. RaiseInternalError(20170504161340,'internal error: TPas2jsCompiler.Run FileCount>0');
  2635. CompilerExe:=aCompilerExe;
  2636. FileCache.BaseDirectory:=aWorkingDir;
  2637. // quick check command line params
  2638. for i:=0 to ParamList.Count-1 do
  2639. ReadParam(ParamList[i],true,true);
  2640. if ShowLogo then
  2641. WriteLogo;
  2642. // read default config
  2643. if not SkipDefaultConfig then
  2644. LoadDefaultConfig;
  2645. // read command line parameters
  2646. for i:=0 to ParamList.Count-1 do
  2647. ReadParam(ParamList[i],false,true);
  2648. // now we know, if the logo can be displayed
  2649. if ShowLogo then
  2650. WriteLogo;
  2651. // show debug info
  2652. if ShowDebug then begin
  2653. WriteOptions;
  2654. WriteDefines;
  2655. end;
  2656. if ShowDebug or ShowTriedUsedFiles then
  2657. WriteFoldersAndSearchPaths;
  2658. if FileCache.MainSrcFile='' then
  2659. ParamFatal('No source file name in command line');
  2660. // compile
  2661. try
  2662. Compile(StartTime);
  2663. except
  2664. on E: ECompilerTerminate do ;
  2665. end;
  2666. end;
  2667. procedure TPas2jsCompiler.WriteHelp;
  2668. const
  2669. MaxLineLen = 78;
  2670. Indent = 12;
  2671. procedure l(s: string);
  2672. var
  2673. p, LastCharStart, WordBreak: PChar;
  2674. Len: integer;
  2675. CodePointCount: Integer;
  2676. procedure InitLine;
  2677. begin
  2678. p:=PChar(s);
  2679. LastCharStart:=p;
  2680. WordBreak:=nil;
  2681. CodePointCount:=0;
  2682. end;
  2683. begin
  2684. if length(s)<=MaxLineLen then begin
  2685. Log.LogRaw(s);
  2686. exit;
  2687. end;
  2688. InitLine;
  2689. repeat
  2690. case p^ of
  2691. #0:
  2692. if p-PChar(s)=length(s) then
  2693. break
  2694. else
  2695. inc(p);
  2696. 'a'..'z','A'..'Z','0'..'9','_','-','.',',','"','''','`',#128..#255:
  2697. begin
  2698. LastCharStart:=p;
  2699. Len:=UTF8CharacterStrictLength(p);
  2700. if Len=0 then Len:=1;
  2701. inc(p,Len);
  2702. end;
  2703. else
  2704. LastCharStart:=p;
  2705. WordBreak:=p;
  2706. inc(p);
  2707. end;
  2708. inc(CodePointCount);
  2709. if CodePointCount>=MaxLineLen then begin
  2710. if (WordBreak=nil) or (WordBreak-PChar(s)<MaxLineLen div 3) then
  2711. WordBreak:=LastCharStart;
  2712. Len:=WordBreak-PChar(s);
  2713. Log.LogRaw(LeftStr(s,Len));
  2714. Delete(s,1,len);
  2715. s:=Space(Indent)+Trim(s);
  2716. InitLine;
  2717. end;
  2718. until false;
  2719. Log.LogRaw(s);
  2720. end;
  2721. var
  2722. i: Integer;
  2723. ParamMacro: TPas2jsMacro;
  2724. begin
  2725. WriteLogo;
  2726. Log.LogLn;
  2727. if CompilerExe<>'' then begin
  2728. l('Usage: '+CompilerExe+' <your.pas>');
  2729. end else begin
  2730. l('Usage: pas2js <your.pas>');
  2731. end;
  2732. Log.LogLn;
  2733. l('Options:');
  2734. l('Put + after a boolean switch option to enable it, - to disable it');
  2735. l(' @<x> : Read compiler options from file <x> in addition to the default '+DefaultConfigFile);
  2736. l(' -B : Rebuild all');
  2737. l(' -d<x> : Defines the symbol <x>. Optional: -d<x>:=<value>');
  2738. l(' -i<x> : Write information and halt. <x> is a combination of the following:');
  2739. l(' D : Write compiler date');
  2740. l(' SO : Write compiler OS');
  2741. l(' SP : Write compiler host processor');
  2742. l(' TO : Write target platform');
  2743. l(' TP : Write target processor');
  2744. l(' V : Write short compiler version');
  2745. l(' W : Write full compiler version');
  2746. l(' -F... Set file names and paths:');
  2747. l(' -Fe<x> : Redirect output to <x>. UTF-8 encoded.');
  2748. l(' -Fi<x> : Add <x> to include paths');
  2749. l(' -Fu<x> : Add <x> to unit paths');
  2750. l(' -FU<x> : Set unit output path to <x>');
  2751. l(' -I<x> : Add <x> to include paths, same as -Fi');
  2752. l(' -J... Extra options of pas2js');
  2753. l(' -Jc : Write all JavaScript concatenated into the output file');
  2754. l(' -Je<x> : Encode messages as <x>.');
  2755. l(' -Jeconsole : Console codepage. This is the default.');
  2756. l(' -Jesystem : System codepage. On non Windows console and system are the same.');
  2757. l(' -Jeutf-8 : Unicode UTF-8. Default when using -Fe.');
  2758. l(' -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-.');
  2759. l(' -Jl : lower case identifiers');
  2760. l(' -Jm : generate source maps');
  2761. l(' -Jmsourceroot=<x> : use x as "sourceRoot", prefix URL for source file names.');
  2762. l(' -Jmbasedir=<x> : write source file names relative to directory x.');
  2763. l(' -Jminclude : include Pascal sources in source map.');
  2764. l(' -Jm- : disable generating source maps');
  2765. l(' -Ju<x> : Add <x> to foreign unit paths. Foreign units are not compiled.');
  2766. //l(' -Jg<x> : Add <x> to group paths. A "-" starts a new group.');
  2767. //l(' -JU<x> : Set unit output path of current group to <y>');
  2768. l(' -l : Write logo');
  2769. l(' -MDelphi: Delphi 7 compatibility mode');
  2770. l(' -MObjFPC: FPC''s Object Pascal compatibility mode (default)');
  2771. l(' -NS<x> : add <x> to namespaces. Namespaces with trailing - are removed.');
  2772. l(' Delphi calls this flag "unit scope names".');
  2773. l(' -n : Do not read the default config files');
  2774. l(' -o<x> : Change main JavaScript file to <x>, "." means stdout');
  2775. l(' -O<x> : Optimizations:');
  2776. l(' -O- : Disable optimizations');
  2777. l(' -O1 : Level 1 optimizations (quick and debugger friendly)');
  2778. //l(' -O2 : Level 2 optimizations (Level 1 + not debugger friendly)');
  2779. l(' -Oo<x> : Enable or disable optimization. The x is case insensitive:');
  2780. l(' -OoEnumNumbers[-] : write enum value as number instead of name. Default in -O1.');
  2781. l(' -OoRemoveNotUsedPrivates[-] : Default is enabled');
  2782. l(' -OoRemoveNotUsedDeclarations[-] : Default enabled for programs with -Jc');
  2783. l(' -P<x> : Set target processor. Case insensitive:');
  2784. l(' -Pecmascript5 : default');
  2785. l(' -Pecmascript6');
  2786. l(' -S<x> : Syntax options. <x> is a combination of the following letters:');
  2787. l(' c : Support operators like C (*=,+=,/= and -=)');
  2788. l(' d : Same as -Mdelphi');
  2789. l(' 2 : Same as -Mobjfpc (default)');
  2790. l(' -T<x> : Set target platform');
  2791. l(' -Tbrowser : default');
  2792. l(' -Tnodejs : add pas.run(), includes -Jc');
  2793. l(' -u<x> : Undefines the symbol <x>');
  2794. l(' -v<x> : Be verbose. <x> is a combination of the following letters:');
  2795. l(' e : show errors (default)');
  2796. l(' w : show warnings');
  2797. l(' n : show notes');
  2798. l(' h : show hints');
  2799. l(' i : show info');
  2800. l(' l : show line numbers');
  2801. l(' a : show everything');
  2802. l(' 0 : show nothing (except errors)');
  2803. l(' b : show file names with full path');
  2804. l(' c : show conditionals');
  2805. l(' t : show tried/used files');
  2806. l(' d : show debug notes and info, enables -vni');
  2807. l(' q : show message numbers');
  2808. l(' x : show used tools');
  2809. l(' -vm<x>,<y>: Do not show messages numbered <x> and <y>.');
  2810. l(' -? : Show this help');
  2811. l(' -h : Show this help');
  2812. Log.LogLn;
  2813. l('Macros: $Name, $Name$ or $Name()');
  2814. for i:=0 to ParamMacros.Count-1 do begin
  2815. ParamMacro:=ParamMacros[i];
  2816. Log.LogRaw([' $',ParamMacro.Name,BoolToStr(ParamMacro.CanHaveParams,'()',''),': ',ParamMacro.Description]);
  2817. end;
  2818. end;
  2819. procedure TPas2jsCompiler.WriteLogo;
  2820. begin
  2821. if FHasShownLogo then exit;
  2822. FHasShownLogo:=true;
  2823. WriteVersionLine;
  2824. Log.LogRaw('Copyright (c) 2017 Mattias Gaertner and others');
  2825. end;
  2826. procedure TPas2jsCompiler.WriteVersionLine;
  2827. begin
  2828. Log.LogRaw('Pas2JS Compiler version '+GetVersion(false));
  2829. end;
  2830. procedure TPas2jsCompiler.WriteOptions;
  2831. var
  2832. co: TP2jsCompilerOption;
  2833. fco: TP2jsFileCacheOption;
  2834. begin
  2835. // boolean options
  2836. for co in TP2jsCompilerOption do
  2837. Log.LogMsgIgnoreFilter(nOptionIsEnabled,
  2838. [p2jscoCaption[co],BoolToStr(co in Options,'enabled','disabled')]);
  2839. for fco in TP2jsFileCacheOption do
  2840. Log.LogMsgIgnoreFilter(nOptionIsEnabled,
  2841. [p2jsfcoCaption[fco],BoolToStr(fco in FileCache.Options,'enabled','disabled')]);
  2842. // default syntax mode
  2843. Log.LogMsgIgnoreFilter(nSyntaxModeIs,[p2jscModeNames[Mode]]);
  2844. // target platform
  2845. Log.LogMsgIgnoreFilter(nTargetPlatformIs,[PasToJsPlatformNames[TargetPlatform]]);
  2846. Log.LogMsgIgnoreFilter(nTargetProcessorIs,[PasToJsProcessorNames[TargetProcessor]]);
  2847. // message encoding
  2848. Log.LogMsgIgnoreFilter(nMessageEncodingIs,[IntToStr(Log.MsgCount)]);
  2849. // source map options
  2850. if SrcMapEnable then begin
  2851. Log.LogMsgIgnoreFilter(nSrcMapSourceRootIs,[SrcMapSourceRoot]);
  2852. Log.LogMsgIgnoreFilter(nSrcMapBaseDirIs,[SrcMapBaseDir]);
  2853. end;
  2854. end;
  2855. procedure TPas2jsCompiler.WriteDefines;
  2856. var
  2857. i: Integer;
  2858. S: String;
  2859. M: TMacroDef;
  2860. begin
  2861. for i:=0 to Defines.Count-1 do
  2862. begin
  2863. S:=Defines[i];
  2864. M:=TMacroDef(Defines.Objects[i]);
  2865. if M<>nil then
  2866. S:=S+'='+M.Value;
  2867. Log.LogMsgIgnoreFilter(nMacroDefined,[S]);
  2868. end;
  2869. end;
  2870. procedure TPas2jsCompiler.WriteFoldersAndSearchPaths;
  2871. procedure WriteFolder(aName, Folder: string);
  2872. begin
  2873. if Folder='' then exit;
  2874. Log.LogMsgIgnoreFilter(nUsingPath,[aName,Folder]);
  2875. if not DirectoryExists(ChompPathDelim(Folder)) then
  2876. Log.LogMsgIgnoreFilter(nFolderNotFound,[aName,Folder]);
  2877. end;
  2878. var
  2879. i: Integer;
  2880. begin
  2881. for i:=0 to FileCache.ForeignUnitPaths.Count-1 do
  2882. WriteFolder('foreign unit path',FileCache.ForeignUnitPaths[i]);
  2883. for i:=0 to FileCache.UnitPaths.Count-1 do
  2884. WriteFolder('unit path',FileCache.UnitPaths[i]);
  2885. for i:=0 to FileCache.IncludePaths.Count-1 do
  2886. WriteFolder('include path',FileCache.IncludePaths[i]);
  2887. WriteFolder('unit output path',FileCache.UnitOutputPath);
  2888. Log.LogMsgIgnoreFilter(nNameValue,['output file',FileCache.MainJSFile]);
  2889. end;
  2890. procedure TPas2jsCompiler.WriteInfo;
  2891. begin
  2892. WriteVersionLine;
  2893. Log.LogLn;
  2894. Log.LogRaw('Compiler date : '+GetCompiledDate);
  2895. Log.LogRaw('Compiler CPU target: '+GetCompiledTargetCPU);
  2896. Log.LogLn;
  2897. Log.LogRaw('Supported targets (targets marked with ''{*}'' are under development):');
  2898. Log.LogRaw([' ',PasToJsPlatformNames[PlatformBrowser],': webbrowser']);
  2899. Log.LogRaw([' ',PasToJsPlatformNames[PlatformNodeJS],': Node.js']);
  2900. Log.LogLn;
  2901. Log.LogRaw('Supported CPU instruction sets:');
  2902. Log.LogRaw(' ECMAScript5, ECMAScript6');
  2903. Log.LogLn;
  2904. Log.LogRaw('Recognized compiler and RTL features:');
  2905. Log.LogRaw(' RTTI,CLASSES,EXCEPTIONS,EXITCODE,RANDOM,DYNARRAYS,COMMANDARGS,');
  2906. Log.LogRaw(' UNICODESTRINGS');
  2907. Log.LogLn;
  2908. Log.LogRaw('Supported Optimizations:');
  2909. Log.LogRaw(' EnumNumbers');
  2910. Log.LogRaw(' RemoveNotUsedPrivates');
  2911. Log.LogLn;
  2912. Log.LogRaw('Supported Whole Program Optimizations:');
  2913. Log.LogRaw(' RemoveNotUsedDeclarations');
  2914. Log.LogLn;
  2915. Log.LogRaw('This program comes under the Library GNU General Public License');
  2916. Log.LogRaw('For more information read COPYING.FPC, included in this distribution');
  2917. Log.LogLn;
  2918. Log.LogRaw('Please report bugs in our bug tracker on:');
  2919. Log.LogRaw(' http://bugs.freepascal.org');
  2920. Log.LogLn;
  2921. Log.LogRaw('More information may be found on our WWW pages (including directions');
  2922. Log.LogRaw('for mailing lists useful for asking questions or discussing potential');
  2923. Log.LogRaw('new features, etc.):');
  2924. Log.LogRaw(' http://www.freepascal.org');
  2925. end;
  2926. function TPas2jsCompiler.GetShownMsgTypes: TMessageTypes;
  2927. begin
  2928. Result:=[mtFatal];
  2929. if coShowErrors in FOptions then Include(Result,mtError);
  2930. if coShowWarnings in FOptions then Include(Result,mtWarning);
  2931. if coShowNotes in FOptions then Include(Result,mtNote);
  2932. if coShowHints in FOptions then Include(Result,mtHint);
  2933. if coShowInfos in FOptions then Include(Result,mtInfo);
  2934. if coShowDebug in FOptions then Include(Result,mtDebug);
  2935. end;
  2936. procedure TPas2jsCompiler.SetOption(Flag: TP2jsCompilerOption; Enable: boolean);
  2937. begin
  2938. if Enable then
  2939. Options:=Options+[Flag]
  2940. else
  2941. Options:=Options-[Flag];
  2942. end;
  2943. function TPas2jsCompiler.FindPasFile(PasFilename: string): TPas2jsCompilerFile;
  2944. var
  2945. Node: TAVLTreeNode;
  2946. begin
  2947. Result:=nil;
  2948. if PasFilename='' then exit;
  2949. Node:=FFiles.FindKey(Pointer(PasFilename),@CompareFileAndCompilerFilePasFile);
  2950. if Node=nil then exit;
  2951. Result:=TPas2jsCompilerFile(Node.Data);
  2952. end;
  2953. procedure TPas2jsCompiler.LoadPasFile(PasFilename, UseUnitName: string; out
  2954. aFile: TPas2jsCompilerFile);
  2955. var
  2956. aPasTree: TPas2jsCompilerResolver;
  2957. begin
  2958. aFile:=nil;
  2959. Log.LogMsg(nParsingFile,[FileCache.FormatPath(PasFilename)],'',0,0,not (coShowLineNumbers in Options));
  2960. aFile:=FindPasFile(PasFilename);
  2961. if aFile<>nil then exit;
  2962. if (PasFilename='') or not FileExists(PasFilename) then begin
  2963. Log.LogMsg(nSourceFileNotFound,[PasFilename]);
  2964. Terminate(ExitCodeFileNotFound);
  2965. end;
  2966. PasFilename:=ExpandFileNameUTF8(PasFilename);
  2967. if DirectoryExists(PasFilename) then begin
  2968. Log.LogMsg(nFileIsFolder,[PasFilename]);
  2969. Terminate(ExitCodeFileNotFound);
  2970. end;
  2971. aFile:=TPas2jsCompilerFile.Create(Self,PasFilename);
  2972. if UseUnitName<>'' then
  2973. begin
  2974. {$IFDEF VerboseSetPasUnitName}
  2975. writeln('TPas2jsCompiler.LoadPasFile File="',PasFilename,'" UseUnit="',UseUnitName,'"');
  2976. {$ENDIF}
  2977. aFile.PasUnitName:=UseUnitName;
  2978. end;
  2979. FFiles.Add(aFile);
  2980. aFile.ShowDebug:=ShowDebug;
  2981. if aFile.IsMainFile then
  2982. aFile.JSFilename:=FileCache.GetResolvedMainJSFile;
  2983. // pastree (engine)
  2984. aPasTree:=aFile.PascalResolver;
  2985. if coShowLineNumbers in Options then
  2986. aPasTree.ScannerLogEvents:=aPasTree.ScannerLogEvents+[sleLineNumber];
  2987. if coShowConditionals in Options then
  2988. aPasTree.ScannerLogEvents:=aPasTree.ScannerLogEvents+[sleConditionals];
  2989. if [coShowLineNumbers,coShowInfos,coShowDebug]*Options<>[] then
  2990. aPasTree.ParserLogEvents:=aPasTree.ParserLogEvents+[pleInterface,pleImplementation];
  2991. // scanner
  2992. aFile.CreateScannerAndParser(FileCache.CreateResolver);
  2993. if ShowDebug then
  2994. Log.LogRaw(['Debug: Opening file "',PasFilename,'"...']);
  2995. // open file (beware: this changes aPasTree.FileResolver.BaseDirectory)
  2996. aFile.OpenFile(PasFilename);
  2997. end;
  2998. function TPas2jsCompiler.FindUsedUnit(const TheUnitName: string
  2999. ): TPas2jsCompilerFile;
  3000. var
  3001. Node: TAVLTreeNode;
  3002. begin
  3003. if not IsValidIdent(TheUnitName,true) then exit(nil);
  3004. Node:=FUnits.FindKey(Pointer(TheUnitName),@CompareUnitnameAndCompilerFile);
  3005. if Node=nil then
  3006. Result:=nil
  3007. else
  3008. Result:=TPas2jsCompilerFile(Node.Data);
  3009. end;
  3010. procedure TPas2jsCompiler.AddUsedUnit(aFile: TPas2jsCompilerFile);
  3011. var
  3012. OldFile: TPas2jsCompilerFile;
  3013. begin
  3014. if aFile.PasUnitName='' then
  3015. RaiseInternalError(20170504161347,'missing PasUnitName "'+aFile.PasFilename+'"');
  3016. OldFile:=FindUsedUnit(aFile.PasUnitName);
  3017. if OldFile<>nil then begin
  3018. if OldFile<>aFile then
  3019. RaiseInternalError(20170504161354,'duplicate unit "'+OldFile.PasUnitName+'" "'+aFile.PasFilename+'" "'+OldFile.PasFilename+'"');
  3020. end else begin
  3021. FUnits.Add(aFile);
  3022. end;
  3023. end;
  3024. end.