2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Dynamic / ComBinder.cs
1 /* ****************************************************************************
2  *
3  * Copyright (c) Microsoft Corporation. 
4  *
5  * This source code is subject to terms and conditions of the Microsoft Public License. A 
6  * copy of the license can be found in the License.html file at the root of this distribution. If 
7  * you cannot locate the  Microsoft Public License, please send an email to 
8  * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
9  * by the terms of the Microsoft Public License.
10  *
11  * You must not remove this notice, or any other, from this software.
12  *
13  *
14  * ***************************************************************************/
15 using System; using Microsoft;
16
17
18 #if !SILVERLIGHT
19
20 using System.Collections.Generic;
21 using System.Diagnostics.CodeAnalysis;
22 #if CODEPLEX_40
23 using System.Linq.Expressions;
24 #else
25 using Microsoft.Linq.Expressions;
26 #endif
27 using System.Security;
28 using System.Security.Permissions;
29
30 #if CODEPLEX_40
31 [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "System.Dynamic")]
32 #else
33 [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Microsoft.Scripting")]
34 #endif
35
36 #if CODEPLEX_40
37 namespace System.Dynamic {
38 #else
39 namespace Microsoft.Scripting {
40 #endif
41
42     /// <summary>
43     /// Provides helper methods to bind COM objects dynamically.
44     /// </summary>
45     public static class ComBinder {
46
47         /// <summary>
48         /// Determines if an object is a COM object.
49         /// </summary>
50         /// <param name="value">The object to test.</param>
51         /// <returns>true if the object is a COM object, false otherwise.</returns>
52         public static bool IsComObject(object value) {
53             return ComObject.IsComObject(value);
54         }
55
56         /// <summary>
57         /// Tries to perform binding of the dynamic get member operation.
58         /// </summary>
59         /// <param name="binder">An instance of the <see cref="GetMemberBinder"/> that represents the details of the dynamic operation.</param>
60         /// <param name="instance">The target of the dynamic operation. </param>
61         /// <param name="result">The new <see cref="DynamicMetaObject"/> representing the result of the binding.</param>
62         /// <param name="delayInvocation">true if member evaluation may be delayed.</param>
63         /// <returns>true if operation was bound successfully; otherwise, false.</returns>
64 #if MICROSOFT_DYNAMIC
65         [SecurityCritical, SecurityTreatAsSafe]
66 #else
67         [SecuritySafeCritical]
68 #endif
69         public static bool TryBindGetMember(GetMemberBinder binder, DynamicMetaObject instance, out DynamicMetaObject result, bool delayInvocation) {
70             ContractUtils.RequiresNotNull(binder, "binder");
71             ContractUtils.RequiresNotNull(instance, "instance");
72
73             if (TryGetMetaObject(ref instance)) {
74                 //
75                 // Demand Full Trust to proceed with the binding.
76                 //
77
78                 new PermissionSet(PermissionState.Unrestricted).Demand();
79
80                 var comGetMember = new ComGetMemberBinder(binder, delayInvocation);
81                 result = instance.BindGetMember(comGetMember);
82                 if (result.Expression.Type.IsValueType) {
83                     result = new DynamicMetaObject(
84                         Expression.Convert(result.Expression, typeof(object)),
85                         result.Restrictions
86                     );
87                 }
88                 return true;
89             } else {
90                 result = null;
91                 return false;
92             }
93         }
94
95         /// <summary>
96         /// Tries to perform binding of the dynamic get member operation.
97         /// </summary>
98         /// <param name="binder">An instance of the <see cref="GetMemberBinder"/> that represents the details of the dynamic operation.</param>
99         /// <param name="instance">The target of the dynamic operation. </param>
100         /// <param name="result">The new <see cref="DynamicMetaObject"/> representing the result of the binding.</param>
101         /// <returns>true if operation was bound successfully; otherwise, false.</returns>
102         public static bool TryBindGetMember(GetMemberBinder binder, DynamicMetaObject instance, out DynamicMetaObject result) {
103             return TryBindGetMember(binder, instance, out result, false);
104         }
105
106         /// <summary>
107         /// Tries to perform binding of the dynamic set member operation.
108         /// </summary>
109         /// <param name="binder">An instance of the <see cref="SetMemberBinder"/> that represents the details of the dynamic operation.</param>
110         /// <param name="instance">The target of the dynamic operation.</param>
111         /// <param name="value">The <see cref="DynamicMetaObject"/> representing the value for the set member operation.</param>
112         /// <param name="result">The new <see cref="DynamicMetaObject"/> representing the result of the binding.</param>
113         /// <returns>true if operation was bound successfully; otherwise, false.</returns>
114 #if MICROSOFT_DYNAMIC
115         [SecurityCritical, SecurityTreatAsSafe]
116 #else
117         [SecuritySafeCritical]
118 #endif
119         public static bool TryBindSetMember(SetMemberBinder binder, DynamicMetaObject instance, DynamicMetaObject value, out DynamicMetaObject result) {
120             ContractUtils.RequiresNotNull(binder, "binder");
121             ContractUtils.RequiresNotNull(instance, "instance");
122             ContractUtils.RequiresNotNull(value, "value");
123
124             if (TryGetMetaObject(ref instance)) {
125                 //
126                 // Demand Full Trust to proceed with the binding.
127                 //
128
129                 new PermissionSet(PermissionState.Unrestricted).Demand();
130
131                 result = instance.BindSetMember(binder, value);
132                 return true;
133             } else {
134                 result = null;
135                 return false;
136             }
137         }
138
139         /// <summary>
140         /// Tries to perform binding of the dynamic invoke operation.
141         /// </summary>    
142         /// <param name="binder">An instance of the <see cref="InvokeBinder"/> that represents the details of the dynamic operation.</param>
143         /// <param name="instance">The target of the dynamic operation. </param>
144         /// <param name="args">An array of <see cref="DynamicMetaObject"/> instances - arguments to the invoke member operation.</param>
145         /// <param name="result">The new <see cref="DynamicMetaObject"/> representing the result of the binding.</param>
146         /// <returns>true if operation was bound successfully; otherwise, false.</returns>
147 #if MICROSOFT_DYNAMIC
148         [SecurityCritical, SecurityTreatAsSafe]
149 #else
150         [SecuritySafeCritical]
151 #endif
152         public static bool TryBindInvoke(InvokeBinder binder, DynamicMetaObject instance, DynamicMetaObject[] args, out DynamicMetaObject result) {
153             ContractUtils.RequiresNotNull(binder, "binder");
154             ContractUtils.RequiresNotNull(instance, "instance");
155             ContractUtils.RequiresNotNull(args, "args");
156             
157             if (TryGetMetaObject(ref instance)) {
158                 //
159                 // Demand Full Trust to proceed with the binding.
160                 //
161
162                 new PermissionSet(PermissionState.Unrestricted).Demand();
163
164                 result = instance.BindInvoke(binder, args);
165                 return true;
166             } else {
167                 result = null;
168                 return false;
169             }
170         }
171
172         /// <summary>
173         /// Tries to perform binding of the dynamic invoke member operation.
174         /// </summary>
175         /// <param name="binder">An instance of the <see cref="InvokeMemberBinder"/> that represents the details of the dynamic operation.</param>
176         /// <param name="instance">The target of the dynamic operation. </param>
177         /// <param name="args">An array of <see cref="DynamicMetaObject"/> instances - arguments to the invoke member operation.</param>
178         /// <param name="result">The new <see cref="DynamicMetaObject"/> representing the result of the binding.</param>
179         /// <returns>true if operation was bound successfully; otherwise, false.</returns>
180 #if MICROSOFT_DYNAMIC
181         [SecurityCritical, SecurityTreatAsSafe]
182 #else
183         [SecuritySafeCritical]
184 #endif
185         public static bool TryBindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject instance, DynamicMetaObject[] args, out DynamicMetaObject result) {
186             ContractUtils.RequiresNotNull(binder, "binder");
187             ContractUtils.RequiresNotNull(instance, "instance");
188             ContractUtils.RequiresNotNull(args, "args");
189
190             if (TryGetMetaObject(ref instance)) {
191                 //
192                 // Demand Full Trust to proceed with the binding.
193                 //
194
195                 new PermissionSet(PermissionState.Unrestricted).Demand();
196
197                 result = instance.BindInvokeMember(binder, args);
198                 return true;
199             } else {
200                 result = null;
201                 return false;
202             }
203         }
204
205         /// <summary>
206         /// Tries to perform binding of the dynamic get index operation.
207         /// </summary>
208         /// <param name="binder">An instance of the <see cref="GetIndexBinder"/> that represents the details of the dynamic operation.</param>
209         /// <param name="instance">The target of the dynamic operation. </param>
210         /// <param name="args">An array of <see cref="DynamicMetaObject"/> instances - arguments to the invoke member operation.</param>
211         /// <param name="result">The new <see cref="DynamicMetaObject"/> representing the result of the binding.</param>
212         /// <returns>true if operation was bound successfully; otherwise, false.</returns>
213 #if MICROSOFT_DYNAMIC
214         [SecurityCritical, SecurityTreatAsSafe]
215 #else
216         [SecuritySafeCritical]
217 #endif
218         public static bool TryBindGetIndex(GetIndexBinder binder, DynamicMetaObject instance, DynamicMetaObject[] args, out DynamicMetaObject result) {
219             ContractUtils.RequiresNotNull(binder, "binder");
220             ContractUtils.RequiresNotNull(instance, "instance");
221             ContractUtils.RequiresNotNull(args, "args");
222
223             if (TryGetMetaObject(ref instance)) {
224                 //
225                 // Demand Full Trust to proceed with the binding.
226                 //
227
228                 new PermissionSet(PermissionState.Unrestricted).Demand();
229
230                 result = instance.BindGetIndex(binder, args);
231                 return true;
232             } else {
233                 result = null;
234                 return false;
235             }
236         }
237
238         /// <summary>
239         /// Tries to perform binding of the dynamic set index operation.
240         /// </summary>
241         /// <param name="binder">An instance of the <see cref="SetIndexBinder"/> that represents the details of the dynamic operation.</param>
242         /// <param name="instance">The target of the dynamic operation. </param>
243         /// <param name="args">An array of <see cref="DynamicMetaObject"/> instances - arguments to the invoke member operation.</param>
244         /// <param name="value">The <see cref="DynamicMetaObject"/> representing the value for the set index operation.</param>
245         /// <param name="result">The new <see cref="DynamicMetaObject"/> representing the result of the binding.</param>
246         /// <returns>true if operation was bound successfully; otherwise, false.</returns>
247 #if MICROSOFT_DYNAMIC
248         [SecurityCritical, SecurityTreatAsSafe]
249 #else
250         [SecuritySafeCritical]
251 #endif
252         public static bool TryBindSetIndex(SetIndexBinder binder, DynamicMetaObject instance, DynamicMetaObject[] args, DynamicMetaObject value, out DynamicMetaObject result) {
253             ContractUtils.RequiresNotNull(binder, "binder");
254             ContractUtils.RequiresNotNull(instance, "instance");
255             ContractUtils.RequiresNotNull(args, "args");
256             ContractUtils.RequiresNotNull(value, "value");
257
258             if (TryGetMetaObject(ref instance)) {
259                 //
260                 // Demand Full Trust to proceed with the binding.
261                 //
262
263                 new PermissionSet(PermissionState.Unrestricted).Demand();
264
265                 result = instance.BindSetIndex(binder, args, value);
266                 return true;
267             } else {
268                 result = null;
269                 return false;
270             }
271         }
272
273         /// <summary>
274         /// Tries to perform binding of the dynamic Convert operation.
275         /// </summary>
276         /// <param name="binder">An instance of the <see cref="ConvertBinder"/> that represents the details of the dynamic operation.</param>
277         /// <param name="instance">The target of the dynamic operation.</param>
278         /// <param name="result">The new <see cref="DynamicMetaObject"/> representing the result of the binding.</param>
279         /// <returns>true if operation was bound successfully; otherwise, false.</returns>
280 #if MICROSOFT_DYNAMIC
281         [SecurityCritical, SecurityTreatAsSafe]
282 #else
283         [SecuritySafeCritical]
284 #endif
285         public static bool TryConvert(ConvertBinder binder, DynamicMetaObject instance, out DynamicMetaObject result) {
286             ContractUtils.RequiresNotNull(binder, "binder");
287             ContractUtils.RequiresNotNull(instance, "instance");
288
289             if (IsComObject(instance.Value)) {
290                 //
291                 // Demand Full Trust to proceed with the binding.
292                 //
293
294                 new PermissionSet(PermissionState.Unrestricted).Demand(); 
295
296                 // Converting a COM object to any interface is always considered possible - it will result in 
297                 // a QueryInterface at runtime
298                 if (binder.Type.IsInterface) {
299                     result = new DynamicMetaObject(
300                         Expression.Convert(
301                             instance.Expression,
302                             binder.Type
303                         ),
304                         BindingRestrictions.GetExpressionRestriction(
305                             Expression.Call(
306                                 typeof(ComObject).GetMethod("IsComObject", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic),
307                                 Helpers.Convert(instance.Expression, typeof(object))
308                             )
309                         )
310                     );
311                     return true;
312                 }
313             }
314
315             result = null;
316             return false;
317         }
318
319         /// <summary>
320         /// Gets the member names associated with the object.
321         /// This function can operate only with objects for which <see cref="IsComObject"/> returns true.
322         /// </summary>
323         /// <param name="value">The object for which member names are requested.</param>
324         /// <returns>The collection of member names.</returns>
325 #if MICROSOFT_DYNAMIC
326         [SecurityCritical, SecurityTreatAsSafe]
327 #else
328         [SecuritySafeCritical]
329 #endif
330         public static IEnumerable<string> GetDynamicMemberNames(object value) {
331             ContractUtils.RequiresNotNull(value, "value");
332             ContractUtils.Requires(IsComObject(value), "value", Strings.ComObjectExpected);
333
334             //
335             // Demand Full Trust to proceed with the binding.
336             //
337
338             new PermissionSet(PermissionState.Unrestricted).Demand(); 
339
340             return ComObject.ObjectToComObject(value).GetMemberNames(false);
341         }
342
343         /// <summary>
344         /// Gets the member names of the data-like members associated with the object.
345         /// This function can operate only with objects for which <see cref="IsComObject"/> returns true.
346         /// </summary>
347         /// <param name="value">The object for which member names are requested.</param>
348         /// <returns>The collection of member names.</returns>
349 #if MICROSOFT_DYNAMIC
350         [SecurityCritical, SecurityTreatAsSafe]
351 #else
352         [SecuritySafeCritical]
353 #endif
354         [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
355         internal static IList<string> GetDynamicDataMemberNames(object value) {
356             ContractUtils.RequiresNotNull(value, "value");
357             ContractUtils.Requires(IsComObject(value), "value", Strings.ComObjectExpected);
358
359             //
360             // Demand Full Trust to proceed with the binding.
361             //
362
363             new PermissionSet(PermissionState.Unrestricted).Demand(); 
364
365             return ComObject.ObjectToComObject(value).GetMemberNames(true);
366         }
367
368         /// <summary>
369         /// Gets the data-like members and associated data for an object.
370         /// This function can operate only with objects for which <see cref="IsComObject"/> returns true.
371         /// </summary>
372         /// <param name="value">The object for which data members are requested.</param>
373         /// <param name="names">The enumeration of names of data members for which to retrieve values.</param>
374         /// <returns>The collection of pairs that represent data member's names and their data.</returns>
375         [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
376         [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
377 #if MICROSOFT_DYNAMIC
378         [SecurityCritical, SecurityTreatAsSafe]
379 #else
380         [SecuritySafeCritical]
381 #endif
382         internal static IList<KeyValuePair<string, object>> GetDynamicDataMembers(object value, IEnumerable<string> names) {
383             ContractUtils.RequiresNotNull(value, "value");
384             ContractUtils.Requires(IsComObject(value), "value", Strings.ComObjectExpected);
385
386             //
387             // Demand Full Trust to proceed with the binding.
388             //
389
390             new PermissionSet(PermissionState.Unrestricted).Demand();
391
392             return ComObject.ObjectToComObject(value).GetMembers(names);
393         }
394
395         private static bool TryGetMetaObject(ref DynamicMetaObject instance) {
396             // If we're already a COM MO don't make a new one
397             // (we do this to prevent recursion if we call Fallback from COM)
398             if (instance is ComUnwrappedMetaObject) {
399                 return false;
400             }
401
402             if (IsComObject(instance.Value)) {
403                 instance = new ComMetaObject(instance.Expression, instance.Restrictions, instance.Value);
404                 return true;
405             }
406
407             return false;
408         }
409
410         /// <summary>
411         /// Special binder that indicates special semantics for COM GetMember operation.
412         /// </summary>
413         internal class ComGetMemberBinder : GetMemberBinder {
414             private readonly GetMemberBinder _originalBinder;
415             internal bool _CanReturnCallables;
416
417             internal ComGetMemberBinder(GetMemberBinder originalBinder, bool CanReturnCallables) :
418                 base(originalBinder.Name, originalBinder.IgnoreCase) {
419                 _originalBinder = originalBinder;
420                 _CanReturnCallables = CanReturnCallables;
421             }
422
423             public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion) {
424                 return _originalBinder.FallbackGetMember(target, errorSuggestion);
425             }
426
427             public override int GetHashCode() {
428                 return _originalBinder.GetHashCode() ^ (_CanReturnCallables ? 1 : 0);
429             }
430
431             public override bool Equals(object obj) {
432                 ComGetMemberBinder other = obj as ComGetMemberBinder;
433                 return other != null &&
434                     _CanReturnCallables == other._CanReturnCallables &&
435                     _originalBinder.Equals(other._originalBinder);
436             }
437         }
438     }
439 }
440
441 #endif