瀏覽代碼

+ implement the high level Invoke() for TRttiMethod (currently only for interface methods), TRttiMethodType and TRttiProcedureType

git-svn-id: trunk@39893 -
svenbarth 6 年之前
父節點
當前提交
afc83b9f70
共有 2 個文件被更改,包括 1433 次插入0 次删除
  1. 198 0
      packages/rtl-objpas/src/inc/rtti.pp
  2. 1235 0
      packages/rtl-objpas/tests/tests.rtti.invoke.pas

+ 198 - 0
packages/rtl-objpas/src/inc/rtti.pp

@@ -303,6 +303,7 @@ type
     function GetParameters: specialize TArray<TRttiParameter>; inline;
     function GetParameters: specialize TArray<TRttiParameter>; inline;
     property CallingConvention: TCallConv read GetCallingConvention;
     property CallingConvention: TCallConv read GetCallingConvention;
     property ReturnType: TRttiType read GetReturnType;
     property ReturnType: TRttiType read GetReturnType;
+    function Invoke(const aProcOrMeth: TValue; const aArgs: array of TValue): TValue; virtual; abstract;
   end;
   end;
 
 
   TRttiMethodType = class(TRttiInvokableType)
   TRttiMethodType = class(TRttiInvokableType)
@@ -314,6 +315,8 @@ type
     function GetParameters(aWithHidden: Boolean): specialize TArray<TRttiParameter>; override;
     function GetParameters(aWithHidden: Boolean): specialize TArray<TRttiParameter>; override;
     function GetCallingConvention: TCallConv; override;
     function GetCallingConvention: TCallConv; override;
     function GetReturnType: TRttiType; override;
     function GetReturnType: TRttiType; override;
+  public
+    function Invoke(const aCallable: TValue; const aArgs: array of TValue): TValue; override;
   end;
   end;
 
 
   TRttiProcedureType = class(TRttiInvokableType)
   TRttiProcedureType = class(TRttiInvokableType)
@@ -323,6 +326,8 @@ type
     function GetParameters(aWithHidden: Boolean): specialize TArray<TRttiParameter>; override;
     function GetParameters(aWithHidden: Boolean): specialize TArray<TRttiParameter>; override;
     function GetCallingConvention: TCallConv; override;
     function GetCallingConvention: TCallConv; override;
     function GetReturnType: TRttiType; override;
     function GetReturnType: TRttiType; override;
