Skip to content

Commit 708ee15

Browse files
sglienkevincentparrett
authored andcommitted
fixed potential memory leak when using parameter matching
1 parent f69a251 commit 708ee15

File tree

3 files changed

+36
-11
lines changed

3 files changed

+36
-11
lines changed

Source/Core/Mocking/Spring.Mocking.Interceptor.pas

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,7 @@ procedure TMockInterceptor.InterceptArrange(const invocation: IInvocation);
408408
fMatch := CreateArgMatch(invocation.Arguments, invocation.Method.GetParameters);
409409
fExpectedCalls.AddRange(invocation.Method, CreateMethodCalls(invocation.Method));
410410
finally
411+
CleanupArguments(invocation.Arguments);
411412
fState := TMockState.Act;
412413
fMatch := nil;
413414
end;
@@ -432,6 +433,7 @@ procedure TMockInterceptor.InterceptAssert(const invocation: IInvocation);
432433
else
433434
callCount := 0;
434435
finally
436+
CleanupArguments(invocation.Arguments);
435437
fState := TMockState.Act;
436438
fMatch := nil;
437439
end;

Source/Core/Mocking/Spring.Mocking.Matching.pas

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ TAny = record
130130
class operator Implicit(const value: TAny): string; overload;
131131
end;
132132

133+
procedure CleanupArguments(const arguments: TArray<TValue>);
134+
133135
implementation
134136

135137
uses
@@ -167,20 +169,17 @@ function GetIndexString(const v: TValue): Integer;
167169

168170
function GetIndexObject(const v: TValue): Integer;
169171
begin
170-
Result := v.AsType<TIndexWrapper>.fIndex;
171-
v.AsType<TIndexWrapper>.Free;
172+
Result := TIndexWrapper(TValueData(v).FAsObject).fIndex;
172173
end;
173174

174175
function GetIndexInterface(const v: TValue): Integer;
175176
begin
176-
Result := (v.AsType<IInterface> as TIndexWrapper).fIndex;
177-
PValue(@v)^ := TValue.Empty;
177+
Result := (PInterface(TValueData(v).FValueData.GetReferenceToRawData)^ as TIndexWrapper).fIndex;
178178
end;
179179

180180
function GetIndexPointer(const v: TValue): Integer;
181181
begin
182-
Result := v.AsType<TIndexWrapper>.fIndex;
183-
v.AsType<TIndexWrapper>.Free;
182+
Result := Integer(TValueData(v).FAsPointer);
184183
end;
185184

186185
function GetIndexRecord(const v: TValue): Integer;
@@ -255,7 +254,7 @@ procedure SetIndexString(typeInfo: PTypeInfo; index: Integer; var Result);
255254
procedure SetIndexObject(typeInfo: PTypeInfo; //FI:O804
256255
index: Integer; var Result);
257256
begin
258-
TObject(PPointer(@Result)^) := TIndexWrapper.Create(index);
257+
TObject(Result) := TIndexWrapper.Create(index);
259258
end;
260259

