tests.rtti.util.pas 8.8 KB

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