pas2jscompiler.pp 104 KB

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