classes.inc 64 KB

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