classes.inc 63 KB

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