@@ -28,11 +28,14 @@ private StructDeclarationSyntax DeclareStruct(TypeDefinitionHandle typeDefHandle
2828
2929 // If the last field has the [FlexibleArray] attribute, we must disable marshaling since the struct
3030 // is only ever valid when accessed via a pointer since the struct acts as a header of an arbitrarily-sized array.
31+ FieldDefinitionHandle flexibleArrayFieldHandle = default ;
32+ MethodDeclarationSyntax ? sizeOfMethod = null ;
3133 if ( typeDef . GetFields ( ) . LastOrDefault ( ) is FieldDefinitionHandle { IsNil : false } lastFieldHandle )
3234 {
3335 FieldDefinition lastField = this . Reader . GetFieldDefinition ( lastFieldHandle ) ;
3436 if ( MetadataUtilities . FindAttribute ( this . Reader , lastField . GetCustomAttributes ( ) , InteropDecorationNamespace , FlexibleArrayAttribute ) is not null )
3537 {
38+ flexibleArrayFieldHandle = lastFieldHandle ;
3639 context = context with { AllowMarshaling = false } ;
3740 }
3841 }
@@ -80,6 +83,37 @@ private StructDeclarationSyntax DeclareStruct(TypeDefinitionHandle typeDefHandle
8083 . WithArgumentList ( BracketedArgumentList ( SingletonSeparatedList ( Argument ( size ) ) ) ) )
8184 . AddModifiers ( TokenWithSpace ( this . Visibility ) , TokenWithSpace ( SyntaxKind . UnsafeKeyword ) , Token ( SyntaxKind . FixedKeyword ) ) ;
8285 }
86+ else if ( fieldDefHandle == flexibleArrayFieldHandle )
87+ {
88+ CustomAttributeHandleCollection fieldAttributes = fieldDef . GetCustomAttributes ( ) ;
89+ var fieldTypeInfo = ( ArrayTypeHandleInfo ) fieldDef . DecodeSignature ( SignatureHandleProvider . Instance , null ) ;
90+ TypeSyntax fieldType = fieldTypeInfo . ElementType . ToTypeSyntax ( typeSettings , GeneratingElement . StructMember , fieldAttributes ) . Type ;
91+
92+ if ( fieldType is PointerTypeSyntax or FunctionPointerTypeSyntax )
93+ {
94+ // These types are not allowed as generic type arguments (https://github.com/dotnet/runtime/issues/13627)
95+ // so we have to generate a special nested struct dedicated to this type instead of using the generic type.
96+ StructDeclarationSyntax helperStruct = this . DeclareVariableLengthInlineArrayHelper ( context , fieldType ) ;
97+ additionalMembers = additionalMembers . Add ( helperStruct ) ;
98+
99+ field = FieldDeclaration (
100+ VariableDeclaration ( IdentifierName ( helperStruct . Identifier . ValueText ) ) )
101+ . AddDeclarationVariables ( fieldDeclarator )
102+ . AddModifiers ( TokenWithSpace ( this . Visibility ) ) ;
103+ }
104+ else
105+ {
106+ this . RequestVariableLengthInlineArrayHelper ( context ) ;
107+ field = FieldDeclaration (
108+ VariableDeclaration (
109+ GenericName ( $ "global::Windows.Win32.VariableLengthInlineArray")
110+ . WithTypeArgumentList ( TypeArgumentList ( ) . AddArguments ( fieldType ) ) ) )
111+ . AddDeclarationVariables ( fieldDeclarator )
112+ . AddModifiers ( TokenWithSpace ( this . Visibility ) ) ;
113+ }
114+
115+ sizeOfMethod = this . DeclareSizeOfMethod ( name , fieldType , typeSettings ) ;
116+ }
83117 else
84118 {
85119 CustomAttributeHandleCollection fieldAttributes = fieldDef . GetCustomAttributes ( ) ;
@@ -334,6 +368,12 @@ private StructDeclarationSyntax DeclareStruct(TypeDefinitionHandle typeDefHandle
334368 }
335369 }
336370
371+ // Add a SizeOf method, if there is a FlexibleArray field.
372+ if ( sizeOfMethod is not null )
373+ {
374+ members . Add ( sizeOfMethod ) ;
375+ }
376+
337377 // Add the additional members, taking care to not introduce redundant declarations.
338378 members . AddRange ( additionalMembers . Where ( c => c is not StructDeclarationSyntax cs || ! members . OfType < StructDeclarationSyntax > ( ) . Any ( m => m . Identifier . ValueText == cs . Identifier . ValueText ) ) ) ;
339379
@@ -370,6 +410,95 @@ private StructDeclarationSyntax DeclareStruct(TypeDefinitionHandle typeDefHandle
370410 return result ;
371411 }
372412
413+ private StructDeclarationSyntax DeclareVariableLengthInlineArrayHelper ( Context context , TypeSyntax fieldType )
414+ {
415+ IdentifierNameSyntax firstElementFieldName = IdentifierName ( "e0" ) ;
416+ List < MemberDeclarationSyntax > members = new ( ) ;
417+
418+ // internal unsafe T e0;
419+ members . Add ( FieldDeclaration ( VariableDeclaration ( fieldType ) . AddVariables ( VariableDeclarator ( firstElementFieldName . Identifier ) ) )
420+ . AddModifiers ( TokenWithSpace ( this . Visibility ) , TokenWithSpace ( SyntaxKind . UnsafeKeyword ) ) ) ;
421+
422+ if ( this . canUseUnsafeAdd )
423+ {
424+ ////[MethodImpl(MethodImplOptions.AggressiveInlining)]
425+ ////get { fixed (int** p = &e0) return *(p + index); }
426+ IdentifierNameSyntax pLocal = IdentifierName ( "p" ) ;
427+ AccessorDeclarationSyntax getter = AccessorDeclaration ( SyntaxKind . GetAccessorDeclaration )
428+ . WithBody ( Block ( ) . AddStatements (
429+ FixedStatement (
430+ VariableDeclaration ( PointerType ( fieldType ) ) . AddVariables (
431+ VariableDeclarator ( pLocal . Identifier ) . WithInitializer ( EqualsValueClause ( PrefixUnaryExpression ( SyntaxKind . AddressOfExpression , firstElementFieldName ) ) ) ) ,
432+ ReturnStatement ( PrefixUnaryExpression ( SyntaxKind . PointerIndirectionExpression , ParenthesizedExpression ( BinaryExpression ( SyntaxKind . AddExpression , pLocal , IdentifierName ( "index" ) ) ) ) ) ) ) )
433+ . AddAttributeLists ( AttributeList ( ) . AddAttributes ( MethodImpl ( MethodImplOptions . AggressiveInlining ) ) ) ;
434+
435+ ////[MethodImpl(MethodImplOptions.AggressiveInlining)]
436+ ////set { fixed (int** p = &e0) *(p + index) = value; }
437+ AccessorDeclarationSyntax setter = AccessorDeclaration ( SyntaxKind . SetAccessorDeclaration )
438+ . WithBody ( Block ( ) . AddStatements (
439+ FixedStatement (
440+ VariableDeclaration ( PointerType ( fieldType ) ) . AddVariables (
441+ VariableDeclarator ( pLocal . Identifier ) . WithInitializer ( EqualsValueClause ( PrefixUnaryExpression ( SyntaxKind . AddressOfExpression , firstElementFieldName ) ) ) ) ,
442+ ExpressionStatement ( AssignmentExpression (
443+ SyntaxKind . SimpleAssignmentExpression ,
444+ PrefixUnaryExpression ( SyntaxKind . PointerIndirectionExpression , ParenthesizedExpression ( BinaryExpression ( SyntaxKind . AddExpression , pLocal , IdentifierName ( "index" ) ) ) ) ,
445+ IdentifierName ( "value" ) ) ) ) ) )
446+ . AddAttributeLists ( AttributeList ( ) . AddAttributes ( MethodImpl ( MethodImplOptions . AggressiveInlining ) ) ) ;
447+
448+ ////internal unsafe T this[int index]
449+ members . Add ( IndexerDeclaration ( fieldType . WithTrailingTrivia ( Space ) )
450+ . AddModifiers ( TokenWithSpace ( this . Visibility ) , TokenWithSpace ( SyntaxKind . UnsafeKeyword ) )
451+ . AddParameterListParameters ( Parameter ( Identifier ( "index" ) ) . WithType ( PredefinedType ( TokenWithSpace ( SyntaxKind . IntKeyword ) ) ) )
452+ . AddAccessorListAccessors ( getter , setter ) ) ;
453+ }
454+
455+ // internal partial struct VariableLengthInlineArrayHelper
456+ return StructDeclaration ( Identifier ( "VariableLengthInlineArrayHelper" ) )
457+ . AddModifiers ( TokenWithSpace ( this . Visibility ) , TokenWithSpace ( SyntaxKind . PartialKeyword ) )
458+ . AddMembers ( members . ToArray ( ) ) ;
459+ }
460+
461+ private MethodDeclarationSyntax DeclareSizeOfMethod ( TypeSyntax structType , TypeSyntax elementType , TypeSyntaxSettings typeSettings )
462+ {
463+ PredefinedTypeSyntax intType = PredefinedType ( TokenWithSpace ( SyntaxKind . IntKeyword ) ) ;
464+ IdentifierNameSyntax countName = IdentifierName ( "count" ) ;
465+ IdentifierNameSyntax localName = IdentifierName ( "v" ) ;
466+ List < StatementSyntax > statements = new ( ) ;
467+
468+ // int v = sizeof(OUTER_STRUCT);
469+ statements . Add ( LocalDeclarationStatement ( VariableDeclaration ( intType ) . AddVariables (
470+ VariableDeclarator ( localName . Identifier ) . WithInitializer ( EqualsValueClause ( SizeOfExpression ( structType ) ) ) ) ) ) ;
471+
472+ // if (count > 1)
473+ // v += checked((count - 1) * sizeof(ELEMENT_TYPE));
474+ // else if (count < 0)
475+ // throw new ArgumentOutOfRangeException(nameof(count));
476+ statements . Add ( IfStatement (
477+ BinaryExpression ( SyntaxKind . GreaterThanExpression , countName , LiteralExpression ( SyntaxKind . NumericLiteralExpression , Literal ( 1 ) ) ) ,
478+ ExpressionStatement ( AssignmentExpression (
479+ SyntaxKind . AddAssignmentExpression ,
480+ localName ,
481+ CheckedExpression ( BinaryExpression (
482+ SyntaxKind . MultiplyExpression ,
483+ ParenthesizedExpression ( BinaryExpression ( SyntaxKind . SubtractExpression , countName , LiteralExpression ( SyntaxKind . NumericLiteralExpression , Literal ( 1 ) ) ) ) ,
484+ SizeOfExpression ( elementType ) ) ) ) ) ,
485+ ElseClause ( IfStatement (
486+ BinaryExpression ( SyntaxKind . LessThanExpression , countName , LiteralExpression ( SyntaxKind . NumericLiteralExpression , Literal ( 0 ) ) ) ,
487+ ThrowStatement ( ObjectCreationExpression ( IdentifierName ( nameof ( ArgumentOutOfRangeException ) ) ) ) ) . WithCloseParenToken ( TokenWithLineFeed ( SyntaxKind . CloseParenToken ) ) ) ) . WithCloseParenToken ( TokenWithLineFeed ( SyntaxKind . CloseParenToken ) ) ) ;
488+
489+ // return v;
490+ statements . Add ( ReturnStatement ( localName ) ) ;
491+
492+ // internal static unsafe int SizeOf(int count)
493+ MethodDeclarationSyntax sizeOfMethod = MethodDeclaration ( intType , Identifier ( "SizeOf" ) )
494+ . AddParameterListParameters ( Parameter ( countName . Identifier ) . WithType ( intType ) )
495+ . WithBody ( Block ( ) . AddStatements ( statements . ToArray ( ) ) )
496+ . AddModifiers ( TokenWithSpace ( this . Visibility ) , TokenWithSpace ( SyntaxKind . StaticKeyword ) , TokenWithSpace ( SyntaxKind . UnsafeKeyword ) )
497+ . WithLeadingTrivia ( ParseLeadingTrivia ( "/// <summary>Computes the amount of memory that must be allocated to store this struct, including the specified number of elements in the variable length inline array at the end.</summary>\n " ) ) ;
498+
499+ return sizeOfMethod ;
500+ }
501+
373502 private ( TypeSyntax FieldType , SyntaxList < MemberDeclarationSyntax > AdditionalMembers , AttributeSyntax ? MarshalAsAttribute ) ReinterpretFieldType ( FieldDefinition fieldDef , TypeSyntax originalType , CustomAttributeHandleCollection customAttributes , Context context )
374503 {
375504 TypeSyntaxSettings typeSettings = context . Filter ( this . fieldTypeSettings ) ;
@@ -397,4 +526,23 @@ private StructDeclarationSyntax DeclareStruct(TypeDefinitionHandle typeDefHandle
397526
398527 return ( originalType , default ( SyntaxList < MemberDeclarationSyntax > ) , marshalAs ) ;
399528 }
529+
530+ private void RequestVariableLengthInlineArrayHelper ( Context context )
531+ {
532+ if ( this . IsWin32Sdk )
533+ {
534+ if ( ! this . IsTypeAlreadyFullyDeclared ( $ "{ this . Namespace } .{ this . variableLengthInlineArrayStruct . Identifier . ValueText } ") )
535+ {
536+ this . DeclareUnscopedRefAttributeIfNecessary ( ) ;
537+ this . volatileCode . GenerateSpecialType ( "VariableLengthInlineArray" , ( ) => this . volatileCode . AddSpecialType ( "VariableLengthInlineArray" , this . variableLengthInlineArrayStruct ) ) ;
538+ }
539+ }
540+ else if ( this . SuperGenerator is not null && this . SuperGenerator . TryGetGenerator ( "Windows.Win32" , out Generator ? generator ) )
541+ {
542+ generator . volatileCode . GenerationTransaction ( delegate
543+ {
544+ generator . RequestVariableLengthInlineArrayHelper ( context ) ;
545+ } ) ;
546+ }
547+ }
400548}
0 commit comments