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 // ComObject
19 
20 using System.Diagnostics;
21 #if CODEPLEX_40
22 using System.Linq.Expressions;
23 #else
24 using Microsoft.Linq.Expressions;
25 #endif
26 using System.Runtime.InteropServices;
27 
28 #if CODEPLEX_40
29 namespace System.Dynamic {
30 #else
31 namespace Microsoft.Scripting {
32 #endif
33 
34     /// <summary>
35     /// VariantBuilder handles packaging of arguments into a Variant for a call to IDispatch.Invoke
36     /// </summary>
37     internal class VariantBuilder {
38 
39         private MemberExpression _variant;
40         private readonly ArgBuilder _argBuilder;
41         private readonly VarEnum _targetComType;
42         internal ParameterExpression TempVariable { get; private set; }
43 
VariantBuilder(VarEnum targetComType, ArgBuilder builder)44         internal VariantBuilder(VarEnum targetComType, ArgBuilder builder) {
45             _targetComType = targetComType;
46             _argBuilder = builder;
47         }
48 
49         internal bool IsByRef {
50             get { return (_targetComType & VarEnum.VT_BYREF) != 0; }
51         }
52 
InitializeArgumentVariant(MemberExpression variant, Expression parameter)53         internal Expression InitializeArgumentVariant(MemberExpression variant, Expression parameter) {
54             //NOTE: we must remember our variant
55             //the reason is that argument order does not map exactly to the order of variants for invoke
56             //and when we are doing clean-up we must be sure we are cleaning the variant we have initialized.
57 
58             _variant = variant;
59 
60             if (IsByRef) {
61                 // temp = argument
62                 // paramVariants._elementN.SetAsByrefT(ref temp)
63                 Debug.Assert(TempVariable == null);
64                 var argExpr = _argBuilder.MarshalToRef(parameter);
65 
66                 TempVariable = Expression.Variable(argExpr.Type, null);
67                 return Expression.Block(
68                     Expression.Assign(TempVariable, argExpr),
69                     Expression.Call(
70                         variant,
71                         Variant.GetByrefSetter(_targetComType & ~VarEnum.VT_BYREF),
72                         TempVariable
73                     )
74                 );
75             }
76 
77             Expression argument = _argBuilder.Marshal(parameter);
78 
79             // we are forced to special case ConvertibleArgBuilder since it does not have
80             // a corresponding _targetComType.
81             if (_argBuilder is ConvertibleArgBuilder) {
82                 return Expression.Call(
83                     variant,
84                     typeof(Variant).GetMethod("SetAsIConvertible"),
85                     argument
86                 );
87             }
88 
89             if (Variant.IsPrimitiveType(_targetComType) ||
90                (_targetComType == VarEnum.VT_DISPATCH) ||
91                (_targetComType == VarEnum.VT_UNKNOWN) ||
92                (_targetComType == VarEnum.VT_VARIANT) ||
93                (_targetComType == VarEnum.VT_RECORD) ||
94                (_targetComType == VarEnum.VT_ARRAY)){
95                 // paramVariants._elementN.AsT = (cast)argN
96                 return Expression.Assign(
97                     Expression.Property(
98                         variant,
99                         Variant.GetAccessor(_targetComType)
100                     ),
101                     argument
102                 );
103             }
104 
105             switch (_targetComType) {
106                 case VarEnum.VT_EMPTY:
107                     return null;
108 
109                 case VarEnum.VT_NULL:
110                     // paramVariants._elementN.SetAsNull();
111                     return Expression.Call(variant, typeof(Variant).GetMethod("SetAsNull"));
112 
113                 default:
114                     Debug.Assert(false, "Unexpected VarEnum");
115                     return null;
116             }
117         }
118 
Release(Expression pUnk)119         private static Expression Release(Expression pUnk) {
120             return Expression.Call(typeof(UnsafeMethods).GetMethod("IUnknownReleaseNotZero"), pUnk);
121         }
122 
Clear()123         internal Expression Clear() {
124             if (IsByRef) {
125                 if (_argBuilder is StringArgBuilder) {
126                     Debug.Assert(TempVariable != null);
127                     return Expression.Call(typeof(Marshal).GetMethod("FreeBSTR"), TempVariable);
128                 } else if (_argBuilder is DispatchArgBuilder) {
129                     Debug.Assert(TempVariable != null);
130                     return Release(TempVariable);
131                 } else if (_argBuilder is UnknownArgBuilder) {
132                     Debug.Assert(TempVariable != null);
133                     return Release(TempVariable);
134                 } else if (_argBuilder is VariantArgBuilder) {
135                     Debug.Assert(TempVariable != null);
136                     return Expression.Call(TempVariable, typeof(Variant).GetMethod("Clear"));
137                 }
138                 return null;
139             }
140 
141 
142             switch (_targetComType) {
143                 case VarEnum.VT_EMPTY:
144                 case VarEnum.VT_NULL:
145                     return null;
146 
147                 case VarEnum.VT_BSTR:
148                 case VarEnum.VT_UNKNOWN:
149                 case VarEnum.VT_DISPATCH:
150                 case VarEnum.VT_ARRAY:
151                 case VarEnum.VT_RECORD:
152                 case VarEnum.VT_VARIANT:
153                     // paramVariants._elementN.Clear()
154                     return Expression.Call(_variant, typeof(Variant).GetMethod("Clear"));
155 
156                 default:
157                     Debug.Assert(Variant.IsPrimitiveType(_targetComType), "Unexpected VarEnum");
158                     return null;
159             }
160         }
161 
UpdateFromReturn(Expression parameter)162         internal Expression UpdateFromReturn(Expression parameter) {
163             if (TempVariable == null) {
164                 return null;
165             }
166             return Expression.Assign(
167                 parameter,
168                 Helpers.Convert(
169                     _argBuilder.UnmarshalFromRef(TempVariable),
170                     parameter.Type
171                 )
172             );
173         }
174     }
175 }
176 
177 #endif
178