Improve naming in produced framework

*   Group top-level declarations by file name instead of package name
*   Remove main prefix from Swift class names
*   Use 'Kotlin' prefix
    * Instead of 'Stdlib'
    * In Swift names for KotlinMutableSet/Dictionary, KotlinNumber and subclasses
This commit is contained in:
Svyatoslav Scherbina
2018-09-05 11:03:54 +03:00
committed by SvyatoslavScherbina
parent 2f61cf9fa3
commit da2736337e
9 changed files with 331 additions and 267 deletions
@@ -20,7 +20,6 @@ import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isUnit import org.jetbrains.kotlin.types.typeUtil.isUnit
@@ -149,24 +148,28 @@ internal class ObjCExportCodeGenerator(
internal fun emitRtti( internal fun emitRtti(
generatedClasses: Collection<ClassDescriptor>, generatedClasses: Collection<ClassDescriptor>,
topLevel: Map<FqName, List<CallableMemberDescriptor>> topLevel: Map<SourceFile, List<CallableMemberDescriptor>>
) { ) {
val objCTypeAdapters = mutableListOf<ObjCTypeAdapter>() val objCTypeAdapters = mutableListOf<ObjCTypeAdapter>()
generatedClasses.forEach { generatedClasses.forEach {
objCTypeAdapters += createTypeAdapter(it) objCTypeAdapters += createTypeAdapter(it)
val className = namer.getClassOrProtocolName(it)
val superClass = it.getSuperClassOrAny()
val superClassName = namer.getClassOrProtocolName(superClass)
dataGenerator.emitEmptyClass(className, superClassName) if (!it.isInterface) {
// Note: it is generated only to be visible for linker. val className = namer.getClassOrProtocolName(it).binaryName
// Methods will be added at runtime. val superClass = it.getSuperClassOrAny()
val superClassName = namer.getClassOrProtocolName(superClass).binaryName
dataGenerator.emitEmptyClass(className, superClassName)
// Note: it is generated only to be visible for linker.
// Methods will be added at runtime.
}
} }
topLevel.forEach { fqName, declarations -> topLevel.forEach { sourceFile, declarations ->
objCTypeAdapters += createTypeAdapterForPackage(fqName, declarations) objCTypeAdapters += createTypeAdapterForFileClass(sourceFile, declarations)
dataGenerator.emitEmptyClass(namer.getPackageName(fqName), namer.kotlinAnyName) val name = namer.getFileClassName(sourceFile).binaryName
dataGenerator.emitEmptyClass(name, namer.kotlinAnyName.binaryName)
} }
NSNumberKind.values().mapNotNull { it.mappedKotlinClassId }.forEach { NSNumberKind.values().mapNotNull { it.mappedKotlinClassId }.forEach {
@@ -831,11 +834,11 @@ private fun ObjCExportCodeGenerator.vtableIndex(descriptor: FunctionDescriptor):
} }
} }
private fun ObjCExportCodeGenerator.createTypeAdapterForPackage( private fun ObjCExportCodeGenerator.createTypeAdapterForFileClass(
fqName: FqName, sourceFile: SourceFile,
declarations: List<CallableMemberDescriptor> declarations: List<CallableMemberDescriptor>
): ObjCExportCodeGenerator.ObjCTypeAdapter { ): ObjCExportCodeGenerator.ObjCTypeAdapter {
val name = namer.getPackageName(fqName) val name = namer.getFileClassName(sourceFile).binaryName
val adapters = declarations.toMethods().map { createMethodAdapter(it, it) } val adapters = declarations.toMethods().map { createMethodAdapter(it, it) }
@@ -934,7 +937,7 @@ private fun ObjCExportCodeGenerator.createTypeAdapter(
val irClass = context.ir.get(descriptor) val irClass = context.ir.get(descriptor)
val typeInfo = constPointer(codegen.typeInfoValue(irClass)) val typeInfo = constPointer(codegen.typeInfoValue(irClass))
val objCName = namer.getClassOrProtocolName(descriptor) val objCName = namer.getClassOrProtocolName(descriptor).binaryName
val vtableSize = if (descriptor.kind == ClassKind.INTERFACE) { val vtableSize = if (descriptor.kind == ClassKind.INTERFACE) {
-1 -1
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.backend.konan.llvm.objcexport.ObjCExportCodeGenerato
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.SourceFile
import org.jetbrains.kotlin.konan.file.File import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.konan.target.CompilerOutputKind import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.KonanTarget
@@ -33,7 +34,7 @@ internal class ObjCExport(val codegen: CodeGenerator) {
val objCCodeGenerator: ObjCExportCodeGenerator val objCCodeGenerator: ObjCExportCodeGenerator
val generatedClasses: Set<ClassDescriptor> val generatedClasses: Set<ClassDescriptor>
val topLevelDeclarations: Map<FqName, List<CallableMemberDescriptor>> val topLevelDeclarations: Map<SourceFile, List<CallableMemberDescriptor>>
if (context.config.produce == CompilerOutputKind.FRAMEWORK) { if (context.config.produce == CompilerOutputKind.FRAMEWORK) {
val headerGenerator = ObjCExportHeaderGeneratorImpl(context) val headerGenerator = ObjCExportHeaderGeneratorImpl(context)
@@ -12,7 +12,6 @@ import org.jetbrains.kotlin.builtins.UnsignedType
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.constants.ArrayValue import org.jetbrains.kotlin.resolve.constants.ArrayValue
import org.jetbrains.kotlin.resolve.constants.KClassValue import org.jetbrains.kotlin.resolve.constants.KClassValue
@@ -43,7 +42,7 @@ abstract class ObjCExportHeaderGenerator(
internal val namer = ObjCExportNamerImpl(moduleDescriptor, builtIns, mapper, topLevelNamePrefix) internal val namer = ObjCExportNamerImpl(moduleDescriptor, builtIns, mapper, topLevelNamePrefix)
internal val generatedClasses = mutableSetOf<ClassDescriptor>() internal val generatedClasses = mutableSetOf<ClassDescriptor>()
internal val topLevel = mutableMapOf<FqName, MutableList<CallableMemberDescriptor>>() internal val topLevel = mutableMapOf<SourceFile, MutableList<CallableMemberDescriptor>>()
private val mappedToNSNumber: List<ClassDescriptor> = with(builtIns) { private val mappedToNSNumber: List<ClassDescriptor> = with(builtIns) {
val result = mutableListOf(boolean, byte, short, int, long, float, double) val result = mutableListOf(boolean, byte, short, int, long, float, double)
@@ -63,15 +62,15 @@ abstract class ObjCExportHeaderGenerator(
result += CustomTypeMapper.Collection(generator, list, "NSArray") result += CustomTypeMapper.Collection(generator, list, "NSArray")
result += CustomTypeMapper.Collection(generator, mutableList, "NSMutableArray") result += CustomTypeMapper.Collection(generator, mutableList, "NSMutableArray")
result += CustomTypeMapper.Collection(generator, set, "NSSet") result += CustomTypeMapper.Collection(generator, set, "NSSet")
result += CustomTypeMapper.Collection(generator, mutableSet, namer.mutableSetName) result += CustomTypeMapper.Collection(generator, mutableSet, namer.mutableSetName.objCName)
result += CustomTypeMapper.Collection(generator, map, "NSDictionary") result += CustomTypeMapper.Collection(generator, map, "NSDictionary")
result += CustomTypeMapper.Collection(generator, mutableMap, namer.mutableMapName) result += CustomTypeMapper.Collection(generator, mutableMap, namer.mutableMapName.objCName)
NSNumberKind.values().forEach { NSNumberKind.values().forEach {
// TODO: NSNumber seem to have different equality semantics. // TODO: NSNumber seem to have different equality semantics.
if (it.mappedKotlinClassId != null) { if (it.mappedKotlinClassId != null) {
val descriptor = moduleDescriptor.findClassAcrossModuleDependencies(it.mappedKotlinClassId)!! val descriptor = moduleDescriptor.findClassAcrossModuleDependencies(it.mappedKotlinClassId)!!
result += CustomTypeMapper.Simple(descriptor, namer.numberBoxName(descriptor)) result += CustomTypeMapper.Simple(descriptor, namer.numberBoxName(descriptor).objCName)
} }
} }
@@ -99,7 +98,7 @@ abstract class ObjCExportHeaderGenerator(
private val kotlinAnyName = namer.kotlinAnyName private val kotlinAnyName = namer.kotlinAnyName
private val stubs = mutableListOf<Stub<*>>() private val stubs = mutableListOf<Stub<*>>()
private val classOrInterfaceToName = mutableMapOf<ClassDescriptor, String>() private val classOrInterfaceToName = mutableMapOf<ClassDescriptor, ObjCExportNamer.ClassOrProtocolName>()
private val classForwardDeclarations = mutableSetOf<String>() private val classForwardDeclarations = mutableSetOf<String>()
private val protocolForwardDeclarations = mutableSetOf<String>() private val protocolForwardDeclarations = mutableSetOf<String>()
@@ -107,37 +106,74 @@ abstract class ObjCExportHeaderGenerator(
private val extensions = mutableMapOf<ClassDescriptor, MutableList<CallableMemberDescriptor>>() private val extensions = mutableMapOf<ClassDescriptor, MutableList<CallableMemberDescriptor>>()
private val extraClassesToTranslate = mutableSetOf<ClassDescriptor>() private val extraClassesToTranslate = mutableSetOf<ClassDescriptor>()
private fun objCInterface(
name: ObjCExportNamer.ClassOrProtocolName,
generics: List<String> = emptyList(),
descriptor: ClassDescriptor? = null,
superClass: String? = null,
superProtocols: List<String> = emptyList(),
categoryName: String? = null,
members: List<Stub<*>> = emptyList(),
attributes: List<String> = emptyList()
): ObjCInterface = ObjCInterface(
name.objCName,
generics,
descriptor,
superClass,
superProtocols,
categoryName,
members,
attributes + name.toNameAttributes()
)
private fun objCProtocol(
name: ObjCExportNamer.ClassOrProtocolName,
descriptor: ClassDescriptor,
superProtocols: List<String>,
members: List<Stub<*>>,
attributes: List<String> = emptyList()
): ObjCProtocol = ObjCProtocol(
name.objCName,
descriptor,
superProtocols,
members,
attributes + name.toNameAttributes()
)
private fun ObjCExportNamer.ClassOrProtocolName.toNameAttributes(): List<String> = listOfNotNull(
binaryName.takeIf { it != objCName }?.let { objcRuntimeNameAttribute(it) },
swiftName.takeIf { it != objCName }?.let { swiftNameAttribute(it) }
)
fun translateModule(): List<Stub<*>> { fun translateModule(): List<Stub<*>> {
// TODO: make the translation order stable // TODO: make the translation order stable
// to stabilize name mangling. // to stabilize name mangling.
stubs.add(ObjCInterface(kotlinAnyName, superClass = "NSObject", members = buildMembers { stubs.add(objCInterface(kotlinAnyName, superClass = "NSObject", members = buildMembers {
+ObjCMethod(null, true, ObjCInstanceType, listOf("init"), emptyList(), listOf("unavailable")) +ObjCMethod(null, true, ObjCInstanceType, listOf("init"), emptyList(), listOf("unavailable"))
+ObjCMethod(null, false, ObjCInstanceType, listOf("new"), emptyList(), listOf("unavailable")) +ObjCMethod(null, false, ObjCInstanceType, listOf("new"), emptyList(), listOf("unavailable"))
+ObjCMethod(null, false, ObjCVoidType, listOf("initialize"), emptyList(), listOf("objc_requires_super")) +ObjCMethod(null, false, ObjCVoidType, listOf("initialize"), emptyList(), listOf("objc_requires_super"))
})) }))
// TODO: add comment to the header. // TODO: add comment to the header.
stubs.add(ObjCInterface( stubs.add(objCInterface(
kotlinAnyName, kotlinAnyName,
superProtocols = listOf("NSCopying"), superProtocols = listOf("NSCopying"),
categoryName = "${kotlinAnyName}Copying" categoryName = "${kotlinAnyName.objCName}Copying"
)) ))
// TODO: only if appears // TODO: only if appears
stubs.add(ObjCInterface( stubs.add(objCInterface(
namer.mutableSetName, namer.mutableSetName,
generics = listOf("ObjectType"), generics = listOf("ObjectType"),
superClass = "NSMutableSet<ObjectType>", superClass = "NSMutableSet<ObjectType>"
attributes = listOf(objcRuntimeNameAttribute("KotlinMutableSet"))
)) ))
// TODO: only if appears // TODO: only if appears
stubs.add(ObjCInterface( stubs.add(objCInterface(
namer.mutableMapName, namer.mutableMapName,
generics = listOf("KeyType", "ObjectType"), generics = listOf("KeyType", "ObjectType"),
superClass = "NSMutableDictionary<KeyType, ObjectType>", superClass = "NSMutableDictionary<KeyType, ObjectType>"
attributes = listOf(objcRuntimeNameAttribute("KotlinMutableDictionary"))
)) ))
stubs.add(ObjCInterface("NSError", categoryName = "NSErrorKotlinException", members = buildMembers { stubs.add(ObjCInterface("NSError", categoryName = "NSErrorKotlinException", members = buildMembers {
@@ -158,7 +194,7 @@ abstract class ObjCExportHeaderGenerator(
if (classDescriptor != null) { if (classDescriptor != null) {
extensions.getOrPut(classDescriptor, { mutableListOf() }) += it extensions.getOrPut(classDescriptor, { mutableListOf() }) += it
} else { } else {
topLevel.getOrPut(packageFragment.fqName, { mutableListOf() }) += it topLevel.getOrPut(it.source.containingFile, { mutableListOf() }) += it
} }
} }
@@ -188,8 +224,8 @@ abstract class ObjCExportHeaderGenerator(
translateExtensions(classDescriptor, declarations) translateExtensions(classDescriptor, declarations)
} }
topLevel.forEach { packageFqName, declarations -> topLevel.forEach { sourceFile, declarations ->
translateTopLevel(packageFqName, declarations) translateTopLevel(sourceFile, declarations)
} }
while (extraClassesToTranslate.isNotEmpty()) { while (extraClassesToTranslate.isNotEmpty()) {
@@ -214,11 +250,10 @@ abstract class ObjCExportHeaderGenerator(
+nsNumberInit(it, listOf("unavailable")) +nsNumberInit(it, listOf("unavailable"))
} }
} }
stubs.add(ObjCInterface( stubs.add(objCInterface(
namer.kotlinNumberName, namer.kotlinNumberName,
superClass = "NSNumber", superClass = "NSNumber",
members = members, members = members
attributes = listOf(objcRuntimeNameAttribute("KotlinNumber"))
)) ))
NSNumberKind.values().forEach { NSNumberKind.values().forEach {
@@ -236,11 +271,10 @@ abstract class ObjCExportHeaderGenerator(
+nsNumberFactory(kind) +nsNumberFactory(kind)
+nsNumberInit(kind) +nsNumberInit(kind)
} }
return ObjCInterface( return objCInterface(
name, name,
superClass = namer.kotlinNumberName, superClass = namer.kotlinNumberName.objCName,
members = members, members = members
attributes = listOf(objcRuntimeNameAttribute("Kotlin${kotlinClassId.shortClassName}"))
) )
} }
@@ -266,21 +300,21 @@ abstract class ObjCExportHeaderGenerator(
) )
} }
private fun translateClassName(descriptor: ClassDescriptor): String = classOrInterfaceToName.getOrPut(descriptor) { private fun translateClassName(descriptor: ClassDescriptor) = classOrInterfaceToName.getOrPut(descriptor) {
assert(mapper.shouldBeExposed(descriptor)) assert(mapper.shouldBeExposed(descriptor))
val forwardDeclarations = if (descriptor.isInterface) protocolForwardDeclarations else classForwardDeclarations val forwardDeclarations = if (descriptor.isInterface) protocolForwardDeclarations else classForwardDeclarations
namer.getClassOrProtocolName(descriptor).also { forwardDeclarations += it } namer.getClassOrProtocolName(descriptor).also { forwardDeclarations += it.objCName }
} }
private fun translateInterface(descriptor: ClassDescriptor) { private fun translateInterface(descriptor: ClassDescriptor) {
if (!generatedClasses.add(descriptor)) return if (!generatedClasses.add(descriptor)) return
val name: String = translateClassName(descriptor) val name = translateClassName(descriptor)
val members: List<Stub<*>> = buildMembers { translateClassOrInterfaceMembers(descriptor) } val members: List<Stub<*>> = buildMembers { translateClassOrInterfaceMembers(descriptor) }
val superProtocols: List<String> = descriptor.superProtocols val superProtocols: List<String> = descriptor.superProtocols
val protocolStub = ObjCProtocol(name, descriptor, superProtocols, members) val protocolStub = objCProtocol(name, descriptor, superProtocols, members)
stubs.add(protocolStub) stubs.add(protocolStub)
} }
@@ -292,7 +326,7 @@ abstract class ObjCExportHeaderGenerator(
.filter { mapper.shouldBeExposed(it) } .filter { mapper.shouldBeExposed(it) }
.map { .map {
translateInterface(it) translateInterface(it)
translateClassName(it) translateClassName(it).objCName
} }
.toList() .toList()
@@ -303,19 +337,19 @@ abstract class ObjCExportHeaderGenerator(
val members = buildMembers { val members = buildMembers {
translateMembers(declarations) translateMembers(declarations)
} }
stubs.add(ObjCInterface(name, categoryName = "Extensions", members = members)) stubs.add(objCInterface(name, categoryName = "Extensions", members = members))
} }
private fun translateTopLevel(packageFqName: FqName, declarations: List<CallableMemberDescriptor>) { private fun translateTopLevel(sourceFile: SourceFile, declarations: List<CallableMemberDescriptor>) {
val name = namer.getPackageName(packageFqName) val name = namer.getFileClassName(sourceFile)
// TODO: stop inheriting KotlinBase. // TODO: stop inheriting KotlinBase.
val members = buildMembers { val members = buildMembers {
translateMembers(declarations) translateMembers(declarations)
} }
stubs.add(ObjCInterface( stubs.add(objCInterface(
name, name,
superClass = namer.kotlinAnyName, superClass = namer.kotlinAnyName.objCName,
members = members, members = members,
attributes = listOf("objc_subclassing_restricted") attributes = listOf("objc_subclassing_restricted")
)) ))
@@ -327,7 +361,7 @@ abstract class ObjCExportHeaderGenerator(
val name = translateClassName(descriptor) val name = translateClassName(descriptor)
val superClass = descriptor.getSuperClassNotAny() val superClass = descriptor.getSuperClassNotAny()
val superName: String = if (superClass == null) { val superName = if (superClass == null) {
kotlinAnyName kotlinAnyName
} else { } else {
translateClass(superClass) translateClass(superClass)
@@ -404,10 +438,10 @@ abstract class ObjCExportHeaderGenerator(
val attributes = if (descriptor.isFinalOrEnum) listOf("objc_subclassing_restricted") else emptyList() val attributes = if (descriptor.isFinalOrEnum) listOf("objc_subclassing_restricted") else emptyList()
val interfaceStub = ObjCInterface( val interfaceStub = objCInterface(
name, name,
descriptor = descriptor, descriptor = descriptor,
superClass = superName, superClass = superName.objCName,
superProtocols = superProtocols, superProtocols = superProtocols,
members = members, members = members,
attributes = attributes attributes = attributes
@@ -763,9 +797,9 @@ abstract class ObjCExportHeaderGenerator(
scheduleClassToBeGenerated(classDescriptor) scheduleClassToBeGenerated(classDescriptor)
return if (classDescriptor.isInterface) { return if (classDescriptor.isInterface) {
ObjCProtocolType(translateClassName(classDescriptor)) ObjCProtocolType(translateClassName(classDescriptor).objCName)
} else { } else {
ObjCClassType(translateClassName(classDescriptor)) ObjCClassType(translateClassName(classDescriptor).objCName)
} }
} }
@@ -9,14 +9,20 @@ import org.jetbrains.kotlin.backend.konan.descriptors.isArray
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.konan.isKonanStdlib
import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.descriptorUtil.* import org.jetbrains.kotlin.resolve.descriptorUtil.*
import org.jetbrains.kotlin.resolve.source.PsiSourceFile
interface ObjCExportNamer { interface ObjCExportNamer {
fun getPackageName(fqName: FqName): String data class ClassOrProtocolName(val swiftName: String, val objCName: String, val binaryName: String = objCName)
fun getClassOrProtocolName(descriptor: ClassDescriptor): String
fun getFileClassName(file: SourceFile): ClassOrProtocolName
fun getClassOrProtocolName(descriptor: ClassDescriptor): ClassOrProtocolName
fun getSelector(method: FunctionDescriptor): String fun getSelector(method: FunctionDescriptor): String
fun getSwiftName(method: FunctionDescriptor): String fun getSwiftName(method: FunctionDescriptor): String
fun getPropertyName(property: PropertyDescriptor): String fun getPropertyName(property: PropertyDescriptor): String
@@ -40,15 +46,27 @@ internal class ObjCExportNamerImpl(
private val topLevelNamePrefix: String = moduleDescriptor.namePrefix private val topLevelNamePrefix: String = moduleDescriptor.namePrefix
) : ObjCExportNamer { ) : ObjCExportNamer {
val kotlinAnyName = "KotlinBase" private fun String.mangleClassOrProtocolName(): ObjCExportNamer.ClassOrProtocolName =
ObjCExportNamer.ClassOrProtocolName(swiftName = this, objCName = "$topLevelNamePrefix${this}")
private val commonPackageSegments = moduleDescriptor.guessMainPackage().pathSegments() private fun String.toUnmangledClassOrProtocolName(): ObjCExportNamer.ClassOrProtocolName =
ObjCExportNamer.ClassOrProtocolName(swiftName = this, objCName = this)
val mutableSetName = "${topLevelNamePrefix}MutableSet" private fun String.toSpecialStandardClassOrProtocolName() = ObjCExportNamer.ClassOrProtocolName(
val mutableMapName = "${topLevelNamePrefix}MutableDictionary" swiftName = "Kotlin$this",
objCName = "${topLevelNamePrefix}$this",
binaryName = "Kotlin$this"
)
fun numberBoxName(descriptor: ClassDescriptor): String = "$topLevelNamePrefix${descriptor.name.asString()}" val kotlinAnyName = "KotlinBase".toUnmangledClassOrProtocolName()
val kotlinNumberName = "${topLevelNamePrefix}Number"
val mutableSetName = "MutableSet".toSpecialStandardClassOrProtocolName()
val mutableMapName = "MutableDictionary".toSpecialStandardClassOrProtocolName()
fun numberBoxName(descriptor: ClassDescriptor): ObjCExportNamer.ClassOrProtocolName =
descriptor.name.asString().toSpecialStandardClassOrProtocolName()
val kotlinNumberName = "Number".toSpecialStandardClassOrProtocolName()
private val methodSelectors = object : Mapping<FunctionDescriptor, String>() { private val methodSelectors = object : Mapping<FunctionDescriptor, String>() {
@@ -109,26 +127,26 @@ internal class ObjCExportNamerImpl(
first.containingDeclaration == second.containingDeclaration first.containingDeclaration == second.containingDeclaration
} }
override fun getPackageName(fqName: FqName): String = classNames.getOrPut(fqName) { override fun getFileClassName(file: SourceFile): ObjCExportNamer.ClassOrProtocolName = classNames.getOrPut(file) {
StringBuilder().apply { val psiSourceFile = file as? PsiSourceFile ?: error("SourceFile '$file' is not PsiSourceFile")
append(topLevelNamePrefix) val psiFile = psiSourceFile.psiFile
fqName.pathSegments().forEachIndexed { index, segment -> val ktFile = psiFile as? KtFile ?: error("PsiFile '$psiFile' is not KtFile")
if (index < commonPackageSegments.size) { StringBuilder(PackagePartClassUtils.getFilePartShortName(ktFile.name)).mangledSequence { append("_") }
assert(commonPackageSegments[index] == segment) }.mangleClassOrProtocolName()
} else {
append(segment.asString().capitalize()) private val predefinedClassNames = mapOf(
} builtIns.any to kotlinAnyName,
} builtIns.mutableSet to mutableSetName,
}.mangledSequence { append("_") } builtIns.mutableMap to mutableMapName
} )
override fun getClassOrProtocolName(descriptor: ClassDescriptor): ObjCExportNamer.ClassOrProtocolName {
predefinedClassNames[descriptor]?.let { return it }
override fun getClassOrProtocolName(descriptor: ClassDescriptor): String {
val mapping = if (descriptor.isInterface) protocolNames else classNames val mapping = if (descriptor.isInterface) protocolNames else classNames
return mapping.getOrPut(descriptor) { return mapping.getOrPut(descriptor) {
StringBuilder().apply { StringBuilder().apply {
append(topLevelNamePrefix)
if (descriptor.module != moduleDescriptor) { if (descriptor.module != moduleDescriptor) {
append(descriptor.module.namePrefix) append(descriptor.module.namePrefix)
} }
@@ -137,7 +155,7 @@ internal class ObjCExportNamerImpl(
.toList().reversed() .toList().reversed()
.joinTo(this, "") { it.name.asString().capitalize() } .joinTo(this, "") { it.name.asString().capitalize() }
}.mangledSequence { append("_") } }.mangledSequence { append("_") }
} }.mangleClassOrProtocolName()
} }
override fun getSelector(method: FunctionDescriptor): String = methodSelectors.getOrPut(method) { override fun getSelector(method: FunctionDescriptor): String = methodSelectors.getOrPut(method) {
@@ -192,7 +210,7 @@ internal class ObjCExportNamerImpl(
assert(mapper.isBaseMethod(method)) assert(mapper.isBaseMethod(method))
val parameters = mapper.bridgeMethod(method).valueParametersAssociated(method) val parameters = mapper.bridgeMethod(method).valueParametersAssociated(method)
StringBuilder().apply { StringBuilder().apply {
append(method.getMangledName(forSwift = true)) append(method.getMangledName(forSwift = true))
append("(") append("(")
@@ -261,9 +279,10 @@ internal class ObjCExportNamerImpl(
init { init {
val any = builtIns.any val any = builtIns.any
classNames.forceAssign(any, kotlinAnyName) predefinedClassNames.forEach { descriptor, name ->
classNames.forceAssign(builtIns.mutableSet, mutableSetName) // Note: it is a hack.
classNames.forceAssign(builtIns.mutableMap, mutableMapName) classNames.forceAssign(descriptor, name.swiftName)
}
fun ClassDescriptor.method(name: String) = fun ClassDescriptor.method(name: String) =
this.unsubstitutedMemberScope.getContributedFunctions( this.unsubstitutedMemberScope.getContributedFunctions(
@@ -453,6 +472,8 @@ private fun ObjCExportMapper.canHaveSameName(first: PropertyDescriptor, second:
} }
internal val ModuleDescriptor.namePrefix: String get() { internal val ModuleDescriptor.namePrefix: String get() {
if (this.isKonanStdlib()) return "Kotlin"
// <fooBar> -> FooBar // <fooBar> -> FooBar
val moduleName = this.name.asString() val moduleName = this.name.asString()
.let { it.substring(1, it.lastIndex) } .let { it.substring(1, it.lastIndex) }
@@ -12,6 +12,9 @@ object StubRenderer {
stub.run { stub.run {
when (this) { when (this) {
is ObjCProtocol -> { is ObjCProtocol -> {
attributes.forEach {
+renderAttribute(it)
}
+renderProtocolHeader() +renderProtocolHeader()
+"@required" +"@required"
renderMembers(this) renderMembers(this)
@@ -15,12 +15,14 @@ open class Stub<out D : DeclarationDescriptor>(val name: String, val descriptor:
abstract class ObjCClass<out D : DeclarationDescriptor>(name: String, abstract class ObjCClass<out D : DeclarationDescriptor>(name: String,
descriptor: D?, descriptor: D?,
val superProtocols: List<String>, val superProtocols: List<String>,
val members: List<Stub<*>>) : Stub<D>(name, descriptor) val members: List<Stub<*>>,
val attributes: List<String>) : Stub<D>(name, descriptor)
class ObjCProtocol(name: String, class ObjCProtocol(name: String,
descriptor: ClassDescriptor, descriptor: ClassDescriptor,
superProtocols: List<String>, superProtocols: List<String>,
members: List<Stub<*>>) : ObjCClass<ClassDescriptor>(name, descriptor, superProtocols, members) members: List<Stub<*>>,
attributes: List<String> = emptyList()) : ObjCClass<ClassDescriptor>(name, descriptor, superProtocols, members, attributes)
class ObjCInterface(name: String, class ObjCInterface(name: String,
val generics: List<String> = emptyList(), val generics: List<String> = emptyList(),
@@ -29,7 +31,7 @@ class ObjCInterface(name: String,
superProtocols: List<String> = emptyList(), superProtocols: List<String> = emptyList(),
val categoryName: String? = null, val categoryName: String? = null,
members: List<Stub<*>> = emptyList(), members: List<Stub<*>> = emptyList(),
val attributes: List<String> = emptyList()) : ObjCClass<ClassDescriptor>(name, descriptor, superProtocols, members) attributes: List<String> = emptyList()) : ObjCClass<ClassDescriptor>(name, descriptor, superProtocols, members, attributes)
class ObjCMethod(descriptor: DeclarationDescriptor?, class ObjCMethod(descriptor: DeclarationDescriptor?,
val isInstanceMethod: Boolean, val isInstanceMethod: Boolean,
@@ -57,8 +57,8 @@ class StdlibTests : TestProvider {
*/ */
func testEmptyDictionary() throws { func testEmptyDictionary() throws {
let immutableEmptyDict = [String: Int]() let immutableEmptyDict = [String: Int]()
try assertTrue(Stdlib.isEmpty(map: immutableEmptyDict), "Empty dictionary") try assertTrue(StdlibKt.isEmpty(map: immutableEmptyDict), "Empty dictionary")
let keys = Stdlib.getKeysAsSet(map: immutableEmptyDict) let keys = StdlibKt.getKeysAsSet(map: immutableEmptyDict)
try assertTrue(keys.isEmpty, "Should have empty set") try assertTrue(keys.isEmpty, "Should have empty set")
} }
@@ -66,7 +66,7 @@ class StdlibTests : TestProvider {
* Tests usage of a map with generics. * Tests usage of a map with generics.
*/ */
func testGenericMapUsage() throws { func testGenericMapUsage() throws {
let map = Stdlib.createLinkedMap() let map = StdlibKt.createLinkedMap()
map[1] = "One" map[1] = "One"
map[10] = "Ten" map[10] = "Ten"
map[11] = "Eleven" map[11] = "Eleven"
@@ -82,7 +82,7 @@ class StdlibTests : TestProvider {
* Checks order of the underlying LinkedHashMap. * Checks order of the underlying LinkedHashMap.
*/ */
func testOrderedMapStored() throws { func testOrderedMapStored() throws {
let pair = Stdlib.createPair() let pair = StdlibKt.createPair()
let map = pair.first as? NSMutableDictionary let map = pair.first as? NSMutableDictionary
map?[1] = "One" map?[1] = "One"
@@ -90,7 +90,7 @@ class StdlibTests : TestProvider {
map?[11] = "Eleven" map?[11] = "Eleven"
map?["10"] = "Ten as string" map?["10"] = "Ten as string"
let gen = pair.second as! StdlibGenericExtensionClass let gen = pair.second as! GenericExtensionClass
let value: String? = gen.getFirstValue() as? String let value: String? = gen.getFirstValue() as? String
try assertEquals(actual: value!, expected: "One", "First value of the map") try assertEquals(actual: value!, expected: "One", "First value of the map")
@@ -102,7 +102,7 @@ class StdlibTests : TestProvider {
* Tests typed map created in Kotlin. * Tests typed map created in Kotlin.
*/ */
func testTypedMapUsage() throws { func testTypedMapUsage() throws {
let map = Stdlib.createTypedMutableMap() let map = StdlibKt.createTypedMutableMap()
map[1] = "One" map[1] = "One"
map[1.0 as Float] = "Float" map[1.0 as Float] = "Float"
map[11] = "Eleven" map[11] = "Eleven"
@@ -116,11 +116,11 @@ class StdlibTests : TestProvider {
* Get first element of the collection. * Get first element of the collection.
*/ */
func testFirstElement() throws { func testFirstElement() throws {
let m = Stdlib.createTypedMutableMap() let m = StdlibKt.createTypedMutableMap()
m[10] = "Str" m[10] = "Str"
try assertEquals(actual: Stdlib.getFirstElement(collection: m.allKeys) as! Int, expected: 10, "First key") try assertEquals(actual: StdlibKt.getFirstElement(collection: m.allKeys) as! Int, expected: 10, "First key")
try assertEquals(actual: Stdlib.getFirstElement(collection: Stdlib.getKeysAsList(map: m as! Dictionary)) as! Int, try assertEquals(actual: StdlibKt.getFirstElement(collection: StdlibKt.getKeysAsList(map: m as! Dictionary)) as! Int,
expected: 10, "First key from a list") expected: 10, "First key from a list")
} }
@@ -129,35 +129,35 @@ class StdlibTests : TestProvider {
*/ */
func testAddDictionary() throws { func testAddDictionary() throws {
let m = [ "ABC": 10, "CDE": 12, "FGH": 3 ] let m = [ "ABC": 10, "CDE": 12, "FGH": 3 ]
Stdlib.addSomeElementsToMap(map: StdlibMutableDictionary(dictionary: m)) StdlibKt.addSomeElementsToMap(map: KotlinMutableDictionary(dictionary: m))
for (k, v) in m { for (k, v) in m {
print("MAP: \(k) - \(v)") print("MAP: \(k) - \(v)")
} }
var smd = StdlibMutableDictionary<NSString, StdlibInt>() var smd = KotlinMutableDictionary<NSString, KotlinInt>()
smd.setObject(333, forKey: "333" as NSString) smd.setObject(333, forKey: "333" as NSString)
try assertEquals(actual: smd.object(forKey: "333" as NSString) as! Int, expected: 333, "Add element to dict") try assertEquals(actual: smd.object(forKey: "333" as NSString) as! Int, expected: 333, "Add element to dict")
Stdlib.addSomeElementsToMap(map: smd) StdlibKt.addSomeElementsToMap(map: smd)
for (k, v) in smd { for (k, v) in smd {
print("MAP: \(k) - \(v)") print("MAP: \(k) - \(v)")
} }
try assertEquals(actual: smd.object(forKey: "XYZ" as NSString) as! Int, expected: 321, "Get element from Kotlin") try assertEquals(actual: smd.object(forKey: "XYZ" as NSString) as! Int, expected: 321, "Get element from Kotlin")
} }
func zeroTo(_ n: Int32) -> StdlibStdlibArray { return StdlibStdlibArray(size: n) { $0 } } func zeroTo(_ n: Int32) -> KotlinArray { return KotlinArray(size: n) { $0 } }
func testList() throws { func testList() throws {
let elements = zeroTo(5) let elements = zeroTo(5)
elements.set(index: 1, value: nil) elements.set(index: 1, value: nil)
let list = Stdlib.list(elements: elements) as! NSArray let list = StdlibKt.list(elements: elements) as! NSArray
try assertEquals(actual: list.object(at: 2) as! NSNumber, expected: NSNumber(value: 2)) try assertEquals(actual: list.object(at: 2) as! NSNumber, expected: NSNumber(value: 2))
try assertEquals(actual: list.object(at: 1) as! NSNull, expected: NSNull()) try assertEquals(actual: list.object(at: 1) as! NSNull, expected: NSNull())
try assertEquals(actual: list.count, expected: 5) try assertEquals(actual: list.count, expected: 5)
} }
func testMutableList() throws { func testMutableList() throws {
let kotlinList = Stdlib.emptyMutableList() as! NSMutableArray let kotlinList = StdlibKt.emptyMutableList() as! NSMutableArray
let nsList = NSMutableArray() let nsList = NSMutableArray()
func apply<T : Equatable>(op: (NSMutableArray)->T) throws { func apply<T : Equatable>(op: (NSMutableArray)->T) throws {
@@ -191,7 +191,7 @@ class StdlibTests : TestProvider {
} }
func testMutableSet() throws { func testMutableSet() throws {
let kotlinSet = Stdlib.emptyMutableSet() as! NSMutableSet let kotlinSet = StdlibKt.emptyMutableSet() as! NSMutableSet
let nsSet = NSMutableSet() let nsSet = NSMutableSet()
func apply<T : Equatable>(op: (NSMutableSet)->T) throws { func apply<T : Equatable>(op: (NSMutableSet)->T) throws {
@@ -214,10 +214,10 @@ class StdlibTests : TestProvider {
try applyVoid { $0.add("bar") } try applyVoid { $0.add("bar") }
try applyVoid { $0.remove("baz") } try applyVoid { $0.remove("baz") }
try applyVoid { $0.add("baz") } try applyVoid { $0.add("baz") }
try applyVoid { $0.add(StdlibTripleVals(first: 1, second: 2, third: 3)) } try applyVoid { $0.add(TripleVals(first: 1, second: 2, third: 3)) }
try apply { $0.member(StdlibTripleVals(first: 1, second: 2, third: 3)) as! StdlibTripleVals } try apply { $0.member(TripleVals(first: 1, second: 2, third: 3)) as! TripleVals }
try apply { $0.member(42) == nil } try apply { $0.member(42) == nil }
try applyVoid { $0.remove(StdlibTripleVals(first: 1, second: 2, third: 3)) } try applyVoid { $0.remove(TripleVals(first: 1, second: 2, third: 3)) }
let NULL0: Any? = nil let NULL0: Any? = nil
let NULL = NULL0 as Any let NULL = NULL0 as Any
@@ -235,7 +235,7 @@ class StdlibTests : TestProvider {
func testMutableMap() throws { func testMutableMap() throws {
// TODO: test KotlinMutableSet/Dictionary constructors // TODO: test KotlinMutableSet/Dictionary constructors
let kotlinMap = Stdlib.emptyMutableMap() as! NSMutableDictionary let kotlinMap = StdlibKt.emptyMutableMap() as! NSMutableDictionary
let nsMap = NSMutableDictionary() let nsMap = NSMutableDictionary()
func apply<T : Equatable>(op: (NSMutableDictionary)->T) throws { func apply<T : Equatable>(op: (NSMutableDictionary)->T) throws {
@@ -257,7 +257,7 @@ class StdlibTests : TestProvider {
try apply { $0.object(forKey: 42) == nil } try apply { $0.object(forKey: 42) == nil }
try applyVoid { $0.setObject(42, forKey: 42 as NSNumber) } try applyVoid { $0.setObject(42, forKey: 42 as NSNumber) }
try applyVoid { $0.setObject(17, forKey: "foo" as NSString) } try applyVoid { $0.setObject(17, forKey: "foo" as NSString) }
let triple = StdlibTripleVals(first: 3, second: 2, third: 1) let triple = TripleVals(first: 3, second: 2, third: 1)
try applyVoid { $0.setObject("bar", forKey: triple) } try applyVoid { $0.setObject("bar", forKey: triple) }
try applyVoid { $0.removeObject(forKey: 42) } try applyVoid { $0.removeObject(forKey: 42) }
try apply { $0.count } try apply { $0.count }
@@ -312,10 +312,10 @@ class StdlibTests : TestProvider {
} }
func testSet() throws { func testSet() throws {
let elements = StdlibStdlibArray(size: 2) { index in nil } let elements = KotlinArray(size: 2) { index in nil }
elements.set(index: 0, value: nil) elements.set(index: 0, value: nil)
elements.set(index: 1, value: 42) elements.set(index: 1, value: 42)
let set = Stdlib.set(elements: elements) as! NSSet let set = StdlibKt.set(elements: elements) as! NSSet
try assertEquals(actual: set.count, expected: 2) try assertEquals(actual: set.count, expected: 2)
try assertEquals(actual: set.member(NSNull()) as! NSNull, expected: NSNull()) try assertEquals(actual: set.member(NSNull()) as! NSNull, expected: NSNull())
try assertEquals(actual: set.member(42) as! NSNumber, expected: NSNumber(value: 42 as Int32)) try assertEquals(actual: set.member(42) as! NSNumber, expected: NSNumber(value: 42 as Int32))
@@ -329,7 +329,7 @@ class StdlibTests : TestProvider {
} }
func testMap() throws { func testMap() throws {
let elements = StdlibStdlibArray(size: 6) { index in nil } let elements = KotlinArray(size: 6) { index in nil }
elements.set(index: 0, value: nil) elements.set(index: 0, value: nil)
elements.set(index: 1, value: 42) elements.set(index: 1, value: 42)
elements.set(index: 2, value: "foo") elements.set(index: 2, value: "foo")
@@ -337,7 +337,7 @@ class StdlibTests : TestProvider {
elements.set(index: 4, value: 42) elements.set(index: 4, value: 42)
elements.set(index: 5, value: nil) elements.set(index: 5, value: nil)
let map = Stdlib.map(keysAndValues: elements) as! NSDictionary let map = StdlibKt.map(keysAndValues: elements) as! NSDictionary
try assertEquals(actual: map.count, expected: 3) try assertEquals(actual: map.count, expected: 3)
try assertEquals(actual: map.object(forKey: NSNull()) as! NSNumber, expected: NSNumber(value: 42)) try assertEquals(actual: map.object(forKey: NSNull()) as! NSNumber, expected: NSNumber(value: 42))
+152 -152
View File
@@ -21,10 +21,10 @@ import Values
func testVals() throws { func testVals() throws {
print("Values from Swift") print("Values from Swift")
let dbl = Values.dbl let dbl = ValuesKt.dbl
let flt = Values.flt let flt = ValuesKt.flt
let int = Values.integer let int = ValuesKt.integer
let long = Values.longInt let long = ValuesKt.longInt
print(dbl) print(dbl)
print(flt) print(flt)
@@ -39,9 +39,9 @@ func testVals() throws {
func testVars() throws { func testVars() throws {
print("Variables from Swift") print("Variables from Swift")
var intVar = Values.intVar var intVar = ValuesKt.intVar
var strVar = Values.str var strVar = ValuesKt.str
var strAsId = Values.strAsAny var strAsId = ValuesKt.strAsAny
print(intVar) print(intVar)
print(strVar) print(strVar)
@@ -52,133 +52,133 @@ func testVars() throws {
try assertEquals(actual: strAsId as! String, expected: "Kotlin String as Any") try assertEquals(actual: strAsId as! String, expected: "Kotlin String as Any")
strAsId = "Swift str" strAsId = "Swift str"
Values.strAsAny = strAsId ValuesKt.strAsAny = strAsId
print(Values.strAsAny) print(ValuesKt.strAsAny)
try assertEquals(actual: Values.strAsAny as! String, expected: strAsId as! String) try assertEquals(actual: ValuesKt.strAsAny as! String, expected: strAsId as! String)
// property with custom getter/setter backed by the Kotlin's var // property with custom getter/setter backed by the Kotlin's var
var intProp : Int32 { var intProp : Int32 {
get { get {
return Values.intVar * 2 return ValuesKt.intVar * 2
} }
set(value) { set(value) {
Values.intVar = 123 + value ValuesKt.intVar = 123 + value
} }
} }
intProp += 10 intProp += 10
print(intProp) print(intProp)
print(Values.intVar) print(ValuesKt.intVar)
try assertEquals(actual: Values.intVar * 2, expected: intProp, "Property backed by var") try assertEquals(actual: ValuesKt.intVar * 2, expected: intProp, "Property backed by var")
} }
func testDoubles() throws { func testDoubles() throws {
print("Doubles in Swift") print("Doubles in Swift")
let minDouble = Values.minDoubleVal as! Double let minDouble = ValuesKt.minDoubleVal as! Double
let maxDouble = Values.maxDoubleVal as! NSNumber let maxDouble = ValuesKt.maxDoubleVal as! NSNumber
print(minDouble) print(minDouble)
print(maxDouble) print(maxDouble)
print(Values.nanDoubleVal) print(ValuesKt.nanDoubleVal)
print(Values.nanFloatVal) print(ValuesKt.nanFloatVal)
print(Values.infDoubleVal) print(ValuesKt.infDoubleVal)
print(Values.infFloatVal) print(ValuesKt.infFloatVal)
try assertEquals(actual: minDouble, expected: Double.leastNonzeroMagnitude, "Min double") try assertEquals(actual: minDouble, expected: Double.leastNonzeroMagnitude, "Min double")
try assertEquals(actual: maxDouble, expected: Double.greatestFiniteMagnitude as NSNumber, "Max double") try assertEquals(actual: maxDouble, expected: Double.greatestFiniteMagnitude as NSNumber, "Max double")
try assertTrue(Values.nanDoubleVal.isNaN, "NaN double") try assertTrue(ValuesKt.nanDoubleVal.isNaN, "NaN double")
try assertTrue(Values.nanFloatVal.isNaN, "NaN float") try assertTrue(ValuesKt.nanFloatVal.isNaN, "NaN float")
try assertEquals(actual: Values.infDoubleVal, expected: Double.infinity, "Inf double") try assertEquals(actual: ValuesKt.infDoubleVal, expected: Double.infinity, "Inf double")
try assertEquals(actual: Values.infFloatVal, expected: -Float.infinity, "-Inf float") try assertEquals(actual: ValuesKt.infFloatVal, expected: -Float.infinity, "-Inf float")
} }
func testNumbers() throws { func testNumbers() throws {
try assertEquals(actual: ValuesBoolean(value: true).boolValue, expected: true) try assertEquals(actual: KotlinBoolean(value: true).boolValue, expected: true)
try assertEquals(actual: ValuesBoolean(value: false).intValue, expected: 0) try assertEquals(actual: KotlinBoolean(value: false).intValue, expected: 0)
try assertEquals(actual: ValuesBoolean(value: true), expected: true) try assertEquals(actual: KotlinBoolean(value: true), expected: true)
try assertFalse(ValuesBoolean(value: false) as! Bool) try assertFalse(KotlinBoolean(value: false) as! Bool)
try assertEquals(actual: ValuesByte(value: -1).int8Value, expected: -1) try assertEquals(actual: KotlinByte(value: -1).int8Value, expected: -1)
try assertEquals(actual: ValuesByte(value: -1).int32Value, expected: -1) try assertEquals(actual: KotlinByte(value: -1).int32Value, expected: -1)
try assertEquals(actual: ValuesByte(value: -1).doubleValue, expected: -1.0) try assertEquals(actual: KotlinByte(value: -1).doubleValue, expected: -1.0)
try assertEquals(actual: ValuesByte(value: -1), expected: NSNumber(value: Int64(-1))) try assertEquals(actual: KotlinByte(value: -1), expected: NSNumber(value: Int64(-1)))
try assertFalse(ValuesByte(value: -1) == NSNumber(value: -1.5)) try assertFalse(KotlinByte(value: -1) == NSNumber(value: -1.5))
try assertEquals(actual: ValuesByte(value: -1), expected: -1) try assertEquals(actual: KotlinByte(value: -1), expected: -1)
try assertTrue(ValuesByte(value: -1) == -1) try assertTrue(KotlinByte(value: -1) == -1)
try assertFalse(ValuesByte(value: -1) == 1) try assertFalse(KotlinByte(value: -1) == 1)
try assertEquals(actual: ValuesByte(value: -1) as! Int32, expected: -1) try assertEquals(actual: KotlinByte(value: -1) as! Int32, expected: -1)
try assertEquals(actual: ValuesShort(value: 111).int16Value, expected: 111) try assertEquals(actual: KotlinShort(value: 111).int16Value, expected: 111)
try assertEquals(actual: ValuesShort(value: -15) as! Int16, expected: -15) try assertEquals(actual: KotlinShort(value: -15) as! Int16, expected: -15)
try assertEquals(actual: ValuesShort(value: 47), expected: 47) try assertEquals(actual: KotlinShort(value: 47), expected: 47)
try assertEquals(actual: ValuesInt(value: 99).int32Value, expected: 99) try assertEquals(actual: KotlinInt(value: 99).int32Value, expected: 99)
try assertEquals(actual: ValuesInt(value: -1) as! Int32, expected: -1) try assertEquals(actual: KotlinInt(value: -1) as! Int32, expected: -1)
try assertEquals(actual: ValuesInt(value: 72), expected: 72) try assertEquals(actual: KotlinInt(value: 72), expected: 72)
try assertEquals(actual: ValuesLong(value: 65).int64Value, expected: 65) try assertEquals(actual: KotlinLong(value: 65).int64Value, expected: 65)
try assertEquals(actual: ValuesLong(value: 10000000000) as! Int64, expected: 10000000000) try assertEquals(actual: KotlinLong(value: 10000000000) as! Int64, expected: 10000000000)
try assertEquals(actual: ValuesLong(value: 8), expected: 8) try assertEquals(actual: KotlinLong(value: 8), expected: 8)
try assertEquals(actual: ValuesUByte(value: 17).uint8Value, expected: 17) try assertEquals(actual: KotlinUByte(value: 17).uint8Value, expected: 17)
try assertEquals(actual: ValuesUByte(value: 42) as! UInt8, expected: 42) try assertEquals(actual: KotlinUByte(value: 42) as! UInt8, expected: 42)
try assertEquals(actual: 88, expected: ValuesUByte(value: 88)) try assertEquals(actual: 88, expected: KotlinUByte(value: 88))
try assertEquals(actual: ValuesUShort(value: 40000).uint16Value, expected: 40000) try assertEquals(actual: KotlinUShort(value: 40000).uint16Value, expected: 40000)
try assertEquals(actual: ValuesUShort(value: 1) as! UInt16, expected: UInt16(1)) try assertEquals(actual: KotlinUShort(value: 1) as! UInt16, expected: UInt16(1))
try assertEquals(actual: ValuesUShort(value: 65000), expected: 65000) try assertEquals(actual: KotlinUShort(value: 65000), expected: 65000)
try assertEquals(actual: ValuesUInt(value: 3).uint32Value, expected: 3) try assertEquals(actual: KotlinUInt(value: 3).uint32Value, expected: 3)
try assertEquals(actual: ValuesUInt(value: UInt32.max) as! UInt32, expected: UInt32.max) try assertEquals(actual: KotlinUInt(value: UInt32.max) as! UInt32, expected: UInt32.max)
try assertEquals(actual: ValuesUInt(value: 2), expected: 2) try assertEquals(actual: KotlinUInt(value: 2), expected: 2)
try assertEquals(actual: ValuesULong(value: 55).uint64Value, expected: 55) try assertEquals(actual: KotlinULong(value: 55).uint64Value, expected: 55)
try assertEquals(actual: ValuesULong(value: 0) as! UInt64, expected: 0) try assertEquals(actual: KotlinULong(value: 0) as! UInt64, expected: 0)
try assertEquals(actual: ValuesULong(value: 7), expected: 7) try assertEquals(actual: KotlinULong(value: 7), expected: 7)
try assertEquals(actual: ValuesFloat(value: 1.0).floatValue, expected: 1.0) try assertEquals(actual: KotlinFloat(value: 1.0).floatValue, expected: 1.0)
try assertEquals(actual: ValuesFloat(value: 22.0) as! Float, expected: 22) try assertEquals(actual: KotlinFloat(value: 22.0) as! Float, expected: 22)
try assertEquals(actual: ValuesFloat(value: 41.0), expected: 41) try assertEquals(actual: KotlinFloat(value: 41.0), expected: 41)
try assertEquals(actual: ValuesFloat(value: -5.5), expected: -5.5) try assertEquals(actual: KotlinFloat(value: -5.5), expected: -5.5)
try assertEquals(actual: ValuesDouble(value: 0.5).doubleValue, expected: 0.5) try assertEquals(actual: KotlinDouble(value: 0.5).doubleValue, expected: 0.5)
try assertEquals(actual: ValuesDouble(value: 45.0) as! Double, expected: 45) try assertEquals(actual: KotlinDouble(value: 45.0) as! Double, expected: 45)
try assertEquals(actual: ValuesDouble(value: 89.0), expected: 89) try assertEquals(actual: KotlinDouble(value: 89.0), expected: 89)
try assertEquals(actual: ValuesDouble(value: -3.7), expected: -3.7) try assertEquals(actual: KotlinDouble(value: -3.7), expected: -3.7)
Values.ensureEqualBooleans(actual: ValuesBoolean(value: true), expected: true) ValuesKt.ensureEqualBooleans(actual: KotlinBoolean(value: true), expected: true)
Values.ensureEqualBooleans(actual: false, expected: false) ValuesKt.ensureEqualBooleans(actual: false, expected: false)
Values.ensureEqualBytes(actual: ValuesByte(value: 42), expected: 42) ValuesKt.ensureEqualBytes(actual: KotlinByte(value: 42), expected: 42)
Values.ensureEqualBytes(actual: -11, expected: -11) ValuesKt.ensureEqualBytes(actual: -11, expected: -11)
Values.ensureEqualShorts(actual: ValuesShort(value: 256), expected: 256) ValuesKt.ensureEqualShorts(actual: KotlinShort(value: 256), expected: 256)
Values.ensureEqualShorts(actual: -1, expected: -1) ValuesKt.ensureEqualShorts(actual: -1, expected: -1)
Values.ensureEqualInts(actual: ValuesInt(value: 100000), expected: 100000) ValuesKt.ensureEqualInts(actual: KotlinInt(value: 100000), expected: 100000)
Values.ensureEqualInts(actual: -7, expected: -7) ValuesKt.ensureEqualInts(actual: -7, expected: -7)
Values.ensureEqualLongs(actual: ValuesLong(value: Int64.max), expected: Int64.max) ValuesKt.ensureEqualLongs(actual: KotlinLong(value: Int64.max), expected: Int64.max)
Values.ensureEqualLongs(actual: 17, expected: 17) ValuesKt.ensureEqualLongs(actual: 17, expected: 17)
Values.ensureEqualUBytes(actual: ValuesUByte(value: 6), expected: 6) ValuesKt.ensureEqualUBytes(actual: KotlinUByte(value: 6), expected: 6)
Values.ensureEqualUBytes(actual: 255, expected: 255) ValuesKt.ensureEqualUBytes(actual: 255, expected: 255)
Values.ensureEqualUShorts(actual: ValuesUShort(value: 300), expected: 300) ValuesKt.ensureEqualUShorts(actual: KotlinUShort(value: 300), expected: 300)
Values.ensureEqualUShorts(actual: 65535, expected: UInt16.max) ValuesKt.ensureEqualUShorts(actual: 65535, expected: UInt16.max)
Values.ensureEqualUInts(actual: ValuesUInt(value: 70000), expected: 70000) ValuesKt.ensureEqualUInts(actual: KotlinUInt(value: 70000), expected: 70000)
Values.ensureEqualUInts(actual: 48, expected: 48) ValuesKt.ensureEqualUInts(actual: 48, expected: 48)
Values.ensureEqualULongs(actual: ValuesULong(value: UInt64.max), expected: UInt64.max) ValuesKt.ensureEqualULongs(actual: KotlinULong(value: UInt64.max), expected: UInt64.max)
Values.ensureEqualULongs(actual: 39, expected: 39) ValuesKt.ensureEqualULongs(actual: 39, expected: 39)
Values.ensureEqualFloats(actual: ValuesFloat(value: 36.6), expected: 36.6) ValuesKt.ensureEqualFloats(actual: KotlinFloat(value: 36.6), expected: 36.6)
Values.ensureEqualFloats(actual: 49.5, expected: 49.5) ValuesKt.ensureEqualFloats(actual: 49.5, expected: 49.5)
Values.ensureEqualFloats(actual: 18, expected: 18.0) ValuesKt.ensureEqualFloats(actual: 18, expected: 18.0)
Values.ensureEqualDoubles(actual: ValuesDouble(value: 12.34), expected: 12.34) ValuesKt.ensureEqualDoubles(actual: KotlinDouble(value: 12.34), expected: 12.34)
Values.ensureEqualDoubles(actual: 56.78, expected: 56.78) ValuesKt.ensureEqualDoubles(actual: 56.78, expected: 56.78)
Values.ensureEqualDoubles(actual: 3, expected: 3) ValuesKt.ensureEqualDoubles(actual: 3, expected: 3)
func checkBox<T: Equatable, B : NSObject>(_ value: T, _ boxFunction: (T) -> B?) throws { func checkBox<T: Equatable, B : NSObject>(_ value: T, _ boxFunction: (T) -> B?) throws {
let box = boxFunction(value)! let box = boxFunction(value)!
@@ -188,27 +188,27 @@ func testNumbers() throws {
try assertTrue(box.isKind(of: B.self)) try assertTrue(box.isKind(of: B.self))
} }
try checkBox(true, Values.box) try checkBox(true, ValuesKt.box)
try checkBox(Int8(-1), Values.box) try checkBox(Int8(-1), ValuesKt.box)
try checkBox(Int16(-2), Values.box) try checkBox(Int16(-2), ValuesKt.box)
try checkBox(Int32(-3), Values.box) try checkBox(Int32(-3), ValuesKt.box)
try checkBox(Int64(-4), Values.box) try checkBox(Int64(-4), ValuesKt.box)
try checkBox(UInt8(5), Values.box) try checkBox(UInt8(5), ValuesKt.box)
try checkBox(UInt16(6), Values.box) try checkBox(UInt16(6), ValuesKt.box)
try checkBox(UInt32(7), Values.box) try checkBox(UInt32(7), ValuesKt.box)
try checkBox(UInt64(8), Values.box) try checkBox(UInt64(8), ValuesKt.box)
try checkBox(Float(8.7), Values.box) try checkBox(Float(8.7), ValuesKt.box)
try checkBox(Double(9.4), Values.box) try checkBox(Double(9.4), ValuesKt.box)
} }
func testLists() throws { func testLists() throws {
let numbersList = Values.numbersList let numbersList = ValuesKt.numbersList
let gold = [1, 2, 13] let gold = [1, 2, 13]
for i in 0..<gold.count { for i in 0..<gold.count {
try assertEquals(actual: gold[i], expected: Int(numbersList[i] as! NSNumber), "Numbers list") try assertEquals(actual: gold[i], expected: Int(numbersList[i] as! NSNumber), "Numbers list")
} }
let anyList = Values.anyList let anyList = ValuesKt.anyList
for i in anyList { for i in anyList {
print(i) print(i)
} }
@@ -216,7 +216,7 @@ func testLists() throws {
} }
func testLazyVal() throws { func testLazyVal() throws {
let lazyVal = Values.lazyVal let lazyVal = ValuesKt.lazyVal
print(lazyVal) print(lazyVal)
try assertEquals(actual: lazyVal, expected: "Lazily initialized string", "lazy value") try assertEquals(actual: lazyVal, expected: "Lazily initialized string", "lazy value")
} }
@@ -224,7 +224,7 @@ func testLazyVal() throws {
let goldenArray = ["Delegated", "global", "array", "property"] let goldenArray = ["Delegated", "global", "array", "property"]
func testDelegatedProp() throws { func testDelegatedProp() throws {
let delegatedGlobalArray = Values.delegatedGlobalArray let delegatedGlobalArray = ValuesKt.delegatedGlobalArray
guard Int(delegatedGlobalArray.size) == goldenArray.count else { guard Int(delegatedGlobalArray.size) == goldenArray.count else {
throw TestError.assertFailed("Size differs") throw TestError.assertFailed("Size differs")
} }
@@ -234,7 +234,7 @@ func testDelegatedProp() throws {
} }
func testGetterDelegate() throws { func testGetterDelegate() throws {
let delegatedList = Values.delegatedList let delegatedList = ValuesKt.delegatedList
guard delegatedList.count == goldenArray.count else { guard delegatedList.count == goldenArray.count else {
throw TestError.assertFailed("Size differs") throw TestError.assertFailed("Size differs")
} }
@@ -244,16 +244,16 @@ func testGetterDelegate() throws {
} }
func testNulls() throws { func testNulls() throws {
let nilVal : Any? = Values.nullVal let nilVal : Any? = ValuesKt.nullVal
try assertTrue(nilVal == nil, "Null value") try assertTrue(nilVal == nil, "Null value")
Values.nullVar = nil ValuesKt.nullVar = nil
var nilVar : Any? = Values.nullVar var nilVar : Any? = ValuesKt.nullVar
try assertTrue(nilVar == nil, "Null variable") try assertTrue(nilVar == nil, "Null variable")
} }
func testAnyVar() throws { func testAnyVar() throws {
let anyVar : Any = Values.anyValue let anyVar : Any = ValuesKt.anyValue
print(anyVar) print(anyVar)
if let str = anyVar as? String { if let str = anyVar as? String {
print(str) print(str)
@@ -264,19 +264,19 @@ func testAnyVar() throws {
} }
func testFunctions() throws { func testFunctions() throws {
let _: Any? = Values.emptyFun() let _: Any? = ValuesKt.emptyFun()
let str = Values.strFun() let str = ValuesKt.strFun()
try assertEquals(actual: str, expected: "fooStr") try assertEquals(actual: str, expected: "fooStr")
try assertEquals(actual: Values.argsFun(i: 10, l: 20, d: 3.5, s: "res") as! String, try assertEquals(actual: ValuesKt.argsFun(i: 10, l: 20, d: 3.5, s: "res") as! String,
expected: "res10203.5") expected: "res10203.5")
} }
func testFuncType() throws { func testFuncType() throws {
let s = "str" let s = "str"
let fFunc: () -> String = { return s } let fFunc: () -> String = { return s }
try assertEquals(actual: Values.funArgument(foo: fFunc), expected: s, "Using function type arguments failed") try assertEquals(actual: ValuesKt.funArgument(foo: fFunc), expected: s, "Using function type arguments failed")
} }
func testGenericsFoo() throws { func testGenericsFoo() throws {
@@ -284,48 +284,48 @@ func testGenericsFoo() throws {
// wrap lambda to workaround issue with type conversion inability: // wrap lambda to workaround issue with type conversion inability:
// (Int) -> String can't be cast to (Any?) -> Any? // (Int) -> String can't be cast to (Any?) -> Any?
let wrapper = { (t: Any?) -> Any? in return fun(t as! Int) } let wrapper = { (t: Any?) -> Any? in return fun(t as! Int) }
let res = Values.genericFoo(t: 42, foo: wrapper) let res = ValuesKt.genericFoo(t: 42, foo: wrapper)
try assertEquals(actual: res as! String, expected: "S 42") try assertEquals(actual: res as! String, expected: "S 42")
} }
func testVararg() throws { func testVararg() throws {
let ktArray = ValuesStdlibArray(size: 3, init: { (_) -> Int in return 42 }) let ktArray = KotlinArray(size: 3, init: { (_) -> Int in return 42 })
let arr: [Int] = Values.varargToList(args: ktArray) as! [Int] let arr: [Int] = ValuesKt.varargToList(args: ktArray) as! [Int]
try assertEquals(actual: arr, expected: [42, 42, 42]) try assertEquals(actual: arr, expected: [42, 42, 42])
} }
func testStrExtFun() throws { func testStrExtFun() throws {
try assertEquals(actual: Values.subExt("String", i: 2), expected: "r") try assertEquals(actual: ValuesKt.subExt("String", i: 2), expected: "r")
try assertEquals(actual: Values.subExt("A", i: 2), expected: "nothing") try assertEquals(actual: ValuesKt.subExt("A", i: 2), expected: "nothing")
} }
func testAnyToString() throws { func testAnyToString() throws {
try assertEquals(actual: Values.toString(nil), expected: "null") try assertEquals(actual: ValuesKt.toString(nil), expected: "null")
try assertEquals(actual: Values.toString(42), expected: "42") try assertEquals(actual: ValuesKt.toString(42), expected: "42")
} }
func testAnyPrint() throws { func testAnyPrint() throws {
print("BEGIN") print("BEGIN")
Values.print(nil) ValuesKt.print(nil)
Values.print("Print") ValuesKt.print("Print")
Values.print(123456789) ValuesKt.print(123456789)
Values.print(3.14) ValuesKt.print(3.14)
Values.print([3, 2, 1]) ValuesKt.print([3, 2, 1])
print("END") print("END")
} }
func testLambda() throws { func testLambda() throws {
try assertEquals(actual: Values.sumLambda(3, 4), expected: 7) try assertEquals(actual: ValuesKt.sumLambda(3, 4), expected: 7)
} }
// -------- Tests for classes and interfaces ------- // -------- Tests for classes and interfaces -------
class ValIEmptyExt : ValuesI { class ValIEmptyExt : I {
func iFun() -> String { func iFun() -> String {
return "ValIEmptyExt::iFun" return "ValIEmptyExt::iFun"
} }
} }
class ValIExt : ValuesI { class ValIExt : I {
func iFun() -> String { func iFun() -> String {
return "ValIExt::iFun" return "ValIExt::iFun"
} }
@@ -337,19 +337,19 @@ func testInterfaceExtension() throws {
} }
func testClassInstances() throws { func testClassInstances() throws {
try assertEquals(actual: ValuesOpenClassI().iFun(), expected: "OpenClassI::iFun") try assertEquals(actual: OpenClassI().iFun(), expected: "OpenClassI::iFun")
try assertEquals(actual: ValuesDefaultInterfaceExt().iFun(), expected: "I::iFun") try assertEquals(actual: DefaultInterfaceExt().iFun(), expected: "I::iFun")
try assertEquals(actual: ValuesFinalClassExtOpen().iFun(), expected: "FinalClassExtOpen::iFun") try assertEquals(actual: FinalClassExtOpen().iFun(), expected: "FinalClassExtOpen::iFun")
try assertEquals(actual: ValuesMultiExtClass().iFun(), expected: "PI::iFun") try assertEquals(actual: MultiExtClass().iFun(), expected: "PI::iFun")
try assertEquals(actual: ValuesMultiExtClass().piFun() as! Int, expected: 42) try assertEquals(actual: MultiExtClass().piFun() as! Int, expected: 42)
try assertEquals(actual: ValuesConstrClass(i: 1, s: "str", a: "Any").iFun(), expected: "OpenClassI::iFun") try assertEquals(actual: ConstrClass(i: 1, s: "str", a: "Any").iFun(), expected: "OpenClassI::iFun")
try assertEquals(actual: ValuesExtConstrClass(i: 123).iFun(), expected: "ExtConstrClass::iFun::123-String-AnyS") try assertEquals(actual: ExtConstrClass(i: 123).iFun(), expected: "ExtConstrClass::iFun::123-String-AnyS")
} }
func testEnum() throws { func testEnum() throws {
try assertEquals(actual: Values.passEnum().enumValue, expected: 42) try assertEquals(actual: ValuesKt.passEnum().enumValue, expected: 42)
try assertEquals(actual: Values.passEnum().name, expected: "ANSWER") try assertEquals(actual: ValuesKt.passEnum().name, expected: "ANSWER")
Values.receiveEnum(e: 1) ValuesKt.receiveEnum(e: 1)
} }
func testDataClass() throws { func testDataClass() throws {
@@ -357,13 +357,13 @@ func testDataClass() throws {
let s = "2" let s = "2"
let t = "3" let t = "3"
let tripleVal = ValuesTripleVals(first: f, second: s, third: t) let tripleVal = TripleVals(first: f, second: s, third: t)
try assertEquals(actual: tripleVal.first as! String, expected: f, "Data class' value") try assertEquals(actual: tripleVal.first as! String, expected: f, "Data class' value")
try assertEquals(actual: tripleVal.component2() as! String, expected: s, "Data class' component") try assertEquals(actual: tripleVal.component2() as! String, expected: s, "Data class' component")
print(tripleVal) print(tripleVal)
try assertEquals(actual: String(describing: tripleVal), expected: "TripleVals(first=\(f), second=\(s), third=\(t))") try assertEquals(actual: String(describing: tripleVal), expected: "TripleVals(first=\(f), second=\(s), third=\(t))")
let tripleVar = ValuesTripleVars(first: f, second: s, third: t) let tripleVar = TripleVars(first: f, second: s, third: t)
try assertEquals(actual: tripleVar.first as! String, expected: f, "Data class' value") try assertEquals(actual: tripleVar.first as! String, expected: f, "Data class' value")
try assertEquals(actual: tripleVar.component2() as! String, expected: s, "Data class' component") try assertEquals(actual: tripleVar.component2() as! String, expected: s, "Data class' component")
print(tripleVar) print(tripleVar)
@@ -377,31 +377,31 @@ func testDataClass() throws {
} }
func testCompanionObj() throws { func testCompanionObj() throws {
try assertEquals(actual: ValuesWithCompanionAndObjectCompanion().str, expected: "String") try assertEquals(actual: WithCompanionAndObjectCompanion().str, expected: "String")
try assertEquals(actual: Values.getCompanionObject().str, expected: "String") try assertEquals(actual: ValuesKt.getCompanionObject().str, expected: "String")
let namedFromCompanion = Values.getCompanionObject().named let namedFromCompanion = ValuesKt.getCompanionObject().named
let named = Values.getNamedObject() let named = ValuesKt.getNamedObject()
try assertTrue(named === namedFromCompanion, "Should be the same Named object") try assertTrue(named === namedFromCompanion, "Should be the same Named object")
try assertEquals(actual: Values.getNamedObjectInterface().iFun(), expected: named.iFun(), "Named object's method") try assertEquals(actual: ValuesKt.getNamedObjectInterface().iFun(), expected: named.iFun(), "Named object's method")
} }
func testInlineClasses() throws { func testInlineClasses() throws {
let ic1: Int32 = 42 let ic1: Int32 = 42
let ic1N = Values.box(ic1: 17) let ic1N = ValuesKt.box(ic1: 17)
let ic2 = "foo" let ic2 = "foo"
let ic2N = "bar" let ic2N = "bar"
let ic3 = ValuesTripleVals(first: 1, second: 2, third: 3) let ic3 = TripleVals(first: 1, second: 2, third: 3)
let ic3N = Values.box(ic3: nil) let ic3N = ValuesKt.box(ic3: nil)
try assertEquals( try assertEquals(
actual: Values.concatenateInlineClassValues(ic1: ic1, ic1N: ic1N, ic2: ic2, ic2N: ic2N, ic3: ic3, ic3N: ic3N), actual: ValuesKt.concatenateInlineClassValues(ic1: ic1, ic1N: ic1N, ic2: ic2, ic2N: ic2N, ic3: ic3, ic3N: ic3N),
expected: "42 17 foo bar TripleVals(first=1, second=2, third=3) null" expected: "42 17 foo bar TripleVals(first=1, second=2, third=3) null"
) )
try assertEquals( try assertEquals(
actual: Values.concatenateInlineClassValues(ic1: ic1, ic1N: nil, ic2: ic2, ic2N: nil, ic3: nil, ic3N: nil), actual: ValuesKt.concatenateInlineClassValues(ic1: ic1, ic1N: nil, ic2: ic2, ic2N: nil, ic3: nil, ic3N: nil),
expected: "42 null foo null null null" expected: "42 null foo null null null"
) )
} }
@@ -29,7 +29,7 @@ class ViewController: UIViewController, UITextViewDelegate, UICollectionViewData
@IBOutlet var input: UITextView! @IBOutlet var input: UITextView!
@IBOutlet var numpad: UICollectionView! @IBOutlet var numpad: UICollectionView!
private let parser = KAPPartialParser(composer: KAPCalculator(), partialComposer: PartialRenderer()) private let parser = PartialParser(composer: Calculator(), partialComposer: PartialRenderer())
@IBAction func numpadButtonPressed(_ sender: UIButton) { @IBAction func numpadButtonPressed(_ sender: UIButton) {
let title = sender.currentTitle! let title = sender.currentTitle!
@@ -112,7 +112,7 @@ private extension String {
} }
} }
private class PartialRenderer: NSObject, KAPPartialExpressionComposer { private class PartialRenderer: NSObject, PartialExpressionComposer {
func missing() -> Any { func missing() -> Any {
return "... ".toAttributed() return "... ".toAttributed()
} }