classes.inc 64 KB

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