added jitforms

ShanE

git-svn-id: trunk@123 -
This commit is contained in:
lazarus 2001-01-15 23:37:16 +00:00
parent 1476ee9a17
commit fa7e42fde5
2 changed files with 573 additions and 0 deletions

1
.gitattributes vendored
View File

@ -78,6 +78,7 @@ designer/customeditor.pp svneol=native#text/pascal
designer/designer.pp svneol=native#text/pascal
designer/designerwidget.pp svneol=native#text/pascal
designer/filesystem.pp svneol=native#text/pascal
designer/jitforms.pp svneol=native#text/pascal
designer/lazarus_control_images.lrs svneol=native#text/pascal
designer/objectinspector.pp svneol=native#text/pascal
designer/propedits.pp svneol=native#text/pascal

572
designer/jitforms.pp Normal file
View File

@ -0,0 +1,572 @@
unit jitforms;
{
Author: Mattias Gaertner
Abstract:
This unit defines a list of forms descendents. The forms are normal TForm
descendents with one exception: Every form has its own class. These classes
are changeable at runtime, so that IDEs can add, remove or rename methods
and such stuff. Also these forms can be loaded from streams and missing
components and methods are added just-in-time to the class definition.
Hence the name for the class: TJITForms.
Subcomponents are looked up in the list of registered components
(TJITForms.RegCompList).
ToDo:
-Add recursion needed for frames.
-activate SetDesigning in TJITForm.Create when LCL is ready for components
in designing state
}
{$mode objfpc}
interface
uses Classes, SysUtils, CompReg, Forms, Controls;
type
//----------------------------------------------------------------------------
// Just-In-Time-Form List
TJITForms = class(TPersistent)
private
FForms: TList;
FCurReadForm:TForm;
FCurReadClass:TClass;
FRegCompList:TRegisteredComponentList;
// jit procedures
function CreatevmtCopy(SourceClass:TClass; const NewClassName:ShortString):Pointer;
procedure FreevmtCopy(vmtCopy:Pointer);
procedure DoAddNewMethod(JITClass:TClass; AName:ShortString; ACode:Pointer);
// AddNewMethod does not check if method already exists
procedure DoRemoveMethod(JITClass:TClass; AName:ShortString; var OldCode:Pointer);
// RemoveMethod does not free code memory
procedure DoRenameMethod(JITClass:TClass; OldName,NewName:ShortString);
procedure DoRenameClass(JITClass:TClass; NewName:ShortString);
// TReader events
procedure ReaderFindMethod(Reader: TReader; const FindMethodName: Ansistring;
var Address: Pointer; var Error: Boolean);
procedure ReaderSetName(Reader: TReader; Component: TComponent;
var NewName: Ansistring);
procedure ReaderReferenceName(Reader: TReader; var RefName: Ansistring);
procedure ReaderAncestorNotFound(Reader: TReader; const ComponentName: Ansistring;
ComponentClass: TPersistentClass; var Component: TComponent);
procedure ReaderError(Reader: TReader; const Message: Ansistring;
var Handled: Boolean);
procedure ReaderFindComponentClass(Reader: TReader; const FindClassName: Ansistring;
var ComponentClass: TComponentClass);
procedure ReaderCreateComponent(Reader: TReader;
ComponentClass: TComponentClass; var Component: TComponent);
// some useful functions
function GetItem(Index:integer):TForm;
function GetClassNameFromStream(s:TStream):shortstring;
function DoCreateJITForm(NewFormName,NewClassName:shortstring):integer;
function OnFindGlobalComponent(const AName:AnsiString):TComponent;
public
property Items[Index:integer]:TForm read GetItem; default;
function Count:integer;
property RegCompList:TRegisteredComponentList read FRegCompList write FRegCompList;
function AddNewJITForm:integer;
function AddJITFormFromStream(BinStream:TStream):integer;
procedure DestroyJITForm(JITForm:TForm);
procedure DestroyJITForm(Index:integer);
function IndexOf(JITForm:TForm):integer;
function FindFormByClassName(AClassName:shortstring):integer;
function FindFormByName(AName:shortstring):integer;
procedure GetUnusedNames(var FormName,FormClassName:shortstring);
procedure AddNewMethod(JITForm:TForm; AName:ShortString);
procedure RemoveMethod(JITForm:TForm; AName:ShortString);
procedure RenameMethod(JITForm:TForm; OldName,NewName:ShortString);
procedure RenameFormAndClass(JITForm:TForm; NewName:ShortString);
constructor Create;
destructor Destroy; override;
published
// the dummy template 'procedure of object' for all events
procedure DoNothing;
end;
implementation
type
//----------------------------------------------------------------------------
// TJITForm is a template TForm descendent class that can be altered at
// runtime
TJITForm = class (TForm)
public
constructor Create(AOwner: TComponent); override;
end;
TJITFormClass = class of TJITForm;
//----------------------------------------------------------------------------
var
MyFindGlobalComponentProc:function(const AName:AnsiString):TComponent of object;
function MyFindGlobalComponent(const AName:AnsiString):TComponent;
begin
Result:=MyFindGlobalComponentProc(AName);
end;
{ TJITForm }
constructor TJITForm.Create(AOwner: TComponent);
begin
// XXX ToDo: uncomment this when LCL is ready for csDesigning
//SetDesigning(true);
inherited Create(AOwner);
end;
{ TJITForms }
constructor TJITForms.Create;
begin
inherited Create;
FForms:=TList.Create;
end;
destructor TJITForms.Destroy;
var a:integer;
begin
for a:=0 to FForms.Count-1 do
DestroyJITForm(a);
FForms.Free;
inherited Destroy;
end;
function TJITForms.GetItem(Index:integer):TForm;
begin
Result:=TForm(FForms[Index]);
end;
function TJITForms.Count:integer;
begin
Result:=FForms.Count;
end;
function TJITForms.IndexOf(JITForm:TForm):integer;
begin
Result:=Count-1;
while (Result>=0) and (Items[Result]<>JITForm) do dec(Result);
end;
procedure TJITForms.DestroyJITForm(JITForm:TForm);
var a:integer;
begin
a:=IndexOf(JITForm);
if a>=0 then DestroyJITForm(a);
end;
procedure TJITForms.DestroyJITForm(Index:integer);
var OldClass:TClass;
begin
OldClass:=Items[Index].ClassType;
Items[Index].Free;
FreevmtCopy(OldClass);
end;
function TJITForms.FindFormByClassName(AClassName:shortstring):integer;
begin
AClassName:=uppercase(AClassName);
Result:=FForms.Count-1;
while (Result>=0) and (uppercase(Items[Result].ClassName)<>AClassName) do
dec(Result);
end;
function TJITForms.FindFormByName(AName:shortstring):integer;
begin
AName:=uppercase(AName);
Result:=FForms.Count-1;
while (Result>=0) and (uppercase(Items[Result].Name)<>AName) do
dec(Result);
end;
procedure TJITForms.GetUnusedNames(var FormName,FormClassName:shortstring);
var a:integer;
begin
a:=1;
repeat
FormName:='Form'+IntToStr(a);
FormClassName:='TForm'+IntToStr(a);
until (FindFormByName(FormName)<0) and (FindFormByClassName(FormClassName)<0);
end;
function TJITForms.DoCreateJITForm(
NewFormName,NewClassName:shortstring):integer;
var
Instance:TComponent;
begin
Result:=-1;
// create new class and an instance
writeln('[TJITForms.DoCreateJITForm] Creating new JIT class '''+NewClassName+''' ...');
Pointer(FCurReadClass):=CreatevmtCopy(TJITForm,'TJITForm');
writeln('[TJITForms.DoCreateJITForm] Creating an instance of JIT class '''+NewClassName+''' ...');
Instance:=TComponent(FCurReadClass.NewInstance);
writeln('[TJITForms.DoCreateJITForm] Initializing new instance ...');
TComponent(FCurReadForm):=Instance;
try
Instance.Create(Application);
if NewFormName<>'' then
Instance.Name:=NewFormName;
DoRenameClass(FCurReadClass,NewClassName);
writeln('[TJITForms.DoCreateJITForm] Initialization was successful!');
except
TComponent(FCurReadForm):=nil;
writeln('[TJITForms.DoCreateJITForm] Error while creating instance');
raise;
end;
Result:=FForms.Add(FCurReadForm);
end;
function TJITForms.AddNewJITForm:integer;
var NewFormName,NewClassName:shortstring;
begin
GetUnusedNames(NewFormName,NewClassName);
Result:=DoCreateJITForm(NewFormName,NewClassName);
end;
function TJITForms.GetClassNameFromStream(s:TStream):shortstring;
var Signature:shortstring;
NameLen:byte;
begin
Result:='';
// read signature
Signature:='1234';
s.Read(Signature[1],length(Signature));
if Signature<>'TPF0' then exit;
// read classname length
NameLen:=0;
s.Read(NameLen,1);
// read classname
if NameLen>0 then begin
SetLength(Result,NameLen);
s.Read(Result[1],NameLen);
end;
s.Position:=0;
end;
function TJITForms.AddJITFormFromStream(BinStream:TStream):integer;
// 0 = ok
// -1 = invalid stream
var
Reader:TReader;
NewClassName:shortstring;
a:integer;
begin
Result:=0;
NewClassName:=GetClassNameFromStream(BinStream);
if NewClassName='' then begin
Result:=-1; exit;
end;
try
Result:=DoCreateJITForm('',NewClassName);
Reader:=TReader.Create(BinStream,4096);
MyFindGlobalComponentProc:=@OnFindGlobalComponent;
FindGlobalComponent:=@MyFindGlobalComponent;
try
// connect TReader events
Reader.OnError:=@ReaderError;
Reader.OnFindMethod:=@ReaderFindMethod;
Reader.OnSetName:=@ReaderSetName;
Reader.OnReferenceName:=@ReaderReferenceName;
Reader.OnAncestorNotFound:=@ReaderAncestorNotFound;
Reader.OnCreateComponent:=@ReaderCreateComponent;
Reader.OnFindComponentClass:=@ReaderFindComponentClass;
Reader.ReadRootComponent(FCurReadForm);
// MG: workaround til visible=true is default
for a:=0 to FCurReadForm.ComponentCount-1 do begin
if FCurReadForm.Components[a] is TControl then
TControl(FCurReadForm.Components[a]).Visible:=true;
end;
// MG: end of workaround
FCurReadForm.Show;
finally
FindGlobalComponent:=nil;
Reader.Free;
end;
Result:=0;
except
writeln('[TJITForms.AddJITFormFromStream] ERROR reading form stream'
+' of Class ''',NewClassName,'''');
Result:=-1;
end;
end;
function TJITForms.OnFindGlobalComponent(const AName:AnsiString):TComponent;
begin
Result:=Application.FindComponent(AName);
end;
procedure TJITForms.AddNewMethod(JITForm:TForm; AName:ShortString);
var CodeTemplate,NewCode:Pointer;
CodeSize:integer;
begin
if JITForm.MethodAddress(AName)<>nil then exit;
CodeTemplate:=MethodAddress('DoNothing');
CodeSize:=100; // !!! what is the real codesize of DoNothing? !!!
GetMem(NewCode,CodeSize);
Move(CodeTemplate^,NewCode^,CodeSize);
DoAddNewMethod(JITForm.ClassType,AName,NewCode);
end;
procedure TJITForms.RemoveMethod(JITForm:TForm; AName:ShortString);
var OldCode:Pointer;
begin
OldCode:=nil;
DoRemoveMethod(JITForm.ClassType,AName,OldCode);
FreeMem(OldCode);
end;
procedure TJITForms.RenameMethod(JITForm:TForm; OldName,NewName:ShortString);
begin
DoRenameMethod(JITForm.ClassType,OldName,NewName);
end;
procedure TJITForms.RenameFormAndClass(JITForm:TForm; NewName:ShortString);
begin
DoRenameClass(JITForm.ClassType,NewName);
JITForm.Name:=NewName;
end;
//------------------------------------------------------------------------------
// adding, removing and renaming of classes and methods at runtime
type
// these definitions are copied from objpas.inc
TMethodNameRec = packed record
Name : PShortString;
Addr : Pointer;
end;
TMethodNameTable = packed record
Count : DWord;
Entries : packed array[0..0] of TMethodNameRec;
end;
PMethodNameTable = ^TMethodNameTable;
function TJITForms.CreatevmtCopy(SourceClass:TClass;
const NewClassName:ShortString):Pointer;
const
vmtSize:integer=2000; //XXX how big is the vmt of class TJITForm ?
var MethodTable, NewMethodTable : PMethodNameTable;
MethodTableSize: integer;
begin
//writeln('[TJITForms.CreatevmtCopy] SourceClass='''+SourceClass.ClassName+''''
// +' NewClassName='''+NewClassName+'''');
// create copy of vmt
GetMem(Result,vmtSize);
// type of self is class of TJITForm => it points to the vmt
Move(Pointer(SourceClass)^,Result^,vmtSize);
// create copy of methodtable
MethodTable:=PMethodNameTable((Pointer(SourceClass)+vmtMethodTable)^);
if Assigned(MethodTable) then begin
MethodTableSize:=SizeOf(DWord)+
MethodTable^.Count*SizeOf(TMethodNameRec);
GetMem(NewMethodTable,MethodTableSize);
Move(MethodTable^,NewMethodTable^,MethodTableSize);
PPointer(Result+vmtMethodTable)^:=NewMethodTable;
end;
PShortString((Pointer(Result)+vmtClassName)^)^:=NewClassName;
end;
procedure TJITForms.FreevmtCopy(vmtCopy:Pointer);
var MethodTable : PMethodNameTable;
begin
//writeln('[TJITForms.FreevmtCopy] ClassName='''+TClass(vmtCopy).ClassName+'''');
if vmtCopy=nil then exit;
MethodTable:=PMethodNameTable((Pointer(vmtCopy)+vmtMethodTable)^);
if (Assigned(MethodTable)) then
FreeMem(MethodTable);
FreeMem(vmtCopy);
end;
procedure TJITForms.DoAddNewMethod(JITClass:TClass;
AName:ShortString; ACode:Pointer);
var OldMethodTable,NewMethodTable: PMethodNameTable;
NewMethodTableSize:integer;
begin
//writeln('[TJITForms.AddNewMethod] '''+JITClass.ClassName+'.'+AName+'''');
OldMethodTable:=PMethodNameTable((Pointer(JITClass)+vmtMethodTable)^);
if Assigned(OldMethodTable) then begin
NewMethodTableSize:=SizeOf(DWord)+
(OldMethodTable^.Count + 1)*SizeOf(TMethodNameRec);
end else begin
NewMethodTableSize:=SizeOf(DWord)+SizeOf(TMethodNameRec);
end;
GetMem(NewMethodTable,NewMethodTableSize);
if Assigned(OldMethodTable) then begin
Move(OldMethodTable^,NewMethodTable^,
NewMethodTableSize-SizeOf(TMethodNameRec));
NewMethodTable^.Count:=NewMethodTable^.Count+1;
end else begin
NewMethodTable^.Count:=1;
end;
{$R-}
//for a:=0 to NewMethodTable^.Count-2 do
// writeln(a,'=',NewMethodTable^.Entries[a].Name^,' $'
// ,HexStr(Integer(NewMethodTable^.Entries[a].Name),8));
with NewMethodTable^.Entries[NewMethodTable^.Count-1] do begin
GetMem(Name,256);
Name^:=AName;
Addr:=ACode;
end;
//for a:=0 to NewMethodTable^.Count-1 do
// writeln(a,'=',NewMethodTable^.Entries[a].Name^,' $'
// ,HexStr(Integer(NewMethodTable^.Entries[a].Name),8));
{$R+}
PMethodNameTable((Pointer(JITClass)+vmtMethodTable)^):=NewMethodTable;
if Assigned(OldMethodTable) then
FreeMem(OldMethodTable);
end;
procedure TJITForms.DoRemoveMethod(JITClass:TClass; AName:ShortString;
var OldCode:Pointer);
var OldMethodTable, NewMethodTable: PMethodNameTable;
NewMethodTableSize:integer;
a:cardinal;
begin
writeln('[TJITForms.RemoveMethod] '''+JITClass.ClassName+'.'+AName+'''');
AName:=uppercase(AName);
OldMethodTable:=PMethodNameTable((Pointer(JITClass)+vmtMethodTable)^);
OldCode:=nil;
if Assigned(OldMethodTable) then begin
a:=0;
while a<OldMethodTable^.Count do begin
{$R-}
if uppercase(OldMethodTable^.Entries[a].Name^)=AName then begin
OldCode:=OldMethodTable^.Entries[a].Addr;
FreeMem(OldMethodTable^.Entries[a].Name);
if OldMethodTable^.Count>0 then begin
NewMethodTableSize:=SizeOf(DWord)+
OldMethodTable^.Count*SizeOf(TMethodNameRec);
GetMem(NewMethodTable,NewMethodTableSize);
NewMethodTable^.Count:=OldMethodTable^.Count-1;
Move(OldMethodTable^,NewMethodTable^,SizeOf(DWord)+
a*SizeOf(TMethodNameRec));
Move(OldMethodTable^.Entries[a],NewMethodTable^.Entries[a+1],
SizeOf(DWord)+a*SizeOf(TMethodNameRec));
end else begin
NewMethodTable:=nil;
end;
PMethodNameTable((Pointer(JITClass)+vmtMethodTable)^):=NewMethodTable;
FreeMem(OldMethodTable);
break;
end;
{$R+}
inc(a);
end;
end;
end;
procedure TJITForms.DoRenameMethod(JITClass:TClass;
OldName,NewName:ShortString);
var MethodTable: PMethodNameTable;
a:integer;
begin
writeln('[TJITForms.RenameMethod] ClassName='''+JITClass.ClassName+''''
+' OldName='''+OldName+''' NewName='''+OldName+'''');
OldName:=uppercase(OldName);
MethodTable:=PMethodNameTable((Pointer(JITClass)+vmtMethodTable)^);
if Assigned(MethodTable) then begin
for a:=0 to MethodTable^.Count-1 do begin
if uppercase(MethodTable^.Entries[a].Name^)=OldName then
MethodTable^.Entries[a].Name^:=NewName;
end;
end;
end;
procedure TJITForms.DoRenameClass(JITClass:TClass; NewName:ShortString);
begin
writeln('[TJITForms.RenameClass] OldName='''+JITClass.ClassName
+''' NewName='''+NewName+''' ');
PShortString((Pointer(JITClass)+vmtClassName)^)^:=NewName;
end;
//------------------------------------------------------------------------------
{
TReader events.
If a LFM is streamed back into the corresponfing TForm descendent, all methods
and components are published members and TReader can set these values.
But at design time we do not have the corresponding TForm descendent. And
there is no compiled code, thus it must be produced it at runtime
(just-in-time).
}
procedure TJITForms.DoNothing;
// this is the template procedure for all unknown procedures
begin
// !!! do not write any code in here !!!
end;
procedure TJITForms.ReaderFindMethod(Reader: TReader;
const FindMethodName: Ansistring; var Address: Pointer; var Error: Boolean);
begin
// writeln('[TJITForms.ReaderFindMethod] '''+FindMethodName+'''');
if Address=nil then begin
AddNewMethod(FCurReadForm,FindMethodName);
Error:=false;
end;
end;
procedure TJITForms.ReaderSetName(Reader: TReader; Component: TComponent;
var NewName: Ansistring);
begin
// writeln('[TJITForms.ReaderSetName] OldName='''+Component.Name+''' NewName='''+NewName+'''');
end;
procedure TJITForms.ReaderReferenceName(Reader: TReader; var RefName: Ansistring);
begin
// writeln('[TJITForms.ReaderReferenceName] Name='''+RefName+'''');
end;
procedure TJITForms.ReaderAncestorNotFound(Reader: TReader;
const ComponentName: Ansistring; ComponentClass: TPersistentClass;
var Component: TComponent);
begin
// writeln('[TJITForms.ReaderAncestorNotFound] ComponentName='''+ComponentName
// +''' Component='''+Component.Name+'''');
end;
procedure TJITForms.ReaderError(Reader: TReader; const Message: Ansistring;
var Handled: Boolean);
begin
writeln('[TJITForms.ReaderError] '''+Message+'''');
end;
procedure TJITForms.ReaderFindComponentClass(Reader: TReader;
const FindClassName: Ansistring; var ComponentClass: TComponentClass);
var
RegComp:TRegisteredComponent;
begin
if ComponentClass=nil then begin
RegComp:=FRegCompList.FindComponentClassByName(FindClassName);
if RegComp<>nil then begin
//write('[TJITForms.ReaderFindComponentClass] '''+FindClassName
// +''' is registered');
ComponentClass:=RegComp.ComponentClass;
end else begin
write('[TJITForms.ReaderFindComponentClass] '''+FindClassName
+''' is unregistered');
end;
end;
writeln('');
end;
procedure TJITForms.ReaderCreateComponent(Reader: TReader;
ComponentClass: TComponentClass; var Component: TComponent);
begin
// writeln('[TJITForms.ReaderCreateComponent] Class='''+ComponentClass.ClassName+'''');
end;
//==============================================================================
end.