Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

K2 support #1525

Merged
merged 20 commits into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* None.

### Enhancements
* None.
* Support for experimental K2-compilation with `kotlin.experimental.tryK2=true`. (Issue [#1483](https://github.com/realm/realm-kotlin/issues/1483))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add the restriction about companion classes and Serialization or maybe reference some other place (either issue or documentation about it)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created #1567 to track it. This should enable users to find and workaround the issue. Do we also need it as known issues?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably not, my guess is that more people will find it if we just pin #1567.


### Fixed
* None.
Expand All @@ -13,7 +13,7 @@
* File format: Generates Realms with file format v23.
* Realm Studio 13.0.0 or above is required to open Realms created by this version.
* This release is compatible with the following Kotlin releases:
* Kotlin 1.8.0 and above. The K2 compiler is not supported yet.
* Kotlin 1.9.0 and above. Support for experimental K2-compilation with `kotlin.experimental.tryK2=true`.
* Ktor 2.1.2 and above.
* Coroutines 1.7.0 and above.
* AtomicFu 0.18.3 and above.
Expand All @@ -22,6 +22,7 @@
* Minimum Gradle version: 6.8.3.
* Minimum Android Gradle Plugin version: 4.1.3.
* Minimum Android SDK: 16.
* Minimum R8: 8.0.34.

### Internal
* None.
Expand Down
2 changes: 2 additions & 0 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ pipeline {
when { expression { runTests } }
steps {
testAndCollect("integration-tests/gradle-plugin-test", "integrationTest")
testAndCollect("integration-tests/gradle-plugin-test", "-Pkotlin.experimental.tryK2=true integrationTest")
}
}
stage('Tests Android Sample App') {
Expand All @@ -268,6 +269,7 @@ pipeline {
when { expression { runTests } }
steps {
testAndCollect("examples/realm-java-compatibility", "connectedAndroidTest")
testAndCollect("examples/realm-java-compatibility", "-Pkotlin.experimental.tryK2=true connectedAndroidTest")
}
}
stage('Track build metrics') {
Expand Down
8 changes: 4 additions & 4 deletions buildSrc/src/main/kotlin/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ object Versions {
const val buildToolsVersion = "33.0.0"
const val buildTools = "7.3.1" // https://maven.google.com/web/index.html?q=gradle#com.android.tools.build:gradle
const val ndkVersion = "23.2.8568313"
const val r8 = "4.0.48" // See https://developer.android.com/build/kotlin-support
const val r8 = "8.0.34" // See https://developer.android.com/build/kotlin-support
rorbech marked this conversation as resolved.
Show resolved Hide resolved
}
const val androidxBenchmarkPlugin = "1.2.0-alpha12" // https://maven.google.com/web/index.html#androidx.benchmark:androidx.benchmark.gradle.plugin
const val androidxStartup = "1.1.1" // https://maven.google.com/web/index.html?q=startup#androidx.startup:startup-runtime
Expand All @@ -119,14 +119,14 @@ object Versions {
const val coroutines = "1.7.0" // https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core
const val datetime = "0.4.0" // https://github.com/Kotlin/kotlinx-datetime
const val detektPlugin = "1.22.0-RC2" // https://github.com/detekt/detekt
const val dokka = "1.6.0" // https://github.com/Kotlin/dokka
const val dokka = "1.9.0" // https://github.com/Kotlin/dokka
const val gradlePluginPublishPlugin = "0.15.0" // https://plugins.gradle.org/plugin/com.gradle.plugin-publish
const val jmh = "1.34" // https://github.com/openjdk/jmh
const val jmhPlugin = "0.6.6" // https://github.com/melix/jmh-gradle-plugin
const val junit = "4.13.2" // https://mvnrepository.com/artifact/junit/junit
const val kbson = "0.3.0" // https://github.com/mongodb/kbson
// When updating the Kotlin version, also remember to update /examples/min-android-sample/build.gradle.kts
const val kotlin = "1.8.21" // https://github.com/JetBrains/kotlin and https://kotlinlang.org/docs/releases.html#release-details
const val kotlin = "1.9.0" // https://github.com/JetBrains/kotlin and https://kotlinlang.org/docs/releases.html#release-details
const val kotlinJvmTarget = "1.8" // Which JVM bytecode version is kotlin compiled to.
const val latestKotlin = "1.9.20-Beta" // https://kotlinlang.org/docs/eap.html#build-details
const val kotlinCompileTesting = "1.5.0" // https://github.com/tschuchortdev/kotlin-compile-testing
Expand All @@ -136,7 +136,7 @@ object Versions {
const val nexusPublishPlugin = "1.1.0" // https://github.com/gradle-nexus/publish-plugin
const val okio = "3.2.0" // https://square.github.io/okio/#releases
const val relinker = "1.4.5" // https://github.com/KeepSafe/ReLinker
const val serialization = "1.4.0" // https://kotlinlang.org/docs/releases.html#release-details
const val serialization = "1.6.0" // https://kotlinlang.org/docs/releases.html#release-details
const val shadowJar = "6.1.0" // https://mvnrepository.com/artifact/com.github.johnrengelman.shadow/com.github.johnrengelman.shadow.gradle.plugin?repo=gradle-plugins
val sourceCompatibilityVersion = JavaVersion.VERSION_1_8 // Language level of any Java source code.
val targetCompatibilityVersion = JavaVersion.VERSION_1_8 // Version of generated JVM bytecode from Java files.
Expand Down
2 changes: 1 addition & 1 deletion examples/kmm-sample/shared/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ kotlin {
implementation("com.google.android.material:material:1.2.0")
}
}
val androidTest by getting {
val androidInstrumentedTest by getting {
dependencies {
implementation(kotlin("test-junit"))
implementation("junit:junit:4.12")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@
* limitations under the License.
*/

@file:OptIn(ExperimentalForeignApi::class)
cmelchior marked this conversation as resolved.
Show resolved Hide resolved

package io.realm.test.multiplatform.util.platform

import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.cstr

actual object PlatformUtils {
Expand Down
1 change: 1 addition & 0 deletions packages/cinterop/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ kotlin {
compilations.all {
kotlinOptions {
freeCompilerArgs += listOf("-opt-in=kotlin.ExperimentalUnsignedTypes")
freeCompilerArgs += listOf("-opt-in=kotlinx.cinterop.ExperimentalForeignApi")
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1985,7 +1985,7 @@ actual object RealmInterop {
realm_wrapper.realm_app_get_all_users(
app.cptr(),
null,
0,
0UL,
capacityCount.ptr
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,14 +136,14 @@ class CinteropTest {
classes[0].apply {
name = "foo".cstr.ptr
primary_key = "".cstr.ptr
num_properties = 1.toULong()
num_computed_properties = 0.toULong()
num_properties = 1UL
num_computed_properties = 0UL
flags = RLM_CLASS_NORMAL.toInt()
}

val classProperties: CPointer<CPointerVarOf<CPointer<realm_property_info_t>>> =
cValuesOf(prop_1_1.ptr).ptr
val realmSchemaNew = realm_schema_new(classes, 1.toULong(), classProperties)
val realmSchemaNew = realm_schema_new(classes, 1UL, classProperties)

assertNoError()
assertTrue(
Expand All @@ -157,7 +157,7 @@ class CinteropTest {
realm_config_set_path(config, "c_api_test.realm")
realm_config_set_schema(config, realmSchemaNew)
realm_config_set_schema_mode(config, realm_schema_mode_e.RLM_SCHEMA_MODE_AUTOMATIC)
realm_config_set_schema_version(config, 1)
realm_config_set_schema_version(config, 1UL)

val realm: CPointer<realm_t>? = realm_open(config)
assertEquals(1U, realm_get_num_classes(realm))
Expand Down
7 changes: 6 additions & 1 deletion packages/library-base/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,12 @@ kotlin {
// name and build type variant as a suffix, this default behaviour can cause mismatch at runtime https://github.com/realm/realm-kotlin/issues/621
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
kotlinOptions {
freeCompilerArgs = listOf("-module-name", "io.realm.kotlin.library")
freeCompilerArgs += listOf("-module-name", "io.realm.kotlin.library")
}
}
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile>().all {
kotlinOptions {
freeCompilerArgs += listOf("-opt-in=kotlinx.cinterop.ExperimentalForeignApi")
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import io.realm.kotlin.log.RealmLog
* An **initializer** for Sync specific functionality that does not fit into the `RealmInitializer`
* in cinterop.o allow Realm to access context properties.
*/
class RealmSyncInitializer : Initializer<Context> {
internal class RealmSyncInitializer : Initializer<Context> {

companion object {
@Suppress("DEPRECATION") // Should only be called below API 21
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ internal class MutableSubscriptionSetImpl<T : BaseRealm>(
return result
}

@Suppress("invisible_member")
@Suppress("invisible_member", "invisible_reference")
override fun <T : RealmObject> removeAll(type: KClass<T>): Boolean {
var result = false
val objectType = io.realm.kotlin.internal.platform.realmObjectCompanionOrThrow(type).`io_realm_kotlin_className`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ internal class SubscriptionImpl(
// Trim the query to match the output of RealmQuery.description()
override val queryDescription: String = RealmInterop.realm_sync_subscription_query_string(nativePointer).trim()

@Suppress("invisible_member")
@Suppress("invisible_member", "invisible_reference")
override fun <T : RealmObject> asQuery(type: KClass<T>): RealmQuery<T> {
// TODO Check for invalid combinations of Realm and type once we properly support
// DynamicRealm
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ internal class SyncConfigurationImpl(
initializerHelper.onSyncError(session, frozenAppPointer, error)
}
} catch (ex: Exception) {
@Suppress("invisible_member")
@Suppress("invisible_member", "invisible_reference")
RealmLog.error("Error thrown and ignored in `onManualResetFallback`: $ex")
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ internal open class SyncSessionImpl(
}
}

@Suppress("invisible_member") // To be able to use RealmImpl.scopedFlow from library-base
@Suppress("invisible_member", "invisible_reference") // To be able to use RealmImpl.scopedFlow from library-base
override fun connectionStateAsFlow(): Flow<ConnectionStateChange> = realm.scopedFlow {
callbackFlow {
val token: AtomicRef<Cancellable> = kotlinx.atomicfu.atomic(NO_OP_NOTIFICATION_TOKEN)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ import org.jetbrains.kotlin.name.Name
internal object Names {
const val REALM_SYNTHETIC_PROPERTY_PREFIX = "io_realm_kotlin_"

val REALM_OBJECT: Name = Name.identifier("RealmObject")
val EMBEDDED_REALM_OBJECT: Name = Name.identifier("EmbeddedRealmObject")
val ASYMMETRIC_REALM_OBJECT: Name = Name.identifier("AsymmetricRealmObject")

val REALM_OBJECT_COMPANION_CLASS_MEMBER: Name =
Name.identifier("${REALM_SYNTHETIC_PROPERTY_PREFIX}class")
val REALM_OBJECT_COMPANION_CLASS_NAME_MEMBER: Name =
Expand All @@ -40,6 +44,9 @@ internal object Names {
Name.identifier("${REALM_SYNTHETIC_PROPERTY_PREFIX}schema")
val REALM_OBJECT_COMPANION_NEW_INSTANCE_METHOD =
Name.identifier("${REALM_SYNTHETIC_PROPERTY_PREFIX}newInstance")
val REALM_OBJECT_TO_STRING_METHOD = Name.identifier("toString")
val REALM_OBJECT_EQUALS = Name.identifier("equals")
val REALM_OBJECT_HASH_CODE = Name.identifier("hashCode")

val SET = Name.special("<set-?>")

Expand Down Expand Up @@ -90,11 +97,12 @@ internal object FqNames {
val PACKAGE_KBSON = FqName("org.mongodb.kbson")
val PACKAGE_KOTLIN_COLLECTIONS = FqName("kotlin.collections")
val PACKAGE_KOTLIN_REFLECT = FqName("kotlin.reflect")
val PACKAGE_TYPES = FqName("io.realm.kotlin.types")
val PACKAGE_TYPES: FqName = FqName("io.realm.kotlin.types")
val PACKAGE_REALM_INTEROP = FqName("io.realm.kotlin.internal.interop")
val PACKAGE_REALM_INTERNAL = FqName("io.realm.kotlin.internal")
val PACKAGE_MONGODB = FqName("io.realm.kotlin.mongodb")
val PACKAGE_MONGODB_INTERNAL = FqName("io.realm.kotlin.mongodb.internal")
val APP_CONFIGURATION_BUILDER = FqName("AppConfiguration.Builder")
}

object ClassIds {
Expand Down Expand Up @@ -160,5 +168,5 @@ object ClassIds {
val APP_IMPL = ClassId(PACKAGE_MONGODB_INTERNAL, Name.identifier("AppImpl"))
val APP_CONFIGURATION = ClassId(PACKAGE_MONGODB, Name.identifier("AppConfiguration"))
val APP_CONFIGURATION_IMPL = ClassId(PACKAGE_MONGODB_INTERNAL, Name.identifier("AppConfigurationImpl"))
val APP_CONFIGURATION_BUILDER = ClassId(FqName("io.realm.kotlin.mongodb.AppConfiguration"), FqName("Builder"), true)
val APP_CONFIGURATION_BUILDER = ClassId(PACKAGE_MONGODB, FqNames.APP_CONFIGURATION_BUILDER, false)
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ import io.realm.kotlin.compiler.ClassIds.EMBEDDED_OBJECT_INTERFACE
import io.realm.kotlin.compiler.ClassIds.KOTLIN_COLLECTIONS_LISTOF
import io.realm.kotlin.compiler.ClassIds.PERSISTED_NAME_ANNOTATION
import io.realm.kotlin.compiler.ClassIds.REALM_OBJECT_INTERFACE
import io.realm.kotlin.compiler.FqNames.PACKAGE_TYPES
import io.realm.kotlin.compiler.Names.ASYMMETRIC_REALM_OBJECT
import io.realm.kotlin.compiler.Names.EMBEDDED_REALM_OBJECT
import io.realm.kotlin.compiler.Names.REALM_OBJECT
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocationWithRange
Expand All @@ -30,9 +34,15 @@ import org.jetbrains.kotlin.com.intellij.openapi.util.text.StringUtil
import org.jetbrains.kotlin.com.intellij.psi.PsiElement
import org.jetbrains.kotlin.com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.FirUserTypeRef
import org.jetbrains.kotlin.fir.types.classId
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.IrBlockBodyBuilder
import org.jetbrains.kotlin.ir.builders.IrBlockBuilder
Expand Down Expand Up @@ -105,6 +115,7 @@ import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes.SUPER_TYPE_LIST
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.types.KotlinType
Expand Down Expand Up @@ -141,10 +152,9 @@ val anyRealmObjectInterfacesFqNames = realmObjectInterfaceFqNames + realmEmbedde

fun IrType.classIdOrFail(): ClassId = getClass()?.classId ?: error("Can't get classId of ${render()}")

inline fun ClassDescriptor.hasInterfacePsi(interfaces: Set<String>): Boolean {
// Using PSI to find super types to avoid cyclic reference (see https://github.com/realm/realm-kotlin/issues/339)
inline fun PsiElement.hasInterface(interfaces: Set<String>): Boolean {
var hasRealmObjectAsSuperType = false
this.findPsi()?.acceptChildren(object : PsiElementVisitor() {
this.acceptChildren(object : PsiElementVisitor() {
override fun visitElement(element: PsiElement) {
if (element.node.elementType == SUPER_TYPE_LIST) {
// Check supertypes for classes with Embbeded/RealmObject as generics and remove
Expand All @@ -169,6 +179,10 @@ inline fun ClassDescriptor.hasInterfacePsi(interfaces: Set<String>): Boolean {

return hasRealmObjectAsSuperType
}
inline fun ClassDescriptor.hasInterfacePsi(interfaces: Set<String>): Boolean {
// Using PSI to find super types to avoid cyclic reference (see https://github.com/realm/realm-kotlin/issues/339)
return this.findPsi()?.hasInterface(interfaces) ?: false
}

// Do to the way PSI works, it can be a bit tricky to uniquely identify when the Realm Kotlin
// RealmObject interface is used. For that reason, once we have determined a match for RealmObject,
Expand All @@ -186,6 +200,34 @@ val ClassDescriptor.isEmbeddedRealmObject: Boolean
val ClassDescriptor.isBaseRealmObject: Boolean
get() = this.hasInterfacePsi(realmObjectPsiNames + embeddedRealmObjectPsiNames + asymmetricRealmObjectPsiNames) && !this.hasInterfacePsi(realmJavaObjectPsiNames)

val realmObjectTypes: Set<Name> = setOf(REALM_OBJECT, EMBEDDED_REALM_OBJECT, ASYMMETRIC_REALM_OBJECT)
val realmObjectClassIds = realmObjectTypes.map { name -> ClassId(PACKAGE_TYPES, name) }

// This is the K2 equivalent of our PSI hack to determine if a symbol has a RealmObject base class.
// There is currently no way to determine this within the resolved type system and there is
// probably no such option around the corner.
// https://kotlinlang.slack.com/archives/C03PK0PE257/p1694599154558669
@OptIn(SymbolInternals::class)
val FirClassSymbol<*>.isBaseRealmObject: Boolean
get() = this.classKind == ClassKind.CLASS &&
this.fir.superTypeRefs.any { typeRef ->
when (typeRef) {
// In SUPERTYPES stage
is FirUserTypeRef -> {
typeRef.qualifier.last().name in realmObjectTypes &&
// Disregard constructor invocations as that means that it is a Realm Java class
!(
typeRef.source?.run { treeStructure.getParent(lighterASTNode) }
?.tokenType?.let { it == KtStubElementTypes.CONSTRUCTOR_CALLEE }
?: false
)
}
// After SUPERTYPES stage
is FirResolvedTypeRef -> typeRef.type.classId in realmObjectClassIds
else -> false
}
}

// JetBrains already have a method `fun IrAnnotationContainer.hasAnnotation(symbol: IrClassSymbol)`
// It is unclear exactly what the difference is and how to get a ClassSymbol from a ClassId,
// so for now just work around it.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ class RealmModelSyntheticMethodsExtension : SyntheticResolveExtension {
!isNestedInRealmModelClass(thisDescriptor) && /* do not override nested class methods */
result.isEmpty() /* = no method has been declared in the current class */
) {
when (name.identifier) {
"toString" -> {
when (name) {
Names.REALM_OBJECT_TO_STRING_METHOD -> {
result.add(
createMethod(
classDescriptor = thisDescriptor,
Expand All @@ -62,7 +62,7 @@ class RealmModelSyntheticMethodsExtension : SyntheticResolveExtension {
)
)
}
"equals" -> {
Names.REALM_OBJECT_EQUALS -> {
result.add(
createMethod(
classDescriptor = thisDescriptor,
Expand All @@ -72,7 +72,7 @@ class RealmModelSyntheticMethodsExtension : SyntheticResolveExtension {
)
)
}
"hashCode" -> {
Names.REALM_OBJECT_HASH_CODE -> {
result.add(
createMethod(
classDescriptor = thisDescriptor,
Expand Down
Loading