/* ****************************************************************************
*
* Copyright (c) Microsoft Corporation.
*
* This source code is subject to terms and conditions of the Microsoft Public License. A
* copy of the license can be found in the License.html file at the root of this distribution. If
* you cannot locate the Microsoft Public License, please send an email to
* dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
* by the terms of the Microsoft Public License.
*
* You must not remove this notice, or any other, from this software.
*
*
* ***************************************************************************/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading;
using Microsoft.Contracts;
namespace Microsoft.Scripting.Actions {
///
/// MethodGroup's represent a unique collection of method's. Typically this
/// unique set is all the methods which are overloaded by the same name including
/// methods with different arity. These methods represent a single logically
/// overloaded element of a .NET type.
///
/// The base DLR binders will produce MethodGroup's when provided with a MemberGroup
/// which contains only methods. The MethodGroup's will be unique instances per
/// each unique group of methods.
///
public class MethodGroup : MemberTracker {
private MethodTracker[] _methods;
private Dictionary _boundGenerics;
internal MethodGroup(params MethodTracker[] methods) {
_methods = methods;
}
public override TrackerTypes MemberType {
get { return TrackerTypes.MethodGroup; }
}
public override Type DeclaringType {
get { return _methods[0].DeclaringType; }
}
public override string Name {
get { return _methods[0].Name; }
}
public bool ContainsInstance {
get {
foreach (MethodTracker mt in _methods) {
if (!mt.IsStatic) return true;
}
return false;
}
}
public bool ContainsStatic {
get {
foreach (MethodTracker mt in _methods) {
if (mt.IsStatic) return true;
}
return false;
}
}
public IList Methods {
get {
return _methods;
}
}
public MethodBase[] GetMethodBases() {
MethodBase[] methods = new MethodBase[Methods.Count];
for (int i = 0; i < Methods.Count; i++) {
methods[i] = Methods[i].Method;
}
return methods;
}
public override Expression GetValue(Expression context, ActionBinder binder, Type type) {
return base.GetValue(context, binder, type);
}
public override MemberTracker BindToInstance(Expression instance) {
if (ContainsInstance) {
return new BoundMemberTracker(this, instance);
}
return this;
}
protected internal override Expression GetBoundValue(Expression context, ActionBinder binder, Type type, Expression instance) {
return binder.ReturnMemberTracker(type, BindToInstance(instance));
}
///
/// Returns a BuiltinFunction bound to the provided type arguments. Returns null if the binding
/// cannot be performed.
///
public MethodGroup MakeGenericMethod(Type[] types) {
TypeList tl = new TypeList(types);
// check for cached method first...
MethodGroup mg;
if (_boundGenerics != null) {
lock (_boundGenerics) {
if (_boundGenerics.TryGetValue(tl, out mg)) {
return mg;
}
}
}
// Search for generic targets with the correct arity (number of type parameters).
// Compatible targets must be MethodInfos by definition (constructors never take
// type arguments).
List targets = new List(Methods.Count);
foreach (MethodTracker mt in Methods) {
MethodInfo mi = mt.Method;
if (mi.ContainsGenericParameters && mi.GetGenericArguments().Length == types.Length)
targets.Add((MethodTracker)MemberTracker.FromMemberInfo(mi.MakeGenericMethod(types)));
}
if (targets.Count == 0) {
return null;
}
// Build a new MethodGroup that will contain targets with bound type arguments & cache it.
mg = new MethodGroup(targets.ToArray());
EnsureBoundGenericDict();
lock (_boundGenerics) {
_boundGenerics[tl] = mg;
}
return mg;
}
private void EnsureBoundGenericDict() {
if (_boundGenerics == null) {
Interlocked.CompareExchange>(
ref _boundGenerics,
new Dictionary(1),
null);
}
}
private class TypeList {
private Type[] _types;
public TypeList(Type[] types) {
Debug.Assert(types != null);
_types = types;
}
[Confined]
public override bool Equals(object obj) {
TypeList tl = obj as TypeList;
if (tl == null || _types.Length != tl._types.Length) return false;
for (int i = 0; i < _types.Length; i++) {
if (_types[i] != tl._types[i]) return false;
}
return true;
}
[Confined]
public override int GetHashCode() {
int hc = 6551;
foreach (Type t in _types) {
hc = (hc << 5) ^ t.GetHashCode();
}
return hc;
}
}
}
}