Support for Packed struct (#205)
diff --git a/.github/workflows/test-package.yml b/.github/workflows/test-package.yml
index 2ee8d6a..642fed7 100644
--- a/.github/workflows/test-package.yml
+++ b/.github/workflows/test-package.yml
@@ -19,7 +19,7 @@
strategy:
fail-fast: false
matrix:
- sdk: [2.12.0]
+ sdk: [2.13.0-211.6.beta] # TODO: revert to 2.13.
steps:
- uses: actions/checkout@v2
- uses: dart-lang/setup-dart@v1.0
@@ -43,7 +43,7 @@
- uses: actions/checkout@v2
- uses: dart-lang/setup-dart@v1.0
with:
- sdk: 2.12.0
+ sdk: 2.13.0-211.6.beta # TODO: revert to 2.13.
- name: Install dependencies
run: dart pub get
- name: Install libclang-10-dev
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1b811cc..82b5807 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+# 2.5.0-beta.1
+- Added support for `Packed` structs. Packed annotations are generated
+automatically but can be overriden using `structs -> pack` config.
+- Updated sdk constraints to `>=2.13.0-211.6.beta`.
+
# 2.4.2
- Fix issues due to declarations having duplicate names.
- Fix name conflict of declaration with ffi library prefix.
diff --git a/README.md b/README.md
index cc1aeac..d8cac98 100644
--- a/README.md
+++ b/README.md
@@ -232,6 +232,27 @@
</td>
</tr>
<tr>
+ <td>structs -> pack</td>
+ <td>Override the @Packed(X) annotation for generated structs.<br><br>
+ <i>Options - none, 1, 2, 4, 8, 16</i><br>
+ You can use RegExp to match with the <b>generated</b> names.<br><br>
+ Note: Ffigen can only reliably identify packing specified using
+ __attribute__((__packed__)). However, structs packed using
+ `#pragma pack(...)` or any other way could <i>potentially</i> be incorrect
+ in which case you can override the generated annotations.
+ </td>
+ <td>
+
+```yaml
+structs:
+ pack:
+ # Matches with the generated name.
+ 'NoPackStruct': none # No packing
+ '.*': 1 # Pack all structs with value 1
+```
+ </td>
+ </tr>
+ <tr>
<td>array-workaround</td>
<td>Should generate workaround for fixed arrays in Structs. See <a href="#array-workaround">Array Workaround</a><br>
<b>Default: false</b>
diff --git a/lib/src/code_generator/library.dart b/lib/src/code_generator/library.dart
index e37a062..c29313f 100644
--- a/lib/src/code_generator/library.dart
+++ b/lib/src/code_generator/library.dart
@@ -5,9 +5,11 @@
import 'dart:io';
import 'package:cli_util/cli_util.dart';
+import 'package:ffigen/src/config_provider/config_types.dart';
import 'package:logging/logging.dart';
import 'package:path/path.dart' as p;
import 'binding.dart';
+import 'struc.dart';
import 'utils.dart';
import 'writer.dart';
@@ -28,6 +30,7 @@
String? header,
bool dartBool = true,
bool sort = false,
+ StructPackingOverride? packingOverride,
}) {
if (sort) _sort();
@@ -38,6 +41,16 @@
_resolveIfNameConflicts(declConflictHandler, b);
}
+ // Override pack values according to config. We do this after declaration
+ // conflicts have been handled so that users can target the generated names.
+ if (packingOverride != null) {
+ for (final b in bindings) {
+ if (b is Struc && packingOverride.isOverriden(b.name)) {
+ b.pack = packingOverride.getOverridenPackValue(b.name);
+ }
+ }
+ }
+
// Seperate bindings which require lookup.
final lookUpBindings = bindings.whereType<LookUpBinding>().toList();
final noLookUpBindings = bindings.whereType<NoLookUpBinding>().toList();
diff --git a/lib/src/code_generator/struc.dart b/lib/src/code_generator/struc.dart
index 2fc96d6..37edfa0 100644
--- a/lib/src/code_generator/struc.dart
+++ b/lib/src/code_generator/struc.dart
@@ -44,6 +44,9 @@
bool get isOpaque => members.isEmpty;
+ /// Value for `@Packed(X)` annotation. Can be null(no packing), 1, 2, 4, 8, 16.
+ int? pack;
+
/// Marker for checking if the dependencies are parsed.
bool parsedDependencies = false;
@@ -52,6 +55,7 @@
String? originalName,
required String name,
this.isInComplete = false,
+ this.pack,
String? dartDoc,
List<Member>? members,
}) : members = members ?? [],
@@ -105,6 +109,10 @@
/// to have the same name as the class.
final localUniqueNamer = UniqueNamer({enclosingClassName});
+ /// Write @Packed(X) annotation if struct is packed.
+ if (pack != null) {
+ s.write('@${w.ffiLibraryPrefix}.Packed($pack)\n');
+ }
// Write class declaration.
s.write(
'class $enclosingClassName extends ${w.ffiLibraryPrefix}.${isOpaque ? 'Opaque' : 'Struct'}{\n');
diff --git a/lib/src/config_provider/config.dart b/lib/src/config_provider/config.dart
index 2830366..cacc7fa 100644
--- a/lib/src/config_provider/config.dart
+++ b/lib/src/config_provider/config.dart
@@ -80,6 +80,10 @@
StructDependencies get structDependencies => _structDependencies;
late StructDependencies _structDependencies;
+ /// Holds config for how struct packing should be overriden.
+ StructPackingOverride get structPackingOverride => _structPackingOverride;
+ late StructPackingOverride _structPackingOverride;
+
/// If tool should generate array workarounds.
///
/// If false(default), structs with inline array members will have all its
@@ -335,6 +339,15 @@
extractedResult: (dynamic result) =>
_structDependencies = result as StructDependencies,
),
+ [strings.structs, strings.structPack]:
+ Specification<StructPackingOverride>(
+ requirement: Requirement.no,
+ validator: structPackingOverrideValidator,
+ extractor: structPackingOverrideExtractor,
+ defaultValue: () => StructPackingOverride(),
+ extractedResult: (dynamic result) =>
+ _structPackingOverride = result as StructPackingOverride,
+ ),
[strings.arrayWorkaround]: Specification<bool>(
requirement: Requirement.no,
validator: booleanValidator,
diff --git a/lib/src/config_provider/config_types.dart b/lib/src/config_provider/config_types.dart
index 0240c19..7fcc352 100644
--- a/lib/src/config_provider/config_types.dart
+++ b/lib/src/config_provider/config_types.dart
@@ -31,6 +31,35 @@
enum StructDependencies { full, opaque }
+/// Holds config for how Structs Packing will be overriden.
+class StructPackingOverride {
+ final Map<RegExp, int?> _matcherMap;
+
+ StructPackingOverride({Map<RegExp, int?>? matcherMap})
+ : _matcherMap = matcherMap ?? {};
+
+ /// Returns true if the user has overriden the pack value.
+ bool isOverriden(String name) {
+ for (final key in _matcherMap.keys) {
+ if (quiver.matchesFull(key, name)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /// Returns pack value for [name]. Ensure that value [isOverriden] before
+ /// using the returned value.
+ int? getOverridenPackValue(String name) {
+ for (final opv in _matcherMap.entries) {
+ if (quiver.matchesFull(opv.key, name)) {
+ return opv.value;
+ }
+ }
+ return null;
+ }
+}
+
/// Represents a single specification in configurations.
///
/// [E] is the return type of the extractedResult.
diff --git a/lib/src/config_provider/spec_utils.dart b/lib/src/config_provider/spec_utils.dart
index 6875af1..ff4a44a 100644
--- a/lib/src/config_provider/spec_utils.dart
+++ b/lib/src/config_provider/spec_utils.dart
@@ -699,3 +699,30 @@
}
return result;
}
+
+StructPackingOverride structPackingOverrideExtractor(dynamic value) {
+ final matcherMap = <RegExp, int?>{};
+ for (final key in value.keys) {
+ matcherMap[RegExp(key as String, dotAll: true)] =
+ strings.packingValuesMap[value[key]];
+ }
+ return StructPackingOverride(matcherMap: matcherMap);
+}
+
+bool structPackingOverrideValidator(List<String> name, dynamic value) {
+ var _result = true;
+
+ if (!checkType<YamlMap>([...name], value)) {
+ _result = false;
+ } else {
+ for (final key in value.keys) {
+ if (!(strings.packingValuesMap.keys.contains(value[key]))) {
+ _logger.severe(
+ "'$name -> $key' must be one of the following - ${strings.packingValuesMap.keys.toList()}");
+ _result = false;
+ }
+ }
+ }
+
+ return _result;
+}
diff --git a/lib/src/header_parser/clang_bindings/clang_bindings.dart b/lib/src/header_parser/clang_bindings/clang_bindings.dart
index 71c02c9..2c69fa7 100644
--- a/lib/src/header_parser/clang_bindings/clang_bindings.dart
+++ b/lib/src/header_parser/clang_bindings/clang_bindings.dart
@@ -376,6 +376,21 @@
late final _dart_clang_getCursorKind _clang_getCursorKind =
_clang_getCursorKind_ptr.asFunction<_dart_clang_getCursorKind>();
+ /// Determine whether the given cursor has any attributes.
+ int clang_Cursor_hasAttrs(
+ CXCursor C,
+ ) {
+ return _clang_Cursor_hasAttrs(
+ C,
+ );
+ }
+
+ late final _clang_Cursor_hasAttrs_ptr =
+ _lookup<ffi.NativeFunction<_c_clang_Cursor_hasAttrs>>(
+ 'clang_Cursor_hasAttrs');
+ late final _dart_clang_Cursor_hasAttrs _clang_Cursor_hasAttrs =
+ _clang_Cursor_hasAttrs_ptr.asFunction<_dart_clang_Cursor_hasAttrs>();
+
/// Retrieve the physical location of the source constructor referenced
/// by the given cursor.
///
@@ -772,6 +787,30 @@
late final _dart_clang_Type_getNamedType _clang_Type_getNamedType =
_clang_Type_getNamedType_ptr.asFunction<_dart_clang_Type_getNamedType>();
+ /// Return the alignment of a type in bytes as per C++[expr.alignof]
+ /// standard.
+ ///
+ /// If the type declaration is invalid, CXTypeLayoutError_Invalid is returned.
+ /// If the type declaration is an incomplete type, CXTypeLayoutError_Incomplete
+ /// is returned.
+ /// If the type declaration is a dependent type, CXTypeLayoutError_Dependent is
+ /// returned.
+ /// If the type declaration is not a constant size type,
+ /// CXTypeLayoutError_NotConstantSize is returned.
+ int clang_Type_getAlignOf(
+ CXType T,
+ ) {
+ return _clang_Type_getAlignOf(
+ T,
+ );
+ }
+
+ late final _clang_Type_getAlignOf_ptr =
+ _lookup<ffi.NativeFunction<_c_clang_Type_getAlignOf>>(
+ 'clang_Type_getAlignOf');
+ late final _dart_clang_Type_getAlignOf _clang_Type_getAlignOf =
+ _clang_Type_getAlignOf_ptr.asFunction<_dart_clang_Type_getAlignOf>();
+
/// Determine whether the given cursor represents an anonymous
/// tag or namespace
int clang_Cursor_isAnonymous(
@@ -2690,6 +2729,14 @@
CXCursor arg0,
);
+typedef _c_clang_Cursor_hasAttrs = ffi.Uint32 Function(
+ CXCursor C,
+);
+
+typedef _dart_clang_Cursor_hasAttrs = int Function(
+ CXCursor C,
+);
+
typedef _c_clang_getCursorLocation = CXSourceLocation Function(
CXCursor arg0,
);
@@ -2870,6 +2917,14 @@
CXType T,
);
+typedef _c_clang_Type_getAlignOf = ffi.Int64 Function(
+ CXType T,
+);
+
+typedef _dart_clang_Type_getAlignOf = int Function(
+ CXType T,
+);
+
typedef _c_clang_Cursor_isAnonymous = ffi.Uint32 Function(
CXCursor C,
);
diff --git a/lib/src/header_parser/parser.dart b/lib/src/header_parser/parser.dart
index ff28679..ddcd9c6 100644
--- a/lib/src/header_parser/parser.dart
+++ b/lib/src/header_parser/parser.dart
@@ -30,6 +30,7 @@
header: config.preamble,
dartBool: config.dartBool,
sort: config.sort,
+ packingOverride: config.structPackingOverride,
);
return library;
diff --git a/lib/src/header_parser/sub_parsers/structdecl_parser.dart b/lib/src/header_parser/sub_parsers/structdecl_parser.dart
index d9c4023..c27d841 100644
--- a/lib/src/header_parser/sub_parsers/structdecl_parser.dart
+++ b/lib/src/header_parser/sub_parsers/structdecl_parser.dart
@@ -8,6 +8,7 @@
import 'package:ffigen/src/config_provider/config_types.dart';
import 'package:logging/logging.dart';
+import '../../strings.dart' as strings;
import '../clang_bindings/clang_bindings.dart' as clang_types;
import '../data.dart';
import '../includer.dart';
@@ -33,6 +34,39 @@
(dartHandleMember && config.useDartHandle) ||
incompleteStructMember;
+ // 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 allignment = 0;
+
+ bool get _isPacked {
+ if (!hasAttr || isInComplete) return false;
+ if (hasPackedAttr) return true;
+
+ return maxChildAlignment > allignment;
+ }
+
+ /// Returns pack value of a struct depending on config, returns null for no
+ /// packing.
+ int? get packValue {
+ if (_isPacked) {
+ if (strings.packingValuesMap.containsKey(allignment)) {
+ return allignment;
+ } else {
+ _logger.warning(
+ 'Unsupported pack value "$allignment" for Struct "${struc!.name}".');
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+
_ParsedStruc();
}
@@ -62,7 +96,6 @@
if (isForwardDeclaration(cursor)) {
cursor = clang.clang_getCursorDefinition(cursor);
}
-
final structUsr = cursor.usr();
final structName = name ?? cursor.spelling();
@@ -119,8 +152,8 @@
}
void _setStructMembers(clang_types.CXCursor cursor) {
- _stack.top.arrayMember = false;
- _stack.top.unimplementedMemberType = false;
+ _stack.top.hasAttr = clang.clang_Cursor_hasAttrs(cursor) != 0;
+ _stack.top.allignment = cursor.type().alignment();
final resultCode = clang.clang_visitChildren(
cursor,
@@ -129,6 +162,10 @@
nullptr,
);
+ _logger.finest(
+ 'Opaque: ${_stack.top.isInComplete}, HasAttr: ${_stack.top.hasAttr}, AlignValue: ${_stack.top.allignment}, MaxChildAlignValue: ${_stack.top.maxChildAlignment}, PackValue: ${_stack.top.packValue}.');
+ _stack.top.struc!.pack = _stack.top.packValue;
+
visitChildrenResultChecker(resultCode);
if (_stack.top.arrayMember && !config.arrayWorkaround) {
@@ -183,6 +220,12 @@
if (cursor.kind == clang_types.CXCursorKind.CXCursor_FieldDecl) {
_logger.finer('===== member: ${cursor.completeStringRepr()}');
+ // Set maxChildAlignValue.
+ final align = cursor.type().alignment();
+ if (align > _stack.top.maxChildAlignment) {
+ _stack.top.maxChildAlignment = align;
+ }
+
final mt = cursor.type().toCodeGenType();
if (mt.broadType == BroadType.ConstantArray) {
_stack.top.arrayMember = true;
@@ -219,6 +262,8 @@
type: mt,
),
);
+ } else if (cursor.kind == clang_types.CXCursorKind.CXCursor_PackedAttr) {
+ _stack.top.hasPackedAttr = true;
}
} catch (e, s) {
_logger.severe(e);
diff --git a/lib/src/header_parser/utils.dart b/lib/src/header_parser/utils.dart
index e1b205c..fda7f65 100644
--- a/lib/src/header_parser/utils.dart
+++ b/lib/src/header_parser/utils.dart
@@ -236,6 +236,10 @@
return clang.clang_getTypeKindSpelling(kind()).toStringAndDispose();
}
+ int alignment() {
+ return clang.clang_Type_getAlignOf(this);
+ }
+
/// For debugging: returns [spelling] [kind] [kindSpelling].
String completeStringRepr() {
final s =
diff --git a/lib/src/strings.dart b/lib/src/strings.dart
index 2f592a2..76ed940 100644
--- a/lib/src/strings.dart
+++ b/lib/src/strings.dart
@@ -64,6 +64,16 @@
const fullStructDependencies = 'full';
const opaqueStructDependencies = 'opaque';
+const structPack = 'pack';
+const Map<Object, int?> packingValuesMap = {
+ 'none': null,
+ 1: 1,
+ 2: 2,
+ 4: 4,
+ 8: 8,
+ 16: 16,
+};
+
const sizemap = 'size-map';
const typedefmap = 'typedef-map';
diff --git a/pubspec.yaml b/pubspec.yaml
index d5ad721..e761291 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -3,12 +3,12 @@
# BSD-style license that can be found in the LICENSE file.
name: ffigen
-version: 2.4.2
+version: 2.5.0-beta.1
homepage: https://github.com/dart-lang/ffigen
description: Generator for FFI bindings, using LibClang to parse C header files.
environment:
- sdk: '>=2.12.0 <3.0.0'
+ sdk: '>=2.13.0-211.6.beta <3.0.0'
dependencies:
ffi: ^1.0.0
diff --git a/test/code_generator_tests/code_generator_test.dart b/test/code_generator_tests/code_generator_test.dart
index 806ae22..7b0fa29 100644
--- a/test/code_generator_tests/code_generator_test.dart
+++ b/test/code_generator_tests/code_generator_test.dart
@@ -378,6 +378,32 @@
);
_matchLib(library, 'sort_bindings');
});
+ test('Pack Structs', () {
+ final library = Library(
+ name: 'Bindings',
+ bindings: [
+ Struc(name: 'NoPacking', pack: null, members: [
+ Member(name: 'a', type: Type.nativeType(SupportedNativeType.Char)),
+ ]),
+ Struc(name: 'Pack1', pack: 1, members: [
+ Member(name: 'a', type: Type.nativeType(SupportedNativeType.Char)),
+ ]),
+ Struc(name: 'Pack2', pack: 2, members: [
+ Member(name: 'a', type: Type.nativeType(SupportedNativeType.Char)),
+ ]),
+ Struc(name: 'Pack2', pack: 4, members: [
+ Member(name: 'a', type: Type.nativeType(SupportedNativeType.Char)),
+ ]),
+ Struc(name: 'Pack2', pack: 8, members: [
+ Member(name: 'a', type: Type.nativeType(SupportedNativeType.Char)),
+ ]),
+ Struc(name: 'Pack16', pack: 16, members: [
+ Member(name: 'a', type: Type.nativeType(SupportedNativeType.Char)),
+ ]),
+ ],
+ );
+ _matchLib(library, 'packed_structs');
+ });
}
/// Utility to match expected bindings to the generated bindings.
diff --git a/test/code_generator_tests/expected_bindings/_expected_packed_structs_bindings.dart b/test/code_generator_tests/expected_bindings/_expected_packed_structs_bindings.dart
new file mode 100644
index 0000000..9c647d7
--- /dev/null
+++ b/test/code_generator_tests/expected_bindings/_expected_packed_structs_bindings.dart
@@ -0,0 +1,39 @@
+// AUTO GENERATED FILE, DO NOT EDIT.
+//
+// Generated by `package:ffigen`.
+import 'dart:ffi' as ffi;
+
+class NoPacking extends ffi.Struct {
+ @ffi.Uint8()
+ external int a;
+}
+
+@ffi.Packed(1)
+class Pack1 extends ffi.Struct {
+ @ffi.Uint8()
+ external int a;
+}
+
+@ffi.Packed(2)
+class Pack2 extends ffi.Struct {
+ @ffi.Uint8()
+ external int a;
+}
+
+@ffi.Packed(4)
+class Pack2_1 extends ffi.Struct {
+ @ffi.Uint8()
+ external int a;
+}
+
+@ffi.Packed(8)
+class Pack2_2 extends ffi.Struct {
+ @ffi.Uint8()
+ external int a;
+}
+
+@ffi.Packed(16)
+class Pack16 extends ffi.Struct {
+ @ffi.Uint8()
+ external int a;
+}
diff --git a/test/config_tests/packed_struct_override_test.dart b/test/config_tests/packed_struct_override_test.dart
new file mode 100644
index 0000000..fd8be93
--- /dev/null
+++ b/test/config_tests/packed_struct_override_test.dart
@@ -0,0 +1,62 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:ffigen/ffigen.dart';
+import 'package:ffigen/src/code_generator.dart';
+import 'package:ffigen/src/strings.dart' as strings;
+import 'package:yaml/yaml.dart' as yaml;
+import 'package:test/test.dart';
+
+import '../test_utils.dart';
+
+late Library actual, expected;
+
+void main() {
+ group('packed_struct_override_test', () {
+ test('Invalid Packed Config values', () {
+ const baseYaml = '''${strings.name}: 'NativeLibrary'
+${strings.description}: 'Packed Struct Override Test'
+${strings.output}: 'unused'
+${strings.headers}:
+ ${strings.entryPoints}:
+ - 'test/header_parser_tests/packed_structs.h'
+${strings.structs}:
+ ${strings.structPack}:
+ ''';
+ expect(
+ () => Config.fromYaml(
+ yaml.loadYaml(baseYaml + "'.*': null") as yaml.YamlMap),
+ throwsA(TypeMatcher<FormatException>()));
+ expect(
+ () => Config.fromYaml(
+ yaml.loadYaml(baseYaml + "'.*': 3") as yaml.YamlMap),
+ throwsA(TypeMatcher<FormatException>()));
+ expect(
+ () => Config.fromYaml(
+ yaml.loadYaml(baseYaml + "'.*': 32") as yaml.YamlMap),
+ throwsA(TypeMatcher<FormatException>()));
+ });
+ test('Override values', () {
+ final config = Config.fromYaml(yaml.loadYaml('''
+${strings.name}: 'NativeLibrary'
+${strings.description}: 'Packed Struct Override Test'
+${strings.output}: 'unused'
+${strings.headers}:
+ ${strings.entryPoints}:
+ - 'test/header_parser_tests/packed_structs.h'
+${strings.structs}:
+ ${strings.structPack}:
+ 'Normal.*': 1
+ 'StructWithAttr': 2
+ 'PackedAttr': none
+ ''') as yaml.YamlMap);
+
+ final library = parse(config);
+
+ expect((library.getBinding('NormalStruct1') as Struc).pack, 1);
+ expect((library.getBinding('StructWithAttr') as Struc).pack, 2);
+ expect((library.getBinding('PackedAttr') as Struc).pack, null);
+ });
+ });
+}
diff --git a/test/header_parser_tests/expected_bindings/_expected_packed_structs_bindings.dart b/test/header_parser_tests/expected_bindings/_expected_packed_structs_bindings.dart
new file mode 100644
index 0000000..d27adc4
--- /dev/null
+++ b/test/header_parser_tests/expected_bindings/_expected_packed_structs_bindings.dart
@@ -0,0 +1,49 @@
+// AUTO GENERATED FILE, DO NOT EDIT.
+//
+// Generated by `package:ffigen`.
+import 'dart:ffi' as ffi;
+
+class NormalStruct1 extends ffi.Struct {
+ @ffi.Int8()
+ external int a;
+}
+
+/// Should not be packed.
+class StructWithAttr extends ffi.Struct {
+ external ffi.Pointer<ffi.Int32> a;
+
+ external ffi.Pointer<ffi.Int32> b;
+}
+
+/// Should be packed with 1.
+@ffi.Packed(1)
+class PackedAttr extends ffi.Struct {
+ @ffi.Int32()
+ external int a;
+}
+
+/// Should be packed with 8.
+@ffi.Packed(8)
+class PackedAttrAlign8 extends ffi.Struct {
+ @ffi.Int32()
+ external int a;
+}
+
+/// Should be packed with 2.
+@ffi.Packed(2)
+class Pack2WithPragma extends ffi.Struct {
+ @ffi.Int32()
+ external int a;
+}
+
+/// Should be packed with 4.
+@ffi.Packed(4)
+class Pack4WithPragma extends ffi.Struct {
+ @ffi.Int64()
+ external int a;
+}
+
+class NormalStruct2 extends ffi.Struct {
+ @ffi.Int8()
+ external int a;
+}
diff --git a/test/header_parser_tests/packed_structs.h b/test/header_parser_tests/packed_structs.h
new file mode 100644
index 0000000..2edec80
--- /dev/null
+++ b/test/header_parser_tests/packed_structs.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+struct NormalStruct1
+{
+ char a;
+};
+
+/// Should not be packed.
+struct StructWithAttr
+{
+ int *a;
+ int *b;
+} __attribute__((annotate("Attr is not __packed__")));
+
+/// Should be packed with 1.
+struct PackedAttr{
+ int a;
+} __attribute__((__packed__));
+
+/// Should be packed with 8.
+struct PackedAttrAlign8{
+ int a;
+} __attribute__((__packed__, aligned(8)));
+
+#pragma pack(push, 2)
+/// Should be packed with 2.
+struct Pack2WithPragma{
+ int a;
+};
+#pragma pack(4)
+/// Should be packed with 4.
+struct Pack4WithPragma{
+ long long a;
+};
+#pragma pack(pop)
+struct NormalStruct2
+{
+ char a;
+};
diff --git a/test/header_parser_tests/packed_structs_test.dart b/test/header_parser_tests/packed_structs_test.dart
new file mode 100644
index 0000000..e334fab
--- /dev/null
+++ b/test/header_parser_tests/packed_structs_test.dart
@@ -0,0 +1,45 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:ffigen/src/code_generator.dart';
+import 'package:ffigen/src/header_parser.dart' as parser;
+import 'package:ffigen/src/config_provider.dart';
+import 'package:logging/logging.dart';
+import 'package:test/test.dart';
+import 'package:yaml/yaml.dart' as yaml;
+import 'package:ffigen/src/strings.dart' as strings;
+
+import '../test_utils.dart';
+
+late Library actual;
+void main() {
+ group('packed_structs_test', () {
+ setUpAll(() {
+ logWarnings(Level.SEVERE);
+ actual = parser.parse(
+ Config.fromYaml(yaml.loadYaml('''
+${strings.name}: 'NativeLibrary'
+${strings.description}: 'Packed Structs Test'
+${strings.output}: 'unused'
+${strings.headers}:
+ ${strings.entryPoints}:
+ - 'test/header_parser_tests/packed_structs.h'
+ ''') as yaml.YamlMap),
+ );
+ });
+
+ test('Expected bindings', () {
+ matchLibraryWithExpected(actual, [
+ 'test',
+ 'debug_generated',
+ 'packed_structs_test_output.dart'
+ ], [
+ 'test',
+ 'header_parser_tests',
+ 'expected_bindings',
+ '_expected_packed_structs_bindings.dart'
+ ]);
+ });
+ });
+}
diff --git a/tool/libclang_config.yaml b/tool/libclang_config.yaml
index f61c097..87f6bb0 100644
--- a/tool/libclang_config.yaml
+++ b/tool/libclang_config.yaml
@@ -76,6 +76,7 @@
- clang_getPointeeType
- clang_getCanonicalType
- clang_Type_getNamedType
+ - clang_Type_getAlignOf
- clang_getTypeDeclaration
- clang_getTypedefDeclUnderlyingType
- clang_getCursorSpelling
@@ -106,3 +107,4 @@
- clang_Cursor_isFunctionInlined
- clang_getCursorDefinition
- clang_Cursor_isNull
+ - clang_Cursor_hasAttrs