[MERGE] KT: build-1.5.20-dev-372 KT/N: e734b52b0 OLD: d72e63334

This commit is contained in:
Vasily Levchenko
2021-02-09 09:00:56 +01:00
1995 changed files with 44173 additions and 26832 deletions
+4
View File
@@ -1,3 +1,6 @@
# 1.4.30 (Feb 2021)
* [KT-44083](https://youtrack.jetbrains.com/issue/KT-44083) Fix NSUInteger size for Watchos x64
# 1.4.30-RC (Jan 2021)
* [KT-44271](https://youtrack.jetbrains.com/issue/KT-44271) Incorrect linking when targeting linux_x64 from mingw_x64 host
* [KT-44219](https://youtrack.jetbrains.com/issue/KT-44219) Non-reified type parameters with recursive bounds are not supported yet
@@ -11,6 +14,7 @@
* [KT-43198](https://youtrack.jetbrains.com/issue/KT-43198) Init blocks inside of inline classes
* [KT-42649](https://youtrack.jetbrains.com/issue/KT-42649) Fix secondary constructors of generic inline classes
* [KT-38772](https://youtrack.jetbrains.com/issue/KT-38772) Support non-reified type parameters in typeOf
* [KT-42428](https://youtrack.jetbrains.com/issue/KT-42428) Inconsistent behavior of map.entries on Kotlin.Native
* Compiler customization
* [KT-40584](https://youtrack.jetbrains.com/issue/KT-40584) Untie Kotlin/Native from the fixed LLVM distribution
* [KT-42234](https://youtrack.jetbrains.com/issue/KT-42234) Move LLVM optimization parameters into konan.properties
@@ -18,7 +18,8 @@ internal fun Type.isStret(target: KonanTarget): Boolean {
val unwrappedType = this.unwrapTypedefs()
val abiInfo: ObjCAbiInfo = when (target) {
KonanTarget.IOS_ARM64,
KonanTarget.TVOS_ARM64 -> DarwinArm64AbiInfo()
KonanTarget.TVOS_ARM64,
KonanTarget.MACOS_ARM64 -> DarwinArm64AbiInfo()
KonanTarget.IOS_X64,
KonanTarget.MACOS_X64,
@@ -45,7 +45,7 @@ fun createInteropLibrary(
irVersion = KlibIrVersion.INSTANCE.toString()
)
val libFile = File(outputPath)
val unzippedDir = if (nopack) libFile else org.jetbrains.kotlin.konan.file.createTempDir(moduleName)
val unzippedDir = if (nopack) libFile else org.jetbrains.kotlin.konan.file.createTempDir("klib")
val layout = KonanLibraryLayoutForWriter(libFile, unzippedDir, target)
KonanLibraryWriterImpl(
moduleName,
+26 -21
View File
@@ -25,15 +25,14 @@ the following platforms:
* Ubuntu Linux x86-64 (14.04, 16.04 and later), other Linux flavours may work as well, host and target
(`-target linux_x64`, default on Linux hosts, hosted on Linux, Windows and macOS).
* Microsoft Windows x86-64 (tested on Windows 7 and Windows 10), host and target (`-target mingw_x64`,
default on Windows hosts). Experimental support is available on Linux and macOS hosts (requires Wine).
default on Windows hosts).
* Microsoft Windows x86-32 cross-compiled target (`-target mingw_x86`), hosted on Windows.
Experimental support is available on Linux and macOS hosts (requires Wine).
* Apple iOS (armv7 and arm64 devices, x86 simulator), cross-compiled target
(`-target ios_arm32|ios_arm64|ios_x64`), hosted on macOS.
* Apple tvOS (arm64 devices, x86 simulator), cross-compiled target
(`-target tvos_arm64|tvos_x64`), hosted on macOS.
* Apple watchOS (arm32/arm64 devices, x86 simulator), cross-compiled target
(`-target watchos_arm32|watchos_arm64|watchos_x86`), hosted on macOS.
(`-target watchos_arm32|watchos_arm64|watchos_x86|watchos_x64`), hosted on macOS.
* Linux arm32 hardfp, Raspberry Pi, cross-compiled target (`-target raspberrypi`), hosted on Linux, Windows and macOS
* Linux MIPS big endian, cross-compiled target (`-target mips`), hosted on Linux.
* Linux MIPS little endian, cross-compiled target (`-target mipsel`), hosted on Linux.
@@ -42,11 +41,8 @@ the following platforms:
* WebAssembly (`-target wasm32`) target, hosted on Linux, Windows or macOS. Webassembly support is experimental
and could be discontinued in further releases.
* Experimental support for Zephyr RTOS (`-target zephyr_stm32f4_disco`) is available on macOS, Linux
and Windows hosts.
To enable experimental targets Kotlin/Native must be recompiled with `org.jetbrains.kotlin.native.experimentalTargets` Gradle property set.
Adding support for other target platforms shouldn't be too hard, if LLVM support is available.
and Windows hosts. "Experimental" here also means that support for this target might be broken at the moment,
and UX might be disappointing.
## Compatibility and features ##
@@ -55,28 +51,37 @@ Produced programs are fully self-sufficient and do not need JVM or other runtime
On macOS it also requires Xcode 11.0 or newer to be installed.
The language and library version supported by this release match Kotlin 1.4.
The language and library version supported by this release match Kotlin 1.5.
However, there are certain limitations, see section [Known Limitations](#limitations).
Currently _Kotlin/Native_ uses reference counting based memory management scheme with a cycle
collection algorithm. Multiple threads could be used, but objects must be explicitly transferred
between threads, and same object couldn't be accessed by two threads concurrently.
between threads, and same object couldn't be accessed by two threads concurrently unless it is frozen.
See the relevant [documentation](https://kotlinlang.org/docs/reference/native/concurrency.html).
We are going to lift these multithreading restrictions, which involves implementing a new memory manager.
More details are available in
["Kotlin/Native Memory Management Roadmap"](https://blog.jetbrains.com/kotlin/2020/07/kotlin-native-memory-management-roadmap/).
_Kotlin/Native_ provides efficient interoperability with libraries written in C or Objective-C, and supports
automatic generation of Kotlin bindings from a C/Objective-C header file.
See the samples coming with the distribution.
_Kotlin/Native_ provides efficient bidirectional interoperability with C and Objective-C.
See the [samples](https://github.com/JetBrains/kotlin-native/tree/master/samples)
and the [tutorials](https://kotlinlang.org/docs/tutorials/).
## Getting Started ##
The most complete experience with Kotlin/Native can be achieved by using
[Gradle](https://kotlinlang.org/docs/tutorials/native/using-gradle.html),
[IntelliJ IDEA](https://kotlinlang.org/docs/tutorials/native/using-intellij-idea.html) or
[Android Studio with KMM plugin](https://kotlinlang.org/docs/mobile/create-first-app.html) if you target iOS.
Download _Kotlin/Native_ distribution and unpack it. You can run command line compiler with
If you are interested in using Kotlin/Native for iOS, then
[Kotlin Multiplatform Mobile portal](https://kotlinlang.org/lp/mobile/) might also be useful for you.
Command line compiler is also
[available](https://kotlinlang.org/docs/tutorials/native/using-command-line-compiler.html).
bin/kotlinc <some_file>.kt <dir_with_kt_files> -o <program_name>
During the first run it will download all the external dependencies, such as LLVM.
To see the list of available flags, run `kotlinc -h`.
For documentation on C interoperability stubs see [INTEROP.md](https://github.com/JetBrains/kotlin-native/blob/master/INTEROP.md).
More information can be found in the overviews of
[Kotlin/Native](https://kotlinlang.org/docs/reference/native-overview.html)
and [Kotlin Multiplatform](https://kotlinlang.org/docs/reference/multiplatform.html).
## <a name="limitations"></a>Known limitations ##
@@ -52,13 +52,19 @@ internal class BitcodeCompiler(val context: Context) {
val profilingFlags = llvmProfilingFlags().map { listOf("-mllvm", it) }.flatten()
// LLVM we use does not have support for arm64_32.
// TODO: fix with LLVM update.
val targetTriple = when (context.config.target) {
// LLVM we use does not have support for arm64_32.
KonanTarget.WATCHOS_ARM64 -> {
require(configurables is AppleConfigurables)
"arm64_32-apple-watchos${configurables.osVersionMin}"
}
// Runtime generates bitcode for mythical macos 10.16 because of old Clang.
// Let's fix it.
KonanTarget.MACOS_ARM64 -> {
require(configurables is AppleConfigurables)
"arm64-apple-macos${configurables.osVersionMin}"
}
else -> context.llvm.targetTriple
}
val flags = overrideClangOptions.takeIf(List<String>::isNotEmpty)
@@ -62,7 +62,7 @@ internal class BuiltInFictitiousFunctionIrClassFactory(
// builtClasses.forEach { it.addFakeOverrides() }
}
class FunctionalInterface(val irClass: IrClass, val arity: Int)
class FunctionalInterface(val irClass: IrClass, val descriptor: FunctionClassDescriptor, val arity: Int)
fun buildAllClasses() {
val maxArity = 255 // See [BuiltInFictitiousFunctionClassFactory].
@@ -112,10 +112,10 @@ internal class BuiltInFictitiousFunctionIrClassFactory(
val builtClasses get() = builtClassesMap.values
val builtFunctionNClasses get() = builtClassesMap.values.mapNotNull {
with(it.descriptor as FunctionClassDescriptor) {
val builtFunctionNClasses get() = builtClassesMap.entries.mapNotNull { (descriptor, irClass) ->
with(descriptor) {
if (functionKind == FunctionClassKind.Function)
FunctionalInterface(it, arity)
FunctionalInterface(irClass, descriptor, arity)
else null
}
}
@@ -21,8 +21,6 @@ import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrEnumEntry
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.ir.util.isEnumClass
import org.jetbrains.kotlin.ir.util.isEnumEntry
import org.jetbrains.kotlin.ir.util.referenceFunction
import org.jetbrains.kotlin.konan.target.*
import org.jetbrains.kotlin.name.isChildOf
@@ -35,6 +33,7 @@ import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.types.typeUtil.isUnit
import org.jetbrains.kotlin.types.typeUtil.makeNullable
import org.jetbrains.kotlin.utils.addIfNotNull
private enum class ScopeKind {
TOP,
@@ -361,8 +360,8 @@ private class ExportedElement(val kind: ElementKind,
|
|extern "C" KObjHeader* ${cname}_instance(KObjHeader**);
|static $objectClassC ${cname}_instance_impl(void) {
| KObjHolder result_holder;
| Kotlin_initRuntimeIfNeeded();
| KObjHolder result_holder;
| KObjHeader* result = ${cname}_instance(result_holder.slot());
| return $objectClassC { .pinned = CreateStablePointer(result)};
|}
@@ -379,8 +378,8 @@ private class ExportedElement(val kind: ElementKind,
return """
|extern "C" KObjHeader* $cname(KObjHeader**);
|static $enumClassC ${cname}_impl(void) {
| KObjHolder result_holder;
| Kotlin_initRuntimeIfNeeded();
| KObjHolder result_holder;
| KObjHeader* result = $cname(result_holder.slot());
| return $enumClassC { .pinned = CreateStablePointer(result)};
|}
@@ -466,12 +465,7 @@ private class ExportedElement(val kind: ElementKind,
private fun addUsedType(type: KotlinType, set: MutableSet<ClassDescriptor>) {
if (type.constructor.declarationDescriptor is TypeParameterDescriptor) return
val clazz = TypeUtils.getClassDescriptor(type)
if (clazz == null) {
context.reportCompilationWarning("cannot get class for $type")
} else {
set += clazz
}
set.addIfNotNull(TypeUtils.getClassDescriptor(type))
}
fun addUsedTypes(set: MutableSet<ClassDescriptor>) {
@@ -602,10 +596,7 @@ internal class CAdapterGenerator(val context: Context) : DeclarationDescriptorVi
return true
}
override fun visitScriptDescriptor(descriptor: ScriptDescriptor, ignored: Void?): Boolean {
context.reportCompilationWarning("visitScriptDescriptor() is ignored")
return true
}
override fun visitScriptDescriptor(descriptor: ScriptDescriptor, ignored: Void?) = true
override fun visitPackageViewDescriptor(descriptor: PackageViewDescriptor, ignored: Void?): Boolean {
if (descriptor.module !in moduleDescriptors) return true
@@ -623,15 +614,9 @@ internal class CAdapterGenerator(val context: Context) : DeclarationDescriptorVi
TODO("visitReceiverParameterDescriptor() shall not be seen")
}
override fun visitVariableDescriptor(descriptor: VariableDescriptor, ignored: Void?): Boolean {
context.reportCompilationWarning("visitVariableDescriptor() is ignored for now")
return true
}
override fun visitVariableDescriptor(descriptor: VariableDescriptor, ignored: Void?) = true
override fun visitTypeParameterDescriptor(descriptor: TypeParameterDescriptor, ignored: Void?): Boolean {
context.reportCompilationWarning("visitTypeParameterDescriptor() is ignored for now")
return true
}
override fun visitTypeParameterDescriptor(descriptor: TypeParameterDescriptor, ignored: Void?) = true
private val seenPackageFragments = mutableSetOf<PackageFragmentDescriptor>()
private var currentPackageFragments: List<PackageFragmentDescriptor> = emptyList()
@@ -641,10 +626,7 @@ internal class CAdapterGenerator(val context: Context) : DeclarationDescriptorVi
TODO("Shall not be called directly")
}
override fun visitTypeAliasDescriptor(descriptor: TypeAliasDescriptor, ignored: Void?): Boolean {
context.reportCompilationWarning("visitTypeAliasDescriptor() is ignored for now")
return true
}
override fun visitTypeAliasDescriptor(descriptor: TypeAliasDescriptor, ignored: Void?) = true
override fun visitPackageFragmentDescriptor(descriptor: PackageFragmentDescriptor, ignored: Void?): Boolean {
val fqName = descriptor.fqName
@@ -996,8 +978,8 @@ internal class CAdapterGenerator(val context: Context) : DeclarationDescriptorVi
val argument = if (needArgument) "value, " else ""
output("extern \"C\" KObjHeader* Kotlin_box${it.shortNameForPredefinedType}($parameter$maybeComma KObjHeader**);")
output("static ${translateType(nullableIt)} ${it.createNullableNameForPredefinedType}Impl($parameter) {")
output("KObjHolder result_holder;", 1)
output("Kotlin_initRuntimeIfNeeded();", 1)
output("KObjHolder result_holder;", 1)
output("KObjHeader* result = Kotlin_box${it.shortNameForPredefinedType}($argument result_holder.slot());", 1)
output("return ${translateType(nullableIt)} { .pinned = CreateStablePointer(result) };", 1)
output("}")
@@ -8,18 +8,23 @@ package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportLazy
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportLazyImpl
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportWarningCollector
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportProblemCollector
import org.jetbrains.kotlin.backend.konan.objcexport.dumpObjCHeader
import org.jetbrains.kotlin.container.*
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
internal fun StorageComponentContainer.initContainer(config: KonanConfig) {
this.useImpl<FrontendServices>()
useImpl<FrontendServices>()
if (config.configuration.get(KonanConfigKeys.EMIT_LAZY_OBJC_HEADER_FILE) != null) {
this.useImpl<ObjCExportLazyImpl>()
this.useInstance(ObjCExportWarningCollector.SILENT)
useImpl<ObjCExportLazyImpl>()
useInstance(object : ObjCExportProblemCollector {
override fun reportWarning(text: String) {}
override fun reportWarning(method: FunctionDescriptor, text: String) {}
override fun reportException(throwable: Throwable) = throw throwable
})
useInstance(object : ObjCExportLazy.Configuration {
override val frameworkName: String
@@ -1,6 +1,5 @@
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.backend.common.LoggingContext
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContextImpl
import org.jetbrains.kotlin.backend.common.overrides.FakeOverrideChecker
@@ -38,6 +37,7 @@ internal fun Context.psiToIr(
) {
// Translate AST to high level IR.
val expectActualLinker = config.configuration.get(CommonConfigurationKeys.EXPECT_ACTUAL_LINKER)?:false
val messageLogger = config.configuration.get(IrMessageLogger.IR_MESSAGE_LOGGER) ?: IrMessageLogger.None
val translator = Psi2IrTranslator(config.configuration.languageVersionSettings, Psi2IrConfiguration(false))
val generatorContext = translator.createGeneratorContext(moduleDescriptor, bindingContext, symbolTable)
@@ -89,7 +89,7 @@ internal fun Context.psiToIr(
moduleDescriptor,
functionIrClassFactory,
translationContext,
this as LoggingContext,
messageLogger,
generatorContext.irBuiltIns,
symbolTable,
forwardDeclarationsModuleDescriptor,
@@ -149,7 +149,8 @@ internal fun Context.psiToIr(
generatorContext.symbolTable,
generatorContext.typeTranslator,
generatorContext.irBuiltIns,
linker = irDeserializer
linker = irDeserializer,
diagnosticReporter = messageLogger
)
pluginExtensions.forEach { extension ->
extension.generate(module, pluginContext)
@@ -169,10 +169,11 @@ internal val copyDefaultValuesToActualPhase = konanUnitPhase(
internal val serializerPhase = konanUnitPhase(
op = {
val expectActualLinker = config.configuration.get(CommonConfigurationKeys.EXPECT_ACTUAL_LINKER) ?: false
val messageLogger = config.configuration.get(IrMessageLogger.IR_MESSAGE_LOGGER) ?: IrMessageLogger.None
serializedIr = irModule?.let { ir ->
KonanIrModuleSerializer(
this, ir.irBuiltins, expectDescriptorToSymbol, skipExpects = !expectActualLinker
messageLogger, ir.irBuiltins, expectDescriptorToSymbol, skipExpects = !expectActualLinker
).serializedIrModule(ir)
}
@@ -341,6 +341,10 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
*/
var forwardingForeignExceptionsTerminatedWith: LLVMValueRef? = null
// Whether the generating function needs to initialize Kotlin runtime before execution. Useful for interop bridges,
// for example.
var needsRuntimeInit = false
init {
irFunction?.let {
if (!irFunction.isExported()) {
@@ -1207,6 +1211,9 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
internal fun epilogue() {
appendingTo(prologueBb) {
if (needsRuntimeInit) {
call(context.llvm.initRuntimeIfNeeded, emptyList())
}
val slots = if (needSlotsPhi)
LLVMBuildArrayAlloca(builder, kObjHeaderPtr, Int32(slotCount).llvm, "")!!
else
@@ -20,16 +20,20 @@ import org.jetbrains.kotlin.backend.konan.serialization.resolveFakeOverrideMaybe
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.konan.CompiledKlibModuleOrigin
import org.jetbrains.kotlin.descriptors.konan.CurrentKlibModuleOrigin
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrClassReference
import org.jetbrains.kotlin.ir.expressions.IrVararg
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.konan.target.Family
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.target.LinkerOutputKind
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.DFS
internal fun TypeBridge.makeNothing() = when (this) {
is ReferenceBridge, is BlockPointerBridge -> kNullInt8Ptr
@@ -208,7 +212,7 @@ internal class ObjCExportCodeGenerator(
}
fun FunctionGenerationContext.initRuntimeIfNeeded() {
callFromBridge(context.llvm.initRuntimeIfNeeded, emptyList())
this.needsRuntimeInit = true
}
inline fun FunctionGenerationContext.convertKotlin(
@@ -226,11 +230,18 @@ internal class ObjCExportCodeGenerator(
return callFromBridge(conversion.owner.llvmFunction, listOf(value), resultLifetime)
}
private val objCTypeAdapters = mutableListOf<ObjCTypeAdapter>()
private fun generateTypeAdaptersForKotlinTypes(spec: ObjCExportCodeSpec?): List<ObjCTypeAdapter> {
val types = spec?.types.orEmpty() + objCClassForAny
val allReverseAdapters = createReverseAdapters(types)
return types.map {
val reverseAdapters = allReverseAdapters.getValue(it).adapters
when (it) {
objCClassForAny -> {
createTypeAdapter(it, superClass = null, reverseAdapters)
}
internal fun generate(spec: ObjCExportCodeSpec) {
spec.types.forEach {
objCTypeAdapters += when (it) {
is ObjCClassForKotlinClass -> {
val superClass = it.superClassNotAny ?: objCClassForAny
@@ -238,20 +249,30 @@ internal class ObjCExportCodeGenerator(
// Note: it is generated only to be visible for linker.
// Methods will be added at runtime.
createTypeAdapter(it, superClass)
createTypeAdapter(it, superClass, reverseAdapters)
}
is ObjCProtocolForKotlinInterface -> createTypeAdapter(it, superClass = null)
is ObjCProtocolForKotlinInterface -> createTypeAdapter(it, superClass = null, reverseAdapters)
}
}
spec.files.forEach {
objCTypeAdapters += createTypeAdapterForFileClass(it)
dataGenerator.emitEmptyClass(it.binaryName, namer.kotlinAnyName.binaryName)
}
}
internal fun emitRtti() {
private fun generateTypeAdapters(spec: ObjCExportCodeSpec?) {
val objCTypeAdapters = mutableListOf<ObjCTypeAdapter>()
objCTypeAdapters += generateTypeAdaptersForKotlinTypes(spec)
spec?.files?.forEach {
objCTypeAdapters += createTypeAdapterForFileClass(it)
dataGenerator.emitEmptyClass(it.binaryName, namer.kotlinAnyName.binaryName)
}
emitTypeAdapters(objCTypeAdapters)
}
internal fun generate(spec: ObjCExportCodeSpec?) {
generateTypeAdapters(spec)
NSNumberKind.values().mapNotNull { it.mappedKotlinClassId }.forEach {
dataGenerator.exportClass(namer.numberBoxName(it).binaryName)
}
@@ -261,10 +282,6 @@ internal class ObjCExportCodeGenerator(
emitSpecialClassesConvertions()
objCTypeAdapters += createTypeAdapter(objCClassForAny, superClass = null)
emitTypeAdapters()
// Replace runtime global with weak linkage:
replaceExternalWeakOrCommonGlobal(
"Kotlin_ObjCInterop_uniquePrefix",
@@ -279,7 +296,7 @@ internal class ObjCExportCodeGenerator(
emitKt42254Hint()
}
private fun emitTypeAdapters() {
private fun emitTypeAdapters(objCTypeAdapters: List<ObjCTypeAdapter>) {
val placedClassAdapters = mutableMapOf<String, ConstPointer>()
val placedInterfaceAdapters = mutableMapOf<String, ConstPointer>()
@@ -370,11 +387,16 @@ internal class ObjCExportCodeGenerator(
private val objCClassForAny = ObjCClassForKotlinClass(
namer.kotlinAnyName.binaryName,
symbols.any,
methods = listOf("equals", "hashCode", "toString").map { name ->
symbols.any.owner.simpleFunctions().single { it.name == Name.identifier(name) }
}.map {
require(mapper.shouldBeExposed(it.descriptor))
ObjCMethodForKotlinMethod(it.symbol)
methods = listOf("equals", "hashCode", "toString").map { nameString ->
val name = Name.identifier(nameString)
val irFunction = symbols.any.owner.simpleFunctions().single { it.name == name }
val descriptor = context.builtIns.any.unsubstitutedMemberScope
.getContributedFunctions(name, NoLookupLocation.FROM_BACKEND).single()
val baseMethod = createObjCMethodSpecBaseMethod(mapper, namer, irFunction.symbol, descriptor)
ObjCMethodForKotlinMethod(baseMethod)
},
categoryMethods = emptyList(),
superClassNotAny = null
@@ -629,7 +651,7 @@ private fun ObjCExportCodeGenerator.generateContinuationToCompletionConverter(
private val ObjCExportBlockCodeGenerator.mappedFunctionNClasses get() =
context.ir.symbols.functionIrClassFactory.builtFunctionNClasses
.filter { it.irClass.descriptor.isMappedFunctionClass() }
.filter { it.descriptor.isMappedFunctionClass() }
private fun ObjCExportBlockCodeGenerator.emitFunctionConverters() {
require(context.producedLlvmModuleContainsStdlib)
@@ -957,11 +979,11 @@ private fun ObjCExportCodeGenerator.generateObjCImpForArrayConstructor(
// TODO: cache bridges.
private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge(
irFunction: IrFunction,
baseIrFunction: IrFunction
baseMethod: ObjCMethodSpec.BaseMethod<IrSimpleFunctionSymbol>
): ConstPointer {
val baseMethod = baseIrFunction.descriptor
val baseIrFunction = baseMethod.symbol.owner
val methodBridge = mapper.bridgeMethod(baseMethod)
val methodBridge = baseMethod.bridge
val parameterToBase = irFunction.allParameters.zip(baseIrFunction.allParameters).toMap()
@@ -991,7 +1013,7 @@ private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge(
MethodBridgeReceiver.Instance -> kotlinReferenceToObjC(parameters[parameter]!!)
MethodBridgeSelector -> {
val selector = namer.getSelector(baseMethod)
val selector = baseMethod.selector
// Selector is referenced thus should be defined to avoid false positive non-public API rejection:
selectorsToDefine[selector] = methodBridge
genSelector(selector)
@@ -1022,7 +1044,7 @@ private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge(
val targetResult = callFromBridge(objcMsgSend, objCArgs)
assert(baseMethod !is ConstructorDescriptor)
assert(baseMethod.symbol !is IrConstructorSymbol)
fun rethrow() {
val error = load(errorOutPtr!!)
@@ -1120,14 +1142,14 @@ private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge(
private fun ObjCExportCodeGenerator.createReverseAdapter(
irFunction: IrFunction,
baseMethod: IrFunction,
baseMethod: ObjCMethodSpec.BaseMethod<IrSimpleFunctionSymbol>,
functionName: String,
vtableIndex: Int?,
itablePlace: ClassLayoutBuilder.InterfaceTablePlace?
): ObjCExportCodeGenerator.KotlinToObjCMethodAdapter {
val nameSignature = functionName.localHash.value
val selector = namer.getSelector(baseMethod.descriptor)
val selector = baseMethod.selector
val kotlinToObjC = generateKotlinToObjCBridge(
irFunction,
@@ -1141,51 +1163,51 @@ private fun ObjCExportCodeGenerator.createReverseAdapter(
}
private fun ObjCExportCodeGenerator.createMethodVirtualAdapter(
baseMethod: IrFunction
baseMethod: ObjCMethodSpec.BaseMethod<IrSimpleFunctionSymbol>
): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter {
assert(mapper.isBaseMethod(baseMethod.descriptor))
val selector = namer.getSelector(baseMethod.descriptor)
val methodBridge = mapper.bridgeMethod(baseMethod.descriptor)
val imp = generateObjCImp(baseMethod, baseMethod, methodBridge, isVirtual = true)
val selector = baseMethod.selector
val methodBridge = baseMethod.bridge
val irFunction = baseMethod.symbol.owner
val imp = generateObjCImp(irFunction, irFunction, methodBridge, isVirtual = true)
return objCToKotlinMethodAdapter(selector, methodBridge, imp)
}
private fun ObjCExportCodeGenerator.createMethodAdapter(
implementation: IrFunction?,
baseMethod: IrFunction
baseMethod: ObjCMethodSpec.BaseMethod<*>
) = createMethodAdapter(DirectAdapterRequest(implementation, baseMethod))
private fun ObjCExportCodeGenerator.createFinalMethodAdapter(
irFunction: IrSimpleFunction
baseMethod: ObjCMethodSpec.BaseMethod<IrSimpleFunctionSymbol>
): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter {
val irFunction = baseMethod.symbol.owner
require(irFunction.modality == Modality.FINAL)
return createMethodAdapter(irFunction, irFunction)
return createMethodAdapter(irFunction, baseMethod)
}
private fun ObjCExportCodeGenerator.createMethodAdapter(
request: DirectAdapterRequest
): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter = this.directMethodAdapters.getOrPut(request) {
val selectorName = namer.getSelector(request.base.descriptor)
val methodBridge = mapper.bridgeMethod(request.base.descriptor)
val selectorName = request.base.selector
val methodBridge = request.base.bridge
val imp = generateObjCImp(request.implementation, request.base, methodBridge)
val imp = generateObjCImp(request.implementation, request.base.symbol.owner, methodBridge)
objCToKotlinMethodAdapter(selectorName, methodBridge, imp)
}
private fun ObjCExportCodeGenerator.createConstructorAdapter(
irConstructor: IrConstructor
): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter = createMethodAdapter(irConstructor, irConstructor)
baseMethod: ObjCMethodSpec.BaseMethod<IrConstructorSymbol>
): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter = createMethodAdapter(baseMethod.symbol.owner, baseMethod)
private fun ObjCExportCodeGenerator.createArrayConstructorAdapter(
irConstructor: IrConstructor
baseMethod: ObjCMethodSpec.BaseMethod<IrConstructorSymbol>
): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter {
val selectorName = namer.getSelector(irConstructor.descriptor)
val methodBridge = mapper.bridgeMethod(irConstructor.descriptor)
val selectorName = baseMethod.selector
val methodBridge = baseMethod.bridge
val irConstructor = baseMethod.symbol.owner
val imp = generateObjCImpForArrayConstructor(irConstructor, methodBridge)
return objCToKotlinMethodAdapter(selectorName, methodBridge, imp)
@@ -1217,7 +1239,7 @@ private fun ObjCExportCodeGenerator.createTypeAdapterForFileClass(
): ObjCExportCodeGenerator.ObjCTypeAdapter {
val name = fileClass.binaryName
val adapters = fileClass.methods.map { createFinalMethodAdapter(it.baseMethod.owner) }
val adapters = fileClass.methods.map { createFinalMethodAdapter(it.baseMethod) }
return ObjCTypeAdapter(
irClass = null,
@@ -1237,7 +1259,8 @@ private fun ObjCExportCodeGenerator.createTypeAdapterForFileClass(
private fun ObjCExportCodeGenerator.createTypeAdapter(
type: ObjCTypeForKotlinType,
superClass: ObjCClassForKotlinClass?
superClass: ObjCClassForKotlinClass?,
reverseAdapters: List<ObjCExportCodeGenerator.KotlinToObjCMethodAdapter>
): ObjCExportCodeGenerator.ObjCTypeAdapter {
val irClass = type.irClassSymbol.owner
val adapters = mutableListOf<ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter>()
@@ -1246,39 +1269,45 @@ private fun ObjCExportCodeGenerator.createTypeAdapter(
type.methods.forEach {
when (it) {
is ObjCInitMethodForKotlinConstructor -> {
adapters += createConstructorAdapter(it.irConstructorSymbol.owner)
adapters += createConstructorAdapter(it.baseMethod)
}
is ObjCFactoryMethodForKotlinArrayConstructor -> {
classAdapters += createArrayConstructorAdapter(it.irConstructorSymbol.owner)
classAdapters += createArrayConstructorAdapter(it.baseMethod)
}
is ObjCGetterForKotlinEnumEntry -> {
classAdapters += createEnumEntryAdapter(it.irEnumEntrySymbol.owner)
classAdapters += createEnumEntryAdapter(it.irEnumEntrySymbol.owner, it.selector)
}
is ObjCClassMethodForKotlinEnumValues -> {
classAdapters += createEnumValuesAdapter(it.valuesFunctionSymbol.owner, it.selector)
}
is ObjCGetterForObjectInstance -> {
classAdapters += if (irClass.isUnit()) {
createUnitInstanceAdapter(it.selector)
} else {
createObjectInstanceAdapter(irClass, it.selector)
}
}
is ObjCMethodForKotlinMethod -> {} // Handled below.
}.let {} // Force exhaustive.
}
val reverseAdapters = mutableListOf<ObjCExportCodeGenerator.KotlinToObjCMethodAdapter>()
val additionalReverseAdapters = mutableListOf<ObjCExportCodeGenerator.KotlinToObjCMethodAdapter>()
if (type is ObjCClassForKotlinClass) {
type.categoryMethods.forEach {
val irFunction = it.baseMethod.owner
adapters += createFinalMethodAdapter(irFunction)
reverseAdapters += nonOverridableAdapter(irFunction.descriptor, hasSelectorAmbiguity = false)
adapters += createFinalMethodAdapter(it.baseMethod)
additionalReverseAdapters += nonOverridableAdapter(it.baseMethod.selector, hasSelectorAmbiguity = false)
}
adapters += createDirectAdapters(type, superClass)
}
reverseAdapters += createReverseAdapters(type)
val virtualAdapters = type.kotlinMethods.map { it.baseMethod.owner }
.filter { it.parentAsClass == irClass && it.isOverridable }
.map { createMethodVirtualAdapter(it) }
val virtualAdapters = type.kotlinMethods
.filter {
val irFunction = it.baseMethod.symbol.owner
irFunction.parentAsClass == irClass && irFunction.isOverridable
}.map { createMethodVirtualAdapter(it.baseMethod) }
val typeInfo = constPointer(codegen.typeInfoValue(irClass))
val objCName = type.binaryName
@@ -1309,19 +1338,6 @@ private fun ObjCExportCodeGenerator.createTypeAdapter(
else -> Pair(emptyList(), -1)
}
when (irClass.kind) {
ClassKind.OBJECT -> {
classAdapters += if (irClass.isUnit()) {
createUnitInstanceAdapter()
} else {
createObjectInstanceAdapter(irClass)
}
}
else -> {
// Nothing special.
}
}
return ObjCTypeAdapter(
irClass,
typeInfo,
@@ -1334,21 +1350,64 @@ private fun ObjCExportCodeGenerator.createTypeAdapter(
adapters,
classAdapters,
virtualAdapters,
reverseAdapters
reverseAdapters + additionalReverseAdapters
)
}
private fun ObjCExportCodeGenerator.createReverseAdapters(
type: ObjCTypeForKotlinType
): List<ObjCExportCodeGenerator.KotlinToObjCMethodAdapter> {
types: List<ObjCTypeForKotlinType>
): Map<ObjCTypeForKotlinType, ReverseAdapters> {
val irClassSymbolToType = types.associateBy { it.irClassSymbol }
val result = mutableMapOf<ObjCTypeForKotlinType, ReverseAdapters>()
fun getOrCreateFor(type: ObjCTypeForKotlinType): ReverseAdapters = result.getOrPut(type) {
// Each type also inherits reverse adapters from super types.
// This is handled in runtime when building TypeInfo for Swift or Obj-C type
// subclassing Kotlin classes or interfaces. See [createTypeInfo] in ObjCExport.mm.
val allSuperClasses = DFS.dfs(
type.irClassSymbol.owner.superClasses,
{ it.owner.superClasses },
object : DFS.NodeHandlerWithListResult<IrClassSymbol, IrClassSymbol>() {
override fun afterChildren(current: IrClassSymbol) {
this.result += current
}
}
)
val inheritsAdaptersFrom = allSuperClasses.mapNotNull { irClassSymbolToType[it] }
val inheritedAdapters = inheritsAdaptersFrom.map { getOrCreateFor(it) }
createReverseAdapters(type, inheritedAdapters)
}
types.forEach { getOrCreateFor(it) }
return result
}
private class ReverseAdapters(
val adapters: List<ObjCExportCodeGenerator.KotlinToObjCMethodAdapter>,
val coveredMethods: Set<IrSimpleFunction>
)
private fun ObjCExportCodeGenerator.createReverseAdapters(
type: ObjCTypeForKotlinType,
inheritedAdapters: List<ReverseAdapters>
): ReverseAdapters {
val result = mutableListOf<ObjCExportCodeGenerator.KotlinToObjCMethodAdapter>()
val allBaseMethods = type.kotlinMethods.map { it.baseMethod.owner }.toSet()
val coveredMethods = mutableSetOf<IrSimpleFunction>()
val methodsCoveredByInheritedAdapters = inheritedAdapters.flatMapTo(mutableSetOf()) { it.coveredMethods }
val allBaseMethodsByIr = type.kotlinMethods.map { it.baseMethod }.associateBy { it.symbol.owner }
for (method in type.irClassSymbol.owner.simpleFunctions()) {
val baseMethods = method.allOverriddenFunctions.filter { it in allBaseMethods }
val baseMethods = method.allOverriddenFunctions.mapNotNull { allBaseMethodsByIr[it] }
if (baseMethods.isEmpty()) continue
val hasSelectorAmbiguity = baseMethods.map { namer.getSelector(it.descriptor) }.distinct().size > 1
val hasSelectorAmbiguity = baseMethods.map { it.selector }.distinct().size > 1
if (method.isOverridable && !hasSelectorAmbiguity) {
val baseMethod = baseMethods.first()
@@ -1360,7 +1419,7 @@ private fun ObjCExportCodeGenerator.createReverseAdapters(
val allOverriddenMethods = method.allOverriddenFunctions
val (inherited, uninherited) = allOverriddenMethods.partition {
it != method && mapper.shouldBeExposed(it.descriptor)
it in methodsCoveredByInheritedAdapters
}
inherited.forEach {
@@ -1380,25 +1439,26 @@ private fun ObjCExportCodeGenerator.createReverseAdapters(
presentMethodTableBridges += functionName
presentItableBridges += itablePlace
result += createReverseAdapter(it, baseMethod, functionName, vtableIndex, itablePlace)
coveredMethods += it
}
}
} else {
// Mark it as non-overridable:
baseMethods.distinctBy { namer.getSelector(it.descriptor) }.forEach { baseMethod ->
result += nonOverridableAdapter(baseMethod.descriptor, hasSelectorAmbiguity)
baseMethods.map { it.selector }.distinct().forEach {
result += nonOverridableAdapter(it, hasSelectorAmbiguity)
}
}
}
return result
return ReverseAdapters(result, coveredMethods)
}
private fun ObjCExportCodeGenerator.nonOverridableAdapter(
baseMethod: FunctionDescriptor,
selector: String,
hasSelectorAmbiguity: Boolean
): ObjCExportCodeGenerator.KotlinToObjCMethodAdapter = KotlinToObjCMethodAdapter(
namer.getSelector(baseMethod),
selector,
-1,
vtableIndex = if (hasSelectorAmbiguity) -2 else -1, // Describes the reason.
kotlinImpl = NullPointer(int8Type),
@@ -1408,7 +1468,7 @@ private fun ObjCExportCodeGenerator.nonOverridableAdapter(
private val ObjCTypeForKotlinType.kotlinMethods: List<ObjCMethodForKotlinMethod>
get() = this.methods.filterIsInstance<ObjCMethodForKotlinMethod>()
internal data class DirectAdapterRequest(val implementation: IrFunction?, val base: IrFunction)
internal data class DirectAdapterRequest(val implementation: IrFunction?, val base: ObjCMethodSpec.BaseMethod<*>)
private fun ObjCExportCodeGenerator.createDirectAdapters(
typeDeclaration: ObjCClassForKotlinClass,
@@ -1417,21 +1477,21 @@ private fun ObjCExportCodeGenerator.createDirectAdapters(
fun ObjCClassForKotlinClass.getAllRequiredDirectAdapters() = this.kotlinMethods.map { method ->
DirectAdapterRequest(
findImplementation(irClassSymbol.owner, method.baseMethod.owner, context),
method.baseMethod.owner
findImplementation(irClassSymbol.owner, method.baseMethod.symbol.owner, context),
method.baseMethod
)
}
val inheritedAdapters = superClass?.getAllRequiredDirectAdapters().orEmpty()
val requiredAdapters = typeDeclaration.getAllRequiredDirectAdapters() - inheritedAdapters
return requiredAdapters.distinctBy { namer.getSelector(it.base.descriptor) }.map { createMethodAdapter(it) }
return requiredAdapters.distinctBy { it.base.selector }.map { createMethodAdapter(it) }
}
private fun findImplementation(irClass: IrClass, method: IrSimpleFunction, context: Context): IrSimpleFunction? {
val override = irClass.simpleFunctions().singleOrNull {
method in it.allOverriddenFunctions
} ?: error("no implementation for ${method.descriptor}\nin ${irClass.descriptor}")
} ?: error("no implementation for ${method.render()}\nin ${irClass.fqNameWhenAvailable}")
return OverriddenFunctionInfo(override, method).getImplementation(context)
}
@@ -1464,23 +1524,20 @@ private fun ObjCExportCodeGenerator.objCToKotlinMethodAdapter(
return ObjCToKotlinMethodAdapter(selector, getEncoding(methodBridge), constPointer(imp))
}
private fun ObjCExportCodeGenerator.createUnitInstanceAdapter() =
generateObjCToKotlinSyntheticGetter(
namer.getObjectInstanceSelector(context.builtIns.unit)
) {
private fun ObjCExportCodeGenerator.createUnitInstanceAdapter(selector: String) =
generateObjCToKotlinSyntheticGetter(selector) {
initRuntimeIfNeeded() // For instance methods it gets called when allocating.
ret(callFromBridge(context.llvm.Kotlin_ObjCExport_convertUnit, listOf(codegen.theUnitInstanceRef.llvm)))
}
private fun ObjCExportCodeGenerator.createObjectInstanceAdapter(
irClass: IrClass
irClass: IrClass,
selector: String
): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter {
assert(irClass.kind == ClassKind.OBJECT)
assert(!irClass.isUnit())
val selector = namer.getObjectInstanceSelector(irClass.descriptor)
return generateObjCToKotlinSyntheticGetter(selector) {
initRuntimeIfNeeded() // For instance methods it gets called when allocating.
val value = getObjectValue(irClass, startLocationInfo = null, exceptionHandler = ExceptionHandler.Caller)
@@ -1489,10 +1546,9 @@ private fun ObjCExportCodeGenerator.createObjectInstanceAdapter(
}
private fun ObjCExportCodeGenerator.createEnumEntryAdapter(
irEnumEntry: IrEnumEntry
irEnumEntry: IrEnumEntry,
selector: String
): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter {
val selector = namer.getEnumEntrySelector(irEnumEntry.descriptor)
return generateObjCToKotlinSyntheticGetter(selector) {
initRuntimeIfNeeded() // For instance methods it gets called when allocating.
@@ -1516,14 +1572,6 @@ private fun ObjCExportCodeGenerator.createEnumValuesAdapter(
return objCToKotlinMethodAdapter(selector, methodBridge, imp)
}
private fun List<CallableMemberDescriptor>.toMethods(): List<FunctionDescriptor> = this.flatMap {
when (it) {
is PropertyDescriptor -> listOfNotNull(it.getter, it.setter)
is FunctionDescriptor -> listOf(it)
else -> error(it)
}
}
private fun objCFunctionType(context: Context, methodBridge: MethodBridge): LLVMTypeRef {
val paramTypes = methodBridge.paramBridges.map { it.objCType }
@@ -1623,6 +1671,7 @@ private fun Context.is64BitNSInteger(): Boolean = when (val target = this.config
KonanTarget.TVOS_ARM64,
KonanTarget.TVOS_X64,
KonanTarget.MACOS_X64,
KonanTarget.MACOS_ARM64,
KonanTarget.WATCHOS_X64 -> true
KonanTarget.WATCHOS_ARM64,
KonanTarget.WATCHOS_ARM32,
@@ -1654,6 +1703,7 @@ internal fun Context.is64BitLong(): Boolean = when (this.config.target) {
KonanTarget.MINGW_X64,
KonanTarget.LINUX_X64,
KonanTarget.MACOS_X64,
KonanTarget.MACOS_ARM64,
KonanTarget.WATCHOS_X64 -> true
KonanTarget.WATCHOS_ARM64,
KonanTarget.WATCHOS_ARM32,
@@ -96,12 +96,10 @@ internal class ObjCExport(val context: Context, symbolTable: SymbolTable) {
if (exportedInterface != null) {
produceFrameworkSpecific(exportedInterface.headerLines)
objCCodeGenerator.generate(codeSpec!!)
exportedInterface.generateWorkaroundForSwiftSR10177()
}
objCCodeGenerator.emitRtti()
objCCodeGenerator.generate(codeSpec)
objCCodeGenerator.dispose()
}
@@ -138,8 +136,7 @@ internal class ObjCExport(val context: Context, symbolTable: SymbolTable) {
modules.child("module.modulemap").writeBytes(moduleMap.toByteArray())
emitInfoPlist(frameworkContents, frameworkName)
if (target == KonanTarget.MACOS_X64) {
if (target.family == Family.OSX) {
framework.child("Versions/Current").createAsSymlink("A")
for (child in listOf(frameworkName, "Headers", "Modules", "Resources")) {
framework.child(child).createAsSymlink("Versions/Current/$child")
@@ -165,7 +162,7 @@ internal class ObjCExport(val context: Context, symbolTable: SymbolTable) {
KonanTarget.IOS_X64 -> "iPhoneSimulator"
KonanTarget.TVOS_ARM64 -> "AppleTVOS"
KonanTarget.TVOS_X64 -> "AppleTVSimulator"
KonanTarget.MACOS_X64 -> "MacOSX"
KonanTarget.MACOS_X64, KonanTarget.MACOS_ARM64 -> "MacOSX"
KonanTarget.WATCHOS_ARM32, KonanTarget.WATCHOS_ARM64 -> "WatchOS"
KonanTarget.WATCHOS_X86, KonanTarget.WATCHOS_X64 -> "WatchSimulator"
else -> error(target)
@@ -17,7 +17,14 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
internal fun ObjCExportedInterface.createCodeSpec(symbolTable: SymbolTable): ObjCExportCodeSpec {
fun createObjCMethods(methods: List<FunctionDescriptor>) = methods.map {
ObjCMethodForKotlinMethod(symbolTable.referenceSimpleFunction(it))
ObjCMethodForKotlinMethod(
createObjCMethodSpecBaseMethod(
mapper,
namer,
symbolTable.referenceSimpleFunction(it),
it
)
)
}
fun List<CallableMemberDescriptor>.toObjCMethods() = createObjCMethods(this.flatMap {
@@ -55,17 +62,22 @@ internal fun ObjCExportedInterface.createCodeSpec(symbolTable: SymbolTable): Obj
} else {
descriptor.constructors.filter { mapper.shouldBeExposed(it) }.mapTo(methods) {
val irConstructorSymbol = symbolTable.referenceConstructor(it)
val baseMethod = createObjCMethodSpecBaseMethod(mapper, namer, irConstructorSymbol, it)
if (descriptor.isArray) {
ObjCFactoryMethodForKotlinArrayConstructor(irConstructorSymbol)
ObjCFactoryMethodForKotlinArrayConstructor(baseMethod)
} else {
ObjCInitMethodForKotlinConstructor(irConstructorSymbol)
ObjCInitMethodForKotlinConstructor(baseMethod)
}
}
if (descriptor.kind == ClassKind.OBJECT) {
methods += ObjCGetterForObjectInstance(namer.getObjectInstanceSelector(descriptor))
}
if (descriptor.kind == ClassKind.ENUM_CLASS) {
descriptor.enumEntries.mapTo(methods) {
ObjCGetterForKotlinEnumEntry(symbolTable.referenceEnumEntry(it))
ObjCGetterForKotlinEnumEntry(symbolTable.referenceEnumEntry(it), namer.getEnumEntrySelector(it))
}
descriptor.getEnumValuesFunctionDescriptor()?.let {
@@ -90,28 +102,53 @@ internal fun ObjCExportedInterface.createCodeSpec(symbolTable: SymbolTable): Obj
return ObjCExportCodeSpec(files, types)
}
internal fun <S : IrFunctionSymbol> createObjCMethodSpecBaseMethod(
mapper: ObjCExportMapper,
namer: ObjCExportNamer,
symbol: S,
descriptor: FunctionDescriptor
): ObjCMethodSpec.BaseMethod<S> {
require(mapper.isBaseMethod(descriptor))
val selector = namer.getSelector(descriptor)
val bridge = mapper.bridgeMethod(descriptor)
return ObjCMethodSpec.BaseMethod(symbol, bridge, selector)
}
internal class ObjCExportCodeSpec(
val files: List<ObjCClassForKotlinFile>,
val types: List<ObjCTypeForKotlinType>
)
internal sealed class ObjCMethodSpec
internal sealed class ObjCMethodSpec {
/**
* Aggregates base method (as defined by [ObjCExportMapper.isBaseMethod])
* and details required to generate code for bridges between Kotlin and Obj-C methods.
*/
data class BaseMethod<out S : IrFunctionSymbol>(val symbol: S, val bridge: MethodBridge, val selector: String)
}
internal class ObjCMethodForKotlinMethod(val baseMethod: IrSimpleFunctionSymbol) : ObjCMethodSpec()
internal class ObjCMethodForKotlinMethod(val baseMethod: BaseMethod<IrSimpleFunctionSymbol>) : ObjCMethodSpec()
internal class ObjCInitMethodForKotlinConstructor(val irConstructorSymbol: IrConstructorSymbol) : ObjCMethodSpec()
internal class ObjCInitMethodForKotlinConstructor(val baseMethod: BaseMethod<IrConstructorSymbol>) : ObjCMethodSpec()
internal class ObjCFactoryMethodForKotlinArrayConstructor(
val irConstructorSymbol: IrConstructorSymbol
val baseMethod: BaseMethod<IrConstructorSymbol>
) : ObjCMethodSpec()
internal class ObjCGetterForKotlinEnumEntry(val irEnumEntrySymbol: IrEnumEntrySymbol) : ObjCMethodSpec()
internal class ObjCGetterForKotlinEnumEntry(
val irEnumEntrySymbol: IrEnumEntrySymbol,
val selector: String
) : ObjCMethodSpec()
internal class ObjCClassMethodForKotlinEnumValues(
val valuesFunctionSymbol: IrFunctionSymbol,
val selector: String
) : ObjCMethodSpec()
internal class ObjCGetterForObjectInstance(val selector: String) : ObjCMethodSpec()
internal sealed class ObjCTypeSpec(val binaryName: String)
internal sealed class ObjCTypeForKotlinType(
@@ -37,13 +37,15 @@ interface ObjCExportTranslator {
fun translateExtensions(classDescriptor: ClassDescriptor, declarations: List<CallableMemberDescriptor>): ObjCInterface
}
interface ObjCExportWarningCollector {
interface ObjCExportProblemCollector {
fun reportWarning(text: String)
fun reportWarning(method: FunctionDescriptor, text: String)
fun reportException(throwable: Throwable)
object SILENT : ObjCExportWarningCollector {
object SILENT : ObjCExportProblemCollector {
override fun reportWarning(text: String) {}
override fun reportWarning(method: FunctionDescriptor, text: String) {}
override fun reportException(throwable: Throwable) {}
}
}
@@ -51,70 +53,78 @@ internal class ObjCExportTranslatorImpl(
private val generator: ObjCExportHeaderGenerator?,
val mapper: ObjCExportMapper,
val namer: ObjCExportNamer,
val warningCollector: ObjCExportWarningCollector,
val problemCollector: ObjCExportProblemCollector,
val objcGenerics: Boolean
) : ObjCExportTranslator {
private val kotlinAnyName = namer.kotlinAnyName
override fun generateBaseDeclarations(): List<ObjCTopLevel<*>> {
val stubs = mutableListOf<ObjCTopLevel<*>>()
stubs.add(objCInterface(namer.kotlinAnyName, superClass = "NSObject", members = buildMembers {
+ObjCMethod(null, true, ObjCInstanceType, listOf("init"), emptyList(), listOf("unavailable"))
+ObjCMethod(null, false, ObjCInstanceType, listOf("new"), emptyList(), listOf("unavailable"))
+ObjCMethod(null, false, ObjCVoidType, listOf("initialize"), emptyList(), listOf("objc_requires_super"))
}))
override fun generateBaseDeclarations(): List<ObjCTopLevel<*>> = buildTopLevel {
add {
objCInterface(namer.kotlinAnyName, superClass = "NSObject", members = buildMembers {
add { ObjCMethod(null, true, ObjCInstanceType, listOf("init"), emptyList(), listOf("unavailable")) }
add { ObjCMethod(null, false, ObjCInstanceType, listOf("new"), emptyList(), listOf("unavailable")) }
add { ObjCMethod(null, false, ObjCVoidType, listOf("initialize"), emptyList(), listOf("objc_requires_super")) }
})
}
// TODO: add comment to the header.
stubs.add(ObjCInterfaceImpl(
namer.kotlinAnyName.objCName,
superProtocols = listOf("NSCopying"),
categoryName = "${namer.kotlinAnyName.objCName}Copying"
))
add {
ObjCInterfaceImpl(
namer.kotlinAnyName.objCName,
superProtocols = listOf("NSCopying"),
categoryName = "${namer.kotlinAnyName.objCName}Copying"
)
}
// TODO: only if appears
stubs.add(objCInterface(
namer.mutableSetName,
generics = listOf("ObjectType"),
superClass = "NSMutableSet<ObjectType>"
))
add {
objCInterface(
namer.mutableSetName,
generics = listOf("ObjectType"),
superClass = "NSMutableSet<ObjectType>"
)
}
// TODO: only if appears
stubs.add(objCInterface(
namer.mutableMapName,
generics = listOf("KeyType", "ObjectType"),
superClass = "NSMutableDictionary<KeyType, ObjectType>"
))
add {
objCInterface(
namer.mutableMapName,
generics = listOf("KeyType", "ObjectType"),
superClass = "NSMutableDictionary<KeyType, ObjectType>"
)
}
val nsErrorCategoryName = "NSError${namer.topLevelNamePrefix}KotlinException"
stubs.add(ObjCInterfaceImpl("NSError", categoryName = nsErrorCategoryName, members = buildMembers {
+ObjCProperty("kotlinException", null, ObjCNullableReferenceType(ObjCIdType), listOf("readonly"))
}))
add {
ObjCInterfaceImpl("NSError", categoryName = nsErrorCategoryName, members = buildMembers {
add { ObjCProperty("kotlinException", null, ObjCNullableReferenceType(ObjCIdType), listOf("readonly")) }
})
}
genKotlinNumbers(stubs)
return stubs
genKotlinNumbers()
}
private fun genKotlinNumbers(stubs: MutableList<ObjCTopLevel<*>>) {
private fun StubBuilder<ObjCTopLevel<*>>.genKotlinNumbers() {
val members = buildMembers {
NSNumberKind.values().forEach {
+nsNumberFactory(it, listOf("unavailable"))
add { nsNumberFactory(it, listOf("unavailable")) }
}
NSNumberKind.values().forEach {
+nsNumberInit(it, listOf("unavailable"))
add { nsNumberInit(it, listOf("unavailable")) }
}
}
stubs.add(objCInterface(
namer.kotlinNumberName,
superClass = "NSNumber",
members = members
))
add {
objCInterface(
namer.kotlinNumberName,
superClass = "NSNumber",
members = members
)
}
NSNumberKind.values().forEach {
if (it.mappedKotlinClassId != null) {
stubs += genKotlinNumber(it.mappedKotlinClassId, it)
if (it.mappedKotlinClassId != null) add {
genKotlinNumber(it.mappedKotlinClassId, it)
}
}
}
@@ -123,8 +133,8 @@ internal class ObjCExportTranslatorImpl(
val name = namer.numberBoxName(kotlinClassId)
val members = buildMembers {
+nsNumberFactory(kind)
+nsNumberInit(kind)
add { nsNumberFactory(kind) }
add { nsNumberInit(kind) }
}
return objCInterface(
name,
@@ -316,19 +326,19 @@ internal class ObjCExportTranslatorImpl(
val selector = getSelector(it)
if (!descriptor.isArray) presentConstructors += selector
+buildMethod(it, it, genericExportScope)
add { buildMethod(it, it, genericExportScope) }
exportThrown(it)
if (selector == "init") {
+ObjCMethod(it, false, ObjCInstanceType, listOf("new"), emptyList(),
if (selector == "init") add {
ObjCMethod(it, false, ObjCInstanceType, listOf("new"), emptyList(),
listOf("availability(swift, unavailable, message=\"use object initializers instead\")"))
}
}
if (descriptor.isArray || descriptor.kind == ClassKind.OBJECT || descriptor.kind == ClassKind.ENUM_CLASS) {
+ObjCMethod(null, false, ObjCInstanceType, listOf("alloc"), emptyList(), listOf("unavailable"))
add { ObjCMethod(null, false, ObjCInstanceType, listOf("alloc"), emptyList(), listOf("unavailable")) }
val parameter = ObjCParameter("zone", null, ObjCRawType("struct _NSZone *"))
+ObjCMethod(descriptor, false, ObjCInstanceType, listOf("allocWithZone:"), listOf(parameter), listOf("unavailable"))
add { ObjCMethod(descriptor, false, ObjCInstanceType, listOf("allocWithZone:"), listOf(parameter), listOf("unavailable")) }
}
// Hide "unimplemented" super constructors:
@@ -338,10 +348,10 @@ internal class ObjCExportTranslatorImpl(
?.forEach {
val selector = getSelector(it)
if (selector !in presentConstructors) {
+buildMethod(it, it, ObjCNoneExportScope, unavailable = true)
add { buildMethod(it, it, ObjCNoneExportScope, unavailable = true) }
if (selector == "init") {
+ObjCMethod(null, false, ObjCInstanceType, listOf("new"), emptyList(), listOf("unavailable"))
add { ObjCMethod(null, false, ObjCInstanceType, listOf("new"), emptyList(), listOf("unavailable")) }
}
// TODO: consider adding exception-throwing impls for these.
@@ -350,8 +360,8 @@ internal class ObjCExportTranslatorImpl(
// TODO: consider adding exception-throwing impls for these.
when (descriptor.kind) {
ClassKind.OBJECT -> {
+ObjCMethod(
ClassKind.OBJECT -> add {
ObjCMethod(
null, false, ObjCInstanceType,
listOf(namer.getObjectInstanceSelector(descriptor)), emptyList(),
listOf(swiftNameAttribute("init()"))
@@ -362,8 +372,10 @@ internal class ObjCExportTranslatorImpl(
descriptor.enumEntries.forEach {
val entryName = namer.getEnumEntrySelector(it)
+ObjCProperty(entryName, it, type, listOf("class", "readonly"),
declarationAttributes = listOf(swiftNameAttribute(entryName)))
add {
ObjCProperty(entryName, it, type, listOf("class", "readonly"),
declarationAttributes = listOf(swiftNameAttribute(entryName)))
}
}
// Note: it is possible to support this function through a common machinery,
@@ -371,7 +383,7 @@ internal class ObjCExportTranslatorImpl(
// to keep this ad hoc here than to add special cases to the most complicated parts
// of the machinery.
descriptor.getEnumValuesFunctionDescriptor()?.let { enumValues ->
+buildEnumValuesMethod(enumValues, genericExportScope)
add { buildEnumValuesMethod(enumValues, genericExportScope) }
}
}
else -> {
@@ -434,12 +446,12 @@ internal class ObjCExportTranslatorImpl(
.filter { mapper.shouldBeExposed(it) }
.toList()
private fun StubBuilder.translateClassMembers(descriptor: ClassDescriptor, objCExportScope: ObjCExportScope) {
private fun StubBuilder<Stub<*>>.translateClassMembers(descriptor: ClassDescriptor, objCExportScope: ObjCExportScope) {
require(!descriptor.isInterface)
translateClassMembers(descriptor.getExposedMembers(), objCExportScope)
}
private fun StubBuilder.translateInterfaceMembers(descriptor: ClassDescriptor) {
private fun StubBuilder<Stub<*>>.translateInterfaceMembers(descriptor: ClassDescriptor) {
require(descriptor.isInterface)
translateBaseMembers(descriptor.getExposedMembers())
}
@@ -460,7 +472,7 @@ internal class ObjCExportTranslatorImpl(
}
}
private fun StubBuilder.translateClassMembers(
private fun StubBuilder<Stub<*>>.translateClassMembers(
members: List<CallableMemberDescriptor>,
objCExportScope: ObjCExportScope
) {
@@ -480,18 +492,18 @@ internal class ObjCExportTranslatorImpl(
mapper.getBaseMethods(method)
.asSequence()
.distinctBy { namer.getSelector(it) }
.forEach { base -> +buildMethod(method, base, objCExportScope) }
.forEach { base -> add { buildMethod(method, base, objCExportScope) } }
}
properties.forEach { property ->
mapper.getBaseProperties(property)
.asSequence()
.distinctBy { namer.getPropertyName(it) }
.forEach { base -> +buildProperty(property, base, objCExportScope) }
.forEach { base -> add { buildProperty(property, base, objCExportScope) } }
}
}
private fun StubBuilder.translateBaseMembers(members: List<CallableMemberDescriptor>) {
private fun StubBuilder<Stub<*>>.translateBaseMembers(members: List<CallableMemberDescriptor>) {
// TODO: add some marks about modality.
val methods = mutableListOf<FunctionDescriptor>()
@@ -515,7 +527,7 @@ internal class ObjCExportTranslatorImpl(
translatePlainMembers(methods, properties, ObjCNoneExportScope)
}
private fun StubBuilder.translatePlainMembers(members: List<CallableMemberDescriptor>, objCExportScope: ObjCExportScope) {
private fun StubBuilder<Stub<*>>.translatePlainMembers(members: List<CallableMemberDescriptor>, objCExportScope: ObjCExportScope) {
val methods = mutableListOf<FunctionDescriptor>()
val properties = mutableListOf<PropertyDescriptor>()
@@ -526,9 +538,9 @@ internal class ObjCExportTranslatorImpl(
translatePlainMembers(methods, properties, objCExportScope)
}
private fun StubBuilder.translatePlainMembers(methods: List<FunctionDescriptor>, properties: List<PropertyDescriptor>, objCExportScope: ObjCExportScope) {
methods.forEach { +buildMethod(it, it, objCExportScope) }
properties.forEach { +buildProperty(it, it, objCExportScope) }
private fun StubBuilder<Stub<*>>.translatePlainMembers(methods: List<FunctionDescriptor>, properties: List<PropertyDescriptor>, objCExportScope: ObjCExportScope) {
methods.forEach { add { buildMethod(it, it, objCExportScope) } }
properties.forEach { add { buildProperty(it, it, objCExportScope) } }
}
// TODO: consider checking that signatures for bases with same selector/name are equal.
@@ -798,7 +810,7 @@ internal class ObjCExportTranslatorImpl(
val firstType = types[0]
val secondType = types[1]
warningCollector.reportWarning(
problemCollector.reportWarning(
"Exposed type '$kotlinType' is '$firstType' and '$secondType' at the same time. " +
"This most likely wouldn't work as expected.")
@@ -943,60 +955,27 @@ internal class ObjCExportTranslatorImpl(
// TODO: consider other namings.
}
}
private inline fun buildTopLevel(block: StubBuilder<ObjCTopLevel<*>>.() -> Unit) = buildStubs(block)
private inline fun buildMembers(block: StubBuilder<Stub<*>>.() -> Unit) = buildStubs(block)
private inline fun <S : Stub<*>> buildStubs(block: StubBuilder<S>.() -> Unit): List<S> =
StubBuilder<S>(problemCollector).apply(block).build()
}
abstract class ObjCExportHeaderGenerator internal constructor(
val moduleDescriptors: List<ModuleDescriptor>,
internal val mapper: ObjCExportMapper,
val namer: ObjCExportNamer,
val objcGenerics:Boolean = false
val objcGenerics: Boolean,
problemCollector: ObjCExportProblemCollector
) {
constructor(
moduleDescriptors: List<ModuleDescriptor>,
builtIns: KotlinBuiltIns,
topLevelNamePrefix: String
) : this(moduleDescriptors, builtIns, topLevelNamePrefix, ObjCExportMapper())
private constructor(
moduleDescriptors: List<ModuleDescriptor>,
builtIns: KotlinBuiltIns,
topLevelNamePrefix: String,
mapper: ObjCExportMapper
) : this(
moduleDescriptors,
mapper,
ObjCExportNamerImpl(moduleDescriptors.toSet(), builtIns, mapper, topLevelNamePrefix, local = false)
)
constructor(
moduleDescriptor: ModuleDescriptor,
builtIns: KotlinBuiltIns,
topLevelNamePrefix: String = moduleDescriptor.namePrefix
) : this(moduleDescriptor, emptyList(), builtIns, topLevelNamePrefix)
constructor(
moduleDescriptor: ModuleDescriptor,
exportedDependencies: List<ModuleDescriptor>,
builtIns: KotlinBuiltIns,
topLevelNamePrefix: String = moduleDescriptor.namePrefix
) : this(listOf(moduleDescriptor) + exportedDependencies, builtIns, topLevelNamePrefix)
private val stubs = mutableListOf<Stub<*>>()
private val classForwardDeclarations = linkedSetOf<String>()
private val protocolForwardDeclarations = linkedSetOf<String>()
private val extraClassesToTranslate = mutableSetOf<ClassDescriptor>()
private val translator = ObjCExportTranslatorImpl(this, mapper, namer,
object : ObjCExportWarningCollector {
override fun reportWarning(text: String) =
this@ObjCExportHeaderGenerator.reportWarning(text)
override fun reportWarning(method: FunctionDescriptor, text: String) =
this@ObjCExportHeaderGenerator.reportWarning(method, text)
},
objcGenerics)
private val translator = ObjCExportTranslatorImpl(this, mapper, namer, problemCollector, objcGenerics)
private val generatedClasses = mutableSetOf<ClassDescriptor>()
private val extensions = mutableMapOf<ClassDescriptor, MutableList<CallableMemberDescriptor>>()
@@ -1050,10 +1029,6 @@ abstract class ObjCExportHeaderGenerator internal constructor(
fun getExportStubs(): ObjCExportedStubs =
ObjCExportedStubs(classForwardDeclarations, protocolForwardDeclarations, stubs)
protected abstract fun reportWarning(text: String)
protected abstract fun reportWarning(method: FunctionDescriptor, text: String)
protected open fun getAdditionalImports(): List<String> = emptyList()
fun translateModule() {
@@ -22,21 +22,26 @@ internal class ObjCExportHeaderGeneratorImpl(
mapper: ObjCExportMapper,
namer: ObjCExportNamer,
objcGenerics: Boolean
) : ObjCExportHeaderGenerator(moduleDescriptors, mapper, namer, objcGenerics) {
) : ObjCExportHeaderGenerator(moduleDescriptors, mapper, namer, objcGenerics, ProblemCollector(context)) {
private class ProblemCollector(val context: Context) : ObjCExportProblemCollector {
override fun reportWarning(text: String) {
context.reportCompilationWarning(text)
}
override fun reportWarning(text: String) {
context.reportCompilationWarning(text)
}
override fun reportWarning(method: FunctionDescriptor, text: String) {
val psi = (method as? DeclarationDescriptorWithSource)?.source?.getPsi()
?: return reportWarning(
"$text\n (at ${DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.render(method)})"
)
override fun reportWarning(method: FunctionDescriptor, text: String) {
val psi = (method as? DeclarationDescriptorWithSource)?.source?.getPsi()
?: return reportWarning(
"$text\n (at ${DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.render(method)})"
)
val location = MessageUtil.psiElementToMessageLocation(psi)
val location = MessageUtil.psiElementToMessageLocation(psi)
context.messageCollector.report(CompilerMessageSeverity.WARNING, text, location)
}
context.messageCollector.report(CompilerMessageSeverity.WARNING, text, location)
override fun reportException(throwable: Throwable) {
throw throwable
}
}
override fun getAdditionalImports(): List<String> =
@@ -53,7 +53,7 @@ interface ObjCExportLazy {
@JvmOverloads
fun createObjCExportLazy(
configuration: ObjCExportLazy.Configuration,
warningCollector: ObjCExportWarningCollector,
problemCollector: ObjCExportProblemCollector,
codeAnalyzer: KotlinCodeAnalyzer,
typeResolver: TypeResolver,
descriptorResolver: DescriptorResolver,
@@ -62,7 +62,7 @@ fun createObjCExportLazy(
deprecationResolver: DeprecationResolver? = null
): ObjCExportLazy = ObjCExportLazyImpl(
configuration,
warningCollector,
problemCollector,
codeAnalyzer,
typeResolver,
descriptorResolver,
@@ -73,7 +73,7 @@ fun createObjCExportLazy(
internal class ObjCExportLazyImpl(
private val configuration: ObjCExportLazy.Configuration,
warningCollector: ObjCExportWarningCollector,
problemCollector: ObjCExportProblemCollector,
private val codeAnalyzer: KotlinCodeAnalyzer,
private val typeResolver: TypeResolver,
private val descriptorResolver: DescriptorResolver,
@@ -94,8 +94,8 @@ internal class ObjCExportLazyImpl(
null,
mapper,
namer,
warningCollector,
objcGenerics = configuration.objcGenerics
problemCollector,
configuration.objcGenerics
)
private val isValid: Boolean
@@ -2,7 +2,6 @@ package org.jetbrains.kotlin.backend.konan.objcexport
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
import org.jetbrains.kotlin.resolve.descriptorUtil.module
@@ -11,15 +10,15 @@ class ObjcExportHeaderGeneratorMobile internal constructor(
moduleDescriptors: List<ModuleDescriptor>,
mapper: ObjCExportMapper,
namer: ObjCExportNamer,
private val warningCollector: ObjCExportWarningCollector,
problemCollector: ObjCExportProblemCollector,
objcGenerics: Boolean,
private val restrictToLocalModules: Boolean
) : ObjCExportHeaderGenerator(moduleDescriptors, mapper, namer, objcGenerics) {
) : ObjCExportHeaderGenerator(moduleDescriptors, mapper, namer, objcGenerics, problemCollector) {
companion object {
fun createInstance(
configuration: ObjCExportLazy.Configuration,
warningCollector: ObjCExportWarningCollector,
problemCollector: ObjCExportProblemCollector,
builtIns: KotlinBuiltIns,
moduleDescriptors: List<ModuleDescriptor>,
deprecationResolver: DeprecationResolver? = null,
@@ -33,7 +32,7 @@ class ObjcExportHeaderGeneratorMobile internal constructor(
moduleDescriptors,
mapper,
namer,
warningCollector,
problemCollector,
configuration.objcGenerics,
restrictToLocalModules
)
@@ -42,12 +41,4 @@ class ObjcExportHeaderGeneratorMobile internal constructor(
override fun shouldTranslateExtraClass(descriptor: ClassDescriptor): Boolean =
!restrictToLocalModules || descriptor.module in moduleDescriptors
override fun reportWarning(text: String) {
warningCollector.reportWarning(text)
}
override fun reportWarning(method: FunctionDescriptor, text: String) {
warningCollector.reportWarning(method, text)
}
}
@@ -5,21 +5,20 @@
package org.jetbrains.kotlin.backend.konan.objcexport
internal class StubBuilder {
private val children = mutableListOf<Stub<*>>()
internal class StubBuilder<S : Stub<*>>(private val problemCollector: ObjCExportProblemCollector) {
private val children = mutableListOf<S>()
operator fun Stub<*>.unaryPlus() {
children.add(this)
inline fun add(provider: () -> S) {
try {
children.add(provider())
} catch (t: Throwable) {
problemCollector.reportException(t)
}
}
operator fun plusAssign(set: Collection<Stub<*>>) {
operator fun plusAssign(set: Collection<S>) {
children += set
}
fun build() = children
}
internal inline fun buildMembers(block: StubBuilder.() -> Unit): List<Stub<*>> = StubBuilder().let {
it.block()
it.build()
fun build(): List<S> = children
}
@@ -1,6 +1,5 @@
package org.jetbrains.kotlin.backend.konan.serialization
import org.jetbrains.kotlin.backend.common.LoggingContext
import org.jetbrains.kotlin.backend.common.serialization.DeclarationTable
import org.jetbrains.kotlin.backend.common.serialization.IrFileSerializer
import org.jetbrains.kotlin.backend.konan.RuntimeNames
@@ -9,15 +8,16 @@ import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.util.IrMessageLogger
import org.jetbrains.kotlin.ir.util.hasAnnotation
class KonanIrFileSerializer(
logger: LoggingContext,
messageLogger: IrMessageLogger,
declarationTable: DeclarationTable,
expectDescriptorToSymbol: MutableMap<DeclarationDescriptor, IrSymbol>,
skipExpects: Boolean,
bodiesOnlyForInlines: Boolean = false
): IrFileSerializer(logger, declarationTable, expectDescriptorToSymbol, skipExpects = skipExpects, bodiesOnlyForInlines = bodiesOnlyForInlines) {
): IrFileSerializer(messageLogger, declarationTable, expectDescriptorToSymbol, skipExpects = skipExpects, bodiesOnlyForInlines = bodiesOnlyForInlines) {
override fun backendSpecificExplicitRoot(node: IrAnnotationContainer): Boolean {
val fqn = when (node) {
@@ -1,6 +1,5 @@
package org.jetbrains.kotlin.backend.konan.serialization
import org.jetbrains.kotlin.backend.common.LoggingContext
import org.jetbrains.kotlin.backend.common.serialization.*
import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureSerializer
import org.jetbrains.kotlin.backend.konan.ir.interop.IrProviderForCEnumAndCStructStubs
@@ -8,13 +7,14 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.util.IrMessageLogger
class KonanIrModuleSerializer(
logger: LoggingContext,
messageLogger: IrMessageLogger,
irBuiltIns: IrBuiltIns,
private val expectDescriptorToSymbol: MutableMap<DeclarationDescriptor, IrSymbol>,
val skipExpects: Boolean
) : IrModuleSerializer<KonanIrFileSerializer>(logger) {
) : IrModuleSerializer<KonanIrFileSerializer>(messageLogger) {
private val signaturer = IdSignatureSerializer(KonanManglerIr)
private val globalDeclarationTable = KonanGlobalDeclarationTable(signaturer, irBuiltIns)
@@ -29,5 +29,5 @@ class KonanIrModuleSerializer(
file.fileEntry.name != IrProviderForCEnumAndCStructStubs.cTypeDefinitionsFileName
override fun createSerializerForFile(file: IrFile): KonanIrFileSerializer =
KonanIrFileSerializer(logger, KonanDeclarationTable(globalDeclarationTable), expectDescriptorToSymbol, skipExpects = skipExpects)
KonanIrFileSerializer(messageLogger, KonanDeclarationTable(globalDeclarationTable), expectDescriptorToSymbol, skipExpects = skipExpects)
}
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.backend.konan.serialization
import org.jetbrains.kotlin.backend.common.LoggingContext
import org.jetbrains.kotlin.backend.common.overrides.FakeOverrideBuilder
import org.jetbrains.kotlin.backend.common.overrides.FakeOverrideClassFilter
import org.jetbrains.kotlin.backend.common.serialization.*
@@ -71,7 +70,7 @@ internal class KonanIrLinker(
private val currentModule: ModuleDescriptor,
override val functionalInterfaceFactory: IrAbstractFunctionFactory,
override val translationPluginContext: TranslationPluginContext?,
logger: LoggingContext,
messageLogger: IrMessageLogger,
builtIns: IrBuiltIns,
symbolTable: SymbolTable,
private val forwardModuleDescriptor: ModuleDescriptor?,
@@ -79,7 +78,7 @@ internal class KonanIrLinker(
private val cenumsProvider: IrProviderForCEnumAndCStructStubs,
exportedDependencies: List<ModuleDescriptor>,
private val cachedLibraries: CachedLibraries
) : KotlinIrLinker(currentModule, logger, builtIns, symbolTable, exportedDependencies) {
) : KotlinIrLinker(currentModule, messageLogger, builtIns, symbolTable, exportedDependencies) {
companion object {
private val C_NAMES_NAME = Name.identifier("cnames")
+235 -60
View File
@@ -153,6 +153,8 @@ tasks.withType(RunExternalTestGroup.class).configureEach {
enableTwoStageCompilation = twoStageEnabled
}
ext.isExperimentalMM = project.globalTestArgs.contains("-memory-model") && project.globalTestArgs.contains("experimental")
allprojects {
// Root directories for test output (logs, compiled files, statistics etc). Only single path must be in each set.
// backend.native/tests
@@ -424,7 +426,16 @@ Task dynamicTest(String name, Closure<KonanDynamicTest> configureClosure) {
if (task.enabled) {
konanArtifacts {
def targetName = target.name
def lib = task.interop
if (lib != null) {
UtilsKt.dependsOnKonanBuildingTask(task, lib, target)
}
dynamic(name, targets: [targetName]) {
if (lib != null) {
libraries {
artifact lib
}
}
srcFiles task.getSources()
baseDir "$testOutputLocal/$name"
extraOpts task.flags
@@ -803,7 +814,8 @@ task runtime_basic_simd(type: KonanLocalTest) {
}
task runtime_worker_random(type: KonanLocalTest) {
enabled = (project.testTarget != 'wasm32') // Uses workers.
enabled = (project.testTarget != 'wasm32') && // Uses workers.
!isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
source = "runtime/basic/worker_random.kt"
}
@@ -917,146 +929,170 @@ task empty_substring(type: KonanLocalTest) {
}
standaloneTest("cleaner_basic") {
enabled = (project.testTarget != 'wasm32') // Cleaners need workers
enabled = (project.testTarget != 'wasm32') && // Cleaners need workers
!isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
source = "runtime/basic/cleaner_basic.kt"
flags = ['-tr', '-Xopt-in=kotlin.native.internal.InternalForKotlinNative']
}
standaloneTest("cleaner_workers") {
enabled = (project.testTarget != 'wasm32') // Cleaners need workers
enabled = (project.testTarget != 'wasm32') && // Cleaners need workers
!isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
source = "runtime/basic/cleaner_workers.kt"
flags = ['-tr', '-Xopt-in=kotlin.native.internal.InternalForKotlinNative']
}
standaloneTest("cleaner_in_main_with_checker") {
enabled = (project.testTarget != 'wasm32') // Cleaners need workers
enabled = (project.testTarget != 'wasm32') && // Cleaners need workers
!isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
source = "runtime/basic/cleaner_in_main_with_checker.kt"
goldValue = "42\n"
}
standaloneTest("cleaner_in_main_without_checker") {
enabled = (project.testTarget != 'wasm32') // Cleaners need workers
enabled = (project.testTarget != 'wasm32') && // Cleaners need workers
!isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
source = "runtime/basic/cleaner_in_main_without_checker.kt"
goldValue = ""
}
standaloneTest("cleaner_leak_without_checker") {
enabled = (project.testTarget != 'wasm32') // Cleaners need workers
enabled = (project.testTarget != 'wasm32') && // Cleaners need workers
!isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
source = "runtime/basic/cleaner_leak_without_checker.kt"
goldValue = ""
}
standaloneTest("cleaner_leak_with_checker") {
enabled = (project.testTarget != 'wasm32') // Cleaners need workers
enabled = (project.testTarget != 'wasm32') && // Cleaners need workers
!isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
source = "runtime/basic/cleaner_leak_with_checker.kt"
expectedExitStatusChecker = { it != 0 }
outputChecker = { s -> (s =~ /Cleaner (0x)?[0-9a-fA-F]+ was disposed during program exit/).find() }
}
standaloneTest("cleaner_in_tls_main_without_checker") {
enabled = (project.testTarget != 'wasm32') // Cleaners need workers
enabled = (project.testTarget != 'wasm32') && // Cleaners need workers
!isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
source = "runtime/basic/cleaner_in_tls_main_without_checker.kt"
}
standaloneTest("cleaner_in_tls_main_with_checker") {
enabled = (project.testTarget != 'wasm32') // Cleaners need workers
enabled = (project.testTarget != 'wasm32') && // Cleaners need workers
!isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
source = "runtime/basic/cleaner_in_tls_main_with_checker.kt"
expectedExitStatusChecker = { it != 0 }
outputChecker = { s -> (s =~ /Cleaner (0x)?[0-9a-fA-F]+ was disposed during program exit/).find() }
}
standaloneTest("cleaner_in_tls_worker") {
enabled = (project.testTarget != 'wasm32') // Cleaners need workers
enabled = (project.testTarget != 'wasm32') && // Cleaners need workers
!isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
source = "runtime/basic/cleaner_in_tls_worker.kt"
flags = ['-Xopt-in=kotlin.native.internal.InternalForKotlinNative']
}
standaloneTest("worker_bound_reference0") {
enabled = (project.testTarget != 'wasm32') // Workers need pthreads.
enabled = (project.testTarget != 'wasm32') && // Workers need pthreads.
!isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
source = "runtime/concurrent/worker_bound_reference0.kt"
flags = ['-tr']
}
task worker0(type: KonanLocalTest) {
enabled = (project.testTarget != 'wasm32') // Workers need pthreads.
enabled = (project.testTarget != 'wasm32') && // Workers need pthreads.
!isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
goldValue = "Got Input processed\nOK\n"
source = "runtime/workers/worker0.kt"
}
task worker1(type: KonanLocalTest) {
enabled = (project.testTarget != 'wasm32') // Workers need pthreads.
enabled = (project.testTarget != 'wasm32') && // Workers need pthreads.
!isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
goldValue = "OK\n"
source = "runtime/workers/worker1.kt"
}
task worker2(type: KonanLocalTest) {
enabled = (project.testTarget != 'wasm32') // Workers need pthreads.
enabled = (project.testTarget != 'wasm32') && // Workers need pthreads.
!isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
goldValue = "OK\n"
source = "runtime/workers/worker2.kt"
}
task worker3(type: KonanLocalTest) {
enabled = (project.testTarget != 'wasm32') // Workers need pthreads.
enabled = (project.testTarget != 'wasm32') && // Workers need pthreads.
!isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
goldValue = "OK\n"
source = "runtime/workers/worker3.kt"
}
task worker4(type: KonanLocalTest) {
enabled = (project.testTarget != 'wasm32') // Workers need pthreads.
enabled = (project.testTarget != 'wasm32') && // Workers need pthreads.
!isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
goldValue = "Got 42\nOK\n"
source = "runtime/workers/worker4.kt"
}
// This tests changes main thread worker queue state, so better be executed alone.
standaloneTest("worker5") {
enabled = (project.testTarget != 'wasm32') // Workers need pthreads.
enabled = (project.testTarget != 'wasm32') && // Workers need pthreads.
!isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
goldValue = "Got 3\nOK\n"
source = "runtime/workers/worker5.kt"
}
task worker6(type: KonanLocalTest) {
enabled = (project.testTarget != 'wasm32') // Workers need pthreads.
enabled = (project.testTarget != 'wasm32') && // Workers need pthreads.
!isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
goldValue = "Got 42\nOK\n"
source = "runtime/workers/worker6.kt"
}
task worker7(type: KonanLocalTest) {
enabled = (project.testTarget != 'wasm32') // Workers need pthreads.
enabled = (project.testTarget != 'wasm32') && // Workers need pthreads.
!isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
goldValue = "Input\nGot kotlin.Unit\nOK\n"
source = "runtime/workers/worker7.kt"
}
task worker8(type: KonanLocalTest) {
enabled = (project.testTarget != 'wasm32') // Workers need pthreads.
enabled = (project.testTarget != 'wasm32') && // Workers need pthreads.
!isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
goldValue = "SharedData(string=Hello, int=10, member=SharedDataMember(double=0.1))\nGot kotlin.Unit\nOK\n"
source = "runtime/workers/worker8.kt"
}
task worker9(type: KonanLocalTest) {
enabled = (project.testTarget != 'wasm32') // Workers need pthreads.
enabled = (project.testTarget != 'wasm32') && // Workers need pthreads.
!isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
goldValue = "zzz\n42\nOK\nfirst 2\nsecond 3\nfrozen OK\n"
source = "runtime/workers/worker9.kt"
}
task worker10(type: KonanLocalTest) {
enabled = (project.testTarget != 'wasm32') // Workers need pthreads.
enabled = (project.testTarget != 'wasm32') && // Workers need pthreads.
!isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
goldValue = "OK\ntrue\ntrue\n"
source = "runtime/workers/worker10.kt"
}
task worker11(type: KonanLocalTest) {
enabled = (project.testTarget != 'wasm32') // Workers need pthreads.
enabled = (project.testTarget != 'wasm32') && // Workers need pthreads.
!isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
goldValue = "OK\n"
source = "runtime/workers/worker11.kt"
}
standaloneTest("worker_threadlocal_no_leak") {
disabled = (project.testTarget == 'wasm32') // Needs pthreads.
disabled = (project.testTarget == 'wasm32') || // Needs pthreads.
isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
source = "runtime/workers/worker_threadlocal_no_leak.kt"
}
task freeze0(type: KonanLocalTest) {
enabled = (project.testTarget != 'wasm32') // No workers on WASM.
enabled = (project.testTarget != 'wasm32') && // No workers on WASM.
!isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
goldValue = "frozen bit is true\n" +
"Worker: SharedData(string=Hello, int=10, member=SharedDataMember(double=0.1))\n" +
"Main: SharedData(string=Hello, int=10, member=SharedDataMember(double=0.1))\n" +
@@ -1065,19 +1101,22 @@ task freeze0(type: KonanLocalTest) {
}
task freeze1(type: KonanLocalTest) {
enabled = (project.testTarget != 'wasm32') // No exceptions on WASM.
enabled = (project.testTarget != 'wasm32') && // No exceptions on WASM.
!isExperimentalMM // Experimental MM does not support freezing yet.
goldValue = "OK, cannot mutate frozen\n"
source = "runtime/workers/freeze1.kt"
}
task freeze_stress(type: KonanLocalTest) {
enabled = (project.testTarget != 'wasm32') // No exceptions on WASM.
enabled = (project.testTarget != 'wasm32') && // No exceptions on WASM.
!isExperimentalMM // Experimental MM does not support freezing yet.
goldValue = "OK\n"
source = "runtime/workers/freeze_stress.kt"
}
task freeze2(type: KonanLocalTest) {
enabled = (project.testTarget != 'wasm32') // No exceptions on WASM.
enabled = (project.testTarget != 'wasm32') && // No exceptions on WASM.
!isExperimentalMM // Experimental MM does not support freezing yet.
goldValue =
"Worker 1: Hello world\n" + "Worker2: 42\n" +
"Worker3: 239.0\n" + "Worker4: a\n" + "OK\n"
@@ -1085,30 +1124,35 @@ task freeze2(type: KonanLocalTest) {
}
task freeze3(type: KonanLocalTest) {
enabled = (project.testTarget != 'wasm32') // No exceptions on WASM.
enabled = (project.testTarget != 'wasm32') && // No exceptions on WASM.
!isExperimentalMM // Experimental MM does not support freezing yet.
goldValue = "OK\n"
source = "runtime/workers/freeze3.kt"
}
task freeze4(type: KonanLocalTest) {
enabled = (project.testTarget != 'wasm32') // No exceptions on WASM.
enabled = (project.testTarget != 'wasm32') && // No exceptions on WASM.
!isExperimentalMM // Experimental MM does not support freezing yet.
goldValue = "OK\n"
source = "runtime/workers/freeze4.kt"
}
task freeze5(type: KonanLocalTest) {
enabled = !isExperimentalMM // Experimental MM does not support freezing yet.
goldValue = "OK\n"
source = "runtime/workers/freeze5.kt"
}
task freeze6(type: KonanLocalTest) {
enabled = (project.testTarget != 'wasm32') // No exceptions on WASM.
enabled = (project.testTarget != 'wasm32') && // No exceptions on WASM.
!isExperimentalMM // Experimental MM does not support freezing yet.
goldValue = "OK\nOK\n"
source = "runtime/workers/freeze6.kt"
}
task atomic0(type: KonanLocalTest) {
enabled = (project.testTarget != 'wasm32') // Workers need pthreads.
enabled = (project.testTarget != 'wasm32') && // Workers need pthreads.
!isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
goldValue = "35\n" + "20\n" + "OK\n"
source = "runtime/workers/atomic0.kt"
}
@@ -1120,46 +1164,54 @@ standaloneTest("atomic1") {
}
task lazy0(type: KonanLocalTest) {
enabled = (project.testTarget != 'wasm32') // Workers need pthreads.
enabled = (project.testTarget != 'wasm32') && // Workers need pthreads.
!isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
goldValue = "OK\n"
source = "runtime/workers/lazy0.kt"
}
task lazy1(type: KonanLocalTest) {
enabled = (project.testTarget != 'wasm32') // Need exceptions.
enabled = (project.testTarget != 'wasm32') && // Need exceptions.
!isExperimentalMM // Experimental MM does not support freezing yet.
goldValue = "OK\n"
source = "runtime/workers/lazy1.kt"
}
standaloneTest("lazy2") {
enabled = !isExperimentalMM // Experimental MM does not have a GC yet.
goldValue = "123\nOK\n"
source = "runtime/workers/lazy2.kt"
}
standaloneTest("lazy3") {
enabled = !isExperimentalMM // Experimental MM does not have a GC yet.
source = "runtime/workers/lazy3.kt"
}
task mutableData1(type: KonanLocalTest) {
enabled = (project.testTarget != 'wasm32') // Workers need pthreads. Need exceptions
enabled = (project.testTarget != 'wasm32') && // Workers need pthreads. Need exceptions
!isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
source = "runtime/workers/mutableData1.kt"
}
task enumIdentity(type: KonanLocalTest) {
enabled = (project.testTarget != 'wasm32') // Workers need pthreads.
enabled = (project.testTarget != 'wasm32') && // Workers need pthreads.
!isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
goldValue = "true\n"
source = "runtime/workers/enum_identity.kt"
}
standaloneTest("leakWorker") {
disabled = (project.testTarget == 'wasm32') // Needs pthreads.
disabled = (project.testTarget == 'wasm32') || // Needs pthreads.
isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
source = "runtime/workers/leak_worker.kt"
expectedExitStatusChecker = { it != 0 }
outputChecker = { s -> s.contains("Unfinished workers detected, 1 workers leaked!") }
}
standaloneTest("leakMemoryWithWorkerTermination") {
disabled = (project.testTarget == 'wasm32') // Needs pthreads.
disabled = (project.testTarget == 'wasm32') || // Needs pthreads.
isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
source = "runtime/workers/leak_memory_with_worker_termination.kt"
expectedExitStatusChecker = { it != 0 }
outputChecker = { s -> s.contains("Memory leaks detected, 1 objects leaked!") }
@@ -1242,6 +1294,7 @@ task enum_nested(type: KonanLocalTest) {
}
task enum_isFrozen(type: KonanLocalTest) {
enabled = !isExperimentalMM // Experimental MM does not support freezing yet.
goldValue = "true\n"
source = "codegen/enum/isFrozen.kt"
}
@@ -1992,7 +2045,8 @@ task typed_array0(type: KonanLocalTest) {
}
task typed_array1(type: KonanLocalTest) {
enabled = (project.testTarget != 'wasm32') // No exceptions on WASM.
enabled = (project.testTarget != 'wasm32') && // No exceptions on WASM.
!isExperimentalMM // Experimental MM does not support freezing yet.
goldValue = "OK\n"
source = "runtime/collections/typed_array1.kt"
}
@@ -2638,21 +2692,24 @@ standaloneTest("args0") {
}
standaloneTest("devirtualization_lateinitInterface") {
disabled = (cacheTesting != null) // Cache is not compatible with -opt.
disabled = (cacheTesting != null) || // Cache is not compatible with -opt.
isExperimentalMM // Experimental MM does not support -opt yet.
goldValue = "42\n"
flags = ["-opt"]
source = "codegen/devirtualization/lateinitInterface.kt"
}
standaloneTest("devirtualization_getter_looking_as_box_function") {
disabled = (cacheTesting != null) // Cache is not compatible with -opt.
disabled = (cacheTesting != null) || // Cache is not compatible with -opt.
isExperimentalMM // Experimental MM does not support -opt yet.
goldValue = "box\n"
flags = ["-opt"]
source = "codegen/devirtualization/getter_looking_as_box_function.kt"
}
standaloneTest("devirtualization_anonymousObject") {
disabled = (cacheTesting != null) // Cache is not compatible with -opt.
disabled = (cacheTesting != null) || // Cache is not compatible with -opt.
isExperimentalMM // Experimental MM does not support -opt yet.
goldValue = "zzz\n"
flags = ["-opt"]
source = "codegen/devirtualization/anonymousObject.kt"
@@ -2848,7 +2905,8 @@ task initializers5(type: KonanLocalTest) {
}
task initializers6(type: KonanLocalTest) {
disabled = project.testTarget == 'wasm32' // Needs workers.
disabled = (project.testTarget == 'wasm32') || // Needs workers.
isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
source = "runtime/basic/initializers6.kt"
}
@@ -2856,6 +2914,10 @@ task initializers7(type: KonanLocalTest) {
source = "runtime/basic/initializers7.kt"
}
task initializers8(type: KonanLocalTest) {
source = "runtime/basic/initializers8.kt"
}
task expression_as_statement(type: KonanLocalTest) {
expectedFail = (project.testTarget == 'wasm32') // uses exceptions.
goldValue = "Ok\n"
@@ -2902,11 +2964,13 @@ task memory_escape1(type: KonanLocalTest) {
}
task memory_cycles0(type: KonanLocalTest) {
enabled = !isExperimentalMM // Experimental MM does not have a GC yet.
goldValue = "42\n"
source = "runtime/memory/cycles0.kt"
}
task memory_cycles1(type: KonanLocalTest) {
enabled = !isExperimentalMM // Experimental MM does not have a GC yet.
source = "runtime/memory/cycles1.kt"
}
@@ -2920,6 +2984,7 @@ task memory_escape2(type: KonanLocalTest) {
}
task memory_weak0(type: KonanLocalTest) {
enabled = !isExperimentalMM // Experimental MM does not have a GC yet.
goldValue = "Data(s=Hello)\nnull\nOK\n"
source = "runtime/memory/weak0.kt"
}
@@ -2930,17 +2995,20 @@ task memory_weak1(type: KonanLocalTest) {
}
standaloneTest("memory_only_gc") {
enabled = !isExperimentalMM // Experimental MM does not have a GC yet.
source = "runtime/memory/only_gc.kt"
}
task memory_stable_ref_cross_thread_check(type: KonanLocalTest) {
disabled = project.testTarget == 'wasm32' // Needs workers.
disabled = (project.testTarget == 'wasm32') || // Needs workers.
isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
source = "runtime/memory/stable_ref_cross_thread_check.kt"
}
standaloneTest("cycle_detector") {
disabled = project.globalTestArgs.contains('-opt') || // Needs debug build.
(project.testTarget == 'wasm32') // CycleDetector is disabled on WASM.
(project.testTarget == 'wasm32') || // CycleDetector is disabled on WASM.
isExperimentalMM // Experimental MM will not support CycleDetector.
flags = ['-tr', '-g']
source = "runtime/memory/cycle_detector.kt"
}
@@ -2957,15 +3025,19 @@ standaloneTest("cycle_collector_deadlock1") {
standaloneTest("leakMemory") {
source = "runtime/memory/leak_memory.kt"
expectedExitStatusChecker = { it != 0 }
outputChecker = { s -> s.contains("Memory leaks detected, 1 objects leaked!") }
if (!isExperimentalMM) { // Experimental MM will not report memory leaks.
expectedExitStatusChecker = { it != 0 }
outputChecker = { s -> s.contains("Memory leaks detected, 1 objects leaked!") }
}
}
standaloneTest("leakMemoryWithTestRunner") {
source = "runtime/memory/leak_memory_test_runner.kt"
flags = ['-tr']
expectedExitStatusChecker = { it != 0 }
outputChecker = { s -> s.contains("Memory leaks detected, 1 objects leaked!") }
if (!isExperimentalMM) { // Experimental MM will not report memory leaks.
expectedExitStatusChecker = { it != 0 }
outputChecker = { s -> s.contains("Memory leaks detected, 1 objects leaked!") }
}
}
standaloneTest("mpp1") {
@@ -3508,6 +3580,65 @@ standaloneTest("testing_filters") {
}
}
standaloneTest("testing_filtered_suites") {
source = "testing/filtered_suites.kt"
flags = ["-tr", "-ea"]
def filters = [
["Filtered_suitesKt.*"], // filter out a class.
["A.*"], // filter out a top-level suite.
["*.common"], // run a test from all suites -> all hooks executed.
["Ignored.*"], // an ignored suite -> no hooks executed.
["A.ignored"] // a suite with only ignored tests -> no hooks executed.
]
def expectedHooks = [
["Filtered_suitesKt.before", "Filtered_suitesKt.after"],
["A.before", "A.after"],
["A.before", "A.after", "Filtered_suitesKt.before", "Filtered_suitesKt.after"],
[],
[]
]
multiRuns = true
multiArguments = filters.collect {
def filter = it.collect { "kotlin.test.tests.$it" }.join(",")
["--ktest_gradle_filter=$filter", "--ktest_logger=SIMPLE"]
}
outputChecker = { String output ->
// The first chunk is empty - drop it.
def outputs = output.split("Starting testing\n").drop(1)
if (outputs.size() != expectedHooks.size()) {
println("Incorrect number of test runs. Expected: ${expectedHooks.size()}, actual: ${outputs.size()}")
return false
}
// Check the correct set of hooks was executed on each run.
for (int i = 0; i < outputs.size(); i++) {
def actual = outputs[i].split('\n')
.findAll { it.startsWith("Hook:") }
.collect { it.replace("Hook: ", "") }
def expected = expectedHooks[i]
if (actual.size() != expected.size()) {
println("Incorrect number of executed hooks for run #$i. Expected: ${expected.size()}. Actual: ${actual.size()}")
println("Expected hooks: $expected")
println("Actual hooks: $actual")
return false
}
for (expectedHook in expected) {
if (!actual.contains(expectedHook)) {
println("Expected hook wasn't executed for run #$i: $expectedHook")
println("Expected hooks: $expected")
println("Actual hooks: $actual")
return false
}
}
}
return true
}
}
// Check that stacktraces and ignored suite are correctly reported in the TC logger.
standaloneTest("testing_stacktrace") {
source = "testing/stacktrace.kt"
@@ -3542,7 +3673,8 @@ tasks.register("driver0", KonanDriverTest) {
}
tasks.register("driver_opt", KonanDriverTest) {
disabled = (cacheTesting != null) // Cache is not compatible with -opt.
disabled = (cacheTesting != null) || // Cache is not compatible with -opt.
isExperimentalMM // Experimental MM does not support -opt yet.
goldValue = "Hello, world!\n"
source = "runtime/basic/driver0.kt"
flags = ["-opt"]
@@ -3750,6 +3882,24 @@ createInterop("kt43265") {
it.defFile 'interop/kt43265/kt43265.def'
}
createInterop("kt43502") {
it.defFile 'interop/kt43502/kt43502.def'
it.headers "$projectDir/interop/kt43502/kt43502.h"
// Note: also hardcoded in def file.
final String libDir = "$buildDir/kt43502/"
// Construct library that contains actual symbol definition.
it.getByTarget(target.name).configure {
doFirst {
UtilsKt.buildStaticLibrary(
project,
[file("$projectDir/interop/kt43502/kt43502.c")],
file("$libDir/kt43502.a"),
file("$libDir/kt43502.objs"),
)
}
}
}
createInterop("leakMemoryWithRunningThread") {
it.defFile 'interop/leakMemoryWithRunningThread/leakMemory.def'
it.headers "$projectDir/interop/leakMemoryWithRunningThread/leakMemory.h"
@@ -4071,14 +4221,24 @@ interopTest("interop_kt43265") {
source = "interop/kt43265/usage.kt"
}
interopTest("interop_leakMemoryWithRunningThreadUnchecked") {
dynamicTest("interop_kt43502") {
disabled = (project.testTarget == 'wasm32') // No interop for wasm yet.
interop = "kt43502"
source = "interop/kt43502/main.kt"
cSource = "$projectDir/interop/kt43502/main.c"
goldValue = "null\n"
}
interopTest("interop_leakMemoryWithRunningThreadUnchecked") {
disabled = (project.testTarget == 'wasm32') || // No interop for wasm yet.
isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
interop = 'leakMemoryWithRunningThread'
source = "interop/leakMemoryWithRunningThread/unchecked.kt"
}
interopTest("interop_leakMemoryWithRunningThreadChecked") {
disabled = (project.testTarget == 'wasm32') // No interop for wasm yet.
disabled = (project.testTarget == 'wasm32') || // No interop for wasm yet.
isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
interop = 'leakMemoryWithRunningThread'
source = "interop/leakMemoryWithRunningThread/checked.kt"
expectedExitStatusChecker = { it != 0 }
@@ -4121,6 +4281,7 @@ standaloneTest("interop_opengl_teapot") {
if (PlatformInfo.isAppleTarget(project)) {
interopTest("interop_objc_smoke") {
enabled = !isExperimentalMM // Experimental MM does not have a GC yet.
goldValue = "84\nFoo\nDeallocated\n" +
"Hello, World!\nKotlin says: Hello, everybody!\nHello from Kotlin\n2, 1\n" +
"true\ntrue\n" +
@@ -4155,6 +4316,7 @@ if (PlatformInfo.isAppleTarget(project)) {
}
interopTestMultifile("interop_objc_tests") {
enabled = !isExperimentalMM // Experimental MM does not have a GC yet.
source = "interop/objc/tests/"
interop = 'objcTests'
flags = ['-tr', '-e', 'main']
@@ -4272,6 +4434,7 @@ if (PlatformInfo.isAppleTarget(project)) {
}
interopTest("interop_objc_illegal_sharing_with_weak") {
enabled = !isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
source = "interop/objc/illegal_sharing_with_weak/main.kt"
interop = 'objc_illegal_sharing_with_weak'
@@ -4284,6 +4447,7 @@ if (PlatformInfo.isAppleTarget(project)) {
}
interopTest("interop_objc_kt42172") {
enabled = !isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
source = "interop/objc/kt42172/main.kt"
interop = "objc_kt42172"
flags = ['-Xopt-in=kotlin.native.internal.InternalForKotlinNative']
@@ -4334,7 +4498,8 @@ standaloneTest("interop_zlib") {
standaloneTest("interop_objc_illegal_sharing") {
dependsOnPlatformLibs(it)
disabled = !isAppleTarget(project)
disabled = !isAppleTarget(project) ||
isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
source = "interop/objc/illegal_sharing.kt"
expectedExitStatusChecker = { it != 0 }
outputChecker = {
@@ -4400,7 +4565,8 @@ dynamicTest("interop_kt42397") {
}
dynamicTest("interop_cleaners_main_thread") {
disabled = (project.target.name != project.hostName)
disabled = (project.target.name != project.hostName) ||
isExperimentalMM // Experimental MM does not have a GC yet.
source = "interop/cleaners/cleaners.kt"
cSource = "$projectDir/interop/cleaners/main_thread.cpp"
clangTool = "clang++"
@@ -4409,7 +4575,8 @@ dynamicTest("interop_cleaners_main_thread") {
}
dynamicTest("interop_cleaners_second_thread") {
disabled = (project.target.name != project.hostName)
disabled = (project.target.name != project.hostName) ||
isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
source = "interop/cleaners/cleaners.kt"
cSource = "$projectDir/interop/cleaners/second_thread.cpp"
clangTool = "clang++"
@@ -4427,7 +4594,8 @@ dynamicTest("interop_cleaners_leak") {
}
dynamicTest("interop_migrating_main_thread_legacy") {
disabled = (project.target.name != project.hostName)
disabled = (project.target.name != project.hostName) ||
isExperimentalMM // Experimental MM will not support legacy destroy runtime mode.
source = "interop/migrating_main_thread/lib.kt"
flags = ['-Xdestroy-runtime-mode=legacy']
clangFlags = ['-DIS_LEGACY']
@@ -4436,7 +4604,8 @@ dynamicTest("interop_migrating_main_thread_legacy") {
}
dynamicTest("interop_migrating_main_thread") {
disabled = (project.target.name != project.hostName)
disabled = (project.target.name != project.hostName) ||
isExperimentalMM // Experimental MM doesn't support multiple mutators yet.
source = "interop/migrating_main_thread/lib.kt"
flags = ['-Xdestroy-runtime-mode=on-shutdown']
cSource = "$projectDir/interop/migrating_main_thread/main.cpp"
@@ -4444,7 +4613,8 @@ dynamicTest("interop_migrating_main_thread") {
}
dynamicTest("interop_memory_leaks") {
disabled = (project.target.name != project.hostName)
disabled = (project.target.name != project.hostName) ||
isExperimentalMM // Experimental MM will not support legacy destroy runtime mode.
source = "interop/memory_leaks/lib.kt"
cSource = "$projectDir/interop/memory_leaks/main.cpp"
clangTool = "clang++"
@@ -4584,6 +4754,7 @@ Task frameworkTest(String name, Closure<FrameworkTest> configurator) {
if (isAppleTarget(project)) {
frameworkTest('testObjCExport') {
enabled = !isExperimentalMM // Experimental MM doesn't support ObjC blocks yet.
final String frameworkName = 'Kt'
final String dir = "$testOutputFramework/testObjCExport"
final File lazyHeader = file("$dir/$target-lazy.h")
@@ -4633,6 +4804,7 @@ if (isAppleTarget(project)) {
}
frameworkTest('testObjCExportStatic') {
enabled = !isExperimentalMM // Experimental MM doesn't support ObjC blocks yet.
final String frameworkName = 'KtStatic'
final String frameworkArtifactName = 'Kt'
final String libraryName = frameworkName + "Library"
@@ -4670,6 +4842,7 @@ if (isAppleTarget(project)) {
}
frameworkTest("testStdlibFramework") {
enabled = !isExperimentalMM // Experimental MM does not have GC yet.
framework('Stdlib') {
sources = ['framework/stdlib']
bitcode = true
@@ -4890,7 +5063,8 @@ tasks.register("metadata_compare_unable_to_import", MetadataComparisonTest) {
}
standaloneTest("local_ea_arraysfieldwrite") {
disabled = (cacheTesting != null) // Cache is not compatible with -opt.
disabled = (cacheTesting != null) || // Cache is not compatible with -opt.
isExperimentalMM // Experimental MM does not support -opt yet.
goldValue = "Array (constructor init):\nSize: 2\nContents: [1, 2]\n" +
"Array (constructor init):\nSize: 2\nContents: [3, 4]\n" +
"Array (default value init):\nSize: 2\nContents: [1, 2]\n" +
@@ -4963,7 +5137,8 @@ private void configureStdlibTest(KonanGTest task, boolean inWorker) {
task.useFilter = false
task.testLogger = KonanTest.Logger.GTEST
task.finalizedBy("resultsTask")
task.enabled = (project.testTarget != 'wasm32') // Uses exceptions
task.enabled = (project.testTarget != 'wasm32') && // Uses exceptions
(!inWorker || !ext.isExperimentalMM) // Experimental MM doesn't support multiple mutators yet.
}
/**
@@ -0,0 +1 @@
char **externPtr = 0;
@@ -0,0 +1,2 @@
libraryPaths = backend.native/tests/build/kt43502
staticLibraries = kt43502.a
@@ -0,0 +1 @@
extern char **externPtr;
@@ -0,0 +1,5 @@
#include "testlib_api.h"
int main() {
testlib_symbols()->kotlin.root.printExternPtr();
}
@@ -0,0 +1,5 @@
import kt43502.*
fun printExternPtr() {
println(externPtr)
}
@@ -663,6 +663,86 @@ __attribute__((swift_name("ArraysInitBlock")))
- (NSString *)log __attribute__((swift_name("log()")));
@end;
__attribute__((swift_name("OverrideKotlinMethods2")))
@protocol KtOverrideKotlinMethods2
@required
- (int32_t)one __attribute__((swift_name("one()")));
@end;
__attribute__((swift_name("OverrideKotlinMethods3")))
@interface KtOverrideKotlinMethods3 : KtBase
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
@end;
__attribute__((swift_name("OverrideKotlinMethods4")))
@interface KtOverrideKotlinMethods4 : KtOverrideKotlinMethods3 <KtOverrideKotlinMethods2>
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
- (int32_t)one __attribute__((swift_name("one()")));
@end;
__attribute__((swift_name("OverrideKotlinMethods5")))
@protocol KtOverrideKotlinMethods5
@required
- (int32_t)one __attribute__((swift_name("one()")));
@end;
__attribute__((swift_name("OverrideKotlinMethods6")))
@protocol KtOverrideKotlinMethods6 <KtOverrideKotlinMethods5>
@required
@end;
__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("OverrideKotlinMethodsKt")))
@interface KtOverrideKotlinMethodsKt : KtBase
/**
@note This method converts all Kotlin exceptions to errors.
*/
+ (BOOL)test0Obj:(id)obj error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("test0(obj:)")));
/**
@note This method converts all Kotlin exceptions to errors.
*/
+ (BOOL)test1Obj:(id)obj error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("test1(obj:)")));
/**
@note This method converts all Kotlin exceptions to errors.
*/
+ (BOOL)test2Obj:(id<KtOverrideKotlinMethods2>)obj error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("test2(obj:)")));
/**
@note This method converts all Kotlin exceptions to errors.
*/
+ (BOOL)test3Obj:(KtOverrideKotlinMethods3 *)obj error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("test3(obj:)")));
/**
@note This method converts all Kotlin exceptions to errors.
*/
+ (BOOL)test4Obj:(KtOverrideKotlinMethods4 *)obj error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("test4(obj:)")));
/**
@note This method converts all Kotlin exceptions to errors.
*/
+ (BOOL)test5Obj:(id<KtOverrideKotlinMethods5>)obj error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("test5(obj:)")));
/**
@note This method converts all Kotlin exceptions to errors.
*/
+ (BOOL)test6Obj:(id<KtOverrideKotlinMethods6>)obj error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("test6(obj:)")));
@end;
__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("OverrideMethodsOfAnyKt")))
@interface KtOverrideMethodsOfAnyKt : KtBase
/**
@note This method converts all Kotlin exceptions to errors.
*/
+ (BOOL)testObj:(id)obj other:(id)other swift:(BOOL)swift error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("test(obj:other:swift:)")));
@end;
__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("ThrowsEmptyKt")))
@interface KtThrowsEmptyKt : KtBase
@@ -0,0 +1,66 @@
package overrideKotlinMethods
import kotlin.test.*
internal interface OverrideKotlinMethods0<T> {
fun one(): T
}
internal interface OverrideKotlinMethods1<T> : OverrideKotlinMethods0<T>
interface OverrideKotlinMethods2 {
fun one(): Int
}
open class OverrideKotlinMethods3 {
internal open fun one(): Number = 3
}
open class OverrideKotlinMethods4 : OverrideKotlinMethods3(), OverrideKotlinMethods1<Int>, OverrideKotlinMethods2 {
override fun one(): Int = 2
}
interface OverrideKotlinMethods5 {
fun one(): Int
}
interface OverrideKotlinMethods6 : OverrideKotlinMethods5
// Using `Any` because Kotlin forbids internal type in public function signature.
@Throws(Throwable::class)
fun test0(obj: Any) {
val obj0 = obj as OverrideKotlinMethods0<*>
assertEquals(1, obj0.one())
}
// Using `Any` because Kotlin forbids internal type in public function signature.
@Throws(Throwable::class)
fun test1(obj: Any) {
val obj1 = obj as OverrideKotlinMethods1<*>
assertEquals(1, obj1.one())
}
@Throws(Throwable::class)
fun test2(obj: OverrideKotlinMethods2) {
assertEquals(1, obj.one())
}
@Throws(Throwable::class)
fun test3(obj: OverrideKotlinMethods3) {
assertEquals(1, obj.one())
}
@Throws(Throwable::class)
fun test4(obj: OverrideKotlinMethods4) {
assertEquals(1, obj.one())
}
@Throws(Throwable::class)
fun test5(obj: OverrideKotlinMethods5) {
assertEquals(1, obj.one())
}
@Throws(Throwable::class)
fun test6(obj: OverrideKotlinMethods6) {
assertEquals(1, obj.one())
}
@@ -0,0 +1,32 @@
/*
* Copyright 2010-2021 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.
*/
import Kt
class OverrideKotlinMethodsImpl : OverrideKotlinMethods4, OverrideKotlinMethods6 {
override func one() -> Int32 {
return 1
}
}
private func test1() throws {
let obj = OverrideKotlinMethodsImpl()
try OverrideKotlinMethodsKt.test0(obj: obj)
try OverrideKotlinMethodsKt.test1(obj: obj)
try OverrideKotlinMethodsKt.test2(obj: obj)
try OverrideKotlinMethodsKt.test3(obj: obj)
try OverrideKotlinMethodsKt.test4(obj: obj)
try OverrideKotlinMethodsKt.test5(obj: obj)
try OverrideKotlinMethodsKt.test6(obj: obj)
}
class OverrideKotlinMethodsTests : SimpleTestProvider {
override init() {
super.init()
test("Test1", test1)
}
}
@@ -0,0 +1,18 @@
package overrideMethodsOfAny
import kotlin.test.*
@Throws(Throwable::class)
fun test(obj: Any, other: Any, swift: Boolean) {
if (!swift) {
// Doesn't work for Swift, see https://youtrack.jetbrains.com/issue/KT-44613.
assertEquals(42, obj.hashCode())
assertTrue(obj.equals(other))
}
assertTrue(obj.equals(obj))
assertFalse(obj.equals(null))
assertFalse(obj.equals(Any()))
assertEquals("toString", obj.toString())
}
@@ -0,0 +1,43 @@
/*
* Copyright 2010-2021 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.
*/
import Kt
private class SwiftOverridingMethodsOfAny : Hashable, Equatable, CustomStringConvertible {
var hashValue: Int { return 42 }
static func == (lhs: SwiftOverridingMethodsOfAny, rhs: SwiftOverridingMethodsOfAny) -> Bool {
return true
}
var description: String { return "toString" }
}
private func testSwift() throws {
try OverrideMethodsOfAnyKt.test(obj: SwiftOverridingMethodsOfAny(), other: SwiftOverridingMethodsOfAny(), swift: true)
}
private class ObjCOverridingMethodsOfAny : NSObject {
override var hash: Int { return 42 }
override func isEqual(_ other: Any?) -> Bool {
return other is ObjCOverridingMethodsOfAny
}
override var description: String { return "toString" }
}
private func testObjC() throws {
try OverrideMethodsOfAnyKt.test(obj: ObjCOverridingMethodsOfAny(), other: ObjCOverridingMethodsOfAny(), swift: false)
}
class OverrideMethodsOfAnyTests : SimpleTestProvider {
override init() {
super.init()
test("TestSwift", testSwift)
test("TestObjC", testObjC)
}
}
@@ -0,0 +1,14 @@
/*
* Copyright 2010-2021 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 runtime.basic.initializers8
import kotlin.test.*
var globalString = "abc"
@Test fun runTest() {
assertEquals("abc", globalString)
}
@@ -6,17 +6,31 @@ import kotlin.test.*
import kotlin.native.concurrent.*
fun main(args : Array<String>) {
fun setHookLegacyMM(hook: ReportUnhandledExceptionHook) : ReportUnhandledExceptionHook? {
assertFailsWith<InvalidMutabilityException> {
setUnhandledExceptionHook { _ -> println("wrong") }
}
return setUnhandledExceptionHook(hook.freeze())
}
fun setHookNewMM(hook: ReportUnhandledExceptionHook) : ReportUnhandledExceptionHook? {
return setUnhandledExceptionHook(hook)
}
fun setHook(hook: ReportUnhandledExceptionHook) : ReportUnhandledExceptionHook? {
return when (kotlin.native.Platform.memoryModel) {
kotlin.native.MemoryModel.EXPERIMENTAL -> setHookNewMM(hook)
else -> setHookLegacyMM(hook)
}
}
fun main() {
val x = 42
val old = setUnhandledExceptionHook({
val old = setHook {
throwable: Throwable -> println("value $x: ${throwable::class.simpleName}")
}.freeze())
}
assertNull(old)
throw Error("an error")
}
}
@@ -0,0 +1,57 @@
package kotlin.test.tests
import kotlin.test.*
private fun hook(message: String) {
print("Hook: ")
println(message)
}
class A {
@Test
fun foo() {}
@Test
fun common() {}
@Ignore
@Test
fun ignored() {}
companion object {
@BeforeClass
fun before() = hook("A.before")
@AfterClass
fun after() = hook("A.after")
}
}
@Ignore
class Ignored {
@Test
fun bar() {}
@Test
fun common() {}
companion object {
@BeforeClass
fun before() = hook("Ignored.before")
@AfterClass
fun after() = hook("Ignored.after")
}
}
@BeforeClass
fun before() = hook("Filtered_suitesKt.before")
@AfterClass
fun after() = hook("Filtered_suitesKt.after")
@Test
fun baz() {}
@Test
fun common() {}
@@ -183,7 +183,7 @@ open class FrameworkTest : DefaultTask(), KonanTestExecutable {
KonanTarget.IOS_ARM32, KonanTarget.IOS_ARM64 -> "iphoneos"
KonanTarget.TVOS_X64 -> "appletvsimulator"
KonanTarget.TVOS_ARM64 -> "appletvos"
KonanTarget.MACOS_X64 -> "macosx"
KonanTarget.MACOS_X64, KonanTarget.MACOS_ARM64 -> "macosx"
KonanTarget.WATCHOS_ARM64 -> "watchos"
KonanTarget.WATCHOS_X64, KonanTarget.WATCHOS_X86 -> "watchsimulator"
else -> throw IllegalStateException("Test target $target is not supported")
@@ -213,6 +213,7 @@ open class FrameworkTest : DefaultTask(), KonanTestExecutable {
KonanTarget.TVOS_X64 -> "SIMCTL_CHILD_DYLD_LIBRARY_PATH"
else -> "DYLD_LIBRARY_PATH"
}
// TODO: macos_arm64?
return if (newMacos && target == KonanTarget.MACOS_X64) emptyMap() else mapOf(
dyldLibraryPathKey to getSwiftLibsPathForTestTarget()
)
@@ -253,7 +254,8 @@ open class FrameworkTest : DefaultTask(), KonanTestExecutable {
KonanTarget.WATCHOS_X64 -> return // bitcode-build-tool doesn't support simulators.
KonanTarget.IOS_ARM64,
KonanTarget.IOS_ARM32 -> Xcode.current.iphoneosSdk
KonanTarget.MACOS_X64 -> Xcode.current.macosxSdk
KonanTarget.MACOS_X64,
KonanTarget.MACOS_ARM64 -> Xcode.current.macosxSdk
KonanTarget.TVOS_ARM64 -> Xcode.current.appletvosSdk
KonanTarget.WATCHOS_ARM32,
KonanTarget.WATCHOS_ARM64 -> Xcode.current.watchosSdk
@@ -436,6 +436,9 @@ open class KonanDynamicTest : KonanStandaloneTest() {
@Input
var clangFlags: List<String> = listOf()
@Input @Optional
var interop: String? = null
// Replace testlib_api.h and all occurrences of the testlib with the actual name of the test
private fun processCSource(): String {
val sourceFile = File(cSource)
@@ -244,6 +244,7 @@ fun compileSwift(project: Project, target: KonanTarget, sources: List<String>, o
KonanTarget.TVOS_X64 -> "x86_64-apple-tvos" + configs.osVersionMin
KonanTarget.TVOS_ARM64 -> "arm64-apple-tvos" + configs.osVersionMin
KonanTarget.MACOS_X64 -> "x86_64-apple-macosx" + configs.osVersionMin
KonanTarget.MACOS_ARM64 -> "arm64-apple-macos" + configs.osVersionMin
KonanTarget.WATCHOS_X86 -> "i386-apple-watchos" + configs.osVersionMin
KonanTarget.WATCHOS_X64 -> "x86_64-apple-watchos" + configs.osVersionMin
else -> throw IllegalStateException("Test target $target is not supported")
+9 -9
View File
@@ -15,16 +15,16 @@
#
# A version of the Kotlin compiler that is used to build Kotlin/Native.
buildKotlinVersion=1.5.0-dev-2205
buildKotlinCompilerRepo=https://teamcity.jetbrains.com/guestAuth/app/rest/builds/buildType:(id:Kotlin_KotlinPublic_Compiler),number:1.5.0-dev-2205,branch:default:any,pinned:true/artifacts/content/maven
buildKotlinVersion=1.5.20-dev-372
buildKotlinCompilerRepo=https://teamcity.jetbrains.com/guestAuth/app/rest/builds/buildType:(id:Kotlin_KotlinPublic_Compiler),number:1.5.20-dev-372,branch:default:any,pinned:true/artifacts/content/maven
remoteRoot=konan_tests
kotlinCompilerRepo=https://teamcity.jetbrains.com/guestAuth/app/rest/builds/buildType:(id:Kotlin_KotlinPublic_Compiler),number:1.5.20-dev-60,branch:default:any,pinned:true/artifacts/content/maven
kotlinVersion=1.5.20-dev-60
kotlinStdlibRepo=https://teamcity.jetbrains.com/guestAuth/app/rest/builds/buildType:(id:Kotlin_KotlinPublic_Compiler),number:1.5.20-dev-60,branch:default:any,pinned:true/artifacts/content/maven
kotlinStdlibVersion=1.5.20-dev-60
kotlinStdlibTestsVersion=1.5.20-dev-60
testKotlinCompilerVersion=1.5.20-dev-60
konanVersion=1.5.0
kotlinCompilerRepo=https://teamcity.jetbrains.com/guestAuth/app/rest/builds/buildType:(id:Kotlin_KotlinPublic_Compiler),number:1.5.20-dev-372,branch:default:any,pinned:true/artifacts/content/maven
kotlinVersion=1.5.20-dev-372
kotlinStdlibRepo=https://teamcity.jetbrains.com/guestAuth/app/rest/builds/buildType:(id:Kotlin_KotlinPublic_Compiler),number:1.5.20-dev-372,branch:default:any,pinned:true/artifacts/content/maven
kotlinStdlibVersion=1.5.20-dev-372
kotlinStdlibTestsVersion=1.5.20-dev-372
testKotlinCompilerVersion=1.5.20-dev-372
konanVersion=1.5.20
# A version of Xcode required to build the Kotlin/Native compiler.
xcodeMajorVersion=12
+28
View File
@@ -111,6 +111,34 @@ target-toolchain-xcode_12_2-macos_x64.default = \
xcode-addon-xcode_12_2-macos_x64.default = \
remote:internal
# macOS Apple Silicon
targetToolchain.macos_x64-macos_arm64 = target-toolchain-xcode_12_2-macos_x64
arch.macos_arm64 = arm64
targetSysRoot.macos_arm64 = target-sysroot-xcode_12_2-macos_x64
# TODO: Check Clang behaviour.
targetCpu.macos_arm64 = cyclone
clangFlags.macos_arm64 = -cc1 -emit-obj -disable-llvm-passes -x ir
clangNooptFlags.macos_arm64 = -O1
clangOptFlags.macos_arm64 = -O3
# See clangDebugFlags.ios_arm64
# TODO: Is it still necessary?
clangDebugFlags.macos_arm64 = -O0 -mllvm -fast-isel=false -mllvm -global-isel=false
linkerKonanFlags.macos_arm64 = -lSystem -lc++ -lobjc -framework Foundation -sdk_version 11.0.1
linkerOptimizationFlags.macos_arm64 = -dead_strip
linkerNoDebugFlags.macos_arm64 = -S
linkerDynamicFlags.macos_arm64 = -dylib
osVersionMinFlagLd.macos_arm64 = -macosx_version_min
osVersionMinFlagClang.macos_arm64 = -mmacosx-version-min
osVersionMin.macos_arm64 = 11.0
runtimeDefinitions.macos_arm64 = KONAN_OSX=1 KONAN_MACOSX=1 KONAN_ARM64=1 KONAN_OBJC_INTEROP=1 \
KONAN_CORE_SYMBOLICATION=1 KONAN_HAS_CXX11_EXCEPTION_FUNCTIONS=1
dependencies.macos_x64-macos_arm64 = \
libffi-3.2.1-3-darwin-macos
target-sysroot-xcode_12_2-macos_arm64.default = \
remote:internal
# Apple's 32-bit iOS.
targetToolchain.macos_x64-ios_arm32 = target-toolchain-xcode_12_2-macos_x64
@@ -1,4 +1,4 @@
depends = darwin posix
depends = darwin osx posix
language = Objective-C
package = platform.Hypervisor
@@ -26,6 +26,7 @@ headers = AppleTextureEncoder.h AssertMacros.h Availability.h AvailabilityIntern
os/activity.h os/availability.h os/base.h os/lock.h \
os/log.h os/object.h os/overflow.h os/signpost.h os/trace.h \
simd/simd.h sys/sysctl.h sys/user.h \
sys/_types/_os_inline.h \
net/if_media.h sys/disk.h sys/kernel_types.h
headerFilter = **
@@ -39,5 +40,6 @@ excludedFunctions = __tg_promote KERNEL_AUDIT_TOKEN KERNEL_SECURITY_TOKEN \
xpc_debugger_api_misuse_info \
vm_stats
compilerOpts = -D_XOPEN_SOURCE -DSHARED_LIBBIND -D_DARWIN_NO_64_BIT_INODE -DSYSCTL_DEF_ENABLED
compilerOpts.macos_x64 = -D_XOPEN_SOURCE -DSHARED_LIBBIND -D_DARWIN_NO_64_BIT_INODE -DSYSCTL_DEF_ENABLED
compilerOpts.macos_arm64 = -D_XOPEN_SOURCE -DSHARED_LIBBIND -DSYSCTL_DEF_ENABLED
linkerOpts = -ldl -lz -lcurses -lbz2 -lcompression -late -lbsm
@@ -1,17 +1,18 @@
depends = darwin posix
language = Objective-C
package = platform.osx
headers = NSSystemDirectories.h \
aliasdb.h bootparams.h bootstrap.h \
com_err.h \
crt_externs.h \
disktab.h dtrace.h \
emmintrin.h eti.h expat.h \
eti.h expat.h \
expat_external.h form.h fsproperties.h get_compat.h \
gssapi.h histedit.h \
krb5.h launch.h lber.h lber_types.h ldif.h libc.h libcharset.h \
libproc.h localcharset.h \
mmintrin.h monitor.h \
monitor.h \
nc_tparm.h ncurses.h ncurses_dll.h \
nlist.h \
panel.h pcap-bpf.h pcap-namedb.h pcap.h printerdb.h \
@@ -20,10 +21,15 @@ headers = NSSystemDirectories.h \
struct.h term.h \
term_entry.h termcap.h tic.h timeconv.h tzfile.h \
unctrl.h vproc.h \
xmmintrin.h \
atm/atm_types.h corpses/task_corpse.h \
hfs/hfs_format.h hfs/hfs_mount.h hfs/hfs_unistr.h \
sys/kauth.h
headers.macos_x64 = \
emmintrin.h \
mmintrin.h \
xmmintrin.h
headers.macos_arm64 = \
arm64/hv/hv_kern_types.h
headerFilter = **
@@ -13,7 +13,8 @@ headers = alloca.h ar.h assert.h complex.h ctype.h dirent.h dlfcn.h err.h errno.
sys/queue.h sys/select.h sys/shm.h sys/socket.h sys/stat.h \
sys/syslimits.h sys/time.h sys/times.h sys/utsname.h sys/wait.h
compilerOpts = -D_XOPEN_SOURCE -DSHARED_LIBBIND -D_DARWIN_NO_64_BIT_INODE -D_DARWIN_C_SOURCE
compilerOpts.macos_x64 = -D_XOPEN_SOURCE -DSHARED_LIBBIND -D_DARWIN_NO_64_BIT_INODE -D_DARWIN_C_SOURCE
compilerOpts.macos_arm64 = -D_XOPEN_SOURCE -DSHARED_LIBBIND -D_DARWIN_C_SOURCE
# -D_ANSI_SOURCE, sigh, breaks user_addr_t
excludedFunctions = KERNEL_AUDIT_TOKEN KERNEL_SECURITY_TOKEN add_profil \
addrsel_policy_init \
@@ -1,2 +1,2 @@
#!/bin/bash
../../../../dist/bin/run_konan defFileDependencies -target macos_x64 *.def
../../../../dist/bin/run_konan defFileDependencies -target macos_x64 -target macos_arm64 *.def
@@ -176,6 +176,10 @@ KNativePtr Kotlin_AtomicNativePtr_get(KRef thiz) {
}
void Kotlin_AtomicReference_checkIfFrozen(KRef value) {
if (CurrentMemoryModel == MemoryModel::kExperimental) {
// TODO: Remove when freezing is implemented.
return;
}
if (value != nullptr && !isPermanentOrFrozen(value)) {
ThrowInvalidMutabilityException(value);
}
@@ -80,4 +80,11 @@ extern "C" const int KonanNeedDebugInfo;
::internal::TODOImpl(CURRENT_SOURCE_LOCATION, ##__VA_ARGS__); \
} while (false)
// Use RuntimeFail() to unconditionally fail, signifying compiler/runtime bug.
// TODO: Consider using `CURRENT_SOURCE_LOCATION` when `KonanNeedDebugInfo` is `true`.
#define RuntimeFail(format, ...) \
do { \
RuntimeAssertFailed(nullptr, format, ##__VA_ARGS__); \
} while (false)
#endif // RUNTIME_ASSERT_H
@@ -85,6 +85,8 @@ struct ObjHeader {
return hasPointerBits(typeInfoOrMeta_, OBJECT_TAG_PERMANENT_CONTAINER);
}
inline bool heap() const { return getPointerBits(typeInfoOrMeta_, OBJECT_TAG_MASK) == 0; }
static MetaObjHeader* createMetaObject(ObjHeader* object);
static void destroyMetaObject(ObjHeader* object);
};
@@ -902,6 +902,8 @@ static const TypeInfo* createTypeInfo(Class clazz, const TypeInfo* superType, co
}
};
// Compiler relies on using reverse adapters here from all supertypes
// in [ObjCExportCodeGenerator.createReverseAdapters].
for (const TypeInfo* t : supers) {
const ObjCTypeAdapter* typeAdapter = getTypeAdapter(t);
if (typeAdapter == nullptr) continue;
@@ -921,6 +923,8 @@ static const TypeInfo* createTypeInfo(Class clazz, const TypeInfo* superType, co
}
}
// Compiler relies on using reverse adapters here from all supertypes
// in [ObjCExportCodeGenerator.createReverseAdapters].
for (const TypeInfo* typeInfo : addedInterfaces) {
const ObjCTypeAdapter* typeAdapter = getTypeAdapter(typeInfo);
@@ -120,6 +120,9 @@ RuntimeState* initRuntime() {
RuntimeAssert(lastStatus != kGlobalRuntimeShutdown, "Kotlin runtime was shut down. Cannot create new runtimes.");
}
firstRuntime = lastStatus == kGlobalRuntimeUninitialized;
if (CurrentMemoryModel == MemoryModel::kExperimental) {
RuntimeCheck(firstRuntime, "Experimental MM does not support multiple mutator threads yet");
}
result->memoryState = InitMemory(firstRuntime);
result->worker = WorkerInit(true);
}
@@ -186,7 +189,7 @@ void AppendToInitializersTail(InitNode *next) {
initTailNode = next;
}
void Kotlin_initRuntimeIfNeeded() {
RUNTIME_NOTHROW void Kotlin_initRuntimeIfNeeded() {
if (!isValidRuntime()) {
initRuntime();
// Register runtime deinit function at thread cleanup.
+1 -1
View File
@@ -33,7 +33,7 @@ enum DestroyRuntimeMode {
DestroyRuntimeMode Kotlin_getDestroyRuntimeMode();
void Kotlin_initRuntimeIfNeeded();
RUNTIME_NOTHROW void Kotlin_initRuntimeIfNeeded();
void Kotlin_deinitRuntimeIfNeeded();
// Can only be called once.
@@ -39,10 +39,6 @@ constexpr std::array<uint32_t, Count> RepeatingPowers(uint32_t base, uint8_t exp
return result;
}
#if defined(__x86_64__) or defined(__i386__)
#pragma clang attribute push (__attribute__((target("avx2"))), apply_to=function)
#endif
template<typename Traits>
ALWAYS_INLINE void polyHashTail(int& n, uint16_t const*& str, typename Traits::Vec128Type& res, uint32_t const* b, uint32_t const* p) {
using VecType = typename Traits::VecType;
@@ -194,8 +190,4 @@ ALWAYS_INLINE void polyHashUnroll8(int& n, uint16_t const*& str, typename Traits
res = Traits::vec128Add(res, Traits::vec128Add(sum1, sum2));
}
#if defined(__x86_64__) or defined(__i386__)
#pragma clang attribute pop
#endif
#endif // RUNTIME_POLYHASH_COMMON_H
@@ -8,9 +8,10 @@
#if defined(__x86_64__) or defined(__i386__)
#include <immintrin.h>
#define __SSE41__ __attribute__((target("sse4.1")))
#define __AVX2__ __attribute__((target("avx2")))
#pragma clang attribute push (__attribute__((target("avx2"))), apply_to=function)
#include <immintrin.h>
namespace {
@@ -26,24 +27,24 @@ struct SSETraits {
using Vec128Type = __m128i;
using U16VecType = __m128i;
ALWAYS_INLINE static VecType initVec() { return _mm_setzero_si128(); }
ALWAYS_INLINE static Vec128Type initVec128() { return _mm_setzero_si128(); }
ALWAYS_INLINE static int vec128toInt(Vec128Type x) { return _mm_cvtsi128_si32(x); }
ALWAYS_INLINE static VecType u16Load(U16VecType x) { return _mm_cvtepu16_epi32(x); }
ALWAYS_INLINE static Vec128Type vec128Mul(Vec128Type x, Vec128Type y) { return _mm_mullo_epi32(x, y); }
ALWAYS_INLINE static Vec128Type vec128Add(Vec128Type x, Vec128Type y) { return _mm_add_epi32(x, y); }
ALWAYS_INLINE static VecType vecMul(VecType x, VecType y) { return _mm_mullo_epi32(x, y); }
ALWAYS_INLINE static VecType vecAdd(VecType x, VecType y) { return _mm_add_epi32(x, y); }
ALWAYS_INLINE static Vec128Type squash2(VecType x, VecType y) {
__SSE41__ static VecType initVec() { return _mm_setzero_si128(); }
__SSE41__ static Vec128Type initVec128() { return _mm_setzero_si128(); }
__SSE41__ static int vec128toInt(Vec128Type x) { return _mm_cvtsi128_si32(x); }
__SSE41__ static VecType u16Load(U16VecType x) { return _mm_cvtepu16_epi32(x); }
__SSE41__ static Vec128Type vec128Mul(Vec128Type x, Vec128Type y) { return _mm_mullo_epi32(x, y); }
__SSE41__ static Vec128Type vec128Add(Vec128Type x, Vec128Type y) { return _mm_add_epi32(x, y); }
__SSE41__ static VecType vecMul(VecType x, VecType y) { return _mm_mullo_epi32(x, y); }
__SSE41__ static VecType vecAdd(VecType x, VecType y) { return _mm_add_epi32(x, y); }
__SSE41__ static Vec128Type squash2(VecType x, VecType y) {
return squash1(_mm_hadd_epi32(x, y)); // [x0 + x1, x2 + x3, y0 + y1, y2 + y3]
}
ALWAYS_INLINE static Vec128Type squash1(VecType z) {
__SSE41__ static Vec128Type squash1(VecType z) {
VecType sum = _mm_hadd_epi32(z, z); // [z0 + z1, z2 + z3, z0 + z1, z2 + z3]
return _mm_hadd_epi32(sum, sum); // [z0..3, same, same, same]
}
static int polyHashUnalignedUnrollUpTo8(int n, uint16_t const* str) {
__SSE41__ static int polyHashUnalignedUnrollUpTo8(int n, uint16_t const* str) {
Vec128Type res = initVec128();
polyHashUnroll2<SSETraits>(n, str, res, &b8[0], &p64[56]);
@@ -52,7 +53,7 @@ struct SSETraits {
return vec128toInt(res);
}
static int polyHashUnalignedUnrollUpTo16(int n, uint16_t const* str) {
__SSE41__ static int polyHashUnalignedUnrollUpTo16(int n, uint16_t const* str) {
Vec128Type res = initVec128();
polyHashUnroll4<SSETraits>(n, str, res, &b16[0], &p64[48]);
@@ -68,19 +69,19 @@ struct AVX2Traits {
using Vec128Type = __m128i;
using U16VecType = __m128i;
ALWAYS_INLINE static VecType initVec() { return _mm256_setzero_si256(); }
ALWAYS_INLINE static Vec128Type initVec128() { return _mm_setzero_si128(); }
ALWAYS_INLINE static int vec128toInt(Vec128Type x) { return _mm_cvtsi128_si32(x); }
ALWAYS_INLINE static VecType u16Load(U16VecType x) { return _mm256_cvtepu16_epi32(x); }
ALWAYS_INLINE static Vec128Type vec128Mul(Vec128Type x, Vec128Type y) { return _mm_mullo_epi32(x, y); }
ALWAYS_INLINE static Vec128Type vec128Add(Vec128Type x, Vec128Type y) { return _mm_add_epi32(x, y); }
ALWAYS_INLINE static VecType vecMul(VecType x, VecType y) { return _mm256_mullo_epi32(x, y); }
ALWAYS_INLINE static VecType vecAdd(VecType x, VecType y) { return _mm256_add_epi32(x, y); }
ALWAYS_INLINE static Vec128Type squash2(VecType x, VecType y) {
__AVX2__ static VecType initVec() { return _mm256_setzero_si256(); }
__AVX2__ static Vec128Type initVec128() { return _mm_setzero_si128(); }
__AVX2__ static int vec128toInt(Vec128Type x) { return _mm_cvtsi128_si32(x); }
__AVX2__ static VecType u16Load(U16VecType x) { return _mm256_cvtepu16_epi32(x); }
__AVX2__ static Vec128Type vec128Mul(Vec128Type x, Vec128Type y) { return _mm_mullo_epi32(x, y); }
__AVX2__ static Vec128Type vec128Add(Vec128Type x, Vec128Type y) { return _mm_add_epi32(x, y); }
__AVX2__ static VecType vecMul(VecType x, VecType y) { return _mm256_mullo_epi32(x, y); }
__AVX2__ static VecType vecAdd(VecType x, VecType y) { return _mm256_add_epi32(x, y); }
__AVX2__ static Vec128Type squash2(VecType x, VecType y) {
return squash1(_mm256_hadd_epi32(x, y)); // [x0 + x1, x2 + x3, y0 + y1, y2 + y3, x4 + x5, x6 + x7, y4 + y5, y6 + y7]
}
ALWAYS_INLINE static Vec128Type squash1(VecType z) {
__AVX2__ static Vec128Type squash1(VecType z) {
VecType sum = _mm256_hadd_epi32(z, z); // [z0 + z1, z2 + z3, z0 + z1, z2 + z3, z4 + z5, z6 + z7, z4 + z5, z6 + z7]
sum = _mm256_hadd_epi32(sum, sum); // [z0..3, z0..3, z0..3, z0..3, z4..7, z4..7, z4..7, z4..7]
Vec128Type lo = _mm256_extracti128_si256(sum, 0); // [z0..3, same, same, same]
@@ -88,7 +89,7 @@ struct AVX2Traits {
return _mm_add_epi32(lo, hi); // [z0..7, same, same, same]
}
static int polyHashUnalignedUnrollUpTo16(int n, uint16_t const* str) {
__AVX2__ static int polyHashUnalignedUnrollUpTo16(int n, uint16_t const* str) {
Vec128Type res = initVec128();
polyHashUnroll2<AVX2Traits>(n, str, res, &b16[0], &p64[48]);
@@ -98,7 +99,7 @@ struct AVX2Traits {
return vec128toInt(res);
}
static int polyHashUnalignedUnrollUpTo32(int n, uint16_t const* str) {
__AVX2__ static int polyHashUnalignedUnrollUpTo32(int n, uint16_t const* str) {
Vec128Type res = initVec128();
polyHashUnroll4<AVX2Traits>(n, str, res, &b32[0], &p64[32]);
@@ -109,7 +110,7 @@ struct AVX2Traits {
return vec128toInt(res);
}
static int polyHashUnalignedUnrollUpTo64(int n, uint16_t const* str) {
__AVX2__ static int polyHashUnalignedUnrollUpTo64(int n, uint16_t const* str) {
Vec128Type res = initVec128();
polyHashUnroll8<AVX2Traits>(n, str, res, &b64[0], &p64[0]);
@@ -128,8 +129,8 @@ struct AVX2Traits {
const bool x64 = false;
#endif
bool initialized = false;
bool sseSupported;
bool avx2Supported;
bool sseSupported = false;
bool avx2Supported = false;
}
@@ -161,6 +162,4 @@ int polyHash_x86(int length, uint16_t const* str) {
return res;
}
#pragma clang attribute pop
#endif
@@ -45,7 +45,7 @@ public typealias ReportUnhandledExceptionHook = Function1<Throwable, Unit>
* with custom exception hooks.
*/
public fun setUnhandledExceptionHook(hook: ReportUnhandledExceptionHook): ReportUnhandledExceptionHook? {
if (!hook.isFrozen) {
if (Platform.memoryModel != MemoryModel.EXPERIMENTAL && !hook.isFrozen) {
throw InvalidMutabilityException("Unhandled exception hook must be frozen")
}
return setUnhandledExceptionHook0(hook)
@@ -16,7 +16,6 @@ internal class TestRunner(val suites: List<TestSuite>, args: Array<String>) {
private var logger: TestLogger = GTestLogger()
private var runTests = true
private var useExitCode = true
private var reportExcludedTestSuites = true
var iterations = 1
private set
var exitCode = 0
@@ -38,7 +37,6 @@ internal class TestRunner(val suites: List<TestSuite>, args: Array<String>) {
logger.log(help); runTests = false
}
"--ktest_no_exit_code" -> useExitCode = false
"--ktest_no_excluded_test_suites" -> reportExcludedTestSuites = false
else -> throw IllegalArgumentException("Unknown option: $it\n$help")
}
2 -> {
@@ -219,9 +217,6 @@ internal class TestRunner(val suites: List<TestSuite>, args: Array<String>) {
|--ktest_logger=GTEST|TEAMCITY|SIMPLE|SILENT - Use the specified output format. The default one is GTEST.
|
|--ktest_no_exit_code - Don't return a non-zero exit code if there are failing tests.
|
|--ktest_no_excluded_test_suites - Don't report test suites that don't match the filter.
| Has no effect when filter is not specified.
""".trimMargin()
private inline fun sendToListeners(event: TestListener.() -> Unit) {
@@ -230,6 +225,15 @@ internal class TestRunner(val suites: List<TestSuite>, args: Array<String>) {
}
private fun TestSuite.run() {
// Do not run @BeforeClass/@AfterClass hooks if all test cases are ignored.
if (testCases.values.all { it.ignored }) {
testCases.values.forEach { testCase ->
sendToListeners { ignore(testCase) }
}
return
}
// Normal path: run all hooks and execute test cases.
doBeforeClass()
testCases.values.forEach { testCase ->
if (testCase.ignored) {
@@ -257,10 +261,12 @@ internal class TestRunner(val suites: List<TestSuite>, args: Array<String>) {
val iterationTime = measureTimeMillis {
suitesFiltered.forEach {
if (it.ignored) {
if (reportExcludedTestSuites) {
sendToListeners { ignoreSuite(it) }
}
sendToListeners { ignoreSuite(it) }
} else {
// Do not run filtered out suites.
if (it.size == 0) {
return@forEach
}
sendToListeners { startSuite(it) }
val time = measureTimeMillis { it.run() }
sendToListeners { finishSuite(it, time) }
+22
View File
@@ -0,0 +1,22 @@
/*
* Copyright 2010-2021 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.
*/
#ifndef RUNTIME_MM_GC_H
#define RUNTIME_MM_GC_H
#include "gc/NoOpGC.hpp"
namespace kotlin {
namespace mm {
// TODO: GC should be extracted into a separate module, so that we can do different GCs without
// the need to redo the entire MM. For now changing GCs can be done by modifying `using` below.
using GC = NoOpGC;
} // namespace mm
} // namespace kotlin
#endif // RUNTIME_MM_GC_H
@@ -8,6 +8,7 @@
#include "ObjectFactory.hpp"
#include "GlobalsRegistry.hpp"
#include "GC.hpp"
#include "StableRefRegistry.hpp"
#include "ThreadRegistry.hpp"
#include "Utils.hpp"
@@ -23,7 +24,8 @@ public:
ThreadRegistry& threadRegistry() noexcept { return threadRegistry_; }
GlobalsRegistry& globalsRegistry() noexcept { return globalsRegistry_; }
StableRefRegistry& stableRefRegistry() noexcept { return stableRefRegistry_; }
ObjectFactory& objectFactory() noexcept { return objectFactory_; }
ObjectFactory<GC>& objectFactory() noexcept { return objectFactory_; }
GC& gc() noexcept { return gc_; }
private:
GlobalData();
@@ -34,7 +36,8 @@ private:
ThreadRegistry threadRegistry_;
GlobalsRegistry globalsRegistry_;
StableRefRegistry stableRefRegistry_;
ObjectFactory objectFactory_;
ObjectFactory<GC> objectFactory_;
GC gc_;
};
} // namespace mm
@@ -26,6 +26,9 @@ public:
using Iterator = MultiSourceQueue<ObjHeader**>::Iterator;
GlobalsRegistry();
~GlobalsRegistry();
static GlobalsRegistry& Instance() noexcept;
void RegisterStorageForGlobal(mm::ThreadData* threadData, ObjHeader** location) noexcept;
@@ -41,11 +44,6 @@ public:
Iterable Iter() noexcept { return globals_.Iter(); }
private:
friend class GlobalData;
GlobalsRegistry();
~GlobalsRegistry();
// TODO: Add-only MultiSourceQueue can be made more efficient. Measure, if it's a problem.
MultiSourceQueue<ObjHeader**> globals_;
};
+45 -2
View File
@@ -95,6 +95,10 @@ void ObjHeader::destroyMetaObject(ObjHeader* object) {
mm::ExtraObjectData::Uninstall(object);
}
ALWAYS_INLINE bool isPermanentOrFrozen(const ObjHeader* obj) {
return obj->permanent() || isFrozen(obj);
}
ALWAYS_INLINE bool isShareable(const ObjHeader* obj) {
// TODO: Remove when legacy MM is gone.
return true;
@@ -140,7 +144,10 @@ extern "C" ALWAYS_INLINE OBJ_GETTER(InitSingleton, ObjHeader** location, const T
extern "C" RUNTIME_NOTHROW void InitAndRegisterGlobal(ObjHeader** location, const ObjHeader* initialValue) {
auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData();
mm::GlobalsRegistry::Instance().RegisterStorageForGlobal(threadData, location);
mm::SetHeapRef(location, const_cast<ObjHeader*>(initialValue));
// Null `initialValue` means that the appropriate value was already set by static initialization.
if (initialValue != nullptr) {
mm::SetHeapRef(location, const_cast<ObjHeader*>(initialValue));
}
}
extern "C" const MemoryModel CurrentMemoryModel = MemoryModel::kExperimental;
@@ -246,6 +253,11 @@ extern "C" RUNTIME_NOTHROW void GC_CollectorCallback(void* worker) {
// Nothing to do
}
extern "C" void Kotlin_native_internal_GC_collect(ObjHeader*) {
auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData();
threadData->gc().PerformFullGC();
}
extern "C" void Kotlin_native_internal_GC_collectCyclic(ObjHeader*) {
// TODO: Remove when legacy MM is gone.
ThrowIllegalArgumentException();
@@ -282,29 +294,45 @@ extern "C" void Kotlin_Any_share(ObjHeader* thiz) {
// Nothing to do
}
extern "C" RUNTIME_NOTHROW void PerformFullGC(MemoryState* memory) {
GetThreadData(memory)->gc().PerformFullGC();
}
extern "C" RUNTIME_NOTHROW bool ClearSubgraphReferences(ObjHeader* root, bool checked) {
// TODO: Remove when legacy MM is gone.
return true;
}
extern "C" RUNTIME_NOTHROW void* CreateStablePointer(ObjHeader* object) {
if (!object)
return nullptr;
auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData();
return mm::StableRefRegistry::Instance().RegisterStableRef(threadData, object);
}
extern "C" RUNTIME_NOTHROW void DisposeStablePointer(void* pointer) {
if (!pointer)
return;
auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData();
auto* node = static_cast<mm::StableRefRegistry::Node*>(pointer);
mm::StableRefRegistry::Instance().UnregisterStableRef(threadData, node);
}
extern "C" RUNTIME_NOTHROW OBJ_GETTER(DerefStablePointer, void* pointer) {
if (!pointer)
RETURN_OBJ(nullptr);
auto* node = static_cast<mm::StableRefRegistry::Node*>(pointer);
ObjHeader* object = **node;
RETURN_OBJ(object);
}
extern "C" RUNTIME_NOTHROW OBJ_GETTER(AdoptStablePointer, void* pointer) {
if (!pointer)
RETURN_OBJ(nullptr);
auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData();
auto* node = static_cast<mm::StableRefRegistry::Node*>(pointer);
ObjHeader* object = **node;
@@ -347,7 +375,22 @@ extern "C" void AdoptReferenceFromSharedVariable(ObjHeader* object) {
// Nothing to do.
}
void CheckGlobalsAccessible() {
extern "C" void CheckGlobalsAccessible() {
// TODO: Remove when legacy MM is gone.
// Always accessible
}
extern "C" RUNTIME_NOTHROW void Kotlin_mm_safePointFunctionEpilogue() {
auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData();
threadData->gc().SafePointFunctionEpilogue();
}
extern "C" RUNTIME_NOTHROW void Kotlin_mm_safePointWhileLoopBody() {
auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData();
threadData->gc().SafePointLoopBody();
}
extern "C" RUNTIME_NOTHROW void Kotlin_mm_safePointExceptionUnwind() {
auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData();
threadData->gc().SafePointExceptionUnwind();
}
@@ -1,61 +0,0 @@
/*
* Copyright 2010-2020 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.
*/
#include "ObjectFactory.hpp"
#include "Alignment.hpp"
#include "Alloc.h"
#include "GlobalData.hpp"
#include "Types.h"
using namespace kotlin;
ObjHeader* mm::ObjectFactory::ThreadQueue::CreateObject(const TypeInfo* typeInfo) noexcept {
RuntimeAssert(!typeInfo->IsArray(), "Must not be an array");
size_t allocSize = typeInfo->instanceSize_;
auto& node = producer_.Insert(allocSize);
auto* object = static_cast<ObjHeader*>(node.Data());
object->typeInfoOrMeta_ = const_cast<TypeInfo*>(typeInfo);
return object;
}
ArrayHeader* mm::ObjectFactory::ThreadQueue::CreateArray(const TypeInfo* typeInfo, uint32_t count) noexcept {
RuntimeAssert(typeInfo->IsArray(), "Must be an array");
uint32_t arraySize = static_cast<uint32_t>(-typeInfo->instanceSize_) * count;
// Note: array body is aligned, but for size computation it is enough to align the sum.
size_t allocSize = AlignUp(sizeof(ArrayHeader) + arraySize, kObjectAlignment);
auto& node = producer_.Insert(allocSize);
auto* array = static_cast<ArrayHeader*>(node.Data());
array->typeInfoOrMeta_ = const_cast<TypeInfo*>(typeInfo);
array->count_ = count;
return array;
}
bool mm::ObjectFactory::Iterator::IsArray() noexcept {
// `ArrayHeader` and `ObjHeader` are kept compatible, so the former can
// be always casted to the other.
auto* object = static_cast<ObjHeader*>((*iterator_).Data());
return object->type_info()->IsArray();
}
ObjHeader* mm::ObjectFactory::Iterator::GetObjHeader() noexcept {
auto* object = static_cast<ObjHeader*>((*iterator_).Data());
RuntimeAssert(!object->type_info()->IsArray(), "Must not be an array");
return object;
}
ArrayHeader* mm::ObjectFactory::Iterator::GetArrayHeader() noexcept {
auto* array = static_cast<ArrayHeader*>((*iterator_).Data());
RuntimeAssert(array->type_info()->IsArray(), "Must be an array");
return array;
}
mm::ObjectFactory::ObjectFactory() noexcept = default;
mm::ObjectFactory::~ObjectFactory() = default;
// static
mm::ObjectFactory& mm::ObjectFactory::Instance() noexcept {
return GlobalData::Instance().objectFactory();
}
@@ -25,12 +25,24 @@ namespace internal {
// A queue that is constructed by collecting subqueues from several `Producer`s.
// This is essentially a heterogeneous `MultiSourceQueue` on top of a singly linked list that
// uses `konanAllocMemory` and `konanFreeMemory`
// uses `Allocator` to allocate and free memory.
// TODO: Consider merging with `MultiSourceQueue` somehow.
template <size_t DataAlignment>
template <size_t DataAlignment, typename Allocator>
class ObjectFactoryStorage : private Pinned {
static_assert(IsValidAlignment(DataAlignment), "DataAlignment is not a valid alignment");
template <typename T>
class Deleter {
public:
void operator()(T* instance) noexcept {
instance->~T();
Allocator::Free(instance);
}
};
template <typename T>
using unique_ptr = std::unique_ptr<T, Deleter<T>>;
public:
// This class does not know its size at compile-time. Does not inherit from `KonanAllocatorAware` because
// in `KonanAllocatorAware::operator new(size_t size, KonanAllocTag)` `size` would be incorrect.
@@ -40,6 +52,13 @@ public:
public:
~Node() = default;
static Node& FromData(void* data) noexcept {
constexpr size_t kDataOffset = DataOffset();
Node* node = reinterpret_cast<Node*>(reinterpret_cast<uintptr_t>(data) - kDataOffset);
RuntimeAssert(node->Data() == data, "Node layout has broken");
return *node;
}
// Note: This can only be trivially destructible data, as nobody can invoke its destructor.
void* Data() noexcept {
constexpr size_t kDataOffset = DataOffset();
@@ -59,37 +78,36 @@ public:
Node() noexcept = default;
static KStdUniquePtr<Node> Create(size_t dataSize) noexcept {
static unique_ptr<Node> Create(Allocator& allocator, size_t dataSize) noexcept {
size_t dataSizeAligned = AlignUp(dataSize, DataAlignment);
size_t totalAlignment = std::max(alignof(Node), DataAlignment);
size_t totalSize = AlignUp(sizeof(Node) + dataSizeAligned, totalAlignment);
RuntimeAssert(
DataOffset() + dataSize <= totalSize, "totalSize %zu is not enough to fit data %zu at offset %zu", totalSize, dataSize,
DataOffset());
void* ptr = konanAllocAlignedMemory(totalSize, totalAlignment);
void* ptr = allocator.Alloc(totalSize, totalAlignment);
if (!ptr) {
// TODO: Try doing GC first.
konan::consoleErrorf("Out of memory trying to allocate %zu. Aborting.\n", totalSize);
konan::consoleErrorf("Out of memory trying to allocate %zu bytes. Aborting.\n", totalSize);
konan::abort();
}
RuntimeAssert(IsAligned(ptr, totalAlignment), "Allocator returned unaligned to %zu pointer %p", totalAlignment, ptr);
return KStdUniquePtr<Node>(new (ptr) Node());
return unique_ptr<Node>(new (ptr) Node());
}
KStdUniquePtr<Node> next_;
unique_ptr<Node> next_;
// There's some more data of an unknown (at compile-time) size here, but it cannot be represented
// with C++ members.
};
class Producer : private MoveOnly {
public:
explicit Producer(ObjectFactoryStorage& owner) noexcept : owner_(owner) {}
Producer(ObjectFactoryStorage& owner, Allocator allocator) noexcept : owner_(owner), allocator_(std::move(allocator)) {}
~Producer() { Publish(); }
Node& Insert(size_t dataSize) noexcept {
AssertCorrect();
auto node = Node::Create(dataSize);
auto node = Node::Create(allocator_, dataSize);
auto* nodePtr = node.get();
if (!root_) {
root_ = std::move(node);
@@ -159,7 +177,8 @@ public:
}
ObjectFactoryStorage& owner_; // weak
KStdUniquePtr<Node> root_;
Allocator allocator_;
unique_ptr<Node> root_;
Node* last_ = nullptr;
};
@@ -245,35 +264,160 @@ private:
}
}
KStdUniquePtr<Node> root_;
unique_ptr<Node> root_;
Node* last_ = nullptr;
SpinLock mutex_;
};
class SimpleAllocator {
public:
void* Alloc(size_t size, size_t alignment) noexcept { return konanAllocAlignedMemory(size, alignment); }
static void Free(void* instance) noexcept { konanFreeMemory(instance); }
};
template <typename BaseAllocator, typename GC>
class AllocatorWithGC {
public:
AllocatorWithGC(BaseAllocator base, GC& gc) noexcept : base_(std::move(base)), gc_(gc) {}
void* Alloc(size_t size, size_t alignment) noexcept {
gc_.SafePointAllocation(size);
if (void* ptr = base_.Alloc(size, alignment)) {
return ptr;
}
// Tell GC that we failed to allocate, and try one more time.
gc_.OnOOM(size);
return base_.Alloc(size, alignment);
}
static void Free(void* instance) noexcept { BaseAllocator::Free(instance); }
private:
BaseAllocator base_;
GC& gc_;
};
} // namespace internal
template <typename GC>
class ObjectFactory : private Pinned {
using GCObjectData = typename GC::ObjectData;
using GCThreadData = typename GC::ThreadData;
using Allocator = internal::AllocatorWithGC<internal::SimpleAllocator, GCThreadData>;
struct HeapObjHeader {
GCObjectData gcData;
alignas(kObjectAlignment) ObjHeader object;
};
// Needs to be kept compatible with `HeapObjHeader` just like `ArrayHeader` is compatible
// with `ObjHeader`: the former can always be casted to the other.
struct HeapArrayHeader {
GCObjectData gcData;
alignas(kObjectAlignment) ArrayHeader array;
};
public:
using Storage = internal::ObjectFactoryStorage<kObjectAlignment>;
using Storage = internal::ObjectFactoryStorage<kObjectAlignment, Allocator>;
class NodeRef {
public:
explicit NodeRef(typename Storage::Node& node) noexcept : node_(node) {}
static NodeRef From(ObjHeader* object) noexcept {
RuntimeAssert(object->heap(), "Must be a heap object");
auto* heapObject = reinterpret_cast<HeapObjHeader*>(reinterpret_cast<uintptr_t>(object) - offsetof(HeapObjHeader, object));
RuntimeAssert(&heapObject->object == object, "HeapObjHeader layout has broken");
return NodeRef(Storage::Node::FromData(heapObject));
}
static NodeRef From(ArrayHeader* array) noexcept {
// `ArrayHeader` and `ObjHeader` are kept compatible, so the former can
// be always casted to the other.
RuntimeAssert(reinterpret_cast<ObjHeader*>(array)->heap(), "Must be a heap object");
auto* heapArray = reinterpret_cast<HeapArrayHeader*>(reinterpret_cast<uintptr_t>(array) - offsetof(HeapArrayHeader, array));
RuntimeAssert(&heapArray->array == array, "HeapArrayHeader layout has broken");
return NodeRef(Storage::Node::FromData(heapArray));
}
NodeRef* operator->() noexcept { return this; }
GCObjectData& GCObjectData() noexcept {
// `HeapArrayHeader` and `HeapObjHeader` are kept compatible, so the former can
// be always casted to the other.
return static_cast<HeapObjHeader*>(node_.Data())->gcData;
}
bool IsArray() const noexcept {
// `HeapArrayHeader` and `HeapObjHeader` are kept compatible, so the former can
// be always casted to the other.
auto* object = &static_cast<HeapObjHeader*>(node_.Data())->object;
return object->type_info()->IsArray();
}
ObjHeader* GetObjHeader() noexcept {
auto* object = &static_cast<HeapObjHeader*>(node_.Data())->object;
RuntimeAssert(!object->type_info()->IsArray(), "Must not be an array");
return object;
}
ArrayHeader* GetArrayHeader() noexcept {
auto* array = &static_cast<HeapArrayHeader*>(node_.Data())->array;
RuntimeAssert(array->type_info()->IsArray(), "Must be an array");
return array;
}
bool operator==(const NodeRef& rhs) const noexcept { return &node_ == &rhs.node_; }
bool operator!=(const NodeRef& rhs) const noexcept { return !(*this == rhs); }
private:
typename Storage::Node& node_;
};
class ThreadQueue : private MoveOnly {
public:
explicit ThreadQueue(ObjectFactory& owner) noexcept : producer_(owner.storage_) {}
ThreadQueue(ObjectFactory& owner, GCThreadData& gc) noexcept :
producer_(owner.storage_, internal::AllocatorWithGC(internal::SimpleAllocator(), gc)) {}
ObjHeader* CreateObject(const TypeInfo* typeInfo) noexcept;
ArrayHeader* CreateArray(const TypeInfo* typeInfo, uint32_t count) noexcept;
ObjHeader* CreateObject(const TypeInfo* typeInfo) noexcept {
RuntimeAssert(!typeInfo->IsArray(), "Must not be an array");
size_t membersSize = typeInfo->instanceSize_ - sizeof(ObjHeader);
size_t allocSize = AlignUp(sizeof(HeapObjHeader) + membersSize, kObjectAlignment);
auto& node = producer_.Insert(allocSize);
auto* heapObject = new (node.Data()) HeapObjHeader();
auto* object = &heapObject->object;
object->typeInfoOrMeta_ = const_cast<TypeInfo*>(typeInfo);
return object;
}
ArrayHeader* CreateArray(const TypeInfo* typeInfo, uint32_t count) noexcept {
RuntimeAssert(typeInfo->IsArray(), "Must be an array");
uint32_t membersSize = static_cast<uint32_t>(-typeInfo->instanceSize_) * count;
// Note: array body is aligned, but for size computation it is enough to align the sum.
size_t allocSize = AlignUp(sizeof(HeapArrayHeader) + membersSize, kObjectAlignment);
auto& node = producer_.Insert(allocSize);
auto* heapArray = new (node.Data()) HeapArrayHeader();
auto* array = &heapArray->array;
array->typeInfoOrMeta_ = const_cast<TypeInfo*>(typeInfo);
array->count_ = count;
return array;
}
void Publish() noexcept { producer_.Publish(); }
void ClearForTests() noexcept { producer_.ClearForTests(); }
private:
Storage::Producer producer_;
typename Storage::Producer producer_;
};
class Iterator {
public:
Storage::Node& operator*() noexcept { return *iterator_; }
NodeRef operator*() noexcept { return NodeRef(*iterator_); }
NodeRef operator->() noexcept { return NodeRef(*iterator_); }
Iterator& operator++() noexcept {
++iterator_;
@@ -284,17 +428,12 @@ public:
bool operator!=(const Iterator& rhs) const noexcept { return iterator_ != rhs.iterator_; }
bool IsArray() noexcept;
ObjHeader* GetObjHeader() noexcept;
ArrayHeader* GetArrayHeader() noexcept;
private:
friend class ObjectFactory;
explicit Iterator(Storage::Iterator iterator) noexcept : iterator_(std::move(iterator)) {}
explicit Iterator(typename Storage::Iterator iterator) noexcept : iterator_(std::move(iterator)) {}
Storage::Iterator iterator_;
typename Storage::Iterator iterator_;
};
class Iterable {
@@ -307,13 +446,11 @@ public:
void EraseAndAdvance(Iterator& iterator) noexcept { iter_.EraseAndAdvance(iterator.iterator_); }
private:
Storage::Iterable iter_;
typename Storage::Iterable iter_;
};
ObjectFactory() noexcept;
~ObjectFactory();
static ObjectFactory& Instance() noexcept;
ObjectFactory() noexcept = default;
~ObjectFactory() = default;
Iterable Iter() noexcept { return Iterable(*this); }
@@ -12,17 +12,25 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "GC.hpp"
#include "TestSupport.hpp"
#include "Types.h"
using namespace kotlin;
using testing::_;
namespace {
using SimpleAllocator = mm::internal::SimpleAllocator;
template <size_t DataAlignment>
using ObjectFactoryStorage = mm::internal::ObjectFactoryStorage<DataAlignment>;
using ObjectFactoryStorage = mm::internal::ObjectFactoryStorage<DataAlignment, SimpleAllocator>;
using ObjectFactoryStorageRegular = ObjectFactoryStorage<alignof(void*)>;
namespace {
template <typename Storage>
using Producer = typename Storage::Producer;
template <size_t DataAlignment>
KStdVector<void*> Collect(ObjectFactoryStorage<DataAlignment>& storage) {
@@ -76,7 +84,7 @@ TEST(ObjectFactoryStorageTest, Empty) {
TEST(ObjectFactoryStorageTest, DoNotPublish) {
ObjectFactoryStorageRegular storage;
ObjectFactoryStorageRegular::Producer producer(storage);
Producer<ObjectFactoryStorageRegular> producer(storage, SimpleAllocator());
producer.Insert<int>(1);
producer.Insert<int>(2);
@@ -88,8 +96,8 @@ TEST(ObjectFactoryStorageTest, DoNotPublish) {
TEST(ObjectFactoryStorageTest, Publish) {
ObjectFactoryStorageRegular storage;
ObjectFactoryStorageRegular::Producer producer1(storage);
ObjectFactoryStorageRegular::Producer producer2(storage);
Producer<ObjectFactoryStorageRegular> producer1(storage, SimpleAllocator());
Producer<ObjectFactoryStorageRegular> producer2(storage, SimpleAllocator());
producer1.Insert<int>(1);
producer1.Insert<int>(2);
@@ -106,7 +114,7 @@ TEST(ObjectFactoryStorageTest, Publish) {
TEST(ObjectFactoryStorageTest, PublishDifferentTypes) {
ObjectFactoryStorage<alignof(MaxAlignedData)> storage;
ObjectFactoryStorage<alignof(MaxAlignedData)>::Producer producer(storage);
Producer<ObjectFactoryStorage<alignof(MaxAlignedData)>> producer(storage, SimpleAllocator());
producer.Insert<int>(1);
producer.Insert<size_t>(2);
@@ -139,7 +147,7 @@ TEST(ObjectFactoryStorageTest, PublishDifferentTypes) {
TEST(ObjectFactoryStorageTest, PublishSeveralTimes) {
ObjectFactoryStorageRegular storage;
ObjectFactoryStorageRegular::Producer producer(storage);
Producer<ObjectFactoryStorageRegular> producer(storage, SimpleAllocator());
// Add 2 elements and publish.
producer.Insert<int>(1);
@@ -167,7 +175,7 @@ TEST(ObjectFactoryStorageTest, PublishInDestructor) {
ObjectFactoryStorageRegular storage;
{
ObjectFactoryStorageRegular::Producer producer(storage);
Producer<ObjectFactoryStorageRegular> producer(storage, SimpleAllocator());
producer.Insert<int>(1);
producer.Insert<int>(2);
}
@@ -177,9 +185,22 @@ TEST(ObjectFactoryStorageTest, PublishInDestructor) {
EXPECT_THAT(actual, testing::ElementsAre(1, 2));
}
TEST(ObjectFactoryStorageTest, FindNode) {
ObjectFactoryStorageRegular storage;
Producer<ObjectFactoryStorageRegular> producer(storage, SimpleAllocator());
auto& node1 = producer.Insert<int>(1);
auto& node2 = producer.Insert<int>(2);
producer.Publish();
EXPECT_THAT(&ObjectFactoryStorageRegular::Node::FromData(node1.Data()), &node1);
EXPECT_THAT(&ObjectFactoryStorageRegular::Node::FromData(node2.Data()), &node2);
}
TEST(ObjectFactoryStorageTest, EraseFirst) {
ObjectFactoryStorageRegular storage;
ObjectFactoryStorageRegular::Producer producer(storage);
Producer<ObjectFactoryStorageRegular> producer(storage, SimpleAllocator());
producer.Insert<int>(1);
producer.Insert<int>(2);
@@ -205,7 +226,7 @@ TEST(ObjectFactoryStorageTest, EraseFirst) {
TEST(ObjectFactoryStorageTest, EraseMiddle) {
ObjectFactoryStorageRegular storage;
ObjectFactoryStorageRegular::Producer producer(storage);
Producer<ObjectFactoryStorageRegular> producer(storage, SimpleAllocator());
producer.Insert<int>(1);
producer.Insert<int>(2);
@@ -231,7 +252,7 @@ TEST(ObjectFactoryStorageTest, EraseMiddle) {
TEST(ObjectFactoryStorageTest, EraseLast) {
ObjectFactoryStorageRegular storage;
ObjectFactoryStorageRegular::Producer producer(storage);
Producer<ObjectFactoryStorageRegular> producer(storage, SimpleAllocator());
producer.Insert<int>(1);
producer.Insert<int>(2);
@@ -257,7 +278,7 @@ TEST(ObjectFactoryStorageTest, EraseLast) {
TEST(ObjectFactoryStorageTest, EraseAll) {
ObjectFactoryStorageRegular storage;
ObjectFactoryStorageRegular::Producer producer(storage);
Producer<ObjectFactoryStorageRegular> producer(storage, SimpleAllocator());
producer.Insert<int>(1);
producer.Insert<int>(2);
@@ -279,7 +300,7 @@ TEST(ObjectFactoryStorageTest, EraseAll) {
TEST(ObjectFactoryStorageTest, EraseTheOnlyElement) {
ObjectFactoryStorageRegular storage;
ObjectFactoryStorageRegular::Producer producer(storage);
Producer<ObjectFactoryStorageRegular> producer(storage, SimpleAllocator());
producer.Insert<int>(1);
@@ -307,7 +328,7 @@ TEST(ObjectFactoryStorageTest, ConcurrentPublish) {
for (int i = 0; i < kThreadCount; ++i) {
expected.push_back(i);
threads.emplace_back([i, &storage, &canStart, &readyCount]() {
ObjectFactoryStorageRegular::Producer producer(storage);
Producer<ObjectFactoryStorageRegular> producer(storage, SimpleAllocator());
producer.Insert<int>(i);
++readyCount;
while (!canStart) {
@@ -335,7 +356,7 @@ TEST(ObjectFactoryStorageTest, IterWhileConcurrentPublish) {
KStdVector<int> expectedBefore;
KStdVector<int> expectedAfter;
ObjectFactoryStorageRegular::Producer producer(storage);
Producer<ObjectFactoryStorageRegular> producer(storage, SimpleAllocator());
for (int i = 0; i < kStartCount; ++i) {
expectedBefore.push_back(i);
expectedAfter.push_back(i);
@@ -351,7 +372,7 @@ TEST(ObjectFactoryStorageTest, IterWhileConcurrentPublish) {
int j = i + kStartCount;
expectedAfter.push_back(j);
threads.emplace_back([j, &storage, &canStart, &startedCount, &readyCount]() {
ObjectFactoryStorageRegular::Producer producer(storage);
Producer<ObjectFactoryStorageRegular> producer(storage, SimpleAllocator());
producer.Insert<int>(j);
++readyCount;
while (!canStart) {
@@ -393,7 +414,7 @@ TEST(ObjectFactoryStorageTest, EraseWhileConcurrentPublish) {
constexpr int kThreadCount = kDefaultThreadCount;
KStdVector<int> expectedAfter;
ObjectFactoryStorageRegular::Producer producer(storage);
Producer<ObjectFactoryStorageRegular> producer(storage, SimpleAllocator());
for (int i = 0; i < kStartCount; ++i) {
if (i % 2 == 0) {
expectedAfter.push_back(i);
@@ -410,7 +431,7 @@ TEST(ObjectFactoryStorageTest, EraseWhileConcurrentPublish) {
int j = i + kStartCount;
expectedAfter.push_back(j);
threads.emplace_back([j, &storage, &canStart, &startedCount, &readyCount]() {
ObjectFactoryStorageRegular::Producer producer(storage);
Producer<ObjectFactoryStorageRegular> producer(storage, SimpleAllocator());
producer.Insert<int>(j);
++readyCount;
while (!canStart) {
@@ -446,10 +467,103 @@ TEST(ObjectFactoryStorageTest, EraseWhileConcurrentPublish) {
EXPECT_THAT(actual, testing::UnorderedElementsAreArray(expectedAfter));
}
using mm::ObjectFactory;
using mm::internal::AllocatorWithGC;
namespace {
class MockAllocator {
public:
MOCK_METHOD(void*, Alloc, (size_t, size_t));
};
class MockAllocatorWrapper {
public:
MockAllocator& operator*() { return *mock_; }
void* Alloc(size_t size, size_t alignment) { return mock_->Alloc(size, alignment); }
private:
KStdUniquePtr<testing::StrictMock<MockAllocator>> mock_ = make_unique<testing::StrictMock<MockAllocator>>();
};
class MockGC {
public:
MOCK_METHOD(void, SafePointAllocation, (size_t));
MOCK_METHOD(void, OnOOM, (size_t));
};
} // namespace
TEST(AllocatorWithGCTest, AllocateWithoutOOM) {
constexpr size_t size = 256;
constexpr size_t alignment = 8;
void* nonNull = reinterpret_cast<void*>(1);
MockAllocatorWrapper baseAllocator;
testing::StrictMock<MockGC> gc;
{
testing::InSequence seq;
EXPECT_CALL(gc, SafePointAllocation(size));
EXPECT_CALL(*baseAllocator, Alloc(size, alignment)).WillOnce(testing::Return(nonNull));
EXPECT_CALL(gc, OnOOM(_)).Times(0);
}
AllocatorWithGC<MockAllocatorWrapper, MockGC> allocator(std::move(baseAllocator), gc);
void* ptr = allocator.Alloc(size, alignment);
EXPECT_THAT(ptr, nonNull);
}
TEST(AllocatorWithGCTest, AllocateWithFixableOOM) {
constexpr size_t size = 256;
constexpr size_t alignment = 8;
void* nonNull = reinterpret_cast<void*>(1);
MockAllocatorWrapper baseAllocator;
testing::StrictMock<MockGC> gc;
{
testing::InSequence seq;
EXPECT_CALL(gc, SafePointAllocation(size));
EXPECT_CALL(*baseAllocator, Alloc(size, alignment)).WillOnce(testing::Return(nullptr));
EXPECT_CALL(gc, OnOOM(size));
EXPECT_CALL(*baseAllocator, Alloc(size, alignment)).WillOnce(testing::Return(nonNull));
}
AllocatorWithGC<MockAllocatorWrapper, MockGC> allocator(std::move(baseAllocator), gc);
void* ptr = allocator.Alloc(size, alignment);
EXPECT_THAT(ptr, nonNull);
}
TEST(AllocatorWithGCTest, AllocateWithUnfixableOOM) {
constexpr size_t size = 256;
constexpr size_t alignment = 8;
MockAllocatorWrapper baseAllocator;
testing::StrictMock<MockGC> gc;
{
testing::InSequence seq;
EXPECT_CALL(gc, SafePointAllocation(size));
EXPECT_CALL(*baseAllocator, Alloc(size, alignment)).WillOnce(testing::Return(nullptr));
EXPECT_CALL(gc, OnOOM(size));
EXPECT_CALL(*baseAllocator, Alloc(size, alignment)).WillOnce(testing::Return(nullptr));
}
AllocatorWithGC<MockAllocatorWrapper, MockGC> allocator(std::move(baseAllocator), gc);
void* ptr = allocator.Alloc(size, alignment);
EXPECT_THAT(ptr, nullptr);
}
namespace {
class GC {
public:
struct ObjectData {
uint32_t flags = 42;
};
class ThreadData {
public:
void SafePointAllocation(size_t size) noexcept {}
void OnOOM(size_t size) noexcept {}
};
};
using ObjectFactory = mm::ObjectFactory<GC>;
KStdUniquePtr<TypeInfo> MakeObjectTypeInfo(int32_t size) {
auto typeInfo = make_unique<TypeInfo>();
typeInfo->typeInfo_ = typeInfo.get();
@@ -468,32 +582,42 @@ KStdUniquePtr<TypeInfo> MakeArrayTypeInfo(int32_t elementSize) {
TEST(ObjectFactoryTest, CreateObject) {
auto typeInfo = MakeObjectTypeInfo(24);
GC::ThreadData gc;
ObjectFactory objectFactory;
ObjectFactory::ThreadQueue threadQueue(objectFactory);
ObjectFactory::ThreadQueue threadQueue(objectFactory, gc);
auto* object = threadQueue.CreateObject(typeInfo.get());
threadQueue.Publish();
auto node = ObjectFactory::NodeRef::From(object);
EXPECT_FALSE(node.IsArray());
EXPECT_THAT(node.GetObjHeader(), object);
EXPECT_THAT(node.GCObjectData().flags, 42);
auto iter = objectFactory.Iter();
auto it = iter.begin();
EXPECT_FALSE(it.IsArray());
EXPECT_THAT(it.GetObjHeader(), object);
EXPECT_THAT(*it, node);
++it;
EXPECT_THAT(it, iter.end());
}
TEST(ObjectFactoryTest, CreateArray) {
auto typeInfo = MakeArrayTypeInfo(24);
GC::ThreadData gc;
ObjectFactory objectFactory;
ObjectFactory::ThreadQueue threadQueue(objectFactory);
ObjectFactory::ThreadQueue threadQueue(objectFactory, gc);
auto* array = threadQueue.CreateArray(typeInfo.get(), 3);
threadQueue.Publish();
auto node = ObjectFactory::NodeRef::From(array);
EXPECT_TRUE(node.IsArray());
EXPECT_THAT(node.GetArrayHeader(), array);
EXPECT_THAT(node.GCObjectData().flags, 42);
auto iter = objectFactory.Iter();
auto it = iter.begin();
EXPECT_TRUE(it.IsArray());
EXPECT_THAT(it.GetArrayHeader(), array);
EXPECT_THAT(*it, node);
++it;
EXPECT_THAT(it, iter.end());
}
@@ -501,8 +625,9 @@ TEST(ObjectFactoryTest, CreateArray) {
TEST(ObjectFactoryTest, Erase) {
auto objectTypeInfo = MakeObjectTypeInfo(24);
auto arrayTypeInfo = MakeArrayTypeInfo(24);
GC::ThreadData gc;
ObjectFactory objectFactory;
ObjectFactory::ThreadQueue threadQueue(objectFactory);
ObjectFactory::ThreadQueue threadQueue(objectFactory, gc);
for (int i = 0; i < 10; ++i) {
threadQueue.CreateObject(objectTypeInfo.get());
@@ -514,7 +639,7 @@ TEST(ObjectFactoryTest, Erase) {
{
auto iter = objectFactory.Iter();
for (auto it = iter.begin(); it != iter.end();) {
if (it.IsArray()) {
if (it->IsArray()) {
iter.EraseAndAdvance(it);
} else {
++it;
@@ -526,7 +651,7 @@ TEST(ObjectFactoryTest, Erase) {
auto iter = objectFactory.Iter();
int count = 0;
for (auto it = iter.begin(); it != iter.end(); ++it, ++count) {
EXPECT_FALSE(it.IsArray());
EXPECT_FALSE(it->IsArray());
}
EXPECT_THAT(count, 10);
}
@@ -544,7 +669,8 @@ TEST(ObjectFactoryTest, ConcurrentPublish) {
for (int i = 0; i < kThreadCount; ++i) {
threads.emplace_back([&typeInfo, &objectFactory, &canStart, &readyCount, &expected, &expectedMutex]() {
ObjectFactory::ThreadQueue threadQueue(objectFactory);
GC::ThreadData gc;
ObjectFactory::ThreadQueue threadQueue(objectFactory, gc);
auto* object = threadQueue.CreateObject(typeInfo.get());
{
std::lock_guard<std::mutex> guard(expectedMutex);
@@ -567,7 +693,7 @@ TEST(ObjectFactoryTest, ConcurrentPublish) {
auto iter = objectFactory.Iter();
KStdVector<ObjHeader*> actual;
for (auto it = iter.begin(); it != iter.end(); ++it) {
actual.push_back(it.GetObjHeader());
actual.push_back(it->GetObjHeader());
}
EXPECT_THAT(actual, testing::UnorderedElementsAreArray(expected));
@@ -0,0 +1,149 @@
/*
* Copyright 2010-2021 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.
*/
#include "RootSet.hpp"
#include "KAssert.h"
#include "GlobalData.hpp"
#include "ThreadData.hpp"
using namespace kotlin;
mm::ThreadRootSet::Iterator::Iterator(begin_t, ThreadRootSet& owner) noexcept :
owner_(owner), phase_(Phase::kStack), stackIterator_(owner_.stack_.begin()) {
Init();
}
mm::ThreadRootSet::Iterator::Iterator(end_t, ThreadRootSet& owner) noexcept : owner_(owner), phase_(Phase::kDone) {}
ObjHeader*& mm::ThreadRootSet::Iterator::operator*() noexcept {
switch (phase_) {
case Phase::kStack:
return *stackIterator_;
case Phase::kTLS:
return **tlsIterator_;
case Phase::kDone:
RuntimeFail("Cannot dereference");
}
}
mm::ThreadRootSet::Iterator& mm::ThreadRootSet::Iterator::operator++() noexcept {
switch (phase_) {
case Phase::kStack:
++stackIterator_;
Init();
return *this;
case Phase::kTLS:
++tlsIterator_;
Init();
return *this;
case Phase::kDone:
return *this;
}
}
bool mm::ThreadRootSet::Iterator::operator==(const Iterator& rhs) const noexcept {
if (phase_ != rhs.phase_) {
return false;
}
switch (phase_) {
case Phase::kDone:
return true;
case Phase::kStack:
return stackIterator_ == rhs.stackIterator_;
case Phase::kTLS:
return tlsIterator_ == rhs.tlsIterator_;
}
}
void mm::ThreadRootSet::Iterator::Init() noexcept {
while (phase_ != Phase::kDone) {
switch (phase_) {
case Phase::kStack:
if (stackIterator_ != owner_.stack_.end()) return;
phase_ = Phase::kTLS;
tlsIterator_ = owner_.tls_.begin();
break;
case Phase::kTLS:
if (tlsIterator_ != owner_.tls_.end()) return;
phase_ = Phase::kDone;
break;
case Phase::kDone:
RuntimeFail("Impossible");
}
}
}
mm::GlobalRootSet::Iterator::Iterator(begin_t, GlobalRootSet& owner) noexcept :
owner_(owner), phase_(Phase::kGlobals), globalsIterator_(owner_.globalsIterable_.begin()) {
Init();
}
mm::GlobalRootSet::Iterator::Iterator(end_t, GlobalRootSet& owner) noexcept : owner_(owner), phase_(Phase::kDone) {}
ObjHeader*& mm::GlobalRootSet::Iterator::operator*() noexcept {
switch (phase_) {
case Phase::kGlobals:
return **globalsIterator_;
case Phase::kStableRefs:
return *stableRefsIterator_;
case Phase::kDone:
RuntimeFail("Cannot dereference");
}
}
mm::GlobalRootSet::Iterator& mm::GlobalRootSet::Iterator::operator++() noexcept {
switch (phase_) {
case Phase::kGlobals:
++globalsIterator_;
Init();
return *this;
case Phase::kStableRefs:
++stableRefsIterator_;
Init();
return *this;
case Phase::kDone:
return *this;
}
}
bool mm::GlobalRootSet::Iterator::operator==(const Iterator& rhs) const noexcept {
if (phase_ != rhs.phase_) {
return false;
}
switch (phase_) {
case Phase::kDone:
return true;
case Phase::kGlobals:
return globalsIterator_ == rhs.globalsIterator_;
case Phase::kStableRefs:
return stableRefsIterator_ == rhs.stableRefsIterator_;
}
}
void mm::GlobalRootSet::Iterator::Init() noexcept {
while (phase_ != Phase::kDone) {
switch (phase_) {
case Phase::kGlobals:
if (globalsIterator_ != owner_.globalsIterable_.end()) return;
phase_ = Phase::kStableRefs;
stableRefsIterator_ = owner_.stableRefsIterable_.begin();
break;
case Phase::kStableRefs:
if (stableRefsIterator_ != owner_.stableRefsIterable_.end()) return;
phase_ = Phase::kDone;
break;
case Phase::kDone:
RuntimeFail("Impossible");
}
}
}
mm::ThreadRootSet::ThreadRootSet(ThreadData& threadData) noexcept : ThreadRootSet(threadData.shadowStack(), threadData.tls()) {}
mm::GlobalRootSet::GlobalRootSet() noexcept :
GlobalRootSet(mm::GlobalData::Instance().globalsRegistry(), mm::GlobalData::Instance().stableRefRegistry()) {}
@@ -0,0 +1,123 @@
/*
* Copyright 2010-2021 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.
*/
#ifndef RUNTIME_MM_ROOT_SET_H
#define RUNTIME_MM_ROOT_SET_H
#include "GlobalsRegistry.hpp"
#include "ShadowStack.hpp"
#include "StableRefRegistry.hpp"
#include "ThreadLocalStorage.hpp"
struct ObjHeader;
namespace kotlin {
namespace mm {
class ThreadData;
class ThreadRootSet {
public:
class Iterator {
public:
struct begin_t {};
static constexpr inline begin_t begin = begin_t{};
struct end_t {};
static constexpr inline end_t end = end_t{};
Iterator(begin_t, ThreadRootSet& owner) noexcept;
Iterator(end_t, ThreadRootSet& owner) noexcept;
ObjHeader*& operator*() noexcept;
Iterator& operator++() noexcept;
bool operator==(const Iterator& rhs) const noexcept;
bool operator!=(const Iterator& rhs) const noexcept { return !(*this == rhs); }
private:
enum class Phase {
kStack,
kTLS,
kDone,
};
void Init() noexcept;
ThreadRootSet& owner_;
Phase phase_;
union {
ShadowStack::Iterator stackIterator_;
ThreadLocalStorage::Iterator tlsIterator_;
};
};
ThreadRootSet(ShadowStack& stack, ThreadLocalStorage& tls) noexcept : stack_(stack), tls_(tls) {}
explicit ThreadRootSet(ThreadData& threadData) noexcept;
Iterator begin() noexcept { return Iterator(Iterator::begin, *this); }
Iterator end() noexcept { return Iterator(Iterator::end, *this); }
private:
ShadowStack& stack_;
ThreadLocalStorage& tls_;
};
class GlobalRootSet {
public:
class Iterator {
public:
struct begin_t {};
static constexpr inline begin_t begin = begin_t{};
struct end_t {};
static constexpr inline end_t end = end_t{};
Iterator(begin_t, GlobalRootSet& owner) noexcept;
Iterator(end_t, GlobalRootSet& owner) noexcept;
ObjHeader*& operator*() noexcept;
Iterator& operator++() noexcept;
bool operator==(const Iterator& rhs) const noexcept;
bool operator!=(const Iterator& rhs) const noexcept { return !(*this == rhs); }
private:
enum class Phase {
kGlobals,
kStableRefs,
kDone,
};
void Init() noexcept;
GlobalRootSet& owner_;
Phase phase_;
union {
GlobalsRegistry::Iterator globalsIterator_;
StableRefRegistry::Iterator stableRefsIterator_;
};
};
GlobalRootSet(GlobalsRegistry& globalsRegistry, StableRefRegistry& stableRefRegistry) noexcept :
globalsIterable_(globalsRegistry.Iter()), stableRefsIterable_(stableRefRegistry.Iter()) {}
GlobalRootSet() noexcept;
Iterator begin() noexcept { return Iterator(Iterator::begin, *this); }
Iterator end() noexcept { return Iterator(Iterator::end, *this); }
private:
// TODO: These use separate locks, which is inefficient, and slightly dangerous. In practice it's
// fine, because this is the only place where these two locks are taken simultaneously.
GlobalsRegistry::Iterable globalsIterable_;
StableRefRegistry::Iterable stableRefsIterable_;
};
} // namespace mm
} // namespace kotlin
#endif // RUNTIME_MM_ROOT_SET_H
@@ -0,0 +1,126 @@
/*
* Copyright 2010-2021 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.
*/
#include "RootSet.hpp"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "ShadowStack.hpp"
using namespace kotlin;
namespace {
// TODO: All the test helpers to create the rootset should be abstracted out.
template <size_t LocalsCount>
class StackEntry : private Pinned {
public:
static_assert(LocalsCount > 0, "Must have at least 1 object on stack");
explicit StackEntry(mm::ShadowStack& shadowStack) : shadowStack_(shadowStack), value_(make_unique<ObjHeader>()) {
// Fill `locals_` with some values.
for (size_t i = 0; i < LocalsCount; ++i) {
(*this)[i] = value_.get() + i;
}
shadowStack_.EnterFrame(data_.data(), 0, kTotalCount);
}
~StackEntry() { shadowStack_.LeaveFrame(data_.data(), 0, kTotalCount); }
ObjHeader*& operator[](size_t index) { return data_[kFrameOverlayCount + index]; }
private:
mm::ShadowStack& shadowStack_;
KStdUniquePtr<ObjHeader> value_;
// The following is what the compiler creates on the stack.
static inline constexpr int kFrameOverlayCount = sizeof(FrameOverlay) / sizeof(ObjHeader**);
static inline constexpr int kTotalCount = kFrameOverlayCount + LocalsCount;
std::array<ObjHeader*, kTotalCount> data_;
};
struct TLSKey {};
} // namespace
TEST(ThreadRootSetTest, Basic) {
mm::ShadowStack stack;
StackEntry<2> entry(stack);
TLSKey key;
mm::ThreadLocalStorage tls;
tls.AddRecord(&key, 3);
tls.Commit();
mm::ThreadRootSet iter(stack, tls);
KStdVector<ObjHeader*> actual;
for (auto& object : iter) {
actual.push_back(object);
}
EXPECT_THAT(actual, testing::ElementsAre(entry[0], entry[1], *tls.Lookup(&key, 0), *tls.Lookup(&key, 1), *tls.Lookup(&key, 2)));
}
TEST(ThreadRootSetTest, Empty) {
mm::ShadowStack stack;
mm::ThreadLocalStorage tls;
mm::ThreadRootSet iter(stack, tls);
KStdVector<ObjHeader*> actual;
for (auto& object : iter) {
actual.push_back(object);
}
EXPECT_THAT(actual, testing::IsEmpty());
}
TEST(GlobalRootSetTest, Basic) {
mm::GlobalsRegistry globals;
mm::GlobalsRegistry::ThreadQueue globalsProducer(globals);
ObjHeader* global1 = reinterpret_cast<ObjHeader*>(1);
ObjHeader* global2 = reinterpret_cast<ObjHeader*>(2);
globalsProducer.Insert(&global1);
globalsProducer.Insert(&global2);
mm::StableRefRegistry stableRefs;
mm::StableRefRegistry::ThreadQueue stableRefsProducer(stableRefs);
ObjHeader* stableRef1 = reinterpret_cast<ObjHeader*>(3);
ObjHeader* stableRef2 = reinterpret_cast<ObjHeader*>(4);
ObjHeader* stableRef3 = reinterpret_cast<ObjHeader*>(5);
stableRefsProducer.Insert(stableRef1);
stableRefsProducer.Insert(stableRef2);
stableRefsProducer.Insert(stableRef3);
globalsProducer.Publish();
stableRefsProducer.Publish();
mm::GlobalRootSet iter(globals, stableRefs);
KStdVector<ObjHeader*> actual;
for (auto& object : iter) {
actual.push_back(object);
}
EXPECT_THAT(actual, testing::ElementsAre(global1, global2, stableRef1, stableRef2, stableRef3));
}
TEST(GlobalRootSetTest, Empty) {
mm::GlobalsRegistry globals;
mm::StableRefRegistry stableRefs;
mm::GlobalRootSet iter(globals, stableRefs);
KStdVector<ObjHeader*> actual;
for (auto& object : iter) {
actual.push_back(object);
}
EXPECT_THAT(actual, testing::IsEmpty());
}
@@ -26,6 +26,9 @@ public:
using Iterator = MultiSourceQueue<ObjHeader*>::Iterator;
using Node = MultiSourceQueue<ObjHeader*>::Node;
StableRefRegistry();
~StableRefRegistry();
static StableRefRegistry& Instance() noexcept;
Node* RegisterStableRef(mm::ThreadData* threadData, ObjHeader* object) noexcept;
@@ -46,11 +49,6 @@ public:
Iterable Iter() noexcept { return stableRefs_.Iter(); }
private:
friend class GlobalData;
StableRefRegistry();
~StableRefRegistry();
// Current approach optimizes for creating and disposing of stable refs:
// * creation just enqueues ref, disposing either queues or deletes the ref immediately (if it still resides in the current queue).
// * when thread is stopped, it'll scan through the local queue (to mark that refs no longer reside in it) and push creation and
+4 -27
View File
@@ -8,31 +8,24 @@
#include "KAssert.h"
ALWAYS_INLINE bool isFrozen(const ObjHeader* obj) {
TODO();
}
ALWAYS_INLINE bool isPermanentOrFrozen(const ObjHeader* obj) {
TODO();
// TODO: Unimplemented
return false;
}
extern "C" {
void MutationCheck(ObjHeader* obj) {
TODO();
// TODO: Unimplemented
}
void FreezeSubgraph(ObjHeader* obj) {
TODO();
// TODO: Unimplemented
}
void EnsureNeverFrozen(ObjHeader* obj) {
TODO();
}
void Kotlin_native_internal_GC_collect(ObjHeader*) {
TODO();
}
void Kotlin_native_internal_GC_suspend(ObjHeader*) {
TODO();
}
@@ -81,10 +74,6 @@ bool Kotlin_native_internal_GC_getTuneThreshold(ObjHeader*) {
TODO();
}
RUNTIME_NOTHROW void PerformFullGC(MemoryState* memory) {
TODO();
}
bool TryAddHeapRef(const ObjHeader* object) {
TODO();
}
@@ -101,16 +90,4 @@ ForeignRefContext InitLocalForeignRef(ObjHeader* object) {
TODO();
}
RUNTIME_NOTHROW void Kotlin_mm_safePointFunctionEpilogue() {
// TODO: Unimplemented
}
RUNTIME_NOTHROW void Kotlin_mm_safePointWhileLoopBody() {
// TODO: Unimplemented
}
RUNTIME_NOTHROW void Kotlin_mm_safePointExceptionUnwind() {
// TODO: Unimplemented
}
} // extern "C"
@@ -9,7 +9,9 @@
#include <atomic>
#include <pthread.h>
#include "GlobalData.hpp"
#include "GlobalsRegistry.hpp"
#include "GC.hpp"
#include "ObjectFactory.hpp"
#include "ShadowStack.hpp"
#include "StableRefRegistry.hpp"
@@ -32,7 +34,8 @@ public:
globalsThreadQueue_(GlobalsRegistry::Instance()),
stableRefThreadQueue_(StableRefRegistry::Instance()),
state_(ThreadState::kRunnable),
objectFactoryThreadQueue_(ObjectFactory::Instance()) {}
gc_(GlobalData::Instance().gc()),
objectFactoryThreadQueue_(GlobalData::Instance().objectFactory(), gc_) {}
~ThreadData() = default;
@@ -48,20 +51,30 @@ public:
ThreadState setState(ThreadState state) noexcept { return state_.exchange(state); }
ObjectFactory::ThreadQueue& objectFactoryThreadQueue() noexcept { return objectFactoryThreadQueue_; }
ObjectFactory<GC>::ThreadQueue& objectFactoryThreadQueue() noexcept { return objectFactoryThreadQueue_; }
ShadowStack& shadowStack() noexcept { return shadowStack_; }
KStdVector<std::pair<ObjHeader**, ObjHeader*>>& initializingSingletons() noexcept { return initializingSingletons_; }
GC::ThreadData& gc() noexcept { return gc_; }
void Publish() noexcept {
// TODO: These use separate locks, which is inefficient.
globalsThreadQueue_.Publish();
stableRefThreadQueue_.Publish();
objectFactoryThreadQueue_.Publish();
}
private:
const pthread_t threadId_;
GlobalsRegistry::ThreadQueue globalsThreadQueue_;
ThreadLocalStorage tls_;
StableRefRegistry::ThreadQueue stableRefThreadQueue_;
std::atomic<ThreadState> state_;
ObjectFactory::ThreadQueue objectFactoryThreadQueue_;
ShadowStack shadowStack_;
GC::ThreadData gc_;
ObjectFactory<GC>::ThreadQueue objectFactoryThreadQueue_;
KStdVector<std::pair<ObjHeader**, ObjHeader*>> initializingSingletons_;
};
@@ -0,0 +1,50 @@
/*
* Copyright 2010-2021 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.
*/
#ifndef RUNTIME_MM_NOOP_GC_H
#define RUNTIME_MM_NOOP_GC_H
#include <cstddef>
#include "Utils.hpp"
namespace kotlin {
namespace mm {
// No-op GC is a GC that does not free memory.
// TODO: It can be made more efficient.
class NoOpGC : private Pinned {
public:
class ObjectData {};
class ThreadData : private Pinned {
public:
using ObjectData = NoOpGC::ObjectData;
explicit ThreadData(NoOpGC& gc) noexcept {}
~ThreadData() = default;
void SafePointFunctionEpilogue() noexcept {}
void SafePointLoopBody() noexcept {}
void SafePointExceptionUnwind() noexcept {}
void SafePointAllocation(size_t size) noexcept {}
void PerformFullGC() noexcept {}
void OnOOM(size_t size) noexcept {}
private:
};
NoOpGC() noexcept = default;
~NoOpGC() = default;
private:
};
} // namespace mm
} // namespace kotlin
#endif // RUNTIME_MM_NOOP_GC_H
@@ -6,7 +6,7 @@ org.gradle.workers.max=4
# Pin Kotlin version:
# CHANGE_VERSION_WITH_RELEASE
kotlin_version=1.4.10
kotlin_version=1.4.30
# Use custom Kotlin/Native home:
kotlin.native.home=../../dist
@@ -6,7 +6,7 @@ org.gradle.workers.max=4
# Pin Kotlin version:
# CHANGE_VERSION_WITH_RELEASE
kotlin_version=1.4.10
kotlin_version=1.4.30
# Sets maven path for the kotlin version other than release
#kotlinCompilerRepo=
@@ -6,7 +6,7 @@ org.gradle.workers.max=4
# Pin Kotlin version:
# CHANGE_VERSION_WITH_RELEASE
kotlin_version=1.4.10
kotlin_version=1.4.30
# Use custom Kotlin/Native home:
kotlin.native.home=../../../dist
+1 -1
View File
@@ -6,7 +6,7 @@ org.gradle.workers.max=4
# Pin Kotlin version:
# CHANGE_VERSION_WITH_RELEASE
kotlin_version=1.4.10
kotlin_version=1.4.30
# Sets maven path for the kotlin version other than release
#kotlinCompilerRepo=
@@ -58,7 +58,7 @@ fun buildLibrary(
): KonanLibraryLayout {
val libFile = File(output)
val unzippedDir = if (nopack) libFile else org.jetbrains.kotlin.konan.file.createTempDir(moduleName)
val unzippedDir = if (nopack) libFile else org.jetbrains.kotlin.konan.file.createTempDir("klib")
val layout = KonanLibraryLayoutForWriter(libFile, unzippedDir, target)
val library = KonanLibraryWriterImpl(
moduleName,
@@ -33,7 +33,7 @@ class AppleConfigurablesImpl(
override val absoluteTargetSysRoot: String get() = when (val provider = xcodePartsProvider) {
is XcodePartsProvider.Local -> when (target) {
KonanTarget.MACOS_X64 -> provider.xcode.macosxSdk
KonanTarget.MACOS_X64, KonanTarget.MACOS_ARM64 -> provider.xcode.macosxSdk
KonanTarget.IOS_ARM32, KonanTarget.IOS_ARM64 -> provider.xcode.iphoneosSdk
KonanTarget.IOS_X64 -> provider.xcode.iphonesimulatorSdk
KonanTarget.TVOS_ARM64 -> provider.xcode.appletvosSdk
@@ -67,6 +67,12 @@ class ClangArgs(private val configurables: Configurables) : Configurables by con
}
}
// PIC is not required on Windows (and Clang will fail with `error: unsupported option '-fPIC'`)
if (configurables !is MingwConfigurables) {
// `-fPIC` allows us to avoid some problems when producing dynamic library.
// See KT-43502.
add(listOf("-fPIC"))
}
}.flatten()
private val osVersionMin: String
@@ -89,6 +95,14 @@ class ClangArgs(private val configurables: Configurables) : Configurables by con
"-mmacosx-version-min=$osVersionMin"
)
// Here we workaround Clang 8 limitation: macOS major version should be 10.
// So we compile runtime with version 10.16 and then override version in BitcodeCompiler.
// TODO: Fix with LLVM Update.
KonanTarget.MACOS_ARM64 -> listOf(
"-arch", "arm64",
"-mmacosx-version-min=10.16"
)
KonanTarget.IOS_ARM32 -> listOf(
"-stdlib=libc++",
"-arch", "armv7",
@@ -42,7 +42,7 @@ fun loadConfigurables(target: KonanTarget, properties: Properties, baseDir: Stri
KonanTarget.LINUX_MIPS32, KonanTarget.LINUX_MIPSEL32 ->
GccConfigurablesImpl(target, properties, baseDir)
KonanTarget.MACOS_X64,
KonanTarget.MACOS_X64, KonanTarget.MACOS_ARM64,
KonanTarget.IOS_ARM32, KonanTarget.IOS_ARM64, KonanTarget.IOS_X64,
KonanTarget.TVOS_ARM64, KonanTarget.TVOS_X64,
KonanTarget.WATCHOS_ARM64, KonanTarget.WATCHOS_ARM32,