Implement very basic Objective-C interop
Also refactor StubGenerator
This commit is contained in:
committed by
SvyatoslavScherbina
parent
a38835cb46
commit
d3185e3e19
+386
-34
@@ -25,19 +25,47 @@ private class StructDeclImpl(spelling: String) : StructDecl(spelling) {
|
|||||||
override var def: StructDefImpl? = null
|
override var def: StructDefImpl? = null
|
||||||
}
|
}
|
||||||
|
|
||||||
private class StructDefImpl(size: Long, align: Int, decl: StructDecl, hasNaturalLayout: Boolean) :
|
private class StructDefImpl(
|
||||||
StructDef(size, align, decl, hasNaturalLayout) {
|
size: Long, align: Int, decl: StructDecl,
|
||||||
|
hasNaturalLayout: Boolean, hasUnalignedFields: Boolean
|
||||||
|
) : StructDef(
|
||||||
|
size, align, decl,
|
||||||
|
hasNaturalLayout = hasNaturalLayout, hasUnalignedFields = hasUnalignedFields
|
||||||
|
) {
|
||||||
|
|
||||||
override val fields = mutableListOf<Field>()
|
override val fields = mutableListOf<Field>()
|
||||||
}
|
}
|
||||||
|
|
||||||
private class EnumDefImpl(spelling: String, type: PrimitiveType) : EnumDef(spelling, type) {
|
private class EnumDefImpl(spelling: String, type: Type) : EnumDef(spelling, type) {
|
||||||
override val constants = mutableListOf<EnumConstant>()
|
override val constants = mutableListOf<EnumConstant>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private interface ObjCClassOrProtocolImpl {
|
||||||
|
val protocols: MutableList<ObjCProtocol>
|
||||||
|
val methods: MutableList<ObjCMethod>
|
||||||
|
val properties: MutableList<ObjCProperty>
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ObjCProtocolImpl(name: String) : ObjCProtocol(name), ObjCClassOrProtocolImpl {
|
||||||
|
override val protocols = mutableListOf<ObjCProtocol>()
|
||||||
|
override val methods = mutableListOf<ObjCMethod>()
|
||||||
|
override val properties = mutableListOf<ObjCProperty>()
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ObjCClassImpl(name: String) : ObjCClass(name), ObjCClassOrProtocolImpl {
|
||||||
|
override val protocols = mutableListOf<ObjCProtocol>()
|
||||||
|
override val methods = mutableListOf<ObjCMethod>()
|
||||||
|
override val properties = mutableListOf<ObjCProperty>()
|
||||||
|
override var baseClass: ObjCClass? = null
|
||||||
|
}
|
||||||
|
|
||||||
internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() {
|
internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() {
|
||||||
|
|
||||||
private data class DeclarationID(val usr: String)
|
private sealed class DeclarationID {
|
||||||
|
data class USR(val usr: String) : DeclarationID()
|
||||||
|
object VaListTag : DeclarationID()
|
||||||
|
object BuiltinVaList : DeclarationID()
|
||||||
|
}
|
||||||
|
|
||||||
private val structById = mutableMapOf<DeclarationID, StructDeclImpl>()
|
private val structById = mutableMapOf<DeclarationID, StructDeclImpl>()
|
||||||
|
|
||||||
@@ -49,21 +77,39 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() {
|
|||||||
override val enums: List<EnumDef>
|
override val enums: List<EnumDef>
|
||||||
get() = enumById.values.toList()
|
get() = enumById.values.toList()
|
||||||
|
|
||||||
|
private val objCClassesByName = mutableMapOf<String, ObjCClassImpl>()
|
||||||
|
|
||||||
|
override val objCClasses: List<ObjCClass> get() = objCClassesByName.values.toList()
|
||||||
|
|
||||||
|
private val objCProtocolsByName = mutableMapOf<String, ObjCProtocolImpl>()
|
||||||
|
|
||||||
|
override val objCProtocols: List<ObjCProtocol> get() = objCProtocolsByName.values.toList()
|
||||||
|
|
||||||
private val typedefById = mutableMapOf<DeclarationID, TypedefDef>()
|
private val typedefById = mutableMapOf<DeclarationID, TypedefDef>()
|
||||||
|
|
||||||
override val typedefs: List<TypedefDef>
|
override val typedefs: List<TypedefDef>
|
||||||
get() = typedefById.values.toList()
|
get() = typedefById.values.toList()
|
||||||
|
|
||||||
val functionByName = mutableMapOf<String, FunctionDecl>()
|
private val functionById = mutableMapOf<DeclarationID, FunctionDecl>()
|
||||||
|
|
||||||
override val functions: List<FunctionDecl>
|
override val functions: List<FunctionDecl>
|
||||||
get() = functionByName.values.toList()
|
get() = functionById.values.toList()
|
||||||
|
|
||||||
override val macroConstants = mutableListOf<ConstantDef>()
|
override val macroConstants = mutableListOf<ConstantDef>()
|
||||||
|
|
||||||
private fun getDeclarationId(cursor: CValue<CXCursor>): DeclarationID {
|
private fun getDeclarationId(cursor: CValue<CXCursor>): DeclarationID {
|
||||||
val usr = clang_getCursorUSR(cursor).convertAndDispose()
|
val usr = clang_getCursorUSR(cursor).convertAndDispose()
|
||||||
return DeclarationID(usr)
|
if (usr == "") {
|
||||||
|
val kind = cursor.kind
|
||||||
|
val spelling = getCursorSpelling(cursor)
|
||||||
|
return when (kind to spelling) {
|
||||||
|
CXCursorKind.CXCursor_StructDecl to "__va_list_tag" -> DeclarationID.VaListTag
|
||||||
|
CXCursorKind.CXCursor_TypedefDecl to "__builtin_va_list" -> DeclarationID.BuiltinVaList
|
||||||
|
else -> error(spelling)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return DeclarationID.USR(usr)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getStructDeclAt(cursor: CValue<CXCursor>): StructDeclImpl {
|
private fun getStructDeclAt(cursor: CValue<CXCursor>): StructDeclImpl {
|
||||||
@@ -91,8 +137,13 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() {
|
|||||||
val type = clang_getCursorType(cursor)
|
val type = clang_getCursorType(cursor)
|
||||||
val size = clang_Type_getSizeOf(type)
|
val size = clang_Type_getSizeOf(type)
|
||||||
val align = clang_Type_getAlignOf(type).toInt()
|
val align = clang_Type_getAlignOf(type).toInt()
|
||||||
val hasNaturalLayout = structHasNaturalLayout(cursor)
|
|
||||||
val structDef = StructDefImpl(size, align, structDecl, hasNaturalLayout)
|
val structDef = StructDefImpl(
|
||||||
|
size, align, structDecl,
|
||||||
|
hasNaturalLayout = structHasNaturalLayout(cursor),
|
||||||
|
hasUnalignedFields = structHasUnalignedFields(cursor)
|
||||||
|
)
|
||||||
|
|
||||||
structDecl.def = structDef
|
structDecl.def = structDef
|
||||||
|
|
||||||
visitChildren(cursor) { childCursor: CValue<CXCursor>, _: CValue<CXCursor> ->
|
visitChildren(cursor) { childCursor: CValue<CXCursor>, _: CValue<CXCursor> ->
|
||||||
@@ -117,7 +168,7 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() {
|
|||||||
val cursorType = clang_getCursorType(cursor)
|
val cursorType = clang_getCursorType(cursor)
|
||||||
val typeSpelling = clang_getTypeSpelling(cursorType).convertAndDispose()
|
val typeSpelling = clang_getTypeSpelling(cursorType).convertAndDispose()
|
||||||
|
|
||||||
val baseType = convertType(clang_getEnumDeclIntegerType(cursor)) as PrimitiveType
|
val baseType = convertType(clang_getEnumDeclIntegerType(cursor))
|
||||||
|
|
||||||
val enumDef = EnumDefImpl(typeSpelling, baseType)
|
val enumDef = EnumDefImpl(typeSpelling, baseType)
|
||||||
|
|
||||||
@@ -137,11 +188,112 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getObjCCategoryClassCursor(cursor: CValue<CXCursor>): CValue<CXCursor> {
|
||||||
|
assert(cursor.kind == CXCursorKind.CXCursor_ObjCCategoryDecl)
|
||||||
|
var classRef: CValue<CXCursor>? = null
|
||||||
|
visitChildren(cursor) { child, _ ->
|
||||||
|
if (child.kind == CXCursorKind.CXCursor_ObjCClassRef) {
|
||||||
|
classRef = child
|
||||||
|
CXChildVisitResult.CXChildVisit_Break
|
||||||
|
} else {
|
||||||
|
CXChildVisitResult.CXChildVisit_Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return clang_getCursorReferenced(classRef!!).apply {
|
||||||
|
assert(this.kind == CXCursorKind.CXCursor_ObjCInterfaceDecl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getObjCClassAt(cursor: CValue<CXCursor>): ObjCClassImpl {
|
||||||
|
assert(cursor.kind == CXCursorKind.CXCursor_ObjCInterfaceDecl) { cursor.kind }
|
||||||
|
|
||||||
|
val name = clang_getCursorDisplayName(cursor).convertAndDispose()
|
||||||
|
|
||||||
|
objCClassesByName[name]?.let { return it }
|
||||||
|
|
||||||
|
val result = ObjCClassImpl(name)
|
||||||
|
objCClassesByName[name] = result
|
||||||
|
|
||||||
|
addChildrenToClassOrProtocol(cursor, result)
|
||||||
|
return result
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getObjCProtocolAt(cursor: CValue<CXCursor>): ObjCProtocolImpl? {
|
||||||
|
assert(cursor.kind == CXCursorKind.CXCursor_ObjCProtocolDecl) { cursor.kind }
|
||||||
|
if (clang_isCursorDefinition(cursor) == 0) {
|
||||||
|
val definition = clang_getCursorDefinition(cursor)
|
||||||
|
if (clang_isCursorDefinition(cursor) == 0) return null
|
||||||
|
return getObjCProtocolAt(definition)
|
||||||
|
}
|
||||||
|
|
||||||
|
val name = clang_getCursorDisplayName(cursor).convertAndDispose()
|
||||||
|
|
||||||
|
objCProtocolsByName[name]?.let { return it }
|
||||||
|
|
||||||
|
val result = ObjCProtocolImpl(name)
|
||||||
|
objCProtocolsByName[name] = result
|
||||||
|
|
||||||
|
addChildrenToClassOrProtocol(cursor, result)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addChildrenToClassOrProtocol(cursor: CValue<CXCursor>, result: ObjCClassOrProtocolImpl) {
|
||||||
|
visitChildren(cursor) { child, _ ->
|
||||||
|
when (child.kind) {
|
||||||
|
CXCursorKind.CXCursor_ObjCSuperClassRef -> {
|
||||||
|
assert(cursor.kind == CXCursorKind.CXCursor_ObjCInterfaceDecl)
|
||||||
|
result as ObjCClassImpl
|
||||||
|
|
||||||
|
assert(result.baseClass == null)
|
||||||
|
result.baseClass = getObjCClassAt(clang_getCursorReferenced(child))
|
||||||
|
}
|
||||||
|
CXCursorKind.CXCursor_ObjCProtocolRef -> {
|
||||||
|
getObjCProtocolAt(clang_getCursorReferenced(child))?.let {
|
||||||
|
if (it !in result.protocols) {
|
||||||
|
result.protocols.add(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CXCursorKind.CXCursor_ObjCClassMethodDecl, CXCursorKind.CXCursor_ObjCInstanceMethodDecl -> {
|
||||||
|
getObjCMethod(child)?.let { method ->
|
||||||
|
result.methods.removeAll { method.replaces(it) }
|
||||||
|
result.methods.add(method)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
CXChildVisitResult.CXChildVisit_Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun getTypedef(type: CValue<CXType>): Type {
|
fun getTypedef(type: CValue<CXType>): Type {
|
||||||
val declCursor = clang_getTypeDeclaration(type)
|
val declCursor = clang_getTypeDeclaration(type)
|
||||||
val name = getCursorSpelling(declCursor)
|
val name = getCursorSpelling(declCursor)
|
||||||
|
|
||||||
val underlying = convertType(clang_getTypedefDeclUnderlyingType(declCursor))
|
val underlying = convertType(clang_getTypedefDeclUnderlyingType(declCursor))
|
||||||
|
if (clang_getCursorLexicalParent(declCursor).kind != CXCursorKind.CXCursor_TranslationUnit) {
|
||||||
|
// Objective-C type parameters are represented as non-top-level typedefs.
|
||||||
|
// Erase for now:
|
||||||
|
return underlying
|
||||||
|
}
|
||||||
|
|
||||||
|
if (library.language == Language.OBJECTIVE_C) {
|
||||||
|
if (name == "BOOL" || name == "Boolean") {
|
||||||
|
assert(clang_Type_getSizeOf(type) == 1L)
|
||||||
|
return BoolType
|
||||||
|
}
|
||||||
|
|
||||||
|
if (underlying is ObjCPointer && (name == "Class" || name == "id") ||
|
||||||
|
underlying is PointerType && name == "SEL") {
|
||||||
|
|
||||||
|
// Ignore implicit Objective-C typedefs:
|
||||||
|
return underlying
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if ((underlying is RecordType && underlying.decl.spelling.split(' ').last() == name) ||
|
if ((underlying is RecordType && underlying.decl.spelling.split(' ').last() == name) ||
|
||||||
(underlying is EnumType && underlying.def.spelling.split(' ').last() == name)) {
|
(underlying is EnumType && underlying.def.spelling.split(' ').last() == name)) {
|
||||||
@@ -189,10 +341,36 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun convertCursorType(cursor: CValue<CXCursor>) =
|
/**
|
||||||
convertType(clang_getCursorType(cursor))
|
* Computes [StructDef.hasUnalignedFields] property.
|
||||||
|
*/
|
||||||
|
fun structHasUnalignedFields(structDefCursor: CValue<CXCursor>): Boolean {
|
||||||
|
var hasUnalignedFields = false
|
||||||
|
visitChildren(structDefCursor) { child, _ ->
|
||||||
|
if (clang_getCursorKind(child) == CXCursorKind.CXCursor_FieldDecl &&
|
||||||
|
clang_Cursor_isBitField(child) == 0 &&
|
||||||
|
clang_Cursor_getOffsetOfField(child) %
|
||||||
|
(clang_Type_getAlignOf(clang_getCursorType(child)) * 8) != 0L) {
|
||||||
|
|
||||||
fun convertType(type: CValue<CXType>): Type {
|
hasUnalignedFields = true
|
||||||
|
CXChildVisitResult.CXChildVisit_Break
|
||||||
|
} else {
|
||||||
|
CXChildVisitResult.CXChildVisit_Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasUnalignedFields
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun convertCursorType(cursor: CValue<CXCursor>) =
|
||||||
|
convertType(clang_getCursorType(cursor), clang_getDeclTypeAttributes(cursor))
|
||||||
|
|
||||||
|
private inline fun objCType(supplier: () -> ObjCPointer) = when (library.language) {
|
||||||
|
Language.C -> UnsupportedType
|
||||||
|
Language.OBJECTIVE_C -> supplier()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun convertType(type: CValue<CXType>, typeAttributes: CValue<CXTypeAttributes>? = null): Type {
|
||||||
val primitiveType = convertUnqualifiedPrimitiveType(type)
|
val primitiveType = convertUnqualifiedPrimitiveType(type)
|
||||||
if (primitiveType != UnsupportedType) {
|
if (primitiveType != UnsupportedType) {
|
||||||
return primitiveType
|
return primitiveType
|
||||||
@@ -217,7 +395,17 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() {
|
|||||||
|
|
||||||
CXType_Void -> VoidType
|
CXType_Void -> VoidType
|
||||||
|
|
||||||
CXType_Typedef -> getTypedef(type)
|
CXType_Typedef -> {
|
||||||
|
val declCursor = clang_getTypeDeclaration(type)
|
||||||
|
val declSpelling = getCursorSpelling(declCursor)
|
||||||
|
val underlying = convertType(clang_getTypedefDeclUnderlyingType(declCursor))
|
||||||
|
when {
|
||||||
|
declSpelling == "instancetype" && underlying is ObjCPointer ->
|
||||||
|
ObjCInstanceType(getNullability(type, typeAttributes))
|
||||||
|
|
||||||
|
else -> getTypedef(type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CXType_Record -> RecordType(getStructDeclAt(clang_getTypeDeclaration(type)))
|
CXType_Record -> RecordType(getStructDeclAt(clang_getTypeDeclaration(type)))
|
||||||
CXType_Enum -> EnumType(getEnumDefAt(clang_getTypeDeclaration(type)))
|
CXType_Enum -> EnumType(getEnumDefAt(clang_getTypeDeclaration(type)))
|
||||||
@@ -245,16 +433,58 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() {
|
|||||||
convertFunctionType(type)
|
convertFunctionType(type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CXType_ObjCObjectPointer -> objCType {
|
||||||
|
val declaration = clang_getTypeDeclaration(clang_getPointeeType(type))
|
||||||
|
val declarationKind = declaration.kind
|
||||||
|
val nullability = getNullability(type, typeAttributes)
|
||||||
|
when (declarationKind) {
|
||||||
|
CXCursorKind.CXCursor_NoDeclFound -> ObjCIdType(nullability, getProtocols(type))
|
||||||
|
|
||||||
|
CXCursorKind.CXCursor_ObjCInterfaceDecl ->
|
||||||
|
ObjCObjectPointer(getObjCClassAt(declaration), nullability, getProtocols(type))
|
||||||
|
|
||||||
|
else -> TODO(declarationKind.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CXType_ObjCId -> objCType { ObjCIdType(getNullability(type, typeAttributes), getProtocols(type)) }
|
||||||
|
|
||||||
|
CXType_ObjCClass -> objCType { ObjCClassPointer(getNullability(type, typeAttributes), getProtocols(type)) }
|
||||||
|
|
||||||
|
CXType_ObjCSel -> PointerType(VoidType)
|
||||||
|
|
||||||
|
CXType_BlockPointer -> objCType { ObjCIdType(getNullability(type, typeAttributes), getProtocols(type)) }
|
||||||
|
|
||||||
else -> UnsupportedType
|
else -> UnsupportedType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getNullability(
|
||||||
|
type: CValue<CXType>, typeAttributes: CValue<CXTypeAttributes>?
|
||||||
|
): ObjCPointer.Nullability {
|
||||||
|
|
||||||
|
if (typeAttributes == null) return ObjCPointer.Nullability.Unspecified
|
||||||
|
|
||||||
|
return when (clang_Type_getNullabilityKind(type, typeAttributes)) {
|
||||||
|
CXNullabilityKind.CXNullabilityKind_Nullable -> ObjCPointer.Nullability.Nullable
|
||||||
|
CXNullabilityKind.CXNullabilityKind_NonNull -> ObjCPointer.Nullability.NonNull
|
||||||
|
CXNullabilityKind.CXNullabilityKind_Unspecified -> ObjCPointer.Nullability.Unspecified
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getProtocols(type: CValue<CXType>): List<ObjCProtocol> {
|
||||||
|
val num = clang_Type_getNumProtocols(type)
|
||||||
|
return (0 until num).mapNotNull { index ->
|
||||||
|
getObjCProtocolAt(clang_Type_getProtocol(type, index))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun convertFunctionType(type: CValue<CXType>): Type {
|
private fun convertFunctionType(type: CValue<CXType>): Type {
|
||||||
val kind = type.kind
|
val kind = type.kind
|
||||||
assert (kind == CXType_Unexposed || kind == CXType_FunctionProto)
|
assert (kind == CXType_Unexposed || kind == CXType_FunctionProto)
|
||||||
|
|
||||||
return if (clang_isFunctionTypeVariadic(type) != 0) {
|
return if (clang_isFunctionTypeVariadic(type) != 0) {
|
||||||
UnsupportedType
|
VoidType // make this function pointer opaque.
|
||||||
} else {
|
} else {
|
||||||
val returnType = convertType(clang_getResultType(type))
|
val returnType = convertType(clang_getResultType(type))
|
||||||
val numArgs = clang_getNumArgTypes(type)
|
val numArgs = clang_getNumArgTypes(type)
|
||||||
@@ -286,38 +516,160 @@ internal class NativeIndexImpl(val library: NativeLibrary) : NativeIndex() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CXIdxEntity_Function -> {
|
CXIdxEntity_Function -> {
|
||||||
val name = entityName!!
|
if (isAvailable(cursor)) {
|
||||||
val returnType = convertType(clang_getCursorResultType(cursor))
|
functionById[getDeclarationId(cursor)] = getFunction(cursor)
|
||||||
val argNum = clang_Cursor_getNumArguments(cursor)
|
|
||||||
val args = (0 .. argNum - 1).map {
|
|
||||||
val argCursor = clang_Cursor_getArgument(cursor, it)
|
|
||||||
val argName = getCursorSpelling(argCursor)
|
|
||||||
val type = convertCursorType(argCursor)
|
|
||||||
Parameter(argName, type)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val binaryName = when (library.language) {
|
|
||||||
Language.C -> clang_Cursor_getMangling(cursor).convertAndDispose()
|
|
||||||
}
|
|
||||||
|
|
||||||
val definitionCursor = clang_getCursorDefinition(cursor)
|
|
||||||
val isDefined = (clang_Cursor_isNull(definitionCursor) == 0)
|
|
||||||
|
|
||||||
val isVararg = clang_Cursor_isVariadic(cursor) != 0
|
|
||||||
|
|
||||||
functionByName[name] = FunctionDecl(name, args, returnType, binaryName, isDefined, isVararg)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CXIdxEntity_Enum -> {
|
CXIdxEntity_Enum -> {
|
||||||
getEnumDefAt(cursor)
|
getEnumDefAt(cursor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CXIdxEntity_ObjCClass -> {
|
||||||
|
if (isAvailable(cursor)) {
|
||||||
|
getObjCClassAt(clang_getCursorReferenced(cursor))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CXIdxEntity_ObjCCategory -> {
|
||||||
|
val classCursor = getObjCCategoryClassCursor(cursor)
|
||||||
|
if (isAvailable(classCursor)) {
|
||||||
|
val objCClass = getObjCClassAt(classCursor)
|
||||||
|
addChildrenToClassOrProtocol(cursor, objCClass)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CXIdxEntity_ObjCProtocol -> {
|
||||||
|
if (isAvailable(cursor)) {
|
||||||
|
getObjCProtocolAt(cursor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CXIdxEntity_ObjCProperty -> {
|
||||||
|
val container = clang_getCursorSemanticParent(cursor)
|
||||||
|
if (isAvailable(cursor) && isAvailable(container)) {
|
||||||
|
val propertyInfo = clang_index_getObjCPropertyDeclInfo(info.ptr)!!.pointed
|
||||||
|
val getter = getObjCMethod(propertyInfo.getter!!.pointed.cursor.readValue())
|
||||||
|
val setter = propertyInfo.setter?.let {
|
||||||
|
getObjCMethod(it.pointed.cursor.readValue())
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
// Ignore declaration.
|
// Ignore declaration.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getFunction(cursor: CValue<CXCursor>): FunctionDecl {
|
||||||
|
val name = clang_getCursorSpelling(cursor).convertAndDispose()
|
||||||
|
val returnType = convertType(clang_getCursorResultType(cursor), clang_getCursorResultTypeAttributes(cursor))
|
||||||
|
|
||||||
|
val parameters = getFunctionParameters(cursor)
|
||||||
|
|
||||||
|
val binaryName = when (library.language) {
|
||||||
|
Language.C, Language.OBJECTIVE_C -> clang_Cursor_getMangling(cursor).convertAndDispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
val definitionCursor = clang_getCursorDefinition(cursor)
|
||||||
|
val isDefined = (clang_Cursor_isNull(definitionCursor) == 0)
|
||||||
|
|
||||||
|
val isVararg = clang_Cursor_isVariadic(cursor) != 0
|
||||||
|
|
||||||
|
return FunctionDecl(name, parameters, returnType, binaryName, isDefined, isVararg)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getObjCMethod(cursor: CValue<CXCursor>): ObjCMethod? {
|
||||||
|
if (!isAvailable(cursor)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val selector = clang_getCursorDisplayName(cursor).convertAndDispose()
|
||||||
|
|
||||||
|
// Ignore some very special methods:
|
||||||
|
when (selector) {
|
||||||
|
"dealloc", "retain", "release", "autorelease", "retainCount", "self" -> return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val encoding = clang_getDeclObjCTypeEncoding(cursor).convertAndDispose()
|
||||||
|
val returnType = convertType(clang_getCursorResultType(cursor), clang_getCursorResultTypeAttributes(cursor))
|
||||||
|
val parameters = getFunctionParameters(cursor)
|
||||||
|
|
||||||
|
val isClass = when (cursor.kind) {
|
||||||
|
CXCursorKind.CXCursor_ObjCClassMethodDecl -> true
|
||||||
|
CXCursorKind.CXCursor_ObjCInstanceMethodDecl -> false
|
||||||
|
else -> error(cursor.kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ObjCMethod(selector, encoding, parameters, returnType,
|
||||||
|
isClass = isClass,
|
||||||
|
nsConsumesSelf = hasAttribute(cursor, NS_CONSUMES_SELF),
|
||||||
|
nsReturnsRetained = hasAttribute(cursor, NS_RETURNS_RETAINED),
|
||||||
|
isOptional = (clang_Cursor_isObjCOptional(cursor) != 0),
|
||||||
|
isInit = (clang_Cursor_isObjCInitMethod(cursor) != 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: unavailable declarations should be imported as deprecated.
|
||||||
|
private fun isAvailable(cursor: CValue<CXCursor>): Boolean = when (clang_getCursorAvailability(cursor)) {
|
||||||
|
CXAvailabilityKind.CXAvailability_Available,
|
||||||
|
CXAvailabilityKind.CXAvailability_Deprecated -> true
|
||||||
|
|
||||||
|
CXAvailabilityKind.CXAvailability_NotAvailable,
|
||||||
|
CXAvailabilityKind.CXAvailability_NotAccessible -> false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getFunctionParameters(cursor: CValue<CXCursor>): List<Parameter> {
|
||||||
|
val argNum = clang_Cursor_getNumArguments(cursor)
|
||||||
|
val args = (0..argNum - 1).map {
|
||||||
|
val argCursor = clang_Cursor_getArgument(cursor, it)
|
||||||
|
val argName = getCursorSpelling(argCursor)
|
||||||
|
val type = convertCursorType(argCursor)
|
||||||
|
Parameter(argName, type,
|
||||||
|
nsConsumed = hasAttribute(argCursor, NS_CONSUMED))
|
||||||
|
}
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
private val NS_CONSUMED = "ns_consumed"
|
||||||
|
private val NS_CONSUMES_SELF = "ns_consumes_self"
|
||||||
|
private val NS_RETURNS_RETAINED = "ns_returns_retained"
|
||||||
|
|
||||||
|
private fun hasAttribute(cursor: CValue<CXCursor>, name: String): Boolean {
|
||||||
|
var result = false
|
||||||
|
visitChildren(cursor) { child, _ ->
|
||||||
|
if (clang_isAttribute(child.kind) != 0 && clang_Cursor_getAttributeSpelling(child)?.toKString() == name) {
|
||||||
|
result = true
|
||||||
|
CXChildVisitResult.CXChildVisit_Break
|
||||||
|
} else {
|
||||||
|
CXChildVisitResult.CXChildVisit_Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun buildNativeIndexImpl(library: NativeLibrary): NativeIndex {
|
fun buildNativeIndexImpl(library: NativeLibrary): NativeIndex {
|
||||||
|
|||||||
+2
-2
@@ -27,7 +27,7 @@ internal fun findMacroConstants(library: NativeLibrary, nativeIndex: NativeIndex
|
|||||||
val names = collectMacroConstantsNames(library)
|
val names = collectMacroConstantsNames(library)
|
||||||
// TODO: apply user-defined filters.
|
// TODO: apply user-defined filters.
|
||||||
|
|
||||||
val constants = expandMacroConstants(library, names, typeConverter = nativeIndex::convertType)
|
val constants = expandMacroConstants(library, names, typeConverter = { nativeIndex.convertType(it) })
|
||||||
|
|
||||||
nativeIndex.macroConstants.addAll(constants)
|
nativeIndex.macroConstants.addAll(constants)
|
||||||
}
|
}
|
||||||
@@ -109,7 +109,7 @@ private fun reparseWithCodeSnippet(library: NativeLibrary,
|
|||||||
// so the code pattern should force the constant evaluation that corresponds to language rules.
|
// so the code pattern should force the constant evaluation that corresponds to language rules.
|
||||||
val codeSnippetLines = when (library.language) {
|
val codeSnippetLines = when (library.language) {
|
||||||
// Note: __auto_type is a GNU extension which is supported by clang.
|
// Note: __auto_type is a GNU extension which is supported by clang.
|
||||||
Language.C -> listOf(
|
Language.C, Language.OBJECTIVE_C -> listOf(
|
||||||
"const __auto_type KNI_INDEXER_VARIABLE = $name;",
|
"const __auto_type KNI_INDEXER_VARIABLE = $name;",
|
||||||
// Clang evaluate API doesn't provide a way to get a `long long` value yet;
|
// Clang evaluate API doesn't provide a way to get a `long long` value yet;
|
||||||
// so extract such values from the enum declaration:
|
// so extract such values from the enum declaration:
|
||||||
|
|||||||
+74
-5
@@ -16,8 +16,9 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.native.interop.indexer
|
package org.jetbrains.kotlin.native.interop.indexer
|
||||||
|
|
||||||
enum class Language {
|
enum class Language(val sourceFileExtension: String) {
|
||||||
C
|
C("c"),
|
||||||
|
OBJECTIVE_C("m")
|
||||||
}
|
}
|
||||||
|
|
||||||
data class NativeLibrary(val includes: List<String>,
|
data class NativeLibrary(val includes: List<String>,
|
||||||
@@ -39,6 +40,8 @@ fun buildNativeIndex(library: NativeLibrary): NativeIndex = buildNativeIndexImpl
|
|||||||
abstract class NativeIndex {
|
abstract class NativeIndex {
|
||||||
abstract val structs: List<StructDecl>
|
abstract val structs: List<StructDecl>
|
||||||
abstract val enums: List<EnumDef>
|
abstract val enums: List<EnumDef>
|
||||||
|
abstract val objCClasses: List<ObjCClass>
|
||||||
|
abstract val objCProtocols: List<ObjCProtocol>
|
||||||
abstract val typedefs: List<TypedefDef>
|
abstract val typedefs: List<TypedefDef>
|
||||||
abstract val functions: List<FunctionDecl>
|
abstract val functions: List<FunctionDecl>
|
||||||
abstract val macroConstants: List<ConstantDef>
|
abstract val macroConstants: List<ConstantDef>
|
||||||
@@ -65,7 +68,8 @@ abstract class StructDecl(val spelling: String) {
|
|||||||
*/
|
*/
|
||||||
abstract class StructDef(val size: Long, val align: Int,
|
abstract class StructDef(val size: Long, val align: Int,
|
||||||
val decl: StructDecl,
|
val decl: StructDecl,
|
||||||
val hasNaturalLayout: Boolean) {
|
val hasNaturalLayout: Boolean,
|
||||||
|
val hasUnalignedFields: Boolean) {
|
||||||
|
|
||||||
abstract val fields: List<Field>
|
abstract val fields: List<Field>
|
||||||
}
|
}
|
||||||
@@ -78,15 +82,48 @@ class EnumConstant(val name: String, val value: Long, val isExplicitlyDefined: B
|
|||||||
/**
|
/**
|
||||||
* C enum definition.
|
* C enum definition.
|
||||||
*/
|
*/
|
||||||
abstract class EnumDef(val spelling: String, val baseType: PrimitiveType) {
|
abstract class EnumDef(val spelling: String, val baseType: Type) {
|
||||||
|
|
||||||
abstract val constants: List<EnumConstant>
|
abstract val constants: List<EnumConstant>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sealed class ObjCClassOrProtocol(val name: String) {
|
||||||
|
abstract val protocols: List<ObjCProtocol>
|
||||||
|
abstract val methods: List<ObjCMethod>
|
||||||
|
abstract val properties: List<ObjCProperty>
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
val isOptional: Boolean, val isInit: Boolean
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun returnsInstancetype(): Boolean = returnType is ObjCInstanceType
|
||||||
|
|
||||||
|
fun getReturnType(container: ObjCClassOrProtocol): Type = if (returnType is ObjCInstanceType) {
|
||||||
|
when (container) {
|
||||||
|
is ObjCClass -> ObjCObjectPointer(container, returnType.nullability, protocols = emptyList())
|
||||||
|
is ObjCProtocol -> ObjCIdType(returnType.nullability, protocols = listOf(container))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
returnType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ObjCProperty(val name: String, val getter: ObjCMethod, val setter: ObjCMethod?) {
|
||||||
|
fun getType(container: ObjCClassOrProtocol): Type = getter.getReturnType(container)
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class ObjCClass(name: String) : ObjCClassOrProtocol(name) {
|
||||||
|
abstract val baseClass: ObjCClass?
|
||||||
|
}
|
||||||
|
abstract class ObjCProtocol(name: String) : ObjCClassOrProtocol(name)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* C function parameter.
|
* C function parameter.
|
||||||
*/
|
*/
|
||||||
class Parameter(val name: String?, val type: Type)
|
class Parameter(val name: String?, val type: Type, val nsConsumed: Boolean)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* C function declaration.
|
* C function declaration.
|
||||||
@@ -117,6 +154,8 @@ interface PrimitiveType : Type
|
|||||||
|
|
||||||
object CharType : PrimitiveType
|
object CharType : PrimitiveType
|
||||||
|
|
||||||
|
object BoolType : PrimitiveType
|
||||||
|
|
||||||
data class IntegerType(val size: Int, val isSigned: Boolean, val spelling: String) : PrimitiveType
|
data class IntegerType(val size: Int, val isSigned: Boolean, val spelling: String) : PrimitiveType
|
||||||
|
|
||||||
// TODO: floating type is not actually defined entirely by its size.
|
// TODO: floating type is not actually defined entirely by its size.
|
||||||
@@ -142,4 +181,34 @@ data class IncompleteArrayType(override val elemType: Type) : ArrayType
|
|||||||
|
|
||||||
data class Typedef(val def: TypedefDef) : Type
|
data class Typedef(val def: TypedefDef) : Type
|
||||||
|
|
||||||
|
sealed class ObjCPointer : Type {
|
||||||
|
enum class Nullability {
|
||||||
|
Nullable, NonNull, Unspecified
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract val nullability: Nullability
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class ObjCQualifiedPointer : ObjCPointer() {
|
||||||
|
abstract val protocols: List<ObjCProtocol>
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ObjCObjectPointer(
|
||||||
|
val def: ObjCClass,
|
||||||
|
override val nullability: Nullability,
|
||||||
|
override val protocols: List<ObjCProtocol>
|
||||||
|
) : ObjCQualifiedPointer()
|
||||||
|
|
||||||
|
data class ObjCClassPointer(
|
||||||
|
override val nullability: Nullability,
|
||||||
|
override val protocols: List<ObjCProtocol>
|
||||||
|
) : ObjCQualifiedPointer()
|
||||||
|
|
||||||
|
data class ObjCIdType(
|
||||||
|
override val nullability: Nullability,
|
||||||
|
override val protocols: List<ObjCProtocol>
|
||||||
|
) : ObjCQualifiedPointer()
|
||||||
|
|
||||||
|
data class ObjCInstanceType(override val nullability: Nullability) : ObjCPointer()
|
||||||
|
|
||||||
object UnsupportedType : Type
|
object UnsupportedType : Type
|
||||||
+7
-4
@@ -176,10 +176,7 @@ internal fun Appendable.appendPreamble(library: NativeLibrary) = this.apply {
|
|||||||
* Creates temporary source file which includes the library.
|
* Creates temporary source file which includes the library.
|
||||||
*/
|
*/
|
||||||
internal fun NativeLibrary.createTempSource(): File {
|
internal fun NativeLibrary.createTempSource(): File {
|
||||||
val suffix = when (language) {
|
val result = createTempFile(suffix = ".${language.sourceFileExtension}")
|
||||||
Language.C -> ".c"
|
|
||||||
}
|
|
||||||
val result = createTempFile(suffix = suffix)
|
|
||||||
result.deleteOnExit()
|
result.deleteOnExit()
|
||||||
|
|
||||||
result.bufferedWriter().use { writer ->
|
result.bufferedWriter().use { writer ->
|
||||||
@@ -479,3 +476,9 @@ internal fun getFilteredHeaders(library: NativeLibrary, index: CXIndex, translat
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun ObjCMethod.replaces(other: ObjCMethod): Boolean =
|
||||||
|
this.isClass == other.isClass && this.selector == other.selector
|
||||||
|
|
||||||
|
fun ObjCProperty.replaces(other: ObjCProperty): Boolean =
|
||||||
|
this.getter.replaces(other.getter)
|
||||||
|
|||||||
@@ -379,22 +379,7 @@ private class Struct(val size: Long, val align: Int, elementTypes: List<CType<*>
|
|||||||
) {
|
) {
|
||||||
override fun read(location: NativePtr) = interpretPointed<ByteVar>(location).readValue<CStructVar>(size, align)
|
override fun read(location: NativePtr) = interpretPointed<ByteVar>(location).readValue<CStructVar>(size, align)
|
||||||
|
|
||||||
override fun write(location: NativePtr, value: CValue<*>) {
|
override fun write(location: NativePtr, value: CValue<*>) = value.write(location)
|
||||||
// TODO: probably CValue must be redesigned.
|
|
||||||
val fakePlacement = object : NativePlacement {
|
|
||||||
var used = false
|
|
||||||
override fun alloc(size: Long, align: Int): NativePointed {
|
|
||||||
assert(!used)
|
|
||||||
assert (size == this@Struct.size)
|
|
||||||
assert (align == this@Struct.align)
|
|
||||||
used = true
|
|
||||||
return interpretPointed<ByteVar>(location)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
value.getPointer(fakePlacement)
|
|
||||||
assert (fakePlacement.used)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CEnumType(private val rawValueCType: CType<Number>) : CType<CEnum>(rawValueCType.ffiType) {
|
private class CEnumType(private val rawValueCType: CType<Number>) : CType<CEnum>(rawValueCType.ffiType) {
|
||||||
|
|||||||
@@ -67,6 +67,25 @@ abstract class CValuesRef<T : CPointed> {
|
|||||||
abstract fun getPointer(placement: NativePlacement): CPointer<T>
|
abstract fun getPointer(placement: NativePlacement): CPointer<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline fun <T : CPointed, R> CValuesRef<T>?.usePointer(block: (CPointer<T>?) -> R): R {
|
||||||
|
val allocated: Boolean
|
||||||
|
val pointer: CPointer<T>? = if (this is CPointer<T>?) {
|
||||||
|
allocated = false
|
||||||
|
this
|
||||||
|
} else {
|
||||||
|
allocated = true
|
||||||
|
this!!.getPointer(nativeHeap)
|
||||||
|
}
|
||||||
|
|
||||||
|
return try {
|
||||||
|
block(pointer)
|
||||||
|
} finally {
|
||||||
|
if (allocated) {
|
||||||
|
nativeHeap.free(pointer.rawValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The (possibly empty) sequence of immutable C values.
|
* The (possibly empty) sequence of immutable C values.
|
||||||
* It is self-contained and doesn't depend on native memory.
|
* It is self-contained and doesn't depend on native memory.
|
||||||
@@ -241,6 +260,11 @@ abstract class CEnumVar : CPrimitiveVar()
|
|||||||
// generics below are used for typedef support
|
// generics below are used for typedef support
|
||||||
// these classes are not supposed to be used directly, instead the typealiases are provided.
|
// these classes are not supposed to be used directly, instead the typealiases are provided.
|
||||||
|
|
||||||
|
@Suppress("FINAL_UPPER_BOUND")
|
||||||
|
class BooleanVarOf<T : Boolean>(override val rawPtr: NativePtr) : CPrimitiveVar() {
|
||||||
|
companion object : Type(1)
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("FINAL_UPPER_BOUND")
|
@Suppress("FINAL_UPPER_BOUND")
|
||||||
class ByteVarOf<T : Byte>(override val rawPtr: NativePtr) : CPrimitiveVar() {
|
class ByteVarOf<T : Byte>(override val rawPtr: NativePtr) : CPrimitiveVar() {
|
||||||
companion object : Type(1)
|
companion object : Type(1)
|
||||||
@@ -271,6 +295,7 @@ class DoubleVarOf<T : Double>(override val rawPtr: NativePtr) : CPrimitiveVar()
|
|||||||
companion object : Type(8)
|
companion object : Type(8)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typealias BooleanVar = BooleanVarOf<Boolean>
|
||||||
typealias ByteVar = ByteVarOf<Byte>
|
typealias ByteVar = ByteVarOf<Byte>
|
||||||
typealias ShortVar = ShortVarOf<Short>
|
typealias ShortVar = ShortVarOf<Short>
|
||||||
typealias IntVar = IntVarOf<Int>
|
typealias IntVar = IntVarOf<Int>
|
||||||
@@ -278,6 +303,20 @@ typealias LongVar = LongVarOf<Long>
|
|||||||
typealias FloatVar = FloatVarOf<Float>
|
typealias FloatVar = FloatVarOf<Float>
|
||||||
typealias DoubleVar = DoubleVarOf<Double>
|
typealias DoubleVar = DoubleVarOf<Double>
|
||||||
|
|
||||||
|
@Suppress("FINAL_UPPER_BOUND", "UNCHECKED_CAST")
|
||||||
|
var <T : Boolean> BooleanVarOf<T>.value: T
|
||||||
|
get() {
|
||||||
|
val byte = nativeMemUtils.getByte(this)
|
||||||
|
return byte.toBoolean() as T
|
||||||
|
}
|
||||||
|
set(value) = nativeMemUtils.putByte(this, value.toByte())
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
inline fun Boolean.toByte(): Byte = if (this) 1 else 0
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
inline fun Byte.toBoolean() = (this - 0 != 0)
|
||||||
|
|
||||||
@Suppress("FINAL_UPPER_BOUND", "UNCHECKED_CAST")
|
@Suppress("FINAL_UPPER_BOUND", "UNCHECKED_CAST")
|
||||||
var <T : Byte> ByteVarOf<T>.value: T
|
var <T : Byte> ByteVarOf<T>.value: T
|
||||||
get() = nativeMemUtils.getByte(this) as T
|
get() = nativeMemUtils.getByte(this) as T
|
||||||
|
|||||||
@@ -218,6 +218,21 @@ fun <T : CVariable> CPointed.readValue(size: Long, align: Int): CValue<T> {
|
|||||||
// TODO: find better name.
|
// TODO: find better name.
|
||||||
inline fun <reified T : CStructVar> T.readValue(): CValue<T> = this.readValue(sizeOf<T>(), alignOf<T>())
|
inline fun <reified T : CStructVar> T.readValue(): CValue<T> = this.readValue(sizeOf<T>(), alignOf<T>())
|
||||||
|
|
||||||
|
fun CValue<*>.write(location: NativePtr) {
|
||||||
|
// TODO: probably CValue must be redesigned.
|
||||||
|
val fakePlacement = object : NativePlacement {
|
||||||
|
var used = false
|
||||||
|
override fun alloc(size: Long, align: Int): NativePointed {
|
||||||
|
assert(!used)
|
||||||
|
used = true
|
||||||
|
return interpretPointed<ByteVar>(location)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getPointer(fakePlacement)
|
||||||
|
assert(fakePlacement.used)
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: optimize
|
// TODO: optimize
|
||||||
fun <T : CVariable> CValues<T>.getBytes(): ByteArray = memScoped {
|
fun <T : CVariable> CValues<T>.getBytes(): ByteArray = memScoped {
|
||||||
val result = ByteArray(size)
|
val result = ByteArray(size)
|
||||||
|
|||||||
@@ -0,0 +1,156 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@file:Suppress("NOTHING_TO_INLINE")
|
||||||
|
|
||||||
|
package kotlinx.cinterop
|
||||||
|
|
||||||
|
interface ObjCObject
|
||||||
|
interface ObjCClass : ObjCObject
|
||||||
|
typealias ObjCObjectMeta = ObjCClass
|
||||||
|
|
||||||
|
abstract class ObjCObjectBase protected constructor() : ObjCObject {
|
||||||
|
final override fun equals(other: Any?): Boolean = TODO()
|
||||||
|
final override fun hashCode(): Int = TODO()
|
||||||
|
final override fun toString(): String = TODO()
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class ObjCObjectBaseMeta protected constructor() : ObjCObjectBase(), ObjCObjectMeta {}
|
||||||
|
|
||||||
|
external fun optional(): Nothing
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The runtime representation of any [ObjCObject].
|
||||||
|
*/
|
||||||
|
@ExportTypeInfo("theObjCPointerHolderTypeInfo")
|
||||||
|
class ObjCPointerHolder(inline val rawPtr: NativePtr) {
|
||||||
|
init {
|
||||||
|
assert(rawPtr != nativeNullPtr)
|
||||||
|
objc_retain(rawPtr)
|
||||||
|
}
|
||||||
|
|
||||||
|
final override fun equals(other: Any?): Boolean = TODO()
|
||||||
|
final override fun hashCode(): Int = TODO()
|
||||||
|
final override fun toString(): String = TODO()
|
||||||
|
}
|
||||||
|
|
||||||
|
@konan.internal.Intrinsic
|
||||||
|
@konan.internal.ExportForCompiler
|
||||||
|
private external fun ObjCObject.initFromPtr(ptr: NativePtr)
|
||||||
|
|
||||||
|
@konan.internal.ExportForCompiler
|
||||||
|
private fun ObjCObject.initFrom(other: ObjCObject?) = this.initFromPtr(other!!.rawPtr)
|
||||||
|
|
||||||
|
fun <T : Any?> Any?.uncheckedCast(): T = @Suppress("UNCHECKED_CAST") (this as T) // TODO: make private
|
||||||
|
|
||||||
|
inline fun <T : ObjCObject?> interpretObjCPointerOrNull(rawPtr: NativePtr): T? = if (rawPtr != nativeNullPtr) {
|
||||||
|
ObjCPointerHolder(rawPtr).uncheckedCast<T>()
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <T : ObjCObject?> interpretObjCPointer(rawPtr: NativePtr): T? = interpretObjCPointerOrNull<T>(rawPtr)!!
|
||||||
|
|
||||||
|
inline val ObjCObject.rawPtr: NativePtr get() = (this.uncheckedCast<ObjCPointerHolder>()).rawPtr
|
||||||
|
inline val ObjCObject?.rawPtr: NativePtr get() = if (this != null) {
|
||||||
|
(this.uncheckedCast<ObjCPointerHolder>()).rawPtr
|
||||||
|
} else {
|
||||||
|
nativeNullPtr
|
||||||
|
}
|
||||||
|
|
||||||
|
class ObjCObjectVar<T : ObjCObject?>(override val rawPtr: NativePtr) : CVariable {
|
||||||
|
companion object : CVariable.Type(pointerSize.toLong(), pointerSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
class ObjCStringVarOf<T : String?>(override val rawPtr: NativePtr) : CVariable {
|
||||||
|
companion object : CVariable.Type(pointerSize.toLong(), pointerSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
var <T : String?> ObjCStringVarOf<T>.value: T
|
||||||
|
get() = TODO()
|
||||||
|
set(value) = TODO()
|
||||||
|
|
||||||
|
@konan.internal.Intrinsic external fun getReceiverOrSuper(receiver: NativePtr, superClass: NativePtr): COpaquePointer?
|
||||||
|
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
annotation class ExternalObjCClass()
|
||||||
|
|
||||||
|
@Target(AnnotationTarget.FUNCTION)
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
annotation class ObjCMethod(val selector: String, val bridge: String)
|
||||||
|
|
||||||
|
@Target(AnnotationTarget.FUNCTION)
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
annotation class ObjCBridge(val selector: String, val encoding: String, val imp: String)
|
||||||
|
|
||||||
|
@Target(AnnotationTarget.CONSTRUCTOR)
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
annotation class ObjCConstructor(val initSelector: String)
|
||||||
|
|
||||||
|
@Target(AnnotationTarget.FILE)
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
annotation class InteropStubs()
|
||||||
|
|
||||||
|
@konan.internal.ExportForCompiler
|
||||||
|
private fun <T : ObjCObject> allocObjCObject(clazz: NativePtr): T {
|
||||||
|
val rawResult = objc_allocWithZone(clazz)
|
||||||
|
if (rawResult == nativeNullPtr) {
|
||||||
|
throw OutOfMemoryError("Unable to allocate Objective-C object")
|
||||||
|
}
|
||||||
|
|
||||||
|
val result = interpretObjCPointerOrNull<T>(rawResult)!!
|
||||||
|
// `objc_allocWithZone` returns retained pointer. Balance it:
|
||||||
|
objc_release(rawResult)
|
||||||
|
// TODO: do not retain this pointer in `interpretObjCPointerOrNull` instead.
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
@konan.internal.Intrinsic
|
||||||
|
@konan.internal.ExportForCompiler
|
||||||
|
private external fun <T : ObjCObject> getObjCClass(): NativePtr
|
||||||
|
|
||||||
|
@konan.internal.Intrinsic external fun getMessenger(superClass: NativePtr): COpaquePointer?
|
||||||
|
@konan.internal.Intrinsic external fun getMessengerLU(superClass: NativePtr): COpaquePointer?
|
||||||
|
|
||||||
|
// Konan runtme:
|
||||||
|
|
||||||
|
@SymbolName("CreateNSStringFromKString")
|
||||||
|
external fun CreateNSStringFromKString(str: String?): NativePtr
|
||||||
|
|
||||||
|
@SymbolName("CreateKStringFromNSString")
|
||||||
|
external fun CreateKStringFromNSString(ptr: NativePtr): String?
|
||||||
|
|
||||||
|
// Objective-C runtime:
|
||||||
|
|
||||||
|
@SymbolName("objc_retainAutoreleaseReturnValue")
|
||||||
|
external fun objc_retainAutoreleaseReturnValue(ptr: NativePtr): NativePtr
|
||||||
|
|
||||||
|
@SymbolName("Kotlin_objc_autoreleasePoolPush")
|
||||||
|
external fun objc_autoreleasePoolPush(): NativePtr
|
||||||
|
|
||||||
|
@SymbolName("Kotlin_objc_autoreleasePoolPop")
|
||||||
|
external fun objc_autoreleasePoolPop(ptr: NativePtr)
|
||||||
|
|
||||||
|
@SymbolName("Kotlin_objc_allocWithZone")
|
||||||
|
private external fun objc_allocWithZone(clazz: NativePtr): NativePtr
|
||||||
|
|
||||||
|
@SymbolName("Kotlin_objc_retain")
|
||||||
|
external fun objc_retain(ptr: NativePtr): NativePtr
|
||||||
|
|
||||||
|
@SymbolName("Kotlin_objc_release")
|
||||||
|
external fun objc_release(ptr: NativePtr)
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* 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 kotlinx.cinterop
|
||||||
|
|
||||||
|
inline fun <R> autoreleasepool(block: () -> R): R {
|
||||||
|
val pool = objc_autoreleasePoolPush()
|
||||||
|
return try {
|
||||||
|
block()
|
||||||
|
} finally {
|
||||||
|
objc_autoreleasePoolPop(pool)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: null checks
|
||||||
|
var <T : ObjCObject?> ObjCObjectVar<T>.value: T
|
||||||
|
get() = interpretObjCPointerOrNull<T>(nativeMemUtils.getNativePtr(this)).uncheckedCast<T>()
|
||||||
|
set(value) = nativeMemUtils.putNativePtr(this, value.rawPtr)
|
||||||
@@ -66,6 +66,11 @@ private tailrec fun convertArgument(
|
|||||||
|
|
||||||
is CEnum -> convertArgument(argument.value, isVariadic, location, additionalPlacement)
|
is CEnum -> convertArgument(argument.value, isVariadic, location, additionalPlacement)
|
||||||
|
|
||||||
|
is ObjCPointerHolder -> {
|
||||||
|
location.reinterpret<COpaquePointerVar>()[0] = interpretCPointer(argument.rawPtr)
|
||||||
|
FFI_TYPE_KIND_POINTER
|
||||||
|
}
|
||||||
|
|
||||||
else -> throw Error("unsupported argument: $argument")
|
else -> throw Error("unsupported argument: $argument")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+77
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
class NativeCodeBuilder {
|
||||||
|
val lines = mutableListOf<String>()
|
||||||
|
|
||||||
|
fun out(line: String): Unit {
|
||||||
|
lines.add(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun buildNativeCodeLines(block: NativeCodeBuilder.() -> Unit): List<String> {
|
||||||
|
val builder = NativeCodeBuilder()
|
||||||
|
builder.block()
|
||||||
|
return builder.lines
|
||||||
|
}
|
||||||
|
|
||||||
|
class KotlinCodeBuilder {
|
||||||
|
private val lines = mutableListOf<String>()
|
||||||
|
|
||||||
|
private val freeStack = mutableListOf<String>()
|
||||||
|
private val nesting get() = freeStack.size
|
||||||
|
|
||||||
|
fun out(line: String) {
|
||||||
|
lines.add(" ".repeat(nesting) + line)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun pushBlock(line: String, free: String = "") {
|
||||||
|
out(line)
|
||||||
|
freeStack.add(free)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun popBlocks() {
|
||||||
|
while (freeStack.isNotEmpty()) {
|
||||||
|
val free = freeStack.last()
|
||||||
|
freeStack.removeAt(freeStack.lastIndex)
|
||||||
|
out("} $free".trim())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun build(): List<String> {
|
||||||
|
this.popBlocks()
|
||||||
|
val result = this.lines.toList()
|
||||||
|
this.lines.clear()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun buildKotlinCodeLines(block: KotlinCodeBuilder.() -> Unit): List<String> {
|
||||||
|
val builder = KotlinCodeBuilder()
|
||||||
|
builder.block()
|
||||||
|
return builder.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StubGenerationContext {
|
||||||
|
val nativeBridges: NativeBridges
|
||||||
|
fun addTopLevelDeclaration(lines: List<String>)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KotlinStub {
|
||||||
|
fun generate(context: StubGenerationContext): Sequence<String>
|
||||||
|
}
|
||||||
+77
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
val kotlinKeywords = setOf(
|
||||||
|
"as", "break", "class", "continue", "do", "else", "false", "for", "fun", "if", "in",
|
||||||
|
"interface", "is", "null", "object", "package", "return", "super", "this", "throw",
|
||||||
|
"true", "try", "typealias", "val", "var", "when", "while"
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The expression written in native language.
|
||||||
|
*/
|
||||||
|
typealias NativeExpression = String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The expression written in Kotlin.
|
||||||
|
*/
|
||||||
|
typealias KotlinExpression = String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For this identifier constructs the string to be parsed by Kotlin as `SimpleName`
|
||||||
|
* defined [here](https://kotlinlang.org/docs/reference/grammar.html#SimpleName).
|
||||||
|
*/
|
||||||
|
fun String.asSimpleName(): String = if (this in kotlinKeywords) {
|
||||||
|
"`$this`"
|
||||||
|
} else {
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the expression to be parsed by Kotlin as string literal with given contents,
|
||||||
|
* i.e. transforms `foo$bar` to `"foo\$bar"`.
|
||||||
|
*/
|
||||||
|
fun String.quoteAsKotlinLiteral(): KotlinExpression {
|
||||||
|
val sb = StringBuilder()
|
||||||
|
sb.append('"')
|
||||||
|
|
||||||
|
this.forEach { c ->
|
||||||
|
val escaped = when (c) {
|
||||||
|
in 'a' .. 'z', in 'A' .. 'Z', in '0' .. '9', '_', '@', ':', '{', '}', '=', '[', ']', '^', '#', '*' -> c.toString()
|
||||||
|
'$' -> "\\$"
|
||||||
|
else -> "\\u" + "%04X".format(c.toInt()) // TODO: improve result readability by preserving more characters.
|
||||||
|
}
|
||||||
|
sb.append(escaped)
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append('"')
|
||||||
|
return sb.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun block(header: String, lines: Iterable<String>) = block(header, lines.asSequence())
|
||||||
|
|
||||||
|
fun block(header: String, lines: Sequence<String>) =
|
||||||
|
sequenceOf("$header {") +
|
||||||
|
lines.map { " $it" } +
|
||||||
|
sequenceOf("}")
|
||||||
|
|
||||||
|
val annotationForUnableToImport
|
||||||
|
get() = "@Deprecated(${"Unable to import this declaration".quoteAsKotlinLiteral()}, level = DeprecationLevel.ERROR)"
|
||||||
|
|
||||||
|
fun String.applyToStrings(vararg arguments: String) =
|
||||||
|
"${this}(${arguments.joinToString { it.quoteAsKotlinLiteral() }})"
|
||||||
+45
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* 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.Type
|
||||||
|
|
||||||
|
data class TypedKotlinValue(val type: Type, val value: KotlinExpression)
|
||||||
|
data class TypedNativeValue(val type: Type, val value: NativeExpression)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates bridges between Kotlin and native, passing arbitrary native-typed values.
|
||||||
|
*
|
||||||
|
* It does the same as [SimpleBridgeGenerator] except that it supports any native types, e.g. struct values.
|
||||||
|
*/
|
||||||
|
interface MappingBridgeGenerator {
|
||||||
|
fun kotlinToNative(
|
||||||
|
builder: KotlinCodeBuilder,
|
||||||
|
nativeBacked: NativeBacked,
|
||||||
|
returnType: Type,
|
||||||
|
kotlinValues: List<TypedKotlinValue>,
|
||||||
|
block: NativeCodeBuilder.(nativeValues: List<NativeExpression>) -> NativeExpression
|
||||||
|
): KotlinExpression
|
||||||
|
|
||||||
|
fun nativeToKotlin(
|
||||||
|
builder: NativeCodeBuilder,
|
||||||
|
nativeBacked: NativeBacked,
|
||||||
|
returnType: Type,
|
||||||
|
nativeValues: List<TypedNativeValue>,
|
||||||
|
block: KotlinCodeBuilder.(kotlinValues: List<KotlinExpression>) -> KotlinExpression
|
||||||
|
): NativeExpression
|
||||||
|
}
|
||||||
+196
@@ -0,0 +1,196 @@
|
|||||||
|
/*
|
||||||
|
* 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.RecordType
|
||||||
|
import org.jetbrains.kotlin.native.interop.indexer.Type
|
||||||
|
import org.jetbrains.kotlin.native.interop.indexer.VoidType
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [MappingBridgeGenerator] implementation which uses [SimpleBridgeGenerator] as the backend and
|
||||||
|
* maps the type using [mirror].
|
||||||
|
*/
|
||||||
|
class MappingBridgeGeneratorImpl(
|
||||||
|
val declarationMapper: DeclarationMapper,
|
||||||
|
val simpleBridgeGenerator: SimpleBridgeGenerator
|
||||||
|
) : MappingBridgeGenerator {
|
||||||
|
|
||||||
|
override fun kotlinToNative(
|
||||||
|
builder: KotlinCodeBuilder,
|
||||||
|
nativeBacked: NativeBacked,
|
||||||
|
returnType: Type,
|
||||||
|
kotlinValues: List<TypedKotlinValue>,
|
||||||
|
block: NativeCodeBuilder.(nativeValues: List<NativeExpression>) -> NativeExpression
|
||||||
|
): KotlinExpression {
|
||||||
|
val bridgeArguments = mutableListOf<BridgeTypedKotlinValue>()
|
||||||
|
|
||||||
|
kotlinValues.forEachIndexed { index, (type, value) ->
|
||||||
|
if (type.unwrapTypedefs() is RecordType) {
|
||||||
|
val tmpVarName = "kni$index"
|
||||||
|
builder.pushBlock("$value.usePointer { $tmpVarName ->")
|
||||||
|
bridgeArguments.add(BridgeTypedKotlinValue(BridgedType.NATIVE_PTR, "$tmpVarName.rawValue"))
|
||||||
|
} else {
|
||||||
|
val info = mirror(declarationMapper, type).info
|
||||||
|
bridgeArguments.add(BridgeTypedKotlinValue(info.bridgedType, info.argToBridged(value)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val unwrappedReturnType = returnType.unwrapTypedefs()
|
||||||
|
val kniRetVal = "kniRetVal"
|
||||||
|
val bridgeReturnType = when (unwrappedReturnType) {
|
||||||
|
VoidType -> BridgedType.VOID
|
||||||
|
is RecordType -> {
|
||||||
|
val mirror = mirror(declarationMapper, returnType)
|
||||||
|
val tmpVarName = kniRetVal
|
||||||
|
builder.out("val $tmpVarName = nativeHeap.alloc<${mirror.pointedTypeName}>()")
|
||||||
|
builder.pushBlock("try {", free = "finally { nativeHeap.free($tmpVarName) }")
|
||||||
|
bridgeArguments.add(BridgeTypedKotlinValue(BridgedType.NATIVE_PTR, "$tmpVarName.rawPtr"))
|
||||||
|
BridgedType.VOID
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val mirror = mirror(declarationMapper, returnType)
|
||||||
|
mirror.info.bridgedType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val callExpr = simpleBridgeGenerator.kotlinToNative(
|
||||||
|
nativeBacked, bridgeReturnType, bridgeArguments
|
||||||
|
) { bridgeNativeValues ->
|
||||||
|
|
||||||
|
val nativeValues = mutableListOf<String>()
|
||||||
|
kotlinValues.forEachIndexed { index, (type, _) ->
|
||||||
|
val unwrappedType = type.unwrapTypedefs()
|
||||||
|
if (unwrappedType is RecordType) {
|
||||||
|
nativeValues.add("*(${unwrappedType.decl.spelling}*)${bridgeNativeValues[index]}")
|
||||||
|
} else {
|
||||||
|
nativeValues.add(mirror(declarationMapper, type).info.cFromBridged(bridgeNativeValues[index]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val nativeResult = block(nativeValues)
|
||||||
|
|
||||||
|
when (unwrappedReturnType) {
|
||||||
|
is VoidType -> {
|
||||||
|
out(nativeResult + ";")
|
||||||
|
""
|
||||||
|
}
|
||||||
|
is RecordType -> {
|
||||||
|
out("*(${unwrappedReturnType.decl.spelling}*)${bridgeNativeValues.last()} = $nativeResult;")
|
||||||
|
""
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
nativeResult
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val result = when (unwrappedReturnType) {
|
||||||
|
is VoidType -> callExpr
|
||||||
|
is RecordType -> {
|
||||||
|
builder.out(callExpr)
|
||||||
|
"$kniRetVal.readValue()"
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val mirror = mirror(declarationMapper, returnType)
|
||||||
|
mirror.info.argFromBridged(callExpr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun nativeToKotlin(
|
||||||
|
builder: NativeCodeBuilder,
|
||||||
|
nativeBacked: NativeBacked,
|
||||||
|
returnType: Type,
|
||||||
|
nativeValues: List<TypedNativeValue>,
|
||||||
|
block: KotlinCodeBuilder.(kotlinValues: List<KotlinExpression>) -> KotlinExpression
|
||||||
|
): NativeExpression {
|
||||||
|
|
||||||
|
val bridgeArguments = mutableListOf<BridgeTypedNativeValue>()
|
||||||
|
|
||||||
|
nativeValues.forEachIndexed { index, (type, value) ->
|
||||||
|
val bridgeArgument = if (type.unwrapTypedefs() is RecordType) {
|
||||||
|
BridgeTypedNativeValue(BridgedType.NATIVE_PTR, "&$value")
|
||||||
|
} else {
|
||||||
|
val info = mirror(declarationMapper, type).info
|
||||||
|
BridgeTypedNativeValue(info.bridgedType, value)
|
||||||
|
}
|
||||||
|
bridgeArguments.add(bridgeArgument)
|
||||||
|
}
|
||||||
|
|
||||||
|
val unwrappedReturnType = returnType.unwrapTypedefs()
|
||||||
|
val kniRetVal = "kniRetVal"
|
||||||
|
val bridgeReturnType = when (unwrappedReturnType) {
|
||||||
|
VoidType -> BridgedType.VOID
|
||||||
|
is RecordType -> {
|
||||||
|
val tmpVarName = kniRetVal
|
||||||
|
builder.out("${unwrappedReturnType.decl.spelling} $tmpVarName;")
|
||||||
|
bridgeArguments.add(BridgeTypedNativeValue(BridgedType.NATIVE_PTR, "&$tmpVarName"))
|
||||||
|
BridgedType.VOID
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val mirror = mirror(declarationMapper, returnType)
|
||||||
|
mirror.info.bridgedType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val callExpr = simpleBridgeGenerator.nativeToKotlin(
|
||||||
|
nativeBacked,
|
||||||
|
bridgeReturnType,
|
||||||
|
bridgeArguments
|
||||||
|
) { bridgeKotlinValues ->
|
||||||
|
val kotlinValues = mutableListOf<String>()
|
||||||
|
nativeValues.forEachIndexed { index, (type, _) ->
|
||||||
|
val mirror = mirror(declarationMapper, type)
|
||||||
|
if (type.unwrapTypedefs() is RecordType) {
|
||||||
|
kotlinValues.add(
|
||||||
|
"interpretPointed<${mirror.pointedTypeName}>(${bridgeKotlinValues[index]}).readValue()"
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
kotlinValues.add(mirror.info.argFromBridged(bridgeKotlinValues[index]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val kotlinResult = block(kotlinValues)
|
||||||
|
when (unwrappedReturnType) {
|
||||||
|
is RecordType -> {
|
||||||
|
"$kotlinResult.write(${bridgeKotlinValues.last()})"
|
||||||
|
}
|
||||||
|
is VoidType -> {
|
||||||
|
kotlinResult
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
mirror(declarationMapper, returnType).info.argToBridged(kotlinResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val result = when (unwrappedReturnType) {
|
||||||
|
is VoidType -> callExpr
|
||||||
|
is RecordType -> {
|
||||||
|
builder.out("$callExpr;")
|
||||||
|
kniRetVal
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
mirror(declarationMapper, returnType).info.cFromBridged(callExpr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
+334
@@ -0,0 +1,334 @@
|
|||||||
|
/*
|
||||||
|
* 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.*
|
||||||
|
|
||||||
|
interface DeclarationMapper {
|
||||||
|
fun getKotlinNameForPointed(structDecl: StructDecl): String
|
||||||
|
fun isMappedToStrict(enumDef: EnumDef): Boolean
|
||||||
|
fun getKotlinNameForValue(enumDef: EnumDef): String
|
||||||
|
}
|
||||||
|
|
||||||
|
val PrimitiveType.kotlinType: String
|
||||||
|
get() = when (this) {
|
||||||
|
is CharType -> "Byte"
|
||||||
|
|
||||||
|
is BoolType -> "Boolean"
|
||||||
|
|
||||||
|
// TODO: C primitive types should probably be generated as type aliases for Kotlin types.
|
||||||
|
is IntegerType -> when (this.size) {
|
||||||
|
1 -> "Byte"
|
||||||
|
2 -> "Short"
|
||||||
|
4 -> "Int"
|
||||||
|
8 -> "Long"
|
||||||
|
else -> TODO(this.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
is FloatingType -> when (this.size) {
|
||||||
|
4 -> "Float"
|
||||||
|
8 -> "Double"
|
||||||
|
else -> TODO(this.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> throw NotImplementedError()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val PrimitiveType.bridgedType: BridgedType
|
||||||
|
get() {
|
||||||
|
val kotlinType = this.kotlinType
|
||||||
|
return BridgedType.values().single {
|
||||||
|
it.kotlinType == kotlinType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val ObjCPointer.isNullable: Boolean
|
||||||
|
get() = this.nullability != ObjCPointer.Nullability.NonNull
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes the Kotlin types used to represent some C type.
|
||||||
|
*/
|
||||||
|
sealed class TypeMirror(val pointedTypeName: String, val info: TypeInfo) {
|
||||||
|
/**
|
||||||
|
* Type to be used in bindings for argument or return value.
|
||||||
|
*/
|
||||||
|
abstract val argType: String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
|
||||||
|
override val argType: String
|
||||||
|
get() = valueTypeName +
|
||||||
|
if (info is TypeInfo.Pointer ||
|
||||||
|
(info is TypeInfo.ObjCPointerInfo && info.type.isNullable)) "?" else ""
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes various type conversions for [TypeMirror].
|
||||||
|
*/
|
||||||
|
sealed class TypeInfo {
|
||||||
|
/**
|
||||||
|
* The conversion from [TypeMirror.argType] to [bridgedType].
|
||||||
|
*/
|
||||||
|
abstract fun argToBridged(name: String): String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The conversion from [bridgedType] to [TypeMirror.argType].
|
||||||
|
*/
|
||||||
|
abstract fun argFromBridged(name: String): String
|
||||||
|
|
||||||
|
abstract val bridgedType: BridgedType
|
||||||
|
|
||||||
|
open fun cFromBridged(name: String): String = name
|
||||||
|
|
||||||
|
open fun cToBridged(name: String): String = name
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
|
||||||
|
class Primitive(override val bridgedType: BridgedType, val varTypeName: String) : TypeInfo() {
|
||||||
|
|
||||||
|
override fun argToBridged(name: String) = name
|
||||||
|
override fun argFromBridged(name: String) = name
|
||||||
|
|
||||||
|
override fun constructPointedType(valueType: String) = "${varTypeName}Of<$valueType>"
|
||||||
|
}
|
||||||
|
|
||||||
|
class Boolean : TypeInfo() {
|
||||||
|
override fun argToBridged(name: String) = "$name.toByte()"
|
||||||
|
|
||||||
|
override fun argFromBridged(name: String) = "$name.toBoolean()"
|
||||||
|
|
||||||
|
override val bridgedType: BridgedType get() = BridgedType.BYTE
|
||||||
|
|
||||||
|
override fun cFromBridged(name: String) = "($name) ? 1 : 0"
|
||||||
|
|
||||||
|
override fun cToBridged(name: String) = "($name) ? 1 : 0"
|
||||||
|
|
||||||
|
override fun constructPointedType(valueType: String) = "BooleanVarOf<$valueType>"
|
||||||
|
}
|
||||||
|
|
||||||
|
class Enum(val className: String, override val bridgedType: BridgedType) : TypeInfo() {
|
||||||
|
override fun argToBridged(name: String) = "$name.value"
|
||||||
|
|
||||||
|
override fun argFromBridged(name: String) = "$className.byValue($name)"
|
||||||
|
|
||||||
|
override fun constructPointedType(valueType: String) = "$className.Var" // TODO: improve
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class Pointer(val pointee: String) : TypeInfo() {
|
||||||
|
override fun argToBridged(name: String) = "$name.rawValue"
|
||||||
|
|
||||||
|
override fun argFromBridged(name: String) = "interpretCPointer<$pointee>($name)"
|
||||||
|
|
||||||
|
override val bridgedType: BridgedType
|
||||||
|
get() = BridgedType.NATIVE_PTR
|
||||||
|
|
||||||
|
override fun cFromBridged(name: String) = "(void*)$name" // Note: required for JVM
|
||||||
|
|
||||||
|
override fun constructPointedType(valueType: String) = "CPointerVarOf<$valueType>"
|
||||||
|
}
|
||||||
|
|
||||||
|
class ObjCPointerInfo(val typeName: String, val type: ObjCPointer) : TypeInfo() {
|
||||||
|
override fun argToBridged(name: String) = "$name.rawPtr"
|
||||||
|
|
||||||
|
override fun argFromBridged(name: String) = "interpretObjCPointerOrNull<$typeName>($name)" +
|
||||||
|
if (type.isNullable) "" else "!!"
|
||||||
|
|
||||||
|
override val bridgedType: BridgedType
|
||||||
|
get() = BridgedType.OBJC_POINTER
|
||||||
|
|
||||||
|
override fun constructPointedType(valueType: String) = "ObjCObjectVar<$valueType>"
|
||||||
|
}
|
||||||
|
|
||||||
|
class NSString(val type: ObjCPointer) : TypeInfo() {
|
||||||
|
override fun argToBridged(name: String) = "CreateNSStringFromKString($name)"
|
||||||
|
|
||||||
|
override fun argFromBridged(name: String) = "CreateKStringFromNSString($name)" +
|
||||||
|
if (type.isNullable) "" else "!!"
|
||||||
|
|
||||||
|
override val bridgedType: BridgedType
|
||||||
|
get() = BridgedType.OBJC_POINTER
|
||||||
|
|
||||||
|
override fun constructPointedType(valueType: String): String {
|
||||||
|
return "ObjCStringVarOf<$valueType>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ByRef(val pointed: String) : TypeInfo() {
|
||||||
|
override fun argToBridged(name: String) = error(pointed)
|
||||||
|
override fun argFromBridged(name: String) = error(pointed)
|
||||||
|
override val bridgedType: BridgedType get() = error(pointed)
|
||||||
|
override fun cFromBridged(name: String) = error(pointed)
|
||||||
|
override fun cToBridged(name: String) = error(pointed)
|
||||||
|
|
||||||
|
// TODO: this method must not exist
|
||||||
|
override fun constructPointedType(valueType: String): String = error(pointed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun mirrorPrimitiveType(type: PrimitiveType): TypeMirror.ByValue {
|
||||||
|
val varTypeName = when (type) {
|
||||||
|
is CharType -> "ByteVar"
|
||||||
|
is BoolType -> "BooleanVar"
|
||||||
|
is IntegerType -> when (type.size) {
|
||||||
|
1 -> "ByteVar"
|
||||||
|
2 -> "ShortVar"
|
||||||
|
4 -> "IntVar"
|
||||||
|
8 -> "LongVar"
|
||||||
|
else -> TODO(type.toString())
|
||||||
|
}
|
||||||
|
is FloatingType -> when (type.size) {
|
||||||
|
4 -> "FloatVar"
|
||||||
|
8 -> "DoubleVar"
|
||||||
|
else -> TODO(type.toString())
|
||||||
|
}
|
||||||
|
else -> TODO(type.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
val info = if (type == BoolType) {
|
||||||
|
TypeInfo.Boolean()
|
||||||
|
} else {
|
||||||
|
TypeInfo.Primitive(type.bridgedType, varTypeName)
|
||||||
|
}
|
||||||
|
return TypeMirror.ByValue(varTypeName, info, type.kotlinType)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun byRefTypeMirror(pointedTypeName: String) : TypeMirror.ByRef {
|
||||||
|
val info = TypeInfo.ByRef(pointedTypeName)
|
||||||
|
return TypeMirror.ByRef(pointedTypeName, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun mirror(declarationMapper: DeclarationMapper, type: Type): TypeMirror = when (type) {
|
||||||
|
is PrimitiveType -> mirrorPrimitiveType(type)
|
||||||
|
|
||||||
|
is RecordType -> byRefTypeMirror(declarationMapper.getKotlinNameForPointed(type.decl).asSimpleName())
|
||||||
|
|
||||||
|
is EnumType -> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
!type.def.isAnonymous -> {
|
||||||
|
val baseTypeMirror = mirror(declarationMapper, type.def.baseType)
|
||||||
|
TypeMirror.ByValue("${kotlinName}Var", baseTypeMirror.info, kotlinName.asSimpleName())
|
||||||
|
}
|
||||||
|
else -> mirror(declarationMapper, type.def.baseType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is PointerType -> {
|
||||||
|
val pointeeType = type.pointeeType
|
||||||
|
val unwrappedPointeeType = pointeeType.unwrapTypedefs()
|
||||||
|
if (unwrappedPointeeType is VoidType) {
|
||||||
|
val info = TypeInfo.Pointer("COpaque")
|
||||||
|
TypeMirror.ByValue("COpaquePointerVar", info, "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}>")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is ArrayType -> {
|
||||||
|
// TODO: array type doesn't exactly correspond neither to pointer nor to value.
|
||||||
|
val elemTypeMirror = mirror(declarationMapper, type.elemType)
|
||||||
|
if (type.elemType.unwrapTypedefs() is ArrayType) {
|
||||||
|
elemTypeMirror
|
||||||
|
} else {
|
||||||
|
val info = TypeInfo.Pointer(elemTypeMirror.pointedTypeName)
|
||||||
|
TypeMirror.ByValue("CArrayPointerVar<${elemTypeMirror.pointedTypeName}>", info,
|
||||||
|
"CArrayPointer<${elemTypeMirror.pointedTypeName}>")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is FunctionType -> byRefTypeMirror("CFunction<${getKotlinFunctionType(declarationMapper, type)}>")
|
||||||
|
|
||||||
|
is Typedef -> {
|
||||||
|
val baseType = mirror(declarationMapper, type.def.aliased)
|
||||||
|
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 ObjCPointer -> objCPointerMirror(type)
|
||||||
|
|
||||||
|
else -> TODO(type.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun objCPointerMirror(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"
|
||||||
|
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
|
||||||
|
is ObjCInstanceType -> TODO(type.toString()) // Must have already been handled.
|
||||||
|
}
|
||||||
|
|
||||||
|
return objCPointerMirror(typeName.asSimpleName(), 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getKotlinFunctionType(declarationMapper: DeclarationMapper, type: FunctionType): String {
|
||||||
|
val returnType = if (type.returnType.unwrapTypedefs() is VoidType) {
|
||||||
|
"Unit"
|
||||||
|
} else {
|
||||||
|
mirror(declarationMapper, type.returnType).argType
|
||||||
|
}
|
||||||
|
return "(" +
|
||||||
|
type.parameterTypes.map { mirror(declarationMapper, it).argType }.joinToString(", ") +
|
||||||
|
") -> " +
|
||||||
|
returnType
|
||||||
|
}
|
||||||
|
|
||||||
+498
@@ -0,0 +1,498 @@
|
|||||||
|
/*
|
||||||
|
* 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.gen.jvm.StubGenerator
|
||||||
|
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||||
|
|
||||||
|
private fun ObjCMethod.getKotlinParameterNames(): List<String> {
|
||||||
|
val selectorParts = this.selector.split(":")
|
||||||
|
|
||||||
|
val result = mutableListOf<String>()
|
||||||
|
|
||||||
|
// The names of all parameters except first must depend only on the selector:
|
||||||
|
this.parameters.forEachIndexed { index, parameter ->
|
||||||
|
if (index > 0) {
|
||||||
|
var name = selectorParts[index]
|
||||||
|
if (name.isEmpty()) {
|
||||||
|
name = "_$index"
|
||||||
|
}
|
||||||
|
|
||||||
|
while (name in result) {
|
||||||
|
name = "_$name"
|
||||||
|
}
|
||||||
|
|
||||||
|
result.add(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.parameters.firstOrNull()?.let {
|
||||||
|
var name = it.name ?: ""
|
||||||
|
if (name.isEmpty()) {
|
||||||
|
name = "arg"
|
||||||
|
}
|
||||||
|
while (name in result) {
|
||||||
|
name = "_$name"
|
||||||
|
}
|
||||||
|
result.add(0, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
class ObjCMethodStub(stubGenerator: StubGenerator,
|
||||||
|
val method: ObjCMethod,
|
||||||
|
private val container: ObjCClassOrProtocol) : KotlinStub, NativeBacked {
|
||||||
|
|
||||||
|
override fun generate(context: StubGenerationContext): Sequence<String> =
|
||||||
|
if (context.nativeBridges.isSupported(this)) {
|
||||||
|
val result = mutableListOf<String>()
|
||||||
|
result.add("@ObjCMethod".applyToStrings(method.selector, bridgeName))
|
||||||
|
result.add(header)
|
||||||
|
|
||||||
|
if (method.isInit && container is ObjCClass) {
|
||||||
|
result.add("")
|
||||||
|
result.add("@ObjCConstructor".applyToStrings(method.selector))
|
||||||
|
result.add("constructor($joinedKotlinParameters) {}")
|
||||||
|
}
|
||||||
|
|
||||||
|
context.addTopLevelDeclaration(
|
||||||
|
listOf("@konan.internal.ExportForCompiler",
|
||||||
|
"@ObjCBridge".applyToStrings(method.selector, method.encoding, implementationTemplate))
|
||||||
|
+ block(bridgeHeader, bodyLines)
|
||||||
|
)
|
||||||
|
|
||||||
|
result.asSequence()
|
||||||
|
} else {
|
||||||
|
sequenceOf(
|
||||||
|
annotationForUnableToImport,
|
||||||
|
header
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val bodyLines: List<String>
|
||||||
|
private val joinedKotlinParameters: String
|
||||||
|
private val header: String
|
||||||
|
private val implementationTemplate: String
|
||||||
|
private 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 nativeBridgeArguments = mutableListOf<TypedKotlinValue>()
|
||||||
|
|
||||||
|
val kniReceiverParameter = "kniR"
|
||||||
|
val kniSuperClassParameter = "kniSC"
|
||||||
|
|
||||||
|
val voidPtr = PointerType(VoidType)
|
||||||
|
|
||||||
|
val returnType = method.getReturnType(container)
|
||||||
|
|
||||||
|
val messengerGetter = if (returnType.isLargeOrUnaligned()) "getMessengerLU" else "getMessenger"
|
||||||
|
|
||||||
|
kotlinObjCBridgeParameters.add(kniSuperClassParameter to "NativePtr")
|
||||||
|
nativeBridgeArguments.add(TypedKotlinValue(voidPtr, "$messengerGetter($kniSuperClassParameter)"))
|
||||||
|
|
||||||
|
if (method.nsConsumesSelf) {
|
||||||
|
// TODO: do this later due to possible exceptions
|
||||||
|
bodyGenerator.out("objc_retain($kniReceiverParameter.rawPtr)")
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlinObjCBridgeParameters.add(kniReceiverParameter to "ObjCObject")
|
||||||
|
nativeBridgeArguments.add(
|
||||||
|
TypedKotlinValue(voidPtr,
|
||||||
|
"getReceiverOrSuper($kniReceiverParameter.rawPtr, $kniSuperClassParameter)"))
|
||||||
|
|
||||||
|
val kotlinParameterNames = method.getKotlinParameterNames()
|
||||||
|
|
||||||
|
method.parameters.forEachIndexed { index, it ->
|
||||||
|
val name = kotlinParameterNames[index]
|
||||||
|
|
||||||
|
val kotlinType = stubGenerator.mirror(it.type).argType
|
||||||
|
kotlinParameters.add(name to kotlinType)
|
||||||
|
|
||||||
|
kotlinObjCBridgeParameters.add(name to kotlinType)
|
||||||
|
nativeBridgeArguments.add(TypedKotlinValue(it.type, name.asSimpleName()))
|
||||||
|
}
|
||||||
|
|
||||||
|
val kotlinReturnType = if (returnType.unwrapTypedefs() is VoidType) {
|
||||||
|
"Unit"
|
||||||
|
} else {
|
||||||
|
stubGenerator.mirror(returnType).argType
|
||||||
|
}
|
||||||
|
|
||||||
|
val result = stubGenerator.mappingBridgeGenerator.kotlinToNative(
|
||||||
|
bodyGenerator,
|
||||||
|
this@ObjCMethodStub,
|
||||||
|
returnType,
|
||||||
|
nativeBridgeArguments
|
||||||
|
) { nativeValues ->
|
||||||
|
val selector = "@selector(${method.selector})"
|
||||||
|
val messengerParameterTypes = mutableListOf<String>()
|
||||||
|
messengerParameterTypes.add("void*")
|
||||||
|
messengerParameterTypes.add("SEL")
|
||||||
|
method.parameters.forEach {
|
||||||
|
messengerParameterTypes.add(it.getTypeStringRepresentation())
|
||||||
|
}
|
||||||
|
|
||||||
|
val messengerReturnType = returnType.getStringRepresentation()
|
||||||
|
|
||||||
|
val messengerType =
|
||||||
|
"$messengerReturnType (* ${method.cAttributes}) (${messengerParameterTypes.joinToString()})"
|
||||||
|
|
||||||
|
val messenger = "(($messengerType) ${nativeValues.first()})"
|
||||||
|
|
||||||
|
val messengerArguments = listOf(nativeValues[1]) + selector + nativeValues.drop(2)
|
||||||
|
|
||||||
|
"$messenger(${messengerArguments.joinToString()})"
|
||||||
|
}
|
||||||
|
bodyGenerator.out("return $result")
|
||||||
|
|
||||||
|
this.implementationTemplate = genImplementationTemplate(stubGenerator)
|
||||||
|
this.bodyLines = bodyGenerator.build()
|
||||||
|
|
||||||
|
bridgeName = "objcKniBridge${stubGenerator.nextUniqueId()}"
|
||||||
|
|
||||||
|
this.bridgeHeader = "internal fun $bridgeName(" +
|
||||||
|
"${kotlinObjCBridgeParameters.joinToString { "${it.first.asSimpleName()}: ${it.second}" }})" +
|
||||||
|
": $kotlinReturnType"
|
||||||
|
|
||||||
|
this.joinedKotlinParameters = kotlinParameters.joinToString { "${it.first.asSimpleName()}: ${it.second}" }
|
||||||
|
|
||||||
|
this.header = buildString {
|
||||||
|
if (container is ObjCClass) append("external ")
|
||||||
|
if (isOverride) {
|
||||||
|
append("override ")
|
||||||
|
} else if (container is ObjCClass) {
|
||||||
|
append("open ")
|
||||||
|
}
|
||||||
|
|
||||||
|
append("fun ${method.kotlinName.asSimpleName()}($joinedKotlinParameters): $kotlinReturnType")
|
||||||
|
|
||||||
|
if (container is ObjCProtocol && method.isOptional) append(" = optional()")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun genImplementationTemplate(stubGenerator: StubGenerator): String {
|
||||||
|
val codeBuilder = NativeCodeBuilder()
|
||||||
|
|
||||||
|
val result = codeBuilder.genMethodImp(stubGenerator, this, method, container)
|
||||||
|
stubGenerator.simpleBridgeGenerator.insertNativeBridge(this, emptyList(), codeBuilder.lines)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Type.isLargeOrUnaligned(): Boolean {
|
||||||
|
val unwrappedType = this.unwrapTypedefs()
|
||||||
|
return when (unwrappedType) {
|
||||||
|
is RecordType -> unwrappedType.decl.def!!.size > 16 || this.hasUnalignedMembers()
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Type.hasUnalignedMembers(): Boolean = when (this) {
|
||||||
|
is Typedef -> this.def.aliased.hasUnalignedMembers()
|
||||||
|
is RecordType -> this.decl.def!!.let { def ->
|
||||||
|
def.hasUnalignedFields || // Check members of fields too:
|
||||||
|
def.fields.any { it.type.hasUnalignedMembers() }
|
||||||
|
}
|
||||||
|
is ArrayType -> this.elemType.hasUnalignedMembers()
|
||||||
|
else -> false
|
||||||
|
|
||||||
|
// TODO: should the recursive checks be made in indexer when computing `hasUnalignedFields`?
|
||||||
|
}
|
||||||
|
|
||||||
|
private val ObjCMethod.kotlinName: String get() = selector.split(":").first()
|
||||||
|
|
||||||
|
private val ObjCClassOrProtocol.protocolsWithSupers: Sequence<ObjCProtocol>
|
||||||
|
get() = this.protocols.asSequence().flatMap { sequenceOf(it) + it.protocolsWithSupers }
|
||||||
|
|
||||||
|
private val ObjCClassOrProtocol.immediateSuperTypes: Sequence<ObjCClassOrProtocol>
|
||||||
|
get() {
|
||||||
|
val baseClass = (this as? ObjCClass)?.baseClass
|
||||||
|
if (baseClass != null) {
|
||||||
|
return sequenceOf(baseClass) + this.protocols.asSequence()
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.protocols.asSequence()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val ObjCClassOrProtocol.selfAndSuperTypes: Sequence<ObjCClassOrProtocol>
|
||||||
|
get() = sequenceOf(this) + this.superTypes
|
||||||
|
|
||||||
|
private val ObjCClassOrProtocol.superTypes: Sequence<ObjCClassOrProtocol>
|
||||||
|
get() = this.immediateSuperTypes.flatMap { it.selfAndSuperTypes }.distinct()
|
||||||
|
|
||||||
|
private fun ObjCClassOrProtocol.declaredMethods(isClass: Boolean): Sequence<ObjCMethod> =
|
||||||
|
this.methods.asSequence().filter { it.isClass == isClass }
|
||||||
|
|
||||||
|
private fun ObjCClassOrProtocol.inheritedMethods(isClass: Boolean): Sequence<ObjCMethod> =
|
||||||
|
this.superTypes.flatMap { it.declaredMethods(isClass) }.distinctBy { it.selector }
|
||||||
|
|
||||||
|
private fun ObjCClassOrProtocol.methodsWithInherited(isClass: Boolean): Sequence<ObjCMethod> =
|
||||||
|
this.selfAndSuperTypes.flatMap { it.declaredMethods(isClass) }.distinctBy { it.selector }
|
||||||
|
|
||||||
|
private fun ObjCMethod.isOverride(container: ObjCClassOrProtocol): Boolean =
|
||||||
|
container.superTypes.any { superType -> superType.methods.any(this::replaces) }
|
||||||
|
|
||||||
|
abstract class ObjCContainerStub(stubGenerator: StubGenerator,
|
||||||
|
private val container: ObjCClassOrProtocol,
|
||||||
|
private val isMeta: Boolean) : KotlinStub {
|
||||||
|
|
||||||
|
private val methods: List<ObjCMethod>
|
||||||
|
|
||||||
|
init {
|
||||||
|
val superMethods = container.inheritedMethods(isMeta)
|
||||||
|
|
||||||
|
// Add all methods declared in the class or protocol:
|
||||||
|
var methods = container.declaredMethods(isMeta)
|
||||||
|
|
||||||
|
// Exclude those which are identically declared in super types:
|
||||||
|
methods -= superMethods
|
||||||
|
|
||||||
|
// Add some special methods from super types:
|
||||||
|
methods += superMethods.filter { it.returnsInstancetype() || it.isInit }
|
||||||
|
|
||||||
|
// Add methods from adopted protocols that must be implemented according to Kotlin rules:
|
||||||
|
if (container is ObjCClass) {
|
||||||
|
methods += container.protocolsWithSupers.flatMap { it.declaredMethods(isMeta) }.filter { !it.isOptional }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add methods inherited from multiple supertypes that must be defined according to Kotlin rules:
|
||||||
|
methods += container.immediateSuperTypes
|
||||||
|
.flatMap { superType ->
|
||||||
|
val methodsWithInherited = superType.methodsWithInherited(isMeta)
|
||||||
|
// Select only those which are represented as non-abstract in Kotlin:
|
||||||
|
when (superType) {
|
||||||
|
is ObjCClass -> methodsWithInherited
|
||||||
|
is ObjCProtocol -> methodsWithInherited.filter { it.isOptional }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.groupBy { it.selector }
|
||||||
|
.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 &&
|
||||||
|
// Select only properties that don't override anything:
|
||||||
|
superProperties.none(it::replaces)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val propertyStubs = properties.map {
|
||||||
|
ObjCPropertyStub(stubGenerator, it, container)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val classHeader: String
|
||||||
|
|
||||||
|
init {
|
||||||
|
val nameSuffix = if (isMeta) "Meta" else ""
|
||||||
|
|
||||||
|
val supers = mutableListOf<String>()
|
||||||
|
|
||||||
|
if (container is ObjCClass) {
|
||||||
|
supers.add(container.baseClassName)
|
||||||
|
}
|
||||||
|
container.protocols.forEach {
|
||||||
|
supers.add(it.kotlinName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (supers.isEmpty()) {
|
||||||
|
assert(container is ObjCProtocol)
|
||||||
|
supers.add("ObjCObject")
|
||||||
|
}
|
||||||
|
|
||||||
|
val keywords = when (container) {
|
||||||
|
is ObjCClass -> "open class"
|
||||||
|
is ObjCProtocol -> "interface"
|
||||||
|
}
|
||||||
|
|
||||||
|
val supersString = supers.joinToString { "$it$nameSuffix".asSimpleName() }
|
||||||
|
val name = "${container.kotlinName}$nameSuffix".asSimpleName()
|
||||||
|
this.classHeader = "@ExternalObjCClass $keywords $name : $supersString"
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun generateBody(context: StubGenerationContext): Sequence<String> {
|
||||||
|
var result = (propertyStubs.asSequence() + methodStubs.asSequence())
|
||||||
|
.flatMap { sequenceOf("") + it.generate(context) }
|
||||||
|
|
||||||
|
if (container is ObjCClass && methodStubs.none {
|
||||||
|
it.method.isInit && it.method.parameters.isEmpty() && context.nativeBridges.isSupported(it)
|
||||||
|
}) {
|
||||||
|
// Always generate default constructor.
|
||||||
|
// If it is not produced for an init method, then include it manually:
|
||||||
|
result += sequenceOf("", "protected constructor() {}")
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun generate(context: StubGenerationContext): Sequence<String> = block(classHeader, generateBody(context))
|
||||||
|
}
|
||||||
|
|
||||||
|
open class ObjCClassOrProtocolStub(
|
||||||
|
stubGenerator: StubGenerator,
|
||||||
|
private val container: ObjCClassOrProtocol
|
||||||
|
) : ObjCContainerStub(
|
||||||
|
stubGenerator,
|
||||||
|
container,
|
||||||
|
isMeta = false
|
||||||
|
) {
|
||||||
|
private val metaClassStub =
|
||||||
|
object : ObjCContainerStub(stubGenerator, container, isMeta = true) {}
|
||||||
|
|
||||||
|
override fun generate(context: StubGenerationContext) =
|
||||||
|
metaClassStub.generate(context) + "" + super.generate(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
class ObjCProtocolStub(stubGenerator: StubGenerator, protocol: ObjCProtocol) :
|
||||||
|
ObjCClassOrProtocolStub(stubGenerator, protocol)
|
||||||
|
|
||||||
|
class ObjCClassStub(stubGenerator: StubGenerator, private val clazz: ObjCClass) :
|
||||||
|
ObjCClassOrProtocolStub(stubGenerator, clazz) {
|
||||||
|
|
||||||
|
override fun generateBody(context: StubGenerationContext) =
|
||||||
|
sequenceOf( "companion object : ${clazz.kotlinName}Meta() {}") +
|
||||||
|
super.generateBody(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
class ObjCPropertyStub(
|
||||||
|
val stubGenerator: StubGenerator, val property: ObjCProperty, val clazz: ObjCClassOrProtocol
|
||||||
|
) : KotlinStub {
|
||||||
|
|
||||||
|
override fun generate(context: StubGenerationContext): Sequence<String> {
|
||||||
|
val type = property.getType(clazz)
|
||||||
|
|
||||||
|
val kotlinType = stubGenerator.mirror(type).argType
|
||||||
|
|
||||||
|
val kind = if (property.setter == null) "val" else "var"
|
||||||
|
val modifiers = if (clazz is ObjCClass) "" else "final "
|
||||||
|
val result = mutableListOf(
|
||||||
|
"$modifiers$kind ${property.name.asSimpleName()}: $kotlinType",
|
||||||
|
" get() = ${property.getter.kotlinName.asSimpleName()}()"
|
||||||
|
)
|
||||||
|
|
||||||
|
property.setter?.let {
|
||||||
|
result.add(" set(value) = ${it.kotlinName.asSimpleName()}(value)")
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.asSequence()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val ObjCClassOrProtocol.kotlinName: String get() = when (this) {
|
||||||
|
is ObjCClass -> this.name
|
||||||
|
is ObjCProtocol -> "${this.name}Protocol"
|
||||||
|
}
|
||||||
|
|
||||||
|
private val ObjCClass.baseClassName: String
|
||||||
|
get() = baseClass?.name ?: "ObjCObject"
|
||||||
|
|
||||||
|
private fun Parameter.getTypeStringRepresentation() =
|
||||||
|
(if (this.nsConsumed) "__attribute__((ns_consumed)) " else "") + type.getStringRepresentation()
|
||||||
|
|
||||||
|
private fun ObjCMethod.getSelfTypeStringRepresentation() = if (this.nsConsumesSelf) {
|
||||||
|
"__attribute__((ns_consumed)) id"
|
||||||
|
} else {
|
||||||
|
"id"
|
||||||
|
}
|
||||||
|
|
||||||
|
val ObjCMethod.cAttributes get() = if (this.nsReturnsRetained) {
|
||||||
|
"__attribute__((ns_returns_retained)) "
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun NativeCodeBuilder.genMethodImp(
|
||||||
|
stubGenerator: StubGenerator,
|
||||||
|
nativeBacked: NativeBacked,
|
||||||
|
method: ObjCMethod,
|
||||||
|
container: ObjCClassOrProtocol
|
||||||
|
): String {
|
||||||
|
|
||||||
|
val returnType = method.getReturnType(container)
|
||||||
|
val cReturnType = returnType.getStringRepresentation()
|
||||||
|
|
||||||
|
val bridgeArguments = mutableListOf<TypedNativeValue>()
|
||||||
|
|
||||||
|
val parameters = mutableListOf<Pair<String, String>>()
|
||||||
|
|
||||||
|
parameters.add("self" to method.getSelfTypeStringRepresentation())
|
||||||
|
|
||||||
|
val receiverType = ObjCIdType(ObjCPointer.Nullability.NonNull, protocols = emptyList())
|
||||||
|
bridgeArguments.add(TypedNativeValue(receiverType, "self"))
|
||||||
|
|
||||||
|
parameters.add("_cmd" to "SEL")
|
||||||
|
|
||||||
|
method.parameters.forEachIndexed { index, parameter ->
|
||||||
|
val name = "p$index"
|
||||||
|
parameters.add(name to parameter.getTypeStringRepresentation())
|
||||||
|
bridgeArguments.add(TypedNativeValue(parameter.type, name))
|
||||||
|
}
|
||||||
|
|
||||||
|
val functionName = "knimi_" + stubGenerator.pkgName.replace('.', '_') + stubGenerator.nextUniqueId()
|
||||||
|
val functionAttr = method.cAttributes
|
||||||
|
|
||||||
|
out("$cReturnType $functionName(${parameters.joinToString { it.second + " " + it.first }}) $functionAttr{")
|
||||||
|
|
||||||
|
val callExpr = stubGenerator.mappingBridgeGenerator.nativeToKotlin(
|
||||||
|
this,
|
||||||
|
nativeBacked,
|
||||||
|
returnType,
|
||||||
|
bridgeArguments
|
||||||
|
) { kotlinValues ->
|
||||||
|
|
||||||
|
val kotlinReceiverType = if (method.isClass) "${container.kotlinName}Meta" else container.kotlinName
|
||||||
|
val kotlinRawReceiver = kotlinValues.first()
|
||||||
|
val kotlinReceiver = "$kotlinRawReceiver.uncheckedCast<${kotlinReceiverType.asSimpleName()}>()"
|
||||||
|
|
||||||
|
val namedArguments = kotlinValues.drop(1).zip(method.getKotlinParameterNames()) { value, name ->
|
||||||
|
"${name.asSimpleName()} = $value"
|
||||||
|
}
|
||||||
|
|
||||||
|
"${kotlinReceiver}.${method.kotlinName.asSimpleName()}(${namedArguments.joinToString()})"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (returnType.unwrapTypedefs() is VoidType) {
|
||||||
|
out(" $callExpr;")
|
||||||
|
} else {
|
||||||
|
out(" return $callExpr;")
|
||||||
|
}
|
||||||
|
|
||||||
|
out("}")
|
||||||
|
|
||||||
|
return functionName
|
||||||
|
}
|
||||||
|
|
||||||
+93
@@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
BYTE("Byte"),
|
||||||
|
SHORT("Short"),
|
||||||
|
INT("Int"),
|
||||||
|
LONG("Long"),
|
||||||
|
FLOAT("Float"),
|
||||||
|
DOUBLE("Double"),
|
||||||
|
NATIVE_PTR("NativePtr"),
|
||||||
|
OBJC_POINTER("NativePtr"),
|
||||||
|
VOID("Unit")
|
||||||
|
}
|
||||||
|
|
||||||
|
data class BridgeTypedKotlinValue(val type: BridgedType, val value: KotlinExpression)
|
||||||
|
data class BridgeTypedNativeValue(val type: BridgedType, val value: NativeExpression)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The entity which depends on native bridges.
|
||||||
|
*/
|
||||||
|
interface NativeBacked
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates simple bridges between Kotlin and native, passing [BridgedType] values.
|
||||||
|
*/
|
||||||
|
interface SimpleBridgeGenerator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the expression to convert given Kotlin values to native counterparts, pass through the bridge,
|
||||||
|
* use inside the native code produced by [block] and then return the result back.
|
||||||
|
*
|
||||||
|
* @param block produces native code lines into the builder and returns the expression to be used as the result.
|
||||||
|
*/
|
||||||
|
fun kotlinToNative(
|
||||||
|
nativeBacked: NativeBacked,
|
||||||
|
returnType: BridgedType,
|
||||||
|
kotlinValues: List<BridgeTypedKotlinValue>,
|
||||||
|
block: NativeCodeBuilder.(nativeValues: List<NativeExpression>) -> NativeExpression
|
||||||
|
): KotlinExpression
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the expression to convert given native values to Kotlin counterparts, pass through the bridge,
|
||||||
|
* use inside the Kotlin code produced by [block] and then return the result back.
|
||||||
|
*/
|
||||||
|
fun nativeToKotlin(
|
||||||
|
nativeBacked: NativeBacked,
|
||||||
|
returnType: BridgedType,
|
||||||
|
nativeValues: List<BridgeTypedNativeValue>,
|
||||||
|
block: KotlinCodeBuilder.(kotlinValues: List<KotlinExpression>) -> KotlinExpression
|
||||||
|
): NativeExpression
|
||||||
|
|
||||||
|
fun insertNativeBridge(
|
||||||
|
nativeBacked: NativeBacked,
|
||||||
|
kotlinLines: List<String>,
|
||||||
|
nativeLines: List<String>
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares all requested native bridges.
|
||||||
|
*/
|
||||||
|
fun prepare(): NativeBridges
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NativeBridges {
|
||||||
|
/**
|
||||||
|
* @return `true` iff given entity is supported by these bridges,
|
||||||
|
* i.e. all bridges it depends on can be successfully generated.
|
||||||
|
*/
|
||||||
|
fun isSupported(nativeBacked: NativeBacked): Boolean
|
||||||
|
|
||||||
|
val kotlinLines: Sequence<String>
|
||||||
|
val nativeLines: Sequence<String>
|
||||||
|
}
|
||||||
|
|
||||||
+219
@@ -0,0 +1,219 @@
|
|||||||
|
/*
|
||||||
|
* 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.gen.jvm.KotlinPlatform
|
||||||
|
import org.jetbrains.kotlin.native.interop.indexer.NativeLibrary
|
||||||
|
import org.jetbrains.kotlin.native.interop.indexer.mapFragmentIsCompilable
|
||||||
|
|
||||||
|
class SimpleBridgeGeneratorImpl(
|
||||||
|
private val platform: KotlinPlatform,
|
||||||
|
private val pkgName: String,
|
||||||
|
private val jvmFileClassName: String,
|
||||||
|
private val libraryForCStubs: NativeLibrary
|
||||||
|
) : SimpleBridgeGenerator {
|
||||||
|
|
||||||
|
private var nextUniqueId = 0
|
||||||
|
|
||||||
|
private val BridgedType.nativeType: String get() = when (platform) {
|
||||||
|
KotlinPlatform.JVM -> when (this) {
|
||||||
|
BridgedType.BYTE -> "jbyte"
|
||||||
|
BridgedType.SHORT -> "jshort"
|
||||||
|
BridgedType.INT -> "jint"
|
||||||
|
BridgedType.LONG -> "jlong"
|
||||||
|
BridgedType.FLOAT -> "jfloat"
|
||||||
|
BridgedType.DOUBLE -> "jdouble"
|
||||||
|
BridgedType.NATIVE_PTR -> "jlong"
|
||||||
|
BridgedType.OBJC_POINTER -> TODO()
|
||||||
|
BridgedType.VOID -> "void"
|
||||||
|
}
|
||||||
|
KotlinPlatform.NATIVE -> when (this) {
|
||||||
|
BridgedType.BYTE -> "int8_t"
|
||||||
|
BridgedType.SHORT -> "int16_t"
|
||||||
|
BridgedType.INT -> "int32_t"
|
||||||
|
BridgedType.LONG -> "int64_t"
|
||||||
|
BridgedType.FLOAT -> "float"
|
||||||
|
BridgedType.DOUBLE -> "double"
|
||||||
|
BridgedType.NATIVE_PTR -> "void*"
|
||||||
|
BridgedType.OBJC_POINTER -> "id"
|
||||||
|
BridgedType.VOID -> "void"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class NativeBridge(val kotlinLines: List<String>, val nativeLines: List<String>)
|
||||||
|
|
||||||
|
override fun kotlinToNative(
|
||||||
|
nativeBacked: NativeBacked,
|
||||||
|
returnType: BridgedType,
|
||||||
|
kotlinValues: List<BridgeTypedKotlinValue>,
|
||||||
|
block: NativeCodeBuilder.(arguments: List<NativeExpression>) -> NativeExpression
|
||||||
|
): KotlinExpression {
|
||||||
|
|
||||||
|
val kotlinLines = mutableListOf<String>()
|
||||||
|
val nativeLines = mutableListOf<String>()
|
||||||
|
|
||||||
|
val kotlinFunctionName = "kniBridge${nextUniqueId++}"
|
||||||
|
val kotlinParameters = kotlinValues.withIndex().joinToString {
|
||||||
|
"p${it.index}: ${it.value.type.kotlinType}"
|
||||||
|
}
|
||||||
|
|
||||||
|
val callExpr = "$kotlinFunctionName(${kotlinValues.joinToString { it.value }})"
|
||||||
|
|
||||||
|
val cFunctionParameters = when (platform) {
|
||||||
|
KotlinPlatform.JVM -> mutableListOf(
|
||||||
|
"jniEnv" to "JNIEnv*",
|
||||||
|
"jclss" to "jclass"
|
||||||
|
)
|
||||||
|
KotlinPlatform.NATIVE -> mutableListOf()
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlinValues.withIndex().mapTo(cFunctionParameters) {
|
||||||
|
"p${it.index}" to it.value.type.nativeType
|
||||||
|
}
|
||||||
|
|
||||||
|
val joinedCParameters = cFunctionParameters.joinToString { (name, type) -> "$type $name" }
|
||||||
|
val cReturnType = returnType.nativeType
|
||||||
|
|
||||||
|
val cFunctionHeader = when (platform) {
|
||||||
|
KotlinPlatform.JVM -> {
|
||||||
|
val funcFullName = buildString {
|
||||||
|
if (pkgName.isNotEmpty()) {
|
||||||
|
append(pkgName)
|
||||||
|
append('.')
|
||||||
|
}
|
||||||
|
append(jvmFileClassName)
|
||||||
|
append('.')
|
||||||
|
append(kotlinFunctionName)
|
||||||
|
}
|
||||||
|
|
||||||
|
val functionName = "Java_" + funcFullName.replace("_", "_1").replace('.', '_').replace("$", "_00024")
|
||||||
|
"JNIEXPORT $cReturnType JNICALL $functionName ($joinedCParameters)"
|
||||||
|
}
|
||||||
|
KotlinPlatform.NATIVE -> {
|
||||||
|
val functionName = pkgName.replace('.', '_') + "_$kotlinFunctionName"
|
||||||
|
kotlinLines.add("@SymbolName(${functionName.quoteAsKotlinLiteral()})")
|
||||||
|
"$cReturnType $functionName ($joinedCParameters)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nativeLines.add(cFunctionHeader + " {")
|
||||||
|
|
||||||
|
buildNativeCodeLines {
|
||||||
|
val cExpr = block(cFunctionParameters.takeLast(kotlinValues.size).map { (name, _) -> name })
|
||||||
|
if (returnType != BridgedType.VOID) {
|
||||||
|
out("return ($cReturnType)$cExpr;")
|
||||||
|
}
|
||||||
|
}.forEach {
|
||||||
|
nativeLines.add(" $it")
|
||||||
|
}
|
||||||
|
|
||||||
|
nativeLines.add("}")
|
||||||
|
kotlinLines.add("private external fun $kotlinFunctionName($kotlinParameters): ${returnType.kotlinType}")
|
||||||
|
|
||||||
|
val nativeBridge = NativeBridge(kotlinLines, nativeLines)
|
||||||
|
nativeBridges.add(nativeBacked to nativeBridge)
|
||||||
|
|
||||||
|
return callExpr
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun nativeToKotlin(
|
||||||
|
nativeBacked: NativeBacked,
|
||||||
|
returnType: BridgedType,
|
||||||
|
nativeValues: List<BridgeTypedNativeValue>,
|
||||||
|
block: KotlinCodeBuilder.(arguments: List<KotlinExpression>) -> KotlinExpression
|
||||||
|
): NativeExpression {
|
||||||
|
|
||||||
|
if (platform != KotlinPlatform.NATIVE) TODO()
|
||||||
|
|
||||||
|
val kotlinLines = mutableListOf<String>()
|
||||||
|
val nativeLines = mutableListOf<String>()
|
||||||
|
|
||||||
|
val kotlinFunctionName = "kniBridge${nextUniqueId++}"
|
||||||
|
val kotlinParameters = nativeValues.withIndex().map {
|
||||||
|
"p${it.index}" to it.value.type.kotlinType
|
||||||
|
}
|
||||||
|
val joinedKotlinParameters = kotlinParameters.joinToString { "${it.first}: ${it.second}" }
|
||||||
|
|
||||||
|
val cFunctionParameters = nativeValues.withIndex().map {
|
||||||
|
"p${it.index}" to it.value.type.nativeType
|
||||||
|
}
|
||||||
|
val joinedCParameters = cFunctionParameters.joinToString { (name, type) -> "$type $name" }
|
||||||
|
val cReturnType = returnType.nativeType
|
||||||
|
|
||||||
|
val symbolName = pkgName.replace('.', '_') + "_$kotlinFunctionName"
|
||||||
|
kotlinLines.add("@konan.internal.ExportForCppRuntime(${symbolName.quoteAsKotlinLiteral()})")
|
||||||
|
val cFunctionHeader = "$cReturnType $symbolName($joinedCParameters)"
|
||||||
|
|
||||||
|
nativeLines.add("$cFunctionHeader;")
|
||||||
|
kotlinLines.add("private fun $kotlinFunctionName($joinedKotlinParameters): ${returnType.kotlinType} {")
|
||||||
|
|
||||||
|
buildKotlinCodeLines {
|
||||||
|
var kotlinExpr = block(kotlinParameters.map { (name, _) -> name })
|
||||||
|
if (returnType == BridgedType.OBJC_POINTER) {
|
||||||
|
// The Kotlin code may lose the ownership on this pointer after returning from the bridge,
|
||||||
|
// so retain the pointer and autorelease it:
|
||||||
|
kotlinExpr = "objc_retainAutoreleaseReturnValue($kotlinExpr)"
|
||||||
|
// (Objective-C does the same for returned pointers).
|
||||||
|
}
|
||||||
|
out("return $kotlinExpr")
|
||||||
|
}.forEach {
|
||||||
|
kotlinLines.add(" $it")
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlinLines.add("}")
|
||||||
|
|
||||||
|
insertNativeBridge(nativeBacked, kotlinLines, nativeLines)
|
||||||
|
|
||||||
|
return "$symbolName(${nativeValues.joinToString { it.value }})"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun insertNativeBridge(nativeBacked: NativeBacked, kotlinLines: List<String>, nativeLines: List<String>) {
|
||||||
|
val nativeBridge = NativeBridge(kotlinLines, nativeLines)
|
||||||
|
nativeBridges.add(nativeBacked to nativeBridge)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val nativeBridges = mutableListOf<Pair<NativeBacked, NativeBridge>>()
|
||||||
|
|
||||||
|
override fun prepare(): NativeBridges {
|
||||||
|
val includedBridges = mutableListOf<NativeBridge>()
|
||||||
|
val excludedClients = mutableSetOf<NativeBacked>()
|
||||||
|
|
||||||
|
nativeBridges.map { it.second.nativeLines }
|
||||||
|
.mapFragmentIsCompilable(libraryForCStubs)
|
||||||
|
.forEachIndexed { index, isCompilable ->
|
||||||
|
if (isCompilable) {
|
||||||
|
includedBridges.add(nativeBridges[index].second)
|
||||||
|
} else {
|
||||||
|
excludedClients.add(nativeBridges[index].first)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: exclude unused bridges.
|
||||||
|
|
||||||
|
return object : NativeBridges {
|
||||||
|
|
||||||
|
override val kotlinLines: Sequence<String>
|
||||||
|
get() = includedBridges.asSequence().flatMap { it.kotlinLines.asSequence() }
|
||||||
|
|
||||||
|
override val nativeLines: Sequence<String>
|
||||||
|
get() = includedBridges.asSequence().flatMap { it.nativeLines.asSequence() }
|
||||||
|
|
||||||
|
override fun isSupported(nativeBacked: NativeBacked): Boolean =
|
||||||
|
nativeBacked !in excludedClients
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+66
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* 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.*
|
||||||
|
|
||||||
|
val EnumDef.isAnonymous: Boolean
|
||||||
|
get() = spelling.contains("(anonymous ") // TODO: it is a hack
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the expression which could be used for this type in C code.
|
||||||
|
* Note: the resulting string doesn't exactly represent this type, but it is enough for current purposes.
|
||||||
|
*
|
||||||
|
* TODO: use libclang to implement?
|
||||||
|
*/
|
||||||
|
fun Type.getStringRepresentation(): String = when (this) {
|
||||||
|
is VoidType -> "void"
|
||||||
|
is CharType -> "char"
|
||||||
|
is BoolType -> "BOOL"
|
||||||
|
is IntegerType -> this.spelling
|
||||||
|
is FloatingType -> this.spelling
|
||||||
|
|
||||||
|
is PointerType, is ArrayType -> "void*"
|
||||||
|
|
||||||
|
is RecordType -> this.decl.spelling
|
||||||
|
|
||||||
|
is EnumType -> if (this.def.isAnonymous) {
|
||||||
|
this.def.baseType.getStringRepresentation()
|
||||||
|
} else {
|
||||||
|
this.def.spelling
|
||||||
|
}
|
||||||
|
|
||||||
|
is Typedef -> this.def.aliased.getStringRepresentation()
|
||||||
|
|
||||||
|
is ObjCPointer -> when (this) {
|
||||||
|
is ObjCIdType -> "id$protocolQualifier"
|
||||||
|
is ObjCClassPointer -> "Class$protocolQualifier"
|
||||||
|
is ObjCObjectPointer -> "${def.name}$protocolQualifier*"
|
||||||
|
is ObjCInstanceType -> TODO(this.toString()) // Must have already been handled.
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> throw kotlin.NotImplementedError()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val ObjCQualifiedPointer.protocolQualifier: String
|
||||||
|
get() = if (this.protocols.isEmpty()) "" else " <${protocols.joinToString { it.name }}>"
|
||||||
|
|
||||||
|
tailrec fun Type.unwrapTypedefs(): Type = if (this is Typedef) {
|
||||||
|
this.def.aliased.unwrapTypedefs()
|
||||||
|
} else {
|
||||||
|
this
|
||||||
|
}
|
||||||
+166
-807
File diff suppressed because it is too large
Load Diff
+31
-3
@@ -324,6 +324,19 @@ private fun downloadDependencies(dependenciesRoot: String, target: String, konan
|
|||||||
maybeExecuteHelper(dependenciesRoot, konanProperties, dependencyList)
|
maybeExecuteHelper(dependenciesRoot, konanProperties, dependencyList)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun selectNativeLanguage(config: Properties): Language {
|
||||||
|
val languages = mapOf(
|
||||||
|
"C" to Language.C,
|
||||||
|
"Objective-C" to Language.OBJECTIVE_C
|
||||||
|
)
|
||||||
|
|
||||||
|
val language = config.getProperty("language") ?: return Language.C
|
||||||
|
|
||||||
|
return languages[language] ?:
|
||||||
|
error("Unexpected language '$language'. Possible values are: ${languages.keys.joinToString { "'$it'" }}")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun processLib(konanHome: String,
|
private fun processLib(konanHome: String,
|
||||||
substitutions: Map<String, String>,
|
substitutions: Map<String, String>,
|
||||||
args: Map<String, List<String>>) {
|
args: Map<String, List<String>>) {
|
||||||
@@ -363,9 +376,24 @@ private fun processLib(konanHome: String,
|
|||||||
|
|
||||||
val defaultOpts = konanProperties.defaultCompilerOpts(target, dependencies)
|
val defaultOpts = konanProperties.defaultCompilerOpts(target, dependencies)
|
||||||
val headerFiles = config.getSpaceSeparated("headers") + additionalHeaders
|
val headerFiles = config.getSpaceSeparated("headers") + additionalHeaders
|
||||||
val compilerOpts = config.getSpaceSeparated("compilerOpts") + defaultOpts + additionalCompilerOpts
|
val language = selectNativeLanguage(config)
|
||||||
|
val compilerOpts: List<String> = mutableListOf<String>().apply {
|
||||||
|
addAll(config.getSpaceSeparated("compilerOpts"))
|
||||||
|
addAll(defaultOpts)
|
||||||
|
addAll(additionalCompilerOpts)
|
||||||
|
addAll(when (language) {
|
||||||
|
Language.C -> emptyList()
|
||||||
|
Language.OBJECTIVE_C -> {
|
||||||
|
// "Objective-C" within interop means "Objective-C with ARC":
|
||||||
|
listOf("-fobjc-arc")
|
||||||
|
// Using this flag here has two effects:
|
||||||
|
// 1. The headers are parsed with ARC enabled, thus the API is visible correctly.
|
||||||
|
// 2. The generated Objective-C stubs are compiled with ARC enabled, so reference counting
|
||||||
|
// calls are inserted automatically.
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
val compiler = "clang"
|
val compiler = "clang"
|
||||||
val language = Language.C
|
|
||||||
val excludeSystemLibs = config.getProperty("excludeSystemLibs")?.toBoolean() ?: false
|
val excludeSystemLibs = config.getProperty("excludeSystemLibs")?.toBoolean() ?: false
|
||||||
val excludeDependentModules = config.getProperty("excludeDependentModules")?.toBoolean() ?: false
|
val excludeDependentModules = config.getProperty("excludeDependentModules")?.toBoolean() ?: false
|
||||||
|
|
||||||
@@ -422,7 +450,7 @@ private fun processLib(konanHome: String,
|
|||||||
outKtFile.parentFile.mkdirs()
|
outKtFile.parentFile.mkdirs()
|
||||||
|
|
||||||
File(nativeLibsDir).mkdirs()
|
File(nativeLibsDir).mkdirs()
|
||||||
val outCFile = File("$nativeLibsDir/$libName.c") // TODO: select the better location.
|
val outCFile = File("$nativeLibsDir/$libName.${language.sourceFileExtension}") // TODO: select the better location.
|
||||||
|
|
||||||
outKtFile.bufferedWriter().use { ktFile ->
|
outKtFile.bufferedWriter().use { ktFile ->
|
||||||
outCFile.bufferedWriter().use { cFile ->
|
outCFile.bufferedWriter().use { cFile ->
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ sourceSets {
|
|||||||
srcDir 'compiler/ir/backend.native/src/'
|
srcDir 'compiler/ir/backend.native/src/'
|
||||||
srcDir "${rootProject.projectDir}/shared/src/main/kotlin"
|
srcDir "${rootProject.projectDir}/shared/src/main/kotlin"
|
||||||
}
|
}
|
||||||
|
resources.srcDir 'compiler/ir/backend.native/resources/'
|
||||||
}
|
}
|
||||||
cli_bc {
|
cli_bc {
|
||||||
java.srcDir 'cli.bc/src'
|
java.srcDir 'cli.bc/src'
|
||||||
@@ -213,7 +214,8 @@ task jars(type: Jar) {
|
|||||||
from 'build/classes/cli_bc',
|
from 'build/classes/cli_bc',
|
||||||
'build/classes/compiler',
|
'build/classes/compiler',
|
||||||
'build/classes/hashInteropStubs',
|
'build/classes/hashInteropStubs',
|
||||||
'build/classes/llvmInteropStubs'
|
'build/classes/llvmInteropStubs',
|
||||||
|
'build/resources/compiler'
|
||||||
|
|
||||||
dependsOn 'build', ':runtime:hostRuntime', 'external_jars'
|
dependsOn 'build', ':runtime:hostRuntime', 'external_jars'
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
org.jetbrains.kotlin.backend.konan.ObjCOverridabilityCondition
|
||||||
+26
@@ -17,6 +17,7 @@
|
|||||||
package org.jetbrains.kotlin.backend.konan
|
package org.jetbrains.kotlin.backend.konan
|
||||||
|
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
@@ -100,6 +101,31 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns) {
|
|||||||
val signExtend = packageScope.getContributedFunctions("signExtend").single()
|
val signExtend = packageScope.getContributedFunctions("signExtend").single()
|
||||||
|
|
||||||
val narrow = packageScope.getContributedFunctions("narrow").single()
|
val narrow = packageScope.getContributedFunctions("narrow").single()
|
||||||
|
|
||||||
|
val objCObject = packageScope.getContributedClassifier("ObjCObject") as ClassDescriptor
|
||||||
|
val objCPointerHolder = packageScope.getContributedClassifier("ObjCPointerHolder") as ClassDescriptor
|
||||||
|
|
||||||
|
val objCPointerHolderValue = objCPointerHolder.unsubstitutedMemberScope
|
||||||
|
.getContributedDescriptors().filterIsInstance<PropertyDescriptor>().single()
|
||||||
|
|
||||||
|
val objCObjectInitFromPtr = packageScope.getContributedFunctions("initFromPtr").single()
|
||||||
|
val objCObjectInitFrom = packageScope.getContributedFunctions("initFrom").single()
|
||||||
|
|
||||||
|
val allocObjCObject = packageScope.getContributedFunctions("allocObjCObject").single()
|
||||||
|
|
||||||
|
val getObjCClass = packageScope.getContributedFunctions("getObjCClass").single()
|
||||||
|
|
||||||
|
val objCObjectRawPtr = packageScope.getContributedVariables("rawPtr").single {
|
||||||
|
val extensionReceiverType = it.extensionReceiverParameter?.type
|
||||||
|
extensionReceiverType != null && !extensionReceiverType.isMarkedNullable &&
|
||||||
|
TypeUtils.getClassDescriptor(extensionReceiverType) == objCObject
|
||||||
|
}
|
||||||
|
|
||||||
|
val getObjCReceiverOrSuper = packageScope.getContributedFunctions("getReceiverOrSuper").single()
|
||||||
|
|
||||||
|
val getObjCMessenger = packageScope.getContributedFunctions("getMessenger").single()
|
||||||
|
val getObjCMessengerLU = packageScope.getContributedFunctions("getMessengerLU").single()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun MemberScope.getContributedVariables(name: String) =
|
private fun MemberScope.getContributedVariables(name: String) =
|
||||||
|
|||||||
+156
@@ -0,0 +1,156 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.konan.descriptors.getStringValue
|
||||||
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.*
|
||||||
|
|
||||||
|
private val interopPackageName = InteropBuiltIns.FqNames.packageName
|
||||||
|
private val objCObjectFqName = interopPackageName.child(Name.identifier("ObjCObject"))
|
||||||
|
private val objCClassFqName = interopPackageName.child(Name.identifier("ObjCClass"))
|
||||||
|
private val externalObjCClassFqName = interopPackageName.child(Name.identifier("ExternalObjCClass"))
|
||||||
|
private val objCMethodFqName = interopPackageName.child(Name.identifier("ObjCMethod"))
|
||||||
|
private val objCBridgeFqName = interopPackageName.child(Name.identifier("ObjCBridge"))
|
||||||
|
|
||||||
|
fun ClassDescriptor.isObjCClass(): Boolean =
|
||||||
|
this.getAllSuperClassifiers().any { it.fqNameSafe == objCObjectFqName } &&
|
||||||
|
this.containingDeclaration.fqNameSafe != interopPackageName
|
||||||
|
|
||||||
|
fun ClassDescriptor.isExternalObjCClass(): Boolean = this.isObjCClass() &&
|
||||||
|
this.parentsWithSelf.filterIsInstance<ClassDescriptor>().any {
|
||||||
|
it.annotations.findAnnotation(externalObjCClassFqName) != null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ClassDescriptor.isObjCMetaClass(): Boolean = this.getAllSuperClassifiers().any {
|
||||||
|
it.fqNameSafe == objCClassFqName
|
||||||
|
}
|
||||||
|
|
||||||
|
fun FunctionDescriptor.isObjCClassMethod() =
|
||||||
|
this.containingDeclaration.let { it is ClassDescriptor && it.isObjCClass() }
|
||||||
|
|
||||||
|
fun FunctionDescriptor.isExternalObjCClassMethod() =
|
||||||
|
this.containingDeclaration.let { it is ClassDescriptor && it.isExternalObjCClass() }
|
||||||
|
|
||||||
|
// Special case: methods from Kotlin Objective-C classes can be called virtually from bridges.
|
||||||
|
fun FunctionDescriptor.canObjCClassMethodBeCalledVirtually(overriddenDescriptor: FunctionDescriptor) =
|
||||||
|
overriddenDescriptor.isOverridable && this.kind.isReal && !this.isExternalObjCClassMethod()
|
||||||
|
|
||||||
|
fun ClassDescriptor.isKotlinObjCClass(): Boolean = this.isObjCClass() && !this.isExternalObjCClass()
|
||||||
|
|
||||||
|
data class ObjCMethodInfo(val descriptor: FunctionDescriptor,
|
||||||
|
val bridge: FunctionDescriptor,
|
||||||
|
val selector: String,
|
||||||
|
val encoding: String,
|
||||||
|
val imp: String)
|
||||||
|
|
||||||
|
private fun CallableDescriptor.getBridgeAnnotation() =
|
||||||
|
this.annotations.findAnnotation(objCBridgeFqName)
|
||||||
|
|
||||||
|
private fun FunctionDescriptor.decodeObjCMethodAnnotation(): ObjCMethodInfo? {
|
||||||
|
assert (this.kind.isReal)
|
||||||
|
|
||||||
|
val methodAnnotation = this.annotations.findAnnotation(objCMethodFqName) ?: return null
|
||||||
|
val packageFragment = this.parents.filterIsInstance<PackageFragmentDescriptor>().single()
|
||||||
|
|
||||||
|
val bridgeName = methodAnnotation.getStringValue("bridge")
|
||||||
|
|
||||||
|
val bridge = packageFragment.getMemberScope()
|
||||||
|
.getContributedFunctions(Name.identifier(bridgeName), NoLookupLocation.FROM_BACKEND)
|
||||||
|
.single()
|
||||||
|
|
||||||
|
val bridgeAnnotation = bridge.getBridgeAnnotation()!!
|
||||||
|
return ObjCMethodInfo(
|
||||||
|
descriptor = this,
|
||||||
|
bridge = bridge,
|
||||||
|
selector = methodAnnotation.getStringValue("selector"),
|
||||||
|
encoding = bridgeAnnotation.getStringValue("encoding"),
|
||||||
|
imp = bridgeAnnotation.getStringValue("imp")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param onlyExternal indicates whether to accept overriding methods from Kotlin classes
|
||||||
|
*/
|
||||||
|
private fun FunctionDescriptor.getObjCMethodInfo(onlyExternal: Boolean): ObjCMethodInfo? {
|
||||||
|
if (this.kind.isReal) {
|
||||||
|
this.decodeObjCMethodAnnotation()?.let { return it }
|
||||||
|
|
||||||
|
if (onlyExternal) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.overriddenDescriptors.asSequence().mapNotNull { it.getObjCMethodInfo(onlyExternal) }.firstOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun FunctionDescriptor.getExternalObjCMethodInfo(): ObjCMethodInfo? = this.getObjCMethodInfo(onlyExternal = true)
|
||||||
|
|
||||||
|
fun FunctionDescriptor.getObjCMethodInfo(): ObjCMethodInfo? = this.getObjCMethodInfo(onlyExternal = false)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes method overriding rules for Objective-C methods.
|
||||||
|
*
|
||||||
|
* This class is applied at [org.jetbrains.kotlin.resolve.OverridingUtil] as configured with
|
||||||
|
* `META-INF/services/org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition` resource.
|
||||||
|
*/
|
||||||
|
class ObjCOverridabilityCondition : ExternalOverridabilityCondition {
|
||||||
|
|
||||||
|
override fun getContract() = ExternalOverridabilityCondition.Contract.BOTH
|
||||||
|
|
||||||
|
override fun isOverridable(
|
||||||
|
superDescriptor: CallableDescriptor,
|
||||||
|
subDescriptor: CallableDescriptor,
|
||||||
|
subClassDescriptor: ClassDescriptor?
|
||||||
|
): ExternalOverridabilityCondition.Result {
|
||||||
|
|
||||||
|
assert(superDescriptor.name == subDescriptor.name)
|
||||||
|
|
||||||
|
val superClass = superDescriptor.containingDeclaration as? ClassDescriptor
|
||||||
|
if (superClass == null || !superClass.isObjCClass()) {
|
||||||
|
return ExternalOverridabilityCondition.Result.UNKNOWN
|
||||||
|
}
|
||||||
|
|
||||||
|
return if (areSelectorsEqual(superDescriptor, subDescriptor)) {
|
||||||
|
ExternalOverridabilityCondition.Result.OVERRIDABLE
|
||||||
|
} else {
|
||||||
|
ExternalOverridabilityCondition.Result.INCOMPATIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun areSelectorsEqual(first: CallableDescriptor, second: CallableDescriptor): Boolean {
|
||||||
|
// The original Objective-C method selector is represented as
|
||||||
|
// function name and parameter names (except first).
|
||||||
|
|
||||||
|
if (first.valueParameters.size != second.valueParameters.size) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
first.valueParameters.forEachIndexed { index, parameter ->
|
||||||
|
if (index > 0 && parameter.name != second.valueParameters[index].name) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+9
-3
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.backend.konan.descriptors
|
package org.jetbrains.kotlin.backend.konan.descriptors
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.konan.Context
|
import org.jetbrains.kotlin.backend.konan.*
|
||||||
import org.jetbrains.kotlin.backend.konan.llvm.functionName
|
import org.jetbrains.kotlin.backend.konan.llvm.functionName
|
||||||
import org.jetbrains.kotlin.backend.konan.llvm.localHash
|
import org.jetbrains.kotlin.backend.konan.llvm.localHash
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
@@ -35,9 +35,15 @@ internal class OverriddenFunctionDescriptor(val descriptor: FunctionDescriptor,
|
|||||||
get() = descriptor.target.bridgeDirectionsTo(overriddenDescriptor)
|
get() = descriptor.target.bridgeDirectionsTo(overriddenDescriptor)
|
||||||
|
|
||||||
val canBeCalledVirtually: Boolean
|
val canBeCalledVirtually: Boolean
|
||||||
|
get() {
|
||||||
|
if (overriddenDescriptor.isObjCClassMethod()) {
|
||||||
|
return descriptor.canObjCClassMethodBeCalledVirtually(this.overriddenDescriptor)
|
||||||
|
}
|
||||||
|
|
||||||
// We check that either method is open, or one of declarations it overrides is open.
|
// We check that either method is open, or one of declarations it overrides is open.
|
||||||
get() = overriddenDescriptor.isOverridable
|
return (overriddenDescriptor.isOverridable
|
||||||
|| DescriptorUtils.getAllOverriddenDeclarations(overriddenDescriptor).any { it.isOverridable }
|
|| DescriptorUtils.getAllOverriddenDeclarations(overriddenDescriptor).any { it.isOverridable })
|
||||||
|
}
|
||||||
|
|
||||||
val inheritsBridge: Boolean
|
val inheritsBridge: Boolean
|
||||||
get() = !descriptor.kind.isReal
|
get() = !descriptor.kind.isReal
|
||||||
|
|||||||
+7
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.builtins.getFunctionalClassKind
|
|||||||
import org.jetbrains.kotlin.builtins.isFunctionType
|
import org.jetbrains.kotlin.builtins.isFunctionType
|
||||||
import org.jetbrains.kotlin.builtins.isSuspendFunctionType
|
import org.jetbrains.kotlin.builtins.isSuspendFunctionType
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
@@ -308,3 +309,9 @@ internal val FunctionDescriptor.needsInlining: Boolean
|
|||||||
internal val FunctionDescriptor.needsSerializedIr: Boolean
|
internal val FunctionDescriptor.needsSerializedIr: Boolean
|
||||||
get() = (this.needsInlining && this.isExported())
|
get() = (this.needsInlining && this.isExported())
|
||||||
|
|
||||||
|
fun AnnotationDescriptor.getStringValue(name: String): String {
|
||||||
|
val constantValue = this.allValueArguments.entries.single {
|
||||||
|
it.key.asString() == name
|
||||||
|
}.value
|
||||||
|
return constantValue.value as String
|
||||||
|
}
|
||||||
|
|||||||
+12
@@ -49,6 +49,18 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable): Sym
|
|||||||
|
|
||||||
val interopCPointerGetRawValue = symbolTable.referenceSimpleFunction(context.interopBuiltIns.cPointerGetRawValue)
|
val interopCPointerGetRawValue = symbolTable.referenceSimpleFunction(context.interopBuiltIns.cPointerGetRawValue)
|
||||||
|
|
||||||
|
val interopAllocObjCObject = symbolTable.referenceSimpleFunction(context.interopBuiltIns.allocObjCObject)
|
||||||
|
|
||||||
|
val interopGetObjCClass = symbolTable.referenceSimpleFunction(context.interopBuiltIns.getObjCClass)
|
||||||
|
|
||||||
|
val interopObjCObjectInitFromPtr =
|
||||||
|
symbolTable.referenceSimpleFunction(context.interopBuiltIns.objCObjectInitFromPtr)
|
||||||
|
|
||||||
|
val interopObjCObjectInitFrom = symbolTable.referenceSimpleFunction(context.interopBuiltIns.objCObjectInitFrom)
|
||||||
|
|
||||||
|
val interopObjCObjectRawValueGetter =
|
||||||
|
symbolTable.referenceSimpleFunction(context.interopBuiltIns.objCObjectRawPtr.getter!!)
|
||||||
|
|
||||||
val getNativeNullPtr = symbolTable.referenceSimpleFunction(context.builtIns.getNativeNullPtr)
|
val getNativeNullPtr = symbolTable.referenceSimpleFunction(context.builtIns.getNativeNullPtr)
|
||||||
|
|
||||||
val boxFunctions = ValueType.values().associate {
|
val boxFunctions = ValueType.values().associate {
|
||||||
|
|||||||
+14
-2
@@ -18,7 +18,10 @@ package org.jetbrains.kotlin.backend.konan.llvm
|
|||||||
|
|
||||||
import llvm.LLVMTypeRef
|
import llvm.LLVMTypeRef
|
||||||
import org.jetbrains.kotlin.backend.common.descriptors.allParameters
|
import org.jetbrains.kotlin.backend.common.descriptors.allParameters
|
||||||
|
import org.jetbrains.kotlin.backend.konan.descriptors.isAbstract
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.isUnit
|
import org.jetbrains.kotlin.backend.konan.descriptors.isUnit
|
||||||
|
import org.jetbrains.kotlin.backend.konan.getObjCMethodInfo
|
||||||
|
import org.jetbrains.kotlin.backend.konan.isExternalObjCClass
|
||||||
import org.jetbrains.kotlin.backend.konan.isValueType
|
import org.jetbrains.kotlin.backend.konan.isValueType
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
@@ -148,8 +151,14 @@ private val FunctionDescriptor.signature: String
|
|||||||
|
|
||||||
// TODO: rename to indicate that it has signature included
|
// TODO: rename to indicate that it has signature included
|
||||||
internal val FunctionDescriptor.functionName: String
|
internal val FunctionDescriptor.functionName: String
|
||||||
get() = with(this.original) { // basic support for generics
|
get() {
|
||||||
"$name$signature"
|
with(this.original) { // basic support for generics
|
||||||
|
this.getObjCMethodInfo()?.let {
|
||||||
|
return "objc:${it.selector}"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "$name$signature"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val FunctionDescriptor.symbolName: String
|
internal val FunctionDescriptor.symbolName: String
|
||||||
@@ -228,3 +237,6 @@ internal val ClassDescriptor.objectInstanceFieldSymbolName: String
|
|||||||
|
|
||||||
return "kobjref:$fqNameSafe"
|
return "kobjref:$fqNameSafe"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal val ClassDescriptor.typeInfoHasVtableAttached: Boolean
|
||||||
|
get() = !this.isAbstract() && !this.isExternalObjCClass()
|
||||||
|
|||||||
+27
@@ -376,6 +376,33 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
|||||||
return LLVMBuildLandingPad(builder, landingpadType, personalityFunction, numClauses, name)!!
|
return LLVMBuildLandingPad(builder, landingpadType, personalityFunction, numClauses, name)!!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline fun ifThenElse(
|
||||||
|
condition: LLVMValueRef,
|
||||||
|
thenValue: LLVMValueRef,
|
||||||
|
elseBlock: () -> LLVMValueRef
|
||||||
|
): LLVMValueRef {
|
||||||
|
val resultType = thenValue.type
|
||||||
|
|
||||||
|
val bbExit = basicBlock(locationInfo = position())
|
||||||
|
val resultPhi = appendingTo(bbExit) {
|
||||||
|
phi(resultType)
|
||||||
|
}
|
||||||
|
|
||||||
|
val bbElse = basicBlock(locationInfo = position())
|
||||||
|
|
||||||
|
condBr(condition, bbExit, bbElse)
|
||||||
|
assignPhis(resultPhi to thenValue)
|
||||||
|
|
||||||
|
appendingTo(bbElse) {
|
||||||
|
val elseValue = elseBlock()
|
||||||
|
br(bbExit)
|
||||||
|
assignPhis(resultPhi to elseValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
positionAtEnd(bbExit)
|
||||||
|
return resultPhi
|
||||||
|
}
|
||||||
|
|
||||||
internal fun debugLocation(locationInfo: LocationInfo): DILocationRef? {
|
internal fun debugLocation(locationInfo: LocationInfo): DILocationRef? {
|
||||||
if (!context.shouldContainDebugInfo()) return null
|
if (!context.shouldContainDebugInfo()) return null
|
||||||
update(currentBlock, locationInfo)
|
update(currentBlock, locationInfo)
|
||||||
|
|||||||
+3
@@ -326,6 +326,9 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
|
|||||||
val throwExceptionFunction = importRtFunction("ThrowException")
|
val throwExceptionFunction = importRtFunction("ThrowException")
|
||||||
val appendToInitalizersTail = importRtFunction("AppendToInitializersTail")
|
val appendToInitalizersTail = importRtFunction("AppendToInitializersTail")
|
||||||
|
|
||||||
|
val createKotlinObjCClass by lazy { importRtFunction("CreateKotlinObjCClass") }
|
||||||
|
val getObjCKotlinTypeInfo by lazy { importRtFunction("GetObjCKotlinTypeInfo") }
|
||||||
|
|
||||||
private val personalityFunctionName = when (context.config.targetManager.target) {
|
private val personalityFunctionName = when (context.config.targetManager.target) {
|
||||||
KonanTarget.MINGW -> "__gxx_personality_seh0"
|
KonanTarget.MINGW -> "__gxx_personality_seh0"
|
||||||
else -> "__gxx_personality_v0"
|
else -> "__gxx_personality_v0"
|
||||||
|
|||||||
+167
-10
@@ -39,6 +39,7 @@ import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
|||||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||||
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
||||||
|
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||||
@@ -156,6 +157,8 @@ internal fun verifyModule(llvmModule: LLVMModuleRef, current: String = "") {
|
|||||||
internal class RTTIGeneratorVisitor(context: Context) : IrElementVisitorVoid {
|
internal class RTTIGeneratorVisitor(context: Context) : IrElementVisitorVoid {
|
||||||
val generator = RTTIGenerator(context)
|
val generator = RTTIGenerator(context)
|
||||||
|
|
||||||
|
val kotlinObjCClassInfoGenerator = KotlinObjCClassInfoGenerator(context)
|
||||||
|
|
||||||
override fun visitElement(element: IrElement) {
|
override fun visitElement(element: IrElement) {
|
||||||
element.acceptChildrenVoid(this)
|
element.acceptChildrenVoid(this)
|
||||||
}
|
}
|
||||||
@@ -163,12 +166,17 @@ internal class RTTIGeneratorVisitor(context: Context) : IrElementVisitorVoid {
|
|||||||
override fun visitClass(declaration: IrClass) {
|
override fun visitClass(declaration: IrClass) {
|
||||||
super.visitClass(declaration)
|
super.visitClass(declaration)
|
||||||
|
|
||||||
if (declaration.descriptor.isIntrinsic) {
|
val descriptor = declaration.descriptor
|
||||||
|
if (descriptor.isIntrinsic) {
|
||||||
// do not generate any code for intrinsic classes as they require special handling
|
// do not generate any code for intrinsic classes as they require special handling
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
generator.generate(declaration.descriptor)
|
generator.generate(descriptor)
|
||||||
|
|
||||||
|
if (descriptor.isKotlinObjCClass()) {
|
||||||
|
kotlinObjCClassInfoGenerator.generate(descriptor)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -734,7 +742,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
functionGenerationContext.condBr(condition, bbExit, bbInit)
|
functionGenerationContext.condBr(condition, bbExit, bbInit)
|
||||||
|
|
||||||
functionGenerationContext.positionAtEnd(bbInit)
|
functionGenerationContext.positionAtEnd(bbInit)
|
||||||
val typeInfo = codegen.typeInfoValue(value.descriptor)
|
val typeInfo = typeInfoForAllocation(value.descriptor)
|
||||||
val initFunction = value.descriptor.constructors.first { it.valueParameters.size == 0 }
|
val initFunction = value.descriptor.constructors.first { it.valueParameters.size == 0 }
|
||||||
val ctor = codegen.llvmFunction(initFunction)
|
val ctor = codegen.llvmFunction(initFunction)
|
||||||
val args = listOf(objectPtr, typeInfo, ctor)
|
val args = listOf(objectPtr, typeInfo, ctor)
|
||||||
@@ -1350,13 +1358,32 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
private fun fieldPtrOfClass(thisPtr: LLVMValueRef, value: PropertyDescriptor): LLVMValueRef {
|
private fun fieldPtrOfClass(thisPtr: LLVMValueRef, value: PropertyDescriptor): LLVMValueRef {
|
||||||
val fieldInfo = context.llvmDeclarations.forField(value)
|
val fieldInfo = context.llvmDeclarations.forField(value)
|
||||||
|
|
||||||
|
val classDescriptor = value.containingDeclaration as ClassDescriptor
|
||||||
val typePtr = pointerType(fieldInfo.classBodyType)
|
val typePtr = pointerType(fieldInfo.classBodyType)
|
||||||
val objectPtr = LLVMBuildGEP(functionGenerationContext.builder, thisPtr, cValuesOf(kImmOne), 1, "")
|
|
||||||
val typedObjPtr = functionGenerationContext.bitcast(typePtr, objectPtr!!)
|
val bodyPtr = getObjectBodyPtr(classDescriptor, thisPtr)
|
||||||
val fieldPtr = LLVMBuildStructGEP(functionGenerationContext.builder, typedObjPtr, fieldInfo.index, "")
|
|
||||||
|
val typedBodyPtr = functionGenerationContext.bitcast(typePtr, bodyPtr)
|
||||||
|
val fieldPtr = LLVMBuildStructGEP(functionGenerationContext.builder, typedBodyPtr, fieldInfo.index, "")
|
||||||
return fieldPtr!!
|
return fieldPtr!!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getObjectBodyPtr(classDescriptor: ClassDescriptor, objectPtr: LLVMValueRef): LLVMValueRef {
|
||||||
|
return if (classDescriptor.isObjCClass()) {
|
||||||
|
assert(classDescriptor.isKotlinObjCClass())
|
||||||
|
|
||||||
|
val objCPtr = callDirect(context.interopBuiltIns.objCPointerHolderValue.getter!!,
|
||||||
|
listOf(objectPtr), Lifetime.IRRELEVANT)
|
||||||
|
|
||||||
|
val objCDeclarations = context.llvmDeclarations.forClass(classDescriptor).objCDeclarations!!
|
||||||
|
val bodyOffset = functionGenerationContext.load(objCDeclarations.bodyOffsetGlobal.llvmGlobal)
|
||||||
|
|
||||||
|
functionGenerationContext.gep(objCPtr, bodyOffset)
|
||||||
|
} else {
|
||||||
|
LLVMBuildGEP(functionGenerationContext.builder, objectPtr, cValuesOf(kImmOne), 1, "")!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
private fun evaluateConst(value: IrConst<*>): LLVMValueRef {
|
private fun evaluateConst(value: IrConst<*>): LLVMValueRef {
|
||||||
@@ -1871,7 +1898,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
functionGenerationContext.allocArray(codegen.typeInfoValue(constructedClass), count = kImmZero,
|
functionGenerationContext.allocArray(codegen.typeInfoValue(constructedClass), count = kImmZero,
|
||||||
lifetime = resultLifetime(callee))
|
lifetime = resultLifetime(callee))
|
||||||
} else {
|
} else {
|
||||||
functionGenerationContext.allocInstance(codegen.typeInfoValue(constructedClass), resultLifetime(callee))
|
functionGenerationContext.allocInstance(typeInfoForAllocation(constructedClass), resultLifetime(callee))
|
||||||
}
|
}
|
||||||
evaluateSimpleFunctionCall(callee.descriptor,
|
evaluateSimpleFunctionCall(callee.descriptor,
|
||||||
listOf(thisValue) + args, Lifetime.IRRELEVANT /* constructor doesn't return anything */)
|
listOf(thisValue) + args, Lifetime.IRRELEVANT /* constructor doesn't return anything */)
|
||||||
@@ -1879,6 +1906,15 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun typeInfoForAllocation(constructedClass: ClassDescriptor): LLVMValueRef {
|
||||||
|
val descriptorForTypeInfo = if (constructedClass.isObjCClass()) {
|
||||||
|
context.interopBuiltIns.objCPointerHolder
|
||||||
|
} else {
|
||||||
|
constructedClass
|
||||||
|
}
|
||||||
|
return codegen.typeInfoValue(descriptorForTypeInfo)
|
||||||
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
private fun evaluateIntrinsicCall(callee: IrCall, args: List<LLVMValueRef>): LLVMValueRef {
|
private fun evaluateIntrinsicCall(callee: IrCall, args: List<LLVMValueRef>): LLVMValueRef {
|
||||||
@@ -1934,10 +1970,122 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
LLVMBuildSExt(functionGenerationContext.builder, intPtrValue, resultType, "")!!
|
LLVMBuildSExt(functionGenerationContext.builder, intPtrValue, resultType, "")!!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
interop.objCObjectInitFromPtr -> {
|
||||||
|
genObjCObjectInitFromPtr(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
interop.getObjCReceiverOrSuper -> {
|
||||||
|
genGetObjCReceiverOrSuper(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
interop.getObjCClass -> {
|
||||||
|
val typeArgument = callee.getTypeArgument(descriptor.typeParameters.single())
|
||||||
|
val classDescriptor = TypeUtils.getClassDescriptor(typeArgument!!)!!
|
||||||
|
genGetObjCClass(classDescriptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
interop.getObjCMessenger -> {
|
||||||
|
genGetObjCMessenger(args, isLU = false)
|
||||||
|
}
|
||||||
|
interop.getObjCMessengerLU -> {
|
||||||
|
genGetObjCMessenger(args, isLU = true)
|
||||||
|
}
|
||||||
|
|
||||||
else -> TODO(callee.descriptor.original.toString())
|
else -> TODO(callee.descriptor.original.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun genGetObjCClass(classDescriptor: ClassDescriptor): LLVMValueRef {
|
||||||
|
assert(!classDescriptor.isInterface)
|
||||||
|
|
||||||
|
return if (classDescriptor.isExternalObjCClass()) {
|
||||||
|
val lookUpFunction = context.llvm.externalFunction("objc_lookUpClass",
|
||||||
|
functionType(int8TypePtr, false, int8TypePtr))
|
||||||
|
|
||||||
|
call(lookUpFunction,
|
||||||
|
listOf(codegen.staticData.cStringLiteral(classDescriptor.name.asString()).llvm))
|
||||||
|
} else {
|
||||||
|
val objCDeclarations = context.llvmDeclarations.forClass(classDescriptor).objCDeclarations!!
|
||||||
|
val classPointerGlobal = objCDeclarations.classPointerGlobal.llvmGlobal
|
||||||
|
val gen = functionGenerationContext
|
||||||
|
|
||||||
|
val storedClass = gen.load(classPointerGlobal)
|
||||||
|
|
||||||
|
val storedClassIsNotNull = gen.icmpNe(storedClass, kNullInt8Ptr)
|
||||||
|
|
||||||
|
return gen.ifThenElse(storedClassIsNotNull, storedClass) {
|
||||||
|
val newClass = call(context.llvm.createKotlinObjCClass,
|
||||||
|
listOf(objCDeclarations.classInfoGlobal.llvmGlobal))
|
||||||
|
|
||||||
|
gen.store(newClass, classPointerGlobal)
|
||||||
|
newClass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun genGetObjCMessenger(args: List<LLVMValueRef>, isLU: Boolean): LLVMValueRef {
|
||||||
|
val gen = functionGenerationContext
|
||||||
|
|
||||||
|
// 'LU' means "large or unaligned".
|
||||||
|
|
||||||
|
// objc_msgSend*_stret functions must be used when return value is returned through memory
|
||||||
|
// pointed by implicit argument, which is passed on the register that would otherwise be used for receiver.
|
||||||
|
// On aarch64 it is never the case, since such implicit argument gets passed on x8.
|
||||||
|
// On x86_64 it is the case if the return value takes more than 16 bytes or is the structure with
|
||||||
|
// unaligned fields (there are some complicated exceptions currently ignored). The latter condition
|
||||||
|
// is "encoded" by stub generator by emitting either `getMessenger` or `getMessengerLU` intrinsic call.
|
||||||
|
val isStret = when (context.config.targetManager.target) {
|
||||||
|
KonanTarget.MACBOOK, KonanTarget.IPHONE_SIM -> isLU // x86_64
|
||||||
|
KonanTarget.IPHONE -> false // aarch64
|
||||||
|
else -> TODO()
|
||||||
|
}
|
||||||
|
|
||||||
|
val messengerNameSuffix = if (isStret) "_stret" else ""
|
||||||
|
|
||||||
|
val functionType = functionType(int8TypePtr, true, int8TypePtr, int8TypePtr)
|
||||||
|
|
||||||
|
val normalMessenger = context.llvm.externalFunction("objc_msgSend$messengerNameSuffix", functionType)
|
||||||
|
val superMessenger = context.llvm.externalFunction("objc_msgSendSuper$messengerNameSuffix", functionType)
|
||||||
|
|
||||||
|
val superClass = args.single()
|
||||||
|
val messenger = LLVMBuildSelect(gen.builder,
|
||||||
|
If = gen.icmpEq(superClass, kNullInt8Ptr),
|
||||||
|
Then = normalMessenger,
|
||||||
|
Else = superMessenger,
|
||||||
|
Name = ""
|
||||||
|
)!!
|
||||||
|
|
||||||
|
return gen.bitcast(int8TypePtr, messenger)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun genObjCObjectInitFromPtr(args: List<LLVMValueRef>): LLVMValueRef {
|
||||||
|
return callDirect(context.interopBuiltIns.objCPointerHolder.unsubstitutedPrimaryConstructor!!,
|
||||||
|
args, Lifetime.IRRELEVANT)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun genGetObjCReceiverOrSuper(args: List<LLVMValueRef>): LLVMValueRef {
|
||||||
|
val gen = functionGenerationContext
|
||||||
|
|
||||||
|
assert(args.size == 2)
|
||||||
|
val receiver = args[0]
|
||||||
|
val superClass = args[1]
|
||||||
|
|
||||||
|
val superClassIsNull = gen.icmpEq(superClass, kNullInt8Ptr)
|
||||||
|
|
||||||
|
return gen.ifThenElse(superClassIsNull, receiver) {
|
||||||
|
val structType = structType(kInt8Ptr, kInt8Ptr)
|
||||||
|
val ptr = gen.alloca(structType)
|
||||||
|
gen.store(receiver,
|
||||||
|
LLVMBuildGEP(gen.builder, ptr, cValuesOf(kImmZero, kImmZero), 2, "")!!)
|
||||||
|
|
||||||
|
gen.store(superClass,
|
||||||
|
LLVMBuildGEP(gen.builder, ptr, cValuesOf(kImmZero, kImmOne), 2, "")!!)
|
||||||
|
|
||||||
|
gen.bitcast(int8TypePtr, ptr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
private val kImmZero = LLVMConstInt(LLVMInt32Type(), 0, 1)!!
|
private val kImmZero = LLVMConstInt(LLVMInt32Type(), 0, 1)!!
|
||||||
private val kImmOne = LLVMConstInt(LLVMInt32Type(), 1, 1)!!
|
private val kImmOne = LLVMConstInt(LLVMInt32Type(), 1, 1)!!
|
||||||
@@ -2008,11 +2156,16 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
fun callVirtual(descriptor: FunctionDescriptor, args: List<LLVMValueRef>,
|
fun callVirtual(descriptor: FunctionDescriptor, args: List<LLVMValueRef>,
|
||||||
resultLifetime: Lifetime): LLVMValueRef {
|
resultLifetime: Lifetime): LLVMValueRef {
|
||||||
assert(LLVMTypeOf(args[0]) == codegen.kObjHeaderPtr)
|
assert(LLVMTypeOf(args[0]) == codegen.kObjHeaderPtr)
|
||||||
val typeInfoPtrPtr = LLVMBuildStructGEP(functionGenerationContext.builder, args[0], 0 /* type_info */, "")!!
|
|
||||||
val typeInfoPtr = functionGenerationContext.load(typeInfoPtrPtr)
|
|
||||||
assert (typeInfoPtr.type == codegen.kTypeInfoPtr)
|
|
||||||
|
|
||||||
val owner = descriptor.containingDeclaration as ClassDescriptor
|
val owner = descriptor.containingDeclaration as ClassDescriptor
|
||||||
|
|
||||||
|
val typeInfoPtr: LLVMValueRef = if (owner.isObjCClass()) {
|
||||||
|
call(context.llvm.getObjCKotlinTypeInfo, listOf(args.first()))
|
||||||
|
} else {
|
||||||
|
val typeInfoPtrPtr = LLVMBuildStructGEP(functionGenerationContext.builder, args[0], 0 /* type_info */, "")!!
|
||||||
|
functionGenerationContext.load(typeInfoPtrPtr)
|
||||||
|
}
|
||||||
|
assert (typeInfoPtr.type == codegen.kTypeInfoPtr)
|
||||||
val llvmMethod = if (!owner.isInterface) {
|
val llvmMethod = if (!owner.isInterface) {
|
||||||
// If this is a virtual method of the class - we can call via vtable.
|
// If this is a virtual method of the class - we can call via vtable.
|
||||||
val index = context.getVtableBuilder(owner).vtableIndex(descriptor)
|
val index = context.getVtableBuilder(owner).vtableIndex(descriptor)
|
||||||
@@ -2068,6 +2221,10 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
val constructedClass = functionGenerationContext.constructedClass!!
|
val constructedClass = functionGenerationContext.constructedClass!!
|
||||||
val thisPtr = currentCodeContext.genGetValue(constructedClass.thisAsReceiverParameter)
|
val thisPtr = currentCodeContext.genGetValue(constructedClass.thisAsReceiverParameter)
|
||||||
|
|
||||||
|
if (constructedClass.isObjCClass()) {
|
||||||
|
return codegen.theUnitInstanceRef.llvm
|
||||||
|
}
|
||||||
|
|
||||||
val thisPtrArgType = codegen.getLLVMType(descriptor.allParameters[0].type)
|
val thisPtrArgType = codegen.getLLVMType(descriptor.allParameters[0].type)
|
||||||
val thisPtrArg = if (thisPtr.type == thisPtrArgType) {
|
val thisPtrArg = if (thisPtr.type == thisPtrArgType) {
|
||||||
thisPtr
|
thisPtr
|
||||||
|
|||||||
+94
@@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* 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.llvm
|
||||||
|
|
||||||
|
import llvm.LLVMStoreSizeOfType
|
||||||
|
import org.jetbrains.kotlin.backend.konan.Context
|
||||||
|
import org.jetbrains.kotlin.backend.konan.ObjCMethodInfo
|
||||||
|
import org.jetbrains.kotlin.backend.konan.descriptors.contributedMethods
|
||||||
|
import org.jetbrains.kotlin.backend.konan.getObjCMethodInfo
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.isFinalClass
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces
|
||||||
|
|
||||||
|
internal class KotlinObjCClassInfoGenerator(override val context: Context) : ContextUtils {
|
||||||
|
fun generate(descriptor: ClassDescriptor) {
|
||||||
|
assert(descriptor.isFinalClass)
|
||||||
|
|
||||||
|
val objCLLvmDeclarations = context.llvmDeclarations.forClass(descriptor).objCDeclarations!!
|
||||||
|
|
||||||
|
val instanceMethods = descriptor.generateMethodDescs()
|
||||||
|
|
||||||
|
val companionObjectDescriptor = descriptor.companionObjectDescriptor
|
||||||
|
val classMethods = companionObjectDescriptor?.generateMethodDescs() ?: emptyList()
|
||||||
|
|
||||||
|
val superclassName = descriptor.getSuperClassNotAny()!!.name.asString()
|
||||||
|
val protocolNames = descriptor.getSuperInterfaces().map { it.name.asString().removeSuffix("Protocol") }
|
||||||
|
|
||||||
|
val bodySize =
|
||||||
|
LLVMStoreSizeOfType(llvmTargetData, context.llvmDeclarations.forClass(descriptor).bodyType).toInt()
|
||||||
|
|
||||||
|
val className = if (descriptor.isExported()) {
|
||||||
|
staticData.cStringLiteral(descriptor.fqNameSafe.asString())
|
||||||
|
} else {
|
||||||
|
NullPointer(int8Type) // Generate as anonymous.
|
||||||
|
}
|
||||||
|
|
||||||
|
val info = Struct(runtime.kotlinObjCClassInfo,
|
||||||
|
className,
|
||||||
|
|
||||||
|
staticData.cStringLiteral(superclassName),
|
||||||
|
staticData.placeGlobalConstArray("", int8TypePtr,
|
||||||
|
protocolNames.map { staticData.cStringLiteral(it) } + NullPointer(int8Type)),
|
||||||
|
|
||||||
|
staticData.placeGlobalConstArray("", runtime.objCMethodDescription, instanceMethods),
|
||||||
|
Int32(instanceMethods.size),
|
||||||
|
|
||||||
|
staticData.placeGlobalConstArray("", runtime.objCMethodDescription, classMethods),
|
||||||
|
Int32(classMethods.size),
|
||||||
|
|
||||||
|
Int32(bodySize),
|
||||||
|
objCLLvmDeclarations.bodyOffsetGlobal.pointer,
|
||||||
|
|
||||||
|
descriptor.typeInfoPtr,
|
||||||
|
companionObjectDescriptor?.typeInfoPtr ?: NullPointer(runtime.typeInfoType),
|
||||||
|
|
||||||
|
objCLLvmDeclarations.classPointerGlobal.pointer
|
||||||
|
)
|
||||||
|
|
||||||
|
objCLLvmDeclarations.classInfoGlobal.setInitializer(info)
|
||||||
|
|
||||||
|
objCLLvmDeclarations.classPointerGlobal.setInitializer(NullPointer(int8Type))
|
||||||
|
objCLLvmDeclarations.bodyOffsetGlobal.setInitializer(Int32(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateMethodDesc(info: ObjCMethodInfo): ConstValue {
|
||||||
|
return Struct(runtime.objCMethodDescription,
|
||||||
|
staticData.cStringLiteral(info.selector),
|
||||||
|
staticData.cStringLiteral(info.encoding),
|
||||||
|
constPointer(context.llvm.externalFunction(info.imp, functionType(voidType))).bitcast(int8TypePtr)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ClassDescriptor.generateMethodDescs(): List<ConstValue> =
|
||||||
|
this.unsubstitutedMemberScope.contributedMethods.filter {
|
||||||
|
it.kind.isReal && it !is ConstructorDescriptor
|
||||||
|
}.mapNotNull { it.getObjCMethodInfo() }.map { generateMethodDesc(it) }
|
||||||
|
}
|
||||||
+37
-3
@@ -21,6 +21,7 @@ import llvm.*
|
|||||||
import org.jetbrains.kotlin.backend.konan.Context
|
import org.jetbrains.kotlin.backend.konan.Context
|
||||||
import org.jetbrains.kotlin.backend.konan.KonanConfigKeys
|
import org.jetbrains.kotlin.backend.konan.KonanConfigKeys
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.*
|
import org.jetbrains.kotlin.backend.konan.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.backend.konan.isKotlinObjCClass
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.*
|
import org.jetbrains.kotlin.serialization.deserialization.descriptors.*
|
||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
@@ -75,10 +76,17 @@ internal class ClassLlvmDeclarations(
|
|||||||
val fields: List<PropertyDescriptor>, // TODO: it is not an LLVM declaration.
|
val fields: List<PropertyDescriptor>, // TODO: it is not an LLVM declaration.
|
||||||
val typeInfoGlobal: StaticData.Global,
|
val typeInfoGlobal: StaticData.Global,
|
||||||
val typeInfo: ConstPointer,
|
val typeInfo: ConstPointer,
|
||||||
val singletonDeclarations: SingletonLlvmDeclarations?)
|
val singletonDeclarations: SingletonLlvmDeclarations?,
|
||||||
|
val objCDeclarations: KotlinObjCClassLlvmDeclarations?)
|
||||||
|
|
||||||
internal class SingletonLlvmDeclarations(val instanceFieldRef: LLVMValueRef)
|
internal class SingletonLlvmDeclarations(val instanceFieldRef: LLVMValueRef)
|
||||||
|
|
||||||
|
internal class KotlinObjCClassLlvmDeclarations(
|
||||||
|
val classPointerGlobal: StaticData.Global,
|
||||||
|
val classInfoGlobal: StaticData.Global,
|
||||||
|
val bodyOffsetGlobal: StaticData.Global
|
||||||
|
)
|
||||||
|
|
||||||
internal class FunctionLlvmDeclarations(val llvmFunction: LLVMValueRef)
|
internal class FunctionLlvmDeclarations(val llvmFunction: LLVMValueRef)
|
||||||
|
|
||||||
internal class FieldLlvmDeclarations(val index: Int, val classBodyType: LLVMTypeRef)
|
internal class FieldLlvmDeclarations(val index: Int, val classBodyType: LLVMTypeRef)
|
||||||
@@ -236,7 +244,7 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
|
|||||||
"ktype:$internalName"
|
"ktype:$internalName"
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!descriptor.isAbstract()) {
|
if (descriptor.typeInfoHasVtableAttached) {
|
||||||
// Create the special global consisting of TypeInfo and vtable.
|
// Create the special global consisting of TypeInfo and vtable.
|
||||||
|
|
||||||
val typeInfoGlobalName = "ktypeglobal:$internalName"
|
val typeInfoGlobalName = "ktypeglobal:$internalName"
|
||||||
@@ -273,7 +281,14 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
return ClassLlvmDeclarations(bodyType, fields, typeInfoGlobal, typeInfoPtr, singletonDeclarations)
|
val objCDeclarations = if (descriptor.isKotlinObjCClass()) {
|
||||||
|
createKotlinObjCClassDeclarations(descriptor)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
return ClassLlvmDeclarations(bodyType, fields, typeInfoGlobal, typeInfoPtr,
|
||||||
|
singletonDeclarations, objCDeclarations)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createSingletonDeclarations(
|
private fun createSingletonDeclarations(
|
||||||
@@ -299,6 +314,23 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
|
|||||||
return SingletonLlvmDeclarations(instanceFieldRef)
|
return SingletonLlvmDeclarations(instanceFieldRef)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun createKotlinObjCClassDeclarations(descriptor: ClassDescriptor): KotlinObjCClassLlvmDeclarations {
|
||||||
|
val internalName = qualifyInternalName(descriptor)
|
||||||
|
|
||||||
|
val classPointerGlobal = staticData.createGlobal(int8TypePtr, "kobjcclassptr:$internalName")
|
||||||
|
|
||||||
|
val classInfoGlobal = staticData.createGlobal(
|
||||||
|
context.llvm.runtime.kotlinObjCClassInfo,
|
||||||
|
"kobjcclassinfo:$internalName"
|
||||||
|
).apply {
|
||||||
|
setConstant(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
val bodyOffsetGlobal = staticData.createGlobal(int32Type, "kobjcbodyoffs:$internalName")
|
||||||
|
|
||||||
|
return KotlinObjCClassLlvmDeclarations(classPointerGlobal, classInfoGlobal, bodyOffsetGlobal)
|
||||||
|
}
|
||||||
|
|
||||||
override fun visitField(declaration: IrField) {
|
override fun visitField(declaration: IrField) {
|
||||||
super.visitField(declaration)
|
super.visitField(declaration)
|
||||||
|
|
||||||
@@ -330,6 +362,8 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
|
|||||||
override fun visitFunction(declaration: IrFunction) {
|
override fun visitFunction(declaration: IrFunction) {
|
||||||
super.visitFunction(declaration)
|
super.visitFunction(declaration)
|
||||||
|
|
||||||
|
if (!declaration.descriptor.kind.isReal) return
|
||||||
|
|
||||||
val descriptor = declaration.descriptor
|
val descriptor = declaration.descriptor
|
||||||
val llvmFunctionType = getLlvmFunctionType(descriptor)
|
val llvmFunctionType = getLlvmFunctionType(descriptor)
|
||||||
|
|
||||||
|
|||||||
+10
-2
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.backend.konan.llvm
|
|||||||
import llvm.*
|
import llvm.*
|
||||||
import org.jetbrains.kotlin.backend.konan.Context
|
import org.jetbrains.kotlin.backend.konan.Context
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.*
|
import org.jetbrains.kotlin.backend.konan.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.backend.konan.isExternalObjCClassMethod
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
@@ -166,11 +167,18 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
|||||||
|
|
||||||
val typeInfoGlobal = llvmDeclarations.typeInfoGlobal
|
val typeInfoGlobal = llvmDeclarations.typeInfoGlobal
|
||||||
|
|
||||||
val typeInfoGlobalValue = if (classDesc.isAbstract()) {
|
val typeInfoGlobalValue = if (!classDesc.typeInfoHasVtableAttached) {
|
||||||
typeInfo
|
typeInfo
|
||||||
} else {
|
} else {
|
||||||
// TODO: compile-time resolution limits binary compatibility
|
// TODO: compile-time resolution limits binary compatibility
|
||||||
val vtableEntries = context.getVtableBuilder(classDesc).vtableEntries.map { it.implementation.entryPointAddress }
|
val vtableEntries = context.getVtableBuilder(classDesc).vtableEntries.map {
|
||||||
|
val implementation = it.implementation
|
||||||
|
if (implementation.isExternalObjCClassMethod()) {
|
||||||
|
NullPointer(int8Type)
|
||||||
|
} else {
|
||||||
|
implementation.entryPointAddress
|
||||||
|
}
|
||||||
|
}
|
||||||
val vtable = ConstArray(int8TypePtr, vtableEntries)
|
val vtable = ConstArray(int8TypePtr, vtableEntries)
|
||||||
Struct(typeInfo, vtable)
|
Struct(typeInfo, vtable)
|
||||||
}
|
}
|
||||||
|
|||||||
+3
@@ -53,4 +53,7 @@ class Runtime(bitcodeFile: String) {
|
|||||||
val dataLayout = LLVMGetDataLayout(llvmModule)!!.toKString()
|
val dataLayout = LLVMGetDataLayout(llvmModule)!!.toKString()
|
||||||
|
|
||||||
val targetData = LLVMCreateTargetData(dataLayout)!!
|
val targetData = LLVMCreateTargetData(dataLayout)!!
|
||||||
|
|
||||||
|
val kotlinObjCClassInfo by lazy { getStructType("KotlinObjCClassInfo") }
|
||||||
|
val objCMethodDescription by lazy { getStructType("ObjCMethodDescription") }
|
||||||
}
|
}
|
||||||
|
|||||||
+4
@@ -152,6 +152,10 @@ internal class StaticData(override val context: Context): ContextUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val stringLiterals = mutableMapOf<String, ConstPointer>()
|
private val stringLiterals = mutableMapOf<String, ConstPointer>()
|
||||||
|
private val cStringLiterals = mutableMapOf<String, ConstPointer>()
|
||||||
|
|
||||||
|
fun cStringLiteral(value: String) =
|
||||||
|
cStringLiterals.getOrPut(value) { placeCStringLiteral(value) }
|
||||||
|
|
||||||
fun kotlinStringLiteral(type: KotlinType, value: IrConst<String>) =
|
fun kotlinStringLiteral(type: KotlinType, value: IrConst<String>) =
|
||||||
stringLiterals.getOrPut(value.value) { createKotlinStringLiteral(type, value) }
|
stringLiterals.getOrPut(value.value) { createKotlinStringLiteral(type, value) }
|
||||||
|
|||||||
+6
@@ -41,3 +41,9 @@ internal fun StaticData.createAlias(name: String, aliasee: ConstPointer): ConstP
|
|||||||
val alias = LLVMAddAlias(context.llvmModule, aliasee.llvmType, aliasee.llvm, name)!!
|
val alias = LLVMAddAlias(context.llvmModule, aliasee.llvmType, aliasee.llvm, name)!!
|
||||||
return constPointer(alias)
|
return constPointer(alias)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun StaticData.placeCStringLiteral(value: String): ConstPointer {
|
||||||
|
val chars = value.toByteArray(Charsets.UTF_8).map { Int8(it) } + Int8(0)
|
||||||
|
|
||||||
|
return placeGlobalConstArray("", int8Type, chars)
|
||||||
|
}
|
||||||
+257
-15
@@ -20,40 +20,282 @@ import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
|||||||
import org.jetbrains.kotlin.backend.common.descriptors.allParameters
|
import org.jetbrains.kotlin.backend.common.descriptors.allParameters
|
||||||
import org.jetbrains.kotlin.backend.common.lower.IrBuildingTransformer
|
import org.jetbrains.kotlin.backend.common.lower.IrBuildingTransformer
|
||||||
import org.jetbrains.kotlin.backend.common.lower.at
|
import org.jetbrains.kotlin.backend.common.lower.at
|
||||||
import org.jetbrains.kotlin.backend.konan.Context
|
import org.jetbrains.kotlin.backend.common.lower.irBlock
|
||||||
import org.jetbrains.kotlin.backend.konan.ValueType
|
import org.jetbrains.kotlin.backend.common.peek
|
||||||
import org.jetbrains.kotlin.backend.konan.isRepresentedAs
|
import org.jetbrains.kotlin.backend.common.pop
|
||||||
import org.jetbrains.kotlin.backend.konan.reportCompilationError
|
import org.jetbrains.kotlin.backend.common.push
|
||||||
|
import org.jetbrains.kotlin.backend.konan.*
|
||||||
|
import org.jetbrains.kotlin.backend.konan.descriptors.getStringValue
|
||||||
|
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.ir.builders.IrBuilder
|
import org.jetbrains.kotlin.ir.IrStatement
|
||||||
import org.jetbrains.kotlin.ir.builders.irCall
|
import org.jetbrains.kotlin.ir.builders.*
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
|
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetObjectValueImpl
|
|
||||||
import org.jetbrains.kotlin.ir.util.defaultType
|
import org.jetbrains.kotlin.ir.util.defaultType
|
||||||
import org.jetbrains.kotlin.ir.util.functions
|
import org.jetbrains.kotlin.ir.util.functions
|
||||||
import org.jetbrains.kotlin.ir.util.getArguments
|
import org.jetbrains.kotlin.ir.util.getArguments
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
|
||||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.TypeUtils
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||||
|
|
||||||
internal class InteropLoweringPart1(val context: Context) : IrElementTransformerVoid(), FileLoweringPass {
|
internal class InteropLoweringPart1(val context: Context) : IrBuildingTransformer(context), FileLoweringPass {
|
||||||
|
|
||||||
|
private val symbols get() = context.ir.symbols
|
||||||
|
private val symbolTable get() = symbols.symbolTable
|
||||||
|
|
||||||
|
lateinit var currentFile: IrFile
|
||||||
|
|
||||||
override fun lower(irFile: IrFile) {
|
override fun lower(irFile: IrFile) {
|
||||||
|
currentFile = irFile
|
||||||
irFile.transformChildrenVoid(this)
|
irFile.transformChildrenVoid(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun IrBuilderWithScope.callAlloc(classSymbol: IrClassSymbol): IrExpression {
|
||||||
|
return irCall(symbols.interopAllocObjCObject, listOf(classSymbol.descriptor.defaultType)).apply {
|
||||||
|
putValueArgument(0, getObjCClass(classSymbol))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun IrBuilderWithScope.getObjCClass(classSymbol: IrClassSymbol): IrExpression {
|
||||||
|
val classDescriptor = classSymbol.descriptor
|
||||||
|
assert(!classDescriptor.isObjCMetaClass())
|
||||||
|
|
||||||
|
if (classDescriptor.isExternalObjCClass()) {
|
||||||
|
val companionObject = classDescriptor.companionObjectDescriptor!!
|
||||||
|
if (companionObject.unsubstitutedPrimaryConstructor != scope.scopeOwner) {
|
||||||
|
// Optimization: get class pointer from companion object thus avoiding lookup by name.
|
||||||
|
return irCall(symbols.interopObjCObjectRawValueGetter).apply {
|
||||||
|
extensionReceiver = irGetObject(symbolTable.referenceClass(companionObject))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return irCall(symbols.interopGetObjCClass, listOf(classDescriptor.defaultType))
|
||||||
|
}
|
||||||
|
|
||||||
|
private val outerClasses = mutableListOf<IrClass>()
|
||||||
|
|
||||||
|
override fun visitClass(declaration: IrClass): IrStatement {
|
||||||
|
if (declaration.descriptor.isKotlinObjCClass()) {
|
||||||
|
checkKotlinObjCClass(declaration)
|
||||||
|
}
|
||||||
|
|
||||||
|
outerClasses.push(declaration)
|
||||||
|
try {
|
||||||
|
return super.visitClass(declaration)
|
||||||
|
} finally {
|
||||||
|
outerClasses.pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkKotlinObjCClass(irClass: IrClass) {
|
||||||
|
val kind = irClass.descriptor.kind
|
||||||
|
if (kind != ClassKind.CLASS && kind != ClassKind.OBJECT) {
|
||||||
|
context.reportCompilationError(
|
||||||
|
"Only classes are supported as subtypes of Objective-C types",
|
||||||
|
currentFile, irClass
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!irClass.descriptor.isFinalClass) {
|
||||||
|
context.reportCompilationError(
|
||||||
|
"Non-final Kotlin subclasses of Objective-C classes are not yet supported",
|
||||||
|
currentFile, irClass
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasObjCClassSupertype = false
|
||||||
|
irClass.descriptor.defaultType.constructor.supertypes.forEach {
|
||||||
|
val descriptor = it.constructor.declarationDescriptor as ClassDescriptor
|
||||||
|
if (!descriptor.isObjCClass()) {
|
||||||
|
context.reportCompilationError(
|
||||||
|
"Mixing Kotlin and Objective-C supertypes is not supported",
|
||||||
|
currentFile, irClass
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (descriptor.kind == ClassKind.CLASS) {
|
||||||
|
hasObjCClassSupertype = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasObjCClassSupertype) {
|
||||||
|
context.reportCompilationError(
|
||||||
|
"Kotlin implementation of Objective-C protocol must have Objective-C superclass (e.g. NSObject)",
|
||||||
|
currentFile, irClass
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression {
|
||||||
|
expression.transformChildrenVoid()
|
||||||
|
|
||||||
|
builder.at(expression)
|
||||||
|
|
||||||
|
val constructedClass = outerClasses.peek()!!
|
||||||
|
val constructedClassDescriptor = constructedClass.descriptor
|
||||||
|
|
||||||
|
if (!constructedClassDescriptor.isObjCClass()) {
|
||||||
|
return expression
|
||||||
|
}
|
||||||
|
|
||||||
|
constructedClassDescriptor.containingDeclaration.let { classContainer ->
|
||||||
|
if (classContainer is ClassDescriptor && classContainer.isObjCClass() &&
|
||||||
|
constructedClassDescriptor == classContainer.companionObjectDescriptor) {
|
||||||
|
|
||||||
|
val outerConstructedClass = outerClasses[outerClasses.lastIndex - 1]
|
||||||
|
assert (outerConstructedClass.descriptor == classContainer)
|
||||||
|
|
||||||
|
assert(expression.getArguments().isEmpty())
|
||||||
|
|
||||||
|
return builder.irBlock(expression) {
|
||||||
|
+expression // Required for the IR to be valid, will be ignored in codegen.
|
||||||
|
+irCall(symbols.interopObjCObjectInitFromPtr).apply {
|
||||||
|
extensionReceiver = irGet(constructedClass.thisReceiver!!.symbol)
|
||||||
|
putValueArgument(0, getObjCClass(outerConstructedClass.symbol))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!constructedClassDescriptor.isExternalObjCClass() &&
|
||||||
|
expression.descriptor.constructedClass.isExternalObjCClass()) {
|
||||||
|
|
||||||
|
// Calling super constructor from Kotlin Objective-C class.
|
||||||
|
|
||||||
|
assert(constructedClassDescriptor.getSuperClassNotAny() == expression.descriptor.constructedClass)
|
||||||
|
|
||||||
|
val initMethod = getObjCInitMethod(expression.descriptor)!!
|
||||||
|
val initMethodInfo = initMethod.getExternalObjCMethodInfo()!!
|
||||||
|
|
||||||
|
assert(expression.dispatchReceiver == null)
|
||||||
|
assert(expression.extensionReceiver == null)
|
||||||
|
|
||||||
|
val initCall = builder.genLoweredObjCMethodCall(
|
||||||
|
initMethodInfo,
|
||||||
|
superQualifier = symbolTable.referenceClass(expression.descriptor.constructedClass),
|
||||||
|
receiver = builder.callAlloc(constructedClass.symbol),
|
||||||
|
arguments = initMethod.valueParameters.map { expression.getValueArgument(it)!! }
|
||||||
|
)
|
||||||
|
|
||||||
|
val superConstructor = symbolTable.referenceConstructor(
|
||||||
|
expression.descriptor.constructedClass.constructors.single { it.valueParameters.size == 0 }
|
||||||
|
)
|
||||||
|
|
||||||
|
return builder.irBlock(expression) {
|
||||||
|
// Required for the IR to be valid, will be ignored in codegen:
|
||||||
|
+IrDelegatingConstructorCallImpl(startOffset, endOffset, superConstructor, superConstructor.descriptor)
|
||||||
|
|
||||||
|
+irCall(symbols.interopObjCObjectInitFrom).apply {
|
||||||
|
extensionReceiver = irGet(constructedClass.thisReceiver!!.symbol)
|
||||||
|
putValueArgument(0, initCall)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return expression
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun IrBuilderWithScope.genLoweredObjCMethodCall(info: ObjCMethodInfo, superQualifier: IrClassSymbol?,
|
||||||
|
receiver: IrExpression, arguments: List<IrExpression>): IrExpression {
|
||||||
|
|
||||||
|
val superClass = superQualifier?.let { getObjCClass(it) } ?:
|
||||||
|
irCall(symbols.getNativeNullPtr)
|
||||||
|
|
||||||
|
val bridge = symbolTable.referenceSimpleFunction(info.bridge)
|
||||||
|
return irCall(bridge).apply {
|
||||||
|
putValueArgument(0, superClass)
|
||||||
|
putValueArgument(1, receiver)
|
||||||
|
|
||||||
|
assert(arguments.size + 2 == info.bridge.valueParameters.size)
|
||||||
|
arguments.forEachIndexed { index, argument ->
|
||||||
|
putValueArgument(index + 2, argument)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getObjCInitMethod(descriptor: ConstructorDescriptor): FunctionDescriptor? {
|
||||||
|
return descriptor.annotations.findAnnotation(FqName("kotlinx.cinterop.ObjCConstructor"))?.let {
|
||||||
|
val initSelector = it.getStringValue("initSelector")
|
||||||
|
descriptor.constructedClass.unsubstitutedMemberScope.getContributedDescriptors().asSequence()
|
||||||
|
.filterIsInstance<FunctionDescriptor>()
|
||||||
|
.single { it.getExternalObjCMethodInfo()?.selector == initSelector }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun visitCall(expression: IrCall): IrExpression {
|
override fun visitCall(expression: IrCall): IrExpression {
|
||||||
expression.transformChildrenVoid()
|
expression.transformChildrenVoid()
|
||||||
|
|
||||||
return when (expression.descriptor.original) {
|
val descriptor = expression.descriptor.original
|
||||||
|
|
||||||
|
if (descriptor is ConstructorDescriptor) {
|
||||||
|
val initMethod = getObjCInitMethod(descriptor)
|
||||||
|
|
||||||
|
if (initMethod != null) {
|
||||||
|
val arguments = descriptor.valueParameters.map { expression.getValueArgument(it)!! }
|
||||||
|
assert(expression.extensionReceiver == null)
|
||||||
|
assert(expression.dispatchReceiver == null)
|
||||||
|
|
||||||
|
builder.at(expression)
|
||||||
|
|
||||||
|
return builder.genLoweredObjCMethodCall(
|
||||||
|
initMethod.getExternalObjCMethodInfo()!!,
|
||||||
|
superQualifier = null,
|
||||||
|
receiver = builder.callAlloc(symbolTable.referenceClass(descriptor.constructedClass)),
|
||||||
|
arguments = arguments
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptor.getExternalObjCMethodInfo()?.let { methodInfo ->
|
||||||
|
val isInteropStubsFile =
|
||||||
|
currentFile.fileAnnotations.any { it.fqName == FqName("kotlinx.cinterop.InteropStubs") }
|
||||||
|
|
||||||
|
// Special case: bridge from Objective-C method implementation template to Kotlin method;
|
||||||
|
// handled in CodeGeneratorVisitor.callVirtual.
|
||||||
|
val useKotlinDispatch = isInteropStubsFile &&
|
||||||
|
builder.scope.scopeOwner.annotations.hasAnnotation(FqName("konan.internal.ExportForCppRuntime"))
|
||||||
|
|
||||||
|
if (!useKotlinDispatch) {
|
||||||
|
val arguments = descriptor.valueParameters.map { expression.getValueArgument(it)!! }
|
||||||
|
assert(expression.extensionReceiver == null)
|
||||||
|
|
||||||
|
if (expression.superQualifier?.isObjCMetaClass() == true) {
|
||||||
|
context.reportCompilationError(
|
||||||
|
"Super calls to Objective-C meta classes are not supported yet",
|
||||||
|
currentFile, expression
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expression.superQualifier?.isInterface == true) {
|
||||||
|
context.reportCompilationError(
|
||||||
|
"Super calls to Objective-C protocols are not allowed",
|
||||||
|
currentFile, expression
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.at(expression)
|
||||||
|
return builder.genLoweredObjCMethodCall(
|
||||||
|
methodInfo,
|
||||||
|
superQualifier = expression.superQualifierSymbol,
|
||||||
|
receiver = expression.dispatchReceiver!!,
|
||||||
|
arguments = arguments
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return when (descriptor) {
|
||||||
context.interopBuiltIns.typeOf -> {
|
context.interopBuiltIns.typeOf -> {
|
||||||
val typeArgument = expression.getSingleTypeArgument()
|
val typeArgument = expression.getSingleTypeArgument()
|
||||||
val classDescriptor = TypeUtils.getClassDescriptor(typeArgument)
|
val classDescriptor = TypeUtils.getClassDescriptor(typeArgument)
|
||||||
@@ -65,8 +307,8 @@ internal class InteropLoweringPart1(val context: Context) : IrElementTransformer
|
|||||||
error("native variable class $classDescriptor must have the companion object")
|
error("native variable class $classDescriptor must have the companion object")
|
||||||
|
|
||||||
IrGetObjectValueImpl(
|
IrGetObjectValueImpl(
|
||||||
expression.startOffset, expression.endOffset,
|
expression.startOffset, expression.endOffset, companionObjectDescriptor.defaultType,
|
||||||
companionObjectDescriptor.defaultType, companionObjectDescriptor
|
symbolTable.referenceClass(companionObjectDescriptor)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ libffiDir.osx = libffi-3.2.1-2-darwin-macos
|
|||||||
llvmLtoFlags.osx =
|
llvmLtoFlags.osx =
|
||||||
llvmLtoOptFlags.osx = -O3 -function-sections
|
llvmLtoOptFlags.osx = -O3 -function-sections
|
||||||
llvmLtoNooptFlags.osx = -O1
|
llvmLtoNooptFlags.osx = -O1
|
||||||
linkerKonanFlags.osx = -lc++
|
linkerKonanFlags.osx = -lc++ -lobjc
|
||||||
linkerOptimizationFlags.osx = -dead_strip
|
linkerOptimizationFlags.osx = -dead_strip
|
||||||
linkerDebugFlags.osx = -S
|
linkerDebugFlags.osx = -S
|
||||||
osVersionMinFlagLd.osx = -macosx_version_min
|
osVersionMinFlagLd.osx = -macosx_version_min
|
||||||
@@ -63,7 +63,7 @@ llvmLtoFlags.ios =
|
|||||||
llvmLtoOptFlags.ios = -O3 -function-sections
|
llvmLtoOptFlags.ios = -O3 -function-sections
|
||||||
linkerDebugFlags.ios = -S
|
linkerDebugFlags.ios = -S
|
||||||
llvmLtoNooptFlags.ios = -O1
|
llvmLtoNooptFlags.ios = -O1
|
||||||
linkerKonanFlags.ios = -lc++
|
linkerKonanFlags.ios = -lc++ -lobjc
|
||||||
linkerOptimizationFlags.ios = -dead_strip
|
linkerOptimizationFlags.ios = -dead_strip
|
||||||
osVersionMinFlagLd.ios = -iphoneos_version_min
|
osVersionMinFlagLd.ios = -iphoneos_version_min
|
||||||
osVersionMinFlagClang.ios = -miphoneos-version-min
|
osVersionMinFlagClang.ios = -miphoneos-version-min
|
||||||
@@ -83,7 +83,7 @@ libffiDir.ios_sim = libffi-3.2.1-2-darwin-ios-sim
|
|||||||
llvmLtoFlags.ios_sim =
|
llvmLtoFlags.ios_sim =
|
||||||
llvmLtoOptFlags.ios_sim = -O3 -function-sections
|
llvmLtoOptFlags.ios_sim = -O3 -function-sections
|
||||||
llvmLtoNooptFlags.ios_sim = -O1
|
llvmLtoNooptFlags.ios_sim = -O1
|
||||||
linkerKonanFlags.ios_sim = -lc++
|
linkerKonanFlags.ios_sim = -lc++ -lobjc
|
||||||
linkerOptimizationFlags.ios_sim = -dead_strip
|
linkerOptimizationFlags.ios_sim = -dead_strip
|
||||||
linkerDebugFlags.ios_sim = -S
|
linkerDebugFlags.ios_sim = -S
|
||||||
osVersionMinFlagLd.ios_sim = -ios_simulator_version_min
|
osVersionMinFlagLd.ios_sim = -ios_simulator_version_min
|
||||||
|
|||||||
@@ -1995,6 +1995,13 @@ kotlinNativeInterop {
|
|||||||
linkerOpts '-framework', 'OpenGL', '-framework', 'GLUT'
|
linkerOpts '-framework', 'OpenGL', '-framework', 'GLUT'
|
||||||
flavor 'native'
|
flavor 'native'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
objcSmoke {
|
||||||
|
defFile 'interop/objc/objcSmoke.def'
|
||||||
|
headers "$projectDir/interop/objc/smoke.h"
|
||||||
|
linkerOpts "-L$buildDir", "-lobjcsmoke"
|
||||||
|
flavor 'native'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2057,4 +2064,19 @@ if (isMac()) {
|
|||||||
source = "interop/basics/opengl_teapot.kt"
|
source = "interop/basics/opengl_teapot.kt"
|
||||||
interop = 'opengl'
|
interop = 'opengl'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task interop_objc_smoke(type: RunInteropKonanTest) {
|
||||||
|
goldValue = "Hello, World!\nKotlin says: Hello, everybody!\nHello from Kotlin\n2, 1\nDeallocated\nDeallocated\n"
|
||||||
|
|
||||||
|
source = "interop/objc/smoke.kt"
|
||||||
|
interop = 'objcSmoke'
|
||||||
|
|
||||||
|
doFirst {
|
||||||
|
execClang {
|
||||||
|
args "$projectDir/interop/objc/smoke.m"
|
||||||
|
args "-lobjc", '-fobjc-arc'
|
||||||
|
args '-fPIC', '-shared', '-o', "$buildDir/libobjcsmoke.dylib"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
language = Objective-C
|
||||||
|
headerFilter = **/smoke.h
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#import <objc/NSObject.h>
|
||||||
|
|
||||||
|
@protocol Printer
|
||||||
|
@required
|
||||||
|
-(void)print:(const char*)string;
|
||||||
|
@end;
|
||||||
|
|
||||||
|
@interface Foo : NSObject
|
||||||
|
@property NSString* name;
|
||||||
|
-(void)hello;
|
||||||
|
-(void)helloWithPrinter:(id <Printer>)printer;
|
||||||
|
@end;
|
||||||
|
|
||||||
|
@protocol MutablePair
|
||||||
|
@required
|
||||||
|
@property (readonly) int first;
|
||||||
|
@property (readonly) int second;
|
||||||
|
|
||||||
|
-(void)update:(int)index add:(int)delta;
|
||||||
|
-(void)update:(int)index sub:(int)delta;
|
||||||
|
|
||||||
|
@end;
|
||||||
|
|
||||||
|
void replacePairElements(id <MutablePair> pair, int first, int second);
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import kotlinx.cinterop.*
|
||||||
|
import objcSmoke.*
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
autoreleasepool {
|
||||||
|
run()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun run() {
|
||||||
|
val foo = Foo()
|
||||||
|
foo.hello()
|
||||||
|
foo.name = "everybody"
|
||||||
|
foo.helloWithPrinter(object : NSObject(), PrinterProtocol {
|
||||||
|
override fun print(string: CPointer<ByteVar>?) {
|
||||||
|
println("Kotlin says: " + string?.toKString())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
Bar().hello()
|
||||||
|
|
||||||
|
val pair = MutablePairImpl(42, 17)
|
||||||
|
replacePairElements(pair, 1, 2)
|
||||||
|
pair.swap()
|
||||||
|
println("${pair.first}, ${pair.second}")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun MutablePairProtocol.swap() {
|
||||||
|
update(0, add = second)
|
||||||
|
update(1, sub = first)
|
||||||
|
update(0, add = second)
|
||||||
|
update(1, sub = second*2)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Bar : Foo() {
|
||||||
|
override fun helloWithPrinter(printer: PrinterProtocol) = memScoped {
|
||||||
|
printer.print("Hello from Kotlin".cstr.getPointer(memScope))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("CONFLICTING_OVERLOADS")
|
||||||
|
class MutablePairImpl(first: Int, second: Int) : NSObject(), MutablePairProtocol {
|
||||||
|
private var elements = intArrayOf(first, second)
|
||||||
|
|
||||||
|
override fun first() = elements.first()
|
||||||
|
override fun second() = elements.last()
|
||||||
|
|
||||||
|
override fun update(index: Int, add: Int) {
|
||||||
|
elements[index] += add
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun update(index: Int, sub: Int) {
|
||||||
|
elements[index] -= sub
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
#import <stdio.h>
|
||||||
|
#import <Foundation/NSString.h>
|
||||||
|
#import "smoke.h"
|
||||||
|
|
||||||
|
@interface CPrinter : NSObject <Printer>
|
||||||
|
-(void)print:(const char*)string;
|
||||||
|
@end;
|
||||||
|
|
||||||
|
@implementation CPrinter
|
||||||
|
-(void)print:(const char*)string {
|
||||||
|
printf("%s\n", string);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
@end;
|
||||||
|
|
||||||
|
@implementation Foo
|
||||||
|
|
||||||
|
@synthesize name;
|
||||||
|
|
||||||
|
-(instancetype)init {
|
||||||
|
if (self = [super init]) {
|
||||||
|
self.name = @"World";
|
||||||
|
}
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)dealloc {
|
||||||
|
printf("Deallocated\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
@end;
|
||||||
|
|
||||||
|
void replacePairElements(id <MutablePair> pair, int first, int second) {
|
||||||
|
[pair update:0 add:(first - pair.first)];
|
||||||
|
[pair update:1 sub:(pair.second - second)];
|
||||||
|
}
|
||||||
@@ -124,7 +124,10 @@ class CompileCppToBitcode extends DefaultTask {
|
|||||||
args "-I$headersDir"
|
args "-I$headersDir"
|
||||||
|
|
||||||
args '-c', '-emit-llvm'
|
args '-c', '-emit-llvm'
|
||||||
args project.fileTree(srcDir).include('**/*.cpp')
|
args project.fileTree(srcDir) {
|
||||||
|
include('**/*.cpp')
|
||||||
|
include('**/*.mm') // Objective-C++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
project.exec {
|
project.exec {
|
||||||
|
|||||||
@@ -350,6 +350,31 @@ ContainerHeader* AllocContainer(size_t size) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
void objc_release(void* ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void runDeallocationHooks(ObjHeader* obj) {
|
||||||
|
#if KONAN_OBJC_INTEROP
|
||||||
|
if (obj->type_info() == theObjCPointerHolderTypeInfo) {
|
||||||
|
void* objcPtr = *reinterpret_cast<void**>(obj + 1); // TODO: use more reliable layout description
|
||||||
|
objc_release(objcPtr);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void DeinitInstanceBodyImpl(const TypeInfo* typeInfo, void* body) {
|
||||||
|
for (int index = 0; index < typeInfo->objOffsetsCount_; index++) {
|
||||||
|
ObjHeader** location = reinterpret_cast<ObjHeader**>(
|
||||||
|
reinterpret_cast<uintptr_t>(body) + typeInfo->objOffsets_[index]);
|
||||||
|
UpdateRef(location, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeinitInstanceBody(const TypeInfo* typeInfo, void* body) {
|
||||||
|
DeinitInstanceBodyImpl(typeInfo, body);
|
||||||
|
}
|
||||||
|
|
||||||
void FreeContainer(ContainerHeader* header) {
|
void FreeContainer(ContainerHeader* header) {
|
||||||
RuntimeAssert(!isPermanent(header), "this kind of container shalln't be freed");
|
RuntimeAssert(!isPermanent(header), "this kind of container shalln't be freed");
|
||||||
#if TRACE_MEMORY
|
#if TRACE_MEMORY
|
||||||
@@ -366,13 +391,12 @@ void FreeContainer(ContainerHeader* header) {
|
|||||||
ObjHeader* obj = reinterpret_cast<ObjHeader*>(header + 1);
|
ObjHeader* obj = reinterpret_cast<ObjHeader*>(header + 1);
|
||||||
|
|
||||||
for (int index = 0; index < header->objectCount_; index++) {
|
for (int index = 0; index < header->objectCount_; index++) {
|
||||||
|
runDeallocationHooks(obj);
|
||||||
|
|
||||||
const TypeInfo* typeInfo = obj->type_info();
|
const TypeInfo* typeInfo = obj->type_info();
|
||||||
|
|
||||||
for (int index = 0; index < typeInfo->objOffsetsCount_; index++) {
|
DeinitInstanceBodyImpl(typeInfo, reinterpret_cast<void*>(obj + 1));
|
||||||
ObjHeader** location = reinterpret_cast<ObjHeader**>(
|
|
||||||
reinterpret_cast<uintptr_t>(obj + 1) + typeInfo->objOffsets_[index]);
|
|
||||||
UpdateRef(location, nullptr);
|
|
||||||
}
|
|
||||||
// Object arrays are *special*.
|
// Object arrays are *special*.
|
||||||
if (typeInfo == theArrayTypeInfo) {
|
if (typeInfo == theArrayTypeInfo) {
|
||||||
ArrayHeader* array = obj->array();
|
ArrayHeader* array = obj->array();
|
||||||
@@ -399,6 +423,15 @@ void FreeContainerNoRef(ContainerHeader* header) {
|
|||||||
#if USE_GC
|
#if USE_GC
|
||||||
removeFreeable(memoryState, header);
|
removeFreeable(memoryState, header);
|
||||||
#endif
|
#endif
|
||||||
|
ObjHeader* obj = reinterpret_cast<ObjHeader*>(header + 1);
|
||||||
|
|
||||||
|
for (int index = 0; index < header->objectCount_; index++) {
|
||||||
|
runDeallocationHooks(obj);
|
||||||
|
|
||||||
|
obj = reinterpret_cast<ObjHeader*>(
|
||||||
|
reinterpret_cast<uintptr_t>(obj) + objectSize(obj));
|
||||||
|
}
|
||||||
|
|
||||||
memoryState->allocCount--;
|
memoryState->allocCount--;
|
||||||
konanFreeMemory(header);
|
konanFreeMemory(header);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -322,6 +322,7 @@ void DeinitMemory(MemoryState*);
|
|||||||
//
|
//
|
||||||
OBJ_GETTER(AllocInstance, const TypeInfo* type_info) RUNTIME_NOTHROW;
|
OBJ_GETTER(AllocInstance, const TypeInfo* type_info) RUNTIME_NOTHROW;
|
||||||
OBJ_GETTER(AllocArrayInstance, const TypeInfo* type_info, uint32_t elements) RUNTIME_NOTHROW;
|
OBJ_GETTER(AllocArrayInstance, const TypeInfo* type_info, uint32_t elements) RUNTIME_NOTHROW;
|
||||||
|
void DeinitInstanceBody(const TypeInfo* typeInfo, void* body);
|
||||||
OBJ_GETTER(InitInstance, ObjHeader** location, const TypeInfo* type_info,
|
OBJ_GETTER(InitInstance, ObjHeader** location, const TypeInfo* type_info,
|
||||||
void (*ctor)(ObjHeader*));
|
void (*ctor)(ObjHeader*));
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,241 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if KONAN_OBJC_INTEROP
|
||||||
|
|
||||||
|
#include <objc/objc.h>
|
||||||
|
#include <objc/runtime.h>
|
||||||
|
#include <objc/message.h>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdint>
|
||||||
|
#include "Memory.h"
|
||||||
|
#include "Natives.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
struct KotlinClassData {
|
||||||
|
const TypeInfo* typeInfo;
|
||||||
|
int32_t bodyOffset;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline struct KotlinClassData* GetKotlinClassData(Class clazz) {
|
||||||
|
void* ivars = object_getIndexedIvars(reinterpret_cast<id>(clazz));
|
||||||
|
return static_cast<struct KotlinClassData*>(ivars);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void SetKotlinTypeInfo(Class clazz, const TypeInfo* typeInfo) {
|
||||||
|
GetKotlinClassData(clazz)->typeInfo = typeInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TypeInfo* GetObjCKotlinTypeInfo(const ObjHeader* obj) {
|
||||||
|
void* objcPtr = *reinterpret_cast<void * const *>(obj + 1); // TODO: use more reliable layout description
|
||||||
|
Class clazz = object_getClass(reinterpret_cast<id>(objcPtr));
|
||||||
|
return GetKotlinClassData(clazz)->typeInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
id objc_msgSendSuper2(struct objc_super *super, SEL op, ...);
|
||||||
|
|
||||||
|
static void DeallocImp(id self, SEL _cmd) {
|
||||||
|
// TODO: doesn't support overriding Kotlin classes.
|
||||||
|
Class clazz = object_getClass(self);
|
||||||
|
RuntimeAssert(clazz != nullptr, "Must not be null");
|
||||||
|
|
||||||
|
struct KotlinClassData* classData = GetKotlinClassData(clazz);
|
||||||
|
void* body = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(self) + classData->bodyOffset);
|
||||||
|
|
||||||
|
const TypeInfo* typeInfo = classData->typeInfo;
|
||||||
|
DeinitInstanceBody(typeInfo, body);
|
||||||
|
|
||||||
|
// Call super.dealloc:
|
||||||
|
struct objc_super s = {self, clazz};
|
||||||
|
auto messenger = reinterpret_cast<void (*) (struct objc_super*, SEL _cmd)>(objc_msgSendSuper2);
|
||||||
|
messenger(&s, _cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void AddDeallocMethod(Class clazz) {
|
||||||
|
Class nsObjectClass = objc_getClass("NSObject");
|
||||||
|
RuntimeAssert(nsObjectClass != nullptr, "NSObject class not found");
|
||||||
|
|
||||||
|
SEL deallocSelector = sel_registerName("dealloc");
|
||||||
|
Method nsObjectDeallocMethod = class_getInstanceMethod(nsObjectClass, deallocSelector);
|
||||||
|
RuntimeAssert(nsObjectDeallocMethod != nullptr, "[NSObject dealloc] method not found");
|
||||||
|
|
||||||
|
const char* nsObjectDeallocMethodTypeEncoding = method_getTypeEncoding(nsObjectDeallocMethod);
|
||||||
|
RuntimeAssert(nsObjectDeallocMethodTypeEncoding != nullptr, "[NSObject dealloc] method has no encoding provided");
|
||||||
|
|
||||||
|
// TODO: something of the above can be cached.
|
||||||
|
|
||||||
|
BOOL added = class_addMethod(clazz, deallocSelector, (IMP)DeallocImp, nsObjectDeallocMethodTypeEncoding);
|
||||||
|
RuntimeAssert(added, "Unable to add dealloc method to Objective-C class");
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ObjCMethodDescription {
|
||||||
|
const char* selector;
|
||||||
|
const char* encoding;
|
||||||
|
const void* imp;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct KotlinObjCClassInfo {
|
||||||
|
const char* name;
|
||||||
|
|
||||||
|
const char* superclassName;
|
||||||
|
const char** protocolNames;
|
||||||
|
|
||||||
|
const struct ObjCMethodDescription* instanceMethods;
|
||||||
|
int32_t instanceMethodsNum;
|
||||||
|
|
||||||
|
const struct ObjCMethodDescription* classMethods;
|
||||||
|
int32_t classMethodsNum;
|
||||||
|
|
||||||
|
int32_t bodySize;
|
||||||
|
int32_t* bodyOffset;
|
||||||
|
|
||||||
|
const TypeInfo* typeInfo;
|
||||||
|
const TypeInfo* metaTypeInfo;
|
||||||
|
|
||||||
|
void** createdClass;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void AddMethods(Class clazz, const struct ObjCMethodDescription* methods, int32_t methodsNum) {
|
||||||
|
for (int32_t i = 0; i < methodsNum; ++i) {
|
||||||
|
const struct ObjCMethodDescription* method = &methods[i];
|
||||||
|
BOOL added = class_addMethod(clazz, sel_registerName(method->selector), (IMP)method->imp, method->encoding);
|
||||||
|
RuntimeAssert(added == YES, "Unable to add method to Objective-C class");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static SimpleMutex classCreationMutex;
|
||||||
|
static int anonymousClassNextId = 0;
|
||||||
|
|
||||||
|
void* CreateKotlinObjCClass(const KotlinObjCClassInfo* info) {
|
||||||
|
LockGuard<SimpleMutex> lockGuard(classCreationMutex);
|
||||||
|
|
||||||
|
void* createdClass = *info->createdClass;
|
||||||
|
if (createdClass != nullptr) {
|
||||||
|
return createdClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
char classNameBuffer[64];
|
||||||
|
const char* className = info->name;
|
||||||
|
if (className == nullptr) {
|
||||||
|
snprintf(classNameBuffer, sizeof(classNameBuffer), "kobjc%d", anonymousClassNextId++);
|
||||||
|
className = classNameBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
Class superclass = objc_getClass(info->superclassName);
|
||||||
|
Class newClass = objc_allocateClassPair(superclass, className, sizeof(struct KotlinClassData));
|
||||||
|
RuntimeAssert(newClass != nullptr, "Failed to allocate Objective-C class");
|
||||||
|
|
||||||
|
Class newMetaclass = object_getClass(reinterpret_cast<id>(newClass));
|
||||||
|
|
||||||
|
for (size_t i = 0;; ++i) {
|
||||||
|
const char* protocolName = info->protocolNames[i];
|
||||||
|
if (protocolName == nullptr) break;
|
||||||
|
Protocol* proto = objc_getProtocol(protocolName);
|
||||||
|
if (proto != nullptr) {
|
||||||
|
BOOL added = class_addProtocol(newClass, proto);
|
||||||
|
RuntimeAssert(added == YES, "Unable to add protocol to Objective-C class");
|
||||||
|
added = class_addProtocol(newMetaclass, proto);
|
||||||
|
RuntimeAssert(added == YES, "Unable to add protocol to Objective-C metaclass");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AddDeallocMethod(newClass);
|
||||||
|
|
||||||
|
AddMethods(newClass, info->instanceMethods, info->instanceMethodsNum);
|
||||||
|
AddMethods(newMetaclass, info->classMethods, info->classMethodsNum);
|
||||||
|
|
||||||
|
SetKotlinTypeInfo(newClass, info->typeInfo);
|
||||||
|
SetKotlinTypeInfo(newMetaclass, info->metaTypeInfo);
|
||||||
|
|
||||||
|
char bodyTypeEncoding[16];
|
||||||
|
snprintf(bodyTypeEncoding, sizeof(bodyTypeEncoding), "[%dc]", info->bodySize);
|
||||||
|
BOOL added = class_addIvar(newClass, "kotlinBody", info->bodySize, /* log2(align) = */ 3, bodyTypeEncoding);
|
||||||
|
RuntimeAssert(added == YES, "Unable to add ivar to Objective-C class");
|
||||||
|
|
||||||
|
objc_registerClassPair(newClass);
|
||||||
|
|
||||||
|
Ivar body = class_getInstanceVariable(newClass, "kotlinBody");
|
||||||
|
RuntimeAssert(body != nullptr, "Unable to get ivar added to Objective-C class");
|
||||||
|
int32_t offset = (int32_t)ivar_getOffset(body);
|
||||||
|
GetKotlinClassData(newClass)->bodyOffset = offset;
|
||||||
|
*info->bodyOffset = offset;
|
||||||
|
|
||||||
|
*info->createdClass = newClass;
|
||||||
|
return newClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* objc_autoreleasePoolPush();
|
||||||
|
void objc_autoreleasePoolPop(void* ptr);
|
||||||
|
id objc_allocWithZone(Class clazz);
|
||||||
|
id objc_retain(id ptr);
|
||||||
|
void objc_release(id ptr);
|
||||||
|
|
||||||
|
void* Kotlin_objc_autoreleasePoolPush() {
|
||||||
|
return objc_autoreleasePoolPush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Kotlin_objc_autoreleasePoolPop(void* ptr) {
|
||||||
|
objc_autoreleasePoolPop(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
id Kotlin_objc_allocWithZone(Class clazz) {
|
||||||
|
return objc_allocWithZone(clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
id Kotlin_objc_retain(id ptr) {
|
||||||
|
return objc_retain(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Kotlin_objc_release(id ptr) {
|
||||||
|
objc_release(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // extern "C"
|
||||||
|
|
||||||
|
#else // KONAN_OBJC_INTEROP
|
||||||
|
|
||||||
|
#include "Assert.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
void* Kotlin_objc_autoreleasePoolPush() {
|
||||||
|
RuntimeAssert(false, "Objective-C interop is disabled");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Kotlin_objc_autoreleasePoolPop(void* ptr) {
|
||||||
|
RuntimeAssert(false, "Objective-C interop is disabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
void* Kotlin_objc_allocWithZone(void* clazz) {
|
||||||
|
RuntimeAssert(false, "Objective-C interop is disabled");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* Kotlin_objc_retain(void* ptr) {
|
||||||
|
RuntimeAssert(false, "Objective-C interop is disabled");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Kotlin_objc_release(void* ptr) {
|
||||||
|
RuntimeAssert(false, "Objective-C interop is disabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // extern "C"
|
||||||
|
|
||||||
|
#endif // KONAN_OBJC_INTEROP
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Natives.h"
|
||||||
|
|
||||||
|
#if KONAN_OBJC_INTEROP
|
||||||
|
|
||||||
|
#import <objc/runtime.h>
|
||||||
|
#import <Foundation/NSString.h>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
Class nsStringClass = nullptr;
|
||||||
|
|
||||||
|
Class getNSStringClass() {
|
||||||
|
Class result = nsStringClass;
|
||||||
|
if (result == nullptr) {
|
||||||
|
// Lookup dynamically to avoid direct reference to Foundation:
|
||||||
|
result = objc_getClass("NSString");
|
||||||
|
RuntimeAssert(result != nullptr, "NSString class not found");
|
||||||
|
nsStringClass = result;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
NSString* CreateNSStringFromKString(const ArrayHeader* str) {
|
||||||
|
if (str == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const KChar* utf16Chars = CharArrayAddressOfElementAt(str, 0);
|
||||||
|
|
||||||
|
NSString* result = [[[getNSStringClass() alloc] initWithBytes:utf16Chars
|
||||||
|
length:str->count_*sizeof(KChar)
|
||||||
|
encoding:NSUTF16LittleEndianStringEncoding] autorelease];
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
OBJ_GETTER(CreateKStringFromNSString, NSString* str) {
|
||||||
|
if (str == nullptr) {
|
||||||
|
RETURN_OBJ(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t length = [str length];
|
||||||
|
NSRange range = {0, length};
|
||||||
|
ArrayHeader* result = AllocArrayInstance(theStringTypeInfo, length, OBJ_RESULT)->array();
|
||||||
|
KChar* rawResult = CharArrayAddressOfElementAt(result, 0);
|
||||||
|
|
||||||
|
[str getCharacters:rawResult range:range];
|
||||||
|
|
||||||
|
RETURN_OBJ(result->obj());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // extern "C"
|
||||||
|
|
||||||
|
#else // KONAN_OBJC_INTEROP
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
void* CreateNSStringFromKString(const ArrayHeader* str) {
|
||||||
|
RuntimeAssert(false, "Objective-C interop is disabled");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
OBJ_GETTER(CreateKStringFromNSString, void* str) {
|
||||||
|
RuntimeAssert(false, "Objective-C interop is disabled");
|
||||||
|
RETURN_OBJ(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // extern "C"
|
||||||
|
|
||||||
|
#endif // KONAN_OBJC_INTEROP
|
||||||
@@ -83,6 +83,7 @@ extern const TypeInfo* theDoubleArrayTypeInfo;
|
|||||||
extern const TypeInfo* theBooleanArrayTypeInfo;
|
extern const TypeInfo* theBooleanArrayTypeInfo;
|
||||||
extern const TypeInfo* theStringTypeInfo;
|
extern const TypeInfo* theStringTypeInfo;
|
||||||
extern const TypeInfo* theThrowableTypeInfo;
|
extern const TypeInfo* theThrowableTypeInfo;
|
||||||
|
extern const TypeInfo* theObjCPointerHolderTypeInfo;
|
||||||
|
|
||||||
KBoolean IsInstance(const ObjHeader* obj, const TypeInfo* type_info) RUNTIME_PURE;
|
KBoolean IsInstance(const ObjHeader* obj, const TypeInfo* type_info) RUNTIME_PURE;
|
||||||
void CheckCast(const ObjHeader* obj, const TypeInfo* type_info);
|
void CheckCast(const ObjHeader* obj, const TypeInfo* type_info);
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include "Assert.h"
|
||||||
|
|
||||||
|
class SimpleMutex {
|
||||||
|
private:
|
||||||
|
int32_t atomicInt = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void lock() {
|
||||||
|
while (!__sync_bool_compare_and_swap(&atomicInt, 0, 1)) {
|
||||||
|
// TODO: yield.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void unlock() {
|
||||||
|
if (!__sync_bool_compare_and_swap(&atomicInt, 1, 0)) {
|
||||||
|
RuntimeAssert(false, "Unable to unlock");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: use std::lock_guard instead?
|
||||||
|
template <class Mutex>
|
||||||
|
class LockGuard {
|
||||||
|
public:
|
||||||
|
explicit LockGuard(Mutex& mutex_) : mutex(mutex_) {
|
||||||
|
mutex.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
~LockGuard() {
|
||||||
|
mutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Mutex& mutex;
|
||||||
|
|
||||||
|
LockGuard(const LockGuard&) = delete;
|
||||||
|
LockGuard& operator=(const LockGuard&) = delete;
|
||||||
|
};
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
apply plugin: 'konan'
|
||||||
|
|
||||||
|
konanInterop {
|
||||||
|
objc {
|
||||||
|
target "macbook"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
konanArtifacts {
|
||||||
|
Window {
|
||||||
|
useInterop 'objc'
|
||||||
|
target "macbook"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
headers = Foundation/Foundation.h Cocoa/Cocoa.h AppKit/NSApplication.h AppKit/NSWindow.h
|
||||||
|
headerFilter = Foundation/** Cocoa/** AppKit/**
|
||||||
|
language = Objective-C
|
||||||
|
linkerOpts = -framework AppKit
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import objc.*
|
||||||
|
import kotlinx.cinterop.*
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
autoreleasepool {
|
||||||
|
runApp()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun runApp() {
|
||||||
|
val app = NSApplication.sharedApplication()
|
||||||
|
|
||||||
|
app.delegate = MyAppDelegate()
|
||||||
|
app.setActivationPolicy(NSApplicationActivationPolicy.NSApplicationActivationPolicyRegular)
|
||||||
|
app.activateIgnoringOtherApps(true)
|
||||||
|
|
||||||
|
app.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MyAppDelegate() : NSObject(), NSApplicationDelegateProtocol {
|
||||||
|
|
||||||
|
private val window: NSWindow
|
||||||
|
|
||||||
|
init {
|
||||||
|
val mainDisplayRect = NSScreen.mainScreen()!!.frame
|
||||||
|
val windowRect = mainDisplayRect.useContents {
|
||||||
|
NSMakeRect(
|
||||||
|
origin.x + size.width * 0.25,
|
||||||
|
origin.y + size.height * 0.25,
|
||||||
|
size.width * 0.5,
|
||||||
|
size.height * 0.5
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val windowStyle = NSWindowStyleMaskTitled or NSWindowStyleMaskMiniaturizable or
|
||||||
|
NSWindowStyleMaskClosable or NSWindowStyleMaskResizable
|
||||||
|
|
||||||
|
window = NSWindow(windowRect, windowStyle, NSBackingStoreBuffered, false).apply {
|
||||||
|
title = "Окошко Konan"
|
||||||
|
opaque = true
|
||||||
|
hasShadow = true
|
||||||
|
preferredBackingLocation = NSWindowBackingLocationVideoMemory
|
||||||
|
hidesOnDeactivate = false
|
||||||
|
backgroundColor = NSColor.whiteColor()
|
||||||
|
releasedWhenClosed = false
|
||||||
|
|
||||||
|
delegate = object : NSObject(), NSWindowDelegateProtocol {
|
||||||
|
override fun windowShouldClose(sender: ObjCObject): Boolean {
|
||||||
|
NSApplication.sharedApplication().stop(this)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun applicationWillFinishLaunching(notification: NSNotification) {
|
||||||
|
window.makeKeyAndOrderFront(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,4 +12,5 @@ include ':concurrent'
|
|||||||
// So temporary switching off for now, as it breaks the build
|
// So temporary switching off for now, as it breaks the build
|
||||||
// of other samples if SDK is not present.
|
// of other samples if SDK is not present.
|
||||||
//include ':androidNativeActivity'
|
//include ':androidNativeActivity'
|
||||||
|
include ':objc'
|
||||||
includeBuild '../'
|
includeBuild '../'
|
||||||
|
|||||||
@@ -43,13 +43,16 @@ class ClangTarget(val target: KonanTarget, konanProperties: KonanProperties) {
|
|||||||
"-DUSE_GCC_UNWIND=1", "-DUSE_PE_COFF_SYMBOLS=1", "-DKONAN_WINDOWS=1")
|
"-DUSE_GCC_UNWIND=1", "-DUSE_PE_COFF_SYMBOLS=1", "-DKONAN_WINDOWS=1")
|
||||||
|
|
||||||
KonanTarget.MACBOOK ->
|
KonanTarget.MACBOOK ->
|
||||||
listOf("--sysroot=$sysRoot", "-mmacosx-version-min=10.11", "-DKONAN_OSX=1")
|
listOf("--sysroot=$sysRoot", "-mmacosx-version-min=10.11", "-DKONAN_OSX=1",
|
||||||
|
"-DKONAN_OBJC_INTEROP=1")
|
||||||
|
|
||||||
KonanTarget.IPHONE ->
|
KonanTarget.IPHONE ->
|
||||||
listOf("-stdlib=libc++", "-arch", "arm64", "-isysroot", "$sysRoot", "-miphoneos-version-min=8.0.0")
|
listOf("-stdlib=libc++", "-arch", "arm64", "-isysroot", "$sysRoot", "-miphoneos-version-min=8.0.0",
|
||||||
|
"-DKONAN_OBJC_INTEROP=1")
|
||||||
|
|
||||||
KonanTarget.IPHONE_SIM ->
|
KonanTarget.IPHONE_SIM ->
|
||||||
listOf("-stdlib=libc++", "-isysroot", "$sysRoot", "-miphoneos-version-min=8.0.0")
|
listOf("-stdlib=libc++", "-isysroot", "$sysRoot", "-miphoneos-version-min=8.0.0",
|
||||||
|
"-DKONAN_OBJC_INTEROP=1")
|
||||||
|
|
||||||
KonanTarget.ANDROID_ARM32 ->
|
KonanTarget.ANDROID_ARM32 ->
|
||||||
listOf("-target", targetArg!!,
|
listOf("-target", targetArg!!,
|
||||||
|
|||||||
Reference in New Issue
Block a user