Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Add support for type adapters#1592

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Draft
clementetb wants to merge32 commits intomain
base:main
Choose a base branch
Loading
fromct/type-adapters
Draft
Show file tree
Hide file tree
Changes from1 commit
Commits
Show all changes
32 commits
Select commitHold shift + click to select a range
7fa81d6
Adapter singleton and instance adapter discoverability for RealmInstant
clementetbDec 1, 2023
eb98479
Add support for other types
clementetbDec 4, 2023
8c3f941
Use map for instanced adapters
clementetbDec 4, 2023
f64bdf3
Clean up configuration + tests
clementetbDec 4, 2023
b5b6a20
Add some compile tests
clementetbDec 5, 2023
57e8bf3
Enable Realm list tests
clementetbDec 5, 2023
43dd5dc
Enable collections
clementetbDec 5, 2023
c53aa14
Disable derived numerical types
clementetbDec 5, 2023
681a73e
More tests
clementetbDec 8, 2023
b607f36
Add support for objects
clementetbDec 8, 2023
37bfd02
Add collections support
clementetbDec 11, 2023
9c8ba57
Add type check for unsupported types
clementetbDec 11, 2023
da05877
Add check on type adapter valid realm types
clementetbDec 11, 2023
9b97c13
More testing
clementetbDec 11, 2023
e9fd266
Test with other annotations
clementetbDec 11, 2023
45b1ebe
Add compiler tests for using the annotation on type parameters
clementetbDec 12, 2023
4f6cf36
Add type parameter adapter for lists
clementetbDec 19, 2023
ad08eb9
Add set and dictionary support
clementetbDec 19, 2023
ab53da9
Add more tests
clementetbDec 19, 2023
148c0ac
Linting
clementetbDec 20, 2023
058afc7
Fix nullable collections not being processed
clementetbDec 20, 2023
38fe50f
Fix expected IR
clementetbDec 20, 2023
a7111b1
Remove uses type adapter compile computed property
clementetbDec 20, 2023
6408db1
Add more tests around adapter supported types
clementetbDec 20, 2023
385e369
Linting
clementetbDec 20, 2023
77cdc2e
Clean up compiler tests
clementetbDec 20, 2023
3b18d8d
Add roundtrip tests
clementetbDec 20, 2023
b4686c0
Add roundtrip for adapted type parameters
clementetbDec 20, 2023
f3b0407
Disable RealmSet tests on TypedAdapter
clementetbDec 20, 2023
4ba7dab
Add changelog entry
clementetbDec 21, 2023
3b76bee
Add documentation
clementetbDec 21, 2023
faab0ab
Update compiler plugin tests
clementetbDec 21, 2023
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
PrevPrevious commit
NextNext commit
Add type check for unsupported types
  • Loading branch information
