Support interop modularity
* Add list of included headers into the manifest * Implement importing "foreign" type declarations * Implement forward declarations more natively in the compiler * Represent Objective-C categories as Kotlin extension methods and properties Also: * Do some refactoring in stub generation * Call bridges directly in stubs for Objective-C properties
This commit is contained in:
committed by
SvyatoslavScherbina
parent
82b2b76c09
commit
79455c5161
+151
-84
@@ -21,7 +21,7 @@ import clang.CXIdxEntityKind.*
|
||||
import clang.CXTypeKind.*
|
||||
import kotlinx.cinterop.*
|
||||
|
||||
private class StructDeclImpl(spelling: String) : StructDecl(spelling) {
|
||||
private class StructDeclImpl(spelling: String, override val location: Location) : StructDecl(spelling) {
|
||||
override var def: StructDefImpl? = null
|
||||
}
|
||||
|
||||
@@ -37,29 +37,43 @@ private class StructDefImpl(
|
||||
override val bitFields = mutableListOf<BitField>()
|
||||
}
|
||||
|
||||
private class EnumDefImpl(spelling: String, type: Type) : EnumDef(spelling, type) {
|
||||
private class EnumDefImpl(spelling: String, type: Type, override val location: Location) : EnumDef(spelling, type) {
|
||||
override val constants = mutableListOf<EnumConstant>()
|
||||
}
|
||||
|
||||
private interface ObjCClassOrProtocolImpl {
|
||||
private interface ObjCContainerImpl {
|
||||
val protocols: MutableList<ObjCProtocol>
|
||||
val methods: MutableList<ObjCMethod>
|
||||
val properties: MutableList<ObjCProperty>
|
||||
}
|
||||
|
||||
private class ObjCProtocolImpl(name: String) : ObjCProtocol(name), ObjCClassOrProtocolImpl {
|
||||
private class ObjCProtocolImpl(
|
||||
name: String,
|
||||
override val location: Location
|
||||
) : ObjCProtocol(name), ObjCContainerImpl {
|
||||
override val protocols = mutableListOf<ObjCProtocol>()
|
||||
override val methods = mutableListOf<ObjCMethod>()
|
||||
override val properties = mutableListOf<ObjCProperty>()
|
||||
}
|
||||
|
||||
private class ObjCClassImpl(name: String) : ObjCClass(name), ObjCClassOrProtocolImpl {
|
||||
private class ObjCClassImpl(
|
||||
name: String,
|
||||
override val location: Location
|
||||
) : ObjCClass(name), ObjCContainerImpl {
|
||||
override val protocols = mutableListOf<ObjCProtocol>()
|
||||
override val methods = mutableListOf<ObjCMethod>()
|
||||
override val properties = mutableListOf<ObjCProperty>()
|
||||
override var baseClass: ObjCClass? = null
|
||||
}
|
||||
|
||||
private class ObjCCategoryImpl(
|
||||
name: String, clazz: ObjCClass
|
||||
) : ObjCCategory(name, clazz), ObjCContainerImpl {
|
||||
override val protocols = mutableListOf<ObjCProtocol>()
|
||||
override val methods = mutableListOf<ObjCMethod>()
|
||||
override val properties = mutableListOf<ObjCProperty>()
|
||||
}
|
||||
|
||||
internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() {
|
||||
|
||||
private sealed class DeclarationID {
|
||||
@@ -67,38 +81,86 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() {
|
||||
object VaList : DeclarationID()
|
||||
object VaListTag : DeclarationID()
|
||||
object BuiltinVaList : DeclarationID()
|
||||
object Protocol : DeclarationID()
|
||||
}
|
||||
|
||||
private val structById = mutableMapOf<DeclarationID, StructDeclImpl>()
|
||||
private inner class TypeDeclarationRegistry<D : TypeDeclaration> {
|
||||
private val all = mutableMapOf<DeclarationID, D>()
|
||||
|
||||
override val structs: List<StructDecl>
|
||||
get() = structById.values.toList()
|
||||
val included = mutableListOf<D>()
|
||||
|
||||
private val enumById = mutableMapOf<DeclarationID, EnumDefImpl>()
|
||||
inline fun getOrPut(cursor: CValue<CXCursor>, create: () -> D) = getOrPut(cursor, create, configure = {})
|
||||
|
||||
override val enums: List<EnumDef>
|
||||
get() = enumById.values.toList()
|
||||
inline fun getOrPut(cursor: CValue<CXCursor>, create: () -> D, configure: (D) -> Unit): D {
|
||||
val key = getDeclarationId(cursor)
|
||||
return all.getOrElse(key) {
|
||||
|
||||
private val objCClassesByName = mutableMapOf<String, ObjCClassImpl>()
|
||||
val value = create()
|
||||
all[key] = value
|
||||
|
||||
override val objCClasses: List<ObjCClass> get() = objCClassesByName.values.toList()
|
||||
val headerId = getHeaderId(getContainingFile(cursor))
|
||||
if (!library.headerInclusionPolicy.excludeAll(headerId)) {
|
||||
// This declaration is used, and thus should be included:
|
||||
included.add(value)
|
||||
}
|
||||
|
||||
private val objCProtocolsByName = mutableMapOf<String, ObjCProtocolImpl>()
|
||||
configure(value)
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
override val objCProtocols: List<ObjCProtocol> get() = objCProtocolsByName.values.toList()
|
||||
}
|
||||
|
||||
private val typedefById = mutableMapOf<DeclarationID, TypedefDef>()
|
||||
private val headerPathToId = mutableMapOf<String, HeaderId>()
|
||||
|
||||
override val typedefs: List<TypedefDef>
|
||||
get() = typedefById.values.toList()
|
||||
internal fun getHeaderId(file: CXFile?): HeaderId {
|
||||
if (file == null) {
|
||||
return HeaderId("builtins")
|
||||
}
|
||||
|
||||
val filePath = clang_getFileName(file).convertAndDispose()
|
||||
return headerPathToId.getOrPut(filePath) {
|
||||
val headerIdValue = headerContentsHash(filePath)
|
||||
HeaderId(headerIdValue)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getContainingFile(cursor: CValue<CXCursor>): CXFile? {
|
||||
return clang_getCursorLocation(cursor).getContainingFile()
|
||||
}
|
||||
|
||||
private fun getLocation(cursor: CValue<CXCursor>): Location {
|
||||
val headerId = getHeaderId(getContainingFile(cursor))
|
||||
return Location(headerId)
|
||||
}
|
||||
|
||||
override val structs: List<StructDecl> get() = structRegistry.included
|
||||
private val structRegistry = TypeDeclarationRegistry<StructDeclImpl>()
|
||||
|
||||
override val enums: List<EnumDef> get() = enumRegistry.included
|
||||
private val enumRegistry = TypeDeclarationRegistry<EnumDefImpl>()
|
||||
|
||||
override val objCClasses: List<ObjCClass> get() = objCClassRegistry.included
|
||||
private val objCClassRegistry = TypeDeclarationRegistry<ObjCClassImpl>()
|
||||
|
||||
override val objCProtocols: List<ObjCProtocol> get() = objCProtocolRegistry.included
|
||||
private val objCProtocolRegistry = TypeDeclarationRegistry<ObjCProtocolImpl>()
|
||||
|
||||
override val objCCategories: Collection<ObjCCategory> get() = objCCategoryById.values
|
||||
private val objCCategoryById = mutableMapOf<DeclarationID, ObjCCategoryImpl>()
|
||||
|
||||
override val typedefs get() = typedefRegistry.included
|
||||
private val typedefRegistry = TypeDeclarationRegistry<TypedefDef>()
|
||||
|
||||
private val functionById = mutableMapOf<DeclarationID, FunctionDecl>()
|
||||
|
||||
override val functions: List<FunctionDecl>
|
||||
get() = functionById.values.toList()
|
||||
override val functions: Collection<FunctionDecl>
|
||||
get() = functionById.values
|
||||
|
||||
override val macroConstants = mutableListOf<ConstantDef>()
|
||||
|
||||
override lateinit var includedHeaders: List<HeaderId>
|
||||
|
||||
private fun getDeclarationId(cursor: CValue<CXCursor>): DeclarationID {
|
||||
val usr = clang_getCursorUSR(cursor).convertAndDispose()
|
||||
if (usr == "") {
|
||||
@@ -108,32 +170,29 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() {
|
||||
CXCursorKind.CXCursor_StructDecl to "__va_list_tag" -> DeclarationID.VaListTag
|
||||
CXCursorKind.CXCursor_StructDecl to "__va_list" -> DeclarationID.VaList
|
||||
CXCursorKind.CXCursor_TypedefDecl to "__builtin_va_list" -> DeclarationID.BuiltinVaList
|
||||
else -> error(spelling)
|
||||
CXCursorKind.CXCursor_ObjCInterfaceDecl to "Protocol" -> DeclarationID.Protocol
|
||||
else -> error(kind to spelling)
|
||||
}
|
||||
}
|
||||
|
||||
return DeclarationID.USR(usr)
|
||||
}
|
||||
|
||||
private fun getStructDeclAt(cursor: CValue<CXCursor>): StructDeclImpl {
|
||||
val declId = getDeclarationId(cursor)
|
||||
|
||||
val structDecl = structById.getOrPut(declId) {
|
||||
val cursorType = clang_getCursorType(cursor)
|
||||
val typeSpelling = clang_getTypeSpelling(cursorType).convertAndDispose()
|
||||
|
||||
StructDeclImpl(typeSpelling)
|
||||
private fun getStructDeclAt(
|
||||
cursor: CValue<CXCursor>
|
||||
): StructDeclImpl = structRegistry.getOrPut(cursor, { createStructDecl(cursor) }) { decl ->
|
||||
val definitionCursor = clang_getCursorDefinition(cursor)
|
||||
if (clang_Cursor_isNull(definitionCursor) == 0) {
|
||||
assert(clang_isCursorDefinition(definitionCursor) != 0)
|
||||
createStructDef(decl, cursor)
|
||||
}
|
||||
}
|
||||
|
||||
if (structDecl.def == null) {
|
||||
val definitionCursor = clang_getCursorDefinition(cursor)
|
||||
if (clang_Cursor_isNull(definitionCursor) == 0) {
|
||||
assert(clang_isCursorDefinition(definitionCursor) != 0)
|
||||
createStructDef(structDecl, cursor)
|
||||
}
|
||||
}
|
||||
private fun createStructDecl(cursor: CValue<CXCursor>): StructDeclImpl {
|
||||
val cursorType = clang_getCursorType(cursor)
|
||||
val typeSpelling = clang_getTypeSpelling(cursorType).convertAndDispose()
|
||||
|
||||
return structDecl
|
||||
return StructDeclImpl(typeSpelling, getLocation(cursor))
|
||||
}
|
||||
|
||||
private fun createStructDef(structDecl: StructDeclImpl, cursor: CValue<CXCursor>) {
|
||||
@@ -185,15 +244,13 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() {
|
||||
TODO("support enum forward declarations")
|
||||
}
|
||||
|
||||
val declId = getDeclarationId(cursor)
|
||||
|
||||
return enumById.getOrPut(declId) {
|
||||
return enumRegistry.getOrPut(cursor) {
|
||||
val cursorType = clang_getCursorType(cursor)
|
||||
val typeSpelling = clang_getTypeSpelling(cursorType).convertAndDispose()
|
||||
|
||||
val baseType = convertType(clang_getEnumDeclIntegerType(cursor))
|
||||
|
||||
val enumDef = EnumDefImpl(typeSpelling, baseType)
|
||||
val enumDef = EnumDefImpl(typeSpelling, baseType, getLocation(cursor))
|
||||
|
||||
visitChildren(cursor) { childCursor, _ ->
|
||||
if (clang_getCursorKind(childCursor) == CXCursorKind.CXCursor_EnumConstantDecl) {
|
||||
@@ -233,13 +290,9 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() {
|
||||
|
||||
val name = clang_getCursorDisplayName(cursor).convertAndDispose()
|
||||
|
||||
objCClassesByName[name]?.let { return it }
|
||||
|
||||
val result = ObjCClassImpl(name)
|
||||
objCClassesByName[name] = result
|
||||
|
||||
addChildrenToClassOrProtocol(cursor, result)
|
||||
return result
|
||||
return objCClassRegistry.getOrPut(cursor, { ObjCClassImpl(name, getLocation(cursor)) }) {
|
||||
addChildrenToObjCContainer(cursor, it)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -253,16 +306,30 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() {
|
||||
|
||||
val name = clang_getCursorDisplayName(cursor).convertAndDispose()
|
||||
|
||||
objCProtocolsByName[name]?.let { return it }
|
||||
|
||||
val result = ObjCProtocolImpl(name)
|
||||
objCProtocolsByName[name] = result
|
||||
|
||||
addChildrenToClassOrProtocol(cursor, result)
|
||||
return result
|
||||
return objCProtocolRegistry.getOrPut(cursor, { ObjCProtocolImpl(name, getLocation(cursor)) }) {
|
||||
addChildrenToObjCContainer(cursor, it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addChildrenToClassOrProtocol(cursor: CValue<CXCursor>, result: ObjCClassOrProtocolImpl) {
|
||||
private fun getObjCCategoryAt(cursor: CValue<CXCursor>): ObjCCategoryImpl? {
|
||||
assert(cursor.kind == CXCursorKind.CXCursor_ObjCCategoryDecl) { cursor.kind }
|
||||
|
||||
val classCursor = getObjCCategoryClassCursor(cursor)
|
||||
if (!isAvailable(classCursor)) return null
|
||||
|
||||
val name = clang_getCursorDisplayName(cursor).convertAndDispose()
|
||||
val declarationId = getDeclarationId(cursor)
|
||||
|
||||
return objCCategoryById.getOrPut(declarationId) {
|
||||
val clazz = getObjCClassAt(classCursor)
|
||||
val category = ObjCCategoryImpl(name, clazz)
|
||||
addChildrenToObjCContainer(cursor, category)
|
||||
category
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun addChildrenToObjCContainer(cursor: CValue<CXCursor>, result: ObjCContainerImpl) {
|
||||
visitChildren(cursor) { child, _ ->
|
||||
when (child.kind) {
|
||||
CXCursorKind.CXCursor_ObjCSuperClassRef -> {
|
||||
@@ -332,10 +399,9 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() {
|
||||
return underlying
|
||||
}
|
||||
|
||||
val declId = getDeclarationId(declCursor)
|
||||
val typedefDef = typedefById.getOrPut(declId) {
|
||||
val typedefDef = typedefRegistry.getOrPut(declCursor) {
|
||||
|
||||
TypedefDef(underlying, name)
|
||||
TypedefDef(underlying, name, getLocation(declCursor))
|
||||
}
|
||||
|
||||
return Typedef(typedefDef)
|
||||
@@ -570,7 +636,9 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() {
|
||||
|
||||
CXIdxEntity_Function -> {
|
||||
if (isSuitableFunction(cursor)) {
|
||||
functionById[getDeclarationId(cursor)] = getFunction(cursor)
|
||||
functionById.getOrPut(getDeclarationId(cursor)) {
|
||||
getFunction(cursor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -579,21 +647,23 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() {
|
||||
}
|
||||
|
||||
CXIdxEntity_ObjCClass -> {
|
||||
if (isAvailable(cursor)) {
|
||||
getObjCClassAt(clang_getCursorReferenced(cursor))
|
||||
if (isAvailable(cursor) &&
|
||||
cursor.kind != CXCursorKind.CXCursor_ObjCClassRef /* not a forward declaration */) {
|
||||
|
||||
getObjCClassAt(cursor)
|
||||
}
|
||||
}
|
||||
|
||||
CXIdxEntity_ObjCCategory -> {
|
||||
val classCursor = getObjCCategoryClassCursor(cursor)
|
||||
if (isAvailable(classCursor)) {
|
||||
val objCClass = getObjCClassAt(classCursor)
|
||||
addChildrenToClassOrProtocol(cursor, objCClass)
|
||||
if (isAvailable(cursor)) {
|
||||
getObjCCategoryAt(cursor)
|
||||
}
|
||||
}
|
||||
|
||||
CXIdxEntity_ObjCProtocol -> {
|
||||
if (isAvailable(cursor)) {
|
||||
if (isAvailable(cursor) &&
|
||||
cursor.kind != CXCursorKind.CXCursor_ObjCProtocolRef /* not a forward declaration */) {
|
||||
|
||||
getObjCProtocolAt(cursor)
|
||||
}
|
||||
}
|
||||
@@ -609,23 +679,16 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() {
|
||||
|
||||
if (getter != null) {
|
||||
val property = ObjCProperty(entityName!!, getter, setter)
|
||||
val classOrProtocol: ObjCClassOrProtocolImpl? = when (container.kind) {
|
||||
CXCursorKind.CXCursor_ObjCCategoryDecl -> {
|
||||
val classCursor = getObjCCategoryClassCursor(container)
|
||||
if (isAvailable(classCursor)) {
|
||||
getObjCClassAt(classCursor)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
val objCContainer: ObjCContainerImpl? = when (container.kind) {
|
||||
CXCursorKind.CXCursor_ObjCCategoryDecl -> getObjCCategoryAt(container)
|
||||
CXCursorKind.CXCursor_ObjCInterfaceDecl -> getObjCClassAt(container)
|
||||
CXCursorKind.CXCursor_ObjCProtocolDecl -> getObjCProtocolAt(container)!!
|
||||
else -> error(container.kind)
|
||||
}
|
||||
|
||||
if (classOrProtocol != null) {
|
||||
classOrProtocol.properties.removeAll { property.replaces(it) }
|
||||
classOrProtocol.properties.add(property)
|
||||
if (objCContainer != null) {
|
||||
objCContainer.properties.removeAll { property.replaces(it) }
|
||||
objCContainer.properties.add(property)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -727,19 +790,23 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() {
|
||||
|
||||
fun buildNativeIndexImpl(library: NativeLibrary): NativeIndex {
|
||||
val result = NativeIndexImpl(library)
|
||||
indexDeclarations(library, result)
|
||||
findMacroConstants(library, result)
|
||||
indexDeclarations(result)
|
||||
findMacroConstants(result)
|
||||
return result
|
||||
}
|
||||
|
||||
private fun indexDeclarations(library: NativeLibrary, nativeIndex: NativeIndexImpl) {
|
||||
private fun indexDeclarations(nativeIndex: NativeIndexImpl) {
|
||||
val index = clang_createIndex(0, 0)!!
|
||||
try {
|
||||
val translationUnit = library.parse(index, options = CXTranslationUnit_DetailedPreprocessingRecord)
|
||||
val translationUnit = nativeIndex.library.parse(index, options = CXTranslationUnit_DetailedPreprocessingRecord)
|
||||
try {
|
||||
translationUnit.ensureNoCompileErrors()
|
||||
|
||||
val headers = getFilteredHeaders(library, index, translationUnit)
|
||||
val headers = getFilteredHeaders(nativeIndex, index, translationUnit)
|
||||
|
||||
nativeIndex.includedHeaders = headers.map {
|
||||
nativeIndex.getHeaderId(it)
|
||||
}
|
||||
|
||||
indexTranslationUnit(index, translationUnit, 0, object : Indexer {
|
||||
override fun indexDeclaration(info: CXIdxDeclInfo) {
|
||||
|
||||
+8
-7
@@ -23,11 +23,11 @@ import java.io.File
|
||||
/**
|
||||
* Finds all "macro constants" and registers them as [NativeIndex.constants] in given index.
|
||||
*/
|
||||
internal fun findMacroConstants(library: NativeLibrary, nativeIndex: NativeIndexImpl) {
|
||||
val names = collectMacroConstantsNames(library)
|
||||
internal fun findMacroConstants(nativeIndex: NativeIndexImpl) {
|
||||
val names = collectMacroConstantsNames(nativeIndex)
|
||||
// TODO: apply user-defined filters.
|
||||
|
||||
val constants = expandMacroConstants(library, names, typeConverter = { nativeIndex.convertType(it) })
|
||||
val constants = expandMacroConstants(nativeIndex.library, names, typeConverter = { nativeIndex.convertType(it) })
|
||||
|
||||
nativeIndex.macroConstants.addAll(constants)
|
||||
}
|
||||
@@ -209,17 +209,17 @@ enum class VisitorState {
|
||||
EXPECT_END, INVALID
|
||||
}
|
||||
|
||||
private fun collectMacroConstantsNames(library: NativeLibrary): List<String> {
|
||||
private fun collectMacroConstantsNames(nativeIndex: NativeIndexImpl): List<String> {
|
||||
val result = mutableSetOf<String>()
|
||||
val index = clang_createIndex(excludeDeclarationsFromPCH = 0, displayDiagnostics = 0)!!
|
||||
try {
|
||||
// Include macros into AST:
|
||||
val options = CXTranslationUnit_DetailedPreprocessingRecord
|
||||
|
||||
val translationUnit = library.parse(index, options)
|
||||
val translationUnit = nativeIndex.library.parse(index, options)
|
||||
try {
|
||||
translationUnit.ensureNoCompileErrors()
|
||||
val headers = getFilteredHeaders(library, index, translationUnit)
|
||||
val headers = getFilteredHeaders(nativeIndex, index, translationUnit)
|
||||
|
||||
visitChildren(translationUnit) { cursor, _ ->
|
||||
val file = memScoped {
|
||||
@@ -229,7 +229,8 @@ private fun collectMacroConstantsNames(library: NativeLibrary): List<String> {
|
||||
}
|
||||
|
||||
if (cursor.kind == CXCursorKind.CXCursor_MacroDefinition &&
|
||||
library.includesDeclaration(cursor) &&
|
||||
nativeIndex.library.includesDeclaration(cursor) &&
|
||||
file != null && // Builtin macros mostly seem to be useless.
|
||||
file in headers &&
|
||||
canMacroBeConstant(cursor))
|
||||
{
|
||||
|
||||
+52
-13
@@ -21,13 +21,33 @@ enum class Language(val sourceFileExtension: String) {
|
||||
OBJECTIVE_C("m")
|
||||
}
|
||||
|
||||
interface HeaderInclusionPolicy {
|
||||
/**
|
||||
* Whether unused declarations from given header should be excluded.
|
||||
*
|
||||
* @param headerName header path relative to the appropriate include path element (e.g. `time.h` or `curl/curl.h`),
|
||||
* or `null` for builtin declarations.
|
||||
*/
|
||||
fun excludeUnused(headerName: String?): Boolean
|
||||
|
||||
/**
|
||||
* Whether all declarations from this header should be excluded.
|
||||
*
|
||||
* Note: the declarations from such headers can be actually present in the internal representation,
|
||||
* but not included into the root collections.
|
||||
*/
|
||||
fun excludeAll(headerId: HeaderId): Boolean
|
||||
|
||||
// TODO: these methods should probably be combined into the only one, but it would require some refactoring.
|
||||
}
|
||||
|
||||
data class NativeLibrary(val includes: List<String>,
|
||||
val additionalPreambleLines: List<String>,
|
||||
val compilerArgs: List<String>,
|
||||
val language: Language,
|
||||
val excludeSystemLibs: Boolean, // TODO: drop?
|
||||
val excludeDepdendentModules: Boolean,
|
||||
val headerFilter: (String) -> Boolean)
|
||||
val headerInclusionPolicy: HeaderInclusionPolicy)
|
||||
|
||||
/**
|
||||
* Retrieves the definitions from given C header file using given compiler arguments (e.g. defines).
|
||||
@@ -38,13 +58,28 @@ fun buildNativeIndex(library: NativeLibrary): NativeIndex = buildNativeIndexImpl
|
||||
* This class describes the IR of definitions from C header file(s).
|
||||
*/
|
||||
abstract class NativeIndex {
|
||||
abstract val structs: List<StructDecl>
|
||||
abstract val enums: List<EnumDef>
|
||||
abstract val objCClasses: List<ObjCClass>
|
||||
abstract val objCProtocols: List<ObjCProtocol>
|
||||
abstract val typedefs: List<TypedefDef>
|
||||
abstract val functions: List<FunctionDecl>
|
||||
abstract val macroConstants: List<ConstantDef>
|
||||
abstract val structs: Collection<StructDecl>
|
||||
abstract val enums: Collection<EnumDef>
|
||||
abstract val objCClasses: Collection<ObjCClass>
|
||||
abstract val objCProtocols: Collection<ObjCProtocol>
|
||||
abstract val objCCategories: Collection<ObjCCategory>
|
||||
abstract val typedefs: Collection<TypedefDef>
|
||||
abstract val functions: Collection<FunctionDecl>
|
||||
abstract val macroConstants: Collection<ConstantDef>
|
||||
abstract val includedHeaders: Collection<HeaderId>
|
||||
}
|
||||
|
||||
/**
|
||||
* The (contents-based) header id.
|
||||
* Its [value] remains valid across different runs of the indexer and the process,
|
||||
* and thus can be used to 'serialize' the id.
|
||||
*/
|
||||
data class HeaderId(val value: String)
|
||||
|
||||
data class Location(val headerId: HeaderId)
|
||||
|
||||
interface TypeDeclaration {
|
||||
val location: Location
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,7 +95,7 @@ class BitField(val name: String, val type: Type, val offset: Long, val size: Int
|
||||
/**
|
||||
* C struct declaration.
|
||||
*/
|
||||
abstract class StructDecl(val spelling: String) {
|
||||
abstract class StructDecl(val spelling: String) : TypeDeclaration {
|
||||
|
||||
abstract val def: StructDef?
|
||||
}
|
||||
@@ -88,17 +123,19 @@ class EnumConstant(val name: String, val value: Long, val isExplicitlyDefined: B
|
||||
/**
|
||||
* C enum definition.
|
||||
*/
|
||||
abstract class EnumDef(val spelling: String, val baseType: Type) {
|
||||
abstract class EnumDef(val spelling: String, val baseType: Type) : TypeDeclaration {
|
||||
|
||||
abstract val constants: List<EnumConstant>
|
||||
}
|
||||
|
||||
sealed class ObjCClassOrProtocol(val name: String) {
|
||||
sealed class ObjCContainer {
|
||||
abstract val protocols: List<ObjCProtocol>
|
||||
abstract val methods: List<ObjCMethod>
|
||||
abstract val properties: List<ObjCProperty>
|
||||
}
|
||||
|
||||
sealed class ObjCClassOrProtocol(val name: String) : ObjCContainer(), TypeDeclaration
|
||||
|
||||
data class ObjCMethod(
|
||||
val selector: String, val encoding: String, val parameters: List<Parameter>, private val returnType: Type,
|
||||
val isClass: Boolean, val nsConsumesSelf: Boolean, val nsReturnsRetained: Boolean,
|
||||
@@ -126,10 +163,12 @@ abstract class ObjCClass(name: String) : ObjCClassOrProtocol(name) {
|
||||
}
|
||||
abstract class ObjCProtocol(name: String) : ObjCClassOrProtocol(name)
|
||||
|
||||
abstract class ObjCCategory(val name: String, val clazz: ObjCClass) : ObjCContainer()
|
||||
|
||||
/**
|
||||
* C function parameter.
|
||||
*/
|
||||
class Parameter(val name: String?, val type: Type, val nsConsumed: Boolean)
|
||||
data class Parameter(val name: String?, val type: Type, val nsConsumed: Boolean)
|
||||
|
||||
/**
|
||||
* C function declaration.
|
||||
@@ -144,7 +183,7 @@ class FunctionDecl(val name: String, val parameters: List<Parameter>, val return
|
||||
* typedef $aliased $name;
|
||||
* ```
|
||||
*/
|
||||
class TypedefDef(val aliased: Type, val name: String)
|
||||
class TypedefDef(val aliased: Type, val name: String, override val location: Location) : TypeDeclaration
|
||||
|
||||
abstract class ConstantDef(val name: String, val type: Type)
|
||||
class IntegerConstantDef(name: String, type: Type, val value: Long) : ConstantDef(name, type)
|
||||
|
||||
+41
-10
@@ -21,6 +21,8 @@ import kotlinx.cinterop.*
|
||||
import java.io.Closeable
|
||||
import java.io.File
|
||||
import java.nio.file.Paths
|
||||
import java.security.DigestInputStream
|
||||
import java.security.MessageDigest
|
||||
|
||||
internal val CValue<CXType>.kind: CXTypeKind get() = this.useContents { kind }
|
||||
|
||||
@@ -437,8 +439,16 @@ internal class ModulesMap(
|
||||
}
|
||||
}
|
||||
|
||||
internal fun getFilteredHeaders(library: NativeLibrary, index: CXIndex, translationUnit: CXTranslationUnit): Set<CXFile> {
|
||||
val result = mutableSetOf<CXFile>()
|
||||
fun HeaderInclusionPolicy.includeAll(headerName: String?, headerId: HeaderId): Boolean =
|
||||
!this.excludeUnused(headerName) && !this.excludeAll(headerId)
|
||||
|
||||
internal fun getFilteredHeaders(
|
||||
nativeIndex: NativeIndexImpl,
|
||||
index: CXIndex,
|
||||
translationUnit: CXTranslationUnit
|
||||
): Set<CXFile?> {
|
||||
val library = nativeIndex.library
|
||||
val result = mutableSetOf<CXFile?>()
|
||||
val topLevelFiles = mutableListOf<CXFile>()
|
||||
var mainFile: CXFile? = null
|
||||
|
||||
@@ -465,11 +475,7 @@ internal fun getFilteredHeaders(library: NativeLibrary, index: CXIndex, translat
|
||||
name
|
||||
} else {
|
||||
// If it is included with `#include "$name"`, then `name` can also be the path relative to the includer.
|
||||
val includerFile = memScoped {
|
||||
val fileVar = alloc<CXFileVar>()
|
||||
clang_getFileLocation(includeLocation, fileVar.ptr, null, null, null)
|
||||
fileVar.value!!
|
||||
}
|
||||
val includerFile = includeLocation.getContainingFile()!!
|
||||
val includerName = headerToName[includerFile] ?: ""
|
||||
val includerPath = clang_getFileName(includerFile).convertAndDispose()
|
||||
|
||||
@@ -483,7 +489,7 @@ internal fun getFilteredHeaders(library: NativeLibrary, index: CXIndex, translat
|
||||
}
|
||||
|
||||
headerToName[file] = headerName
|
||||
if (library.headerFilter(headerName)) {
|
||||
if (library.headerInclusionPolicy.includeAll(headerName, nativeIndex.getHeaderId(file))) {
|
||||
result.add(file)
|
||||
}
|
||||
}
|
||||
@@ -493,15 +499,19 @@ internal fun getFilteredHeaders(library: NativeLibrary, index: CXIndex, translat
|
||||
ModulesMap(library, translationUnit).use { modulesMap ->
|
||||
val topLevelModules = topLevelFiles.map { modulesMap.getModule(it) }.toSet()
|
||||
result.removeAll {
|
||||
val module = modulesMap.getModule(it)
|
||||
val module = modulesMap.getModule(it!!)
|
||||
module !in topLevelModules
|
||||
}
|
||||
// Note: if some of the top-level headers don't belong to modules,
|
||||
// then all non-modular headers are included.
|
||||
}
|
||||
} else {
|
||||
if (library.headerInclusionPolicy.includeAll(headerName = null, headerId = nativeIndex.getHeaderId(null))) {
|
||||
// Builtins.
|
||||
result.add(null)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
result.add(mainFile!!)
|
||||
|
||||
return result
|
||||
@@ -512,3 +522,24 @@ fun ObjCMethod.replaces(other: ObjCMethod): Boolean =
|
||||
|
||||
fun ObjCProperty.replaces(other: ObjCProperty): Boolean =
|
||||
this.getter.replaces(other.getter)
|
||||
|
||||
fun File.sha256(): String {
|
||||
val digest = MessageDigest.getInstance("SHA-256")
|
||||
DigestInputStream(this.inputStream(), digest).use { dis ->
|
||||
val buffer = ByteArray(8192)
|
||||
// Read all bytes:
|
||||
while (dis.read(buffer, 0, buffer.size) != -1) {}
|
||||
}
|
||||
// Convert to hex:
|
||||
return digest.digest().joinToString("") {
|
||||
Integer.toHexString((it.toInt() and 0xff) + 0x100).substring(1)
|
||||
}
|
||||
}
|
||||
|
||||
fun headerContentsHash(filePath: String) = File(filePath).sha256()
|
||||
|
||||
internal fun CValue<CXSourceLocation>.getContainingFile(): CXFile? = memScoped {
|
||||
val fileVar = alloc<CXFileVar>()
|
||||
clang_getFileLocation(this@getContainingFile, fileVar.ptr, null, null, null)
|
||||
fileVar.value
|
||||
}
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.native.interop.gen
|
||||
|
||||
import org.jetbrains.kotlin.native.interop.indexer.HeaderId
|
||||
import org.jetbrains.kotlin.native.interop.indexer.HeaderInclusionPolicy
|
||||
import org.jetbrains.kotlin.native.interop.indexer.Location
|
||||
|
||||
interface Imports {
|
||||
fun getPackage(location: Location): String?
|
||||
}
|
||||
|
||||
class ImportsImpl(internal val headerIdToPackage: Map<HeaderId, String>) : Imports {
|
||||
|
||||
override fun getPackage(location: Location): String? =
|
||||
headerIdToPackage[location.headerId]
|
||||
|
||||
}
|
||||
|
||||
class HeadersInclusionPolicyImpl(
|
||||
private val nameGlobs: List<String>,
|
||||
private val importsImpl: ImportsImpl
|
||||
) : HeaderInclusionPolicy {
|
||||
|
||||
override fun excludeUnused(headerName: String?): Boolean {
|
||||
if (nameGlobs.isEmpty()) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (headerName == null) {
|
||||
// Builtins; included only if no globs are specified:
|
||||
return true
|
||||
}
|
||||
|
||||
return nameGlobs.all { !headerName.matchesToGlob(it) }
|
||||
}
|
||||
|
||||
override fun excludeAll(headerId: HeaderId): Boolean {
|
||||
return headerId in importsImpl.headerIdToPackage
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun String.matchesToGlob(glob: String): Boolean =
|
||||
java.nio.file.FileSystems.getDefault()
|
||||
.getPathMatcher("glob:$glob").matches(java.nio.file.Paths.get(this))
|
||||
+260
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.native.interop.gen
|
||||
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
interface KotlinScope {
|
||||
/**
|
||||
* @return the string to be used to reference the classifier in current scope.
|
||||
*/
|
||||
fun reference(classifier: Classifier): String
|
||||
|
||||
/**
|
||||
* @return the string to be used as a name in the declaration of the classifier in current scope.
|
||||
*/
|
||||
fun declare(classifier: Classifier): String
|
||||
}
|
||||
|
||||
data class Classifier(
|
||||
val pkg: String,
|
||||
val topLevelName: String,
|
||||
private val nestedNames: List<String> = emptyList()
|
||||
) {
|
||||
|
||||
companion object {
|
||||
fun topLevel(pkg: String, name: String): Classifier {
|
||||
assert(!name.contains('.'))
|
||||
assert(!name.contains('`'))
|
||||
return Classifier(pkg, name)
|
||||
}
|
||||
}
|
||||
|
||||
val isTopLevel: Boolean get() = this.nestedNames.isEmpty()
|
||||
|
||||
fun nested(name: String): Classifier {
|
||||
assert(!name.contains('.'))
|
||||
assert(!name.contains('`'))
|
||||
return this.copy(nestedNames = nestedNames + name)
|
||||
}
|
||||
|
||||
val relativeFqName: String get() = buildString {
|
||||
append(topLevelName.asSimpleName())
|
||||
nestedNames.forEach {
|
||||
append('.')
|
||||
append(it.asSimpleName())
|
||||
}
|
||||
}
|
||||
|
||||
val fqName: String get() = buildString {
|
||||
if (pkg.isNotEmpty()) {
|
||||
append(pkg)
|
||||
append('.')
|
||||
}
|
||||
append(relativeFqName)
|
||||
}
|
||||
}
|
||||
|
||||
val Classifier.type
|
||||
get() = KotlinClassifierType(this, arguments = emptyList(), nullable = false)
|
||||
|
||||
fun Classifier.typeWith(vararg arguments: KotlinType) =
|
||||
KotlinClassifierType(this, arguments.toList(), nullable = false)
|
||||
|
||||
interface KotlinType {
|
||||
/**
|
||||
* @return the string to be used in the given scope to denote this type.
|
||||
*/
|
||||
fun render(scope: KotlinScope): String
|
||||
}
|
||||
|
||||
data class KotlinClassifierType(
|
||||
val classifier: Classifier,
|
||||
val arguments: List<KotlinType>,
|
||||
val nullable: Boolean
|
||||
) : KotlinType {
|
||||
|
||||
override fun render(scope: KotlinScope): String = buildString {
|
||||
append(scope.reference(classifier))
|
||||
if (arguments.isNotEmpty()) {
|
||||
append('<')
|
||||
arguments.joinTo(this) { it.render(scope) }
|
||||
append('>')
|
||||
}
|
||||
if (nullable) {
|
||||
append('?')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun KotlinClassifierType.makeNullableAsSpecified(nullable: Boolean) = if (this.nullable == nullable) {
|
||||
this
|
||||
} else {
|
||||
this.copy(nullable = nullable)
|
||||
}
|
||||
|
||||
fun KotlinClassifierType.makeNullable() = this.makeNullableAsSpecified(true)
|
||||
|
||||
data class KotlinFunctionType(
|
||||
val parameterTypes: List<KotlinType>,
|
||||
val returnType: KotlinType
|
||||
) : KotlinType {
|
||||
|
||||
override fun render(scope: KotlinScope) = buildString {
|
||||
append('(')
|
||||
parameterTypes.joinTo(this) { it.render(scope) }
|
||||
append(") -> ")
|
||||
append(returnType.render(scope))
|
||||
}
|
||||
}
|
||||
|
||||
internal val cnamesStructsPackageName = "cnames.structs"
|
||||
|
||||
object KotlinTypes {
|
||||
val boolean by BuiltInType
|
||||
val byte by BuiltInType
|
||||
val short by BuiltInType
|
||||
val int by BuiltInType
|
||||
val long by BuiltInType
|
||||
val float by BuiltInType
|
||||
val double by BuiltInType
|
||||
val unit by BuiltInType
|
||||
val string by BuiltInType
|
||||
val any by BuiltInType
|
||||
|
||||
val nativePtr by InteropType
|
||||
|
||||
val cOpaque by InteropType
|
||||
val cOpaquePointer by InteropType
|
||||
val cOpaquePointerVar by InteropType
|
||||
|
||||
val booleanVarOf by InteropClassifier
|
||||
|
||||
val objCObject by InteropClassifier
|
||||
val objCObjectMeta by InteropClassifier
|
||||
val objCClass by InteropClassifier
|
||||
|
||||
val cValuesRef by InteropClassifier
|
||||
|
||||
val cPointer by InteropClassifier
|
||||
val cPointerVar by InteropClassifier
|
||||
val cArrayPointer by InteropClassifier
|
||||
val cArrayPointerVar by InteropClassifier
|
||||
val cPointerVarOf by InteropClassifier
|
||||
|
||||
val cFunction by InteropClassifier
|
||||
|
||||
val objCObjectVar by InteropClassifier
|
||||
val objCStringVarOf by InteropClassifier
|
||||
|
||||
val objCObjectBase by InteropClassifier
|
||||
val objCObjectBaseMeta by InteropClassifier
|
||||
|
||||
val cValue by InteropClassifier
|
||||
|
||||
private object BuiltInType {
|
||||
operator fun getValue(thisRef: KotlinTypes, property: KProperty<*>): KotlinClassifierType =
|
||||
Classifier.topLevel("kotlin", property.name.capitalize()).type
|
||||
}
|
||||
|
||||
private object InteropClassifier {
|
||||
operator fun getValue(thisRef: KotlinTypes, property: KProperty<*>): Classifier =
|
||||
Classifier.topLevel("kotlinx.cinterop", property.name.capitalize())
|
||||
}
|
||||
|
||||
private object InteropType {
|
||||
operator fun getValue(thisRef: KotlinTypes, property: KProperty<*>): KotlinClassifierType =
|
||||
InteropClassifier.getValue(thisRef, property).type
|
||||
}
|
||||
}
|
||||
|
||||
class KotlinFile(
|
||||
val pkg: String,
|
||||
namesToBeDeclared: List<String>
|
||||
) : KotlinScope {
|
||||
|
||||
// Note: all names are related to classifiers currently.
|
||||
|
||||
private val namesToBeDeclared: Set<String>
|
||||
|
||||
init {
|
||||
this.namesToBeDeclared = mutableSetOf()
|
||||
|
||||
namesToBeDeclared.forEach {
|
||||
if (it in this.namesToBeDeclared) {
|
||||
throw IllegalArgumentException("'$it' is going to be declared twice")
|
||||
} else {
|
||||
this.namesToBeDeclared.add(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val importedNameToPkg = mutableMapOf<String, String>()
|
||||
|
||||
override fun reference(classifier: Classifier): String = if (classifier.topLevelName in namesToBeDeclared) {
|
||||
if (classifier.pkg == this.pkg) {
|
||||
classifier.relativeFqName
|
||||
} else {
|
||||
// Don't import if would clash with own declaration:
|
||||
classifier.fqName
|
||||
}
|
||||
} else if (classifier.pkg == this.pkg) {
|
||||
throw IllegalArgumentException(
|
||||
"'${classifier.topLevelName}' from the file package was not reserved for declaration"
|
||||
)
|
||||
} else {
|
||||
val pkg = importedNameToPkg.getOrPut(classifier.topLevelName) { classifier.pkg }
|
||||
if (pkg == classifier.pkg) {
|
||||
// Is successfully imported:
|
||||
classifier.relativeFqName
|
||||
} else {
|
||||
classifier.fqName
|
||||
}
|
||||
}
|
||||
|
||||
private val alreadyDeclared = mutableSetOf<String>()
|
||||
|
||||
override fun declare(classifier: Classifier): String {
|
||||
if (classifier.pkg != this.pkg) {
|
||||
throw IllegalArgumentException("wrong package; expected '$pkg', got '${classifier.pkg}'")
|
||||
}
|
||||
|
||||
if (!classifier.isTopLevel) {
|
||||
throw IllegalArgumentException(
|
||||
"'${classifier.relativeFqName}' is not top-level thus can't be declared at file scope"
|
||||
)
|
||||
}
|
||||
|
||||
val topLevelName = classifier.topLevelName
|
||||
if (topLevelName in alreadyDeclared) {
|
||||
throw IllegalStateException("'$topLevelName' is already declared")
|
||||
}
|
||||
alreadyDeclared.add(topLevelName)
|
||||
|
||||
return topLevelName.asSimpleName()
|
||||
}
|
||||
|
||||
fun buildImports(): List<String> = importedNameToPkg.mapNotNull { (name, pkg) ->
|
||||
if (pkg == "kotlin" || pkg == "kotlinx.cinterop") {
|
||||
// Is already imported either by default or with '*':
|
||||
null
|
||||
} else {
|
||||
"import $pkg.${name.asSimpleName()}"
|
||||
}
|
||||
}.sorted()
|
||||
|
||||
}
|
||||
+7
-5
@@ -26,7 +26,8 @@ import org.jetbrains.kotlin.native.interop.indexer.VoidType
|
||||
*/
|
||||
class MappingBridgeGeneratorImpl(
|
||||
val declarationMapper: DeclarationMapper,
|
||||
val simpleBridgeGenerator: SimpleBridgeGenerator
|
||||
val simpleBridgeGenerator: SimpleBridgeGenerator,
|
||||
val kotlinScope: KotlinScope
|
||||
) : MappingBridgeGenerator {
|
||||
|
||||
override fun kotlinToNative(
|
||||
@@ -56,7 +57,7 @@ class MappingBridgeGeneratorImpl(
|
||||
is RecordType -> {
|
||||
val mirror = mirror(declarationMapper, returnType)
|
||||
val tmpVarName = kniRetVal
|
||||
builder.out("val $tmpVarName = nativeHeap.alloc<${mirror.pointedTypeName}>()")
|
||||
builder.out("val $tmpVarName = nativeHeap.alloc<${mirror.pointedType.render(kotlinScope)}>()")
|
||||
builder.pushBlock("try {", free = "finally { nativeHeap.free($tmpVarName) }")
|
||||
bridgeArguments.add(BridgeTypedKotlinValue(BridgedType.NATIVE_PTR, "$tmpVarName.rawPtr"))
|
||||
BridgedType.VOID
|
||||
@@ -106,7 +107,7 @@ class MappingBridgeGeneratorImpl(
|
||||
}
|
||||
else -> {
|
||||
val mirror = mirror(declarationMapper, returnType)
|
||||
mirror.info.argFromBridged(callExpr)
|
||||
mirror.info.argFromBridged(callExpr, kotlinScope)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,11 +159,12 @@ class MappingBridgeGeneratorImpl(
|
||||
nativeValues.forEachIndexed { index, (type, _) ->
|
||||
val mirror = mirror(declarationMapper, type)
|
||||
if (type.unwrapTypedefs() is RecordType) {
|
||||
val pointedTypeName = mirror.pointedType.render(kotlinScope)
|
||||
kotlinValues.add(
|
||||
"interpretPointed<${mirror.pointedTypeName}>(${bridgeKotlinValues[index]}).readValue()"
|
||||
"interpretPointed<$pointedTypeName>(${bridgeKotlinValues[index]}).readValue()"
|
||||
)
|
||||
} else {
|
||||
kotlinValues.add(mirror.info.argFromBridged(bridgeKotlinValues[index]))
|
||||
kotlinValues.add(mirror.info.argFromBridged(bridgeKotlinValues[index], kotlinScope))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+147
-99
@@ -19,29 +19,46 @@ package org.jetbrains.kotlin.native.interop.gen
|
||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||
|
||||
interface DeclarationMapper {
|
||||
fun getKotlinNameForPointed(structDecl: StructDecl): String
|
||||
fun getKotlinClassForPointed(structDecl: StructDecl): Classifier
|
||||
fun isMappedToStrict(enumDef: EnumDef): Boolean
|
||||
fun getKotlinNameForValue(enumDef: EnumDef): String
|
||||
fun getPackageFor(declaration: TypeDeclaration): String
|
||||
}
|
||||
|
||||
val PrimitiveType.kotlinType: String
|
||||
get() = when (this) {
|
||||
is CharType -> "kotlin.Byte"
|
||||
fun DeclarationMapper.getKotlinClassFor(
|
||||
objCClassOrProtocol: ObjCClassOrProtocol,
|
||||
isMeta: Boolean = false
|
||||
): Classifier {
|
||||
val pkg = if (objCClassOrProtocol.shouldBeImportedAsForwardDeclaration()) {
|
||||
when (objCClassOrProtocol) {
|
||||
is ObjCClass -> "objcnames.classes"
|
||||
is ObjCProtocol -> "objcnames.protocols"
|
||||
}
|
||||
} else {
|
||||
this.getPackageFor(objCClassOrProtocol)
|
||||
}
|
||||
val className = objCClassOrProtocol.kotlinClassName(isMeta)
|
||||
return Classifier.topLevel(pkg, className)
|
||||
}
|
||||
|
||||
is BoolType -> "kotlin.Boolean"
|
||||
val PrimitiveType.kotlinType: KotlinClassifierType
|
||||
get() = when (this) {
|
||||
is CharType -> KotlinTypes.byte
|
||||
|
||||
is BoolType -> KotlinTypes.boolean
|
||||
|
||||
// TODO: C primitive types should probably be generated as type aliases for Kotlin types.
|
||||
is IntegerType -> when (this.size) {
|
||||
1 -> "kotlin.Byte"
|
||||
2 -> "kotlin.Short"
|
||||
4 -> "kotlin.Int"
|
||||
8 -> "kotlin.Long"
|
||||
1 -> KotlinTypes.byte
|
||||
2 -> KotlinTypes.short
|
||||
4 -> KotlinTypes.int
|
||||
8 -> KotlinTypes.long
|
||||
else -> TODO(this.toString())
|
||||
}
|
||||
|
||||
is FloatingType -> when (this.size) {
|
||||
4 -> "kotlin.Float"
|
||||
8 -> "kotlin.Double"
|
||||
4 -> KotlinTypes.float
|
||||
8 -> KotlinTypes.double
|
||||
else -> TODO(this.toString())
|
||||
}
|
||||
|
||||
@@ -62,30 +79,31 @@ private val ObjCPointer.isNullable: Boolean
|
||||
/**
|
||||
* Describes the Kotlin types used to represent some C type.
|
||||
*/
|
||||
sealed class TypeMirror(val pointedTypeName: String, val info: TypeInfo) {
|
||||
sealed class TypeMirror(val pointedType: KotlinClassifierType, val info: TypeInfo) {
|
||||
/**
|
||||
* Type to be used in bindings for argument or return value.
|
||||
*/
|
||||
abstract val argType: String
|
||||
abstract val argType: KotlinType
|
||||
|
||||
/**
|
||||
* Mirror for C type to be represented in Kotlin as by-value type.
|
||||
*/
|
||||
class ByValue(pointedTypeName: String, info: TypeInfo, val valueTypeName: String) :
|
||||
TypeMirror(pointedTypeName, info) {
|
||||
class ByValue(pointedType: KotlinClassifierType, info: TypeInfo, val valueType: KotlinClassifierType) :
|
||||
TypeMirror(pointedType, info) {
|
||||
|
||||
override val argType: String
|
||||
get() = valueTypeName +
|
||||
if (info is TypeInfo.Pointer ||
|
||||
(info is TypeInfo.ObjCPointerInfo && info.type.isNullable)) "?" else ""
|
||||
override val argType: KotlinType
|
||||
get() = if ((info is TypeInfo.Pointer || (info is TypeInfo.ObjCPointerInfo && info.type.isNullable))) {
|
||||
valueType.makeNullable()
|
||||
} else {
|
||||
valueType
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mirror for C type to be represented in Kotlin as by-ref type.
|
||||
*/
|
||||
class ByRef(pointedTypeName: String, info: TypeInfo) : TypeMirror(pointedTypeName, info) {
|
||||
override val argType: String
|
||||
get() = "CValue<$pointedTypeName>"
|
||||
class ByRef(pointedType: KotlinClassifierType, info: TypeInfo) : TypeMirror(pointedType, info) {
|
||||
override val argType: KotlinType get() = KotlinTypes.cValue.typeWith(pointedType)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,109 +114,113 @@ sealed class TypeInfo {
|
||||
/**
|
||||
* The conversion from [TypeMirror.argType] to [bridgedType].
|
||||
*/
|
||||
abstract fun argToBridged(name: String): String
|
||||
abstract fun argToBridged(expr: KotlinExpression): KotlinExpression
|
||||
|
||||
/**
|
||||
* The conversion from [bridgedType] to [TypeMirror.argType].
|
||||
*/
|
||||
abstract fun argFromBridged(name: String): String
|
||||
abstract fun argFromBridged(expr: KotlinExpression, scope: KotlinScope): KotlinExpression
|
||||
|
||||
abstract val bridgedType: BridgedType
|
||||
|
||||
open fun cFromBridged(name: String): String = name
|
||||
open fun cFromBridged(expr: NativeExpression): NativeExpression = expr
|
||||
|
||||
open fun cToBridged(name: String): String = name
|
||||
open fun cToBridged(expr: NativeExpression): NativeExpression = expr
|
||||
|
||||
/**
|
||||
* If this info is for [TypeMirror.ByValue], then this method describes how to
|
||||
* construct pointed-type from value type.
|
||||
*/
|
||||
abstract fun constructPointedType(valueType: String): String
|
||||
abstract fun constructPointedType(valueType: KotlinType): KotlinClassifierType
|
||||
|
||||
class Primitive(override val bridgedType: BridgedType, val varTypeName: String) : TypeInfo() {
|
||||
class Primitive(override val bridgedType: BridgedType, val varClass: Classifier) : TypeInfo() {
|
||||
|
||||
override fun argToBridged(name: String) = name
|
||||
override fun argFromBridged(name: String) = name
|
||||
override fun argToBridged(expr: KotlinExpression) = expr
|
||||
override fun argFromBridged(expr: KotlinExpression, scope: KotlinScope) = expr
|
||||
|
||||
override fun constructPointedType(valueType: String) = "${varTypeName}Of<$valueType>"
|
||||
override fun constructPointedType(valueType: KotlinType) = varClass.typeWith(valueType)
|
||||
}
|
||||
|
||||
class Boolean : TypeInfo() {
|
||||
override fun argToBridged(name: String) = "$name.toByte()"
|
||||
override fun argToBridged(expr: KotlinExpression) = "$expr.toByte()"
|
||||
|
||||
override fun argFromBridged(name: String) = "$name.toBoolean()"
|
||||
override fun argFromBridged(expr: KotlinExpression, scope: KotlinScope) = "$expr.toBoolean()"
|
||||
|
||||
override val bridgedType: BridgedType get() = BridgedType.BYTE
|
||||
|
||||
override fun cFromBridged(name: String) = "($name) ? 1 : 0"
|
||||
override fun cFromBridged(expr: NativeExpression) = "($expr) ? 1 : 0"
|
||||
|
||||
override fun cToBridged(name: String) = "($name) ? 1 : 0"
|
||||
override fun cToBridged(expr: NativeExpression) = "($expr) ? 1 : 0"
|
||||
|
||||
override fun constructPointedType(valueType: String) = "BooleanVarOf<$valueType>"
|
||||
override fun constructPointedType(valueType: KotlinType) = KotlinTypes.booleanVarOf.typeWith(valueType)
|
||||
}
|
||||
|
||||
class Enum(val className: String, override val bridgedType: BridgedType) : TypeInfo() {
|
||||
override fun argToBridged(name: String) = "$name.value"
|
||||
class Enum(val clazz: Classifier, override val bridgedType: BridgedType) : TypeInfo() {
|
||||
override fun argToBridged(expr: KotlinExpression) = "$expr.value"
|
||||
|
||||
override fun argFromBridged(name: String) = "$className.byValue($name)"
|
||||
override fun argFromBridged(expr: KotlinExpression, scope: KotlinScope) =
|
||||
scope.reference(clazz) + ".byValue($expr)"
|
||||
|
||||
override fun constructPointedType(valueType: String) = "$className.Var" // TODO: improve
|
||||
override fun constructPointedType(valueType: KotlinType) =
|
||||
clazz.nested("Var").type // TODO: improve
|
||||
|
||||
}
|
||||
|
||||
class Pointer(val pointee: String) : TypeInfo() {
|
||||
override fun argToBridged(name: String) = "$name.rawValue"
|
||||
class Pointer(val pointee: KotlinType) : TypeInfo() {
|
||||
override fun argToBridged(expr: String) = "$expr.rawValue"
|
||||
|
||||
override fun argFromBridged(name: String) = "interpretCPointer<$pointee>($name)"
|
||||
override fun argFromBridged(expr: KotlinExpression, scope: KotlinScope) =
|
||||
"interpretCPointer<${pointee.render(scope)}>($expr)"
|
||||
|
||||
override val bridgedType: BridgedType
|
||||
get() = BridgedType.NATIVE_PTR
|
||||
|
||||
override fun cFromBridged(name: String) = "(void*)$name" // Note: required for JVM
|
||||
override fun cFromBridged(expr: String) = "(void*)$expr" // Note: required for JVM
|
||||
|
||||
override fun constructPointedType(valueType: String) = "CPointerVarOf<$valueType>"
|
||||
override fun constructPointedType(valueType: KotlinType) = KotlinTypes.cPointerVarOf.typeWith(valueType)
|
||||
}
|
||||
|
||||
class ObjCPointerInfo(val typeName: String, val type: ObjCPointer) : TypeInfo() {
|
||||
override fun argToBridged(name: String) = "$name.rawPtr"
|
||||
class ObjCPointerInfo(val kotlinType: KotlinType, val type: ObjCPointer) : TypeInfo() {
|
||||
override fun argToBridged(expr: String) = "$expr.rawPtr"
|
||||
|
||||
override fun argFromBridged(name: String) = "interpretObjCPointerOrNull<$typeName>($name)" +
|
||||
if (type.isNullable) "" else "!!"
|
||||
override fun argFromBridged(expr: KotlinExpression, scope: KotlinScope) =
|
||||
"interpretObjCPointerOrNull<${kotlinType.render(scope)}>($expr)" +
|
||||
if (type.isNullable) "" else "!!"
|
||||
|
||||
override val bridgedType: BridgedType
|
||||
get() = BridgedType.OBJC_POINTER
|
||||
|
||||
override fun constructPointedType(valueType: String) = "ObjCObjectVar<$valueType>"
|
||||
override fun constructPointedType(valueType: KotlinType) = KotlinTypes.objCObjectVar.typeWith(valueType)
|
||||
}
|
||||
|
||||
class NSString(val type: ObjCPointer) : TypeInfo() {
|
||||
override fun argToBridged(name: String) = "CreateNSStringFromKString($name)"
|
||||
override fun argToBridged(expr: String) = "CreateNSStringFromKString($expr)"
|
||||
|
||||
override fun argFromBridged(name: String) = "CreateKStringFromNSString($name)" +
|
||||
override fun argFromBridged(expr: KotlinExpression, scope: KotlinScope) = "CreateKStringFromNSString($expr)" +
|
||||
if (type.isNullable) "" else "!!"
|
||||
|
||||
override val bridgedType: BridgedType
|
||||
get() = BridgedType.OBJC_POINTER
|
||||
|
||||
override fun constructPointedType(valueType: String): String {
|
||||
return "ObjCStringVarOf<$valueType>"
|
||||
override fun constructPointedType(valueType: KotlinType): KotlinClassifierType {
|
||||
return KotlinTypes.objCStringVarOf.typeWith(valueType)
|
||||
}
|
||||
}
|
||||
|
||||
class ByRef(val pointed: String) : TypeInfo() {
|
||||
override fun argToBridged(name: String) = error(pointed)
|
||||
override fun argFromBridged(name: String) = error(pointed)
|
||||
class ByRef(val pointed: KotlinType) : TypeInfo() {
|
||||
override fun argToBridged(expr: String) = error(pointed)
|
||||
override fun argFromBridged(expr: KotlinExpression, scope: KotlinScope) = error(pointed)
|
||||
override val bridgedType: BridgedType get() = error(pointed)
|
||||
override fun cFromBridged(name: String) = error(pointed)
|
||||
override fun cToBridged(name: String) = error(pointed)
|
||||
override fun cFromBridged(expr: String) = error(pointed)
|
||||
override fun cToBridged(expr: String) = error(pointed)
|
||||
|
||||
// TODO: this method must not exist
|
||||
override fun constructPointedType(valueType: String): String = error(pointed)
|
||||
override fun constructPointedType(valueType: KotlinType): KotlinClassifierType = error(pointed)
|
||||
}
|
||||
}
|
||||
|
||||
fun mirrorPrimitiveType(type: PrimitiveType): TypeMirror.ByValue {
|
||||
val varTypeName = when (type) {
|
||||
val varClassName = when (type) {
|
||||
is CharType -> "ByteVar"
|
||||
is BoolType -> "BooleanVar"
|
||||
is IntegerType -> when (type.size) {
|
||||
@@ -216,37 +238,45 @@ fun mirrorPrimitiveType(type: PrimitiveType): TypeMirror.ByValue {
|
||||
else -> TODO(type.toString())
|
||||
}
|
||||
|
||||
val varClass = Classifier.topLevel("kotlinx.cinterop", varClassName)
|
||||
val varClassOf = Classifier.topLevel("kotlinx.cinterop", "${varClassName}Of")
|
||||
|
||||
val info = if (type == BoolType) {
|
||||
TypeInfo.Boolean()
|
||||
} else {
|
||||
TypeInfo.Primitive(type.bridgedType, varTypeName)
|
||||
TypeInfo.Primitive(type.bridgedType, varClassOf)
|
||||
}
|
||||
return TypeMirror.ByValue(varTypeName, info, type.kotlinType)
|
||||
return TypeMirror.ByValue(varClass.type, info, type.kotlinType)
|
||||
}
|
||||
|
||||
private fun byRefTypeMirror(pointedTypeName: String) : TypeMirror.ByRef {
|
||||
val info = TypeInfo.ByRef(pointedTypeName)
|
||||
return TypeMirror.ByRef(pointedTypeName, info)
|
||||
private fun byRefTypeMirror(pointedType: KotlinClassifierType) : TypeMirror.ByRef {
|
||||
val info = TypeInfo.ByRef(pointedType)
|
||||
return TypeMirror.ByRef(pointedType, info)
|
||||
}
|
||||
|
||||
fun mirror(declarationMapper: DeclarationMapper, type: Type): TypeMirror = when (type) {
|
||||
is PrimitiveType -> mirrorPrimitiveType(type)
|
||||
|
||||
is RecordType -> byRefTypeMirror(declarationMapper.getKotlinNameForPointed(type.decl).asSimpleName())
|
||||
is RecordType -> byRefTypeMirror(declarationMapper.getKotlinClassForPointed(type.decl).type)
|
||||
|
||||
is EnumType -> {
|
||||
val pkg = declarationMapper.getPackageFor(type.def)
|
||||
val kotlinName = declarationMapper.getKotlinNameForValue(type.def)
|
||||
|
||||
when {
|
||||
declarationMapper.isMappedToStrict(type.def) -> {
|
||||
val classSimpleName = kotlinName.asSimpleName()
|
||||
val bridgedType = (type.def.baseType.unwrapTypedefs() as PrimitiveType).bridgedType
|
||||
val info = TypeInfo.Enum(classSimpleName, bridgedType)
|
||||
TypeMirror.ByValue("$classSimpleName.Var", info, classSimpleName)
|
||||
val clazz = Classifier.topLevel(pkg, kotlinName)
|
||||
val info = TypeInfo.Enum(clazz, bridgedType)
|
||||
TypeMirror.ByValue(clazz.nested("Var").type, info, clazz.type)
|
||||
}
|
||||
!type.def.isAnonymous -> {
|
||||
val baseTypeMirror = mirror(declarationMapper, type.def.baseType)
|
||||
TypeMirror.ByValue("${kotlinName}Var", baseTypeMirror.info, kotlinName.asSimpleName())
|
||||
TypeMirror.ByValue(
|
||||
Classifier.topLevel(pkg, kotlinName + "Var").type,
|
||||
baseTypeMirror.info,
|
||||
Classifier.topLevel(pkg, kotlinName).type
|
||||
)
|
||||
}
|
||||
else -> mirror(declarationMapper, type.def.baseType)
|
||||
}
|
||||
@@ -256,15 +286,18 @@ fun mirror(declarationMapper: DeclarationMapper, type: Type): TypeMirror = when
|
||||
val pointeeType = type.pointeeType
|
||||
val unwrappedPointeeType = pointeeType.unwrapTypedefs()
|
||||
if (unwrappedPointeeType is VoidType) {
|
||||
val info = TypeInfo.Pointer("COpaque")
|
||||
TypeMirror.ByValue("COpaquePointerVar", info, "COpaquePointer")
|
||||
val info = TypeInfo.Pointer(KotlinTypes.cOpaque)
|
||||
TypeMirror.ByValue(KotlinTypes.cOpaquePointerVar, info, KotlinTypes.cOpaquePointer)
|
||||
} else if (unwrappedPointeeType is ArrayType) {
|
||||
mirror(declarationMapper, pointeeType)
|
||||
} else {
|
||||
val pointeeMirror = mirror(declarationMapper, pointeeType)
|
||||
val info = TypeInfo.Pointer(pointeeMirror.pointedTypeName)
|
||||
TypeMirror.ByValue("CPointerVar<${pointeeMirror.pointedTypeName}>", info,
|
||||
"CPointer<${pointeeMirror.pointedTypeName}>")
|
||||
val info = TypeInfo.Pointer(pointeeMirror.pointedType)
|
||||
TypeMirror.ByValue(
|
||||
KotlinTypes.cPointerVar.typeWith(pointeeMirror.pointedType),
|
||||
info,
|
||||
KotlinTypes.cPointer.typeWith(pointeeMirror.pointedType)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,61 +307,76 @@ fun mirror(declarationMapper: DeclarationMapper, type: Type): TypeMirror = when
|
||||
if (type.elemType.unwrapTypedefs() is ArrayType) {
|
||||
elemTypeMirror
|
||||
} else {
|
||||
val info = TypeInfo.Pointer(elemTypeMirror.pointedTypeName)
|
||||
TypeMirror.ByValue("CArrayPointerVar<${elemTypeMirror.pointedTypeName}>", info,
|
||||
"CArrayPointer<${elemTypeMirror.pointedTypeName}>")
|
||||
val info = TypeInfo.Pointer(elemTypeMirror.pointedType)
|
||||
TypeMirror.ByValue(
|
||||
KotlinTypes.cArrayPointerVar.typeWith(elemTypeMirror.pointedType),
|
||||
info,
|
||||
KotlinTypes.cArrayPointer.typeWith(elemTypeMirror.pointedType)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is FunctionType -> byRefTypeMirror("CFunction<${getKotlinFunctionType(declarationMapper, type)}>")
|
||||
is FunctionType -> byRefTypeMirror(KotlinTypes.cFunction.typeWith(getKotlinFunctionType(declarationMapper, type)))
|
||||
|
||||
is Typedef -> {
|
||||
val baseType = mirror(declarationMapper, type.def.aliased)
|
||||
val pkg = declarationMapper.getPackageFor(type.def)
|
||||
|
||||
val name = type.def.name
|
||||
when (baseType) {
|
||||
is TypeMirror.ByValue -> TypeMirror.ByValue("${name}Var", baseType.info, name.asSimpleName())
|
||||
is TypeMirror.ByRef -> TypeMirror.ByRef(name.asSimpleName(), baseType.info)
|
||||
is TypeMirror.ByValue -> TypeMirror.ByValue(
|
||||
Classifier.topLevel(pkg, "${name}Var").type,
|
||||
baseType.info,
|
||||
Classifier.topLevel(pkg, name).type
|
||||
)
|
||||
|
||||
is TypeMirror.ByRef -> TypeMirror.ByRef(Classifier.topLevel(pkg, name).type, baseType.info)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
is ObjCPointer -> objCPointerMirror(type)
|
||||
is ObjCPointer -> objCPointerMirror(declarationMapper, type)
|
||||
|
||||
else -> TODO(type.toString())
|
||||
}
|
||||
|
||||
private fun objCPointerMirror(type: ObjCPointer): TypeMirror.ByValue {
|
||||
private fun objCPointerMirror(declarationMapper: DeclarationMapper, type: ObjCPointer): TypeMirror.ByValue {
|
||||
if (type is ObjCObjectPointer && type.def.name == "NSString") {
|
||||
val info = TypeInfo.NSString(type)
|
||||
val valueType = if (type.isNullable) "String?" else "String"
|
||||
val valueType = KotlinTypes.string.makeNullableAsSpecified(type.isNullable)
|
||||
return TypeMirror.ByValue(info.constructPointedType(valueType), info, valueType)
|
||||
}
|
||||
|
||||
val typeName = when (type) {
|
||||
is ObjCIdType -> type.protocols.firstOrNull()?.kotlinName ?: "ObjCObject"
|
||||
is ObjCClassPointer -> "ObjCClass"
|
||||
is ObjCObjectPointer -> type.def.name
|
||||
val clazz = when (type) {
|
||||
is ObjCIdType -> type.protocols.firstOrNull()?.let { declarationMapper.getKotlinClassFor(it) }
|
||||
?: KotlinTypes.objCObject
|
||||
is ObjCClassPointer -> KotlinTypes.objCClass
|
||||
is ObjCObjectPointer -> declarationMapper.getKotlinClassFor(type.def)
|
||||
is ObjCInstanceType -> TODO(type.toString()) // Must have already been handled.
|
||||
}
|
||||
|
||||
return objCPointerMirror(typeName.asSimpleName(), type)
|
||||
return objCPointerMirror(clazz, type)
|
||||
}
|
||||
|
||||
private fun objCPointerMirror(typeName: String, type: ObjCPointer): TypeMirror.ByValue {
|
||||
val valueType = if (type.isNullable) "$typeName?" else typeName
|
||||
return TypeMirror.ByValue("ObjCObjectVar<$valueType>",
|
||||
TypeInfo.ObjCPointerInfo(typeName, type), typeName)
|
||||
private fun objCPointerMirror(clazz: Classifier, type: ObjCPointer): TypeMirror.ByValue {
|
||||
val kotlinType = clazz.type
|
||||
val pointedType = KotlinTypes.objCObjectVar.typeWith(kotlinType.makeNullableAsSpecified(type.isNullable))
|
||||
return TypeMirror.ByValue(
|
||||
pointedType,
|
||||
TypeInfo.ObjCPointerInfo(kotlinType, type),
|
||||
kotlinType
|
||||
)
|
||||
}
|
||||
|
||||
fun getKotlinFunctionType(declarationMapper: DeclarationMapper, type: FunctionType): String {
|
||||
fun getKotlinFunctionType(declarationMapper: DeclarationMapper, type: FunctionType): KotlinFunctionType {
|
||||
val returnType = if (type.returnType.unwrapTypedefs() is VoidType) {
|
||||
"Unit"
|
||||
KotlinTypes.unit
|
||||
} else {
|
||||
mirror(declarationMapper, type.returnType).argType
|
||||
}
|
||||
return "(" +
|
||||
type.parameterTypes.map { mirror(declarationMapper, it).argType }.joinToString(", ") +
|
||||
") -> " +
|
||||
return KotlinFunctionType(
|
||||
type.parameterTypes.map { mirror(declarationMapper, it).argType },
|
||||
returnType
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+164
-67
@@ -56,7 +56,7 @@ private fun ObjCMethod.getKotlinParameterNames(): List<String> {
|
||||
|
||||
class ObjCMethodStub(stubGenerator: StubGenerator,
|
||||
val method: ObjCMethod,
|
||||
private val container: ObjCClassOrProtocol) : KotlinStub, NativeBacked {
|
||||
private val container: ObjCContainer) : KotlinStub, NativeBacked {
|
||||
|
||||
override fun generate(context: StubGenerationContext): Sequence<String> =
|
||||
if (context.nativeBridges.isSupported(this)) {
|
||||
@@ -88,16 +88,14 @@ class ObjCMethodStub(stubGenerator: StubGenerator,
|
||||
private val joinedKotlinParameters: String
|
||||
private val header: String
|
||||
private val implementationTemplate: String
|
||||
private val bridgeName: String
|
||||
internal val bridgeName: String
|
||||
private val bridgeHeader: String
|
||||
|
||||
private val isOverride = method.isOverride(container)
|
||||
|
||||
init {
|
||||
val bodyGenerator = KotlinCodeBuilder()
|
||||
|
||||
val kotlinParameters = mutableListOf<Pair<String, String>>()
|
||||
val kotlinObjCBridgeParameters = mutableListOf<Pair<String, String>>()
|
||||
val kotlinParameters = mutableListOf<Pair<String, KotlinType>>()
|
||||
val kotlinObjCBridgeParameters = mutableListOf<Pair<String, KotlinType>>()
|
||||
val nativeBridgeArguments = mutableListOf<TypedKotlinValue>()
|
||||
|
||||
val kniReceiverParameter = "kniR"
|
||||
@@ -105,11 +103,11 @@ class ObjCMethodStub(stubGenerator: StubGenerator,
|
||||
|
||||
val voidPtr = PointerType(VoidType)
|
||||
|
||||
val returnType = method.getReturnType(container)
|
||||
val returnType = method.getReturnType(container.classOrProtocol)
|
||||
|
||||
val messengerGetter = if (returnType.isLargeOrUnaligned()) "getMessengerLU" else "getMessenger"
|
||||
|
||||
kotlinObjCBridgeParameters.add(kniSuperClassParameter to "NativePtr")
|
||||
kotlinObjCBridgeParameters.add(kniSuperClassParameter to KotlinTypes.nativePtr)
|
||||
nativeBridgeArguments.add(TypedKotlinValue(voidPtr, "$messengerGetter($kniSuperClassParameter)"))
|
||||
|
||||
if (method.nsConsumesSelf) {
|
||||
@@ -117,7 +115,7 @@ class ObjCMethodStub(stubGenerator: StubGenerator,
|
||||
bodyGenerator.out("objc_retain($kniReceiverParameter.rawPtr)")
|
||||
}
|
||||
|
||||
kotlinObjCBridgeParameters.add(kniReceiverParameter to "ObjCObject")
|
||||
kotlinObjCBridgeParameters.add(kniReceiverParameter to KotlinTypes.objCObject.type)
|
||||
nativeBridgeArguments.add(
|
||||
TypedKotlinValue(voidPtr,
|
||||
"getReceiverOrSuper($kniReceiverParameter.rawPtr, $kniSuperClassParameter)"))
|
||||
@@ -135,10 +133,10 @@ class ObjCMethodStub(stubGenerator: StubGenerator,
|
||||
}
|
||||
|
||||
val kotlinReturnType = if (returnType.unwrapTypedefs() is VoidType) {
|
||||
"Unit"
|
||||
KotlinTypes.unit
|
||||
} else {
|
||||
stubGenerator.mirror(returnType).argType
|
||||
}
|
||||
}.render(stubGenerator.kotlinFile)
|
||||
|
||||
val result = stubGenerator.mappingBridgeGenerator.kotlinToNative(
|
||||
bodyGenerator,
|
||||
@@ -172,36 +170,67 @@ class ObjCMethodStub(stubGenerator: StubGenerator,
|
||||
|
||||
bridgeName = "objcKniBridge${stubGenerator.nextUniqueId()}"
|
||||
|
||||
this.bridgeHeader = "internal fun $bridgeName(" +
|
||||
"${kotlinObjCBridgeParameters.joinToString { "${it.first.asSimpleName()}: ${it.second}" }})" +
|
||||
": $kotlinReturnType"
|
||||
this.bridgeHeader = buildString {
|
||||
append("internal fun ")
|
||||
append(bridgeName)
|
||||
append('(')
|
||||
kotlinObjCBridgeParameters.joinTo(this) {
|
||||
"${it.first.asSimpleName()}: ${it.second.render(stubGenerator.kotlinFile)}"
|
||||
}
|
||||
append("): ")
|
||||
append(kotlinReturnType)
|
||||
}
|
||||
|
||||
this.joinedKotlinParameters = kotlinParameters.joinToString { "${it.first.asSimpleName()}: ${it.second}" }
|
||||
this.joinedKotlinParameters = kotlinParameters.joinToString {
|
||||
"${it.first.asSimpleName()}: ${it.second.render(stubGenerator.kotlinFile)}"
|
||||
}
|
||||
|
||||
this.header = buildString {
|
||||
if (container is ObjCClass) append("external ")
|
||||
if (isOverride) {
|
||||
append("override ")
|
||||
} else if (container is ObjCClass) {
|
||||
append("open ")
|
||||
if (container !is ObjCProtocol) append("external ")
|
||||
val modality = when (container) {
|
||||
is ObjCClassOrProtocol -> if (method.isOverride(container)) {
|
||||
"override "
|
||||
} else when (container) {
|
||||
is ObjCClass -> "open "
|
||||
is ObjCProtocol -> ""
|
||||
}
|
||||
is ObjCCategory -> ""
|
||||
}
|
||||
append(modality)
|
||||
|
||||
append("fun ${method.kotlinName.asSimpleName()}($joinedKotlinParameters): $kotlinReturnType")
|
||||
append("fun ")
|
||||
if (container is ObjCCategory) {
|
||||
val receiverType = stubGenerator.declarationMapper
|
||||
.getKotlinClassFor(container.clazz, isMeta = method.isClass).type
|
||||
.render(stubGenerator.kotlinFile)
|
||||
|
||||
append(receiverType)
|
||||
append('.')
|
||||
}
|
||||
append("${method.kotlinName.asSimpleName()}($joinedKotlinParameters): $kotlinReturnType")
|
||||
|
||||
if (container is ObjCProtocol && method.isOptional) append(" = optional()")
|
||||
}
|
||||
}
|
||||
|
||||
private fun genImplementationTemplate(stubGenerator: StubGenerator): String {
|
||||
val codeBuilder = NativeCodeBuilder()
|
||||
private fun genImplementationTemplate(stubGenerator: StubGenerator): String = when (container) {
|
||||
is ObjCClassOrProtocol -> {
|
||||
val codeBuilder = NativeCodeBuilder()
|
||||
|
||||
val result = codeBuilder.genMethodImp(stubGenerator, this, method, container)
|
||||
stubGenerator.simpleBridgeGenerator.insertNativeBridge(this, emptyList(), codeBuilder.lines)
|
||||
|
||||
return result
|
||||
val result = codeBuilder.genMethodImp(stubGenerator, this, method, container)
|
||||
stubGenerator.simpleBridgeGenerator.insertNativeBridge(this, emptyList(), codeBuilder.lines)
|
||||
result
|
||||
}
|
||||
is ObjCCategory -> ""
|
||||
}
|
||||
}
|
||||
|
||||
private val ObjCContainer.classOrProtocol: ObjCClassOrProtocol
|
||||
get() = when (this) {
|
||||
is ObjCClassOrProtocol -> this
|
||||
is ObjCCategory -> this.clazz
|
||||
}
|
||||
|
||||
private fun Type.isLargeOrUnaligned(): Boolean {
|
||||
val unwrappedType = this.unwrapTypedefs()
|
||||
return when (unwrappedType) {
|
||||
@@ -263,6 +292,7 @@ abstract class ObjCContainerStub(stubGenerator: StubGenerator,
|
||||
private val isMeta: Boolean) : KotlinStub {
|
||||
|
||||
private val methods: List<ObjCMethod>
|
||||
private val properties: List<ObjCProperty>
|
||||
|
||||
init {
|
||||
val superMethods = container.inheritedMethods(isMeta)
|
||||
@@ -295,45 +325,47 @@ abstract class ObjCContainerStub(stubGenerator: StubGenerator,
|
||||
.mapNotNull { (_, inheritedMethods) -> if (inheritedMethods.size > 1) inheritedMethods.first() else null }
|
||||
|
||||
this.methods = methods.distinctBy { it.selector }.toList()
|
||||
}
|
||||
|
||||
private val methodStubs = methods.map {
|
||||
ObjCMethodStub(stubGenerator, it, container)
|
||||
}
|
||||
|
||||
private val properties: List<ObjCProperty>
|
||||
|
||||
init {
|
||||
val superProperties = container.superTypes.flatMap { it.properties.asSequence() }
|
||||
|
||||
this.properties = container.properties.filter {
|
||||
it.getter.isClass == isMeta &&
|
||||
this.properties = container.properties.filter { property ->
|
||||
property.getter.isClass == isMeta &&
|
||||
// Select only properties that don't override anything:
|
||||
superProperties.none(it::replaces)
|
||||
superMethods.none { property.getter.replaces(it) || property.setter?.replaces(it) ?: false }
|
||||
}
|
||||
}
|
||||
|
||||
private val methodToStub = methods.map {
|
||||
it to ObjCMethodStub(stubGenerator, it, container)
|
||||
}.toMap()
|
||||
|
||||
private val methodStubs get() = methodToStub.values
|
||||
|
||||
val propertyStubs = properties.map {
|
||||
ObjCPropertyStub(stubGenerator, it, container)
|
||||
createObjCPropertyStub(stubGenerator, it, container, this.methodToStub)
|
||||
}
|
||||
|
||||
private val classHeader: String
|
||||
|
||||
init {
|
||||
val nameSuffix = if (isMeta) "Meta" else ""
|
||||
|
||||
val supers = mutableListOf<String>()
|
||||
val supers = mutableListOf<KotlinType>()
|
||||
|
||||
if (container is ObjCClass) {
|
||||
supers.add(container.baseClassName)
|
||||
val baseClass = container.baseClass
|
||||
val baseClassifier = if (baseClass != null) {
|
||||
stubGenerator.declarationMapper.getKotlinClassFor(baseClass, isMeta)
|
||||
} else {
|
||||
if (isMeta) KotlinTypes.objCObjectBaseMeta else KotlinTypes.objCObjectBase
|
||||
}
|
||||
|
||||
supers.add(baseClassifier.type)
|
||||
}
|
||||
container.protocols.forEach {
|
||||
supers.add(it.kotlinName)
|
||||
supers.add(stubGenerator.declarationMapper.getKotlinClassFor(it, isMeta).type)
|
||||
}
|
||||
|
||||
if (supers.isEmpty()) {
|
||||
assert(container is ObjCProtocol)
|
||||
supers.add("ObjCObject")
|
||||
val classifier = if (isMeta) KotlinTypes.objCObjectMeta else KotlinTypes.objCObject
|
||||
supers.add(classifier.type)
|
||||
}
|
||||
|
||||
val keywords = when (container) {
|
||||
@@ -341,8 +373,9 @@ abstract class ObjCContainerStub(stubGenerator: StubGenerator,
|
||||
is ObjCProtocol -> "interface"
|
||||
}
|
||||
|
||||
val supersString = supers.joinToString { "$it$nameSuffix".asSimpleName() }
|
||||
val name = "${container.kotlinName}$nameSuffix".asSimpleName()
|
||||
val supersString = supers.joinToString { it.render(stubGenerator.kotlinFile) }
|
||||
val classifier = stubGenerator.declarationMapper.getKotlinClassFor(container, isMeta)
|
||||
val name = stubGenerator.kotlinFile.declare(classifier)
|
||||
this.classHeader = "@ExternalObjCClass $keywords $name : $supersString"
|
||||
}
|
||||
|
||||
@@ -382,32 +415,81 @@ open class ObjCClassOrProtocolStub(
|
||||
class ObjCProtocolStub(stubGenerator: StubGenerator, protocol: ObjCProtocol) :
|
||||
ObjCClassOrProtocolStub(stubGenerator, protocol)
|
||||
|
||||
class ObjCClassStub(stubGenerator: StubGenerator, private val clazz: ObjCClass) :
|
||||
class ObjCClassStub(private val stubGenerator: StubGenerator, private val clazz: ObjCClass) :
|
||||
ObjCClassOrProtocolStub(stubGenerator, clazz) {
|
||||
|
||||
override fun generateBody(context: StubGenerationContext) =
|
||||
sequenceOf( "companion object : ${clazz.kotlinName}Meta() {}") +
|
||||
super.generateBody(context)
|
||||
override fun generateBody(context: StubGenerationContext): Sequence<String> {
|
||||
val companionSuper = stubGenerator.declarationMapper
|
||||
.getKotlinClassFor(clazz, isMeta = true).type
|
||||
.render(stubGenerator.kotlinFile)
|
||||
return sequenceOf( "companion object : $companionSuper() {}") +
|
||||
super.generateBody(context)
|
||||
}
|
||||
}
|
||||
|
||||
class ObjCCategoryStub(
|
||||
private val stubGenerator: StubGenerator, private val category: ObjCCategory
|
||||
) : KotlinStub {
|
||||
|
||||
// TODO: consider removing members that are also present in the class or its supertypes.
|
||||
|
||||
private val methodToStub = category.methods.map {
|
||||
it to ObjCMethodStub(stubGenerator, it, category)
|
||||
}.toMap()
|
||||
|
||||
private val methodStubs get() = methodToStub.values
|
||||
|
||||
private val propertyStubs = category.properties.map {
|
||||
createObjCPropertyStub(stubGenerator, it, category, methodToStub)
|
||||
}
|
||||
|
||||
override fun generate(context: StubGenerationContext): Sequence<String> {
|
||||
val description = "${category.clazz.name} (${category.name})"
|
||||
return sequenceOf("// @interface $description") +
|
||||
propertyStubs.asSequence().flatMap { sequenceOf("") + it.generate(context) } +
|
||||
methodStubs.asSequence().flatMap { sequenceOf("") + it.generate(context) } +
|
||||
sequenceOf("// @end; // $description")
|
||||
}
|
||||
}
|
||||
|
||||
private fun createObjCPropertyStub(
|
||||
stubGenerator: StubGenerator,
|
||||
property: ObjCProperty,
|
||||
container: ObjCContainer,
|
||||
methodToStub: Map<ObjCMethod, ObjCMethodStub>
|
||||
): ObjCPropertyStub {
|
||||
// Note: the code below assumes that if the property is generated,
|
||||
// then its accessors are also generated as explicit methods.
|
||||
val getterStub = methodToStub[property.getter]!!
|
||||
val setterStub = property.setter?.let { methodToStub[it]!! }
|
||||
return ObjCPropertyStub(stubGenerator, property, container, getterStub, setterStub)
|
||||
}
|
||||
|
||||
class ObjCPropertyStub(
|
||||
val stubGenerator: StubGenerator, val property: ObjCProperty, val clazz: ObjCClassOrProtocol
|
||||
val stubGenerator: StubGenerator, val property: ObjCProperty, val container: ObjCContainer,
|
||||
val getterStub: ObjCMethodStub, val setterStub: ObjCMethodStub?
|
||||
) : KotlinStub {
|
||||
|
||||
override fun generate(context: StubGenerationContext): Sequence<String> {
|
||||
val type = property.getType(clazz)
|
||||
val type = property.getType(container.classOrProtocol)
|
||||
|
||||
val kotlinType = stubGenerator.mirror(type).argType
|
||||
val kotlinType = stubGenerator.mirror(type).argType.render(stubGenerator.kotlinFile)
|
||||
|
||||
val kind = if (property.setter == null) "val" else "var"
|
||||
val modifiers = if (clazz is ObjCClass) "" else "final "
|
||||
val modifiers = if (container is ObjCProtocol) "final " else ""
|
||||
val receiver = when (container) {
|
||||
is ObjCClassOrProtocol -> ""
|
||||
is ObjCCategory -> stubGenerator.declarationMapper
|
||||
.getKotlinClassFor(container.clazz, isMeta = property.getter.isClass).type
|
||||
.render(stubGenerator.kotlinFile) + "."
|
||||
}
|
||||
val result = mutableListOf(
|
||||
"$modifiers$kind ${property.name.asSimpleName()}: $kotlinType",
|
||||
" get() = ${property.getter.kotlinName.asSimpleName()}()"
|
||||
"$modifiers$kind $receiver${property.name.asSimpleName()}: $kotlinType",
|
||||
" get() = ${getterStub.bridgeName}(nativeNullPtr, this)"
|
||||
)
|
||||
|
||||
property.setter?.let {
|
||||
result.add(" set(value) = ${it.kotlinName.asSimpleName()}(value)")
|
||||
result.add(" set(value) = ${setterStub!!.bridgeName}(nativeNullPtr, this, value)")
|
||||
}
|
||||
|
||||
return result.asSequence()
|
||||
@@ -415,13 +497,25 @@ class ObjCPropertyStub(
|
||||
|
||||
}
|
||||
|
||||
val ObjCClassOrProtocol.kotlinName: String get() = when (this) {
|
||||
is ObjCClass -> this.name
|
||||
is ObjCProtocol -> "${this.name}Protocol"
|
||||
fun ObjCClassOrProtocol.kotlinClassName(isMeta: Boolean): String {
|
||||
val baseClassName = when (this) {
|
||||
is ObjCClass -> this.name
|
||||
is ObjCProtocol -> "${this.name}Protocol"
|
||||
}
|
||||
|
||||
return if (isMeta) "${baseClassName}Meta" else baseClassName
|
||||
}
|
||||
|
||||
private val ObjCClass.baseClassName: String
|
||||
get() = baseClass?.name ?: "ObjCObjectBase"
|
||||
fun ObjCClassOrProtocol.shouldBeImportedAsForwardDeclaration(): Boolean {
|
||||
// Note: currently it is not very easy to distinct class or protocol declarations from forward references,
|
||||
// so the code below treats the empty declarations as forward ones;
|
||||
// it is correct because empty and forward declarations feel exactly the same.
|
||||
|
||||
return this.protocols.isEmpty() &&
|
||||
this.methods.isEmpty() &&
|
||||
this.properties.isEmpty() &&
|
||||
(this as? ObjCClass)?.baseClass == null
|
||||
}
|
||||
|
||||
private fun Parameter.getTypeStringRepresentation() =
|
||||
(if (this.nsConsumed) "__attribute__((ns_consumed)) " else "") + type.getStringRepresentation()
|
||||
@@ -477,9 +571,12 @@ private fun NativeCodeBuilder.genMethodImp(
|
||||
bridgeArguments
|
||||
) { kotlinValues ->
|
||||
|
||||
val kotlinReceiverType = if (method.isClass) "${container.kotlinName}Meta" else container.kotlinName
|
||||
val kotlinReceiverType = stubGenerator.declarationMapper
|
||||
.getKotlinClassFor(container, isMeta = method.isClass)
|
||||
.type.render(stubGenerator.kotlinFile)
|
||||
|
||||
val kotlinRawReceiver = kotlinValues.first()
|
||||
val kotlinReceiver = "$kotlinRawReceiver.uncheckedCast<${kotlinReceiverType.asSimpleName()}>()"
|
||||
val kotlinReceiver = "$kotlinRawReceiver.uncheckedCast<$kotlinReceiverType>()"
|
||||
|
||||
val namedArguments = kotlinValues.drop(1).zip(method.getKotlinParameterNames()) { value, name ->
|
||||
"${name.asSimpleName()} = $value"
|
||||
|
||||
+10
-10
@@ -19,16 +19,16 @@ package org.jetbrains.kotlin.native.interop.gen
|
||||
/**
|
||||
* The type which has exact counterparts on both Kotlin and native side and can be directly passed through bridges.
|
||||
*/
|
||||
enum class BridgedType(val kotlinType: String, val convertor: String? = null) {
|
||||
BYTE("kotlin.Byte", "toByte"),
|
||||
SHORT("kotlin.Short", "toShort"),
|
||||
INT("kotlin.Int", "toInt"),
|
||||
LONG("kotlin.Long", "toLong"),
|
||||
FLOAT("kotlin.Float", "toFloat"),
|
||||
DOUBLE("kotlin.Double", "toDouble"),
|
||||
NATIVE_PTR("NativePtr"),
|
||||
OBJC_POINTER("NativePtr"),
|
||||
VOID("kotlin.Unit")
|
||||
enum class BridgedType(val kotlinType: KotlinClassifierType, val convertor: String? = null) {
|
||||
BYTE(KotlinTypes.byte, "toByte"),
|
||||
SHORT(KotlinTypes.short, "toShort"),
|
||||
INT(KotlinTypes.int, "toInt"),
|
||||
LONG(KotlinTypes.long, "toLong"),
|
||||
FLOAT(KotlinTypes.float, "toFloat"),
|
||||
DOUBLE(KotlinTypes.double, "toDouble"),
|
||||
NATIVE_PTR(KotlinTypes.nativePtr),
|
||||
OBJC_POINTER(KotlinTypes.nativePtr),
|
||||
VOID(KotlinTypes.unit)
|
||||
}
|
||||
|
||||
data class BridgeTypedKotlinValue(val type: BridgedType, val value: KotlinExpression)
|
||||
|
||||
+8
-5
@@ -25,7 +25,8 @@ class SimpleBridgeGeneratorImpl(
|
||||
private val platform: KotlinPlatform,
|
||||
private val pkgName: String,
|
||||
private val jvmFileClassName: String,
|
||||
private val libraryForCStubs: NativeLibrary
|
||||
private val libraryForCStubs: NativeLibrary,
|
||||
private val kotlinScope: KotlinScope
|
||||
) : SimpleBridgeGenerator {
|
||||
|
||||
private var nextUniqueId = 0
|
||||
@@ -69,7 +70,7 @@ class SimpleBridgeGeneratorImpl(
|
||||
|
||||
val kotlinFunctionName = "kniBridge${nextUniqueId++}"
|
||||
val kotlinParameters = kotlinValues.withIndex().joinToString {
|
||||
"p${it.index}: ${it.value.type.kotlinType}"
|
||||
"p${it.index}: ${it.value.type.kotlinType.render(kotlinScope)}"
|
||||
}
|
||||
|
||||
val callExpr = "$kotlinFunctionName(${kotlinValues.joinToString { it.value }})"
|
||||
@@ -130,7 +131,8 @@ class SimpleBridgeGeneratorImpl(
|
||||
}
|
||||
|
||||
nativeLines.add("}")
|
||||
kotlinLines.add("private external fun $kotlinFunctionName($kotlinParameters): ${returnType.kotlinType}")
|
||||
val kotlinReturnType = returnType.kotlinType.render(kotlinScope)
|
||||
kotlinLines.add("private external fun $kotlinFunctionName($kotlinParameters): $kotlinReturnType")
|
||||
|
||||
val nativeBridge = NativeBridge(kotlinLines, nativeLines)
|
||||
nativeBridges.add(nativeBacked to nativeBridge)
|
||||
@@ -154,7 +156,7 @@ class SimpleBridgeGeneratorImpl(
|
||||
val kotlinParameters = nativeValues.withIndex().map {
|
||||
"p${it.index}" to it.value.type.kotlinType
|
||||
}
|
||||
val joinedKotlinParameters = kotlinParameters.joinToString { "${it.first}: ${it.second}" }
|
||||
val joinedKotlinParameters = kotlinParameters.joinToString { "${it.first}: ${it.second.render(kotlinScope)}" }
|
||||
|
||||
val cFunctionParameters = nativeValues.withIndex().map {
|
||||
"p${it.index}" to it.value.type.nativeType
|
||||
@@ -167,7 +169,8 @@ class SimpleBridgeGeneratorImpl(
|
||||
val cFunctionHeader = "$cReturnType $symbolName($joinedCParameters)"
|
||||
|
||||
nativeLines.add("$cFunctionHeader;")
|
||||
kotlinLines.add("private fun $kotlinFunctionName($joinedKotlinParameters): ${returnType.kotlinType} {")
|
||||
val kotlinReturnType = returnType.kotlinType.render(kotlinScope)
|
||||
kotlinLines.add("private fun $kotlinFunctionName($joinedKotlinParameters): $kotlinReturnType {")
|
||||
|
||||
buildKotlinCodeLines {
|
||||
var kotlinExpr = block(kotlinParameters.map { (name, _) -> name })
|
||||
|
||||
+164
-49
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.native.interop.gen.jvm
|
||||
import org.jetbrains.kotlin.native.interop.gen.*
|
||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||
import java.lang.IllegalStateException
|
||||
import java.util.*
|
||||
|
||||
enum class KotlinPlatform {
|
||||
JVM,
|
||||
@@ -31,7 +32,9 @@ class StubGenerator(
|
||||
val libName: String,
|
||||
val dumpShims: Boolean,
|
||||
val verbose: Boolean = false,
|
||||
val platform: KotlinPlatform = KotlinPlatform.JVM) {
|
||||
val platform: KotlinPlatform = KotlinPlatform.JVM,
|
||||
val imports: Imports
|
||||
) {
|
||||
|
||||
private var theCounter = 0
|
||||
fun nextUniqueId() = theCounter++
|
||||
@@ -60,10 +63,8 @@ class StubGenerator(
|
||||
* The names that should not be used for struct classes to prevent name clashes
|
||||
*/
|
||||
val forbiddenStructNames = run {
|
||||
val functionNames = nativeIndex.functions.map { it.name }
|
||||
val fieldNames = nativeIndex.structs.mapNotNull { it.def }.flatMap { it.fields }.map { it.name }
|
||||
val typedefNames = nativeIndex.typedefs.map { it.name }
|
||||
(functionNames + fieldNames + typedefNames).toSet()
|
||||
typedefNames.toSet()
|
||||
}
|
||||
|
||||
val StructDecl.isAnonymous: Boolean
|
||||
@@ -89,6 +90,8 @@ class StubGenerator(
|
||||
spelling
|
||||
}
|
||||
|
||||
// TODO: don't mangle struct names because it wouldn't work if the struct
|
||||
// is imported into another interop library.
|
||||
return if (strippedCName !in forbiddenStructNames) strippedCName else (strippedCName + "Struct")
|
||||
}
|
||||
|
||||
@@ -129,11 +132,26 @@ class StubGenerator(
|
||||
}
|
||||
|
||||
val declarationMapper = object : DeclarationMapper {
|
||||
override fun getKotlinNameForPointed(structDecl: StructDecl): String = structDecl.kotlinName
|
||||
override fun getKotlinClassForPointed(structDecl: StructDecl): Classifier {
|
||||
val baseName = structDecl.kotlinName
|
||||
val pkg = when (platform) {
|
||||
KotlinPlatform.JVM -> pkgName
|
||||
KotlinPlatform.NATIVE -> if (structDecl.def == null) {
|
||||
cnamesStructsPackageName // to be imported as forward declaration.
|
||||
} else {
|
||||
getPackageFor(structDecl)
|
||||
}
|
||||
}
|
||||
return Classifier.topLevel(pkg, baseName)
|
||||
}
|
||||
|
||||
override fun isMappedToStrict(enumDef: EnumDef): Boolean = enumDef.isStrictEnum
|
||||
|
||||
override fun getKotlinNameForValue(enumDef: EnumDef): String = enumDef.kotlinName
|
||||
|
||||
override fun getPackageFor(declaration: TypeDeclaration): String {
|
||||
return imports.getPackage(declaration.location) ?: pkgName
|
||||
}
|
||||
}
|
||||
|
||||
fun mirror(type: Type): TypeMirror = mirror(declarationMapper, type)
|
||||
@@ -142,6 +160,63 @@ class StubGenerator(
|
||||
|
||||
private val macroConstantsByName = nativeIndex.macroConstants.associateBy { it.name }
|
||||
|
||||
val kotlinFile = KotlinFile(pkgName, namesToBeDeclared = computeNamesToBeDeclared())
|
||||
|
||||
private fun computeNamesToBeDeclared(): MutableList<String> {
|
||||
return mutableListOf<String>().apply {
|
||||
nativeIndex.typedefs.forEach {
|
||||
getTypeDeclaringNames(Typedef(it), this)
|
||||
}
|
||||
|
||||
nativeIndex.objCProtocols.forEach {
|
||||
add(it.kotlinClassName(isMeta = false))
|
||||
add(it.kotlinClassName(isMeta = true))
|
||||
}
|
||||
|
||||
nativeIndex.objCClasses.forEach {
|
||||
add(it.kotlinClassName(isMeta = false))
|
||||
add(it.kotlinClassName(isMeta = true))
|
||||
}
|
||||
|
||||
nativeIndex.structs.forEach {
|
||||
getTypeDeclaringNames(RecordType(it), this)
|
||||
}
|
||||
|
||||
nativeIndex.enums.forEach {
|
||||
if (!it.isAnonymous) {
|
||||
getTypeDeclaringNames(EnumType(it), this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all names to be declared for the given type declaration,
|
||||
* and adds them to [result].
|
||||
*
|
||||
* TODO: refactor to compute these names directly from declarations.
|
||||
*/
|
||||
private fun getTypeDeclaringNames(type: Type, result: MutableList<String>) {
|
||||
if (type.unwrapTypedefs() == VoidType) {
|
||||
return
|
||||
}
|
||||
|
||||
val mirror = mirror(type)
|
||||
val varClassifier = mirror.pointedType.classifier
|
||||
if (varClassifier.pkg == pkgName) {
|
||||
result.add(varClassifier.topLevelName)
|
||||
}
|
||||
when (mirror) {
|
||||
is TypeMirror.ByValue -> {
|
||||
val valueClassifier = mirror.valueType.classifier
|
||||
if (valueClassifier.pkg == pkgName && valueClassifier.topLevelName != varClassifier.topLevelName) {
|
||||
result.add(valueClassifier.topLevelName)
|
||||
}
|
||||
}
|
||||
is TypeMirror.ByRef -> {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The output currently used by the generator.
|
||||
* Should append line separator after any usage.
|
||||
@@ -262,7 +337,9 @@ class StubGenerator(
|
||||
}
|
||||
}
|
||||
|
||||
block("class ${decl.kotlinName.asSimpleName()}(rawPtr: NativePtr) : CStructVar(rawPtr)") {
|
||||
val kotlinName = kotlinFile.declare(declarationMapper.getKotlinClassForPointed(decl))
|
||||
|
||||
block("class $kotlinName(rawPtr: NativePtr) : CStructVar(rawPtr)") {
|
||||
out("")
|
||||
out("companion object : Type(${def.size}, ${def.align})") // FIXME: align
|
||||
out("")
|
||||
@@ -276,7 +353,7 @@ class StubGenerator(
|
||||
val fieldRefType = mirror(field.type)
|
||||
val unwrappedFieldType = field.type.unwrapTypedefs()
|
||||
if (unwrappedFieldType is ArrayType) {
|
||||
val type = (fieldRefType as TypeMirror.ByValue).valueTypeName
|
||||
val type = (fieldRefType as TypeMirror.ByValue).valueType.render(kotlinFile)
|
||||
|
||||
if (platform == KotlinPlatform.JVM) {
|
||||
val length = getArrayLength(unwrappedFieldType)
|
||||
@@ -288,13 +365,13 @@ class StubGenerator(
|
||||
out("val ${field.name.asSimpleName()}: $type")
|
||||
out(" get() = arrayMemberAt($offset)")
|
||||
} else {
|
||||
val pointedTypeName = fieldRefType.pointedType.render(kotlinFile)
|
||||
if (fieldRefType is TypeMirror.ByValue) {
|
||||
val pointedTypeName = fieldRefType.pointedTypeName
|
||||
out("var ${field.name.asSimpleName()}: ${fieldRefType.argType}")
|
||||
out("var ${field.name.asSimpleName()}: ${fieldRefType.argType.render(kotlinFile)}")
|
||||
out(" get() = memberAt<$pointedTypeName>($offset).value")
|
||||
out(" set(value) { memberAt<$pointedTypeName>($offset).value = value }")
|
||||
} else {
|
||||
out("val ${field.name.asSimpleName()}: ${fieldRefType.pointedTypeName}")
|
||||
out("val ${field.name.asSimpleName()}: $pointedTypeName")
|
||||
out(" get() = memberAt($offset)")
|
||||
}
|
||||
}
|
||||
@@ -308,7 +385,7 @@ class StubGenerator(
|
||||
for (field in def.bitFields) {
|
||||
val typeMirror = mirror(field.type)
|
||||
val typeInfo = typeMirror.info
|
||||
val kotlinType = typeMirror.argType
|
||||
val kotlinType = typeMirror.argType.render(kotlinFile)
|
||||
val rawType = typeInfo.bridgedType
|
||||
|
||||
out("var ${field.name.asSimpleName()}: $kotlinType")
|
||||
@@ -318,7 +395,7 @@ class StubGenerator(
|
||||
val readBitsExpr =
|
||||
"readBits(this.rawPtr, ${field.offset}, ${field.size}, $signed).${rawType.convertor!!}()"
|
||||
|
||||
out(" get() = ${typeInfo.argFromBridged(readBitsExpr)}")
|
||||
out(" get() = ${typeInfo.argFromBridged(readBitsExpr, kotlinFile)}")
|
||||
|
||||
val rawValue = typeInfo.argToBridged("value")
|
||||
val setExpr = "writeBits(this.rawPtr, ${field.offset}, ${field.size}, $rawValue.toLong())"
|
||||
@@ -339,8 +416,9 @@ class StubGenerator(
|
||||
/**
|
||||
* Produces to [out] the definition of Kotlin class representing the reference to given forward (incomplete) struct.
|
||||
*/
|
||||
private fun generateForwardStruct(s: StructDecl) {
|
||||
out("class ${s.kotlinName.asSimpleName()}(rawPtr: NativePtr) : COpaque(rawPtr)")
|
||||
private fun generateForwardStruct(s: StructDecl) = when (platform) {
|
||||
KotlinPlatform.JVM -> out("class ${s.kotlinName.asSimpleName()}(rawPtr: NativePtr) : COpaque(rawPtr)")
|
||||
KotlinPlatform.NATIVE -> {}
|
||||
}
|
||||
|
||||
private fun EnumConstant.isMoreCanonicalThan(other: EnumConstant): Boolean = with(other.name.toLowerCase()) {
|
||||
@@ -359,7 +437,7 @@ class StubGenerator(
|
||||
}
|
||||
|
||||
val baseTypeMirror = mirror(e.baseType)
|
||||
val baseKotlinType = baseTypeMirror.argType
|
||||
val baseKotlinType = baseTypeMirror.argType.render(kotlinFile)
|
||||
|
||||
val canonicalsByValue = e.constants
|
||||
.groupingBy { it.value }
|
||||
@@ -373,7 +451,9 @@ class StubGenerator(
|
||||
|
||||
val (canonicalConstants, aliasConstants) = e.constants.partition { canonicalsByValue[it.value] == it }
|
||||
|
||||
block("enum class ${e.kotlinName.asSimpleName()}(override val value: $baseKotlinType) : CEnum") {
|
||||
val clazz = (mirror(EnumType(e)) as TypeMirror.ByValue).valueType.classifier
|
||||
|
||||
block("enum class ${kotlinFile.declare(clazz)}(override val value: $baseKotlinType) : CEnum") {
|
||||
canonicalConstants.forEach {
|
||||
out("${it.name.asSimpleName()}(${it.value}),")
|
||||
}
|
||||
@@ -391,10 +471,11 @@ class StubGenerator(
|
||||
}
|
||||
out("")
|
||||
block("class Var(rawPtr: NativePtr) : CEnumVar(rawPtr)") {
|
||||
out("companion object : Type(${baseTypeMirror.pointedTypeName}.size.toInt())")
|
||||
val basePointedTypeName = baseTypeMirror.pointedType.render(kotlinFile)
|
||||
out("companion object : Type($basePointedTypeName.size.toInt())")
|
||||
out("var value: ${e.kotlinName.asSimpleName()}")
|
||||
out(" get() = byValue(this.reinterpret<${baseTypeMirror.pointedTypeName}>().value)")
|
||||
out(" set(value) { this.reinterpret<${baseTypeMirror.pointedTypeName}>().value = value.value }")
|
||||
out(" get() = byValue(this.reinterpret<$basePointedTypeName>().value)")
|
||||
out(" set(value) { this.reinterpret<$basePointedTypeName>().value = value.value }")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -415,7 +496,7 @@ class StubGenerator(
|
||||
|
||||
val typeName: String
|
||||
|
||||
val baseKotlinType = mirror(e.baseType).argType
|
||||
val baseKotlinType = mirror(e.baseType).argType.render(kotlinFile)
|
||||
if (e.isAnonymous) {
|
||||
if (constants.isNotEmpty()) {
|
||||
out("// ${e.spelling}:")
|
||||
@@ -429,15 +510,17 @@ class StubGenerator(
|
||||
}
|
||||
|
||||
// Generate as typedef:
|
||||
val varTypeName = typeMirror.info.constructPointedType(typeMirror.valueTypeName)
|
||||
out("typealias ${typeMirror.pointedTypeName} = $varTypeName")
|
||||
out("typealias ${typeMirror.valueTypeName} = $baseKotlinType")
|
||||
val varTypeName = typeMirror.info.constructPointedType(typeMirror.valueType).render(kotlinFile)
|
||||
val varTypeClassifier = typeMirror.pointedType.classifier
|
||||
val valueTypeClassifier = typeMirror.valueType.classifier
|
||||
out("typealias ${kotlinFile.declare(varTypeClassifier)} = $varTypeName")
|
||||
out("typealias ${kotlinFile.declare(valueTypeClassifier)} = $baseKotlinType")
|
||||
|
||||
if (constants.isNotEmpty()) {
|
||||
out("")
|
||||
}
|
||||
|
||||
typeName = typeMirror.valueTypeName
|
||||
typeName = typeMirror.valueType.render(kotlinFile)
|
||||
}
|
||||
|
||||
for (constant in constants) {
|
||||
@@ -450,15 +533,19 @@ class StubGenerator(
|
||||
val mirror = mirror(Typedef(def))
|
||||
val baseMirror = mirror(def.aliased)
|
||||
|
||||
val varType = mirror.pointedType
|
||||
when (baseMirror) {
|
||||
is TypeMirror.ByValue -> {
|
||||
val valueTypeName = (mirror as TypeMirror.ByValue).valueTypeName
|
||||
val varTypeAliasee = mirror.info.constructPointedType(valueTypeName)
|
||||
out("typealias ${mirror.pointedTypeName} = $varTypeAliasee")
|
||||
out("typealias $valueTypeName = ${baseMirror.valueTypeName}")
|
||||
val valueType = (mirror as TypeMirror.ByValue).valueType
|
||||
val varTypeAliasee = mirror.info.constructPointedType(valueType).render(kotlinFile)
|
||||
val valueTypeAliasee = baseMirror.valueType.render(kotlinFile)
|
||||
|
||||
out("typealias ${kotlinFile.declare(varType.classifier)} = $varTypeAliasee")
|
||||
out("typealias ${kotlinFile.declare(valueType.classifier)} = $valueTypeAliasee")
|
||||
}
|
||||
is TypeMirror.ByRef -> {
|
||||
out("typealias ${mirror.pointedTypeName} = ${baseMirror.pointedTypeName}")
|
||||
val varTypeAliasee = baseMirror.pointedType.render(kotlinFile)
|
||||
out("typealias ${kotlinFile.declare(varType.classifier)} = $varTypeAliasee")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -499,7 +586,7 @@ class StubGenerator(
|
||||
|
||||
init {
|
||||
// TODO: support dumpShims
|
||||
val kotlinParameters = mutableListOf<Pair<String, String>>()
|
||||
val kotlinParameters = mutableListOf<Pair<String, KotlinType>>()
|
||||
val bodyGenerator = KotlinCodeBuilder()
|
||||
val bridgeArguments = mutableListOf<TypedKotlinValue>()
|
||||
|
||||
@@ -517,15 +604,17 @@ class StubGenerator(
|
||||
val representAsValuesRef = representCFunctionParameterAsValuesRef(parameter.type)
|
||||
|
||||
val bridgeArgument = if (representCFunctionParameterAsString(parameter.type)) {
|
||||
kotlinParameters.add(parameterName to "String?")
|
||||
kotlinParameters.add(parameterName to KotlinTypes.string.makeNullable())
|
||||
bodyGenerator.pushBlock("$parameterName?.cstr.usePointer { $tmpVarName ->")
|
||||
tmpVarName
|
||||
} else if (representCFunctionParameterAsWString(parameter.type)) {
|
||||
kotlinParameters.add(parameterName to "String?")
|
||||
kotlinParameters.add(parameterName to KotlinTypes.string.makeNullable())
|
||||
bodyGenerator.pushBlock("$parameterName?.wcstr.usePointer { $tmpVarName ->")
|
||||
tmpVarName
|
||||
} else if (representAsValuesRef != null) {
|
||||
kotlinParameters.add(parameterName to "CValuesRef<${mirror(representAsValuesRef).pointedTypeName}>?")
|
||||
val pointedType = mirror(representAsValuesRef).pointedType
|
||||
val parameterType = KotlinTypes.cValuesRef.typeWith(pointedType).makeNullable()
|
||||
kotlinParameters.add(parameterName to parameterType)
|
||||
bodyGenerator.pushBlock("$parameterName.usePointer { $tmpVarName ->")
|
||||
tmpVarName
|
||||
} else {
|
||||
@@ -550,13 +639,13 @@ class StubGenerator(
|
||||
} else {
|
||||
val returnTypeKind = getFfiTypeKind(func.returnType)
|
||||
|
||||
kotlinParameters.add("vararg variadicArguments" to "Any?")
|
||||
kotlinParameters.add("vararg variadicArguments" to KotlinTypes.any.makeNullable())
|
||||
bodyGenerator.pushBlock("memScoped {")
|
||||
|
||||
val resultVar = "kniResult"
|
||||
|
||||
val resultPtr = if (!func.returnsVoid()) {
|
||||
val returnType = mirror(func.returnType).pointedTypeName
|
||||
val returnType = mirror(func.returnType).pointedType.render(kotlinFile)
|
||||
bodyGenerator.out("val $resultVar = allocFfiReturnValueBuffer<$returnType>(typeOf<$returnType>())")
|
||||
"$resultVar.rawPtr"
|
||||
} else {
|
||||
@@ -581,12 +670,14 @@ class StubGenerator(
|
||||
}
|
||||
|
||||
val returnType = if (func.returnsVoid()) {
|
||||
"Unit"
|
||||
KotlinTypes.unit
|
||||
} else {
|
||||
mirror(func.returnType).argType
|
||||
}
|
||||
}.render(kotlinFile)
|
||||
|
||||
val joinedKotlinParameters = kotlinParameters.joinToString { (name, type) -> "$name: $type" }
|
||||
val joinedKotlinParameters = kotlinParameters.joinToString { (name, type) ->
|
||||
"$name: ${type.render(kotlinFile)}"
|
||||
}
|
||||
this.header = "fun ${func.name}($joinedKotlinParameters): $returnType"
|
||||
|
||||
this.bodyLines = bodyGenerator.build()
|
||||
@@ -626,10 +717,10 @@ class StubGenerator(
|
||||
}
|
||||
|
||||
val narrowedValue: Number = when (unwrappedType.kotlinType) {
|
||||
"kotlin.Byte" -> value.toByte()
|
||||
"kotlin.Short" -> value.toShort()
|
||||
"kotlin.Int" -> value.toInt()
|
||||
"kotlin.Long" -> value
|
||||
KotlinTypes.byte -> value.toByte()
|
||||
KotlinTypes.short -> value.toShort()
|
||||
KotlinTypes.int -> value.toInt()
|
||||
KotlinTypes.long -> value
|
||||
else -> return null
|
||||
}
|
||||
|
||||
@@ -663,7 +754,7 @@ class StubGenerator(
|
||||
}
|
||||
}
|
||||
|
||||
val kotlinType = mirror(constant.type).argType
|
||||
val kotlinType = mirror(constant.type).argType.render(kotlinFile)
|
||||
|
||||
// TODO: improve value rendering.
|
||||
|
||||
@@ -680,12 +771,20 @@ class StubGenerator(
|
||||
|
||||
stubs.addAll(generateStubsForFunctions(functionsToBind))
|
||||
|
||||
nativeIndex.objCProtocols.mapTo(stubs) {
|
||||
ObjCProtocolStub(this, it)
|
||||
nativeIndex.objCProtocols.forEach {
|
||||
if (!it.shouldBeImportedAsForwardDeclaration()) {
|
||||
stubs.add(ObjCProtocolStub(this, it))
|
||||
}
|
||||
}
|
||||
|
||||
nativeIndex.objCClasses.mapTo(stubs) {
|
||||
ObjCClassStub(this, it)
|
||||
nativeIndex.objCClasses.forEach {
|
||||
if (!it.shouldBeImportedAsForwardDeclaration()) {
|
||||
stubs.add(ObjCClassStub(this, it))
|
||||
}
|
||||
}
|
||||
|
||||
nativeIndex.objCCategories.mapTo(stubs) {
|
||||
ObjCCategoryStub(this, it)
|
||||
}
|
||||
|
||||
nativeIndex.macroConstants.forEach {
|
||||
@@ -751,6 +850,7 @@ class StubGenerator(
|
||||
add("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
|
||||
add("UNUSED_PARAMETER") // For constructors.
|
||||
add("MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED") // Workaround for multiple-inherited properties.
|
||||
add("EXTENSION_SHADOWED_BY_MEMBER") // For Objective-C categories represented as extensions.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -763,6 +863,11 @@ class StubGenerator(
|
||||
out("import konan.SymbolName")
|
||||
}
|
||||
out("import kotlinx.cinterop.*")
|
||||
|
||||
kotlinFile.buildImports().forEach {
|
||||
out(it)
|
||||
}
|
||||
|
||||
out("")
|
||||
|
||||
val context = object : StubGenerationContext {
|
||||
@@ -847,10 +952,20 @@ class StubGenerator(
|
||||
}
|
||||
}
|
||||
|
||||
fun addManifestProperties(properties: Properties) {
|
||||
properties["exportForwardDeclarations"] = nativeIndex.structs
|
||||
.filter { it.def == null }
|
||||
.joinToString(" ") {
|
||||
"$cnamesStructsPackageName.${it.kotlinName}"
|
||||
}
|
||||
|
||||
// TODO: consider exporting Objective-C class and protocol forward refs.
|
||||
}
|
||||
|
||||
val simpleBridgeGenerator: SimpleBridgeGenerator =
|
||||
SimpleBridgeGeneratorImpl(platform, pkgName, jvmFileClassName, libraryForCStubs)
|
||||
SimpleBridgeGeneratorImpl(platform, pkgName, jvmFileClassName, libraryForCStubs, kotlinFile)
|
||||
|
||||
val mappingBridgeGenerator: MappingBridgeGenerator =
|
||||
MappingBridgeGeneratorImpl(declarationMapper, simpleBridgeGenerator)
|
||||
MappingBridgeGeneratorImpl(declarationMapper, simpleBridgeGenerator, kotlinFile)
|
||||
|
||||
}
|
||||
|
||||
+21
-14
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.jetbrains.kotlin.native.interop.gen.jvm
|
||||
|
||||
import org.jetbrains.kotlin.native.interop.gen.HeadersInclusionPolicyImpl
|
||||
import org.jetbrains.kotlin.native.interop.gen.ImportsImpl
|
||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||
import java.io.File
|
||||
import java.io.StringReader
|
||||
@@ -134,10 +136,6 @@ private fun <T> Collection<T>.atMostOne(): T? {
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.matchesToGlob(glob: String): Boolean =
|
||||
java.nio.file.FileSystems.getDefault()
|
||||
.getPathMatcher("glob:$glob").matches(java.nio.file.Paths.get(this))
|
||||
|
||||
private fun Properties.getHostSpecific(
|
||||
name: String) = getProperty("$name.$host")
|
||||
|
||||
@@ -360,6 +358,15 @@ private fun argsToCompiler(staticLibraries: List<String>, libraryPaths: List<Str
|
||||
.map { it -> listOf("-includeBinary", it) } .flatten()
|
||||
}
|
||||
|
||||
private fun parseImports(args: Map<String, List<String>>): ImportsImpl {
|
||||
val headerIdToPackage = (args["-import"] ?: emptyList()).map { arg ->
|
||||
val (pkg, joinedIds) = arg.split(':')
|
||||
val ids = joinedIds.split(',')
|
||||
ids.map { HeaderId(it) to pkg }
|
||||
}.reversed().flatten().toMap()
|
||||
|
||||
return ImportsImpl(headerIdToPackage)
|
||||
}
|
||||
|
||||
private fun processLib(konanHome: String,
|
||||
substitutions: Map<String, String>,
|
||||
@@ -444,14 +451,8 @@ private fun processLib(konanHome: String,
|
||||
val libName = args["-cstubsname"]?.atMostOne() ?: fqParts.joinToString("") + "stubs"
|
||||
|
||||
val headerFilterGlobs = config.getSpaceSeparated("headerFilter")
|
||||
|
||||
val headerFilter = { name: String ->
|
||||
if (headerFilterGlobs.isEmpty()) {
|
||||
true
|
||||
} else {
|
||||
headerFilterGlobs.any { name.matchesToGlob(it) }
|
||||
}
|
||||
}
|
||||
val imports = parseImports(args)
|
||||
val headerInclusionPolicy = HeadersInclusionPolicyImpl(headerFilterGlobs, imports)
|
||||
|
||||
val library = NativeLibrary(
|
||||
includes = headerFiles,
|
||||
@@ -460,7 +461,7 @@ private fun processLib(konanHome: String,
|
||||
language = language,
|
||||
excludeSystemLibs = excludeSystemLibs,
|
||||
excludeDepdendentModules = excludeDependentModules,
|
||||
headerFilter = headerFilter
|
||||
headerInclusionPolicy = headerInclusionPolicy
|
||||
)
|
||||
|
||||
val configuration = InteropConfiguration(
|
||||
@@ -473,7 +474,7 @@ private fun processLib(konanHome: String,
|
||||
|
||||
val nativeIndex = buildNativeIndex(library)
|
||||
|
||||
val gen = StubGenerator(nativeIndex, configuration, libName, generateShims, verbose, flavor)
|
||||
val gen = StubGenerator(nativeIndex, configuration, libName, generateShims, verbose, flavor, imports)
|
||||
|
||||
outKtFile.parentFile.mkdirs()
|
||||
|
||||
@@ -486,6 +487,12 @@ private fun processLib(konanHome: String,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: if a library has partially included headers, then it shouldn't be used as a dependency.
|
||||
manifestAddendProperties["includedHeaders"] = nativeIndex.includedHeaders.joinToString(" ") { it.value }
|
||||
manifestAddendProperties["pkg"] = outKtPkg
|
||||
|
||||
gen.addManifestProperties(manifestAddendProperties)
|
||||
|
||||
manifestAddend?.parentFile?.mkdirs()
|
||||
manifestAddend?.let { manifestAddendProperties.storeProperties(it) }
|
||||
|
||||
|
||||
+58
-1
@@ -16,7 +16,12 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.konan
|
||||
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.ClassifierAliasingPackageFragmentDescriptor
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.ExportedForwardDeclarationsPackageFragmentDescriptor
|
||||
import org.jetbrains.kotlin.backend.konan.library.KonanLibraryReader
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
@@ -25,6 +30,23 @@ import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
interface InteropLibrary {
|
||||
fun createSyntheticPackages(
|
||||
module: ModuleDescriptor,
|
||||
kotlinPackageFragments: List<PackageFragmentDescriptor>
|
||||
): List<PackageFragmentDescriptor>
|
||||
}
|
||||
|
||||
fun createInteropLibrary(reader: KonanLibraryReader): InteropLibrary? {
|
||||
val pkg = reader.manifestProperties.getProperty("pkg") ?: return null
|
||||
val exportForwardDeclarations = reader.manifestProperties
|
||||
.getProperty("exportForwardDeclarations").split(' ')
|
||||
.map { it.trim() }.filter { it.isNotEmpty() }
|
||||
.map { FqName(it) }
|
||||
|
||||
return InteropLibraryImpl(FqName(pkg), exportForwardDeclarations)
|
||||
}
|
||||
|
||||
private val cPointerName = "CPointer"
|
||||
private val nativePointedName = "NativePointed"
|
||||
|
||||
@@ -35,6 +57,13 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns) {
|
||||
|
||||
val cPointer = packageName.child(Name.identifier(cPointerName)).toUnsafe()
|
||||
val nativePointed = packageName.child(Name.identifier(nativePointedName)).toUnsafe()
|
||||
|
||||
val cNames = FqName("cnames")
|
||||
val cNamesStructs = cNames.child(Name.identifier("structs"))
|
||||
|
||||
val objCNames = FqName("objcnames")
|
||||
val objCNamesClasses = objCNames.child(Name.identifier("classes"))
|
||||
val objCNamesProtocols = objCNames.child(Name.identifier("protocols"))
|
||||
}
|
||||
|
||||
private val packageScope = builtIns.builtInsModule.getPackage(FqNames.packageName).memberScope
|
||||
@@ -174,4 +203,32 @@ private fun MemberScope.getContributedClass(name: String): ClassDescriptor =
|
||||
this.getContributedClassifier(Name.identifier(name), NoLookupLocation.FROM_BUILTINS) as ClassDescriptor
|
||||
|
||||
private fun MemberScope.getContributedFunctions(name: String) =
|
||||
this.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BUILTINS)
|
||||
this.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BUILTINS)
|
||||
|
||||
private class InteropLibraryImpl(
|
||||
private val packageFqName: FqName,
|
||||
private val exportForwardDeclarations: List<FqName>
|
||||
) : InteropLibrary {
|
||||
override fun createSyntheticPackages(
|
||||
module: ModuleDescriptor,
|
||||
kotlinPackageFragments: List<PackageFragmentDescriptor>
|
||||
): List<PackageFragmentDescriptor> {
|
||||
val interopPackageFragments = kotlinPackageFragments.filter { it.fqName == packageFqName }
|
||||
|
||||
val fqNames = InteropBuiltIns.FqNames
|
||||
|
||||
val result = mutableListOf<PackageFragmentDescriptor>()
|
||||
|
||||
// Allow references to forwarding declarations to be resolved into classifiers declared in this library:
|
||||
listOf(fqNames.cNamesStructs, fqNames.objCNamesClasses, fqNames.objCNamesProtocols).mapTo(result) { fqName ->
|
||||
ClassifierAliasingPackageFragmentDescriptor(interopPackageFragments, module, fqName)
|
||||
}
|
||||
// TODO: use separate namespaces for structs, enums, Objective-C protocols etc.
|
||||
|
||||
result.add(ExportedForwardDeclarationsPackageFragmentDescriptor(
|
||||
module, packageFqName, exportForwardDeclarations
|
||||
))
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
+20
-1
@@ -17,12 +17,14 @@
|
||||
package org.jetbrains.kotlin.backend.konan
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.createForwardDeclarationsModule
|
||||
import org.jetbrains.kotlin.backend.konan.library.KonanLibraryReader
|
||||
import org.jetbrains.kotlin.backend.konan.library.KonanLibrarySearchPathResolver
|
||||
import org.jetbrains.kotlin.backend.konan.library.impl.LibraryReaderImpl
|
||||
import org.jetbrains.kotlin.backend.konan.util.profile
|
||||
import org.jetbrains.kotlin.backend.konan.util.removeSuffixIfPresent
|
||||
import org.jetbrains.kotlin.backend.konan.util.suffixIfNot
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.*
|
||||
@@ -34,6 +36,8 @@ import org.jetbrains.kotlin.konan.target.*
|
||||
import org.jetbrains.kotlin.konan.target.TargetManager.*
|
||||
import org.jetbrains.kotlin.konan.target.CompilerOutputKind.*
|
||||
import org.jetbrains.kotlin.konan.util.DependencyProcessor
|
||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
|
||||
class KonanConfig(val project: Project, val configuration: CompilerConfiguration) {
|
||||
|
||||
@@ -116,10 +120,25 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
|
||||
return allMetadata
|
||||
}
|
||||
|
||||
private var forwardDeclarationsModule: ModuleDescriptorImpl? = null
|
||||
|
||||
internal fun getOrCreateForwardDeclarationsModule(
|
||||
builtIns: KotlinBuiltIns, storageManager: StorageManager? = null
|
||||
): ModuleDescriptorImpl {
|
||||
forwardDeclarationsModule?.let { return it }
|
||||
val result = createForwardDeclarationsModule(
|
||||
builtIns,
|
||||
storageManager ?: LockBasedStorageManager()
|
||||
)
|
||||
|
||||
forwardDeclarationsModule = result
|
||||
return result
|
||||
}
|
||||
|
||||
internal val moduleDescriptors: List<ModuleDescriptorImpl> by lazy {
|
||||
for (module in loadedDescriptors) {
|
||||
// Yes, just to all of them.
|
||||
module.setDependencies(loadedDescriptors)
|
||||
module.setDependencies(loadedDescriptors + getOrCreateForwardDeclarationsModule(module.builtIns))
|
||||
}
|
||||
|
||||
loadedDescriptors
|
||||
|
||||
+2
-1
@@ -39,7 +39,8 @@ object TopDownAnalyzerFacadeForKonan {
|
||||
builtIns.builtInsModule = module
|
||||
|
||||
if (!module.isStdlib()) {
|
||||
context.setDependencies(listOf(module) + config.moduleDescriptors)
|
||||
context.setDependencies(listOf(module) + config.moduleDescriptors +
|
||||
config.getOrCreateForwardDeclarationsModule(builtIns, projectContext.storageManager))
|
||||
} else {
|
||||
assert (config.moduleDescriptors.isEmpty())
|
||||
context.setDependencies(module)
|
||||
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.konan.descriptors
|
||||
|
||||
import org.jetbrains.kotlin.backend.konan.InteropBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.PackageFragmentDescriptorImpl
|
||||
import org.jetbrains.kotlin.incremental.components.LookupLocation
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScopeImpl
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
import org.jetbrains.kotlin.storage.getValue
|
||||
import org.jetbrains.kotlin.utils.Printer
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
|
||||
|
||||
/**
|
||||
* The package fragment to export forward declarations from interop package namespace, i.e.
|
||||
* redirect "$pkg.$name" to e.g. "cnames.structs.$name".
|
||||
*/
|
||||
class ExportedForwardDeclarationsPackageFragmentDescriptor(
|
||||
module: ModuleDescriptor, fqName: FqName, declarations: List<FqName>
|
||||
) : PackageFragmentDescriptorImpl(module, fqName) {
|
||||
|
||||
private val memberScope = object : MemberScopeImpl() {
|
||||
|
||||
private val nameToFqName = declarations.map { it.shortName() to it }.toMap()
|
||||
|
||||
override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? {
|
||||
val declFqName = nameToFqName[name] ?: return null
|
||||
|
||||
val packageView = module.getPackage(declFqName.parent())
|
||||
return packageView.memberScope.getContributedClassifier(name, location)
|
||||
}
|
||||
|
||||
override fun printScopeStructure(p: Printer) {
|
||||
p.println(this::class.java.simpleName, " {")
|
||||
p.pushIndent()
|
||||
|
||||
// TODO
|
||||
|
||||
p.popIndent()
|
||||
p.println("}")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun getMemberScope() = memberScope
|
||||
}
|
||||
|
||||
/**
|
||||
* The package fragment that redirects all requests for classifier lookup to its targets.
|
||||
*/
|
||||
class ClassifierAliasingPackageFragmentDescriptor(
|
||||
targets: List<PackageFragmentDescriptor>, module: ModuleDescriptor, fqName: FqName
|
||||
) : PackageFragmentDescriptorImpl(module, fqName) {
|
||||
|
||||
private val memberScope = object : MemberScopeImpl() {
|
||||
|
||||
override fun getContributedClassifier(name: Name, location: LookupLocation) =
|
||||
targets.firstNotNullResult {
|
||||
it.getMemberScope().getContributedClassifier(name, location)
|
||||
}
|
||||
|
||||
override fun printScopeStructure(p: Printer) {
|
||||
p.println(this::class.java.simpleName, " {")
|
||||
p.pushIndent()
|
||||
|
||||
p.println("targets = " + targets)
|
||||
|
||||
p.popIndent()
|
||||
p.println("}")
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMemberScope(): MemberScope = memberScope
|
||||
}
|
||||
|
||||
/**
|
||||
* Package fragment which creates descriptors for forward declarations on demand.
|
||||
*/
|
||||
private class ForwardDeclarationsPackageFragmentDescriptor(
|
||||
storageManager: StorageManager,
|
||||
module: ModuleDescriptor, fqName: FqName, supertypeName: Name, classKind: ClassKind
|
||||
) : PackageFragmentDescriptorImpl(module, fqName) {
|
||||
|
||||
private val memberScope = object : MemberScopeImpl() {
|
||||
|
||||
private val declarations = storageManager.createMemoizedFunction(this::createDeclaration)
|
||||
|
||||
private val supertype by storageManager.createLazyValue {
|
||||
val descriptor = builtIns.builtInsModule.getPackage(InteropBuiltIns.FqNames.packageName)
|
||||
.memberScope
|
||||
.getContributedClassifier(supertypeName, NoLookupLocation.FROM_BACKEND) as ClassDescriptor
|
||||
|
||||
descriptor.defaultType
|
||||
}
|
||||
|
||||
private fun createDeclaration(name: Name): ClassDescriptor {
|
||||
return ClassDescriptorImpl(
|
||||
this@ForwardDeclarationsPackageFragmentDescriptor,
|
||||
name, Modality.FINAL, classKind,
|
||||
listOf(supertype), SourceElement.NO_SOURCE, false
|
||||
).apply {
|
||||
this.initialize(MemberScope.Empty, emptySet(), null)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getContributedClassifier(name: Name, location: LookupLocation) = declarations(name)
|
||||
|
||||
override fun printScopeStructure(p: Printer) {
|
||||
p.println(this::class.java.simpleName, "{}")
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMemberScope(): MemberScope = memberScope
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates module which "contains" forward declarations.
|
||||
* Note: this module should be unique per compilation and should always be the last dependency of any module.
|
||||
*/
|
||||
fun createForwardDeclarationsModule(builtIns: KotlinBuiltIns, storageManager: StorageManager): ModuleDescriptorImpl {
|
||||
val module = ModuleDescriptorImpl(
|
||||
Name.special("<forward declarations>"),
|
||||
storageManager,
|
||||
builtIns
|
||||
)
|
||||
|
||||
fun createPackage(fqName: FqName, supertypeName: String, classKind: ClassKind = ClassKind.CLASS) =
|
||||
ForwardDeclarationsPackageFragmentDescriptor(
|
||||
storageManager,
|
||||
module,
|
||||
fqName,
|
||||
Name.identifier(supertypeName),
|
||||
classKind
|
||||
)
|
||||
|
||||
val fqNames = InteropBuiltIns.FqNames
|
||||
|
||||
val packageFragmentProvider = PackageFragmentProviderImpl(
|
||||
listOf(
|
||||
createPackage(fqNames.cNamesStructs, "COpaque"),
|
||||
createPackage(fqNames.objCNamesClasses, "ObjCObjectBase"),
|
||||
createPackage(fqNames.objCNamesProtocols, "ObjCObject", ClassKind.INTERFACE)
|
||||
)
|
||||
)
|
||||
|
||||
module.initialize(packageFragmentProvider)
|
||||
module.setDependencies(module)
|
||||
|
||||
return module
|
||||
}
|
||||
+2
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.backend.konan.library
|
||||
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.konan.properties.Properties
|
||||
|
||||
interface KonanLibraryReader {
|
||||
val libraryName: String
|
||||
@@ -25,6 +26,7 @@ interface KonanLibraryReader {
|
||||
val includedPaths: List<String>
|
||||
val linkerOpts: List<String>
|
||||
val escapeAnalysis: ByteArray?
|
||||
val manifestProperties: Properties
|
||||
fun moduleDescriptor(specifics: LanguageVersionSettings): ModuleDescriptor
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.backend.konan.library.impl
|
||||
|
||||
import org.jetbrains.kotlin.backend.konan.library.KonanLibraryReader
|
||||
import org.jetbrains.kotlin.backend.konan.createInteropLibrary
|
||||
import org.jetbrains.kotlin.backend.konan.serialization.deserializeModule
|
||||
import org.jetbrains.kotlin.konan.file.File
|
||||
import org.jetbrains.kotlin.konan.properties.*
|
||||
@@ -34,7 +35,7 @@ class LibraryReaderImpl(var libraryFile: File, val currentAbiVersion: Int, val t
|
||||
|
||||
private val reader = MetadataReaderImpl(inPlace)
|
||||
|
||||
val manifestProperties: Properties by lazy {
|
||||
override val manifestProperties: Properties by lazy {
|
||||
inPlace.manifestFile.loadProperties()
|
||||
}
|
||||
|
||||
@@ -69,7 +70,7 @@ class LibraryReaderImpl(var libraryFile: File, val currentAbiVersion: Int, val t
|
||||
reader.loadSerializedPackageFragment(fqName)
|
||||
|
||||
override fun moduleDescriptor(specifics: LanguageVersionSettings)
|
||||
= deserializeModule(specifics, {packageMetadata(it)}, moduleHeaderData)
|
||||
= deserializeModule(specifics, {packageMetadata(it)}, moduleHeaderData, createInteropLibrary(this))
|
||||
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -473,7 +473,7 @@ internal class InteropLoweringPart1(val context: Context) : IrBuildingTransforme
|
||||
|
||||
if (!useKotlinDispatch) {
|
||||
val arguments = descriptor.valueParameters.map { expression.getValueArgument(it)!! }
|
||||
assert(expression.extensionReceiver == null)
|
||||
assert(expression.dispatchReceiver == null || expression.extensionReceiver == null)
|
||||
|
||||
if (expression.superQualifier?.isObjCMetaClass() == true) {
|
||||
context.reportCompilationError(
|
||||
@@ -493,7 +493,7 @@ internal class InteropLoweringPart1(val context: Context) : IrBuildingTransforme
|
||||
return builder.genLoweredObjCMethodCall(
|
||||
methodInfo,
|
||||
superQualifier = expression.superQualifierSymbol,
|
||||
receiver = expression.dispatchReceiver!!,
|
||||
receiver = expression.dispatchReceiver ?: expression.extensionReceiver!!,
|
||||
arguments = arguments
|
||||
)
|
||||
}
|
||||
|
||||
+12
-6
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.backend.konan.serialization
|
||||
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.InteropLibrary
|
||||
import org.jetbrains.kotlin.backend.konan.KonanBuiltIns
|
||||
import org.jetbrains.kotlin.backend.konan.KonanConfigKeys
|
||||
import org.jetbrains.kotlin.backend.konan.library.LinkData
|
||||
@@ -75,15 +76,19 @@ object NullFlexibleTypeDeserializer : FlexibleTypeDeserializer {
|
||||
}
|
||||
|
||||
fun createKonanPackageFragmentProvider(
|
||||
fragmentNames: List<String>,
|
||||
fragmentNames: List<String>,
|
||||
packageLoader: (String)->KonanLinkData.PackageFragment,
|
||||
storageManager: StorageManager, module: ModuleDescriptor,
|
||||
configuration: DeserializationConfiguration): PackageFragmentProvider {
|
||||
storageManager: StorageManager, module: ModuleDescriptor,
|
||||
configuration: DeserializationConfiguration,
|
||||
interopLibrary: InteropLibrary?): PackageFragmentProvider {
|
||||
|
||||
val packageFragments = fragmentNames.map{
|
||||
KonanPackageFragment(it, packageLoader, storageManager, module)
|
||||
}
|
||||
val provider = PackageFragmentProviderImpl(packageFragments)
|
||||
val syntheticInteropPackageFragments =
|
||||
interopLibrary?.createSyntheticPackages(module, packageFragments) ?: emptyList()
|
||||
|
||||
val provider = PackageFragmentProviderImpl(packageFragments + syntheticInteropPackageFragments)
|
||||
|
||||
val notFoundClasses = NotFoundClasses(storageManager, module)
|
||||
|
||||
@@ -115,7 +120,8 @@ public fun parseModuleHeader(libraryData: ByteArray): Library =
|
||||
KonanSerializerProtocol.extensionRegistry)
|
||||
|
||||
internal fun deserializeModule(languageVersionSettings: LanguageVersionSettings,
|
||||
packageLoader:(String)->ByteArray, library: ByteArray): ModuleDescriptorImpl {
|
||||
packageLoader:(String)->ByteArray, library: ByteArray,
|
||||
interopLibrary: InteropLibrary?): ModuleDescriptorImpl {
|
||||
|
||||
val libraryProto = parseModuleHeader(library)
|
||||
val moduleName = libraryProto.moduleName
|
||||
@@ -131,7 +137,7 @@ internal fun deserializeModule(languageVersionSettings: LanguageVersionSettings,
|
||||
libraryProto.packageFragmentNameList,
|
||||
{it -> parsePackageFragment(packageLoader(it))},
|
||||
storageManager,
|
||||
moduleDescriptor, deserializationConfiguration)
|
||||
moduleDescriptor, deserializationConfiguration, interopLibrary)
|
||||
|
||||
moduleDescriptor.initialize(provider)
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import sysstat.*
|
||||
import kotlinx.cinterop.*
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val statBuf = nativeHeap.alloc<statStruct>()
|
||||
val statBuf = nativeHeap.alloc<stat>()
|
||||
val res = stat("/", statBuf.ptr)
|
||||
println(res)
|
||||
println(statBuf.st_uid)
|
||||
|
||||
@@ -7,10 +7,13 @@
|
||||
|
||||
@interface Foo : NSObject
|
||||
@property NSString* name;
|
||||
-(void)hello;
|
||||
-(void)helloWithPrinter:(id <Printer>)printer;
|
||||
@end;
|
||||
|
||||
@interface Foo (FooExtensions)
|
||||
-(void)hello;
|
||||
@end;
|
||||
|
||||
@protocol MutablePair
|
||||
@required
|
||||
@property (readonly) int first;
|
||||
|
||||
@@ -24,11 +24,6 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void)hello {
|
||||
CPrinter* printer = [[CPrinter alloc] init];
|
||||
[self helloWithPrinter:printer];
|
||||
}
|
||||
|
||||
-(void)helloWithPrinter:(id <Printer>)printer {
|
||||
NSString* message = [NSString stringWithFormat:@"Hello, %@!", self.name];
|
||||
[printer print:message.UTF8String];
|
||||
@@ -40,6 +35,14 @@
|
||||
|
||||
@end;
|
||||
|
||||
@implementation Foo (FooExtensions)
|
||||
|
||||
-(void)hello {
|
||||
CPrinter* printer = [[CPrinter alloc] init];
|
||||
[self helloWithPrinter:printer];
|
||||
}
|
||||
@end;
|
||||
|
||||
void replacePairElements(id <MutablePair> pair, int first, int second) {
|
||||
[pair update:0 add:(first - pair.first)];
|
||||
[pair update:1 sub:(pair.second - second)];
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
language = Objective-C
|
||||
package = platform.objc
|
||||
headers = objc/List.h objc/NSObjCRuntime.h objc/NSObject.h objc/Object.h objc/Protocol.h \
|
||||
objc/hashtable.h objc/hashtable2.h objc/message.h objc/objc-api.h \
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
language = Objective-C
|
||||
package = platform.osx
|
||||
headers = AppleTextureEncoder.h AssertMacros.h Availability.h AvailabilityInternal.h \
|
||||
AvailabilityMacros.h Block.h ConditionalMacros.h MacTypes.h NSSystemDirectories.h \
|
||||
|
||||
@@ -156,7 +156,11 @@ private val File.zipUri: URI
|
||||
fun File.zipFileSystem(mutable: Boolean = false): FileSystem {
|
||||
val zipUri = this.zipUri
|
||||
val attributes = hashMapOf("create" to mutable.toString())
|
||||
return FileSystems.newFileSystem(zipUri, attributes, null)
|
||||
return try {
|
||||
FileSystems.newFileSystem(zipUri, attributes, null)
|
||||
} catch (e: FileSystemAlreadyExistsException) {
|
||||
FileSystems.getFileSystem(zipUri)
|
||||
}
|
||||
}
|
||||
|
||||
fun File.mutableZipFileSystem() = this.zipFileSystem(mutable = true)
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package org.jetbrains.kotlin.cli.utilities
|
||||
|
||||
import org.jetbrains.kotlin.backend.konan.library.impl.KonanLibrary
|
||||
import org.jetbrains.kotlin.konan.file.File
|
||||
import org.jetbrains.kotlin.konan.properties.loadProperties
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.kotlin.cli.bc.main as konancMain
|
||||
import org.jetbrains.kotlin.native.interop.gen.jvm.interop
|
||||
import org.jetbrains.kotlin.cli.klib.main as klibMain
|
||||
@@ -8,11 +11,16 @@ import org.jetbrains.kotlin.cli.klib.main as klibMain
|
||||
fun invokeCinterop(args: Array<String>) {
|
||||
var outputFileName = "nativelib"
|
||||
var target = "host"
|
||||
val libraries = mutableListOf<String>()
|
||||
for (i in args.indices) {
|
||||
if (args[i].startsWith("-o"))
|
||||
outputFileName = args.getOrElse(i+1){outputFileName}
|
||||
if (args[i] == "-target")
|
||||
target = args.getOrElse(i+1){target}
|
||||
val arg = args[i]
|
||||
val nextArg = args.getOrNull(i + 1)
|
||||
if (arg.startsWith("-o"))
|
||||
outputFileName = nextArg ?: outputFileName
|
||||
if (arg == "-target")
|
||||
target = nextArg ?: target
|
||||
if (arg == "-library")
|
||||
libraries.addIfNotNull(nextArg)
|
||||
}
|
||||
|
||||
val buildDir = File("$outputFileName-build")
|
||||
@@ -21,12 +29,23 @@ fun invokeCinterop(args: Array<String>) {
|
||||
val cstubsName ="cstubs"
|
||||
val manifest = File(buildDir, "manifest.properties")
|
||||
|
||||
val importArgs = libraries.flatMap {
|
||||
val library = KonanLibrary(File(it))
|
||||
val manifestProperties = library.manifestFile.loadProperties()
|
||||
// TODO: handle missing properties?
|
||||
val pkg = manifestProperties["pkg"] as String
|
||||
val headerIds = (manifestProperties["includedHeaders"] as String).split(' ')
|
||||
val arg = "$pkg:${headerIds.joinToString(",")}"
|
||||
listOf("-import", arg)
|
||||
}
|
||||
|
||||
val additionalArgs = listOf<String>(
|
||||
"-generated", generatedDir.path,
|
||||
"-natives", nativesDir.path,
|
||||
"-cstubsname", cstubsName,
|
||||
"-manifest", manifest.path,
|
||||
"-flavor", "native")
|
||||
"-flavor", "native"
|
||||
) + importArgs
|
||||
|
||||
val cinteropArgs = (additionalArgs + args.toList()).toTypedArray()
|
||||
val cinteropArgsToCompiler = mutableListOf<String>()
|
||||
@@ -39,7 +58,7 @@ fun invokeCinterop(args: Array<String>) {
|
||||
"-o", outputFileName,
|
||||
"-target", target,
|
||||
"-manifest", manifest.path
|
||||
) + cinteropArgsToCompiler
|
||||
) + cinteropArgsToCompiler + libraries.flatMap { listOf("-library", it) }
|
||||
konancMain(konancArgs)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user