classes.inc 72 KB

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