2
0

classes.inc 64 KB

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