Kotlin native. Serialization. Backend Internal error: Exception during IR lowering - kotlin

I am trying to make a simple serializable class. But something goes wrong. Compilation fails for native build. But jvm target works fine.
Superclass:
import kotlinx.serialization.*
import kotlinx.serialization.json.*
import kotlin.native.concurrent.ThreadLocal
#Serializable
sealed class DefaultRequest {
private val id = Companion.id
private val name = this::class::simpleName.get()
#Transient
private val format = Json { encodeDefaults = true }
#ThreadLocal
protected companion object {
private var id = -1
get() { return ++field }
}
override fun toString() = format.encodeToString(this)
}
Subclass:
import kotlinx.serialization.Serializable
#Serializable
class PingRequest : DefaultRequest()
I had a look on this tutorial: https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/polymorphism.md#sealed-classes
It looks very easy and simple. But i don't understand where i did a mistake.
Error log from ide:
Compilation failed: Backend Internal error: Exception during IR lowering
File being compiled: /src/commonMain/kotlin/connection/serializable/requests/PingRequest.kt
The root cause org.jetbrains.kotlin.backend.common.CompilationException was thrown at: org.jetbrains.kotlin.ir.symbols.impl.IrBindableSymbolBase.getOwner(IrPrivateSymbolBase.kt:59)
* Source files:
* Compiler version info: Konan: 1.7.20-Beta / Kotlin: 1.7.20
* Output kind: PROGRAM
UPDATE:
So. After changing the code from this:
private val id = Companion.id
private val name = this::class::simpleName.get()
to this:
private val id: Int
private val name: String
init {
id = Companion.id
name = this::class::simpleName.get()!!
}
the problem has gone. But i still have a question. Why is it not working?
private val id = Companion.id
private val name = this::class::simpleName.get()

Related

Attempt to invoke interface method on a null object reference in kotlin

