Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit b90b066

Browse files
committed
Merge pull request #1919 from VSadov/master
Reconciling recent changes in System.Linq.Expressions and System.Dyna…
2 parents d98e0a4 + 728e6d9 commit b90b066

File tree

15 files changed

+660
-274
lines changed

15 files changed

+660
-274
lines changed

src/Common/src/System/Dynamic/Utils/CacheDict.cs

Lines changed: 61 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -4,49 +4,79 @@
44
using System;
55
using System.Collections.Generic;
66
using System.Text;
7+
using System.Threading;
78
using System.Diagnostics;
89

910
namespace System.Dynamic.Utils
1011
{
1112
/// <summary>
1213
/// Provides a dictionary-like object used for caches which holds onto a maximum
1314
/// number of elements specified at construction time.
14-
///
15-
/// This class is not thread safe.
1615
/// </summary>
1716
internal class CacheDict<TKey, TValue>
1817
{
19-
private readonly Dictionary<TKey, KeyInfo> _dict = new Dictionary<TKey, KeyInfo>();
20-
private readonly LinkedList<TKey> _list = new LinkedList<TKey>();
21-
private readonly int _maxSize;
18+
19+
// cache size is always ^2.
20+
// items are placed at [hash ^ mask]
21+
// new item will displace previous one at the same location.
22+
private readonly int mask;
23+
private readonly Entry[] entries;
24+
25+
// class, to ensure atomic updates.
26+
private sealed class Entry
27+
{
28+
internal readonly int hash;
29+
internal readonly TKey key;
30+
internal readonly TValue value;
31+
32+
internal Entry(int hash, TKey key, TValue value)
33+
{
34+
this.hash = hash;
35+
this.key = key;
36+
this.value = value;
37+
}
38+
}
2239

2340
/// <summary>
2441
/// Creates a dictionary-like object used for caches.
2542
/// </summary>
26-
/// <param name="maxSize">The maximum number of elements to store.</param>
27-
public CacheDict(int maxSize)
43+
/// <param name="maxSize">The maximum number of elements to store will be this number aligned to next ^2.</param>
44+
internal CacheDict(int size)
2845
{
29-
_maxSize = maxSize;
46+
var alignedSize = AlignSize(size);
47+
this.mask = alignedSize - 1;
48+
this.entries = new Entry[alignedSize];
49+
}
50+
51+
private static int AlignSize(int size)
52+
{
53+
Debug.Assert(size > 0);
54+
55+
size--;
56+
size |= size >> 1;
57+
size |= size >> 2;
58+
size |= size >> 4;
59+
size |= size >> 8;
60+
size |= size >> 16;
61+
size++;
62+
63+
Debug.Assert((size & (~size + 1)) == size, "aligned size should be a power of 2");
64+
return size;
3065
}
3166

3267
/// <summary>
3368
/// Tries to get the value associated with 'key', returning true if it's found and
3469
/// false if it's not present.
3570
/// </summary>
36-
public bool TryGetValue(TKey key, out TValue value)
71+
internal bool TryGetValue(TKey key, out TValue value)
3772
{
38-
KeyInfo storedValue;
39-
if (_dict.TryGetValue(key, out storedValue))
40-
{
41-
LinkedListNode<TKey> node = storedValue.List;
42-
if (node.Previous != null)
43-
{
44-
// move us to the head of the list...
45-
_list.Remove(node);
46-
_list.AddFirst(node);
47-
}
73+
int hash = key.GetHashCode();
74+
int idx = hash & mask;
4875

49-
value = storedValue.Value;
76+
var entry = Volatile.Read(ref this.entries[idx]);
77+
if (entry != null && entry.hash == hash && entry.key.Equals(key))
78+
{
79+
value = entry.value;
5080
return true;
5181
}
5282

@@ -55,63 +85,30 @@ public bool TryGetValue(TKey key, out TValue value)
5585
}
5686

5787
/// <summary>
58-
/// Adds a new element to the cache, replacing and moving it to the front if the
59-
/// element is already present.
88+
/// Adds a new element to the cache, possibly replacing some
89+
/// element that is already present.
6090
/// </summary>
61-
public void Add(TKey key, TValue value)
91+
internal void Add(TKey key, TValue value)
6292
{
63-
KeyInfo keyInfo;
64-
if (_dict.TryGetValue(key, out keyInfo))
65-
{
66-
// remove original entry from the linked list
67-
_list.Remove(keyInfo.List);
68-
}
69-
else if (_list.Count == _maxSize)
93+
var hash = key.GetHashCode();
94+
var idx = hash & mask;
95+
96+
var entry = Volatile.Read(ref this.entries[idx]);
97+
if (entry == null || entry.hash != hash || !entry.key.Equals(key))
7098
{
71-
// we've reached capacity, remove the last used element...
72-
LinkedListNode<TKey> node = _list.Last;
73-
_list.RemoveLast();
74-
bool res = _dict.Remove(node.Value);
75-
Debug.Assert(res);
99+
Volatile.Write(ref entries[idx], new Entry(hash, key, value));
76100
}
77-
78-
// add the new entry to the head of the list and into the dictionary
79-
LinkedListNode<TKey> listNode = new LinkedListNode<TKey>(key);
80-
_list.AddFirst(listNode);
81-
_dict[key] = new CacheDict<TKey, TValue>.KeyInfo(value, listNode);
82101
}
83102

84103
/// <summary>
85-
/// Returns the value associated with the given key, or throws KeyNotFoundException
86-
/// if the key is not present.
104+
/// Sets the value associated with the given key.
87105
/// </summary>
88-
public TValue this[TKey key]
106+
internal TValue this[TKey key]
89107
{
90-
get
91-
{
92-
TValue res;
93-
if (TryGetValue(key, out res))
94-
{
95-
return res;
96-
}
97-
throw new KeyNotFoundException();
98-
}
99108
set
100109
{
101110
Add(key, value);
102111
}
103112
}
104-
105-
private struct KeyInfo
106-
{
107-
internal readonly TValue Value;
108-
internal readonly LinkedListNode<TKey> List;
109-
110-
internal KeyInfo(TValue value, LinkedListNode<TKey> list)
111-
{
112-
Value = value;
113-
List = list;
114-
}
115-
}
116113
}
117114
}

src/Common/src/System/Dynamic/Utils/DelegateHelpers.cs

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
using System.Runtime.CompilerServices;
1111
using System.Reflection;
1212

13+
#if FEATURE_REFEMIT
14+
using System.Reflection.Emit;
15+
#endif
16+
1317
namespace System.Dynamic.Utils
1418
{
1519
internal static partial class DelegateHelpers
@@ -22,5 +26,138 @@ internal static partial class DelegateHelpers
2226
public delegate object VBCallSiteDelegate5<T>(T callSite, object instance, ref object arg1, ref object arg2, ref object arg3, ref object arg4, ref object arg5);
2327
public delegate object VBCallSiteDelegate6<T>(T callSite, object instance, ref object arg1, ref object arg2, ref object arg3, ref object arg4, ref object arg5, ref object arg6);
2428
public delegate object VBCallSiteDelegate7<T>(T callSite, object instance, ref object arg1, ref object arg2, ref object arg3, ref object arg4, ref object arg5, ref object arg6, ref object arg7);
29+
30+
internal static Delegate CreateObjectArrayDelegate(Type delegateType, System.Func<object[], object> handler)
31+
{
32+
#if FEATURE_REFEMIT
33+
return CreateObjectArrayDelegateRefEmit(delegateType, handler);
34+
#else
35+
return Internal.Runtime.Augments.DynamicDelegateAugments.CreateObjectArrayDelegate(delegateType, handler);
36+
#endif
37+
}
38+
39+
40+
#if FEATURE_REFEMIT
41+
42+
// We will generate the following code:
43+
//
44+
// object ret;
45+
// object[] args = new object[parameterCount];
46+
// args[0] = param0;
47+
// args[1] = param1;
48+
// ...
49+
// try {
50+
// ret = handler.Invoke(args);
51+
// } finally {
52+
// param0 = (T0)args[0]; // only generated for each byref argument
53+
// }
54+
// return (TRet)ret;
55+
private static Delegate CreateObjectArrayDelegateRefEmit(Type delegateType, System.Func<object[], object> handler)
56+
{
57+
MethodInfo delegateInvokeMethod = delegateType.GetMethod("Invoke");
58+
59+
Type returnType = delegateInvokeMethod.ReturnType;
60+
bool hasReturnValue = returnType != typeof(void);
61+
62+
ParameterInfo[] parameters = delegateInvokeMethod.GetParameters();
63+
Type[] paramTypes = new Type[parameters.Length + 1];
64+
paramTypes[0] = typeof(Func<object[], object>);
65+
for (int i = 0; i < parameters.Length; i++)
66+
{
67+
paramTypes[i + 1] = parameters[i].ParameterType;
68+
}
69+
70+
DynamicMethod thunkMethod = new DynamicMethod("Thunk", returnType, paramTypes);
71+
ILGenerator ilgen = thunkMethod.GetILGenerator();
72+
73+
LocalBuilder argArray = ilgen.DeclareLocal(typeof(object[]));
74+
LocalBuilder retValue = ilgen.DeclareLocal(typeof(object));
75+
76+
// create the argument array
77+
ilgen.Emit(OpCodes.Ldc_I4, parameters.Length);
78+
ilgen.Emit(OpCodes.Newarr, typeof(object));
79+
ilgen.Emit(OpCodes.Stloc, argArray);
80+
81+
// populate object array
82+
bool hasRefArgs = false;
83+
for (int i = 0; i < parameters.Length; i++)
84+
{
85+
bool paramIsByReference = parameters[i].ParameterType.IsByRef;
86+
Type paramType = parameters[i].ParameterType;
87+
if (paramIsByReference)
88+
paramType = paramType.GetElementType();
89+
90+
hasRefArgs = hasRefArgs || paramIsByReference;
91+
92+
ilgen.Emit(OpCodes.Ldloc, argArray);
93+
ilgen.Emit(OpCodes.Ldc_I4, i);
94+
ilgen.Emit(OpCodes.Ldarg, i + 1);
95+
96+
if (paramIsByReference)
97+
{
98+
ilgen.Emit(OpCodes.Ldobj, paramType);
99+
}
100+
Type boxType = ConvertToBoxableType(paramType);
101+
ilgen.Emit(OpCodes.Box, boxType);
102+
ilgen.Emit(OpCodes.Stelem_Ref);
103+
}
104+
105+
if (hasRefArgs)
106+
{
107+
ilgen.BeginExceptionBlock();
108+
}
109+
110+
// load delegate
111+
ilgen.Emit(OpCodes.Ldarg_0);
112+
113+
// load array
114+
ilgen.Emit(OpCodes.Ldloc, argArray);
115+
116+
// invoke Invoke
117+
MethodInfo invoke = typeof(Func<object[], object>).GetMethod("Invoke");
118+
ilgen.Emit(OpCodes.Callvirt, invoke);
119+
ilgen.Emit(OpCodes.Stloc, retValue);
120+
121+
if (hasRefArgs)
122+
{
123+
// copy back ref/out args
124+
ilgen.BeginFinallyBlock();
125+
for (int i = 0; i < parameters.Length; i++)
126+
{
127+
if (parameters[i].ParameterType.IsByRef)
128+
{
129+
Type byrefToType = parameters[i].ParameterType.GetElementType();
130+
131+
// update parameter
132+
ilgen.Emit(OpCodes.Ldarg, i + 1);
133+
ilgen.Emit(OpCodes.Ldloc, argArray);
134+
ilgen.Emit(OpCodes.Ldc_I4, i);
135+
ilgen.Emit(OpCodes.Ldelem_Ref);
136+
ilgen.Emit(OpCodes.Unbox_Any, byrefToType);
137+
ilgen.Emit(OpCodes.Stobj, byrefToType);
138+
}
139+
}
140+
ilgen.EndExceptionBlock();
141+
}
142+
143+
if (hasReturnValue)
144+
{
145+
ilgen.Emit(OpCodes.Ldloc, retValue);
146+
ilgen.Emit(OpCodes.Unbox_Any, ConvertToBoxableType(returnType));
147+
}
148+
149+
ilgen.Emit(OpCodes.Ret);
150+
151+
// TODO: we need to cache these.
152+
return thunkMethod.CreateDelegate(delegateType, handler);
153+
}
154+
155+
private static Type ConvertToBoxableType(Type t)
156+
{
157+
return (t.IsPointer) ? typeof(IntPtr) : t;
158+
}
159+
160+
#endif // FEATURE_REFEMIT
161+
25162
}
26163
}

