classes.inc 62 KB

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