/
mormot.core.rtti.pas
9107 lines (8450 loc) · 299 KB
/
mormot.core.rtti.pas
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/// Framework Core Low-Level Cross-Compiler RTTI Definitions
// - this unit is a part of the Open Source Synopse mORMot framework 2,
// licensed under a MPL/GPL/LGPL three license - see LICENSE.md
unit mormot.core.rtti;
{
*****************************************************************************
Cross-Compiler RTTI Definitions shared by all framework units
- Low-Level Cross-Compiler RTTI Definitions
- Enumerations RTTI
- Published Class Properties and Methods RTTI
- IInvokable Interface RTTI
- Efficient Dynamic Arrays and Records Process
- Managed Types Finalization, Random or Copy
- RTTI Value Types used for JSON Parsing
- RTTI-based Registration for Custom JSON Parsing
- High Level TObjectWithID and TObjectWithCustomCreate Class Types
- Redirect Most Used FPC RTL Functions to Optimized x86_64 Assembly
Purpose of this unit is to avoid any direct use of TypInfo.pas RTL unit,
which is not exactly compatible between compilers, and lack of direct
RTTI access with no memory allocation. We define pointers to RTTI
record/object to access TypeInfo() via a set of explicit methods.
Here fake record/objects are just wrappers around pointers defined in
Delphi/FPC RTL's TypInfo.pas with the magic of inlining.
We redefined all RTTI definitions as TRtti* types to avoid confusion
with type names as published by the TypInfo unit.
TRttiCustom class is the main cached entry of our customizable RTTI,
accessible from the global Rtti.* methods.
See mormot.core.rtti.fpc.inc and mormot.core.rtti.delphi.inc for
compiler-specific code.
*****************************************************************************
}
interface
{$I ..\mormot.defines.inc}
uses
sysutils,
classes,
contnrs,
typinfo, // use official RTL for accurate layouts (especially FPC unaligned)
mormot.core.base,
mormot.core.os,
mormot.core.unicode,
mormot.core.text; // ESynException, and text process (e.g. for enums)
{ ************* Low-Level Cross-Compiler RTTI Definitions }
type
/// the kind of Exception raised by this unit
ERttiException = class(ESynException);
/// map TOrdType, to specify ordinal (rkInteger and rkEnumeration) storage size and sign
// - note: on FPC, Int64 is stored as its own TRttiKind, not as rkInteger
TRttiOrd = (
roSByte,
roUByte,
roSWord,
roUWord,
roSLong,
roULong
{$ifdef FPC_NEWRTTI} ,
roSQWord,
roUQWord
{$endif FPC_NEWRTTI});
/// map TFloatType, to specify floating point (ftFloat) storage size and precision
TRttiFloat = (
rfSingle,
rfDouble,
rfExtended,
rfComp,
rfCurr);
{$ifdef FPC}
/// map TTypeKind, to specify available type families for FPC RTTI values
// - FPC types differs from Delphi, and are taken from FPC typinfo.pp unit
// - here below, we defined rkLString instead of rkAString to match Delphi -
// see https://lists.freepascal.org/pipermail/fpc-devel/2013-June/032360.html
// "Compiler uses internally some LongStrings which is not possible to use
// for variable declarations" so rkLStringOld seems never used in practice
TRttiKind = (
rkUnknown,
rkInteger,
rkChar,
rkEnumeration,
rkFloat,
rkSet,
rkMethod,
rkSString,
rkLStringOld {=rkLString},
rkLString {=rkAString},
rkWString,
rkVariant,
rkArray,
rkRecord,
rkInterface,
rkClass,
rkObject,
rkWChar,
rkBool,
rkInt64,
rkQWord,
rkDynArray,
rkInterfaceRaw,
rkProcVar,
rkUString,
rkUChar,
rkHelper,
rkFile,
rkClassRef,
rkPointer);
const
/// potentially managed types in TRttiKind enumerates
rkManagedTypes = [rkLStringOld,
rkLString,
rkWstring,
rkUstring,
rkArray,
rkObject,
rkRecord,
rkDynArray,
rkInterface,
rkVariant];
/// maps record or object in TRttiKind enumerates
rkRecordTypes = [rkObject,
rkRecord];
type
/// TTypeKind enumerate as defined in Delphi 6 and up
// - dkUString and following appear only since Delphi 2009
TDelphiType = (
dkUnknown,
dkInteger,
dkChar,
dkEnumeration,
dkFloat,
dkString,
dkSet,
dkClass,
dkMethod,
dkWChar,
dkLString,
dkWString,
dkVariant,
dkArray,
dkRecord,
dkInterface,
dkInt64,
dkDynArray,
dkUString,
dkClassRef,
dkPointer,
dkProcedure);
const
/// convert our TRttiKind to Delphi's TTypeKind enumerate
// - used internally for cross-compiler TDynArray binary serialization
FPCTODELPHI: array[TRttiKind] of TDelphiType = (
dkUnknown,
dkInteger,
dkChar,
dkEnumeration,
dkFloat,
dkSet,
dkMethod,
dkString,
dkLString,
dkLString,
dkWString,
dkVariant,
dkArray,
dkRecord,
dkInterface,
dkClass,
dkRecord,
dkWChar,
dkEnumeration,
dkInt64,
dkInt64,
dkDynArray,
dkInterface,
dkProcedure,
dkUString,
dkWChar,
dkPointer,
dkPointer,
dkClassRef,
dkPointer);
/// convert Delphi's TTypeKind to our TRttiKind enumerate
DELPHITOFPC: array[TDelphiType] of TRttiKind = (
rkUnknown,
rkInteger,
rkChar,
rkEnumeration,
rkFloat,
rkSString,
rkSet,
rkClass,
rkMethod,
rkWChar,
rkLString,
rkWString,
rkVariant,
rkArray,
rkRecord,
rkInterface,
rkInt64,
rkDynArray,
rkUString,
rkClassRef,
rkPointer,
rkProcVar);
{$else}
/// available type families for Delphi 6 and up, similar to typinfo.pas
// - redefined here to leverage FPC and Delphi compatibility as much as possible
TRttiKind = (
rkUnknown,
rkInteger,
rkChar,
rkEnumeration,
rkFloat,
rkSString,
rkSet,
rkClass,
rkMethod,
rkWChar,
rkLString,
rkWString,
rkVariant,
rkArray,
rkRecord,
rkInterface,
rkInt64,
rkDynArray
{$ifdef UNICODE},
rkUString,
rkClassRef,
rkPointer,
rkProcedure
{$endif UNICODE});
const
/// potentially managed types in TRttiKind enumerates
rkManagedTypes = [rkLString,
rkWstring,
{$ifdef UNICODE}
rkUstring,
{$endif UNICODE}
rkArray,
rkRecord,
rkDynArray,
rkInterface,
rkVariant
];
/// maps record or object in TTypeKind RTTI enumerates
rkRecordTypes = [rkRecord];
{$endif FPC}
/// maps long string in TRttiKind RTTI enumerates
rkStringTypes =
[rkLString,
{$ifdef FPC}
rkLStringOld,
{$endif FPC}
{$ifdef HASVARUSTRING}
rkUString,
{$endif HASVARUSTRING}
rkWString
];
/// maps types with proper TRttiProp.RttiOrd field
// - i.e. rkOrdinalTypes excluding the 64-bit values
rkHasRttiOrdTypes =
[rkInteger,
rkChar,
rkWChar,
{$ifdef FPC}
rkBool,
rkUChar,
{$endif FPC}
rkEnumeration,
rkSet
];
/// maps 1, 8, 16, 32 and 64-bit ordinal in TRttiKind RTTI enumerates
rkOrdinalTypes =
rkHasRttiOrdTypes + [ {$ifdef FPC} rkQWord, {$endif} rkInt64 ];
/// maps integer and floating point types in TRttiKind RTTI enumerates
rkNumberTypes = rkOrdinalTypes + [ rkFloat ];
/// maps values which expect TRttiProp.GetOrdProp/SetOrdProp
// - includes 32-bit ordinals and pointers
rkGetOrdPropTypes =
rkHasRttiOrdTypes + [ rkClass, rkDynArray, rkInterface ];
/// maps ordinal values which expect TRttiProp.GetInt64Prop/SetInt64Prop
// - includes 64-bit ordinals
rkGetInt64PropTypes =
[rkInt64 {$ifdef FPC} , rkQWord {$endif} ];
/// maps records or dynamic arrays
rkRecordOrDynArrayTypes = rkRecordTypes + [rkDynArray];
/// maps records or static arrays
rkRecordOrArrayTypes = rkRecordTypes + [rkArray];
/// all recognized TRttiKind enumerates, i.e. all but rkUnknown
rkAllTypes = [succ(low(TRttiKind))..high(TRttiKind)];
/// quick retrieve how many bytes an ordinal consist in
ORDTYPE_SIZE: array[TRttiOrd] of byte = (
1, // roSByte
1, // roUByte
2, // roSWord
2, // roUWord
4, // roSLong
4 // roULong
{$ifdef FPC_NEWRTTI} , 8, 8 {$endif} ); // roSQWord, roUQWord
/// quick retrieve how many bytes a floating-point consist in
FLOATTYPE_SIZE: array[TRttiFloat] of byte = (
4, // rfSingle
8, // rfDouble
{$ifdef TSYNEXTENDED80} 10 {$else} 8 {$endif}, // rfExtended
8, // rfComp
8 ); // rfCurr
type
PRttiKind = ^TRttiKind;
TRttiKinds = set of TRttiKind;
PRttiOrd = ^TRttiOrd;
PRttiFloat = ^TRttiFloat;
type
/// pointer to low-level RTTI of a type definition, as returned by TypeInfo()
// system function
// - equivalency to PTypeInfo as defined in TypInfo RTL unit and old mORMot.pas
// - this is the main entry point of all the information exposed by this unit
PRttiInfo = ^TRttiInfo;
/// double-reference to RTTI type definition
// - Delphi and newer FPC do store all nested TTypeInfo as pointer to pointer,
// to ease linking of the executable
PPRttiInfo = ^PRttiInfo;
/// dynamic array of low-level RTTI type definitions
PRttiInfoDynArray = array of PRttiInfo;
/// pointer to a RTTI class property definition as stored in PRttiProps.PropList
// - equivalency to PPropInfo as defined in TypInfo RTL unit and old mORMot.pas
PRttiProp = ^TRttiProp;
/// used to store a chain of properties RTTI
// - could be used e.g. by TOrmPropInfo to handled flattened properties
PRttiPropDynArray = array of PRttiProp;
/// pointer to all RTTI class properties definitions
// - as returned by PRttiInfo.RttiProps() or GetRttiProps()
PRttiProps = ^TRttiProps;
/// a wrapper to published properties of a class, as defined by compiler RTTI
// - access properties for only a given class level, not inherited properties
// - start enumeration by getting a PRttiProps with PRttiInfo.RttiProps(), then
// use P := PropList to get the first PRttiProp, and iterate with P^.Next
// - this enumeration is very fast and doesn't require any temporary memory,
// as in the TypInfo.GetPropInfos() PPropList usage
// - for TOrm, you should better use the Properties.Fields[] array,
// which is faster and contains the properties published in parent classes
TRttiProps = object
public
/// number of published properties in this object
function PropCount: integer;
{$ifdef HASINLINE}inline;{$endif}
/// point to a TPropInfo packed array
// - layout is as such, with variable TPropInfo storage size:
// ! PropList: array[1..PropCount] of TPropInfo
// - use TPropInfo.Next to get the next one:
// ! P := PropList;
// ! for i := 1 to PropCount do
// ! begin
// ! // ... do something with P
// ! P := P^.Next;
// ! end;
function PropList: PRttiProp;
{$ifdef HASINLINE}inline;{$endif}
/// retrieve a Field property RTTI information from a Property Name
function FieldProp(const PropName: ShortString): PRttiProp;
end;
/// pointer to TClassType, as returned by PRttiInfo.RttiClass()
// - as returned by PRttiInfo.RttiClass() or GetRttiClass()
// - equivalency to PClassData/PClassType as defined in old mORMot.pas
PRttiClass = ^TRttiClass;
/// a wrapper to class type information, as defined by the compiler RTTI
// - get a PRttiClass with PRttiInfo.RttiClass() or GetRttiClass()
TRttiClass = object
public
/// the class type
function RttiClass: TClass;
{$ifdef HASINLINE}inline;{$endif}
/// the parent class type information
function ParentInfo: PRttiInfo;
{$ifdef HASINLINE}inline;{$endif}
/// the number of published properties of this class and all parents
// - use RttiProps if you want to properties only published in this class
function PropCount: integer;
{$ifdef HASINLINE}inline;{$endif}
/// the name (without .pas extension) of the unit were the class was defined
// - then the PRttiProps information follows: use the method
// RttiProps to retrieve its address
function UnitName: PShortString;
{$ifdef HASINLINE}inline;{$endif}
/// get the information about the published properties of this class
// - stored after UnitName memory
function RttiProps: PRttiProps;
{$ifdef HASINLINE}inline;{$endif}
/// fast and easy find if this class inherits from a specific class type
// - you should rather consider using TRttiInfo.InheritsFrom directly
function InheritsFrom(AClass: TClass): boolean;
end;
/// pointer to TEnumType, as returned by PRttiInfo.EnumBaseType/SetEnumType
// - equivalency to PEnumType as defined in old mORMot.pas
PRttiEnumType = ^TRttiEnumType;
/// a wrapper to enumeration type information, as defined by the compiler RTTI
// and returned by PRttiInfo.EnumBaseType/SetEnumType
// - we use this to store the enumeration values as integer, but easily provide
// a text equivalent, translated if necessary, from the enumeration type
// definition itself
TRttiEnumType = object
private
// as used by TRttiInfo.EnumBaseType/SetBaseType
function EnumBaseType: PRttiEnumType;
{$ifdef HASINLINE}inline;{$endif}
function SetBaseType: PRttiEnumType;
{$ifdef HASINLINE}inline;{$endif}
public
/// specify ordinal storage size and sign
// - is prefered to MaxValue to identify the number of stored bytes
function RttiOrd: TRttiOrd;
{$ifdef HASINLINE}inline;{$endif}
/// first value of enumeration type, typicaly 0
// - may be < 0 e.g. for boolean
function MinValue: PtrInt;
{$ifdef HASINLINE}inline;{$endif}
/// same as ord(high(type)): not the enumeration count, but the highest index
function MaxValue: PtrInt;
{$ifdef HASINLINE}inline;{$endif}
/// a concatenation of shortstrings, containing the enumeration names
// - those shortstrings are not aligned whatsoever (even if
// FPC_REQUIRES_PROPER_ALIGNMENT is set)
function NameList: PShortString;
{$ifdef HASINLINE}inline;{$endif}
/// get the corresponding enumeration name
// - return a void '' ShortString if Value is invalid (>MaxValue)
function GetEnumNameOrd(Value: cardinal): PShortString;
{$ifdef FPC} inline; {$endif}
/// get the corresponding enumeration name
// - return the first one if Value is invalid (>MaxValue)
// - Value will be converted to the matching ordinal value (byte or word)
function GetEnumName(const Value): PShortString;
{$ifdef HASINLINE}inline;{$endif}
/// get the caption text corresponding to a enumeration name
// - return the first one if Value is invalid (>MaxValue)
// - Value will be converted to the matching ordinal value (byte or word)
function GetCaption(const Value): string;
/// get all caption names, ready to be display, as lines separated by #13#10
// - return "string" type, i.e. UnicodeString for Delphi 2009+
// - if UsedValuesBits is not nil, only the corresponding bits set are added
function GetCaptionStrings(UsedValuesBits: pointer = nil): string;
/// add caption names, ready to be display, to a TStrings class
// - add pointer(ord(element)) as Objects[] value
// - if UsedValuesBits is not nil, only the corresponding bits set are added
// - can be used e.g. to populate a combo box as such:
// ! PTypeInfo(TypeInfo(TMyEnum))^.EnumBaseType^.AddCaptionStrings(ComboBox.Items);
procedure AddCaptionStrings(Strings: TStrings;
UsedValuesBits: pointer = nil);
/// retrieve all element names as a dynamic array of RawUtf8
// - names could be optionally trimmed left from their initial lower chars
procedure GetEnumNameAll(var result: TRawUtf8DynArray;
TrimLeftLowerCase: boolean); overload;
/// retrieve all element names as CSV, with optional quotes
procedure GetEnumNameAll(out result: RawUtf8; const Prefix: RawUtf8 = '';
quotedValues: boolean = false; const Suffix: RawUtf8 = '';
trimedValues: boolean = false; unCamelCased: boolean = false); overload;
/// retrieve all trimed element names as CSV
procedure GetEnumNameTrimedAll(var result: RawUtf8; const Prefix: RawUtf8 = '';
quotedValues: boolean = false; const Suffix: RawUtf8 = '');
/// get all enumeration names as a JSON array of strings
function GetEnumNameAllAsJsonArray(TrimLeftLowerCase: boolean;
UnCamelCased: boolean = false): RawUtf8;
/// get the corresponding enumeration ordinal value, from its name
// - if EnumName does start with lowercases 'a'..'z', they will be searched:
// e.g. GetEnumNameValue('sllWarning') will find sllWarning item
// - if Value does not start with lowercases 'a'..'z', they will be ignored:
// e.g. GetEnumNameValue('Warning') will find sllWarning item
// - return -1 if not found (don't use directly this value to avoid any GPF)
function GetEnumNameValue(const EnumName: ShortString): integer; overload;
{$ifdef HASINLINE}inline;{$endif}
/// get the corresponding enumeration ordinal value, from its name
// - if Value does start with lowercases 'a'..'z', they will be searched:
// e.g. GetEnumNameValue('sllWarning') will find sllWarning item
// - if Value does not start with lowercases 'a'..'z', they will be ignored:
// e.g. GetEnumNameValue('Warning') will find sllWarning item
// - return -1 if not found (don't use directly this value to avoid any GPF)
function GetEnumNameValue(Value: PUtf8Char): integer; overload;
{$ifdef HASINLINE}inline;{$endif}
/// get the corresponding enumeration ordinal value, from its name
// - if Value does start with lowercases 'a'..'z', they will be searched:
// e.g. GetEnumNameValue('sllWarning') will find sllWarning item
// - if AlsoTrimLowerCase is TRUE, and EnumName does not start with
// lowercases 'a'..'z', they will be ignored: e.g. GetEnumNameValue('Warning')
// will find sllWarning item
// - return -1 if not found, or if RTTI's MinValue is not 0
function GetEnumNameValue(Value: PUtf8Char; ValueLen: integer;
AlsoTrimLowerCase: boolean = true): integer; overload;
/// get the corresponding enumeration ordinal value, from its trimmed name
function GetEnumNameValueTrimmed(Value: PUtf8Char; ValueLen: integer;
ExactCase: boolean): integer;
/// get the corresponding enumeration name, without the first lowercase chars
// (otDone -> 'Done')
// - Value will be converted to the matching ordinal value (byte or word)
function GetEnumNameTrimed(const Value): RawUtf8;
{$ifdef HASINLINE}inline;{$endif}
/// get the enumeration names corresponding to a set value
function GetSetNameCsv(Value: cardinal; SepChar: AnsiChar = ',';
FullSetsAsStar: boolean = false): RawUtf8; overload;
/// get the enumeration names corresponding to a set value
procedure GetSetNameCsv(W: TTextWriter; Value: cardinal; SepChar: AnsiChar = ',';
FullSetsAsStar: boolean = false); overload;
/// get the corresponding enumeration ordinal value, from its name without
// its first lowercase chars ('Done' will find otDone e.g.)
// - return -1 if not found, or if RTTI's MinValue is not 0
function GetEnumNameTrimedValue(const EnumName: ShortString): integer; overload;
/// get the corresponding enumeration ordinal value, from its name without
// its first lowercase chars ('Done' will find otDone e.g.)
// - return -1 if not found, or if RTTI's MinValue is not 0
function GetEnumNameTrimedValue(Value: PUtf8Char; ValueLen: integer = 0): integer; overload;
/// compute how many bytes this type will use to be stored as a enumerate
function SizeInStorageAsEnum: integer;
{$ifdef HASINLINE}inline;{$endif}
/// compute how many bytes (1, 2, 4) this type will use to be stored as a set
// - consider using TRttiInfo.SetEnumSize if ISFPC32 conditional is defined
function SizeInStorageAsSet: integer;
{$ifdef HASINLINE}inline;{$endif}
/// store an enumeration value from its ordinal representation
procedure SetEnumFromOrdinal(out Value; Ordinal: PtrUInt);
{$ifdef HASINLINE}inline;{$endif}
end;
/// RTTI of a record/object type definition (managed) field
// - defined here since this structure is not available in oldest
// Delphi's TypInfo.pas
// - maps TRecordElement in FPC rtti.inc or TManagedField in TypInfo
TRttiRecordField = record
/// the RTTI of this managed field
{$ifdef HASDIRECTTYPEINFO}
TypeInfo: PRttiInfo;
{$else}
TypeInfoRef: PPRttiInfo;
{$endif HASDIRECTTYPEINFO}
/// where this managed field starts in the record memory layout
Offset: PtrUInt;
end;
/// pointer to the RTTI of a record/object type definition (managed) field
PRttiRecordField = ^TRttiRecordField;
/// define the interface abilities
TRttiIntfFlag = (
ifHasGuid,
ifDispInterface,
ifDispatch
{$ifdef FPC} ,
ifHasStrGUID {$endif});
/// define the set of interface abilities
TRttiIntfFlags = set of TRttiIntfFlag;
/// a wrapper to interface type information, as defined by the the compiler RTTI
TRttiInterfaceTypeData = object
/// ancestor interface type
function IntfParent: PRttiInfo;
{$ifdef HASINLINE}inline;{$endif}
/// interface abilities - not inlined to avoid random trouble on FPC trunk
function IntfFlags: TRttiIntfFlags;
/// interface 128-bit Guid
function IntfGuid: PGuid;
{$ifdef HASINLINE}inline;{$endif}
/// where the interface has been defined
function IntfUnit: PShortString;
{$ifdef HASINLINE}inline;{$endif}
end;
/// pointer to a wrapper to interface type information
PRttiInterfaceTypeData = ^TRttiInterfaceTypeData;
/// record RTTI as returned by TRttiInfo.RecordManagedFields
TRttiRecordManagedFields = record
/// the record size in bytes
Size: PtrInt;
/// how many managed Fields[] are defined in this record
Count: PtrInt;
/// points to the first field RTTI
// - use inc(Fields) to go to the next one
Fields: PRttiRecordField;
end;
/// enhanced RTTI of a record/object type definition
// - as returned by TRttiInfo.RecordAllFields on Delphi 2010+
TRttiRecordAllField = record
/// the field RTTI definition
TypeInfo: PRttiInfo;
/// the field offset in the record
Offset: PtrUInt;
/// the field property name
Name: PShortString;
end;
PRttiRecordAllField = ^TRttiRecordAllField;
/// as returned by TRttiInfo.RecordAllFields
TRttiRecordAllFields = array of TRttiRecordAllField;
/// quick identification of some RTTI value types
TRttiCacheFlag = (
rcfQWord,
rcfBoolean,
rcfHasRttiOrd,
rcfGetOrdProp,
rcfGetInt64Prop,
rcfIsRawBlob,
rcfIsCurrency,
rcfIsNumber);
/// as used by TRttiCache.Flags
// - rcfQWord/rcfBoolean map Info^.IsQWord/IsBoolean
// - rcfIsRawBlob/rcfIsCurrency map Info^.IsRawBlob/IsCurrency
// - set rcfHasRttiOrd/rcfGetOrdProp/rcfGetInt64Prop to access the value
TRttiCacheFlags = set of TRttiCacheFlag;
/// convenient wrapper about PRttiInfo content and its more precise information
// - may be cached between use for more efficient process
TRttiCache = record
/// the associated RTTI TypeInfo()
Info: PRttiInfo;
/// the size in bytes of a value of this type - equals Info^.RttiSize
Size: integer;
/// equals Info^.Kind
Kind: TRttiKind;
/// quick identification of specific types, e.g. rkOrdinalTypes
Flags: TRttiCacheFlags;
/// for rkHasRttiOrdTypes/rcfHasRttiOrd, equals Info^.RttiOrd
RttiOrd: TRttiOrd;
/// corresponding TRttiVarData.VType
// - rkEnumeration,rkSet,rkDynArray,rkClass,rkInterface,rkRecord,rkArray are
// identified as varAny with TVarData.VAny pointing to the actual value, and
// will be handled as expected by TJsonWriter.AddRttiVarData
RttiVarDataVType: cardinal;
/// type-specific information
case TRttiKind of
rkFloat: (
RttiFloat: TRttiFloat);
rkLString: ( // from TypeInfo() on older Delphi with no CP RTTI
CodePage: cardinal; // RawBlob=CP_RAWBYTESTRING not CP_RAWBLOB
Engine: TSynAnsiConvert);
rkEnumeration,
rkSet: (
EnumMin,
EnumMax: cardinal;
EnumInfo: PRttiEnumType;
EnumList: PShortString);
rkDynArray,
rkArray: (
ItemInfo: PRttiInfo; // = nil for unmanaged types
ItemSize: integer;
ItemCount: integer; // rkArray only
);
end;
/// map extended PRttiInfo content
PRttiCache = ^TRttiCache;
{$A-}
/// main entry-point wrapper to access RTTI for a given pascal type
// - as returned by the TypeInfo() low-level compiler function
// - other RTTI objects can be computed from a pointer to this structure
// - user types defined as an alias don't have this type information:
// ! type
// ! TNewType = TOldType;
// here TypeInfo(TNewType) = TypeInfo(TOldType)
// - user types defined as new types have this type information:
// ! type
// ! TNewType = type TOldType;
// here TypeInfo(TNewType) <> TypeInfo(TOldType)
TRttiInfo = object
public
/// the value type family
// - not defined as an inlined function, since first field is always aligned
Kind: TRttiKind;
/// the declared name of the type ('String','Word','RawUnicode'...)
// - won't adjust internal/cardinal names on FPC as with Name method
RawName: ShortString;
/// the declared name of the type ('String','Word','RawUnicode'...)
// - will return '' if @self is nil
// - on FPC, will adjust 'integer'/'cardinal' from 'longint'/'longword' RTTI
function Name: PShortString;
{$ifdef ISDELPHI2006ANDUP}inline;{$endif}
/// efficiently finalize any (managed) type value
// - do nothing for unmanaged types (e.g. integer)
// - if you are sure that your type is managed, you may call directly
// $ RTTI_FINALIZE[Info^.Kind](Data, Info);
procedure Clear(Data: pointer);
{$ifdef HASINLINE}inline;{$endif}
/// efficiently copy any (managed) type value
// - do nothing for unmanaged types (e.g. integer)
// - if you are sure that your type is managed, you may call directly
// $ RTTI_MANAGEDCOPY[Info^.Kind](Dest, Source, Info);
procedure Copy(Dest, Source: pointer);
{$ifdef HASINLINE}inline;{$endif}
/// compute extended information about this RTTI type
procedure ComputeCache(out Cache: TRttiCache);
/// for ordinal types, get the storage size and sign
function RttiOrd: TRttiOrd;
{$ifdef HASINLINE}inline;{$endif}
/// return TRUE if the property is an unsigned 64-bit field (QWord/UInt64)
function IsQWord: boolean;
{$ifdef HASINLINE}inline;{$endif}
/// return TRUE if the property is a boolean field
function IsBoolean: boolean;
{$ifdef HASINLINE}inline;{$endif}
/// return TRUE if the property is a currency field
function IsCurrency: boolean;
{$ifdef HASINLINE}inline;{$endif}
/// return true if this property is a BLOB (RawBlob)
function IsRawBlob: boolean;
{$ifdef HASINLINE}inline;{$endif}
/// for rkFloat: get the storage size and precision
// - will also properly detect our currency internal type as rfCurr
function RttiFloat: TRttiFloat;
{$ifdef HASINLINE}inline;{$endif}
/// for rkEnumeration: get the enumeration type information
function EnumBaseType: PRttiEnumType; overload;
{$ifdef HASINLINE}inline;{$endif}
/// for rkEnumeration: get the enumeration values information
function EnumBaseType(out NameList: PShortString;
out Min, Max: integer): PRttiEnumType; overload;
{$ifdef HASINLINE}inline;{$endif}
/// for rkSet: get the type information of its associated enumeration
function SetEnumType: PRttiEnumType; overload;
{$ifdef HASINLINE}inline;{$endif}
/// for rkSet: get the associated enumeration values information
function SetEnumType(out NameList: PShortString;
out Min, Max: integer): PRttiEnumType; overload;
{$ifdef HASINLINE}inline;{$endif}
/// for rkSet: in how many bytes this type is stored
// - is very efficient on latest FPC only - i.e. ifdef ISFPC32
function SetEnumSize: PtrInt; {$ifdef ISFPC32} inline; {$endif}
/// compute in how many bytes this type is stored
// - will use Kind (and RttiOrd/RttiFloat) to return the exact value
function RttiSize: PtrInt;
/// check if this type is a managed type, or has any managed field
// - will also check for the nested fields e.g. for rkRecordTypes
function IsManaged: boolean;
/// for rkRecordTypes: get the record size
// - returns 0 if the type is not a record/object
function RecordSize: PtrInt;
{$ifdef HASINLINE}inline;{$endif}
/// for rkRecordTypes: retrieve RTTI information about all managed fields
// of this record
// - non managed fields (e.g. integers, double...) are not listed here
// - also includes the total record size in bytes
// - caller should ensure the type is indeed a record/object
// - note: if FPC_OLDRTTI is defined, unmanaged fields are included
procedure RecordManagedFields(out Fields: TRttiRecordManagedFields);
{$ifdef HASINLINE}inline;{$endif}
/// for rkRecordTypes: check if this record as any managed fields
function RecordManagedFieldsCount: integer;
{$ifdef HASINLINE}inline;{$endif}
/// for rkRecordTypes: retrieve enhanced RTTI information about all fields
// of this record, for JSON serialization without text definition
// - this information is currently only available since Delphi 2010
// - if any field has no RTTI (e.g. a static array of unmanaged type), then
// it will ignore this uncomplete, therefore non-useful RTTI
// - in practice, it may be a good habit to always define the records used
// within the SOA (e.g. as DTOs) calling RegisterFromText, and don't rely on
// this RTTI, since it will be more cross-platform, and more customizable
function RecordAllFields(out RecSize: PtrInt): TRttiRecordAllFields;
/// for rkDynArray: get the dynamic array standard RTTI of the stored item
// - returns nil if the item has no managed field
// - caller should ensure the type is indeed a dynamic array
function DynArrayItemType: PRttiInfo; overload;
{$ifdef HASINLINE}inline;{$endif}
/// for rkDynArray: get the dynamic array deep RTTI of the stored item
// - works for both managed and unmanaged types, on FPC and Delphi 2010+
// - caller should ensure the type is indeed a dynamic array
function DynArrayItemTypeExtended: PRttiInfo;
/// for rkDynArray: get the dynamic array type information of the stored item
// - this overloaded method will also return the item size in bytes
// - caller should ensure the type is indeed a dynamic array
function DynArrayItemType(out aDataSize: PtrInt): PRttiInfo; overload;
{$ifdef HASINLINE}inline;{$endif}
/// for rkDynArray: get the dynamic array size (in bytes) of the stored item
function DynArrayItemSize: PtrInt;
{$ifdef HASINLINE}inline;{$endif}
/// for rkArray: get the static array type information of the stored item
// - returns nil if the array type is unmanaged (i.e. behave like Delphi)
// - aDataSize is the size in bytes of all aDataCount static items (not
// the size of each item)
// - caller should ensure the type is indeed a static array
function ArrayItemType(out aDataCount, aDataSize: PtrInt): PRttiInfo;
{$ifdef HASINLINE}inline;{$endif}
/// for rkArray: get the size in bytes of all the static array items
// - caller should ensure the type is indeed a static array
function ArraySize: PtrInt;
{$ifdef HASINLINE}inline;{$endif}
/// recognize most used string types, returning their code page
// - will return the exact code page on FPC and since Delphi 2009, from RTTI
// - for non Unicode versions of Delphi, will recognize WinAnsiString as
// CODEPAGE_US, RawUnicode as CP_UTF16, RawByteString as CP_RAWBYTESTRING,
// AnsiString as 0, and any other type as RawUtf8
// - it will also recognize RawBlob as the fake CP_RAWBLOB codepage
function AnsiStringCodePage: integer;
{$ifdef HASCODEPAGE}inline;{$endif}
{$ifdef HASCODEPAGE}
/// returning the code page stored in the RTTI
// - without recognizing e.g. RawBlob
// - caller should ensure the type is indeed a rkLString
function AnsiStringCodePageStored: integer; inline;
{$endif HASCODEPAGE}
/// retrieve rkLString, rkSString, rkUString, rkWString, rkChar, rkWChar
// values as RawUtf8, from a pointer to its memory storage
// - makes heap allocations and encoding conversion, so may be slow
procedure StringToUtf8(Data: pointer; var Value: RawUtf8);
/// for rkClass: get the class type information
function RttiClass: PRttiClass;
{$ifdef HASINLINE}inline;{$endif}
/// for rkClass: get the class type information
function RttiNonVoidClass: PRttiClass;
{$ifdef HASINLINE}inline;{$endif}
/// for rkClass: return the number of published properties in this class
// - you can count the plain fields without any getter function, if you
// do need only the published properties corresponding to some value
// actually stored, and ignore e.g. any textual conversion
function ClassFieldCount(onlyWithoutGetter: boolean): integer;
/// for rkClass: fast and easy check if a class inherits from this RTTI
function InheritsFrom(AClass: TClass): boolean;
/// for rkInterface: get the interface type information
function InterfaceType: PRttiInterfaceTypeData;
{$ifdef HASINLINE}inline;{$endif}
/// for rkInterface: get the TGuid of a given interface type information
// - returns nil if this type is not an interface
function InterfaceGuid: PGuid;
/// for rkInterface: get the unit name of a given interface type information
// - returns '' if this type is not an interface
function InterfaceUnitName: PShortString;
/// for rkInterface: get the ancestor/parent of a given interface type information
// - returns nil if this type has no parent
function InterfaceAncestor: PRttiInfo;
/// for rkInterface: get all ancestors/parents of a given interface type information
// - only ancestors with an associated TGuid will be added
// - if OnlyImplementedBy is not nil, only the interface explicitly
// implemented by this class will be added, and AncestorsImplementedEntry[]
// will contain the corresponding PInterfaceEntry values
procedure InterfaceAncestors(out Ancestors: PRttiInfoDynArray;
OnlyImplementedBy: TInterfacedObjectClass;
out AncestorsImplementedEntry: TPointerDynArray);
end;
{$A+}
/// how a RTTI property definition access its value
// - as returned by TPropInfo.Getter/Setter/GetterIs/SetterIs methods
TRttiPropCall = (
rpcNone,
rpcField,
rpcMethod,
rpcIndexed);
/// a wrapper containing a RTTI class property definition
// - used for direct Delphi / UTF-8 SQL type mapping/conversion
// - doesn't depend on RTL's TypInfo unit, to enhance cross-compiler support
TRttiProp = object
public
/// raw retrieval of the property read access definition
// - note: 'var Call' generated incorrect code on Delphi XE4 -> use PMethod
function Getter(Instance: TObject; Call: PMethod): TRttiPropCall;
{$ifdef HASINLINE}inline;{$endif}
/// raw retrieval of the property access definition
function Setter(Instance: TObject; Call: PMethod): TRttiPropCall;
{$ifdef HASINLINE}inline;{$endif}
/// raw retrieval of rkInteger,rkEnumeration,rkSet,rkChar,rkWChar,rkBool
// - rather call GetOrdValue/GetInt64Value
// - returns an Int64 to properly support cardinal values
function GetOrdProp(Instance: TObject): Int64;
/// raw assignment of rkInteger,rkEnumeration,rkSet,rkChar,rkWChar,rkBool
// - rather call SetOrdValue/SetInt64Value
procedure SetOrdProp(Instance: TObject; Value: PtrInt);
/// raw retrieval of rkClass
function GetObjProp(Instance: TObject): TObject;
/// raw retrieval of rkInt64, rkQWord
// - rather call GetInt64Value
function GetInt64Prop(Instance: TObject): Int64;
/// raw assignment of rkInt64, rkQWord
// - rather call SetInt64Value
procedure SetInt64Prop(Instance: TObject; const Value: Int64);
/// raw retrieval of rkLString
procedure GetLongStrProp(Instance: TObject; var Value: RawByteString);
/// raw assignment of rkLString
procedure SetLongStrProp(Instance: TObject; const Value: RawByteString);
/// raw copy of rkLString
procedure CopyLongStrProp(Source,Dest: TObject);
/// raw retrieval of rkString into an Ansi7String
procedure GetShortStrProp(Instance: TObject; var Value: RawUtf8);
/// raw retrieval of rkWString
procedure GetWideStrProp(Instance: TObject; var Value: WideString);
/// raw assignment of rkWString
procedure SetWideStrProp(Instance: TObject; const Value: WideString);
{$ifdef HASVARUSTRING}
/// raw retrieval of rkUString
procedure GetUnicodeStrProp(Instance: TObject; var Value: UnicodeString);
/// raw assignment of rkUString
procedure SetUnicodeStrProp(Instance: TObject; const Value: UnicodeString);
{$endif HASVARUSTRING}
/// raw retrieval of rkFloat/currency
// - use instead GetCurrencyValue
procedure GetCurrencyProp(Instance: TObject; var Value: currency);
/// raw assignment of rkFloat/currency
procedure SetCurrencyProp(Instance: TObject; const Value: currency);
/// raw retrieval of rkFloat/double
function GetDoubleProp(Instance: TObject): double;
/// raw assignment of rkFloat/double
procedure SetDoubleProp(Instance: TObject; Value: Double);
/// raw retrieval of rkFloat - with conversion to 64-bit double
// - use instead GetDoubleValue
function GetFloatProp(Instance: TObject): double;
/// raw assignment of rkFloat
// - use instead SetDoubleValue
procedure SetFloatProp(Instance: TObject; Value: TSynExtended);
/// raw retrieval of rkVariant
// - will use varByRef from the field address if SetByRef is true
procedure GetVariantProp(Instance: TObject; var Result: Variant; SetByRef: boolean);
/// raw assignment of rkVariant
procedure SetVariantProp(Instance: TObject; const Value: Variant);
/// raw retrieval of the 'stored' flag using getter
/// - called by IsStored when inlined
function GetIsStored(Instance: TObject): boolean;
public
/// contains the index value of an indexed class data property
// - outside SQLite3, this can be used to define a VARCHAR() length value
// for the textual field definition (sftUtf8Text/sftAnsiText); e.g.
// the following will create a NAME VARCHAR(40) field:
// ! Name: RawUtf8 index 40 read fName write fName;
// - is used by a dynamic array property for fast usage of the
// TOrm.DynArray(DynArrayFieldIndex) method
function Index: integer;
{$ifdef HASINLINE}inline;{$endif}
/// contains the default value for an ordinal or set property
// - NO_DEFAULT=$80000000 indicates none was defined in source code
// - see also TPropInfo.DefaultOr0
function Default: integer;
{$ifdef HASINLINE}inline;{$endif}
/// return the Default RTTI value defined for this property, or 0 if not set
function DefaultOr0: integer;
{$ifdef HASINLINE}inline;{$endif}
/// index of the property in the current inherited class definition
// - first name index at a given class level is 0
// - index is reset to 0 at every inherited class level
function NameIndex: integer;
{$ifdef HASINLINE}inline;{$endif}
/// the property Name, directly returned from RTTI
function Name: PShortString;
{$ifdef HASINLINE}inline;{$endif}
/// the property Name, converted as a RawUtf8
function NameUtf8: RawUtf8;
/// the type information of this property
// - will de-reference the PropType pointer on Delphi and newer FPC compilers
function TypeInfo: PRttiInfo;
{$ifdef HASINLINE}inline;{$endif}
/// get the next property information
// - no range check: use RttiProps()^.PropCount to determine the properties count
// - get the first PRttiProp with RttiProps()^.PropList