[ffigen] Add support nested anonymous union/struct (#511)
diff --git a/pkgs/ffigen/CHANGELOG.md b/pkgs/ffigen/CHANGELOG.md index 400cc2f..bd0617d 100644 --- a/pkgs/ffigen/CHANGELOG.md +++ b/pkgs/ffigen/CHANGELOG.md
@@ -1,3 +1,7 @@ +# 7.2.5 + +- Add support nested anonymous union/struct + # 7.2.4 - Add new supported typedef - `uintptr_t` (mapped to `ffi.UintPtr`).
diff --git a/pkgs/ffigen/lib/src/code_generator/utils.dart b/pkgs/ffigen/lib/src/code_generator/utils.dart index c000bdf..5c18c7f 100644 --- a/pkgs/ffigen/lib/src/code_generator/utils.dart +++ b/pkgs/ffigen/lib/src/code_generator/utils.dart
@@ -15,6 +15,11 @@ /// /// Adds the resulting name to the used names by default. String makeUnique(String name, [bool addToUsedUpNames = true]) { + // For example, nested structures/unions may not have a name + if (name.isEmpty) { + name = 'unnamed'; + } + var crName = name; var i = 1; while (_usedUpNames.contains(crName)) {
diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/compounddecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/compounddecl_parser.dart index c2efed6..5a7709c 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/compounddecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/compounddecl_parser.dart
@@ -44,10 +44,13 @@ // A struct without any attribute is definitely not packed. #pragma pack(...) // also adds an attribute, but it's unexposed and cannot be travesed. bool hasAttr = false; + // A struct which as a __packed__ attribute is definitely packed. bool hasPackedAttr = false; + // Stores the maximum alignment from all the children. int maxChildAlignment = 0; + // Alignment of this struct. int alignment = 0; @@ -240,50 +243,80 @@ clang_types.CXCursor parent, Pointer<Void> clientData) { final parsed = _stack.top; try { - if (cursor.kind == clang_types.CXCursorKind.CXCursor_FieldDecl) { - _logger.finer('===== member: ${cursor.completeStringRepr()}'); + switch (cursor.kind) { + case clang_types.CXCursorKind.CXCursor_FieldDecl: + _logger.finer('===== member: ${cursor.completeStringRepr()}'); - // Set maxChildAlignValue. - final align = cursor.type().alignment(); - if (align > parsed.maxChildAlignment) { - parsed.maxChildAlignment = align; - } + // Set maxChildAlignValue. + final align = cursor.type().alignment(); + if (align > parsed.maxChildAlignment) { + parsed.maxChildAlignment = align; + } - final mt = cursor.type().toCodeGenType(); - if (mt is IncompleteArray) { - // TODO(68): Structs with flexible Array Members are not supported. - parsed.flexibleArrayMember = true; - } - if (clang.clang_getFieldDeclBitWidth(cursor) != -1) { - // TODO(84): Struct with bitfields are not suppoorted. - parsed.bitFieldMember = true; - } - if (mt is HandleType) { - parsed.dartHandleMember = true; - } - if (mt.isIncompleteCompound) { - parsed.incompleteCompoundMember = true; - } - if (mt.baseType is UnimplementedType) { - parsed.unimplementedMemberType = true; - } + final mt = cursor.type().toCodeGenType(); + if (mt is IncompleteArray) { + // TODO(68): Structs with flexible Array Members are not supported. + parsed.flexibleArrayMember = true; + } + if (clang.clang_getFieldDeclBitWidth(cursor) != -1) { + // TODO(84): Struct with bitfields are not suppoorted. + parsed.bitFieldMember = true; + } + if (mt is HandleType) { + parsed.dartHandleMember = true; + } + if (mt.isIncompleteCompound) { + parsed.incompleteCompoundMember = true; + } + if (mt.baseType is UnimplementedType) { + parsed.unimplementedMemberType = true; + } - parsed.compound.members.add( - Member( - dartDoc: getCursorDocComment( - cursor, - nesting.length + commentPrefix.length, + parsed.compound.members.add( + Member( + dartDoc: getCursorDocComment( + cursor, + nesting.length + commentPrefix.length, + ), + originalName: cursor.spelling(), + name: config.structDecl.renameMemberUsingConfig( + parsed.compound.originalName, + cursor.spelling(), + ), + type: mt, ), - originalName: cursor.spelling(), - name: config.structDecl.renameMemberUsingConfig( - parsed.compound.originalName, - cursor.spelling(), + ); + + break; + + case clang_types.CXCursorKind.CXCursor_PackedAttr: + parsed.hasPackedAttr = true; + + break; + case clang_types.CXCursorKind.CXCursor_UnionDecl: + case clang_types.CXCursorKind.CXCursor_StructDecl: + final mt = cursor.type().toCodeGenType(); + + // If the union/struct are anonymous, then we need to add them now, + // otherwise they will be added in the next iteration. + if (!cursor.isAnonymousRecordDecl()) break; + + parsed.compound.members.add( + Member( + dartDoc: getCursorDocComment( + cursor, + nesting.length + commentPrefix.length, + ), + originalName: cursor.spelling(), + name: config.structDecl.renameMemberUsingConfig( + parsed.compound.originalName, + cursor.spelling(), + ), + type: mt, ), - type: mt, - ), - ); - } else if (cursor.kind == clang_types.CXCursorKind.CXCursor_PackedAttr) { - parsed.hasPackedAttr = true; + ); + + break; } } catch (e, s) { _logger.severe(e);
diff --git a/pkgs/ffigen/lib/src/header_parser/utils.dart b/pkgs/ffigen/lib/src/header_parser/utils.dart index 614d5e5..d53cb8b 100644 --- a/pkgs/ffigen/lib/src/header_parser/utils.dart +++ b/pkgs/ffigen/lib/src/header_parser/utils.dart
@@ -97,6 +97,12 @@ return clang.clang_getCursorType(this); } + /// Determine whether the given cursor + /// represents an anonymous record declaration. + bool isAnonymousRecordDecl() { + return clang.clang_Cursor_isAnonymousRecordDecl(this) == 1; + } + /// Only valid for [clang.CXCursorKind.CXCursor_FunctionDecl]. Type will have /// kind [clang.CXTypeKind.CXType_Invalid] otherwise. clang_types.CXType returnType() {
diff --git a/pkgs/ffigen/pubspec.yaml b/pkgs/ffigen/pubspec.yaml index 387e08d..04b52ef 100644 --- a/pkgs/ffigen/pubspec.yaml +++ b/pkgs/ffigen/pubspec.yaml
@@ -3,7 +3,7 @@ # BSD-style license that can be found in the LICENSE file. name: ffigen -version: 7.2.4 +version: 7.2.5 description: Generator for FFI bindings, using LibClang to parse C header files. repository: https://github.com/dart-lang/ffigen
diff --git a/pkgs/ffigen/test/header_parser_tests/nested_parsing.h b/pkgs/ffigen/test/header_parser_tests/nested_parsing.h index cb21825..8557b64 100644 --- a/pkgs/ffigen/test/header_parser_tests/nested_parsing.h +++ b/pkgs/ffigen/test/header_parser_tests/nested_parsing.h
@@ -40,3 +40,23 @@ // Incomplete struct array. struct EmptyStruct b[3]; }; + +struct Struct6 +{ + // An anonymous, unnamed union. + union + { + float a; + }; + + // An unnamed union. + union + { + float b; + } c; + + union + { + float d; + } e; +};
diff --git a/pkgs/ffigen/test/header_parser_tests/nested_parsing_test.dart b/pkgs/ffigen/test/header_parser_tests/nested_parsing_test.dart index 70c205d..813c747 100644 --- a/pkgs/ffigen/test/header_parser_tests/nested_parsing_test.dart +++ b/pkgs/ffigen/test/header_parser_tests/nested_parsing_test.dart
@@ -57,6 +57,10 @@ expect(actual.getBindingAsString('Struct5'), expected.getBindingAsString('Struct5')); }); + test('Struct6', () { + expect(actual.getBindingAsString('Struct6'), + expected.getBindingAsString('Struct6')); + }); }); } @@ -110,6 +114,47 @@ Struct(name: 'EmptyStruct'), Struct(name: 'Struct4'), Struct(name: 'Struct5'), + Struct( + name: 'Struct6', + members: [ + Member( + name: '', + type: Union( + name: 'UnnamedUnion1', + members: [ + Member( + name: 'a', + type: floatType, + ), + ], + ), + ), + Member( + name: 'c', + type: Union( + name: 'UnnamedUnion2', + members: [ + Member( + name: 'b', + type: floatType, + ), + ], + ), + ), + Member( + name: 'e', + type: Union( + name: 'UnnamedUnion3', + members: [ + Member( + name: 'd', + type: floatType, + ), + ], + ), + ), + ], + ), ], ); }