classes.inc 64 KB

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