Skip to content

Commit

Permalink
Add DeclarationOrigin to native declarations
Browse files Browse the repository at this point in the history
This update introduces `DeclarationOrigin` information in various native declarations, such as `NativeFunction`, `NativeTypeAlias`, `NativeEnumeration`, and related classes. It provides a structure for noting where specific declarations originate from - be it from a platform-specific header, a library header or an unknown origin. This improves traceability, allows for more precise logging and debugging, and could enhance code generation based on origin information.
  • Loading branch information
Alexandre Mommers committed Feb 13, 2024
1 parent 4218797 commit 12cdc56
Show file tree
Hide file tree
Showing 19 changed files with 120 additions and 36 deletions.
4 changes: 4 additions & 0 deletions klang/klang/src/main/kotlin/klang/DeclarationRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ interface DeclarationRepository {

fun findObjectiveCCategoryByName(name: String) = findDeclarationByName<ObjectiveCCategory>(name)

fun findLibraryDeclaration() = declarations.asSequence()
.filterIsInstance<SourceableDeclaration>()
.filter { it.source is DeclarationOrigin.LibraryHeader }
.toList()
}

inline fun <reified T : NameableDeclaration> DeclarationRepository.findDeclarationByName(declarationName: String) = declarations
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package klang

import klang.domain.DeclarationOrigin
import klang.domain.NameableDeclaration
import klang.domain.ObjectiveCProtocol

object ObjectiveCRootClass: NameableDeclaration {
override val name: String = "NSObject"

override val source: DeclarationOrigin = DeclarationOrigin.PlatformHeader
}


Expand Down
32 changes: 31 additions & 1 deletion klang/klang/src/main/kotlin/klang/domain/NativeDeclaration.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,40 @@ sealed interface NativeDeclaration {
}
}