After implementing viewmodels to jetpack compose app when I running the app it's showing a error :-
Attempt to invoke interface method 'boolean java.util.Set.contains(java.lang.Object)' on a null object reference
java.lang.NullPointerException: Attempt to invoke interface method 'boolean java.util.Set.contains(java.lang.Object)' on a null object reference
at com.example.android.ui.GameViewModel.pickRandomWordAndShuffle(GameViewModel.kt:21)
at com.example.android.ui.GameViewModel.(GameViewModel.kt:10)
here is my code:-
import androidx.lifecycle.ViewModel
import com.example.android.unscramble.data.allWords
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
class GameViewModel : ViewModel() {
private val _uiState =
MutableStateFlow(GameUiState(currentScrambledWord = pickRandomWordAndShuffle()))
val uiState: StateFlow<GameUiState> = _uiState.asStateFlow()
private var _count = 0
val count: Int
get() = _count
private lateinit var currentWord: String
private var usedWords: MutableSet<String> = mutableSetOf()
private fun shuffleCurrentWord(word: String): String {
val tempWord = word.toCharArray()
// Scramble the word
tempWord.shuffle()
while (String(tempWord) == word) {
tempWord.shuffle()
}
return String(tempWord)
}
private fun pickRandomWordAndShuffle(): String {
// Continue picking up a new random word until you get one that hasn't been used before
currentWord = allWords.random()
if (usedWords.contains(currentWord)) {
return pickRandomWordAndShuffle()
} else {
usedWords.add(currentWord)
return shuffleCurrentWord(currentWord)
}
}
private fun resetGame() {
usedWords.clear()
_uiState.value = GameUiState(currentScrambledWord = pickRandomWordAndShuffle())
}
init {
resetGame()
}
}
It's not showing any compile time errors. I don't konw what should I do.
You're initializing _uiState before you initialize usedWords. This calls pickRandomWordAndShuffle() before usedWords has been initialized, so it's still null in the GameViewModel instance being created.
If you move the declaration of usedWords above _uiState it should work.
HOWEVER: It's generally a bad idea to call member functions before an instance has been fully initialized, for this exact reason.
You could make _uiState and uiState lazy, which would make this safer. For example:
// Copyright 2023 Google LLC.
// SPDX-License-Identifier: Apache-2.0
private val _uiState by lazy {
MutableStateFlow(GameUiState(currentScrambledWord = pickRandomWordAndShuffle()))
}
val uiState: StateFlow<GameUiState> by lazy { _uiState.asStateFlow() }
which will wait until something uses uiState (which looking at your code only happens externally, so you'll be guaranteed that it won't be initialized until the GameViewModel has been fully initialized.

Kotlin validator for List<Pair<A, B>> doesn't work

I have a data class which I need to validate:
import javax.validation.Valid
import whatever.pckg.validation.PkiSignWithBusinessCode
import whatever.pckg.validation.NullOrNotBlank
data class UploadFileReq(
val id: String? = null,
...(other fields)...
#get:Valid
val signaturesInfo: MutableList<Pair<SignatureInfo, Object>> = mutableListOf() # Object here is for simplicity
) {
#PkiSignWithBusinessCode
data class SignatureInfo(
val typeSign: String = "",
#get:NullOrNotBlank
val businessCode: String? = null,
)
}
#NullOrNotBlank annotation is just a simple merge of standard #NotBlank and #Null annotations.
I also have another custom validation annotation #PkiSignWithBusinessCode, its definition is below:
import whatever.pckg.UploadFileReq
import javax.validation.*
import kotlin.annotation.AnnotationRetention.RUNTIME
import kotlin.reflect.KClass
#Constraint(validatedBy = [PkiSignWithBusinessCodeValidator::class])
#Target(AnnotationTarget.CLASS)
#Retention(RUNTIME)
annotation class PkiSignWithBusinessCode(
val message: String = "PKI signature requires filled businessCode",
val groups: Array<KClass<*>> = [],
val payload: Array<KClass<out Payload>> = []
)
class PkiSignWithBusinessCodeValidator: ConstraintValidator<PkiSignWithBusinessCode, UploadFileReq.SignatureInfo>> {
override fun isValid(obj: UploadFileReq.SignatureInfo?, context: ConstraintValidatorContext): Boolean {
if (obj != null) {
if ((obj.typeSign == "PKI") && (obj.businessCode == null)) {
return false
}
}
return true
}
Logic of above annotation is quite simple - when typeSign equals PKI and businessCode is null, then validator should treat that as invalid object.
For your reference here's a simple unit-test that tries to check the work of #PkiSignWithBusinessCode:
import org.junit.jupiter.api.Test
import whatever.pckg.UploadFileReq
import javax.validation.Validation
import kotlin.test.assertEquals
class PkiSignWithBusinessCodeTest {
#Test
fun `validate PkiSignWithBusinessCodeTest`() {
val validator = Validation.buildDefaultValidatorFactory().validator
val signatureInfo = UploadFileReq.SignatureInfo(
typeSign = "PKI",
businessCode = null
)
val uploadFileReq = UploadFileReq(
null,
signaturesInfo = mutableListOf(signatureInfo to Object)
)
val result = validator.validate(uploadFileReq)
assertEquals(1, result.size)
assertEquals("PKI signature requires filled businessCode", result.first().messageTemplate)
}
}
But this test obviously fails on first assertion state: java.lang.AssertionError: Expected <1>, actual <0>. So no constraint violations found by validator.
The problem is that Spring ignores validation rule of above annotation. As an assumption I suppose that somehow Pair class wrap prevents Spring from using my validation annotation. Maybe it's a bug?
Or maybe I overlooked something in my code?
Found a workaround on this - need to make own ValidatingPair with #Valid annotations on first and second members of this new Pair:
import javax.validation.Valid
data class ValidatingPair<out A, out B>(
#get:Valid
public val first: A,
#get:Valid
public val second: B
) : java.io.Serializable {
override fun toString(): String = "($first, $second)"
}
And make:
val signaturesInfo: MutableList<Pair<SignatureInfo, Object>>
to become
val signaturesInfo: MutableList<ValidatingPair<SignatureInfo, Object>>
Then validation starts working for list members.

Use Kotlin's data class in service-proxy of Vert.x

I'm trying to pass data class to the service-proxy of Vert.x like this:
data class Entity(val field: String)
#ProxyGen
#VertxGen
public interface DatabaseService {
DatabaseService createEntity(Entity entity, Handler<AsyncResult<Void>> resultHandler);
}
However, the service-proxy requires a DataObject as the parameter type.
Below are what I've tried so far.
First, I rewrite the data class as:
#DataObject
data class Entity(val field: String) {
constructor(json: JsonObject) : this(
json.getString("field")
)
fun toJson(): JsonObject = JsonObject.mapFrom(this)
}
Although this works, the code is redundant, so I tried the kapt with the following generator:
override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
roundEnv.getElementsAnnotatedWith(ProxyDataObject::class.java).forEach { el ->
val className = el.simpleName.toString()
val pack = processingEnv.elementUtils.getPackageOf(el).toString()
val filename = "Proxy$className"
val classBuilder = TypeSpec.classBuilder(filename)
val primaryConstructorBuilder = FunSpec.constructorBuilder()
val secondaryConstructorBuilder = FunSpec.constructorBuilder().addParameter("json", JsonObject::class)
val secondaryConstructorCodeBlocks = mutableListOf<CodeBlock>()
el.enclosedElements.forEach {
if (it.kind == ElementKind.FIELD) {
val name = it.simpleName.toString()
val kClass = getClass(it) // get the corresponding Kotlin class
val jsonTypeName = getJsonTypeName(it) // get the corresponding type name in methods of JsonObject
classBuilder.addProperty(PropertySpec.builder(name, kClass).initializer(name).build())
primaryConstructorBuilder.addParameter(name, kClass)
secondaryConstructorCodeBlocks.add(CodeBlock.of("json.get$jsonTypeName(\"$name\")"))
}
}
secondaryConstructorBuilder.callThisConstructor(secondaryConstructorCodeBlocks)
classBuilder
.addAnnotation(DataObject::class)
.addModifiers(KModifier.DATA)
.primaryConstructor(primaryConstructorBuilder.build())
.addFunction(secondaryConstructorBuilder.build())
.addFunction(
FunSpec.builder("toJson").returns(JsonObject::class).addStatement("return JsonObject.mapFrom(this)").build()
)
val generatedFile = FileSpec.builder(pack, filename).addType(classBuilder.build()).build()
generatedFile.writeTo(processingEnv.filer)
}
return true
}
Then I can get the correct generated file by simply writing the original data class, but when I execute the building after cleaning, I still get the following error:
Could not generate model for DatabaseService#createEntity(ProxyEntity,io.vertx.core.Handler<io.vertx.core.AsyncResult<java.lang.Void>>): type ProxyEntity is not legal for use for a parameter in proxy
It seems that the generated annotation #DataObject is not processed.
So what should I do? Is there a better solution?

How to change a kotlin private val using reflection?

I can access a private val value using reflection as below
fun main() {
val mainClass = MainClass()
val f = MainClass::class.memberProperties.find { it.name == "info" }
f?.let {
it.isAccessible = true
val w = it.get(mainClass) as String
println(w)
}
}
class MainClass {
private val info: String = "Hello"
}
But if I want to change info, how could I do it using reflection?
Answer
In short, you have to use Java reflection APIs in this case, and here is how to do it:
fun main() {
val mainClass = MainClass()
val f = MainClass::class.java.getDeclaredField("info")
f.isAccessible = true
f.set(mainClass, "set from reflection")
mainClass.printInfo() // Prints "set from reflection"
}
class MainClass {
private val info: String = "Hello"
fun printInfo() = println(info)
}
Reason for using Java reflection APIs
It is not possible to do with Kotlin reflection APIs since no setter code is generated for a read-only (val) property. So to change it, we need to use Java reflection APIs which is more low-level. First, we use Tools -> Kotlin -> Show Kotlin Bytecode to see what the generated bytecode looks like. Then we see this:
// ================MainClass.class =================
// class version 50.0 (50)
// access flags 0x31
public final class MainClass {
// access flags 0x12
private final Ljava/lang/String; info = "Hello"
// ...
i.e that the info fields in the MainClass Kotlin class causes the compiler to emit JVM code for a regular MainClass Java class with a final String info field. So to change it, we can use Java reflection APIs, as in the code above.
Kotlin reflection API attempt
If the field would have been private var you would be able to Use Kotlin reflection APIs like this:
f?.let {
val mutableProp = it as KMutableProperty<*>
it.isAccessible = true
mutableProp.setter.call(mainClass, "set from Kotlin reflection")
val w = it.get(mainClass) as String
println(w)
}
but if you try this with private val you will get the below exception
Exception in thread "main" java.lang.ClassCastException: class kotlin.reflect.jvm.internal.KProperty1Impl cannot be cast to class kotlin.reflect.KMutableProperty (kotlin.reflect.jvm.internal.KProperty1Impl and kotlin.reflect.KMutableProperty are in unnamed module of loader 'app')
at MainKt.main(main.kt:107)
at MainKt.main(main.kt)
since no setter code is generated for val fields, and thus the info property will have a Kotlin Reflection API type of KProperty and not KMutableProperty.
This is working solution
import kotlin.reflect.KMutableProperty
import kotlin.reflect.full.memberProperties
class MySolution {
var name = ""
var email = ""
}
#Suppress("UNCHECKED_CAST")
fun main() {
//Dummy Result Set
val rs = mapOf<String,String>("name" to "My Name", "email" to "My Email")
val mySol = MySolution();
val xyzMp = MySolution::class.memberProperties;
xyzMp.forEach { mp ->
val prop = mp as KMutableProperty<String>
prop.setter.call(mySol, rs[mp.name])
}
println(mySol.name)
println(mySol.email)
println("*****Enjoy*******")
output
My Name
My Email
**** *Enjoy*******

Using data class in micronaut properties

I'm writing configuration properties and would like to use data class to hold the data.
Problem is, data classes have a primary constructor and are immutable, and micronaut tries to inject the values as beans.
Example:
#ConfigurationProperties("gerencianet")
data class GerenciaNetConfiguration(
val clientId: String,
val clientSecret: String,
val apiUrl: String,
val notificationUrl: String,
val datePattern: String = "yyyy-MM-dd HH:mm:ss"
)
Error: Caused by: io.micronaut.context.exceptions.NoSuchBeanException: No bean of type [java.lang.String] exists. Make sure the bean is not disabled by bean requirements
Is there support for it?
You can inject the values as constructor parameters using #Parameter. It avoids common mistakes with #Value.
For example, if your application.yml looks like this:
group:
first-value: asdf
second-value: ghjk
Then a Kotlin class might look like:
import io.micronaut.context.annotation.Property
import javax.inject.Singleton
#Singleton
class MyClass(#Property(name = "group.first-value") val firstValue: String) {
fun doIt(): String {
return firstValue
}
}
Or, similarly, a method:
import io.micronaut.context.annotation.Factory
import io.micronaut.context.annotation.Property
import javax.inject.Singleton
#Factory
class MyFactory {
#Singleton
fun getSomeValue(#Property(name = "group.first-value") firstValue: String): SomeClass {
return SomeClass.newBuilder()
.setTheValue(firstValue)
.build()
}
}
One option you have is to do something like this:
import io.micronaut.context.annotation.ConfigurationBuilder
import io.micronaut.context.annotation.ConfigurationProperties
#ConfigurationProperties("my.engine")
internal class EngineConfig {
#ConfigurationBuilder(prefixes = ["with"])
val builder = EngineImpl.builder()
#ConfigurationBuilder(prefixes = ["with"], configurationPrefix = "crank-shaft") / <3>
val crankShaft = CrankShaft.builder()
#set:ConfigurationBuilder(prefixes = ["with"], configurationPrefix = "spark-plug")
var sparkPlug = SparkPlug.builder()
}
That is from our test suite at https://github.com/micronaut-projects/micronaut-core/blob/1c3e2c3280da200c96e629a4edb9df87875ef2ff/test-suite-kotlin/src/test/kotlin/io/micronaut/docs/config/builder/EngineConfig.kt.
You can also inject the values as constructor parameters using #Value.
I hope that helps.