Remove old plaintext StubGenerator and introduce Stub IR-based generator

This commit is contained in:
Sergey Bogolepov
2019-06-07 11:34:48 +03:00
committed by Sergey Bogolepov
parent 367b95a0b8
commit bff177a072
17 changed files with 2734 additions and 1555 deletions
@@ -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>
}
@@ -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) }
@@ -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()
}
}
@@ -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) {
@@ -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
}
@@ -234,7 +234,6 @@ class SimpleBridgeGeneratorImpl(
}
// TODO: exclude unused bridges.
return object : NativeBridges {
override val kotlinLines: Sequence<String>
@@ -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)
}
@@ -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()
)
}
}
@@ -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())
}
}
@@ -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)
}
}
}
}
@@ -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)))
}
}
}
}
@@ -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
}
@@ -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
}
}
@@ -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
}
@@ -33,4 +33,9 @@ class InteropConfiguration(
val exportForwardDeclarations: List<String>,
val disableDesignatedInitializerChecks: Boolean,
val target: KonanTarget
)
)
enum class KotlinPlatform {
JVM,
NATIVE
}
@@ -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)