Support anonymous struct and union fields in interop

Fix #729
This commit is contained in:
Svyatoslav Scherbina
2017-08-14 16:24:26 +03:00
committed by SvyatoslavScherbina
parent c81ceed7c6
commit ce27695290
5 changed files with 78 additions and 46 deletions
@@ -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<Field>()
@@ -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<CXCursor>, _: CValue<CXCursor> ->
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<CXType>, containerType: CValue<CXType>) {
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<CXCursor>): 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<CXCursor>) =
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 -> {
@@ -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<Field>
}
@@ -137,17 +137,41 @@ internal typealias CursorVisitor = (cursor: CValue<CXCursor>, parent: CValue<CXC
internal fun visitChildren(parent: CValue<CXCursor>, 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<CXType>): List<CValue<CXCursor>> {
val result = mutableListOf<CValue<CXCursor>>()
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<CValue<CXCursor>>
result.add(cursor)
CXVisitorResult.CXVisit_Continue
}, clientData)
} finally {
resultPtr.dispose()
}
return result
}
internal fun CValue<CXCursor>.isLeaf(): Boolean {
var hasChildren = false
@@ -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
@@ -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