/* ****************************************************************************
*
* 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.Dynamic;
using System.Reflection;
using Microsoft.Scripting.Actions.Calls;
using Microsoft.Scripting.Runtime;
using Microsoft.Scripting.Utils;
namespace Microsoft.Scripting.Actions{
/// <summary>
/// Represents a logical member of a type. The member could either be real concrete member on a type or
/// an extension member.
///
/// This seperates the "physical" members that .NET knows exist on types from the members that
/// logically exist on a type. It also provides other abstractions above the level of .NET reflection
/// such as MemberGroups and NamespaceTracker's.
///
/// It also provides a wrapper around the reflection APIs which cannot be extended from partial trust.
/// </summary>
public abstract class MemberTracker {
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2105:ArrayFieldsShouldNotBeReadOnly")]
public static readonly MemberTracker[] EmptyTrackers = new MemberTracker[0];
private static readonly Dictionary<MemberKey, MemberTracker> _trackers = new Dictionary<MemberKey, MemberTracker>();
/// <summary>
/// We ensure we only produce one MemberTracker for each member which logically lives on the declaring type. So
/// for example if you get a member from a derived class which is declared on the base class it should be the same
/// as getting the member from the base class. Thats easy enough until you get into extension members here there
/// might be one extension member which is being applied to multiple types. Therefore we need to take into account the
/// extension type when ensuring that we only have 1 MemberTracker ever created.
/// </summary>
class MemberKey {
private readonly MemberInfo Member;
private readonly Type Extending;
public MemberKey(MemberInfo member, Type extending) {
Member = member;
Extending = extending;
}
public override int GetHashCode() {
int res = Member.GetHashCode();
if (Extending != null) {
res ^= Extending.GetHashCode();
}
return res;
}
public override bool Equals(object obj) {
MemberKey other = obj as MemberKey;
if (other == null) return false;
return other.Member == Member &&
other.Extending == Extending;
}
}
internal MemberTracker() {
}
/// <summary>
/// The type of member tracker.
/// </summary>
public abstract TrackerTypes MemberType {
get;
}
/// <summary>
/// The logical declaring type of the member.
/// </summary>
public abstract Type DeclaringType {
get;
}
/// <summary>
/// The name of the member.
/// </summary>
public abstract string Name {
get;
}
public static MemberTracker FromMemberInfo(MemberInfo member) {
return FromMemberInfo(member, null);
}
public static MemberTracker FromMemberInfo(MemberInfo member, Type extending) {
ContractUtils.RequiresNotNull(member, "member");
lock (_trackers) {
MemberTracker res;
MemberKey key = new MemberKey(member, extending);
if (_trackers.TryGetValue(key, out res)) return res;
switch (member.MemberType) {
case MemberTypes.Constructor: res = new ConstructorTracker((ConstructorInfo)member); break;
case MemberTypes.Event: res = new EventTracker((EventInfo)member); break;
case MemberTypes.Field: res = new FieldTracker((FieldInfo)member); break;
case MemberTypes.Method:
MethodInfo mi = (MethodInfo)member;
if (extending != null) {
res = new ExtensionMethodTracker(mi, member.IsDefined(typeof(StaticExtensionMethodAttribute), false), extending);
} else {
res = new MethodTracker(mi);
}
break;
case MemberTypes.TypeInfo:
case MemberTypes.NestedType: res = new NestedTypeTracker((Type)member); break;
case MemberTypes.Property: res = new ReflectedPropertyTracker((PropertyInfo)member); break;
default: throw Error.UnknownMemberType(member.MemberType);
}
_trackers[key] = res;
return res;
}
}
#region Public expression builders
/// <summary>
/// Gets the expression that creates the value.
///
/// Returns null if it's an error to get the value. The caller can then call GetErrorForGet to get
/// the correct error Expression (or null if they should provide a default).
/// </summary>
public virtual DynamicMetaObject GetValue(OverloadResolverFactory resolverFactory, ActionBinder binder, Type type) {
return binder.ReturnMemberTracker(type, this);
}
/// <summary>
/// Gets an expression that assigns a value to the left hand side.
///
/// Returns null if it's an error to assign to. The caller can then call GetErrorForSet to
/// get the correct error Expression (or null if a default error should be provided).
/// </summary>
public virtual DynamicMetaObject SetValue(OverloadResolverFactory resolverFactory, ActionBinder binder, Type type, DynamicMetaObject value) {
return null;
}
/// <summary>
/// Gets an expression that performs a call on the object using the specified arguments.
///
/// Returns null if it's an error to perform the specific operation. The caller can then call
/// GetErrorsForDoCall to get the correct error Expression (or null if a default error should be provided).
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Call")] // TODO: fix
internal virtual DynamicMetaObject Call(OverloadResolverFactory resolverFactory, ActionBinder binder, params DynamicMetaObject[] arguments) {
return null;
}
#endregion
#region Public error expression builders
/// <summary>
/// Returns the error associated with getting the value.
///
/// A null return value indicates that the default error message should be provided by the caller.
/// </summary>
public virtual ErrorInfo GetError(ActionBinder binder) {
return null;
}
/// <summary>
/// Returns the error associated with accessing this member via a bound instance.
///
/// A null return value indicates that the default error message should be provided by the caller.
/// </summary>
public virtual ErrorInfo GetBoundError(ActionBinder binder, DynamicMetaObject instance) {
return null;
}
/// <summary>
/// Helper for getting values that have been bound. Called from BoundMemberTracker. Custom member
/// trackers can override this to provide their own behaviors when bound to an instance.
/// </summary>
protected internal virtual DynamicMetaObject GetBoundValue(OverloadResolverFactory resolverFactory, ActionBinder binder, Type type, DynamicMetaObject instance) {
return GetValue(resolverFactory, binder, type);
}
/// <summary>
/// Helper for setting values that have been bound. Called from BoundMemberTracker. Custom member
/// trackers can override this to provide their own behaviors when bound to an instance.
/// </summary>
protected internal virtual DynamicMetaObject SetBoundValue(OverloadResolverFactory resolverFactory, ActionBinder binder, Type type, DynamicMetaObject value, DynamicMetaObject instance) {
return SetValue(resolverFactory, binder, type, instance);
}
/// <summary>
/// Binds the member tracker to the specified instance rturning a new member tracker if binding
/// is possible. If binding is not possible the existing member tracker will be returned. For example
/// binding to a static field results in returning the original MemberTracker. Binding to an instance
/// field results in a new BoundMemberTracker which will get GetBoundValue/SetBoundValue to pass the
/// instance through.
/// </summary>
public virtual MemberTracker BindToInstance(DynamicMetaObject instance) {
return this;
}
#endregion
}
}
|