// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; using System.Reflection; namespace Microsoft.ClearScript { internal sealed class BindSignature : IEquatable { private readonly Type context; private readonly BindingFlags flags; private readonly TargetInfo targetInfo; private readonly string name; private readonly Type[] typeArgs; private readonly ArgInfo[] argData; public BindSignature(Type context, BindingFlags flags, HostTarget target, string name, Type[] typeArgs, object[] args) { this.context = context; this.flags = flags; targetInfo = new TargetInfo(target); this.name = name; this.typeArgs = typeArgs; argData = new ArgInfo[args.Length]; for (var index = 0; index < args.Length; index++) { argData[index] = new ArgInfo(args[index]); } } #region Object overrides public override bool Equals(object obj) { return Equals(obj as BindSignature); } public override int GetHashCode() { var accumulator = new HashAccumulator(); accumulator.Update(context); accumulator.Update((int)flags); targetInfo.UpdateHash(ref accumulator); accumulator.Update(name); foreach (var type in typeArgs) { accumulator.Update(type); } foreach (var argInfo in argData) { argInfo.UpdateHash(ref accumulator); } return accumulator.HashCode; } #endregion #region IEquatable implementation public bool Equals(BindSignature that) { if (that == null) { return false; } if (context != that.context) { return false; } if (flags != that.flags) { return false; } if (!targetInfo.Equals(that.targetInfo)) { return false; } if (name != that.name) { return false; } if (typeArgs.Length != that.typeArgs.Length) { return false; } for (var index = 0; index < typeArgs.Length; index++) { if (typeArgs[index] != that.typeArgs[index]) { return false; } } if (argData.Length != that.argData.Length) { return false; } for (var index = 0; index < argData.Length; index++) { if (!argData[index].Equals(that.argData[index])) { return false; } } return true; } #endregion #region Nested type: TargetKind private enum TargetKind { Static, Null, Instance } #endregion #region Nested type: TargetInfo private sealed class TargetInfo : IEquatable { private readonly TargetKind kind; private readonly Type targetType; private readonly Type instanceType; public TargetInfo(HostTarget target) { if (target is HostType) { kind = TargetKind.Static; targetType = target.Type; } else if (target.InvokeTarget == null) { kind = TargetKind.Null; targetType = target.Type; } else { kind = TargetKind.Instance; targetType = target.Type; var tempType = target.InvokeTarget.GetType(); if (tempType != targetType) { instanceType = tempType; } } } public void UpdateHash(ref HashAccumulator accumulator) { accumulator.Update((int)kind); accumulator.Update(targetType); accumulator.Update(instanceType); } #region Object overrides public override bool Equals(object obj) { return Equals(obj as TargetInfo); } public override int GetHashCode() { var accumulator = new HashAccumulator(); UpdateHash(ref accumulator); return accumulator.HashCode; } #endregion #region IEquatable implementation public bool Equals(TargetInfo that) { return (that != null) && (kind == that.kind) && (targetType == that.targetType) && (instanceType == that.instanceType); } #endregion } #endregion #region Nested type: ArgKind private enum ArgKind { Null, ByValue, Out, Ref } #endregion #region Nested type: ArgInfo private sealed class ArgInfo : IEquatable { private readonly ArgKind kind; private readonly Type type; public ArgInfo(object arg) { if (arg == null) { kind = ArgKind.Null; return; } if (arg is IOutArg outArg) { kind = ArgKind.Out; type = outArg.Type; return; } if (arg is IRefArg refArg) { kind = ArgKind.Ref; type = refArg.Type; return; } if (arg is HostType) { kind = ArgKind.ByValue; type = typeof(HostType); return; } if (arg is HostMethod) { kind = ArgKind.ByValue; type = typeof(HostMethod); return; } if (arg is HostIndexedProperty) { kind = ArgKind.ByValue; type = typeof(HostIndexedProperty); return; } if (arg is ScriptMethod) { kind = ArgKind.ByValue; type = typeof(ScriptMethod); return; } if (arg is HostTarget hostTarget) { kind = ArgKind.ByValue; type = hostTarget.Type; return; } kind = ArgKind.ByValue; type = arg.GetType(); } public void UpdateHash(ref HashAccumulator accumulator) { accumulator.Update((int)kind); accumulator.Update(type); } #region Object overrides public override bool Equals(object obj) { return Equals(obj as ArgInfo); } public override int GetHashCode() { var accumulator = new HashAccumulator(); UpdateHash(ref accumulator); return accumulator.HashCode; } #endregion #region IEquatable implementation public bool Equals(ArgInfo that) { return (that != null) && (kind == that.kind) && (type == that.type); } #endregion } #endregion #region Nested type: HashAccumulator private struct HashAccumulator { public int HashCode { get; private set; } public void Update(int value) { HashCode = unchecked((HashCode * 31) + value); } public void Update(object obj) { HashCode = unchecked((HashCode * 31) + (obj?.GetHashCode() ?? 0)); } } #endregion } }