stubcreator.pp 12 KB

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