src/Common/src/System/Dynamic/Utils/TypeExtensions.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,25 @@ internal static partial class TypeExtensions
1010
{
1111
private static readonly CacheDict<MethodBase, ParameterInfo[]> s_paramInfoCache = new CacheDict<MethodBase, ParameterInfo[]>(75);
1212

13-
public static ParameterInfo[] GetParametersCached(this MethodBase method)
13+
internal static ParameterInfo[] GetParametersCached(this MethodBase method)
1414
{
1515
ParameterInfo[] pis;
16-
lock (s_paramInfoCache)
16+
var pic = s_paramInfoCache;
17+
if (!pic.TryGetValue(method, out pis))
1718
{
18-
if (!s_paramInfoCache.TryGetValue(method, out pis))
19-
{
20-
pis = method.GetParameters();
19+
pis = method.GetParameters();
2120

22-
Type t = method.DeclaringType;
23-
if (t != null && TypeUtils.CanCache(t))
24-
{
25-
s_paramInfoCache[method] = pis;
26-
}
21+
Type t = method.DeclaringType;
22+
if (t != null && TypeUtils.CanCache(t))
23+
{
24+
pic[method] = pis;
2725
}
2826
}
27+
2928
return pis;
3029
}
3130

31+
3232
public static bool IsSubclassOf(this Type source, Type other)
3333
{
3434
return source.GetTypeInfo().IsSubclassOf(other);

0 commit comments

Comments
 (0)