261260
procedure SetIndexInterface(typeInfo: PTypeInfo; //FI:O804
@@ -267,7 +266,7 @@ procedure SetIndexInterface(typeInfo: PTypeInfo; //FI:O804
267266
procedure SetIndexPointer(typeInfo: PTypeInfo; //FI:O804
268267
index: Integer; var Result);
269268
begin
270-
Pointer(Result) := TIndexWrapper.Create(index);
269+
NativeInt(Result) := index;
271270
end;
272271

273272
procedure SetIndexRecord(typeInfo: PTypeInfo; index: Integer; var Result);
@@ -301,6 +300,18 @@ procedure SetIndexVariant(typeInfo: PTypeInfo; //FI:O804
301300
PVariant(@Result)^ := index;
302301
end;
303302

303+
procedure CleanupArguments(const arguments: TArray<TValue>);
304+
var
305+
i: Integer;
306+
begin
307+
for i := 0 to High(arguments) do
308+
if arguments[i].IsType(TypeInfo(TIndexWrapper)) then
309+
begin
310+
TObject(TValueData(arguments[i]).FAsObject).Free;
311+
TValueData(arguments[i]).FAsObject := nil;
312+
end;
313+
end;
314+
304315

305316
{$REGION 'TMatcherFactory'}
306317

@@ -375,7 +386,7 @@ class function TMatcherFactory.GetIndex(const v: TValue): Integer;
375386
GetIndexFail, GetIndexOrdinal, GetIndexObject, GetIndexFail, GetIndexString,
376387
GetIndexString, GetIndexString, GetIndexVariant, GetIndexArray, GetIndexRecord,
377388
GetIndexInterface, GetIndexOrdinal, GetIndexArray, GetIndexString, GetIndexFail,
378-
GetIndexPointer, GetIndexFail {$IF Declared(tkMRecord)}, GetIndexFail{$IFEND});
389+
GetIndexObject, GetIndexFail {$IF Declared(tkMRecord)}, GetIndexFail{$IFEND});
379390
begin
380391
Result := Handlers[TValueData(v).FTypeInfo.Kind](v) - 1;
381392
end;
@@ -387,7 +398,7 @@ class procedure TMatcherFactory.SetIndex(typeInfo: PTypeInfo; index: Integer; va
387398
SetIndexFail, SetIndexOrdinal, SetIndexObject, SetIndexFail, SetIndexString,
388399
SetIndexString, SetIndexString, SetIndexVariant, SetIndexArray, SetIndexRecord,
389400
SetIndexInterface, SetIndexOrdinal, SetIndexArray, SetIndexString, SetIndexFail,
390-
SetIndexPointer, SetIndexFail {$IF Declared(tkMRecord)}, SetIndexFail{$IFEND});
401+
SetIndexObject, SetIndexFail {$IF Declared(tkMRecord)}, SetIndexFail{$IFEND});
391402
begin
392403
Handlers[typeInfo.Kind](typeInfo, index + 1, Result);
393404
end;
@@ -469,7 +480,7 @@ class function TArg.IsEqual<T>(const value: T): T;
469480
Result := TMatcherFactory.CreateMatcher<T>(
470481
function(const arg: TValue): Boolean
471482
begin
472-
Result := arg.Convert<T>.Equals(TValue.From<T>(value));
483+
Result := arg.Convert(TypeInfo(T)).Equals(TValue.From(@value, TypeInfo(T)));
473484
end);
474485
end;
475486

Tests/Source/Core/Spring.Tests.Mocking.pas

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ TParameterMatchingTests = class(TTestCase)
5050
procedure TestSet;
5151

5252
procedure ReturnsMultipleValues;
53+
procedure WrapperObjectsNotLeaking;
5354
end;
5455

5556
ReceivedChecksForInputValueOfVarParams = class(TTestCase)
@@ -107,6 +108,7 @@ implementation
107108
procedure TestDynArray(const v: TArray<string>);
108109
procedure TestEnum(const value: TTestEnum);
109110
procedure TestSet(const n: Integer; const value: TTestSet; const i: Integer = 0);
111+
procedure TestObject(const obj: TObject);
110112
function GetNext: Integer;
111113

112114
function GetEvent: IInvokableNotifyEvent<Integer>;
@@ -396,6 +398,16 @@ procedure TParameterMatchingTests.VerifyChecksParameterValuesProperly;
396398
end);
397399
end;
398400

401+
procedure TParameterMatchingTests.WrapperObjectsNotLeaking;
402+
var
403+
mock: Mock<IMockTest>;
404+
begin
405+
mock.Setup.Executes.When(Args.Any).TestObject(Arg.IsAny<TObject>);
406+
mock.Instance.TestObject(nil);
407+
mock.Received.TestObject(Arg = nil);
408+
Pass;
409+
end;
410+
399411
{$ENDREGION}
400412

401413

0 commit comments

Comments
 (0)