pas2jspcucompiler.pp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. {
  2. This file is part of the Free Component Library (FCL)
  3. Copyright (c) 2018 Michael Van Canneyt
  4. Pascal to Javascript converter class.
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************
  11. Abstract:
  12. FileSystem aware compiler descendent with support for PCU files.
  13. }
  14. unit pas2jspcucompiler;
  15. {$mode objfpc}{$H+}
  16. {$I pas2js_defines.inc}
  17. {$IF defined(VerboseUnitQueue) or defined(VerbosePCUFiler)}
  18. {$DEFINE ReallyVerbose}
  19. {$ENDIF}
  20. interface
  21. uses
  22. SysUtils,Classes,
  23. pastree,
  24. pas2jscompiler, pas2jsfs, pas2jsfscompiler, Pas2JsFiler;
  25. Type
  26. TFilerPCUSupport = Class(TPCUSupport)
  27. Private
  28. // This is the format that will be written.
  29. FPCUFormat : TPas2JSPrecompileFormat;
  30. // This is the format that will be read.
  31. FFoundFormat : TPas2JSPrecompileFormat;
  32. FPrecompileInitialFlags: TPCUInitialFlags;
  33. FPCUReader: TPCUCustomReader;
  34. FPCUReaderStream: TStream;
  35. function OnPCUConverterIsElementUsed(Sender: TObject; El: TPasElement): boolean;
  36. function OnPCUConverterIsTypeInfoUsed(Sender: TObject; El: TPasElement): boolean;
  37. function OnWriterIsElementUsed(Sender: TObject; El: TPasElement): boolean;
  38. procedure OnFilerGetSrc(Sender: TObject; aFilename: string; out p: PChar; out Count: integer);
  39. Public
  40. constructor create(aCompilerFile: TPas2JSCompilerFile; aFormat: TPas2JSPrecompileFormat);
  41. Destructor destroy; override;
  42. Function Compiler : TPas2JSCompiler;
  43. Function HandleException(E: exception) : Boolean; override;
  44. function FindPCU(const UseUnitName: string): string;override;
  45. function FindPCU(const UseUnitName: string; out aFormat: TPas2JSPrecompileFormat): string;
  46. Function HasReader : Boolean; override;
  47. Function ReadContinue: Boolean; override;
  48. Function ReadCanContinue : Boolean; override;
  49. Procedure SetInitialCompileFlags; override;
  50. Procedure WritePCU; override;
  51. procedure CreatePCUReader; override;
  52. Procedure ReadUnit; override;
  53. property PrecompileInitialFlags: TPCUInitialFlags read FPrecompileInitialFlags;
  54. end;
  55. { TPas2jsPCUCompilerFile }
  56. TPas2jsPCUCompilerFile = Class(TPas2jsCompilerFile)
  57. Function CreatePCUSupport: TPCUSupport; override;
  58. end;
  59. { TPas2jsPCUCompiler }
  60. TPas2jsPCUCompiler = Class(TPas2JSFSCompiler)
  61. Private
  62. FPrecompileFormat : TPas2JSPrecompileFormat;
  63. Protected
  64. procedure WritePrecompiledFormats; override;
  65. function CreateCompilerFile(const UnitFileName: String): TPas2jsCompilerFile; override;
  66. Procedure HandleOptionPCUFormat(Value : string) ; override;
  67. end;
  68. implementation
  69. uses fppas2js, pscanner, pas2jslogger, pasresolveeval, jstree, pas2jsfileutils;
  70. {$IFDEF HASPAS2JSFILER}
  71. { ---------------------------------------------------------------------
  72. TFilerPCUSupport
  73. ---------------------------------------------------------------------}
  74. { TFilerPCUSupport }
  75. constructor TFilerPCUSupport.create(aCompilerFile: TPas2JSCompilerFile; aFormat: TPas2JSPrecompileFormat);
  76. begin
  77. Inherited Create(aCompilerFile);
  78. FPCUFormat:=AFormat;
  79. FPrecompileInitialFlags:=TPCUInitialFlags.Create;
  80. end;
  81. destructor TFilerPCUSupport.destroy;
  82. begin
  83. FreeAndNil(FPrecompileInitialFlags);
  84. FreeAndNil(FPCUReader);
  85. FreeAndNil(FPCUReaderStream);
  86. inherited destroy;
  87. end;
  88. function TFilerPCUSupport.Compiler: TPas2JSCompiler;
  89. begin
  90. Result:=MyFile.Compiler;
  91. end;
  92. Function TFilerPCUSupport.HandleException(E: Exception) : Boolean;
  93. begin
  94. Result:=False;
  95. if E is EPas2JsReadError then
  96. begin
  97. Result:=True;
  98. if EPas2JsReadError(E).Owner is TPCUCustomReader then
  99. begin
  100. MyFile.Log.Log(mtError,E.Message,0,MyFile.PCUFilename);
  101. end else begin
  102. MyFile.Log.Log(mtError,E.Message);
  103. end;
  104. Compiler.Terminate(ExitCodePCUError);
  105. end
  106. else if (E is EPas2JsWriteError) then
  107. begin
  108. MyFile.Log.Log(mtFatal,E.ClassName+':'+E.Message);
  109. Compiler.Terminate(ExitCodeErrorInternal);
  110. Result:=True;
  111. end
  112. end;
  113. function TFilerPCUSupport.FindPCU(const UseUnitName: string): string;
  114. begin
  115. Result:=FindPCU(UseUnitName,FFoundFormat);
  116. end;
  117. function TFilerPCUSupport.HasReader: Boolean;
  118. begin
  119. Result:=Assigned(FPCUReader);
  120. end;
  121. function TFilerPCUSupport.ReadContinue: Boolean;
  122. begin
  123. Result:=FPCUReader.ReadContinue;
  124. end;
  125. function TFilerPCUSupport.ReadCanContinue: Boolean;
  126. begin
  127. Result:=FPCUReader.ReadCanContinue;
  128. end;
  129. procedure TFilerPCUSupport.SetInitialCompileFlags;
  130. begin
  131. PrecompileInitialFlags.ParserOptions:=MyFile.Parser.Options;
  132. PrecompileInitialFlags.ModeSwitches:=MyFile.Scanner.CurrentModeSwitches;
  133. PrecompileInitialFlags.BoolSwitches:=MyFile.Scanner.CurrentBoolSwitches;
  134. PrecompileInitialFlags.ConverterOptions:=MyFile.GetInitialConverterOptions;
  135. PrecompileInitialFlags.TargetPlatform:=Compiler.TargetPlatform;
  136. PrecompileInitialFlags.TargetProcessor:=Compiler.TargetProcessor;
  137. end;
  138. procedure TFilerPCUSupport.CreatePCUReader;
  139. var
  140. aFile: TPas2jsFile;
  141. s: String;
  142. begin
  143. if MyFile.PCUFilename='' then
  144. RaiseInternalError(20180312144742,MyFile.PCUFilename);
  145. if FPCUReader<>nil then
  146. RaiseInternalError(20180312142938,GetObjName(FPCUReader));
  147. if FFoundFormat=nil then
  148. RaiseInternalError(20180312142954,'');
  149. FPCUReader:=FFoundFormat.ReaderClass.Create;
  150. FPCUReader.SourceFilename:=ExtractFileName(MyFile.PCUFilename);
  151. if MyFile.ShowDebug then
  152. MyFile.Log.LogMsg(nParsingFile,[QuoteStr(MyFile.PCUFilename)]);
  153. aFile:=Compiler.FS.LoadFile(MyFile.PCUFilename,true);
  154. if aFile=nil then
  155. RaiseInternalError(20180312145941,MyFile.PCUFilename);
  156. FPCUReaderStream:=TMemoryStream.Create;
  157. s:=aFile.Source;
  158. //writeln('TPas2jsCompilerFile.CreatePCUReader ',PCUFilename,'-----START-----');
  159. //writeln(s);
  160. //writeln('TPas2jsCompilerFile.CreatePCUReader ',PCUFilename,'-----END-------');
  161. if s<>'' then
  162. begin
  163. FPCUReaderStream.Write(s[1],length(s));
  164. FPCUReaderStream.Position:=0;
  165. end;
  166. end;
  167. procedure TFilerPCUSupport.ReadUnit;
  168. begin
  169. FPCUReader.ReadPCU(MyFile.PascalResolver,FPCUReaderStream);
  170. SetPasModule(MyFile.PascalResolver.RootElement);
  171. SetReaderState(prsCanContinue);
  172. end;
  173. function TFilerPCUSupport.FindPCU(const UseUnitName: string; out aFormat: TPas2JSPrecompileFormat): string;
  174. function SearchInDir(DirPath: string): boolean;
  175. var
  176. i: Integer;
  177. CurFormat: TPas2JSPrecompileFormat;
  178. Filename: String;
  179. begin
  180. if DirPath='' then exit(false);
  181. DirPath:=IncludeTrailingPathDelimiter(DirPath);
  182. for i:=0 to PrecompileFormats.Count-1 do
  183. begin
  184. CurFormat:=PrecompileFormats[i];
  185. if not CurFormat.Enabled then continue;
  186. Filename:=DirPath+UseUnitName+'.'+CurFormat.Ext;
  187. if Compiler.FS.PCUExists(Filename) then
  188. begin
  189. FindPCU:=Filename;
  190. aFormat:=CurFormat;
  191. exit(true);
  192. end;
  193. end;
  194. Result:=false;
  195. end;
  196. var
  197. L : TstringList;
  198. i: Integer;
  199. begin
  200. Result:='';
  201. aFormat:=nil;
  202. L:=TstringList.Create;
  203. try
  204. Compiler.FS.GetPCUDirs(L,MyFile.FileResolver.BaseDirectory);
  205. for i:=0 to L.Count-1 do
  206. if SearchInDir(L[i]) then exit;
  207. finally
  208. L.Free;
  209. end;
  210. end;
  211. function TFilerPCUSupport.OnWriterIsElementUsed(Sender: TObject;
  212. El: TPasElement): boolean;
  213. begin
  214. Result:=MyFile.UseAnalyzer.IsUsed(El);
  215. end;
  216. procedure TFilerPCUSupport.WritePCU;
  217. Const
  218. AllowCompressed =
  219. {$IFDEF DisablePCUCompressed}false{$ELSE}true{$ENDIF};
  220. var
  221. Writer: TPCUWriter;
  222. ms: TMemoryStream;
  223. DestDir: String;
  224. JS: TJSElement;
  225. FN : String;
  226. begin
  227. if FPCUFormat=Nil then
  228. exit; // Don't write
  229. if MyFile.PasModule.ClassType<>TPasModule then
  230. begin
  231. {$IFDEF REALLYVERBOSE}
  232. writeln('TPas2jsCompilerFile.WritePCU not a unit: ',MyFile.PasFilename,' skip');
  233. {$ENDIF}
  234. exit;
  235. end;
  236. if (MyFile.PCUFilename<>'') or (FPCUReader<>nil) then
  237. begin
  238. {$IFDEF REALLYVERBOSE}
  239. writeln('TPas2jsCompilerFile.WritePCU already precompiled "',MyFile.PCUFilename,'" Reader=',GetObjName(FPCUReader));
  240. {$ENDIF}
  241. exit;
  242. end;
  243. // Determine output filename
  244. FN:=ExtractFilenameOnly(MyFile.PasFilename)+'.'+FPCUFormat.Ext;
  245. if Compiler.FS.UnitOutputPath<>'' then
  246. FN:=Compiler.FS.UnitOutputPath+FN
  247. else
  248. FN:=ExtractFilePath(MyFile.PasFilename)+FN;
  249. // Set as our filename
  250. SetPCUFilename(FN);
  251. {$IFDEF REALLYVERBOSE}
  252. writeln('TPas2jsCompilerFile.WritePCU precompiling ',MyFile.PCUFilename);
  253. {$ENDIF}
  254. JS:=nil;
  255. ms:=TMemoryStream.Create;
  256. Writer:=FPCUFormat.WriterClass.Create;
  257. try
  258. Writer.GUID:=Compiler.PrecompileGUID;
  259. Writer.OnGetSrc:=@OnFilerGetSrc;
  260. Writer.OnIsElementUsed:=@OnWriterIsElementUsed;
  261. // create JavaScript for procs, initialization, finalization
  262. MyFile.CreateConverter;
  263. MyFile.Converter.Options:=MyFile.Converter.Options+[coStoreImplJS];
  264. MyFile.Converter.OnIsElementUsed:=@OnPCUConverterIsElementUsed;
  265. MyFile.Converter.OnIsTypeInfoUsed:=@OnPCUConverterIsTypeInfoUsed;
  266. JS:=MyFile.Converter.ConvertPasElement(MyFile.PasModule,MyFile.PascalResolver);
  267. MyFile.Converter.Options:=MyFile.Converter.Options-[coStoreImplJS];
  268. {$IFDEF REALLYVERBOSE}
  269. writeln('TPas2jsCompilerFile.WritePCU create pcu ... ',MyFile.PCUFilename);
  270. {$ENDIF}
  271. Writer.WritePCU(MyFile.PascalResolver,MyFile.Converter,PrecompileInitialFlags,ms,AllowCompressed);
  272. {$IFDEF REALLYVERBOSE}
  273. writeln('TPas2jsCompilerFile.WritePCU precompiled ',MyFile.PCUFilename);
  274. {$ENDIF}
  275. MyFile.Log.LogMsg(nWritingFile,[QuoteStr(Compiler.FS.FormatPath(MyFile.PCUFilename))],'',0,0,
  276. not (coShowLineNumbers in Compiler.Options));
  277. // check output directory
  278. DestDir:=ChompPathDelim(ExtractFilePath(MyFile.PCUFilename));
  279. if (DestDir<>'') and not Compiler.FS.DirectoryExists(DestDir) then
  280. begin
  281. {$IFDEF REALLYVERBOSE}
  282. writeln('TPas2jsCompilerFile.WritePCU output dir not found "',DestDir,'"');
  283. {$ENDIF}
  284. MyFile.Log.LogMsg(nOutputDirectoryNotFound,[QuoteStr(Compiler.FS.FormatPath(DestDir))]);
  285. Compiler.Terminate(ExitCodeFileNotFound);
  286. end;
  287. if Compiler.FS.DirectoryExists(MyFile.PCUFilename) then
  288. begin
  289. {$IFDEF REALLYVERBOSE}
  290. writeln('TPas2jsCompilerFile.WritePCU file is folder "',DestDir,'"');
  291. {$ENDIF}
  292. MyFile.Log.LogMsg(nFileIsFolder,[QuoteStr(Compiler.FS.FormatPath(MyFile.PCUFilename))]);
  293. Compiler.Terminate(ExitCodeWriteError);
  294. end;
  295. ms.Position:=0;
  296. Compiler.FS.SaveToFile(ms,MyFile.PCUFilename);
  297. {$IFDEF REALLYVERBOSE}
  298. writeln('TPas2jsCompilerFile.WritePCU written ',MyFile.PCUFilename);
  299. {$ENDIF}
  300. finally
  301. JS.Free;
  302. Writer.Free;
  303. ms.Free;
  304. end;
  305. end;
  306. procedure TFilerPCUSupport.OnFilerGetSrc(Sender: TObject; aFilename: string;
  307. out p: PChar; out Count: integer);
  308. var
  309. SrcFile: TPas2jsFile;
  310. begin
  311. if Sender=nil then
  312. RaiseInternalError(20180311135558,aFilename);
  313. SrcFile:=MyFile.Compiler.FS.LoadFile(aFilename);
  314. if SrcFile=nil then
  315. RaiseInternalError(20180311135329,aFilename);
  316. p:=PChar(SrcFile.Source);
  317. Count:=length(SrcFile.Source);
  318. end;
  319. function TFilerPCUSupport.OnPCUConverterIsElementUsed(Sender: TObject;
  320. El: TPasElement): boolean;
  321. begin
  322. if (coKeepNotUsedPrivates in MyFile.Compiler.Options) then
  323. Result:=true
  324. else
  325. Result:=MyFile.UseAnalyzer.IsUsed(El);
  326. end;
  327. function TFilerPCUSupport.OnPCUConverterIsTypeInfoUsed(Sender: TObject;
  328. El: TPasElement): boolean;
  329. begin
  330. if Sender=nil then ;
  331. if El=nil then ;
  332. // PCU does not need precompiled typeinfo
  333. Result:=false;
  334. end;
  335. { TPas2jsPCUCompiler }
  336. procedure TPas2jsPCUCompiler.WritePrecompiledFormats;
  337. Var
  338. I : Integer;
  339. begin
  340. if PrecompileFormats.Count>0 then
  341. begin
  342. writeHelpLine(' -JU<x> : Create precompiled units in format x.');
  343. for i:=0 to PrecompileFormats.Count-1 do
  344. with PrecompileFormats[i] do
  345. writeHelpLine(' -JU'+Ext+' : '+Description);
  346. writeHelpLine(' -JU- : Disable prior -JU<x> option. Do not create precompiled units.');
  347. end;
  348. end;
  349. function TPas2jsPCUCompiler.CreateCompilerFile(const UnitFileName: String): TPas2jsCompilerFile;
  350. begin
  351. Result:=TPas2JSPCUCompilerFile.Create(Self,UnitFileName);
  352. end;
  353. procedure TPas2jsPCUCompiler.HandleOptionPCUFormat(Value: string);
  354. Var
  355. Found : Boolean;
  356. I : integer;
  357. PF: TPas2JSPrecompileFormat;
  358. begin
  359. Found:=false;
  360. for i:=0 to PrecompileFormats.Count-1 do
  361. begin
  362. PF:=PrecompileFormats[i];
  363. if not SameText(Value,PF.Ext) then continue;
  364. FPrecompileFormat:=PrecompileFormats[i];
  365. Found:=true;
  366. end;
  367. if not Found then
  368. ParamFatal('invalid precompile output format (-JU) "'+Value+'"');
  369. end;
  370. { TPas2jsPCUCompilerFile }
  371. function TPas2jsPCUCompilerFile.CreatePCUSupport: TPCUSupport;
  372. Var
  373. PF: TPas2JSPrecompileFormat;
  374. begin
  375. // Note that if no format was preset, no files will be written
  376. PF:=(Compiler as TPas2jsPCUCompiler).FPrecompileFormat;
  377. if PF<>Nil then
  378. Result:=TFilerPCUSupport.Create(Self,PF)
  379. else
  380. Result:=Nil;
  381. end;
  382. {$ENDIF}
  383. end.