ppuparser.pas 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638
  1. {
  2. pas2jni - JNI bridge generator for Pascal.
  3. Copyright (c) 2013 by Yury Sidorov.
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15. ****************************************************************************}
  16. unit ppuparser;
  17. {$mode objfpc}{$H+}
  18. interface
  19. uses
  20. Classes, SysUtils, def;
  21. type
  22. TCheckItemResult = (crDefault, crInclude, crExclude);
  23. TOnCheckItem = function (const ItemName: string): TCheckItemResult of object;
  24. { TPPUParser }
  25. TPPUParser = class
  26. private
  27. FOnCheckItem: TOnCheckItem;
  28. function FindUnit(const AName: string): string;
  29. function ReadUnit(const AName: string): string;
  30. function InternalParse(const AUnitName: string): TUnitDef;
  31. public
  32. SearchPath: TStringList;
  33. Units: TDef;
  34. constructor Create(const ASearchPath: string);
  35. destructor Destroy; override;
  36. procedure Parse(const AUnitName: string);
  37. property OnCheckItem: TOnCheckItem read FOnCheckItem write FOnCheckItem;
  38. end;
  39. var
  40. ppudumpprog: string;
  41. implementation
  42. uses process, pipes, fpjson, jsonparser;
  43. type
  44. TCharSet = set of char;
  45. function WordPosition(const N: Integer; const S: string;
  46. const WordDelims: TCharSet): Integer;
  47. var
  48. Count, I: Integer;
  49. begin
  50. Count := 0;
  51. I := 1;
  52. Result := 0;
  53. while (I <= Length(S)) and (Count <> N) do
  54. begin
  55. { skip over delimiters }
  56. while (I <= Length(S)) and (S[I] in WordDelims) do
  57. Inc(I);
  58. { if we're not beyond end of S, we're at the start of a word }
  59. if I <= Length(S) then
  60. Inc(Count);
  61. { if not finished, find the end of the current word }
  62. if Count <> N then
  63. while (I <= Length(S)) and not (S[I] in WordDelims) do
  64. Inc(I)
  65. else
  66. Result := I;
  67. end;
  68. end;
  69. function ExtractWord(N: Integer; const S: string;
  70. const WordDelims: TCharSet): string;
  71. var
  72. I: Integer;
  73. Len: Integer;
  74. begin
  75. Len := 0;
  76. I := WordPosition(N, S, WordDelims);
  77. if I <> 0 then
  78. { find the end of the current word }
  79. while (I <= Length(S)) and not (S[I] in WordDelims) do
  80. begin
  81. { add the I'th character to result }
  82. Inc(Len);
  83. SetLength(Result, Len);
  84. Result[Len] := S[I];
  85. Inc(I);
  86. end;
  87. SetLength(Result, Len);
  88. end;
  89. { TPPUParser }
  90. constructor TPPUParser.Create(const ASearchPath: string);
  91. var
  92. i, j: integer;
  93. s, d: string;
  94. sr: TSearchRec;
  95. begin
  96. SearchPath:=TStringList.Create;
  97. SearchPath.Delimiter:=';';
  98. SearchPath.DelimitedText:=ASearchPath;
  99. i:=0;
  100. while i < SearchPath.Count do begin
  101. s:=SearchPath[i];
  102. if (Pos('*', s) > 0) or (Pos('?', s) > 0) then begin
  103. d:=ExtractFilePath(s);
  104. j:=FindFirst(s, faDirectory, sr);
  105. while j = 0 do begin
  106. if (sr.Name <> '.') and (sr.Name <> '..') then
  107. SearchPath.Add(d + sr.Name);
  108. j:=FindNext(sr);
  109. end;
  110. FindClose(sr);
  111. SearchPath.Delete(i);
  112. end
  113. else
  114. Inc(i);
  115. end;
  116. Units:=TDef.Create(nil, dtNone);
  117. end;
  118. destructor TPPUParser.Destroy;
  119. begin
  120. Units.Free;
  121. SearchPath.Free;
  122. inherited Destroy;
  123. end;
  124. procedure TPPUParser.Parse(const AUnitName: string);
  125. begin
  126. InternalParse(AUnitName);
  127. end;
  128. function TPPUParser.FindUnit(const AName: string): string;
  129. var
  130. i: integer;
  131. fn: string;
  132. begin
  133. fn:=ChangeFileExt(LowerCase(AName), '.ppu');
  134. if FileExists(fn) then begin
  135. Result:=fn;
  136. exit;
  137. end;
  138. for i:=0 to SearchPath.Count - 1 do begin
  139. Result:=IncludeTrailingPathDelimiter(SearchPath[i]) + fn;
  140. if FileExists(Result) then
  141. exit;
  142. end;
  143. raise Exception.CreateFmt('Unable to find PPU file for unit "%s".', [AName]);
  144. end;
  145. function TPPUParser.ReadUnit(const AName: string): string;
  146. procedure _ReadOutput(o: TInputPipeStream; var s: string);
  147. var
  148. i, j: integer;
  149. begin
  150. with o do
  151. while NumBytesAvailable > 0 do begin
  152. i:=NumBytesAvailable;
  153. j:=Length(s);
  154. SetLength(s, j + i);
  155. ReadBuffer(s[j + 1], i);
  156. end;
  157. end;
  158. var
  159. p: TProcess;
  160. s, un, err: ansistring;
  161. ec: integer;
  162. begin
  163. un:=FindUnit(AName);
  164. p:=TProcess.Create(nil);
  165. try
  166. if ppudumpprog = '' then begin
  167. ppudumpprog:='ppudump';
  168. // Check for ppudump in the same folder as pas2jni
  169. s:=ExtractFilePath(ParamStr(0));
  170. if s <> '' then begin
  171. s:=s + ppudumpprog + ExtractFileExt(ParamStr(0));
  172. if FileExists(s) then
  173. ppudumpprog:=s;
  174. end;
  175. end;
  176. p.Executable:=ppudumpprog;
  177. p.Parameters.Add('-Fj');
  178. p.Parameters.Add(un);
  179. p.Options:=[poUsePipes, poNoConsole];
  180. p.ShowWindow:=swoHIDE;
  181. p.StartupOptions:=[suoUseShowWindow];
  182. try
  183. p.Execute;
  184. except
  185. raise Exception.CreateFmt('Unable to run "%s".'+LineEnding+'%s', [p.Executable, Exception(ExceptObject).Message]);
  186. end;
  187. s:='';
  188. err:='';
  189. repeat
  190. _ReadOutput(p.Output, s);
  191. _ReadOutput(p.Stderr, err);
  192. until not p.Running;
  193. ec:=p.ExitStatus;
  194. if Copy(s, 1, 1) <> '[' then begin
  195. ec:=-1;
  196. err:='Output of ppudump is not in JSON format.' + LineEnding + 'Probably old version of ppudump has been used.';
  197. end;
  198. if ec <> 0 then begin
  199. if err = '' then
  200. if Length(s) < 300 then
  201. err:=s;
  202. raise Exception.CreateFmt('Error reading contents of unit "%s" using "%s".'+LineEnding+'Error code: %d'+LineEnding+'%s', [un, ppudumpprog, ec, err]);
  203. end;
  204. finally
  205. p.Free;
  206. end;
  207. Result:=s;
  208. {$ifopt D+}
  209. // Lines.SaveToFile(AName + '-dump.txt');
  210. {$endif}
  211. end;
  212. function TPPUParser.InternalParse(const AUnitName: string): TUnitDef;
  213. var
  214. junit: TJSONObject;
  215. jp: TJSONParser;
  216. deref: array of TUnitDef;
  217. CurUnit: TUnitDef;
  218. IsSystemUnit: boolean;
  219. AMainUnit: boolean;
  220. CurObjName: string;
  221. function _GetRef(Ref: TJSONObject; ExpectedClass: TDefClass = nil): TDef;
  222. var
  223. j: integer;
  224. u: TUnitDef;
  225. begin
  226. Result:=nil;
  227. if Ref = nil then
  228. exit;
  229. u:=CurUnit;
  230. j:=Ref.Get('Unit', -1);
  231. if j >= 0 then begin
  232. u:=deref[j];
  233. if u.DefType = dtNone then begin
  234. // Reading unit
  235. u:=InternalParse(LowerCase(u.Name));
  236. if u = nil then
  237. exit;
  238. if u.CPU <> CurUnit.CPU then
  239. raise Exception.CreateFmt('Invalid target CPU of unit "%s": %s', [u.Name, u.CPU]);
  240. if u.OS <> CurUnit.OS then
  241. raise Exception.CreateFmt('Invalid target OS of unit "%s": %s', [u.Name, u.OS]);
  242. if u.PPUVer <> CurUnit.PPUVer then
  243. raise Exception.CreateFmt('Invalid PPU version of unit "%s": %s', [u.Name, u.PPUVer]);
  244. deref[j].Free;
  245. deref[j]:=u;
  246. end;
  247. end;
  248. j:=Ref.Integers['Id'];
  249. Result:=u.FindDef(j);
  250. if Result = nil then begin
  251. if ExpectedClass <> nil then
  252. Result:=ExpectedClass.Create(u, dtNone)
  253. else
  254. Result:=TDef.Create(u, dtNone);
  255. Result.DefId:=j;
  256. end;
  257. if (ExpectedClass <> nil) and (Result <> nil) then
  258. if (Result.DefType <> dtNone) and not (Result is ExpectedClass) then
  259. raise Exception.CreateFmt('Unexpected class. Expected: %s, got: %s', [ExpectedClass.ClassName, Result.ClassName]);
  260. end;
  261. procedure _ReadDefs(CurDef: TDef; jobj: TJSONObject; const ItemsName: string);
  262. var
  263. i, j: integer;
  264. jt, s: string;
  265. d: TDef;
  266. it: TJSONObject;
  267. jarr, arr: TJSONArray;
  268. begin
  269. jarr:=jobj.Get(ItemsName, TJSONArray(nil));
  270. if jarr = nil then
  271. exit;
  272. with jarr do
  273. for i:=0 to Count - 1 do begin
  274. it:=Objects[i];
  275. CurObjName:=it.Get('Name', '');
  276. jt:=it.Strings['Type'];
  277. if jt = 'obj' then begin
  278. if it.Strings['ObjType'] <> 'class' then
  279. continue;
  280. d:=TClassDef.Create(CurDef, dtClass);
  281. end
  282. else
  283. if jt = 'rec' then begin
  284. if IsSystemUnit and (CompareText(CurObjName, 'tguid') = 0) then begin
  285. d:=TTypeDef.Create(CurDef, dtType);
  286. TTypeDef(d).BasicType:=btGuid;
  287. end
  288. else
  289. d:=TRecordDef.Create(CurDef, dtRecord);
  290. end
  291. else
  292. if jt = 'proc' then
  293. d:=TProcDef.Create(CurDef, dtProc)
  294. else
  295. if jt = 'proctype' then begin
  296. d:=TProcDef.Create(CurDef, dtProcType);
  297. TProcDef(d).ProcType:=ptProcedure;
  298. end
  299. else
  300. if jt = 'param' then begin
  301. d:=TVarDef.Create(CurDef, dtParam);
  302. TVarDef(d).VarOpt:=[voRead];
  303. end
  304. else
  305. if jt = 'prop' then begin
  306. d:=TVarDef.Create(CurDef, dtProp);
  307. TVarDef(d).VarOpt:=[];
  308. end
  309. else
  310. if jt = 'field' then
  311. d:=TVarDef.Create(CurDef, dtField)
  312. else
  313. if jt = 'var' then
  314. d:=TVarDef.Create(CurDef, dtVar)
  315. else
  316. if jt = 'ord' then begin
  317. d:=TTypeDef.Create(CurDef, dtType);
  318. with TTypeDef(d) do begin
  319. s:=it.Strings['OrdType'];
  320. j:=it.Get('Size', 0);
  321. if s = 'void' then
  322. BasicType:=btVoid
  323. else
  324. if s = 'uint' then begin
  325. case j of
  326. 1: BasicType:=btByte;
  327. 2: BasicType:=btWord;
  328. 4: BasicType:=btLongWord;
  329. else BasicType:=btInt64;
  330. end;
  331. end
  332. else
  333. if s = 'sint' then begin
  334. case j of
  335. 1: BasicType:=btShortInt;
  336. 2: BasicType:=btSmallInt;
  337. 4: BasicType:=btLongInt;
  338. else BasicType:=btInt64;
  339. end;
  340. end
  341. else
  342. if (s = 'pasbool') or (s = 'bool') then
  343. BasicType:=btBoolean
  344. else
  345. if s = 'char' then begin
  346. if j = 1 then
  347. BasicType:=btChar
  348. else
  349. BasicType:=btWideChar;
  350. end
  351. else
  352. if s = 'currency' then
  353. BasicType:=btDouble;
  354. end;
  355. end
  356. else
  357. if jt = 'float' then begin
  358. d:=TTypeDef.Create(CurDef, dtType);
  359. with TTypeDef(d) do
  360. if it.Strings['FloatType'] = 'single' then
  361. BasicType:=btSingle
  362. else
  363. BasicType:=btDouble;
  364. end
  365. else
  366. if jt = 'string' then begin
  367. d:=TTypeDef.Create(CurDef, dtType);
  368. s:=it.Strings['StrType'];
  369. with TTypeDef(d) do
  370. if (s = 'wide') or (s = 'unicode') or (s = 'long') then
  371. BasicType:=btWideString
  372. else
  373. BasicType:=btString;
  374. if not (IsSystemUnit and (CompareText(CurObjName, 'rawbytestring') = 0)) then
  375. CurObjName:=s + 'string';
  376. end
  377. else
  378. if jt = 'enum' then begin
  379. d:=TTypeDef.Create(CurDef, dtEnum);
  380. TTypeDef(d).BasicType:=btEnum;
  381. end
  382. else
  383. if jt = 'set' then
  384. d:=TSetDef.Create(CurDef, dtSet)
  385. else
  386. if jt = 'ptr' then begin
  387. d:=TTypeDef.Create(CurDef, dtType);
  388. TTypeDef(d).BasicType:=btPointer;
  389. end
  390. else
  391. if jt = 'const' then
  392. d:=TConstDef.Create(CurDef, dtConst)
  393. else
  394. continue;
  395. if (CurObjName = '') and (d.DefType <> dtEnum) then begin
  396. d.Free;
  397. continue;
  398. end;
  399. // Common def attributes
  400. d.Name:=CurObjName;
  401. d.DefId:=it.Get('Id', -1);
  402. d.SymId:=it.Get('SymId', -1);
  403. s:=it.Get('Visibility', '');
  404. d.IsPrivate:=(s <> '') and (s <> 'public') and (s <> 'published');
  405. if Copy(d.Name, 1, 1) = '$' then
  406. d.IsPrivate:=True;
  407. // Specific def attributes
  408. case d.DefType of
  409. dtClass:
  410. with TClassDef(d) do begin
  411. AncestorClass:=TClassDef(_GetRef(it.Get('Ancestor', TJSONObject(nil)), TClassDef));
  412. _ReadDefs(d, it, 'Fields');
  413. end;
  414. dtRecord:
  415. with TRecordDef(d) do begin
  416. Size:=it.Integers['Size'];
  417. _ReadDefs(d, it, 'Fields');
  418. end;
  419. dtProc, dtProcType:
  420. with TProcDef(d) do begin
  421. arr:=it.Get('Options', TJSONArray(nil));
  422. if arr <> nil then
  423. for j:=0 to arr.Count - 1 do begin
  424. s:=arr.Strings[j];
  425. if s = 'procedure' then
  426. ProcType:=ptProcedure
  427. else
  428. if s = 'function' then
  429. ProcType:=ptFunction
  430. else
  431. if s = 'constructor' then begin
  432. ProcType:=ptConstructor;
  433. if CompareText(Name, 'create') = 0 then
  434. Name:='Create'; // fix char case for standard constructors
  435. end
  436. else
  437. if s = 'destructor' then
  438. ProcType:=ptDestructor
  439. else
  440. if s = 'overriding' then begin
  441. ProcType:=ptDestructor;
  442. ProcOpt:=ProcOpt + [poOverride];
  443. if ProcType <> ptConstructor then
  444. IsPrivate:=True;
  445. end
  446. else
  447. if s = 'overload' then
  448. ProcOpt:=ProcOpt + [poOverload]
  449. else
  450. if s = 'abstract' then
  451. TClassDef(Parent).HasAbstractMethods:=True;
  452. end;
  453. ReturnType:=_GetRef(it.Get('RetType', TJSONObject(nil)));
  454. if (DefType = dtProcType) and not ( (ReturnType is TTypeDef) and (TTypeDef(ReturnType).BasicType = btVoid) ) then
  455. ProcType:=ptFunction;
  456. if it.Get('MethodPtr', False) then
  457. ProcOpt:=ProcOpt + [poMethodPtr];
  458. if IsSystemUnit and (ProcType = ptFunction) and (Name = 'int') then
  459. Name:='Int';
  460. _ReadDefs(d, it, 'Params');
  461. end;
  462. dtVar, dtField, dtParam:
  463. with TVarDef(d) do begin
  464. VarType:=_GetRef(it.Objects['VarType']);
  465. s:=it.Get('Spez', '');
  466. if s = 'out' then
  467. VarOpt:=[voWrite, voOut]
  468. else
  469. if s = 'var' then
  470. VarOpt:=[voRead, voWrite, voVar]
  471. else
  472. if s = 'const' then
  473. VarOpt:=[voRead, voConst];
  474. end;
  475. dtProp:
  476. with TVarDef(d) do begin
  477. VarType:=_GetRef(it.Objects['PropType']);
  478. if it.Get('Getter', TJSONObject(nil)) <> nil then
  479. VarOpt:=VarOpt + [voRead];
  480. if it.Get('Setter', TJSONObject(nil)) <> nil then
  481. VarOpt:=VarOpt + [voWrite];
  482. _ReadDefs(d, it, 'Params');
  483. end;
  484. dtEnum:
  485. _ReadDefs(d, it, 'Elements');
  486. dtSet:
  487. with TSetDef(d) do begin
  488. Size:=it.Integers['Size'];
  489. Base:=it.Integers['Base'];
  490. ElMax:=it.Integers['Max'];
  491. ElType:=TTypeDef(_GetRef(it.Objects['ElType'], TTypeDef));
  492. if (ElType <> nil) and (ElType.Name = '') then
  493. ElType.Name:=CurObjName + 'El';
  494. end;
  495. dtConst:
  496. with TConstDef(d) do begin
  497. VarType:=_GetRef(it.Get('TypeRef', TJSONObject(nil)));
  498. s:=it.Strings['ValType'];
  499. if s = 'int' then
  500. Value:=IntToStr(it.Int64s['Value'])
  501. else
  502. if s = 'float' then begin
  503. Str(it.Floats['Value'], s);
  504. Value:=s;
  505. end
  506. else
  507. if s = 'string' then begin
  508. s:=it.Strings['Value'];
  509. s:=StringReplace(s, '\', '\\', [rfReplaceAll]);
  510. s:=StringReplace(s, '"', '\"', [rfReplaceAll]);
  511. s:=StringReplace(s, #9, '\t', [rfReplaceAll]);
  512. s:=StringReplace(s, #10, '\n', [rfReplaceAll]);
  513. s:=StringReplace(s, #13, '\r', [rfReplaceAll]);
  514. Value:='"' + s + '"';
  515. end
  516. else
  517. FreeAndNil(d);
  518. end;
  519. end;
  520. end;
  521. end;
  522. var
  523. i, j: integer;
  524. s: string;
  525. begin
  526. Result:=nil;
  527. for i:=0 to Units.Count - 1 do
  528. if CompareText(Units[i].Name, AUnitName) = 0 then begin
  529. Result:=TUnitDef(Units[i]);
  530. exit;
  531. end;
  532. AMainUnit:=FOnCheckItem(AUnitName) = crInclude;
  533. if not AMainUnit and ( (CompareText(AUnitName, 'windows') = 0) or (CompareText(AUnitName, 'unix') = 0) ) then begin
  534. Result:=nil;
  535. exit;
  536. end;
  537. s:=ReadUnit(AUnitName);
  538. try
  539. junit:=nil;
  540. try
  541. jp:=TJSONParser.Create(s);
  542. try
  543. junit:=TJSONObject(jp.Parse.Items[0]);
  544. finally
  545. jp.Free;
  546. end;
  547. IsSystemUnit:=CompareText(AUnitName, 'system') = 0;
  548. Result:=TUnitDef.Create(nil, dtUnit);
  549. Units.Add(Result);
  550. Result.Name:=junit.Strings['Name'];
  551. Result.PPUVer:=junit.Integers['Version'];
  552. Result.CPU:=junit.Strings['TargetCPU'];
  553. Result.OS:=junit.Strings['TargetOS'];
  554. Result.IntfCRC:=junit.Strings['InterfaceCRC'];
  555. if junit.Find('Units') <> nil then
  556. with junit.Arrays['Units'] do begin
  557. SetLength(deref, Count);
  558. for i:=0 to Count - 1 do begin
  559. deref[i]:=TUnitDef.Create(nil, dtNone);
  560. deref[i].Name:=Strings[i];
  561. end;
  562. end;
  563. CurUnit:=Result;
  564. _ReadDefs(CurUnit, junit, 'Interface');
  565. Result.ResolveDefs;
  566. if AMainUnit then
  567. Result.IsUsed:=True;
  568. SetLength(Result.UsedUnits, Length(deref));
  569. j:=0;
  570. for i:=0 to High(deref) do
  571. if deref[i].DefType = dtNone then
  572. deref[i].Free
  573. else begin
  574. Result.UsedUnits[j]:=deref[i];
  575. Inc(j);
  576. end;
  577. SetLength(Result.UsedUnits, j);
  578. finally
  579. junit.Free;
  580. end;
  581. except
  582. if CurObjName <> '' then
  583. CurObjName:=Format('; Object: "%s"', [CurObjName]);
  584. raise Exception.CreateFmt('%s' + LineEnding + 'Unit: "%s"%s', [Exception(ExceptObject).Message, AUnitName, CurObjName]);
  585. end;
  586. end;
  587. end.