{**************************************************************************************************} { } { Project JEDI Code Library (JCL) } { } { The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); } { you may not use this file except in compliance with the License. You may obtain a copy of the } { License at http://www.mozilla.org/MPL/ } { } { Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF } { ANY KIND, either express or implied. See the License for the specific language governing rights } { and limitations under the License. } { } { The Original Code is JclRTTI.pas. } { } { The Initial Developer of the Original Code is Marcel Bestebroer. } { Portions created Marcel Bestebroer are Copyright (C) Marcel Bestebroer. All rights reserved. } { } { Contributor(s): } { Theo Bebekis } { Marcel Bestebroer (marcelb) } { Robert Marquardt (marquardt) } { Robert Rossmair (rrossmair) } { Matthias Thoma (mthoma) } { Petr Vones (pvones) } { } {**************************************************************************************************} { } { Various RunTime Type Information routines. Includes retrieving RTTI information for different } { types, declaring/generating new types, data conversion to user displayable values and 'is'/'as' } { operator hooking. } { } { Unit owner: Marcel Bestebroer } { } {**************************************************************************************************} // Last modified: $Date: 2005/05/05 20:08:44 $ // For history see end of file unit JclRTTI; {$I jcl.inc} interface uses {$IFDEF HAS_UNIT_TYPES} Types, {$IFDEF CLR} System.Runtime.InteropServices, System.Reflection, System.ComponentModel, Variants, {$ELSE} {$IFDEF SUPPORTS_INLINE} Windows, {$ENDIF SUPPORTS_INLINE} {$ENDIF CLR} {$ELSE} Windows, {$ENDIF HAS_UNIT_TYPES} Classes, SysUtils, TypInfo, JclBase; type // TypeInfo writing IJclInfoWriter = interface ['{7DAD522D-46EA-11D5-B0C0-4854E825F345}'] function GetWrap: Integer; procedure SetWrap(const Value: Integer); procedure Write(const S: string); procedure Writeln(const S: string = ''); procedure Indent; procedure Outdent; property Wrap: Integer read GetWrap write SetWrap; end; TJclInfoWriter = class(TInterfacedObject, IJclInfoWriter) private FCurLine: string; FIndentLevel: Integer; FWrap: Integer; protected function GetWrap: Integer; procedure SetWrap(const Value: Integer); procedure DoWrap; procedure DoWriteCompleteLines; procedure PrimWrite(const S: string); virtual; abstract; property CurLine: string read FCurLine write FCurLine; property IndentLevel: Integer read FIndentLevel write FIndentLevel; public constructor Create(const AWrap: Integer = 80); destructor Destroy; override; procedure Indent; procedure Outdent; procedure Write(const S: string); procedure Writeln(const S: string = ''); property Wrap: Integer read GetWrap write SetWrap; end; TJclInfoStringsWriter = class(TJclInfoWriter) private FStrings: TStrings; protected procedure PrimWrite(const S: string); override; public constructor Create(const AStrings: TStrings; const AWrap: Integer = 80); property Strings: TStrings read FStrings; end; // TypeInfo retrieval IJclBaseInfo = interface procedure WriteTo(const Dest: IJclInfoWriter); procedure DeclarationTo(const Dest: IJclInfoWriter); end; IJclTypeInfo = interface(IJclBaseInfo) ['{7DAD5220-46EA-11D5-B0C0-4854E825F345}'] function GetName: string; function GetTypeData: PTypeData; function GetTypeInfo: PTypeInfo; function GetTypeKind: TTypeKind; property Name: string read GetName; property TypeData: PTypeData read GetTypeData; property TypeInfo: PTypeInfo read GetTypeInfo; property TypeKind: TTypeKind read GetTypeKind; end; // Ordinal types IJclOrdinalTypeInfo = interface(IJclTypeInfo) ['{7DAD5221-46EA-11D5-B0C0-4854E825F345}'] function GetOrdinalType: TOrdType; property OrdinalType: TOrdType read GetOrdinalType; end; IJclOrdinalRangeTypeInfo = interface(IJclOrdinalTypeInfo) ['{7DAD5222-46EA-11D5-B0C0-4854E825F345}'] function GetMinValue: Int64; function GetMaxValue: Int64; property MinValue: Int64 read GetMinValue; property MaxValue: Int64 read GetMaxValue; end; IJclEnumerationTypeInfo = interface(IJclOrdinalRangeTypeInfo) ['{7DAD5223-46EA-11D5-B0C0-4854E825F345}'] function GetBaseType: IJclEnumerationTypeInfo; function GetNames(const I: Integer): string; {$IFDEF RTL140_UP} function GetUnitName: string; {$ENDIF RTL140_UP} function IndexOfName(const Name: string): Integer; property BaseType: IJclEnumerationTypeInfo read GetBaseType; property Names[const I: Integer]: string read GetNames; default; {$IFDEF RTL140_UP} property UnitName: string read GetUnitName; {$ENDIF RTL140_UP} end; IJclSetTypeInfo = interface(IJclOrdinalTypeInfo) ['{7DAD5224-46EA-11D5-B0C0-4854E825F345}'] function GetBaseType: IJclOrdinalTypeInfo; procedure GetAsList(const Value; const WantRanges: Boolean; const Strings: TStrings); procedure SetAsList(out Value; const Strings: TStrings); property BaseType: IJclOrdinalTypeInfo read GetBaseType; end; // Float types IJclFloatTypeInfo = interface(IJclTypeInfo) ['{7DAD5225-46EA-11D5-B0C0-4854E825F345}'] function GetFloatType: TFloatType; property FloatType: TFloatType read GetFloatType; end; // Short string types IJclStringTypeInfo = interface(IJclTypeInfo) ['{7DAD5226-46EA-11D5-B0C0-4854E825F345}'] function GetMaxLength: Integer; property MaxLength: Integer read GetMaxLength; end; // Class types TJclPropSpecKind = (pskNone, pskStaticMethod, pskVirtualMethod, pskField, pskConstant); IJclPropInfo = interface ['{7DAD5227-46EA-11D5-B0C0-4854E825F345}'] function GetPropType: IJclTypeInfo; function GetReader: {$IFDEF CLR}MethodInfo{$ELSE}Pointer{$ENDIF}; function GetWriter: {$IFDEF CLR}MethodInfo{$ELSE}Pointer{$ENDIF}; function GetStoredProc: {$IFDEF CLR}MethodInfo{$ELSE}Pointer{$ENDIF}; function GetIndex: Integer; function GetDefault: Longint; function GetNameIndex: Smallint; function GetName: string; function GetReaderType: TJclPropSpecKind; function GetWriterType: TJclPropSpecKind; function GetStoredType: TJclPropSpecKind; function GetReaderValue: Integer; function GetWriterValue: Integer; function GetStoredValue: Integer; function IsStored(const AInstance: TObject): Boolean; function HasDefault: Boolean; function HasIndex: Boolean; property PropType: IJclTypeInfo read GetPropType; property Reader: {$IFDEF CLR}MethodInfo{$ELSE}Pointer{$ENDIF} read GetReader; property Writer: {$IFDEF CLR}MethodInfo{$ELSE}Pointer{$ENDIF} read GetWriter; property StoredProc: {$IFDEF CLR}MethodInfo{$ELSE}Pointer{$ENDIF} read GetStoredProc; property ReaderType: TJclPropSpecKind read GetReaderType; property WriterType: TJclPropSpecKind read GetWriterType; property StoredType: TJclPropSpecKind read GetStoredType; property ReaderValue: Integer read GetReaderValue; property WriterValue: Integer read GetWriterValue; property StoredValue: Integer read GetStoredValue; property Index: Integer read GetIndex; property Default: Longint read GetDefault; property NameIndex: Smallint read GetNameIndex; property Name: string read GetName; end; IJclClassTypeInfo = interface(IJclTypeInfo) ['{7DAD5228-46EA-11D5-B0C0-4854E825F345}'] function GetClassRef: TClass; function GetParent: IJclClassTypeInfo; function GetTotalPropertyCount: Integer; function GetPropertyCount: Integer; function GetProperties(const PropIdx: Integer): IJclPropInfo; function GetPropNames(const Name: string): IJclPropInfo; function GetUnitName: string; property ClassRef: TClass read GetClassRef; property Parent: IJclClassTypeInfo read GetParent; property TotalPropertyCount: Integer read GetTotalPropertyCount; property PropertyCount: Integer read GetPropertyCount; property Properties[const PropIdx: Integer]: IJclPropInfo read GetProperties; property PropNames[const Name: string]: IJclPropInfo read GetPropNames; property UnitName: string read GetUnitName; end; // Event types IJclEventParamInfo = interface ['{7DAD5229-46EA-11D5-B0C0-4854E825F345}'] function GetFlags: TParamFlags; function GetName: string; {$IFNDEF CLR} function GetRecSize: Integer; {$ENDIF ~CLR} function GetTypeName: string; function GetParam: {$IFDEF CLR}ParameterInfo{$ELSE}Pointer{$ENDIF}; property Flags: TParamFlags read GetFlags; property Name: string read GetName; {$IFNDEF CLR} property RecSize: Integer read GetRecSize; {$ENDIF ~CLR} property TypeName: string read GetTypeName; property Param: {$IFDEF CLR}ParameterInfo{$ELSE}Pointer{$ENDIF} read GetParam; end; IJclEventTypeInfo = interface(IJclTypeInfo) ['{7DAD522A-46EA-11D5-B0C0-4854E825F345}'] function GetMethodKind: TMethodKind; function GetParameterCount: Integer; function GetParameters(const ParamIdx: Integer): IJclEventParamInfo; function GetResultTypeName: string; property MethodKind: TMethodKind read GetMethodKind; property ParameterCount: Integer read GetParameterCount; property Parameters[const ParamIdx: Integer]: IJclEventParamInfo read GetParameters; property ResultTypeName: string read GetResultTypeName; end; // Interface types IJclInterfaceTypeInfo = interface(IJclTypeInfo) ['{7DAD522B-46EA-11D5-B0C0-4854E825F345}'] function GetParent: IJclInterfaceTypeInfo; function GetFlags: TIntfFlagsBase; function GetGUID: TGUID; {$IFDEF RTL140_UP} function GetPropertyCount: Integer; {$ENDIF RTL140_UP} function GetUnitName: string; property Parent: IJclInterfaceTypeInfo read GetParent; property Flags: TIntfFlagsBase read GetFlags; property GUID: TGUID read GetGUID; {$IFDEF RTL140_UP} property PropertyCount: Integer read GetPropertyCount; {$ENDIF RTL140_UP} property UnitName: string read GetUnitName; end; // Int64 types IJclInt64TypeInfo = interface(IJclTypeInfo) ['{7DAD522C-46EA-11D5-B0C0-4854E825F345}'] function GetMinValue: Int64; function GetMaxValue: Int64; property MinValue: Int64 read GetMinValue; property MaxValue: Int64 read GetMaxValue; end; {$IFDEF RTL140_UP} // Dynamic array types IJclDynArrayTypeInfo = interface(IJclTypeInfo) ['{7DAD522E-46EA-11D5-B0C0-4854E825F345}'] function GetElementSize: Longint; function GetElementType: IJclTypeInfo; function GetElementsNeedCleanup: Boolean; function GetVarType: Integer; function GetUnitName: string; property ElementSize: Longint read GetElementSize; property ElementType: IJclTypeInfo read GetElementType; property ElementsNeedCleanup: Boolean read GetElementsNeedCleanup; property VarType: Integer read GetVarType; property UnitName: string read GetUnitName; end; {$ENDIF RTL140_UP} EJclRTTIError = class(EJclError); function JclTypeInfo(ATypeInfo: PTypeInfo): IJclTypeInfo; // Enumeration types const PREFIX_CUT_LOWERCASE = 255; PREFIX_CUT_EQUAL = 254; MaxPrefixCut = 250; function JclEnumValueToIdent(TypeInfo: PTypeInfo; const Value): string; {$IFNDEF CLR} function JclGenerateEnumType(const TypeName: ShortString; const Literals: array of string): PTypeInfo; function JclGenerateEnumTypeBasedOn(const TypeName: ShortString; BaseType: PTypeInfo; const PrefixCut: Byte): PTypeInfo; function JclGenerateSubRange(BaseType: PTypeInfo; const TypeName: string; const MinValue, MaxValue: Integer): PTypeInfo; {$ENDIF ~CLR} // Integer types function JclStrToTypedInt(Value: string; TypeInfo: PTypeInfo): Integer; function JclTypedIntToStr(Value: Integer; TypeInfo: PTypeInfo): string; // Sets function JclSetToList(TypeInfo: PTypeInfo; const Value; const WantBrackets: Boolean; const WantRanges: Boolean; const Strings: TStrings): string; function JclSetToStr(TypeInfo: PTypeInfo; const Value; const WantBrackets: Boolean = False; const WantRanges: Boolean = False): string; procedure JclStrToSet(TypeInfo: PTypeInfo; var SetVar; const Value: string); procedure JclIntToSet(TypeInfo: PTypeInfo; var SetVar; const Value: Integer); function JclSetToInt(TypeInfo: PTypeInfo; const SetVar): Integer; {$IFNDEF CLR} function JclGenerateSetType(BaseType: PTypeInfo; const TypeName: ShortString): PTypeInfo; {$ENDIF ~CLR} {$IFNDEF CLR} // User generated type info managment procedure RemoveTypeInfo(TypeInfo: PTypeInfo); {$ENDIF ~CLR} // Is/As hooking function JclIsClass(const AnObj: TObject; const AClass: TClass): Boolean; function JclIsClassByName(const AnObj: TObject; const AClass: TClass): Boolean; implementation uses {$IFDEF HAS_UNIT_RTLCONSTS} RtlConsts, {$ENDIF HAS_UNIT_RTLCONSTS} SysConst, JclLogic, JclResources, JclStrings, JclSysUtils; //=== { TJclInfoWriter } ===================================================== constructor TJclInfoWriter.Create(const AWrap: Integer); begin inherited Create; Wrap := AWrap; end; destructor TJclInfoWriter.Destroy; begin if CurLine <> '' then Writeln(''); inherited Destroy; end; function TJclInfoWriter.GetWrap: Integer; begin Result := FWrap; end; procedure TJclInfoWriter.SetWrap(const Value: Integer); begin FWrap := Value; end; procedure TJclInfoWriter.DoWrap; const {$IFDEF CLR} WrapChars: array[0..33] of Char = ( #0, #1, #2, #3, #4, #5, #6, #7, #8, #9, #10, #11, #12, #13, #14, #15, #16, #17, #18, #19, #20, #21, #22, #23, #24, #25, #26, #27, #28, #29, #30, #31, #32, '-'); {$ELSE} WrapChars = [#0..' ', '-']; {$ENDIF CLR} var TmpLines: TStringList; I: Integer; TmpLines2: TStringList; EndedInCRLF: Boolean; LineBreakLength: Integer; begin LineBreakLength := Length(AnsiLineBreak); EndedInCRLF := Copy(CurLine, Length(CurLine) - LineBreakLength + 1, LineBreakLength) = AnsiLineBreak; TmpLines := TStringList.Create; try TmpLines.Text := CurLine; TmpLines2 := TStringList.Create; try I := TmpLines.Count-1; if not EndedInCRLF then Dec(I); while I >= 0 do begin TmpLines[I] := StringOfChar(' ', 2 * IndentLevel) + TmpLines[I]; if (Wrap > 0) and (Length(TmpLines[I]) > Wrap) then begin TmpLines2.Text := WrapText( TmpLines[I], AnsiLineBreak + StringOfChar(' ', 2 * (IndentLevel+1)), WrapChars, Wrap); TmpLines.Delete(I); TmpLines.Insert(I, Copy(TmpLines2.Text, 1, Length(TmpLines2.Text) - 2)); end; Dec(I); end; CurLine := TmpLines.Text; if not EndedInCRLF then Delete(FCurLine, Length(FCurLine) - LineBreakLength + 1, LineBreakLength); finally TmpLines2.Free; end; finally TmpLines.Free; end; end; procedure TJclInfoWriter.DoWriteCompleteLines; var CRLFPos: Integer; begin CRLFPos := StrLastPos(AnsiLineBreak, CurLine); if CRLFPos > 0 then begin PrimWrite(Copy(CurLine, 1, CRLFPos-1)); Delete(FCurLine, 1, CRLFPos+1); end; end; procedure TJclInfoWriter.Indent; begin IndentLevel := IndentLevel + 1; end; procedure TJclInfoWriter.Outdent; begin IndentLevel := IndentLevel - 1; end; procedure TJclInfoWriter.Write(const S: string); begin CurLine := CurLine + S; DoWrap; DoWriteCompleteLines; end; procedure TJclInfoWriter.Writeln(const S: string); begin Write(S + AnsiLineBreak); end; //=== { TJclInfoStringsWriter } ============================================== constructor TJclInfoStringsWriter.Create(const AStrings: TStrings; const AWrap: Integer); begin inherited Create(AWrap); FStrings := AStrings; end; procedure TJclInfoStringsWriter.PrimWrite(const S: string); begin Strings.Add(S); end; //=== { TJclTypeInfo } ======================================================= type TJclTypeInfo = class(TInterfacedObject, IJclTypeInfo) private FTypeData: PTypeData; FTypeInfo: PTypeInfo; protected function GetName: string; function GetTypeData: PTypeData; function GetTypeInfo: PTypeInfo; function GetTypeKind: TTypeKind; procedure WriteTo(const Dest: IJclInfoWriter); virtual; procedure DeclarationTo(const Dest: IJclInfoWriter); virtual; public constructor Create(ATypeInfo: PTypeInfo); property Name: string read GetName; property TypeData: PTypeData read GetTypeData; property TypeInfo: PTypeInfo read GetTypeInfo; property TypeKind: TTypeKind read GetTypeKind; end; constructor TJclTypeInfo.Create(ATypeInfo: PTypeInfo); begin inherited Create; FTypeInfo := ATypeInfo; FTypeData := TypInfo.GetTypeData(ATypeInfo); end; function TJclTypeInfo.GetName: string; begin Result := TypeInfo.Name; end; function TJclTypeInfo.GetTypeData: PTypeData; begin Result := FTypeData; end; function TJclTypeInfo.GetTypeInfo: PTypeInfo; begin Result := FTypeInfo; end; function TJclTypeInfo.GetTypeKind: TTypeKind; begin Result := TypeInfo.Kind end; procedure TJclTypeInfo.WriteTo(const Dest: IJclInfoWriter); begin {$IFDEF CLR} Dest.Writeln(RsRTTIName + Name); Dest.Writeln(RsRTTITypeKind + JclEnumValueToIdent(Borland.Delphi.System.TypeInfo(TTypeKind), TypeInfo.Kind)); Dest.Writeln(Format(RsRTTITypeInfoAt, [TypeInfo])); {$ELSE} Dest.Writeln(LoadResString(@RsRTTIName) + Name); Dest.Writeln(LoadResString(@RsRTTITypeKind) + JclEnumValueToIdent(System.TypeInfo(TTypeKind), TypeInfo.Kind)); Dest.Writeln(Format(LoadResString(@RsRTTITypeInfoAt), [TypeInfo])); {$ENDIF CLR} end; procedure TJclTypeInfo.DeclarationTo(const Dest: IJclInfoWriter); begin {$IFDEF CLR} Dest.Write(Format(RsDeclarationFormat, [Name])); {$ELSE} Dest.Write(Format(LoadResString(@RsDeclarationFormat), [Name])); {$ENDIF CLR} end; //=== { TJclOrdinalTypeInfo } ================================================ type TJclOrdinalTypeInfo = class(TJclTypeInfo, IJclOrdinalTypeInfo) protected function GetOrdinalType: TOrdType; procedure WriteTo(const Dest: IJclInfoWriter); override; public property OrdinalType: TOrdType read GetOrdinalType; end; function TJclOrdinalTypeInfo.GetOrdinalType: TOrdType; begin Result := TypeData.OrdType; end; procedure TJclOrdinalTypeInfo.WriteTo(const Dest: IJclInfoWriter); begin inherited WriteTo(Dest); {$IFDEF CLR} Dest.Writeln(RsRTTIOrdinalType + JclEnumValueToIdent(Borland.Delphi.System.TypeInfo(TOrdType), TypeData.OrdType)); {$ELSE} Dest.Writeln(LoadResString(@RsRTTIOrdinalType) + JclEnumValueToIdent(System.TypeInfo(TOrdType), TypeData.OrdType)); {$ENDIF CLR} end; //=== { TJclOrdinalRangeTypeInfo } =========================================== type TJclOrdinalRangeTypeInfo = class(TJclOrdinalTypeInfo, IJclOrdinalRangeTypeInfo) protected function GetMinValue: Int64; function GetMaxValue: Int64; procedure WriteTo(const Dest: IJclInfoWriter); override; procedure DeclarationTo(const Dest: IJclInfoWriter); override; public property MinValue: Int64 read GetMinValue; property MaxValue: Int64 read GetMaxValue; end; function TJclOrdinalRangeTypeInfo.GetMinValue: Int64; begin if OrdinalType = otULong then Result := Longword(TypeData.MinValue) else Result := TypeData.MinValue; end; function TJclOrdinalRangeTypeInfo.GetMaxValue: Int64; begin if OrdinalType = otULong then Result := Longword(TypeData.MaxValue) else Result := TypeData.MaxValue; end; procedure TJclOrdinalRangeTypeInfo.WriteTo(const Dest: IJclInfoWriter); begin inherited WriteTo(Dest); {$IFDEF CLR} Dest.Writeln(RsRTTIMinValue + IntToStr(MinValue)); Dest.Writeln(RsRTTIMaxValue + IntToStr(MaxValue)); {$ELSE} Dest.Writeln(LoadResString(@RsRTTIMinValue) + IntToStr(MinValue)); Dest.Writeln(LoadResString(@RsRTTIMaxValue) + IntToStr(MaxValue)); {$ENDIF CLR} end; procedure TJclOrdinalRangeTypeInfo.DeclarationTo(const Dest: IJclInfoWriter); const cRange = '..'; begin Dest.Write(Name + ' = '); if TypeInfo.Kind in [tkChar, tkWChar] then begin if (MinValue < Ord(' ')) or (MinValue > Ord('~')) then Dest.Write('#' + IntToStr(MinValue) + cRange) else Dest.Write('''' + Chr(Byte(MinValue)) + '''' + cRange); if (MaxValue < Ord(' ')) or (MaxValue > Ord('~')) then Dest.Write('#' + IntToStr(MaxValue)) else Dest.Write('''' + Chr(Byte(MaxValue)) + ''''); end else Dest.Write(IntToStr(MinValue) + '..' + IntToStr(MaxValue)); {$IFDEF CLR} Dest.Writeln('; // ' + JclEnumValueToIdent(Borland.Delphi.System.TypeInfo(TOrdType), TypeData.OrdType)); {$ELSE} Dest.Writeln('; // ' + JclEnumValueToIdent(System.TypeInfo(TOrdType), TypeData.OrdType)); {$ENDIF CLR} end; //=== { TJclEnumerationTypeInfo } ============================================ type TJclEnumerationTypeInfo = class(TJclOrdinalRangeTypeInfo, IJclEnumerationTypeInfo) protected function GetBaseType: IJclEnumerationTypeInfo; function GetNames(const I: Integer): string; {$IFDEF RTL140_UP} function GetUnitName: string; {$ENDIF RTL140_UP} function IndexOfName(const Name: string): Integer; procedure WriteTo(const Dest: IJclInfoWriter); override; procedure DeclarationTo(const Dest: IJclInfoWriter); override; public property BaseType: IJclEnumerationTypeInfo read GetBaseType; property Names[const I: Integer]: string read GetNames; default; {$IFDEF RTL140_UP} property UnitName: string read GetUnitName; {$ENDIF RTL140_UP} end; function TJclEnumerationTypeInfo.GetBaseType: IJclEnumerationTypeInfo; begin {$IFDEF CLR} if TypeData.ParentInfo = TypeInfo then Result := Self else Result := TJclEnumerationTypeInfo.Create(TypeData.ParentInfo); {$ELSE} if TypeData.BaseType^ = TypeInfo then Result := Self else Result := TJclEnumerationTypeInfo.Create(TypeData.BaseType^); {$ENDIF CLR} end; function TJclEnumerationTypeInfo.GetNames(const I: Integer): string; var Base: IJclEnumerationTypeInfo; {$IFNDEF CLR} Idx: Integer; P: ^ShortString; {$ENDIF ~CLR} begin Base := BaseType; {$IFDEF CLR} if (I >= 0) and (I < Length(Enum.GetNames(Base.TypeInfo))) then Result := Enum.GetNames(Base.TypeInfo)[I] else Result := ''; {$ELSE} Idx := I; P := @Base.TypeData.NameList; while Idx <> 0 do begin Inc(Integer(P), Length(P^) + 1); Dec(Idx); end; Result := P^; {$ENDIF CLR} end; {$IFDEF RTL140_UP} function TJclEnumerationTypeInfo.GetUnitName: string; {$IFDEF CLR} begin Result := BaseType.TypeData.EnumUnitName; end; {$ELSE} var I: Integer; P: ^ShortString; begin if BaseType.TypeInfo = TypeInfo then begin I := MaxValue - MinValue; P := @TypeData.NameList; while I >= 0 do begin Inc(Integer(P), Length(P^) + 1); Dec(I); end; Result := P^; end else Result := TypeData.NameList; end; {$ENDIF CLR} {$ENDIF RTL140_UP} function TJclEnumerationTypeInfo.IndexOfName(const Name: string): Integer; begin Result := MaxValue; while (Result >= MinValue) and {$IFDEF CLR} not SameText(Name, Names[Result]) do {$ELSE} not AnsiSameText(Name, Names[Result]) do {$ENDIF CLR} Dec(Result); if Result < MinValue then Result := -1; end; procedure TJclEnumerationTypeInfo.WriteTo(const Dest: IJclInfoWriter); var Idx: Integer; Prefix: string; begin inherited WriteTo(Dest); {$IFDEF CLR} Dest.Writeln(RsRTTIUnitName + UnitName); Dest.Write(RsRTTINameList); {$ELSE} {$IFDEF RTL140_UP} Dest.Writeln(LoadResString(@RsRTTIUnitName) + UnitName); {$ENDIF RTL140_UP} Dest.Write(LoadResString(@RsRTTINameList)); {$ENDIF CLR} Prefix := '('; for Idx := MinValue to MaxValue do begin Dest.Write(Prefix + Names[Idx]); Prefix := ', '; end; Dest.Writeln(')'); end; procedure TJclEnumerationTypeInfo.DeclarationTo(const Dest: IJclInfoWriter); var Prefix: string; I: Integer; begin if Name[1] <> '.' then Dest.Write(Name + ' = '); if BaseType.TypeInfo = TypeInfo then begin Dest.Write('('); Prefix := ''; for I := MinValue to MaxValue do begin Dest.Write(Prefix + Names[I]); Prefix := ', '; end; Dest.Write(')'); end else Dest.Write(Names[MinValue] + ' .. ' + Names[MaxValue]); if Name[1] <> '.' then begin {$IFDEF CLR} Dest.Write('; // ' + JclEnumValueToIdent(Borland.Delphi.System.TypeInfo(TOrdType), TypeData.OrdType)); {$ELSE} Dest.Write('; // ' + JclEnumValueToIdent(System.TypeInfo(TOrdType), TypeData.OrdType)); {$ENDIF CLR} Dest.Writeln(''); end; end; //=== { TJclSetTypeInfo } ==================================================== type TJclSetTypeInfo = class(TJclOrdinalTypeInfo, IJclSetTypeInfo) protected function GetBaseType: IJclOrdinalTypeInfo; procedure GetAsList(const Value; const WantRanges: Boolean; const Strings: TStrings); procedure SetAsList(out Value; const Strings: TStrings); procedure WriteTo(const Dest: IJclInfoWriter); override; procedure DeclarationTo(const Dest: IJclInfoWriter); override; public property BaseType: IJclOrdinalTypeInfo read GetBaseType; end; function TJclSetTypeInfo.GetBaseType: IJclOrdinalTypeInfo; begin {$IFDEF CLR} Result := JclTypeInfo(TypeData.CompType) as IJclOrdinalTypeInfo; {$ELSE} Result := JclTypeInfo(TypeData.CompType^) as IJclOrdinalTypeInfo; {$ENDIF CLR} end; procedure TJclSetTypeInfo.GetAsList(const Value; const WantRanges: Boolean; const Strings: TStrings); var BaseInfo: IJclOrdinalRangeTypeInfo; FirstBit: Byte; LastBit: Byte; Bit: Byte; StartBit: Integer; procedure AddRange; var FirstOrdNum: Int64; LastOrdNum: Int64; OrdNum: Int64; begin FirstOrdNum := (StartBit - FirstBit) + BaseInfo.MinValue; LastOrdNum := (Bit - 1 - FirstBit) + BaseInfo.MinValue; if WantRanges and (LastOrdNum <> FirstOrdNum) then begin if BaseInfo.TypeKind = tkEnumeration then Strings.Add((BaseInfo as IJclEnumerationTypeInfo).Names[FirstOrdNum] + ' .. ' + (BaseInfo as IJclEnumerationTypeInfo).Names[LastOrdNum]) else Strings.Add(IntToStr(FirstOrdNum) + ' .. ' + IntToStr(LastOrdNum)); end else begin OrdNum := FirstOrdNum; while OrdNum <= LastOrdNum do begin if BaseInfo.TypeKind = tkEnumeration then Strings.Add((BaseInfo as IJclEnumerationTypeInfo).Names[OrdNum]) else Strings.Add(IntToStr(OrdNum)); Inc(OrdNum); end; end; end; begin BaseInfo := BaseType as IJclOrdinalRangeTypeInfo; FirstBit := BaseInfo.MinValue mod 8; LastBit := BaseInfo.MaxValue - (BaseInfo.MinValue - FirstBit); Bit := FirstBit; StartBit := -1; Strings.BeginUpdate; try while Bit <= LastBit do begin if TestBitBuffer(Value, Bit) then begin if StartBit = -1 then StartBit := Bit; end else begin if StartBit <> -1 then begin AddRange; StartBit := -1; end; end; Inc(Bit); end; if StartBit <> -1 then AddRange; finally Strings.EndUpdate; end; end; procedure TJclSetTypeInfo.SetAsList(out Value; const Strings: TStrings); var BaseInfo: IJclOrdinalRangeTypeInfo; FirstBit: Integer; I: Integer; FirstIdent: string; LastIdent: string; RangePos: Integer; FirstOrd: Int64; LastOrd: Int64; CurOrd: Integer; procedure ClearValue; var LastBit: Integer; ByteCount: Integer; begin LastBit := BaseInfo.MaxValue - BaseInfo.MinValue + 1 + FirstBit; ByteCount := (LastBit - FirstBit) div 8; if LastBit mod 8 <> 0 then Inc(ByteCount); {$IFDEF CLR} // "set of" is a "array of Byte" while ByteCount > 0 do TDynByteArray(Value)[ByteCount - 1] := 0; {$ELSE} FillChar(Value, ByteCount, 0); {$ENDIF CLR} end; begin BaseInfo := BaseType as IJclOrdinalRangeTypeInfo; FirstBit := BaseInfo.MinValue mod 8; ClearValue; Strings.BeginUpdate; try for I := 0 to Strings.Count - 1 do begin if Trim(Strings[I]) <> '' then begin FirstIdent := Trim(Strings[I]); RangePos := Pos('..', FirstIdent); if RangePos > 0 then begin LastIdent := Trim(StrRestOf(FirstIdent, RangePos + 2)); FirstIdent := Trim(Copy(FirstIdent, 1, RangePos - 1)); end else LastIdent := FirstIdent; if BaseInfo.TypeKind = tkEnumeration then begin FirstOrd := (BaseInfo as IJclEnumerationTypeInfo).IndexOfName(FirstIdent); LastOrd := (BaseInfo as IJclEnumerationTypeInfo).IndexOfName(LastIdent); {$IFDEF CLR} if FirstOrd = -1 then raise EJclRTTIError.CreateFmt(RsRTTIUnknownIdentifier, [FirstIdent]); if LastOrd = -1 then raise EJclRTTIError.CreateFmt(RsRTTIUnknownIdentifier, [LastIdent]); {$ELSE} if FirstOrd = -1 then raise EJclRTTIError.CreateResFmt(@RsRTTIUnknownIdentifier, [FirstIdent]); if LastOrd = -1 then raise EJclRTTIError.CreateResFmt(@RsRTTIUnknownIdentifier, [LastIdent]); {$ENDIF CLR} end else begin FirstOrd := StrToInt(FirstIdent); LastOrd := StrToInt(LastIdent); end; Dec(FirstOrd, BaseInfo.MinValue); Dec(LastOrd, BaseInfo.MinValue); for CurOrd := FirstOrd to LastOrd do SetBitBuffer(Value, CurOrd + FirstBit); end; end; finally Strings.EndUpdate; end; end; procedure TJclSetTypeInfo.WriteTo(const Dest: IJclInfoWriter); begin inherited WriteTo(Dest); {$IFDEF CLR} Dest.Writeln(RsRTTIBasedOn); {$ELSE} Dest.Writeln(LoadResString(@RsRTTIBasedOn)); {$ENDIF CLR} Dest.Indent; try BaseType.WriteTo(Dest); finally Dest.Outdent; end; end; procedure TJclSetTypeInfo.DeclarationTo(const Dest: IJclInfoWriter); var Base: IJclOrdinalTypeInfo; BaseEnum: IJclEnumerationTypeInfo; begin if Name[1] <> '.' then Dest.Write(Name + ' = set of '); Base := BaseType; if Base.Name[1] = '.' then begin {$IFDEF CLR} if Supports(Base, IJclEnumerationTypeInfo, BaseEnum) then BaseEnum.DeclarationTo(Dest) else Dest.Write(RsRTTITypeError); {$ELSE} if Base.QueryInterface(IJclEnumerationTypeInfo, BaseEnum) = S_OK then BaseEnum.DeclarationTo(Dest) else Dest.Write(LoadResString(@RsRTTITypeError)); {$ENDIF CLR} end else Dest.Write(Base.Name); if Name[1] <> '.' then begin {$IFDEF CLR} Dest.Write('; // ' + JclEnumValueToIdent(Borland.Delphi.System.TypeInfo(TOrdType), TypeData.OrdType)); {$ELSE} Dest.Write('; // ' + JclEnumValueToIdent(System.TypeInfo(TOrdType), TypeData.OrdType)); {$ENDIF CLR} Dest.Writeln(''); end; end; //=== { TJclFloatTypeInfo } ================================================== type TJclFloatTypeInfo = class(TJclTypeInfo, IJclFloatTypeInfo) protected function GetFloatType: TFloatType; procedure WriteTo(const Dest: IJclInfoWriter); override; procedure DeclarationTo(const Dest: IJclInfoWriter); override; public property FloatType: TFloatType read GetFloatType; end; function TJclFloatTypeInfo.GetFloatType: TFloatType; begin Result := TypeData.FloatType; end; procedure TJclFloatTypeInfo.WriteTo(const Dest: IJclInfoWriter); begin inherited WriteTo(Dest); {$IFDEF CLR} Dest.Writeln(RsRTTIFloatType + JclEnumValueToIdent(Borland.Delphi.System.TypeInfo(TFloatType), TypeData.FloatType)); {$ELSE} Dest.Writeln(LoadResString(@RsRTTIFloatType) + JclEnumValueToIdent(System.TypeInfo(TFloatType), TypeData.FloatType)); {$ENDIF CLR} end; procedure TJclFloatTypeInfo.DeclarationTo(const Dest: IJclInfoWriter); var S: string; FT: TFloatType; begin FT := FloatType; {$IFDEF CLR} S := StrRestOf(JclEnumValueToIdent(Borland.Delphi.System.TypeInfo(TFloatType), FT), 3); {$ELSE} S := StrRestOf(JclEnumValueToIdent(System.TypeInfo(TFloatType), FT), 3); {$ENDIF CLR} Dest.Writeln(Name + ' = type ' + S + ';'); end; //=== { TJclStringTypeInfo } ================================================= type TJclStringTypeInfo = class(TJclTypeInfo, IJclStringTypeInfo) protected function GetMaxLength: Integer; procedure WriteTo(const Dest: IJclInfoWriter); override; procedure DeclarationTo(const Dest: IJclInfoWriter); override; public property MaxLength: Integer read GetMaxLength; end; function TJclStringTypeInfo.GetMaxLength: Integer; begin Result := TypeData.MaxLength; end; procedure TJclStringTypeInfo.WriteTo(const Dest: IJclInfoWriter); begin inherited WriteTo(Dest); {$IFDEF CLR} Dest.Writeln(RsRTTIMaxLen + IntToStr(MaxLength)); {$ELSE} Dest.Writeln(LoadResString(@RsRTTIMaxLen) + IntToStr(MaxLength)); {$ENDIF CLR} end; procedure TJclStringTypeInfo.DeclarationTo(const Dest: IJclInfoWriter); begin if Name[1] <> '.' then Dest.Write(Name + ' = '); Dest.Write('string[' + IntToStr(MaxLength) + ']'); if Name[1] <> '.' then Dest.Writeln(';'); end; //=== { TJclPropInfo } ======================================================= type TJclPropInfo = class(TInterfacedObject, IJclPropInfo) private FPropInfo: PPropInfo; protected function GetPropInfo: PPropInfo; function GetPropType: IJclTypeInfo; function GetReader: {$IFDEF CLR}MethodInfo{$ELSE}Pointer{$ENDIF}; function GetWriter: {$IFDEF CLR}MethodInfo{$ELSE}Pointer{$ENDIF}; function GetStoredProc: {$IFDEF CLR}MethodInfo{$ELSE}Pointer{$ENDIF}; function GetIndex: Integer; function GetDefault: Longint; function GetNameIndex: Smallint; function GetName: string; {$IFDEF CLR} function MethodInfoToPropSecpKind(Info: MethodInfo): TJclPropSpecKind; {$ENDIF CLR} function GetSpecKind(const Value: Integer): TJclPropSpecKind; function GetSpecValue(const Value: Integer): Integer; function GetReaderType: TJclPropSpecKind; function GetWriterType: TJclPropSpecKind; function GetStoredType: TJclPropSpecKind; function GetReaderValue: Integer; function GetWriterValue: Integer; function GetStoredValue: Integer; public constructor Create(const APropInfo: PPropInfo); function IsStored(const AInstance: TObject): Boolean; function HasDefault: Boolean; function HasIndex: Boolean; property PropInfo: PPropInfo read GetPropInfo; property PropType: IJclTypeInfo read GetPropType; property Reader: {$IFDEF CLR}MethodInfo{$ELSE}Pointer{$ENDIF} read GetReader; property Writer: {$IFDEF CLR}MethodInfo{$ELSE}Pointer{$ENDIF} read GetWriter; property StoredProc: {$IFDEF CLR}MethodInfo{$ELSE}Pointer{$ENDIF} read GetStoredProc; property ReaderType: TJclPropSpecKind read GetReaderType; property WriterType: TJclPropSpecKind read GetWriterType; property StoredType: TJclPropSpecKind read GetStoredType; property ReaderValue: Integer read GetReaderValue; property WriterValue: Integer read GetWriterValue; property StoredValue: Integer read GetStoredValue; property Index: Integer read GetIndex; property Default: Longint read GetDefault; property NameIndex: Smallint read GetNameIndex; property Name: string read GetName; end; constructor TJclPropInfo.Create(const APropInfo: PPropInfo); begin inherited Create; FPropInfo := APropInfo; end; function TJclPropInfo.GetPropInfo: PPropInfo; begin Result := FPropInfo; end; function TJclPropInfo.GetPropType: IJclTypeInfo; begin {$IFDEF CLR} Result := JclTypeInfo(PropInfo.PropType); {$ELSE} Result := JclTypeInfo(PropInfo.PropType^); {$ENDIF CLR} end; function TJclPropInfo.GetReader: {$IFDEF CLR}MethodInfo{$ELSE}Pointer{$ENDIF}; begin {$IFDEF CLR} Result := (PropInfo as PropertyInfo).GetGetMethod; {$ELSE} Result := PropInfo.GetProc; {$ENDIF CLR} end; function TJclPropInfo.GetWriter: {$IFDEF CLR}MethodInfo{$ELSE}Pointer{$ENDIF}; begin {$IFDEF CLR} Result := (PropInfo as PropertyInfo).GetSetMethod; {$ELSE} Result := PropInfo.SetProc; {$ENDIF CLR} end; function TJclPropInfo.GetStoredProc: {$IFDEF CLR}MethodInfo{$ELSE}Pointer{$ENDIF}; {$IFDEF CLR} var I: Integer; Accessors: array of MethodInfo; Attributes: array of Attribute; begin Result := nil; Attributes := Attribute.GetCustomAttributes(PropInfo, True); // .NET serializing system: NonSerializedAttribute for I := 0 to Length(Attributes) - 1 do if Attributes[I] is NonSerializedAttribute then Exit; // .NET form designer storage: DesignerSerializationVisibilityAttribute for I := 0 to Length(Attributes) - 1 do if Attributes[I] is DesignerSerializationVisibilityAttribute then Exit; if PropInfo is PropertyInfo then begin Accessors := PropertyInfo(PropInfo).GetAccessors; for I := 0 to High(Accessors) do begin if Accessors[I].ReturnType.Equals(TypeOf(System.Boolean)) and Accessors[I].Name.StartsWith('stored_') then begin Result := Accessors[I]; Break; end; end; end; end; {$ELSE} begin Result := PropInfo.StoredProc; end; {$ENDIF CLR} function TJclPropInfo.GetIndex: Integer; begin {$IFDEF CLR} Result := Integer($8000000); {$ELSE} Result := PropInfo.Index; {$ENDIF CLR} end; function TJclPropInfo.GetDefault: Longint; begin {$IFDEF CLR} Result := GetOrdPropDefault(PropInfo); {$ELSE} Result := PropInfo.Default; {$ENDIF CLR} end; function TJclPropInfo.GetNameIndex: Smallint; begin {$IFDEF CLR} Result := 0; {$ELSE} Result := PropInfo.NameIndex; {$ENDIF CLR} end; function TJclPropInfo.GetName: string; begin Result := PropInfo.Name; end; {$IFDEF CLR} function TJclPropInfo.MethodInfoToPropSecpKind(Info: MethodInfo): TJclPropSpecKind; begin if Info.IsStatic then Result := pskStaticMethod else if Info.IsVirtual then Result := pskVirtualMethod else Result := pskNone; end; {$ENDIF CLR} function TJclPropInfo.GetSpecKind(const Value: Integer): TJclPropSpecKind; var P: Integer; begin P := Value shr 24; case P of $00: if Value < 2 then Result := pskConstant else Result := pskStaticMethod; $FE: Result := pskVirtualMethod; $FF: Result := pskField; else Result := pskStaticMethod; end; end; function TJclPropInfo.GetSpecValue(const Value: Integer): Integer; begin case GetSpecKind(Value) of pskStaticMethod, pskConstant: Result := Value; pskVirtualMethod: Result := Smallint(Value and $0000FFFF); pskField: Result := Value and $00FFFFFF; else Result := 0; end; end; function TJclPropInfo.GetReaderType: TJclPropSpecKind; begin {$IFDEF CLR} Result := MethodInfoToPropSecpKind(Reader); {$ELSE} Result := GetSpecKind(Integer(Reader)); {$ENDIF CLR} end; function TJclPropInfo.GetWriterType: TJclPropSpecKind; begin {$IFDEF CLR} Result := MethodInfoToPropSecpKind(Writer); {$ELSE} Result := GetSpecKind(Integer(Writer)); {$ENDIF CLR} end; function TJclPropInfo.GetStoredType: TJclPropSpecKind; begin {$IFDEF CLR} Result := MethodInfoToPropSecpKind(StoredProc); {$ELSE} Result := GetSpecKind(Integer(StoredProc)); {$ENDIF CLR} end; function TJclPropInfo.GetReaderValue: Integer; begin {$IFDEF CLR} Result := 0; {$ELSE} Result := GetSpecValue(Integer(Reader)); {$ENDIF CLR} end; function TJclPropInfo.GetWriterValue: Integer; begin {$IFDEF CLR} Result := 0; {$ELSE} Result := GetSpecValue(Integer(Writer)); {$ENDIF CLR} end; function TJclPropInfo.GetStoredValue: Integer; begin {$IFDEF CLR} Result := 0; {$ELSE} Result := GetSpecValue(Integer(StoredProc)); {$ENDIF CLR} end; function TJclPropInfo.IsStored(const AInstance: TObject): Boolean; begin Result := IsStoredProp(AInstance, FPropInfo); end; function TJclPropInfo.HasDefault: Boolean; begin Result := Longword(Default) <> $80000000; end; function TJclPropInfo.HasIndex: Boolean; begin Result := Longword(Index) <> $80000000; end; //=== { TJclClassTypeInfo } ================================================== type TJclClassTypeInfo = class(TJclTypeInfo, IJclClassTypeInfo) protected function GetClassRef: TClass; function GetParent: IJclClassTypeInfo; function GetTotalPropertyCount: Integer; function GetPropertyCount: Integer; function GetProperties(const PropIdx: Integer): IJclPropInfo; function GetPropNames(const Name: string): IJclPropInfo; function GetUnitName: string; procedure WriteTo(const Dest: IJclInfoWriter); override; procedure DeclarationTo(const Dest: IJclInfoWriter); override; public property ClassRef: TClass read GetClassRef; property Parent: IJclClassTypeInfo read GetParent; property TotalPropertyCount: Integer read GetTotalPropertyCount; property PropertyCount: Integer read GetPropertyCount; property Properties[const PropIdx: Integer]: IJclPropInfo read GetProperties; property PropNames[const Name: string]: IJclPropInfo read GetPropNames; property UnitName: string read GetUnitName; end; function TJclClassTypeInfo.GetClassRef: TClass; begin Result := TypeData.ClassType; end; function TJclClassTypeInfo.GetParent: IJclClassTypeInfo; begin {$IFDEF CLR} if (TypeData.ParentInfo <> nil) then Result := JclTypeInfo(TypeData.ParentInfo) as IJclClassTypeInfo {$ELSE} if (TypeData.ParentInfo <> nil) and (TypeData.ParentInfo^ <> nil) then Result := JclTypeInfo(TypeData.ParentInfo^) as IJclClassTypeInfo {$ENDIF CLR} else Result := nil; end; function TJclClassTypeInfo.GetTotalPropertyCount: Integer; begin Result := TypeData.PropCount; end; function TJclClassTypeInfo.GetPropertyCount: Integer; {$IFDEF CLR} begin Result := TypeData.PropCount; end; {$ELSE} var PropData: ^TPropData; begin PropData := @TypeData.UnitName; Inc(Integer(PropData), 1 + Length(UnitName)); Result := PropData.PropCount; end; {$ENDIF CLR} function TJclClassTypeInfo.GetProperties(const PropIdx: Integer): IJclPropInfo; {$IFDEF CLR} var List: TPropList; begin if PropIdx + 1 > TypeData.PropCount then Result := Parent.Properties[PropIdx - TypeData.PropCount] else begin List := GetPropInfos(TypeInfo); if PropIdx > 0 then Result := TJclPropInfo.Create(List[PropIdx]) else Result := TJclPropInfo.Create(List[0]); end; end; {$ELSE} var PropData: ^TPropData; Prop: PPropInfo; Idx: Integer; RecSize: Integer; begin PropData := @TypeData.UnitName; Inc(Integer(PropData), 1 + Length(UnitName)); if PropIdx + 1 > PropData.PropCount then Result := Parent.Properties[PropIdx - PropData.PropCount] else begin Prop := PPropInfo(PropData); Inc(Integer(Prop), 2); if PropIdx > 0 then begin RecSize := SizeOf(TPropInfo) - SizeOf(ShortString); Idx := PropIdx; while Idx > 0 do begin Inc(Integer(Prop), RecSize); Inc(Integer(Prop), 1 + PByte(Prop)^); Dec(Idx); end; end; Result := TJclPropInfo.Create(Prop); end; end; {$ENDIF CLR} function TJclClassTypeInfo.GetPropNames(const Name: string): IJclPropInfo; var PropInfo: PPropInfo; begin PropInfo := GetPropInfo(TypeInfo, Name); if PropInfo <> nil then Result := TJclPropInfo.Create(PropInfo) else Result := nil; end; function TJclClassTypeInfo.GetUnitName: string; begin Result := TypeData.UnitName; end; procedure TJclClassTypeInfo.WriteTo(const Dest: IJclInfoWriter); const {$IFDEF CLR} cFmt1 = '[%s %d]'; cFmt2 = '[%s %s %s]'; cFmt3 = '[%s=%s]'; cFmt4 = '[%s=%s %s]'; {$ELSE} cFmt1 = '[%s %d]'; cFmt2 = '[%s %s $%p]'; cFmt3 = '[%s=%s]'; cFmt4 = '[%s=%s $%p]'; {$ENDIF CLR} var I: Integer; Prop: IJclPropInfo; begin inherited WriteTo(Dest); Dest.Writeln(RsRTTIClassName + ClassRef.ClassName); Dest.Writeln(RsRTTIParent + Parent.ClassRef.ClassName); Dest.Writeln(RsRTTIUnitName + UnitName); Dest.Writeln(RsRTTIPropCount + IntToStr(PropertyCount) + ' (' + IntToStr(TotalPropertyCount) + ')'); Dest.Indent; try for I := 0 to PropertyCount-1 do begin Prop := Properties[I]; Dest.Writeln(Prop.Name + ': ' + Prop.PropType.Name); Dest.Indent; try if Prop.HasIndex then Dest.Writeln(Format(cFmt1, [RsRTTIIndex, Prop.Index])); if Prop.HasDefault then Dest.Writeln(Format(cFmt1, [RsRTTIDefault, Prop.Default])); case Prop.ReaderType of pskStaticMethod: Dest.Writeln(Format(cFmt2, [RsRTTIPropRead, RsRTTIStaticMethod, {$IFDEF CLR} Prop.Reader.ToString()])); {$ELSE} Pointer(Prop.ReaderValue)])); {$ENDIF CLR} pskField: Dest.Writeln(Format(cFmt2, [RsRTTIPropRead, RsRTTIField, {$IFDEF CLR} Prop.Reader.ToString()])); {$ELSE} Pointer(Prop.ReaderValue)])); {$ENDIF CLR} pskVirtualMethod: Dest.Writeln(Format(cFmt2, [RsRTTIPropRead, RsRTTIVirtualMethod, {$IFDEF CLR} Prop.Reader.ToString()])); {$ELSE} Pointer(Prop.ReaderValue)])); {$ENDIF CLR} end; case Prop.WriterType of pskStaticMethod: Dest.Writeln(Format(cFmt2, [RsRTTIPropWrite, RsRTTIStaticMethod, {$IFDEF CLR} Prop.Writer.ToString()])); {$ELSE} Pointer(Prop.WriterValue)])); {$ENDIF CLR} pskField: Dest.Writeln(Format(cFmt2, [RsRTTIPropWrite, RsRTTIField, {$IFDEF CLR} Prop.Writer.ToString()])); {$ELSE} Pointer(Prop.WriterValue)])); {$ENDIF CLR} pskVirtualMethod: Dest.Writeln(Format(cFmt2, [RsRTTIPropWrite, RsRTTIVirtualMethod, {$IFDEF CLR} Prop.Writer.ToString()])); {$ELSE} Pointer(Prop.WriterValue)])); {$ENDIF CLR} end; case Prop.StoredType of pskConstant: if Boolean(Prop.StoredValue) then Dest.Writeln(Format(cFmt3, [RsRTTIPropStored, RsRTTITrue])) else Dest.Writeln(Format(cFmt3, [RsRTTIPropStored, RsRTTIFalse])); pskStaticMethod: Dest.Writeln(Format(cFmt4, [RsRTTIPropStored, RsRTTIStaticMethod, {$IFDEF CLR} Prop.StoredProc.ToString()])); {$ELSE} Pointer(Prop.StoredValue)])); {$ENDIF CLR} pskField: Dest.Writeln(Format(cFmt4, [RsRTTIPropStored, RsRTTIField, {$IFDEF CLR} Prop.StoredProc.ToString()])); {$ELSE} Pointer(Prop.StoredValue)])); {$ENDIF CLR} pskVirtualMethod: Dest.Writeln(Format(cFmt4, [RsRTTIPropStored, RsRTTIVirtualMethod, {$IFDEF CLR} Prop.StoredProc.ToString()])); {$ELSE} Pointer(Prop.StoredValue)])); {$ENDIF CLR} end; finally Dest.Outdent; end; end; finally Dest.Outdent; end; end; procedure TJclClassTypeInfo.DeclarationTo(const Dest: IJclInfoWriter); var {$IFDEF CLR} IntfTbl: array of &Type; {$ELSE} IntfTbl: PInterfaceTable; {$ENDIF CLR} I: Integer; Prop: IJclPropInfo; begin if (Parent <> nil) and {$IFDEF CLR} not SameText(Parent.Name, 'TObject') then {$ELSE} not AnsiSameText(Parent.Name, 'TObject') then {$ENDIF CLR} begin Dest.Write(Name + ' = class(' + Parent.Name); {$IFDEF CLR} IntfTbl := ClassRef.ClassInfo.GetInterfaces; if IntfTbl <> nil then for I := 0 to High(IntfTbl) do Dest.Write(', [''' + JclGUIDToString(IntfTbl[I].TypeData.Guid) + ''']'); {$ELSE} IntfTbl := ClassRef.GetInterfaceTable; if IntfTbl <> nil then for I := 0 to IntfTbl.EntryCount-1 do Dest.Write(', [''' + JclGUIDToString(IntfTbl.Entries[I].IID) + ''']'); {$ENDIF CLR} Dest.Writeln(') // unit ' + UnitName); end else Dest.Writeln(Name + ' = class // unit ' + UnitName); if PropertyCount > 0 then begin Dest.Writeln('published'); Dest.Indent; try for I := 0 to PropertyCount-1 do begin Prop := Properties[I]; Dest.Write('property ' + Prop.Name + ': ' + Prop.PropType.Name); if Prop.HasIndex then Dest.Write(Format(' index %d', [Prop.Index])); case Prop.ReaderType of {$IFDEF CLR} pskStaticMethod: Dest.Write(Format(' read [static method %s]', [Prop.Reader.ToString()])); pskField: Dest.Write(Format(' read [field %s]', [Prop.Reader.ToString()])); pskVirtualMethod: Dest.Write(Format(' read [virtual method %s]', [Prop.Reader.ToString()])); {$ELSE} pskStaticMethod: Dest.Write(Format(' read [static method $%p]', [Pointer(Prop.ReaderValue)])); pskField: Dest.Write(Format(' read [field $%p]', [Pointer(Prop.ReaderValue)])); pskVirtualMethod: Dest.Write(Format(' read [virtual method $%p]', [Pointer(Prop.ReaderValue)])); {$ENDIF CLR} end; case Prop.WriterType of {$IFDEF CLR} pskStaticMethod: Dest.Write(Format(' write [static method %s]', [Prop.Writer.ToString()])); pskField: Dest.Write(Format(' write [field %s]', [Prop.Writer.ToString()])); pskVirtualMethod: Dest.Write(Format(' write [virtual method %s]', [Prop.Writer.ToString()])); {$ELSE} pskStaticMethod: Dest.Write(Format(' write [static method $%p]', [Pointer(Prop.WriterValue)])); pskField: Dest.Write(Format(' write [field $%p]', [Pointer(Prop.WriterValue)])); pskVirtualMethod: Dest.Write(Format(' write [virtual method $%p]', [Pointer(Prop.WriterValue)])); {$ENDIF CLR} end; case Prop.StoredType of pskConstant: if Boolean(Prop.StoredValue) then Dest.Write(' stored = True') else Dest.Write(' stored = False'); {$IFDEF CLR} pskStaticMethod: Dest.Write(Format(' stored = [static method %s]', [Prop.StoredProc.ToString()])); pskField: Dest.Write(Format(' stored = [field %s]', [Prop.StoredProc.ToString()])); pskVirtualMethod: Dest.Write(Format(' stored = [virtual method %s]', [Prop.StoredProc.ToString()])); {$ELSE} pskStaticMethod: Dest.Write(Format(' stored = [static method $%p]', [Pointer(Prop.StoredValue)])); pskField: Dest.Write(Format(' stored = [field $%p]', [Pointer(Prop.StoredValue)])); pskVirtualMethod: Dest.Write(Format(' stored = [virtual method $%p]', [Pointer(Prop.StoredValue)])); {$ENDIF CLR} end; if Prop.HasDefault then Dest.Write(' default ' + IntToStr(Prop.Default)); Dest.Writeln(';'); end; finally Dest.Outdent; end; end; Dest.Writeln('end;'); end; //=== { TJclEventParamInfo } ================================================= type TJclEventParamInfo = class(TInterfacedObject, IJclEventParamInfo) private FParam: {$IFDEF CLR}ParameterInfo{$ELSE}Pointer{$ENDIF}; protected function GetFlags: TParamFlags; function GetName: string; function GetRecSize: Integer; function GetTypeName: string; function GetParam: {$IFDEF CLR}ParameterInfo{$ELSE}Pointer{$ENDIF}; public constructor Create(const AParam: {$IFDEF CLR}ParameterInfo{$ELSE}Pointer{$ENDIF}); property Flags: TParamFlags read GetFlags; property Name: string read GetName; property RecSize: Integer read GetRecSize; property TypeName: string read GetTypeName; property Param: {$IFDEF CLR}ParameterInfo{$ELSE}Pointer{$ENDIF} read GetParam; end; constructor TJclEventParamInfo.Create(const AParam: {$IFDEF CLR}ParameterInfo{$ELSE}Pointer{$ENDIF}); begin inherited Create; FParam := AParam; end; function TJclEventParamInfo.GetFlags: TParamFlags; {$IFDEF CLR} var Attr: Attribute; {$ENDIF CLR} begin {$IFDEF CLR} Result := []; if FParam.IsOut then Result := [pfOut] else if FParam.ParameterType.IsByRef then Result := [pfVar] else if FindAttribute(FParam.ParameterType, TypeOf(TConstantParamAttribute), Attr) then Result := [pfConst]; with FParam.ParameterType do if IsArray or (IsByRef and HasElementType and GetElementType.IsArray) then Include(Result, pfArray); {$ELSE} Result := TParamFlags(PByte(Param)^); {$ENDIF CLR} end; function TJclEventParamInfo.GetName: string; {$IFDEF CLR} begin Result := FParam.Name; end; {$ELSE} var PName: PShortString; begin PName := Param; Inc(Integer(PName)); Result := PName^; end; {$ENDIF CLR} function TJclEventParamInfo.GetRecSize: Integer; begin Result := 3 + Length(Name) + Length(TypeName); end; function TJclEventParamInfo.GetTypeName: string; {$IFDEF CLR} begin Result := FParam.ParameterType.Name; end; {$ELSE} var PName: PShortString; begin PName := Param; Inc(Integer(PName)); Inc(Integer(PName), PByte(PName)^ + 1); Result := PName^; end; {$ENDIF CLR} function TJclEventParamInfo.GetParam: {$IFDEF CLR}ParameterInfo{$ELSE}Pointer{$ENDIF}; begin Result := FParam; end; //=== { TJclEventTypeInfo } ================================================== type TJclEventTypeInfo = class(TJclTypeInfo, IJclEventTypeInfo) protected function GetMethodKind: TMethodKind; function GetParameterCount: Integer; function GetParameters(const ParamIdx: Integer): IJclEventParamInfo; function GetResultTypeName: string; procedure WriteTo(const Dest: IJclInfoWriter); override; procedure DeclarationTo(const Dest: IJclInfoWriter); override; public property MethodKind: TMethodKind read GetMethodKind; property ParameterCount: Integer read GetParameterCount; property Parameters[const ParamIdx: Integer]: IJclEventParamInfo read GetParameters; property ResultTypeName: string read GetResultTypeName; end; function TJclEventTypeInfo.GetMethodKind: TMethodKind; begin Result := TypeData.MethodKind; end; function TJclEventTypeInfo.GetParameterCount: Integer; begin Result := TypeData.ParamCount; end; function TJclEventTypeInfo.GetParameters(const ParamIdx: Integer): IJclEventParamInfo; {$IFNDEF CLR} var I: Integer; Param: Pointer; {$ENDIF ~CLR} begin Result := nil; {$IFDEF CLR} if ParamIdx < TypeData.ParamCount then Result := TJclEventParamInfo.Create(TypeData.Params[ParamIdx]); {$ELSE} Param := @TypeData.ParamList[0]; I := ParamIdx; while I >= 0 do begin Result := TJclEventParamInfo.Create(Param); Inc(Integer(Param), Result.RecSize); Dec(I); end; {$ENDIF CLR} end; function TJclEventTypeInfo.GetResultTypeName: string; {$IFDEF CLR} begin Result := TypeData.ResultTypeName; end; {$ELSE} var LastParam: IJclEventParamInfo; ResPtr: PShortString; begin if MethodKind = mkFunction then begin if ParameterCount > 0 then begin LastParam := Parameters[ParameterCount-1]; ResPtr := Pointer(Longint(LastParam.Param) + LastParam.RecSize); end else ResPtr := @TypeData.ParamList[0]; Result := ResPtr^; end else Result := ''; end; {$ENDIF CLR} procedure TJclEventTypeInfo.WriteTo(const Dest: IJclInfoWriter); var I: Integer; Param: IJclEventParamInfo; ParamFlags: TParamFlags; begin inherited WriteTo(Dest); {$IFDEF CLR} Dest.Writeln(RsRTTIMethodKind + JclEnumValueToIdent(Borland.Delphi.System.TypeInfo(TMethodKind), TypeData.MethodKind)); Dest.Writeln(RsRTTIParamCount + IntToStr(ParameterCount)); Dest.Indent; try for I := 0 to ParameterCount-1 do begin if I > 0 then Dest.Writeln(''); Param := Parameters[I]; ParamFlags := Param.Flags; Dest.Writeln(RsRTTIName + Param.Name); Dest.Writeln(RsRTTIType + Param.TypeName); Dest.Writeln(RsRTTIFlags + JclSetToStr(Borland.Delphi.System.TypeInfo(TParamFlags), ParamFlags, True, False)); end; finally Dest.Outdent; end; if MethodKind = mkFunction then Dest.Writeln(RsRTTIReturnType + ResultTypeName); {$ELSE} Dest.Writeln(LoadResString(@RsRTTIMethodKind) + JclEnumValueToIdent(System.TypeInfo(TMethodKind), TypeData.MethodKind)); Dest.Writeln(LoadResString(@RsRTTIParamCount) + IntToStr(ParameterCount)); Dest.Indent; try for I := 0 to ParameterCount-1 do begin if I > 0 then Dest.Writeln(''); Param := Parameters[I]; ParamFlags := Param.Flags; Dest.Writeln(LoadResString(@RsRTTIName) + Param.Name); Dest.Writeln(LoadResString(@RsRTTIType) + Param.TypeName); Dest.Writeln(LoadResString(@RsRTTIFlags) + JclSetToStr(System.TypeInfo(TParamFlags), ParamFlags, True, False)); end; finally Dest.Outdent; end; if MethodKind = mkFunction then Dest.Writeln(LoadResString(@RsRTTIReturnType) + ResultTypeName); {$ENDIF CLR} end; procedure TJclEventTypeInfo.DeclarationTo(const Dest: IJclInfoWriter); var Prefix: string; I: Integer; Param: IJclEventParamInfo; begin Dest.Write(Name + ' = '); if MethodKind = mkFunction then Dest.Write('function') else Dest.Write('procedure'); Prefix := '('; for I := 0 to ParameterCount-1 do begin Dest.Write(Prefix); Prefix := '; '; Param := Parameters[I]; {$IFDEF CLR} if pfVar in Param.Flags then Dest.Write(RsRTTIVar) else if pfConst in Param.Flags then Dest.Write(RsRTTIConst) else if pfOut in Param.Flags then Dest.Write(RsRTTIOut); {$ELSE} if pfVar in Param.Flags then Dest.Write(LoadResString(@RsRTTIVar)) else if pfConst in Param.Flags then Dest.Write(LoadResString(@RsRTTIConst)) else if pfOut in Param.Flags then Dest.Write(LoadResString(@RsRTTIOut)); {$ENDIF CLR} Dest.Write(Param.Name); if Param.TypeName <> '' then begin Dest.Write(': '); {$IFDEF CLR} if pfArray in Param.Flags then Dest.Write(RsRTTIArrayOf); if AnsiSameText(Param.TypeName, 'TVarRec') and (pfArray in Param.Flags) then Dest.Write(TrimRight(RsRTTIConst)) {$ELSE} if pfArray in Param.Flags then Dest.Write(LoadResString(@RsRTTIArrayOf)); if AnsiSameText(Param.TypeName, 'TVarRec') and (pfArray in Param.Flags) then Dest.Write(TrimRight(LoadResString(@RsRTTIConst))) {$ENDIF CLR} else Dest.Write(Param.TypeName); end; end; if ParameterCount <> 0 then Dest.Write(')'); if MethodKind = mkFunction then Dest.Write(': ' + ResultTypeName); Dest.Writeln(' of object;'); end; //=== { TJclInterfaceTypeInfo } ============================================== type TJclInterfaceTypeInfo = class(TJclTypeInfo, IJclInterfaceTypeInfo) protected function GetParent: IJclInterfaceTypeInfo; function GetFlags: TIntfFlagsBase; function GetGUID: TGUID; {$IFDEF RTL140_UP} function GetPropertyCount: Integer; {$ENDIF RTL140_UP} function GetUnitName: string; procedure WriteTo(const Dest: IJclInfoWriter); override; procedure DeclarationTo(const Dest: IJclInfoWriter); override; public property Parent: IJclInterfaceTypeInfo read GetParent; property Flags: TIntfFlagsBase read GetFlags; property GUID: TGUID read GetGUID; {$IFDEF RTL140_UP} property PropertyCount: Integer read GetPropertyCount; {$ENDIF RTL140_UP} property UnitName: string read GetUnitName; end; function TJclInterfaceTypeInfo.GetParent: IJclInterfaceTypeInfo; begin {$IFDEF CLR} if TypeInfo.BaseType <> nil then Result := JclTypeInfo(TypeInfo.BaseType) as IJclInterfaceTypeInfo {$ELSE} if (TypeData.IntfParent <> nil) and (TypeData.IntfParent^ <> nil) then Result := JclTypeInfo(TypeData.IntfParent^) as IJclInterfaceTypeInfo {$ENDIF CLR} else Result := nil; end; function TJclInterfaceTypeInfo.GetFlags: TIntfFlagsBase; begin {$IFDEF CLR} Result := []; {$ELSE} Result := TypeData.IntfFlags; {$ENDIF CLR} end; const NullGUID: TGUID = '{00000000-0000-0000-0000-000000000000}'; function TJclInterfaceTypeInfo.GetGUID: TGUID; begin if ifHasGuid in Flags then Result := TypeData.Guid else Result := NullGUID; end; {$IFDEF RTL140_UP} function TJclInterfaceTypeInfo.GetPropertyCount: Integer; {$IFDEF CLR} begin Result := TypeData.PropCount; end; {$ELSE} var PropData: ^TPropData; begin PropData := @TypeData.IntfUnit; Inc(Integer(PropData), 1 + Length(UnitName)); Result := PropData.PropCount; end; {$ENDIF CLR} {$ENDIF RTL140_UP} function TJclInterfaceTypeInfo.GetUnitName: string; begin Result := TypeData.IntfUnit; end; procedure TJclInterfaceTypeInfo.WriteTo(const Dest: IJclInfoWriter); var IntfFlags: TIntfFlagsBase; begin inherited WriteTo(Dest); if ifHasGuid in Flags then {$IFDEF CLR} Dest.Writeln(RsRTTIGUID + JclGuidToString(GUID)); IntfFlags := Flags; Dest.Writeln(RsRTTIFlags + JclSetToStr(Borland.Delphi.System.TypeInfo(TIntfFlagsBase), IntfFlags, True, False)); Dest.Writeln(RsRTTIUnitName + UnitName); if Parent <> nil then Dest.Writeln(RsRTTIParent + Parent.Name); Dest.Writeln(RsRTTIPropCount + IntToStr(PropertyCount)); {$ELSE} Dest.Writeln(LoadResString(@RsRTTIGUID) + JclGuidToString(GUID)); IntfFlags := Flags; Dest.Writeln(LoadResString(@RsRTTIFlags) + JclSetToStr(System.TypeInfo(TIntfFlagsBase), IntfFlags, True, False)); Dest.Writeln(LoadResString(@RsRTTIUnitName) + UnitName); if Parent <> nil then Dest.Writeln(LoadResString(@RsRTTIParent) + Parent.Name); {$IFDEF RTL140_UP} Dest.Writeln(LoadResString(@RsRTTIPropCount) + IntToStr(PropertyCount)); {$ENDIF RTL140_UP} {$ENDIF CLR} end; procedure TJclInterfaceTypeInfo.DeclarationTo(const Dest: IJclInfoWriter); begin Dest.Write(Name + ' = '); if ifDispInterface in Flags then Dest.Write('dispinterface') else Dest.Write('interface'); if (Parent <> nil) and not (ifDispInterface in Flags) and not AnsiSameText(Parent.Name, 'IUnknown') then Dest.Write('(' + Parent.Name + ')'); Dest.Writeln(' // unit ' + UnitName); Dest.Indent; try if ifHasGuid in Flags then Dest.Writeln('[''' + JclGuidToString(GUID) + ''']'); finally Dest.Outdent; Dest.Writeln('end;'); end; end; //=== { TJclInt64TypeInfo } ================================================== type TJclInt64TypeInfo = class(TJclTypeInfo, IJclInt64TypeInfo) protected function GetMinValue: Int64; function GetMaxValue: Int64; procedure WriteTo(const Dest: IJclInfoWriter); override; procedure DeclarationTo(const Dest: IJclInfoWriter); override; public property MinValue: Int64 read GetMinValue; property MaxValue: Int64 read GetMaxValue; end; function TJclInt64TypeInfo.GetMinValue: Int64; begin Result := TypeData.MinInt64Value; end; function TJclInt64TypeInfo.GetMaxValue: Int64; begin Result := TypeData.MaxInt64Value; end; procedure TJclInt64TypeInfo.WriteTo(const Dest: IJclInfoWriter); begin inherited WriteTo(Dest); {$IFDEF CLR} Dest.Writeln(RsRTTIMinValue + IntToStr(MinValue)); Dest.Writeln(RsRTTIMaxValue + IntToStr(MaxValue)); {$ELSE} Dest.Writeln(LoadResString(@RsRTTIMinValue) + IntToStr(MinValue)); Dest.Writeln(LoadResString(@RsRTTIMaxValue) + IntToStr(MaxValue)); {$ENDIF CLR} end; procedure TJclInt64TypeInfo.DeclarationTo(const Dest: IJclInfoWriter); begin Dest.Writeln(Name + ' = ' + IntToStr(MinValue) + ' .. ' + IntToStr(MaxValue) + ';'); end; //=== { TJclDynArrayTypeInfo } =============================================== {$IFDEF RTL140_UP} type TJclDynArrayTypeInfo = class(TJclTypeInfo, IJclDynArrayTypeInfo) protected function GetElementSize: Longint; function GetElementType: IJclTypeInfo; function GetElementsNeedCleanup: Boolean; function GetVarType: Integer; function GetUnitName: string; procedure WriteTo(const Dest: IJclInfoWriter); override; procedure DeclarationTo(const Dest: IJclInfoWriter); override; public property ElementSize: Longint read GetElementSize; property ElementType: IJclTypeInfo read GetElementType; property ElementsNeedCleanup: Boolean read GetElementsNeedCleanup; property VarType: Integer read GetVarType; property UnitName: string read GetUnitName; end; function TJclDynArrayTypeInfo.GetElementSize: Longint; begin {$IFDEF CLR} Result := Marshal.SizeOf(TypeInfo.GetElementType); {$ELSE} Result := TypeData.elSize; {$ENDIF CLR} end; function TJclDynArrayTypeInfo.GetElementType: IJclTypeInfo; begin {$IFDEF CLR} Result := JclTypeInfo(TypeInfo.GetElementType); {$ELSE} if TypeData.elType = nil then begin if TypeData.elType2 <> nil then Result := JclTypeInfo(TypeData.elType2^) else Result := nil; end else Result := JclTypeInfo(TypeData.elType^); {$ENDIF CLR} end; function TJclDynArrayTypeInfo.GetElementsNeedCleanup: Boolean; begin {$IFDEF CLR} Result := False; {$ELSE} Result := TypeData.elType <> nil; {$ENDIF CLR} end; function TJclDynArrayTypeInfo.GetVarType: Integer; begin {$IFDEF CLR} Result := Variant.VarType(TypeInfo); {$ELSE} Result := TypeData.varType; {$ENDIF CLR} end; function TJclDynArrayTypeInfo.GetUnitName: string; begin Result := TypeData.DynUnitName; end; procedure TJclDynArrayTypeInfo.WriteTo(const Dest: IJclInfoWriter); begin inherited WriteTo(Dest); Dest.Writeln(RsRTTIElSize + IntToStr(ElementSize)); if ElementType = nil then Dest.Writeln(RsRTTIElType + RsRTTITypeError) else if ElementType.Name[1] <> '.' then Dest.Writeln(RsRTTIElType + ElementType.Name) else begin Dest.Writeln(RsRTTIElType); Dest.Indent; try ElementType.WriteTo(Dest); finally Dest.Outdent; end; end; Dest.Write(RsRTTIElNeedCleanup); if ElementsNeedCleanup then Dest.Writeln(RsRTTITrue) else Dest.Writeln(RsRTTIFalse); Dest.Writeln(RsRTTIVarType + IntToStr(VarType)); Dest.Writeln(RsRTTIUnitName + UnitName); end; procedure TJclDynArrayTypeInfo.DeclarationTo(const Dest: IJclInfoWriter); begin if Name[1] <> '.' then Dest.Write(Name + ' = ' + RsRTTIArrayOf) else Dest.Write(RsRTTIArrayOf); if ElementType = nil then Dest.Write(RsRTTITypeError) else if ElementType.Name[1] = '.' then ElementType.DeclarationTo(Dest) else Dest.Write(ElementType.Name); if Name[1] <> '.' then Dest.Writeln('; // Unit ' + UnitName); end; {$ENDIF RTL140_UP} //=== Typeinfo retrieval ===================================================== function JclTypeInfo(ATypeInfo: PTypeInfo): IJclTypeInfo; begin case ATypeInfo.Kind of tkInteger, tkChar, tkWChar: Result := TJclOrdinalRangeTypeInfo.Create(ATypeInfo); tkEnumeration: Result := TJclEnumerationTypeInfo.Create(ATypeInfo); tkSet: Result := TJclSetTypeInfo.Create(ATypeInfo); tkFloat: Result := TJclFloatTypeInfo.Create(ATypeInfo); tkString: Result := TJclStringTypeInfo.Create(ATypeInfo); tkClass: Result := TJclClassTypeInfo.Create(ATypeInfo); tkMethod: Result := TJclEventTypeInfo.Create(ATypeInfo); tkInterface: Result := TJclInterfaceTypeInfo.Create(ATypeInfo); tkInt64: Result := TJclInt64TypeInfo.Create(ATypeInfo); {$IFDEF RTL140_UP} tkDynArray: Result := TJclDynArrayTypeInfo.Create(ATypeInfo); {$ENDIF RTL140_UP} else Result := TJclTypeInfo.Create(ATypeInfo); end; end; //=== User generated type info managment ===================================== {$IFNDEF CLR} var TypeList: TThreadList; type PTypeItem = ^TTypeItem; TTypeItem = record TypeInfo: PTypeInfo; RefCount: Integer; end; procedure FreeTypeData(const TypeInfo: PTypeInfo); var TD: PTypeData; begin TD := GetTypeData(TypeInfo); if TypeInfo.Kind = tkSet then RemoveTypeInfo(TD^.CompType^) else if (TypeInfo.Kind = tkEnumeration) and (TD^.BaseType^ <> TypeInfo) then RemoveTypeInfo(GetTypeData(TypeInfo)^.BaseType^); FreeMem(GetTypeData(TypeInfo)^.BaseType); FreeMem(TypeInfo); end; procedure AddType(const TypeInfo: PTypeInfo); var Item: PTypeItem; begin New(Item); try Item.TypeInfo := TypeInfo; Item.RefCount := 1; TypeList.Add(Item); except Dispose(Item); raise; end; end; procedure DeleteType(const TypeItem: PTypeItem); begin FreeTypeData(TypeItem.TypeInfo); TypeList.Remove(TypeItem); Dispose(TypeItem); end; procedure DoRefType(const TypeInfo: PTypeInfo; Add: Integer); var I: Integer; List: TList; begin List := TypeList.LockList; try I := List.Count-1; while (I >= 0) and (PTypeItem(List[I]).TypeInfo <> TypeInfo) do Dec(I); if I > -1 then Inc(PTypeItem(List[I]).RefCount, Add); finally TypeList.UnlockList; end; end; procedure ReferenceType(const TypeInfo: PTypeInfo); begin DoRefType(TypeInfo, 1); end; procedure DeReferenceType(const TypeInfo: PTypeInfo); begin DoRefType(TypeInfo, -1); end; procedure ClearInfoList; var L: TList; begin L := TypeList.LockList; try while L.Count > 0 do RemoveTypeInfo(PTypeItem(L[L.Count-1])^.TypeInfo); finally TypeList.UnlockList; end; end; procedure NewInfoItem(const TypeInfo: PTypeInfo); begin TypeList.Add(TypeInfo); end; procedure RemoveTypeInfo(TypeInfo: PTypeInfo); var I: Integer; List: TList; Item: PTypeItem; begin Item := nil; List := TypeList.LockList; try I := List.Count-1; while (I >= 0) and (PTypeItem(List[I]).TypeInfo <> TypeInfo) do Dec(I); if I > -1 then Item := PTypeItem(List[I]); finally TypeList.UnlockList; end; if Item <> nil then begin Dec(Item.RefCount); if Item.RefCount <= 0 then DeleteType(Item); end; end; {$ENDIF ~CLR} //=== Enumerations =========================================================== function JclEnumValueToIdent(TypeInfo: PTypeInfo; const Value): string; var MinEnum: Integer; MaxEnum: Integer; EnumVal: Int64; OrdType: TOrdType; begin OrdType := GetTypeData(TypeInfo).OrdType; MinEnum := GetTypeData(TypeInfo).MinValue; MaxEnum := GetTypeData(TypeInfo).MaxValue; case OrdType of otSByte: EnumVal := Smallint(Value); otUByte: EnumVal := Byte(Value); otSWord: EnumVal := Shortint(Value); otUWord: EnumVal := Word(Value); otSLong: EnumVal := Integer(Value); otULong: EnumVal := Longword(Value); else EnumVal := 0; end; // Check range... if (EnumVal < MinEnum) or (EnumVal > MaxEnum) then {$IFDEf CLR} Result := Format(RsRTTIValueOutOfRange, [RsRTTIOrdinal + IntToStr(EnumVal)]) {$ELSE} Result := Format(LoadResString(@RsRTTIValueOutOfRange), [LoadResString(@RsRTTIOrdinal) + IntToStr(EnumVal)]) {$ENDIF CLR} else Result := GetEnumName(TypeInfo, EnumVal); end; {$IFNDEF CLR} function JclGenerateEnumType(const TypeName: ShortString; const Literals: array of string): PTypeInfo; type PInteger = ^Integer; var StringSize: Integer; I: Integer; TypeData: PTypeData; CurName: PShortString; begin StringSize := 0; for I := Low(Literals) to High(Literals) do StringSize := StringSize + 1 + Length(Literals[I]); Result := AllocMem(SizeOf(TTypeInfo) + SizeOf(TOrdType) + (2*SizeOf(Integer)) + SizeOf(PPTypeInfo) + StringSize {$IFDEF RTL140_UP}+ 1{$ENDIF RTL140_UP}); try with Result^ do begin Kind := tkEnumeration; Name := TypeName; end; TypeData := GetTypeData(Result); TypeData^.BaseType := AllocMem(SizeOf(Pointer)); if Length(Literals) < 256 then TypeData^.OrdType := otUByte else if Length(Literals) < 65536 then TypeData^.OrdType := otUWord else TypeData^.OrdType := otULong; TypeData^.MinValue := 0; TypeData^.MaxValue := Length(Literals)-1; TypeData^.BaseType^ := Result; // No sub-range: basetype points to itself CurName := @TypeData^.NameList; for I := Low(Literals) to High(Literals) do begin CurName^ := Literals[I]; Inc(Integer(CurName), Length(Literals[I])+1); end; {$IFDEF RTL140_UP} CurName^ := ''; // Unit name unknown {$ENDIF RTL140_UP} AddType(Result); except try ReallocMem(Result, 0); except Result := nil; end; raise; end; end; function JclGenerateEnumTypeBasedOn(const TypeName: ShortString; BaseType: PTypeInfo; const PrefixCut: Byte): PTypeInfo; var BaseInfo: IJclTypeInfo; BaseKind: TTypeKind; Literals: array of string; I: Integer; S: string; begin BaseInfo := JclTypeInfo(BaseType); BaseKind := BaseInfo.TypeKind; if BaseInfo.TypeKind <> tkEnumeration then raise EJclRTTIError.CreateResFmt(@RsRTTIInvalidBaseType, [BaseInfo.Name, JclEnumValueToIdent(System.TypeInfo(TTypeKind), BaseKind)]); with BaseInfo as IJclEnumerationTypeInfo do begin SetLength(Literals, MaxValue - MinValue + 1); for I := MinValue to MaxValue do begin S := Names[I]; if PrefixCut = PREFIX_CUT_LOWERCASE then while (Length(S) > 0) and (S[1] in AnsiLowercaseLetters) do Delete(S, 1, 1); if (PrefixCut > 0) and (PrefixCut < MaxPrefixCut) then Delete(S, 1, PrefixCut); if S = '' then S := Names[I]; Literals[I- MinValue] := S; end; if PrefixCut = PREFIX_CUT_EQUAL then begin S := Literals[High(Literals)]; I := High(Literals)-1; while (I >= 0) and (S > '') do begin while Copy(Literals[I], 1, Length(S)) <> S do Delete(S, Length(S), 1); Dec(I); end; if S > '' then for I := Low(Literals) to High(Literals) do begin Literals[I] := StrRestOf(Literals[I], Length(S)); if Literals[I] = '' then Literals[I] := Names[I + MinValue]; end; end; end; Result := JclGenerateEnumType(TypeName, Literals); end; function JclGenerateSubRange(BaseType: PTypeInfo; const TypeName: string; const MinValue, MaxValue: Integer): PTypeInfo; var TypeData: PTypeData; begin Result := AllocMem(SizeOf(TTypeInfo) + SizeOf(TOrdType) + (2*SizeOf(Integer)) + SizeOf(PPTypeInfo)); try with Result^ do begin Kind := BaseType^.Kind; Name := TypeName; end; TypeData := GetTypeData(Result); TypeData^.OrdType := GetTypeData(BaseType)^.OrdType; TypeData^.MinValue := MinValue; TypeData^.MaxValue := MaxValue; TypeData^.BaseType := AllocMem(SizeOf(Pointer)); TypeData^.BaseType^ := BaseType; AddType(Result); except try ReallocMem(Result, 0); except Result := nil; end; raise; end; ReferenceType(BaseType); end; {$ENDIF ~CLR} //=== Integers =============================================================== function JclStrToTypedInt(Value: string; TypeInfo: PTypeInfo): Integer; var Conv: TIdentToInt; HaveConversion: Boolean; Info: IJclTypeInfo; RangeInfo: IJclOrdinalRangeTypeInfo; TmpVal: Int64; begin if TypeInfo <> nil then Conv := FindIdentToInt(TypeInfo) else Conv := nil; HaveConversion := (@Conv <> nil) and Conv(Value, Result); if not HaveConversion then begin if TypeInfo <> nil then begin Info := JclTypeInfo(TypeInfo); {$IFDEF CLR} if not Supports(Info, IJclOrdinalRangeTypeInfo, RangeInfo) then {$ELSE} if Info.QueryInterface(IJclOrdinalRangeTypeInfo, RangeInfo) <> S_OK then {$ENDIF CLR} RangeInfo := nil; TmpVal := StrToInt64(Value); if (RangeInfo <> nil) and ((TmpVal < RangeInfo.MinValue) or (TmpVal > RangeInfo.MaxValue)) then {$IFDEF CLR} raise EConvertError.CreateFmt(SInvalidInteger, [Value]); {$ELSE} raise EConvertError.CreateResFmt(@SInvalidInteger, [Value]); {$ENDIF CLR} Result := Integer(TmpVal); end else Result := StrToInt(Value) end; end; function JclTypedIntToStr(Value: Integer; TypeInfo: PTypeInfo): string; var Conv: TIntToIdent; HaveConversion: Boolean; begin if TypeInfo <> nil then Conv := FindIntToIdent(TypeInfo) else Conv := nil; HaveConversion := (@Conv <> nil) and Conv(Value, Result); if not HaveConversion then begin if (TypeInfo <> nil) and (GetTypeData(TypeInfo).OrdType = otULong) then Result := IntToStr(Int64(Cardinal(Value))) else Result := IntToStr(Value) end; end; //=== Sets =================================================================== function JclSetToList(TypeInfo: PTypeInfo; const Value; const WantBrackets: Boolean; const WantRanges: Boolean; const Strings: TStrings): string; var SetType: IJclSetTypeInfo; I: Integer; begin I := Strings.Count; Result := ''; SetType := JclTypeInfo(TypeInfo) as IJclSetTypeInfo; SetType.GetAsList(Value, WantRanges, Strings); for I := I to Strings.Count - 1 do begin if Result <> '' then Result := Result + ', ' + Strings[I] else Result := Result + Strings[I]; end; if WantBrackets then Result := '[' + Result + ']'; end; function JclSetToStr(TypeInfo: PTypeInfo; const Value; const WantBrackets: Boolean; const WantRanges: Boolean): string; var Dummy: TStringList; begin Dummy := TStringList.Create; try Result := JclSetToList(TypeInfo, Value, WantBrackets, WantRanges, Dummy); finally Dummy.Free; end; end; procedure JclStrToSet(TypeInfo: PTypeInfo; var SetVar; const Value: string); var SetInfo: IJclSetTypeInfo; S: TStringList; begin SetInfo := JclTypeInfo(TypeInfo) as IJclSetTypeInfo; S := TStringList.Create; try StrToStrings(Value, ',', S); if S.Count > 0 then begin if S[0][1] = '[' then begin S[0] := Copy(S[0], 2, Length(S[0])); S[S.Count-1] := Copy(S[S.Count-1], 1, Length(S[S.Count-1]) - 1); end; end; SetInfo.SetAsList(SetVar, S); finally S.Free; end; end; procedure JclIntToSet(TypeInfo: PTypeInfo; var SetVar; const Value: Integer); var BitShift: Integer; TmpInt64: Int64; EnumMin: Integer; EnumMax: Integer; ResBytes: Integer; CompType: PTypeInfo; begin CompType := GetTypeData(TypeInfo).CompType{$IFNDEF CLR}^{$ENDIF}; EnumMin := GetTypeData(CompType).MinValue; EnumMax := GetTypeData(CompType).MaxValue; ResBytes := (EnumMax div 8) - (EnumMin div 8) + 1; BitShift := EnumMin mod 8; TmpInt64 := Longword(Value) shl BitShift; {$IFDEF CLR} SetVar := BitConverter.GetBytes(TmpInt64); {$ELSE} Move(TmpInt64, SetVar, ResBytes); {$ENDIF CLR} end; function JclSetToInt(TypeInfo: PTypeInfo; const SetVar): Integer; var BitShift: Integer; TmpInt64: Int64; EnumMin: Integer; EnumMax: Integer; ResBytes: Integer; CompType: PTypeInfo; begin CompType := GetTypeData(TypeInfo).CompType{$IFNDEF CLR}^{$ENDIF}; EnumMin := GetTypeData(CompType).MinValue; EnumMax := GetTypeData(CompType).MaxValue; ResBytes := (EnumMax div 8) - (EnumMin div 8) + 1; BitShift := EnumMin mod 8; if (EnumMax - EnumMin) > 32 then {$IFDEF CLR} raise EJclRTTIError.CreateFmt(RsRTTIValueOutOfRange, [IntToStr(EnumMax - EnumMin) + ' ' + RsRTTIBits]); TmpInt64 := BitConverter.ToInt64(TDynByteArray(SetVar), 0); TmpInt64 := TmpInt64 shr BitShift; Result := BitConverter.ToInt32(Copy(BitConverter.GetBytes(TmpInt64), 0, ResBytes), 0); {$ELSE} raise EJclRTTIError.CreateResFmt(@RsRTTIValueOutOfRange, [IntToStr(EnumMax - EnumMin) + ' ' + LoadResString(@RsRTTIBits)]); Result := 0; TmpInt64 := 0; Move(SetVar, TmpInt64, ResBytes + 1); TmpInt64 := TmpInt64 shr BitShift; Move(TmpInt64, Result, ResBytes); {$ENDIF CLR} end; {$IFNDEF CLR} function JclGenerateSetType(BaseType: PTypeInfo; const TypeName: ShortString): PTypeInfo; var TypeData: PTypeData; ValCount: Integer; begin Result := AllocMem(SizeOf(TTypeInfo) + SizeOf(TOrdType) + SizeOf(PPTypeInfo)); try with Result^ do begin Kind := tkSet; Name := TypeName; end; with GetTypeData(BaseType)^ do ValCount := MaxValue - MinValue + (MinValue mod 8); TypeData := GetTypeData(Result); case ValCount of 0..8: TypeData^.OrdType := otUByte; 9..16: TypeData^.OrdType := otUWord; 17..32: TypeData^.OrdType := otULong; 33..64: Byte(TypeData^.OrdType) := 8; 65..128: Byte(TypeData^.OrdType) := 16; 129..256: Byte(TypeData^.OrdType) := 32; else Byte(TypeData^.OrdType) := 255; end; TypeData^.CompType := AllocMem(SizeOf(Pointer)); TypeData^.CompType^ := BaseType; AddType(Result); except try ReallocMem(Result, 0); except Result := nil; end; raise; end; ReferenceType(BaseType); end; //=== Is/As hooking ========================================================== type PReadLoc = ^TReadLoc; TReadLoc = packed record {$IFDEF OPTIMIZATION_ON} Code: array [0..9] of Byte; {$ELSE} Code: array [0..17] of Byte; {$ENDIF OPTIMIZATION_ON} OpCode_Call: Byte; CallOffset: Longint; end; PJmp = ^TJmp; TJmp = packed record case OpCodeJmp: Byte of $E9: (JmpOffset: Longint); $FF: (OpCode2: Byte; EntryOffset: Longint); end; {$ENDIF ~CLR} // Copied from System.pas (_IsClass function) function JclIsClass(const AnObj: TObject; const AClass: TClass): Boolean; {$IFDEF CLR} begin Result := (AnObj <> nil) and (AClass.ClassInfo.IsInstanceOfType(AnObj)); end; {$ELSE} asm { -> EAX left operand (class) } { EDX VMT of right operand } { <- AL left is derived from right } TEST EAX,EAX JE @@exit @@loop: MOV EAX,[EAX] CMP EAX,EDX JE @@success MOV EAX,[EAX].vmtParent TEST EAX,EAX JNE @@loop JMP @@exit @@success: MOV AL,1 @@exit: end; {$ENDIF ~CLR} function JclIsClassByName(const AnObj: TObject; const AClass: TClass): Boolean; var CurClass: TClass; CurClass2: TClass; begin Result := AnObj <> nil; if Result then begin CurClass := AnObj.ClassType; Result := False; while not Result and (CurClass <> nil) do begin Result := CurClass.ClassNameIs(AClass.ClassName); if not Result then CurClass := CurClass.ClassParent; end; if CurClass <> nil then CurClass := CurClass.ClassParent; CurClass2 := AClass.ClassParent; while Result and (CurClass <> nil) and (CurClass2 <> nil) do begin Result := CurClass.ClassNameIs(CurClass2.ClassName); if Result then begin CurClass := CurClass.ClassParent; CurClass2 := CurClass2.ClassParent; end; end; Result := Result and (CurClass = CurClass2); end; end; function JclAsClass(const AnObj: TObject; const AClass: TClass): TObject; begin if (AnObj = nil) or (AnObj is AClass) then Result := AnObj else {$IFDEF CLR} raise EInvalidCast.Create(SInvalidCast); {$ELSE} raise EInvalidCast.CreateRes(@SInvalidCast); {$ENDIF CLR} end; {$IFNDEF CLR} initialization TypeList := TThreadList.Create; finalization ClearInfoList; FreeAndNil(TypeList); {$ENDIF ~CLR} // History: // $Log: JclRTTI.pas,v $ // Revision 1.24 2005/05/05 20:08:44 ahuser // JCL.NET support // // Revision 1.23 2005/03/14 08:46:53 rrossmair // - check-in in preparation for release 1.95 // // Revision 1.22 2005/03/08 16:10:08 marquardt // standard char sets extended and used, some optimizations for string literals // // Revision 1.21 2005/03/08 08:33:17 marquardt // overhaul of exceptions and resourcestrings, minor style cleaning // // Revision 1.20 2005/03/06 18:15:02 marquardt // JclGUIDToString and JclStringToGUID moved to JclSysUtils.pas, CrLf replaced by AnsiLineBreak // // Revision 1.19 2005/03/01 00:10:26 ahuser // Delphi 2005 inline support // // Revision 1.18 2005/02/24 16:34:40 marquardt // remove divider lines, add section lines (unfinished) // // Revision 1.17 2004/11/15 05:25:28 mthoma // Fixed #1055. // // Revision 1.16 2004/10/17 20:25:21 mthoma // style cleaning, adjusting contributors // // Revision 1.15 2004/09/30 07:50:29 marquardt // remove JclIsClass pure pascal contributions // // Revision 1.14 2004/08/03 07:22:37 marquardt // resourcestring cleanup // // Revision 1.13 2004/08/01 05:52:11 marquardt // move constructors/destructors // // Revision 1.12 2004/07/31 06:21:01 marquardt // fixing TStringLists, adding BeginUpdate/EndUpdate, finalization improved // // Revision 1.11 2004/07/28 18:00:51 marquardt // various style cleanings, some minor fixes // // Revision 1.10 2004/06/14 11:05:51 marquardt // symbols added to all ENDIFs and some other minor style changes like removing IFOPT // // Revision 1.9 2004/06/11 14:08:51 twm // Bugfix: now uses AnsiLineBreak rather than AnsiCrLf so it will work with unix systems // // Revision 1.8 2004/05/05 00:09:59 mthoma // Updated headers: Added donors as contributors, adjusted the initial authors, added cvs names when they were not obvious. Changed $data to $date where necessary, // // Revision 1.7 2004/04/23 22:08:39 mthoma // Removed non delphi language version of JclIsClass. // // Revision 1.6 2004/04/15 16:19:36 // add pure pascal implementation (JclIsClass) // // Revision 1.5 2004/04/06 04:53:18 // adapt compiler conditions, add log entry // end.