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.Diagnostics;7using System.Dynamic;8using System.Linq.Expressions;9using System.Management.Automation.InteropServices;10using System.Runtime.InteropServices;11using ComTypes = System.Runtime.InteropServices.ComTypes;12 13namespace System.Management.Automation.ComInterop14{15 internal sealed class ComInvokeBinder16 {17 private readonly ComMethodDesc _methodDesc;18 private readonly Expression _method; // ComMethodDesc to be called19 private readonly Expression _dispatch; // IDispatch20 21 private readonly CallInfo _callInfo;22 private readonly DynamicMetaObject[] _args;23 private readonly bool[] _isByRef;24 private readonly Expression _instance;25 26 private BindingRestrictions _restrictions;27 28 private VarEnumSelector _varEnumSelector;29 private string[] _keywordArgNames;30 private int _totalExplicitArgs; // Includes the individual elements of ArgumentKind.Dictionary (if any)31 32 private ParameterExpression _dispatchObject;33 private ParameterExpression _dispatchPointer;34 private ParameterExpression _dispId;35 private ParameterExpression _dispParams;36 private ParameterExpression _paramVariants;37 private ParameterExpression _invokeResult;38 private ParameterExpression _returnValue;39 private ParameterExpression _dispIdsOfKeywordArgsPinned;40 private ParameterExpression _propertyPutDispId;41 42 internal ComInvokeBinder(43 CallInfo callInfo,44 DynamicMetaObject[] args,45 bool[] isByRef,46 BindingRestrictions restrictions,47 Expression method,48 Expression dispatch,49 ComMethodDesc methodDesc)50 {51 Debug.Assert(callInfo != null, nameof(callInfo));52 Debug.Assert(args != null, nameof(args));53 Debug.Assert(isByRef != null, nameof(isByRef));54 Debug.Assert(method != null, nameof(method));55 Debug.Assert(dispatch != null, nameof(dispatch));56 57 Debug.Assert(TypeUtils.AreReferenceAssignable(typeof(ComMethodDesc), method.Type), "method type");58 Debug.Assert(TypeUtils.AreReferenceAssignable(typeof(IDispatch), dispatch.Type), "dispatch type");59 60 _method = method;61 _dispatch = dispatch;62 _methodDesc = methodDesc;63 64 _callInfo = callInfo;65 _args = args;66 _isByRef = isByRef;67 _restrictions = restrictions;68 69 // Set Instance to some value so that CallBinderHelper has the right number of parameters to work with70 _instance = dispatch;71 }72 73 private ParameterExpression DispatchObjectVariable74 {75 get { return EnsureVariable(ref _dispatchObject, typeof(IDispatch), "dispatchObject"); }76 }77 78 private ParameterExpression DispatchPointerVariable79 {80 get { return EnsureVariable(ref _dispatchPointer, typeof(IntPtr), "dispatchPointer"); }81 }82 83 private ParameterExpression DispIdVariable84 {85 get { return EnsureVariable(ref _dispId, typeof(int), "dispId"); }86 }87 88 private ParameterExpression DispParamsVariable89 {90 get { return EnsureVariable(ref _dispParams, typeof(ComTypes.DISPPARAMS), "dispParams"); }91 }92 93 private ParameterExpression InvokeResultVariable94 {95 get { return EnsureVariable(ref _invokeResult, typeof(Variant), "invokeResult"); }96 }97 98 private ParameterExpression ReturnValueVariable99 {100 get { return EnsureVariable(ref _returnValue, typeof(object), "returnValue"); }101 }102 103 private ParameterExpression DispIdsOfKeywordArgsPinnedVariable104 {105 get { return EnsureVariable(ref _dispIdsOfKeywordArgsPinned, typeof(GCHandle), "dispIdsOfKeywordArgsPinned"); }106 }107 108 private ParameterExpression PropertyPutDispIdVariable109 {110 get { return EnsureVariable(ref _propertyPutDispId, typeof(int), "propertyPutDispId"); }111 }112 113 private ParameterExpression ParamVariantsVariable114 {115 get116 {117 _paramVariants ??= Expression.Variable(VariantArray.GetStructType(_args.Length), "paramVariants");118 return _paramVariants;119 }120 }121 122 private static ParameterExpression EnsureVariable(ref ParameterExpression var, Type type, string name)123 {124 if (var != null)125 {126 return var;127 }128 return var = Expression.Variable(type, name);129 }130 131 private static Type MarshalType(DynamicMetaObject mo, bool isByRef)132 {133 Type marshalType = (mo.Value == null && mo.HasValue && !mo.LimitType.IsValueType) ? null : mo.LimitType;134 135 // we are not checking that mo.Expression is writeable or whether evaluating it has no sideeffects136 // the assumption is that whoever matched it with ByRef arginfo took care of this.137 if (isByRef)138 {139 // Null just means that null was supplied.140 marshalType ??= mo.Expression.Type;141 marshalType = marshalType.MakeByRefType();142 }143 return marshalType;144 }145 146 internal DynamicMetaObject Invoke()147 {148 _keywordArgNames = _callInfo.ArgumentNames.ToArray();149 _totalExplicitArgs = _args.Length;150 151 Type[] marshalArgTypes = new Type[_args.Length];152 153 // We already tested the instance, so no need to test it again154 for (int i = 0; i < _args.Length; i++)155 {156 DynamicMetaObject curMo = _args[i];157 marshalArgTypes[i] = MarshalType(curMo, _isByRef[i]);158 }159 160 _varEnumSelector = new VarEnumSelector(marshalArgTypes);161 162 return new DynamicMetaObject(163 CreateScope(MakeIDispatchInvokeTarget()),164 BindingRestrictions.Combine(_args).Merge(_restrictions)165 );166 }167 168 private static void AddNotNull(List<ParameterExpression> list, ParameterExpression var)169 {170 if (var != null)171 {172 list.Add(var);173 }174 }175 176 private Expression CreateScope(Expression expression)177 {178 List<ParameterExpression> vars = new List<ParameterExpression>();179 AddNotNull(vars, _dispatchObject);180 AddNotNull(vars, _dispatchPointer);181 AddNotNull(vars, _dispId);182 AddNotNull(vars, _dispParams);183 AddNotNull(vars, _paramVariants);184 AddNotNull(vars, _invokeResult);185 AddNotNull(vars, _returnValue);186 AddNotNull(vars, _dispIdsOfKeywordArgsPinned);187 AddNotNull(vars, _propertyPutDispId);188 return vars.Count > 0 ? Expression.Block(vars, expression) : expression;189 }190 191 private Expression GenerateTryBlock()192 {193 //194 // Declare variables195 //196 ParameterExpression excepInfo = Expression.Variable(typeof(ExcepInfo), "excepInfo");197 ParameterExpression argErr = Expression.Variable(typeof(uint), "argErr");198 ParameterExpression hresult = Expression.Variable(typeof(int), "hresult");199 200 List<Expression> tryStatements = new List<Expression>();201 202 if (_keywordArgNames.Length > 0)203 {204 string[] names = _keywordArgNames.AddFirst(_methodDesc.Name);205 206 tryStatements.Add(207 Expression.Assign(208 Expression.Field(209 DispParamsVariable,210 typeof(ComTypes.DISPPARAMS).GetField(nameof(ComTypes.DISPPARAMS.rgdispidNamedArgs))211 ),212 Expression.Call(typeof(UnsafeMethods).GetMethod(nameof(UnsafeMethods.GetIdsOfNamedParameters)),213 DispatchObjectVariable,214 Expression.Constant(names),215 DispIdVariable,216 DispIdsOfKeywordArgsPinnedVariable217 )218 )219 );220 }221 222 //223 // Marshal the arguments to Variants224 //225 // For a call like this:226 // comObj.Foo(100, 101, 102, x=123, z=125)227 // DISPPARAMS needs to be setup like this:228 // cArgs: 5229 // cNamedArgs: 2230 // rgArgs: 123, 125, 102, 101, 100231 // rgdispidNamedArgs: dispid x, dispid z (the dispids of x and z respectively)232 233 Expression[] parameters = MakeArgumentExpressions();234 235 int reverseIndex = _varEnumSelector.VariantBuilders.Length - 1;236 int positionalArgs = _varEnumSelector.VariantBuilders.Length - _keywordArgNames.Length; // args passed by position order and not by name237 for (int i = 0; i < _varEnumSelector.VariantBuilders.Length; i++, reverseIndex--)238 {239 int variantIndex;240 if (i >= positionalArgs)241 {242 // Named arguments are in order at the start of rgArgs243 variantIndex = i - positionalArgs;244 }245 else246 {247 // Positional arguments are in reverse order at the tail of rgArgs248 variantIndex = reverseIndex;249 }250 VariantBuilder variantBuilder = _varEnumSelector.VariantBuilders[i];251 252 Expression marshal = variantBuilder.InitializeArgumentVariant(253 VariantArray.GetStructField(ParamVariantsVariable, variantIndex),254 parameters[i + 1]255 );256 257 if (marshal != null)258 {259 tryStatements.Add(marshal);260 }261 }262 263 //264 // Call Invoke265 //266 267 ComTypes.INVOKEKIND invokeKind;268 if (_methodDesc.IsPropertyPut)269 {270 if (_methodDesc.IsPropertyPutRef)271 {272 invokeKind = ComTypes.INVOKEKIND.INVOKE_PROPERTYPUTREF;273 }274 else275 {276 invokeKind = ComTypes.INVOKEKIND.INVOKE_PROPERTYPUT;277 }278 }279 else280 {281 // INVOKE_PROPERTYGET should only be needed for COM objects without typeinfo, where we might have to treat properties as methods282 invokeKind = ComTypes.INVOKEKIND.INVOKE_FUNC | ComTypes.INVOKEKIND.INVOKE_PROPERTYGET;283 }284 285 MethodCallExpression invoke = Expression.Call(286 typeof(UnsafeMethods).GetMethod(nameof(UnsafeMethods.IDispatchInvoke)),287 DispatchPointerVariable,288 DispIdVariable,289 Expression.Constant(invokeKind),290 DispParamsVariable,291 InvokeResultVariable,292 excepInfo,293 argErr294 );295 296 Expression expr = Expression.Assign(hresult, invoke);297 tryStatements.Add(expr);298 299 //300 // ComRuntimeHelpers.CheckThrowException(int hresult, ref ExcepInfo excepInfo, ComMethodDesc method, object[] args, uint argErr)301 List<Expression> args = new List<Expression>();302 foreach (Expression parameter in parameters)303 {304 args.Add(Expression.TypeAs(parameter, typeof(object)));305 }306 307 expr = Expression.Call(308 typeof(ComRuntimeHelpers).GetMethod(nameof(ComRuntimeHelpers.CheckThrowException)),309 hresult,310 excepInfo,311 Expression.Constant(_methodDesc, typeof(ComMethodDesc)),312 Expression.NewArrayInit(typeof(object), args),313 argErr314 );315 tryStatements.Add(expr);316 317 //318 // _returnValue = (ReturnType)_invokeResult.ToObject();319 //320 Expression invokeResultObject =321 Expression.Call(322 InvokeResultVariable,323 typeof(Variant).GetMethod(nameof(Variant.ToObject)));324 325 VariantBuilder[] variants = _varEnumSelector.VariantBuilders;326 327 Expression[] parametersForUpdates = MakeArgumentExpressions();328 tryStatements.Add(Expression.Assign(ReturnValueVariable, invokeResultObject));329 330 for (int i = 0, n = variants.Length; i < n; i++)331 {332 Expression updateFromReturn = variants[i].UpdateFromReturn(parametersForUpdates[i + 1]);333 if (updateFromReturn != null)334 {335 tryStatements.Add(updateFromReturn);336 }337 }338 339 tryStatements.Add(Expression.Empty());340 341 return Expression.Block(new[] { excepInfo, argErr, hresult }, tryStatements);342 }343 344 private Expression GenerateFinallyBlock()345 {346 List<Expression> finallyStatements = new List<Expression>347 {348 //349 // UnsafeMethods.IUnknownRelease(dispatchPointer);350 //351 Expression.Call(352 typeof(UnsafeMethods).GetMethod(nameof(UnsafeMethods.IUnknownRelease)),353 DispatchPointerVariable354 )355 };356 357 //358 // Clear memory allocated for marshalling359 //360 for (int i = 0, n = _varEnumSelector.VariantBuilders.Length; i < n; i++)361 {362 Expression clear = _varEnumSelector.VariantBuilders[i].Clear();363 if (clear != null)364 {365 finallyStatements.Add(clear);366 }367 }368 369 //370 // _invokeResult.Clear()371 //372 373 finallyStatements.Add(374 Expression.Call(375 InvokeResultVariable,376 typeof(Variant).GetMethod(nameof(Variant.Clear))377 )378 );379 380 //381 // _dispIdsOfKeywordArgsPinned.Free()382 //383 if (_dispIdsOfKeywordArgsPinned != null)384 {385 finallyStatements.Add(386 Expression.Call(387 DispIdsOfKeywordArgsPinnedVariable,388 typeof(GCHandle).GetMethod(nameof(GCHandle.Free))389 )390 );391 }392 393 finallyStatements.Add(Expression.Empty());394 return Expression.Block(finallyStatements);395 }396 397 /// <summary>398 /// Create a stub for the target of the optimized loop.399 /// </summary>400 /// <returns></returns>401 private Expression MakeIDispatchInvokeTarget()402 {403 Debug.Assert(_varEnumSelector.VariantBuilders.Length == _totalExplicitArgs);404 405 List<Expression> exprs = new List<Expression>406 {407 //408 // _dispId = ((DispCallable)this).ComMethodDesc.DispId;409 //410 Expression.Assign(411 DispIdVariable,412 Expression.Property(_method, typeof(ComMethodDesc).GetProperty(nameof(ComMethodDesc.DispId)))413 )414 };415 416 //417 // _dispParams.rgvararg = RuntimeHelpers.UnsafeMethods.ConvertVariantByrefToPtr(ref _paramVariants._element0)418 //419 if (_totalExplicitArgs != 0)420 {421 exprs.Add(422 Expression.Assign(423 Expression.Field(424 DispParamsVariable,425 typeof(ComTypes.DISPPARAMS).GetField(nameof(ComTypes.DISPPARAMS.rgvarg))426 ),427 Expression.Call(428 typeof(UnsafeMethods).GetMethod(nameof(UnsafeMethods.ConvertVariantByrefToPtr)),429 VariantArray.GetStructField(ParamVariantsVariable, 0)430 )431 )432 );433 }434 435 //436 // _dispParams.cArgs = <number_of_params>;437 //438 exprs.Add(439 Expression.Assign(440 Expression.Field(441 DispParamsVariable,442 typeof(ComTypes.DISPPARAMS).GetField(nameof(ComTypes.DISPPARAMS.cArgs))443 ),444 Expression.Constant(_totalExplicitArgs)445 )446 );447 448 if (_methodDesc.IsPropertyPut)449 {450 //451 // dispParams.cNamedArgs = 1;452 // dispParams.rgdispidNamedArgs = RuntimeHelpers.UnsafeMethods.GetNamedArgsForPropertyPut()453 //454 exprs.Add(455 Expression.Assign(456 Expression.Field(457 DispParamsVariable,458 typeof(ComTypes.DISPPARAMS).GetField(nameof(ComTypes.DISPPARAMS.cNamedArgs))459 ),460 Expression.Constant(1)461 )462 );463 464 exprs.Add(465 Expression.Assign(466 PropertyPutDispIdVariable,467 Expression.Constant(ComDispIds.DISPID_PROPERTYPUT)468 )469 );470 471 exprs.Add(472 Expression.Assign(473 Expression.Field(474 DispParamsVariable,475 typeof(ComTypes.DISPPARAMS).GetField(nameof(ComTypes.DISPPARAMS.rgdispidNamedArgs))476 ),477 Expression.Call(478 typeof(UnsafeMethods).GetMethod(nameof(UnsafeMethods.ConvertInt32ByrefToPtr)),479 PropertyPutDispIdVariable480 )481 )482 );483 }484 else485 {486 //487 // _dispParams.cNamedArgs = N;488 //489 exprs.Add(490 Expression.Assign(491 Expression.Field(492 DispParamsVariable,493 typeof(ComTypes.DISPPARAMS).GetField(nameof(ComTypes.DISPPARAMS.cNamedArgs))494 ),495 Expression.Constant(_keywordArgNames.Length)496 )497 );498 }499 500 //501 // _dispatchObject = _dispatch502 // _dispatchPointer = Marshal.GetIDispatchForObject(_dispatchObject);503 //504 505 exprs.Add(Expression.Assign(DispatchObjectVariable, _dispatch));506 507 exprs.Add(508 Expression.Assign(509 DispatchPointerVariable,510 Expression.Call(511 typeof(Marshal).GetMethod(nameof(Marshal.GetIDispatchForObject)),512 DispatchObjectVariable513 )514 )515 );516 517 Expression tryStatements = GenerateTryBlock();518 Expression finallyStatements = GenerateFinallyBlock();519 520 exprs.Add(Expression.TryFinally(tryStatements, finallyStatements));521 522 exprs.Add(ReturnValueVariable);523 var vars = new List<ParameterExpression>();524 foreach (VariantBuilder variant in _varEnumSelector.VariantBuilders)525 {526 if (variant.TempVariable != null)527 {528 vars.Add(variant.TempVariable);529 }530 }531 532 // If the method returns void, return AutomationNull533 if (_methodDesc.ReturnType == typeof(void))534 {535 exprs.Add(System.Management.Automation.Language.ExpressionCache.AutomationNullConstant);536 }537 538 return Expression.Block(vars, exprs);539 }540 541 /// <summary>542 /// Gets expressions to access all the arguments. This includes the instance argument.543 /// </summary>544 private Expression[] MakeArgumentExpressions()545 {546 Expression[] res;547 int copy = 0;548 if (_instance != null)549 {550 res = new Expression[_args.Length + 1];551 res[copy++] = _instance;552 }553 else554 {555 res = new Expression[_args.Length];556 }557 558 for (int i = 0; i < _args.Length; i++)559 {560 res[copy++] = _args[i].Expression;561 }562 return res;563 }564 }565}566 