{ @abstract(Component wrapper for IFPS3 compiler and executer) A component wrapper for IFPS3, including debugging support. } unit uPSComponentExt; interface uses {$IFNDEF LINUX} Windows, {$ENDIF} SysUtils, Classes, uPSRuntime, uPSDebugger, uPSUtils, uPSComponent, contnrs, uPSCompiler, uPSC_dll, uPSR_dll, uPSPreProcessor, typInfo; const {alias to @link(ifps3.cdRegister)} cdRegister = uPSRuntime.cdRegister; {alias to @link(ifps3.cdPascal)} cdPascal = uPSRuntime.cdPascal; { alias to ifps3.cdCdecl } CdCdecl = uPSRuntime.CdCdecl; {alias to @link(ifps3.cdStdcall)} CdStdCall = uPSRuntime.CdStdCall; type {Alias to @link(ifps3.TPSCallingConvention)} TDelphiCallingConvention = uPSRuntime.TPSCallingConvention; {Alias to @link(ifps3.TPSRuntimeClassImporter)} TPSRuntimeClassImporter = uPSRuntime.TPSRuntimeClassImporter; TPSScriptExtension = class; {Base class for all plugins for the component} TPSOnCompCleanup = Function (Sender: TObject; aComp: TPSPascalCompiler):Boolean of object; TPSOnInsertProcedure = Procedure (Sender: TObject; aProc: String; OnTop: Boolean) of object; TPSOnException = procedure (Sender: TPSExec; ExError: TPSError; const ExParam: string; ExObject: TObject; ProcNo, Position: Cardinal) of object; TMethodList = class; TProcObj = Class private FName : String; fOwner : TMethodList; procedure SetName(const Value: String); public ProcType : TStringList; Method : TMethod; constructor create(aOwner: TMethodList); destructor Destroy; override; property Name: String read FName write SetName; end; TMethodObj = Class Instance : TPersistent; PropName : String; ProcName : String; end; TMethodList = class private fOwner : TPSScriptExtension; fProcList : TObjectList; fEventList : TObjectList; function GetObject(Index: Integer): TMethodObj; virtual; function GetProcObj(Index: Integer): TProcObj; function GetMethodName(Instance: TObject; PropName: String): String; procedure SetMethodName(Instance: TObject; PropName: String; const Value: String); procedure CreateProc(ProcName: string; aPropType: TTypeData); public constructor create(aOwner: TPSScriptExtension); destructor Destroy; override; function methodIndexOf(Instance: TObject; PropName: String):Integer; Function ProcIndexOf(Name: String): Integer; Procedure ListEventsName(EventType:string; List : TStrings); Procedure AddProcedure(ProcName, ProcType:String); procedure InsertMethod(NewProc: String; OnTop: Boolean = false); Procedure FillMethods; procedure ClearProcList; Procedure ClearAll; function ProcCount :Integer; Function MethodCount :Integer; property Procs[Index: Integer]: TProcObj read GetProcObj ; property Methods[Index: Integer]: TMethodObj read GetObject; property ProcName[Instance: TObject; PropName:String]: String read GetMethodName write SetMethodName; end; TPSScriptExtension = class(TPSScriptDebugger) private FOnBeforeCleanUp: TPSOnCompCleanup; FMethodList : TMethodList; FOnInsertMethod: TPSOnInsertProcedure; FNeedCompiling :Boolean; FOnScriptChance: TNotifyEvent; FOnException: TPSOnException; fItems, fInserts: TStrings; fScriptPos : Cardinal; fObjectNest: STring; Procedure GetCodeProps ; function GetProcName(Instance: TObject; PropName: String): string; procedure SetProcName(Instance: TObject; PropName: String; const Value: string); protected procedure Notification(AComponent: TComponent; Operation: TOperation); override; procedure DoVerifyProc(Sender: TPSScript; Proc: TPSInternalProcedure; const Decl: string; var Error: Boolean); reintroduce; Function DoBeforeCleanup(Sender: TObject; aComp: TPSPascalCompiler):Boolean; procedure DoScriptChance(sender:TObject); public {Create an instance of the CompExec component} constructor Create(AOwner: TComponent); override; {Destroy the CompExec component} destructor Destroy; override; function Compile: Boolean; Override; function Execute: Boolean; Override; { Create a list of all var's, const's, Type's and functions } Procedure GetValueDefs(aItems, aInserts: TStrings; Const aObjectNest: String=''; aScriptPos: Integer = 0); {Compile the source only when the source is modified} procedure CompileIfNeeded; {Is the source modified} Property NeedCompiling : Boolean read FNeedCompiling; {Fills all function in the script to there connected Events. This is called automatic after a succesfull Compilition} Procedure FillMethods; {Removes all events from the Objects Fills all function in the script to there connected Events. This function is automatic called before a Compilition} procedure ClearProcList; Procedure RemoveObjEvents(Obj: TObject); {This property helps you set the events that must becalled from within the script Instance is the object where the Propname must be set. You need te create the function yopur self in the script. When the new Procname dose not exists in the script, it is automatic created for you.} property ProcName[Instance: TObject; PropName:String]: string read GetProcName write SetProcName; property MethodList : TMethodList read FMethodList; published property OnBeforeCleanUp: TPSOnCompCleanup read FOnBeforeCleanUp write FOnBeforeCleanUp; // property OnInsertMethod : TPSOnInsertProcedure read FOnInsertMethod write FOnInsertMethod; Property OnScriptChance : TNotifyEvent read FOnScriptChance write fOnScriptChance; property OnException : TPSOnException read FOnException write FOnException; end; implementation resourcestring sMissingEndStatment = 'Missing some ''End'' statments'; function CompExportCheck(Sender: TPSPascalCompiler; Proc: TPSInternalProcedure; const ProcDecl: string): Boolean; begin TPSScriptExtension(Sender.ID).DoVerifyProc(Sender.Id, Proc, ProcDecl, Result); Result := not Result; end; Function BeforeCleanup(Sender: TPSPascalCompiler):Boolean; begin result := TPSScriptExtension(Sender.ID).DoBeforeCleanUp(Sender.ID,Sender); end; procedure CEException(Sender: TPSExec; ExError: TIFError; const ExParam: string; ExObject: TObject; ProcNo, Position: Cardinal); begin if @TPSScriptExtension(Sender.ID).FOnException <> nil then TPSScriptExtension(Sender.ID).FOnException(Sender, ExError, ExParam, ExObject, ProcNo, Position); end; { TPSScriptExtension } function TPSScriptExtension.Compile: Boolean; begin ClearProcList; result := inherited Compile; if result then FillMethods; FNeedCompiling := not result; end; constructor TPSScriptExtension.Create(AOwner: TComponent); begin inherited Create(AOwner); Comp.OnBeforeCleanup := BeforeCleanup; Comp.OnExportCheck := CompExportCheck; Exec.OnException := CEException; TStringList(script).OnChange := DoScriptChance; FMethodList := TMethodList.create(Self); FNeedCompiling := True; end; destructor TPSScriptExtension.Destroy; begin FMethodList.Free; FMethodList := nil; inherited Destroy; end; procedure TPSScriptExtension.DoVerifyProc(Sender: TPSScript; Proc: TPSInternalProcedure; const Decl: string; var Error: Boolean); var n{,m,p} : Integer; tstType : TPSProceduralType; begin Error := False; for n := 0 to sender.comp.GetTypeCount -1 do begin If comp.GetType(n) is TPSProceduralType then begin tstType := comp.GetType(n) as TPSProceduralType; If tstType.ProcDef.Same(Proc.Decl) then begin MethodList.addprocedure(Proc.OriginalName, tstType.Name); // Proc. aExport := etExportDecl; end; end; end; if assigned(OnVerifyProc) then begin onVerifyProc(Sender, Proc, Decl, Error); end; end; type TMyPascalCompiler = class(TPSPascalCompiler); const sIFPSParameterMode : array [pmIn..pmInOut] of string = ('','\style{+B}out\style{-B} ','\style{+B}Var\style{-B} '); Procedure TPSScriptExtension.GetCodeProps; Function existsItem(aName:String):Boolean; Begin result := FInserts.indexof(aName)<> -1; end; Procedure addListItem(aType, aName:String; aDef:String=''); var x : LongInt; begin If not ((aName ='') or (aName[1]='!')) then begin x := FInserts.Add(aName); fItems.Insert(x, format('%s \column{}\style{+B}%s\style{-B} %s',[aType, aName, aDef])); end; end; procedure Getdecl(decl : TPSParametersDecl; var T,v :string); var m : Integer; begin v := ''; for m := 0 to Decl.ParamCount-1 do begin v := V +';'+sIFPSParameterMode[Decl.Params[m].Mode]+ Decl.Params[m].OrgName; if Decl.Params[m].aType <> nil then v := v +':'+ Decl.Params[m].aType.OriginalName; end; delete(v,1,1); If v <> '' then v := '('+ v +')'; if Decl.Result<>nil then begin v := v +':'+ Decl.Result.OriginalName; t := 'Function'; end else t := 'Procedure'; end; Function getTypeDef(xr: TPSType; aZoek:string = ''):Boolean; forward; Function getClassDef(xc: TPSCompileTimeClass; aZoek:string = ''):Boolean; var Show : Boolean; Zoek,bZoek : String; tci : TPSDelphiClassItem; n : Integer; T,v : String; begin Show := aZoek=''; Zoek := aZoek; If Pos('.',aZoek)>0 then begin Zoek := copy(aZoek, 1 ,Pos('.',aZoek)-1); bZoek := copy(aZoek, Pos('.',aZoek)+1, 999); end else bZoek := ''; result := (xc <> nil) and Show; if XC<> nil then begin For n := 0 to xc.Count-1 do begin tci := xc.Items[n]; If (tci = nil) or existsItem(tci.OrgName) then continue; if tci is TPSDelphiClassItemConstructor then begin Getdecl(tci.decl, T, V); If Show then addListItem('Constructor',tci.OrgName, v); end else if tci is TPSDelphiClassItemMethod then begin If Show then begin Getdecl(tci.decl, T, V); addListItem(T,tci.OrgName, v) end else If (tci.decl.Result <> nil) and (tci.Name = Zoek) then result := getTypeDef(tci.decl.Result, bZoek); end else if tci is TPSDelphiClassItemProperty then begin If Show then begin t := ''; If tci.Decl.Result<> nil then t := ': '+ tci.Decl.Result.OriginalName; addListItem('Property',tci.OrgName, t); end else If (tci.decl.Result <> nil) and (tci.Name = Zoek) then result := getTypeDef(tci.decl.Result, bZoek); end; If result and not show then exit; end; result := getClassDef(XC.ClassInheritsFrom, aZoek) or result; end; end; Function getTypeDef(xr: TPSType; aZoek:string = ''):Boolean; var Show : Boolean; Zoek : String; xri : PIFPSRecordFieldTypeDef; n : Integer; begin Show := aZoek=''; result := (xr <> nil) and Show; if xr <> nil then begin If xr is TPSRecordType then begin Zoek := aZoek; If Pos('.',aZoek)>0 then begin Zoek := copy(aZoek, 1 ,Pos('.',aZoek)-1); aZoek := copy(aZoek, Pos('.',aZoek)+1, 999); end else aZoek := ''; for n := 0 to (xr as TPSRecordType).RecValCount-1 do begin xri := (xr as TPSRecordType).RecVal(n); If Show then begin addListItem('Var',xri.FieldOrgName,xri.aType.OriginalName) end else If (xri.aType <> nil) and (xri.FieldName = Zoek) then result := getTypeDef(xri.aType, aZoek); end; end else If (xr is TPSClassType) then begin result := getClassDef((xr as TPSClassType).Cl, aZoek) end else result := False; end; end; Function FindVarProc(aVarName:string; aZoek : string= ''):Boolean; var // cv : String; hh, h, i : Longint; proc : TPSProcedure; ip : TPSInternalProcedure; ipv : PIFPSProcVar; ipp : TPSParameterDecl; // t : String; begin Hh := MakeHash(aVarName); result := False; If FScriptPos =0 then exit; for i := Comp.GetProcCount -1 downto 0 do begin Proc := Comp.GetProc(i); If (Proc.ClassType = TPSInternalProcedure) and ((Proc as TPSInternalProcedure).DeclarePos < FScriptPos) then begin ip := Proc as TPSInternalProcedure; for h := 0 to ip.ProcVars.Count-1 do begin ipv := PIFPSProcVar(ip.ProcVars[h]); If aVarName = '' then begin addListItem('Var',ipv.OrgName, ': '+ipv.AType.OriginalName); end else If (ipv.NameHash = HH) and (ipv.Name = aVarName) then begin result := getTypeDef(ipv.aType, aZoek); exit; end; end; for h := 0 to ip.Decl.ParamCount-1 do begin ipp := TPSParameterDecl(ip.Decl.Params[h]); If aVarName = '' then begin addListItem('Var',ipp.OrgName, ': '+ipp.aType.OriginalName); end else If {(ipp.Hash = HH) and} (ipp.Name = aVarName) then begin result := getTypeDef(ipp.aType, aZoek); exit; end; end; end; end; end; Function FindVarFunctType(aProcName:string): Boolean; var cv : String; h, i : Longint; proc : TPSProcedure; xr : TPSRegProc; // t : String; begin cv := aProcName; If Pos('.',aProcName)>0 then begin cv := copy(aProcName, 1 ,Pos('.',aProcName)-1); aProcName := copy(aProcName, Pos('.',aProcName)+1, 999); end else aProcName := ''; H := MakeHash(Cv); // Result := False; for i :=0 to Comp.GetVarCount -1 do begin if (Comp.GetVar(I).NameHash = H) and (Comp.GetVar(I).Name = CV) then begin Result := getTypeDef(Comp.GetVar(I).aType, aProcName); Exit; end; end; result := FindVarProc(cv, aProcName); If result then exit; for i :=0 to Comp.GetProcCount -1 do begin Proc := Comp.GetProc(i); If Proc.ClassType = TPSInternalProcedure then begin if ((Proc as TPSInternalProcedure).NameHash = H) and ((Proc as TPSInternalProcedure).Name = CV) then begin Result := getTypeDef((Proc as TPSInternalProcedure).Decl.Result, aProcName); exit; end; end; end; with TMyPascalCompiler(Comp) do begin for i := 0 to FRegProcs.Count-1 do begin xr := FRegProcs[i]; if (xr.NameHash = H) and (xr.Name = CV) then begin result := getTypeDef(xr.Decl.Result, aProcName); exit; end; end; end; end; Var n : Integer; s, t, v : String; proc : TPSProcedure; xr : TPSRegProc; begin If (fItems = nil) or (fInserts = Nil) then exit; fItems.BeginUpdate; fInserts.BeginUpdate; tStringList(fInserts).Sorted := true; tStringList(fInserts).Duplicates := dupAccept; try fInserts.Clear; fItems.Clear; If (FObjectNest <> '') then begin FindVarFunctType(FastUpperCase(FObjectNest)); exit; end; for n := 0 to Comp.GetTypeCount-1 do begin addListItem('Type',Comp.GetType(n).OriginalName); end; for n := 0 to Comp.GetVarCount-1 do begin addListItem('Var',Comp.GetVar(n).OrgName, ': '+Comp.Getvar(n).aType.OriginalName); end; with TMyPascalCompiler(Comp) do begin for n := 0 to FConstants.Count-1 do begin addListItem('Const', TPSConstant(FConstants[n]).OrgName ); end; for n := 0 to FRegProcs.Count-1 do begin xr := FRegProcs[n]; Getdecl(xr.decl, T, v); addListItem(t,xr.OrgName, v ); end; end; FindVarProc(''); for n := 0 to Comp.GetProcCount-1 do begin s := ''; proc := Comp.GetProc(n); If Proc.ClassType = TPSInternalProcedure then begin s := (Proc as TPSInternalProcedure).OriginalName; Getdecl((Proc as TPSInternalProcedure).decl, T, v); end; If s <> '' then begin addListItem(t,s, v ); end; end; Finally fInserts.EndUpdate; fItems.EndUpdate; end; end; procedure TPSScriptExtension.GetValueDefs(aItems, aInserts: TStrings; const aObjectNest: STring; aScriptPos: Integer); begin fItems := aItems; fInserts := aInserts; FScriptPos := aScriptPos; fObjectNest := aObjectNest; Try compile; finally fItems := Nil; fInserts := Nil; FScriptPos := 0; fObjectNest := ''; end; end; function TPSScriptExtension.DoBeforeCleanup(Sender: TObject; aComp: TPSPascalCompiler): Boolean; begin result := true; If fItems <> nil then GetCodeProps; If @FOnBeforeCleanUp<> nil then result := FOnBeforeCleanUp(Sender, aComp); end; function TPSScriptExtension.Execute: Boolean; begin CompileIfNeeded; MethodList.FillMethods; result := inherited Execute; end; procedure TPSScriptExtension.DoScriptChance(sender: TObject); begin FNeedCompiling := True; self.ClearProcList; If @FOnScriptChance <> NIL then FOnScriptChance(sender); end; procedure TPSScriptExtension.CompileIfNeeded; begin if FNeedCompiling then begin Compile; end; end; procedure TPSScriptExtension.Notification(AComponent: TComponent; Operation: TOperation); begin inherited; If Operation = opRemove then begin if MethodList <> nil then MethodList.SetMethodName(aComponent,'',''); end; end; function TPSScriptExtension.GetProcName(Instance: TObject; PropName: String): string; begin Result := MethodList.ProcName[Instance, Propname]; end; procedure TPSScriptExtension.SetProcName(Instance: TObject; PropName: String; const Value: string); begin MethodList.ProcName[Instance, Propname] := Value; end; procedure TPSScriptExtension.ClearProcList; begin MethodList.ClearProcList; end; procedure TPSScriptExtension.RemoveObjEvents(Obj: TObject); begin MethodList.SetMethodName(Obj, '', ''); end; procedure TPSScriptExtension.FillMethods; begin MethodList.FillMethods; end; { TMethodList } procedure TMethodList.AddProcedure(ProcName, ProcType: String); var po : TProcObj; x,y : Integer; begin ProcType := Uppercase(ProcType); x := ProcIndexOf(ProcName); if x <> -1 then begin y := Procs[x].ProcType.IndexOf(ProcType); If y = -1 then TProcObj(fProcList.Items[x]).ProcType.add(ProcType); end else begin po := TProcObj.create(self); po.Name := ProcName; po.ProcType.add(ProcType); fProcList.add(po); end end; procedure TMethodList.ClearProcList; begin fProcList.Clear; end; constructor TMethodList.create(aOwner: TPSScriptExtension); begin inherited create; fOwner := aOwner; fProcList := TObjectList.create(true); fEventList := TObjectList.create(true); end; procedure TMethodList.CreateProc(ProcName:String; aPropType: TTypeData); var newProc: string; P: PByte; i: Integer; pf : TParamFlags; {$IFDEF FPC} // mh: TParamFlags(P^) doesn't compile in FPC, this function will "fix" it. // yes it's ugly, but I don't know an other way to fix it function GetParamFlags(P: Byte): TParamFlags; begin result := []; if (Ord(pfVar) and P <> 0) then Include(result, pfVar); if (Ord(pfConst) and P <> 0) then Include(result, pfConst); if (Ord(pfArray) and P <> 0) then Include(result, pfArray); if (Ord(pfAddress) and P <> 0) then Include(result, pfAddress); if (Ord(pfReference) and P <> 0) then Include(result, pfReference); if (Ord(pfOut) and P <> 0) then Include(result, pfOut); end; {$ENDIF} begin WITH aPropType do begin if MethodKind=mkProcedure then NewProc:='procedure ' else NewProc:='function '; NewProc:=NewProc + ProcName+'('; P:=PByte(@ParamList); for i:=0 to Pred(ParamCount) do begin {$IFDEF FPC} pf:=GetParamFlags(P^); {$ELSE} pf:=TParamFlags(P^); {$ENDIF} if pfVar in pf then NewProc:=NewProc+'var '; if pfConst in pf then NewProc:=NewProc+'const '; Inc(P); NewProc:=NewProc +PShortString(P)^ +' : '; Inc(P,Succ(P^)); if pfArray in pf then NewProc:=NewProc+'array of '; NewProc := NewProc + PShortString(P)^; Inc(P,Succ(P^)); If i < Pred(ParamCount) then NewProc := NewProc + '; '; end; NewProc := NewProc +')' ; if (MethodKind=mkFunction) then NewProc := NewProc +':'+ PShortString(P)^; NewProc:=NewProc+';'^m^j +'Begin'^m^j^m^j +'End;'^m^j; If @fowner.FOnInsertMethod <> nil then begin fowner.FOnInsertMethod(fOwner, NewProc, false); end else begin InsertMethod(NewProc); end; fowner.CompileIfNeeded; end; end; procedure TMethodList.InsertMethod(NewProc: String; OnTop: Boolean = false); var x : Integer; sl : TStringList; nBegins : Integer; nProcs : Integer; line, test : String; function IsItem(line,item:String; First :Boolean = false):Boolean; var nPos : Integer; begin repeat nPos := pos(item,line); result := ((npos>0) and ((length(Line)-nPos<= length(item)) or not(line[nPos+length(item)] in ['0'..'9','A'..'Z','_'])) And ((Npos = 1) or ((not first) and not(line[nPos-1] in ['0'..'9','A'..'Z','_'])))); if nPos <> 0 then line := copy(line,nPos+Length(Item),Length(line)); until (Result) or (nPos = 0); end; function DelSpaces(AText: String): String; var i: Integer; begin Result := ''; for i := 1 to Length(AText) do if AText[i] <> ' ' then Result := Result + AText[i]; end; function IsProcDecl(AnOriginalProcDecl: String): Boolean; var bIsFunc: Boolean; iLineNo: Integer; sProcKey: String; sProcDecl: String; begin Result := false; sProcDecl := Line; iLineNo := x; bIsFunc := isItem(AnOriginalProcDecl,'FUNCTION',true); if bIsFunc then sProcKey := 'FUNCTION' else sProcKey := 'PROCEDURE'; sProcDecl := copy(sProcDecl,Pos(sProcKey,sProcDecl),Length(sProcDecl)); while not IsItem(sProcDecl,'BEGIN') do begin inc(iLineNo); if iLineNo > (fowner.script.Count - 1) then exit; sProcDecl := sProcDecl + ' ' + uppercase(trim(fowner.script[iLineNo])) + ' '; end; sProcDecl := DelSpaces(sProcDecl); AnOriginalProcDecl := DelSpaces(AnOriginalProcDecl); sProcDecl := copy(sProcDecl,1,Length(AnOriginalProcDecl)); Result := sProcDecl = AnOriginalProcDecl; end; begin sl := TStringList.create; Try sl.Text := NewProc; test := uppercase(trim(sl[0])); finally Sl.free; end; nProcs := 0; nBegins := 0; x := 0; If Not Ontop Then begin for x := 0 to fOwner.script.count -1 do begin Line := fowner.script[x]; Line := uppercase(trim(line)); If IsItem(line,'PROCEDURE', true) or IsItem(line,'FUNCTION', true) then begin If nBegins >0 then Raise exception.create('Missing some ''end'' statments'); If (nProcs = 0) and IsProcDecl(test) and (not IsItem(line,'FORWARD')) and (not IsItem(line,'EXTERNAL')) then Exit; Inc(nProcs); end; if IsItem(line,'FORWARD') or IsItem(line,'EXTERNAL') then dec(nProcs); If Pos('END',line) < Pos('BEGIN',line) then begin If IsItem(line,'END') then begin If (nBegins = 0) and (nProcs=0) then Break; Dec(nBegins); If nBegins = 0 then Dec(nProcs); end; If IsItem(line,'BEGIN') or IsItem(line,'TRY') or IsItem(line,'CASE') then begin If nProcs = 0 then Break; Inc(nBegins); end; end else begin If IsItem(line,'BEGIN') or IsItem(line,'TRY') or IsItem(line,'CASE') then begin If nProcs = 0 then Break; Inc(nBegins); end; If IsItem(line,'END') then begin If (nBegins = 0) and (nProcs=0) then Break; Dec(nBegins); If nBegins = 0 then Dec(nProcs); end; end; end; end; FOwner.script.BeginUpdate; Try If (nProcs <> 0) or (nBegins<>0) then Raise exception.create(sMissingEndStatment); If (Not Ontop) and (x>0) and (Trim(FOwner.script[x-1])<>'') then begin FOwner.script.Insert(x,''); inc(x); end; FOwner.script.Insert(x,NewProc); FOwner.script.text := FOwner.script.text; finally FOwner.script.EndUpdate; end; end; destructor TMethodList.Destroy; begin fProcList.Free; {<< Needs Eventlist for removing Methods} fEventList.Free; inherited; end; procedure TMethodList.FillMethods; var x, y : Integer; m : TMethod; begin for x := 0 to fEventList.Count-1 do begin Y := ProcIndexOf(MethodS[x].ProcName); If (Y >= 0) and assigned(Methods[x].Instance) then begin m := Procs[Y].Method; if m.Data = nil then begin m := fOwner.Exec.GetProcAsMethodN(Procs[Y].name); TProcObj(fProcList.Items[Y]).Method := m; end; SetMethodProp(Methods[x].Instance, Methods[x].propname, m ); end; end; end; function TMethodList.GetMethodName(Instance: TObject; PropName: String): String; var x : Integer; begin fOwner.CompileIfNeeded; x := methodIndexOf(Instance,PropName); If x>=0 then result := Methods[x].ProcName else result := ''; end; function TMethodList.GetObject(Index: Integer): TMethodObj; begin result := TMethodObj(fEventList.items[Index]); end; function TMethodList.GetProcObj(Index: Integer): TProcObj; begin result := TProcObj(fProcList.items[Index]); end; procedure TMethodList.ListEventsName(EventType: string; List: TStrings); var x : Integer; begin If List = nil then exit; EventType := Uppercase(EventType); List.Clear; fOwner.CompileIfNeeded; for x := 0 to fProcList.count-1 do begin If Procs[x].ProcType.indexof(EventType)<> -1 then List.add(Procs[x].name); end; end; function TMethodList.MethodCount: Integer; begin result := fEventList.count; end; function TMethodList.methodIndexOf(Instance: TObject; PropName: String): Integer; var x : integer; begin Result := -1; for x := 0 to fEventList.count-1 do begin if (TMethodObj(fEventList.Items[x]).Instance = Instance) and ((propName='') or(TMethodObj(fEventList.Items[x]).PropName = PropName)) then begin Result := x; exit; end; end; end; function TMethodList.ProcCount: Integer; begin result := fProcList.count; end; function TMethodList.ProcIndexOf(Name: String): Integer; var x : integer; begin result := -1; Name := Uppercase(name); For x := 0 to fProcList.count-1 do begin If Uppercase(TProcObj(fProcList.Items[x]).name) = name then begin Result := x; exit; end; end; end; procedure TMethodList.SetMethodName(Instance: TObject; PropName: String; const Value: String); var x, y : Integer; mo : TMethodObj; function TypeData(Instance: TObject; const PropName: string):PTypeData; var PropInfo: PPropInfo; begin // assume failure Result := Nil; PropInfo := GetPropInfo(Instance, PropName); if PropInfo <> nil then begin Result:= GetTypeData(PropInfo^.PropType{$IFNDEF FPC}^{$ENDIF}); end end; begin If PropName = '' then begin x := 0; While x < MethodCount do begin If (Methods[x].Instance = Instance) or (Instance = nil) then fEventList.Delete(x) else Inc(x); end; end else begin x := methodIndexOf(Instance, PropName); if value = '' then begin if x >= 0 then fEventList.Delete(x); end else begin fOwner.CompileIfNeeded; y := ProcIndexOf(Value); If (Y = -1) then begin CreateProc(Value, TypeData(Instance,propName)^); y := 0; end; If (x = -1) then begin If (Y <> -1) then begin mo := TMethodObj.create; mo.Instance := TPersistent(Instance); mo.ProPName := Propname; mo.procName := Value; If (methodIndexOf(Instance,'')<>-1) and Instance.InheritsFrom(TComponent) then fOwner.FreeNotification(TComponent(Instance)); fEventList.add(mo); end; end else begin Methods[x].procname := Value; end; end; end; end; procedure TMethodList.ClearAll; begin fProclist.clear; fEventList.Clear; end; { TProcObj } constructor TProcObj.create(aOwner: TMethodList); begin inherited create(); fOwner := aOwner; ProcType := TStringList.Create; end; destructor TProcObj.Destroy; var x : Integer; m :TMethod; begin m.Code := nil; m.Data := nil; If ((Method.Data <> nil) or (method.Code<> nil)) and (fOwner<>nil) and assigned(fOwner) then begin for x := 0 to fOwner.MethodCount-1 do begin If (name = fOwner.Methods[x].ProcName) and assigned(fOwner.Methods[x].Instance) then begin Try SetMethodProp(fOwner.Methods[x].Instance, fOwner.Methods[x].PropName,m); except; end; end; end; end; ProcType.free; inherited; end; procedure TProcObj.SetName(const Value: String); var x : Integer; begin If FName <> Value then begin If fName<>'' then begin for x := 0 to fOwner.MethodCount-1 do begin If Fname = fOwner.Methods[x].ProcName then begin fOwner.Methods[x].ProcName := Value; end; end; end; FName := Value; end; end; end.