tests.rtti.util.pas 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. unit Tests.Rtti.Util;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses
  5. TypInfo, Rtti;
  6. {$ifndef fpc}
  7. type
  8. CodePointer = Pointer;
  9. TValueHelper = record helper for TValue
  10. function AsUnicodeString: UnicodeString;
  11. function AsAnsiString: AnsiString;
  12. function AsChar: Char; inline;
  13. function AsAnsiChar: AnsiChar;
  14. function AsWideChar: WideChar;
  15. end;
  16. {$endif}
  17. const
  18. {$if defined(cpui386) or defined(cpux86_64) or defined(cpum68k)}
  19. DefaultCC = ccReg;
  20. {$else}
  21. DefaultCC = ccStdCall;
  22. {$endif}
  23. function CopyValue({$ifdef fpc}constref{$else}const [ref]{$endif} aValue: TValue): TValue;
  24. function EqualValues({$ifdef fpc}constref{$else}const [ref]{$endif} aValue1, aValue2: TValue): Boolean;
  25. function TypeKindToStr(aTypeKind: TTypeKind): String; inline;
  26. function GetInstValue(aValue: TObject): TValue;
  27. function GetPointerValue(aValue: Pointer): TValue;
  28. function GetIntValue(aValue: SizeInt): TValue;
  29. function GetAnsiString(const aValue: AnsiString): TValue;
  30. function GetShortString(const aValue: ShortString): TValue;
  31. function GetSingleValue(aValue: Single): TValue;
  32. function GetDoubleValue(aValue: Double): TValue;
  33. function GetExtendedValue(aValue: Extended): TValue;
  34. function GetCompValue(aValue: Comp): TValue;
  35. function GetCurrencyValue(aValue: Currency): TValue;
  36. function GetArray(const aArg: array of SizeInt): TValue;
  37. implementation
  38. uses
  39. SysUtils, Math;
  40. {$ifndef fpc}
  41. function TValueHelper.AsUnicodeString: UnicodeString;
  42. begin
  43. Result := UnicodeString(AsString);
  44. end;
  45. function TValueHelper.AsAnsiString: AnsiString;
  46. begin
  47. Result := AnsiString(AsString);
  48. end;
  49. function TValue.AsWideChar: WideChar;
  50. begin
  51. if Kind <> tkWideChar then
  52. raise EInvalidCast.Create('Invalid cast');
  53. Result := WideChar(Word(AsOrdinal));
  54. end;
  55. function TValue.AsAnsiChar: AnsiChar;
  56. begin
  57. if Kind <> tkChar then
  58. raise EInvalidCast.Create('Invalid cast');
  59. Result := AnsiChar(Byte(AsOrdinal));
  60. end;
  61. function TValue.AsChar: Char;
  62. begin
  63. Result := AsWideChar;
  64. end;
  65. {$endif}
  66. function CopyValue({$ifdef fpc}constref{$else}const [ref]{$endif} aValue: TValue): TValue;
  67. var
  68. arrptr: Pointer;
  69. len, i: SizeInt;
  70. begin
  71. if aValue.Kind = tkDynArray then begin
  72. { we need to decouple the source reference, so we're going to be a bit
  73. cheeky here }
  74. len := aValue.GetArrayLength;
  75. arrptr := Nil;
  76. DynArraySetLength(arrptr, aValue.TypeInfo, 1, @len);
  77. TValue.Make(@arrptr, aValue.TypeInfo, Result);
  78. for i := 0 to len - 1 do
  79. Result.SetArrayElement(i, aValue.GetArrayElement(i));
  80. end else
  81. TValue.Make(aValue.GetReferenceToRawData, aValue.TypeInfo, Result);
  82. end;
  83. function EqualValues({$ifdef fpc}constref{$else}const [ref]{$endif} aValue1, aValue2: TValue): Boolean;
  84. var
  85. td1, td2: PTypeData;
  86. i: SizeInt;
  87. begin
  88. {$ifdef debug}
  89. Writeln('Empty: ', aValue1.IsEmpty, ' ', aValue2.IsEmpty);
  90. Writeln('Kind: ', aValue1.Kind, ' ', aValue2.Kind);
  91. Writeln('Array: ', aValue1.IsArray, ' ', aValue2.IsArray);
  92. {$endif}
  93. if aValue1.IsEmpty and aValue2.IsEmpty then
  94. Result := True
  95. else if aValue1.IsEmpty and not aValue2.IsEmpty then
  96. Result := False
  97. else if not aValue1.IsEmpty and aValue2.IsEmpty then
  98. Result := False
  99. else if aValue1.IsArray and aValue2.IsArray then begin
  100. if aValue1.GetArrayLength = aValue2.GetArrayLength then begin
  101. Result := True;
  102. for i := 0 to aValue1.GetArrayLength - 1 do
  103. if not EqualValues(aValue1.GetArrayElement(i), aValue2.GetArrayElement(i)) then begin
  104. {$ifdef debug}
  105. Writeln('Element ', i, ' differs: ', HexStr(aValue1.GetArrayElement(i).AsOrdinal, 4), ' ', HexStr(aValue2.GetArrayElement(i).AsOrdinal, 4));
  106. {$endif}
  107. Result := False;
  108. Break;
  109. end;
  110. end else
  111. Result := False;
  112. end else if aValue1.Kind = aValue2.Kind then begin
  113. td1 := aValue1.TypeData;
  114. td2 := aValue2.TypeData;
  115. case aValue1.Kind of
  116. tkBool:
  117. Result := aValue1.AsBoolean xor not aValue2.AsBoolean;
  118. tkSet:
  119. if td1^.SetSize = td2^.SetSize then
  120. if td1^.SetSize < SizeOf(SizeInt) then
  121. Result := aValue1.AsOrdinal = aValue2.AsOrdinal
  122. else
  123. Result := CompareMem(aValue1.GetReferenceToRawData, aValue2.GetReferenceToRawData, td1^.SetSize)
  124. else
  125. Result := False;
  126. tkEnumeration,
  127. tkChar,
  128. tkWChar,
  129. tkUChar,
  130. tkInt64,
  131. tkInteger:
  132. Result := aValue1.AsOrdinal = aValue2.AsOrdinal;
  133. tkQWord:
  134. Result := aValue1.AsUInt64 = aValue2.AsUInt64;
  135. tkFloat:
  136. if td1^.FloatType <> td2^.FloatType then
  137. Result := False
  138. else begin
  139. case td1^.FloatType of
  140. ftSingle:
  141. Result := SameValue(Single(aValue1.AsExtended), Single(aValue2.AsExtended));
  142. ftDouble:
  143. Result := SameValue(Double(aValue1.AsExtended), Double(aValue2.AsExtended));
  144. ftExtended:
  145. Result := SameValue(aValue1.AsExtended, aValue2.AsExtended);
  146. ftComp:
  147. Result := aValue1.AsInt64 = aValue2.AsInt64;
  148. ftCurr:
  149. Result := aValue1.AsCurrency = aValue2.AsCurrency;
  150. end;
  151. end;
  152. tkSString,
  153. tkUString,
  154. tkAString,
  155. tkWString:
  156. Result := aValue1.AsString = aValue2.AsString;
  157. tkDynArray,
  158. tkArray:
  159. if aValue1.GetArrayLength = aValue2.GetArrayLength then begin
  160. Result := True;
  161. for i := 0 to aValue1.GetArrayLength - 1 do
  162. if not EqualValues(aValue1.GetArrayElement(i), aValue2.GetArrayElement(i)) then begin
  163. Result := False;
  164. Break;
  165. end;
  166. end else
  167. Result := False;
  168. tkClass,
  169. tkClassRef,
  170. tkInterface,
  171. tkInterfaceRaw,
  172. tkPointer:
  173. Result := PPointer(aValue1.GetReferenceToRawData)^ = PPointer(aValue2.GetReferenceToRawData)^;
  174. tkProcVar:
  175. Result := PCodePointer(aValue1.GetReferenceToRawData)^ = PCodePointer(aValue2.GetReferenceToRawData)^;
  176. tkRecord,
  177. tkObject,
  178. tkMethod,
  179. tkVariant: begin
  180. if aValue1.DataSize = aValue2.DataSize then
  181. Result := CompareMem(aValue1.GetReferenceToRawData, aValue2.GetReferenceToRawData, aValue1.DataSize)
  182. else
  183. Result := False;
  184. end
  185. else
  186. Result := False;
  187. end;
  188. end else
  189. Result := False;
  190. end;
  191. function TypeKindToStr(aTypeKind: TTypeKind): String;
  192. begin
  193. {$ifdef fpc}
  194. Str(aTypeKind, Result);
  195. {$else}
  196. Result := GetEnumName(TypeInfo(TTypeKind), Ord(aTypeKind));
  197. {$endif}
  198. end;
  199. function GetInstValue(aValue: TObject): TValue;
  200. begin
  201. Result := TValue.{$ifdef fpc}specialize{$endif}From<TObject>(aValue);
  202. end;
  203. function GetPointerValue(aValue: Pointer): TValue;
  204. begin
  205. Result := TValue.{$ifdef fpc}specialize{$endif}From<Pointer>(aValue);
  206. end;
  207. function GetIntValue(aValue: SizeInt): TValue;
  208. begin
  209. Result := TValue.{$ifdef fpc}specialize{$endif}From<SizeInt>(aValue);
  210. end;
  211. function GetAnsiString(const aValue: AnsiString): TValue;
  212. begin
  213. Result := TValue.{$ifdef fpc}specialize{$endif}From<AnsiString>(aValue);
  214. end;
  215. function GetShortString(const aValue: ShortString): TValue;
  216. begin
  217. Result := TValue.{$ifdef fpc}specialize{$endif}From<ShortString>(aValue);
  218. end;
  219. function GetSingleValue(aValue: Single): TValue;
  220. begin
  221. Result := TValue.{$ifdef fpc}specialize{$endif}From<Single>(aValue);
  222. end;
  223. function GetDoubleValue(aValue: Double): TValue;
  224. begin
  225. Result := TValue.{$ifdef fpc}specialize{$endif}From<Double>(aValue);
  226. end;
  227. function GetExtendedValue(aValue: Extended): TValue;
  228. begin
  229. Result := TValue.{$ifdef fpc}specialize{$endif}From<Extended>(aValue);
  230. end;
  231. function GetCompValue(aValue: Comp): TValue;
  232. begin
  233. Result := TValue.{$ifdef fpc}specialize{$endif}From<Comp>(aValue);
  234. end;
  235. function GetCurrencyValue(aValue: Currency): TValue;
  236. begin
  237. Result := TValue.{$ifdef fpc}specialize{$endif}From<Currency>(aValue);
  238. end;
  239. {$ifdef fpc}
  240. function GetArray(const aArg: array of SizeInt): TValue;
  241. begin
  242. Result := specialize OpenArrayToDynArrayValue<SizeInt>(aArg);
  243. end;
  244. {$endif}
  245. end.