Skip to content

Commit eb98479

Browse files
committed
Add support for other types
1 parent 7fa81d6 commit eb98479

File tree

5 files changed

+240
-85
lines changed

5 files changed

+240
-85
lines changed

packages/library-base/src/commonMain/kotlin/io/realm/kotlin/internal/InternalConfiguration.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ public interface InternalConfiguration : Configuration {
3737
public val writeDispatcherFactory: CoroutineDispatcherFactory
3838
public val schemaMode: SchemaMode
3939
public val logger: ContextLogger
40+
// TODO make it a map
41+
// public val adapters: Map<KClass<RealmTypeAdapter<*, *>>, RealmTypeAdapter<*, *>>
4042
public val adapters: List<RealmTypeAdapter<*, *>>
4143

4244
// Temporary work-around for https://github.com/realm/realm-kotlin/issues/724

packages/library-base/src/commonMain/kotlin/io/realm/kotlin/types/RealmTypeAdapter.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package io.realm.kotlin.types
66
* @param R realm type.
77
* @param U user type.
88
*/
9+
// TODO Perform some validation on supported R realm-types
910
public interface RealmTypeAdapter<R, U> { // where P is a supported realm type
1011

1112
public fun fromRealm(realmValue: R): U

packages/plugin-compiler/src/main/kotlin/io/realm/kotlin/compiler/AccessorModifierIrGeneration.kt

Lines changed: 144 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ import org.jetbrains.kotlin.ir.builders.irGet
7373
import org.jetbrains.kotlin.ir.builders.irGetObject
7474
import org.jetbrains.kotlin.ir.builders.irIfNull
7575
import org.jetbrains.kotlin.ir.builders.irLetS
76-
import org.jetbrains.kotlin.ir.builders.irNull
7776
import org.jetbrains.kotlin.ir.builders.irReturn
7877
import org.jetbrains.kotlin.ir.builders.irString
7978
import org.jetbrains.kotlin.ir.declarations.IrClass
@@ -109,6 +108,8 @@ import org.jetbrains.kotlin.ir.types.isShort
109108
import org.jetbrains.kotlin.ir.types.isString
110109
import org.jetbrains.kotlin.ir.types.isSubtypeOfClass
111110
import org.jetbrains.kotlin.ir.types.makeNotNull
111+
import org.jetbrains.kotlin.ir.types.superTypes
112+
import org.jetbrains.kotlin.ir.types.typeOrNull
112113
import org.jetbrains.kotlin.ir.util.classId
113114
import org.jetbrains.kotlin.ir.util.defaultType
114115
import org.jetbrains.kotlin.ir.util.parentAsClass
@@ -123,7 +124,7 @@ import org.jetbrains.kotlin.types.KotlinType
123124
import org.jetbrains.kotlin.types.StarProjectionImpl
124125
import org.jetbrains.kotlin.types.isNullable
125126
import org.jetbrains.kotlin.types.typeUtil.supertypes
126-
import java.lang.IllegalStateException
127+
import kotlin.IllegalStateException
127128
import kotlin.collections.set
128129

129130
/**
@@ -413,7 +414,6 @@ class AccessorModifierIrGeneration(private val pluginContext: IrPluginContext) {
413414
fromRealmValue = longToChar,
414415
toPublic = null,
415416
setFunction = setValue,
416-
// fromPublic = charToLong,
417417
fromPublic = { _, value ->
418418
irCall(callee = charToLong).apply {
419419
putValueArgument(0, value)
@@ -436,7 +436,6 @@ class AccessorModifierIrGeneration(private val pluginContext: IrPluginContext) {
436436
fromRealmValue = longToShort,
437437
toPublic = null,
438438
setFunction = setValue,
439-
// fromPublic = shortToLong,
440439
fromPublic = { _, value ->
441440
irCall(callee = shortToLong).apply {
442441
putValueArgument(0, value)
@@ -459,7 +458,6 @@ class AccessorModifierIrGeneration(private val pluginContext: IrPluginContext) {
459458
fromRealmValue = longToInt,
460459
toPublic = null,
461460
setFunction = setValue,
462-
// fromPublic = intToLong,
463461
fromPublic = { _, value ->
464462
irCall(callee = intToLong).apply {
465463
putValueArgument(0, value)
@@ -736,77 +734,86 @@ class AccessorModifierIrGeneration(private val pluginContext: IrPluginContext) {
736734
declaration.hasAnnotation(TYPE_ADAPTER_ANNOTATION) -> {
737735
logDebug("Object property named ${declaration.name} is an adapted type.")
738736

739-
// TODO check nullability
737+
val adapterClassReference =
738+
declaration.getAnnotation(TYPE_ADAPTER_ANNOTATION.asSingleFqName())
739+
.getValueArgument(0)!! as IrClassReference
740+
val adapterClass: IrClass = adapterClassReference.classType.getClass()!!
740741

742+
// TODO find correct super type adapter type, might be multiple ones
743+
val (realmType: IrTypeArgument, userType) =
744+
(adapterClassReference.symbol.superTypes().first() as IrSimpleType)
745+
.arguments
746+
.let { arguments ->
747+
arguments[0] to arguments[1]
748+
}
749+
750+
// TODO check nullability
741751
// 1. Extract what is the actual schema property
742752
// TODO extract the type from the annotation, by now hardcoded one
743-
val schemaProperty = SchemaProperty(
744-
propertyType = PropertyType.RLM_PROPERTY_TYPE_TIMESTAMP,
745-
declaration = declaration,
746-
collectionType = CollectionType.NONE
747-
)
748-
fields[name] = schemaProperty
749-
// 2. Modify the accessor to use the type adapter. The type adapter might or
750-
// not be provided (singleton vs class)
751-
752-
val adapterClassReference = declaration.getAnnotation(TYPE_ADAPTER_ANNOTATION.asSingleFqName()).getValueArgument(0)!! as IrClassReference
753-
val adapterClass: IrClass = adapterClassReference.classType.getClass()!!
754753

755-
// check kind (object / class)
756-
when (adapterClass.kind) {
757-
ClassKind.CLASS -> {
758-
modifyAccessor(
759-
property = schemaProperty,
760-
getFunction = getInstant,
761-
fromRealmValue = null,
762-
toPublic = {objReference, realmValue ->
763-
irCall(callee = providedAdapterFromRealm).apply {
764-
// pass the class from the annotation
765-
putValueArgument(0, objReference)
766-
putValueArgument(1, adapterClassReference)
767-
putValueArgument(2, realmValue)
768-
}
769-
},
770-
setFunction = setValue,
771-
fromPublic = { objReference, publicValue ->
772-
irCall(callee = providedAdapterToRealm).apply {
773-
// pass the class from the annotation
774-
putValueArgument(0, objReference)
775-
putValueArgument(1, adapterClassReference)
776-
putValueArgument(2, publicValue)
777-
}
778-
},
779-
toRealmValue = null
780-
)
781-
}
782-
ClassKind.OBJECT -> {
783-
val fromRealm = adapterClass.lookupFunction(REALM_TYPE_ADAPTER_FROM_REALM)
784-
val toRealm = adapterClass.lookupFunction(REALM_TYPE_ADAPTER_TO_REALM)
785-
786-
modifyAccessor(
787-
property = schemaProperty,
788-
getFunction = getInstant,
789-
fromRealmValue = null,
790-
toPublic = { _, realmValue ->
791-
irCall(callee = fromRealm).apply {
792-
putValueArgument(0, realmValue)
793-
dispatchReceiver = irGetObject(adapterClass.symbol)
794-
}
795-
},
796-
setFunction = setValue,
797-
fromPublic = { _, publicValue ->
798-
irCall(callee = toRealm).apply {
799-
putValueArgument(0, publicValue)
800-
dispatchReceiver = irGetObject(adapterClass.symbol)
801-
}
802-
},
803-
toRealmValue = null
804-
)
754+
val schemaProperty =
755+
retrieveSchemaProperty(declaration, realmType.typeOrNull!!)
756+
757+
if(schemaProperty!= null) {
758+
fields[name] = schemaProperty!!
759+
// 2. Modify the accessor to use the type adapter. The type adapter might or
760+
// not be provided (singleton vs class)
761+
762+
// check kind (object / class)
763+
when (adapterClass.kind) {
764+
ClassKind.CLASS -> {
765+
modifyAccessor(
766+
property = schemaProperty,
767+
getFunction = getInstant,
768+
fromRealmValue = null,
769+
toPublic = {objReference, realmValue ->
770+
irCall(callee = providedAdapterFromRealm).apply {
771+
// pass the class from the annotation
772+
putValueArgument(0, objReference)
773+
putValueArgument(1, adapterClassReference)
774+
putValueArgument(2, realmValue)
775+
}
776+
},
777+
setFunction = setValue,
778+
fromPublic = { objReference, publicValue ->
779+
irCall(callee = providedAdapterToRealm).apply {
780+
// pass the class from the annotation
781+
putValueArgument(0, objReference)
782+
putValueArgument(1, adapterClassReference)
783+
putValueArgument(2, publicValue)
784+
}
785+
},
786+
toRealmValue = null
787+
)
788+
}
789+
ClassKind.OBJECT -> {
790+
val fromRealm = adapterClass.lookupFunction(REALM_TYPE_ADAPTER_FROM_REALM)
791+
val toRealm = adapterClass.lookupFunction(REALM_TYPE_ADAPTER_TO_REALM)
792+
793+
modifyAccessor(
794+
property = schemaProperty,
795+
getFunction = getInstant,
796+
fromRealmValue = null,
797+
toPublic = { _, realmValue ->
798+
irCall(callee = fromRealm).apply {
799+
putValueArgument(0, realmValue)
800+
dispatchReceiver = irGetObject(adapterClass.symbol)
801+
}
802+
},
803+
setFunction = setValue,
804+
fromPublic = { _, publicValue ->
805+
irCall(callee = toRealm).apply {
806+
putValueArgument(0, publicValue)
807+
dispatchReceiver = irGetObject(adapterClass.symbol)
808+
}
809+
},
810+
toRealmValue = null
811+
)
812+
}
813+
else -> throw IllegalStateException("Unsupported type")
805814
}
806-
else -> throw IllegalStateException("Unsupported type")
807815
}
808816
}
809-
810817
else -> {
811818
logError("Realm does not support persisting properties of this type. Mark the field with `@Ignore` to suppress this error.", declaration.locationOf())
812819
}
@@ -817,6 +824,74 @@ class AccessorModifierIrGeneration(private val pluginContext: IrPluginContext) {
817824
})
818825
}
819826

827+
private fun retrieveSchemaProperty(property: IrProperty, type: IrType): SchemaProperty? =
828+
when {
829+
type.isLong() -> SchemaProperty(
830+
propertyType = PropertyType.RLM_PROPERTY_TYPE_INT,
831+
declaration = property,
832+
collectionType = CollectionType.NONE
833+
)
834+
type.isBoolean() -> SchemaProperty(
835+
propertyType = PropertyType.RLM_PROPERTY_TYPE_BOOL,
836+
declaration = property,
837+
collectionType = CollectionType.NONE
838+
)
839+
type.isString() -> SchemaProperty(
840+
propertyType = PropertyType.RLM_PROPERTY_TYPE_STRING,
841+
declaration = property,
842+
collectionType = CollectionType.NONE
843+
)
844+
type.isByteArray() -> SchemaProperty(
845+
propertyType = PropertyType.RLM_PROPERTY_TYPE_BINARY,
846+
declaration = property,
847+
collectionType = CollectionType.NONE
848+
)
849+
type.isRealmAny() -> SchemaProperty(
850+
propertyType = PropertyType.RLM_PROPERTY_TYPE_MIXED,
851+
declaration = property,
852+
collectionType = CollectionType.NONE
853+
)
854+
type.isRealmInstant() -> SchemaProperty(
855+
propertyType = PropertyType.RLM_PROPERTY_TYPE_TIMESTAMP,
856+
declaration = property,
857+
collectionType = CollectionType.NONE
858+
)
859+
type.isFloat() -> SchemaProperty(
860+
propertyType = PropertyType.RLM_PROPERTY_TYPE_FLOAT,
861+
declaration = property,
862+
collectionType = CollectionType.NONE
863+
)
864+
type.isDouble() -> SchemaProperty(
865+
propertyType = PropertyType.RLM_PROPERTY_TYPE_DOUBLE,
866+
declaration = property,
867+
collectionType = CollectionType.NONE
868+
)
869+
// type.isLinkingObject() ->PropertyType.RLM_PROPERTY_TYPE_OBJECT // TODO should be supported (I think so)
870+
// type.isRealmInstant() ->PropertyType.RLM_PROPERTY_TYPE_LINKING_OBJECTS // TODO should be supported (Unsure write some API)
871+
type.isDecimal128() -> SchemaProperty(
872+
propertyType = PropertyType.RLM_PROPERTY_TYPE_DECIMAL128,
873+
declaration = property,
874+
collectionType = CollectionType.NONE
875+
)
876+
type.isObjectId() -> SchemaProperty(
877+
propertyType = PropertyType.RLM_PROPERTY_TYPE_OBJECT_ID,
878+
declaration = property,
879+
collectionType = CollectionType.NONE
880+
)
881+
type.isRealmUUID() -> SchemaProperty(
882+
propertyType = PropertyType.RLM_PROPERTY_TYPE_UUID,
883+
declaration = property,
884+
collectionType = CollectionType.NONE
885+
)
886+
else -> {
887+
logError(
888+
"Invalid type parameter, only Realm types are supported", // TODO find a better error message
889+
property.locationOf()
890+
)
891+
null
892+
}
893+
}
894+
820895
private fun processCollectionField(
821896
collectionType: CollectionType,
822897
fields: MutableMap<String, SchemaProperty>,

packages/test-base/src/commonTest/kotlin/io/realm/kotlin/test/common/TypeAdapterTests.kt

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,24 @@ import kotlin.test.BeforeTest
2828
import kotlin.test.Test
2929
import kotlin.test.assertEquals
3030

31+
/**
32+
* This test suite should cover these points
33+
* Schema validation
34+
* Singleton / instanced adapters
35+
* All types including collections
36+
* Nullability
37+
* Default values
38+
* Compatibility with other annotations:
39+
* - PrimaryKey
40+
* - Index
41+
* - Fulltext search
42+
* - PersistedName
43+
* - Ignore
44+
* Backlinks
45+
* missing instance type adapters
46+
* copyFromRealm
47+
* access objects realm?
48+
*/
3149
class TypeAdapterTests {
3250
private lateinit var tmpDir: String
3351
private lateinit var realm: Realm
@@ -54,22 +72,6 @@ class TypeAdapterTests {
5472
PlatformUtils.deleteTempDir(tmpDir)
5573
}
5674

57-
/**
58-
* We shall cover with these tests:
59-
*
60-
* Singleton / instanced adapters
61-
* All types including collections
62-
* Nullability
63-
* Default values
64-
* Compatibility with other annotations:
65-
* - PrimaryKey
66-
* - Index
67-
* - Fulltext search
68-
* - PersistedName
69-
* - Ignore
70-
* Backlinks
71-
*/
72-
7375
@Test
7476
fun useSingletonAdapter() {
7577
val expectedDate = BsonDateTime()

0 commit comments

Comments
 (0)