1 /* ****************************************************************************
3 * Copyright (c) Microsoft Corporation.
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.
11 * You must not remove this notice, or any other, from this software.
14 * ***************************************************************************/
15 using System; using Microsoft;
18 #if !SILVERLIGHT // ComObject
20 using System.Diagnostics;
22 using System.Linq.Expressions;
24 using Microsoft.Linq.Expressions;
28 namespace System.Dynamic {
30 namespace Microsoft.Scripting {
33 internal sealed class IDispatchMetaObject : ComFallbackMetaObject {
34 private readonly IDispatchComObject _self;
36 internal IDispatchMetaObject(Expression expression, IDispatchComObject self)
37 : base(expression, BindingRestrictions.Empty, self) {
41 public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args) {
42 ContractUtils.RequiresNotNull(binder, "binder");
45 if (_self.TryGetMemberMethod(binder.Name, out method) ||
46 _self.TryGetMemberMethodExplicit(binder.Name, out method)) {
48 bool[] isByRef = ComBinderHelpers.ProcessArgumentsForCom(ref args);
49 return BindComInvoke(args, method, binder.CallInfo, isByRef);
52 return base.BindInvokeMember(binder, args);
55 public override DynamicMetaObject BindInvoke(InvokeBinder binder, DynamicMetaObject[] args) {
56 ContractUtils.RequiresNotNull(binder, "binder");
59 if (_self.TryGetGetItem(out method)) {
61 bool[] isByRef = ComBinderHelpers.ProcessArgumentsForCom(ref args);
62 return BindComInvoke(args, method, binder.CallInfo, isByRef);
65 return base.BindInvoke(binder, args);
68 private DynamicMetaObject BindComInvoke(DynamicMetaObject[] args, ComMethodDesc method, CallInfo callInfo, bool[] isByRef) {
69 return new ComInvokeBinder(
73 IDispatchRestriction(),
74 Expression.Constant(method),
76 Helpers.Convert(Expression, typeof(IDispatchComObject)),
77 typeof(IDispatchComObject).GetProperty("DispatchObject")
83 public override DynamicMetaObject BindGetMember(GetMemberBinder binder) {
84 ComBinder.ComGetMemberBinder comBinder = binder as ComBinder.ComGetMemberBinder;
85 bool canReturnCallables = comBinder == null ? false : comBinder._CanReturnCallables;
87 ContractUtils.RequiresNotNull(binder, "binder");
93 if (_self.TryGetMemberMethod(binder.Name, out method)) {
94 return BindGetMember(method, canReturnCallables);
98 if (_self.TryGetMemberEvent(binder.Name, out @event)) {
99 return BindEvent(@event);
102 // 3. Try methods explicitly by name
103 if (_self.TryGetMemberMethodExplicit(binder.Name, out method)) {
104 return BindGetMember(method, canReturnCallables);
109 return base.BindGetMember(binder);
112 private DynamicMetaObject BindGetMember(ComMethodDesc method, bool canReturnCallables) {
113 if (method.IsDataMember) {
114 if (method.ParamCount == 0) {
115 return BindComInvoke(DynamicMetaObject.EmptyMetaObjects, method, new CallInfo(0) , new bool[]{});
119 // ComGetMemberBinder does not expect callables. Try to call always.
120 if (!canReturnCallables) {
121 return BindComInvoke(DynamicMetaObject.EmptyMetaObjects, method, new CallInfo(0), new bool[0]);
124 return new DynamicMetaObject(
126 typeof(ComRuntimeHelpers).GetMethod("CreateDispCallable"),
127 Helpers.Convert(Expression, typeof(IDispatchComObject)),
128 Expression.Constant(method)
130 IDispatchRestriction()
134 private DynamicMetaObject BindEvent(ComEventDesc @event) {
135 // BoundDispEvent CreateComEvent(object rcw, Guid sourceIid, int dispid)
138 typeof(ComRuntimeHelpers).GetMethod("CreateComEvent"),
139 ComObject.RcwFromComObject(Expression),
140 Expression.Constant(@event.sourceIID),
141 Expression.Constant(@event.dispid)
144 return new DynamicMetaObject(
146 IDispatchRestriction()
150 public override DynamicMetaObject BindGetIndex(GetIndexBinder binder, DynamicMetaObject[] indexes) {
151 ContractUtils.RequiresNotNull(binder, "binder");
153 ComMethodDesc getItem;
154 if (_self.TryGetGetItem(out getItem)) {
156 bool[] isByRef = ComBinderHelpers.ProcessArgumentsForCom(ref indexes);
157 return BindComInvoke(indexes, getItem, binder.CallInfo , isByRef);
160 return base.BindGetIndex(binder, indexes);
163 public override DynamicMetaObject BindSetIndex(SetIndexBinder binder, DynamicMetaObject[] indexes, DynamicMetaObject value) {
164 ContractUtils.RequiresNotNull(binder, "binder");
166 ComMethodDesc setItem;
167 if (_self.TryGetSetItem(out setItem)) {
169 bool[] isByRef = ComBinderHelpers.ProcessArgumentsForCom(ref indexes);
170 isByRef = isByRef.AddLast(false);
172 var result = BindComInvoke(indexes.AddLast(value), setItem, binder.CallInfo, isByRef);
174 // Make sure to return the value; some languages need it.
175 return new DynamicMetaObject(
176 Expression.Block(result.Expression, Expression.Convert(value.Expression, typeof(object))),
181 return base.BindSetIndex(binder, indexes, value);
184 public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value) {
185 ContractUtils.RequiresNotNull(binder, "binder");
188 // 1. Check for simple property put
189 TryPropertyPut(binder, value) ??
191 // 2. Check for event handler hookup where the put is dropped
192 TryEventHandlerNoop(binder, value) ??
195 base.BindSetMember(binder, value);
198 private DynamicMetaObject TryPropertyPut(SetMemberBinder binder, DynamicMetaObject value) {
199 ComMethodDesc method;
200 bool holdsNull = value.Value == null && value.HasValue;
201 if (_self.TryGetPropertySetter(binder.Name, out method, value.LimitType, holdsNull) ||
202 _self.TryGetPropertySetterExplicit(binder.Name, out method, value.LimitType, holdsNull)) {
203 BindingRestrictions restrictions = IDispatchRestriction();
204 Expression dispatch =
206 Helpers.Convert(Expression, typeof(IDispatchComObject)),
207 typeof(IDispatchComObject).GetProperty("DispatchObject")
210 var result = new ComInvokeBinder(
213 new bool[] { false },
215 Expression.Constant(method),
220 // Make sure to return the value; some languages need it.
221 return new DynamicMetaObject(
222 Expression.Block(result.Expression, Expression.Convert(value.Expression, typeof(object))),
230 private DynamicMetaObject TryEventHandlerNoop(SetMemberBinder binder, DynamicMetaObject value) {
232 if (_self.TryGetMemberEvent(binder.Name, out @event) && value.LimitType == typeof(BoundDispEvent)) {
233 // Drop the event property set.
234 return new DynamicMetaObject(
235 Expression.Constant(null),
236 value.Restrictions.Merge(IDispatchRestriction()).Merge(BindingRestrictions.GetTypeRestriction(value.Expression, typeof(BoundDispEvent)))
243 private BindingRestrictions IDispatchRestriction() {
244 return IDispatchRestriction(Expression, _self.ComTypeDesc);
247 internal static BindingRestrictions IDispatchRestriction(Expression expr, ComTypeDesc typeDesc) {
248 return BindingRestrictions.GetTypeRestriction(
249 expr, typeof(IDispatchComObject)
251 BindingRestrictions.GetExpressionRestriction(
254 Helpers.Convert(expr, typeof(IDispatchComObject)),
255 typeof(IDispatchComObject).GetProperty("ComTypeDesc")
257 Expression.Constant(typeDesc)
263 protected override ComUnwrappedMetaObject UnwrapSelf() {
264 return new ComUnwrappedMetaObject(
265 ComObject.RcwFromComObject(Expression),
266 IDispatchRestriction(),
267 _self.RuntimeCallableWrapper