classes.inc 69 KB

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