mkxmlrpc.pp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870
  1. {
  2. Automatic XML-RPC wrapper generator
  3. Copyright (c) 2003 by
  4. Areca Systems GmbH / Sebastian Guenther, [email protected]
  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. program MkXMLRPC;
  12. {$mode objfpc}
  13. {$H+}
  14. uses SysUtils, Classes, PParser, PasTree, PasWrite;
  15. resourcestring
  16. SCmdLineInvalidOption = 'Ignoring unknown option "%s"';
  17. SNoServerClassNameProvided =
  18. 'No server class name provided (use --serverclass=<name>)';
  19. SNoUnitNameProvided =
  20. 'No name for generated unit provided (use --unitname=<name>)';
  21. type
  22. TParserEngine = class(TPasTreeContainer)
  23. protected
  24. Modules, UsedModules: TList;
  25. CurModule: TPasModule;
  26. public
  27. constructor Create;
  28. destructor Destroy; override;
  29. function CreateElement(AClass: TPTreeElement; const AName: String;
  30. AParent: TPasElement; AVisibility: TPasMemberVisibility;
  31. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
  32. override;
  33. function FindElement(const AName: String): TPasElement; override;
  34. function FindModule(const AName: String): TPasModule; override;
  35. end;
  36. TServerClass = class
  37. Element: TPasClassType;
  38. ImplName: String;
  39. end;
  40. TRPCList = class
  41. ServerClasses: TList;
  42. UsedModules: TStringList;
  43. constructor Create;
  44. destructor Destroy; override;
  45. procedure AddServerClass(const AClassName: String);
  46. end;
  47. var
  48. Engine: TParserEngine;
  49. constructor TParserEngine.Create;
  50. begin
  51. inherited Create;
  52. Modules := TList.Create;
  53. UsedModules := TList.Create;
  54. end;
  55. destructor TParserEngine.Destroy;
  56. begin
  57. UsedModules.Free;
  58. Modules.Free;
  59. inherited Destroy;
  60. end;
  61. function TParserEngine.CreateElement(AClass: TPTreeElement; const AName: String;
  62. AParent: TPasElement; AVisibility: TPasMemberVisibility;
  63. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
  64. begin
  65. Result := AClass.Create(AName, AParent);
  66. Result.Visibility := AVisibility;
  67. if AClass.InheritsFrom(TPasModule) then
  68. begin
  69. Modules.Add(Result);
  70. CurModule := TPasModule(Result);
  71. end;
  72. end;
  73. function TParserEngine.FindElement(const AName: String): TPasElement;
  74. function FindInModule(AModule: TPasModule; const LocalName: String): TPasElement;
  75. var
  76. l: TList;
  77. i, j: Integer;
  78. Found: Boolean;
  79. begin
  80. l := AModule.InterfaceSection.Declarations;
  81. for i := 0 to l.Count - 1 do
  82. begin
  83. Result := TPasElement(l[i]);
  84. if CompareText(Result.Name, LocalName) = 0 then
  85. begin
  86. Found := False;
  87. for j := 0 to UsedModules.Count - 1 do
  88. if CompareText(TPasModule(UsedModules[j]).Name, AModule.Name) = 0 then
  89. begin
  90. Found := True;
  91. break;
  92. end;
  93. if not Found then
  94. UsedModules.Add(AModule);
  95. exit;
  96. end;
  97. end;
  98. Result := nil;
  99. end;
  100. var
  101. i: Integer;
  102. //ModuleName, LocalName: String;
  103. Module: TPasElement;
  104. begin
  105. {!!!: Don't know if we ever will have to use the following:
  106. i := Pos('.', AName);
  107. if i <> 0 then
  108. begin
  109. WriteLn('Dot found in name: ', AName);
  110. Result := nil;
  111. end else
  112. begin}
  113. Result := FindInModule(CurModule, AName);
  114. if not Assigned(Result) then
  115. for i := CurModule.InterfaceSection.UsesList.Count - 1 downto 0 do
  116. begin
  117. Module := TPasElement(CurModule.InterfaceSection.UsesList[i]);
  118. if Module.ClassType = TPasModule then
  119. begin
  120. Result := FindInModule(TPasModule(Module), AName);
  121. if Assigned(Result) then
  122. exit;
  123. end;
  124. end;
  125. {end;}
  126. end;
  127. function TParserEngine.FindModule(const AName: String): TPasModule;
  128. var
  129. i: Integer;
  130. begin
  131. for i := Modules.Count - 1 downto 0 do
  132. begin
  133. Result := TPasModule(Modules[i]);
  134. if CompareText(Result.Name, AName) = 0 then
  135. exit;
  136. end;
  137. Result := nil;
  138. end;
  139. constructor TRPCList.Create;
  140. begin
  141. ServerClasses := TList.Create;
  142. UsedModules := TStringList.Create;
  143. end;
  144. destructor TRPCList.Destroy;
  145. var
  146. i: Integer;
  147. begin
  148. UsedModules.Free;
  149. for i := 0 to ServerClasses.Count - 1 do
  150. TServerClass(ServerClasses[i]).Free;
  151. ServerClasses.Free;
  152. end;
  153. procedure TRPCList.AddServerClass(const AClassName: String);
  154. var
  155. Element: TPasClassType;
  156. ServerClass: TServerClass;
  157. begin
  158. Element := TPasClassType(Engine.FindElement(AClassName));
  159. if not Assigned(Element) then
  160. begin
  161. WriteLn(StdErr, 'Server class "', AClassName, '" not found!');
  162. Halt(3);
  163. end;
  164. if (not Element.InheritsFrom(TPasClassType)) or
  165. (Element.ObjKind <> okClass) then
  166. begin
  167. WriteLn('"', AClassName, '" is not a class!');
  168. Halt(4);
  169. end;
  170. ServerClass := TServerClass.Create;
  171. ServerClasses.Add(ServerClass);
  172. ServerClass.Element := Element;
  173. ServerClass.ImplName := Copy(Element.Name, 2, Length(Element.Name));
  174. UsedModules.Add(Element.GetModule.Name);
  175. end;
  176. var
  177. OutputFilename, UnitName: String;
  178. RPCList: TRPCList;
  179. procedure WriteClassServerSource(ServerClass: TPasClassType;
  180. ImplementationSection: TPasSection; Method, ProcImpl: TPasProcedureImpl;
  181. const MethodPrefix: String; NestingLevel: Integer);
  182. { Method: Main server method
  183. ProcImpl: Current procedure (may be identical with Method) }
  184. type
  185. TConversionInfo = record
  186. ConverterName, TypecastFunction: String;
  187. ArgIsParent: Boolean;
  188. end;
  189. function MakeStructConverter(AClass: TPasClassType;
  190. Referrer: TPasProcedureImpl): TPasProcedureImpl; forward;
  191. function MakeArrayConverter(Member, ArraySizeProp: TPasProperty;
  192. ProcessProc, Referrer: TPasProcedureImpl): TPasProcedureImpl; forward;
  193. function FindArraySizeProperty(AArrayProp: TPasProperty): TPasProperty;
  194. var
  195. i: Integer;
  196. Name: String;
  197. begin
  198. Name := Copy(AArrayProp.Name, 1, Length(AArrayProp.Name) - 1) + 'Count';
  199. for i := 0 to TPasClassType(AArrayProp.Parent).Members.Count - 1 do
  200. begin
  201. Result := TPasProperty(TPasClassType(AArrayProp.Parent).Members[i]);
  202. if (Result.ClassType = TPasProperty) and (Result.Visibility = visPublic)
  203. and (CompareStr(Result.Name, Name) = 0) then
  204. exit;
  205. end;
  206. Name := AArrayProp.Name + 'Count';
  207. for i := 0 to TPasClassType(AArrayProp.Parent).Members.Count - 1 do
  208. begin
  209. Result := TPasProperty(TPasClassType(AArrayProp.Parent).Members[i]);
  210. if (Result.ClassType = TPasProperty) and (Result.Visibility = visPublic)
  211. and (CompareStr(Result.Name, Name) = 0) then
  212. exit;
  213. end;
  214. Result := nil;
  215. end;
  216. function GetConversionInfo(Element: TPasElement;
  217. Referrer: TPasProcedureImpl): TConversionInfo;
  218. var
  219. s: String;
  220. ArraySizeProp: TPasProperty;
  221. begin
  222. FillChar(Result, SizeOf(Result), 0);
  223. Result.ArgIsParent := False;
  224. if Element.ClassType = TPasProperty then
  225. begin
  226. ArraySizeProp := FindArraySizeProperty(TPasProperty(Element));
  227. if Assigned(ArraySizeProp) then
  228. begin
  229. Result.ConverterName := MakeArrayConverter(TPasProperty(Element),
  230. ArraySizeProp, ProcImpl, Referrer).Name;
  231. Result.ArgIsParent := True;
  232. exit;
  233. end else
  234. Element := TPasProperty(Element).VarType;
  235. end;
  236. if Element.ClassType = TPasUnresolvedTypeRef then
  237. begin
  238. s := UpperCase(Element.Name);
  239. if (s = 'BYTE') or (s = 'SHORTINT') or (S = 'SMALLINT') or
  240. (s = 'INTEGER') or (s = 'LONGINT') or (s = 'CARDINAL') or
  241. (s = 'INT64') or (s = 'QUADWORD') then
  242. Result.ConverterName := 'AWriter.CreateIntValue'
  243. else if (s = 'BOOLEAN') or (s = 'WORDBOOL') or (s = 'LONGBOOL') then
  244. Result.ConverterName := 'AWriter.CreateBooleanValue'
  245. else if s = 'STRING' then
  246. Result.ConverterName := 'AWriter.CreateStringValue'
  247. else if (s = 'FLOAT') or (s = 'SINGLE') or (s = 'DOUBLE') or
  248. (s = 'EXTENDED') then
  249. Result.ConverterName := 'AWriter.CreateDoubleValue'
  250. else if s = 'TDATETIME' then
  251. Result.ConverterName := 'AWriter.CreateDateTimeValue';
  252. end else if Element.ClassType = TPasClassType then
  253. Result.ConverterName := MakeStructConverter(TPasClassType(Element), Referrer).Name
  254. else if Element.ClassType = TPasEnumType then
  255. begin
  256. Result.ConverterName := 'AWriter.CreateIntValue';
  257. Result.TypecastFunction := 'Ord';
  258. end;
  259. if Length(Result.ConverterName) = 0 then
  260. raise Exception.Create('Result type not supported: ' + Element.ClassName +
  261. ' ' + Element.Name);
  262. end;
  263. function MakeAccessor(ConversionInfo: TConversionInfo;
  264. const DataSource, ArrayIndex: String): String;
  265. begin
  266. Result := ConversionInfo.ConverterName + '(';
  267. if ConversionInfo.TypecastFunction <> '' then
  268. Result := Result + ConversionInfo.TypecastFunction + '(';
  269. Result := Result + DataSource;
  270. if ConversionInfo.TypecastFunction <> '' then
  271. Result := Result + ')';
  272. if ArrayIndex <> '' then
  273. Result := Result + '[' + ArrayIndex + ']';
  274. Result := Result + ')';
  275. end;
  276. function GetParseValueFnName(PasType: TPasElement): String;
  277. var
  278. s: String;
  279. begin
  280. SetLength(Result, 0);
  281. if PasType.ClassType = TPasArgument then
  282. begin
  283. if TPasArgument(PasType).Access = argVar then
  284. raise Exception.Create('"var" arguments are not allowed');
  285. PasType := TPasArgument(PasType).ArgType;
  286. end;
  287. if PasType.ClassType = TPasUnresolvedTypeRef then
  288. begin
  289. s := UpperCase(PasType.Name);
  290. if (s = 'BYTE') or (s = 'SHORTINT') or (S = 'SMALLINT') or
  291. (s = 'INTEGER') or (s = 'LONGINT') or (s = 'CARDINAL') or
  292. (s = 'INT64') or (s = 'QUADWORD') then
  293. Result := 'Int'
  294. else if (s = 'BOOLEAN') or (s = 'WORDBOOL') or (s = 'LONGBOOL') then
  295. Result := 'Boolean'
  296. else if s = 'STRING' then
  297. Result := 'String'
  298. else if (s = 'FLOAT') or (s = 'SINGLE') or (s = 'DOUBLE') or
  299. (s = 'EXTENDED') then
  300. Result := 'Double'
  301. else if s = 'TDATETIME' then
  302. Result := 'DateTime';
  303. end;
  304. if Length(Result) = 0 then
  305. raise Exception.Create('Argument type not supported: ' +
  306. PasType.ClassName + ' ' + PasType.Name);
  307. end;
  308. function NeedLocalProc(const ProcName: String;
  309. Referrer: TPasProcedureImpl): TPasProcedureImpl;
  310. var
  311. i, j: Integer;
  312. begin
  313. for i := 0 to Method.Locals.Count - 1 do
  314. begin
  315. Result := TPasProcedureImpl(Method.Locals[i]);
  316. if Result.Name = ProcName then
  317. begin
  318. j := Method.Locals.IndexOf(Referrer);
  319. if (j >= 0) and (i >= j) then
  320. begin
  321. // Move existing converter to the top and exit
  322. Method.Locals.Delete(i);
  323. j := Method.Locals.IndexOf(ProcImpl);
  324. if j < 0 then
  325. j := 0;
  326. Method.Locals.Insert(j, Result);
  327. end;
  328. exit;
  329. end;
  330. end;
  331. Result := nil;
  332. end;
  333. function MakeStructConverter(AClass: TPasClassType;
  334. Referrer: TPasProcedureImpl): TPasProcedureImpl;
  335. var
  336. ConverterName, s: String;
  337. Commands: TPasImplCommands;
  338. i: Integer;
  339. LocalMember: TPasElement;
  340. ConversionInfo: TConversionInfo;
  341. begin
  342. ConverterName := 'Convert' + AClass.Name;
  343. Result := NeedLocalProc(ConverterName, Referrer);
  344. if Assigned(Result) then
  345. exit;
  346. Result := TPasProcedureImpl.Create(ConverterName, Method);
  347. i := Method.Locals.IndexOf(Referrer);
  348. if i < 0 then
  349. i := 0;
  350. Method.Locals.Insert(i, Result);
  351. Result.ProcType := TPasFunctionType.Create('', Result);
  352. Result.ProcType.CreateArgument('Inst', AClass.Name);
  353. TPasFunctionType(Result.ProcType).ResultEl :=
  354. TPasResultElement.Create('', Result);
  355. TPasFunctionType(Result.ProcType).ResultEl.ResultType :=
  356. TPasUnresolvedTypeRef.Create('TXMLRPCStruct', Result);
  357. Result.Body := TPasImplBlock.Create('', Result);
  358. Commands := Result.Body.AddCommands;
  359. Commands.Commands.Add('Result := AWriter.CreateStruct');
  360. for i := 0 to AClass.Members.Count - 1 do
  361. begin
  362. LocalMember := TPasElement(AClass.Members[i]);
  363. if LocalMember.ClassType = TPasProperty then
  364. begin
  365. ConversionInfo := GetConversionInfo(LocalMember, Result);
  366. if ConversionInfo.ArgIsParent then
  367. s := 'Inst'
  368. else
  369. s := 'Inst.' + LocalMember.Name;
  370. s := 'AWriter.AddStructMember(Result, ''' + LocalMember.Name + ''', ' +
  371. MakeAccessor(ConversionInfo, s, '') + ')';
  372. Commands.Commands.Add(s);
  373. end;
  374. end;
  375. end;
  376. function MakeArrayConverter(Member, ArraySizeProp: TPasProperty;
  377. ProcessProc, Referrer: TPasProcedureImpl): TPasProcedureImpl;
  378. var
  379. i: Integer;
  380. ConverterName, s: String;
  381. Commands: TPasImplCommands;
  382. VarMember: TPasVariable;
  383. ForLoop: TPasImplForLoop;
  384. ConversionInfo: TConversionInfo;
  385. begin
  386. ConverterName := 'Convert' + Member.Parent.Name + '_' + Member.Name;
  387. Result := NeedLocalProc(ConverterName, Referrer);
  388. if Assigned(Result) then
  389. exit;
  390. Result := TPasProcedureImpl.Create(ConverterName, Method);
  391. i := Method.Locals.IndexOf(Referrer);
  392. if i < 0 then
  393. i := 0;
  394. Method.Locals.Insert(i, Result);
  395. Result.ProcType := TPasFunctionType.Create('', Result);
  396. Result.ProcType.CreateArgument('Inst', Member.Parent.Name);
  397. TPasFunctionType(Result.ProcType).ResultEl :=
  398. TPasResultElement.Create('', Result);
  399. TPasFunctionType(Result.ProcType).ResultEl.ResultType :=
  400. TPasUnresolvedTypeRef.Create('TXMLRPCArray', Result);
  401. Result.Body := TPasImplBlock.Create('', Result);
  402. Commands := Result.Body.AddCommands;
  403. Commands.Commands.Add('Result := AWriter.CreateArray');
  404. VarMember := TPasVariable.Create('i', Result);
  405. Result.Locals.Add(VarMember);
  406. VarMember.VarType := TPasUnresolvedTypeRef.Create('Integer', VarMember);
  407. ForLoop := Result.Body.AddForLoop(TPasVariable.Create('i', Result),
  408. '0', MethodPrefix + ArraySizeProp.Name + ' - 1');
  409. ForLoop.Body := TPasImplCommand.Create('', ForLoop);
  410. ConversionInfo := GetConversionInfo(Member.VarType, Result);
  411. if ConversionInfo.ArgIsParent then
  412. s := 'Inst'
  413. else
  414. s := 'Inst.' + Member.Name + '[i]';
  415. s := 'AWriter.AddArrayElement(Result, ' +
  416. MakeAccessor(ConversionInfo, s, '') + ')';
  417. TPasImplCommand(ForLoop.Body).Command := s;
  418. end;
  419. function CreateDispatcher(VarType: TPasClassType;
  420. Referrer: TPasProcedureImpl): TPasProcedureImpl;
  421. var
  422. DispatcherName: String;
  423. begin
  424. DispatcherName := 'Dispatch' + VarType.Name;
  425. Result := NeedLocalProc(DispatcherName, Referrer);
  426. if Assigned(Result) then
  427. exit;
  428. // Create new dispatcher method
  429. Result := TPasProcedureImpl.Create(DispatcherName, Method);
  430. if ProcImpl = Method then
  431. Method.Locals.Insert(0, Result)
  432. else
  433. Method.Locals.Insert(Method.Locals.IndexOf(Referrer), Result);
  434. Result.ProcType := TPasProcedureType.Create('', Result);
  435. Result.ProcType.CreateArgument('Inst', VarType.Name);
  436. Result.ProcType.CreateArgument('Level', 'Integer');
  437. WriteClassServerSource(VarType,
  438. ImplementationSection, Method, Result, 'Inst.', NestingLevel + 1);
  439. end;
  440. var
  441. IfElse, ParentIfElse: TPasImplIfElse;
  442. procedure CreateBranch(const MethodName: String);
  443. begin
  444. if Assigned(ParentIfElse) then
  445. begin
  446. IfElse := TPasImplIfElse.Create('', ParentIfElse);
  447. ParentIfElse.ElseBranch := IfElse;
  448. end else
  449. begin
  450. IfElse := TPasImplIfElse.Create('', ProcImpl.Body);
  451. ProcImpl.Body.Elements.Add(IfElse);
  452. end;
  453. ParentIfElse := IfElse;
  454. IfElse.Condition := 's = ''' + UpperCase(MethodName) + '''';
  455. end;
  456. procedure ProcessMethodCall(Member: TPasProcedure);
  457. function MakeProcArgs(Args: TList): String;
  458. var
  459. i: Integer;
  460. begin
  461. if (not Assigned(Args)) or (Args.Count = 0) then
  462. Result := ''
  463. else
  464. begin
  465. Result := '(';
  466. for i := 0 to Args.Count - 1 do
  467. begin
  468. if i > 0 then
  469. Result := Result + ', ';
  470. Result := Result + 'AParser.GetPrev' + GetParseValueFnName(TPasType(Args[i]));
  471. end;
  472. Result := Result + ')';
  473. end;
  474. end;
  475. var
  476. Commands: TPasImplCommands;
  477. s: String;
  478. begin
  479. CreateBranch(Member.Name);
  480. Commands := TPasImplCommands.Create('', IfElse);
  481. IfElse.IfBranch := Commands;
  482. if TPasProcedure(Member).ProcType.Args.Count > 0 then
  483. Commands.Commands.Add('AParser.ResetValueCursor');
  484. if Member.ClassType = TPasProcedure then
  485. begin
  486. Commands.Commands.Add(MethodPrefix + Member.Name +
  487. MakeProcArgs(TPasProcedure(Member).ProcType.Args));
  488. Commands.Commands.Add('AWriter.WriteResponse(nil)');
  489. end else
  490. begin
  491. // function
  492. s := MethodPrefix + Member.Name +
  493. MakeProcArgs(TPasProcedure(Member).ProcType.Args);
  494. Commands.Commands.Add('AWriter.WriteResponse(' +
  495. MakeAccessor(GetConversionInfo(TPasFunctionType(TPasFunction(Member).
  496. ProcType).ResultEl.ResultType, ProcImpl), s, '') + ')');
  497. end;
  498. end;
  499. procedure ProcessProperty(Member: TPasProperty);
  500. var
  501. LocalIfElse: TPasImplIfElse;
  502. IsArray, IsStruct: Boolean;
  503. s, s2: String;
  504. Commands: TPasImplCommands;
  505. Command: TPasImplCommand;
  506. ConversionInfo: TConversionInfo;
  507. begin
  508. if Member.ReadAccessorName <> '' then
  509. begin
  510. CreateBranch('Get' + Member.Name);
  511. IsArray := (Member.Args.Count = 1) and
  512. Assigned(FindArraySizeProperty(Member));
  513. IsStruct := Member.VarType.ClassType = TPasClassType;
  514. if IsStruct then
  515. s := CreateDispatcher(TPasClassType(Member.VarType), ProcImpl).Name +
  516. '(' + MethodPrefix + Member.Name;
  517. if NestingLevel = 0 then
  518. s2 := '1'
  519. else
  520. s2 := 'Level + 1';
  521. if IsArray or (IsStruct and (Member.Args.Count = 0)) then
  522. begin
  523. LocalIfElse := TPasImplIfElse.Create('', IfElse);
  524. IfElse.IfBranch := LocalIfElse;
  525. LocalIfElse.Condition := 'APath.Count <= ' + s2;
  526. end;
  527. if IsStruct then
  528. if IsArray then
  529. begin
  530. LocalIfElse.IfBranch := TPasImplCommand.Create('', LocalIfElse);
  531. TPasImplCommand(LocalIfElse.IfBranch).Command :=
  532. 'AWriter.WriteResponse(' +
  533. MakeAccessor(GetConversionInfo(Member, ProcImpl),
  534. Copy(MethodPrefix, 1, Length(MethodPrefix) - 1), '') + ')';
  535. LocalIfElse.ElseBranch := TPasImplCommand.Create('', LocalIfElse);
  536. TPasImplCommand(LocalIfElse.ElseBranch).Command :=
  537. s + '[AParser.GetNext' +
  538. GetParseValueFnName(TPasArgument(Member.Args[0]).ArgType) + '], ' +
  539. s2 + ')';
  540. end else
  541. begin
  542. if Member.Args.Count = 0 then
  543. begin
  544. LocalIfElse.IfBranch := TPasImplCommand.Create('', LocalIfElse);
  545. TPasImplCommand(LocalIfElse.IfBranch).Command :=
  546. 'AWriter.WriteResponse(' +
  547. MakeAccessor(GetConversionInfo(Member, ProcImpl),
  548. MethodPrefix + Member.Name, '') + ')';
  549. LocalIfElse.ElseBranch := TPasImplCommand.Create('', LocalIfElse);
  550. TPasImplCommand(LocalIfElse.ElseBranch).Command := s + ', ' + s2 + ')';
  551. end else
  552. begin
  553. IfElse.IfBranch := TPasImplCommand.Create('', IfElse);
  554. TPasImplCommand(IfElse.IfBranch).Command := s + '[AParser.GetNext' +
  555. GetParseValueFnName(TPasArgument(Member.Args[0]).ArgType) + '], ' +
  556. s2 + ')';
  557. end;
  558. end
  559. else if IsArray then
  560. begin
  561. LocalIfElse.IfBranch := TPasImplCommand.Create('', LocalIfElse);
  562. TPasImplCommand(LocalIfElse.IfBranch).Command :=
  563. 'AWriter.WriteResponse(' +
  564. MakeAccessor(GetConversionInfo(Member, ProcImpl),
  565. Copy(MethodPrefix, 1, Length(MethodPrefix) - 1), '') + ')';
  566. LocalIfElse.ElseBranch := TPasImplCommand.Create('', LocalIfElse);
  567. TPasImplCommand(LocalIfElse.ElseBranch).Command :=
  568. 'AWriter.WriteResponse(' +
  569. MakeAccessor(GetConversionInfo(Member.VarType, ProcImpl),
  570. MethodPrefix + Member.Name, 'AParser.GetNext' +
  571. GetParseValueFnName(TPasArgument(Member.Args[0]).ArgType)) + ')';
  572. end else
  573. begin
  574. IfElse.IfBranch := TPasImplCommand.Create('', IfElse);
  575. TPasImplCommand(IfElse.IfBranch).Command := 'AWriter.WriteResponse(' +
  576. MakeAccessor(GetConversionInfo(Member.VarType, ProcImpl),
  577. MethodPrefix + Member.Name, '') + ')';
  578. end;
  579. end;
  580. if Member.WriteAccessorName <> '' then
  581. begin
  582. CreateBranch('Set' + Member.Name);
  583. Commands := TPasImplCommands.Create('', IfElse);
  584. IfElse.IfBranch := Commands;
  585. Commands.Commands.Add('// Not supported by mkxmlrpc yet');
  586. end;
  587. end;
  588. var
  589. VarMember: TPasVariable;
  590. i: Integer;
  591. Command: TPasImplCommand;
  592. Member: TPasElement;
  593. begin
  594. VarMember := TPasVariable.Create('s', ProcImpl);
  595. ProcImpl.Locals.Add(VarMember);
  596. VarMember.VarType := TPasUnresolvedTypeRef.Create('String', VarMember);
  597. ProcImpl.Body := TPasImplBlock.Create('', ProcImpl);
  598. if NestingLevel = 0 then
  599. ProcImpl.Body.AddCommand('s := APath[' + IntToStr(NestingLevel) + ']')
  600. else
  601. ProcImpl.Body.AddCommand('s := APath[Level]');
  602. ParentIfElse := nil;
  603. for i := 0 to ServerClass.Members.Count - 1 do
  604. begin
  605. Member := TPasElement(ServerClass.Members[i]);
  606. if Member.Visibility <> visPublic then
  607. continue;
  608. if (Member.ClassType = TPasProcedure) or (Member.ClassType = TPasFunction)
  609. then
  610. ProcessMethodCall(TPasProcedure(Member))
  611. else if Member.ClassType = TPasProperty then
  612. ProcessProperty(TPasProperty(Member))
  613. else if (Member.ClassType <> TPasConstructor) and
  614. (Member.ClassType <> TPasDestructor) then
  615. WriteLn('Warning: Unsupportet member type: ', Member.ElementTypeName);
  616. end;
  617. if Assigned(ParentIfElse) then
  618. begin
  619. Command := TPasImplCommand.Create('', ParentIfElse);
  620. ParentIfElse.ElseBranch := Command;
  621. end else
  622. begin
  623. Command := TPasImplCommand.Create('', ProcImpl.Body);
  624. ProcImpl.Body.Elements.Add(Command);
  625. end;
  626. Command.Command := 'AWriter.WriteFaultResponse(2, ''Invalid method name'')';
  627. end;
  628. procedure WriteFPCServerSource;
  629. var
  630. i, j: Integer;
  631. Module: TPasModule;
  632. InterfaceSection, ImplementationSection: TPasSection;
  633. VarMember: TPasVariable;
  634. PropertyMember: TPasProperty;
  635. ProcMember: TPasProcedure;
  636. Arg: TPasArgument;
  637. ServerClass: TPasClassType;
  638. Stream: TStream;
  639. ProcImpl: TPasProcedureImpl;
  640. Found: Boolean;
  641. begin
  642. Module := TPasModule.Create(UnitName, nil);
  643. try
  644. InterfaceSection := TPasSection.Create('', Module);
  645. Module.InterfaceSection := InterfaceSection;
  646. ImplementationSection := TPasSection.Create('', Module);
  647. Module.ImplementationSection := ImplementationSection;
  648. InterfaceSection.AddUnitToUsesList('Classes');
  649. InterfaceSection.AddUnitToUsesList('XMLRPC');
  650. for i := 0 to RPCList.UsedModules.Count - 1 do
  651. InterfaceSection.AddUnitToUsesList(RPCList.UsedModules[i]);
  652. for i := 0 to RPCList.ServerClasses.Count - 1 do
  653. with TServerClass(RPCList.ServerClasses[i]) do
  654. begin
  655. ServerClass := TPasClassType.Create('T' + ImplName + 'XMLRPCServlet',
  656. InterfaceSection);
  657. InterfaceSection.Declarations.Add(ServerClass);
  658. ServerClass.ObjKind := okClass;
  659. ServerClass.AncestorType :=
  660. TPasUnresolvedTypeRef.Create('TXMLRPCServlet', ServerClass);
  661. // Create private field which holds the implementation instance
  662. VarMember := TPasVariable.Create('F' + ImplName, ServerClass);
  663. VarMember.Visibility := visPrivate;
  664. VarMember.VarType := TPasUnresolvedTypeRef.Create(Element.Name, VarMember);
  665. ServerClass.Members.Add(VarMember);
  666. // Create dispatcher method
  667. ProcMember := TPasProcedure.Create('Dispatch', ServerClass);
  668. ProcMember.Visibility := visProtected;
  669. ProcMember.AddModifier(pmOverride);
  670. ProcMember.ProcType := TPasProcedureType.Create('', ProcMember);
  671. ProcMember.ProcType.CreateArgument('AParser', 'TXMLRPCParser').
  672. Visibility := visPublic;
  673. ProcMember.ProcType.CreateArgument('AWriter', 'TXMLRPCWriter').
  674. Visibility := visPublic;
  675. ProcMember.ProcType.CreateArgument('APath', 'TStrings').
  676. Visibility := visPublic;
  677. ServerClass.Members.Add(ProcMember);
  678. // Create published property for implementation instance
  679. PropertyMember := TPasProperty.Create(ImplName, ServerClass);
  680. PropertyMember.Visibility := visPublished;
  681. PropertyMember.VarType := VarMember.VarType;
  682. VarMember.VarType.AddRef;
  683. PropertyMember.ReadAccessorName := 'F' + ImplName;
  684. PropertyMember.WriteAccessorName := 'F' + ImplName;
  685. ServerClass.Members.Add(PropertyMember);
  686. // Create dispatcher implementation
  687. ProcImpl := TPasProcedureImpl.Create('Dispatch', ServerClass);
  688. ImplementationSection.Declarations.Add(ProcImpl);
  689. ProcImpl.ProcType := ProcMember.ProcType;
  690. ProcMember.ProcType.AddRef;
  691. ProcImpl.ProcType.AddRef;
  692. WriteClassServerSource(Element, ImplementationSection, ProcImpl,
  693. ProcImpl, ImplName + '.', 0);
  694. end;
  695. for i := 0 to Engine.UsedModules.Count - 1 do
  696. begin
  697. Found := False;
  698. for j := 0 to RPCList.UsedModules.Count - 1 do
  699. if CompareText(RPCList.UsedModules[j],
  700. TPasModule(Engine.UsedModules[i]).Name) = 0 then
  701. begin
  702. Found := True;
  703. break;
  704. end;
  705. if not Found then
  706. ImplementationSection.AddUnitToUsesList(
  707. TPasModule(Engine.UsedModules[i]).Name);
  708. end;
  709. Stream := THandleStream.Create(StdOutputHandle);
  710. try
  711. WritePasFile(Module, Stream);
  712. finally
  713. Stream.Free;
  714. end;
  715. Stream := TFileStream.Create(OutputFilename, fmCreate);
  716. try
  717. WritePasFile(Module, Stream);
  718. finally
  719. Stream.Free;
  720. end;
  721. finally
  722. Module.Free;
  723. end;
  724. end;
  725. var
  726. i, j: Integer;
  727. s, Cmd, Arg: String;
  728. InputFiles, ClassList: TStringList;
  729. begin
  730. InputFiles := TStringList.Create;
  731. ClassList := TStringList.Create;
  732. try
  733. for i := 1 to ParamCount do
  734. begin
  735. s := ParamStr(i);
  736. j := Pos('=', s);
  737. if j > 0 then
  738. begin
  739. Cmd := Copy(s, 1, j - 1);
  740. Arg := Copy(s, j + 1, Length(s));
  741. end else
  742. begin
  743. Cmd := s;
  744. SetLength(Arg, 0);
  745. end;
  746. if (Cmd = '-i') or (Cmd = '--input') then
  747. InputFiles.Add(Arg)
  748. else if Cmd = '--output' then
  749. OutputFilename := Arg
  750. else if Cmd = '--unitname' then
  751. UnitName := Arg
  752. else if Cmd = '--serverclass' then
  753. ClassList.Add(Arg)
  754. else
  755. WriteLn(StdErr, Format(SCmdLineInvalidOption, [s]));
  756. end;
  757. if ClassList.Count = 0 then
  758. begin
  759. WriteLn(StdErr, SNoServerClassNameProvided);
  760. Halt(2);
  761. end;
  762. if UnitName = '' then
  763. begin
  764. WriteLn(StdErr, SNoUnitNameProvided);
  765. Halt(2);
  766. end;
  767. Engine := TParserEngine.Create;
  768. try
  769. // Engine.SetPackageName('XMLRPC');
  770. for i := 0 to InputFiles.Count - 1 do
  771. ParseSource(Engine, InputFiles[i], '', '');
  772. RPCList := TRPCList.Create;
  773. try
  774. for i := 0 to ClassList.Count - 1 do
  775. RPCList.AddServerClass(ClassList[i]);
  776. WriteFPCServerSource;
  777. finally
  778. RPCList.Free;
  779. end;
  780. finally
  781. Engine.Free;
  782. end;
  783. finally
  784. InputFiles.Free;
  785. ClassList.Free;
  786. end;
  787. end.