classes.inc 64 KB

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