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

Commit 728e6d9

Browse files
committed
Reconciling recent changes in System.Linq.Expressions and System.Dynamic.Runtime
The branch were these fixes were made should be switched over to use dotnet\corefx, but before that happens the sources must match.
1 parent d98e0a4 commit 728e6d9

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)