stubcreator.pp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  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. Function Execute: Boolean;
  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. function TStubCreator.Execute: Boolean;
  174. begin
  175. FLastErrorClass:='';
  176. FLastError:='';
  177. Result := False;
  178. if Defines.IndexOf('MakeStub')=-1 then
  179. Try
  180. DoExecute;
  181. Result := True;
  182. except
  183. On E : Exception do
  184. begin
  185. FLastErrorClass:=E.Classname;
  186. FLastError:=E.Message;
  187. end;
  188. end;
  189. end;
  190. procedure TStubCreator.GetLastError(out AError, AErrorClass: String);
  191. begin
  192. AError:=FLastError;
  193. AErrorClass:=FLastErrorClass;
  194. end;
  195. procedure TStubCreator.DoExecute;
  196. Var
  197. M : TPasModule;
  198. begin
  199. If (ConfigFileName<>'') then
  200. ReadConfig(ConfigFileName);
  201. if InputFilename = '' then
  202. raise Exception.Create(SErrNoSourceGiven);
  203. if (OutputFilename = '') and (FoutputStream=Nil) and (FWriteStream=Nil) then
  204. raise Exception.Create(SErrNoDestGiven);
  205. if CompareText(ForwardClasses,'all')=0 then
  206. begin
  207. Include(Foptions,woForwardClasses);
  208. ForwardClasses:='';
  209. end
  210. else if (ForwardClasses<>'') then
  211. Include(Foptions,woForwardClasses);
  212. Include(Foptions,woForceOverload);
  213. M:=GetModule;
  214. if M=Nil then
  215. raise Exception.Create(SErrNoSourceParsed);
  216. try
  217. WriteModule(M);
  218. finally
  219. M.Free;
  220. end;
  221. end;
  222. { TSimpleEngine }
  223. function TSimpleEngine.CreateElement(AClass: TPTreeElement; const AName: String;
  224. AParent: TPasElement; AVisibility: TPasMemberVisibility;
  225. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
  226. begin
  227. Result := AClass.Create(AName, AParent);
  228. Result.Visibility := AVisibility;
  229. Result.SourceFilename := ASourceFilename;
  230. Result.SourceLinenumber := ASourceLinenumber;
  231. end;
  232. function TSimpleEngine.FindElement(const AName: String): TPasElement;
  233. begin
  234. { dummy implementation, see TFPDocEngine.FindElement for a real example }
  235. Result := nil;
  236. if AName<>'' then ; // Keep compiler happy
  237. end;
  238. Function TStubCreator.GetModule : TPasModule;
  239. Var
  240. SE : TSimpleEngine;
  241. FileResolver: TFileResolver;
  242. Parser: TPasParser;
  243. Scanner: TPascalScanner;
  244. var
  245. s: String;
  246. begin
  247. Result := nil;
  248. FileResolver := nil;
  249. Scanner := nil;
  250. Parser := nil;
  251. SE:=TSimpleEngine.Create;
  252. try
  253. // File resolver
  254. FileResolver := TFileResolver.Create;
  255. FileResolver.UseStreams:=True;
  256. FileResolver.AddIncludePath(ExtractFilePath(InputFileName));
  257. For S in FIncludePaths do
  258. FileResolver.AddIncludePath(S);
  259. // Scanner
  260. Scanner := TPascalScanner.Create(FileResolver);
  261. Scanner.Options:=[po_AsmWhole,po_KeepClassForward,po_ExtConstWithoutExpr];
  262. SCanner.LogEvents:=SE.ScannerLogEvents;
  263. SCanner.OnLog:=SE.Onlog;
  264. For S in FDefines do
  265. Scanner.AddDefine(S);
  266. if FDefines.IndexOf('MAKESTUB')=-1 then
  267. Scanner.AddDefine('MAKESTUB');
  268. Scanner.OpenFile(InputFilename);
  269. // Parser
  270. Parser:=TPasParser.Create(Scanner, FileResolver, SE);
  271. Parser.LogEvents:=SE.ParserLogEvents;
  272. Parser.OnLog:=SE.Onlog;
  273. Parser.Options:=Parser.Options+[po_AsmWhole,po_delphi,po_KeepClassForward,po_ExtConstWithoutExpr,po_AsyncProcs];
  274. Parser.ParseMain(Result);
  275. finally
  276. Parser.Free;
  277. Scanner.Free;
  278. FileResolver.Free;
  279. SE.Free;
  280. end;
  281. end;
  282. function TStubCreator.MaybeGetFileStream(AStream: TStream; const AFileName: String; AfileMode : Word) : TStream;
  283. begin
  284. If Assigned(AStream) then
  285. Result:=AStream
  286. else if (AFileName<>'') then
  287. Result:=TFileStream.Create(AFileName,aFileMode)
  288. else
  289. Result:=Nil;
  290. end;
  291. constructor TStubCreator.Create(AOwner: TComponent);
  292. begin
  293. inherited Create(AOwner);
  294. FDefines:=TStringList.Create;
  295. FIncludePaths:=TStringList.Create;
  296. FLineNumberWidth:=4;
  297. FIndentSize:=2;
  298. FExtraUnits:='';
  299. FOptions:=[woNoImplementation,woNoExternalClass,woNoExternalVar,woNoExternalFunc,woNoAsm,woSkipPrivateExternals,woAlwaysRecordHelper,woSkipHints];
  300. end;
  301. destructor TStubCreator.Destroy;
  302. begin
  303. FreeAndNil(FWriteStream);
  304. FreeAndNil(FOutputStream);
  305. FreeAndNil(FHeaderStream);
  306. FreeAndNil(FIncludePaths);
  307. FreeAndNil(FDefines);
  308. inherited Destroy;
  309. end;
  310. procedure TStubCreator.WriteModule(M : TPAsModule);
  311. Var
  312. F,H : TStream;
  313. W : TPasWriter;
  314. U : String;
  315. begin
  316. W:=Nil;
  317. F:=MaybeGetFileStream(OutputStream,FOutputFile,fmCreate);
  318. if (F=Nil) then
  319. if FWriteStream<>nil then
  320. F:=FWriteStream
  321. else
  322. F:=TIOStream.Create(iosOutPut);
  323. try
  324. H:=MaybeGetFileStream(HeaderStream,FHeaderFile,fmOpenRead or fmShareDenyWrite);
  325. if Assigned(h) then
  326. try
  327. F.CopyFrom(H,H.Size);
  328. finally
  329. if H<>HeaderStream then
  330. H.Free;
  331. end;
  332. W:=TPasWriter.Create(F);
  333. W.Options:=FOptions;
  334. U:=FExtraUnits;
  335. if Pos(LowerCase(DTypesUnit),LowerCase(U)) = 0 then
  336. begin
  337. if (U<>'') then
  338. U:=','+U;
  339. U:=DTypesUnit+U;
  340. end;
  341. W.ExtraUnits:=U;
  342. if FIndentSize<>-1 then
  343. W.IndentSize:=FIndentSize;
  344. if FLineNumberWidth>0 then
  345. W.LineNumberWidth:=FLineNumberWidth;
  346. W.ForwardClasses.CommaText:=FForwardClasses;
  347. W.WriteModule(M);
  348. if Assigned(FWriteStream) then
  349. DoWriteEvent;
  350. finally
  351. W.Free;
  352. if (F<>OutputStream) and (F<>FWriteStream) then
  353. F.Free;
  354. end;
  355. end;
  356. end.