classes.inc 64 KB

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