stubcreator.pp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. {
  2. Copyright (C) 2017 - 2020 by Michael Van Canneyt [email protected]
  3. pas2js Delphi stub generator - component
  4. See the file COPYING.FPC, included in this distribution,
  5. for details about the copyright.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  9. }
  10. unit stubcreator;
  11. {$mode objfpc}{$H+}
  12. interface
  13. uses
  14. Classes, SysUtils, strutils, inifiles, pscanner, pparser, pastree, iostream, paswrite;
  15. Const
  16. DTypesUnit = 'jsdelphisystem';
  17. type
  18. { We have to override abstract TPasTreeContainer methods }
  19. TSimpleEngine = class(TPasTreeContainer)
  20. public
  21. function CreateElement(AClass: TPTreeElement; const AName: String;
  22. AParent: TPasElement; AVisibility: TPasMemberVisibility;
  23. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
  24. override;
  25. function FindElement(const AName: String): TPasElement; override;
  26. end;
  27. TWriteCallBack = Procedure (Data : Pointer; AFileData : PAnsiChar; AFileDataLen: Int32); stdcall;
  28. TWriteEvent = Procedure(AFileData : String) of object;
  29. { TStubCreator }
  30. TStubCreator = Class(TComponent)
  31. private
  32. FConfigFile: String;
  33. FHeaderStream: TStream;
  34. FIncludePaths: TStrings;
  35. FInputFile: String;
  36. FOnWrite: TWriteEvent;
  37. FOnWriteCallBack: TWriteCallBack;
  38. FOutputFile: String;
  39. FDefines : TStrings;
  40. FOptions: TPasWriterOptions;
  41. FLineNumberWidth,
  42. FIndentSize : Integer;
  43. FExtraUnits : String;
  44. FForwardClasses : String;
  45. FHeaderFile : String;
  46. FOutputStream: TStream;
  47. FWriteStream : TStringStream;
  48. FCallBackData : Pointer;
  49. FLastErrorClass : String;
  50. FLastError : String;
  51. procedure SetDefines(AValue: TStrings);
  52. procedure SetIncludePaths(AValue: TStrings);
  53. procedure SetOnWrite(AValue: TWriteEvent);
  54. procedure SetWriteCallback(AValue: TWriteCallBack);
  55. Protected
  56. procedure DoExecute;virtual;
  57. Procedure DoWriteEvent; virtual;
  58. procedure ReadConfig(const aFileName: String); virtual;
  59. procedure ReadConfig(const aIni: TIniFile); virtual;
  60. procedure WriteModule(M: TPasModule); virtual;
  61. function GetModule: TPasModule; virtual;
  62. Function MaybeGetFileStream(AStream : TStream; Const AFileName : String; aFileMode : Word) : TStream; virtual;
  63. Public
  64. Constructor Create(AOwner : TComponent); override;
  65. Destructor Destroy; override;
  66. Procedure Execute;
  67. Procedure GetLastError(Out AError,AErrorClass : String);
  68. // Streams take precedence over filenames. They will be freed on destroy!
  69. // OutputStream can be used combined with write callbacks.
  70. Property OutputStream : TStream Read FOutputStream Write FOutputStream;
  71. Property HeaderStream : TStream Read FHeaderStream Write FHeaderStream;
  72. Property OnWriteCallBack : TWriteCallBack Read FOnWriteCallBack Write SetWriteCallback;
  73. Property CallbackData : Pointer Read FCallBackData Write FCallBackData;
  74. Published
  75. Property Defines : TStrings Read FDefines Write SetDefines;
  76. Property ConfigFileName : String Read FConfigFile Write FConfigFile;
  77. Property InputFileName : String Read FInputFile write FInputFile;
  78. Property OutputFileName : String Read FOutputFile write FOutputFile;
  79. Property HeaderFileName : String Read FHeaderFile write FHeaderFile;
  80. Property ForwardClasses : String Read FForwardClasses write FForwardClasses;
  81. Property IncludePaths : TStrings Read FIncludePaths Write SetIncludePaths;
  82. Property OnWrite : TWriteEvent Read FOnWrite Write SetOnWrite;
  83. end;
  84. Implementation
  85. ResourceString
  86. SErrNoDestGiven = 'No destination file specified.';
  87. SErrNoSourceParsed = 'Parsing produced no file.';
  88. procedure TStubCreator.SetDefines(AValue: TStrings);
  89. begin
  90. if FDefines=AValue then Exit;
  91. FDefines.Assign(AValue);
  92. end;
  93. procedure TStubCreator.SetIncludePaths(AValue: TStrings);
  94. begin
  95. if FIncludePaths=AValue then Exit;
  96. FIncludePaths.Assign(AValue);
  97. end;
  98. procedure TStubCreator.SetOnWrite(AValue: TWriteEvent);
  99. begin
  100. if FOnWrite=AValue then Exit;
  101. FOnWrite:=AValue;
  102. FreeAndNil(FWriteStream);
  103. if Assigned(AValue) then
  104. FWriteStream:=TStringStream.Create('');
  105. end;
  106. procedure TStubCreator.SetWriteCallback(AValue: TWriteCallBack);
  107. begin
  108. if FOnWriteCallBack=AValue then Exit;
  109. FOnWriteCallBack:=AValue;
  110. FreeAndNil(FWriteStream);
  111. if Assigned(AValue) then
  112. FWriteStream:=TStringStream.Create('');
  113. end;
  114. procedure TStubCreator.DoWriteEvent;
  115. Var
  116. S : String;
  117. begin
  118. If Assigned(FOnWrite) then
  119. FOnWrite(FWriteStream.DataString);
  120. if Assigned(FOnWriteCallBack) then
  121. begin
  122. S:=FWriteStream.DataString;
  123. FOnWriteCallBack(FCallBackData,PChar(S),Length(S));
  124. end;
  125. end;
  126. { TStubCreator }
  127. procedure TStubCreator.ReadConfig(const aFileName: String);
  128. Var
  129. ini : TMemIniFile;
  130. begin
  131. ini:=TMemIniFile.Create(AFileName);
  132. try
  133. ReadConfig(Ini);
  134. finally
  135. Ini.Free;
  136. end;
  137. end;
  138. procedure TStubCreator.ReadConfig(const aIni: TIniFile);
  139. Const
  140. DelChars = [',',' '];
  141. Var
  142. O : TPaswriterOptions;
  143. S : String;
  144. I : Integer;
  145. begin
  146. O:=[];
  147. With aIni do
  148. begin
  149. if ReadBool('Config','addlinenumber',False) then
  150. Include(O,woAddLineNumber);
  151. if ReadBool('Config','addsourcelinenumber',False) then
  152. Include(O,woAddLineNumber);
  153. FOptions:=FOptions+O;
  154. InputFilename:=ReadString('config','input',InputFilename);
  155. OutputFilename:=ReadString('config','output',OutputFilename);
  156. HeaderFilename:=ReadString('config','header',HeaderFilename);
  157. FIndentSize:=ReadInteger('config','indentsize',FIndentSize);
  158. FLineNumberWidth:=ReadInteger('config','linenumberwidth',FLineNumberWidth);
  159. FExtraUnits:=ReadString('config','extra',FExtraUnits);
  160. FForwardClasses:=ReadString('config','forwardclasses',FForwardClasses);
  161. S:=ReadString('config','defines','');
  162. if (S<>'') then
  163. For I:=1 to WordCount(S,DelChars) do
  164. FDefines.Add(UpperCase(ExtractWord(I,S,DelChars)));
  165. S:=ReadString('config','includepaths','');
  166. if (S<>'') then
  167. For I:=1 to WordCount(S,[',',';']) do
  168. FIncludePaths.Add(ExtractWord(I,S,[',',';']));
  169. end;
  170. if (FForwardClasses<>'') or (FForwardClasses='all') then
  171. Include(O,woForwardClasses);
  172. end;
  173. procedure TStubCreator.Execute;
  174. begin
  175. FLastErrorClass:='';
  176. FLastError:='';
  177. Try
  178. DoExecute;
  179. except
  180. On E : Exception do
  181. begin
  182. FLastErrorClass:=E.Classname;
  183. FLastError:=E.Message;
  184. Raise;
  185. end;
  186. end;
  187. end;
  188. procedure TStubCreator.GetLastError(out AError, AErrorClass: String);
  189. begin
  190. AError:=FLastError;
  191. AErrorClass:=FLastErrorClass;
  192. end;
  193. procedure TStubCreator.DoExecute;
  194. Var
  195. M : TPasModule;
  196. begin
  197. If (ConfigFileName<>'') then
  198. ReadConfig(ConfigFileName);
  199. if InputFilename = '' then
  200. raise Exception.Create(SErrNoSourceGiven);
  201. if (OutputFilename = '') and (FoutputStream=Nil) and (FWriteStream=Nil) then
  202. raise Exception.Create(SErrNoDestGiven);
  203. if CompareText(ForwardClasses,'all')=0 then
  204. begin
  205. Include(Foptions,woForwardClasses);
  206. ForwardClasses:='';
  207. end
  208. else if (ForwardClasses<>'') then
  209. Include(Foptions,woForwardClasses);
  210. Include(Foptions,woForceOverload);
  211. M:=GetModule;
  212. if M=Nil then
  213. raise Exception.Create(SErrNoSourceParsed);
  214. try
  215. WriteModule(M);
  216. finally
  217. M.Free;
  218. end;
  219. end;
  220. { TSimpleEngine }
  221. function TSimpleEngine.CreateElement(AClass: TPTreeElement; const AName: String;
  222. AParent: TPasElement; AVisibility: TPasMemberVisibility;
  223. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
  224. begin
  225. Result := AClass.Create(AName, AParent);
  226. Result.Visibility := AVisibility;
  227. Result.SourceFilename := ASourceFilename;
  228. Result.SourceLinenumber := ASourceLinenumber;
  229. end;
  230. function TSimpleEngine.FindElement(const AName: String): TPasElement;
  231. begin
  232. { dummy implementation, see TFPDocEngine.FindElement for a real example }
  233. Result := nil;
  234. if AName<>'' then ; // Keep compiler happy
  235. end;
  236. Function TStubCreator.GetModule : TPasModule;
  237. Var
  238. SE : TSimpleEngine;
  239. FileResolver: TFileResolver;
  240. Parser: TPasParser;
  241. Scanner: TPascalScanner;
  242. var
  243. s: String;
  244. begin
  245. Result := nil;
  246. FileResolver := nil;
  247. Scanner := nil;
  248. Parser := nil;
  249. SE:=TSimpleEngine.Create;
  250. try
  251. // File resolver
  252. FileResolver := TFileResolver.Create;
  253. FileResolver.UseStreams:=True;
  254. FileResolver.AddIncludePath(ExtractFilePath(InputFileName));
  255. For S in FIncludePaths do
  256. FileResolver.AddIncludePath(S);
  257. // Scanner
  258. Scanner := TPascalScanner.Create(FileResolver);
  259. Scanner.Options:=[po_AsmWhole,po_KeepClassForward];
  260. SCanner.LogEvents:=SE.ScannerLogEvents;
  261. SCanner.OnLog:=SE.Onlog;
  262. For S in FDefines do
  263. Scanner.AddDefine(S);
  264. Scanner.OpenFile(InputFilename);
  265. // Parser
  266. Parser:=TPasParser.Create(Scanner, FileResolver, SE);
  267. Parser.LogEvents:=SE.ParserLogEvents;
  268. Parser.OnLog:=SE.Onlog;
  269. Parser.Options:=Parser.Options+[po_AsmWhole,po_delphi,po_KeepClassForward];
  270. Parser.ParseMain(Result);
  271. finally
  272. Parser.Free;
  273. Scanner.Free;
  274. FileResolver.Free;
  275. SE.Free;
  276. end;
  277. end;
  278. function TStubCreator.MaybeGetFileStream(AStream: TStream; const AFileName: String; AfileMode : Word) : TStream;
  279. begin
  280. If Assigned(AStream) then
  281. Result:=AStream
  282. else if (AFileName<>'') then
  283. Result:=TFileStream.Create(AFileName,aFileMode)
  284. else
  285. Result:=Nil;
  286. end;
  287. constructor TStubCreator.Create(AOwner: TComponent);
  288. begin
  289. inherited Create(AOwner);
  290. FDefines:=TStringList.Create;
  291. FIncludePaths:=TStringList.Create;
  292. FLineNumberWidth:=4;
  293. FIndentSize:=2;
  294. FExtraUnits:='';
  295. FOptions:=[woNoImplementation,woNoExternalClass,woNoExternalVar,woNoExternalFunc];
  296. end;
  297. destructor TStubCreator.Destroy;
  298. begin
  299. FreeAndNil(FWriteStream);
  300. FreeAndNil(FOutputStream);
  301. FreeAndNil(FHeaderStream);
  302. FreeAndNil(FIncludePaths);
  303. FreeAndNil(FDefines);
  304. inherited Destroy;
  305. end;
  306. procedure TStubCreator.WriteModule(M : TPAsModule);
  307. Var
  308. F,H : TStream;
  309. W : TPasWriter;
  310. U : String;
  311. begin
  312. W:=Nil;
  313. F:=MaybeGetFileStream(OutputStream,FOutputFile,fmCreate);
  314. if (F=Nil) then
  315. if FWriteStream<>nil then
  316. F:=FWriteStream
  317. else
  318. F:=TIOStream.Create(iosOutPut);
  319. try
  320. H:=MaybeGetFileStream(HeaderStream,FHeaderFile,fmOpenRead or fmShareDenyWrite);
  321. if Assigned(h) then
  322. try
  323. F.CopyFrom(H,H.Size);
  324. finally
  325. if H<>HeaderStream then
  326. H.Free;
  327. end;
  328. W:=TPasWriter.Create(F);
  329. W.Options:=FOptions;
  330. U:=FExtraUnits;
  331. if Pos(LowerCase(DTypesUnit),LowerCase(U)) = 0 then
  332. begin
  333. if (U<>'') then
  334. U:=','+U;
  335. U:=DTypesUnit+U;
  336. end;
  337. W.ExtraUnits:=U;
  338. if FIndentSize<>-1 then
  339. W.IndentSize:=FIndentSize;
  340. if FLineNumberWidth>0 then
  341. W.LineNumberWidth:=FLineNumberWidth;
  342. W.ForwardClasses.CommaText:=FForwardClasses;
  343. W.WriteModule(M);
  344. if Assigned(FWriteStream) then
  345. DoWriteEvent;
  346. finally
  347. W.Free;
  348. if (F<>OutputStream) and (F<>FWriteStream) then
  349. F.Free;
  350. end;
  351. end;
  352. end.