classes.inc 69 KB

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