classes.inc 54 KB

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