Skip to content

Commit 0547eda

Browse files
committed
Optimized a memory usage in the ReflectionHelpers.GetBestFitMethod method
1 parent 9739da3 commit 0547eda

File tree

3 files changed

+96
-57
lines changed

3 files changed

+96
-57
lines changed

src/MsieJavaScriptEngine/Helpers/ReflectionHelpers.cs

Lines changed: 77 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
using System;
2+
#if NET45_OR_GREATER || NETSTANDARD
3+
using System.Buffers;
4+
#endif
25
using System.Collections.Generic;
36
using System.Linq;
47
using System.Reflection;
8+
#if NET40
9+
10+
using PolyfillsForOldDotNet.System.Buffers;
11+
#endif
512

613
using MsieJavaScriptEngine.Utilities;
714

@@ -113,6 +120,11 @@ public static void FixPropertyValueType(ref object value, PropertyInfo property)
113120
public static void FixArgumentTypes(ref object[] argValues, ParameterInfo[] parameters)
114121
{
115122
int argCount = argValues.Length;
123+
if (argCount == 0)
124+
{
125+
return;
126+
}
127+
116128
int parameterCount = parameters.Length;
117129

118130
for (int argIndex = 0; argIndex < argCount; argIndex++)
@@ -147,54 +159,72 @@ public static void FixArgumentTypes(ref object[] argValues, ParameterInfo[] para
147159

148160
public static MethodBase GetBestFitMethod(MethodBase[] methods, object[] argValues)
149161
{
150-
MethodWithMetadata[] methodCandidates = methods
151-
.Select(m => new MethodWithMetadata
152-
{
153-
Method = m,
154-
ParameterTypes = m.GetParameters()
155-
.Select(p => p.ParameterType)
156-
.ToArray()
157-
})
158-
.ToArray()
159-
;
160-
int argCount = argValues.Length;
161-
MethodWithMetadata[] sameArityMethods = methodCandidates
162-
.Where(m => m.ParameterTypes.Length == argCount)
163-
.ToArray()
164-
;
165-
166-
int sameArityMethodCount = sameArityMethods.Length;
167-
if (sameArityMethodCount == 0)
162+
int methodCount = methods.Length;
163+
if (methodCount == 0)
168164
{
169165
return null;
170166
}
171167

172-
Type[] argTypes = argValues
173-
.Select(a => a != null ? a.GetType() : typeof(object))
174-
.ToArray()
175-
;
176-
var compatibleMethods = new List<MethodWithMetadata>();
177-
178-
for (int methodIndex = 0; methodIndex < sameArityMethodCount; methodIndex++)
168+
if (methodCount == 1)
179169
{
180-
MethodWithMetadata method = sameArityMethods[methodIndex];
181-
ushort compatibilityScore;
170+
MethodBase method = methods[0];
171+
ParameterInfo[] parameters = method.GetParameters();
182172

183-
if (CompareParameterTypes(argValues, argTypes, method.ParameterTypes, out compatibilityScore))
173+
MethodBase bestFitMethod = null;
174+
if (CompareParameterTypes(argValues, parameters, out _))
184175
{
185-
method.CompatibilityScore = compatibilityScore;
186-
compatibleMethods.Add(method);
176+
bestFitMethod = method;
187177
}
178+
179+
return bestFitMethod;
188180
}
189181

190-
int compatibleMethodCount = compatibleMethods.Count;
191-
if (compatibleMethodCount > 0)
182+
MethodWithMetadata[] compatibleMethods = null;
183+
int compatibleMethodCount = 0;
184+
185+
var methodArrayPool = ArrayPool<MethodWithMetadata>.Shared;
186+
MethodWithMetadata[] buffer = methodArrayPool.Rent(methodCount);
187+
188+
try
192189
{
193-
if (compatibleMethodCount == 1)
190+
for (int methodIndex = 0; methodIndex < methodCount; methodIndex++)
191+
{
192+
MethodBase method = methods[methodIndex];
193+
ParameterInfo[] parameters = method.GetParameters();
194+
ushort compatibilityScore;
195+
196+
if (CompareParameterTypes(argValues, parameters, out compatibilityScore))
197+
{
198+
compatibleMethodCount++;
199+
200+
int compatibleMethodIndex = compatibleMethodCount - 1;
201+
buffer[compatibleMethodIndex] = new MethodWithMetadata
202+
{
203+
Method = method,
204+
CompatibilityScore = compatibilityScore
205+
};
206+
}
207+
}
208+
209+
if (compatibleMethodCount > 0)
194210
{
195-
return compatibleMethods[0].Method;
211+
if (compatibleMethodCount == 1)
212+
{
213+
return buffer[0].Method;
214+
}
215+
216+
compatibleMethods = new MethodWithMetadata[compatibleMethodCount];
217+
Array.Copy(buffer, compatibleMethods, compatibleMethodCount);
196218
}
219+
}
220+
finally
221+
{
222+
bool clearArray = compatibleMethodCount > 0;
223+
methodArrayPool.Return(buffer, clearArray);
224+
}
197225

226+
if (compatibleMethods != null)
227+
{
198228
MethodWithMetadata bestFitMethod = compatibleMethods
199229
.OrderByDescending(m => m.CompatibilityScore)
200230
.First()
@@ -206,24 +236,29 @@ public static MethodBase GetBestFitMethod(MethodBase[] methods, object[] argValu
206236
return null;
207237
}
208238

209-
private static bool CompareParameterTypes(object[] argValues, Type[] argTypes, Type[] parameterTypes,
239+
private static bool CompareParameterTypes(object[] argValues, ParameterInfo[] parameters,
210240
out ushort compatibilityScore)
211241
{
212-
int argValueCount = argValues.Length;
213-
int argTypeCount = argTypes.Length;
214-
int parameterCount = parameterTypes.Length;
242+
int argCount = argValues.Length;
243+
int parameterCount = parameters.Length;
215244
compatibilityScore = 0;
216245

217-
if (argValueCount != argTypeCount || argTypeCount != parameterCount)
246+
if (argCount != parameterCount)
218247
{
219248
return false;
220249
}
250+
else if (argCount == 0)
251+
{
252+
compatibilityScore = ushort.MaxValue;
253+
return true;
254+
}
221255

222-
for (int argIndex = 0; argIndex < argValueCount; argIndex++)
256+
for (int argIndex = 0; argIndex < argCount; argIndex++)
223257
{
224258
object argValue = argValues[argIndex];
225-
Type argType = argTypes[argIndex];
226-
Type parameterType = parameterTypes[argIndex];
259+
Type argType = argValue != null ? argValue.GetType() : typeof(object);
260+
ParameterInfo parameter = parameters[argIndex];
261+
Type parameterType = parameter.ParameterType;
227262

228263
if (argType == parameterType)
229264
{
@@ -239,8 +274,6 @@ private static bool CompareParameterTypes(object[] argValues, Type[] argTypes, T
239274
{
240275
return false;
241276
}
242-
243-
continue;
244277
}
245278
}
246279

@@ -297,12 +330,6 @@ public MethodBase Method
297330
set;
298331
}
299332

300-
public Type[] ParameterTypes
301-
{
302-
get;
303-
set;
304-
}
305-
306333
/// TODO: In future will need to change type to <c>double</c>
307334
public ushort CompatibilityScore
308335
{

src/MsieJavaScriptEngine/MsieJavaScriptEngine.csproj

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
<IncludeSymbols>true</IncludeSymbols>
2525
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
2626
<PackageTags>JavaScript;ECMAScript;MSIE;IE;Edge;Chakra</PackageTags>
27-
<PackageReleaseNotes>Added a `README.md` file to NuGet package.</PackageReleaseNotes>
27+
<PackageReleaseNotes>Optimized a memory usage in the `ReflectionHelpers.GetBestFitMethod` method.</PackageReleaseNotes>
2828
<NeutralLanguage>en-US</NeutralLanguage>
2929
<PackageOutputPath>../../nuget</PackageOutputPath>
3030
<GeneratePackageOnBuild Condition=" '$(Configuration)' == 'Release' ">true</GeneratePackageOnBuild>
@@ -39,13 +39,12 @@
3939
<PackageReference Include="ResxToCs.MSBuild" Version="1.0.0-alpha7" PrivateAssets="All" />
4040
</ItemGroup>
4141

42-
<ItemGroup Condition=" '$(TargetFramework)' == 'net40-client' Or '$(TargetFramework)' == 'net45' ">
43-
<Compile Remove="Resources/NetCoreStrings.*" />
44-
<EmbeddedResource Include="Resources/*.min.js" />
42+
<ItemGroup Condition=" '$(TargetFramework)' == 'net40-client' ">
43+
<PackageReference Include="PolyfillsForOldDotNet.System.Buffers" Version="0.1.2" />
4544
</ItemGroup>
4645

47-
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' Or '$(TargetFramework)' == 'netstandard2.0' ">
48-
<Compile Remove="Resources/NetFrameworkStrings.*" />
46+
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' Or '$(TargetFramework)' == 'netstandard1.3' ">
47+
<PackageReference Include="System.Buffers" Version="4.0.0" />
4948
</ItemGroup>
5049

5150
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
@@ -54,6 +53,19 @@
5453
<PackageReference Include="System.Threading.Thread" Version="4.0.0" />
5554
</ItemGroup>
5655

56+
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
57+
<PackageReference Include="System.Buffers" Version="4.4.0" />
58+
</ItemGroup>
59+
60+
<ItemGroup Condition=" '$(TargetFramework)' == 'net40-client' Or '$(TargetFramework)' == 'net45' ">
61+
<Compile Remove="Resources/NetCoreStrings.*" />
62+
<EmbeddedResource Include="Resources/*.min.js" />
63+
</ItemGroup>
64+
65+
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' Or '$(TargetFramework)' == 'netstandard2.0' ">
66+
<Compile Remove="Resources/NetFrameworkStrings.*" />
67+
</ItemGroup>
68+
5769
<ItemGroup>
5870
<None Include="../../images/MsieJavaScriptEngine_Logo_128x128.png">
5971
<PackagePath>$(PackageIcon)</PackagePath>

src/MsieJavaScriptEngine/readme.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
=============
2222
RELEASE NOTES
2323
=============
24-
Added a `README.md` file to NuGet package.
24+
Optimized a memory usage in the `ReflectionHelpers.GetBestFitMethod` method.
2525

2626
============
2727
PROJECT SITE

0 commit comments

Comments
 (0)