classes.inc 73 KB

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