2
0

classes.inc 55 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241
  1. {
  2. This file is part of the Free Component Library (FCL)
  3. Copyright (c) 1999-2000 by Michael Van Canneyt and Florian Klaempfl
  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. {**********************************************************************
  11. * Class implementations are in separate files. *
  12. **********************************************************************}
  13. type
  14. {$ifdef CPU16}
  15. TFilerFlagsInt = Byte;
  16. {$else CPU16}
  17. TFilerFlagsInt = LongInt;
  18. {$endif CPU16}
  19. var
  20. ClassList : TThreadlist;
  21. ClassAliasList : TStringList;
  22. {
  23. Include all message strings
  24. Add a language with IFDEF LANG_NAME
  25. just befor the final ELSE. This way English will always be the default.
  26. }
  27. {$IFDEF LANG_GERMAN}
  28. {$i constsg.inc}
  29. {$ELSE}
  30. {$IFDEF LANG_SPANISH}
  31. {$i constss.inc}
  32. {$ENDIF}
  33. {$ENDIF}
  34. { Utility routines }
  35. {$i util.inc}
  36. { TBits implementation }
  37. {$i bits.inc}
  38. { All streams implementations: }
  39. { Tstreams THandleStream TFileStream TResourcseStreams TStringStream }
  40. { TCustomMemoryStream TMemoryStream }
  41. {$i streams.inc}
  42. { TParser implementation}
  43. {$i parser.inc}
  44. { TCollection and TCollectionItem implementations }
  45. {$i collect.inc}
  46. { TList and TThreadList implementations }
  47. {$i lists.inc}
  48. { TStrings and TStringList implementations }
  49. {$i stringl.inc}
  50. { TThread implementation }
  51. { system independend threading code }
  52. var
  53. { event executed by SychronizeInternal to wake main thread if it sleeps in
  54. CheckSynchronize }
  55. SynchronizeTimeoutEvent: PRtlEvent;
  56. { the head of the queue containing the entries to be Synchronized - Nil if the
  57. queue is empty }
  58. ThreadQueueHead: TThread.PThreadQueueEntry;
  59. { the tail of the queue containing the entries to be Synchronized - Nil if the
  60. queue is empty }
  61. ThreadQueueTail: TThread.PThreadQueueEntry;
  62. { used for serialized access to the queue }
  63. ThreadQueueLock: TRtlCriticalSection;
  64. { this list holds all instances of external threads that need to be freed at
  65. the end of the program }
  66. ExternalThreads: TThreadList;
  67. { this list signals that the ExternalThreads list is cleared and thus the
  68. thread instances don't need to remove themselves }
  69. ExternalThreadsCleanup: Boolean = False;
  70. { this must be a global var, otherwise unwanted optimizations might happen in
  71. TThread.SpinWait() }
  72. SpinWaitDummy: LongWord;
  73. threadvar
  74. { the instance of the current thread; in case of an external thread this is
  75. Nil until TThread.GetCurrentThread was called once (the RTLs need to ensure
  76. that threadvars are initialized with 0!) }
  77. CurrentThreadVar: TThread;
  78. type
  79. { this type is used if a thread is created using
  80. TThread.CreateAnonymousThread }
  81. TAnonymousThread = class(TThread)
  82. private
  83. fProc: TProcedure;
  84. protected
  85. procedure Execute; override;
  86. public
  87. { as in TThread aProc needs to be changed to TProc once closures are
  88. supported }
  89. constructor Create(aProc: TProcedure);
  90. end;
  91. procedure TAnonymousThread.Execute;
  92. begin
  93. fProc();
  94. end;
  95. constructor TAnonymousThread.Create(aProc: TProcedure);
  96. begin
  97. { an anonymous thread is created suspended and with FreeOnTerminate set }
  98. inherited Create(True);
  99. FreeOnTerminate := True;
  100. fProc := aProc;
  101. end;
  102. type
  103. { this type is used by TThread.GetCurrentThread if the thread does not yet
  104. have a value in CurrentThreadVar (Note: the main thread is also created as
  105. a TExternalThread) }
  106. TExternalThread = class(TThread)
  107. protected
  108. { dummy method to remove the warning }
  109. procedure Execute; override;
  110. public
  111. constructor Create;
  112. destructor Destroy; override;
  113. end;
  114. procedure TExternalThread.Execute;
  115. begin
  116. { empty }
  117. end;
  118. constructor TExternalThread.Create;
  119. begin
  120. FExternalThread := True;
  121. { the parameter is unimportant if FExternalThread is True }
  122. inherited Create(False);
  123. with ExternalThreads.LockList do
  124. try
  125. Add(Self);
  126. finally
  127. ExternalThreads.UnlockList;
  128. end;
  129. end;
  130. destructor TExternalThread.Destroy;
  131. begin
  132. inherited;
  133. if not ExternalThreadsCleanup then
  134. with ExternalThreads.LockList do
  135. try
  136. Extract(Self);
  137. finally
  138. ExternalThreads.UnlockList;
  139. end;
  140. end;
  141. function ThreadProc(ThreadObjPtr: Pointer): PtrInt;
  142. var
  143. FreeThread: Boolean;
  144. Thread: TThread absolute ThreadObjPtr;
  145. begin
  146. { if Suspend checks FSuspended before doing anything, make sure it }
  147. { knows we're currently not suspended (this flag may have been set }
  148. { to true if CreateSuspended was true) }
  149. // Thread.FSuspended:=false;
  150. // wait until AfterConstruction has been called, so we cannot
  151. // free ourselves before TThread.Create has finished
  152. // (since that one may check our VTM in case of $R+, and
  153. // will call the AfterConstruction method in all cases)
  154. // Thread.Suspend;
  155. try
  156. { The thread may be already terminated at this point, e.g. if it was intially
  157. suspended, or if it wasn't ever scheduled for execution for whatever reason.
  158. So bypass user code if terminated. }
  159. if not Thread.Terminated then begin
  160. CurrentThreadVar := Thread;
  161. Thread.Execute;
  162. end;
  163. except
  164. Thread.FFatalException := TObject(AcquireExceptionObject);
  165. end;
  166. FreeThread := Thread.FFreeOnTerminate;
  167. Result := Thread.FReturnValue;
  168. Thread.FFinished := True;
  169. Thread.DoTerminate;
  170. if FreeThread then
  171. Thread.Free;
  172. EndThread(Result);
  173. end;
  174. { system-dependent code }
  175. {$i tthread.inc}
  176. constructor TThread.Create(CreateSuspended: Boolean;
  177. const StackSize: SizeUInt);
  178. begin
  179. inherited Create;
  180. if FExternalThread then
  181. FThreadID := GetCurrentThreadID
  182. else
  183. SysCreate(CreateSuspended, StackSize);
  184. end;
  185. destructor TThread.Destroy;
  186. begin
  187. if not FExternalThread then begin
  188. SysDestroy;
  189. if FHandle <> TThreadID(0) then
  190. CloseThread(FHandle);
  191. end;
  192. RemoveQueuedEvents(Self);
  193. DoneSynchronizeEvent;
  194. { set CurrentThreadVar to Nil? }
  195. inherited Destroy;
  196. end;
  197. procedure TThread.Start;
  198. begin
  199. { suspend/resume are now deprecated in Delphi (they also don't work
  200. on most platforms in FPC), so a different method was required
  201. to start a thread if it's create with fSuspended=true -> that's
  202. what this method is for. }
  203. Resume;
  204. end;
  205. function TThread.GetSuspended: Boolean;
  206. begin
  207. GetSuspended:=FSuspended;
  208. end;
  209. procedure TThread.AfterConstruction;
  210. begin
  211. inherited AfterConstruction;
  212. // enable for all platforms once http://bugs.freepascal.org/view.php?id=16884
  213. // is fixed for all platforms (in case the fix for non-unix platforms also
  214. // requires this field at least)
  215. {$if defined(unix) or defined(windows) or defined(os2) or defined(hasamiga)}
  216. if not FExternalThread and not FInitialSuspended then
  217. Resume;
  218. {$endif}
  219. end;
  220. procedure ExecuteThreadQueueEntry(aEntry: TThread.PThreadQueueEntry);
  221. begin
  222. if Assigned(aEntry^.Method) then
  223. aEntry^.Method()
  224. // enable once closures are supported
  225. {else
  226. aEntry^.ThreadProc();}
  227. end;
  228. procedure ThreadQueueAppend(aEntry: TThread.PThreadQueueEntry);
  229. begin
  230. { do we really need a synchronized call? }
  231. if GetCurrentThreadID = MainThreadID then begin
  232. ExecuteThreadQueueEntry(aEntry);
  233. if not Assigned(aEntry^.SyncEvent) then
  234. Dispose(aEntry);
  235. end else begin
  236. System.EnterCriticalSection(ThreadQueueLock);
  237. try
  238. { add the entry to the thread queue }
  239. if Assigned(ThreadQueueTail) then begin
  240. ThreadQueueTail^.Next := aEntry;
  241. end else
  242. ThreadQueueHead := aEntry;
  243. ThreadQueueTail := aEntry;
  244. finally
  245. System.LeaveCriticalSection(ThreadQueueLock);
  246. end;
  247. { ensure that the main thread knows that something awaits }
  248. RtlEventSetEvent(SynchronizeTimeoutEvent);
  249. if assigned(WakeMainThread) then
  250. WakeMainThread(aEntry^.Thread);
  251. { is this a Synchronize or Queue entry? }
  252. if Assigned(aEntry^.SyncEvent) then begin
  253. RtlEventWaitFor(aEntry^.SyncEvent);
  254. if Assigned(aEntry^.Exception) then
  255. raise aEntry^.Exception;
  256. end;
  257. end;
  258. end;
  259. procedure TThread.InitSynchronizeEvent;
  260. begin
  261. if Assigned(FSynchronizeEntry) then
  262. Exit;
  263. New(FSynchronizeEntry);
  264. FillChar(FSynchronizeEntry^, SizeOf(TThreadQueueEntry), 0);
  265. FSynchronizeEntry^.Thread := Self;
  266. FSynchronizeEntry^.SyncEvent := RtlEventCreate;
  267. end;
  268. procedure TThread.DoneSynchronizeEvent;
  269. begin
  270. if not Assigned(FSynchronizeEntry) then
  271. Exit;
  272. RtlEventDestroy(FSynchronizeEntry^.SyncEvent);
  273. Dispose(FSynchronizeEntry);
  274. FSynchronizeEntry := Nil;
  275. end;
  276. class procedure TThread.Synchronize(AThread: TThread; AMethod: TThreadMethod);
  277. begin
  278. { ensure that we have a TThread instance }
  279. if not Assigned(AThread) then
  280. AThread := CurrentThread;
  281. { the Synchronize event is instantiated on demand }
  282. AThread.InitSynchronizeEvent;
  283. AThread.FSynchronizeEntry^.Exception := Nil;
  284. AThread.FSynchronizeEntry^.Method := AMethod;
  285. ThreadQueueAppend(AThread.FSynchronizeEntry);
  286. AThread.FSynchronizeEntry^.Method := Nil;
  287. AThread.FSynchronizeEntry^.Next := Nil;
  288. end;
  289. procedure TThread.Synchronize(AMethod: TThreadMethod);
  290. begin
  291. TThread.Synchronize(self,AMethod);
  292. end;
  293. Function PopThreadQueueHead : TThread.PThreadQueueEntry;
  294. begin
  295. Result:=ThreadQueueHead;
  296. if (Result<>Nil) then
  297. begin
  298. System.EnterCriticalSection(ThreadQueueLock);
  299. try
  300. Result:=ThreadQueueHead;
  301. if Result<>Nil then
  302. ThreadQueueHead:=ThreadQueueHead^.Next;
  303. if Not Assigned(ThreadQueueHead) then
  304. ThreadQueueTail := Nil;
  305. finally
  306. System.LeaveCriticalSection(ThreadQueueLock);
  307. end;
  308. end;
  309. end;
  310. function CheckSynchronize(timeout : longint=0) : boolean;
  311. { assumes being called from GUI thread }
  312. var
  313. ExceptObj: Exception;
  314. tmpentry: TThread.PThreadQueueEntry;
  315. begin
  316. result:=false;
  317. { first sanity check }
  318. if Not IsMultiThread then
  319. Exit
  320. { second sanity check }
  321. else if GetCurrentThreadID<>MainThreadID then
  322. raise EThread.CreateFmt(SCheckSynchronizeError,[GetCurrentThreadID]);
  323. if timeout>0 then
  324. RtlEventWaitFor(SynchronizeTimeoutEvent,timeout)
  325. else
  326. RtlEventResetEvent(SynchronizeTimeoutEvent);
  327. tmpentry := PopThreadQueueHead;
  328. while Assigned(tmpentry) do
  329. begin
  330. { step 2: execute the method }
  331. exceptobj := Nil;
  332. try
  333. ExecuteThreadQueueEntry(tmpentry);
  334. except
  335. exceptobj := Exception(AcquireExceptionObject);
  336. end;
  337. { step 3: error handling and cleanup }
  338. if Assigned(tmpentry^.SyncEvent) then
  339. begin
  340. { for Synchronize entries we pass back the Exception and trigger
  341. the event that Synchronize waits in }
  342. tmpentry^.Exception := exceptobj;
  343. RtlEventSetEvent(tmpentry^.SyncEvent)
  344. end
  345. else
  346. begin
  347. { for Queue entries we dispose the entry and raise the exception }
  348. Dispose(tmpentry);
  349. if Assigned(exceptobj) then
  350. raise exceptobj;
  351. end;
  352. tmpentry := PopThreadQueueHead;
  353. end;
  354. end;
  355. class function TThread.GetCurrentThread: TThread;
  356. begin
  357. { if this is the first time GetCurrentThread is called for an external thread
  358. we need to create a corresponding TExternalThread instance }
  359. Result := CurrentThreadVar;
  360. if not Assigned(Result) then begin
  361. Result := TExternalThread.Create;
  362. CurrentThreadVar := Result;
  363. end;
  364. end;
  365. class function TThread.GetIsSingleProcessor: Boolean;
  366. begin
  367. Result := FProcessorCount <= 1;
  368. end;
  369. procedure TThread.Queue(aMethod: TThreadMethod);
  370. begin
  371. Queue(Self, aMethod);
  372. end;
  373. class procedure TThread.Queue(aThread: TThread; aMethod: TThreadMethod); static;
  374. var
  375. queueentry: PThreadQueueEntry;
  376. begin
  377. { ensure that we have a valid TThread instance }
  378. if not Assigned(aThread) then
  379. aThread := CurrentThread;
  380. New(queueentry);
  381. FillChar(queueentry^, SizeOf(TThreadQueueEntry), 0);
  382. queueentry^.Thread := aThread;
  383. queueentry^.Method := aMethod;
  384. { the queueentry is freed by CheckSynchronize (or by RemoveQueuedEvents) }
  385. ThreadQueueAppend(queueentry);
  386. end;
  387. class procedure TThread.RemoveQueuedEvents(aThread: TThread; aMethod: TThreadMethod);
  388. var
  389. entry, tmpentry, lastentry: PThreadQueueEntry;
  390. begin
  391. { anything to do at all? }
  392. if not Assigned(aThread) or not Assigned(aMethod) then
  393. Exit;
  394. System.EnterCriticalSection(ThreadQueueLock);
  395. try
  396. lastentry := Nil;
  397. entry := ThreadQueueHead;
  398. while Assigned(entry) do begin
  399. { first check for the thread }
  400. if Assigned(aThread) and (entry^.Thread <> aThread) then begin
  401. lastentry := entry;
  402. entry := entry^.Next;
  403. Continue;
  404. end;
  405. { then check for the method }
  406. if entry^.Method <> aMethod then begin
  407. lastentry := entry;
  408. entry := entry^.Next;
  409. Continue;
  410. end;
  411. { skip entries added by Synchronize }
  412. if Assigned(entry^.SyncEvent) then begin
  413. lastentry := entry;
  414. entry := entry^.Next;
  415. Continue;
  416. end;
  417. { ok, we need to remove this entry }
  418. tmpentry := entry;
  419. if Assigned(lastentry) then
  420. lastentry^.Next := entry^.Next;
  421. entry := entry^.Next;
  422. if ThreadQueueHead = tmpentry then
  423. ThreadQueueHead := entry;
  424. if ThreadQueueTail = tmpentry then
  425. ThreadQueueTail := lastentry;
  426. { only dispose events added by Queue }
  427. if not Assigned(tmpentry^.SyncEvent) then
  428. Dispose(tmpentry);
  429. end;
  430. finally
  431. System.LeaveCriticalSection(ThreadQueueLock);
  432. end;
  433. end;
  434. class procedure TThread.RemoveQueuedEvents(aMethod: TThreadMethod);
  435. begin
  436. RemoveQueuedEvents(Nil, aMethod);
  437. end;
  438. class procedure TThread.RemoveQueuedEvents(aThread: TThread);
  439. begin
  440. RemoveQueuedEvents(aThread, Nil);
  441. end;
  442. class function TThread.CheckTerminated: Boolean;
  443. begin
  444. { this method only works with threads created by TThread, so we can make a
  445. shortcut here }
  446. if not Assigned(CurrentThreadVar) then
  447. raise EThreadExternalException.Create(SThreadExternal);
  448. Result := CurrentThreadVar.FTerminated;
  449. end;
  450. class procedure TThread.SetReturnValue(aValue: Integer);
  451. begin
  452. { this method only works with threads created by TThread, so we can make a
  453. shortcut here }
  454. if not Assigned(CurrentThreadVar) then
  455. raise EThreadExternalException.Create(SThreadExternal);
  456. CurrentThreadVar.FReturnValue := aValue;
  457. end;
  458. class function TThread.CreateAnonymousThread(aProc: TProcedure): TThread;
  459. begin
  460. if not Assigned(aProc) then
  461. raise Exception.Create(SNoProcGiven);
  462. Result := TAnonymousThread.Create(aProc);
  463. end;
  464. {$ifdef THREADNAME_IS_ANSISTRING}
  465. { the platform implements the AnsiString variant and the UnicodeString variant
  466. simply calls the AnsiString variant }
  467. class procedure TThread.NameThreadForDebugging(aThreadName: UnicodeString; aThreadID: TThreadID);
  468. begin
  469. NameThreadForDebugging(AnsiString(aThreadName), aThreadID);
  470. end;
  471. {$ifndef HAS_TTHREAD_NAMETHREADFORDEBUGGING}
  472. class procedure TThread.NameThreadForDebugging(aThreadName: AnsiString; aThreadID: TThreadID);
  473. begin
  474. { empty }
  475. end;
  476. {$endif}
  477. {$else}
  478. {$ifndef HAS_TTHREAD_NAMETHREADFORDEBUGGING}
  479. { the platform implements the UnicodeString variant and the AnsiString variant
  480. simply calls the UnicodeString variant }
  481. class procedure TThread.NameThreadForDebugging(aThreadName: UnicodeString; aThreadID: TThreadID);
  482. begin
  483. { empty }
  484. end;
  485. {$endif}
  486. class procedure TThread.NameThreadForDebugging(aThreadName: AnsiString; aThreadID: TThreadID);
  487. begin
  488. NameThreadForDebugging(UnicodeString(aThreadName), aThreadID);
  489. end;
  490. {$endif}
  491. class procedure TThread.Yield;
  492. begin
  493. ThreadSwitch;
  494. end;
  495. class procedure TThread.Sleep(aMilliseconds: Cardinal);
  496. begin
  497. SysUtils.Sleep(aMilliseconds);
  498. end;
  499. class procedure TThread.SpinWait(aIterations: LongWord);
  500. var
  501. i: LongWord;
  502. begin
  503. { yes, it's just a simple busy wait to burn some cpu cycles... and as the job
  504. of this loop is to burn CPU cycles we switch off any optimizations that
  505. could interfere with this (e.g. loop unrolling) }
  506. { Do *NOT* do $PUSH, $OPTIMIZATIONS OFF, <code>, $POP because optimization is
  507. not a local switch, which means $PUSH/POP doesn't affect it, so that turns
  508. off *ALL* optimizations for code below this point. Thanks to this we shipped
  509. large parts of the classes unit with optimizations off between 2012-12-27
  510. and 2014-06-06.
  511. Instead, use a global var for the spinlock, because that is always handled
  512. as volatile, so the access won't be optimized away by the compiler. (KB) }
  513. for i:=1 to aIterations do
  514. begin
  515. Inc(SpinWaitDummy); // SpinWaitDummy *MUST* be global
  516. end;
  517. end;
  518. {$ifndef HAS_TTHREAD_GETSYSTEMTIMES}
  519. class procedure TThread.GetSystemTimes(out aSystemTimes: TSystemTimes);
  520. begin
  521. { by default we just return a zeroed out record }
  522. FillChar(aSystemTimes, SizeOf(aSystemTimes), 0);
  523. end;
  524. {$endif}
  525. class function TThread.GetTickCount: LongWord;
  526. begin
  527. Result := SysUtils.GetTickCount;
  528. end;
  529. class function TThread.GetTickCount64: QWord;
  530. begin
  531. Result := SysUtils.GetTickCount64;
  532. end;
  533. { TPersistent implementation }
  534. {$i persist.inc }
  535. {$i sllist.inc}
  536. {$i resref.inc}
  537. { TComponent implementation }
  538. {$i compon.inc}
  539. { TBasicAction implementation }
  540. {$i action.inc}
  541. { TDataModule implementation }
  542. {$i dm.inc}
  543. { Class and component registration routines }
  544. {$I cregist.inc}
  545. { Interface related stuff }
  546. {$I intf.inc}
  547. {**********************************************************************
  548. * Miscellaneous procedures and functions *
  549. **********************************************************************}
  550. function ExtractStrings(Separators, WhiteSpace: TSysCharSet; Content: PChar; Strings: TStrings; AddEmptyStrings : Boolean = False): Integer;
  551. var
  552. b, c : pchar;
  553. procedure SkipWhitespace;
  554. begin
  555. while (c^ in Whitespace) do
  556. inc (c);
  557. end;
  558. procedure AddString;
  559. var
  560. l : integer;
  561. s : string;
  562. begin
  563. l := c-b;
  564. if (l > 0) or AddEmptyStrings then
  565. begin
  566. if assigned(Strings) then
  567. begin
  568. setlength(s, l);
  569. if l>0 then
  570. move (b^, s[1],l*SizeOf(char));
  571. Strings.Add (s);
  572. end;
  573. inc (result);
  574. end;
  575. end;
  576. var
  577. quoted : char;
  578. begin
  579. result := 0;
  580. c := Content;
  581. Quoted := #0;
  582. Separators := Separators + [#13, #10] - ['''','"'];
  583. SkipWhitespace;
  584. b := c;
  585. while (c^ <> #0) do
  586. begin
  587. if (c^ = Quoted) then
  588. begin
  589. if ((c+1)^ = Quoted) then
  590. inc (c)
  591. else
  592. Quoted := #0
  593. end
  594. else if (Quoted = #0) and (c^ in ['''','"']) then
  595. Quoted := c^;
  596. if (Quoted = #0) and (c^ in Separators) then
  597. begin
  598. AddString;
  599. inc (c);
  600. SkipWhitespace;
  601. b := c;
  602. end
  603. else
  604. inc (c);
  605. end;
  606. if (c <> b) then
  607. AddString;
  608. end;
  609. { Point and rectangle constructors }
  610. function Point(AX, AY: Integer): TPoint;
  611. begin
  612. with Result do
  613. begin
  614. X := AX;
  615. Y := AY;
  616. end;
  617. end;
  618. function SmallPoint(AX, AY: SmallInt): TSmallPoint;
  619. begin
  620. with Result do
  621. begin
  622. X := AX;
  623. Y := AY;
  624. end;
  625. end;
  626. function Rect(ALeft, ATop, ARight, ABottom: Integer): TRect;
  627. begin
  628. with Result do
  629. begin
  630. Left := ALeft;
  631. Top := ATop;
  632. Right := ARight;
  633. Bottom := ABottom;
  634. end;
  635. end;
  636. function Bounds(ALeft, ATop, AWidth, AHeight: Integer): TRect;
  637. begin
  638. with Result do
  639. begin
  640. Left := ALeft;
  641. Top := ATop;
  642. Right := ALeft + AWidth;
  643. Bottom := ATop + AHeight;
  644. end;
  645. end;
  646. function PointsEqual(const P1, P2: TPoint): Boolean; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
  647. begin
  648. { lazy, but should work }
  649. result:=QWord(P1)=QWord(P2);
  650. end;
  651. function PointsEqual(const P1, P2: TSmallPoint): Boolean; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
  652. begin
  653. { lazy, but should work }
  654. result:=DWord(P1)=DWord(P2);
  655. end;
  656. function InvalidPoint(X, Y: Integer): Boolean;
  657. begin
  658. result:=(X=-1) and (Y=-1);
  659. end;
  660. function InvalidPoint(const At: TPoint): Boolean;
  661. begin
  662. result:=(At.x=-1) and (At.y=-1);
  663. end;
  664. function InvalidPoint(const At: TSmallPoint): Boolean;
  665. begin
  666. result:=(At.x=-1) and (At.y=-1);
  667. end;
  668. { Object filing routines }
  669. var
  670. IntConstList: TThreadList;
  671. type
  672. TIntConst = class
  673. IntegerType: PTypeInfo; // The integer type RTTI pointer
  674. IdentToIntFn: TIdentToInt; // Identifier to Integer conversion
  675. IntToIdentFn: TIntToIdent; // Integer to Identifier conversion
  676. constructor Create(AIntegerType: PTypeInfo; AIdentToInt: TIdentToInt;
  677. AIntToIdent: TIntToIdent);
  678. end;
  679. constructor TIntConst.Create(AIntegerType: PTypeInfo; AIdentToInt: TIdentToInt;
  680. AIntToIdent: TIntToIdent);
  681. begin
  682. IntegerType := AIntegerType;
  683. IdentToIntFn := AIdentToInt;
  684. IntToIdentFn := AIntToIdent;
  685. end;
  686. procedure RegisterIntegerConsts(IntegerType: Pointer; IdentToIntFn: TIdentToInt;
  687. IntToIdentFn: TIntToIdent);
  688. begin
  689. IntConstList.Add(TIntConst.Create(IntegerType, IdentToIntFn, IntToIdentFn));
  690. end;
  691. function FindIntToIdent(AIntegerType: Pointer): TIntToIdent;
  692. var
  693. i: Integer;
  694. begin
  695. with IntConstList.LockList do
  696. try
  697. for i := 0 to Count - 1 do
  698. if TIntConst(Items[i]).IntegerType = AIntegerType then
  699. exit(TIntConst(Items[i]).IntToIdentFn);
  700. Result := nil;
  701. finally
  702. IntConstList.UnlockList;
  703. end;
  704. end;
  705. function FindIdentToInt(AIntegerType: Pointer): TIdentToInt;
  706. var
  707. i: Integer;
  708. begin
  709. with IntConstList.LockList do
  710. try
  711. for i := 0 to Count - 1 do
  712. with TIntConst(Items[I]) do
  713. if TIntConst(Items[I]).IntegerType = AIntegerType then
  714. exit(IdentToIntFn);
  715. Result := nil;
  716. finally
  717. IntConstList.UnlockList;
  718. end;
  719. end;
  720. function IdentToInt(const Ident: String; var Int: LongInt;
  721. const Map: array of TIdentMapEntry): Boolean;
  722. var
  723. i: Integer;
  724. begin
  725. for i := Low(Map) to High(Map) do
  726. if CompareText(Map[i].Name, Ident) = 0 then
  727. begin
  728. Int := Map[i].Value;
  729. exit(True);
  730. end;
  731. Result := False;
  732. end;
  733. function IntToIdent(Int: LongInt; var Ident: String;
  734. const Map: array of TIdentMapEntry): Boolean;
  735. var
  736. i: Integer;
  737. begin
  738. for i := Low(Map) to High(Map) do
  739. if Map[i].Value = Int then
  740. begin
  741. Ident := Map[i].Name;
  742. exit(True);
  743. end;
  744. Result := False;
  745. end;
  746. function GlobalIdentToInt(const Ident: String; var Int: LongInt):boolean;
  747. var
  748. i : Integer;
  749. begin
  750. with IntConstList.LockList do
  751. try
  752. for i := 0 to Count - 1 do
  753. if TIntConst(Items[I]).IdentToIntFn(Ident, Int) then
  754. Exit(True);
  755. Result := false;
  756. finally
  757. IntConstList.UnlockList;
  758. end;
  759. end;
  760. { TPropFixup }
  761. // Tainted. TPropFixup is being removed.
  762. Type
  763. TInitHandler = Class(TObject)
  764. AHandler : TInitComponentHandler;
  765. AClass : TComponentClass;
  766. end;
  767. {$ifndef i8086}
  768. type
  769. TCodePtrList = TList;
  770. {$endif i8086}
  771. Var
  772. InitHandlerList : TList;
  773. FindGlobalComponentList : TCodePtrList;
  774. procedure RegisterFindGlobalComponentProc(AFindGlobalComponent: TFindGlobalComponent);
  775. begin
  776. if not(assigned(FindGlobalComponentList)) then
  777. FindGlobalComponentList:=TCodePtrList.Create;
  778. if FindGlobalComponentList.IndexOf(CodePointer(AFindGlobalComponent))<0 then
  779. FindGlobalComponentList.Add(CodePointer(AFindGlobalComponent));
  780. end;
  781. procedure UnregisterFindGlobalComponentProc(AFindGlobalComponent: TFindGlobalComponent);
  782. begin
  783. if assigned(FindGlobalComponentList) then
  784. FindGlobalComponentList.Remove(CodePointer(AFindGlobalComponent));
  785. end;
  786. function FindGlobalComponent(const Name: string): TComponent;
  787. var
  788. i : sizeint;
  789. begin
  790. FindGlobalComponent:=nil;
  791. if assigned(FindGlobalComponentList) then
  792. begin
  793. for i:=FindGlobalComponentList.Count-1 downto 0 do
  794. begin
  795. FindGlobalComponent:=TFindGlobalComponent(FindGlobalComponentList[i])(name);
  796. if assigned(FindGlobalComponent) then
  797. break;
  798. end;
  799. end;
  800. end;
  801. procedure RegisterInitComponentHandler(ComponentClass: TComponentClass; Handler: TInitComponentHandler);
  802. Var
  803. I : Integer;
  804. H: TInitHandler;
  805. begin
  806. If (InitHandlerList=Nil) then
  807. InitHandlerList:=TList.Create;
  808. H:=TInitHandler.Create;
  809. H.Aclass:=ComponentClass;
  810. H.AHandler:=Handler;
  811. try
  812. With InitHandlerList do
  813. begin
  814. I:=0;
  815. While (I<Count) and not H.AClass.InheritsFrom(TInitHandler(Items[I]).AClass) do
  816. Inc(I);
  817. { override? }
  818. if (I<Count) and (TInitHandler(Items[I]).AClass=H.AClass) then
  819. begin
  820. TInitHandler(Items[I]).AHandler:=Handler;
  821. H.Free;
  822. end
  823. else
  824. InitHandlerList.Insert(I,H);
  825. end;
  826. except
  827. H.Free;
  828. raise;
  829. end;
  830. end;
  831. { all targets should at least include the sysres.inc dummy in the system unit to compile this }
  832. function CreateComponentfromRes(const res : string;Inst : THandle;var Component : TComponent) : Boolean;
  833. var
  834. ResStream : TResourceStream;
  835. begin
  836. result:=true;
  837. if Inst=0 then
  838. Inst:=HInstance;
  839. try
  840. ResStream:=TResourceStream.Create(Inst,res,RT_RCDATA);
  841. try
  842. Component:=ResStream.ReadComponent(Component);
  843. finally
  844. ResStream.Free;
  845. end;
  846. except
  847. on EResNotFound do
  848. result:=false;
  849. end;
  850. end;
  851. function DefaultInitHandler(Instance: TComponent; RootAncestor: TClass): Boolean;
  852. function doinit(_class : TClass) : boolean;
  853. begin
  854. result:=false;
  855. if (_class.ClassType=TComponent) or (_class.ClassType=RootAncestor) then
  856. exit;
  857. result:=doinit(_class.ClassParent);
  858. result:=CreateComponentfromRes(_class.ClassName,0,Instance) or result;
  859. end;
  860. begin
  861. GlobalNameSpace.BeginWrite;
  862. try
  863. result:=doinit(Instance.ClassType);
  864. finally
  865. GlobalNameSpace.EndWrite;
  866. end;
  867. end;
  868. function InitInheritedComponent(Instance: TComponent; RootAncestor: TClass): Boolean;
  869. Var
  870. I : Integer;
  871. begin
  872. I:=0;
  873. if not Assigned(InitHandlerList) then begin
  874. Result := True;
  875. Exit;
  876. end;
  877. Result:=False;
  878. With InitHandlerList do
  879. begin
  880. I:=0;
  881. // Instance is the normally the lowest one, so that one should be used when searching.
  882. While Not result and (I<Count) do
  883. begin
  884. If (Instance.InheritsFrom(TInitHandler(Items[i]).AClass)) then
  885. Result:=TInitHandler(Items[i]).AHandler(Instance,RootAncestor);
  886. Inc(I);
  887. end;
  888. end;
  889. end;
  890. function InitComponentRes(const ResName: String; Instance: TComponent): Boolean;
  891. begin
  892. Result:=ReadComponentRes(ResName,Instance)=Instance;
  893. end;
  894. function SysReadComponentRes(HInstance : THandle; const ResName: String; Instance: TComponent): TComponent;
  895. Var
  896. H : TFPResourceHandle;
  897. begin
  898. { Windows unit also has a FindResource function, use the one from
  899. system unit here. }
  900. H:=system.FindResource(HInstance,ResName,RT_RCDATA);
  901. if (PtrInt(H)=0) then
  902. Result:=Nil
  903. else
  904. With TResourceStream.Create(HInstance,ResName,RT_RCDATA) do
  905. try
  906. Result:=ReadComponent(Instance);
  907. Finally
  908. Free;
  909. end;
  910. end;
  911. function ReadComponentRes(const ResName: String; Instance: TComponent): TComponent;
  912. begin
  913. Result:=SysReadComponentRes(Hinstance,Resname,Instance);
  914. end;
  915. function ReadComponentResEx(HInstance: THandle; const ResName: String): TComponent;
  916. begin
  917. Result:=SysReadComponentRes(Hinstance,ResName,Nil);
  918. end;
  919. function ReadComponentResFile(const FileName: String; Instance: TComponent): TComponent;
  920. var
  921. FileStream: TStream;
  922. begin
  923. FileStream := TFileStream.Create(FileName, fmOpenRead {!!!:or fmShareDenyWrite});
  924. try
  925. Result := FileStream.ReadComponentRes(Instance);
  926. finally
  927. FileStream.Free;
  928. end;
  929. end;
  930. procedure WriteComponentResFile(const FileName: String; Instance: TComponent);
  931. var
  932. FileStream: TStream;
  933. begin
  934. FileStream := TFileStream.Create(FileName, fmCreate);
  935. try
  936. FileStream.WriteComponentRes(Instance.ClassName, Instance);
  937. finally
  938. FileStream.Free;
  939. end;
  940. end;
  941. Function FindNestedComponent(Root : TComponent; APath : String; CStyle : Boolean = True) : TComponent;
  942. Function GetNextName : String; {$ifdef CLASSESINLINE} inline; {$endif CLASSESINLINE}
  943. Var
  944. P : Integer;
  945. CM : Boolean;
  946. begin
  947. P:=Pos('.',APath);
  948. CM:=False;
  949. If (P=0) then
  950. begin
  951. If CStyle then
  952. begin
  953. P:=Pos('->',APath);
  954. CM:=P<>0;
  955. end;
  956. If (P=0) Then
  957. P:=Length(APath)+1;
  958. end;
  959. Result:=Copy(APath,1,P-1);
  960. Delete(APath,1,P+Ord(CM));
  961. end;
  962. Var
  963. C : TComponent;
  964. S : String;
  965. begin
  966. If (APath='') then
  967. Result:=Nil
  968. else
  969. begin
  970. Result:=Root;
  971. While (APath<>'') And (Result<>Nil) do
  972. begin
  973. C:=Result;
  974. S:=Uppercase(GetNextName);
  975. Result:=C.FindComponent(S);
  976. If (Result=Nil) And (S='OWNER') then
  977. Result:=C;
  978. end;
  979. end;
  980. end;
  981. threadvar
  982. GlobalLoaded, GlobalLists: TFpList;
  983. procedure BeginGlobalLoading;
  984. begin
  985. if not Assigned(GlobalLists) then
  986. GlobalLists := TFpList.Create;
  987. GlobalLists.Add(GlobalLoaded);
  988. GlobalLoaded := TFpList.Create;
  989. end;
  990. { Notify all global components that they have been loaded completely }
  991. procedure NotifyGlobalLoading;
  992. var
  993. i: Integer;
  994. begin
  995. for i := 0 to GlobalLoaded.Count - 1 do
  996. TComponent(GlobalLoaded[i]).Loaded;
  997. end;
  998. procedure EndGlobalLoading;
  999. begin
  1000. { Free the memory occupied by BeginGlobalLoading }
  1001. GlobalLoaded.Free;
  1002. GlobalLoaded := TFpList(GlobalLists.Last);
  1003. GlobalLists.Delete(GlobalLists.Count - 1);
  1004. if GlobalLists.Count = 0 then
  1005. begin
  1006. GlobalLists.Free;
  1007. GlobalLists := nil;
  1008. end;
  1009. end;
  1010. function CollectionsEqual(C1, C2: TCollection): Boolean;
  1011. begin
  1012. // !!!: Implement this
  1013. CollectionsEqual:=false;
  1014. end;
  1015. function CollectionsEqual(C1, C2: TCollection; Owner1, Owner2: TComponent): Boolean;
  1016. procedure stream_collection(s : tstream;c : tcollection;o : tcomponent);
  1017. var
  1018. w : twriter;
  1019. begin
  1020. w:=twriter.create(s,4096);
  1021. try
  1022. w.root:=o;
  1023. w.flookuproot:=o;
  1024. w.writecollection(c);
  1025. finally
  1026. w.free;
  1027. end;
  1028. end;
  1029. var
  1030. s1,s2 : tmemorystream;
  1031. begin
  1032. result:=false;
  1033. if (c1.classtype<>c2.classtype) or
  1034. (c1.count<>c2.count) then
  1035. exit;
  1036. if c1.count = 0 then
  1037. begin
  1038. result:= true;
  1039. exit;
  1040. end;
  1041. s1:=tmemorystream.create;
  1042. try
  1043. s2:=tmemorystream.create;
  1044. try
  1045. stream_collection(s1,c1,owner1);
  1046. stream_collection(s2,c2,owner2);
  1047. result:=(s1.size=s2.size) and (CompareChar(s1.memory^,s2.memory^,s1.size)=0);
  1048. finally
  1049. s2.free;
  1050. end;
  1051. finally
  1052. s1.free;
  1053. end;
  1054. end;
  1055. { Object conversion routines }
  1056. type
  1057. CharToOrdFuncty = Function(var charpo: Pointer): Cardinal;
  1058. function CharToOrd(var P: Pointer): Cardinal;
  1059. begin
  1060. result:= ord(pchar(P)^);
  1061. inc(pchar(P));
  1062. end;
  1063. function WideCharToOrd(var P: Pointer): Cardinal;
  1064. begin
  1065. result:= ord(pwidechar(P)^);
  1066. inc(pwidechar(P));
  1067. end;
  1068. function Utf8ToOrd(var P:Pointer): Cardinal;
  1069. begin
  1070. // Should also check for illegal utf8 combinations
  1071. Result := Ord(PChar(P)^);
  1072. Inc(P);
  1073. if (Result and $80) <> 0 then
  1074. if (Ord(Result) and %11100000) = %11000000 then begin
  1075. Result := ((Result and %00011111) shl 6)
  1076. or (ord(PChar(P)^) and %00111111);
  1077. Inc(P);
  1078. end else if (Ord(Result) and %11110000) = %11100000 then begin
  1079. Result := ((Result and %00011111) shl 12)
  1080. or ((ord(PChar(P)^) and %00111111) shl 6)
  1081. or (ord((PChar(P)+1)^) and %00111111);
  1082. Inc(P,2);
  1083. end else begin
  1084. Result := ((ord(Result) and %00011111) shl 18)
  1085. or ((ord(PChar(P)^) and %00111111) shl 12)
  1086. or ((ord((PChar(P)+1)^) and %00111111) shl 6)
  1087. or (ord((PChar(P)+2)^) and %00111111);
  1088. Inc(P,3);
  1089. end;
  1090. end;
  1091. procedure ObjectBinaryToText(Input, Output: TStream; Encoding: TObjectTextEncoding);
  1092. procedure OutStr(s: String);
  1093. begin
  1094. if Length(s) > 0 then
  1095. Output.Write(s[1], Length(s));
  1096. end;
  1097. procedure OutLn(s: String);
  1098. begin
  1099. OutStr(s + LineEnding);
  1100. end;
  1101. procedure Outchars(P, LastP : Pointer; CharToOrdFunc: CharToOrdFuncty;
  1102. UseBytes: boolean = false);
  1103. var
  1104. res, NewStr: String;
  1105. w: Cardinal;
  1106. InString, NewInString: Boolean;
  1107. begin
  1108. if p = nil then begin
  1109. res:= '''''';
  1110. end
  1111. else
  1112. begin
  1113. res := '';
  1114. InString := False;
  1115. while P < LastP do
  1116. begin
  1117. NewInString := InString;
  1118. w := CharToOrdfunc(P);
  1119. if w = ord('''') then
  1120. begin //quote char
  1121. if not InString then
  1122. NewInString := True;
  1123. NewStr := '''''';
  1124. end
  1125. else if (Ord(w) >= 32) and ((Ord(w) < 127) or (UseBytes and (Ord(w)<256))) then
  1126. begin //printable ascii or bytes
  1127. if not InString then
  1128. NewInString := True;
  1129. NewStr := char(w);
  1130. end
  1131. else
  1132. begin //ascii control chars, non ascii
  1133. if InString then
  1134. NewInString := False;
  1135. NewStr := '#' + IntToStr(w);
  1136. end;
  1137. if NewInString <> InString then
  1138. begin
  1139. NewStr := '''' + NewStr;
  1140. InString := NewInString;
  1141. end;
  1142. res := res + NewStr;
  1143. end;
  1144. if InString then
  1145. res := res + '''';
  1146. end;
  1147. OutStr(res);
  1148. end;
  1149. procedure OutString(s: String);
  1150. begin
  1151. OutChars(Pointer(S),PChar(S)+Length(S),@CharToOrd,Encoding=oteLFM);
  1152. end;
  1153. procedure OutWString(W: WideString);
  1154. begin
  1155. OutChars(Pointer(W),pwidechar(W)+Length(W),@WideCharToOrd);
  1156. end;
  1157. procedure OutUString(W: UnicodeString);
  1158. begin
  1159. OutChars(Pointer(W),pwidechar(W)+Length(W),@WideCharToOrd);
  1160. end;
  1161. procedure OutUtf8Str(s: String);
  1162. begin
  1163. if Encoding=oteLFM then
  1164. OutChars(Pointer(S),PChar(S)+Length(S),@CharToOrd)
  1165. else
  1166. OutChars(Pointer(S),PChar(S)+Length(S),@Utf8ToOrd);
  1167. end;
  1168. function ReadWord : word; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
  1169. begin
  1170. Result:=Input.ReadWord;
  1171. Result:=LEtoN(Result);
  1172. end;
  1173. function ReadDWord : longword; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
  1174. begin
  1175. Result:=Input.ReadDWord;
  1176. Result:=LEtoN(Result);
  1177. end;
  1178. function ReadQWord : qword; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
  1179. begin
  1180. Input.ReadBuffer(Result,sizeof(Result));
  1181. Result:=LEtoN(Result);
  1182. end;
  1183. {$ifndef FPUNONE}
  1184. {$IFNDEF FPC_HAS_TYPE_EXTENDED}
  1185. function ExtendedToDouble(e : pointer) : double;
  1186. var mant : qword;
  1187. exp : smallint;
  1188. sign : boolean;
  1189. d : qword;
  1190. begin
  1191. move(pbyte(e)[0],mant,8); //mantissa : bytes 0..7
  1192. move(pbyte(e)[8],exp,2); //exponent and sign: bytes 8..9
  1193. mant:=LEtoN(mant);
  1194. exp:=LetoN(word(exp));
  1195. sign:=(exp and $8000)<>0;
  1196. if sign then exp:=exp and $7FFF;
  1197. case exp of
  1198. 0 : mant:=0; //if denormalized, value is too small for double,
  1199. //so it's always zero
  1200. $7FFF : exp:=2047 //either infinity or NaN
  1201. else
  1202. begin
  1203. dec(exp,16383-1023);
  1204. if (exp>=-51) and (exp<=0) then //can be denormalized
  1205. begin
  1206. mant:=mant shr (-exp);
  1207. exp:=0;
  1208. end
  1209. else
  1210. if (exp<-51) or (exp>2046) then //exponent too large.
  1211. begin
  1212. Result:=0;
  1213. exit;
  1214. end
  1215. else //normalized value
  1216. mant:=mant shl 1; //hide most significant bit
  1217. end;
  1218. end;
  1219. d:=word(exp);
  1220. d:=d shl 52;
  1221. mant:=mant shr 12;
  1222. d:=d or mant;
  1223. if sign then d:=d or $8000000000000000;
  1224. Result:=pdouble(@d)^;
  1225. end;
  1226. {$ENDIF}
  1227. {$endif}
  1228. function ReadInt(ValueType: TValueType): Int64;
  1229. begin
  1230. case ValueType of
  1231. vaInt8: Result := ShortInt(Input.ReadByte);
  1232. vaInt16: Result := SmallInt(ReadWord);
  1233. vaInt32: Result := LongInt(ReadDWord);
  1234. vaInt64: Result := Int64(ReadQWord);
  1235. end;
  1236. end;
  1237. function ReadInt: Int64;
  1238. begin
  1239. Result := ReadInt(TValueType(Input.ReadByte));
  1240. end;
  1241. {$ifndef FPUNONE}
  1242. function ReadExtended : extended;
  1243. {$IFNDEF FPC_HAS_TYPE_EXTENDED}
  1244. var ext : array[0..9] of byte;
  1245. {$ENDIF}
  1246. begin
  1247. {$IFNDEF FPC_HAS_TYPE_EXTENDED}
  1248. Input.ReadBuffer(ext[0],10);
  1249. Result:=ExtendedToDouble(@(ext[0]));
  1250. {$ELSE}
  1251. Input.ReadBuffer(Result,sizeof(Result));
  1252. {$ENDIF}
  1253. end;
  1254. {$endif}
  1255. function ReadSStr: String;
  1256. var
  1257. len: Byte;
  1258. begin
  1259. len := Input.ReadByte;
  1260. SetLength(Result, len);
  1261. if (len > 0) then
  1262. Input.ReadBuffer(Result[1], len);
  1263. end;
  1264. function ReadLStr: String;
  1265. var
  1266. len: DWord;
  1267. begin
  1268. len := ReadDWord;
  1269. SetLength(Result, len);
  1270. if (len > 0) then
  1271. Input.ReadBuffer(Result[1], len);
  1272. end;
  1273. function ReadWStr: WideString;
  1274. var
  1275. len: DWord;
  1276. {$IFDEF ENDIAN_BIG}
  1277. i : integer;
  1278. {$ENDIF}
  1279. begin
  1280. len := ReadDWord;
  1281. SetLength(Result, len);
  1282. if (len > 0) then
  1283. begin
  1284. Input.ReadBuffer(Pointer(@Result[1])^, len*2);
  1285. {$IFDEF ENDIAN_BIG}
  1286. for i:=1 to len do
  1287. Result[i]:=widechar(SwapEndian(word(Result[i])));
  1288. {$ENDIF}
  1289. end;
  1290. end;
  1291. function ReadUStr: UnicodeString;
  1292. var
  1293. len: DWord;
  1294. {$IFDEF ENDIAN_BIG}
  1295. i : integer;
  1296. {$ENDIF}
  1297. begin
  1298. len := ReadDWord;
  1299. SetLength(Result, len);
  1300. if (len > 0) then
  1301. begin
  1302. Input.ReadBuffer(Pointer(@Result[1])^, len*2);
  1303. {$IFDEF ENDIAN_BIG}
  1304. for i:=1 to len do
  1305. Result[i]:=widechar(SwapEndian(word(Result[i])));
  1306. {$ENDIF}
  1307. end;
  1308. end;
  1309. procedure ReadPropList(indent: String);
  1310. procedure ProcessValue(ValueType: TValueType; Indent: String);
  1311. procedure ProcessBinary;
  1312. var
  1313. ToDo, DoNow, i: LongInt;
  1314. lbuf: array[0..31] of Byte;
  1315. s: String;
  1316. begin
  1317. ToDo := ReadDWord;
  1318. OutLn('{');
  1319. while ToDo > 0 do begin
  1320. DoNow := ToDo;
  1321. if DoNow > 32 then DoNow := 32;
  1322. Dec(ToDo, DoNow);
  1323. s := Indent + ' ';
  1324. Input.ReadBuffer(lbuf, DoNow);
  1325. for i := 0 to DoNow - 1 do
  1326. s := s + IntToHex(lbuf[i], 2);
  1327. OutLn(s);
  1328. end;
  1329. OutLn(indent + '}');
  1330. end;
  1331. var
  1332. s: String;
  1333. { len: LongInt; }
  1334. IsFirst: Boolean;
  1335. {$ifndef FPUNONE}
  1336. ext: Extended;
  1337. {$endif}
  1338. begin
  1339. case ValueType of
  1340. vaList: begin
  1341. OutStr('(');
  1342. IsFirst := True;
  1343. while True do begin
  1344. ValueType := TValueType(Input.ReadByte);
  1345. if ValueType = vaNull then break;
  1346. if IsFirst then begin
  1347. OutLn('');
  1348. IsFirst := False;
  1349. end;
  1350. OutStr(Indent + ' ');
  1351. ProcessValue(ValueType, Indent + ' ');
  1352. end;
  1353. OutLn(Indent + ')');
  1354. end;
  1355. vaInt8: OutLn(IntToStr(ShortInt(Input.ReadByte)));
  1356. vaInt16: OutLn( IntToStr(SmallInt(ReadWord)));
  1357. vaInt32: OutLn(IntToStr(LongInt(ReadDWord)));
  1358. vaInt64: OutLn(IntToStr(Int64(ReadQWord)));
  1359. {$ifndef FPUNONE}
  1360. vaExtended: begin
  1361. ext:=ReadExtended;
  1362. Str(ext,S);// Do not use localized strings.
  1363. OutLn(S);
  1364. end;
  1365. {$endif}
  1366. vaString: begin
  1367. OutString(ReadSStr);
  1368. OutLn('');
  1369. end;
  1370. vaIdent: OutLn(ReadSStr);
  1371. vaFalse: OutLn('False');
  1372. vaTrue: OutLn('True');
  1373. vaBinary: ProcessBinary;
  1374. vaSet: begin
  1375. OutStr('[');
  1376. IsFirst := True;
  1377. while True do begin
  1378. s := ReadSStr;
  1379. if Length(s) = 0 then break;
  1380. if not IsFirst then OutStr(', ');
  1381. IsFirst := False;
  1382. OutStr(s);
  1383. end;
  1384. OutLn(']');
  1385. end;
  1386. vaLString:
  1387. begin
  1388. OutString(ReadLStr);
  1389. OutLn('');
  1390. end;
  1391. vaWString:
  1392. begin
  1393. OutWString(ReadWStr);
  1394. OutLn('');
  1395. end;
  1396. vaUString:
  1397. begin
  1398. OutWString(ReadWStr);
  1399. OutLn('');
  1400. end;
  1401. vaNil:
  1402. OutLn('nil');
  1403. vaCollection: begin
  1404. OutStr('<');
  1405. while Input.ReadByte <> 0 do begin
  1406. OutLn(Indent);
  1407. Input.Seek(-1, soFromCurrent);
  1408. OutStr(indent + ' item');
  1409. ValueType := TValueType(Input.ReadByte);
  1410. if ValueType <> vaList then
  1411. OutStr('[' + IntToStr(ReadInt(ValueType)) + ']');
  1412. OutLn('');
  1413. ReadPropList(indent + ' ');
  1414. OutStr(indent + ' end');
  1415. end;
  1416. OutLn('>');
  1417. end;
  1418. {vaSingle: begin OutLn('!!Single!!'); exit end;
  1419. vaCurrency: begin OutLn('!!Currency!!'); exit end;
  1420. vaDate: begin OutLn('!!Date!!'); exit end;}
  1421. vaUTF8String: begin
  1422. OutUtf8Str(ReadLStr);
  1423. OutLn('');
  1424. end;
  1425. else
  1426. Raise EReadError.CreateFmt(SErrInvalidPropertyType,[Ord(ValueType)]);
  1427. end;
  1428. end;
  1429. begin
  1430. while Input.ReadByte <> 0 do begin
  1431. Input.Seek(-1, soFromCurrent);
  1432. OutStr(indent + ReadSStr + ' = ');
  1433. ProcessValue(TValueType(Input.ReadByte), Indent);
  1434. end;
  1435. end;
  1436. procedure ReadObject(indent: String);
  1437. var
  1438. b: Byte;
  1439. ObjClassName, ObjName: String;
  1440. ChildPos: LongInt;
  1441. begin
  1442. // Check for FilerFlags
  1443. b := Input.ReadByte;
  1444. if (b and $f0) = $f0 then begin
  1445. if (b and 2) <> 0 then ChildPos := ReadInt;
  1446. end else begin
  1447. b := 0;
  1448. Input.Seek(-1, soFromCurrent);
  1449. end;
  1450. ObjClassName := ReadSStr;
  1451. ObjName := ReadSStr;
  1452. OutStr(Indent);
  1453. if (b and 1) <> 0 then OutStr('inherited')
  1454. else
  1455. if (b and 4) <> 0 then OutStr('inline')
  1456. else OutStr('object');
  1457. OutStr(' ');
  1458. if ObjName <> '' then
  1459. OutStr(ObjName + ': ');
  1460. OutStr(ObjClassName);
  1461. if (b and 2) <> 0 then OutStr('[' + IntToStr(ChildPos) + ']');
  1462. OutLn('');
  1463. ReadPropList(indent + ' ');
  1464. while Input.ReadByte <> 0 do begin
  1465. Input.Seek(-1, soFromCurrent);
  1466. ReadObject(indent + ' ');
  1467. end;
  1468. OutLn(indent + 'end');
  1469. end;
  1470. type
  1471. PLongWord = ^LongWord;
  1472. const
  1473. signature: PChar = 'TPF0';
  1474. begin
  1475. if Input.ReadDWord <> PLongWord(Pointer(signature))^ then
  1476. raise EReadError.Create('Illegal stream image' {###SInvalidImage});
  1477. ReadObject('');
  1478. end;
  1479. procedure ObjectBinaryToText(Input, Output: TStream);
  1480. begin
  1481. ObjectBinaryToText(Input,Output,oteDFM);
  1482. end;
  1483. procedure ObjectTextToBinary(Input, Output: TStream);
  1484. var
  1485. parser: TParser;
  1486. procedure WriteWord(w : word); {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
  1487. begin
  1488. w:=NtoLE(w);
  1489. Output.WriteWord(w);
  1490. end;
  1491. procedure WriteDWord(lw : longword); {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
  1492. begin
  1493. lw:=NtoLE(lw);
  1494. Output.WriteDWord(lw);
  1495. end;
  1496. procedure WriteQWord(qw : qword); {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
  1497. begin
  1498. qw:=NtoLE(qw);
  1499. Output.WriteBuffer(qw,sizeof(qword));
  1500. end;
  1501. {$ifndef FPUNONE}
  1502. {$IFNDEF FPC_HAS_TYPE_EXTENDED}
  1503. procedure DoubleToExtended(d : double; e : pointer);
  1504. var mant : qword;
  1505. exp : smallint;
  1506. sign : boolean;
  1507. begin
  1508. mant:=(qword(d) and $000FFFFFFFFFFFFF) shl 12;
  1509. exp :=(qword(d) shr 52) and $7FF;
  1510. sign:=(qword(d) and $8000000000000000)<>0;
  1511. case exp of
  1512. 0 : begin
  1513. if mant<>0 then //denormalized value: hidden bit is 0. normalize it
  1514. begin
  1515. exp:=16383-1022;
  1516. while (mant and $8000000000000000)=0 do
  1517. begin
  1518. dec(exp);
  1519. mant:=mant shl 1;
  1520. end;
  1521. dec(exp); //don't shift, most significant bit is not hidden in extended
  1522. end;
  1523. end;
  1524. 2047 : exp:=$7FFF //either infinity or NaN
  1525. else
  1526. begin
  1527. inc(exp,16383-1023);
  1528. mant:=(mant shr 1) or $8000000000000000; //unhide hidden bit
  1529. end;
  1530. end;
  1531. if sign then exp:=exp or $8000;
  1532. mant:=NtoLE(mant);
  1533. exp:=NtoLE(word(exp));
  1534. move(mant,pbyte(e)[0],8); //mantissa : bytes 0..7
  1535. move(exp,pbyte(e)[8],2); //exponent and sign: bytes 8..9
  1536. end;
  1537. {$ENDIF}
  1538. procedure WriteExtended(e : extended);
  1539. {$IFNDEF FPC_HAS_TYPE_EXTENDED}
  1540. var ext : array[0..9] of byte;
  1541. {$ENDIF}
  1542. begin
  1543. {$IFNDEF FPC_HAS_TYPE_EXTENDED}
  1544. DoubleToExtended(e,@(ext[0]));
  1545. Output.WriteBuffer(ext[0],10);
  1546. {$ELSE}
  1547. Output.WriteBuffer(e,sizeof(e));
  1548. {$ENDIF}
  1549. end;
  1550. {$endif}
  1551. procedure WriteString(s: String);
  1552. var size : byte;
  1553. begin
  1554. if length(s)>255 then size:=255
  1555. else size:=length(s);
  1556. Output.WriteByte(size);
  1557. if Length(s) > 0 then
  1558. Output.WriteBuffer(s[1], size);
  1559. end;
  1560. procedure WriteLString(Const s: String);
  1561. begin
  1562. WriteDWord(Length(s));
  1563. if Length(s) > 0 then
  1564. Output.WriteBuffer(s[1], Length(s));
  1565. end;
  1566. procedure WriteWString(Const s: WideString);
  1567. var len : longword;
  1568. {$IFDEF ENDIAN_BIG}
  1569. i : integer;
  1570. ws : widestring;
  1571. {$ENDIF}
  1572. begin
  1573. len:=Length(s);
  1574. WriteDWord(len);
  1575. if len > 0 then
  1576. begin
  1577. {$IFDEF ENDIAN_BIG}
  1578. setlength(ws,len);
  1579. for i:=1 to len do
  1580. ws[i]:=widechar(SwapEndian(word(s[i])));
  1581. Output.WriteBuffer(ws[1], len*sizeof(widechar));
  1582. {$ELSE}
  1583. Output.WriteBuffer(s[1], len*sizeof(widechar));
  1584. {$ENDIF}
  1585. end;
  1586. end;
  1587. procedure WriteInteger(value: Int64);
  1588. begin
  1589. if (value >= -128) and (value <= 127) then begin
  1590. Output.WriteByte(Ord(vaInt8));
  1591. Output.WriteByte(byte(value));
  1592. end else if (value >= -32768) and (value <= 32767) then begin
  1593. Output.WriteByte(Ord(vaInt16));
  1594. WriteWord(word(value));
  1595. end else if (value >= -2147483648) and (value <= 2147483647) then begin
  1596. Output.WriteByte(Ord(vaInt32));
  1597. WriteDWord(longword(value));
  1598. end else begin
  1599. Output.WriteByte(ord(vaInt64));
  1600. WriteQWord(qword(value));
  1601. end;
  1602. end;
  1603. procedure ProcessWideString(const left : widestring);
  1604. var ws : widestring;
  1605. begin
  1606. ws:=left+parser.TokenWideString;
  1607. while parser.NextToken = '+' do
  1608. begin
  1609. parser.NextToken; // Get next string fragment
  1610. if not (parser.Token in [toString,toWString]) then
  1611. parser.CheckToken(toWString);
  1612. ws:=ws+parser.TokenWideString;
  1613. end;
  1614. Output.WriteByte(Ord(vaWstring));
  1615. WriteWString(ws);
  1616. end;
  1617. procedure ProcessProperty; forward;
  1618. procedure ProcessValue;
  1619. var
  1620. {$ifndef FPUNONE}
  1621. flt: Extended;
  1622. {$endif}
  1623. s: String;
  1624. stream: TMemoryStream;
  1625. begin
  1626. case parser.Token of
  1627. toInteger:
  1628. begin
  1629. WriteInteger(parser.TokenInt);
  1630. parser.NextToken;
  1631. end;
  1632. {$ifndef FPUNONE}
  1633. toFloat:
  1634. begin
  1635. Output.WriteByte(Ord(vaExtended));
  1636. flt := Parser.TokenFloat;
  1637. WriteExtended(flt);
  1638. parser.NextToken;
  1639. end;
  1640. {$endif}
  1641. toString:
  1642. begin
  1643. s := parser.TokenString;
  1644. while parser.NextToken = '+' do
  1645. begin
  1646. parser.NextToken; // Get next string fragment
  1647. case parser.Token of
  1648. toString : s:=s+parser.TokenString;
  1649. toWString : begin
  1650. ProcessWideString(s);
  1651. exit;
  1652. end
  1653. else parser.CheckToken(toString);
  1654. end;
  1655. end;
  1656. if (length(S)>255) then
  1657. begin
  1658. Output.WriteByte(Ord(vaLString));
  1659. WriteLString(S);
  1660. end
  1661. else
  1662. begin
  1663. Output.WriteByte(Ord(vaString));
  1664. WriteString(s);
  1665. end;
  1666. end;
  1667. toWString:
  1668. ProcessWideString('');
  1669. toSymbol:
  1670. begin
  1671. if CompareText(parser.TokenString, 'True') = 0 then
  1672. Output.WriteByte(Ord(vaTrue))
  1673. else if CompareText(parser.TokenString, 'False') = 0 then
  1674. Output.WriteByte(Ord(vaFalse))
  1675. else if CompareText(parser.TokenString, 'nil') = 0 then
  1676. Output.WriteByte(Ord(vaNil))
  1677. else
  1678. begin
  1679. Output.WriteByte(Ord(vaIdent));
  1680. WriteString(parser.TokenComponentIdent);
  1681. end;
  1682. Parser.NextToken;
  1683. end;
  1684. // Set
  1685. '[':
  1686. begin
  1687. parser.NextToken;
  1688. Output.WriteByte(Ord(vaSet));
  1689. if parser.Token <> ']' then
  1690. while True do
  1691. begin
  1692. parser.CheckToken(toSymbol);
  1693. WriteString(parser.TokenString);
  1694. parser.NextToken;
  1695. if parser.Token = ']' then
  1696. break;
  1697. parser.CheckToken(',');
  1698. parser.NextToken;
  1699. end;
  1700. Output.WriteByte(0);
  1701. parser.NextToken;
  1702. end;
  1703. // List
  1704. '(':
  1705. begin
  1706. parser.NextToken;
  1707. Output.WriteByte(Ord(vaList));
  1708. while parser.Token <> ')' do
  1709. ProcessValue;
  1710. Output.WriteByte(0);
  1711. parser.NextToken;
  1712. end;
  1713. // Collection
  1714. '<':
  1715. begin
  1716. parser.NextToken;
  1717. Output.WriteByte(Ord(vaCollection));
  1718. while parser.Token <> '>' do
  1719. begin
  1720. parser.CheckTokenSymbol('item');
  1721. parser.NextToken;
  1722. // ConvertOrder
  1723. Output.WriteByte(Ord(vaList));
  1724. while not parser.TokenSymbolIs('end') do
  1725. ProcessProperty;
  1726. parser.NextToken; // Skip 'end'
  1727. Output.WriteByte(0);
  1728. end;
  1729. Output.WriteByte(0);
  1730. parser.NextToken;
  1731. end;
  1732. // Binary data
  1733. '{':
  1734. begin
  1735. Output.WriteByte(Ord(vaBinary));
  1736. stream := TMemoryStream.Create;
  1737. try
  1738. parser.HexToBinary(stream);
  1739. WriteDWord(stream.Size);
  1740. Output.WriteBuffer(Stream.Memory^, stream.Size);
  1741. finally
  1742. stream.Free;
  1743. end;
  1744. parser.NextToken;
  1745. end;
  1746. else
  1747. parser.Error(SInvalidProperty);
  1748. end;
  1749. end;
  1750. procedure ProcessProperty;
  1751. var
  1752. name: String;
  1753. begin
  1754. // Get name of property
  1755. parser.CheckToken(toSymbol);
  1756. name := parser.TokenString;
  1757. while True do begin
  1758. parser.NextToken;
  1759. if parser.Token <> '.' then break;
  1760. parser.NextToken;
  1761. parser.CheckToken(toSymbol);
  1762. name := name + '.' + parser.TokenString;
  1763. end;
  1764. WriteString(name);
  1765. parser.CheckToken('=');
  1766. parser.NextToken;
  1767. ProcessValue;
  1768. end;
  1769. procedure ProcessObject;
  1770. var
  1771. Flags: Byte;
  1772. ObjectName, ObjectType: String;
  1773. ChildPos: Integer;
  1774. begin
  1775. if parser.TokenSymbolIs('OBJECT') then
  1776. Flags :=0 { IsInherited := False }
  1777. else begin
  1778. if parser.TokenSymbolIs('INHERITED') then
  1779. Flags := 1 { IsInherited := True; }
  1780. else begin
  1781. parser.CheckTokenSymbol('INLINE');
  1782. Flags := 4;
  1783. end;
  1784. end;
  1785. parser.NextToken;
  1786. parser.CheckToken(toSymbol);
  1787. ObjectName := '';
  1788. ObjectType := parser.TokenString;
  1789. parser.NextToken;
  1790. if parser.Token = ':' then begin
  1791. parser.NextToken;
  1792. parser.CheckToken(toSymbol);
  1793. ObjectName := ObjectType;
  1794. ObjectType := parser.TokenString;
  1795. parser.NextToken;
  1796. if parser.Token = '[' then begin
  1797. parser.NextToken;
  1798. ChildPos := parser.TokenInt;
  1799. parser.NextToken;
  1800. parser.CheckToken(']');
  1801. parser.NextToken;
  1802. Flags := Flags or 2;
  1803. end;
  1804. end;
  1805. if Flags <> 0 then begin
  1806. Output.WriteByte($f0 or Flags);
  1807. if (Flags and 2) <> 0 then
  1808. WriteInteger(ChildPos);
  1809. end;
  1810. WriteString(ObjectType);
  1811. WriteString(ObjectName);
  1812. // Convert property list
  1813. while not (parser.TokenSymbolIs('END') or
  1814. parser.TokenSymbolIs('OBJECT') or
  1815. parser.TokenSymbolIs('INHERITED') or
  1816. parser.TokenSymbolIs('INLINE')) do
  1817. ProcessProperty;
  1818. Output.WriteByte(0); // Terminate property list
  1819. // Convert child objects
  1820. while not parser.TokenSymbolIs('END') do ProcessObject;
  1821. parser.NextToken; // Skip end token
  1822. Output.WriteByte(0); // Terminate property list
  1823. end;
  1824. const
  1825. signature: PChar = 'TPF0';
  1826. begin
  1827. parser := TParser.Create(Input);
  1828. try
  1829. Output.WriteBuffer(signature[0], 4);
  1830. ProcessObject;
  1831. finally
  1832. parser.Free;
  1833. end;
  1834. end;
  1835. procedure ObjectResourceToText(Input, Output: TStream);
  1836. begin
  1837. Input.ReadResHeader;
  1838. ObjectBinaryToText(Input, Output);
  1839. end;
  1840. procedure ObjectTextToResource(Input, Output: TStream);
  1841. var
  1842. StartPos, FixupInfo: LongInt;
  1843. parser: TParser;
  1844. name: String;
  1845. begin
  1846. // Get form type name
  1847. StartPos := Input.Position;
  1848. parser := TParser.Create(Input);
  1849. try
  1850. if not parser.TokenSymbolIs('OBJECT') then parser.CheckTokenSymbol('INHERITED');
  1851. parser.NextToken;
  1852. parser.CheckToken(toSymbol);
  1853. parser.NextToken;
  1854. parser.CheckToken(':');
  1855. parser.NextToken;
  1856. parser.CheckToken(toSymbol);
  1857. name := parser.TokenString;
  1858. finally
  1859. parser.Free;
  1860. Input.Position := StartPos;
  1861. end;
  1862. name := UpperCase(name);
  1863. Output.WriteResourceHeader(name,FixupInfo); // Write resource header
  1864. ObjectTextToBinary(Input, Output); // Convert the stuff!
  1865. Output.FixupResourceHeader(FixupInfo); // Insert real resource data size
  1866. end;
  1867. { Utility routines }
  1868. function LineStart(Buffer, BufPos: PChar): PChar;
  1869. begin
  1870. Result := BufPos;
  1871. while Result > Buffer do begin
  1872. Dec(Result);
  1873. if Result[0] = #10 then break;
  1874. end;
  1875. end;
  1876. procedure CommonInit;
  1877. begin
  1878. SynchronizeTimeoutEvent:=RtlEventCreate;
  1879. InitCriticalSection(ThreadQueueLock);
  1880. MainThreadID:=GetCurrentThreadID;
  1881. ExternalThreads := TThreadList.Create;
  1882. TThread.FProcessorCount := CPUCount;
  1883. InitCriticalsection(ResolveSection);
  1884. InitHandlerList:=Nil;
  1885. FindGlobalComponentList:=nil;
  1886. IntConstList := TThreadList.Create;
  1887. ClassList := TThreadList.Create;
  1888. ClassAliasList := TStringList.Create;
  1889. { on unix this maps to a simple rw synchornizer }
  1890. GlobalNameSpace := TMultiReadExclusiveWriteSynchronizer.Create;
  1891. RegisterInitComponentHandler(TComponent,@DefaultInitHandler);
  1892. end;
  1893. procedure CommonCleanup;
  1894. var
  1895. i: Integer;
  1896. tmpentry: TThread.PThreadQueueEntry;
  1897. begin
  1898. GlobalNameSpace.BeginWrite;
  1899. with IntConstList.LockList do
  1900. try
  1901. for i := 0 to Count - 1 do
  1902. TIntConst(Items[I]).Free;
  1903. finally
  1904. IntConstList.UnlockList;
  1905. end;
  1906. IntConstList.Free;
  1907. ClassList.Free;
  1908. ClassAliasList.Free;
  1909. RemoveFixupReferences(nil, '');
  1910. DoneCriticalsection(ResolveSection);
  1911. GlobalLists.Free;
  1912. ComponentPages.Free;
  1913. FreeAndNil(NeedResolving);
  1914. { GlobalNameSpace is an interface so this is enough }
  1915. GlobalNameSpace:=nil;
  1916. if (InitHandlerList<>Nil) then
  1917. for i := 0 to InitHandlerList.Count - 1 do
  1918. TInitHandler(InitHandlerList.Items[I]).Free;
  1919. InitHandlerList.Free;
  1920. InitHandlerList:=Nil;
  1921. FindGlobalComponentList.Free;
  1922. FindGlobalComponentList:=nil;
  1923. ExternalThreadsCleanup:=True;
  1924. with ExternalThreads.LockList do
  1925. try
  1926. for i := 0 to Count - 1 do
  1927. TThread(Items[i]).Free;
  1928. finally
  1929. ExternalThreads.UnlockList;
  1930. end;
  1931. FreeAndNil(ExternalThreads);
  1932. RtlEventDestroy(SynchronizeTimeoutEvent);
  1933. { clean up the queue, but keep in mind that the entries used for Synchronize
  1934. are owned by the corresponding TThread }
  1935. while Assigned(ThreadQueueHead) do begin
  1936. tmpentry := ThreadQueueHead;
  1937. ThreadQueueHead := tmpentry^.Next;
  1938. if not Assigned(tmpentry^.SyncEvent) then
  1939. Dispose(tmpentry);
  1940. end;
  1941. DoneCriticalSection(ThreadQueueLock);
  1942. end;
  1943. { TFiler implementation }
  1944. {$i filer.inc}
  1945. { TReader implementation }
  1946. {$i reader.inc}
  1947. { TWriter implementations }
  1948. {$i writer.inc}
  1949. {$i twriter.inc}