unit Tests.Rtti.Util; {$mode objfpc}{$H+} interface uses Rtti; {$ifndef fpc} type CodePointer = Pointer; TValueHelper = record helper for TValue function AsUnicodeString: UnicodeString; function AsAnsiString: AnsiString; end; {$endif} function CopyValue({$ifdef fpc}constref{$else}const [ref]{$endif} aValue: TValue): TValue; function EqualValues({$ifdef fpc}constref{$else}const [ref]{$endif} aValue1, aValue2: TValue): Boolean; function TypeKindToStr(aTypeKind: TTypeKind): String; inline; function GetInstValue(aValue: TObject): TValue; function GetPointerValue(aValue: Pointer): TValue; function GetIntValue(aValue: SizeInt): TValue; function GetAnsiString(const aValue: AnsiString): TValue; function GetShortString(const aValue: ShortString): TValue; function GetSingleValue(aValue: Single): TValue; function GetDoubleValue(aValue: Double): TValue; function GetExtendedValue(aValue: Extended): TValue; function GetCompValue(aValue: Comp): TValue; function GetCurrencyValue(aValue: Currency): TValue; function GetArray(const aArg: array of SizeInt): TValue; implementation uses TypInfo, SysUtils, Math; {$ifndef fpc} function TValueHelper.AsUnicodeString: UnicodeString; begin Result := UnicodeString(AsString); end; function TValueHelper.AsAnsiString: AnsiString; begin Result := AnsiString(AsString); end; {$endif} function CopyValue({$ifdef fpc}constref{$else}const [ref]{$endif} aValue: TValue): TValue; var arrptr: Pointer; len, i: SizeInt; begin if aValue.Kind = tkDynArray then begin { we need to decouple the source reference, so we're going to be a bit cheeky here } len := aValue.GetArrayLength; arrptr := Nil; DynArraySetLength(arrptr, aValue.TypeInfo, 1, @len); TValue.Make(@arrptr, aValue.TypeInfo, Result); for i := 0 to len - 1 do Result.SetArrayElement(i, aValue.GetArrayElement(i)); end else TValue.Make(aValue.GetReferenceToRawData, aValue.TypeInfo, Result); end; function EqualValues({$ifdef fpc}constref{$else}const [ref]{$endif} aValue1, aValue2: TValue): Boolean; var td1, td2: PTypeData; i: SizeInt; begin {$ifdef debug} Writeln('Empty: ', aValue1.IsEmpty, ' ', aValue2.IsEmpty); Writeln('Kind: ', aValue1.Kind, ' ', aValue2.Kind); Writeln('Array: ', aValue1.IsArray, ' ', aValue2.IsArray); {$endif} if aValue1.IsEmpty and aValue2.IsEmpty then Result := True else if aValue1.IsEmpty and not aValue2.IsEmpty then Result := False else if not aValue1.IsEmpty and aValue2.IsEmpty then Result := False else if aValue1.IsArray and aValue2.IsArray then begin if aValue1.GetArrayLength = aValue2.GetArrayLength then begin Result := True; for i := 0 to aValue1.GetArrayLength - 1 do if not EqualValues(aValue1.GetArrayElement(i), aValue2.GetArrayElement(i)) then begin {$ifdef debug} Writeln('Element ', i, ' differs: ', HexStr(aValue1.GetArrayElement(i).AsOrdinal, 4), ' ', HexStr(aValue2.GetArrayElement(i).AsOrdinal, 4)); {$endif} Result := False; Break; end; end else Result := False; end else if aValue1.Kind = aValue2.Kind then begin td1 := aValue1.TypeData; td2 := aValue2.TypeData; case aValue1.Kind of tkBool: Result := aValue1.AsBoolean xor not aValue2.AsBoolean; tkSet: if td1^.SetSize = td2^.SetSize then if td1^.SetSize < SizeOf(SizeInt) then Result := aValue1.AsOrdinal = aValue2.AsOrdinal else Result := CompareMem(aValue1.GetReferenceToRawData, aValue2.GetReferenceToRawData, td1^.SetSize) else Result := False; tkEnumeration, tkChar, tkWChar, tkUChar, tkInt64, tkInteger: Result := aValue1.AsOrdinal = aValue2.AsOrdinal; tkQWord: Result := aValue1.AsUInt64 = aValue2.AsUInt64; tkFloat: if td1^.FloatType <> td2^.FloatType then Result := False else begin case td1^.FloatType of ftSingle: Result := SameValue(Single(aValue1.AsExtended), Single(aValue2.AsExtended)); ftDouble: Result := SameValue(Double(aValue1.AsExtended), Double(aValue2.AsExtended)); ftExtended: Result := SameValue(aValue1.AsExtended, aValue2.AsExtended); ftComp: Result := aValue1.AsInt64 = aValue2.AsInt64; ftCurr: Result := aValue1.AsCurrency = aValue2.AsCurrency; end; end; tkSString, tkUString, tkAString, tkWString: Result := aValue1.AsString = aValue2.AsString; tkDynArray, tkArray: if aValue1.GetArrayLength = aValue2.GetArrayLength then begin Result := True; for i := 0 to aValue1.GetArrayLength - 1 do if not EqualValues(aValue1.GetArrayElement(i), aValue2.GetArrayElement(i)) then begin Result := False; Break; end; end else Result := False; tkClass, tkClassRef, tkInterface, tkInterfaceRaw, tkPointer: Result := PPointer(aValue1.GetReferenceToRawData)^ = PPointer(aValue2.GetReferenceToRawData)^; tkProcVar: Result := PCodePointer(aValue1.GetReferenceToRawData)^ = PCodePointer(aValue2.GetReferenceToRawData)^; tkRecord, tkObject, tkMethod, tkVariant: begin if aValue1.DataSize = aValue2.DataSize then Result := CompareMem(aValue1.GetReferenceToRawData, aValue2.GetReferenceToRawData, aValue1.DataSize) else Result := False; end else Result := False; end; end else Result := False; end; function TypeKindToStr(aTypeKind: TTypeKind): String; begin {$ifdef fpc} Str(aTypeKind, Result); {$else} Result := GetEnumName(TypeInfo(TTypeKind), Ord(aTypeKind)); {$endif} end; function GetInstValue(aValue: TObject): TValue; begin Result := TValue.{$ifdef fpc}specialize{$endif}From(aValue); end; function GetPointerValue(aValue: Pointer): TValue; begin Result := TValue.{$ifdef fpc}specialize{$endif}From(aValue); end; function GetIntValue(aValue: SizeInt): TValue; begin Result := TValue.{$ifdef fpc}specialize{$endif}From(aValue); end; function GetAnsiString(const aValue: AnsiString): TValue; begin Result := TValue.{$ifdef fpc}specialize{$endif}From(aValue); end; function GetShortString(const aValue: ShortString): TValue; begin Result := TValue.{$ifdef fpc}specialize{$endif}From(aValue); end; function GetSingleValue(aValue: Single): TValue; begin Result := TValue.{$ifdef fpc}specialize{$endif}From(aValue); end; function GetDoubleValue(aValue: Double): TValue; begin Result := TValue.{$ifdef fpc}specialize{$endif}From(aValue); end; function GetExtendedValue(aValue: Extended): TValue; begin Result := TValue.{$ifdef fpc}specialize{$endif}From(aValue); end; function GetCompValue(aValue: Comp): TValue; begin Result := TValue.{$ifdef fpc}specialize{$endif}From(aValue); end; function GetCurrencyValue(aValue: Currency): TValue; begin Result := TValue.{$ifdef fpc}specialize{$endif}From(aValue); end; {$ifdef fpc} function GetArray(const aArg: array of SizeInt): TValue; begin Result := specialize OpenArrayToDynArrayValue(aArg); end; {$endif} end.