Add support nested anonymous union/struct (#511)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 400cc2f..bd0617d 100644
--- a/CHANGELOG.md
+++ b/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/lib/src/code_generator/utils.dart b/lib/src/code_generator/utils.dart
index c000bdf..5c18c7f 100644
--- a/lib/src/code_generator/utils.dart
+++ b/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/lib/src/header_parser/sub_parsers/compounddecl_parser.dart b/lib/src/header_parser/sub_parsers/compounddecl_parser.dart
index c2efed6..5a7709c 100644
--- a/lib/src/header_parser/sub_parsers/compounddecl_parser.dart
+++ b/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/lib/src/header_parser/utils.dart b/lib/src/header_parser/utils.dart
index 614d5e5..d53cb8b 100644
--- a/lib/src/header_parser/utils.dart
+++ b/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/pubspec.yaml b/pubspec.yaml
index 387e08d..04b52ef 100644
--- a/pubspec.yaml
+++ b/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/test/header_parser_tests/nested_parsing.h b/test/header_parser_tests/nested_parsing.h
index cb21825..8557b64 100644
--- a/test/header_parser_tests/nested_parsing.h
+++ b/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/test/header_parser_tests/nested_parsing_test.dart b/test/header_parser_tests/nested_parsing_test.dart
index 70c205d..813c747 100644
--- a/test/header_parser_tests/nested_parsing_test.dart
+++ b/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,
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
],
);
}