2
0

tests.rtti.util.pas 9.2 KB

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