classes.inc 73 KB

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