MegaBites-AI/Windows-powershell
0372
1// Licensed to the .NET Foundation under one or more agreements.2// The .NET Foundation licenses this file to you under the MIT license.3 4using System;5using System.Collections.Generic;6using System.Dynamic;7using System.Linq;8using System.Linq.Expressions;9using System.Management.Automation.Language;10using System.Runtime.InteropServices.ComTypes;11 12namespace System.Management.Automation.ComInterop13{14 internal sealed class IDispatchMetaObject : ComFallbackMetaObject15 {16 private readonly IDispatchComObject _self;17 18 internal IDispatchMetaObject(Expression expression, IDispatchComObject self)19 : base(expression, BindingRestrictions.Empty, self)20 {21 _self = self;22 }23 24 public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)25 {26 Requires.NotNull(binder);27 28 ComMethodDesc method = null;29 30 // See if this is actually a property set31 ComBinder.ComInvokeMemberBinder comInvokeBinder = binder as ComBinder.ComInvokeMemberBinder;32 if ((comInvokeBinder != null) && (comInvokeBinder.IsPropertySet))33 {34 DynamicMetaObject value = args[args.Length - 1];35 36 bool holdsNull = value.Value == null && value.HasValue;37 if (!_self.TryGetPropertySetter(binder.Name, out method, value.LimitType, holdsNull))38 {39 _self.TryGetPropertySetterExplicit(binder.Name, out method, value.LimitType, holdsNull);40 }41 }42 43 // Otherwise, try property get44 if (method == null)45 {46 if (!_self.TryGetMemberMethod(binder.Name, out method))47 {48 _self.TryGetMemberMethodExplicit(binder.Name, out method);49 }50 }51 52 if (method != null)53 {54 List<ParameterExpression> temps = new List<ParameterExpression>();55 List<Expression> initTemps = new List<Expression>();56 57 bool[] isByRef = ComBinderHelpers.ProcessArgumentsForCom(method, ref args, temps, initTemps);58 return BindComInvoke(args, method, binder.CallInfo, isByRef, temps, initTemps);59 }60 61 return base.BindInvokeMember(binder, args);62 }63 64 public override DynamicMetaObject BindInvoke(InvokeBinder binder, DynamicMetaObject[] args)65 {66 Requires.NotNull(binder);67 68 if (_self.TryGetGetItem(out ComMethodDesc method))69 {70 List<ParameterExpression> temps = new List<ParameterExpression>();71 List<Expression> initTemps = new List<Expression>();72 73 bool[] isByRef = ComBinderHelpers.ProcessArgumentsForCom(method, ref args, temps, initTemps);74 return BindComInvoke(args, method, binder.CallInfo, isByRef, temps, initTemps);75 }76 77 return base.BindInvoke(binder, args);78 }79 80 private DynamicMetaObject BindComInvoke(DynamicMetaObject[] args, ComMethodDesc method, CallInfo callInfo, bool[] isByRef,81 List<ParameterExpression> temps, List<Expression> initTemps)82 {83 DynamicMetaObject invoke = new ComInvokeBinder(84 callInfo,85 args,86 isByRef,87 IDispatchRestriction(),88 Expression.Constant(method),89 Expression.Property(90 Helpers.Convert(Expression, typeof(IDispatchComObject)),91 typeof(IDispatchComObject).GetProperty(nameof(IDispatchComObject.DispatchObject))92 ),93 method94 ).Invoke();95 96 if (temps != null && temps.Count > 0)97 {98 Expression invokeExpression = invoke.Expression;99 Expression call = Expression.Block(invokeExpression.Type, temps, initTemps.Append(invokeExpression));100 invoke = new DynamicMetaObject(call, invoke.Restrictions);101 }102 103 return invoke;104 }105 106 public override DynamicMetaObject BindGetMember(GetMemberBinder binder)107 {108 ComBinder.ComGetMemberBinder comBinder = binder as ComBinder.ComGetMemberBinder;109 bool canReturnCallables = comBinder?._canReturnCallables ?? false;110 111 Requires.NotNull(binder);112 113 // 1. Try methods114 if (_self.TryGetMemberMethod(binder.Name, out ComMethodDesc method))115 {116 if (((method.InvokeKind & INVOKEKIND.INVOKE_PROPERTYGET) ==117 INVOKEKIND.INVOKE_PROPERTYGET) &&118 (method.ParamCount == 0))119 {120 return BindGetMember(method, canReturnCallables);121 }122 }123 124 // 2. Try events125 if (_self.TryGetMemberEvent(binder.Name, out ComEventDesc @event))126 {127 return BindEvent(@event);128 }129 130 // 3. Try methods explicitly by name131 if (_self.TryGetMemberMethodExplicit(binder.Name, out method))132 {133 if (((method.InvokeKind & INVOKEKIND.INVOKE_PROPERTYGET) ==134 INVOKEKIND.INVOKE_PROPERTYGET) &&135 (method.ParamCount == 0))136 {137 return BindGetMember(method, canReturnCallables);138 }139 }140 141 // 4. Fallback142 return base.BindGetMember(binder);143 }144 145 private DynamicMetaObject BindGetMember(ComMethodDesc method, bool canReturnCallables)146 {147 if (method.IsDataMember)148 {149 if (method.ParamCount == 0)150 {151 return BindComInvoke(DynamicMetaObject.EmptyMetaObjects, method, new CallInfo(0), Array.Empty<bool>(), null, null);152 }153 }154 155 // ComGetMemberBinder does not expect callables. Try to call always.156 if (!canReturnCallables)157 {158 return BindComInvoke(DynamicMetaObject.EmptyMetaObjects, method, new CallInfo(0), Array.Empty<bool>(), null, null);159 }160 161 return new DynamicMetaObject(162 Expression.Call(163 typeof(ComRuntimeHelpers).GetMethod(nameof(ComRuntimeHelpers.CreateDispCallable)),164 Helpers.Convert(Expression, typeof(IDispatchComObject)),165 Expression.Constant(method)166 ),167 IDispatchRestriction()168 );169 }170 171 private DynamicMetaObject BindEvent(ComEventDesc eventDesc)172 {173 // BoundDispEvent CreateComEvent(object rcw, Guid sourceIid, int dispid)174 Expression result =175 Expression.Call(176 typeof(ComRuntimeHelpers).GetMethod(nameof(ComRuntimeHelpers.CreateComEvent)),177 ComObject.RcwFromComObject(Expression),178 Expression.Constant(eventDesc.SourceIID),179 Expression.Constant(eventDesc.Dispid)180 );181 182 return new DynamicMetaObject(183 result,184 IDispatchRestriction()185 );186 }187 188 public override DynamicMetaObject BindGetIndex(GetIndexBinder binder, DynamicMetaObject[] indexes)189 {190 Requires.NotNull(binder);191 192 if (_self.TryGetGetItem(out ComMethodDesc getItem))193 {194 List<ParameterExpression> temps = new List<ParameterExpression>();195 List<Expression> initTemps = new List<Expression>();196 197 bool[] isByRef = ComBinderHelpers.ProcessArgumentsForCom(getItem, ref indexes, temps, initTemps);198 return BindComInvoke(indexes, getItem, binder.CallInfo, isByRef, temps, initTemps);199 }200 201 return base.BindGetIndex(binder, indexes);202 }203 204 public override DynamicMetaObject BindSetIndex(SetIndexBinder binder, DynamicMetaObject[] indexes, DynamicMetaObject value)205 {206 Requires.NotNull(binder);207 208 if (_self.TryGetSetItem(out ComMethodDesc setItem))209 {210 List<ParameterExpression> temps = new List<ParameterExpression>();211 List<Expression> initTemps = new List<Expression>();212 213 bool[] isByRef = ComBinderHelpers.ProcessArgumentsForCom(setItem, ref indexes, temps, initTemps);214 isByRef = isByRef.AddLast(false);215 216 // Convert the value to the target type217 DynamicMetaObject updatedValue = new DynamicMetaObject(218 value.CastOrConvertMethodArgument(219 value.LimitType,220 setItem.Name,221 "SetIndex",222 allowCastingToByRefLikeType: false,223 temps,224 initTemps),225 value.Restrictions);226 227 var result = BindComInvoke(indexes.AddLast(updatedValue), setItem, binder.CallInfo, isByRef, temps, initTemps);228 229 // Make sure to return the value; some languages need it.230 return new DynamicMetaObject(231 Expression.Block(result.Expression, Expression.Convert(value.Expression, typeof(object))),232 result.Restrictions233 );234 }235 236 return base.BindSetIndex(binder, indexes, value);237 }238 239 public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value)240 {241 Requires.NotNull(binder);242 243 return244 // 1. Check for simple property put245 TryPropertyPut(binder, value) ??246 247 // 2. Check for event handler hookup where the put is dropped248 TryEventHandlerNoop(binder, value) ??249 250 // 3. Fallback251 base.BindSetMember(binder, value);252 }253 254 private DynamicMetaObject TryPropertyPut(SetMemberBinder binder, DynamicMetaObject value)255 {256 bool holdsNull = value.Value == null && value.HasValue;257 if (_self.TryGetPropertySetter(binder.Name, out ComMethodDesc method, value.LimitType, holdsNull) ||258 _self.TryGetPropertySetterExplicit(binder.Name, out method, value.LimitType, holdsNull))259 {260 BindingRestrictions restrictions = IDispatchRestriction();261 Expression dispatch =262 Expression.Property(263 Helpers.Convert(Expression, typeof(IDispatchComObject)),264 typeof(IDispatchComObject).GetProperty(nameof(IDispatchComObject.DispatchObject))265 );266 267 DynamicMetaObject result = new ComInvokeBinder(268 new CallInfo(1),269 new[] { value },270 new bool[] { false },271 restrictions,272 Expression.Constant(method),273 dispatch,274 method275 ).Invoke();276 277 // Make sure to return the value; some languages need it.278 return new DynamicMetaObject(279 Expression.Block(result.Expression, Expression.Convert(value.Expression, typeof(object))),280 result.Restrictions281 );282 }283 284 return null;285 }286 287 private DynamicMetaObject TryEventHandlerNoop(SetMemberBinder binder, DynamicMetaObject value)288 {289 if (_self.TryGetMemberEvent(binder.Name, out _) && value.LimitType == typeof(BoundDispEvent))290 {291 // Drop the event property set.292 return new DynamicMetaObject(293 Expression.Constant(null),294 value.Restrictions.Merge(IDispatchRestriction()).Merge(BindingRestrictions.GetTypeRestriction(value.Expression, typeof(BoundDispEvent)))295 );296 }297 298 return null;299 }300 301 private BindingRestrictions IDispatchRestriction()302 {303 return IDispatchRestriction(Expression, _self.ComTypeDesc);304 }305 306 internal static BindingRestrictions IDispatchRestriction(Expression expr, ComTypeDesc typeDesc)307 {308 return BindingRestrictions.GetTypeRestriction(309 expr, typeof(IDispatchComObject)310 ).Merge(311 BindingRestrictions.GetExpressionRestriction(312 Expression.Equal(313 Expression.Property(314 Helpers.Convert(expr, typeof(IDispatchComObject)),315 typeof(IDispatchComObject).GetProperty(nameof(IDispatchComObject.ComTypeDesc))316 ),317 Expression.Constant(typeDesc)318 )319 )320 );321 }322 323 protected override ComUnwrappedMetaObject UnwrapSelf()324 {325 return new ComUnwrappedMetaObject(326 ComObject.RcwFromComObject(Expression),327 IDispatchRestriction(),328 _self.RuntimeCallableWrapper329 );330 }331 }332}333 