stubcreator.pp 12 KB

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