| // Copyright (c) 2020, 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 'dart:ffi'; |
| |
| import 'package:ffigen/src/code_generator.dart'; |
| 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'; |
| import '../utils.dart'; |
| |
| final _logger = Logger('ffigen.header_parser.structdecl_parser'); |
| |
| /// Holds temporary information regarding [struc] while parsing. |
| class _ParsedStruc { |
| Struc? struc; |
| bool unimplementedMemberType = false; |
| bool flexibleArrayMember = false; |
| bool arrayMember = false; |
| bool bitFieldMember = false; |
| bool dartHandleMember = false; |
| bool incompleteStructMember = false; |
| |
| bool get isInComplete => |
| unimplementedMemberType || |
| flexibleArrayMember || |
| (arrayMember && !config.arrayWorkaround) || |
| bitFieldMember || |
| (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(); |
| } |
| |
| final _stack = Stack<_ParsedStruc>(); |
| |
| /// Parses a struct declaration. |
| Struc? parseStructDeclaration( |
| clang_types.CXCursor cursor, { |
| |
| /// Optionally provide name (useful in case struct is inside a typedef). |
| String? name, |
| |
| /// Option to ignore struct filter (Useful in case of extracting structs |
| /// when they are passed/returned by an included function.) |
| bool ignoreFilter = false, |
| |
| /// To track if the struct was used by reference(i.e struct*). (Used to only |
| /// generate these as opaque if `struct-dependencies` was set to opaque). |
| bool pointerReference = false, |
| |
| /// If the struct name should be updated, if it was already seen. |
| bool updateName = true, |
| }) { |
| _stack.push(_ParsedStruc()); |
| |
| // Parse the cursor definition instead, if this is a forward declaration. |
| if (isForwardDeclaration(cursor)) { |
| cursor = clang.clang_getCursorDefinition(cursor); |
| } |
| final structUsr = cursor.usr(); |
| final structName = name ?? cursor.spelling(); |
| |
| if (structName.isEmpty) { |
| if (ignoreFilter) { |
| // This struct is defined inside some other struct and hence must be generated. |
| _stack.top.struc = Struc( |
| name: incrementalNamer.name('unnamedStruct'), |
| usr: structUsr, |
| dartDoc: getCursorDocComment(cursor), |
| ); |
| _setStructMembers(cursor); |
| } else { |
| _logger.finest('unnamed structure or typedef structure declaration'); |
| } |
| } else if ((ignoreFilter || shouldIncludeStruct(structUsr, structName)) && |
| (!bindingsIndex.isSeenStruct(structUsr))) { |
| _logger.fine( |
| '++++ Adding Structure: structName: $structName, ${cursor.completeStringRepr()}'); |
| _stack.top.struc = Struc( |
| usr: structUsr, |
| originalName: structName, |
| name: config.structDecl.renameUsingConfig(structName), |
| dartDoc: getCursorDocComment(cursor), |
| ); |
| // Adding to seen here to stop recursion if a struct has itself as a |
| // member, members are updated later. |
| bindingsIndex.addStructToSeen(structUsr, _stack.top.struc!); |
| } |
| |
| if (bindingsIndex.isSeenStruct(structUsr)) { |
| _stack.top.struc = bindingsIndex.getSeenStruct(structUsr); |
| |
| final skipDependencies = _stack.top.struc!.parsedDependencies || |
| (config.structDependencies == StructDependencies.opaque && |
| pointerReference && |
| ignoreFilter); |
| |
| if (!skipDependencies) { |
| // Prevents infinite recursion if struct has a pointer to itself. |
| _stack.top.struc!.parsedDependencies = true; |
| _setStructMembers(cursor); |
| } else if (!_stack.top.struc!.parsedDependencies) { |
| _logger.fine('Skipped dependencies.'); |
| } |
| |
| if (updateName) { |
| // If struct is seen, update it's name. |
| _stack.top.struc!.name = config.structDecl.renameUsingConfig(structName); |
| } |
| } |
| |
| return _stack.pop().struc; |
| } |
| |
| void _setStructMembers(clang_types.CXCursor cursor) { |
| _stack.top.hasAttr = clang.clang_Cursor_hasAttrs(cursor) != 0; |
| _stack.top.allignment = cursor.type().alignment(); |
| |
| final resultCode = clang.clang_visitChildren( |
| cursor, |
| Pointer.fromFunction(_structMembersVisitor, |
| clang_types.CXChildVisitResult.CXChildVisit_Break), |
| 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) { |
| _logger.fine( |
| '---- Removed Struct members, reason: struct has array members ${cursor.completeStringRepr()}'); |
| _logger.warning( |
| 'Removed All Struct Members from: ${_stack.top.struc!.name}(${_stack.top.struc!.originalName}), Array members not supported'); |
| } else if (_stack.top.unimplementedMemberType) { |
| _logger.fine( |
| '---- Removed Struct members, reason: member with unimplementedtype ${cursor.completeStringRepr()}'); |
| _logger.warning( |
| 'Removed All Struct Members from ${_stack.top.struc!.name}(${_stack.top.struc!.originalName}), struct member has an unsupported type.'); |
| } else if (_stack.top.flexibleArrayMember) { |
| _logger.fine( |
| '---- Removed Struct members, reason: incomplete array member ${cursor.completeStringRepr()}'); |
| _logger.warning( |
| 'Removed All Struct Members from ${_stack.top.struc!.name}(${_stack.top.struc!.originalName}), Flexible array members not supported.'); |
| } else if (_stack.top.bitFieldMember) { |
| _logger.fine( |
| '---- Removed Struct members, reason: bitfield members ${cursor.completeStringRepr()}'); |
| _logger.warning( |
| 'Removed All Struct Members from ${_stack.top.struc!.name}(${_stack.top.struc!.originalName}), Bit Field members not supported.'); |
| } else if (_stack.top.dartHandleMember && config.useDartHandle) { |
| _logger.fine( |
| '---- Removed Struct members, reason: Dart_Handle member. ${cursor.completeStringRepr()}'); |
| _logger.warning( |
| 'Removed All Struct Members from ${_stack.top.struc!.name}(${_stack.top.struc!.originalName}), Dart_Handle member not supported.'); |
| } else if (_stack.top.incompleteStructMember) { |
| _logger.fine( |
| '---- Removed Struct members, reason: Incomplete Nested Struct member. ${cursor.completeStringRepr()}'); |
| _logger.warning( |
| 'Removed All Struct Members from ${_stack.top.struc!.name}(${_stack.top.struc!.originalName}), Incomplete Nested Struct member not supported.'); |
| } |
| |
| // Clear all struct members if struct is incomplete. |
| if (_stack.top.isInComplete) { |
| _stack.top.struc!.members.clear(); |
| } |
| |
| // C allow empty structs, but it's undefined behaviour at runtine. So we need |
| // to mark a struct incomplete if it has no members. |
| _stack.top.struc!.isInComplete = |
| _stack.top.isInComplete || _stack.top.struc!.members.isEmpty; |
| } |
| |
| /// Visitor for the struct cursor [CXCursorKind.CXCursor_StructDecl]. |
| /// |
| /// Child visitor invoked on struct cursor. |
| int _structMembersVisitor(clang_types.CXCursor cursor, |
| clang_types.CXCursor parent, Pointer<Void> clientData) { |
| 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; |
| } |
| |
| final mt = cursor.type().toCodeGenType(); |
| if (mt.broadType == BroadType.ConstantArray) { |
| _stack.top.arrayMember = true; |
| } |
| if (mt.broadType == BroadType.IncompleteArray) { |
| // TODO(68): Structs with flexible Array Members are not supported. |
| _stack.top.flexibleArrayMember = true; |
| } |
| if (clang.clang_getFieldDeclBitWidth(cursor) != -1) { |
| // TODO(84): Struct with bitfields are not suppoorted. |
| _stack.top.bitFieldMember = true; |
| } |
| if (mt.broadType == BroadType.Handle) { |
| _stack.top.dartHandleMember = true; |
| } |
| if (mt.isIncompleteStruct) { |
| _stack.top.incompleteStructMember = true; |
| } |
| if (mt.getBaseType().broadType == BroadType.Unimplemented) { |
| _stack.top.unimplementedMemberType = true; |
| } |
| |
| _stack.top.struc!.members.add( |
| Member( |
| dartDoc: getCursorDocComment( |
| cursor, |
| nesting.length + commentPrefix.length, |
| ), |
| originalName: cursor.spelling(), |
| name: config.structDecl.renameMemberUsingConfig( |
| _stack.top.struc!.originalName, |
| cursor.spelling(), |
| ), |
| type: mt, |
| ), |
| ); |
| } else if (cursor.kind == clang_types.CXCursorKind.CXCursor_PackedAttr) { |
| _stack.top.hasPackedAttr = true; |
| } |
| } catch (e, s) { |
| _logger.severe(e); |
| _logger.severe(s); |
| rethrow; |
| } |
| return clang_types.CXChildVisitResult.CXChildVisit_Continue; |
| } |