-
Notifications
You must be signed in to change notification settings - Fork 4.8k
/
AttributedMarshallingModelGeneratorFactory.cs
292 lines (249 loc) · 15 KB
/
AttributedMarshallingModelGeneratorFactory.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
namespace Microsoft.Interop
{
public class AttributedMarshallingModelGeneratorFactory : IMarshallingGeneratorFactory
{
private static readonly BlittableMarshaller s_blittable = new BlittableMarshaller();
private static readonly Forwarder s_forwarder = new Forwarder();
private readonly IMarshallingGeneratorFactory _innerMarshallingGenerator;
public AttributedMarshallingModelGeneratorFactory(IMarshallingGeneratorFactory innerMarshallingGenerator, InteropGenerationOptions options)
{
Options = options;
_innerMarshallingGenerator = innerMarshallingGenerator;
ElementMarshallingGeneratorFactory = this;
}
public InteropGenerationOptions Options { get; }
/// <summary>
/// The <see cref="IMarshallingGeneratorFactory"/> to use for collection elements.
/// This property is settable to enable decorating factories to ensure that element marshalling also goes through the decorator support.
/// </summary>
public IMarshallingGeneratorFactory ElementMarshallingGeneratorFactory { get; set; }
public IMarshallingGenerator Create(TypePositionInfo info, StubCodeContext context)
{
return info.MarshallingAttributeInfo switch
{
NativeMarshallingAttributeInfo marshalInfo => CreateCustomNativeTypeMarshaller(info, context, marshalInfo),
BlittableTypeAttributeInfo => s_blittable,
GeneratedNativeMarshallingAttributeInfo => s_forwarder,
_ => _innerMarshallingGenerator.Create(info, context)
};
}
private static ExpressionSyntax GetNumElementsExpressionFromMarshallingInfo(TypePositionInfo info, CountInfo count, StubCodeContext context)
{
return count switch
{
SizeAndParamIndexInfo(int size, SizeAndParamIndexInfo.UnspecifiedParam) => GetConstSizeExpression(size),
ConstSizeCountInfo(int size) => GetConstSizeExpression(size),
SizeAndParamIndexInfo(SizeAndParamIndexInfo.UnspecifiedConstSize, TypePositionInfo param) => CheckedExpression(SyntaxKind.CheckedExpression, GetExpressionForParam(param)),
SizeAndParamIndexInfo(int size, TypePositionInfo param) => CheckedExpression(SyntaxKind.CheckedExpression, BinaryExpression(SyntaxKind.AddExpression, GetConstSizeExpression(size), GetExpressionForParam(param))),
CountElementCountInfo(TypePositionInfo elementInfo) => CheckedExpression(SyntaxKind.CheckedExpression, GetExpressionForParam(elementInfo)),
_ => throw new MarshallingNotSupportedException(info, context)
{
NotSupportedDetails = Resources.ArraySizeMustBeSpecified
},
};
static LiteralExpressionSyntax GetConstSizeExpression(int size)
{
return LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(size));
}
ExpressionSyntax GetExpressionForParam(TypePositionInfo paramInfo)
{
ExpressionSyntax numElementsExpression = GetIndexedNumElementsExpression(
context,
paramInfo,
out int numIndirectionLevels);
ManagedTypeInfo type = paramInfo.ManagedType;
MarshallingInfo marshallingInfo = paramInfo.MarshallingAttributeInfo;
for (int i = 0; i < numIndirectionLevels; i++)
{
if (marshallingInfo is NativeContiguousCollectionMarshallingInfo collectionInfo)
{
type = collectionInfo.ElementType;
marshallingInfo = collectionInfo.ElementMarshallingInfo;
}
else
{
throw new MarshallingNotSupportedException(info, context)
{
NotSupportedDetails = Resources.CollectionSizeParamTypeMustBeIntegral
};
}
}
if (type is not SpecialTypeInfo specialType || !specialType.SpecialType.IsIntegralType())
{
throw new MarshallingNotSupportedException(info, context)
{
NotSupportedDetails = Resources.CollectionSizeParamTypeMustBeIntegral
};
}
return CastExpression(
PredefinedType(Token(SyntaxKind.IntKeyword)),
ParenthesizedExpression(numElementsExpression));
}
static ExpressionSyntax GetIndexedNumElementsExpression(StubCodeContext context, TypePositionInfo numElementsInfo, out int numIndirectionLevels)
{
Stack<string> indexerStack = new();
StubCodeContext? currentContext = context;
StubCodeContext lastContext = null!;
while (currentContext is not null)
{
if (currentContext is ContiguousCollectionElementMarshallingCodeContext collectionContext)
{
indexerStack.Push(collectionContext.IndexerIdentifier);
}
lastContext = currentContext;
currentContext = currentContext.ParentContext;
}
numIndirectionLevels = indexerStack.Count;
ExpressionSyntax indexedNumElements = IdentifierName(lastContext.GetIdentifiers(numElementsInfo).managed);
while (indexerStack.Count > 0)
{
NameSyntax indexer = IdentifierName(indexerStack.Pop());
indexedNumElements = ElementAccessExpression(indexedNumElements)
.AddArgumentListArguments(Argument(indexer));
}
return indexedNumElements;
}
}
private IMarshallingGenerator CreateCustomNativeTypeMarshaller(TypePositionInfo info, StubCodeContext context, NativeMarshallingAttributeInfo marshalInfo)
{
ValidateCustomNativeTypeMarshallingSupported(info, context, marshalInfo);
ICustomNativeTypeMarshallingStrategy marshallingStrategy = new SimpleCustomNativeTypeMarshalling(marshalInfo.NativeMarshallingType.Syntax);
if ((marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedToNativeStackalloc) != 0)
{
marshallingStrategy = new StackallocOptimizationMarshalling(marshallingStrategy);
}
if ((marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.FreeNativeResources) != 0)
{
marshallingStrategy = new FreeNativeCleanupStrategy(marshallingStrategy);
}
// Collections have extra configuration, so handle them here.
if (marshalInfo is NativeContiguousCollectionMarshallingInfo collectionMarshallingInfo)
{
return CreateNativeCollectionMarshaller(info, context, collectionMarshallingInfo, marshallingStrategy);
}
if (marshalInfo.ValuePropertyType is not null)
{
marshallingStrategy = DecorateWithValuePropertyStrategy(marshalInfo, marshallingStrategy);
}
IMarshallingGenerator marshallingGenerator = new CustomNativeTypeMarshallingGenerator(marshallingStrategy, enableByValueContentsMarshalling: false);
if ((marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedTypePinning) != 0)
{
return new PinnableManagedValueMarshaller(marshallingGenerator);
}
return marshallingGenerator;
}
private void ValidateCustomNativeTypeMarshallingSupported(TypePositionInfo info, StubCodeContext context, NativeMarshallingAttributeInfo marshalInfo)
{
// The marshalling method for this type doesn't support marshalling from native to managed,
// but our scenario requires marshalling from native to managed.
if ((info.RefKind == RefKind.Ref || info.RefKind == RefKind.Out || info.IsManagedReturnPosition)
&& (marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.NativeToManaged) == 0)
{
throw new MarshallingNotSupportedException(info, context)
{
NotSupportedDetails = string.Format(Resources.CustomTypeMarshallingNativeToManagedUnsupported, marshalInfo.NativeMarshallingType.FullTypeName)
};
}
// The marshalling method for this type doesn't support marshalling from managed to native by value,
// but our scenario requires marshalling from managed to native by value.
else if (!info.IsByRef
&& (marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedToNative) == 0
&& (context.SingleFrameSpansNativeContext && (marshalInfo.MarshallingFeatures & (CustomMarshallingFeatures.ManagedTypePinning | CustomMarshallingFeatures.ManagedToNativeStackalloc)) == 0))
{
throw new MarshallingNotSupportedException(info, context)
{
NotSupportedDetails = string.Format(Resources.CustomTypeMarshallingManagedToNativeUnsupported, marshalInfo.NativeMarshallingType.FullTypeName)
};
}
// The marshalling method for this type doesn't support marshalling from managed to native by reference,
// but our scenario requires marshalling from managed to native by reference.
// "in" byref supports stack marshalling.
else if (info.RefKind == RefKind.In
&& (marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedToNative) == 0
&& !(context.SingleFrameSpansNativeContext && (marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedToNativeStackalloc) != 0))
{
throw new MarshallingNotSupportedException(info, context)
{
NotSupportedDetails = string.Format(Resources.CustomTypeMarshallingManagedToNativeUnsupported, marshalInfo.NativeMarshallingType.FullTypeName)
};
}
// The marshalling method for this type doesn't support marshalling from managed to native by reference,
// but our scenario requires marshalling from managed to native by reference.
// "ref" byref marshalling doesn't support stack marshalling
else if (info.RefKind == RefKind.Ref
&& (marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedToNative) == 0)
{
throw new MarshallingNotSupportedException(info, context)
{
NotSupportedDetails = string.Format(Resources.CustomTypeMarshallingManagedToNativeUnsupported, marshalInfo.NativeMarshallingType.FullTypeName)
};
}
}
private ICustomNativeTypeMarshallingStrategy DecorateWithValuePropertyStrategy(NativeMarshallingAttributeInfo marshalInfo, ICustomNativeTypeMarshallingStrategy nativeTypeMarshaller)
{
TypeSyntax valuePropertyTypeSyntax = marshalInfo.ValuePropertyType!.Syntax;
if ((marshalInfo.MarshallingFeatures & CustomMarshallingFeatures.NativeTypePinning) != 0)
{
return new PinnableMarshallerTypeMarshalling(nativeTypeMarshaller, valuePropertyTypeSyntax);
}
return new CustomNativeTypeWithValuePropertyMarshalling(nativeTypeMarshaller, valuePropertyTypeSyntax);
}
private IMarshallingGenerator CreateNativeCollectionMarshaller(
TypePositionInfo info,
StubCodeContext context,
NativeContiguousCollectionMarshallingInfo collectionInfo,
ICustomNativeTypeMarshallingStrategy marshallingStrategy)
{
var elementInfo = new TypePositionInfo(collectionInfo.ElementType, collectionInfo.ElementMarshallingInfo) { ManagedIndex = info.ManagedIndex };
IMarshallingGenerator elementMarshaller = Create(
elementInfo,
new ContiguousCollectionElementMarshallingCodeContext(StubCodeContext.Stage.Setup, string.Empty, context));
TypeSyntax elementType = elementMarshaller.AsNativeType(elementInfo);
bool isBlittable = elementMarshaller is BlittableMarshaller;
if (isBlittable)
{
marshallingStrategy = new ContiguousBlittableElementCollectionMarshalling(marshallingStrategy, collectionInfo.ElementType.Syntax);
}
else
{
marshallingStrategy = new ContiguousNonBlittableElementCollectionMarshalling(marshallingStrategy, elementMarshaller, elementInfo);
}
// Explicitly insert the Value property handling here (before numElements handling) so that the numElements handling will be emitted before the Value property handling in unmarshalling.
if (collectionInfo.ValuePropertyType is not null)
{
marshallingStrategy = DecorateWithValuePropertyStrategy(collectionInfo, marshallingStrategy);
}
ExpressionSyntax numElementsExpression = LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0));
if (info.IsManagedReturnPosition || (info.IsByRef && info.RefKind != RefKind.In))
{
// In this case, we need a numElementsExpression supplied from metadata, so we'll calculate it here.
numElementsExpression = GetNumElementsExpressionFromMarshallingInfo(info, collectionInfo.ElementCountInfo, context);
}
marshallingStrategy = new NumElementsExpressionMarshalling(
marshallingStrategy,
numElementsExpression,
SizeOfExpression(elementType));
if (collectionInfo.UseDefaultMarshalling && info.ManagedType is SzArrayType)
{
return new ArrayMarshaller(
new CustomNativeTypeMarshallingGenerator(marshallingStrategy, enableByValueContentsMarshalling: true),
elementType,
isBlittable,
Options);
}
IMarshallingGenerator marshallingGenerator = new CustomNativeTypeMarshallingGenerator(marshallingStrategy, enableByValueContentsMarshalling: false);
if ((collectionInfo.MarshallingFeatures & CustomMarshallingFeatures.ManagedTypePinning) != 0)
{
return new PinnableManagedValueMarshaller(marshallingGenerator);
}
return marshallingGenerator;
}
}
}