-
-
Notifications
You must be signed in to change notification settings - Fork 141
/
Copy pathmormot.core.interfaces.pas
7820 lines (7322 loc) · 291 KB
/
mormot.core.interfaces.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 Interface/SOLID Processing
// - 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.interfaces;
{
*****************************************************************************
Implements SOLID Process via Interface types
- IInvokable Interface Methods and Parameters RTTI Extraction
- TInterfaceFactory Generating Runtime Implementation Class
- TInterfaceResolver TInjectableObject for IoC / Dependency Injection
- TInterfaceStub TInterfaceMock for Dependency Mocking
- TInterfacedObjectFake with JITted Methods Execution
- TInterfaceMethodExecute for Method Execution from JSON
- SetWeak and SetWeakZero Weak Interface Reference
*****************************************************************************
}
interface
{$I ..\mormot.defines.inc}
uses
sysutils,
classes,
{$ifdef ISDELPHI}
typinfo, // for proper Delphi inlining
{$endif ISDELPHI}
mormot.core.base,
mormot.core.os,
mormot.core.unicode,
mormot.core.text,
mormot.core.rtti,
mormot.core.buffers,
mormot.core.variants,
mormot.core.data,
mormot.core.json,
mormot.core.threads,
mormot.core.test, // for TInterfaceMock
mormot.core.log;
{ ************ IInvokable Interface Methods and Parameters RTTI Extraction }
type
/// handled kind of parameters for an interface-based service provider method
// - we do not handle all kind of variables, but provide some enhanced types
// handled by JsonToObject/ObjectToJson functions (smvObject) or
// TDynArray.LoadFromJson / TJsonWriter.AddDynArrayJson methods (smvDynArray)
// - records will be serialized as Base64 string, with our RecordSave/RecordLoad
// low-level format by default, or as true JSON objects since Delphi 2010 or
// after a Rtti.RegisterFromText/TRttiJson.RegisterCustomSerializer call
// - imvRawJson will transmit the raw JSON content, without serialization
TInterfaceMethodValueType = (
imvNone,
imvSelf,
imvBoolean,
imvEnum,
imvSet,
imvInteger,
imvCardinal,
imvInt64,
imvDouble,
imvDateTime,
imvCurrency,
imvRawUtf8,
imvString,
imvRawByteString,
imvWideString,
imvRecord,
imvVariant,
imvObject,
imvRawJson,
imvDynArray,
imvInterface);
/// handled kind of parameters internal variables for an interface-based method
// - reference-counted variables will have their own storage
// - all non referenced-counted variables are stored within some 64-bit content
// - imvVariant kind of parameter will be handled as a special imvvRecord
TInterfaceMethodValueVar = (
imvvNone,
imvvSelf,
imvv64,
imvvRawUtf8,
imvvString,
imvvWideString,
imvvRecord,
imvvObject,
imvvDynArray,
imvvInterface);
/// set of parameters for an interface-based service provider method
TInterfaceMethodValueTypes = set of TInterfaceMethodValueType;
/// handled kind of parameters direction for an interface-based service method
// - IN, IN/OUT, OUT directions can be applied to arguments, and will
// be available through our JSON-serialized remote access: smdVar and smdOut
// kind of parameters will be returned within the "result": JSON array
// - smdResult is used for a function method, to handle the returned value
TInterfaceMethodValueDirection = (
imdConst,
imdVar,
imdOut,
imdResult);
/// set of parameters direction for an interface-based service method
TInterfaceMethodValueDirections = set of TInterfaceMethodValueDirection;
/// set of low-level processing options at assembly level
// - vPassedByReference is included if the parameter is passed as reference
// (i.e. defined as var/out, or is a record or a reference-counted type result)
// - vIsQword is set for ValueType=imvInt64 over a QWord unsigned 64-bit value
// - vIsDynArrayString is set for ValueType=imvDynArray of string values
// - vIsInterfaceJson is set for an interface with custom JSON serializers
// - vIsOnStack is set when the Value is to be located on stack
TInterfaceMethodValueAsm = set of (
vPassedByReference,
vIsQword,
vIsDynArrayString,
vIsInterfaceJson,
vIsOnStack);
/// a pointer to an interface-based service provider method description
// - since TInterfaceFactory instances are shared in a global list, we
// can safely use such pointers in our code to refer to a particular method
PInterfaceMethod = ^TInterfaceMethod;
/// describe a service provider method argument
{$ifdef USERECORDWITHMETHODS}
TInterfaceMethodArgument = record
{$else}
TInterfaceMethodArgument = object
{$endif USERECORDWITHMETHODS}
public
/// the argument name, as declared in object pascal
ParamName: PShortString;
/// the type name, as declared in object pascal
ArgTypeName: PShortString;
/// the low-level RTTI information of this argument
// - use ArgRtti.Info to retrieve the TypeInfo() of this argument
ArgRtti: TRttiJson;
/// we do not handle all kind of object pascal variables
ValueType: TInterfaceMethodValueType;
/// the variable direction as defined at code level
ValueDirection: TInterfaceMethodValueDirection;
/// how the variable may be stored
ValueVar: TInterfaceMethodValueVar;
/// how the variable is to be passed at asm level
ValueKindAsm: TInterfaceMethodValueAsm;
/// specify if the argument is passed as register
// - contains 0 if parameter is not a register
// - i386: 1 for EAX, 2 for EDX and 3 for ECX registers
// - x86_64: 1=RCX/RDI 2=RDX/RSI 3=R8/RDX 4=R9/RCX, with stack backing store
// - ARM: 1=R0 2=R1 3=R2 4=R3, with a backing store on the stack
// - AARCH64: 1=X0 2=X1, ..., 8=X7, with a backing store on the stack
RegisterIdent: byte;
/// specify if a floating-point argument is passed as register
// - i386/x87: contains always 0 - the HAS_FPREG conditional is not defined
// - x86_64: 1 for XMM0, 2 for XMM1, , ..., 8 for XMM7
// - ARMHF: 1 for D0, 2 for D1, ..., 8 for D7
// - AARCH64: 1 for V0, 2 for V1, ..., 8 for V7
FPRegisterIdent: byte;
/// index of the associated variable in the local array[ArgsUsedCount[]]
IndexVar: byte;
/// size (in bytes) of this argument on the stack
SizeInStack: byte;
/// byte offset in the CPU stack of this argument (16-bit)
// - may be -1 if pure register parameter with no backup on stack (x86)
InStackOffset: SmallInt;
/// how TInterfaceMethodExecuteRaw.RawExecute should handle this value
RawExecute: (reValReg, reValStack, reRefReg, reRefStack, reValFpReg, reNone);
/// 64-bit aligned position in TInterfaceMethod.ArgsSizeAsValue memory
OffsetAsValue: cardinal;
/// serialize the argument into the TServiceContainer.Contract JSON format
// - non standard types (e.g. class, enumerate, dynamic array or record)
// are identified by their type identifier - so contract does not extend
// up to the content of such high-level structures
procedure SerializeToContract(WR: TJsonWriter);
/// unserialize a JSON value into this argument
function SetFromJson(var Ctxt: TJsonParserContext; Method: PInterfaceMethod;
V: pointer; Error: PShortString): boolean;
/// append the JSON value corresponding to this argument
procedure AddJson(WR: TJsonWriter; V: pointer;
ObjectOptions: TTextWriterWriteObjectOptions = [woDontStoreDefault]);
/// append the value corresponding to this argument as within a JSON string
// - will escape any JSON string character, and include a pending ','
procedure AddJsonEscaped(WR: TJsonWriter; V: pointer);
/// append the JSON value corresponding to this argument, from its text value
// - includes a pending ','
procedure AddValueJson(WR: TJsonWriter; const Value: RawUtf8);
/// append the default JSON value corresponding to this argument
// - includes a pending ','
procedure AddDefaultJson(WR: TJsonWriter);
/// add a value into a TDocVariant object or array
// - Dest should already have set its Kind to either dvObject or dvArray
procedure AddAsVariant(var Dest: TDocVariantData; V: pointer);
/// normalize a value containing one input or output argument
// - sets and enumerates will be translated into text (also in embedded
// objects and T*ObjArray), and record/class/arrays into TDocVariantData
procedure FixValue(var Value: variant);
/// normalize a value containing one input or output argument, and add
// it to a destination variant Document
// - sets and enumerates will be translated to strings (also in embedded
// objects and T*ObjArray)
procedure FixValueAndAddToObject(const Value: variant;
var DestDoc: TDocVariantData);
end;
/// pointer to a service provider method argument
PInterfaceMethodArgument = ^TInterfaceMethodArgument;
/// describe a service provider method arguments
TInterfaceMethodArgumentDynArray = array of TInterfaceMethodArgument;
/// callback called by TInterfaceMethodExecute to process an interface
// callback parameter
// - implementation should set the Obj local variable to an instance of
// a fake class implementing the aParamInfo interface
TOnInterfaceMethodExecuteCallback = procedure(var Ctxt: TJsonParserContext;
ParamInterfaceInfo: TRttiJson; out Obj) of object;
/// how TInterfaceMethod.ArgsValuesAsDocVariant will return the generated document
// - will return either a dvObject or dvArray TDocVariantData, depending on
// the expected returned document layout
// - returned content could be "normalized" (for any set or enumerate) if
// Kind is pdvObjectFixed
TInterfaceMethodParamsDocVariantKind = (
pdvArray,
pdvObject,
pdvObjectFixed);
/// refine one TInterfaceMethod processing
// - imfIsInherited if the method is inherited from another parent interface
// - imfResultIsServiceCustomAnswer is set if the result is a
// TServiceCustomAnswer record and requires specific raw HTTP-level process
// - imfResultIsServiceCustomStatus is set if the result is a
// TServiceCustomStatus integer, i.e. a custom HTTP status
// - imfInputIsOctetStream is set if there is a single input parameter as
// RawByteString/RawBlob so that TRestRoutingRest.ExecuteSoaByInterface will
// identify binary body input with mime-type 'application/octet-stream'
TInterfaceMethodFlag = (
imfIsInherited,
imfResultIsServiceCustomAnswer,
imfResultIsServiceCustomStatus,
imfInputIsOctetStream);
/// how TInterfaceMethod should implement this service provider method
TInterfaceMethodFlags = set of TInterfaceMethodFlag;
/// describe an interface-based service provider method
{$ifdef USERECORDWITHMETHODS}
TInterfaceMethod = record
{$else}
TInterfaceMethod = object
{$endif USERECORDWITHMETHODS}
public
/// the method URI, i.e. the method name
// - as declared in object pascal code, e.g. 'Add' for ICalculator.Add
// - this property value is hashed internally for faster access
Uri: RawUtf8;
/// the method default result, formatted as a JSON array
// - example of content may be '[]' for a procedure or '[0]' for a function
// - any var/out and potential function result will be set as a JSON array
// of values, with 0 for numerical values, "" for textual values,
// false for booleans, [] for dynamic arrays, a void record serialized
// as expected (including customized serialization) and null for objects
DefaultResult: RawUtf8;
/// the fully qualified dotted method name, including the interface name
// - as used by TServiceContainerInterfaceMethod.InterfaceDotMethodName
// - match the URI fullpath name, e.g. 'Calculator.Add'
InterfaceDotMethodName: RawUtf8;
/// method index in the original (non emulated) interface
// - our custom methods start at index 3 (RESERVED_VTABLE_SLOTS), since
// QueryInterface, _AddRef, and _Release are always defined by default
// - so it maps TServiceFactory.Interface.Methods[ExecutionMethodIndex-3]
ExecutionMethodIndex: byte;
/// how this method is defined and should be processed
Flags: TInterfaceMethodFlags;
/// the directions of arguments with SPI parameters defined
HasSpiParams: TInterfaceMethodValueDirections;
/// is 0 for the root interface, 1..n for all inherited interfaces
HierarchyLevel: byte;
/// the number of const / var parameters in Args[]
// - i.e. the number of elements in the input JSON array
ArgsInputValuesCount: byte;
/// the number of var / out parameters + in Args[]
// - i.e. the number of elements in the output JSON array or object
ArgsOutputValuesCount: byte;
/// needed CPU stack size (in bytes) for all arguments
// - under x64, does not include the backup space for the four registers
ArgsSizeInStack: word;
/// describe expected method arguments
// - Args[0] is always imvSelf
// - if method is a function, an additional imdResult argument is appended
Args: TInterfaceMethodArgumentDynArray;
/// the index of the result pseudo-argument in Args[] (signed 8-bit)
// - is -1 if the method is defined as a procedure (not a function)
ArgsResultIndex: ShortInt;
/// the index of the first const / var argument in Args[] (signed 8-bit)
ArgsInFirst: ShortInt;
/// the index of the last const / var argument in Args[] (signed 8-bit)
ArgsInLast: ShortInt;
/// the index of the first var / out / result argument in Args[] (signed 8-bit)
ArgsOutFirst: ShortInt;
/// the index of the last var / out / result argument in Args[] (signed 8-bit)
ArgsOutLast: ShortInt;
/// the index of the last argument in Args[], excepting result (signed 8-bit)
ArgsNotResultLast: ShortInt;
/// the index of the last var / out argument in Args[] (signed 8-bit)
ArgsOutNotResultLast: ShortInt;
/// the index of the first argument expecting manual stack initialization
// - set for Args[].ValueVar >= imvvRawUtf8
ArgsManagedFirst: ShortInt;
/// how manual stack initialization arguments are defined
// - set for Args[].ValueVar >= imvvRawUtf8
ArgsManagedCount: byte;
/// contains all used kind of arguments
ArgsUsed: TInterfaceMethodValueTypes;
/// 64-bit aligned cumulative size for all arguments values
// - follow Args[].OffsetAsValue distribution
ArgsSizeAsValue: cardinal;
/// contains the count of variables for all used kind of arguments
ArgsUsedCount: array[TInterfaceMethodValueVar] of byte;
/// retrieve an argument index in Args[] from its name
// - search is case insensitive
// - if Input is TRUE, will search within const / var arguments
// - if Input is FALSE, will search within var / out / result arguments
// - returns -1 if not found
function ArgIndex(ArgName: PUtf8Char; ArgNameLen: integer;
Input: boolean): PtrInt;
/// find the next input (const / var) argument index in Args[]
// - returns true if arg is the new value, false otherwise
function ArgNextInput(var arg: integer): boolean;
{$ifdef HASINLINE} inline; {$endif}
/// find the next output (var / out / result) argument index in Args[]
// - returns true if arg is the new value, false otherwise
function ArgNextOutput(var arg: integer): boolean;
{$ifdef HASINLINE} inline; {$endif}
/// convert parameters encoded as a JSON array into a JSON object
// - if Input is TRUE, will handle const / var arguments
// - if Input is FALSE, will handle var / out / result arguments
function ArgsArrayToObject(P: PUtf8Char; Input: boolean): RawUtf8;
/// convert parameters encoded as name=value or name='"value"' or name='{somejson}'
// into a JSON object
// - on Windows, use double-quotes ("") anywhere you expect single-quotes (")
// - as expected e.g. from a command line tool
// - if Input is TRUE, will handle const / var arguments
// - if Input is FALSE, will handle var / out / result arguments
function ArgsCommandLineToObject(P: PUtf8Char; Input: boolean;
RaiseExceptionOnUnknownParam: boolean = false): RawUtf8;
/// returns a dynamic array list of all parameter names
// - if Input is TRUE, will handle const / var arguments
// - if Input is FALSE, will handle var / out / result arguments
function ArgsNames(Input: boolean): TRawUtf8DynArray;
/// computes a TDocVariant containing the input or output arguments values
// - Values[] should contain the input/output raw values as variant
// - Kind will specify the expected returned document layout
procedure ArgsValuesAsDocVariant(Kind: TInterfaceMethodParamsDocVariantKind;
out Dest: TDocVariantData; const Values: TVariantDynArray; Input: boolean;
Options: TDocVariantOptions =
[dvoReturnNullForUnknownProperty, dvoValueCopiedByReference]);
/// normalize a TDocVariant containing the input or output arguments values
// - "normalization" will ensure sets and enums are seralized as text
// - if Input is TRUE, will handle const / var arguments
// - if Input is FALSE, will handle var / out / result arguments
procedure ArgsAsDocVariantFix(var ArgsObject: TDocVariantData; Input: boolean);
/// convert a TDocVariant array containing the input or output arguments
// values in order, into an object with named parameters
// - here sets and enums will keep their current values, mainly numerical
// - if Input is TRUE, will handle const / var arguments
// - if Input is FALSE, will handle var / out / result arguments
procedure ArgsAsDocVariantObject(const ArgsParams: TDocVariantData;
var ArgsObject: TDocVariantData; Input: boolean);
/// computes a TDocVariant containing the input or output arguments values
// - Values[] should point to the input/output raw binary values, as stored
// in TInterfaceMethodExecute.Values during execution
procedure ArgsStackAsDocVariant(Values: PPointerArray;
out Dest: TDocVariantData; Input: boolean);
end;
/// describe all mtehods of an interface-based service provider
TInterfaceMethodDynArray = array of TInterfaceMethod;
// backward compatibility types redirections
{$ifndef PUREMORMOT2}
type
TServiceMethodValueType = TInterfaceMethodValueType;
TServiceMethodValueTypes = TInterfaceMethodValueTypes;
TServiceMethodValueVar = TInterfaceMethodValueVar;
TServiceMethodValueDirection = TInterfaceMethodValueDirection;
TServiceMethodValueDirections = TInterfaceMethodValueDirections;
TServiceMethodArgument = TInterfaceMethodArgument;
PServiceMethodArgument = PInterfaceMethodArgument;
TServiceMethodArgumentDynArray = TInterfaceMethodArgumentDynArray;
TServiceMethodParamsDocVariantKind = TInterfaceMethodParamsDocVariantKind;
TServiceMethod = TInterfaceMethod;
TServiceMethodDynArray = TInterfaceMethodDynArray;
PServiceMethod = PInterfaceMethod;
const
// TServiceMethodValueType = TInterfaceMethodValueType items
smvNone = imvNone;
smvSelf = imvSelf;
smvBoolean = imvBoolean;
smvEnum = imvEnum;
smvSet = imvSet;
smvInteger = imvInteger;
smvCardinal = imvCardinal;
smvInt64 = imvInt64;
smvDouble = imvDouble;
smvDateTime = imvDateTime;
smvCurrency = imvCurrency;
smvRawUtf8 = imvRawUtf8;
smvString = imvString;
smvRawByteString = imvRawByteString;
smvWideString = imvWideString;
// smvBinary = imvBinary; not defined any more (handle by RTTI itself)
smvRecord = imvRecord;
smvVariant = imvVariant;
smvObject = imvObject;
smvRawJson = imvRawJson;
smvDynArray = imvDynArray;
smvInterface = imvInterface;
// TServiceMethodValueVar = TInterfaceMethodValueVar items
smvvNone = imvvNone;
smvvSelf = imvvSelf;
smvv64 = imvv64;
smvvRawUtf8 = imvvRawUtf8;
smvvString = imvvString;
smvvWideString = imvvWideString;
smvvRecord = imvvRecord;
smvvObject = imvvObject;
smvvDynArray = imvvDynArray;
smvvInterface = imvvInterface;
// TServiceMethodValueDirection = TInterfaceMethodValueDirection items
smdConst = imdConst;
smdVar = imdVar;
smdOut = imdOut;
smdResult = imdResult;
{$endif PUREMORMOT2}
{ ************ TInterfaceFactory Generating Runtime Implementation Class }
const
/// maximum number of methods handled by interfaces
// - if you think this constant is too low, you are clearing breaking
// the "Interface Segregation" SOLID principle: so don't ask to increase
// this value, we won't allow to write obviously un-SOLID code! :)
// - indexes would also fit in a signed [-1..127] 8-bit ShortInt
MAX_METHOD_COUNT = 128;
/// maximum number of method arguments handled by interfaces
// - if you consider this as a low value, you should better define some
// records/classes as DTOs instead of multiplicating parameters: so don't
// ask to increase this value, we rather encourage writing clean code
// - used e.g. to avoid creating dynamic arrays if not needed, and
// ease method calls
MAX_METHOD_ARGS = 32;
/// IInterface QueryInterface, _AddRef and _Release methods are hard-coded
RESERVED_VTABLE_SLOTS = 3;
type
/// internal pseudo methods when an interface is used as remote service
// - match TInterfaceFactory MethodIndex 0..3
// - imFree expects an associated ClientID, other methods won't
TServiceInternalMethod = (
imFree,
imContract,
imSignature,
imInstance);
const
/// URI of some pseudo methods when an interface is used as remote service
// - match TInterfaceFactory MethodIndex 0..3
SERVICE_PSEUDO_METHOD: array[TServiceInternalMethod] of RawUtf8 = (
'_free_',
'_contract_',
'_signature_',
'_instance_');
/// the number of MethodIndex which are a TServiceInternalMethod, i.e. 4
SERVICE_PSEUDO_METHOD_COUNT = length(SERVICE_PSEUDO_METHOD);
var
/// default value for TInterfaceFactory.JsonParserOptions
JSONPARSER_SERVICE: TJsonParserOptions =
[jpoHandleCustomVariants,
jpoIgnoreUnknownEnum,
jpoIgnoreUnknownProperty,
jpoIgnoreStringType,
jpoAllowInt64Hex,
jpoNullDontReleaseObjectInstance];
type
{$M+}
TInterfaceFactory = class;
{$M-}
/// exception dedicated to interface factory, used e.g. for services and mock/stubs
EInterfaceFactory = class(ESynException);
/// may be used to store the Methods[] indexes of a TInterfaceFactory
// - current implementation handles up to 128 methods, a limit above
// which "Interface Segregation" principle is obviously broken
TInterfaceFactoryMethodBits = set of 0 .. MAX_METHOD_COUNT - 1;
/// index-based reference to one TInterfaceFactory argument
TInterfaceFactoryArgument = record
/// the index of the method argument in TInterfaceFactory.Methods[]
MethodIndex: byte;
/// the index of the method argument in TInterfaceFactory.Methods[].Args[]
ArgIndex: byte;
end;
/// index-based reference to several TInterfaceFactory argument
TInterfaceFactoryArgumentDynArray = array of TInterfaceFactoryArgument;
/// per-type reference of TInterfaceFactory arguments
TInterfaceFactoryPerArgumentDynArray =
array[TInterfaceMethodValueType] of TInterfaceFactoryArgumentDynArray;
/// a dynamic array of TInterfaceFactory instances
TInterfaceFactoryObjArray = array of TInterfaceFactory;
/// class handling interface RTTI and fake implementation class
// - an internal JIT compiler will generate the raw asm opcodes to redirect
// any interface execution into a fake class
// - a thread-safe global list of such class instances is implemented to cache
// information for better speed: use class function TInterfaceFactory.Get()
// and not manual TInterfaceFactory.Create / Free
// - if you want to search the interfaces by name or TGuid, call once
// Get(TypeInfo(IMyInterface)) or RegisterInterfaces() for proper registration
// - will use TInterfaceFactoryRtti classes generated from compiler RTTI
TInterfaceFactory = class
protected
fInterfaceRtti: TRttiJson;
fMethods: TInterfaceMethodDynArray;
fInterfaceName: RawUtf8;
fInterfaceUri: RawUtf8;
fDocVariantOptions: TDocVariantOptions; // (16-bit)
fJsonParserOptions: TJsonParserOptions; // (16-bit)
fMethodsCount: byte;
fAddMethodsLevel: byte;
fMethodIndexCallbackReleased: ShortInt; // (8-bit)
fMethodIndexCurrentFrameCallback: ShortInt;
fArgUsed: TInterfaceFactoryPerArgumentDynArray;
// contains e.g. [{"method":"Add","arguments":[...]},{"method":"...}]
fContract: RawUtf8;
{$ifdef CPUX86} // i386 stub requires "ret ArgsSizeInStack"
fFakeVTable: TPointerDynArray;
{$endif CPUX86}
procedure AddMethodsFromTypeInfo(aInterface: PRttiInfo); virtual; abstract;
// low-level JIT redirection of the VMT to TInterfacedObjectFake.FakeCall
function GetMethodsVirtualTable: pointer;
public
/// this is the main entry point to the global interface factory cache
// - access to this method is thread-safe
// - this method will also register the class to further retrieval
class function Get(aInterface: PRttiInfo): TInterfaceFactory; overload;
/// retrieve an interface factory from cache, from its TGuid
// - access to this method is thread-safe
// - you shall have registered the interface by a previous call to the
// overloaded Get(TypeInfo(IMyInterface)) method or RegisterInterfaces()
// - if the supplied TGuid has not been previously registered, returns nil
class function Get({$ifdef FPC_HAS_CONSTREF}constref{$else}const{$endif}
aGuid: TGuid): TInterfaceFactory; overload;
/// retrieve an interface factory from cache, from its name (e.g. 'IMyInterface')
// - access to this method is thread-safe
// - you shall have registered the interface by a previous call to the
// overloaded Get(TypeInfo(IMyInterface)) method or RegisterInterfaces()
// - if the supplied TGuid has not been previously registered, returns nil
class function Get(const aInterfaceName: RawUtf8): TInterfaceFactory; overload;
/// register one or several interfaces to the global interface factory cache
// - so that you can use TInterfaceFactory.Get(aGuid) or Get(aName)
class procedure RegisterInterfaces(const aInterfaces: array of PRttiInfo);
/// could be used to retrieve an array of TypeInfo() from their Guid
class function Guid2TypeInfo(const aGuids: array of TGuid): PRttiInfoDynArray; overload;
/// could be used to retrieve an array of TypeInfo() from their Guid
class function Guid2TypeInfo({$ifdef FPC_HAS_CONSTREF}constref{$else}const{$endif}
aGuid: TGuid): PRttiInfo; overload;
/// returns the list of all declared TInterfaceFactory
// - as used by SOA and mocking/stubing features of this unit
class function GetUsedInterfaces: TSynObjectListLightLocked;
/// add some TInterfaceFactory instances from their Guid
class procedure AddToObjArray(var Obj: TInterfaceFactoryObjArray;
const aGuids: array of TGuid);
/// register some TypeInfo() containing unsafe parameter values
// - i.e. any RTTI type containing Sensitive Personal Information, e.g.
// a bank card number or a plain password
// - such values will force associated values to be ignored during loging,
// as a more tuned alternative to optNoLogInput or optNoLogOutput
class procedure RegisterUnsafeSpiType(const Types: array of PRttiInfo);
/// initialize the internal properties from the supplied interface RTTI
// - it will check and retrieve all methods of the supplied interface,
// and prepare all internal structures for later use
// - do not call this constructor directly, but TInterfaceFactory.Get()
constructor Create(aInterface: PRttiInfo);
/// find the index of a particular URI in internal Methods[] list
// - will search for a match against Methods[].Uri property
// - won't find the default AddRef/Release/QueryInterface methods,
// nor the _free_/_instance_/... pseudo-methods
// - will return -1 if the method is not known
// - if aUrl does not have an exact method match, it will try with a
// trailing underscore, so that e.g. /service/start will match IService._Start()
function FindMethodIndex(const aUrl: RawUtf8): PtrInt;
/// find the index of a particular method in internal Methods[] list
// - without accepting /service/start for IService._Start()
function FindMethodIndexExact(const aMethodName: RawUtf8): PtrInt;
/// find a particular method in internal Methods[] list
// - just a wrapper around FindMethodIndex() returing a PInterfaceMethod
// - will return nil if the method is not known
function FindMethod(const aUrl: RawUtf8): PInterfaceMethod;
/// find the index of a particular interface.method in internal Methods[] list
// - will search for a match against Methods[].InterfaceDotMethodName property
// - won't find the default AddRef/Release/QueryInterface methods
// - will return -1 if the method is not known
function FindFullMethodIndex(const aFullMethodName: RawUtf8;
alsoSearchExactMethodName: boolean = false): integer;
/// find the index of a particular method in internal Methods[] list
// - won't find the default AddRef/Release/QueryInterface methods
// - will raise an EInterfaceFactory if the method is not known
function CheckMethodIndex(const aUrl: RawUtf8): PtrInt; overload;
/// find the index of a particular method in internal Methods[] list
// - won't find the default AddRef/Release/QueryInterface methods
// - will raise an EInterfaceFactory if the method is not known
function CheckMethodIndex(aUrl: PUtf8Char): integer; overload;
/// returns the method name from its method index
// - the method index should start at 0 for _free_/_contract_/_signature_
// pseudo-methods, and start at index 3 for real Methods[]
function GetMethodName(aMethodIndex: integer): RawUtf8;
/// set the Methods[] indexes bit from some methods names
// - won't find the default AddRef/Release/QueryInterface methods
// - will raise an EInterfaceFactory if the method is not known
procedure CheckMethodIndexes(const aUrl: array of RawUtf8;
aSetAllIfNone: boolean; out aBits: TInterfaceFactoryMethodBits);
/// returns the full 'Interface.MethodName' text, from a method index
// - the method index should start at 0 for _free_/_contract_/_signature_
// pseudo-methods, and start at index 3 for real Methods[]
// - will return plain 'Interface' text, if aMethodIndex is incorrect
function GetFullMethodName(aMethodIndex: integer): RawUtf8;
/// the declared internal methods
// - list does not contain default AddRef/Release/QueryInterface methods
// - nor the _free_/_contract_/_signature_ pseudo-methods
property Methods: TInterfaceMethodDynArray
read fMethods;
/// the number of internal methods
// - does not include the default AddRef/Release/QueryInterface methods
// - nor the _free_/_contract_/_signature_ pseudo-methods: so you should
// add SERVICE_PSEUDO_METHOD_COUNT to compute the regular MethodIndex
property MethodsCount: byte
read fMethodsCount;
/// reference all known interface arguments per value type
property ArgUsed: TInterfaceFactoryPerArgumentDynArray
read fArgUsed;
/// identifies a CallbackReleased() method in this interface
// - i.e. the index in Methods[] of the following signature:
// ! procedure CallbackReleased(const callback: IInvokable; const interfaceName: RawUtf8);
// - this method will be called e.g. by TInterfacedCallback.Destroy, when
// a callback is released on the client side so that you may be able e.g. to
// unsubscribe the callback from an interface list (via InterfaceArrayDelete)
// - contains -1 if no such method do exist in the interface definition
property MethodIndexCallbackReleased: ShortInt
read fMethodIndexCallbackReleased;
/// identifies a CurrentFrame() method in this interface
// - i.e. the index in Methods[] of the following signature:
// ! procedure CurrentFrame(isLast: boolean);
// - this method will be called e.g. by TRestHttpClientWebsockets.CallbackRequest
// for interface callbacks in case of WebSockets jumbo frames, to allow e.g.
// faster database access via a batch
// - contains -1 if no such method do exist in the interface definition
property MethodIndexCurrentFrameCallback: ShortInt
read fMethodIndexCurrentFrameCallback;
/// the interface name, without its initial 'I'
// - e.g. ICalculator -> 'Calculator'
property InterfaceUri: RawUtf8
read fInterfaceUri write fInterfaceUri;
/// the registered Interface high-level compiler RTTI type
property InterfaceRtti: TRttiJson
read fInterfaceRtti;
/// the interface TGUID, as stored in the RTTI
function InterfaceGuid: PGuid;
{$ifdef HASINLINE} inline; {$endif}
/// the service contract as a JSON array
property Contract: RawUtf8
read fContract;
/// how this interface will work with variants (including TDocVariant)
// - by default, contains JSON_FAST_FLOAT for best performance - i.e.
// [dvoReturnNullForUnknownProperty,dvoValueCopiedByReference, dvoAllowDoubleValue]
property DocVariantOptions: TDocVariantOptions
read fDocVariantOptions write fDocVariantOptions;
/// how this interface will process its JSON parsing
// - by default, contains JSONPARSER_SERVICE very relaxed parsing options
property JsonParserOptions: TJsonParserOptions
read fJsonParserOptions write fJsonParserOptions;
published
/// will return the interface name, e.g. 'ICalculator'
// - published property to be serializable as JSON e.g. for debbuging info
property InterfaceName: RawUtf8
read fInterfaceName;
end;
PInterfaceFactory = ^TInterfaceFactory;
{$ifdef HASINTERFACERTTI}
/// class handling interface RTTI and fake implementation class
// - this class only exists for Delphi 6 and up, and newer FPC, which has
// the expected RTTI - see http://bugs.freepascal.org/view.php?id=26774
TInterfaceFactoryRtti = class(TInterfaceFactory)
protected
procedure AddMethodsFromTypeInfo(aInterface: PRttiInfo); override;
end;
{$endif HASINTERFACERTTI}
/// class handling interface implementation generated from source
// - this class targets oldest FPC, which did not generate the expected RTTI -
// see http://bugs.freepascal.org/view.php?id=26774
// - mormot.soa.codegen.pas will generate a new inherited class, overriding
// abstract AddMethodsFromTypeInfo() to define the interface methods
TInterfaceFactoryGenerated = class(TInterfaceFactory)
protected
fTempStrings: TRawUtf8DynArray;
/// the overriden AddMethodsFromTypeInfo() method will call e.g. as
// ! AddMethod('Add',[
// ! 0,'n1',TypeInfo(integer),
// ! 0,'n2',TypeInfo(integer),
// ! 3,'result',TypeInfo(integer)]);
// with 0=ord(imdConst) and 3=ord(imdResult)
procedure AddMethod(const aName: RawUtf8; const aParams: array of const); virtual;
public
/// register one interface type definition from the current class
// - will be called by mormot.soa.codegen generated code, in initialization
// section, so that the needed type information will be available
class procedure RegisterInterface(aInterface: PRttiInfo); virtual;
end;
/// a record type to be used as result for a function method for custom content
// for interface-based services
// - all answers are pure JSON object by default: using this kind of record
// as result will allow a response of any type (e.g. binary, HTML or text)
// - this kind of answer will be understood by our TServiceContainerClient
// implementation, and it may be used with plain AJAX or HTML requests
// (via POST), to retrieve some custom content
TServiceCustomAnswer = record
/// mandatory response type, as encoded in the HTTP header
// - set the response mime-type - use e.g. JSON_CONTENT_TYPE_HEADER_VAR
// TEXT_CONTENT_TYPE_HEADER or BINARY_CONTENT_TYPE_HEADER constants or
// GetMimeContentType() function
// - if this field is not set, then JSON_CONTENT_TYPE_HEADER will be forced
Header: RawUtf8;
/// the response body
// - corresponding to the response type, as defined in Header
Content: RawByteString;
/// the HTTP response code
// - if not overriden, will default to HTTP_SUCCESS = 200 on server side
// - on client side, will always contain HTTP_SUCCESS = 200 on success,
// or any error should be handled as expected by the caller (e.g. using
// TServiceFactoryClient.GetErrorMessage for decoding REST/SOA errors)
Status: cardinal;
end;
PServiceCustomAnswer = ^TServiceCustomAnswer;
/// an integer type to be used as result for a function method to customize
// the HTTP response code for interface-based services
// - by default, our protocol returns HTTP_SUCCESS = 200 for any process
// - using this type as result allow to return the execution error code as a
// regular HTTP_* response code, in addition to the regular JSON answer - i.e.
// there will be a "result" member in the transmitted JSON anyway
// - the returned value should be in HTTP response code range, i.e. 200..599
// - by design, HTTP_NOCONTENT can/should not be used: return HTTP_SUCCESS and
// set rsoHttp200WithNoBodyReturns204 option to let TRestServer.Uri decide and
// return HTTP_SUCCESS if there is an output body, or HTTP_NOCONTENT if void
TServiceCustomStatus = type cardinal;
/// returns the interface name of a registered Guid, or its hexadecimal value
function ToText({$ifdef FPC_HAS_CONSTREF}constref{$else}const{$endif}
aGuid: TGuid): ShortString; overload;
{ ************ TInterfaceResolver TInjectableObject for IoC / Dependency Injection }
type
/// exception raised in case of Dependency Injection (aka IoC) issue
EInterfaceResolver = class(ESynException);
{$M+}
/// abstract factory class allowing to call interface resolution in cascade
// - you can inherit from this class to chain the TryResolve() calls so
// that several kind of implementations may be asked by a TInjectableObject,
// e.g. TInterfaceStub, TServiceContainer or TDDDRepositoryRestObjectMapping
// - this will implement factory pattern, as a safe and thread-safe DI/IoC
TInterfaceResolver = class
protected
/// override this method to resolve an interface from this instance
function TryResolve(aInterface: PRttiInfo; out Obj): boolean; virtual; abstract;
public
/// override this method check if this instance implements aInterface RTTI
// - this default implementation will call TryResolve() on a local IInterface
// which is somewhat slow, and should better be overriden
function Implements(aInterface: PRttiInfo): boolean; virtual;
/// can be used to perform an DI/IoC for a given interface
// - will search for the supplied interface to its internal list of resolvers
// - returns TRUE and set the Obj variable with a matching instance
// - can be used as such to resolve an ICalculator interface:
// ! var calc: ICalculator;
// ! begin
// ! if Catalog.Resolve(TypeInfo(ICalculator),calc) then
// ! ... use calc methods
function Resolve(aInterface: PRttiInfo; out Obj): boolean; overload;
/// can be used to perform an DI/IoC for a given interface
// - you shall have registered the interface TGuid by a previous call to
// ! TInterfaceFactory.RegisterInterfaces([TypeInfo(ICalculator),...])
// - returns TRUE and set the Obj variable with a matching instance
// - returns FALSE (or raise aRaiseIfNotFound) if aGuid is not available
// - can be used as such to resolve an ICalculator interface:
// ! var calc: ICalculator;
// ! begin
// ! if ServiceContainer.Resolve(ICalculator,cal) then
// ! ... use calc methods
function Resolve({$ifdef FPC_HAS_CONSTREF}constref{$else}const{$endif}aGuid: TGuid;
out Obj; aRaiseIfNotFound: ESynExceptionClass = nil): boolean; overload;
/// can be used to perform several DI/IoC for a given set of interfaces
// - here interfaces and instances are provided as TypeInfo,@Instance pairs
// - raise an EServiceException if any interface can't be resolved, unless
// aRaiseExceptionIfNotFound is set to FALSE
procedure ResolveByPair(const aInterfaceObjPairs: array of pointer;
aRaiseExceptionIfNotFound: boolean = true);
/// can be used to perform several DI/IoC for a given set of interfaces
// - here interfaces and instances are provided as TGuid and @Instance
// - you shall have registered the interface TGuid by a previous call to
// ! TInterfaceFactory.RegisterInterfaces([TypeInfo(ICalculator),...])
// - raise an EServiceException if any interface can't be resolved, unless
// aRaiseExceptionIfNotFound is set to FALSE
procedure Resolve(const aInterfaces: array of TGuid;
const aObjs: array of pointer;
aRaiseExceptionIfNotFound: boolean = true); overload;
end;
{$M-}
/// used to store a list of TInterfacedObject instances
TInterfacedObjectObjArray = array of TInterfacedObject;
/// used to store a list of TInterfaceResolver instances
TInterfaceResolverObjArray = array of TInterfaceResolver;
/// abstract factory class targetting a single kind of interface
TInterfaceResolverForSingleInterface = class(TInterfaceResolver)
protected
fInterfaceTypeInfo: PRttiInfo;
fInterfaceAncestors: PRttiInfoDynArray;
fInterfaceAncestorsImplementationEntry: TPointerDynArray;
fImplementationEntry: PInterfaceEntry;
fImplementation: TRttiCustom;
function TryResolve(aInterface: PRttiInfo; out Obj): boolean; override;
function GetImplementationName: RawUtf8;
// main IoC/DI virtual method - call fImplementation.CreateNew by default
function CreateInstance: TInterfacedObject; virtual;
public
/// this overriden constructor will check and store the supplied class
// to implement an interface
constructor Create(aInterface: PRttiInfo;
aImplementation: TInterfacedObjectClass); overload;
/// this overriden constructor will check and store the supplied class
// to implement an interface by TGuid
constructor Create(const aInterface: TGuid;
aImplementation: TInterfacedObjectClass); overload;
/// you can use this method to resolve the interface as a new instance
function GetOneInstance(out Obj): boolean;
/// check if can resolve the supplied interface RTTI
function Implements(aInterface: PRttiInfo): boolean; override;
published
/// the class name which will implement each repository instance
property ImplementationClass: RawUtf8
read GetImplementationName;
end;
type
/// how TInterfaceResolverList store one interface/class
TInterfaceResolverListEntry = record
/// contains TypeInfo(ISomeInterface)
TypeInfo: PRttiInfo;
/// the associated RTTI - mainly used to call its ClassNewInstance method
ImplementationClass: TRttiCustom;
/// low-level interface VMT information for fast creation
InterfaceEntry: PInterfaceEntry;
/// shared instance
// - will be released with the TInterfaceResolverListEntries array
Instance: IInterface;
end;
PInterfaceResolverListEntry = ^TInterfaceResolverListEntry;
/// how TInterfaceResolverList store one interface/class
TInterfaceResolverListEntries = array of TInterfaceResolverListEntry;
/// event signature used by TInterfaceResolverList.OnCreateInstance
TOnResolverCreateInstance = procedure(
Sender: TInterfaceResolver; Instance: TInterfacedObject) of object;
/// register a thread-safe list of classes to implement some interfaces
// - as used e.g. by TInterfaceResolverInjected.RegisterGlobal()
TInterfaceResolverList = class(TInterfaceResolver)
protected
fEntry: TInterfaceResolverListEntries;
fSafe: TRWLightLock;
fOnCreateInstance: TOnResolverCreateInstance;
function PrepareAddAndWriteLock(aInterface: PRttiInfo;
aImplementationClass: TClass): PInterfaceEntry; // fSafe.WriteUnLock after
function TryResolve(aInterface: PRttiInfo; out Obj): boolean; override;
public
/// check if a given interface can be resolved, from its RTTI
function Implements(aInterface: PRttiInfo): boolean; override;
/// register a given implementaiton class for an interface
// - a new aImplementationClass instance will be created for each resolution
procedure Add(aInterface: PRttiInfo;
aImplementationClass: TInterfacedObjectClass); overload;
/// register a given implementation class instance for an interface
// - the shared aImplementation instance will be returned for each resolution
// - aImplementation will be owned by the internal registration list
procedure Add(aInterface: PRttiInfo;
aImplementation: TInterfacedObject); overload;
/// unregister a given implementation class for an interface
// - raise EInterfaceResolver if an TInterfacedObject instance was registered
procedure Delete(aInterface: PRttiInfo);
/// is called when a new aImplementationClass instance has been created
property OnCreateInstance: TOnResolverCreateInstance
read fOnCreateInstance write fOnCreateInstance;
/// low-level access to the internal registered interface/class list
// - should be protected via the Safe locking methods
property Entry: TInterfaceResolverListEntries
read fEntry;
/// low-level access to the internal lock for thread-safety
property Safe: TRWLightLock
read fSafe;
end;
/// abstract factory class targetting any kind of interface
// - you can inherit from this class to customize dependency injection (DI/IoC),
// defining the resolution via InjectStub/InjectResolver/InjectInstance methods,
// and doing the instance resolution using the overloaded Resolve*() methods
// - TServiceContainer will inherit from this class, as the main entry point
// for interface-based services of the framework (via TRest.Services)
// - you can use RegisterGlobal() class method to define some process-wide DI
TInterfaceResolverInjected = class(TInterfaceResolver)
protected
fResolvers: TInterfaceResolverObjArray;
fResolversToBeReleased: TInterfaceResolverObjArray;
fDependencies: TInterfacedObjectObjArray;
function TryResolve(aInterface: PRttiInfo; out Obj): boolean; override;
public
/// define a global class type for interface resolution
// - most of the time, you will need a local DI/IoC resolution list; but
// you may use this method to register a set of shared and global resolution
// patterns, common to the whole injection process
// - by default, TAutoLocker and TLockedDocVariant will be registered by
// this unit to implement IAutoLocker and ILockedDocVariant interfaces
class procedure RegisterGlobal(aInterface: PRttiInfo;
aImplementationClass: TInterfacedObjectClass); overload;
/// define a global instance for interface resolution
// - most of the time, you will need a local DI/IoC resolution list; but
// you may use this method to register a set of shared and global resolution
// patterns, common to the whole injection process
// - the supplied instance will be owned by the global list (incrementing
// its internal reference count), until it will be released via
// ! RegisterGlobalDelete()
// - the supplied instance will be freed in the finalization of this unit,
// if not previously released via RegisterGlobalDelete()
class procedure RegisterGlobal(aInterface: PRttiInfo;
aImplementation: TInterfacedObject); overload;
/// undefine a global instance for interface resolution
// - you can unregister a given instance previously defined via
// ! RegisterGlobal(aInterface,aImplementation)
// - if you do not call RegisterGlobalDelete(), the remaning instances will
// be freed in the finalization of this unit
class procedure RegisterGlobalDelete(aInterface: PRttiInfo);
/// prepare and setup interface DI/IoC resolution with some blank
// TInterfaceStub specified by their TGuid
procedure InjectStub(const aStubsByGuid: array of TGuid); overload; virtual;
/// prepare and setup interface DI/IoC resolution with TInterfaceResolver
// kind of factory
// - e.g. a customized TInterfaceStub/TInterfaceMock, a TServiceContainer,
// a TDDDRepositoryRestObjectMapping or any factory class
// - by default, only TInterfaceStub/TInterfaceMock will be owned by this
// instance, and released by Destroy - unless you set OwnOtherResolvers
procedure InjectResolver(const aOtherResolvers: array of TInterfaceResolver;
OwnOtherResolvers: boolean = false); overload; virtual;
/// prepare and setup interface DI/IoC resolution from a TInterfacedObject instance
// - any TInterfacedObject declared as dependency will have its reference
// count increased, and decreased in Destroy
procedure InjectInstance(const aDependencies: array of TInterfacedObject);
overload; virtual;
/// check if a given interface can be resolved, from its RTTI