| File: Microsoft\CSharp\RuntimeBinder\Semantics\Types\TypeArray.cs | Web Access |
| Project: src\runtime\src\libraries\Microsoft.CSharp\src\Microsoft.CSharp.csproj (Microsoft.CSharp) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; namespace Microsoft.CSharp.RuntimeBinder.Semantics { ///////////////////////////////////////////////////////////////////////////////// // Encapsulates a type list, including its size. internal sealed class TypeArray { //////////////////////////////////////////////////////////////////////////////// // Allocate a type array; used to represent a parameter list. // We use a hash table to make sure that allocating the same type array twice // returns the same value. This does two things: // // 1) Save a lot of memory. // 2) Make it so parameter lists can be compared by a simple pointer comparison private readonly struct TypeArrayKey : IEquatable<TypeArrayKey> { private readonly CType[] _types; private readonly int _hashCode; public TypeArrayKey(CType[] types) { _types = types; int hashCode = 0x162A16FE; foreach (CType type in types) { hashCode = (hashCode << 5) - hashCode; if (type != null) { hashCode ^= type.GetHashCode(); } } _hashCode = hashCode; } public bool Equals(TypeArrayKey other) { CType[] types = _types; CType[] otherTypes = other._types; if (otherTypes == types) { return true; } if (other._hashCode != _hashCode || otherTypes.Length != types.Length) { return false; } for (int i = 0; i < types.Length; i++) { if (types[i] != otherTypes[i]) { return false; } } return true; } [ExcludeFromCodeCoverage(Justification = "Typed overload should always be the method called")] public override bool Equals(object obj) { Debug.Fail("Sub-optimal overload called. Check if this can be avoided."); return obj is TypeArrayKey key && Equals(key); } public override int GetHashCode() => _hashCode; } // The RuntimeBinder uses a global lock when Binding that keeps this dictionary safe. private static readonly Dictionary<TypeArrayKey, TypeArray> s_tableTypeArrays = new Dictionary<TypeArrayKey, TypeArray>(); public static readonly TypeArray Empty = new TypeArray(Array.Empty<CType>()); private TypeArray(CType[] types) { Debug.Assert(types != null); Items = types; } public int Count => Items.Length; public CType[] Items { get; } public CType this[int i] => Items[i]; [Conditional("DEBUG")] public void AssertValid() { Debug.Assert(Count >= 0); Debug.Assert(Array.TrueForAll(Items, item => item != null)); } public void CopyItems(int i, int c, CType[] dest) => Array.Copy(Items, i, dest, 0, c); public static TypeArray Allocate(int ctype, TypeArray array, int offset) { if (ctype == 0) { return Empty; } if (ctype == array.Count) { return array; } CType[] types = array.Items; CType[] newTypes = new CType[ctype]; Array.ConstrainedCopy(types, offset, newTypes, 0, ctype); return Allocate(newTypes); } public static TypeArray Allocate(params CType[] types) { if (types?.Length > 0) { RuntimeBinder.EnsureLockIsTaken(); TypeArrayKey key = new TypeArrayKey(types); if (!s_tableTypeArrays.TryGetValue(key, out TypeArray result)) { result = new TypeArray(types); s_tableTypeArrays.Add(key, result); } return result; } return Empty; } public static TypeArray Concat(TypeArray pta1, TypeArray pta2) { CType[] prgtype1 = pta1.Items; if (prgtype1.Length == 0) { return pta2; } CType[] prgtype2 = pta2.Items; if (prgtype2.Length == 0) { return pta1; } CType[] combined = new CType[prgtype1.Length + prgtype2.Length]; Array.Copy(prgtype1, combined, prgtype1.Length); Array.Copy(prgtype2, 0, combined, prgtype1.Length, prgtype2.Length); return Allocate(combined); } } }