[MERGE] KT: build-1.5.20-dev-372 KT/N: e734b52b0 OLD: d72e63334
This commit is contained in:
@@ -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
|
||||
|
||||
+2
-1
@@ -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,
|
||||
|
||||
+1
-1
@@ -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,
|
||||
|
||||
@@ -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 ##
|
||||
|
||||
|
||||
+7
-1
@@ -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)
|
||||
|
||||
+4
-4
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
+9
-27
@@ -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("}")
|
||||
|
||||
+9
-4
@@ -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
|
||||
|
||||
+4
-3
@@ -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)
|
||||
|
||||
+2
-1
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
+7
@@ -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
|
||||
|
||||
+161
-111
@@ -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,
|
||||
|
||||
+3
-6
@@ -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)
|
||||
|
||||
+46
-9
@@ -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(
|
||||
|
||||
+87
-112
@@ -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() {
|
||||
|
||||
+16
-11
@@ -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> =
|
||||
|
||||
+5
-5
@@ -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
|
||||
|
||||
+4
-13
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+10
-11
@@ -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
|
||||
}
|
||||
|
||||
+3
-3
@@ -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) {
|
||||
|
||||
+4
-4
@@ -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)
|
||||
}
|
||||
|
||||
+2
-3
@@ -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")
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
+1
-1
@@ -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",
|
||||
|
||||
+1
-1
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user