classes.inc 55 KB

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