interface NameableDeclaration : NativeDeclaration {
/**
* This interface represents a nameable declaration.
*/
interface NameableDeclaration : SourceableDeclaration {
val name: String
}

interface ResolvableDeclaration {
fun DeclarationRepository.resolve()
}

/**
* Represents the origin of a native declaration.
*/
sealed interface DeclarationOrigin {

/**
* Represents an unknown origin of a native declaration.
*/
object UnknownOrigin : DeclarationOrigin

/**
* Represents a platform-specific header used for native declarations, like libc.
*/
object PlatformHeader : DeclarationOrigin

/**
* Represents a header file used for native declarations in a library.
*
* @property file The path to the header file.
*/
class LibraryHeader(val file: String) : DeclarationOrigin
}

interface SourceableDeclaration : NativeDeclaration {
val source: DeclarationOrigin
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ data class NativeEnumeration(
override val name: String,
var values: List<Pair<String, Long>> = emptyList(),
//TODO add support for other types
var type: TypeRef = typeOf("int").unchecked("Type 'int' not found")
var type: TypeRef = typeOf("int").unchecked("Type 'int' not found"),
override val source: DeclarationOrigin = DeclarationOrigin.UnknownOrigin
) : NameableDeclaration, ResolvableDeclaration {

override fun <T : NativeDeclaration> merge(other: T) {
Expand Down
3 changes: 2 additions & 1 deletion klang/klang/src/main/kotlin/klang/domain/NativeFunction.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import klang.DeclarationRepository
data class NativeFunction(
override val name: String,
var returnType: TypeRef,
val arguments: List<Argument>
val arguments: List<Argument>,
override val source: DeclarationOrigin = DeclarationOrigin.UnknownOrigin
): NameableDeclaration, NativeDeclaration, ResolvableDeclaration {

data class Argument(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ data class NativeStructure(
override val name: String,
var fields: List<StructureField> = listOf(),
var isUnion: Boolean = false,
override val source: DeclarationOrigin = DeclarationOrigin.UnknownOrigin,
) : NameableDeclaration, ResolvableDeclaration {
override fun <T : NativeDeclaration> merge(other: T) {
if (other is NativeStructure) {
Expand Down
3 changes: 2 additions & 1 deletion klang/klang/src/main/kotlin/klang/domain/NativeTypeAlias.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import klang.DeclarationRepository

data class NativeTypeAlias(
override val name: String,
var typeRef: TypeRef
var typeRef: TypeRef,
override val source: DeclarationOrigin = DeclarationOrigin.UnknownOrigin
) :NameableDeclaration, NativeDeclaration, ResolvableDeclaration {

override fun DeclarationRepository.resolve() {
Expand Down
6 changes: 5 additions & 1 deletion klang/klang/src/main/kotlin/klang/domain/NativeVariable.kt
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
package klang.domain

data class NativeVariable(override val name: String, val type: String): NameableDeclaration, NativeDeclaration
data class NativeVariable(
override val name: String,
val type: String,
override val source: DeclarationOrigin
): NameableDeclaration, NativeDeclaration
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ internal val AnonymousCategoryName = "AnonymousCategory"
data class ObjectiveCCategory(
override val name: String,
var superType: TypeRef,
val methods: List<ObjectiveCClass.Method>
val methods: List<ObjectiveCClass.Method>,
override val source: DeclarationOrigin = DeclarationOrigin.UnknownOrigin
) : NameableDeclaration, ResolvableDeclaration {

override fun DeclarationRepository.resolve() {
Expand Down
9 changes: 6 additions & 3 deletions klang/klang/src/main/kotlin/klang/domain/ObjectiveCClass.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ data class ObjectiveCClass(
var protocols: Set<TypeRef>,
var properties: List<Property>,
var methods: List<Method>,
var categories: Set<ObjectiveCCategory> = setOf()
var categories: Set<ObjectiveCCategory> = setOf(),
override val source: DeclarationOrigin = DeclarationOrigin.UnknownOrigin
) : NameableDeclaration, ResolvableDeclaration {

data class Property(
Expand All @@ -17,14 +18,16 @@ data class ObjectiveCClass(
val assign: Boolean? = null,
val readwrite: Boolean? = null,
val nonatomic: Boolean? = null,
val unsafe_unretained: Boolean? = null
val unsafe_unretained: Boolean? = null,
override val source: DeclarationOrigin= DeclarationOrigin.UnknownOrigin
) : NameableDeclaration

data class Method(
override val name: String,
var returnType: TypeRef,
val instance: Boolean,
val arguments: List<Argument> = listOf()
val arguments: List<Argument> = listOf(),
override val source: DeclarationOrigin = DeclarationOrigin.UnknownOrigin
) : NameableDeclaration, ResolvableDeclaration {
data class Argument(
val name: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ data class ObjectiveCProtocol(
override val name: String,
val protocols: Set<String>,
var properties: List<ObjectiveCClass.Property>,
var methods: List<ObjectiveCClass.Method>
var methods: List<ObjectiveCClass.Method>,
override val source: DeclarationOrigin = DeclarationOrigin.UnknownOrigin
) : NameableDeclaration
6 changes: 4 additions & 2 deletions klang/klang/src/main/kotlin/klang/domain/PrimitiveType.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ sealed class PrimitiveType: NameableDeclaration

data object VoidType: PrimitiveType() {
override val name: String = "void"
override val source: DeclarationOrigin = DeclarationOrigin.PlatformHeader
}

class FixeSizeType(val size: Int, override val name: String, val isFloating: Boolean = false): PrimitiveType()
class PlatformDependantSizeType(val size: IntRange, override val name: String): PrimitiveType()
class FixeSizeType(val size: Int, override val name: String, val isFloating: Boolean = false, override val source: DeclarationOrigin = DeclarationOrigin.PlatformHeader): PrimitiveType()
class PlatformDependantSizeType(val size: IntRange, override val name: String, override val source: DeclarationOrigin = DeclarationOrigin.PlatformHeader): PrimitiveType()

data object StringType: PrimitiveType() {
override val name: String = "char *"
override val source: DeclarationOrigin = DeclarationOrigin.PlatformHeader
}


Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package klang.parser.libclang

import klang.DeclarationRepository
import klang.InMemoryDeclarationRepository
import klang.domain.DeclarationOrigin
import klang.domain.NameableDeclaration
import klang.parse
import klang.parser.libclang.panama.OriginProcessor.toOrigin
import klang.parser.libclang.panama.toNativeEnumeration
import klang.parser.libclang.panama.toNativeStructure
import klang.parser.libclang.panama.toNativeTypeAlias
Expand Down Expand Up @@ -37,10 +39,11 @@ fun parseFileWithPanama(file: String, filePath: Path?, headerPaths: Array<Path>)
.asSequence()
.filter { it.declarationIsOnFilePath(filePath) }
.map {
val origin = it.pos().toOrigin(filePath)
when (it) {
is Scoped -> it.scopedToLocalDeclaration()
is Typedef -> it.typeDefToLocalDeclaration()
is Declaration.Function -> it.toNativeTypeAlias()
is Scoped -> it.scopedToLocalDeclaration(origin = origin)
is Typedef -> it.typeDefToLocalDeclaration(origin)
is Declaration.Function -> it.toNativeTypeAlias(origin)
else -> {
logger.error { "not found $it" }
null
Expand All @@ -56,18 +59,18 @@ internal fun Declaration.declarationIsOnFilePath(filePath: Path?): Boolean = fil
?.pathString
?.let { pos().path().parent.pathString.contains(it) } ?: true

private fun Typedef.typeDefToLocalDeclaration(): NameableDeclaration? = type().let { type ->
private fun Typedef.typeDefToLocalDeclaration(origin: DeclarationOrigin): NameableDeclaration? = type().let { type ->
when (type) {
is TypeImpl.DeclaredImpl -> type.tree().scopedToLocalDeclaration(name())
else -> toNativeTypeAlias()
is TypeImpl.DeclaredImpl -> type.tree().scopedToLocalDeclaration(name(), origin)
else -> toNativeTypeAlias(origin)
}
}

private fun Scoped.scopedToLocalDeclaration(name: String? = null): NameableDeclaration? {
private fun Scoped.scopedToLocalDeclaration(name: String? = null, origin: DeclarationOrigin): NameableDeclaration? {
return when (kind()) {
Declaration.Scoped.Kind.ENUM -> toNativeEnumeration(name)
Declaration.Scoped.Kind.STRUCT -> toNativeStructure(name)
Declaration.Scoped.Kind.UNION -> toNativeStructure(name, isUnion = true)
Declaration.Scoped.Kind.ENUM -> toNativeEnumeration(name, origin)
Declaration.Scoped.Kind.STRUCT -> toNativeStructure(name, origin = origin)
Declaration.Scoped.Kind.UNION -> toNativeStructure(name, isUnion = true, origin)

else -> {
logger.error { "not found ${kind()}" }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package klang.parser.libclang.panama

import klang.domain.DeclarationOrigin
import klang.domain.NativeEnumeration
import org.openjdk.jextract.Declaration

internal fun Declaration.Scoped.toNativeEnumeration(name: String?) = NativeEnumeration(
internal fun Declaration.Scoped.toNativeEnumeration(name: String?, origin: DeclarationOrigin) = NativeEnumeration(
name ?: name(),
members().toEnumValues()
members().toEnumValues(),
source = origin
)

private fun List<Declaration>.toEnumValues(): List<Pair<String, Long>> = filterIsInstance<Declaration.Constant>()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package klang.parser.libclang.panama

import klang.domain.DeclarationOrigin
import klang.domain.NameableDeclaration
import klang.domain.NativeFunction
import org.openjdk.jextract.Declaration
import org.openjdk.jextract.Declaration.Variable

internal fun Declaration.Function.toNativeTypeAlias(): NameableDeclaration = NativeFunction(
internal fun Declaration.Function.toNativeTypeAlias(origin: DeclarationOrigin): NameableDeclaration = NativeFunction(
name(),
returnType = type().toTypeRef(),
arguments = parameters().map { it.toArgument() }
arguments = parameters().map { it.toArgument() },
source = origin
)

private fun Variable.toArgument() = NativeFunction.Argument(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
package klang.parser.libclang.panama

import klang.domain.DeclarationOrigin
import klang.domain.NativeStructure
import klang.domain.StructureField
import klang.domain.TypeRefField
import org.openjdk.jextract.Declaration
import org.openjdk.jextract.impl.TypeImpl

internal fun Declaration.Scoped.toNativeStructure(name: String?, isUnion: Boolean = false) = Triple(
internal fun Declaration.Scoped.toNativeStructure(name: String?, isUnion: Boolean = false, origin: DeclarationOrigin) = Triple(
name ?: name(),
members().toStructureFields(),
isUnion
).let { (name, fields, isUnion) ->
NativeStructure(
name,
fields,
isUnion
isUnion,
origin
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package klang.parser.libclang.panama

import klang.domain.DeclarationOrigin
import klang.domain.NameableDeclaration
import klang.domain.NativeTypeAlias
import org.openjdk.jextract.Declaration

internal fun Declaration.Typedef.toNativeTypeAlias(): NameableDeclaration? = (name() to type().toTypeRef())
.let { (name, typeRef) -> NativeTypeAlias(name, typeRef) }
internal fun Declaration.Typedef.toNativeTypeAlias(origin: DeclarationOrigin): NameableDeclaration? = (name() to type().toTypeRef())
.let { (name, typeRef) -> NativeTypeAlias(name, typeRef, origin) }
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package klang.parser.libclang.panama

import klang.domain.DeclarationOrigin
import org.openjdk.jextract.Position
import java.nio.file.Path
import kotlin.io.path.absolutePathString

object OriginProcessor {

internal fun Position?.toOrigin(filePath: Path?): DeclarationOrigin = when {
filePath == null || this == null -> DeclarationOrigin.UnknownOrigin
else -> when {
isInFilePath(filePath) -> DeclarationOrigin.LibraryHeader(path().absolutePathString())
else -> DeclarationOrigin.PlatformHeader
}
}

private fun Position.isInFilePath(filePath: Path) =
path().absolutePathString().contains(filePath.absolutePathString())

}
13 changes: 8 additions & 5 deletions klang/klang/src/test/kotlin/klang/parser/libclang/SDL2ItTest.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package klang.parser.libclang

import klang.domain.NameableDeclaration
import klang.domain.DeclarationOrigin.LibraryHeader
import klang.domain.SourceableDeclaration
import klang.helper.HeaderManager
import klang.helper.HeaderManager.inferPlatformSuffix
import klang.helper.unzipFromClasspath
Expand All @@ -27,17 +28,19 @@ class SDL2ItTest : ParserTestCommon({
.also {
it.resolveTypes { resolvableDeclaration ->
when (resolvableDeclaration) {
is NameableDeclaration -> resolvableDeclaration.name.startsWith("_").not()
else -> true
is SourceableDeclaration -> (resolvableDeclaration.source is LibraryHeader)
else -> false
}
}
}


repository.findFunctionByName("SDL_Rect")
.let { println("SDL_Rect $it") }

// Then
repository.apply {
println(declarations.size)
val libraryDeclarations = findLibraryDeclaration()
println(libraryDeclarations.size)
}

}
Expand Down

0 comments on commit 12cdc56

Please sign in to comment.