pas2jscompiler.pp 104 KB

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