[ffigen] BindingsIndex refactor (#283)
Unify and simplify how Types are cached and cycles are broken in the type graph.
- All types that need to be cached (those that have a declaration cursor) are cached in `BindingsIndex._declaredTypes`
- All calls to convert a `CXType` to an ffigen `Type` now go through `getCodeGenType`
- All calls to the related sub-parsers (`parseCompoundDeclaration`, `parseEnumDeclaration`, `parseTypedefDeclaration`) also go through `getCodeGenType`
- Those sub-parsers (mostly) don't know about `BindingsIndex`, and don't know about caching or cycle breaking. That's all handled in one place in `getCodeGenType`
- Cycles are broken by dividing type conversion into 2 stages (the only conversion this matters for is compound types):
- First the `Type` is created, with as many fields filled in as possible, without a recursive call to `getCodeGenType` (everything except the list of members)
- Next, `getCodeGenType` inserts the type into the cache
- Finally, the members are filled in. Recursive calls to `getCodeGenType` are allowed here, as they will hit the cache, breaking any cycles.diff --git a/pkgs/ffigen/example/libclang-example/generated_bindings.dart b/pkgs/ffigen/example/libclang-example/generated_bindings.dart
index 125e78c..6f73232 100644
--- a/pkgs/ffigen/example/libclang-example/generated_bindings.dart
+++ b/pkgs/ffigen/example/libclang-example/generated_bindings.dart
@@ -9296,8 +9296,6 @@
typedef DartClang_Cursor_getTranslationUnit = CXTranslationUnit Function(
CXCursor arg0);
-class CXCursorSetImpl extends ffi.Opaque {}
-
/// A fast container representing a set of CXCursors.
typedef CXCursorSet = ffi.Pointer<custom_import.CXCursorSetImpl>;
typedef NativeClang_createCXCursorSet = CXCursorSet Function();
diff --git a/pkgs/ffigen/lib/src/header_parser/includer.dart b/pkgs/ffigen/lib/src/header_parser/includer.dart
index 9211d5f..aaad693 100644
--- a/pkgs/ffigen/lib/src/header_parser/includer.dart
+++ b/pkgs/ffigen/lib/src/header_parser/includer.dart
@@ -20,22 +20,22 @@
bool shouldIncludeStruct(String usr, String name) {
return _shouldIncludeDecl(
- usr, name, bindingsIndex.isSeenStruct, config.structDecl.shouldInclude);
+ usr, name, bindingsIndex.isSeenType, config.structDecl.shouldInclude);
}
bool shouldIncludeUnion(String usr, String name) {
return _shouldIncludeDecl(
- usr, name, bindingsIndex.isSeenUnion, config.unionDecl.shouldInclude);
+ usr, name, bindingsIndex.isSeenType, config.unionDecl.shouldInclude);
}
bool shouldIncludeFunc(String usr, String name) {
return _shouldIncludeDecl(
- usr, name, bindingsIndex.isSeenFunc, config.functionDecl.shouldInclude);
+ usr, name, bindingsIndex.isSeenType, config.functionDecl.shouldInclude);
}
bool shouldIncludeEnumClass(String usr, String name) {
- return _shouldIncludeDecl(usr, name, bindingsIndex.isSeenEnumClass,
- config.enumClassDecl.shouldInclude);
+ return _shouldIncludeDecl(
+ usr, name, bindingsIndex.isSeenType, config.enumClassDecl.shouldInclude);
}
bool shouldIncludeUnnamedEnumConstant(String usr, String name) {
@@ -55,7 +55,7 @@
bool shouldIncludeTypealias(String usr, String name) {
return _shouldIncludeDecl(
- usr, name, bindingsIndex.isSeenTypealias, config.typedefs.shouldInclude);
+ usr, name, bindingsIndex.isSeenType, config.typedefs.shouldInclude);
}
/// True if a cursor should be included based on headers config, used on root
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 431334a..f1eb651 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
@@ -18,13 +18,15 @@
/// Holds temporary information regarding [compound] while parsing.
class _ParsedCompound {
- Compound? compound;
+ Compound compound;
bool unimplementedMemberType = false;
bool flexibleArrayMember = false;
bool bitFieldMember = false;
bool dartHandleMember = false;
bool incompleteCompoundMember = false;
+ _ParsedCompound(this.compound);
+
bool get isInComplete =>
unimplementedMemberType ||
flexibleArrayMember ||
@@ -40,32 +42,30 @@
// Stores the maximum alignment from all the children.
int maxChildAlignment = 0;
// Alignment of this struct.
- int allignment = 0;
+ int alignment = 0;
bool get _isPacked {
if (!hasAttr || isInComplete) return false;
if (hasPackedAttr) return true;
- return maxChildAlignment > allignment;
+ return maxChildAlignment > alignment;
}
/// Returns pack value of a struct depending on config, returns null for no
/// packing.
int? get packValue {
- if (compound!.isStruct && _isPacked) {
- if (strings.packingValuesMap.containsKey(allignment)) {
- return allignment;
+ if (compound.isStruct && _isPacked) {
+ if (strings.packingValuesMap.containsKey(alignment)) {
+ return alignment;
} else {
_logger.warning(
- 'Unsupported pack value "$allignment" for Struct "${compound!.name}".');
+ 'Unsupported pack value "$alignment" for Struct "${compound.name}".');
return null;
}
} else {
return null;
}
}
-
- _ParsedCompound();
}
final _stack = Stack<_ParsedCompound>();
@@ -83,31 +83,18 @@
/// generate these as opaque if `dependency-only` was set to opaque).
bool pointerReference = false,
}) {
- _stack.push(_ParsedCompound());
-
// Set includer functions according to compoundType.
final bool Function(String, String) shouldIncludeDecl;
- final bool Function(String) isSeenDecl;
- final Compound? Function(String) getSeenDecl;
- final void Function(String, Compound) addDeclToSeen;
final Declaration configDecl;
- final String className;
+ final String className = _compoundTypeDebugName(compoundType);
switch (compoundType) {
case CompoundType.struct:
shouldIncludeDecl = shouldIncludeStruct;
- isSeenDecl = bindingsIndex.isSeenStruct;
- getSeenDecl = bindingsIndex.getSeenStruct;
- addDeclToSeen = bindingsIndex.addStructToSeen;
configDecl = config.structDecl;
- className = 'Struct';
break;
case CompoundType.union:
shouldIncludeDecl = shouldIncludeUnion;
- isSeenDecl = bindingsIndex.isSeenUnion;
- getSeenDecl = bindingsIndex.getSeenUnion;
- addDeclToSeen = bindingsIndex.addUnionToSeen;
configDecl = config.unionDecl;
- className = 'Union';
break;
}
@@ -134,110 +121,110 @@
if (ignoreFilter) {
// This declaration is defined inside some other declaration and hence
// must be generated.
- _stack.top.compound = Compound.fromType(
+ return Compound.fromType(
type: compoundType,
name: incrementalNamer.name('Unnamed$className'),
usr: declUsr,
dartDoc: getCursorDocComment(cursor),
);
- _setMembers(cursor, className);
} else {
_logger.finest('unnamed $className declaration');
}
- } else if ((ignoreFilter || shouldIncludeDecl(declUsr, declName)) &&
- (!isSeenDecl(declUsr))) {
+ } else if (ignoreFilter || shouldIncludeDecl(declUsr, declName)) {
_logger.fine(
'++++ Adding $className: Name: $declName, ${cursor.completeStringRepr()}');
- _stack.top.compound = Compound.fromType(
+ return Compound.fromType(
type: compoundType,
usr: declUsr,
originalName: declName,
name: configDecl.renameUsingConfig(declName),
dartDoc: getCursorDocComment(cursor),
);
- // Adding to seen here to stop recursion if a declaration has itself as a
- // member, members are updated later.
- addDeclToSeen(declUsr, _stack.top.compound!);
}
-
- if (isSeenDecl(declUsr)) {
- _stack.top.compound = getSeenDecl(declUsr);
-
- // Skip dependencies if already seen OR user has specified `dependency-only`
- // as opaque AND this is a pointer reference AND the declaration was not
- // included according to config (ignoreFilter).
- final skipDependencies = _stack.top.compound!.parsedDependencies ||
- (pointerReference &&
- ignoreFilter &&
- ((compoundType == CompoundType.struct &&
- config.structDependencies == CompoundDependencies.opaque) ||
- (compoundType == CompoundType.union &&
- config.unionDependencies == CompoundDependencies.opaque)));
-
- if (!skipDependencies) {
- // Prevents infinite recursion if struct has a pointer to itself.
- _stack.top.compound!.parsedDependencies = true;
- _setMembers(cursor, className);
- } else if (!_stack.top.compound!.parsedDependencies) {
- _logger.fine('Skipped dependencies.');
- }
- }
-
- return _stack.pop().compound;
+ return null;
}
-void _setMembers(clang_types.CXCursor cursor, String className) {
- _stack.top.hasAttr = clang.clang_Cursor_hasAttrs(cursor) != 0;
- _stack.top.allignment = cursor.type().alignment();
+void fillCompoundMembersIfNeeded(
+ Compound compound,
+ clang_types.CXCursor cursor, {
+ /// Option to ignore declaration filter (Useful in case of extracting
+ /// declarations when they are passed/returned by an included function.)
+ bool ignoreFilter = false,
+
+ /// To track if the declaration was used by reference(i.e T*). (Used to only
+ /// generate these as opaque if `dependency-only` was set to opaque).
+ bool pointerReference = false,
+}) {
+ final compoundType = compound.compoundType;
+
+ // Skip dependencies if already seen OR user has specified `dependency-only`
+ // as opaque AND this is a pointer reference AND the declaration was not
+ // included according to config (ignoreFilter).
+ final skipDependencies = compound.parsedDependencies ||
+ (pointerReference &&
+ ignoreFilter &&
+ ((compoundType == CompoundType.struct &&
+ config.structDependencies == CompoundDependencies.opaque) ||
+ (compoundType == CompoundType.union &&
+ config.unionDependencies == CompoundDependencies.opaque)));
+ if (skipDependencies) return;
+
+ final parsed = _ParsedCompound(compound);
+ final String className = _compoundTypeDebugName(compoundType);
+ parsed.hasAttr = clang.clang_Cursor_hasAttrs(cursor) != 0;
+ parsed.alignment = cursor.type().alignment();
+ compound.parsedDependencies = true; // Break cycles.
+
+ _stack.push(parsed);
final resultCode = clang.clang_visitChildren(
cursor,
Pointer.fromFunction(_compoundMembersVisitor, exceptional_visitor_return),
nullptr,
);
+ _stack.pop();
_logger.finest(
- 'Opaque: ${_stack.top.isInComplete}, HasAttr: ${_stack.top.hasAttr}, AlignValue: ${_stack.top.allignment}, MaxChildAlignValue: ${_stack.top.maxChildAlignment}, PackValue: ${_stack.top.packValue}.');
- _stack.top.compound!.pack = _stack.top.packValue;
+ 'Opaque: ${parsed.isInComplete}, HasAttr: ${parsed.hasAttr}, AlignValue: ${parsed.alignment}, MaxChildAlignValue: ${parsed.maxChildAlignment}, PackValue: ${parsed.packValue}.');
+ compound.pack = parsed.packValue;
visitChildrenResultChecker(resultCode);
- if (_stack.top.unimplementedMemberType) {
+ if (parsed.unimplementedMemberType) {
_logger.fine(
'---- Removed $className members, reason: member with unimplementedtype ${cursor.completeStringRepr()}');
_logger.warning(
- 'Removed All $className Members from ${_stack.top.compound!.name}(${_stack.top.compound!.originalName}), struct member has an unsupported type.');
- } else if (_stack.top.flexibleArrayMember) {
+ 'Removed All $className Members from ${compound.name}(${compound.originalName}), struct member has an unsupported type.');
+ } else if (parsed.flexibleArrayMember) {
_logger.fine(
'---- Removed $className members, reason: incomplete array member ${cursor.completeStringRepr()}');
_logger.warning(
- 'Removed All $className Members from ${_stack.top.compound!.name}(${_stack.top.compound!.originalName}), Flexible array members not supported.');
- } else if (_stack.top.bitFieldMember) {
+ 'Removed All $className Members from ${compound.name}(${compound.originalName}), Flexible array members not supported.');
+ } else if (parsed.bitFieldMember) {
_logger.fine(
'---- Removed $className members, reason: bitfield members ${cursor.completeStringRepr()}');
_logger.warning(
- 'Removed All $className Members from ${_stack.top.compound!.name}(${_stack.top.compound!.originalName}), Bit Field members not supported.');
- } else if (_stack.top.dartHandleMember && config.useDartHandle) {
+ 'Removed All $className Members from ${compound.name}(${compound.originalName}), Bit Field members not supported.');
+ } else if (parsed.dartHandleMember && config.useDartHandle) {
_logger.fine(
'---- Removed $className members, reason: Dart_Handle member. ${cursor.completeStringRepr()}');
_logger.warning(
- 'Removed All $className Members from ${_stack.top.compound!.name}(${_stack.top.compound!.originalName}), Dart_Handle member not supported.');
- } else if (_stack.top.incompleteCompoundMember) {
+ 'Removed All $className Members from ${compound.name}(${compound.originalName}), Dart_Handle member not supported.');
+ } else if (parsed.incompleteCompoundMember) {
_logger.fine(
'---- Removed $className members, reason: Incomplete Nested Struct member. ${cursor.completeStringRepr()}');
_logger.warning(
- 'Removed All $className Members from ${_stack.top.compound!.name}(${_stack.top.compound!.originalName}), Incomplete Nested Struct member not supported.');
+ 'Removed All $className Members from ${compound.name}(${compound.originalName}), Incomplete Nested Struct member not supported.');
}
// Clear all members if declaration is incomplete.
- if (_stack.top.isInComplete) {
- _stack.top.compound!.members.clear();
+ if (parsed.isInComplete) {
+ compound.members.clear();
}
// C allows empty structs/union, but it's undefined behaviour at runtine.
// So we need to mark a declaration incomplete if it has no members.
- _stack.top.compound!.isInComplete =
- _stack.top.isInComplete || _stack.top.compound!.members.isEmpty;
+ compound.isInComplete = parsed.isInComplete || compound.members.isEmpty;
}
/// Visitor for the struct/union cursor [CXCursorKind.CXCursor_StructDecl]/
@@ -246,36 +233,37 @@
/// Child visitor invoked on struct/union cursor.
int _compoundMembersVisitor(clang_types.CXCursor cursor,
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()}');
// Set maxChildAlignValue.
final align = cursor.type().alignment();
- if (align > _stack.top.maxChildAlignment) {
- _stack.top.maxChildAlignment = align;
+ if (align > parsed.maxChildAlignment) {
+ parsed.maxChildAlignment = align;
}
final mt = cursor.type().toCodeGenType();
if (mt.broadType == BroadType.IncompleteArray) {
// TODO(68): Structs with flexible Array Members are not supported.
- _stack.top.flexibleArrayMember = true;
+ parsed.flexibleArrayMember = true;
}
if (clang.clang_getFieldDeclBitWidth(cursor) != -1) {
// TODO(84): Struct with bitfields are not suppoorted.
- _stack.top.bitFieldMember = true;
+ parsed.bitFieldMember = true;
}
if (mt.broadType == BroadType.Handle) {
- _stack.top.dartHandleMember = true;
+ parsed.dartHandleMember = true;
}
if (mt.isIncompleteCompound) {
- _stack.top.incompleteCompoundMember = true;
+ parsed.incompleteCompoundMember = true;
}
if (mt.getBaseType().broadType == BroadType.Unimplemented) {
- _stack.top.unimplementedMemberType = true;
+ parsed.unimplementedMemberType = true;
}
- _stack.top.compound!.members.add(
+ parsed.compound.members.add(
Member(
dartDoc: getCursorDocComment(
cursor,
@@ -283,14 +271,14 @@
),
originalName: cursor.spelling(),
name: config.structDecl.renameMemberUsingConfig(
- _stack.top.compound!.originalName,
+ parsed.compound.originalName,
cursor.spelling(),
),
type: mt,
),
);
} else if (cursor.kind == clang_types.CXCursorKind.CXCursor_PackedAttr) {
- _stack.top.hasPackedAttr = true;
+ parsed.hasPackedAttr = true;
}
} catch (e, s) {
_logger.severe(e);
@@ -299,3 +287,7 @@
}
return clang_types.CXChildVisitResult.CXChildVisit_Continue;
}
+
+String _compoundTypeDebugName(CompoundType compoundType) {
+ return compoundType == CompoundType.struct ? "Struct" : "Union";
+}
diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/enumdecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/enumdecl_parser.dart
index c4b2ab8..4e5e150 100644
--- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/enumdecl_parser.dart
+++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/enumdecl_parser.dart
@@ -54,8 +54,7 @@
if (enumName.isEmpty) {
_logger.fine('Saving anonymous enum.');
saveUnNamedEnum(cursor);
- } else if ((ignoreFilter || shouldIncludeEnumClass(enumUsr, enumName)) &&
- (!bindingsIndex.isSeenEnumClass(enumUsr))) {
+ } else if (ignoreFilter || shouldIncludeEnumClass(enumUsr, enumName)) {
_logger.fine('++++ Adding Enum: ${cursor.completeStringRepr()}');
_stack.top.enumClass = EnumClass(
usr: enumUsr,
@@ -63,12 +62,8 @@
originalName: enumName,
name: config.enumClassDecl.renameUsingConfig(enumName),
);
- bindingsIndex.addEnumClassToSeen(enumUsr, _stack.top.enumClass!);
_addEnumConstant(cursor);
}
- if (bindingsIndex.isSeenEnumClass(enumUsr)) {
- _stack.top.enumClass = bindingsIndex.getSeenEnumClass(enumUsr);
- }
return _stack.pop().enumClass;
}
diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/functiondecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/functiondecl_parser.dart
index 3b8cd63..870cf47 100644
--- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/functiondecl_parser.dart
+++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/functiondecl_parser.dart
@@ -39,9 +39,8 @@
'---- Removed Function, reason: inline function: ${cursor.completeStringRepr()}');
_logger.warning(
"Skipped Function '$funcName', inline functions are not supported.");
- return _stack
- .pop()
- .func; // Returning null so that [addToBindings] function excludes this.
+ // Returning null so that [addToBindings] function excludes this.
+ return _stack.pop().func;
}
if (rt.isIncompleteCompound || _stack.top.incompleteStructParameter) {
@@ -49,9 +48,8 @@
'---- Removed Function, reason: Incomplete struct pass/return by value: ${cursor.completeStringRepr()}');
_logger.warning(
"Skipped Function '$funcName', Incomplete struct pass/return by value not supported.");
- return _stack
- .pop()
- .func; // Returning null so that [addToBindings] function excludes this.
+ // Returning null so that [addToBindings] function excludes this.
+ return _stack.pop().func;
}
if (rt.getBaseType().broadType == BroadType.Unimplemented ||
@@ -60,9 +58,8 @@
'---- Removed Function, reason: unsupported return type or parameter type: ${cursor.completeStringRepr()}');
_logger.warning(
"Skipped Function '$funcName', function has unsupported return type or parameter type.");
- return _stack
- .pop()
- .func; // Returning null so that [addToBindings] function excludes this.
+ // Returning null so that [addToBindings] function excludes this.
+ return _stack.pop().func;
}
_stack.top.func = Func(
diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart
index 90d5664..d8f84cf 100644
--- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart
+++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/typedefdecl_parser.dart
@@ -13,8 +13,7 @@
final _logger = Logger('ffigen.header_parser.typedefdecl_parser');
-/// Holds temporary information regarding a typedef referenced [Binding]
-/// while parsing.
+/// Parses a typedef declaration.
///
/// Notes:
/// - Pointer to Typedefs structs are skipped if the struct is seen.
@@ -30,16 +29,6 @@
///
/// typedef A D; // Typeref.
/// ```
-class _ParsedTypealias {
- Typealias? typealias;
- String? typedefName;
- bool typedefToPointer = false;
- _ParsedTypealias();
-}
-
-final _stack = Stack<_ParsedTypealias>();
-
-/// Parses a typedef declaration.
///
/// Returns `null` if the typedef could not be generated or has been excluded
/// by the config.
@@ -47,7 +36,6 @@
clang_types.CXCursor cursor, {
bool pointerReference = false,
}) {
- _stack.push(_ParsedTypealias());
final typedefName = cursor.spelling();
final typedefUsr = cursor.usr();
if (shouldIncludeTypealias(typedefUsr, typedefName)) {
@@ -85,20 +73,14 @@
bindingsIndex.addUnsupportedTypealiasToSeen(typedefUsr);
} else {
// Create typealias.
- _stack.top.typealias = Typealias(
+ return Typealias(
usr: typedefUsr,
originalName: typedefName,
name: config.typedefs.renameUsingConfig(typedefName),
type: s,
dartDoc: getCursorDocComment(cursor),
);
- bindingsIndex.addTypealiasToSeen(typedefUsr, _stack.top.typealias!);
}
}
-
- if (bindingsIndex.isSeenTypealias(typedefUsr)) {
- _stack.top.typealias = bindingsIndex.getSeenTypealias(typedefUsr);
- }
-
- return _stack.pop().typealias;
+ return null;
}
diff --git a/pkgs/ffigen/lib/src/header_parser/translation_unit_parser.dart b/pkgs/ffigen/lib/src/header_parser/translation_unit_parser.dart
index 9a576c0..60c623e 100644
--- a/pkgs/ffigen/lib/src/header_parser/translation_unit_parser.dart
+++ b/pkgs/ffigen/lib/src/header_parser/translation_unit_parser.dart
@@ -12,9 +12,8 @@
import 'clang_bindings/clang_bindings.dart' as clang_types;
import 'data.dart';
import 'includer.dart';
-import 'sub_parsers/compounddecl_parser.dart';
-import 'sub_parsers/enumdecl_parser.dart';
import 'sub_parsers/functiondecl_parser.dart';
+import 'type_extractor/extractor.dart';
import 'utils.dart';
final _logger = Logger('ffigen.header_parser.translation_unit_parser');
@@ -46,13 +45,11 @@
addToBindings(parseFunctionDeclaration(cursor));
break;
case clang_types.CXCursorKind.CXCursor_StructDecl:
- addToBindings(parseCompoundDeclaration(cursor, CompoundType.struct));
- break;
case clang_types.CXCursorKind.CXCursor_UnionDecl:
- addToBindings(parseCompoundDeclaration(cursor, CompoundType.union));
+ addToBindings(_getCodeGenTypeFromCursor(cursor)?.compound);
break;
case clang_types.CXCursorKind.CXCursor_EnumDecl:
- addToBindings(parseEnumDeclaration(cursor));
+ addToBindings(_getCodeGenTypeFromCursor(cursor)?.enumClass);
break;
case clang_types.CXCursorKind.CXCursor_MacroDefinition:
saveMacroDefinition(cursor);
@@ -82,3 +79,7 @@
_bindings.add(b);
}
}
+
+Type? _getCodeGenTypeFromCursor(clang_types.CXCursor cursor) {
+ return getCodeGenType(cursor.type(), ignoreFilter: false);
+}
diff --git a/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart b/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart
index 6063035..8ea5d96 100644
--- a/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart
+++ b/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart
@@ -22,13 +22,47 @@
Type getCodeGenType(
clang_types.CXType cxtype, {
+ /// Option to ignore declaration filter (Useful in case of extracting
+ /// declarations when they are passed/returned by an included function.)
+ bool ignoreFilter = true,
+
/// Passed on if a value was marked as a pointer before this one.
bool pointerReference = false,
}) {
_logger.fine('${_padding}getCodeGenType ${cxtype.completeStringRepr()}');
- final kind = cxtype.kind;
- switch (kind) {
+ // Special case: Elaborated types just refer to another type.
+ if (cxtype.kind == clang_types.CXTypeKind.CXType_Elaborated) {
+ return getCodeGenType(clang.clang_Type_getNamedType(cxtype),
+ ignoreFilter: ignoreFilter, pointerReference: pointerReference);
+ }
+
+ // If the type has a declaration cursor, then use the BindingsIndex to break
+ // any potential cycles, and dedupe the Type.
+ final cursor = clang.clang_getTypeDeclaration(cxtype);
+ if (cursor.kind != clang_types.CXCursorKind.CXCursor_NoDeclFound) {
+ final usr = cursor.usr();
+ var type = bindingsIndex.getSeenType(usr);
+ if (type == null) {
+ final result =
+ _createTypeFromCursor(cxtype, cursor, ignoreFilter, pointerReference);
+ type = result.type;
+ if (type == null) {
+ return Type.unimplemented(
+ 'Type: ${cxtype.kindSpelling()} not implemented');
+ }
+ if (result.addToCache) {
+ bindingsIndex.addTypeToSeen(usr, type);
+ }
+ }
+ _fillFromCursorIfNeeded(type, cursor, ignoreFilter, pointerReference);
+ return type;
+ }
+
+ // If the type doesn't have a declaration cursor, then it's a basic type such
+ // as int, or a simple derived type like a pointer, so doesn't need to be
+ // cached.
+ switch (cxtype.kind) {
case clang_types.CXTypeKind.CXType_Pointer:
final pt = clang.clang_getPointeeType(cxtype);
final s = getCodeGenType(pt, pointerReference: true);
@@ -41,67 +75,6 @@
return Type.handle();
}
return Type.pointer(s);
- case clang_types.CXTypeKind.CXType_Typedef:
- final spelling = clang.clang_getTypedefName(cxtype).toStringAndDispose();
- if (config.typedefTypeMappings.containsKey(spelling)) {
- _logger.fine(' Type $spelling mapped from type-map');
- return Type.importedType(config.typedefTypeMappings[spelling]!);
- }
- // Get name from supported typedef name if config allows.
- if (config.useSupportedTypedefs) {
- if (suportedTypedefToSuportedNativeType.containsKey(spelling)) {
- _logger.fine(' Type Mapped from supported typedef');
- return Type.nativeType(
- suportedTypedefToSuportedNativeType[spelling]!);
- } else if (supportedTypedefToImportedType.containsKey(spelling)) {
- _logger.fine(' Type Mapped from supported typedef');
- return Type.importedType(supportedTypedefToImportedType[spelling]!);
- }
- }
-
- // This is important or we get stuck in infinite recursion.
- final cursor = clang.clang_getTypeDeclaration(cxtype);
- final typedefUsr = cursor.usr();
-
- if (bindingsIndex.isSeenTypealias(typedefUsr)) {
- return Type.typealias(bindingsIndex.getSeenTypealias(typedefUsr)!);
- } else {
- final typealias =
- parseTypedefDeclaration(cursor, pointerReference: pointerReference);
-
- if (typealias != null) {
- return Type.typealias(typealias);
- } else {
- // Use underlying type if typealias couldn't be created or if
- // the user excluded this typedef.
- final ct = clang.clang_getTypedefDeclUnderlyingType(cursor);
- return getCodeGenType(ct, pointerReference: pointerReference);
- }
- }
- case clang_types.CXTypeKind.CXType_Elaborated:
- final et = clang.clang_Type_getNamedType(cxtype);
- final s = getCodeGenType(et, pointerReference: pointerReference);
- return s;
- case clang_types.CXTypeKind.CXType_Record:
- return _extractfromRecord(cxtype, pointerReference);
- case clang_types.CXTypeKind.CXType_Enum:
- final cursor = clang.clang_getTypeDeclaration(cxtype);
- final usr = cursor.usr();
-
- if (bindingsIndex.isSeenEnumClass(usr)) {
- return Type.enumClass(bindingsIndex.getSeenEnumClass(usr)!);
- } else {
- final enumClass = parseEnumDeclaration(
- cursor,
- ignoreFilter: true,
- );
- if (enumClass == null) {
- // Handle anonymous enum declarations within another declaration.
- return Type.nativeType(Type.enumNativeType);
- } else {
- return Type.enumClass(enumClass);
- }
- }
case clang_types.CXTypeKind.CXType_FunctionProto:
// Primarily used for function pointers.
return _extractFromFunctionProto(cxtype);
@@ -141,34 +114,105 @@
}
}
-Type _extractfromRecord(clang_types.CXType cxtype, bool pointerReference) {
- Type type;
+class _CreateTypeFromCursorResult {
+ final Type? type;
- final cursor = clang.clang_getTypeDeclaration(cxtype);
+ // Flag that controls whether the type is added to the cache. It should not
+ // be added to the cache if it's just a fallback implementation, such as the
+ // int that is returned when an enum is excluded by the config. Later we might
+ // need to build the full enum type (eg if it's part of an included struct),
+ // and if we put the fallback int in the cache then the full enum will never
+ // be created.
+ final bool addToCache;
+
+ _CreateTypeFromCursorResult(this.type, {this.addToCache = true});
+}
+
+_CreateTypeFromCursorResult _createTypeFromCursor(clang_types.CXType cxtype,
+ clang_types.CXCursor cursor, bool ignoreFilter, bool pointerReference) {
+ switch (cxtype.kind) {
+ case clang_types.CXTypeKind.CXType_Typedef:
+ final spelling = clang.clang_getTypedefName(cxtype).toStringAndDispose();
+ if (config.typedefTypeMappings.containsKey(spelling)) {
+ _logger.fine(' Type $spelling mapped from type-map');
+ return _CreateTypeFromCursorResult(
+ Type.importedType(config.typedefTypeMappings[spelling]!));
+ }
+ // Get name from supported typedef name if config allows.
+ if (config.useSupportedTypedefs) {
+ if (suportedTypedefToSuportedNativeType.containsKey(spelling)) {
+ _logger.fine(' Type Mapped from supported typedef');
+ return _CreateTypeFromCursorResult(
+ Type.nativeType(suportedTypedefToSuportedNativeType[spelling]!));
+ } else if (supportedTypedefToImportedType.containsKey(spelling)) {
+ _logger.fine(' Type Mapped from supported typedef');
+ return _CreateTypeFromCursorResult(
+ Type.importedType(supportedTypedefToImportedType[spelling]!));
+ }
+ }
+
+ final typealias =
+ parseTypedefDeclaration(cursor, pointerReference: pointerReference);
+
+ if (typealias != null) {
+ return _CreateTypeFromCursorResult(Type.typealias(typealias));
+ } else {
+ // Use underlying type if typealias couldn't be created or if the user
+ // excluded this typedef.
+ final ct = clang.clang_getTypedefDeclUnderlyingType(cursor);
+ return _CreateTypeFromCursorResult(
+ getCodeGenType(ct, pointerReference: pointerReference),
+ addToCache: false);
+ }
+ case clang_types.CXTypeKind.CXType_Record:
+ return _CreateTypeFromCursorResult(
+ _extractfromRecord(cxtype, cursor, ignoreFilter, pointerReference));
+ case clang_types.CXTypeKind.CXType_Enum:
+ final enumClass = parseEnumDeclaration(
+ cursor,
+ ignoreFilter: ignoreFilter,
+ );
+ if (enumClass == null) {
+ // Handle anonymous enum declarations within another declaration.
+ return _CreateTypeFromCursorResult(Type.nativeType(Type.enumNativeType),
+ addToCache: false);
+ } else {
+ return _CreateTypeFromCursorResult(Type.enumClass(enumClass));
+ }
+ default:
+ throw UnimplementedError(
+ 'Unknown cursor kind: ${cursor.completeStringRepr()}');
+ }
+}
+
+void _fillFromCursorIfNeeded(Type? type, clang_types.CXCursor cursor,
+ bool ignoreFilter, bool pointerReference) {
+ if (type == null) return;
+ if (type.compound != null) {
+ fillCompoundMembersIfNeeded(type.compound!, cursor,
+ ignoreFilter: ignoreFilter, pointerReference: pointerReference);
+ }
+}
+
+Type? _extractfromRecord(clang_types.CXType cxtype, clang_types.CXCursor cursor,
+ bool ignoreFilter, bool pointerReference) {
_logger.fine('${_padding}_extractfromRecord: ${cursor.completeStringRepr()}');
final cursorKind = clang.clang_getCursorKind(cursor);
if (cursorKind == clang_types.CXCursorKind.CXCursor_StructDecl ||
cursorKind == clang_types.CXCursorKind.CXCursor_UnionDecl) {
- final declUsr = cursor.usr();
final declSpelling = cursor.spelling();
// Set includer functions according to compoundType.
- final bool Function(String) isSeenDecl;
- final Compound? Function(String) getSeenDecl;
final CompoundType compoundType;
final Map<String, ImportedType> compoundTypeMappings;
switch (cursorKind) {
case clang_types.CXCursorKind.CXCursor_StructDecl:
- isSeenDecl = bindingsIndex.isSeenStruct;
- getSeenDecl = bindingsIndex.getSeenStruct;
compoundType = CompoundType.struct;
compoundTypeMappings = config.structTypeMappings;
break;
case clang_types.CXCursorKind.CXCursor_UnionDecl:
- isSeenDecl = bindingsIndex.isSeenUnion;
- getSeenDecl = bindingsIndex.getSeenUnion;
compoundType = CompoundType.union;
compoundTypeMappings = config.unionTypeMappings;
break;
@@ -181,32 +225,20 @@
if (compoundTypeMappings.containsKey(declSpelling)) {
_logger.fine(' Type Mapped from type-map');
return Type.importedType(compoundTypeMappings[declSpelling]!);
- } else if (isSeenDecl(declUsr)) {
- type = Type.compound(getSeenDecl(declUsr)!);
-
- // This will parse the dependencies if needed.
- parseCompoundDeclaration(
- cursor,
- compoundType,
- ignoreFilter: true,
- pointerReference: pointerReference,
- );
} else {
- final struc = parseCompoundDeclaration(
+ final struct = parseCompoundDeclaration(
cursor,
compoundType,
- ignoreFilter: true,
+ ignoreFilter: ignoreFilter,
pointerReference: pointerReference,
);
- type = Type.compound(struc!);
+ if (struct == null) return null;
+ return Type.compound(struct);
}
- } else {
- _logger.fine(
- 'typedeclarationCursorVisitor: _extractfromRecord: Not Implemented, ${cursor.completeStringRepr()}');
- return Type.unimplemented('Type: ${cxtype.kindSpelling()} not implemented');
}
-
- return type;
+ _logger.fine(
+ 'typedeclarationCursorVisitor: _extractfromRecord: Not Implemented, ${cursor.completeStringRepr()}');
+ return Type.unimplemented('Type: ${cxtype.kindSpelling()} not implemented');
}
// Used for function pointer arguments.
diff --git a/pkgs/ffigen/lib/src/header_parser/utils.dart b/pkgs/ffigen/lib/src/header_parser/utils.dart
index bac2eaf..b110134 100644
--- a/pkgs/ffigen/lib/src/header_parser/utils.dart
+++ b/pkgs/ffigen/lib/src/header_parser/utils.dart
@@ -89,14 +89,14 @@
return s;
}
- /// Dispose type using [type.dispose].
+ /// Type associated with the pointer if any. Type will have kind
+ /// [clang.CXTypeKind.CXType_Invalid] otherwise.
clang_types.CXType type() {
return clang.clang_getCursorType(this);
}
- /// Only valid for [clang.CXCursorKind.CXCursor_FunctionDecl].
- ///
- /// Dispose type using [type.dispose].
+ /// Only valid for [clang.CXCursorKind.CXCursor_FunctionDecl]. Type will have
+ /// kind [clang.CXTypeKind.CXType_Invalid] otherwise.
clang_types.CXType returnType() {
return clang.clang_getResultType(type());
}
@@ -329,134 +329,42 @@
/// Tracks if a binding is 'seen' or not.
class BindingsIndex {
// Tracks if bindings are already seen, Map key is USR obtained from libclang.
- final Map<String, Struc> _structs = {};
- final Map<String, Union> _unions = {};
+ final Map<String, Type> _declaredTypes = {};
final Map<String, Func> _functions = {};
- final Map<String, EnumClass> _enumClass = {};
final Map<String, Constant> _unnamedEnumConstants = {};
final Map<String, String> _macros = {};
final Map<String, Global> _globals = {};
/// Contains usr for typedefs which cannot be generated.
final Set<String> _unsupportedTypealiases = {};
- final Map<String, Typealias> _typealiases = {};
/// Index for headers.
final Map<String, bool> _headerCache = {};
- bool isSeenStruct(String usr) {
- return _structs.containsKey(usr);
- }
-
- void addStructToSeen(String usr, Compound struc) {
- _structs[usr] = struc as Struc;
- }
-
- Struc? getSeenStruct(String usr) {
- return _structs[usr];
- }
-
- bool isSeenUnion(String usr) {
- return _unions.containsKey(usr);
- }
-
- void addUnionToSeen(String usr, Compound union) {
- _unions[usr] = union as Union;
- }
-
- Union? getSeenUnion(String usr) {
- return _unions[usr];
- }
-
- bool isSeenFunc(String usr) {
- return _functions.containsKey(usr);
- }
-
- void addFuncToSeen(String usr, Func func) {
- _functions[usr] = func;
- }
-
- Func? getSeenFunc(String usr) {
- return _functions[usr];
- }
-
- bool isSeenEnumClass(String usr) {
- return _enumClass.containsKey(usr);
- }
-
- void addEnumClassToSeen(String usr, EnumClass enumClass) {
- _enumClass[usr] = enumClass;
- }
-
- EnumClass? getSeenEnumClass(String usr) {
- return _enumClass[usr];
- }
-
- bool isSeenUnnamedEnumConstant(String usr) {
- return _unnamedEnumConstants.containsKey(usr);
- }
-
- void addUnnamedEnumConstantToSeen(String usr, Constant enumConstant) {
- _unnamedEnumConstants[usr] = enumConstant;
- }
-
- Constant? getSeenUnnamedEnumConstant(String usr) {
- return _unnamedEnumConstants[usr];
- }
-
- bool isSeenGlobalVar(String usr) {
- return _globals.containsKey(usr);
- }
-
- void addGlobalVarToSeen(String usr, Global global) {
- _globals[usr] = global;
- }
-
- Global? getSeenGlobalVar(String usr) {
- return _globals[usr];
- }
-
- bool isSeenMacro(String usr) {
- return _macros.containsKey(usr);
- }
-
- void addMacroToSeen(String usr, String macro) {
- _macros[usr] = macro;
- }
-
- String? getSeenMacro(String usr) {
- return _macros[usr];
- }
-
- bool isSeenTypealias(String usr) {
- return _typealiases.containsKey(usr);
- }
-
- void addTypealiasToSeen(String usr, Typealias t) {
- _typealiases[usr] = t;
- }
-
- bool isSeenUnsupportedTypealias(String usr) {
- return _unsupportedTypealiases.contains(usr);
- }
-
- void addUnsupportedTypealiasToSeen(String usr) {
- _unsupportedTypealiases.add(usr);
- }
-
- Typealias? getSeenTypealias(String usr) {
- return _typealiases[usr];
- }
-
- bool isSeenHeader(String source) {
- return _headerCache.containsKey(source);
- }
-
- void addHeaderToSeen(String source, bool includeStatus) {
- _headerCache[source] = includeStatus;
- }
-
- bool? getSeenHeaderStatus(String source) {
- return _headerCache[source];
- }
+ bool isSeenType(String usr) => _declaredTypes.containsKey(usr);
+ void addTypeToSeen(String usr, Type type) => _declaredTypes[usr] = type;
+ Type? getSeenType(String usr) => _declaredTypes[usr];
+ bool isSeenFunc(String usr) => _functions.containsKey(usr);
+ void addFuncToSeen(String usr, Func func) => _functions[usr] = func;
+ Func? getSeenFunc(String usr) => _functions[usr];
+ bool isSeenUnnamedEnumConstant(String usr) =>
+ _unnamedEnumConstants.containsKey(usr);
+ void addUnnamedEnumConstantToSeen(String usr, Constant enumConstant) =>
+ _unnamedEnumConstants[usr] = enumConstant;
+ Constant? getSeenUnnamedEnumConstant(String usr) =>
+ _unnamedEnumConstants[usr];
+ bool isSeenGlobalVar(String usr) => _globals.containsKey(usr);
+ void addGlobalVarToSeen(String usr, Global global) => _globals[usr] = global;
+ Global? getSeenGlobalVar(String usr) => _globals[usr];
+ bool isSeenMacro(String usr) => _macros.containsKey(usr);
+ void addMacroToSeen(String usr, String macro) => _macros[usr] = macro;
+ String? getSeenMacro(String usr) => _macros[usr];
+ bool isSeenUnsupportedTypealias(String usr) =>
+ _unsupportedTypealiases.contains(usr);
+ void addUnsupportedTypealiasToSeen(String usr) =>
+ _unsupportedTypealiases.add(usr);
+ bool isSeenHeader(String source) => _headerCache.containsKey(source);
+ void addHeaderToSeen(String source, bool includeStatus) =>
+ _headerCache[source] = includeStatus;
+ bool? getSeenHeaderStatus(String source) => _headerCache[source];
}
diff --git a/pkgs/ffigen/lib/src/strings.dart b/pkgs/ffigen/lib/src/strings.dart
index cbfc610..58e9b5d 100644
--- a/pkgs/ffigen/lib/src/strings.dart
+++ b/pkgs/ffigen/lib/src/strings.dart
@@ -178,6 +178,8 @@
'/usr/lib/llvm-9/lib/',
'/usr/lib/llvm-10/lib/',
'/usr/lib/llvm-11/lib/',
+ '/usr/lib/llvm-12/lib/',
+ '/usr/lib/llvm-13/lib/',
'/usr/lib/',
'/usr/lib64/',
};