classes.inc 63 KB

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