mkxmlrpc.pp 27 KB

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