diff --git a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Indexer.kt b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Indexer.kt index a74844a0e62..914ac1f3ca5 100644 --- a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Indexer.kt +++ b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Indexer.kt @@ -27,10 +27,10 @@ private class StructDeclImpl(spelling: String) : StructDecl(spelling) { private class StructDefImpl( size: Long, align: Int, decl: StructDecl, - hasNaturalLayout: Boolean, hasUnalignedFields: Boolean + hasNaturalLayout: Boolean ) : StructDef( size, align, decl, - hasNaturalLayout = hasNaturalLayout, hasUnalignedFields = hasUnalignedFields + hasNaturalLayout = hasNaturalLayout ) { override val fields = mutableListOf() @@ -140,20 +140,39 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() { val structDef = StructDefImpl( size, align, structDecl, - hasNaturalLayout = structHasNaturalLayout(cursor), - hasUnalignedFields = structHasUnalignedFields(cursor) + hasNaturalLayout = structHasNaturalLayout(cursor) ) structDecl.def = structDef - visitChildren(cursor) { childCursor: CValue, _: CValue -> - if (clang_getCursorKind(childCursor) == CXCursorKind.CXCursor_FieldDecl) { - val name = clang_getCursorSpelling(childCursor).convertAndDispose() - val fieldType = convertCursorType(childCursor) - val offset = clang_Cursor_getOffsetOfField(childCursor) - structDef.fields.add(Field(name, fieldType, offset)) + addDeclaredFields(structDef, type, type) + } + + private fun addDeclaredFields(structDef: StructDefImpl, structType: CValue, containerType: CValue) { + getFields(containerType).forEach { fieldCursor -> + val name = getCursorSpelling(fieldCursor) + if (name.isNotEmpty()) { + val fieldType = convertCursorType(fieldCursor) + val offset = clang_Type_getOffsetOf(structType, name) + if (clang_Cursor_isBitField(fieldCursor) == 0) { + val typeAlign = clang_Type_getAlignOf(clang_getCursorType(fieldCursor)) + structDef.fields.add(Field(name, fieldType, offset, typeAlign)) + } else { + // Ignore bit fields for now. + } + } else { + // Unnamed field. + val fieldType = clang_getCursorType(fieldCursor) + when (fieldType.kind) { + CXTypeKind.CXType_Record -> { + // Unnamed struct fields also contribute their fields: + addDeclaredFields(structDef, structType, fieldType) + } + else -> { + // Nothing. + } + } } - CXChildVisitResult.CXChildVisit_Continue } } @@ -341,27 +360,6 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() { } } - /** - * Computes [StructDef.hasUnalignedFields] property. - */ - fun structHasUnalignedFields(structDefCursor: CValue): Boolean { - var hasUnalignedFields = false - visitChildren(structDefCursor) { child, _ -> - if (clang_getCursorKind(child) == CXCursorKind.CXCursor_FieldDecl && - clang_Cursor_isBitField(child) == 0 && - clang_Cursor_getOffsetOfField(child) % - (clang_Type_getAlignOf(clang_getCursorType(child)) * 8) != 0L) { - - hasUnalignedFields = true - CXChildVisitResult.CXChildVisit_Break - } else { - CXChildVisitResult.CXChildVisit_Continue - } - } - - return hasUnalignedFields - } - private fun convertCursorType(cursor: CValue) = convertType(clang_getCursorType(cursor), clang_getDeclTypeAttributes(cursor)) @@ -507,7 +505,12 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() { when (kind) { CXIdxEntity_Struct, CXIdxEntity_Union -> { - getStructDeclAt(cursor) + if (entityName == null) { + // Skip anonymous struct. + // (It gets included anyway if used as a named field type). + } else { + getStructDeclAt(cursor) + } } CXIdxEntity_Typedef -> { diff --git a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/NativeIndex.kt b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/NativeIndex.kt index 056384fecd4..204416ce163 100644 --- a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/NativeIndex.kt +++ b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/NativeIndex.kt @@ -50,7 +50,10 @@ abstract class NativeIndex { /** * C struct field. */ -class Field(val name: String, val type: Type, val offset: Long) +class Field(val name: String, val type: Type, val offset: Long, val typeAlign: Long) + +val Field.isAligned: Boolean + get() = offset % (typeAlign * 8) == 0L /** * C struct declaration. @@ -68,8 +71,7 @@ abstract class StructDecl(val spelling: String) { */ abstract class StructDef(val size: Long, val align: Int, val decl: StructDecl, - val hasNaturalLayout: Boolean, - val hasUnalignedFields: Boolean) { + val hasNaturalLayout: Boolean) { abstract val fields: List } diff --git a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Utils.kt b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Utils.kt index 6852c83864c..8c6ba62f60b 100644 --- a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Utils.kt +++ b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Utils.kt @@ -137,17 +137,41 @@ internal typealias CursorVisitor = (cursor: CValue, parent: CValue, visitor: CursorVisitor) { val visitorPtr = StableObjPtr.create(visitor) - val clientData = visitorPtr.value - clang_visitChildren(parent, staticCFunction { cursor, parent, clientData -> - @Suppress("NAME_SHADOWING", "UNCHECKED_CAST") - val visitor = StableObjPtr.fromValue(clientData!!).get() as CursorVisitor - visitor(cursor, parent) - }, clientData) + try { + val clientData = visitorPtr.value + clang_visitChildren(parent, staticCFunction { cursor, parent, clientData -> + @Suppress("NAME_SHADOWING", "UNCHECKED_CAST") + val visitor = StableObjPtr.fromValue(clientData!!).get() as CursorVisitor + visitor(cursor, parent) + }, clientData) + } finally { + visitorPtr.dispose() + } } internal fun visitChildren(translationUnit: CXTranslationUnit, visitor: CursorVisitor) = visitChildren(clang_getTranslationUnitCursor(translationUnit), visitor) +internal fun getFields(type: CValue): List> { + val result = mutableListOf>() + val resultPtr = StableObjPtr.create(result) + try { + val clientData = resultPtr.value + + @Suppress("NAME_SHADOWING", "UNCHECKED_CAST") + clang_Type_visitFields(type, staticCFunction { cursor, clientData -> + val result = StableObjPtr.fromValue(clientData!!).get() as MutableList> + result.add(cursor) + CXVisitorResult.CXVisit_Continue + }, clientData) + + } finally { + resultPtr.dispose() + } + + return result +} + internal fun CValue.isLeaf(): Boolean { var hasChildren = false diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/ObjCStubs.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/ObjCStubs.kt index 21aa13c0d0c..d555a2927de 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/ObjCStubs.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/ObjCStubs.kt @@ -213,8 +213,11 @@ private fun Type.isLargeOrUnaligned(): Boolean { private fun Type.hasUnalignedMembers(): Boolean = when (this) { is Typedef -> this.def.aliased.hasUnalignedMembers() is RecordType -> this.decl.def!!.let { def -> - def.hasUnalignedFields || // Check members of fields too: - def.fields.any { it.type.hasUnalignedMembers() } + def.fields.any { + !it.isAligned || + // Check members of fields too: + it.type.hasUnalignedMembers() + } } is ArrayType -> this.elemType.hasUnalignedMembers() else -> false diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/StubGenerator.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/StubGenerator.kt index 0db7e8fde1f..02797099ba6 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/StubGenerator.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/StubGenerator.kt @@ -267,9 +267,9 @@ class StubGenerator( out("companion object : Type(${def.size}, ${def.align})") // FIXME: align out("") for (field in def.fields) { - if (field.name.isEmpty()) continue - try { + assert(field.name.isNotEmpty()) + if (field.offset < 0) throw NotImplementedError(); assert(field.offset % 8 == 0L) val offset = field.offset / 8