classes.inc 64 KB

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