fpc/rtl/objpas/classes/classes.inc
nickysn e342c11085 + introduced a private type TCodePtrList, which is the specialization
TFPGList<CodePointer> on archs/memory models where CodePointer is different
  than Pointer. On archs/memory models where CodePointer = Pointer, TCodePtrList
  is simply an alias of TList and the fgl unit isn't pulled in.
* the type of FindGlobalComponentList changed to TCodePtrList and the typecasts
  from TFindGlobalComponent (procvar type) to Pointer changed to CodePointer.
  This fixes compilation of unit classes in the i8086 medium memory model.

git-svn-id: trunk@25357 -
2013-08-23 19:25:51 +00:00

2178 lines
53 KiB
PHP

{
This file is part of the Free Component Library (FCL)
Copyright (c) 1999-2000 by Michael Van Canneyt and Florian Klaempfl
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**********************************************************************}
{**********************************************************************
* Class implementations are in separate files. *
**********************************************************************}
var
ClassList : TThreadlist;
ClassAliasList : TStringList;
{
Include all message strings
Add a language with IFDEF LANG_NAME
just befor the final ELSE. This way English will always be the default.
}
{$IFDEF LANG_GERMAN}
{$i constsg.inc}
{$ELSE}
{$IFDEF LANG_SPANISH}
{$i constss.inc}
{$ENDIF}
{$ENDIF}
{ Utility routines }
{$i util.inc}
{ TBits implementation }
{$i bits.inc}
{ All streams implementations: }
{ Tstreams THandleStream TFileStream TResourcseStreams TStringStream }
{ TCustomMemoryStream TMemoryStream }
{$i streams.inc}
{ TParser implementation}
{$i parser.inc}
{ TCollection and TCollectionItem implementations }
{$i collect.inc}
{ TList and TThreadList implementations }
{$i lists.inc}
{ TStrings and TStringList implementations }
{$i stringl.inc}
{ TThread implementation }
{ system independend threading code }
var
{ event executed by SychronizeInternal to wake main thread if it sleeps in
CheckSynchronize }
SynchronizeTimeoutEvent: PRtlEvent;
{ the head of the queue containing the entries to be Synchronized - Nil if the
queue is empty }
ThreadQueueHead: TThread.PThreadQueueEntry;
{ the tail of the queue containing the entries to be Synchronized - Nil if the
queue is empty }
ThreadQueueTail: TThread.PThreadQueueEntry;
{ used for serialized access to the queue }
ThreadQueueLock: TRtlCriticalSection;
{ this list holds all instances of external threads that need to be freed at
the end of the program }
ExternalThreads: TThreadList;
threadvar
{ the instance of the current thread; in case of an external thread this is
Nil until TThread.GetCurrentThread was called once (the RTLs need to ensure
that threadvars are initialized with 0!) }
CurrentThreadVar: TThread;
type
{ this type is used if a thread is created using
TThread.CreateAnonymousThread }
TAnonymousThread = class(TThread)
private
fProc: TProcedure;
protected
procedure Execute; override;
public
{ as in TThread aProc needs to be changed to TProc once closures are
supported }
constructor Create(aProc: TProcedure);
end;
procedure TAnonymousThread.Execute;
begin
fProc();
end;
constructor TAnonymousThread.Create(aProc: TProcedure);
begin
{ an anonymous thread is created suspended and with FreeOnTerminate set }
inherited Create(True);
FreeOnTerminate := True;
fProc := aProc;
end;
type
{ this type is used by TThread.GetCurrentThread if the thread does not yet
have a value in CurrentThreadVar (Note: the main thread is also created as
a TExternalThread) }
TExternalThread = class(TThread)
protected
{ dummy method to remove the warning }
procedure Execute; override;
public
constructor Create;
end;
procedure TExternalThread.Execute;
begin
{ empty }
end;
constructor TExternalThread.Create;
begin
FExternalThread := True;
{ the parameter is unimportant if FExternalThread is True }
inherited Create(False);
end;
function ThreadProc(ThreadObjPtr: Pointer): PtrInt;
var
FreeThread: Boolean;
Thread: TThread absolute ThreadObjPtr;
begin
{ if Suspend checks FSuspended before doing anything, make sure it }
{ knows we're currently not suspended (this flag may have been set }
{ to true if CreateSuspended was true) }
// Thread.FSuspended:=false;
// wait until AfterConstruction has been called, so we cannot
// free ourselves before TThread.Create has finished
// (since that one may check our VTM in case of $R+, and
// will call the AfterConstruction method in all cases)
// Thread.Suspend;
try
{ The thread may be already terminated at this point, e.g. if it was intially
suspended, or if it wasn't ever scheduled for execution for whatever reason.
So bypass user code if terminated. }
if not Thread.Terminated then begin
CurrentThreadVar := Thread;
Thread.Execute;
end;
except
Thread.FFatalException := TObject(AcquireExceptionObject);
end;
FreeThread := Thread.FFreeOnTerminate;
Result := Thread.FReturnValue;
Thread.FFinished := True;
Thread.DoTerminate;
if FreeThread then
Thread.Free;
EndThread(Result);
end;
{ system-dependent code }
{$i tthread.inc}
constructor TThread.Create(CreateSuspended: Boolean;
const StackSize: SizeUInt);
begin
inherited Create;
if FExternalThread then
FThreadID := GetCurrentThreadID
else
SysCreate(CreateSuspended, StackSize);
end;
destructor TThread.Destroy;
begin
if not FExternalThread then begin
SysDestroy;
if FHandle <> TThreadID(0) then
CloseThread(FHandle);
end;
RemoveQueuedEvents(Self);
DoneSynchronizeEvent;
{ set CurrentThreadVar to Nil? }
inherited Destroy;
end;
procedure TThread.Start;
begin
{ suspend/resume are now deprecated in Delphi (they also don't work
on most platforms in FPC), so a different method was required
to start a thread if it's create with fSuspended=true -> that's
what this method is for. }
Resume;
end;
function TThread.GetSuspended: Boolean;
begin
GetSuspended:=FSuspended;
end;
procedure TThread.AfterConstruction;
begin
inherited AfterConstruction;
// enable for all platforms once http://bugs.freepascal.org/view.php?id=16884
// is fixed for all platforms (in case the fix for non-unix platforms also
// requires this field at least)
{$if defined(unix) or defined(windows)}
if not FExternalThread and not FInitialSuspended then
Resume;
{$endif}
end;
procedure ExecuteThreadQueueEntry(aEntry: TThread.PThreadQueueEntry);
begin
if Assigned(aEntry^.Method) then
aEntry^.Method()
// enable once closures are supported
{else
aEntry^.ThreadProc();}
end;
procedure ThreadQueueAppend(aEntry: TThread.PThreadQueueEntry);
begin
{ do we really need a synchronized call? }
if GetCurrentThreadID = MainThreadID then begin
ExecuteThreadQueueEntry(aEntry);
if not Assigned(aEntry^.SyncEvent) then
Dispose(aEntry);
end else begin
System.EnterCriticalSection(ThreadQueueLock);
try
{ add the entry to the thread queue }
if Assigned(ThreadQueueTail) then begin
ThreadQueueTail^.Next := aEntry;
end else
ThreadQueueHead := aEntry;
ThreadQueueTail := aEntry;
finally
System.LeaveCriticalSection(ThreadQueueLock);
end;
{ ensure that the main thread knows that something awaits }
RtlEventSetEvent(SynchronizeTimeoutEvent);
if assigned(WakeMainThread) then
WakeMainThread(aEntry^.Thread);
{ is this a Synchronize or Queue entry? }
if Assigned(aEntry^.SyncEvent) then begin
RtlEventWaitFor(aEntry^.SyncEvent);
if Assigned(aEntry^.Exception) then
raise aEntry^.Exception;
end;
end;
end;
procedure TThread.InitSynchronizeEvent;
begin
if Assigned(FSynchronizeEntry) then
Exit;
New(FSynchronizeEntry);
FillChar(FSynchronizeEntry^, SizeOf(TThreadQueueEntry), 0);
FSynchronizeEntry^.Thread := Self;
FSynchronizeEntry^.SyncEvent := RtlEventCreate;
end;
procedure TThread.DoneSynchronizeEvent;
begin
if not Assigned(FSynchronizeEntry) then
Exit;
RtlEventDestroy(FSynchronizeEntry^.SyncEvent);
Dispose(FSynchronizeEntry);
FSynchronizeEntry := Nil;
end;
class procedure TThread.Synchronize(AThread: TThread; AMethod: TThreadMethod);
begin
{ ensure that we have a TThread instance }
if not Assigned(AThread) then
AThread := CurrentThread;
{ the Synchronize event is instantiated on demand }
AThread.InitSynchronizeEvent;
AThread.FSynchronizeEntry^.Exception := Nil;
AThread.FSynchronizeEntry^.Method := AMethod;
ThreadQueueAppend(AThread.FSynchronizeEntry);
AThread.FSynchronizeEntry^.Method := Nil;
AThread.FSynchronizeEntry^.Next := Nil;
end;
procedure TThread.Synchronize(AMethod: TThreadMethod);
begin
TThread.Synchronize(self,AMethod);
end;
function CheckSynchronize(timeout : longint=0) : boolean;
{ assumes being called from GUI thread }
var
exceptobj: Exception;
tmpentry: TThread.PThreadQueueEntry;
begin
result:=false;
{ first sanity check }
if Not IsMultiThread then
Exit
{ second sanity check }
else if GetCurrentThreadID<>MainThreadID then
raise EThread.CreateFmt(SCheckSynchronizeError,[GetCurrentThreadID])
else
begin
if timeout>0 then
begin
RtlEventWaitFor(SynchronizeTimeoutEvent,timeout);
end
else
RtlEventResetEvent(SynchronizeTimeoutEvent);
System.EnterCriticalSection(ThreadQueueLock);
try
{ Note: we don't need to pay attention to recursive calls to
Synchronize as those calls will be executed in the context of
the GUI thread and thus will be executed immediatly instead of
queuing them }
while Assigned(ThreadQueueHead) do begin
{ step 1: update the list }
tmpentry := ThreadQueueHead;
ThreadQueueHead := ThreadQueueHead^.Next;
if not Assigned(ThreadQueueHead) then
ThreadQueueTail := Nil;
{ step 2: execute the method }
exceptobj := Nil;
try
ExecuteThreadQueueEntry(tmpentry);
except
exceptobj := Exception(AcquireExceptionObject);
end;
{ step 3: error handling and cleanup }
if Assigned(tmpentry^.SyncEvent) then begin
{ for Synchronize entries we pass back the Exception and trigger
the event that Synchronize waits in }
tmpentry^.Exception := exceptobj;
RtlEventSetEvent(tmpentry^.SyncEvent)
end else begin
{ for Queue entries we dispose the entry and raise the exception }
Dispose(tmpentry);
if Assigned(exceptobj) then
raise exceptobj;
end;
end;
finally
System.LeaveCriticalSection(ThreadQueueLock);
end;
end;
end;
class function TThread.GetCurrentThread: TThread;
begin
{ if this is the first time GetCurrentThread is called for an external thread
we need to create a corresponding TExternalThread instance }
Result := CurrentThreadVar;
if not Assigned(Result) then begin
Result := TExternalThread.Create;
CurrentThreadVar := Result;
end;
end;
class function TThread.GetIsSingleProcessor: Boolean;
begin
Result := FProcessorCount <= 1;
end;
procedure TThread.Queue(aMethod: TThreadMethod);
begin
Queue(Self, aMethod);
end;
class procedure TThread.Queue(aThread: TThread; aMethod: TThreadMethod); static;
var
queueentry: PThreadQueueEntry;
begin
{ ensure that we have a valid TThread instance }
if not Assigned(aThread) then
aThread := CurrentThread;
New(queueentry);
FillChar(queueentry^, SizeOf(TThreadQueueEntry), 0);
queueentry^.Thread := aThread;
queueentry^.Method := aMethod;
{ the queueentry is freed by CheckSynchronize (or by RemoveQueuedEvents) }
ThreadQueueAppend(queueentry);
end;
class procedure TThread.RemoveQueuedEvents(aThread: TThread; aMethod: TThreadMethod);
var
entry, tmpentry, lastentry: PThreadQueueEntry;
begin
{ anything to do at all? }
if not Assigned(aThread) or not Assigned(aMethod) then
Exit;
System.EnterCriticalSection(ThreadQueueLock);
try
lastentry := Nil;
entry := ThreadQueueHead;
while Assigned(entry) do begin
{ first check for the thread }
if Assigned(aThread) and (entry^.Thread <> aThread) then begin
lastentry := entry;
entry := entry^.Next;
Continue;
end;
{ then check for the method }
if entry^.Method <> aMethod then begin
lastentry := entry;
entry := entry^.Next;
Continue;
end;
{ skip entries added by Synchronize }
if Assigned(entry^.SyncEvent) then begin
lastentry := entry;
entry := entry^.Next;
Continue;
end;
{ ok, we need to remove this entry }
tmpentry := entry;
if Assigned(lastentry) then
lastentry^.Next := entry^.Next;
entry := entry^.Next;
if ThreadQueueHead = tmpentry then
ThreadQueueHead := entry;
if ThreadQueueTail = tmpentry then
ThreadQueueTail := lastentry;
{ only dispose events added by Queue }
if not Assigned(tmpentry^.SyncEvent) then
Dispose(tmpentry);
end;
finally
System.LeaveCriticalSection(ThreadQueueLock);
end;
end;
class procedure TThread.RemoveQueuedEvents(aMethod: TThreadMethod);
begin
RemoveQueuedEvents(Nil, aMethod);
end;
class procedure TThread.RemoveQueuedEvents(aThread: TThread);
begin
RemoveQueuedEvents(aThread, Nil);
end;
class function TThread.CheckTerminated: Boolean;
begin
{ this method only works with threads created by TThread, so we can make a
shortcut here }
if not Assigned(CurrentThreadVar) then
raise EThreadExternalException.Create(SThreadExternal);
Result := CurrentThreadVar.FTerminated;
end;
class procedure TThread.SetReturnValue(aValue: Integer);
begin
{ this method only works with threads created by TThread, so we can make a
shortcut here }
if not Assigned(CurrentThreadVar) then
raise EThreadExternalException.Create(SThreadExternal);
CurrentThreadVar.FReturnValue := aValue;
end;
class function TThread.CreateAnonymousThread(aProc: TProcedure): TThread;
begin
if not Assigned(aProc) then
raise Exception.Create(SNoProcGiven);
Result := TAnonymousThread.Create(aProc);
end;
{$ifdef THREADNAME_IS_ANSISTRING}
{ the platform implements the AnsiString variant and the UnicodeString variant
simply calls the AnsiString variant }
class procedure TThread.NameThreadForDebugging(aThreadName: UnicodeString; aThreadID: TThreadID);
begin
NameThreadForDebugging(AnsiString(aThreadName), aThreadID);
end;
{$ifndef HAS_TTHREAD_NAMETHREADFORDEBUGGING}
class procedure TThread.NameThreadForDebugging(aThreadName: AnsiString; aThreadID: TThreadID);
begin
{ empty }
end;
{$endif}
{$else}
{$ifndef HAS_TTHREAD_NAMETHREADFORDEBUGGING}
{ the platform implements the UnicodeString variant and the AnsiString variant
simply calls the UnicodeString variant }
class procedure TThread.NameThreadForDebugging(aThreadName: UnicodeString; aThreadID: TThreadID);
begin
{ empty }
end;
{$endif}
class procedure TThread.NameThreadForDebugging(aThreadName: AnsiString; aThreadID: TThreadID);
begin
NameThreadForDebugging(UnicodeString(aThreadName), aThreadID);
end;
{$endif}
class procedure TThread.Yield;
begin
ThreadSwitch;
end;
class procedure TThread.Sleep(aMilliseconds: Cardinal);
begin
SysUtils.Sleep(aMilliseconds);
end;
class procedure TThread.SpinWait(aIterations: LongWord);
begin
{ yes, it's just a simple busy wait to burn some cpu cycles... and as the job
of this loop is to burn CPU cycles we switch off any optimizations that
could interfere with this (e.g. loop unrolling) }
{$PUSH}
{$OPTIMIZATION OFF}
while aIterations > 0 do
Dec(aIterations);
{$POP}
end;
{$ifndef HAS_TTHREAD_GETSYSTEMTIMES}
class procedure TThread.GetSystemTimes(out aSystemTimes: TSystemTimes);
begin
{ by default we just return a zeroed out record }
FillChar(aSystemTimes, SizeOf(aSystemTimes), 0);
end;
{$endif}
class function TThread.GetTickCount: LongWord;
begin
Result := SysUtils.GetTickCount;
end;
class function TThread.GetTickCount64: QWord;
begin
Result := SysUtils.GetTickCount64;
end;
{ TPersistent implementation }
{$i persist.inc }
{$i sllist.inc}
{$i resref.inc}
{ TComponent implementation }
{$i compon.inc}
{ TBasicAction implementation }
{$i action.inc}
{ TDataModule implementation }
{$i dm.inc}
{ Class and component registration routines }
{$I cregist.inc}
{ Interface related stuff }
{$I intf.inc}
{**********************************************************************
* Miscellaneous procedures and functions *
**********************************************************************}
function ExtractStrings(Separators, WhiteSpace: TSysCharSet; Content: PChar; Strings: TStrings): Integer;
var
b, c : pchar;
procedure SkipWhitespace;
begin
while (c^ in Whitespace) do
inc (c);
end;
procedure AddString;
var
l : integer;
s : string;
begin
l := c-b;
if l > 0 then
begin
if assigned(Strings) then
begin
setlength(s, l);
move (b^, s[1],l);
Strings.Add (s);
end;
inc (result);
end;
end;
var
quoted : char;
begin
result := 0;
c := Content;
Quoted := #0;
Separators := Separators + [#13, #10] - ['''','"'];
SkipWhitespace;
b := c;
while (c^ <> #0) do
begin
if (c^ = Quoted) then
begin
if ((c+1)^ = Quoted) then
inc (c)
else
Quoted := #0
end
else if (Quoted = #0) and (c^ in ['''','"']) then
Quoted := c^;
if (Quoted = #0) and (c^ in Separators) then
begin
AddString;
inc (c);
SkipWhitespace;
b := c;
end
else
inc (c);
end;
if (c <> b) then
AddString;
end;
{ Point and rectangle constructors }
function Point(AX, AY: Integer): TPoint;
begin
with Result do
begin
X := AX;
Y := AY;
end;
end;
function SmallPoint(AX, AY: SmallInt): TSmallPoint;
begin
with Result do
begin
X := AX;
Y := AY;
end;
end;
function Rect(ALeft, ATop, ARight, ABottom: Integer): TRect;
begin
with Result do
begin
Left := ALeft;
Top := ATop;
Right := ARight;
Bottom := ABottom;
end;
end;
function Bounds(ALeft, ATop, AWidth, AHeight: Integer): TRect;
begin
with Result do
begin
Left := ALeft;
Top := ATop;
Right := ALeft + AWidth;
Bottom := ATop + AHeight;
end;
end;
function PointsEqual(const P1, P2: TPoint): Boolean; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
begin
{ lazy, but should work }
result:=QWord(P1)=QWord(P2);
end;
function PointsEqual(const P1, P2: TSmallPoint): Boolean; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
begin
{ lazy, but should work }
result:=DWord(P1)=DWord(P2);
end;
function InvalidPoint(X, Y: Integer): Boolean;
begin
result:=(X=-1) and (Y=-1);
end;
function InvalidPoint(const At: TPoint): Boolean;
begin
result:=(At.x=-1) and (At.y=-1);
end;
function InvalidPoint(const At: TSmallPoint): Boolean;
begin
result:=(At.x=-1) and (At.y=-1);
end;
{ Object filing routines }
var
IntConstList: TThreadList;
type
TIntConst = class
IntegerType: PTypeInfo; // The integer type RTTI pointer
IdentToIntFn: TIdentToInt; // Identifier to Integer conversion
IntToIdentFn: TIntToIdent; // Integer to Identifier conversion
constructor Create(AIntegerType: PTypeInfo; AIdentToInt: TIdentToInt;
AIntToIdent: TIntToIdent);
end;
constructor TIntConst.Create(AIntegerType: PTypeInfo; AIdentToInt: TIdentToInt;
AIntToIdent: TIntToIdent);
begin
IntegerType := AIntegerType;
IdentToIntFn := AIdentToInt;
IntToIdentFn := AIntToIdent;
end;
procedure RegisterIntegerConsts(IntegerType: Pointer; IdentToIntFn: TIdentToInt;
IntToIdentFn: TIntToIdent);
begin
IntConstList.Add(TIntConst.Create(IntegerType, IdentToIntFn, IntToIdentFn));
end;
function FindIntToIdent(AIntegerType: Pointer): TIntToIdent;
var
i: Integer;
begin
with IntConstList.LockList do
try
for i := 0 to Count - 1 do
if TIntConst(Items[i]).IntegerType = AIntegerType then
exit(TIntConst(Items[i]).IntToIdentFn);
Result := nil;
finally
IntConstList.UnlockList;
end;
end;
function FindIdentToInt(AIntegerType: Pointer): TIdentToInt;
var
i: Integer;
begin
with IntConstList.LockList do
try
for i := 0 to Count - 1 do
with TIntConst(Items[I]) do
if TIntConst(Items[I]).IntegerType = AIntegerType then
exit(IdentToIntFn);
Result := nil;
finally
IntConstList.UnlockList;
end;
end;
function IdentToInt(const Ident: String; var Int: LongInt;
const Map: array of TIdentMapEntry): Boolean;
var
i: Integer;
begin
for i := Low(Map) to High(Map) do
if CompareText(Map[i].Name, Ident) = 0 then
begin
Int := Map[i].Value;
exit(True);
end;
Result := False;
end;
function IntToIdent(Int: LongInt; var Ident: String;
const Map: array of TIdentMapEntry): Boolean;
var
i: Integer;
begin
for i := Low(Map) to High(Map) do
if Map[i].Value = Int then
begin
Ident := Map[i].Name;
exit(True);
end;
Result := False;
end;
function GlobalIdentToInt(const Ident: String; var Int: LongInt):boolean;
var
i : Integer;
begin
with IntConstList.LockList do
try
for i := 0 to Count - 1 do
if TIntConst(Items[I]).IdentToIntFn(Ident, Int) then
Exit(True);
Result := false;
finally
IntConstList.UnlockList;
end;
end;
{ TPropFixup }
// Tainted. TPropFixup is being removed.
Type
TInitHandler = Class(TObject)
AHandler : TInitComponentHandler;
AClass : TComponentClass;
end;
{$ifndef i8086}
type
TCodePtrList = TList;
{$endif i8086}
Var
InitHandlerList : TList;
FindGlobalComponentList : TCodePtrList;
procedure RegisterFindGlobalComponentProc(AFindGlobalComponent: TFindGlobalComponent);
begin
if not(assigned(FindGlobalComponentList)) then
FindGlobalComponentList:=TCodePtrList.Create;
if FindGlobalComponentList.IndexOf(CodePointer(AFindGlobalComponent))<0 then
FindGlobalComponentList.Add(CodePointer(AFindGlobalComponent));
end;
procedure UnregisterFindGlobalComponentProc(AFindGlobalComponent: TFindGlobalComponent);
begin
if assigned(FindGlobalComponentList) then
FindGlobalComponentList.Remove(CodePointer(AFindGlobalComponent));
end;
function FindGlobalComponent(const Name: string): TComponent;
var
i : sizeint;
begin
FindGlobalComponent:=nil;
if assigned(FindGlobalComponentList) then
begin
for i:=FindGlobalComponentList.Count-1 downto 0 do
begin
FindGlobalComponent:=TFindGlobalComponent(FindGlobalComponentList[i])(name);
if assigned(FindGlobalComponent) then
break;
end;
end;
end;
procedure RegisterInitComponentHandler(ComponentClass: TComponentClass; Handler: TInitComponentHandler);
Var
I : Integer;
H: TInitHandler;
begin
If (InitHandlerList=Nil) then
InitHandlerList:=TList.Create;
H:=TInitHandler.Create;
H.Aclass:=ComponentClass;
H.AHandler:=Handler;
try
With InitHandlerList do
begin
I:=0;
While (I<Count) and not H.AClass.InheritsFrom(TInitHandler(Items[I]).AClass) do
Inc(I);
{ override? }
if (I<Count) and (TInitHandler(Items[I]).AClass=H.AClass) then
begin
TInitHandler(Items[I]).AHandler:=Handler;
H.Free;
end
else
InitHandlerList.Insert(I,H);
end;
except
H.Free;
raise;
end;
end;
{ all targets should at least include the sysres.inc dummy in the system unit to compile this }
function CreateComponentfromRes(const res : string;Inst : THandle;var Component : TComponent) : Boolean;
var
ResStream : TResourceStream;
begin
result:=true;
if Inst=0 then
Inst:=HInstance;
try
ResStream:=TResourceStream.Create(Inst,res,RT_RCDATA);
try
Component:=ResStream.ReadComponent(Component);
finally
ResStream.Free;
end;
except
on EResNotFound do
result:=false;
end;
end;
function DefaultInitHandler(Instance: TComponent; RootAncestor: TClass): Boolean;
function doinit(_class : TClass) : boolean;
begin
result:=false;
if (_class.ClassType=TComponent) or (_class.ClassType=RootAncestor) then
exit;
result:=doinit(_class.ClassParent);
result:=CreateComponentfromRes(_class.ClassName,0,Instance) or result;
end;
begin
GlobalNameSpace.BeginWrite;
try
result:=doinit(Instance.ClassType);
finally
GlobalNameSpace.EndWrite;
end;
end;
function InitInheritedComponent(Instance: TComponent; RootAncestor: TClass): Boolean;
Var
I : Integer;
begin
I:=0;
if not Assigned(InitHandlerList) then begin
Result := True;
Exit;
end;
Result:=False;
With InitHandlerList do
begin
I:=0;
// Instance is the normally the lowest one, so that one should be used when searching.
While Not result and (I<Count) do
begin
If (Instance.InheritsFrom(TInitHandler(Items[i]).AClass)) then
Result:=TInitHandler(Items[i]).AHandler(Instance,RootAncestor);
Inc(I);
end;
end;
end;
function InitComponentRes(const ResName: String; Instance: TComponent): Boolean;
begin
{ !!!: Too Win32-specific }
InitComponentRes := False;
end;
function ReadComponentRes(const ResName: String; Instance: TComponent): TComponent;
begin
{ !!!: Too Win32-specific }
ReadComponentRes := nil;
end;
function ReadComponentResEx(HInstance: THandle; const ResName: String): TComponent;
begin
{ !!!: Too Win32-specific in VCL }
ReadComponentResEx := nil;
end;
function ReadComponentResFile(const FileName: String; Instance: TComponent): TComponent;
var
FileStream: TStream;
begin
FileStream := TFileStream.Create(FileName, fmOpenRead {!!!:or fmShareDenyWrite});
try
Result := FileStream.ReadComponentRes(Instance);
finally
FileStream.Free;
end;
end;
procedure WriteComponentResFile(const FileName: String; Instance: TComponent);
var
FileStream: TStream;
begin
FileStream := TFileStream.Create(FileName, fmCreate);
try
FileStream.WriteComponentRes(Instance.ClassName, Instance);
finally
FileStream.Free;
end;
end;
Function FindNestedComponent(Root : TComponent; APath : String; CStyle : Boolean = True) : TComponent;
Function GetNextName : String; {$ifdef CLASSESINLINE} inline; {$endif CLASSESINLINE}
Var
P : Integer;
CM : Boolean;
begin
P:=Pos('.',APath);
CM:=False;
If (P=0) then
begin
If CStyle then
begin
P:=Pos('->',APath);
CM:=P<>0;
end;
If (P=0) Then
P:=Length(APath)+1;
end;
Result:=Copy(APath,1,P-1);
Delete(APath,1,P+Ord(CM));
end;
Var
C : TComponent;
S : String;
begin
If (APath='') then
Result:=Nil
else
begin
Result:=Root;
While (APath<>'') And (Result<>Nil) do
begin
C:=Result;
S:=Uppercase(GetNextName);
Result:=C.FindComponent(S);
If (Result=Nil) And (S='OWNER') then
Result:=C;
end;
end;
end;
threadvar
GlobalLoaded, GlobalLists: TFpList;
procedure BeginGlobalLoading;
begin
if not Assigned(GlobalLists) then
GlobalLists := TFpList.Create;
GlobalLists.Add(GlobalLoaded);
GlobalLoaded := TFpList.Create;
end;
{ Notify all global components that they have been loaded completely }
procedure NotifyGlobalLoading;
var
i: Integer;
begin
for i := 0 to GlobalLoaded.Count - 1 do
TComponent(GlobalLoaded[i]).Loaded;
end;
procedure EndGlobalLoading;
begin
{ Free the memory occupied by BeginGlobalLoading }
GlobalLoaded.Free;
GlobalLoaded := TFpList(GlobalLists.Last);
GlobalLists.Delete(GlobalLists.Count - 1);
if GlobalLists.Count = 0 then
begin
GlobalLists.Free;
GlobalLists := nil;
end;
end;
function CollectionsEqual(C1, C2: TCollection): Boolean;
begin
// !!!: Implement this
CollectionsEqual:=false;
end;
function CollectionsEqual(C1, C2: TCollection; Owner1, Owner2: TComponent): Boolean;
procedure stream_collection(s : tstream;c : tcollection;o : tcomponent);
var
w : twriter;
begin
w:=twriter.create(s,4096);
try
w.root:=o;
w.flookuproot:=o;
w.writecollection(c);
finally
w.free;
end;
end;
var
s1,s2 : tmemorystream;
begin
result:=false;
if (c1.classtype<>c2.classtype) or
(c1.count<>c2.count) then
exit;
if c1.count = 0 then
begin
result:= true;
exit;
end;
s1:=tmemorystream.create;
try
s2:=tmemorystream.create;
try
stream_collection(s1,c1,owner1);
stream_collection(s2,c2,owner2);
result:=(s1.size=s2.size) and (CompareChar(s1.memory^,s2.memory^,s1.size)=0);
finally
s2.free;
end;
finally
s1.free;
end;
end;
{ Object conversion routines }
type
CharToOrdFuncty = Function(var charpo: Pointer): Cardinal;
function CharToOrd(var P: Pointer): Cardinal;
begin
result:= ord(pchar(P)^);
inc(pchar(P));
end;
function WideCharToOrd(var P: Pointer): Cardinal;
begin
result:= ord(pwidechar(P)^);
inc(pwidechar(P));
end;
function Utf8ToOrd(var P:Pointer): Cardinal;
begin
// Should also check for illegal utf8 combinations
Result := Ord(PChar(P)^);
Inc(P);
if (Result and $80) <> 0 then
if (Ord(Result) and %11100000) = %11000000 then begin
Result := ((Result and %00011111) shl 6)
or (ord(PChar(P)^) and %00111111);
Inc(P);
end else if (Ord(Result) and %11110000) = %11100000 then begin
Result := ((Result and %00011111) shl 12)
or ((ord(PChar(P)^) and %00111111) shl 6)
or (ord((PChar(P)+1)^) and %00111111);
Inc(P,2);
end else begin
Result := ((ord(Result) and %00011111) shl 18)
or ((ord(PChar(P)^) and %00111111) shl 12)
or ((ord((PChar(P)+1)^) and %00111111) shl 6)
or (ord((PChar(P)+2)^) and %00111111);
Inc(P,3);
end;
end;
procedure ObjectBinaryToText(Input, Output: TStream; Encoding: TObjectTextEncoding);
procedure OutStr(s: String);
begin
if Length(s) > 0 then
Output.Write(s[1], Length(s));
end;
procedure OutLn(s: String);
begin
OutStr(s + LineEnding);
end;
procedure Outchars(P, LastP : Pointer; CharToOrdFunc: CharToOrdFuncty;
UseBytes: boolean = false);
var
res, NewStr: String;
w: Cardinal;
InString, NewInString: Boolean;
begin
if p = nil then begin
res:= '''''';
end
else
begin
res := '';
InString := False;
while P < LastP do
begin
NewInString := InString;
w := CharToOrdfunc(P);
if w = ord('''') then
begin //quote char
if not InString then
NewInString := True;
NewStr := '''''';
end
else if (Ord(w) >= 32) and ((Ord(w) < 127) or (UseBytes and (Ord(w)<256))) then
begin //printable ascii or bytes
if not InString then
NewInString := True;
NewStr := char(w);
end
else
begin //ascii control chars, non ascii
if InString then
NewInString := False;
NewStr := '#' + IntToStr(w);
end;
if NewInString <> InString then
begin
NewStr := '''' + NewStr;
InString := NewInString;
end;
res := res + NewStr;
end;
if InString then
res := res + '''';
end;
OutStr(res);
end;
procedure OutString(s: String);
begin
OutChars(Pointer(S),PChar(S)+Length(S),@CharToOrd,Encoding=oteLFM);
end;
procedure OutWString(W: WideString);
begin
OutChars(Pointer(W),pwidechar(W)+Length(W),@WideCharToOrd);
end;
procedure OutUString(W: UnicodeString);
begin
OutChars(Pointer(W),pwidechar(W)+Length(W),@WideCharToOrd);
end;
procedure OutUtf8Str(s: String);
begin
if Encoding=oteLFM then
OutChars(Pointer(S),PChar(S)+Length(S),@CharToOrd)
else
OutChars(Pointer(S),PChar(S)+Length(S),@Utf8ToOrd);
end;
function ReadWord : word; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
begin
Result:=Input.ReadWord;
Result:=LEtoN(Result);
end;
function ReadDWord : longword; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
begin
Result:=Input.ReadDWord;
Result:=LEtoN(Result);
end;
function ReadQWord : qword; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
begin
Input.ReadBuffer(Result,sizeof(Result));
Result:=LEtoN(Result);
end;
{$ifndef FPUNONE}
{$IFNDEF FPC_HAS_TYPE_EXTENDED}
function ExtendedToDouble(e : pointer) : double;
var mant : qword;
exp : smallint;
sign : boolean;
d : qword;
begin
move(pbyte(e)[0],mant,8); //mantissa : bytes 0..7
move(pbyte(e)[8],exp,2); //exponent and sign: bytes 8..9
mant:=LEtoN(mant);
exp:=LetoN(word(exp));
sign:=(exp and $8000)<>0;
if sign then exp:=exp and $7FFF;
case exp of
0 : mant:=0; //if denormalized, value is too small for double,
//so it's always zero
$7FFF : exp:=2047 //either infinity or NaN
else
begin
dec(exp,16383-1023);
if (exp>=-51) and (exp<=0) then //can be denormalized
begin
mant:=mant shr (-exp);
exp:=0;
end
else
if (exp<-51) or (exp>2046) then //exponent too large.
begin
Result:=0;
exit;
end
else //normalized value
mant:=mant shl 1; //hide most significant bit
end;
end;
d:=word(exp);
d:=d shl 52;
mant:=mant shr 12;
d:=d or mant;
if sign then d:=d or $8000000000000000;
Result:=pdouble(@d)^;
end;
{$ENDIF}
{$endif}
function ReadInt(ValueType: TValueType): Int64;
begin
case ValueType of
vaInt8: Result := ShortInt(Input.ReadByte);
vaInt16: Result := SmallInt(ReadWord);
vaInt32: Result := LongInt(ReadDWord);
vaInt64: Result := Int64(ReadQWord);
end;
end;
function ReadInt: Int64;
begin
Result := ReadInt(TValueType(Input.ReadByte));
end;
{$ifndef FPUNONE}
function ReadExtended : extended;
{$IFNDEF FPC_HAS_TYPE_EXTENDED}
var ext : array[0..9] of byte;
{$ENDIF}
begin
{$IFNDEF FPC_HAS_TYPE_EXTENDED}
Input.ReadBuffer(ext[0],10);
Result:=ExtendedToDouble(@(ext[0]));
{$ELSE}
Input.ReadBuffer(Result,sizeof(Result));
{$ENDIF}
end;
{$endif}
function ReadSStr: String;
var
len: Byte;
begin
len := Input.ReadByte;
SetLength(Result, len);
if (len > 0) then
Input.ReadBuffer(Result[1], len);
end;
function ReadLStr: String;
var
len: DWord;
begin
len := ReadDWord;
SetLength(Result, len);
if (len > 0) then
Input.ReadBuffer(Result[1], len);
end;
function ReadWStr: WideString;
var
len: DWord;
{$IFDEF ENDIAN_BIG}
i : integer;
{$ENDIF}
begin
len := ReadDWord;
SetLength(Result, len);
if (len > 0) then
begin
Input.ReadBuffer(Pointer(@Result[1])^, len*2);
{$IFDEF ENDIAN_BIG}
for i:=1 to len do
Result[i]:=widechar(SwapEndian(word(Result[i])));
{$ENDIF}
end;
end;
function ReadUStr: UnicodeString;
var
len: DWord;
{$IFDEF ENDIAN_BIG}
i : integer;
{$ENDIF}
begin
len := ReadDWord;
SetLength(Result, len);
if (len > 0) then
begin
Input.ReadBuffer(Pointer(@Result[1])^, len*2);
{$IFDEF ENDIAN_BIG}
for i:=1 to len do
Result[i]:=widechar(SwapEndian(word(Result[i])));
{$ENDIF}
end;
end;
procedure ReadPropList(indent: String);
procedure ProcessValue(ValueType: TValueType; Indent: String);
procedure ProcessBinary;
var
ToDo, DoNow, i: LongInt;
lbuf: array[0..31] of Byte;
s: String;
begin
ToDo := ReadDWord;
OutLn('{');
while ToDo > 0 do begin
DoNow := ToDo;
if DoNow > 32 then DoNow := 32;
Dec(ToDo, DoNow);
s := Indent + ' ';
Input.ReadBuffer(lbuf, DoNow);
for i := 0 to DoNow - 1 do
s := s + IntToHex(lbuf[i], 2);
OutLn(s);
end;
OutLn(indent + '}');
end;
var
s: String;
{ len: LongInt; }
IsFirst: Boolean;
{$ifndef FPUNONE}
ext: Extended;
{$endif}
begin
case ValueType of
vaList: begin
OutStr('(');
IsFirst := True;
while True do begin
ValueType := TValueType(Input.ReadByte);
if ValueType = vaNull then break;
if IsFirst then begin
OutLn('');
IsFirst := False;
end;
OutStr(Indent + ' ');
ProcessValue(ValueType, Indent + ' ');
end;
OutLn(Indent + ')');
end;
vaInt8: OutLn(IntToStr(ShortInt(Input.ReadByte)));
vaInt16: OutLn( IntToStr(SmallInt(ReadWord)));
vaInt32: OutLn(IntToStr(LongInt(ReadDWord)));
vaInt64: OutLn(IntToStr(Int64(ReadQWord)));
{$ifndef FPUNONE}
vaExtended: begin
ext:=ReadExtended;
Str(ext,S);// Do not use localized strings.
OutLn(S);
end;
{$endif}
vaString: begin
OutString(ReadSStr);
OutLn('');
end;
vaIdent: OutLn(ReadSStr);
vaFalse: OutLn('False');
vaTrue: OutLn('True');
vaBinary: ProcessBinary;
vaSet: begin
OutStr('[');
IsFirst := True;
while True do begin
s := ReadSStr;
if Length(s) = 0 then break;
if not IsFirst then OutStr(', ');
IsFirst := False;
OutStr(s);
end;
OutLn(']');
end;
vaLString:
begin
OutString(ReadLStr);
OutLn('');
end;
vaWString:
begin
OutWString(ReadWStr);
OutLn('');
end;
vaUString:
begin
OutWString(ReadWStr);
OutLn('');
end;
vaNil:
OutLn('nil');
vaCollection: begin
OutStr('<');
while Input.ReadByte <> 0 do begin
OutLn(Indent);
Input.Seek(-1, soFromCurrent);
OutStr(indent + ' item');
ValueType := TValueType(Input.ReadByte);
if ValueType <> vaList then
OutStr('[' + IntToStr(ReadInt(ValueType)) + ']');
OutLn('');
ReadPropList(indent + ' ');
OutStr(indent + ' end');
end;
OutLn('>');
end;
{vaSingle: begin OutLn('!!Single!!'); exit end;
vaCurrency: begin OutLn('!!Currency!!'); exit end;
vaDate: begin OutLn('!!Date!!'); exit end;}
vaUTF8String: begin
OutUtf8Str(ReadLStr);
OutLn('');
end;
else
Raise EReadError.CreateFmt(SErrInvalidPropertyType,[Ord(ValueType)]);
end;
end;
begin
while Input.ReadByte <> 0 do begin
Input.Seek(-1, soFromCurrent);
OutStr(indent + ReadSStr + ' = ');
ProcessValue(TValueType(Input.ReadByte), Indent);
end;
end;
procedure ReadObject(indent: String);
var
b: Byte;
ObjClassName, ObjName: String;
ChildPos: LongInt;
begin
// Check for FilerFlags
b := Input.ReadByte;
if (b and $f0) = $f0 then begin
if (b and 2) <> 0 then ChildPos := ReadInt;
end else begin
b := 0;
Input.Seek(-1, soFromCurrent);
end;
ObjClassName := ReadSStr;
ObjName := ReadSStr;
OutStr(Indent);
if (b and 1) <> 0 then OutStr('inherited')
else
if (b and 4) <> 0 then OutStr('inline')
else OutStr('object');
OutStr(' ');
if ObjName <> '' then
OutStr(ObjName + ': ');
OutStr(ObjClassName);
if (b and 2) <> 0 then OutStr('[' + IntToStr(ChildPos) + ']');
OutLn('');
ReadPropList(indent + ' ');
while Input.ReadByte <> 0 do begin
Input.Seek(-1, soFromCurrent);
ReadObject(indent + ' ');
end;
OutLn(indent + 'end');
end;
type
PLongWord = ^LongWord;
const
signature: PChar = 'TPF0';
begin
if Input.ReadDWord <> PLongWord(Pointer(signature))^ then
raise EReadError.Create('Illegal stream image' {###SInvalidImage});
ReadObject('');
end;
procedure ObjectBinaryToText(Input, Output: TStream);
begin
ObjectBinaryToText(Input,Output,oteDFM);
end;
procedure ObjectTextToBinary(Input, Output: TStream);
var
parser: TParser;
procedure WriteWord(w : word); {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
begin
w:=NtoLE(w);
Output.WriteWord(w);
end;
procedure WriteDWord(lw : longword); {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
begin
lw:=NtoLE(lw);
Output.WriteDWord(lw);
end;
procedure WriteQWord(qw : qword); {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
begin
qw:=NtoLE(qw);
Output.WriteBuffer(qw,sizeof(qword));
end;
{$ifndef FPUNONE}
{$IFNDEF FPC_HAS_TYPE_EXTENDED}
procedure DoubleToExtended(d : double; e : pointer);
var mant : qword;
exp : smallint;
sign : boolean;
begin
mant:=(qword(d) and $000FFFFFFFFFFFFF) shl 12;
exp :=(qword(d) shr 52) and $7FF;
sign:=(qword(d) and $8000000000000000)<>0;
case exp of
0 : begin
if mant<>0 then //denormalized value: hidden bit is 0. normalize it
begin
exp:=16383-1022;
while (mant and $8000000000000000)=0 do
begin
dec(exp);
mant:=mant shl 1;
end;
dec(exp); //don't shift, most significant bit is not hidden in extended
end;
end;
2047 : exp:=$7FFF //either infinity or NaN
else
begin
inc(exp,16383-1023);
mant:=(mant shr 1) or $8000000000000000; //unhide hidden bit
end;
end;
if sign then exp:=exp or $8000;
mant:=NtoLE(mant);
exp:=NtoLE(word(exp));
move(mant,pbyte(e)[0],8); //mantissa : bytes 0..7
move(exp,pbyte(e)[8],2); //exponent and sign: bytes 8..9
end;
{$ENDIF}
procedure WriteExtended(e : extended);
{$IFNDEF FPC_HAS_TYPE_EXTENDED}
var ext : array[0..9] of byte;
{$ENDIF}
begin
{$IFNDEF FPC_HAS_TYPE_EXTENDED}
DoubleToExtended(e,@(ext[0]));
Output.WriteBuffer(ext[0],10);
{$ELSE}
Output.WriteBuffer(e,sizeof(e));
{$ENDIF}
end;
{$endif}
procedure WriteString(s: String);
var size : byte;
begin
if length(s)>255 then size:=255
else size:=length(s);
Output.WriteByte(size);
if Length(s) > 0 then
Output.WriteBuffer(s[1], size);
end;
procedure WriteLString(Const s: String);
begin
WriteDWord(Length(s));
if Length(s) > 0 then
Output.WriteBuffer(s[1], Length(s));
end;
procedure WriteWString(Const s: WideString);
var len : longword;
{$IFDEF ENDIAN_BIG}
i : integer;
ws : widestring;
{$ENDIF}
begin
len:=Length(s);
WriteDWord(len);
if len > 0 then
begin
{$IFDEF ENDIAN_BIG}
setlength(ws,len);
for i:=1 to len do
ws[i]:=widechar(SwapEndian(word(s[i])));
Output.WriteBuffer(ws[1], len*sizeof(widechar));
{$ELSE}
Output.WriteBuffer(s[1], len*sizeof(widechar));
{$ENDIF}
end;
end;
procedure WriteInteger(value: Int64);
begin
if (value >= -128) and (value <= 127) then begin
Output.WriteByte(Ord(vaInt8));
Output.WriteByte(byte(value));
end else if (value >= -32768) and (value <= 32767) then begin
Output.WriteByte(Ord(vaInt16));
WriteWord(word(value));
end else if (value >= -2147483648) and (value <= 2147483647) then begin
Output.WriteByte(Ord(vaInt32));
WriteDWord(longword(value));
end else begin
Output.WriteByte(ord(vaInt64));
WriteQWord(qword(value));
end;
end;
procedure ProcessWideString(const left : widestring);
var ws : widestring;
begin
ws:=left+parser.TokenWideString;
while parser.NextToken = '+' do
begin
parser.NextToken; // Get next string fragment
if not (parser.Token in [toString,toWString]) then
parser.CheckToken(toWString);
ws:=ws+parser.TokenWideString;
end;
Output.WriteByte(Ord(vaWstring));
WriteWString(ws);
end;
procedure ProcessProperty; forward;
procedure ProcessValue;
var
{$ifndef FPUNONE}
flt: Extended;
{$endif}
s: String;
stream: TMemoryStream;
begin
case parser.Token of
toInteger:
begin
WriteInteger(parser.TokenInt);
parser.NextToken;
end;
{$ifndef FPUNONE}
toFloat:
begin
Output.WriteByte(Ord(vaExtended));
flt := Parser.TokenFloat;
WriteExtended(flt);
parser.NextToken;
end;
{$endif}
toString:
begin
s := parser.TokenString;
while parser.NextToken = '+' do
begin
parser.NextToken; // Get next string fragment
case parser.Token of
toString : s:=s+parser.TokenString;
toWString : begin
ProcessWideString(s);
exit;
end
else parser.CheckToken(toString);
end;
end;
if (length(S)>255) then
begin
Output.WriteByte(Ord(vaLString));
WriteLString(S);
end
else
begin
Output.WriteByte(Ord(vaString));
WriteString(s);
end;
end;
toWString:
ProcessWideString('');
toSymbol:
begin
if CompareText(parser.TokenString, 'True') = 0 then
Output.WriteByte(Ord(vaTrue))
else if CompareText(parser.TokenString, 'False') = 0 then
Output.WriteByte(Ord(vaFalse))
else if CompareText(parser.TokenString, 'nil') = 0 then
Output.WriteByte(Ord(vaNil))
else
begin
Output.WriteByte(Ord(vaIdent));
WriteString(parser.TokenComponentIdent);
end;
Parser.NextToken;
end;
// Set
'[':
begin
parser.NextToken;
Output.WriteByte(Ord(vaSet));
if parser.Token <> ']' then
while True do
begin
parser.CheckToken(toSymbol);
WriteString(parser.TokenString);
parser.NextToken;
if parser.Token = ']' then
break;
parser.CheckToken(',');
parser.NextToken;
end;
Output.WriteByte(0);
parser.NextToken;
end;
// List
'(':
begin
parser.NextToken;
Output.WriteByte(Ord(vaList));
while parser.Token <> ')' do
ProcessValue;
Output.WriteByte(0);
parser.NextToken;
end;
// Collection
'<':
begin
parser.NextToken;
Output.WriteByte(Ord(vaCollection));
while parser.Token <> '>' do
begin
parser.CheckTokenSymbol('item');
parser.NextToken;
// ConvertOrder
Output.WriteByte(Ord(vaList));
while not parser.TokenSymbolIs('end') do
ProcessProperty;
parser.NextToken; // Skip 'end'
Output.WriteByte(0);
end;
Output.WriteByte(0);
parser.NextToken;
end;
// Binary data
'{':
begin
Output.WriteByte(Ord(vaBinary));
stream := TMemoryStream.Create;
try
parser.HexToBinary(stream);
WriteDWord(stream.Size);
Output.WriteBuffer(Stream.Memory^, stream.Size);
finally
stream.Free;
end;
parser.NextToken;
end;
else
parser.Error(SInvalidProperty);
end;
end;
procedure ProcessProperty;
var
name: String;
begin
// Get name of property
parser.CheckToken(toSymbol);
name := parser.TokenString;
while True do begin
parser.NextToken;
if parser.Token <> '.' then break;
parser.NextToken;
parser.CheckToken(toSymbol);
name := name + '.' + parser.TokenString;
end;
WriteString(name);
parser.CheckToken('=');
parser.NextToken;
ProcessValue;
end;
procedure ProcessObject;
var
Flags: Byte;
ObjectName, ObjectType: String;
ChildPos: Integer;
begin
if parser.TokenSymbolIs('OBJECT') then
Flags :=0 { IsInherited := False }
else begin
if parser.TokenSymbolIs('INHERITED') then
Flags := 1 { IsInherited := True; }
else begin
parser.CheckTokenSymbol('INLINE');
Flags := 4;
end;
end;
parser.NextToken;
parser.CheckToken(toSymbol);
ObjectName := '';
ObjectType := parser.TokenString;
parser.NextToken;
if parser.Token = ':' then begin
parser.NextToken;
parser.CheckToken(toSymbol);
ObjectName := ObjectType;
ObjectType := parser.TokenString;
parser.NextToken;
if parser.Token = '[' then begin
parser.NextToken;
ChildPos := parser.TokenInt;
parser.NextToken;
parser.CheckToken(']');
parser.NextToken;
Flags := Flags or 2;
end;
end;
if Flags <> 0 then begin
Output.WriteByte($f0 or Flags);
if (Flags and 2) <> 0 then
WriteInteger(ChildPos);
end;
WriteString(ObjectType);
WriteString(ObjectName);
// Convert property list
while not (parser.TokenSymbolIs('END') or
parser.TokenSymbolIs('OBJECT') or
parser.TokenSymbolIs('INHERITED') or
parser.TokenSymbolIs('INLINE')) do
ProcessProperty;
Output.WriteByte(0); // Terminate property list
// Convert child objects
while not parser.TokenSymbolIs('END') do ProcessObject;
parser.NextToken; // Skip end token
Output.WriteByte(0); // Terminate property list
end;
const
signature: PChar = 'TPF0';
begin
parser := TParser.Create(Input);
try
Output.WriteBuffer(signature[0], 4);
ProcessObject;
finally
parser.Free;
end;
end;
procedure ObjectResourceToText(Input, Output: TStream);
begin
Input.ReadResHeader;
ObjectBinaryToText(Input, Output);
end;
procedure ObjectTextToResource(Input, Output: TStream);
var
StartPos, FixupInfo: LongInt;
parser: TParser;
name: String;
begin
// Get form type name
StartPos := Input.Position;
parser := TParser.Create(Input);
try
if not parser.TokenSymbolIs('OBJECT') then parser.CheckTokenSymbol('INHERITED');
parser.NextToken;
parser.CheckToken(toSymbol);
parser.NextToken;
parser.CheckToken(':');
parser.NextToken;
parser.CheckToken(toSymbol);
name := parser.TokenString;
finally
parser.Free;
Input.Position := StartPos;
end;
name := UpperCase(name);
Output.WriteResourceHeader(name,FixupInfo); // Write resource header
ObjectTextToBinary(Input, Output); // Convert the stuff!
Output.FixupResourceHeader(FixupInfo); // Insert real resource data size
end;
{ Utility routines }
function LineStart(Buffer, BufPos: PChar): PChar;
begin
Result := BufPos;
while Result > Buffer do begin
Dec(Result);
if Result[0] = #10 then break;
end;
end;
procedure CommonInit;
begin
SynchronizeTimeoutEvent:=RtlEventCreate;
InitCriticalSection(ThreadQueueLock);
MainThreadID:=GetCurrentThreadID;
ExternalThreads := TThreadList.Create;
TThread.FProcessorCount := CPUCount;
InitCriticalsection(ResolveSection);
InitHandlerList:=Nil;
FindGlobalComponentList:=nil;
IntConstList := TThreadList.Create;
ClassList := TThreadList.Create;
ClassAliasList := TStringList.Create;
{ on unix this maps to a simple rw synchornizer }
GlobalNameSpace := TMultiReadExclusiveWriteSynchronizer.Create;
RegisterInitComponentHandler(TComponent,@DefaultInitHandler);
end;
procedure CommonCleanup;
var
i: Integer;
tmpentry: TThread.PThreadQueueEntry;
begin
GlobalNameSpace.BeginWrite;
with IntConstList.LockList do
try
for i := 0 to Count - 1 do
TIntConst(Items[I]).Free;
finally
IntConstList.UnlockList;
end;
IntConstList.Free;
ClassList.Free;
ClassAliasList.Free;
RemoveFixupReferences(nil, '');
DoneCriticalsection(ResolveSection);
GlobalLists.Free;
ComponentPages.Free;
FreeAndNil(NeedResolving);
{ GlobalNameSpace is an interface so this is enough }
GlobalNameSpace:=nil;
if (InitHandlerList<>Nil) then
for i := 0 to InitHandlerList.Count - 1 do
TInitHandler(InitHandlerList.Items[I]).Free;
InitHandlerList.Free;
InitHandlerList:=Nil;
FindGlobalComponentList.Free;
FindGlobalComponentList:=nil;
with ExternalThreads.LockList do
try
for i := 0 to Count - 1 do
TThread(Items[i]).Free;
finally
ExternalThreads.UnlockList;
end;
FreeAndNil(ExternalThreads);
RtlEventDestroy(SynchronizeTimeoutEvent);
{ clean up the queue, but keep in mind that the entries used for Synchronize
are owned by the corresponding TThread }
while Assigned(ThreadQueueHead) do begin
tmpentry := ThreadQueueHead;
ThreadQueueHead := tmpentry^.Next;
if not Assigned(tmpentry^.SyncEvent) then
Dispose(tmpentry);
end;
DoneCriticalSection(ThreadQueueLock);
end;
{ TFiler implementation }
{$i filer.inc}
{ TReader implementation }
{$i reader.inc}
{ TWriter implementations }
{$i writer.inc}
{$i twriter.inc}