+  public
+    function Invoke(const aCallable: TValue; const aArgs: array of TValue): TValue; override;
   end;
   end;
 
 
   TDispatchKind = (
   TDispatchKind = (
@@ -365,6 +370,9 @@ type
     property VirtualIndex: SmallInt read GetVirtualIndex;
     property VirtualIndex: SmallInt read GetVirtualIndex;
     function ToString: String; override;
     function ToString: String; override;
     function GetParameters: specialize TArray<TRttiParameter>; inline;
     function GetParameters: specialize TArray<TRttiParameter>; inline;
+    function Invoke(aInstance: TObject; const aArgs: array of TValue): TValue;
+    function Invoke(aInstance: TClass; const aArgs: array of TValue): TValue;
+    function Invoke(aInstance: TValue; const aArgs: array of TValue): TValue;
   end;
   end;
 
 
   TRttiStructuredType = class(TRttiType)
   TRttiStructuredType = class(TRttiType)
@@ -631,12 +639,28 @@ resourcestring
   SErrInvalidTypecast         = 'Invalid class typecast';
   SErrInvalidTypecast         = 'Invalid class typecast';
   SErrRttiObjectNoHandle      = 'RTTI object instance has no valid handle property';
   SErrRttiObjectNoHandle      = 'RTTI object instance has no valid handle property';
   SErrRttiObjectAlreadyRegistered = 'A RTTI object with handle 0x%x is already registered';
   SErrRttiObjectAlreadyRegistered = 'A RTTI object with handle 0x%x is already registered';
+  SErrInvokeInsufficientRtti  = 'Insufficient RTTI to invoke function';
+  SErrInvokeStaticNoSelf      = 'Static function must not be called with in an instance: %s';
+  SErrInvokeNotStaticNeedsSelf = 'Non static function must be called with an instance: %s';
+  SErrInvokeClassMethodClassSelf = 'Class method needs to be called with a class type: %s';
+  SErrInvokeArrayArgExpected  = 'Array argument expected for parameter %s of method %s';
+  SErrInvokeArgInvalidType    = 'Invalid type of argument for parameter %s of method %s';
+  SErrInvokeArgCount          = 'Invalid argument count for method %s; expected %d, but got %d';
+  SErrInvokeNoCodeAddr        = 'Failed to determine code address for method: %s';
+  SErrInvokeRttiDataError     = 'The RTTI data is inconsistent for method: %s';
+  SErrInvokeCallableNotProc   = 'The callable value is not a procedure variable for: %s';
+  SErrInvokeCallableNotMethod = 'The callable value is not a method variable for: %s';
 
 
 var
 var
   PoolRefCount : integer;
   PoolRefCount : integer;
   GRttiPool    : TRttiPool;
   GRttiPool    : TRttiPool;
   FuncCallMgr: TFunctionCallManagerArray;
   FuncCallMgr: TFunctionCallManagerArray;
 
 
+function CCToStr(aCC: TCallConv): String; inline;
+begin
+  WriteStr(Result, aCC);
+end;
+
 procedure NoInvoke(aCodeAddress: CodePointer; const aArgs: TFunctionCallParameterArray; aCallConv: TCallConv;
 procedure NoInvoke(aCodeAddress: CodePointer; const aArgs: TFunctionCallParameterArray; aCallConv: TCallConv;
             aResultType: PTypeInfo; aResultValue: Pointer; aFlags: TFunctionCallFlags);
             aResultType: PTypeInfo; aResultValue: Pointer; aFlags: TFunctionCallFlags);
 begin
 begin
@@ -801,6 +825,108 @@ begin
   FuncCallMgr[aCallConv].Invoke(aCodeAddress, funcargs, aCallConv, aResultType, Result.GetReferenceToRawData, flags);
   FuncCallMgr[aCallConv].Invoke(aCodeAddress, funcargs, aCallConv, aResultType, Result.GetReferenceToRawData, flags);
 end;
 end;
 
 
+function Invoke(const aName: String; aCodeAddress: CodePointer; aCallConv: TCallConv; aStatic: Boolean; aInstance: TValue; constref aArgs: array of TValue; const aParams: specialize TArray<TRttiParameter>; aReturnType: TRttiType): TValue;
+var
+  arrparam, param: TRttiParameter;
+  unhidden, highs, i: SizeInt;
+  args: TFunctionCallParameterArray;
+  highargs: array of SizeInt;
+  restype: PTypeInfo;
+  resptr: Pointer;
+  mgr: TFunctionCallManager;
+  flags: TFunctionCallFlags;
+begin
+  mgr := FuncCallMgr[aCallConv];
+  if not Assigned(mgr.Invoke) then
+    raise EInvocationError.CreateFmt(SErrCallConvNotSupported, [CCToStr(aCallConv)]);
+
+  if not Assigned(aCodeAddress) then
+    raise EInvocationError.CreateFmt(SErrInvokeNoCodeAddr, [aName]);
+
+  unhidden := 0;
+  highs := 0;
+  for param in aParams do begin
+    if unhidden < Length(aArgs) then begin
+      if pfArray in param.Flags then begin
+        if Assigned(aArgs[unhidden].TypeInfo) and not aArgs[unhidden].IsArray and (aArgs[unhidden].Kind <> param.ParamType.TypeKind) then
+          raise EInvocationError.CreateFmt(SErrInvokeArrayArgExpected, [param.Name, aName]);
+      end else if not (pfHidden in param.Flags) then begin
+        if aArgs[unhidden].Kind <> param.ParamType.TypeKind then
+          raise EInvocationError.CreateFmt(SErrInvokeArgInvalidType, [param.Name, aName]);
+      end;
+    end;
+    if not (pfHidden in param.Flags) then
+      Inc(unhidden);
+    if pfHigh in param.Flags then
+      Inc(highs);
+  end;
+
+  if unhidden <> Length(aArgs) then
+    raise EInvocationError.CreateFmt(SErrInvokeArgCount, [aName, unhidden, Length(aArgs)]);
+
+  if Assigned(aReturnType) then begin
+    TValue.Make(Nil, aReturnType.FTypeInfo, Result);
+    resptr := Result.GetReferenceToRawData;
+    restype := aReturnType.FTypeInfo;
+  end else begin
+    Result := TValue.Empty;
+    resptr := Nil;
+    restype := Nil;
+  end;
+
+  SetLength(highargs, highs);
+  SetLength(args, Length(aParams));
+  unhidden := 0;
+  highs := 0;
+
+  for i := 0 to High(aParams) do begin
+    param := aParams[i];
+    args[i].Info.ParamType := param.ParamType.FTypeInfo;
+    args[i].Info.ParamFlags := param.Flags;
+    args[i].Info.ParaLocs := Nil;
+
+    if pfHidden in param.Flags then begin
+      if pfSelf in param.Flags then
+        args[i].ValueRef := aInstance.GetReferenceToRawData
+      else if pfResult in param.Flags then begin
+        if not Assigned(restype) then
+          raise EInvocationError.CreateFmt(SErrInvokeRttiDataError, [aName]);
+        args[i].ValueRef := resptr;
+        restype := Nil;
+        resptr := Nil;
+      end else if pfHigh in param.Flags then begin
+        { the corresponding array argument is the *previous* unhidden argument }
+        if aArgs[unhidden - 1].IsArray then
+          highargs[highs] := aArgs[unhidden - 1].GetArrayLength - 1
+        else if not Assigned(aArgs[unhidden - 1].TypeInfo) then
+          highargs[highs] := -1
+        else
+          highargs[highs] := 0;
+        args[i].ValueRef := @highargs[highs];
+        Inc(highs);
+      end;
+    end else begin
+      if (pfArray in param.Flags) then begin
+        if not Assigned(aArgs[unhidden].TypeInfo) then
+          args[i].ValueRef := Nil
+        else if aArgs[unhidden].Kind = tkDynArray then
+          args[i].ValueRef := PPointer(aArgs[unhidden].GetReferenceToRawData)^
+        else
+          args[i].ValueRef := aArgs[unhidden].GetReferenceToRawData;
+      end else
+        args[i].ValueRef := aArgs[unhidden].GetReferenceToRawData;
+
+      Inc(unhidden);
+    end;
+  end;
+
+  flags := [];
+  if aStatic then
+    Include(flags, fcfStatic);
+
+  mgr.Invoke(aCodeAddress, args, aCallConv, restype, resptr, flags);
+end;
+
 function CreateCallbackProc(aHandler: TFunctionCallProc; aCallConv: TCallConv; aArgs: array of PTypeInfo; aResultType: PTypeInfo; aFlags: TFunctionCallFlags; aContext: Pointer): TFunctionCallCallback;
 function CreateCallbackProc(aHandler: TFunctionCallProc; aCallConv: TCallConv; aArgs: array of PTypeInfo; aResultType: PTypeInfo; aFlags: TFunctionCallFlags; aContext: Pointer): TFunctionCallCallback;
 begin
 begin
   if not Assigned(FuncCallMgr[aCallConv].CreateCallbackProc) then
   if not Assigned(FuncCallMgr[aCallConv].CreateCallbackProc) then
@@ -2324,6 +2450,54 @@ begin
   Result := GetParameters(False);
   Result := GetParameters(False);
 end;
 end;
 
 
+function TRttiMethod.Invoke(aInstance: TObject; const aArgs: array of TValue): TValue;
+var
+  instance: TValue;
+begin
+  TValue.Make(@aInstance, TypeInfo(TObject), instance);
+  Result := Invoke(instance, aArgs);
+end;
+
+function TRttiMethod.Invoke(aInstance: TClass; const aArgs: array of TValue): TValue;
+var
+  instance: TValue;
+begin
+  TValue.Make(@aInstance, TypeInfo(TClass), instance);
+  Result := Invoke(instance, aArgs);
+end;
+
+function TRttiMethod.Invoke(aInstance: TValue; const aArgs: array of TValue): TValue;
+var
+  addr: CodePointer;
+  vmt: PCodePointer;
+begin
+  if not HasExtendedInfo then
+    raise EInvocationError.Create(SErrInvokeInsufficientRtti);
+
+  if IsStatic and not aInstance.IsEmpty then
+    raise EInvocationError.CreateFmt(SErrInvokeStaticNoSelf, [Name]);
+
+  if not IsStatic and aInstance.IsEmpty then
+    raise EInvocationError.CreateFmt(SErrInvokeNotStaticNeedsSelf, [Name]);
+
+  if not IsStatic and IsClassMethod and not aInstance.IsClass then
+    raise EInvocationError.CreateFmt(SErrInvokeClassMethodClassSelf, [Name]);
+
+  addr := Nil;
+  if IsStatic then
+    addr := CodeAddress
+  else begin
+    vmt := Nil;
+    if aInstance.Kind in [tkInterface, tkInterfaceRaw] then
+      vmt := PCodePointer(PPPointer(aInstance.GetReferenceToRawData)^^);
+    { ToDo }
+    if Assigned(vmt) then
+      addr := vmt[VirtualIndex];
+  end;
+
+  Result := Rtti.Invoke(Name, addr, CallingConvention, IsStatic, aInstance, aArgs, GetParameters(True), ReturnType);
+end;
+
 { TRttiInvokableType }
 { TRttiInvokableType }
 
 
 function TRttiInvokableType.GetParameters: specialize TArray<TRttiParameter>;
 function TRttiInvokableType.GetParameters: specialize TArray<TRttiParameter>;
@@ -2445,6 +2619,22 @@ begin
     Result := Nil;
     Result := Nil;
 end;
 end;
 
 
+function TRttiMethodType.Invoke(const aCallable: TValue; const aArgs: array of TValue): TValue;
+var
+  method: PMethod;
+  inst: TValue;
+begin
+  if aCallable.Kind <> tkMethod then
+    raise EInvocationError.CreateFmt(SErrInvokeCallableNotMethod, [Name]);
+
+  method := PMethod(aCallable.GetReferenceToRawData);
+
+  { by using a pointer we can also use this for non-class instance methods }
+  TValue.Make(@method^.Data, PTypeInfo(TypeInfo(Pointer)), inst);
+
+  Result := Rtti.Invoke(Name, method^.Code, CallingConvention, False, inst, aArgs, GetParameters(True), ReturnType);
+end;
+
 { TRttiProcedureType }
 { TRttiProcedureType }
 
 
 function TRttiProcedureType.GetParameters(aWithHidden: Boolean): specialize TArray<TRttiParameter>;
 function TRttiProcedureType.GetParameters(aWithHidden: Boolean): specialize TArray<TRttiParameter>;
@@ -2517,6 +2707,14 @@ begin
   end;
   end;
 end;
 end;
 
 
+function TRttiProcedureType.Invoke(const aCallable: TValue; const aArgs: array of TValue): TValue;
+begin
+  if aCallable.Kind <> tkProcVar then
+    raise EInvocationError.CreateFmt(SErrInvokeCallableNotProc, [Name]);
+
+  Result := Rtti.Invoke(Name, PCodePointer(aCallable.GetReferenceToRawData)^, CallingConvention, True, TValue.Empty, aArgs, GetParameters(True), ReturnType);
+end;
+
 { TRttiStringType }
 { TRttiStringType }
 
 
 function TRttiStringType.GetStringKind: TRttiStringKind;
 function TRttiStringType.GetStringKind: TRttiStringKind;

+ 1235 - 0
packages/rtl-objpas/tests/tests.rtti.invoke.pas

@@ -17,6 +17,10 @@ uses
   sysutils, typinfo, Rtti;
   sysutils, typinfo, Rtti;
 
 
 type
 type
+{$ifndef fpc}
+  CodePointer = Pointer;
+{$endif}
+
   TTestInvoke = class(TTestCase)
   TTestInvoke = class(TTestCase)
   private type
   private type
     TInvokeFlag = (
     TInvokeFlag = (
@@ -25,10 +29,20 @@ type
     );
     );
     TInvokeFlags = set of TInvokeFlag;
     TInvokeFlags = set of TInvokeFlag;
   private
   private
+    function EqualValues(aValue1, aValue2: TValue): Boolean;
+
     function DoInvoke(aCodeAddress: CodePointer; aArgs: TValueArray; aCallConv: TCallConv; aResultType: PTypeInfo; aFlags: TInvokeFlags; out aValid: Boolean): TValue;
     function DoInvoke(aCodeAddress: CodePointer; aArgs: TValueArray; aCallConv: TCallConv; aResultType: PTypeInfo; aFlags: TInvokeFlags; out aValid: Boolean): TValue;
     procedure DoStaticInvokeTestOrdinalCompare(const aTestName: String; aAddress: CodePointer; aCallConv: TCallConv; aValues: TValueArray; aReturnType: PTypeInfo; aResult: Int64);
     procedure DoStaticInvokeTestOrdinalCompare(const aTestName: String; aAddress: CodePointer; aCallConv: TCallConv; aValues: TValueArray; aReturnType: PTypeInfo; aResult: Int64);
     procedure DoStaticInvokeTestAnsiStringCompare(const aTestName: String; aAddress: CodePointer; aCallConv: TCallConv; aValues: TValueArray; aReturnType: PTypeInfo; constref aResult: AnsiString);
     procedure DoStaticInvokeTestAnsiStringCompare(const aTestName: String; aAddress: CodePointer; aCallConv: TCallConv; aValues: TValueArray; aReturnType: PTypeInfo; constref aResult: AnsiString);
     procedure DoStaticInvokeTestUnicodeStringCompare(const aTestName: String; aAddress: CodePointer; aCallConv: TCallConv; aValues: TValueArray; aReturnType: PTypeInfo; constref aResult: UnicodeString);
     procedure DoStaticInvokeTestUnicodeStringCompare(const aTestName: String; aAddress: CodePointer; aCallConv: TCallConv; aValues: TValueArray; aReturnType: PTypeInfo; constref aResult: UnicodeString);
+    procedure DoIntfInvoke(aIndex: SizeInt; aInputArgs, aOutputArgs: TValueArray; aResult: TValue);
+    procedure DoMethodInvoke(aInst: TObject; aMethod: TMethod; aTypeInfo: PTypeInfo; aIndex: SizeInt; aInputArgs, aOutputArgs: TValueArray; aResult: TValue);
+    procedure DoProcVarInvoke(aInst: TObject; aProc: CodePointer; aTypeInfo: PTypeInfo; aIndex: SizeInt; aInputArgs, aOutputArgs: TValueArray; aResult: TValue);
+{$ifndef InLazIDE}
+    {$ifdef fpc}generic{$endif} procedure GenDoMethodInvoke<T>(aInst: TObject; aMethod: T; aIndex: SizeInt; aInputArgs, aOutputArgs: TValueArray; aResult: TValue);
+    {$ifdef fpc}generic{$endif} procedure GenDoProcvarInvoke<T>(aInst: TObject; aProc: T; aIndex: SizeInt; aInputArgs, aOutputArgs: TValueArray; aResult: TValue);
+    {$ifdef fpc}generic{$endif} function GetRecValue<T>(aReverse: Boolean): TValue;
+{$endif}
 {$ifdef fpc}
 {$ifdef fpc}
     procedure Status(const aMsg: String);
     procedure Status(const aMsg: String);
 {$endif}
 {$endif}
@@ -42,6 +56,15 @@ type
     procedure TestInt64;
     procedure TestInt64;
 
 
     procedure TestTObject;
     procedure TestTObject;
+
+    procedure TestIntfMethods;
+    procedure TestIntfMethodsRecs;
+
+    procedure TestMethodVars;
+    procedure TestMethodVarsRecs;
+
+    procedure TestProcVars;
+    procedure TestProcVarsRecs;
   end;
   end;
 
 
 {$ifndef fpc}
 {$ifndef fpc}
@@ -65,6 +88,96 @@ begin
 end;
 end;
 {$endif}
 {$endif}
 
 
+function TTestInvoke.EqualValues(aValue1, aValue2: TValue): Boolean;
+var
+  td1, td2: PTypeData;
+  i: SizeInt;
+begin
+{$ifdef debug}
+  Writeln('Empty: ', aValue1.IsEmpty, ' ', aValue2.IsEmpty);
+  Writeln('Kind: ', aValue1.Kind, ' ', aValue2.Kind);
+  Writeln('Array: ', aValue1.IsArray, ' ', aValue2.IsArray);
+{$endif}
+  if aValue1.IsEmpty and aValue2.IsEmpty then
+    Result := True
+  else if aValue1.IsEmpty and not aValue2.IsEmpty then
+    Result := False
+  else if not aValue1.IsEmpty and aValue2.IsEmpty then
+    Result := False
+  else if aValue1.IsArray and aValue2.IsArray then begin
+    if aValue1.GetArrayLength = aValue2.GetArrayLength then begin
+      Result := True;
+      for i := 0 to aValue1.GetArrayLength - 1 do
+        if not EqualValues(aValue1.GetArrayElement(i), aValue2.GetArrayElement(i)) then begin
+          Writeln('Element ', i, ' differs: ', HexStr(aValue1.GetArrayElement(i).AsOrdinal, 4), ' ', HexStr(aValue2.GetArrayElement(i).AsOrdinal, 4));
+          Result := False;
+          Break;
+        end;
+    end else
+      Result := False;
+  end else if aValue1.Kind = aValue2.Kind then begin
+    td1 := aValue1.TypeData;
+    td2 := aValue2.TypeData;
+    case aValue1.Kind of
+      tkBool:
+        Result := aValue1.AsBoolean xor not aValue2.AsBoolean;
+      tkSet:
+        if td1^.SetSize = td2^.SetSize then
+          if td1^.SetSize < SizeOf(SizeInt) then
+            Result := aValue1.AsOrdinal = aValue2.AsOrdinal
+          else
+            Result := CompareMem(aValue1.GetReferenceToRawData, aValue2.GetReferenceToRawData, td1^.SetSize)
+        else
+          Result := False;
+      tkEnumeration,
+      tkChar,
+      tkWChar,
+      tkUChar,
+      tkInt64,
+      tkInteger:
+        Result := aValue1.AsOrdinal = aValue2.AsOrdinal;
+      tkQWord:
+        Result := aValue1.AsUInt64 = aValue2.AsUInt64;
+      tkSString,
+      tkUString,
+      tkAString,
+      tkWString:
+        Result := aValue1.AsString = aValue2.AsString;
+      tkDynArray,
+      tkArray:
+        if aValue1.GetArrayLength = aValue2.GetArrayLength then begin
+          Result := True;
+          for i := 0 to aValue1.GetArrayLength - 1 do
+            if not EqualValues(aValue1.GetArrayElement(i), aValue2.GetArrayElement(i)) then begin
+              Result := False;
+              Break;
+            end;
+        end else
+          Result := False;
+      tkClass,
+      tkClassRef,
+      tkInterface,
+      tkInterfaceRaw,
+      tkPointer:
+        Result := PPointer(aValue1.GetReferenceToRawData)^ = PPointer(aValue2.GetReferenceToRawData)^;
+      tkProcVar:
+        Result := PCodePointer(aValue1.GetReferenceToRawData)^ = PCodePointer(aValue2.GetReferenceToRawData)^;
+      tkRecord,
+      tkObject,
+      tkMethod,
+      tkVariant: begin
+        if aValue1.DataSize = aValue2.DataSize then
+          Result := CompareMem(aValue1.GetReferenceToRawData, aValue2.GetReferenceToRawData, aValue1.DataSize)
+        else
+          Result := False;
+      end
+      else
+        Result := False;
+    end;
+  end else
+    Result := False;
+end;
+
 function TTestInvoke.DoInvoke(aCodeAddress: CodePointer; aArgs: TValueArray;
 function TTestInvoke.DoInvoke(aCodeAddress: CodePointer; aArgs: TValueArray;
   aCallConv: TCallConv; aResultType: PTypeInfo; aFlags: TInvokeFlags; out aValid: Boolean): TValue;
   aCallConv: TCallConv; aResultType: PTypeInfo; aFlags: TInvokeFlags; out aValid: Boolean): TValue;
 begin
 begin
@@ -521,6 +634,1128 @@ begin
   DoStaticInvokeTestClassCompare('TTestClass Pascal', @TestTTestClassPascal, ccPascal, values, TypeInfo(TTestClass), rescls);
   DoStaticInvokeTestClassCompare('TTestClass Pascal', @TestTTestClassPascal, ccPascal, values, TypeInfo(TTestClass), rescls);
 end;
 end;
 
 
+type
+  TTestRecord1 = packed record
+    b: array[0..0] of Byte;
+  end;
+
+  TTestRecord2 = packed record
+    b: array[0..1] of Byte;
+  end;
+
+  TTestRecord3 = packed record
+    b: array[0..2] of Byte;
+  end;
+
+  TTestRecord4 = packed record
+    b: array[0..3] of Byte;
+  end;
+
+  TTestRecord5 = packed record
+    b: array[0..4] of Byte;
+  end;
+
+  TTestRecord6 = packed record
+    b: array[0..5] of Byte;
+  end;
+
+  TTestRecord7 = packed record
+    b: array[0..6] of Byte;
+  end;
+
+  TTestRecord8 = packed record
+    b: array[0..7] of Byte;
+  end;
+
+  TTestRecord9 = packed record
+    b: array[0..8] of Byte;
+  end;
+
+  TTestRecord10 = packed record
+    b: array[0..9] of Byte;
+  end;
+
+  {$M+}
+  ITestInterface = interface
+    procedure Test1;
+    function Test2: SizeInt;
+    function Test3(aArg1, aArg2, aArg3, aArg4, aArg5, aArg6, aArg7, aArg8, aArg9, aArg10: SizeInt): SizeInt;
+    procedure Test4(aArg1: AnsiString; aArg2: UnicodeString; aArg3: WideString; aArg4: ShortString);
+    function Test5: AnsiString;
+    function Test6: UnicodeString;
+    function Test7: WideString;
+    function Test8: ShortString;
+    procedure Test9(aArg1: SizeInt; var aArg2: SizeInt; out aArg3: SizeInt; {$ifdef fpc}constref{$else}const [ref]{$endif} aArg4: SizeInt);
+    procedure Test10(aArg1: AnsiString; var aArg2: AnsiString; out aArg3: AnsiString; {$ifdef fpc}constref{$else}const [ref]{$endif} aArg4: AnsiString);
+    procedure Test11(aArg1: ShortString; var aArg2: ShortString; out aArg3: ShortString; {$ifdef fpc}constref{$else}const [ref]{$endif} aArg4: ShortString);
+    procedure Test12(aArg1: array of SizeInt; var aArg2: array of SizeInt; out aArg3: array of SizeInt; {$ifdef fpc}constref{$else}const [ref]{$endif} aArg4: array of SizeInt);
+
+    function TestRecSize1(aArg1: TTestRecord1): TTestRecord1;
+    function TestRecSize2(aArg1: TTestRecord2): TTestRecord2;
+    function TestRecSize3(aArg1: TTestRecord3): TTestRecord3;
+    function TestRecSize4(aArg1: TTestRecord4): TTestRecord4;
+    function TestRecSize5(aArg1: TTestRecord5): TTestRecord5;
+    function TestRecSize6(aArg1: TTestRecord6): TTestRecord6;
+    function TestRecSize7(aArg1: TTestRecord7): TTestRecord7;
+    function TestRecSize8(aArg1: TTestRecord8): TTestRecord8;
+    function TestRecSize9(aArg1: TTestRecord9): TTestRecord9;
+    function TestRecSize10(aArg1: TTestRecord10): TTestRecord10;
+  end;
+  {$M-}
+
+  TTestInterfaceClass = class(TInterfacedObject, ITestInterface)
+  private
+    procedure Test1;
+    function Test2: SizeInt;
+    function Test3(aArg1, aArg2, aArg3, aArg4, aArg5, aArg6, aArg7, aArg8, aArg9, aArg10: SizeInt): SizeInt;
+    procedure Test4(aArg1: AnsiString; aArg2: UnicodeString; aArg3: WideString; aArg4: ShortString);
+    function Test5: AnsiString;
+    function Test6: UnicodeString;
+    function Test7: WideString;
+    function Test8: ShortString;
+    procedure Test9(aArg1: SizeInt; var aArg2: SizeInt; out aArg3: SizeInt; {$ifdef fpc}constref{$else}const [ref]{$endif} aArg4: SizeInt);
+    procedure Test10(aArg1: AnsiString; var aArg2: AnsiString; out aArg3: AnsiString; {$ifdef fpc}constref{$else}const [ref]{$endif} aArg4: AnsiString);
+    procedure Test11(aArg1: ShortString; var aArg2: ShortString; out aArg3: ShortString; {$ifdef fpc}constref{$else}const [ref]{$endif} aArg4: ShortString);
+    procedure Test12(aArg1: array of SizeInt; var aArg2: array of SizeInt; out aArg3: array of SizeInt; {$ifdef fpc}constref{$else}const [ref]{$endif} aArg4: array of SizeInt);
+
+    function TestRecSize1(aArg1: TTestRecord1): TTestRecord1;
+    function TestRecSize2(aArg1: TTestRecord2): TTestRecord2;
+    function TestRecSize3(aArg1: TTestRecord3): TTestRecord3;
+    function TestRecSize4(aArg1: TTestRecord4): TTestRecord4;
+    function TestRecSize5(aArg1: TTestRecord5): TTestRecord5;
+    function TestRecSize6(aArg1: TTestRecord6): TTestRecord6;
+    function TestRecSize7(aArg1: TTestRecord7): TTestRecord7;
+    function TestRecSize8(aArg1: TTestRecord8): TTestRecord8;
+    function TestRecSize9(aArg1: TTestRecord9): TTestRecord9;
+    function TestRecSize10(aArg1: TTestRecord10): TTestRecord10;
+  public
+    InputArgs: array of TValue;
+    OutputArgs: array of TValue;
+    ResultValue: TValue;
+    CalledMethod: SizeInt;
+    InOutMapping: array of SizeInt;
+    procedure Reset;
+  public class var
+    ProcVarInst: TTestInterfaceClass;
+    ProcVarRecInst: TTestInterfaceClass;
+  public const
+    RecSizeMarker = SizeInt($80000000);
+  end;
+
+  TMethodTest1 = procedure of object;
+  TMethodTest2 = function: SizeInt of object;
+  TMethodTest3 = function(aArg1, aArg2, aArg3, aArg4, aArg5, aArg6, aArg7, aArg8, aArg9, aArg10: SizeInt): SizeInt of object;
+  TMethodTest4 = procedure(aArg1: AnsiString; aArg2: UnicodeString; aArg3: WideString; aArg4: ShortString) of object;
+  TMethodTest5 = function: AnsiString of object;
+  TMethodTest6 = function: UnicodeString of object;
+  TMethodTest7 = function: WideString of object;
+  TMethodTest8 = function: ShortString of object;
+  TMethodTest9 = procedure(aArg1: SizeInt; var aArg2: SizeInt; out aArg3: SizeInt; {$ifdef fpc}constref{$else}const [ref]{$endif} aArg4: SizeInt) of object;
+  TMethodTest10 = procedure(aArg1: AnsiString; var aArg2: AnsiString; out aArg3: AnsiString; {$ifdef fpc}constref{$else}const [ref]{$endif} aArg4: AnsiString) of object;
+  TMethodTest11 = procedure(aArg1: ShortString; var aArg2: ShortString; out aArg3: ShortString; {$ifdef fpc}constref{$else}const [ref]{$endif} aArg4: ShortString) of object;
+  TMethodTest12 = procedure(aArg1: array of SizeInt; var aArg2: array of SizeInt; out aArg3: array of SizeInt; {$ifdef fpc}constref{$else}const [ref]{$endif} aArg4: array of SizeInt) of object;
+
+  TMethodTestRecSize1 = function(aArg1: TTestRecord1): TTestRecord1 of object;
+  TMethodTestRecSize2 = function(aArg1: TTestRecord2): TTestRecord2 of object;
+  TMethodTestRecSize3 = function(aArg1: TTestRecord3): TTestRecord3 of object;
+  TMethodTestRecSize4 = function(aArg1: TTestRecord4): TTestRecord4 of object;
+  TMethodTestRecSize5 = function(aArg1: TTestRecord5): TTestRecord5 of object;
+  TMethodTestRecSize6 = function(aArg1: TTestRecord6): TTestRecord6 of object;
+  TMethodTestRecSize7 = function(aArg1: TTestRecord7): TTestRecord7 of object;
+  TMethodTestRecSize8 = function(aArg1: TTestRecord8): TTestRecord8 of object;
+  TMethodTestRecSize9 = function(aArg1: TTestRecord9): TTestRecord9 of object;
+  TMethodTestRecSize10 = function(aArg1: TTestRecord10): TTestRecord10 of object;
+
+  TProcVarTest1 = procedure;
+  TProcVarTest2 = function: SizeInt;
+  TProcVarTest3 = function(aArg1, aArg2, aArg3, aArg4, aArg5, aArg6, aArg7, aArg8, aArg9, aArg10: SizeInt): SizeInt;
+  TProcVarTest4 = procedure(aArg1: AnsiString; aArg2: UnicodeString; aArg3: WideString; aArg4: ShortString);
+  TProcVarTest5 = function: AnsiString;
+  TProcVarTest6 = function: UnicodeString;
+  TProcVarTest7 = function: WideString;
+  TProcVarTest8 = function: ShortString;
+  TProcVarTest9 = procedure(aArg1: SizeInt; var aArg2: SizeInt; out aArg3: SizeInt; {$ifdef fpc}constref{$else}const [ref]{$endif} aArg4: SizeInt);
+  TProcVarTest10 = procedure(aArg1: AnsiString; var aArg2: AnsiString; out aArg3: AnsiString; {$ifdef fpc}constref{$else}const [ref]{$endif} aArg4: AnsiString);
+  TProcVarTest11 = procedure(aArg1: ShortString; var aArg2: ShortString; out aArg3: ShortString; {$ifdef fpc}constref{$else}const [ref]{$endif} aArg4: ShortString);
+  TProcVarTest12 = procedure(aArg1: array of SizeInt; var aArg2: array of SizeInt; out aArg3: array of SizeInt; {$ifdef fpc}constref{$else}const [ref]{$endif} aArg4: array of SizeInt);
+
+  TProcVarTestRecSize1 = function(aArg1: TTestRecord1): TTestRecord1;
+  TProcVarTestRecSize2 = function(aArg1: TTestRecord2): TTestRecord2;
+  TProcVarTestRecSize3 = function(aArg1: TTestRecord3): TTestRecord3;
+  TProcVarTestRecSize4 = function(aArg1: TTestRecord4): TTestRecord4;
+  TProcVarTestRecSize5 = function(aArg1: TTestRecord5): TTestRecord5;
+  TProcVarTestRecSize6 = function(aArg1: TTestRecord6): TTestRecord6;
+  TProcVarTestRecSize7 = function(aArg1: TTestRecord7): TTestRecord7;
+  TProcVarTestRecSize8 = function(aArg1: TTestRecord8): TTestRecord8;
+  TProcVarTestRecSize9 = function(aArg1: TTestRecord9): TTestRecord9;
+  TProcVarTestRecSize10 = function(aArg1: TTestRecord10): TTestRecord10;
+
+procedure TTestInterfaceClass.Test1;
+begin
+  SetLength(InputArgs, 0);
+  SetLength(OutputArgs, 0);
+  ResultValue := TValue.Empty;
+  CalledMethod := 1;
+end;
+
+function TTestInterfaceClass.Test2: SizeInt;
+begin
+  SetLength(InputArgs, 0);
+  SetLength(OutputArgs, 0);
+  Result := 42;
+  TValue.Make(@Result, TypeInfo(Result), ResultValue);
+  CalledMethod := 2;
+end;
+
+function TTestInterfaceClass.Test3(aArg1, aArg2, aArg3, aArg4, aArg5, aArg6, aArg7, aArg8, aArg9, aArg10: SizeInt): SizeInt;
+begin
+  SetLength(InputArgs, 10);
+  TValue.Make(@aArg1, TypeInfo(aArg1), InputArgs[0]);
+  TValue.Make(@aArg2, TypeInfo(aArg2), InputArgs[1]);
+  TValue.Make(@aArg3, TypeInfo(aArg3), InputArgs[2]);
+  TValue.Make(@aArg4, TypeInfo(aArg4), InputArgs[3]);
+  TValue.Make(@aArg5, TypeInfo(aArg5), InputArgs[4]);
+  TValue.Make(@aArg6, TypeInfo(aArg6), InputArgs[5]);
+  TValue.Make(@aArg7, TypeInfo(aArg7), InputArgs[6]);
+  TValue.Make(@aArg8, TypeInfo(aArg8), InputArgs[7]);
+  TValue.Make(@aArg9, TypeInfo(aArg9), InputArgs[8]);
+  TValue.Make(@aArg10, TypeInfo(aArg10), InputArgs[9]);
+  SetLength(OutputArgs, 0);
+  Result := 42;
+  TValue.Make(@Result, TypeInfo(Result), ResultValue);
+  CalledMethod := 3;
+end;
+
+procedure TTestInterfaceClass.Test4(aArg1: AnsiString; aArg2: UnicodeString; aArg3: WideString; aArg4: ShortString);
+begin
+  SetLength(InputArgs, 4);
+  TValue.Make(@aArg1, TypeInfo(aArg1), InputArgs[0]);
+  TValue.Make(@aArg2, TypeInfo(aArg2), InputArgs[1]);
+  TValue.Make(@aArg3, TypeInfo(aArg3), InputArgs[2]);
+  TValue.Make(@aArg4, TypeInfo(aArg4), InputArgs[3]);
+  SetLength(OutputArgs, 0);
+  ResultValue := TValue.Empty;
+  CalledMethod := 4;
+end;
+
+function TTestInterfaceClass.Test5: AnsiString;
+begin
+  SetLength(InputArgs, 0);
+  SetLength(OutputArgs, 0);
+  Result := 'Hello World';
+  TValue.Make(@Result, TypeInfo(Result), ResultValue);
+  CalledMethod := 5;
+end;
+
+function TTestInterfaceClass.Test6: UnicodeString;
+begin
+  SetLength(InputArgs, 0);
+  SetLength(OutputArgs, 0);
+  Result := 'Hello World';
+  TValue.Make(@Result, TypeInfo(Result), ResultValue);
+  CalledMethod := 6;
+end;
+
+function TTestInterfaceClass.Test7: WideString;
+begin
+  SetLength(InputArgs, 0);
+  SetLength(OutputArgs, 0);
+  Result := 'Hello World';
+  TValue.Make(@Result, TypeInfo(Result), ResultValue);
+  CalledMethod := 7;
+end;
+
+function TTestInterfaceClass.Test8: ShortString;
+begin
+  SetLength(InputArgs, 0);
+  SetLength(OutputArgs, 0);
+  Result := 'Hello World';
+  TValue.Make(@Result, TypeInfo(Result), ResultValue);
+  CalledMethod := 8;
+end;
+
+procedure TTestInterfaceClass.Test9(aArg1: SizeInt; var aArg2: SizeInt; out aArg3: SizeInt; {$ifdef fpc}constref{$else}const [ref]{$endif} aArg4: SizeInt);
+begin
+  SetLength(InputArgs, 4);
+  TValue.Make(@aArg1, TypeInfo(aArg1), InputArgs[0]);
+  TValue.Make(@aArg2, TypeInfo(aArg2), InputArgs[1]);
+  TValue.Make(@aArg3, TypeInfo(aArg3), InputArgs[2]);
+  TValue.Make(@aArg4, TypeInfo(aArg4), InputArgs[3]);
+  aArg2 := $1234;
+  aArg3 := $5678;
+  SetLength(OutputArgs, 2);
+  TValue.Make(@aArg2, TypeInfo(aArg2), OutputArgs[0]);
+  TValue.Make(@aArg3, TypeInfo(aArg3), OutputArgs[1]);
+  SetLength(InOutMapping, 2);
+  InOutMapping[0] := 1;
+  InOutMapping[1] := 2;
+  ResultValue := TValue.Empty;
+  CalledMethod := 9;
+end;
+
+procedure TTestInterfaceClass.Test10(aArg1: AnsiString; var aArg2: AnsiString; out aArg3: AnsiString; {$ifdef fpc}constref{$else}const [ref]{$endif} aArg4: AnsiString);
+begin
+  SetLength(InputArgs, 4);
+  TValue.Make(@aArg1, TypeInfo(aArg1), InputArgs[0]);
+  TValue.Make(@aArg2, TypeInfo(aArg2), InputArgs[1]);
+  TValue.Make(@aArg3, TypeInfo(aArg3), InputArgs[2]);
+  TValue.Make(@aArg4, TypeInfo(aArg4), InputArgs[3]);
+  aArg2 := 'Foo';
+  aArg3 := 'Bar';
+  SetLength(OutputArgs, 2);
+  TValue.Make(@aArg2, TypeInfo(aArg2), OutputArgs[0]);
+  TValue.Make(@aArg3, TypeInfo(aArg3), OutputArgs[1]);
+  SetLength(InOutMapping, 2);
+  InOutMapping[0] := 1;
+  InOutMapping[1] := 2;
+  ResultValue := TValue.Empty;
+  CalledMethod := 10;
+end;
+
+procedure TTestInterfaceClass.Test11(aArg1: ShortString; var aArg2: ShortString; out aArg3: ShortString; {$ifdef fpc}constref{$else}const [ref]{$endif} aArg4: ShortString);
+begin
+  SetLength(InputArgs, 4);
+  TValue.Make(@aArg1, TypeInfo(aArg1), InputArgs[0]);
+  TValue.Make(@aArg2, TypeInfo(aArg2), InputArgs[1]);
+  TValue.Make(@aArg3, TypeInfo(aArg3), InputArgs[2]);
+  TValue.Make(@aArg4, TypeInfo(aArg4), InputArgs[3]);
+  aArg2 := 'Foo';
+  aArg3 := 'Bar';
+  SetLength(OutputArgs, 2);
+  TValue.Make(@aArg2, TypeInfo(aArg2), OutputArgs[0]);
+  TValue.Make(@aArg3, TypeInfo(aArg3), OutputArgs[1]);
+  SetLength(InOutMapping, 2);
+  InOutMapping[0] := 1;
+  InOutMapping[1] := 2;
+  ResultValue := TValue.Empty;
+  CalledMethod := 11;
+end;
+
+procedure TTestInterfaceClass.Test12(aArg1: array of SizeInt; var aArg2: array of SizeInt; out aArg3: array of SizeInt; {$ifdef fpc}constref{$else}const [ref]{$endif} aArg4: array of SizeInt);
+{$ifdef fpc}
+var
+  i: SizeInt;
+  start: SizeInt;
+{$endif}
+begin
+{$ifdef fpc}
+  SetLength(InputArgs, 4);
+  InputArgs[0] := specialize OpenArrayToDynArrayValue<SizeInt>(aArg1);
+  InputArgs[1] := specialize OpenArrayToDynArrayValue<SizeInt>(aArg2);
+  InputArgs[2] := specialize OpenArrayToDynArrayValue<SizeInt>(aArg3);
+  InputArgs[3] := specialize OpenArrayToDynArrayValue<SizeInt>(aArg4);
+  SetLength(OutputArgs, 2);
+  start := $4321;
+  for i := 0 to High(aArg2) do
+    aArg2[i] := start + i;
+  start := $9876;
+  for i := 0 to High(aArg3) do
+    aArg3[i] := start + i;
+  OutputArgs[0] := specialize OpenArrayToDynArrayValue<SizeInt>(aArg2);
+  OutputArgs[1] := specialize OpenArrayToDynArrayValue<SizeInt>(aArg3);
+  SetLength(InOutMapping, 2);
+  InOutMapping[0] := 1;
+  InOutMapping[1] := 2;
+  ResultValue := TValue.Empty;
+  CalledMethod := 12;
+{$endif}
+end;
+
+function TTestInterfaceClass.TestRecSize1(aArg1: TTestRecord1): TTestRecord1;
+var
+  i: LongInt;
+begin
+  SetLength(InputArgs, 1);
+  TValue.Make(@aArg1, TypeInfo(aArg1), InputArgs[0]);
+  SetLength(OutputArgs, 0);
+  for i := 0 to High(aArg1.b) do
+    Result.b[High(Result.b) - i] := aArg1.b[i];
+  TValue.Make(@Result, TypeInfo(Result), ResultValue);
+  CalledMethod := 1 or RecSizeMarker;
+end;
+
+function TTestInterfaceClass.TestRecSize2(aArg1: TTestRecord2): TTestRecord2;
+var
+  i: LongInt;
+begin
+  SetLength(InputArgs, 1);
+  TValue.Make(@aArg1, TypeInfo(aArg1), InputArgs[0]);
+  SetLength(OutputArgs, 0);
+  for i := 0 to High(aArg1.b) do
+    Result.b[High(Result.b) - i] := aArg1.b[i];
+  TValue.Make(@Result, TypeInfo(Result), ResultValue);
+  CalledMethod := 2 or RecSizeMarker;
+end;
+
+function TTestInterfaceClass.TestRecSize3(aArg1: TTestRecord3): TTestRecord3;
+var
+  i: LongInt;
+begin
+  SetLength(InputArgs, 1);
+  TValue.Make(@aArg1, TypeInfo(aArg1), InputArgs[0]);
+  SetLength(OutputArgs, 0);
+  for i := 0 to High(aArg1.b) do
+    Result.b[High(Result.b) - i] := aArg1.b[i];
+  TValue.Make(@Result, TypeInfo(Result), ResultValue);
+  CalledMethod := 3 or RecSizeMarker;
+end;
+
+function TTestInterfaceClass.TestRecSize4(aArg1: TTestRecord4): TTestRecord4;
+var
+  i: LongInt;
+begin
+  SetLength(InputArgs, 1);
+  TValue.Make(@aArg1, TypeInfo(aArg1), InputArgs[0]);
+  SetLength(OutputArgs, 0);
+  for i := 0 to High(aArg1.b) do
+    Result.b[High(Result.b) - i] := aArg1.b[i];
+  TValue.Make(@Result, TypeInfo(Result), ResultValue);
+  CalledMethod := 4 or RecSizeMarker;
+end;
+
+function TTestInterfaceClass.TestRecSize5(aArg1: TTestRecord5): TTestRecord5;
+var
+  i: LongInt;
+begin
+  SetLength(InputArgs, 1);
+  TValue.Make(@aArg1, TypeInfo(aArg1), InputArgs[0]);
+  SetLength(OutputArgs, 0);
+  for i := 0 to High(aArg1.b) do
+    Result.b[High(Result.b) - i] := aArg1.b[i];
+  TValue.Make(@Result, TypeInfo(Result), ResultValue);
+  CalledMethod := 5 or RecSizeMarker;
+end;
+
+function TTestInterfaceClass.TestRecSize6(aArg1: TTestRecord6): TTestRecord6;
+var
+  i: LongInt;
+begin
+  SetLength(InputArgs, 1);
+  TValue.Make(@aArg1, TypeInfo(aArg1), InputArgs[0]);
+  SetLength(OutputArgs, 0);
+  for i := 0 to High(aArg1.b) do
+    Result.b[High(Result.b) - i] := aArg1.b[i];
+  TValue.Make(@Result, TypeInfo(Result), ResultValue);
+  CalledMethod := 6 or RecSizeMarker;
+end;
+
+function TTestInterfaceClass.TestRecSize7(aArg1: TTestRecord7): TTestRecord7;
+var
+  i: LongInt;
+begin
+  SetLength(InputArgs, 1);
+  TValue.Make(@aArg1, TypeInfo(aArg1), InputArgs[0]);
+  SetLength(OutputArgs, 0);
+  for i := 0 to High(aArg1.b) do
+    Result.b[High(Result.b) - i] := aArg1.b[i];
+  TValue.Make(@Result, TypeInfo(Result), ResultValue);
+  CalledMethod := 7 or RecSizeMarker;
+end;
+
+function TTestInterfaceClass.TestRecSize8(aArg1: TTestRecord8): TTestRecord8;
+var
+  i: LongInt;
+begin
+  SetLength(InputArgs, 1);
+  TValue.Make(@aArg1, TypeInfo(aArg1), InputArgs[0]);
+  SetLength(OutputArgs, 0);
+  for i := 0 to High(aArg1.b) do
+    Result.b[High(Result.b) - i] := aArg1.b[i];
+  TValue.Make(@Result, TypeInfo(Result), ResultValue);
+  CalledMethod := 8 or RecSizeMarker;
+end;
+
+function TTestInterfaceClass.TestRecSize9(aArg1: TTestRecord9): TTestRecord9;
+var
+  i: LongInt;
+begin
+  SetLength(InputArgs, 1);
+  TValue.Make(@aArg1, TypeInfo(aArg1), InputArgs[0]);
+  SetLength(OutputArgs, 0);
+  for i := 0 to High(aArg1.b) do
+    Result.b[High(Result.b) - i] := aArg1.b[i];
+  TValue.Make(@Result, TypeInfo(Result), ResultValue);
+  CalledMethod := 9 or RecSizeMarker;
+end;
+
+function TTestInterfaceClass.TestRecSize10(aArg1: TTestRecord10): TTestRecord10;
+var
+  i: LongInt;
+begin
+  SetLength(InputArgs, 1);
+  TValue.Make(@aArg1, TypeInfo(aArg1), InputArgs[0]);
+  SetLength(OutputArgs, 0);
+  for i := 0 to High(aArg1.b) do
+    Result.b[High(Result.b) - i] := aArg1.b[i];
+  TValue.Make(@Result, TypeInfo(Result), ResultValue);
+  CalledMethod := 10 or RecSizeMarker;
+end;
+
+procedure TTestInterfaceClass.Reset;
+begin
+  InputArgs := Nil;
+  OutputArgs := Nil;
+  InOutMapping := Nil;
+  ResultValue := TValue.Empty;
+  CalledMethod := 0;
+end;
+
+procedure ProcTest1;
+begin
+  TTestInterfaceClass.ProcVarInst.Test1;
+end;
+
+function ProcTest2: SizeInt;
+begin
+  Result := TTestInterfaceClass.ProcVarInst.Test2;
+end;
+
+function ProcTest3(aArg1, aArg2, aArg3, aArg4, aArg5, aArg6, aArg7, aArg8, aArg9, aArg10: SizeInt): SizeInt;
+begin
+  Result := TTestInterfaceClass.ProcVarInst.Test3(aArg1, aArg2, aArg3, aArg4, aArg5, aArg6, aArg7, aArg8, aArg9, aArg10);
+end;
+
+procedure ProcTest4(aArg1: AnsiString; aArg2: UnicodeString; aArg3: WideString; aArg4: ShortString);
+begin
+  TTestInterfaceClass.ProcVarInst.Test4(aArg1, aArg2, aArg3, aArg4);
+end;
+
+function ProcTest5: AnsiString;
+begin
+  Writeln('Result @ ', HexStr(@Result));
+  Result := TTestInterfaceClass.ProcVarInst.Test5;
+end;
+
+function ProcTest6: UnicodeString;
+begin
+  Result := TTestInterfaceClass.ProcVarInst.Test6;
+end;
+
+function ProcTest7: WideString;
+begin
+  Result := TTestInterfaceClass.ProcVarInst.Test7;
+end;
+
+function ProcTest8: ShortString;
+begin
+  Result := TTestInterfaceClass.ProcVarInst.Test8;
+end;
+
+procedure ProcTest9(aArg1: SizeInt; var aArg2: SizeInt; out aArg3: SizeInt; {$ifdef fpc}constref{$else}const [ref]{$endif} aArg4: SizeInt);
+begin
+  TTestInterfaceClass.ProcVarInst.Test9(aArg1, aArg2, aArg3, aArg4);
+end;
+
+procedure ProcTest10(aArg1: AnsiString; var aArg2: AnsiString; out aArg3: AnsiString; {$ifdef fpc}constref{$else}const [ref]{$endif} aArg4: AnsiString);
+begin
+  TTestInterfaceClass.ProcVarInst.Test10(aArg1, aArg2, aArg3, aArg4);
+end;
+
+procedure ProcTest11(aArg1: ShortString; var aArg2: ShortString; out aArg3: ShortString; {$ifdef fpc}constref{$else}const [ref]{$endif} aArg4: ShortString);
+begin
+  TTestInterfaceClass.ProcVarInst.Test11(aArg1, aArg2, aArg3, aArg4);
+end;
+
+procedure ProcTest12(aArg1: array of SizeInt; var aArg2: array of SizeInt; out aArg3: array of SizeInt; {$ifdef fpc}constref{$else}const [ref]{$endif} aArg4: array of SizeInt);
+begin
+  TTestInterfaceClass.ProcVarInst.Test12(aArg1, aArg2, aArg3, aArg4);
+end;
+
+function ProcTestRecSize1(aArg1: TTestRecord1): TTestRecord1;
+begin
+  Result := TTestInterfaceClass.ProcVarRecInst.TestRecSize1(aArg1);
+end;
+
+function ProcTestRecSize2(aArg1: TTestRecord2): TTestRecord2;
+begin
+  Result := TTestInterfaceClass.ProcVarRecInst.TestRecSize2(aArg1);
+end;
+
+function ProcTestRecSize3(aArg1: TTestRecord3): TTestRecord3;
+begin
+  Result := TTestInterfaceClass.ProcVarRecInst.TestRecSize3(aArg1);
+end;
+
+function ProcTestRecSize4(aArg1: TTestRecord4): TTestRecord4;
+begin
+  Result := TTestInterfaceClass.ProcVarRecInst.TestRecSize4(aArg1);
+end;
+
+function ProcTestRecSize5(aArg1: TTestRecord5): TTestRecord5;
+begin
+  Result := TTestInterfaceClass.ProcVarRecInst.TestRecSize5(aArg1);
+end;
+
+function ProcTestRecSize6(aArg1: TTestRecord6): TTestRecord6;
+begin
+  Result := TTestInterfaceClass.ProcVarRecInst.TestRecSize6(aArg1);
+end;
+
+function ProcTestRecSize7(aArg1: TTestRecord7): TTestRecord7;
+begin
+  Result := TTestInterfaceClass.ProcVarRecInst.TestRecSize7(aArg1);
+end;
+
+function ProcTestRecSize8(aArg1: TTestRecord8): TTestRecord8;
+begin
+  Result := TTestInterfaceClass.ProcVarRecInst.TestRecSize8(aArg1);
+end;
+
+function ProcTestRecSize9(aArg1: TTestRecord9): TTestRecord9;
+begin
+  Result := TTestInterfaceClass.ProcVarRecInst.TestRecSize9(aArg1);
+end;
+
+function ProcTestRecSize10(aArg1: TTestRecord10): TTestRecord10;
+begin
+  Result := TTestInterfaceClass.ProcVarRecInst.TestRecSize10(aArg1);
+end;
+
+function CopyValue({$ifdef fpc}constref{$else}const [ref]{$endif} aValue: TValue): TValue;
+var
+  arrptr: Pointer;
+  len, i: SizeInt;
+begin
+  if aValue.Kind = tkDynArray then begin
+    { we need to decouple the source reference, so we're going to be a bit
+      cheeky here }
+    len := aValue.GetArrayLength;
+    arrptr := Nil;
+    DynArraySetLength(arrptr, aValue.TypeInfo, 1, @len);
+    TValue.Make(@arrptr, aValue.TypeInfo, Result);
+    for i := 0 to len - 1 do
+      Result.SetArrayElement(i, aValue.GetArrayElement(i));
+  end else
+    TValue.Make(aValue.GetReferenceToRawData, aValue.TypeInfo, Result);
+end;
+
+procedure TTestInvoke.DoIntfInvoke(aIndex: SizeInt; aInputArgs,
+  aOutputArgs: TValueArray; aResult: TValue);
+var
+  cls: TTestInterfaceClass;
+  intf: ITestInterface;
+  name: String;
+  context: TRttiContext;
+  t: TRttiType;
+  inst, res: TValue;
+  method: TRttiMethod;
+  i: SizeInt;
+  input: array of TValue;
+begin
+  cls := TTestInterfaceClass.Create;
+  intf := cls;
+
+  TValue.Make(@intf, TypeInfo(intf), inst);
+
+  if aIndex and TTestInterfaceClass.RecSizeMarker <> 0 then
+    name := 'TestRecSize' + IntToStr(aIndex and not TTestInterfaceClass.RecSizeMarker)
+  else
+    name := 'Test' + IntToStr(aIndex);
+
+  context := TRttiContext.Create;
+  try
+    t := context.GetType(TypeInfo(ITestInterface));
+    method := t.GetMethod(name);
+    Check(Assigned(method), 'Method not found: ' + name);
+
+    { arguments might be modified by Invoke (Note: Copy() does not uniquify the
+      IValueData of managed types) }
+    SetLength(input, Length(aInputArgs));
+    for i := 0 to High(input) do
+      input[i] := CopyValue(aInputArgs[i]);
+
+    try
+    res := method.Invoke(inst, aInputArgs);
+    except
+      DumpExceptionBacktrace(output);
+      raise;
+    end;
+    CheckEquals(aIndex, cls.CalledMethod, 'Wrong method called for ' + name);
+    Check(EqualValues(cls.ResultValue, res), 'Reported result value differs from returned for ' + name);
+    Check(EqualValues(aResult, res), 'Expected result value differs from returned for ' + name);
+    CheckEquals(Length(aInputArgs), Length(cls.InputArgs), 'Count of input args differs for ' + name);
+    CheckEquals(Length(cls.OutputArgs), Length(cls.InOutMapping), 'Count of output args and in-out-mapping differs for ' + name);
+    CheckEquals(Length(aOutputArgs), Length(cls.OutputArgs), 'Count of output args differs for ' + name);
+    for i := 0 to High(aInputArgs) do begin
+      Check(EqualValues(input[i], cls.InputArgs[i]), Format('Input argument %d differs for %s', [i + 1, name]));
+    end;
+    for i := 0 to High(aOutputArgs) do begin
+      Check(EqualValues(aOutputArgs[i], cls.OutputArgs[i]), Format('Output argument %d differs for %s', [i + 1, name]));
+      Check(EqualValues(aOutputArgs[i], aInputArgs[cls.InOutMapping[i]]), Format('New output argument %d differs from expected output for %s', [i + 1, name]));
+    end;
+  finally
+    context.Free;
+  end;
+end;
+
+procedure TTestInvoke.DoMethodInvoke(aInst: TObject; aMethod: TMethod;
+  aTypeInfo: PTypeInfo; aIndex: SizeInt; aInputArgs, aOutputArgs: TValueArray; aResult: TValue);
+var
+  cls: TTestInterfaceClass;
+  name: String;
+  context: TRttiContext;
+  t: TRttiType;
+  callable, res: TValue;
+  method: TRttiMethodType;
+  i: SizeInt;
+  input: array of TValue;
+begin
+  cls := aInst as TTestInterfaceClass;
+  cls.Reset;
+
+  if aIndex and TTestInterfaceClass.RecSizeMarker <> 0 then
+    name := 'TestRecSize' + IntToStr(aIndex and not TTestInterfaceClass.RecSizeMarker)
+  else
+    name := 'Test' + IntToStr(aIndex);
+
+  TValue.Make(@aMethod, aTypeInfo, callable);
+
+  context := TRttiContext.Create;
+  try
+    t := context.GetType(aTypeInfo);
+    Check(t is TRttiMethodType, 'Not a method variable: ' + aTypeInfo^.Name);
+    method := t as TRttiMethodType;
+
+    { arguments might be modified by Invoke (Note: Copy() does not uniquify the
+      IValueData of managed types) }
+    SetLength(input, Length(aInputArgs));
+    for i := 0 to High(input) do
+      input[i] := CopyValue(aInputArgs[i]);
+
+    res := method.Invoke(callable, aInputArgs);
+    CheckEquals(aIndex, cls.CalledMethod, 'Wrong method called for ' + name);
+    Check(EqualValues(cls.ResultValue, res), 'Reported result value differs from returned for ' + name);
+    Check(EqualValues(aResult, res), 'Expected result value differs from returned for ' + name);
+    CheckEquals(Length(aInputArgs), Length(cls.InputArgs), 'Count of input args differs for ' + name);
+    CheckEquals(Length(cls.OutputArgs), Length(cls.InOutMapping), 'Count of output args and in-out-mapping differs for ' + name);
+    CheckEquals(Length(aOutputArgs), Length(cls.OutputArgs), 'Count of output args differs for ' + name);
+    for i := 0 to High(aInputArgs) do begin
+      Check(EqualValues(input[i], cls.InputArgs[i]), Format('Input argument %d differs for %s', [i + 1, name]));
+    end;
+    for i := 0 to High(aOutputArgs) do begin
+      Check(EqualValues(aOutputArgs[i], cls.OutputArgs[i]), Format('Output argument %d differs for %s', [i + 1, name]));
+      Check(EqualValues(aOutputArgs[i], aInputArgs[cls.InOutMapping[i]]), Format('New output argument %d differs from expected output for %s', [i + 1, name]));
+    end;
+  finally
+    context.Free;
+  end;
+end;
+
+procedure TTestInvoke.DoProcVarInvoke(aInst: TObject; aProc: CodePointer;
+  aTypeInfo: PTypeInfo; aIndex: SizeInt; aInputArgs, aOutputArgs: TValueArray; aResult: TValue);
+var
+  cls: TTestInterfaceClass;
+  name: String;
+  context: TRttiContext;
+  t: TRttiType;
+  callable, res: TValue;
+  proc: TRttiProcedureType;
+  i: SizeInt;
+  input: array of TValue;
+begin
+  cls := aInst as TTestInterfaceClass;
+  cls.Reset;
+
+  if aIndex and TTestInterfaceClass.RecSizeMarker <> 0 then begin
+    name := 'TestRecSize' + IntToStr(aIndex and not TTestInterfaceClass.RecSizeMarker);
+    TTestInterfaceClass.ProcVarRecInst := cls;
+  end else begin
+    name := 'Test' + IntToStr(aIndex);
+    TTestInterfaceClass.ProcVarInst := cls;
+  end;
+
+  TValue.Make(@aProc, aTypeInfo, callable);
+
+  context := TRttiContext.Create;
+  try
+    t := context.GetType(aTypeInfo);
+    Check(t is TRttiProcedureType, 'Not a procedure variable: ' + aTypeInfo^.Name);
+    proc := t as TRttiProcedureType;
+
+    { arguments might be modified by Invoke (Note: Copy() does not uniquify the
+      IValueData of managed types) }
+    SetLength(input, Length(aInputArgs));
+    for i := 0 to High(input) do
+      input[i] := CopyValue(aInputArgs[i]);
+
+    writeln('calling ', name);
+    res := proc.Invoke(callable, aInputArgs);
+    writeln('called ', name);
+    CheckEquals(aIndex, cls.CalledMethod, 'Wrong method called for ' + name);
+    Check(EqualValues(cls.ResultValue, res), 'Reported result value differs from returned for ' + name);
+    Check(EqualValues(aResult, res), 'Expected result value differs from returned for ' + name);
+    CheckEquals(Length(aInputArgs), Length(cls.InputArgs), 'Count of input args differs for ' + name);
+    CheckEquals(Length(cls.OutputArgs), Length(cls.InOutMapping), 'Count of output args and in-out-mapping differs for ' + name);
+    CheckEquals(Length(aOutputArgs), Length(cls.OutputArgs), 'Count of output args differs for ' + name);
+    for i := 0 to High(aInputArgs) do begin
+      Check(EqualValues(input[i], cls.InputArgs[i]), Format('Input argument %d differs for %s', [i + 1, name]));
+    end;
+    for i := 0 to High(aOutputArgs) do begin
+      Check(EqualValues(aOutputArgs[i], cls.OutputArgs[i]), Format('Output argument %d differs for %s', [i + 1, name]));
+      Check(EqualValues(aOutputArgs[i], aInputArgs[cls.InOutMapping[i]]), Format('New output argument %d differs from expected output for %s', [i + 1, name]));
+    end;
+  finally
+    context.Free;
+  end;
+end;
+
+{$ifndef InLazIDE}
+{$ifdef fpc}generic{$endif} procedure TTestInvoke.GenDoMethodInvoke<T>(aInst: TObject; aMethod: T; aIndex: SizeInt; aInputArgs, aOutputArgs: TValueArray; aResult: TValue);
+begin
+  DoMethodInvoke(aInst, TMethod(aMethod), TypeInfo(T), aIndex, aInputArgs, aOutputArgs, aResult);
+end;
+
+{$ifdef fpc}generic{$endif} procedure TTestInvoke.GenDoProcVarInvoke<T>(aInst: TObject; aProc: T; aIndex: SizeInt; aInputArgs, aOutputArgs: TValueArray; aResult: TValue);
+begin
+  DoProcVarInvoke(aInst, CodePointer(aProc), TypeInfo(T), aIndex, aInputArgs, aOutputArgs, aResult);
+end;
+
+{$ifdef fpc}generic{$endif} function TTestInvoke.GetRecValue<T>(aReverse: Boolean): TValue;
+var
+  i: LongInt;
+  arr: array of Byte;
+begin
+  SetLength(arr, SizeOf(T));
+  RandSeed := $54827982;
+  if not aReverse then begin
+    for i := 0 to High(arr) do
+      arr[i] := Random($ff);
+  end else begin
+    for i := High(arr) downto 0 do
+      arr[i] := Random($ff);
+  end;
+  TValue.Make(@arr[0], PTypeInfo(TypeInfo(T)), Result);
+end;
+{$endif}
+
+function GetIntValue(aValue: SizeInt): TValue;
+begin
+  Result := TValue.{$ifdef fpc}specialize{$endif}From<SizeInt>(aValue);
+end;
+
+function GetAnsiString(const aValue: AnsiString): TValue;
+begin
+  Result := TValue.{$ifdef fpc}specialize{$endif}From<AnsiString>(aValue);
+end;
+
+function GetShortString(const aValue: ShortString): TValue;
+begin
+  Result := TValue.{$ifdef fpc}specialize{$endif}From<ShortString>(aValue);
+end;
+
+{$ifdef fpc}
+function GetArray(const aArg: array of SizeInt): TValue;
+begin
+  Result := specialize OpenArrayToDynArrayValue<SizeInt>(aArg);
+end;
+{$endif}
+
+procedure TTestInvoke.TestIntfMethods;
+begin
+  DoIntfInvoke(1, [], [], TValue.Empty);
+  DoIntfInvoke(2, [], [], TValue.{$ifdef fpc}specialize{$endif}From<SizeInt>(42));
+
+  DoIntfInvoke(3, [
+    GetIntValue(7), GetIntValue(2), GetIntValue(5), GetIntValue(1), GetIntValue(10), GetIntValue(8), GetIntValue(6), GetIntValue(3), GetIntValue(9), GetIntValue(3)
+    ], [], GetIntValue(42));
+
+  DoIntfInvoke(4, [
+    TValue.{$ifdef fpc}specialize{$endif}From<AnsiString>('Alpha'),
+    TValue.{$ifdef fpc}specialize{$endif}From<UnicodeString>('Beta'),
+    TValue.{$ifdef fpc}specialize{$endif}From<WideString>('Gamma'),
+    TValue.{$ifdef fpc}specialize{$endif}From<ShortString>('Delta')
+    ], [], TValue.Empty);
+
+  DoIntfInvoke(5, [], [], TValue.{$ifdef fpc}specialize{$endif}From<AnsiString>('Hello World'));
+  DoIntfInvoke(6, [], [], TValue.{$ifdef fpc}specialize{$endif}From<UnicodeString>('Hello World'));
+  DoIntfInvoke(7, [], [], TValue.{$ifdef fpc}specialize{$endif}From<WideString>('Hello World'));
+  DoIntfInvoke(8, [], [], TValue.{$ifdef fpc}specialize{$endif}From<ShortString>('Hello World'));
+
+  DoIntfInvoke(9, [
+    GetIntValue($1234), GetIntValue($4321), GetIntValue($8765), GetIntValue($5678)
+    ], [
+    GetIntValue($1234), GetIntValue($5678)
+    ], TValue.Empty);
+
+  DoIntfInvoke(10, [
+    GetAnsiString('Alpha'), GetAnsiString('Beta'), GetAnsiString(''), GetAnsiString('Delta')
+    ], [
+    GetAnsiString('Foo'), GetAnsiString('Bar')
+    ], TValue.Empty);
+
+  DoIntfInvoke(11, [
+    GetShortString('Alpha'), GetShortString('Beta'), GetShortString(''), GetShortString('Delta')
+    ], [
+    GetShortString('Foo'), GetShortString('Bar')
+    ], TValue.Empty);
+
+{$ifdef fpc}
+  DoIntfInvoke(12, [
+    GetArray([$1234, $2345, $3456, $4567]), GetArray([$4321, $5431, $6543, $7654]), GetArray([$5678, $6789, $7890, $8901]), GetArray([$8765, $7654, $6543, $5432])
+    ], [
+    GetArray([$4321, $4322, $4323, $4324]), GetArray([$9876, $9877, $9878, $9879])
+    ], TValue.Empty);
+{$endif}
+end;
+
+procedure TTestInvoke.TestIntfMethodsRecs;
+begin
+  DoIntfInvoke(1 or TTestInterfaceClass.RecSizeMarker,
+    [{$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord1>(False)], [],
+    {$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord1>(True));
+
+  DoIntfInvoke(2 or TTestInterfaceClass.RecSizeMarker,
+    [{$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord2>(False)], [],
+    {$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord2>(True));
+
+  DoIntfInvoke(3 or TTestInterfaceClass.RecSizeMarker,
+    [{$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord3>(False)], [],
+    {$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord3>(True));
+
+  DoIntfInvoke(4 or TTestInterfaceClass.RecSizeMarker,
+    [{$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord4>(False)], [],
+    {$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord4>(True));
+
+  DoIntfInvoke(5 or TTestInterfaceClass.RecSizeMarker,
+    [{$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord5>(False)], [],
+    {$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord5>(True));
+
+  DoIntfInvoke(6 or TTestInterfaceClass.RecSizeMarker,
+    [{$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord6>(False)], [],
+    {$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord6>(True));
+
+  DoIntfInvoke(7 or TTestInterfaceClass.RecSizeMarker,
+    [{$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord7>(False)], [],
+    {$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord7>(True));
+
+  DoIntfInvoke(8 or TTestInterfaceClass.RecSizeMarker,
+    [{$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord8>(False)], [],
+    {$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord8>(True));
+
+  DoIntfInvoke(9 or TTestInterfaceClass.RecSizeMarker,
+    [{$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord9>(False)], [],
+    {$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord9>(True));
+
+  DoIntfInvoke(10 or TTestInterfaceClass.RecSizeMarker,
+    [{$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord10>(False)], [],
+    {$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord10>(True));
+end;
+
+procedure TTestInvoke.TestMethodVars;
+var
+  cls: TTestInterfaceClass;
+begin
+  cls := TTestInterfaceClass.Create;
+  try
+    {$ifdef fpc}specialize{$endif} GenDoMethodInvoke<TMethodTest1>(cls, {$ifdef fpc}@{$endif}cls.Test1, 1, [], [], TValue.Empty);
+    {$ifdef fpc}specialize{$endif} GenDoMethodInvoke<TMethodTest2>(cls, {$ifdef fpc}@{$endif}cls.Test2, 2, [], [], TValue.{$ifdef fpc}{$ifdef fpc}specialize{$endif}{$endif}From<SizeInt>(42));
+
+    {$ifdef fpc}specialize{$endif} GenDoMethodInvoke<TMethodTest3>(cls, {$ifdef fpc}@{$endif}cls.Test3, 3, [
+      GetIntValue(7), GetIntValue(2), GetIntValue(5), GetIntValue(1), GetIntValue(10), GetIntValue(8), GetIntValue(6), GetIntValue(3), GetIntValue(9), GetIntValue(3)
+      ], [], GetIntValue(42));
+
+    {$ifdef fpc}specialize{$endif} GenDoMethodInvoke<TMethodTest4>(cls, {$ifdef fpc}@{$endif}cls.Test4, 4, [
+      TValue.{$ifdef fpc}specialize{$endif}From<AnsiString>('Alpha'),
+      TValue.{$ifdef fpc}specialize{$endif}From<UnicodeString>('Beta'),
+      TValue.{$ifdef fpc}specialize{$endif}From<WideString>('Gamma'),
+      TValue.{$ifdef fpc}specialize{$endif}From<ShortString>('Delta')
+      ], [], TValue.Empty);
+
+    {$ifdef fpc}specialize{$endif} GenDoMethodInvoke<TMethodTest5>(cls, {$ifdef fpc}@{$endif}cls.Test5, 5, [], [], TValue.{$ifdef fpc}specialize{$endif}From<AnsiString>('Hello World'));
+    {$ifdef fpc}specialize{$endif} GenDoMethodInvoke<TMethodTest6>(cls, {$ifdef fpc}@{$endif}cls.Test6, 6, [], [], TValue.{$ifdef fpc}specialize{$endif}From<UnicodeString>('Hello World'));
+    {$ifdef fpc}specialize{$endif} GenDoMethodInvoke<TMethodTest7>(cls, {$ifdef fpc}@{$endif}cls.Test7, 7, [], [], TValue.{$ifdef fpc}specialize{$endif}From<WideString>('Hello World'));
+    {$ifdef fpc}specialize{$endif} GenDoMethodInvoke<TMethodTest8>(cls, {$ifdef fpc}@{$endif}cls.Test8, 8, [], [], TValue.{$ifdef fpc}specialize{$endif}From<ShortString>('Hello World'));
+
+    {$ifdef fpc}specialize{$endif} GenDoMethodInvoke<TMethodTest9>(cls, {$ifdef fpc}@{$endif}cls.Test9, 9, [
+      GetIntValue($1234), GetIntValue($4321), GetIntValue($8765), GetIntValue($5678)
+      ], [
+      GetIntValue($1234), GetIntValue($5678)
+      ], TValue.Empty);
+
+    {$ifdef fpc}specialize{$endif} GenDoMethodInvoke<TMethodTest10>(cls, {$ifdef fpc}@{$endif}cls.Test10, 10, [
+      GetAnsiString('Alpha'), GetAnsiString('Beta'), GetAnsiString(''), GetAnsiString('Delta')
+      ], [
+      GetAnsiString('Foo'), GetAnsiString('Bar')
+      ], TValue.Empty);
+
+    {$ifdef fpc}specialize{$endif} GenDoMethodInvoke<TMethodTest11>(cls, {$ifdef fpc}@{$endif}cls.Test11, 11, [
+      GetShortString('Alpha'), GetShortString('Beta'), GetShortString(''), GetShortString('Delta')
+      ], [
+      GetShortString('Foo'), GetShortString('Bar')
+      ], TValue.Empty);
+
+  {$ifdef fpc}
+    specialize GenDoMethodInvoke<TMethodTest12>(cls, {$ifdef fpc}@{$endif}cls.Test12, 12, [
+      GetArray([$1234, $2345, $3456, $4567]), GetArray([$4321, $5431, $6543, $7654]), GetArray([$5678, $6789, $7890, $8901]), GetArray([$8765, $7654, $6543, $5432])
+      ], [
+      GetArray([$4321, $4322, $4323, $4324]), GetArray([$9876, $9877, $9878, $9879])
+      ], TValue.Empty);
+  {$endif}
+  finally
+    cls.Free;
+  end;
+end;
+
+procedure TTestInvoke.TestMethodVarsRecs;
+var
+  cls: TTestInterfaceClass;
+begin
+  cls := TTestInterfaceClass.Create;
+  try
+    {$ifdef fpc}specialize{$endif} GenDoMethodInvoke<TMethodTestRecSize1>(cls, {$ifdef fpc}@{$endif}cls.TestRecSize1, 1 or TTestInterfaceClass.RecSizeMarker,
+      [{$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord1>(False)], [],
+      {$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord1>(True));
+
+    {$ifdef fpc}specialize{$endif} GenDoMethodInvoke<TMethodTestRecSize2>(cls, {$ifdef fpc}@{$endif}cls.TestRecSize2, 2 or TTestInterfaceClass.RecSizeMarker,
+      [{$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord2>(False)], [],
+      {$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord2>(True));
+
+    {$ifdef fpc}specialize{$endif} GenDoMethodInvoke<TMethodTestRecSize3>(cls, {$ifdef fpc}@{$endif}cls.TestRecSize3, 3 or TTestInterfaceClass.RecSizeMarker,
+      [{$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord3>(False)], [],
+      {$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord3>(True));
+
+    {$ifdef fpc}specialize{$endif} GenDoMethodInvoke<TMethodTestRecSize4>(cls, {$ifdef fpc}@{$endif}cls.TestRecSize4, 4 or TTestInterfaceClass.RecSizeMarker,
+      [{$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord4>(False)], [],
+      {$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord4>(True));
+
+    {$ifdef fpc}specialize{$endif} GenDoMethodInvoke<TMethodTestRecSize5>(cls, {$ifdef fpc}@{$endif}cls.TestRecSize5, 5 or TTestInterfaceClass.RecSizeMarker,
+      [{$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord5>(False)], [],
+      {$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord5>(True));
+
+    {$ifdef fpc}specialize{$endif} GenDoMethodInvoke<TMethodTestRecSize6>(cls, {$ifdef fpc}@{$endif}cls.TestRecSize6, 6 or TTestInterfaceClass.RecSizeMarker,
+      [{$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord6>(False)], [],
+      {$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord6>(True));
+
+    {$ifdef fpc}specialize{$endif} GenDoMethodInvoke<TMethodTestRecSize7>(cls, {$ifdef fpc}@{$endif}cls.TestRecSize7, 7 or TTestInterfaceClass.RecSizeMarker,
+      [{$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord7>(False)], [],
+      {$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord7>(True));
+
+    {$ifdef fpc}specialize{$endif} GenDoMethodInvoke<TMethodTestRecSize8>(cls, {$ifdef fpc}@{$endif}cls.TestRecSize8, 8 or TTestInterfaceClass.RecSizeMarker,
+      [{$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord8>(False)], [],
+      {$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord8>(True));
+
+    {$ifdef fpc}specialize{$endif} GenDoMethodInvoke<TMethodTestRecSize9>(cls, {$ifdef fpc}@{$endif}cls.TestRecSize9, 9 or TTestInterfaceClass.RecSizeMarker,
+      [{$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord9>(False)], [],
+      {$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord9>(True));
+
+    {$ifdef fpc}specialize{$endif} GenDoMethodInvoke<TMethodTestRecSize10>(cls, {$ifdef fpc}@{$endif}cls.TestRecSize10, 10 or TTestInterfaceClass.RecSizeMarker,
+      [{$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord10>(False)], [],
+      {$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord10>(True));
+  finally
+    cls.Free;
+  end;
+end;
+
+procedure TTestInvoke.TestProcVars;
+var
+  cls: TTestInterfaceClass;
+begin
+  cls := TTestInterfaceClass.Create;
+  try
+    {$ifdef fpc}specialize{$endif} GenDoProcVarInvoke<TProcVarTest1>(cls, {$ifdef fpc}@{$endif}ProcTest1, 1, [], [], TValue.Empty);
+    {$ifdef fpc}specialize{$endif} GenDoProcVarInvoke<TProcVarTest2>(cls, {$ifdef fpc}@{$endif}ProcTest2, 2, [], [], TValue.{$ifdef fpc}{$ifdef fpc}specialize{$endif}{$endif}From<SizeInt>(42));
+
+    {$ifdef fpc}specialize{$endif} GenDoProcVarInvoke<TProcVarTest3>(cls, {$ifdef fpc}@{$endif}ProcTest3, 3, [
+      GetIntValue(7), GetIntValue(2), GetIntValue(5), GetIntValue(1), GetIntValue(10), GetIntValue(8), GetIntValue(6), GetIntValue(3), GetIntValue(9), GetIntValue(3)
+      ], [], GetIntValue(42));
+
+    {$ifdef fpc}specialize{$endif} GenDoProcVarInvoke<TProcVarTest4>(cls, {$ifdef fpc}@{$endif}ProcTest4, 4, [
+      TValue.{$ifdef fpc}specialize{$endif}From<AnsiString>('Alpha'),
+      TValue.{$ifdef fpc}specialize{$endif}From<UnicodeString>('Beta'),
+      TValue.{$ifdef fpc}specialize{$endif}From<WideString>('Gamma'),
+      TValue.{$ifdef fpc}specialize{$endif}From<ShortString>('Delta')
+      ], [], TValue.Empty);
+
+    {$ifdef fpc}specialize{$endif} GenDoProcVarInvoke<TProcVarTest5>(cls, {$ifdef fpc}@{$endif}ProcTest5, 5, [], [], TValue.{$ifdef fpc}specialize{$endif}From<AnsiString>('Hello World'));
+    {$ifdef fpc}specialize{$endif} GenDoProcVarInvoke<TProcVarTest6>(cls, {$ifdef fpc}@{$endif}ProcTest6, 6, [], [], TValue.{$ifdef fpc}specialize{$endif}From<UnicodeString>('Hello World'));
+    {$ifdef fpc}specialize{$endif} GenDoProcVarInvoke<TProcVarTest7>(cls, {$ifdef fpc}@{$endif}ProcTest7, 7, [], [], TValue.{$ifdef fpc}specialize{$endif}From<WideString>('Hello World'));
+    {$ifdef fpc}specialize{$endif} GenDoProcVarInvoke<TProcVarTest8>(cls, {$ifdef fpc}@{$endif}ProcTest8, 8, [], [], TValue.{$ifdef fpc}specialize{$endif}From<ShortString>('Hello World'));
+
+    {$ifdef fpc}specialize{$endif} GenDoProcVarInvoke<TProcVarTest9>(cls, {$ifdef fpc}@{$endif}ProcTest9, 9, [
+      GetIntValue($1234), GetIntValue($4321), GetIntValue($8765), GetIntValue($5678)
+      ], [
+      GetIntValue($1234), GetIntValue($5678)
+      ], TValue.Empty);
+
+    {$ifdef fpc}specialize{$endif} GenDoProcVarInvoke<TProcVarTest10>(cls, {$ifdef fpc}@{$endif}ProcTest10, 10, [
+      GetAnsiString('Alpha'), GetAnsiString('Beta'), GetAnsiString(''), GetAnsiString('Delta')
+      ], [
+      GetAnsiString('Foo'), GetAnsiString('Bar')
+      ], TValue.Empty);
+
+    {$ifdef fpc}specialize{$endif} GenDoProcVarInvoke<TProcVarTest11>(cls, {$ifdef fpc}@{$endif}ProcTest11, 11, [
+      GetShortString('Alpha'), GetShortString('Beta'), GetShortString(''), GetShortString('Delta')
+      ], [
+      GetShortString('Foo'), GetShortString('Bar')
+      ], TValue.Empty);
+
+  {$ifdef fpc}
+    specialize GenDoProcVarInvoke<TProcVarTest12>(cls, {$ifdef fpc}@{$endif}ProcTest12, 12, [
+      GetArray([$1234, $2345, $3456, $4567]), GetArray([$4321, $5431, $6543, $7654]), GetArray([$5678, $6789, $7890, $8901]), GetArray([$8765, $7654, $6543, $5432])
+      ], [
+      GetArray([$4321, $4322, $4323, $4324]), GetArray([$9876, $9877, $9878, $9879])
+      ], TValue.Empty);
+  {$endif}
+  finally
+    cls.Free;
+  end;
+end;
+
+procedure TTestInvoke.TestProcVarsRecs;
+var
+  cls: TTestInterfaceClass;
+begin
+  try
+  cls := TTestInterfaceClass.Create;
+  try
+    {$ifdef fpc}specialize{$endif} GenDoProcVarInvoke<TProcVarTestRecSize1>(cls, {$ifdef fpc}@{$endif}ProcTestRecSize1, 1 or TTestInterfaceClass.RecSizeMarker,
+      [{$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord1>(False)], [],
+      {$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord1>(True));
+
+    {$ifdef fpc}specialize{$endif} GenDoProcVarInvoke<TProcVarTestRecSize2>(cls, {$ifdef fpc}@{$endif}ProcTestRecSize2, 2 or TTestInterfaceClass.RecSizeMarker,
+      [{$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord2>(False)], [],
+      {$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord2>(True));
+
+    {$ifdef fpc}specialize{$endif} GenDoProcVarInvoke<TProcVarTestRecSize3>(cls, {$ifdef fpc}@{$endif}ProcTestRecSize3, 3 or TTestInterfaceClass.RecSizeMarker,
+      [{$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord3>(False)], [],
+      {$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord3>(True));
+
+    {$ifdef fpc}specialize{$endif} GenDoProcVarInvoke<TProcVarTestRecSize4>(cls, {$ifdef fpc}@{$endif}ProcTestRecSize4, 4 or TTestInterfaceClass.RecSizeMarker,
+      [{$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord4>(False)], [],
+      {$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord4>(True));
+
+    {$ifdef fpc}specialize{$endif} GenDoProcVarInvoke<TProcVarTestRecSize5>(cls, {$ifdef fpc}@{$endif}ProcTestRecSize5, 5 or TTestInterfaceClass.RecSizeMarker,
+      [{$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord5>(False)], [],
+      {$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord5>(True));
+
+    {$ifdef fpc}specialize{$endif} GenDoProcVarInvoke<TProcVarTestRecSize6>(cls, {$ifdef fpc}@{$endif}ProcTestRecSize6, 6 or TTestInterfaceClass.RecSizeMarker,
+      [{$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord6>(False)], [],
+      {$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord6>(True));
+
+    {$ifdef fpc}specialize{$endif} GenDoProcVarInvoke<TProcVarTestRecSize7>(cls, {$ifdef fpc}@{$endif}ProcTestRecSize7, 7 or TTestInterfaceClass.RecSizeMarker,
+      [{$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord7>(False)], [],
+      {$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord7>(True));
+
+    {$ifdef fpc}specialize{$endif} GenDoProcVarInvoke<TProcVarTestRecSize8>(cls, {$ifdef fpc}@{$endif}ProcTestRecSize8, 8 or TTestInterfaceClass.RecSizeMarker,
+      [{$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord8>(False)], [],
+      {$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord8>(True));
+
+    {$ifdef fpc}specialize{$endif} GenDoProcVarInvoke<TProcVarTestRecSize9>(cls, {$ifdef fpc}@{$endif}ProcTestRecSize9, 9 or TTestInterfaceClass.RecSizeMarker,
+      [{$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord9>(False)], [],
+      {$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord9>(True));
+
+    {$ifdef fpc}specialize{$endif} GenDoProcVarInvoke<TProcVarTestRecSize10>(cls, {$ifdef fpc}@{$endif}ProcTestRecSize10, 10 or TTestInterfaceClass.RecSizeMarker,
+      [{$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord10>(False)], [],
+      {$ifdef fpc}specialize{$endif} GetRecValue<TTestRecord10>(True));
+  finally
+    cls.Free;
+  end;
+  except
+    DumpExceptionBacktrace(output);
+    raise;
+  end;
+end;
+
 begin
 begin
 {$ifdef fpc}
 {$ifdef fpc}
   RegisterTest(TTestInvoke);
   RegisterTest(TTestInvoke);