classes.inc 63 KB

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