Remove old plaintext StubGenerator and introduce Stub IR-based generator
This commit is contained in:
committed by
Sergey Bogolepov
parent
367b95a0b8
commit
bff177a072
-9
@@ -100,13 +100,4 @@ inline fun buildKotlinCodeLines(scope: KotlinScope, block: KotlinCodeBuilder.()
|
||||
val builder = KotlinCodeBuilder(scope)
|
||||
builder.block()
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
interface StubGenerationContext {
|
||||
val nativeBridges: NativeBridges
|
||||
fun addTopLevelDeclaration(lines: List<String>)
|
||||
}
|
||||
|
||||
interface KotlinStub {
|
||||
fun generate(context: StubGenerationContext): Sequence<String>
|
||||
}
|
||||
-17
@@ -71,22 +71,5 @@ private val charactersAllowedInKotlinStringLiterals: Set<Char> = mutableSetOf<Ch
|
||||
addAll(listOf('_', '@', ':', ';', '.', ',', '{', '}', '=', '[', ']', '^', '#', '*', ' ', '(', ')'))
|
||||
}
|
||||
|
||||
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() }})"
|
||||
|
||||
fun List<KotlinParameter>.renderParameters(scope: KotlinScope) = buildString {
|
||||
this@renderParameters.renderParametersTo(scope, this)
|
||||
}
|
||||
|
||||
fun List<KotlinParameter>.renderParametersTo(scope: KotlinScope, buffer: Appendable) =
|
||||
this.joinTo(buffer, ", ") { it.render(scope) }
|
||||
|
||||
-128
@@ -1,128 +0,0 @@
|
||||
/*
|
||||
* 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.ArrayType
|
||||
import org.jetbrains.kotlin.native.interop.indexer.GlobalDecl
|
||||
import org.jetbrains.kotlin.native.interop.indexer.unwrapTypedefs
|
||||
|
||||
class GlobalVariableStub(global: GlobalDecl, stubGenerator: StubGenerator) : KotlinStub, NativeBacked {
|
||||
|
||||
private val getAddressExpression: KotlinExpression by lazy {
|
||||
stubGenerator.simpleBridgeGenerator.kotlinToNative(
|
||||
nativeBacked = this,
|
||||
returnType = BridgedType.NATIVE_PTR,
|
||||
kotlinValues = emptyList(),
|
||||
independent = false
|
||||
) {
|
||||
"&${global.name}"
|
||||
}
|
||||
}
|
||||
|
||||
private val setterStub = object : NativeBacked {}
|
||||
|
||||
val header: String
|
||||
val getter: KotlinExpression
|
||||
val setter: KotlinExpression?
|
||||
|
||||
init {
|
||||
val kotlinScope = stubGenerator.kotlinFile
|
||||
|
||||
// TODO: consider sharing the logic below with field generator.
|
||||
|
||||
val kotlinType: KotlinType
|
||||
|
||||
val mirror = mirror(stubGenerator.declarationMapper, global.type)
|
||||
val unwrappedType = global.type.unwrapTypedefs()
|
||||
|
||||
if (unwrappedType is ArrayType) {
|
||||
kotlinType = (mirror as TypeMirror.ByValue).valueType
|
||||
getter = mirror.info.argFromBridged(getAddressExpression, kotlinScope, nativeBacked = this) + "!!"
|
||||
setter = null
|
||||
} else {
|
||||
if (mirror is TypeMirror.ByValue) {
|
||||
getter = mirror.info.argFromBridged(stubGenerator.simpleBridgeGenerator.kotlinToNative(
|
||||
nativeBacked = this,
|
||||
returnType = mirror.info.bridgedType,
|
||||
kotlinValues = emptyList(),
|
||||
independent = false
|
||||
) {
|
||||
mirror.info.cToBridged(expr = global.name)
|
||||
}, kotlinScope, nativeBacked = this)
|
||||
|
||||
setter = if (global.isConst) {
|
||||
null
|
||||
} else {
|
||||
val bridgedValue = BridgeTypedKotlinValue(mirror.info.bridgedType, mirror.info.argToBridged("value"))
|
||||
|
||||
stubGenerator.simpleBridgeGenerator.kotlinToNative(
|
||||
nativeBacked = setterStub,
|
||||
returnType = BridgedType.VOID,
|
||||
kotlinValues = listOf(bridgedValue),
|
||||
independent = false
|
||||
) { nativeValues ->
|
||||
out("${global.name} = ${mirror.info.cFromBridged(
|
||||
nativeValues.single(),
|
||||
scope,
|
||||
nativeBacked = setterStub
|
||||
)};")
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
kotlinType = mirror.argType
|
||||
} else {
|
||||
val pointedTypeName = mirror.pointedType.render(kotlinScope)
|
||||
val storagePointed = "interpretPointed<$pointedTypeName>($getAddressExpression)"
|
||||
kotlinType = mirror.pointedType
|
||||
getter = storagePointed
|
||||
setter = null
|
||||
}
|
||||
}
|
||||
|
||||
header = buildString {
|
||||
append(getDeclarationName(kotlinScope, global.name))
|
||||
append(": ")
|
||||
append(kotlinType.render(kotlinScope))
|
||||
}
|
||||
}
|
||||
|
||||
// Try to use the provided name. If failed, mangle it with underscore and try again:
|
||||
private tailrec fun getDeclarationName(scope: KotlinScope, name: String): String =
|
||||
scope.declareProperty(name) ?: getDeclarationName(scope, name + "_")
|
||||
|
||||
override fun generate(context: StubGenerationContext): Sequence<String> {
|
||||
val lines = mutableListOf<String>()
|
||||
if (context.nativeBridges.isSupported(this)) {
|
||||
val mutable = setter != null && context.nativeBridges.isSupported(setterStub)
|
||||
val kind = if (mutable) "var" else "val"
|
||||
lines.add("$kind $header")
|
||||
lines.add(" get() = $getter")
|
||||
if (mutable) {
|
||||
lines.add(" set(value) { $setter }")
|
||||
}
|
||||
} else {
|
||||
lines.add(annotationForUnableToImport)
|
||||
lines.add("val $header")
|
||||
lines.add(" get() = TODO()")
|
||||
}
|
||||
|
||||
return lines.asSequence()
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -284,7 +284,7 @@ abstract class KotlinFile(
|
||||
|
||||
override fun declare(classifier: Classifier): String {
|
||||
if (classifier.pkg != this.pkg) {
|
||||
throw IllegalArgumentException("wrong package; expected '$pkg', got '${classifier.pkg}'")
|
||||
throw IllegalArgumentException("wrong package for classifier ${classifier.fqName}; expected '$pkg', got '${classifier.pkg}'")
|
||||
}
|
||||
|
||||
if (!classifier.isTopLevel) {
|
||||
|
||||
+293
-342
@@ -17,10 +17,9 @@
|
||||
package org.jetbrains.kotlin.native.interop.gen
|
||||
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import org.jetbrains.kotlin.native.interop.gen.jvm.StubGenerator
|
||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||
|
||||
private fun ObjCMethod.getKotlinParameterNames(forConstructorOrFactory: Boolean = false): List<String> {
|
||||
internal fun ObjCMethod.getKotlinParameterNames(forConstructorOrFactory: Boolean = false): List<String> {
|
||||
val selectorParts = this.selector.split(":")
|
||||
|
||||
val result = mutableListOf<String>()
|
||||
@@ -66,194 +65,168 @@ private fun ObjCMethod.getFirstKotlinParameterNameCandidate(forConstructorOrFact
|
||||
}
|
||||
|
||||
private fun ObjCMethod.getKotlinParameters(
|
||||
stubGenerator: StubGenerator,
|
||||
stubIrBuilder: StubsBuildingContext,
|
||||
forConstructorOrFactory: Boolean
|
||||
): List<KotlinParameter> {
|
||||
): List<FunctionParameterStub> {
|
||||
val names = getKotlinParameterNames(forConstructorOrFactory) // TODO: consider refactoring.
|
||||
val result = mutableListOf<KotlinParameter>()
|
||||
val result = mutableListOf<FunctionParameterStub>()
|
||||
|
||||
this.parameters.mapIndexedTo(result) { index, it ->
|
||||
val kotlinType = stubGenerator.mirror(it.type).argType
|
||||
val kotlinType = stubIrBuilder.mirror(it.type).argType
|
||||
val name = names[index]
|
||||
val annotations = if (it.nsConsumed) listOf("@CCall.Consumed") else emptyList()
|
||||
KotlinParameter(name, kotlinType, isVararg = false, annotations = annotations)
|
||||
val annotations = if (it.nsConsumed) listOf(AnnotationStub.ObjC.Consumed) else emptyList()
|
||||
FunctionParameterStub(name, WrapperStubType(kotlinType), isVararg = false, annotations = annotations)
|
||||
}
|
||||
|
||||
if (this.isVariadic) {
|
||||
result += KotlinParameter(
|
||||
result += FunctionParameterStub(
|
||||
names.last(),
|
||||
KotlinTypes.any.makeNullable(),
|
||||
WrapperStubType(KotlinTypes.any.makeNullable()),
|
||||
isVararg = true,
|
||||
annotations = emptyList()
|
||||
)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
class ObjCMethodStub(private val stubGenerator: StubGenerator,
|
||||
val method: ObjCMethod,
|
||||
private val container: ObjCContainer,
|
||||
private val isDesignatedInitializer: Boolean) : KotlinStub, NativeBacked {
|
||||
|
||||
override fun generate(context: StubGenerationContext): Sequence<String> =
|
||||
if (context.nativeBridges.isSupported(this)) {
|
||||
val result = mutableListOf<String>()
|
||||
result.addAll(objCMethodAnnotations)
|
||||
result.add(header)
|
||||
|
||||
if (method.isInit) {
|
||||
// TODO: generate only constructor/factory in this case.
|
||||
val kotlinScope = stubGenerator.kotlinFile
|
||||
|
||||
val parameters = method.getKotlinParameters(stubGenerator, forConstructorOrFactory = true)
|
||||
.renderParameters(kotlinScope)
|
||||
|
||||
when (container) {
|
||||
is ObjCClass -> {
|
||||
result.add(0,
|
||||
deprecatedInit(
|
||||
container.kotlinClassName(method.isClass),
|
||||
kotlinMethodParameters.map { it.name },
|
||||
factory = false
|
||||
)
|
||||
)
|
||||
|
||||
// TODO: consider generating non-designated initializers as factories.
|
||||
val designated = isDesignatedInitializer ||
|
||||
stubGenerator.configuration.disableDesignatedInitializerChecks
|
||||
|
||||
result.add("")
|
||||
result.add("@ObjCConstructor(${method.selector.quoteAsKotlinLiteral()}, $designated)")
|
||||
result.add("constructor($parameters) {}")
|
||||
}
|
||||
is ObjCCategory -> {
|
||||
assert(!method.isClass)
|
||||
|
||||
val className = stubGenerator.declarationMapper
|
||||
.getKotlinClassFor(container.clazz, isMeta = false).type
|
||||
.render(kotlinScope)
|
||||
|
||||
result.add(0,
|
||||
deprecatedInit(
|
||||
className,
|
||||
kotlinMethodParameters.map { it.name },
|
||||
factory = true
|
||||
)
|
||||
)
|
||||
|
||||
// TODO: add support for type parameters to [KotlinType] etc.
|
||||
val receiver = kotlinScope.reference(KotlinTypes.objCClassOf) + "<T>"
|
||||
|
||||
val originalReturnType = method.getReturnType(container.clazz)
|
||||
val returnType = if (originalReturnType is ObjCPointer) {
|
||||
if (originalReturnType.isNullable) "T?" else "T"
|
||||
} else {
|
||||
// This shouldn't happen actually.
|
||||
this.kotlinReturnType
|
||||
}
|
||||
|
||||
result.add("")
|
||||
result.addAll(buildObjCMethodAnnotations("@ObjCFactory"))
|
||||
result.add("external fun <T : $className> $receiver.create($parameters): $returnType")
|
||||
}
|
||||
is ObjCProtocol -> {} // Nothing to do.
|
||||
}
|
||||
}
|
||||
|
||||
result.asSequence()
|
||||
} else {
|
||||
sequenceOf(
|
||||
annotationForUnableToImport,
|
||||
header
|
||||
)
|
||||
}
|
||||
|
||||
private val kotlinMethodParameters: List<KotlinParameter>
|
||||
private val kotlinReturnType: String
|
||||
private val header: String
|
||||
internal val objCMethodAnnotations: List<String>
|
||||
private class ObjCMethodStubBuilder(
|
||||
private val method: ObjCMethod,
|
||||
private val container: ObjCContainer,
|
||||
private val isDesignatedInitializer: Boolean,
|
||||
override val context: StubsBuildingContext
|
||||
) : StubElementBuilder {
|
||||
private val isStret: Boolean
|
||||
private val stubReturnType: StubType
|
||||
val annotations = mutableListOf<AnnotationStub>()
|
||||
private val kotlinMethodParameters: List<FunctionParameterStub>
|
||||
private val external: Boolean
|
||||
private val receiver: ReceiverParameterStub?
|
||||
private val name: String = method.kotlinName
|
||||
private val origin = StubOrigin.ObjCMethod(method, container)
|
||||
private val modality: MemberStubModality
|
||||
|
||||
init {
|
||||
kotlinMethodParameters = method.getKotlinParameters(stubGenerator, forConstructorOrFactory = false)
|
||||
|
||||
val returnType = method.getReturnType(container.classOrProtocol)
|
||||
|
||||
isStret = returnType.isStret(stubGenerator.configuration.target)
|
||||
|
||||
this.kotlinReturnType = if (returnType.unwrapTypedefs() is VoidType) {
|
||||
KotlinTypes.unit
|
||||
isStret = returnType.isStret(context.configuration.target)
|
||||
stubReturnType = if (returnType.unwrapTypedefs() is VoidType) {
|
||||
WrapperStubType(KotlinTypes.unit)
|
||||
} else {
|
||||
stubGenerator.mirror(returnType).argType
|
||||
}.render(stubGenerator.kotlinFile)
|
||||
|
||||
objCMethodAnnotations = buildObjCMethodAnnotations("@ObjCMethod")
|
||||
|
||||
val joinedKotlinParameters = kotlinMethodParameters.renderParameters(stubGenerator.kotlinFile)
|
||||
|
||||
this.header = buildString {
|
||||
if (container !is ObjCProtocol) append("external ")
|
||||
val modality = when (container) {
|
||||
is ObjCClassOrProtocol -> if (method.isOverride(container)) {
|
||||
"override "
|
||||
} else when (container) {
|
||||
is ObjCClass -> "open "
|
||||
is ObjCProtocol -> ""
|
||||
}
|
||||
is ObjCCategory -> ""
|
||||
}
|
||||
append(modality)
|
||||
|
||||
append("fun ")
|
||||
if (container is ObjCCategory) {
|
||||
val receiverType = stubGenerator.declarationMapper
|
||||
.getKotlinClassFor(container.clazz, isMeta = method.isClass).type
|
||||
.render(stubGenerator.kotlinFile)
|
||||
|
||||
append(receiverType)
|
||||
append('.')
|
||||
}
|
||||
append("${method.kotlinName.asSimpleName()}($joinedKotlinParameters): $kotlinReturnType")
|
||||
|
||||
if (container is ObjCProtocol && method.isOptional) append(" = optional()")
|
||||
WrapperStubType(context.mirror(returnType).argType)
|
||||
}
|
||||
val methodAnnotation = AnnotationStub.ObjC.Method(
|
||||
method.selector,
|
||||
method.encoding,
|
||||
isStret
|
||||
)
|
||||
annotations += buildObjCMethodAnnotations(methodAnnotation)
|
||||
kotlinMethodParameters = method.getKotlinParameters(context, forConstructorOrFactory = false)
|
||||
external = (container !is ObjCProtocol)
|
||||
modality = when (container) {
|
||||
is ObjCClassOrProtocol -> {
|
||||
if (method.isOverride(container)) {
|
||||
MemberStubModality.OVERRIDE
|
||||
} else when (container) {
|
||||
is ObjCClass -> MemberStubModality.OPEN
|
||||
is ObjCProtocol -> MemberStubModality.OPEN
|
||||
}
|
||||
}
|
||||
is ObjCCategory -> MemberStubModality.FINAL
|
||||
}
|
||||
receiver = if (container is ObjCCategory) {
|
||||
val receiverType = ClassifierStubType(context.getKotlinClassFor(container.clazz, isMeta = method.isClass))
|
||||
ReceiverParameterStub(receiverType)
|
||||
} else null
|
||||
}
|
||||
|
||||
private fun buildObjCMethodAnnotations(main: String) = listOfNotNull(
|
||||
buildObjCMethodAnnotation(main),
|
||||
"@CCall.ConsumesReceiver".takeIf { method.nsConsumesSelf },
|
||||
"@CCall.ReturnsRetained".takeIf { method.nsReturnsRetained }
|
||||
private fun buildObjCMethodAnnotations(main: AnnotationStub): List<AnnotationStub> = listOfNotNull(
|
||||
main,
|
||||
AnnotationStub.ObjC.ConsumesReceiver.takeIf { method.nsConsumesSelf },
|
||||
AnnotationStub.ObjC.ReturnsRetained.takeIf { method.nsReturnsRetained }
|
||||
)
|
||||
|
||||
private fun buildObjCMethodAnnotation(annotation: String) = buildString {
|
||||
append(annotation)
|
||||
append('(')
|
||||
append(method.selector.quoteAsKotlinLiteral())
|
||||
append(", ")
|
||||
append(method.encoding.quoteAsKotlinLiteral())
|
||||
if (isStret) {
|
||||
append(", ")
|
||||
append("true")
|
||||
fun isDefaultConstructor(): Boolean =
|
||||
method.isInit && method.parameters.isEmpty()
|
||||
|
||||
override fun build(): List<FunctionalStub> {
|
||||
val replacement = if (method.isInit) {
|
||||
val parameters = method.getKotlinParameters(context, forConstructorOrFactory = true)
|
||||
when (container) {
|
||||
is ObjCClass -> {
|
||||
annotations.add(0, deprecatedInit(
|
||||
container.kotlinClassName(method.isClass),
|
||||
kotlinMethodParameters.map { it.name },
|
||||
factory = false
|
||||
))
|
||||
val designated = isDesignatedInitializer ||
|
||||
context.configuration.disableDesignatedInitializerChecks
|
||||
|
||||
val annotations = listOf(AnnotationStub.ObjC.Constructor(method.selector, designated))
|
||||
val constructor = ConstructorStub(parameters, annotations)
|
||||
constructor
|
||||
}
|
||||
is ObjCCategory -> {
|
||||
assert(!method.isClass)
|
||||
|
||||
|
||||
val clazz = context.getKotlinClassFor(container.clazz, isMeta = false).type
|
||||
|
||||
annotations.add(0, deprecatedInit(
|
||||
clazz.classifier.relativeFqName,
|
||||
kotlinMethodParameters.map { it.name },
|
||||
factory = true
|
||||
))
|
||||
|
||||
val factoryAnnotation = AnnotationStub.ObjC.Factory(
|
||||
method.selector,
|
||||
method.encoding,
|
||||
isStret
|
||||
)
|
||||
val annotations = buildObjCMethodAnnotations(factoryAnnotation)
|
||||
|
||||
val originalReturnType = method.getReturnType(container.clazz)
|
||||
val typeParameter = TypeParameterStub("T", WrapperStubType(clazz))
|
||||
val returnType = if (originalReturnType is ObjCPointer) {
|
||||
typeParameter.getStubType(originalReturnType.isNullable)
|
||||
} else {
|
||||
// This shouldn't happen actually.
|
||||
this.stubReturnType
|
||||
}
|
||||
val typeArgument = TypeArgumentStub(typeParameter.getStubType(false))
|
||||
val receiverType = ClassifierStubType(KotlinTypes.objCClassOf, listOf(typeArgument))
|
||||
val receiver = ReceiverParameterStub(receiverType)
|
||||
val createMethod = FunctionStub(
|
||||
"create",
|
||||
returnType,
|
||||
parameters,
|
||||
receiver = receiver,
|
||||
typeParameters = listOf(typeParameter),
|
||||
external = true,
|
||||
origin = StubOrigin.None,
|
||||
annotations = annotations,
|
||||
modality = MemberStubModality.FINAL
|
||||
)
|
||||
createMethod
|
||||
}
|
||||
is ObjCProtocol -> null
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
append(')')
|
||||
return listOfNotNull(
|
||||
FunctionStub(
|
||||
name,
|
||||
stubReturnType,
|
||||
kotlinMethodParameters.toList(),
|
||||
origin,
|
||||
annotations.toList(),
|
||||
external,
|
||||
receiver,
|
||||
modality),
|
||||
replacement
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun deprecatedInit(className: String, initParameterNames: List<String>, factory: Boolean): String {
|
||||
val replacement = if (factory) "$className.create" else className
|
||||
val replacementKind = if (factory) "factory method" else "constructor"
|
||||
val replaceWith = "$replacement(${initParameterNames.joinToString { it.asSimpleName() }})"
|
||||
|
||||
return deprecated("Use $replacementKind instead", replaceWith)
|
||||
}
|
||||
|
||||
private fun deprecated(message: String, replaceWith: String): String =
|
||||
"@Deprecated(${message.quoteAsKotlinLiteral()}, " +
|
||||
"ReplaceWith(${replaceWith.quoteAsKotlinLiteral()}), " +
|
||||
"DeprecationLevel.ERROR)"
|
||||
|
||||
private val ObjCContainer.classOrProtocol: ObjCClassOrProtocol
|
||||
internal val ObjCContainer.classOrProtocol: ObjCClassOrProtocol
|
||||
get() = when (this) {
|
||||
is ObjCClassOrProtocol -> this
|
||||
is ObjCCategory -> this.clazz
|
||||
@@ -265,7 +238,7 @@ private val ObjCContainer.classOrProtocol: ObjCClassOrProtocol
|
||||
*
|
||||
* The entire implementation is just the real ABI approximation which is enough for practical cases.
|
||||
*/
|
||||
private fun Type.isStret(target: KonanTarget): Boolean {
|
||||
internal fun Type.isStret(target: KonanTarget): Boolean {
|
||||
val unwrappedType = this.unwrapTypedefs()
|
||||
return when (target) {
|
||||
KonanTarget.IOS_ARM64 ->
|
||||
@@ -286,6 +259,13 @@ private fun Type.isStret(target: KonanTarget): Boolean {
|
||||
}
|
||||
}
|
||||
|
||||
private fun deprecatedInit(className: String, initParameterNames: List<String>, factory: Boolean): AnnotationStub {
|
||||
val replacement = if (factory) "$className.create" else className
|
||||
val replacementKind = if (factory) "factory method" else "constructor"
|
||||
val replaceWith = "$replacement(${initParameterNames.joinToString { it.asSimpleName() }})"
|
||||
return AnnotationStub.Deprecated("Use $replacementKind instead", replaceWith)
|
||||
}
|
||||
|
||||
private fun Type.isIntegerLikeType(): Boolean = when (this) {
|
||||
is RecordType -> {
|
||||
val def = this.decl.def
|
||||
@@ -325,7 +305,7 @@ private fun Type.hasUnalignedMembers(): Boolean = when (this) {
|
||||
// TODO: should the recursive checks be made in indexer when computing `hasUnalignedFields`?
|
||||
}
|
||||
|
||||
private val ObjCMethod.kotlinName: String
|
||||
internal val ObjCMethod.kotlinName: String
|
||||
get() {
|
||||
val candidate = selector.split(":").first()
|
||||
val trimmed = candidate.trimEnd('_')
|
||||
@@ -337,10 +317,10 @@ private val ObjCMethod.kotlinName: String
|
||||
}
|
||||
}
|
||||
|
||||
private val ObjCClassOrProtocol.protocolsWithSupers: Sequence<ObjCProtocol>
|
||||
internal val ObjCClassOrProtocol.protocolsWithSupers: Sequence<ObjCProtocol>
|
||||
get() = this.protocols.asSequence().flatMap { sequenceOf(it) + it.protocolsWithSupers }
|
||||
|
||||
private val ObjCClassOrProtocol.immediateSuperTypes: Sequence<ObjCClassOrProtocol>
|
||||
internal val ObjCClassOrProtocol.immediateSuperTypes: Sequence<ObjCClassOrProtocol>
|
||||
get() {
|
||||
val baseClass = (this as? ObjCClass)?.baseClass
|
||||
if (baseClass != null) {
|
||||
@@ -350,28 +330,28 @@ private val ObjCClassOrProtocol.immediateSuperTypes: Sequence<ObjCClassOrProtoco
|
||||
return this.protocols.asSequence()
|
||||
}
|
||||
|
||||
private val ObjCClassOrProtocol.selfAndSuperTypes: Sequence<ObjCClassOrProtocol>
|
||||
internal val ObjCClassOrProtocol.selfAndSuperTypes: Sequence<ObjCClassOrProtocol>
|
||||
get() = sequenceOf(this) + this.superTypes
|
||||
|
||||
private val ObjCClassOrProtocol.superTypes: Sequence<ObjCClassOrProtocol>
|
||||
internal val ObjCClassOrProtocol.superTypes: Sequence<ObjCClassOrProtocol>
|
||||
get() = this.immediateSuperTypes.flatMap { it.selfAndSuperTypes }.distinct()
|
||||
|
||||
private fun ObjCClassOrProtocol.declaredMethods(isClass: Boolean): Sequence<ObjCMethod> =
|
||||
internal fun ObjCClassOrProtocol.declaredMethods(isClass: Boolean): Sequence<ObjCMethod> =
|
||||
this.methods.asSequence().filter { it.isClass == isClass }
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun Sequence<ObjCMethod>.inheritedTo(container: ObjCClassOrProtocol, isMeta: Boolean): Sequence<ObjCMethod> =
|
||||
internal fun Sequence<ObjCMethod>.inheritedTo(container: ObjCClassOrProtocol, isMeta: Boolean): Sequence<ObjCMethod> =
|
||||
this // TODO: exclude methods that are marked as unavailable in [container].
|
||||
|
||||
private fun ObjCClassOrProtocol.inheritedMethods(isClass: Boolean): Sequence<ObjCMethod> =
|
||||
internal fun ObjCClassOrProtocol.inheritedMethods(isClass: Boolean): Sequence<ObjCMethod> =
|
||||
this.immediateSuperTypes.flatMap { it.methodsWithInherited(isClass) }
|
||||
.distinctBy { it.selector }
|
||||
.inheritedTo(this, isClass)
|
||||
|
||||
private fun ObjCClassOrProtocol.methodsWithInherited(isClass: Boolean): Sequence<ObjCMethod> =
|
||||
internal fun ObjCClassOrProtocol.methodsWithInherited(isClass: Boolean): Sequence<ObjCMethod> =
|
||||
(this.declaredMethods(isClass) + this.inheritedMethods(isClass)).distinctBy { it.selector }
|
||||
|
||||
private fun ObjCClass.getDesignatedInitializerSelectors(result: MutableSet<String>): Set<String> {
|
||||
internal fun ObjCClass.getDesignatedInitializerSelectors(result: MutableSet<String>): Set<String> {
|
||||
// Note: Objective-C initializers act as usual methods and thus are inherited by subclasses.
|
||||
// Swift considers all super initializers to be available (unless otherwise specified explicitly),
|
||||
// but seems to consider them as non-designated if class declares its own ones explicitly.
|
||||
@@ -392,16 +372,22 @@ private fun ObjCClass.getDesignatedInitializerSelectors(result: MutableSet<Strin
|
||||
return result
|
||||
}
|
||||
|
||||
private fun ObjCMethod.isOverride(container: ObjCClassOrProtocol): Boolean =
|
||||
internal fun ObjCMethod.isOverride(container: ObjCClassOrProtocol): Boolean =
|
||||
container.superTypes.any { superType -> superType.methods.any(this::replaces) }
|
||||
|
||||
abstract class ObjCContainerStub(stubGenerator: StubGenerator,
|
||||
private val container: ObjCClassOrProtocol,
|
||||
protected val metaContainerStub: ObjCContainerStub?
|
||||
) : KotlinStub {
|
||||
|
||||
internal abstract class ObjCContainerStubBuilder(
|
||||
final override val context: StubsBuildingContext,
|
||||
private val container: ObjCClassOrProtocol,
|
||||
protected val metaContainerStub: ObjCContainerStubBuilder?
|
||||
) : StubElementBuilder {
|
||||
private val isMeta: Boolean get() = metaContainerStub == null
|
||||
|
||||
private val designatedInitializerSelectors = if (container is ObjCClass && !isMeta) {
|
||||
container.getDesignatedInitializerSelectors(mutableSetOf())
|
||||
} else {
|
||||
emptySet()
|
||||
}
|
||||
|
||||
private val methods: List<ObjCMethod>
|
||||
private val properties: List<ObjCProperty>
|
||||
|
||||
@@ -446,139 +432,130 @@ abstract class ObjCContainerStub(stubGenerator: StubGenerator,
|
||||
}
|
||||
}
|
||||
|
||||
private val designatedInitializerSelectors = if (container is ObjCClass && !isMeta) {
|
||||
container.getDesignatedInitializerSelectors(mutableSetOf())
|
||||
} else {
|
||||
emptySet()
|
||||
}
|
||||
|
||||
private val methodToStub = methods.map {
|
||||
it to ObjCMethodStub(
|
||||
stubGenerator, it, container,
|
||||
isDesignatedInitializer = it.selector in designatedInitializerSelectors
|
||||
)
|
||||
it to ObjCMethodStubBuilder(it, container, it.selector in designatedInitializerSelectors, context)
|
||||
}.toMap()
|
||||
|
||||
private val methodStubs get() = methodToStub.values
|
||||
|
||||
val propertyStubs = properties.mapNotNull {
|
||||
createObjCPropertyStub(stubGenerator, it, container, this.methodToStub)
|
||||
private val propertyBuilders = properties.mapNotNull {
|
||||
createObjCPropertyBuilder(context, it, container, this.methodToStub)
|
||||
}
|
||||
|
||||
private val classHeader: String
|
||||
private val modality = when (container) {
|
||||
is ObjCClass -> ClassStubModality.OPEN
|
||||
is ObjCProtocol -> ClassStubModality.INTERFACE
|
||||
}
|
||||
|
||||
init {
|
||||
val supers = mutableListOf<KotlinType>()
|
||||
private val classifier = context.getKotlinClassFor(container, isMeta)
|
||||
|
||||
private val externalObjCAnnotation = when (container) {
|
||||
is ObjCProtocol -> {
|
||||
protocolGetter = if (metaContainerStub != null) {
|
||||
metaContainerStub.protocolGetter!!
|
||||
} else {
|
||||
// TODO: handle the case when protocol getter stub can't be compiled.
|
||||
context.generateNextUniqueId("kniprot_")
|
||||
}
|
||||
AnnotationStub.ObjC.ExternalClass(protocolGetter)
|
||||
}
|
||||
is ObjCClass -> {
|
||||
protocolGetter = null
|
||||
val binaryName = container.binaryName
|
||||
AnnotationStub.ObjC.ExternalClass("", binaryName ?: "")
|
||||
}
|
||||
}
|
||||
|
||||
private val interfaces: List<StubType> by lazy {
|
||||
val interfaces = mutableListOf<StubType>()
|
||||
if (container is ObjCClass) {
|
||||
val baseClass = container.baseClass
|
||||
val baseClassifier = if (baseClass != null) {
|
||||
stubGenerator.declarationMapper.getKotlinClassFor(baseClass, isMeta)
|
||||
context.getKotlinClassFor(baseClass, isMeta)
|
||||
} else {
|
||||
if (isMeta) KotlinTypes.objCObjectBaseMeta else KotlinTypes.objCObjectBase
|
||||
}
|
||||
|
||||
supers.add(baseClassifier.type)
|
||||
interfaces += WrapperStubType(baseClassifier.type)
|
||||
}
|
||||
container.protocols.forEach {
|
||||
supers.add(stubGenerator.declarationMapper.getKotlinClassFor(it, isMeta).type)
|
||||
interfaces += WrapperStubType(context.getKotlinClassFor(it, isMeta).type)
|
||||
}
|
||||
|
||||
if (supers.isEmpty()) {
|
||||
if (interfaces.isEmpty()) {
|
||||
assert(container is ObjCProtocol)
|
||||
val classifier = if (isMeta) KotlinTypes.objCObjectMeta else KotlinTypes.objCObject
|
||||
supers.add(classifier.type)
|
||||
interfaces += WrapperStubType(classifier.type)
|
||||
}
|
||||
|
||||
if (!isMeta && container.isProtocolClass()) {
|
||||
// TODO: map Protocol type to ObjCProtocol instead.
|
||||
supers.add(KotlinTypes.objCProtocol.type)
|
||||
interfaces += WrapperStubType(KotlinTypes.objCProtocol.type)
|
||||
}
|
||||
|
||||
val keywords = when (container) {
|
||||
is ObjCClass -> "open class"
|
||||
is ObjCProtocol -> "interface"
|
||||
}
|
||||
|
||||
val supersString = supers.joinToString { it.render(stubGenerator.kotlinFile) }
|
||||
val classifier = stubGenerator.declarationMapper.getKotlinClassFor(container, isMeta)
|
||||
val name = stubGenerator.kotlinFile.declare(classifier)
|
||||
|
||||
val externalObjCClassAnnotationName = "@ExternalObjCClass"
|
||||
|
||||
val externalObjCClassAnnotation: String = when (container) {
|
||||
is ObjCProtocol -> {
|
||||
protocolGetter = if (metaContainerStub != null) {
|
||||
metaContainerStub.protocolGetter!!
|
||||
} else {
|
||||
val nativeBacked = object : NativeBacked {}
|
||||
// TODO: handle the case when protocol getter stub can't be compiled.
|
||||
genProtocolGetter(stubGenerator, nativeBacked, container)
|
||||
}
|
||||
|
||||
externalObjCClassAnnotationName.applyToStrings(protocolGetter)
|
||||
}
|
||||
is ObjCClass -> {
|
||||
protocolGetter = null
|
||||
val binaryName = container.binaryName
|
||||
if (binaryName != null) {
|
||||
externalObjCClassAnnotationName.applyToStrings("", binaryName)
|
||||
} else {
|
||||
externalObjCClassAnnotationName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.classHeader = "$externalObjCClassAnnotation $keywords $name : $supersString"
|
||||
interfaces
|
||||
}
|
||||
|
||||
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)
|
||||
}) {
|
||||
private fun buildBody(): Pair<List<PropertyStub>, List<FunctionalStub>> {
|
||||
val defaultConstructor = if (container is ObjCClass && methodToStub.values.none { it.isDefaultConstructor() }) {
|
||||
// Always generate default constructor.
|
||||
// If it is not produced for an init method, then include it manually:
|
||||
result += sequenceOf("", "protected constructor() {}")
|
||||
}
|
||||
ConstructorStub(listOf(), listOf(), VisibilityModifier.PROTECTED)
|
||||
} else null
|
||||
|
||||
return result
|
||||
return Pair(
|
||||
propertyBuilders.flatMap { it.build() },
|
||||
methodToStub.values.flatMap { it.build() } + listOfNotNull(defaultConstructor)
|
||||
)
|
||||
}
|
||||
|
||||
override fun generate(context: StubGenerationContext): Sequence<String> = block(classHeader, generateBody(context))
|
||||
protected fun buildClassStub(origin: StubOrigin, companion: ClassStub.Companion? = null): ClassStub {
|
||||
val (properties, methods) = buildBody()
|
||||
return ClassStub.Simple(
|
||||
classifier,
|
||||
properties = properties,
|
||||
functions = methods,
|
||||
origin = origin,
|
||||
modality = modality,
|
||||
annotations = listOf(externalObjCAnnotation),
|
||||
interfaces = interfaces,
|
||||
companion = companion
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
open class ObjCClassOrProtocolStub(
|
||||
stubGenerator: StubGenerator,
|
||||
internal sealed class ObjCClassOrProtocolStubBuilder(
|
||||
context: StubsBuildingContext,
|
||||
private val container: ObjCClassOrProtocol
|
||||
) : ObjCContainerStub(
|
||||
stubGenerator,
|
||||
) : ObjCContainerStubBuilder(
|
||||
context,
|
||||
container,
|
||||
metaContainerStub = object : ObjCContainerStub(stubGenerator, container, metaContainerStub = null) {}
|
||||
) {
|
||||
override fun generate(context: StubGenerationContext) =
|
||||
metaContainerStub!!.generate(context) + "" + super.generate(context)
|
||||
metaContainerStub = object : ObjCContainerStubBuilder(context, container, metaContainerStub = null) {
|
||||
|
||||
override fun build(): List<StubIrElement> =
|
||||
listOf(buildClassStub(StubOrigin.None))
|
||||
}
|
||||
)
|
||||
|
||||
internal class ObjCProtocolStubBuilder(
|
||||
context: StubsBuildingContext,
|
||||
private val protocol: ObjCProtocol
|
||||
) : ObjCClassOrProtocolStubBuilder(context, protocol), StubElementBuilder {
|
||||
override fun build(): List<StubIrElement> {
|
||||
val classStub = buildClassStub(StubOrigin.ObjCProtocol(protocol))
|
||||
return listOf(*metaContainerStub!!.build().toTypedArray(), classStub)
|
||||
}
|
||||
}
|
||||
|
||||
class ObjCProtocolStub(stubGenerator: StubGenerator, protocol: ObjCProtocol) :
|
||||
ObjCClassOrProtocolStub(stubGenerator, protocol)
|
||||
|
||||
class ObjCClassStub(private val stubGenerator: StubGenerator, private val clazz: ObjCClass) :
|
||||
ObjCClassOrProtocolStub(stubGenerator, clazz) {
|
||||
|
||||
override fun generateBody(context: StubGenerationContext): Sequence<String> {
|
||||
val companionSuper = stubGenerator.declarationMapper
|
||||
.getKotlinClassFor(clazz, isMeta = true).type
|
||||
.render(stubGenerator.kotlinFile)
|
||||
internal class ObjCClassStubBuilder(
|
||||
context: StubsBuildingContext,
|
||||
private val clazz: ObjCClass
|
||||
) : ObjCClassOrProtocolStubBuilder(context, clazz), StubElementBuilder {
|
||||
override fun build(): List<StubIrElement> {
|
||||
val companionSuper = ClassifierStubType(context.getKotlinClassFor(clazz, isMeta = true))
|
||||
|
||||
val objCClassType = KotlinTypes.objCClassOf.typeWith(
|
||||
stubGenerator.declarationMapper.getKotlinClassFor(clazz, isMeta = false).type
|
||||
).render(stubGenerator.kotlinFile)
|
||||
context.getKotlinClassFor(clazz, isMeta = false).type
|
||||
).let { WrapperStubType(it) }
|
||||
|
||||
return sequenceOf( "companion object : $companionSuper(), $objCClassType {}") +
|
||||
super.generateBody(context)
|
||||
val superClassInit = SuperClassInit(companionSuper)
|
||||
val companion = ClassStub.Companion(superClassInit, listOf(objCClassType))
|
||||
val classStub = buildClassStub(StubOrigin.ObjCClass(clazz), companion)
|
||||
return listOf(*metaContainerStub!!.build().toTypedArray(), classStub)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -594,77 +571,71 @@ class GeneratedObjCCategoriesMembers {
|
||||
|
||||
}
|
||||
|
||||
class ObjCCategoryStub(
|
||||
private val stubGenerator: StubGenerator, private val category: ObjCCategory
|
||||
) : KotlinStub {
|
||||
|
||||
private val generatedMembers = stubGenerator.generatedObjCCategoriesMembers
|
||||
internal class ObjCCategoryStubBuilder(
|
||||
override val context: StubsBuildingContext,
|
||||
private val category: ObjCCategory
|
||||
) : StubElementBuilder {
|
||||
private val generatedMembers = context.generatedObjCCategoriesMembers
|
||||
.getOrPut(category.clazz, { GeneratedObjCCategoriesMembers() })
|
||||
|
||||
// TODO: consider removing members that are also present in the class or its supertypes.
|
||||
|
||||
private val methodToStub = category.methods.filter { generatedMembers.register(it) }.map {
|
||||
it to ObjCMethodStub(stubGenerator, it, category, isDesignatedInitializer = false)
|
||||
private val methodToBuilder = category.methods.filter { generatedMembers.register(it) }.map {
|
||||
it to ObjCMethodStubBuilder(it, category, isDesignatedInitializer = false, context = context)
|
||||
}.toMap()
|
||||
|
||||
private val methodStubs get() = methodToStub.values
|
||||
private val methodBuilders get() = methodToBuilder.values
|
||||
|
||||
private val propertyStubs = category.properties.filter { generatedMembers.register(it) }.mapNotNull {
|
||||
createObjCPropertyStub(stubGenerator, it, category, methodToStub)
|
||||
private val propertyBuilders = category.properties.filter { generatedMembers.register(it) }.mapNotNull {
|
||||
createObjCPropertyBuilder(context, it, category, methodToBuilder)
|
||||
}
|
||||
|
||||
override fun generate(context: StubGenerationContext): Sequence<String> {
|
||||
override fun build(): List<StubIrElement> {
|
||||
val description = "${category.clazz.name} (${category.name})"
|
||||
return sequenceOf("// @interface $description") +
|
||||
propertyStubs.asSequence().flatMap { sequenceOf("") + it.generate(context) } +
|
||||
methodStubs.asSequence().flatMap { sequenceOf("") + it.generate(context) } +
|
||||
sequenceOf("// @end; // $description")
|
||||
val meta = StubContainerMeta(
|
||||
"// @interface $description",
|
||||
"// @end; // $description"
|
||||
)
|
||||
val container = SimpleStubContainer(
|
||||
meta = meta,
|
||||
functions = methodBuilders.flatMap { it.build() },
|
||||
properties = propertyBuilders.flatMap { it.build() }
|
||||
)
|
||||
return listOf(container)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createObjCPropertyStub(
|
||||
stubGenerator: StubGenerator,
|
||||
private fun createObjCPropertyBuilder(
|
||||
context: StubsBuildingContext,
|
||||
property: ObjCProperty,
|
||||
container: ObjCContainer,
|
||||
methodToStub: Map<ObjCMethod, ObjCMethodStub>
|
||||
): ObjCPropertyStub? {
|
||||
methodToStub: Map<ObjCMethod, ObjCMethodStubBuilder>
|
||||
): ObjCPropertyStubBuilder? {
|
||||
// Note: the code below assumes that if the property is generated,
|
||||
// then its accessors are also generated as explicit methods.
|
||||
val getterStub = methodToStub[property.getter] ?: return null
|
||||
val setterStub = property.setter?.let { methodToStub[it] ?: return null }
|
||||
return ObjCPropertyStub(stubGenerator, property, container, getterStub, setterStub)
|
||||
return ObjCPropertyStubBuilder(context, property, container, getterStub, setterStub)
|
||||
}
|
||||
|
||||
class ObjCPropertyStub(
|
||||
val stubGenerator: StubGenerator, val property: ObjCProperty, val container: ObjCContainer,
|
||||
val getterStub: ObjCMethodStub, val setterStub: ObjCMethodStub?
|
||||
) : KotlinStub {
|
||||
|
||||
override fun generate(context: StubGenerationContext): Sequence<String> {
|
||||
private class ObjCPropertyStubBuilder(
|
||||
override val context: StubsBuildingContext,
|
||||
private val property: ObjCProperty,
|
||||
private val container: ObjCContainer,
|
||||
private val getterBuilder: ObjCMethodStubBuilder,
|
||||
private val setterMethod: ObjCMethodStubBuilder?
|
||||
) : StubElementBuilder {
|
||||
override fun build(): List<PropertyStub> {
|
||||
val type = property.getType(container.classOrProtocol)
|
||||
|
||||
val kotlinType = stubGenerator.mirror(type).argType.render(stubGenerator.kotlinFile)
|
||||
|
||||
val kind = if (property.setter == null) "val" else "var"
|
||||
val modifiers = if (container is ObjCProtocol) "final " else ""
|
||||
val kotlinType = context.mirror(type).argType
|
||||
val getter = PropertyAccessor.Getter.ExternalGetter(annotations = getterBuilder.annotations)
|
||||
val setter = property.setter?.let { PropertyAccessor.Setter.ExternalSetter(annotations = setterMethod!!.annotations) }
|
||||
val kind = setter?.let { PropertyStub.Kind.Var(getter, it) } ?: PropertyStub.Kind.Val(getter)
|
||||
val modality = MemberStubModality.FINAL
|
||||
val receiver = when (container) {
|
||||
is ObjCClassOrProtocol -> ""
|
||||
is ObjCCategory -> stubGenerator.declarationMapper
|
||||
.getKotlinClassFor(container.clazz, isMeta = property.getter.isClass).type
|
||||
.render(stubGenerator.kotlinFile) + "."
|
||||
is ObjCClassOrProtocol -> null
|
||||
is ObjCCategory -> ClassifierStubType(context.getKotlinClassFor(container.clazz, isMeta = property.getter.isClass))
|
||||
}
|
||||
val result = mutableListOf(
|
||||
"$modifiers$kind $receiver${property.name.asSimpleName()}: $kotlinType",
|
||||
" ${getterStub.objCMethodAnnotations.joinToString(" ")} external get"
|
||||
)
|
||||
|
||||
property.setter?.let {
|
||||
result.add(" ${setterStub!!.objCMethodAnnotations.joinToString(" ")} external set")
|
||||
}
|
||||
|
||||
return result.asSequence()
|
||||
return listOf(PropertyStub(property.name, WrapperStubType(kotlinType), kind, modality, receiver))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun ObjCClassOrProtocol.kotlinClassName(isMeta: Boolean): String {
|
||||
@@ -676,27 +647,7 @@ fun ObjCClassOrProtocol.kotlinClassName(isMeta: Boolean): String {
|
||||
return if (isMeta) "${baseClassName}Meta" else baseClassName
|
||||
}
|
||||
|
||||
private fun genProtocolGetter(
|
||||
stubGenerator: StubGenerator,
|
||||
nativeBacked: NativeBacked,
|
||||
protocol: ObjCProtocol
|
||||
): String {
|
||||
val functionName = "kniprot_" + stubGenerator.pkgName.replace('.', '_') + stubGenerator.nextUniqueId()
|
||||
|
||||
val builder = NativeCodeBuilder(stubGenerator.simpleBridgeGenerator.topLevelNativeScope)
|
||||
|
||||
with(builder) {
|
||||
out("Protocol* $functionName() {")
|
||||
out(" return @protocol(${protocol.name});")
|
||||
out("}")
|
||||
}
|
||||
|
||||
stubGenerator.simpleBridgeGenerator.insertNativeBridge(nativeBacked, emptyList(), builder.lines)
|
||||
|
||||
return functionName
|
||||
}
|
||||
|
||||
private fun ObjCClassOrProtocol.isProtocolClass(): Boolean = when (this) {
|
||||
internal fun ObjCClassOrProtocol.isProtocolClass(): Boolean = when (this) {
|
||||
is ObjCClass -> (name == "Protocol" || binaryName == "Protocol")
|
||||
is ObjCProtocol -> false
|
||||
}
|
||||
|
||||
-1
@@ -234,7 +234,6 @@ class SimpleBridgeGeneratorImpl(
|
||||
}
|
||||
|
||||
// TODO: exclude unused bridges.
|
||||
|
||||
return object : NativeBridges {
|
||||
|
||||
override val kotlinLines: Sequence<String>
|
||||
|
||||
+425
@@ -0,0 +1,425 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
package org.jetbrains.kotlin.native.interop.gen
|
||||
|
||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||
|
||||
interface StubIrElement {
|
||||
fun <T, R> accept(visitor: StubIrVisitor<T, R>, data: T): R
|
||||
}
|
||||
|
||||
sealed class StubContainer : StubIrElement {
|
||||
abstract val meta: StubContainerMeta
|
||||
abstract val classes: List<ClassStub>
|
||||
abstract val functions: List<FunctionalStub>
|
||||
abstract val properties: List<PropertyStub>
|
||||
abstract val typealiases: List<TypealiasStub>
|
||||
abstract val simpleContainers: List<SimpleStubContainer>
|
||||
}
|
||||
|
||||
/**
|
||||
* Meta information about [StubContainer].
|
||||
* For example, can be used for comments in textual representation.
|
||||
*/
|
||||
class StubContainerMeta(
|
||||
val textAtStart: String = "",
|
||||
val textAtEnd: String = ""
|
||||
)
|
||||
|
||||
class SimpleStubContainer(
|
||||
override val meta: StubContainerMeta = StubContainerMeta(),
|
||||
override val classes: List<ClassStub> = emptyList(),
|
||||
override val functions: List<FunctionalStub> = emptyList(),
|
||||
override val properties: List<PropertyStub> = emptyList(),
|
||||
override val typealiases: List<TypealiasStub> = emptyList(),
|
||||
override val simpleContainers: List<SimpleStubContainer> = emptyList()
|
||||
) : StubContainer() {
|
||||
|
||||
override fun <T, R> accept(visitor: StubIrVisitor<T, R>, data: T): R {
|
||||
return visitor.visitSimpleStubContainer(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
val StubContainer.children: List<StubIrElement>
|
||||
get() = (classes as List<StubIrElement>) + properties + functions + typealiases
|
||||
|
||||
|
||||
sealed class StubType {
|
||||
abstract val nullable: Boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks that abstract value of such type can be passed as value.
|
||||
*/
|
||||
sealed class ValueStub
|
||||
|
||||
class TypeParameterStub(
|
||||
val name: String,
|
||||
val upperBound: StubType? = null
|
||||
) {
|
||||
fun getStubType(nullable: Boolean): StubType =
|
||||
SymbolicStubType(name, nullable = nullable)
|
||||
|
||||
}
|
||||
|
||||
// Add variance if needed
|
||||
class TypeArgumentStub(val type: StubType)
|
||||
|
||||
/**
|
||||
* Wrapper over [KotlinType].
|
||||
*/
|
||||
class WrapperStubType(
|
||||
val kotlinType: KotlinType
|
||||
) : StubType() {
|
||||
override val nullable: Boolean
|
||||
get() = when (kotlinType) {
|
||||
is KotlinClassifierType -> kotlinType.nullable
|
||||
is KotlinFunctionType -> kotlinType.nullable
|
||||
else -> error("Unknown KotlinType: $kotlinType")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper over [Classifier].
|
||||
*/
|
||||
class ClassifierStubType(
|
||||
val classifier: Classifier,
|
||||
val typeArguments: List<TypeArgumentStub> = emptyList(),
|
||||
override val nullable: Boolean = false
|
||||
) : StubType()
|
||||
|
||||
|
||||
/**
|
||||
* Fallback variant for all cases where we cannot refer to specific [KotlinType] or [Classifier].
|
||||
*/
|
||||
class SymbolicStubType(
|
||||
val name: String,
|
||||
override val nullable: Boolean = false
|
||||
) : StubType()
|
||||
|
||||
/**
|
||||
* Represents a source of StubIr element.
|
||||
*/
|
||||
sealed class StubOrigin {
|
||||
/**
|
||||
* Special case when element of IR was generated.
|
||||
*/
|
||||
object None : StubOrigin()
|
||||
|
||||
class ObjCMethod(
|
||||
val method: org.jetbrains.kotlin.native.interop.indexer.ObjCMethod,
|
||||
val container: ObjCContainer
|
||||
) : StubOrigin()
|
||||
|
||||
class ObjCClass(
|
||||
val clazz: org.jetbrains.kotlin.native.interop.indexer.ObjCClass
|
||||
) : StubOrigin()
|
||||
|
||||
class ObjCProtocol(
|
||||
val protocol: org.jetbrains.kotlin.native.interop.indexer.ObjCProtocol
|
||||
) : StubOrigin()
|
||||
|
||||
class Enum(val enum: EnumDef) : StubOrigin()
|
||||
|
||||
class Function(val function: FunctionDecl) : StubOrigin()
|
||||
|
||||
class FunctionParameter(val parameter: Parameter) : StubOrigin()
|
||||
|
||||
class Struct(val struct: StructDecl) : StubOrigin()
|
||||
}
|
||||
|
||||
interface StubElementWithOrigin : StubIrElement {
|
||||
val origin: StubOrigin
|
||||
}
|
||||
|
||||
interface AnnotationHolder {
|
||||
val annotations: List<AnnotationStub>
|
||||
}
|
||||
|
||||
sealed class AnnotationStub {
|
||||
sealed class ObjC : AnnotationStub() {
|
||||
object ConsumesReceiver : ObjC()
|
||||
object ReturnsRetained : ObjC()
|
||||
class Method(val selector: String, val encoding: String, val isStret: Boolean = false) : ObjC()
|
||||
class Factory(val selector: String, val encoding: String, val isStret: Boolean = false) : ObjC()
|
||||
object Consumed : ObjC()
|
||||
class Constructor(val selector: String, val designated: Boolean) : ObjC()
|
||||
class ExternalClass(val protocolGetter: String = "", val binaryName: String = "") : ObjC()
|
||||
}
|
||||
|
||||
sealed class CCall : AnnotationStub() {
|
||||
object CString : CCall()
|
||||
object WCString : CCall()
|
||||
class Symbol(val symbolName: String) : CCall()
|
||||
}
|
||||
|
||||
class CStruct(val struct: String) : AnnotationStub()
|
||||
class CNaturalStruct(val members: List<StructMember>) : AnnotationStub()
|
||||
|
||||
class CLength(val length: Long) : AnnotationStub()
|
||||
|
||||
class Deprecated(val message: String, val replaceWith: String) : AnnotationStub()
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile-time known values.
|
||||
*/
|
||||
sealed class ConstantStub : ValueStub()
|
||||
class StringConstantStub(val value: String) : ConstantStub()
|
||||
data class IntegralConstantStub(val value: Long, val size: Int, val isSigned: Boolean) : ConstantStub()
|
||||
data class DoubleConstantStub(val value: Double, val size: Int) : ConstantStub()
|
||||
|
||||
|
||||
class PropertyStub(
|
||||
val name: String,
|
||||
val type: StubType,
|
||||
val kind: Kind,
|
||||
val modality: MemberStubModality = MemberStubModality.FINAL,
|
||||
val receiverType: StubType? = null,
|
||||
override val annotations: List<AnnotationStub> = emptyList()
|
||||
) : StubIrElement, AnnotationHolder {
|
||||
sealed class Kind {
|
||||
class Val(
|
||||
val getter: PropertyAccessor.Getter
|
||||
) : Kind()
|
||||
|
||||
class Var(
|
||||
val getter: PropertyAccessor.Getter,
|
||||
val setter: PropertyAccessor.Setter
|
||||
) : Kind()
|
||||
|
||||
class Constant(val constant: ConstantStub) : Kind()
|
||||
}
|
||||
|
||||
override fun <T, R> accept(visitor: StubIrVisitor<T, R>, data: T): R {
|
||||
return visitor.visitProperty(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
enum class ClassStubModality {
|
||||
INTERFACE, OPEN, ABSTRACT, NONE
|
||||
}
|
||||
|
||||
enum class VisibilityModifier {
|
||||
PRIVATE, PROTECTED, INTERNAL, PUBLIC
|
||||
}
|
||||
|
||||
class ConstructorParameterStub(val name: String, val type: StubType, val qualifier: Qualifier = Qualifier.NONE) {
|
||||
sealed class Qualifier {
|
||||
class VAL(val overrides: Boolean) : Qualifier()
|
||||
class VAR(val overrides: Boolean) : Qualifier()
|
||||
object NONE : Qualifier()
|
||||
}
|
||||
}
|
||||
|
||||
class GetConstructorParameter(
|
||||
val constructorParameterStub: ConstructorParameterStub
|
||||
) : ValueStub()
|
||||
|
||||
class SuperClassInit(
|
||||
val type: StubType,
|
||||
val arguments: List<ValueStub> = listOf()
|
||||
)
|
||||
|
||||
sealed class ClassStub : StubContainer(), StubElementWithOrigin, AnnotationHolder {
|
||||
|
||||
abstract val superClassInit: SuperClassInit?
|
||||
abstract val interfaces: List<StubType>
|
||||
abstract val childrenClasses: List<ClassStub>
|
||||
abstract val companion : Companion?
|
||||
|
||||
class Simple(
|
||||
val classifier: Classifier,
|
||||
val modality: ClassStubModality,
|
||||
val constructorParameters: List<ConstructorParameterStub> = emptyList(),
|
||||
override val superClassInit: SuperClassInit? = null,
|
||||
override val interfaces: List<StubType> = emptyList(),
|
||||
override val properties: List<PropertyStub> = emptyList(),
|
||||
override val origin: StubOrigin,
|
||||
override val annotations: List<AnnotationStub> = emptyList(),
|
||||
override val childrenClasses: List<ClassStub> = emptyList(),
|
||||
override val companion: Companion? = null,
|
||||
override val functions: List<FunctionalStub> = emptyList(),
|
||||
override val simpleContainers: List<SimpleStubContainer> = emptyList()
|
||||
) : ClassStub()
|
||||
|
||||
class Companion(
|
||||
override val superClassInit: SuperClassInit? = null,
|
||||
override val interfaces: List<StubType> = emptyList(),
|
||||
override val properties: List<PropertyStub> = emptyList(),
|
||||
override val origin: StubOrigin = StubOrigin.None,
|
||||
override val annotations: List<AnnotationStub> = emptyList(),
|
||||
override val childrenClasses: List<ClassStub> = emptyList(),
|
||||
override val functions: List<FunctionalStub> = emptyList(),
|
||||
override val simpleContainers: List<SimpleStubContainer> = emptyList()
|
||||
) : ClassStub() {
|
||||
override val companion: Companion? = null
|
||||
}
|
||||
|
||||
class Enum(
|
||||
val classifier: Classifier,
|
||||
val entries: List<EnumEntryStub>,
|
||||
val constructorParameters: List<ConstructorParameterStub> = emptyList(),
|
||||
override val superClassInit: SuperClassInit? = null,
|
||||
override val interfaces: List<StubType> = emptyList(),
|
||||
override val properties: List<PropertyStub> = emptyList(),
|
||||
override val origin: StubOrigin,
|
||||
override val annotations: List<AnnotationStub> = emptyList(),
|
||||
override val childrenClasses: List<ClassStub> = emptyList(),
|
||||
override val companion: Companion?= null,
|
||||
override val functions: List<FunctionalStub> = emptyList(),
|
||||
override val simpleContainers: List<SimpleStubContainer> = emptyList()
|
||||
) : ClassStub()
|
||||
|
||||
override val meta: StubContainerMeta = StubContainerMeta()
|
||||
|
||||
override val classes: List<ClassStub>
|
||||
get() = childrenClasses + listOfNotNull(companion)
|
||||
|
||||
override fun <T, R> accept(visitor: StubIrVisitor<T, R>, data: T) =
|
||||
visitor.visitClass(this, data)
|
||||
|
||||
override val typealiases: List<TypealiasStub> = emptyList()
|
||||
}
|
||||
|
||||
class ReceiverParameterStub(
|
||||
val type: StubType
|
||||
)
|
||||
|
||||
class FunctionParameterStub(
|
||||
val name: String,
|
||||
val type: StubType,
|
||||
override val annotations: List<AnnotationStub> = emptyList(),
|
||||
val isVararg: Boolean = false,
|
||||
val origin: StubOrigin = StubOrigin.None
|
||||
) : AnnotationHolder
|
||||
|
||||
enum class MemberStubModality {
|
||||
OVERRIDE,
|
||||
OPEN,
|
||||
FINAL
|
||||
}
|
||||
|
||||
interface FunctionalStub : AnnotationHolder, StubIrElement, NativeBacked {
|
||||
val parameters: List<FunctionParameterStub>
|
||||
}
|
||||
|
||||
sealed class PropertyAccessor : FunctionalStub {
|
||||
|
||||
sealed class Getter : PropertyAccessor() {
|
||||
|
||||
override val parameters: List<FunctionParameterStub> = emptyList()
|
||||
|
||||
class SimpleGetter(
|
||||
override val annotations: List<AnnotationStub> = emptyList(),
|
||||
val constant: ConstantStub? = null
|
||||
) : Getter()
|
||||
|
||||
class ExternalGetter(
|
||||
override val annotations: List<AnnotationStub> = emptyList()
|
||||
) : Getter()
|
||||
|
||||
class ArrayMemberAt(
|
||||
val offset: Long
|
||||
) : Getter() {
|
||||
override val parameters: List<FunctionParameterStub> = emptyList()
|
||||
override val annotations: List<AnnotationStub> = emptyList()
|
||||
}
|
||||
|
||||
class MemberAt(
|
||||
val offset: Long,
|
||||
val typeArguments: List<TypeArgumentStub> = emptyList(),
|
||||
val hasValueAccessor: Boolean
|
||||
) : Getter() {
|
||||
override val annotations: List<AnnotationStub> = emptyList()
|
||||
}
|
||||
|
||||
class ReadBits(
|
||||
val offset: Long,
|
||||
val size: Int,
|
||||
val signed: Boolean
|
||||
) : Getter() {
|
||||
override val annotations: List<AnnotationStub> = emptyList()
|
||||
}
|
||||
|
||||
class InterpretPointed(val cGlobalName:String, pointedType: WrapperStubType) : Getter() {
|
||||
override val annotations: List<AnnotationStub> = emptyList()
|
||||
val typeParameters: List<StubType> = listOf(pointedType)
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Setter : PropertyAccessor() {
|
||||
|
||||
override val parameters: List<FunctionParameterStub> = emptyList()
|
||||
|
||||
class SimpleSetter(
|
||||
override val annotations: List<AnnotationStub> = emptyList()
|
||||
) : Setter()
|
||||
|
||||
class ExternalSetter(
|
||||
override val annotations: List<AnnotationStub> = emptyList()
|
||||
) : Setter()
|
||||
|
||||
class MemberAt(
|
||||
val offset: Long,
|
||||
override val annotations: List<AnnotationStub> = emptyList(),
|
||||
val typeArguments: List<TypeArgumentStub> = emptyList()
|
||||
) : Setter()
|
||||
|
||||
class WriteBits(
|
||||
val offset: Long,
|
||||
val size: Int,
|
||||
override val annotations: List<AnnotationStub> = emptyList()
|
||||
) : Setter()
|
||||
}
|
||||
|
||||
override fun <T, R> accept(visitor: StubIrVisitor<T, R>, data: T) =
|
||||
visitor.visitPropertyAccessor(this, data)
|
||||
|
||||
}
|
||||
|
||||
class FunctionStub(
|
||||
val name: String,
|
||||
val returnType: StubType,
|
||||
override val parameters: List<FunctionParameterStub>,
|
||||
override val origin: StubOrigin,
|
||||
override val annotations: List<AnnotationStub>,
|
||||
val external: Boolean = false,
|
||||
val receiver: ReceiverParameterStub?,
|
||||
val modality: MemberStubModality,
|
||||
val typeParameters: List<TypeParameterStub> = emptyList()
|
||||
) : StubElementWithOrigin, FunctionalStub {
|
||||
|
||||
override fun <T, R> accept(visitor: StubIrVisitor<T, R>, data: T) =
|
||||
visitor.visitFunction(this, data)
|
||||
}
|
||||
|
||||
// TODO: should we support non-trivial constructors?
|
||||
class ConstructorStub(
|
||||
override val parameters: List<FunctionParameterStub>,
|
||||
override val annotations: List<AnnotationStub>,
|
||||
val visibility: VisibilityModifier = VisibilityModifier.PUBLIC
|
||||
) : FunctionalStub {
|
||||
|
||||
override fun <T, R> accept(visitor: StubIrVisitor<T, R>, data: T) =
|
||||
visitor.visitConstructor(this, data)
|
||||
}
|
||||
|
||||
class EnumEntryStub(
|
||||
val name: String,
|
||||
val constant: IntegralConstantStub,
|
||||
val aliases: List<Alias>
|
||||
) {
|
||||
class Alias(val name: String)
|
||||
}
|
||||
|
||||
class TypealiasStub(
|
||||
val alias: ClassifierStubType,
|
||||
val aliasee: StubType
|
||||
) : StubIrElement {
|
||||
|
||||
override fun <T, R> accept(visitor: StubIrVisitor<T, R>, data: T) =
|
||||
visitor.visitTypealias(this, data)
|
||||
}
|
||||
+280
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
package org.jetbrains.kotlin.native.interop.gen
|
||||
|
||||
import org.jetbrains.kotlin.native.interop.gen.jvm.KotlinPlatform
|
||||
import org.jetbrains.kotlin.native.interop.indexer.ObjCProtocol
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
|
||||
class BridgeBuilderResult(
|
||||
val kotlinFile: KotlinFile,
|
||||
val nativeBridges: NativeBridges,
|
||||
val propertyAccessorBridgeBodies: Map<PropertyAccessor, String>,
|
||||
val functionBridgeBodies: Map<FunctionStub, List<String>>
|
||||
)
|
||||
|
||||
/**
|
||||
* Generates [NativeBridges] and corresponding function bodies and property accessors.
|
||||
*/
|
||||
class StubIrBridgeBuilder(
|
||||
private val context: StubIrContext,
|
||||
private val builderResult: StubIrBuilderResult) {
|
||||
|
||||
private val globalAddressExpressions = mutableMapOf<Pair<String, PropertyAccessor>, KotlinExpression>()
|
||||
|
||||
private fun getGlobalAddressExpression(cGlobalName: String, accessor: PropertyAccessor) =
|
||||
globalAddressExpressions.getOrPut(Pair(cGlobalName, accessor)) {
|
||||
simpleBridgeGenerator.kotlinToNative(
|
||||
nativeBacked = accessor,
|
||||
returnType = BridgedType.NATIVE_PTR,
|
||||
kotlinValues = emptyList(),
|
||||
independent = false
|
||||
) {
|
||||
"&$cGlobalName"
|
||||
}
|
||||
}
|
||||
|
||||
private val declarationMapper = builderResult.declarationMapper
|
||||
|
||||
private val kotlinFile = object : KotlinFile(
|
||||
context.configuration.pkgName,
|
||||
namesToBeDeclared = builderResult.stubs.computeNamesToBeDeclared(context.configuration.pkgName)
|
||||
) {
|
||||
override val mappingBridgeGenerator: MappingBridgeGenerator
|
||||
get() = this@StubIrBridgeBuilder.mappingBridgeGenerator
|
||||
}
|
||||
|
||||
private val simpleBridgeGenerator: SimpleBridgeGenerator =
|
||||
SimpleBridgeGeneratorImpl(
|
||||
context.platform,
|
||||
context.configuration.pkgName,
|
||||
context.jvmFileClassName,
|
||||
context.libraryForCStubs,
|
||||
topLevelNativeScope = object : NativeScope {
|
||||
override val mappingBridgeGenerator: MappingBridgeGenerator
|
||||
get() = this@StubIrBridgeBuilder.mappingBridgeGenerator
|
||||
},
|
||||
topLevelKotlinScope = kotlinFile
|
||||
)
|
||||
|
||||
private val mappingBridgeGenerator: MappingBridgeGenerator =
|
||||
MappingBridgeGeneratorImpl(declarationMapper, simpleBridgeGenerator)
|
||||
|
||||
private val propertyAccessorBridgeBodies = mutableMapOf<PropertyAccessor, String>()
|
||||
private val functionBridgeBodies = mutableMapOf<FunctionStub, List<String>>()
|
||||
|
||||
private val bridgeGeneratingVisitor = object : StubIrVisitor<StubContainer?, Unit> {
|
||||
override fun visitClass(element: ClassStub, owner: StubContainer?) {
|
||||
element.annotations.filterIsInstance<AnnotationStub.ObjC.ExternalClass>().firstOrNull()?.let {
|
||||
if (it.protocolGetter.isNotEmpty() && element.origin is StubOrigin.ObjCProtocol) {
|
||||
val protocol = (element.origin as StubOrigin.ObjCProtocol).protocol
|
||||
// TODO: handle the case when protocol getter stub can't be compiled.
|
||||
generateProtocolGetter(it.protocolGetter, protocol)
|
||||
}
|
||||
}
|
||||
element.children.forEach {
|
||||
it.accept(this, element)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitTypealias(element: TypealiasStub, owner: StubContainer?) {
|
||||
}
|
||||
|
||||
override fun visitFunction(element: FunctionStub, owner: StubContainer?) {
|
||||
when {
|
||||
element.external -> tryProcessCCallAnnotation(element)
|
||||
element.isOptionalObjCMethod() -> {}
|
||||
owner != null && owner.isInterface -> {}
|
||||
else -> generateBridgeBody(element)
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryProcessCCallAnnotation(function: FunctionStub) {
|
||||
val origin = function.origin as? StubOrigin.Function
|
||||
?: return
|
||||
val cCallAnnotation = function.annotations.firstIsInstanceOrNull<AnnotationStub.CCall.Symbol>()
|
||||
?: return
|
||||
val cCallSymbolName = cCallAnnotation.symbolName
|
||||
simpleBridgeGenerator.insertNativeBridge(
|
||||
function,
|
||||
emptyList(),
|
||||
listOf("extern const void* $cCallSymbolName __asm(${cCallSymbolName.quoteAsKotlinLiteral()});",
|
||||
"extern const void* $cCallSymbolName = &${origin.function.name};")
|
||||
)
|
||||
}
|
||||
|
||||
override fun visitProperty(element: PropertyStub, owner: StubContainer?) {
|
||||
when (val kind = element.kind) {
|
||||
is PropertyStub.Kind.Constant -> { }
|
||||
is PropertyStub.Kind.Val -> {
|
||||
visitPropertyAccessor(kind.getter, owner)
|
||||
}
|
||||
is PropertyStub.Kind.Var -> {
|
||||
visitPropertyAccessor(kind.getter, owner)
|
||||
visitPropertyAccessor(kind.setter, owner)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitConstructor(constructorStub: ConstructorStub, owner: StubContainer?) {
|
||||
}
|
||||
|
||||
override fun visitPropertyAccessor(accessor: PropertyAccessor, owner: StubContainer?) {
|
||||
when (accessor) {
|
||||
is PropertyAccessor.Getter.SimpleGetter -> {
|
||||
if (accessor in builderResult.bridgeGenerationComponents.getterToBridgeInfo) {
|
||||
val extra = builderResult.bridgeGenerationComponents.getterToBridgeInfo.getValue(accessor)
|
||||
val typeInfo = extra.typeInfo
|
||||
val expression = if (extra.isArray) {
|
||||
val getAddressExpression = getGlobalAddressExpression(extra.cGlobalName, accessor)
|
||||
typeInfo.argFromBridged(getAddressExpression, kotlinFile, nativeBacked = accessor) + "!!"
|
||||
} else {
|
||||
typeInfo.argFromBridged(simpleBridgeGenerator.kotlinToNative(
|
||||
nativeBacked = accessor,
|
||||
returnType = typeInfo.bridgedType,
|
||||
kotlinValues = emptyList(),
|
||||
independent = false
|
||||
) {
|
||||
typeInfo.cToBridged(expr = extra.cGlobalName)
|
||||
}, kotlinFile, nativeBacked = accessor)
|
||||
}
|
||||
propertyAccessorBridgeBodies[accessor] = expression
|
||||
}
|
||||
}
|
||||
|
||||
is PropertyAccessor.Getter.ReadBits -> {
|
||||
val extra = builderResult.bridgeGenerationComponents.getterToBridgeInfo.getValue(accessor)
|
||||
val rawType = extra.typeInfo.bridgedType
|
||||
val readBits = "readBits(this.rawPtr, ${accessor.offset}, ${accessor.size}, ${accessor.signed}).${rawType.convertor!!}()"
|
||||
val getExpr = extra.typeInfo.argFromBridged(readBits, kotlinFile, object : NativeBacked {})
|
||||
propertyAccessorBridgeBodies[accessor] = getExpr
|
||||
}
|
||||
|
||||
is PropertyAccessor.Setter.SimpleSetter -> when (accessor) {
|
||||
in builderResult.bridgeGenerationComponents.setterToBridgeInfo -> {
|
||||
val extra = builderResult.bridgeGenerationComponents.setterToBridgeInfo.getValue(accessor)
|
||||
val typeInfo = extra.typeInfo
|
||||
val bridgedValue = BridgeTypedKotlinValue(typeInfo.bridgedType, typeInfo.argToBridged("value"))
|
||||
val setter = simpleBridgeGenerator.kotlinToNative(
|
||||
nativeBacked = accessor,
|
||||
returnType = BridgedType.VOID,
|
||||
kotlinValues = listOf(bridgedValue),
|
||||
independent = false
|
||||
) { nativeValues ->
|
||||
out("${extra.cGlobalName} = ${typeInfo.cFromBridged(
|
||||
nativeValues.single(),
|
||||
scope,
|
||||
nativeBacked = accessor
|
||||
)};")
|
||||
""
|
||||
}
|
||||
propertyAccessorBridgeBodies[accessor] = setter
|
||||
}
|
||||
}
|
||||
|
||||
is PropertyAccessor.Setter.WriteBits -> {
|
||||
val extra = builderResult.bridgeGenerationComponents.setterToBridgeInfo.getValue(accessor)
|
||||
val rawValue = extra.typeInfo.argToBridged("value")
|
||||
propertyAccessorBridgeBodies[accessor] = "writeBits(this.rawPtr, ${accessor.offset}, ${accessor.size}, $rawValue.toLong())"
|
||||
}
|
||||
|
||||
is PropertyAccessor.Getter.InterpretPointed -> {
|
||||
val getAddressExpression = getGlobalAddressExpression(accessor.cGlobalName, accessor)
|
||||
propertyAccessorBridgeBodies[accessor] = getAddressExpression
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitSimpleStubContainer(simpleStubContainer: SimpleStubContainer, owner: StubContainer?) {
|
||||
simpleStubContainer.classes.forEach {
|
||||
it.accept(this, simpleStubContainer)
|
||||
}
|
||||
simpleStubContainer.functions.forEach {
|
||||
it.accept(this, simpleStubContainer)
|
||||
}
|
||||
simpleStubContainer.properties.forEach {
|
||||
it.accept(this, simpleStubContainer)
|
||||
}
|
||||
simpleStubContainer.typealiases.forEach {
|
||||
it.accept(this, simpleStubContainer)
|
||||
}
|
||||
simpleStubContainer.simpleContainers.forEach {
|
||||
it.accept(this, simpleStubContainer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isCValuesRef(type: StubType): Boolean {
|
||||
if (type !is WrapperStubType) return false
|
||||
|
||||
return type.kotlinType is KotlinClassifierType && type.kotlinType.classifier == KotlinTypes.cValuesRef
|
||||
}
|
||||
|
||||
private fun generateBridgeBody(function: FunctionStub) {
|
||||
assert(function.origin is StubOrigin.Function) { "Can't create bridge for ${function.name}" }
|
||||
val origin = function.origin as StubOrigin.Function
|
||||
val bodyGenerator = KotlinCodeBuilder(scope = kotlinFile)
|
||||
val bridgeArguments = mutableListOf<TypedKotlinValue>()
|
||||
var isVararg = false
|
||||
function.parameters.forEachIndexed { index, parameter ->
|
||||
isVararg = isVararg or parameter.isVararg
|
||||
val parameterName = parameter.name.asSimpleName()
|
||||
val bridgeArgument = when {
|
||||
parameter in builderResult.bridgeGenerationComponents.cStringParameters -> {
|
||||
bodyGenerator.pushMemScoped()
|
||||
"$parameterName?.cstr?.getPointer(memScope)"
|
||||
}
|
||||
parameter in builderResult.bridgeGenerationComponents.wCStringParameters -> {
|
||||
bodyGenerator.pushMemScoped()
|
||||
"$parameterName?.wcstr?.getPointer(memScope)"
|
||||
}
|
||||
isCValuesRef(parameter.type) -> {
|
||||
bodyGenerator.pushMemScoped()
|
||||
bodyGenerator.getNativePointer(parameterName)
|
||||
}
|
||||
else -> {
|
||||
parameterName
|
||||
}
|
||||
}
|
||||
bridgeArguments += TypedKotlinValue(origin.function.parameters[index].type, bridgeArgument)
|
||||
}
|
||||
// TODO: Improve assertion message.
|
||||
assert(!isVararg || context.platform != KotlinPlatform.NATIVE) {
|
||||
"Function ${function.name} was processed incorrectly."
|
||||
}
|
||||
val result = mappingBridgeGenerator.kotlinToNative(
|
||||
bodyGenerator,
|
||||
function,
|
||||
origin.function.returnType,
|
||||
bridgeArguments,
|
||||
independent = false
|
||||
) { nativeValues ->
|
||||
"${origin.function.name}(${nativeValues.joinToString()})"
|
||||
}
|
||||
bodyGenerator.returnResult(result)
|
||||
functionBridgeBodies[function] = bodyGenerator.build()
|
||||
}
|
||||
|
||||
private fun generateProtocolGetter(protocolGetterName: String, protocol: ObjCProtocol) {
|
||||
val builder = NativeCodeBuilder(simpleBridgeGenerator.topLevelNativeScope)
|
||||
val nativeBacked = object : NativeBacked {}
|
||||
with(builder) {
|
||||
out("Protocol* $protocolGetterName() {")
|
||||
out(" return @protocol(${protocol.name});")
|
||||
out("}")
|
||||
}
|
||||
simpleBridgeGenerator.insertNativeBridge(nativeBacked, emptyList(), builder.lines)
|
||||
}
|
||||
|
||||
fun build(): BridgeBuilderResult {
|
||||
bridgeGeneratingVisitor.visitSimpleStubContainer(builderResult.stubs, null)
|
||||
return BridgeBuilderResult(
|
||||
kotlinFile,
|
||||
simpleBridgeGenerator.prepare(),
|
||||
propertyAccessorBridgeBodies.toMap(),
|
||||
functionBridgeBodies.toMap()
|
||||
)
|
||||
}
|
||||
}
|
||||
+368
@@ -0,0 +1,368 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
package org.jetbrains.kotlin.native.interop.gen
|
||||
|
||||
import org.jetbrains.kotlin.native.interop.gen.jvm.InteropConfiguration
|
||||
import org.jetbrains.kotlin.native.interop.gen.jvm.KotlinPlatform
|
||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||
|
||||
/**
|
||||
* Additional components that are required to generate bridges.
|
||||
*/
|
||||
interface BridgeGenerationComponents {
|
||||
|
||||
class GlobalSetterBridgeInfo(
|
||||
val cGlobalName: String,
|
||||
val typeInfo: TypeInfo
|
||||
)
|
||||
|
||||
class GlobalGetterBridgeInfo(
|
||||
val cGlobalName: String,
|
||||
val typeInfo: TypeInfo,
|
||||
val isArray: Boolean
|
||||
)
|
||||
|
||||
val setterToBridgeInfo: Map<PropertyAccessor.Setter, GlobalSetterBridgeInfo>
|
||||
|
||||
val getterToBridgeInfo: Map<PropertyAccessor.Getter, GlobalGetterBridgeInfo>
|
||||
|
||||
val enumToTypeMirror: Map<ClassStub.Enum, TypeMirror>
|
||||
|
||||
val wCStringParameters: Set<FunctionParameterStub>
|
||||
|
||||
val cStringParameters: Set<FunctionParameterStub>
|
||||
}
|
||||
|
||||
class BridgeGenerationComponentsBuilder(
|
||||
val getterToBridgeInfo: MutableMap<PropertyAccessor.Getter, BridgeGenerationComponents.GlobalGetterBridgeInfo> = mutableMapOf(),
|
||||
val setterToBridgeInfo: MutableMap<PropertyAccessor.Setter, BridgeGenerationComponents.GlobalSetterBridgeInfo> = mutableMapOf(),
|
||||
val enumToTypeMirror: MutableMap<ClassStub.Enum, TypeMirror> = mutableMapOf(),
|
||||
val wCStringParameters: MutableSet<FunctionParameterStub> = mutableSetOf(),
|
||||
val cStringParameters: MutableSet<FunctionParameterStub> = mutableSetOf()
|
||||
) {
|
||||
fun build(): BridgeGenerationComponents = object : BridgeGenerationComponents {
|
||||
override val getterToBridgeInfo =
|
||||
this@BridgeGenerationComponentsBuilder.getterToBridgeInfo.toMap()
|
||||
|
||||
override val setterToBridgeInfo =
|
||||
this@BridgeGenerationComponentsBuilder.setterToBridgeInfo.toMap()
|
||||
|
||||
override val enumToTypeMirror =
|
||||
this@BridgeGenerationComponentsBuilder.enumToTypeMirror.toMap()
|
||||
|
||||
override val wCStringParameters: Set<FunctionParameterStub> =
|
||||
this@BridgeGenerationComponentsBuilder.wCStringParameters.toSet()
|
||||
|
||||
override val cStringParameters: Set<FunctionParameterStub> =
|
||||
this@BridgeGenerationComponentsBuilder.cStringParameters.toSet()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Common part of all [StubIrBuilder] implementations.
|
||||
*/
|
||||
interface StubsBuildingContext {
|
||||
val configuration: InteropConfiguration
|
||||
|
||||
fun mirror(type: Type): TypeMirror
|
||||
|
||||
val declarationMapper: DeclarationMapper
|
||||
|
||||
fun generateNextUniqueId(prefix: String): String
|
||||
|
||||
val generatedObjCCategoriesMembers: MutableMap<ObjCClass, GeneratedObjCCategoriesMembers>
|
||||
|
||||
val platform: KotlinPlatform
|
||||
|
||||
fun isStrictEnum(enumDef: EnumDef): Boolean
|
||||
|
||||
val macroConstantsByName: Map<String, MacroDef>
|
||||
|
||||
fun tryCreateIntegralStub(type: Type, value: Long): IntegralConstantStub?
|
||||
|
||||
fun tryCreateDoubleStub(type: Type, value: Double): DoubleConstantStub?
|
||||
|
||||
val bridgeComponentsBuilder: BridgeGenerationComponentsBuilder
|
||||
|
||||
fun getKotlinClassFor(objCClassOrProtocol: ObjCClassOrProtocol, isMeta: Boolean = false): Classifier
|
||||
|
||||
fun getKotlinClassForPointed(structDecl: StructDecl): Classifier
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
internal interface StubElementBuilder {
|
||||
val context: StubsBuildingContext
|
||||
|
||||
fun build(): List<StubIrElement>
|
||||
}
|
||||
|
||||
class StubsBuildingContextImpl(
|
||||
private val stubIrContext: StubIrContext
|
||||
) : StubsBuildingContext {
|
||||
|
||||
override val configuration: InteropConfiguration = stubIrContext.configuration
|
||||
override val platform: KotlinPlatform = stubIrContext.platform
|
||||
val imports: Imports = stubIrContext.imports
|
||||
private val nativeIndex: NativeIndex = stubIrContext.nativeIndex
|
||||
|
||||
private var theCounter = 0
|
||||
|
||||
override fun generateNextUniqueId(prefix: String) =
|
||||
prefix + pkgName.replace('.', '_') + theCounter++
|
||||
|
||||
override fun mirror(type: Type): TypeMirror = mirror(declarationMapper, type)
|
||||
|
||||
/**
|
||||
* Indicates whether this enum should be represented as Kotlin enum.
|
||||
*/
|
||||
|
||||
override fun isStrictEnum(enumDef: EnumDef): Boolean = with(enumDef) {
|
||||
if (this.isAnonymous) {
|
||||
return false
|
||||
}
|
||||
|
||||
val name = this.kotlinName
|
||||
|
||||
if (name in configuration.strictEnums) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (name in configuration.nonStrictEnums) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Let the simple heuristic decide:
|
||||
return !this.constants.any { it.isExplicitlyDefined }
|
||||
}
|
||||
|
||||
override val generatedObjCCategoriesMembers = mutableMapOf<ObjCClass, GeneratedObjCCategoriesMembers>()
|
||||
|
||||
override val declarationMapper = object : DeclarationMapper {
|
||||
override fun getKotlinClassForPointed(structDecl: StructDecl): Classifier {
|
||||
val baseName = structDecl.kotlinName
|
||||
val pkg = when (platform) {
|
||||
KotlinPlatform.JVM -> pkgName
|
||||
KotlinPlatform.NATIVE -> if (structDecl.def == null) {
|
||||
cnamesStructsPackageName // to be imported as forward declaration.
|
||||
} else {
|
||||
getPackageFor(structDecl)
|
||||
}
|
||||
}
|
||||
return Classifier.topLevel(pkg, baseName)
|
||||
}
|
||||
|
||||
override fun isMappedToStrict(enumDef: EnumDef): Boolean = isStrictEnum(enumDef)
|
||||
|
||||
override fun getKotlinNameForValue(enumDef: EnumDef): String = enumDef.kotlinName
|
||||
|
||||
override fun getPackageFor(declaration: TypeDeclaration): String {
|
||||
return imports.getPackage(declaration.location) ?: pkgName
|
||||
}
|
||||
|
||||
override val useUnsignedTypes: Boolean
|
||||
get() = when (platform) {
|
||||
KotlinPlatform.JVM -> false
|
||||
KotlinPlatform.NATIVE -> true
|
||||
}
|
||||
}
|
||||
|
||||
override val macroConstantsByName: Map<String, MacroDef> =
|
||||
(nativeIndex.macroConstants + nativeIndex.wrappedMacros).associateBy { it.name }
|
||||
|
||||
/**
|
||||
* The name to be used for this enum in Kotlin
|
||||
*/
|
||||
val EnumDef.kotlinName: String
|
||||
get() = if (spelling.startsWith("enum ")) {
|
||||
spelling.substringAfter(' ')
|
||||
} else {
|
||||
assert (!isAnonymous)
|
||||
spelling
|
||||
}
|
||||
|
||||
|
||||
private val pkgName: String
|
||||
get() = configuration.pkgName
|
||||
|
||||
/**
|
||||
* The name to be used for this struct in Kotlin
|
||||
*/
|
||||
val StructDecl.kotlinName: String
|
||||
get() = stubIrContext.getKotlinName(this)
|
||||
|
||||
override fun tryCreateIntegralStub(type: Type, value: Long): IntegralConstantStub? {
|
||||
val integerType = type.unwrapTypedefs() as? IntegerType ?: return null
|
||||
val size = integerType.size
|
||||
if (size != 1 && size != 2 && size != 4 && size != 8) return null
|
||||
return IntegralConstantStub(value, size, declarationMapper.isMappedToSigned(integerType))
|
||||
}
|
||||
|
||||
override fun tryCreateDoubleStub(type: Type, value: Double): DoubleConstantStub? {
|
||||
val unwrappedType = type.unwrapTypedefs() as? FloatingType ?: return null
|
||||
val size = unwrappedType.size
|
||||
if (size != 4 && size != 8) return null
|
||||
return DoubleConstantStub(value, size)
|
||||
}
|
||||
|
||||
override val bridgeComponentsBuilder = BridgeGenerationComponentsBuilder()
|
||||
|
||||
override fun getKotlinClassFor(objCClassOrProtocol: ObjCClassOrProtocol, isMeta: Boolean): Classifier {
|
||||
return declarationMapper.getKotlinClassFor(objCClassOrProtocol, isMeta)
|
||||
}
|
||||
|
||||
override fun getKotlinClassForPointed(structDecl: StructDecl): Classifier {
|
||||
val classifier = declarationMapper.getKotlinClassForPointed(structDecl)
|
||||
return classifier
|
||||
}
|
||||
}
|
||||
|
||||
data class StubIrBuilderResult(
|
||||
val stubs: SimpleStubContainer,
|
||||
val declarationMapper: DeclarationMapper,
|
||||
val bridgeGenerationComponents: BridgeGenerationComponents
|
||||
)
|
||||
|
||||
/**
|
||||
* Produces [StubIrBuilderResult] for given [KotlinPlatform] using [InteropConfiguration].
|
||||
*/
|
||||
class StubIrBuilder(
|
||||
private val context: StubIrContext,
|
||||
private val verbose: Boolean = false
|
||||
) {
|
||||
|
||||
private val configuration = context.configuration
|
||||
private val nativeIndex: NativeIndex = context.nativeIndex
|
||||
|
||||
private val classes = mutableListOf<ClassStub>()
|
||||
private val functions = mutableListOf<FunctionStub>()
|
||||
private val globals = mutableListOf<PropertyStub>()
|
||||
private val typealiases = mutableListOf<TypealiasStub>()
|
||||
private val containers = mutableListOf<SimpleStubContainer>()
|
||||
|
||||
private fun addStubs(stubs: List<StubIrElement>) = stubs.forEach(this::addStub)
|
||||
|
||||
private fun addStub(stub: StubIrElement) {
|
||||
when(stub) {
|
||||
is ClassStub -> classes += stub
|
||||
is FunctionStub -> functions += stub
|
||||
is PropertyStub -> globals += stub
|
||||
is TypealiasStub -> typealiases += stub
|
||||
is SimpleStubContainer -> containers += stub
|
||||
else -> error("Unexpected stub: $stub")
|
||||
}
|
||||
}
|
||||
|
||||
private val excludedFunctions: Set<String>
|
||||
get() = configuration.excludedFunctions
|
||||
|
||||
private val excludedMacros: Set<String>
|
||||
get() = configuration.excludedMacros
|
||||
|
||||
private val buildingContext = StubsBuildingContextImpl(context)
|
||||
|
||||
fun build(): StubIrBuilderResult {
|
||||
nativeIndex.objCProtocols.filter { !it.isForwardDeclaration }.forEach { generateStubsForObjCProtocol(it) }
|
||||
nativeIndex.objCClasses.filter { !it.isForwardDeclaration && !it.isNSStringSubclass()} .forEach { generateStubsForObjCClass(it) }
|
||||
nativeIndex.objCCategories.filter { !it.clazz.isNSStringSubclass() }.forEach { generateStubsForObjCCategory(it) }
|
||||
nativeIndex.structs.forEach { generateStubsForStruct(it) }
|
||||
nativeIndex.enums.forEach { generateStubsForEnum(it) }
|
||||
nativeIndex.functions.filter { it.name !in excludedFunctions }.forEach { generateStubsForFunction(it) }
|
||||
nativeIndex.typedefs.forEach { generateStubsForTypedef(it) }
|
||||
nativeIndex.globals.filter { it.name !in excludedFunctions }.forEach { generateStubsForGlobal(it) }
|
||||
nativeIndex.macroConstants.filter { it.name !in excludedMacros }.forEach { generateStubsForMacroConstant(it) }
|
||||
nativeIndex.wrappedMacros.filter { it.name !in excludedMacros }.forEach { generateStubsForWrappedMacro(it) }
|
||||
|
||||
val meta = StubContainerMeta()
|
||||
val stubs = SimpleStubContainer(
|
||||
meta,
|
||||
classes.toList(),
|
||||
functions.toList(),
|
||||
globals.toList(),
|
||||
typealiases.toList(),
|
||||
containers.toList()
|
||||
)
|
||||
return StubIrBuilderResult(
|
||||
stubs,
|
||||
buildingContext.declarationMapper,
|
||||
buildingContext.bridgeComponentsBuilder.build()
|
||||
)
|
||||
}
|
||||
|
||||
private fun log(message: String) {
|
||||
if (verbose) {
|
||||
println(message)
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateStubsForWrappedMacro(macro: WrappedMacroDef) {
|
||||
try {
|
||||
generateStubsForGlobal(GlobalDecl(macro.name, macro.type, isConst = true))
|
||||
} catch (e: Throwable) {
|
||||
log("Warning: cannot generate stubs for macro ${macro.name}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateStubsForMacroConstant(constant: ConstantDef) {
|
||||
try {
|
||||
addStubs(MacroConstantStubBuilder(buildingContext, constant).build())
|
||||
} catch (e: Throwable) {
|
||||
log("Warning: cannot generate stubs for constant ${constant.name}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateStubsForEnum(enumDef: EnumDef) {
|
||||
try {
|
||||
addStubs(EnumStubBuilder(buildingContext, enumDef).build())
|
||||
} catch (e: Throwable) {
|
||||
log("Warning: cannot generate definition for enum ${enumDef.spelling}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateStubsForFunction(func: FunctionDecl) {
|
||||
try {
|
||||
addStubs(FunctionStubBuilder(buildingContext, func).build())
|
||||
} catch (e: Throwable) {
|
||||
log("Warning: cannot generate stubs for function ${func.name}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateStubsForStruct(decl: StructDecl) {
|
||||
try {
|
||||
addStubs(StructStubBuilder(buildingContext, decl).build())
|
||||
} catch (e: Throwable) {
|
||||
log("Warning: cannot generate definition for struct ${decl.spelling}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateStubsForTypedef(typedefDef: TypedefDef) {
|
||||
try {
|
||||
addStubs(TypedefStubBuilder(buildingContext, typedefDef).build())
|
||||
} catch (e: Throwable) {
|
||||
log("Warning: cannot generate typedef ${typedefDef.name}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateStubsForGlobal(global: GlobalDecl) {
|
||||
try {
|
||||
addStubs(GlobalStubBuilder(buildingContext, global).build())
|
||||
} catch (e: Throwable) {
|
||||
log("Warning: cannot generate stubs for global ${global.name}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateStubsForObjCProtocol(objCProtocol: ObjCProtocol) {
|
||||
addStubs(ObjCProtocolStubBuilder(buildingContext, objCProtocol).build())
|
||||
}
|
||||
|
||||
private fun generateStubsForObjCClass(objCClass: ObjCClass) {
|
||||
addStubs(ObjCClassStubBuilder(buildingContext, objCClass).build())
|
||||
}
|
||||
|
||||
private fun generateStubsForObjCCategory(objCCategory: ObjCCategory) {
|
||||
addStubs(ObjCCategoryStubBuilder(buildingContext, objCCategory).build())
|
||||
}
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
package org.jetbrains.kotlin.native.interop.gen
|
||||
|
||||
import org.jetbrains.kotlin.native.interop.gen.jvm.InteropConfiguration
|
||||
import org.jetbrains.kotlin.native.interop.gen.jvm.KotlinPlatform
|
||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
class StubIrContext(
|
||||
val configuration: InteropConfiguration,
|
||||
val nativeIndex: NativeIndex,
|
||||
val imports: Imports,
|
||||
val platform: KotlinPlatform,
|
||||
val libName: String
|
||||
) {
|
||||
val libraryForCStubs = configuration.library.copy(
|
||||
includes = mutableListOf<String>().apply {
|
||||
add("stdint.h")
|
||||
add("string.h")
|
||||
if (platform == KotlinPlatform.JVM) {
|
||||
add("jni.h")
|
||||
}
|
||||
addAll(configuration.library.includes)
|
||||
},
|
||||
compilerArgs = configuration.library.compilerArgs,
|
||||
additionalPreambleLines = configuration.library.additionalPreambleLines +
|
||||
when (configuration.library.language) {
|
||||
Language.C -> emptyList()
|
||||
Language.OBJECTIVE_C -> listOf("void objc_terminate();")
|
||||
}
|
||||
).precompileHeaders()
|
||||
|
||||
val jvmFileClassName = if (configuration.pkgName.isEmpty()) {
|
||||
libName
|
||||
} else {
|
||||
configuration.pkgName.substringAfterLast('.')
|
||||
}
|
||||
|
||||
private val anonymousStructKotlinNames = mutableMapOf<StructDecl, String>()
|
||||
|
||||
private val forbiddenStructNames = run {
|
||||
val typedefNames = nativeIndex.typedefs.map { it.name }
|
||||
typedefNames.toSet()
|
||||
}
|
||||
|
||||
/**
|
||||
* The name to be used for this struct in Kotlin
|
||||
*/
|
||||
fun getKotlinName(decl: StructDecl): String {
|
||||
val spelling = decl.spelling
|
||||
if (decl.isAnonymous) {
|
||||
val names = anonymousStructKotlinNames
|
||||
return names.getOrPut(decl) {
|
||||
"anonymousStruct${names.size + 1}"
|
||||
}
|
||||
}
|
||||
|
||||
val strippedCName = if (spelling.startsWith("struct ") || spelling.startsWith("union ")) {
|
||||
spelling.substringAfter(' ')
|
||||
} else {
|
||||
spelling
|
||||
}
|
||||
|
||||
// TODO: don't mangle struct names because it wouldn't work if the struct
|
||||
// is imported into another interop library.
|
||||
return if (strippedCName !in forbiddenStructNames) strippedCName else (strippedCName + "Struct")
|
||||
}
|
||||
|
||||
fun addManifestProperties(properties: Properties) {
|
||||
val exportForwardDeclarations = configuration.exportForwardDeclarations.toMutableList()
|
||||
|
||||
nativeIndex.structs
|
||||
.filter { it.def == null }
|
||||
.mapTo(exportForwardDeclarations) {
|
||||
"$cnamesStructsPackageName.${getKotlinName(it)}"
|
||||
}
|
||||
|
||||
properties["exportForwardDeclarations"] = exportForwardDeclarations.joinToString(" ")
|
||||
|
||||
// TODO: consider exporting Objective-C class and protocol forward refs.
|
||||
}
|
||||
}
|
||||
|
||||
class StubIrDriver(
|
||||
private val context: StubIrContext,
|
||||
private val verbose: Boolean = false
|
||||
) {
|
||||
fun run(outKtFile: File, outCFile: File, entryPoint: String?) {
|
||||
val builderResult = StubIrBuilder(context, verbose).build()
|
||||
val bridgeBuilderResult = StubIrBridgeBuilder(context, builderResult).build()
|
||||
outKtFile.bufferedWriter().use { ktFile ->
|
||||
File(outCFile.absolutePath).bufferedWriter().use { cFile ->
|
||||
StubIrTextEmitter(
|
||||
context,
|
||||
builderResult,
|
||||
bridgeBuilderResult
|
||||
).emit(ktFile, cFile, entryPoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+499
@@ -0,0 +1,499 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
package org.jetbrains.kotlin.native.interop.gen
|
||||
|
||||
import org.jetbrains.kotlin.native.interop.gen.jvm.KotlinPlatform
|
||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||
|
||||
internal class MacroConstantStubBuilder(
|
||||
override val context: StubsBuildingContext,
|
||||
private val constant: ConstantDef
|
||||
) : StubElementBuilder {
|
||||
override fun build(): List<StubIrElement> {
|
||||
val kotlinName = constant.name
|
||||
val declaration = when (constant) {
|
||||
is IntegerConstantDef -> {
|
||||
val literal = context.tryCreateIntegralStub(constant.type, constant.value) ?: return emptyList()
|
||||
val kotlinType = WrapperStubType(context.mirror(constant.type).argType)
|
||||
when (context.platform) {
|
||||
KotlinPlatform.NATIVE -> PropertyStub(kotlinName, kotlinType, PropertyStub.Kind.Constant(literal))
|
||||
// No reason to make it const val with backing field on Kotlin/JVM yet:
|
||||
KotlinPlatform.JVM -> {
|
||||
val getter = PropertyAccessor.Getter.SimpleGetter(constant = literal)
|
||||
PropertyStub(kotlinName, kotlinType, PropertyStub.Kind.Val(getter))
|
||||
}
|
||||
}
|
||||
}
|
||||
is FloatingConstantDef -> {
|
||||
val literal = context.tryCreateDoubleStub(constant.type, constant.value) ?: return emptyList()
|
||||
val kotlinType = WrapperStubType(context.mirror(constant.type).argType)
|
||||
val getter = PropertyAccessor.Getter.SimpleGetter(constant = literal)
|
||||
PropertyStub(kotlinName, kotlinType, PropertyStub.Kind.Val(getter))
|
||||
}
|
||||
is StringConstantDef -> {
|
||||
val literal = StringConstantStub(constant.value.quoteAsKotlinLiteral())
|
||||
val kotlinType = WrapperStubType(KotlinTypes.string)
|
||||
val getter = PropertyAccessor.Getter.SimpleGetter(constant = literal)
|
||||
PropertyStub(kotlinName, kotlinType, PropertyStub.Kind.Val(getter))
|
||||
}
|
||||
else -> return emptyList()
|
||||
}
|
||||
return listOf(declaration)
|
||||
}
|
||||
}
|
||||
|
||||
internal class StructStubBuilder(
|
||||
override val context: StubsBuildingContext,
|
||||
private val decl: StructDecl
|
||||
) : StubElementBuilder {
|
||||
override fun build(): List<StubIrElement> {
|
||||
val platform = context.platform
|
||||
val def = decl.def ?: return generateForwardStruct(decl)
|
||||
|
||||
val structAnnotation: AnnotationStub? = if (platform == KotlinPlatform.JVM) {
|
||||
if (def.kind == StructDef.Kind.STRUCT && def.fieldsHaveDefaultAlignment()) {
|
||||
AnnotationStub.CNaturalStruct(def.members)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} else {
|
||||
tryRenderStructOrUnion(def)?.let {
|
||||
AnnotationStub.CStruct(it)
|
||||
}
|
||||
}
|
||||
val classifier = context.getKotlinClassForPointed(decl)
|
||||
|
||||
val fields: List<PropertyStub?> = def.fields.map { field ->
|
||||
try {
|
||||
assert(field.name.isNotEmpty())
|
||||
assert(field.offset % 8 == 0L)
|
||||
val offset = field.offset / 8
|
||||
val fieldRefType = context.mirror(field.type)
|
||||
val unwrappedFieldType = field.type.unwrapTypedefs()
|
||||
if (unwrappedFieldType is ArrayType) {
|
||||
val type = (fieldRefType as TypeMirror.ByValue).valueType
|
||||
val annotations = if (platform == KotlinPlatform.JVM) {
|
||||
val length = getArrayLength(unwrappedFieldType)
|
||||
// TODO: @CLength should probably be used on types instead of properties.
|
||||
listOf(AnnotationStub.CLength(length))
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
val kind = PropertyStub.Kind.Val(PropertyAccessor.Getter.ArrayMemberAt(offset))
|
||||
// TODO: Should receiver be added?
|
||||
PropertyStub(field.name, WrapperStubType(type), kind, annotations = annotations)
|
||||
} else {
|
||||
val pointedType = WrapperStubType(fieldRefType.pointedType)
|
||||
val pointedTypeArgument = TypeArgumentStub(pointedType)
|
||||
if (fieldRefType is TypeMirror.ByValue) {
|
||||
val kind = PropertyStub.Kind.Var(
|
||||
PropertyAccessor.Getter.MemberAt(offset, typeArguments = listOf(pointedTypeArgument), hasValueAccessor = true),
|
||||
PropertyAccessor.Setter.MemberAt(offset, typeArguments = listOf(pointedTypeArgument))
|
||||
)
|
||||
PropertyStub(field.name, WrapperStubType(fieldRefType.argType), kind)
|
||||
} else {
|
||||
val kind = PropertyStub.Kind.Val(PropertyAccessor.Getter.MemberAt(offset, hasValueAccessor = false))
|
||||
PropertyStub(field.name, pointedType, kind)
|
||||
}
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
val bitFields: List<PropertyStub> = def.bitFields.map { field ->
|
||||
val typeMirror = context.mirror(field.type)
|
||||
val typeInfo = typeMirror.info
|
||||
val kotlinType = typeMirror.argType
|
||||
val signed = field.type.isIntegerTypeSigned()
|
||||
val readBits = PropertyAccessor.Getter.ReadBits(field.offset, field.size, signed)
|
||||
val writeBits = PropertyAccessor.Setter.WriteBits(field.offset, field.size)
|
||||
// TODO: Use something instead of [GlobalGetterBridgeInfo].
|
||||
context.bridgeComponentsBuilder.getterToBridgeInfo[readBits] = BridgeGenerationComponents.GlobalGetterBridgeInfo("", typeInfo, false)
|
||||
context.bridgeComponentsBuilder.setterToBridgeInfo[writeBits] = BridgeGenerationComponents.GlobalSetterBridgeInfo("", typeInfo)
|
||||
val kind = PropertyStub.Kind.Var(readBits, writeBits)
|
||||
PropertyStub(field.name, WrapperStubType(kotlinType), kind)
|
||||
}
|
||||
|
||||
val superClass = SymbolicStubType("CStructVar")
|
||||
val rawPtrConstructorParam = ConstructorParameterStub("rawPtr", SymbolicStubType("NativePtr"))
|
||||
val superClassInit = SuperClassInit(superClass, listOf(GetConstructorParameter(rawPtrConstructorParam)))
|
||||
|
||||
// TODO: How we will differ Type and CStructVar.Type?
|
||||
val companionSuper = SymbolicStubType("Type")
|
||||
val typeSize = listOf(IntegralConstantStub(def.size, 4, true), IntegralConstantStub(def.align.toLong(), 4, true))
|
||||
val companionSuperInit = SuperClassInit(companionSuper, typeSize)
|
||||
val companion = ClassStub.Companion(companionSuperInit)
|
||||
|
||||
return listOf(ClassStub.Simple(
|
||||
classifier,
|
||||
origin = StubOrigin.Struct(decl),
|
||||
properties = fields.filterNotNull() + if (platform == KotlinPlatform.NATIVE) bitFields else emptyList(),
|
||||
functions = emptyList(),
|
||||
modality = ClassStubModality.NONE,
|
||||
annotations = listOfNotNull(structAnnotation),
|
||||
superClassInit = superClassInit,
|
||||
constructorParameters = listOf(rawPtrConstructorParam),
|
||||
companion = companion
|
||||
))
|
||||
}
|
||||
|
||||
private fun getArrayLength(type: ArrayType): Long {
|
||||
val unwrappedElementType = type.elemType.unwrapTypedefs()
|
||||
val elementLength = if (unwrappedElementType is ArrayType) {
|
||||
getArrayLength(unwrappedElementType)
|
||||
} else {
|
||||
1L
|
||||
}
|
||||
|
||||
val elementCount = when (type) {
|
||||
is ConstArrayType -> type.length
|
||||
is IncompleteArrayType -> 0L
|
||||
else -> TODO(type.toString())
|
||||
}
|
||||
|
||||
return elementLength * elementCount
|
||||
}
|
||||
|
||||
private tailrec fun Type.isIntegerTypeSigned(): Boolean = when (this) {
|
||||
is IntegerType -> this.isSigned
|
||||
is BoolType -> false
|
||||
is EnumType -> this.def.baseType.isIntegerTypeSigned()
|
||||
is Typedef -> this.def.aliased.isIntegerTypeSigned()
|
||||
else -> error(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces to [out] the definition of Kotlin class representing the reference to given forward (incomplete) struct.
|
||||
*/
|
||||
private fun generateForwardStruct(s: StructDecl): List<StubIrElement> = when (context.platform) {
|
||||
KotlinPlatform.JVM -> {
|
||||
val classifier = context.getKotlinClassForPointed(s)
|
||||
val superClass = SymbolicStubType("COpaque")
|
||||
val rawPtrConstructorParam = ConstructorParameterStub("rawPtr", SymbolicStubType("NativePtr"))
|
||||
val superClassInit = SuperClassInit(superClass, listOf(GetConstructorParameter(rawPtrConstructorParam)))
|
||||
val origin = StubOrigin.Struct(s)
|
||||
listOf(ClassStub.Simple(classifier, ClassStubModality.NONE, listOf(rawPtrConstructorParam), superClassInit, origin = origin))
|
||||
}
|
||||
KotlinPlatform.NATIVE -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
internal class EnumStubBuilder(
|
||||
override val context: StubsBuildingContext,
|
||||
private val enumDef: EnumDef
|
||||
) : StubElementBuilder {
|
||||
override fun build(): List<StubIrElement> {
|
||||
if (!context.isStrictEnum(enumDef)) {
|
||||
return generateEnumAsConstants(enumDef)
|
||||
}
|
||||
val baseTypeMirror = context.mirror(enumDef.baseType)
|
||||
val baseType = WrapperStubType(baseTypeMirror.argType)
|
||||
|
||||
val clazz = (context.mirror(EnumType(enumDef)) as TypeMirror.ByValue).valueType.classifier
|
||||
val qualifier = ConstructorParameterStub.Qualifier.VAL(overrides = true)
|
||||
val valueParamStub = ConstructorParameterStub("value", baseType, qualifier)
|
||||
|
||||
val canonicalsByValue = enumDef.constants
|
||||
.groupingBy { it.value }
|
||||
.reduce { _, accumulator, element ->
|
||||
if (element.isMoreCanonicalThan(accumulator)) {
|
||||
element
|
||||
} else {
|
||||
accumulator
|
||||
}
|
||||
}
|
||||
val (canonicalConstants, aliasConstants) = enumDef.constants.partition { canonicalsByValue[it.value] == it }
|
||||
|
||||
val canonicalEntries = canonicalConstants.map { constant ->
|
||||
val literal = context.tryCreateIntegralStub(enumDef.baseType, constant.value)
|
||||
?: error("Cannot create enum value ${constant.value} of type ${enumDef.baseType}")
|
||||
val aliases = aliasConstants.filter { it.value == constant.value }.map { EnumEntryStub.Alias(it.name) }
|
||||
EnumEntryStub(constant.name, literal, aliases)
|
||||
}
|
||||
|
||||
val enum = ClassStub.Enum(clazz, canonicalEntries,
|
||||
origin = StubOrigin.Enum(enumDef),
|
||||
constructorParameters = listOf(valueParamStub),
|
||||
interfaces = listOf(SymbolicStubType("CEnum"))
|
||||
)
|
||||
context.bridgeComponentsBuilder.enumToTypeMirror[enum] = baseTypeMirror
|
||||
|
||||
return listOf(enum)
|
||||
}
|
||||
|
||||
|
||||
private fun EnumConstant.isMoreCanonicalThan(other: EnumConstant): Boolean = with(other.name.toLowerCase()) {
|
||||
contains("min") || contains("max") ||
|
||||
contains("first") || contains("last") ||
|
||||
contains("begin") || contains("end")
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces to [out] the Kotlin definitions for given enum which shouldn't be represented as Kotlin enum.
|
||||
*/
|
||||
private fun generateEnumAsConstants(e: EnumDef): List<StubIrElement> {
|
||||
// TODO: if this enum defines e.g. a type of struct field, then it should be generated inside the struct class
|
||||
// to prevent name clashing
|
||||
|
||||
val entries = mutableListOf<PropertyStub>()
|
||||
val typealiases = mutableListOf<TypealiasStub>()
|
||||
|
||||
val constants = e.constants.filter {
|
||||
// Macro "overrides" the original enum constant.
|
||||
it.name !in context.macroConstantsByName
|
||||
}
|
||||
|
||||
val kotlinType: KotlinType
|
||||
|
||||
val baseKotlinType = context.mirror(e.baseType).argType
|
||||
val meta = if (e.isAnonymous) {
|
||||
kotlinType = baseKotlinType
|
||||
StubContainerMeta(textAtStart = if (constants.isNotEmpty()) "// ${e.spelling}:" else "")
|
||||
} else {
|
||||
val typeMirror = context.mirror(EnumType(e))
|
||||
if (typeMirror !is TypeMirror.ByValue) {
|
||||
error("unexpected enum type mirror: $typeMirror")
|
||||
}
|
||||
|
||||
val varTypeName = typeMirror.info.constructPointedType(typeMirror.valueType)
|
||||
val varTypeClassifier = typeMirror.pointedType.classifier
|
||||
val valueTypeClassifier = typeMirror.valueType.classifier
|
||||
typealiases += TypealiasStub(ClassifierStubType(varTypeClassifier), WrapperStubType(varTypeName))
|
||||
typealiases += TypealiasStub(ClassifierStubType(valueTypeClassifier), WrapperStubType(baseKotlinType))
|
||||
|
||||
kotlinType = typeMirror.valueType
|
||||
StubContainerMeta()
|
||||
}
|
||||
|
||||
for (constant in constants) {
|
||||
val literal = context.tryCreateIntegralStub(e.baseType, constant.value) ?: continue
|
||||
val getter = PropertyAccessor.Getter.SimpleGetter(constant = literal)
|
||||
val kind = PropertyStub.Kind.Val(getter)
|
||||
entries += PropertyStub(
|
||||
constant.name,
|
||||
WrapperStubType(kotlinType),
|
||||
kind,
|
||||
MemberStubModality.FINAL,
|
||||
null
|
||||
)
|
||||
}
|
||||
val container = SimpleStubContainer(
|
||||
meta,
|
||||
properties = entries.toList(),
|
||||
typealiases = typealiases.toList()
|
||||
)
|
||||
return listOf(container)
|
||||
}
|
||||
}
|
||||
|
||||
internal class FunctionStubBuilder(
|
||||
override val context: StubsBuildingContext,
|
||||
private val func: FunctionDecl
|
||||
) : StubElementBuilder {
|
||||
override fun build(): List<StubIrElement> {
|
||||
val platform = context.platform
|
||||
val parameters = mutableListOf<FunctionParameterStub>()
|
||||
|
||||
func.parameters.forEachIndexed { index, parameter ->
|
||||
val parameterName = parameter.name.let {
|
||||
if (it == null || it.isEmpty()) {
|
||||
"arg$index"
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
val representAsValuesRef = representCFunctionParameterAsValuesRef(parameter.type)
|
||||
val origin = StubOrigin.FunctionParameter(parameter)
|
||||
parameters += when {
|
||||
representCFunctionParameterAsString(func, parameter.type) -> {
|
||||
val annotations = when (platform) {
|
||||
KotlinPlatform.JVM -> emptyList()
|
||||
KotlinPlatform.NATIVE -> listOf(AnnotationStub.CCall.CString)
|
||||
}
|
||||
val type = WrapperStubType(KotlinTypes.string.makeNullable())
|
||||
val functionParameterStub = FunctionParameterStub(parameterName, type, annotations, origin = origin)
|
||||
context.bridgeComponentsBuilder.cStringParameters += functionParameterStub
|
||||
functionParameterStub
|
||||
}
|
||||
representCFunctionParameterAsWString(func, parameter.type) -> {
|
||||
val annotations = when (platform) {
|
||||
KotlinPlatform.JVM -> emptyList()
|
||||
KotlinPlatform.NATIVE -> listOf(AnnotationStub.CCall.WCString)
|
||||
}
|
||||
val type = WrapperStubType(KotlinTypes.string.makeNullable())
|
||||
val functionParameterStub = FunctionParameterStub(parameterName, type, annotations, origin = origin)
|
||||
context.bridgeComponentsBuilder.wCStringParameters += functionParameterStub
|
||||
functionParameterStub
|
||||
}
|
||||
representAsValuesRef != null -> {
|
||||
FunctionParameterStub(parameterName, WrapperStubType(representAsValuesRef), origin = origin)
|
||||
}
|
||||
else -> {
|
||||
val mirror = context.mirror(parameter.type)
|
||||
val type = WrapperStubType(mirror.argType)
|
||||
FunctionParameterStub(parameterName, type, origin = origin)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val returnType = WrapperStubType(if (func.returnsVoid()) {
|
||||
KotlinTypes.unit
|
||||
} else {
|
||||
context.mirror(func.returnType).argType
|
||||
})
|
||||
|
||||
|
||||
val annotations: List<AnnotationStub>
|
||||
val mustBeExternal: Boolean
|
||||
if (!func.isVararg || platform != KotlinPlatform.NATIVE) {
|
||||
annotations = emptyList()
|
||||
mustBeExternal = false
|
||||
} else {
|
||||
val type = WrapperStubType(KotlinTypes.any.makeNullable())
|
||||
parameters += FunctionParameterStub("variadicArguments", type, isVararg = true)
|
||||
annotations = listOf(AnnotationStub.CCall.Symbol(context.generateNextUniqueId("knifunptr_")))
|
||||
mustBeExternal = true
|
||||
}
|
||||
val functionStub = FunctionStub(
|
||||
func.name,
|
||||
returnType,
|
||||
parameters.toList(),
|
||||
StubOrigin.Function(func),
|
||||
annotations,
|
||||
mustBeExternal,
|
||||
null,
|
||||
MemberStubModality.FINAL
|
||||
)
|
||||
return listOf(functionStub)
|
||||
}
|
||||
|
||||
|
||||
private fun FunctionDecl.returnsVoid(): Boolean = this.returnType.unwrapTypedefs() is VoidType
|
||||
|
||||
private fun representCFunctionParameterAsValuesRef(type: Type): KotlinType? {
|
||||
val pointeeType = when (type) {
|
||||
is PointerType -> type.pointeeType
|
||||
is ArrayType -> type.elemType
|
||||
else -> return null
|
||||
}
|
||||
|
||||
val unwrappedPointeeType = pointeeType.unwrapTypedefs()
|
||||
|
||||
if (unwrappedPointeeType is VoidType) {
|
||||
// Represent `void*` as `CValuesRef<*>?`:
|
||||
return KotlinTypes.cValuesRef.typeWith(StarProjection).makeNullable()
|
||||
}
|
||||
|
||||
if (unwrappedPointeeType is FunctionType) {
|
||||
// Don't represent function pointer as `CValuesRef<T>?` currently:
|
||||
return null
|
||||
}
|
||||
|
||||
if (unwrappedPointeeType is ArrayType) {
|
||||
return representCFunctionParameterAsValuesRef(pointeeType)
|
||||
}
|
||||
|
||||
|
||||
return KotlinTypes.cValuesRef.typeWith(context.mirror(pointeeType).pointedType).makeNullable()
|
||||
}
|
||||
|
||||
|
||||
private val platformWStringTypes = setOf("LPCWSTR")
|
||||
|
||||
private val noStringConversion: Set<String>
|
||||
get() = context.configuration.noStringConversion
|
||||
|
||||
private fun Type.isAliasOf(names: Set<String>): Boolean {
|
||||
var type = this
|
||||
while (type is Typedef) {
|
||||
if (names.contains(type.def.name)) return true
|
||||
type = type.def.aliased
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun representCFunctionParameterAsString(function: FunctionDecl, type: Type): Boolean {
|
||||
val unwrappedType = type.unwrapTypedefs()
|
||||
return unwrappedType is PointerType && unwrappedType.pointeeIsConst &&
|
||||
unwrappedType.pointeeType.unwrapTypedefs() == CharType &&
|
||||
!noStringConversion.contains(function.name)
|
||||
}
|
||||
|
||||
// We take this approach as generic 'const short*' shall not be used as String.
|
||||
private fun representCFunctionParameterAsWString(function: FunctionDecl, type: Type) = type.isAliasOf(platformWStringTypes)
|
||||
&& !noStringConversion.contains(function.name)
|
||||
}
|
||||
|
||||
internal class GlobalStubBuilder(
|
||||
override val context: StubsBuildingContext,
|
||||
private val global: GlobalDecl
|
||||
) : StubElementBuilder {
|
||||
override fun build(): List<StubIrElement> {
|
||||
val mirror = context.mirror(global.type)
|
||||
val unwrappedType = global.type.unwrapTypedefs()
|
||||
|
||||
val kotlinType: KotlinType
|
||||
val kind: PropertyStub.Kind
|
||||
if (unwrappedType is ArrayType) {
|
||||
kotlinType = (mirror as TypeMirror.ByValue).valueType
|
||||
val getter = PropertyAccessor.Getter.SimpleGetter()
|
||||
val extra = BridgeGenerationComponents.GlobalGetterBridgeInfo(global.name, mirror.info, isArray = true)
|
||||
context.bridgeComponentsBuilder.getterToBridgeInfo[getter] = extra
|
||||
kind = PropertyStub.Kind.Val(getter)
|
||||
} else {
|
||||
when (mirror) {
|
||||
is TypeMirror.ByValue -> {
|
||||
kotlinType = mirror.argType
|
||||
val getter = PropertyAccessor.Getter.SimpleGetter()
|
||||
val getterExtra = BridgeGenerationComponents.GlobalGetterBridgeInfo(global.name, mirror.info, isArray = false)
|
||||
context.bridgeComponentsBuilder.getterToBridgeInfo[getter] = getterExtra
|
||||
kind = if (global.isConst) {
|
||||
PropertyStub.Kind.Val(getter)
|
||||
} else {
|
||||
val setter = PropertyAccessor.Setter.SimpleSetter()
|
||||
val setterExtra = BridgeGenerationComponents.GlobalSetterBridgeInfo(global.name, mirror.info)
|
||||
context.bridgeComponentsBuilder.setterToBridgeInfo[setter] = setterExtra
|
||||
PropertyStub.Kind.Var(getter, setter)
|
||||
}
|
||||
}
|
||||
is TypeMirror.ByRef -> {
|
||||
kotlinType = mirror.pointedType
|
||||
val getter = PropertyAccessor.Getter.InterpretPointed(global.name, WrapperStubType(kotlinType))
|
||||
kind = PropertyStub.Kind.Val(getter)
|
||||
}
|
||||
}
|
||||
}
|
||||
return listOf(PropertyStub(global.name, WrapperStubType(kotlinType), kind))
|
||||
}
|
||||
}
|
||||
|
||||
internal class TypedefStubBuilder(
|
||||
override val context: StubsBuildingContext,
|
||||
private val typedefDef: TypedefDef
|
||||
) : StubElementBuilder {
|
||||
override fun build(): List<StubIrElement> {
|
||||
val mirror = context.mirror(Typedef(typedefDef))
|
||||
val baseMirror = context.mirror(typedefDef.aliased)
|
||||
|
||||
val varType = mirror.pointedType.classifier
|
||||
return when (baseMirror) {
|
||||
is TypeMirror.ByValue -> {
|
||||
val valueType = (mirror as TypeMirror.ByValue).valueType
|
||||
val varTypeAliasee = mirror.info.constructPointedType(valueType)
|
||||
val valueTypeAliasee = baseMirror.valueType
|
||||
listOf(
|
||||
TypealiasStub(ClassifierStubType(varType), WrapperStubType(varTypeAliasee)),
|
||||
TypealiasStub(ClassifierStubType(valueType.classifier), WrapperStubType(valueTypeAliasee))
|
||||
)
|
||||
}
|
||||
is TypeMirror.ByRef -> {
|
||||
val varTypeAliasee = baseMirror.pointedType
|
||||
listOf(TypealiasStub(ClassifierStubType(varType), WrapperStubType(varTypeAliasee)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
package org.jetbrains.kotlin.native.interop.gen
|
||||
|
||||
import org.jetbrains.kotlin.native.interop.indexer.ObjCProtocol
|
||||
|
||||
private val StubOrigin.ObjCMethod.isOptional: Boolean
|
||||
get() = container is ObjCProtocol && method.isOptional
|
||||
|
||||
fun FunctionStub.isOptionalObjCMethod(): Boolean = this.origin is StubOrigin.ObjCMethod &&
|
||||
this.origin.isOptional
|
||||
|
||||
val StubContainer.isInterface: Boolean
|
||||
get() = if (this is ClassStub.Simple) {
|
||||
modality == ClassStubModality.INTERFACE
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute which names will be declared by [StubContainer] in the given [pkgName]
|
||||
*/
|
||||
fun StubContainer.computeNamesToBeDeclared(pkgName: String): List<String> {
|
||||
|
||||
fun checkPackageCorrectness(classifier: Classifier) {
|
||||
assert(classifier.pkg == pkgName) {
|
||||
"""Wrong classifier package.
|
||||
|Expected: $pkgName
|
||||
|Got: ${classifier.pkg}
|
||||
|""".trimMargin()
|
||||
}
|
||||
}
|
||||
|
||||
val classNames = classes.mapNotNull {
|
||||
when (it) {
|
||||
is ClassStub.Simple -> it.classifier
|
||||
is ClassStub.Companion -> null
|
||||
is ClassStub.Enum -> it.classifier
|
||||
}
|
||||
}.onEach { checkPackageCorrectness(it) }.map { it.topLevelName }
|
||||
|
||||
val typealiasNames = typealiases
|
||||
.onEach { checkPackageCorrectness(it.alias.classifier) }
|
||||
.map { it.alias.classifier.topLevelName }
|
||||
|
||||
val namesFromNestedContainers = simpleContainers
|
||||
.flatMap { it.computeNamesToBeDeclared(pkgName) }
|
||||
|
||||
return classNames + typealiasNames + namesFromNestedContainers
|
||||
}
|
||||
|
||||
val StubContainer.defaultMemberModality: MemberStubModality
|
||||
get() = when (this) {
|
||||
is SimpleStubContainer -> MemberStubModality.FINAL
|
||||
is ClassStub.Simple -> if (this.modality == ClassStubModality.INTERFACE) {
|
||||
MemberStubModality.OPEN
|
||||
} else {
|
||||
MemberStubModality.FINAL
|
||||
}
|
||||
is ClassStub.Companion -> MemberStubModality.FINAL
|
||||
is ClassStub.Enum -> MemberStubModality.FINAL
|
||||
}
|
||||
+663
@@ -0,0 +1,663 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
package org.jetbrains.kotlin.native.interop.gen
|
||||
|
||||
import org.jetbrains.kotlin.native.interop.gen.jvm.KotlinPlatform
|
||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import java.lang.IllegalStateException
|
||||
|
||||
/**
|
||||
* Emits stubs and bridge functions as *.kt and *.c files.
|
||||
* Many unintuitive printings are made for compatability with previous version of stub generator.
|
||||
*
|
||||
* [omitEmptyLines] is useful for testing output (e.g. diff calculating).
|
||||
*/
|
||||
class StubIrTextEmitter(
|
||||
private val context: StubIrContext,
|
||||
private val builderResult: StubIrBuilderResult,
|
||||
private val bridgeBuilderResult: BridgeBuilderResult,
|
||||
private val omitEmptyLines: Boolean = false
|
||||
) {
|
||||
private val kotlinFile = bridgeBuilderResult.kotlinFile
|
||||
private val nativeBridges = bridgeBuilderResult.nativeBridges
|
||||
private val propertyAccessorBridgeBodies = bridgeBuilderResult.propertyAccessorBridgeBodies
|
||||
private val functionBridgeBodies = bridgeBuilderResult.functionBridgeBodies
|
||||
|
||||
private val pkgName: String
|
||||
get() = context.configuration.pkgName
|
||||
|
||||
private val jvmFileClassName = if (pkgName.isEmpty()) {
|
||||
context.libName
|
||||
} else {
|
||||
pkgName.substringAfterLast('.')
|
||||
}
|
||||
|
||||
private val StubContainer.isTopLevelContainer: Boolean
|
||||
get() = this == builderResult.stubs
|
||||
|
||||
companion object {
|
||||
private val VALID_PACKAGE_NAME_REGEX = "[a-zA-Z0-9_.]+".toRegex()
|
||||
}
|
||||
|
||||
/**
|
||||
* The output currently used by the generator.
|
||||
* Should append line separator after any usage.
|
||||
*/
|
||||
private var out: (String) -> Unit = {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
|
||||
private fun emitEmptyLine() {
|
||||
if (!omitEmptyLines) {
|
||||
out("")
|
||||
}
|
||||
}
|
||||
|
||||
private fun <R> withOutput(output: (String) -> Unit, action: () -> R): R {
|
||||
val oldOut = out
|
||||
out = output
|
||||
try {
|
||||
return action()
|
||||
} finally {
|
||||
out = oldOut
|
||||
}
|
||||
}
|
||||
|
||||
private fun <R> withOutput(appendable: Appendable, action: () -> R): R {
|
||||
return withOutput({ appendable.appendln(it) }, action)
|
||||
}
|
||||
|
||||
private fun generateLinesBy(action: () -> Unit): List<String> {
|
||||
val result = mutableListOf<String>()
|
||||
withOutput({ result.add(it) }, action)
|
||||
return result
|
||||
}
|
||||
|
||||
private fun generateKotlinFragmentBy(block: () -> Unit): Sequence<String> {
|
||||
val lines = generateLinesBy(block)
|
||||
return lines.asSequence()
|
||||
}
|
||||
|
||||
private fun <R> indent(action: () -> R): R {
|
||||
val oldOut = out
|
||||
return withOutput({ oldOut(" $it") }, action)
|
||||
}
|
||||
|
||||
private fun <R> block(header: String, body: () -> R): R {
|
||||
out("$header {")
|
||||
val res = indent {
|
||||
body()
|
||||
}
|
||||
out("}")
|
||||
return res
|
||||
}
|
||||
|
||||
private fun emitKotlinFileHeader() {
|
||||
if (context.platform == KotlinPlatform.JVM) {
|
||||
out("@file:JvmName(${jvmFileClassName.quoteAsKotlinLiteral()})")
|
||||
}
|
||||
if (context.platform == KotlinPlatform.NATIVE) {
|
||||
out("@file:kotlinx.cinterop.InteropStubs")
|
||||
}
|
||||
|
||||
val suppress = mutableListOf("UNUSED_VARIABLE", "UNUSED_EXPRESSION").apply {
|
||||
if (context.configuration.library.language == Language.OBJECTIVE_C) {
|
||||
add("CONFLICTING_OVERLOADS")
|
||||
add("RETURN_TYPE_MISMATCH_ON_INHERITANCE")
|
||||
add("PROPERTY_TYPE_MISMATCH_ON_INHERITANCE") // Multiple-inheriting property with conflicting types
|
||||
add("VAR_TYPE_MISMATCH_ON_INHERITANCE") // Multiple-inheriting mutable property with conflicting types
|
||||
add("RETURN_TYPE_MISMATCH_ON_OVERRIDE")
|
||||
add("WRONG_MODIFIER_CONTAINING_DECLARATION") // For `final val` in interface.
|
||||
add("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
|
||||
add("UNUSED_PARAMETER") // For constructors.
|
||||
add("MANY_IMPL_MEMBER_NOT_IMPLEMENTED") // Workaround for multiple-inherited properties.
|
||||
add("MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED") // Workaround for multiple-inherited properties.
|
||||
add("EXTENSION_SHADOWED_BY_MEMBER") // For Objective-C categories represented as extensions.
|
||||
add("REDUNDANT_NULLABLE") // This warning appears due to Obj-C typedef nullability incomplete support.
|
||||
add("DEPRECATION") // For uncheckedCast.
|
||||
add("DEPRECATION_ERROR") // For initializers.
|
||||
}
|
||||
}
|
||||
|
||||
out("@file:Suppress(${suppress.joinToString { it.quoteAsKotlinLiteral() }})")
|
||||
if (pkgName != "") {
|
||||
val packageName = pkgName.split(".").joinToString("."){
|
||||
if(it.matches(VALID_PACKAGE_NAME_REGEX)){
|
||||
it
|
||||
}else{
|
||||
"`$it`"
|
||||
}
|
||||
}
|
||||
out("package $packageName")
|
||||
out("")
|
||||
}
|
||||
if (context.platform == KotlinPlatform.NATIVE) {
|
||||
out("import kotlin.native.SymbolName")
|
||||
out("import kotlinx.cinterop.internal.*")
|
||||
}
|
||||
out("import kotlinx.cinterop.*")
|
||||
|
||||
kotlinFile.buildImports().forEach {
|
||||
out(it)
|
||||
}
|
||||
|
||||
out("")
|
||||
|
||||
out("// NOTE THIS FILE IS AUTO-GENERATED")
|
||||
}
|
||||
fun emit(ktFile: Appendable, cFile: Appendable, entryPoint: String?) {
|
||||
|
||||
withOutput(cFile) {
|
||||
context.libraryForCStubs.preambleLines.forEach {
|
||||
out(it)
|
||||
}
|
||||
out("")
|
||||
|
||||
out("// NOTE THIS FILE IS AUTO-GENERATED")
|
||||
out("")
|
||||
|
||||
nativeBridges.nativeLines.forEach(out)
|
||||
|
||||
if (entryPoint != null) {
|
||||
out("extern int Konan_main(int argc, char** argv);")
|
||||
out("")
|
||||
out("__attribute__((__used__))")
|
||||
out("int $entryPoint(int argc, char** argv) {")
|
||||
out(" return Konan_main(argc, argv);")
|
||||
out("}")
|
||||
}
|
||||
}
|
||||
|
||||
// Stubs generation may affect imports list so do it before header generation.
|
||||
val stubLines = generateKotlinFragmentBy {
|
||||
printer.visitSimpleStubContainer(builderResult.stubs, null)
|
||||
}
|
||||
|
||||
withOutput(ktFile) {
|
||||
emitKotlinFileHeader()
|
||||
stubLines.forEach(out)
|
||||
nativeBridges.kotlinLines.forEach(out)
|
||||
if (context.platform == KotlinPlatform.JVM) {
|
||||
out("private val loadLibrary = System.loadLibrary(\"${context.libName}\")")
|
||||
}
|
||||
}
|
||||
}
|
||||
private val printer = object : StubIrVisitor<StubContainer?, Unit> {
|
||||
|
||||
override fun visitClass(element: ClassStub, owner: StubContainer?) {
|
||||
element.annotations.forEach {
|
||||
out(renderAnnotation(it))
|
||||
}
|
||||
val header = renderClassHeader(element)
|
||||
when {
|
||||
element is ClassStub.Simple && element.children.isEmpty() -> out(header)
|
||||
element is ClassStub.Companion && element.children.isEmpty() -> out(header)
|
||||
else -> block(header) {
|
||||
if (element is ClassStub.Enum) {
|
||||
emitEnumBody(element)
|
||||
} else {
|
||||
element.children.forEach {
|
||||
emitEmptyLine()
|
||||
it.accept(this, element)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitTypealias(element: TypealiasStub, owner: StubContainer?) {
|
||||
val alias = renderClassifierDeclaration(element.alias.classifier) + renderTypeArguments(element.alias.typeArguments)
|
||||
val aliasee = renderStubType(element.aliasee)
|
||||
out("typealias $alias = $aliasee")
|
||||
}
|
||||
|
||||
override fun visitFunction(element: FunctionStub, owner: StubContainer?) {
|
||||
val modality = renderMemberModality(element.modality, owner)
|
||||
element.annotations.forEach {
|
||||
out(renderAnnotation(it))
|
||||
}
|
||||
val parameters = element.parameters.joinToString(prefix = "(", postfix = ")") { renderFunctionParameter(it) }
|
||||
val receiver = element.receiver?.let { renderFunctionReceiver(it) + "." } ?: ""
|
||||
val typeParameters = renderTypeParameters(element.typeParameters)
|
||||
val header = "${modality}fun$typeParameters $receiver${element.name.asSimpleName()}$parameters: ${renderStubType(element.returnType)}"
|
||||
when {
|
||||
element.external -> out("external $header")
|
||||
element.isOptionalObjCMethod() -> out("$header = optional()")
|
||||
owner != null && owner.isInterface -> out(header)
|
||||
else -> if (nativeBridges.isSupported(element)) {
|
||||
block(header) {
|
||||
functionBridgeBodies.getValue(element).forEach(out)
|
||||
}
|
||||
} else {
|
||||
sequenceOf(
|
||||
annotationForUnableToImport,
|
||||
"$header = throw UnsupportedOperationException()"
|
||||
).forEach(out)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun visitProperty(element: PropertyStub, owner: StubContainer?) {
|
||||
val modality = renderMemberModality(element.modality, owner)
|
||||
val receiver = if (element.receiverType != null) "${renderStubType(element.receiverType)}." else ""
|
||||
val name = if (owner?.isTopLevelContainer == true) {
|
||||
getTopLevelPropertyDeclarationName(kotlinFile, element.name)
|
||||
} else {
|
||||
element.name.asSimpleName()
|
||||
}
|
||||
val header = "$receiver$name: ${renderStubType(element.type)}"
|
||||
|
||||
if (element.kind is PropertyStub.Kind.Val && !nativeBridges.isSupported(element.kind.getter)
|
||||
|| element.kind is PropertyStub.Kind.Var && !nativeBridges.isSupported(element.kind.getter)) {
|
||||
out(annotationForUnableToImport)
|
||||
out("val $header")
|
||||
out(" get() = TODO()")
|
||||
} else {
|
||||
element.annotations.forEach {
|
||||
out(renderAnnotation(it))
|
||||
}
|
||||
when (val kind = element.kind) {
|
||||
is PropertyStub.Kind.Constant -> {
|
||||
out("${modality}const val $header = ${renderValueUsage(kind.constant)}")
|
||||
}
|
||||
is PropertyStub.Kind.Val -> {
|
||||
val shouldWriteInline = kind.getter is PropertyAccessor.Getter.SimpleGetter && kind.getter.constant != null
|
||||
if (shouldWriteInline) {
|
||||
out("${modality}val $header ${renderGetter(kind.getter)}")
|
||||
} else {
|
||||
out("${modality}val $header")
|
||||
indent {
|
||||
out(renderGetter(kind.getter))
|
||||
}
|
||||
}
|
||||
}
|
||||
is PropertyStub.Kind.Var -> {
|
||||
val isSupported = nativeBridges.isSupported(kind.setter)
|
||||
val variableKind = if (isSupported) "var" else "val"
|
||||
|
||||
out("$modality$variableKind $header")
|
||||
indent {
|
||||
out(renderGetter(kind.getter))
|
||||
if (isSupported) {
|
||||
out(renderSetter(kind.setter))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to use the provided name. If failed, mangle it with underscore and try again:
|
||||
private tailrec fun getTopLevelPropertyDeclarationName(scope: KotlinScope, name: String): String =
|
||||
scope.declareProperty(name) ?: getTopLevelPropertyDeclarationName(scope, name + "_")
|
||||
|
||||
override fun visitConstructor(constructorStub: ConstructorStub, owner: StubContainer?) {
|
||||
constructorStub.annotations.forEach {
|
||||
out(renderAnnotation(it))
|
||||
}
|
||||
val visibility = renderVisibilityModifier(constructorStub.visibility)
|
||||
out("${visibility}constructor(${constructorStub.parameters.joinToString { renderFunctionParameter(it) }}) {}")
|
||||
}
|
||||
|
||||
override fun visitPropertyAccessor(propertyAccessor: PropertyAccessor, owner: StubContainer?) {
|
||||
|
||||
}
|
||||
|
||||
override fun visitSimpleStubContainer(simpleStubContainer: SimpleStubContainer, owner: StubContainer?) {
|
||||
if (simpleStubContainer.meta.textAtStart.isNotEmpty()) {
|
||||
out(simpleStubContainer.meta.textAtStart)
|
||||
}
|
||||
simpleStubContainer.classes.forEach {
|
||||
emitEmptyLine()
|
||||
it.accept(this, simpleStubContainer)
|
||||
}
|
||||
simpleStubContainer.functions.forEach {
|
||||
emitEmptyLine()
|
||||
it.accept(this, simpleStubContainer)
|
||||
}
|
||||
simpleStubContainer.properties.forEach {
|
||||
emitEmptyLine()
|
||||
it.accept(this, simpleStubContainer)
|
||||
}
|
||||
simpleStubContainer.typealiases.forEach {
|
||||
emitEmptyLine()
|
||||
it.accept(this, simpleStubContainer)
|
||||
}
|
||||
simpleStubContainer.simpleContainers.forEach {
|
||||
emitEmptyLine()
|
||||
it.accept(this, simpleStubContainer)
|
||||
}
|
||||
if (simpleStubContainer.meta.textAtEnd.isNotEmpty()) {
|
||||
out(simpleStubContainer.meta.textAtEnd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// About method naming convention:
|
||||
// - "emit" prefix means that method will call `out` by itself.
|
||||
// - "render" prefix means that method returns string that should be emitted by caller.
|
||||
private fun emitEnumBody(enum: ClassStub.Enum) {
|
||||
|
||||
enum.entries.forEach {
|
||||
out(renderEnumEntry(it) + ",")
|
||||
}
|
||||
|
||||
val simpleKotlinName = enum.classifier.topLevelName.asSimpleName()
|
||||
val typeMirror = builderResult.bridgeGenerationComponents.enumToTypeMirror.getValue(enum)
|
||||
val baseKotlinType = typeMirror.argType.render(kotlinFile)
|
||||
val basePointedTypeName = typeMirror.pointedType.render(kotlinFile)
|
||||
|
||||
out(";")
|
||||
emitEmptyLine()
|
||||
block("companion object") {
|
||||
enum.entries.forEach { entry ->
|
||||
entry.aliases.forEach {
|
||||
out("val ${it.name.asSimpleName()} = ${entry.name.asSimpleName()}")
|
||||
}
|
||||
|
||||
}
|
||||
emitEmptyLine()
|
||||
|
||||
out("fun byValue(value: $baseKotlinType) = " +
|
||||
"$simpleKotlinName.values().find { it.value == value }!!")
|
||||
}
|
||||
emitEmptyLine()
|
||||
block("class Var(rawPtr: NativePtr) : CEnumVar(rawPtr)") {
|
||||
out("companion object : Type($basePointedTypeName.size.toInt())")
|
||||
out("var value: $simpleKotlinName")
|
||||
out(" get() = byValue(this.reinterpret<$basePointedTypeName>().value)")
|
||||
out(" set(value) { this.reinterpret<$basePointedTypeName>().value = value.value }")
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderFunctionReceiver(receiver: ReceiverParameterStub): String {
|
||||
return renderStubType(receiver.type)
|
||||
}
|
||||
|
||||
private fun renderFunctionParameter(parameter: FunctionParameterStub): String {
|
||||
val annotations = if (parameter.annotations.isEmpty())
|
||||
""
|
||||
else
|
||||
parameter.annotations.joinToString(separator = " ") { renderAnnotation(it) } + " "
|
||||
val vararg = if (parameter.isVararg) "vararg " else ""
|
||||
return "$annotations$vararg${parameter.name.asSimpleName()}: ${renderStubType(parameter.type)}"
|
||||
}
|
||||
|
||||
private fun renderMemberModality(modality: MemberStubModality, container: StubContainer?): String =
|
||||
if (container?.defaultMemberModality == modality) {
|
||||
""
|
||||
} else
|
||||
when (modality) {
|
||||
MemberStubModality.OVERRIDE -> "override "
|
||||
MemberStubModality.OPEN -> "open "
|
||||
MemberStubModality.FINAL -> "final "
|
||||
}
|
||||
|
||||
private fun renderVisibilityModifier(visibilityModifier: VisibilityModifier) = when (visibilityModifier) {
|
||||
VisibilityModifier.PRIVATE -> "private "
|
||||
VisibilityModifier.PROTECTED -> "protected "
|
||||
VisibilityModifier.INTERNAL -> "internal "
|
||||
VisibilityModifier.PUBLIC -> ""
|
||||
}
|
||||
|
||||
private fun renderClassHeader(classStub: ClassStub): String {
|
||||
val modality = when (classStub) {
|
||||
is ClassStub.Simple -> renderClassStubModality(classStub.modality)
|
||||
is ClassStub.Companion -> ""
|
||||
is ClassStub.Enum -> "enum class "
|
||||
}
|
||||
val className = when (classStub) {
|
||||
is ClassStub.Simple -> renderClassifierDeclaration(classStub.classifier)
|
||||
is ClassStub.Companion -> "companion object"
|
||||
is ClassStub.Enum -> renderClassifierDeclaration(classStub.classifier)
|
||||
}
|
||||
val constructorParams = when (classStub) {
|
||||
is ClassStub.Simple -> renderConstructorParams(classStub.constructorParameters)
|
||||
is ClassStub.Companion -> ""
|
||||
is ClassStub.Enum -> renderConstructorParams(classStub.constructorParameters)
|
||||
}
|
||||
val inheritance = mutableListOf<String>().apply {
|
||||
addIfNotNull(classStub.superClassInit?.let { renderSuperInit(it) })
|
||||
addAll(classStub.interfaces.map { renderStubType(it) })
|
||||
}.let { if (it.isNotEmpty()) " : ${it.joinToString()}" else "" }
|
||||
|
||||
return "$modality$className$constructorParams$inheritance"
|
||||
}
|
||||
|
||||
private fun renderClassifierDeclaration(classifier: Classifier): String =
|
||||
kotlinFile.declare(classifier)
|
||||
|
||||
private fun renderClassStubModality(classStubModality: ClassStubModality): String = when (classStubModality) {
|
||||
ClassStubModality.INTERFACE -> "interface "
|
||||
ClassStubModality.OPEN -> "open class "
|
||||
ClassStubModality.ABSTRACT -> "abstract class "
|
||||
ClassStubModality.NONE -> "class "
|
||||
}
|
||||
|
||||
private fun renderConstructorParams(parameters: List<ConstructorParameterStub>): String =
|
||||
if (parameters.isEmpty()) {
|
||||
""
|
||||
} else {
|
||||
parameters.joinToString(prefix = "(", postfix = ")") { renderConstructorParameter(it) }
|
||||
}
|
||||
|
||||
private fun renderConstructorParameter(parameterStub: ConstructorParameterStub): String {
|
||||
val prefix = when (parameterStub.qualifier) {
|
||||
is ConstructorParameterStub.Qualifier.VAL -> if (parameterStub.qualifier.overrides) "override val " else "val "
|
||||
is ConstructorParameterStub.Qualifier.VAR -> if (parameterStub.qualifier.overrides) "override var " else "var "
|
||||
ConstructorParameterStub.Qualifier.NONE -> ""
|
||||
}
|
||||
return "$prefix${parameterStub.name.asSimpleName()}: ${renderStubType(parameterStub.type)}"
|
||||
}
|
||||
|
||||
private fun renderSuperInit(superClassInit: SuperClassInit): String {
|
||||
val parameters = superClassInit.arguments.joinToString(prefix = "(", postfix = ")") { renderValueUsage(it) }
|
||||
return "${renderStubType(superClassInit.type)}$parameters"
|
||||
}
|
||||
|
||||
private fun renderStubType(stubType: StubType): String = when (stubType) {
|
||||
is WrapperStubType -> stubType.kotlinType.render(kotlinFile)
|
||||
is ClassifierStubType -> {
|
||||
val classifier = kotlinFile.reference(stubType.classifier)
|
||||
val typeArguments = renderTypeArguments(stubType.typeArguments)
|
||||
val nullability = if (stubType.nullable) "?" else ""
|
||||
"$classifier$typeArguments$nullability"
|
||||
}
|
||||
is SymbolicStubType -> stubType.name + if (stubType.nullable) "?" else ""
|
||||
}
|
||||
|
||||
private fun renderValueUsage(value: ValueStub): String = when (value) {
|
||||
is StringConstantStub -> value.value
|
||||
is IntegralConstantStub -> renderIntegralConstant(value)!!
|
||||
is DoubleConstantStub -> renderDoubleConstant(value)!!
|
||||
is GetConstructorParameter -> value.constructorParameterStub.name
|
||||
}
|
||||
|
||||
private fun renderAnnotation(annotationStub: AnnotationStub): String = when (annotationStub) {
|
||||
AnnotationStub.ObjC.ConsumesReceiver -> "@CCall.ConsumesReceiver"
|
||||
AnnotationStub.ObjC.ReturnsRetained -> "@CCall.ReturnsRetained"
|
||||
is AnnotationStub.ObjC.Method -> {
|
||||
val stret = if (annotationStub.isStret) ", true" else ""
|
||||
val selector = annotationStub.selector.quoteAsKotlinLiteral()
|
||||
val encoding = annotationStub.encoding.quoteAsKotlinLiteral()
|
||||
"@ObjCMethod($selector, $encoding$stret)"
|
||||
}
|
||||
is AnnotationStub.ObjC.Factory -> {
|
||||
val stret = if (annotationStub.isStret) ", true" else ""
|
||||
val selector = annotationStub.selector.quoteAsKotlinLiteral()
|
||||
val encoding = annotationStub.encoding.quoteAsKotlinLiteral()
|
||||
"@ObjCFactory($selector, $encoding$stret)"
|
||||
}
|
||||
AnnotationStub.ObjC.Consumed ->
|
||||
"@CCall.Consumed"
|
||||
is AnnotationStub.ObjC.Constructor ->
|
||||
"@ObjCConstructor(${annotationStub.selector.quoteAsKotlinLiteral()}, ${annotationStub.designated})"
|
||||
is AnnotationStub.ObjC.ExternalClass -> {
|
||||
val protocolGetter = annotationStub.protocolGetter.quoteAsKotlinLiteral()
|
||||
val binaryName = annotationStub.binaryName.quoteAsKotlinLiteral()
|
||||
"@ExternalObjCClass" + when {
|
||||
annotationStub.protocolGetter.isEmpty() && annotationStub.binaryName.isEmpty() -> ""
|
||||
annotationStub.protocolGetter.isEmpty() -> "(\"\", $binaryName)"
|
||||
annotationStub.binaryName.isEmpty() -> "($protocolGetter)"
|
||||
else -> "($protocolGetter, $binaryName)"
|
||||
}
|
||||
}
|
||||
AnnotationStub.CCall.CString ->
|
||||
"@CCall.CString"
|
||||
AnnotationStub.CCall.WCString ->
|
||||
"@CCall.WCString"
|
||||
is AnnotationStub.CCall.Symbol ->
|
||||
"@CCall(${annotationStub.symbolName.quoteAsKotlinLiteral()})"
|
||||
is AnnotationStub.CStruct ->
|
||||
"@CStruct(${annotationStub.struct.quoteAsKotlinLiteral()})"
|
||||
is AnnotationStub.CNaturalStruct ->
|
||||
"@CNaturalStruct(${annotationStub.members.joinToString { it.name.quoteAsKotlinLiteral() }})"
|
||||
is AnnotationStub.CLength ->
|
||||
"@CLength(${annotationStub.length})"
|
||||
is AnnotationStub.Deprecated ->
|
||||
"@Deprecated(${annotationStub.message.quoteAsKotlinLiteral()}, " +
|
||||
"ReplaceWith(${annotationStub.replaceWith.quoteAsKotlinLiteral()}), " +
|
||||
"DeprecationLevel.ERROR)"
|
||||
}
|
||||
|
||||
private fun renderEnumEntry(enumEntryStub: EnumEntryStub): String =
|
||||
"${enumEntryStub.name.asSimpleName()}(${renderValueUsage(enumEntryStub.constant)})"
|
||||
|
||||
private fun renderGetter(accessor: PropertyAccessor.Getter): String {
|
||||
val annotations = accessor.annotations.joinToString(separator = "") { renderAnnotation(it) + " " }
|
||||
|
||||
return annotations + if (accessor is PropertyAccessor.Getter.ExternalGetter) {
|
||||
"external get"
|
||||
} else {
|
||||
"get() = ${renderPropertyAccessorBody(accessor)}"
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderSetter(accessor: PropertyAccessor.Setter): String {
|
||||
val annotations = accessor.annotations.joinToString(separator = "") { renderAnnotation(it) + " " }
|
||||
return annotations + if (accessor is PropertyAccessor.Setter.ExternalSetter) {
|
||||
"external set"
|
||||
} else {
|
||||
"set(value) { ${renderPropertyAccessorBody(accessor)} }"
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderPropertyAccessorBody(accessor: PropertyAccessor): String = when (accessor) {
|
||||
is PropertyAccessor.Getter.SimpleGetter -> {
|
||||
when {
|
||||
accessor in propertyAccessorBridgeBodies -> propertyAccessorBridgeBodies.getValue(accessor)
|
||||
accessor.constant != null -> renderValueUsage(accessor.constant)
|
||||
else -> error("Bridge body for getter was not generated")
|
||||
}
|
||||
}
|
||||
|
||||
is PropertyAccessor.Getter.ArrayMemberAt -> "arrayMemberAt(${accessor.offset})"
|
||||
|
||||
is PropertyAccessor.Getter.MemberAt -> {
|
||||
val typeArguments = renderTypeArguments(accessor.typeArguments)
|
||||
val valueAccess = if (accessor.hasValueAccessor) ".value" else ""
|
||||
"memberAt$typeArguments(${accessor.offset})$valueAccess"
|
||||
}
|
||||
|
||||
is PropertyAccessor.Getter.ReadBits -> {
|
||||
propertyAccessorBridgeBodies.getValue(accessor)
|
||||
}
|
||||
|
||||
is PropertyAccessor.Setter.SimpleSetter -> when {
|
||||
accessor in propertyAccessorBridgeBodies -> propertyAccessorBridgeBodies.getValue(accessor)
|
||||
else -> error("Bridge body for setter was not generated")
|
||||
}
|
||||
|
||||
is PropertyAccessor.Setter.MemberAt -> {
|
||||
if (accessor.typeArguments.isEmpty()) {
|
||||
error("Unexpected memberAt setter without type parameters!")
|
||||
} else {
|
||||
val typeArguments = renderTypeArguments(accessor.typeArguments)
|
||||
"memberAt$typeArguments(${accessor.offset}).value = value"
|
||||
}
|
||||
}
|
||||
|
||||
is PropertyAccessor.Setter.WriteBits -> {
|
||||
propertyAccessorBridgeBodies.getValue(accessor)
|
||||
}
|
||||
|
||||
is PropertyAccessor.Getter.InterpretPointed -> {
|
||||
val typeParameters = accessor.typeParameters.joinToString(prefix = "<", postfix = ">") { renderStubType(it) }
|
||||
val getAddressExpression = propertyAccessorBridgeBodies.getValue(accessor)
|
||||
"interpretPointed$typeParameters($getAddressExpression)"
|
||||
}
|
||||
is PropertyAccessor.Getter.ExternalGetter,
|
||||
is PropertyAccessor.Setter.ExternalSetter -> error("External property accessor shouldn't have a body!")
|
||||
}
|
||||
|
||||
private fun renderIntegralConstant(integralValue: IntegralConstantStub): String? {
|
||||
val (value, size, isSigned) = integralValue
|
||||
return if (isSigned) {
|
||||
if (value == Long.MIN_VALUE) {
|
||||
return "${value + 1} - 1" // Workaround for "The value is out of range" compile error.
|
||||
}
|
||||
|
||||
val narrowedValue: Number = when (size) {
|
||||
1 -> value.toByte()
|
||||
2 -> value.toShort()
|
||||
4 -> value.toInt()
|
||||
8 -> value
|
||||
else -> return null
|
||||
}
|
||||
|
||||
narrowedValue.toString()
|
||||
} else {
|
||||
// Note: stub generator is built and run with different ABI versions,
|
||||
// so Kotlin unsigned types can't be used here currently.
|
||||
|
||||
val narrowedValue: String = when (size) {
|
||||
1 -> (value and 0xFF).toString()
|
||||
2 -> (value and 0xFFFF).toString()
|
||||
4 -> (value and 0xFFFFFFFF).toString()
|
||||
8 -> java.lang.Long.toUnsignedString(value)
|
||||
else -> return null
|
||||
}
|
||||
|
||||
"${narrowedValue}u"
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderDoubleConstant(doubleValue: DoubleConstantStub): String? {
|
||||
val (value, size) = doubleValue
|
||||
return when (size) {
|
||||
4 -> {
|
||||
val floatValue = value.toFloat()
|
||||
val bits = java.lang.Float.floatToRawIntBits(floatValue)
|
||||
"bitsToFloat($bits) /* == $floatValue */"
|
||||
}
|
||||
8 -> {
|
||||
val bits = java.lang.Double.doubleToRawLongBits(value)
|
||||
"bitsToDouble($bits) /* == $value */"
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderTypeArguments(typeArguments: List<TypeArgumentStub>) = if (typeArguments.isNotEmpty()) {
|
||||
typeArguments.joinToString(", ", "<", ">") { renderStubType(it.type) }
|
||||
} else {
|
||||
""
|
||||
}
|
||||
|
||||
private fun renderTypeParameters(typeParameters: List<TypeParameterStub>) = if (typeParameters.isNotEmpty()) {
|
||||
typeParameters.joinToString(", ", " <", ">") { renderTypeParameter(it) }
|
||||
} else {
|
||||
""
|
||||
}
|
||||
|
||||
private fun renderTypeParameter(typeParameterStub: TypeParameterStub): String {
|
||||
val name = typeParameterStub.name
|
||||
return typeParameterStub.upperBound?.let {
|
||||
"$name : ${renderStubType(it)}"
|
||||
} ?: name
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
package org.jetbrains.kotlin.native.interop.gen
|
||||
|
||||
interface StubIrVisitor<T, R> {
|
||||
|
||||
fun visitClass(element: ClassStub, data: T): R
|
||||
|
||||
fun visitTypealias(element: TypealiasStub, data: T): R
|
||||
|
||||
fun visitFunction(element: FunctionStub, data: T): R
|
||||
|
||||
fun visitProperty(element: PropertyStub, data: T): R
|
||||
|
||||
fun visitConstructor(constructorStub: ConstructorStub, data: T): R
|
||||
|
||||
fun visitPropertyAccessor(propertyAccessor: PropertyAccessor, data: T): R
|
||||
|
||||
fun visitSimpleStubContainer(simpleStubContainer: SimpleStubContainer, data: T): R
|
||||
}
|
||||
+6
-1
@@ -33,4 +33,9 @@ class InteropConfiguration(
|
||||
val exportForwardDeclarations: List<String>,
|
||||
val disableDesignatedInitializerChecks: Boolean,
|
||||
val target: KonanTarget
|
||||
)
|
||||
)
|
||||
|
||||
enum class KotlinPlatform {
|
||||
JVM,
|
||||
NATIVE
|
||||
}
|
||||
-1043
File diff suppressed because it is too large
Load Diff
+8
-13
@@ -202,9 +202,8 @@ private fun processCLib(args: Array<String>, additionalArgs: Map<String, Any> =
|
||||
val excludedMacros = def.config.excludedMacros.toSet()
|
||||
val staticLibraries = def.config.staticLibraries + argParser.getValuesAsArray("staticLibrary")
|
||||
val libraryPaths = def.config.libraryPaths + argParser.getValuesAsArray("libraryPath")
|
||||
val fqParts = (argParser.get<String>("pkg") ?: def.config.packageName)?.let {
|
||||
it.split('.')
|
||||
} ?: defFile!!.name.split('.').reversed().drop(1)
|
||||
val fqParts = (argParser.get<String>("pkg") ?: def.config.packageName)?.split('.')
|
||||
?: defFile!!.name.split('.').reversed().drop(1)
|
||||
|
||||
val outKtFileName = fqParts.last() + ".kt"
|
||||
|
||||
@@ -235,18 +234,14 @@ private fun processCLib(args: Array<String>, additionalArgs: Map<String, Any> =
|
||||
target = tool.target
|
||||
)
|
||||
|
||||
val gen = StubGenerator(nativeIndex, configuration, libName, verbose, flavor, imports)
|
||||
|
||||
outKtFile.parentFile.mkdirs()
|
||||
|
||||
File(nativeLibsDir).mkdirs()
|
||||
val outCFile = tempFiles.create(libName, ".${language.sourceFileExtension}")
|
||||
|
||||
outKtFile.bufferedWriter().use { ktFile ->
|
||||
File(outCFile.absolutePath).bufferedWriter().use { cFile ->
|
||||
gen.generateFiles(ktFile = ktFile, cFile = cFile, entryPoint = entryPoint)
|
||||
}
|
||||
}
|
||||
val stubIrContext = StubIrContext(configuration, nativeIndex, imports, flavor, libName)
|
||||
val stubIrDriver = StubIrDriver(stubIrContext, verbose)
|
||||
stubIrDriver.run(outKtFile, File(outCFile.absolutePath), entryPoint)
|
||||
|
||||
// TODO: if a library has partially included headers, then it shouldn't be used as a dependency.
|
||||
def.manifestAddendProperties["includedHeaders"] = nativeIndex.includedHeaders.joinToString(" ") { it.value }
|
||||
@@ -258,7 +253,7 @@ private fun processCLib(args: Array<String>, additionalArgs: Map<String, Any> =
|
||||
|
||||
def.manifestAddendProperties["interop"] = "true"
|
||||
|
||||
gen.addManifestProperties(def.manifestAddendProperties)
|
||||
stubIrContext.addManifestProperties(def.manifestAddendProperties)
|
||||
|
||||
manifestAddend?.parentFile?.mkdirs()
|
||||
manifestAddend?.let { def.manifestAddendProperties.storeProperties(it) }
|
||||
@@ -267,7 +262,7 @@ private fun processCLib(args: Array<String>, additionalArgs: Map<String, Any> =
|
||||
|
||||
val outOFile = tempFiles.create(libName,".o")
|
||||
|
||||
val compilerCmd = arrayOf(compiler, *gen.libraryForCStubs.compilerArgs.toTypedArray(),
|
||||
val compilerCmd = arrayOf(compiler, *stubIrContext.libraryForCStubs.compilerArgs.toTypedArray(),
|
||||
"-c", outCFile.absolutePath, "-o", outOFile.absolutePath)
|
||||
|
||||
runCmd(compilerCmd, verbose)
|
||||
@@ -282,7 +277,7 @@ private fun processCLib(args: Array<String>, additionalArgs: Map<String, Any> =
|
||||
} else if (flavor == KotlinPlatform.NATIVE) {
|
||||
val outBcName = libName + ".bc"
|
||||
val outLib = File(nativeLibsDir, outBcName)
|
||||
val compilerCmd = arrayOf(compiler, *gen.libraryForCStubs.compilerArgs.toTypedArray(),
|
||||
val compilerCmd = arrayOf(compiler, *stubIrContext.libraryForCStubs.compilerArgs.toTypedArray(),
|
||||
"-emit-llvm", "-c", outCFile.absolutePath, "-o", outLib.absolutePath)
|
||||
|
||||
runCmd(compilerCmd, verbose)
|
||||
|
||||
Reference in New Issue
Block a user