/* **************************************************************************** * * 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 Microsoft; #if !SILVERLIGHT using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; #if CODEPLEX_40 using System.Linq.Expressions; #else using Microsoft.Linq.Expressions; #endif using System.Security; using System.Security.Permissions; #if CODEPLEX_40 [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Dynamic")] #else [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Microsoft.Scripting")] #endif #if CODEPLEX_40 namespace System.Dynamic { #else namespace Microsoft.Scripting { #endif /// /// Provides helper methods to bind COM objects dynamically. /// #if MICROSOFT_DYNAMIC public #else internal #endif static class ComBinder { /// /// Determines if an object is a COM object. /// /// The object to test. /// true if the object is a COM object, false otherwise. public static bool IsComObject(object value) { return ComObject.IsComObject(value); } /// /// Tries to perform binding of the dynamic get member operation. /// /// An instance of the that represents the details of the dynamic operation. /// The target of the dynamic operation. /// The new representing the result of the binding. /// true if member evaluation may be delayed. /// true if operation was bound successfully; otherwise, false. #if CLR2 [SecurityCritical, SecurityTreatAsSafe] #else [SecuritySafeCritical] #endif public static bool TryBindGetMember(GetMemberBinder binder, DynamicMetaObject instance, out DynamicMetaObject result, bool delayInvocation) { ContractUtils.RequiresNotNull(binder, "binder"); ContractUtils.RequiresNotNull(instance, "instance"); if (TryGetMetaObject(ref instance)) { // // Demand Full Trust to proceed with the binding. // new PermissionSet(PermissionState.Unrestricted).Demand(); var comGetMember = new ComGetMemberBinder(binder, delayInvocation); result = instance.BindGetMember(comGetMember); if (result.Expression.Type.IsValueType) { result = new DynamicMetaObject( Expression.Convert(result.Expression, typeof(object)), result.Restrictions ); } return true; } else { result = null; return false; } } /// /// Tries to perform binding of the dynamic get member operation. /// /// An instance of the that represents the details of the dynamic operation. /// The target of the dynamic operation. /// The new representing the result of the binding. /// true if operation was bound successfully; otherwise, false. public static bool TryBindGetMember(GetMemberBinder binder, DynamicMetaObject instance, out DynamicMetaObject result) { return TryBindGetMember(binder, instance, out result, false); } /// /// Tries to perform binding of the dynamic set member operation. /// /// An instance of the that represents the details of the dynamic operation. /// The target of the dynamic operation. /// The representing the value for the set member operation. /// The new representing the result of the binding. /// true if operation was bound successfully; otherwise, false. #if CLR2 [SecurityCritical, SecurityTreatAsSafe] #else [SecuritySafeCritical] #endif public static bool TryBindSetMember(SetMemberBinder binder, DynamicMetaObject instance, DynamicMetaObject value, out DynamicMetaObject result) { ContractUtils.RequiresNotNull(binder, "binder"); ContractUtils.RequiresNotNull(instance, "instance"); ContractUtils.RequiresNotNull(value, "value"); if (TryGetMetaObject(ref instance)) { // // Demand Full Trust to proceed with the binding. // new PermissionSet(PermissionState.Unrestricted).Demand(); result = instance.BindSetMember(binder, value); return true; } else { result = null; return false; } } /// /// Tries to perform binding of the dynamic invoke operation. /// /// An instance of the that represents the details of the dynamic operation. /// The target of the dynamic operation. /// An array of instances - arguments to the invoke member operation. /// The new representing the result of the binding. /// true if operation was bound successfully; otherwise, false. #if CLR2 [SecurityCritical, SecurityTreatAsSafe] #else [SecuritySafeCritical] #endif public static bool TryBindInvoke(InvokeBinder binder, DynamicMetaObject instance, DynamicMetaObject[] args, out DynamicMetaObject result) { ContractUtils.RequiresNotNull(binder, "binder"); ContractUtils.RequiresNotNull(instance, "instance"); ContractUtils.RequiresNotNull(args, "args"); if (TryGetMetaObject(ref instance)) { // // Demand Full Trust to proceed with the binding. // new PermissionSet(PermissionState.Unrestricted).Demand(); result = instance.BindInvoke(binder, args); return true; } else { result = null; return false; } } /// /// Tries to perform binding of the dynamic invoke member operation. /// /// An instance of the that represents the details of the dynamic operation. /// The target of the dynamic operation. /// An array of instances - arguments to the invoke member operation. /// The new representing the result of the binding. /// true if operation was bound successfully; otherwise, false. #if CLR2 [SecurityCritical, SecurityTreatAsSafe] #else [SecuritySafeCritical] #endif public static bool TryBindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject instance, DynamicMetaObject[] args, out DynamicMetaObject result) { ContractUtils.RequiresNotNull(binder, "binder"); ContractUtils.RequiresNotNull(instance, "instance"); ContractUtils.RequiresNotNull(args, "args"); if (TryGetMetaObject(ref instance)) { // // Demand Full Trust to proceed with the binding. // new PermissionSet(PermissionState.Unrestricted).Demand(); result = instance.BindInvokeMember(binder, args); return true; } else { result = null; return false; } } /// /// Tries to perform binding of the dynamic get index operation. /// /// An instance of the that represents the details of the dynamic operation. /// The target of the dynamic operation. /// An array of instances - arguments to the invoke member operation. /// The new representing the result of the binding. /// true if operation was bound successfully; otherwise, false. #if CLR2 [SecurityCritical, SecurityTreatAsSafe] #else [SecuritySafeCritical] #endif public static bool TryBindGetIndex(GetIndexBinder binder, DynamicMetaObject instance, DynamicMetaObject[] args, out DynamicMetaObject result) { ContractUtils.RequiresNotNull(binder, "binder"); ContractUtils.RequiresNotNull(instance, "instance"); ContractUtils.RequiresNotNull(args, "args"); if (TryGetMetaObject(ref instance)) { // // Demand Full Trust to proceed with the binding. // new PermissionSet(PermissionState.Unrestricted).Demand(); result = instance.BindGetIndex(binder, args); return true; } else { result = null; return false; } } /// /// Tries to perform binding of the dynamic set index operation. /// /// An instance of the that represents the details of the dynamic operation. /// The target of the dynamic operation. /// An array of instances - arguments to the invoke member operation. /// The representing the value for the set index operation. /// The new representing the result of the binding. /// true if operation was bound successfully; otherwise, false. #if CLR2 [SecurityCritical, SecurityTreatAsSafe] #else [SecuritySafeCritical] #endif public static bool TryBindSetIndex(SetIndexBinder binder, DynamicMetaObject instance, DynamicMetaObject[] args, DynamicMetaObject value, out DynamicMetaObject result) { ContractUtils.RequiresNotNull(binder, "binder"); ContractUtils.RequiresNotNull(instance, "instance"); ContractUtils.RequiresNotNull(args, "args"); ContractUtils.RequiresNotNull(value, "value"); if (TryGetMetaObject(ref instance)) { // // Demand Full Trust to proceed with the binding. // new PermissionSet(PermissionState.Unrestricted).Demand(); result = instance.BindSetIndex(binder, args, value); return true; } else { result = null; return false; } } /// /// Tries to perform binding of the dynamic Convert operation. /// /// An instance of the that represents the details of the dynamic operation. /// The target of the dynamic operation. /// The new representing the result of the binding. /// true if operation was bound successfully; otherwise, false. #if CLR2 [SecurityCritical, SecurityTreatAsSafe] #else [SecuritySafeCritical] #endif public static bool TryConvert(ConvertBinder binder, DynamicMetaObject instance, out DynamicMetaObject result) { ContractUtils.RequiresNotNull(binder, "binder"); ContractUtils.RequiresNotNull(instance, "instance"); if (IsComObject(instance.Value)) { // // Demand Full Trust to proceed with the binding. // new PermissionSet(PermissionState.Unrestricted).Demand(); // Converting a COM object to any interface is always considered possible - it will result in // a QueryInterface at runtime if (binder.Type.IsInterface) { result = new DynamicMetaObject( Expression.Convert( instance.Expression, binder.Type ), BindingRestrictions.GetExpressionRestriction( Expression.Call( typeof(ComObject).GetMethod("IsComObject", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic), Helpers.Convert(instance.Expression, typeof(object)) ) ) ); return true; } } result = null; return false; } /// /// Gets the member names associated with the object. /// This function can operate only with objects for which returns true. /// /// The object for which member names are requested. /// The collection of member names. #if CLR2 [SecurityCritical, SecurityTreatAsSafe] #else [SecuritySafeCritical] #endif public static IEnumerable GetDynamicMemberNames(object value) { ContractUtils.RequiresNotNull(value, "value"); ContractUtils.Requires(IsComObject(value), "value", Strings.ComObjectExpected); // // Demand Full Trust to proceed with the binding. // new PermissionSet(PermissionState.Unrestricted).Demand(); return ComObject.ObjectToComObject(value).GetMemberNames(false); } /// /// Gets the member names of the data-like members associated with the object. /// This function can operate only with objects for which returns true. /// /// The object for which member names are requested. /// The collection of member names. #if CLR2 [SecurityCritical, SecurityTreatAsSafe] #else [SecuritySafeCritical] #endif [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal static IList GetDynamicDataMemberNames(object value) { ContractUtils.RequiresNotNull(value, "value"); ContractUtils.Requires(IsComObject(value), "value", Strings.ComObjectExpected); // // Demand Full Trust to proceed with the binding. // new PermissionSet(PermissionState.Unrestricted).Demand(); return ComObject.ObjectToComObject(value).GetMemberNames(true); } /// /// Gets the data-like members and associated data for an object. /// This function can operate only with objects for which returns true. /// /// The object for which data members are requested. /// The enumeration of names of data members for which to retrieve values. /// The collection of pairs that represent data member's names and their data. [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] #if CLR2 [SecurityCritical, SecurityTreatAsSafe] #else [SecuritySafeCritical] #endif internal static IList> GetDynamicDataMembers(object value, IEnumerable names) { ContractUtils.RequiresNotNull(value, "value"); ContractUtils.Requires(IsComObject(value), "value", Strings.ComObjectExpected); // // Demand Full Trust to proceed with the binding. // new PermissionSet(PermissionState.Unrestricted).Demand(); return ComObject.ObjectToComObject(value).GetMembers(names); } private static bool TryGetMetaObject(ref DynamicMetaObject instance) { // If we're already a COM MO don't make a new one // (we do this to prevent recursion if we call Fallback from COM) if (instance is ComUnwrappedMetaObject) { return false; } if (IsComObject(instance.Value)) { instance = new ComMetaObject(instance.Expression, instance.Restrictions, instance.Value); return true; } return false; } /// /// Special binder that indicates special semantics for COM GetMember operation. /// internal class ComGetMemberBinder : GetMemberBinder { private readonly GetMemberBinder _originalBinder; internal bool _CanReturnCallables; internal ComGetMemberBinder(GetMemberBinder originalBinder, bool CanReturnCallables) : base(originalBinder.Name, originalBinder.IgnoreCase) { _originalBinder = originalBinder; _CanReturnCallables = CanReturnCallables; } public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion) { return _originalBinder.FallbackGetMember(target, errorSuggestion); } public override int GetHashCode() { return _originalBinder.GetHashCode() ^ (_CanReturnCallables ? 1 : 0); } public override bool Equals(object obj) { ComGetMemberBinder other = obj as ComGetMemberBinder; return other != null && _CanReturnCallables == other._CanReturnCallables && _originalBinder.Equals(other._originalBinder); } } } } #endif