@clementetb
clementetb committedDec 11, 2023
commit9c8ba57ded76cf42dc78af24df9d4242af0ab643
Original file line numberDiff line numberDiff line change
Expand Up@@ -91,10 +91,12 @@ import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl
import org.jetbrains.kotlin.ir.interpreter.getAnnotation
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrStarProjection
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.IrTypeArgument
import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.types.impl.IrAbstractSimpleType
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.originalKotlinType
import org.jetbrains.kotlin.ir.types.isBoolean
import org.jetbrains.kotlin.ir.types.isByte
Expand DownExpand Up@@ -143,6 +145,10 @@ class AccessorModifierIrGeneration(private val pluginContext: IrPluginContext) {
private val realmEmbeddedBacklinksClass: IrClass = pluginContext.lookupClassOrThrow(REALM_EMBEDDED_BACKLINKS)
private val realmObjectInterface = pluginContext.lookupClassOrThrow(REALM_OBJECT_INTERFACE).symbol
private val embeddedRealmObjectInterface = pluginContext.lookupClassOrThrow(EMBEDDED_OBJECT_INTERFACE).symbol
// Attempt to find the interface for asymmetric objects.
// The class will normally only be on the classpath for library-sync builds, not
// library-base builds.
private val asymmetricRealmObjectInterface: IrClass? = pluginContext.referenceClass(ASYMMETRIC_OBJECT_INTERFACE)?.owner

private val objectIdClass: IrClass = pluginContext.lookupClassOrThrow(KBSON_OBJECT_ID)
private val decimal128Class: IrClass = pluginContext.lookupClassOrThrow(KBSON_DECIMAL128)
Expand DownExpand Up@@ -248,11 +254,6 @@ class AccessorModifierIrGeneration(private val pluginContext: IrPluginContext) {
objectReferenceProperty = irClass.lookupProperty(OBJECT_REFERENCE)
objectReferenceType = objectReferenceProperty.backingField!!.type

// Attempt to find the interface for asymmetric objects.
// The class will normally only be on the classpath for library-sync builds, not
// library-base builds.
val asymmetricRealmObjectInterface: IrClass? = pluginContext.referenceClass(ASYMMETRIC_OBJECT_INTERFACE)?.owner

irClass.transformChildrenVoid(object : IrElementTransformerVoid() {
@Suppress("LongMethod")
override fun visitProperty(declaration: IrProperty): IrStatement {
Expand DownExpand Up@@ -306,7 +307,14 @@ class AccessorModifierIrGeneration(private val pluginContext: IrPluginContext) {

// TODO find correct super type adapter type, might be multiple ones because inheritance
val (realmType: IrTypeArgument, userType) =
(adapterClassReference.symbol.superTypes().first() as IrSimpleType)
adapterClassReference.symbol
.superTypes()
.first {
it.classId == ClassIds.REALM_TYPE_ADAPTER_INTERFACE
}
.let {
it as IrSimpleType
}
.arguments
.let { arguments ->
arguments[0] to arguments[1]
Expand All@@ -317,9 +325,9 @@ class AccessorModifierIrGeneration(private val pluginContext: IrPluginContext) {
if(actualType != null) {
propertyTypeRaw = actualType
propertyType = actualType.makeNotNull()
if(propertyType.isChar() || propertyType.isByte() || propertyType.isShort() || propertyType.isInt() || propertyType.isMutableRealmInteger()) {
if(!propertyType.isPersistedPrimitiveType()) {
// TODO improve messaging
logError("Unsupported Realm storage type. Use`Long` instead", declaration.locationOf())
logError("Unsupported Realm storage type. Usea valid type` instead", declaration.locationOf())
}
nullable = actualType.isNullable()
} else {
Expand DownExpand Up@@ -879,23 +887,29 @@ class AccessorModifierIrGeneration(private val pluginContext: IrPluginContext) {
propertyTypeRaw: IrType,
typeAdapterMethodReferences: TypeAdapterMethodReferences?,
) {
val type: KotlinType =
typeAdapterMethodReferences?.propertyType?.originalKotlinType
?: declaration.symbol.owner.toIrBasedDescriptor().type
val type: IrType =
typeAdapterMethodReferences?.propertyType
?: declaration.backingField!!.type

val collectionGenericType = (type as IrSimpleTypeImpl).arguments[0]

if (type.arguments[0] is StarProjectionImpl) {
if(typeAdapterMethodReferences != null && !collectionGenericType.typeOrNull!!.makeNotNull().isPersistedPrimitiveType()) {
// TODO improve messaging
logError("Unsupported Realm storage type. Use a valid type instead", declaration.locationOf())
}

if (collectionGenericType is IrStarProjection) {
logError(
"Error in field ${declaration.name} - ${collectionType.description} cannot use a '*' projection.",
declaration.locationOf()
)
return
}
val collectionGenericType = type.arguments[0].type
val coreGenericTypes = getCollectionGenericCoreType(collectionType, declaration, type)

val coreGenericTypes = getCollectionGenericCoreType(collectionType, declaration, type.kotlinType!!)
// Only process field if we got valid generics
if (coreGenericTypes != null) {
val genericPropertyType = getPropertyTypeFromKotlinType(collectionGenericType)
val genericPropertyType = getPropertyTypeFromKotlinType(collectionGenericType.typeOrNull!!.toIrBasedKotlinType())

// Only process
if (genericPropertyType != null) {
Expand DownExpand Up@@ -1208,6 +1222,42 @@ class AccessorModifierIrGeneration(private val pluginContext: IrPluginContext) {
return propertyClassId == mutableRealmIntegerClassId
}

fun IrType.isPersistedPrimitiveType(): Boolean = isRealmAny() ||
isByteArray() ||
isString() ||
// isLinkingObject() ||
// isEmbeddedLinkingObject() ||
// isPersistedPrimitiveType() ||
// isMutableRealmInteger() ||
// isByte() ||
// isChar() ||
// isShort() ||
// isInt() ||
isLong() ||
isBoolean() ||
isFloat() ||
isDouble() ||
isDecimal128() ||
// isEmbeddedLinkingObject() ||
// isLinkingObject() ||
isRealmList() ||
isRealmSet() ||
isRealmDictionary() ||
isRealmInstant() ||
isObjectId() ||
isRealmObjectId() ||
isRealmUUID() ||
isRealmList() ||
isRealmSet() ||
isRealmDictionary() ||
isSubtypeOfClass(embeddedRealmObjectInterface) ||
asymmetricRealmObjectInterface?.let {
isSubtypeOfClass(
asymmetricRealmObjectInterface.symbol
)
} ?: false ||
isSubtypeOfClass(realmObjectInterface)

@Suppress("ReturnCount", "LongMethod")
private fun getCollectionGenericCoreType(
collectionType: CollectionType,
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -126,6 +126,7 @@ object ClassIds {
val TYPED_REALM_OBJECT_INTERFACE = ClassId(FqNames.PACKAGE_TYPES, Name.identifier("TypedRealmObject"))
val EMBEDDED_OBJECT_INTERFACE = ClassId(FqNames.PACKAGE_TYPES, Name.identifier("EmbeddedRealmObject"))
val ASYMMETRIC_OBJECT_INTERFACE = ClassId(FqNames.PACKAGE_TYPES, Name.identifier("AsymmetricRealmObject"))
val REALM_TYPE_ADAPTER_INTERFACE = ClassId(FqNames.PACKAGE_TYPES, Name.identifier("RealmTypeAdapter"))

val CLASS_APP_CONFIGURATION = ClassId(FqNames.PACKAGE_MONGODB, Name.identifier("AppConfiguration"))

Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,7 @@ 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.ClassIds.REALM_TYPE_ADAPTER_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
Expand DownExpand Up@@ -261,6 +262,9 @@ val IrClass.isEmbeddedRealmObject: Boolean
val IrClass.isAsymmetricRealmObject: Boolean
get() = superTypes.any { it.classId == ASYMMETRIC_OBJECT_INTERFACE }

val IrClass.isRealmTypeAdapter: Boolean
get() = superTypes.any { it.classId == REALM_TYPE_ADAPTER_INTERFACE }

val IrType.classId: ClassId?
get() = this.getClass()?.classId

Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,6 +19,7 @@ package io.realm.kotlin.compiler
import io.realm.kotlin.compiler.ClassIds.MODEL_OBJECT_ANNOTATION
import io.realm.kotlin.compiler.ClassIds.REALM_MODEL_COMPANION
import io.realm.kotlin.compiler.ClassIds.REALM_OBJECT_INTERNAL_INTERFACE
import io.realm.kotlin.compiler.ClassIds.REALM_TYPE_ADAPTER_INTERFACE
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
Expand All@@ -31,8 +32,11 @@ import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.expressions.impl.IrClassReferenceImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.defaultType
import org.jetbrains.kotlin.ir.types.starProjectedType
import org.jetbrains.kotlin.ir.types.superTypes
import org.jetbrains.kotlin.ir.types.typeOrNull
import org.jetbrains.kotlin.ir.util.companionObject
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.isAnonymousObject
Expand All@@ -59,6 +63,22 @@ private class RealmModelLowering(private val pluginContext: IrPluginContext) : C
override fun lower(irFile: IrFile) = runOnFilePostfix(irFile)

override fun lower(irClass: IrClass) {
if (irClass.isRealmTypeAdapter) {
// Validate that the R type parameter is a valid RealmType
// irClass.symbol
// .superTypes()
// .first {
// it.classId == REALM_TYPE_ADAPTER_INTERFACE
// }
// .let {
// it as IrSimpleType
// }
// .arguments
// .let { arguments ->
// arguments[0].typeOrNull!!.isPersistedPrimitiveType()
// }
}

if (irClass.isBaseRealmObject) {
// Throw error with classes that we do not support
if (irClass.isData) {
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -365,6 +365,9 @@ object TypeDescriptor {
allSingularFieldTypes + allListFieldTypes + allSetFieldTypes + allDictionaryFieldTypes
val allPrimaryKeyFieldTypes = allFieldTypes.filter { it.isPrimaryKeySupported }

val unsupportedRealmTypeAdaptersClassifiers =
setOf(Byte::class, Char::class, Short::class, Int::class, MutableRealmInt::class)

// Realm field type represents the type of a given user specified field in the RealmObject
data class RealmFieldType(
val collectionType: CollectionType,
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,6 +21,7 @@ import com.tschuchort.compiletesting.SourceFile
import io.realm.kotlin.internal.interop.CollectionType
import io.realm.kotlin.test.util.Compiler.compileFromSource
import io.realm.kotlin.test.util.TypeDescriptor.allFieldTypes
import io.realm.kotlin.test.util.TypeDescriptor.unsupportedRealmTypeAdaptersClassifiers
import io.realm.kotlin.types.MutableRealmInt
import io.realm.kotlin.types.ObjectId
import io.realm.kotlin.types.RealmInstant
Expand All@@ -39,7 +40,7 @@ import kotlin.test.assertTrue
* - [x] Adapter annotation on unsupported types: delegate, function etc
* - [ ] Adapter not matching public type
* - [x] Adapters type supportness
* - [] Adapters type unsupportness
* - [x] Adapters type unsupportness
* - [ ] Instanced and singleton adapters
* - [ ] Other annotations Ignore, Index etc
*/
Expand DownExpand Up@@ -124,10 +125,6 @@ class TypeAdaptersTests {
fun `type adapters supportness`() {
val defaults = mapOf<KClassifier, Any>(
Boolean::class to true,
Byte::class to "1",
Char::class to "\'c\'",
Short::class to "1",
Int::class to "1",
Long::class to "1",
Float::class to "1.4f",
Double::class to "1.4",
Expand All@@ -138,18 +135,12 @@ class TypeAdaptersTests {
BsonObjectId::class to "BsonObjectId()",
RealmUUID::class to "RealmUUID.random()",
ByteArray::class to "byteArrayOf(42)",
MutableRealmInt::class to "MutableRealmInt.create(42)",
RealmObject::class to "TestObject2()"
RealmObject::class to "TestObject2()",
)

allFieldTypes
.filterNot { type ->
// TODO tidy list unsupported types in TypeDescriptor
type.elementType.classifier == Byte::class ||
type.elementType.classifier == Char::class ||
type.elementType.classifier == Short::class ||
type.elementType.classifier == Int::class ||
type.elementType.classifier == MutableRealmInt::class
type.elementType.classifier in unsupportedRealmTypeAdaptersClassifiers
}
.forEach { type ->
val elementType = type.elementType
Expand All@@ -161,7 +152,6 @@ class TypeAdaptersTests {
} else {
type.toKotlinLiteral()
}
println(kotlinLiteral)

val result = compileFromSource(
plugins = listOf(io.realm.kotlin.compiler.Registrar()),
Expand DownExpand Up@@ -204,8 +194,75 @@ class TypeAdaptersTests {
)
)
assertEquals(KotlinCompilation.ExitCode.OK, result.exitCode, result.messages)
// assertTrue(result.messages.contains("Invalid type parameter, only Realm types are supported"), result.messages)
}
}

@Test
fun `type adapters unsupportness`() {
val defaults = mapOf<KClassifier, Any>(
Byte::class to "1",
Char::class to "\'c\'",
Short::class to "1",
Int::class to "1",
MutableRealmInt::class to "MutableRealmInt.create(42)",
)

allFieldTypes
.filter { type ->
type.elementType.classifier in unsupportedRealmTypeAdaptersClassifiers
}
.forEach { type ->
val elementType = type.elementType
val default = if (!elementType.nullable) defaults[elementType.classifier]
?: error("unmapped default") else null

val kotlinLiteral = if(type.elementType.classifier == RealmObject::class) {
type.toKotlinLiteral().replace("RealmObject", "TestObject2")
} else {
type.toKotlinLiteral()
}

val result = compileFromSource(
plugins = listOf(io.realm.kotlin.compiler.Registrar()),
source = SourceFile.kotlin(
"typeadapter_supportness_$kotlinLiteral.kt",
"""
import io.realm.kotlin.types.RealmAny
import io.realm.kotlin.types.RealmDictionary
import io.realm.kotlin.types.RealmList
import io.realm.kotlin.types.RealmSet
import io.realm.kotlin.types.RealmInstant
import io.realm.kotlin.types.MutableRealmInt
import io.realm.kotlin.types.RealmObject
import io.realm.kotlin.types.RealmTypeAdapter
import io.realm.kotlin.types.RealmUUID
import io.realm.kotlin.types.annotations.TypeAdapter
import io.realm.kotlin.types.ObjectId
import org.mongodb.kbson.BsonDecimal128
import org.mongodb.kbson.BsonObjectId

class UserType

class NonRealmType

class TestObject2: RealmObject {
var name: String = ""
}

class TestObject : RealmObject {
@TypeAdapter(adapter = ValidRealmTypeAdapter::class)
var userType: UserType = UserType()
}

object ValidRealmTypeAdapter : RealmTypeAdapter<$kotlinLiteral, UserType> {
override fun fromRealm(realmValue: $kotlinLiteral): UserType = TODO()

override fun toRealm(value: UserType): $kotlinLiteral = TODO()
}
""".trimIndent()
)
)
assertEquals(KotlinCompilation.ExitCode.COMPILATION_ERROR, result.exitCode, result.messages)
}
}
}

[8]ページ先頭

©2009-2025 Movatter.jp