classes.inc 74 KB

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