All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.psi.stubs.impl.KotlinConstantValue.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0-RC
Show newest version
/*
 * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
 * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
 */

package org.jetbrains.kotlin.psi.stubs.impl

import com.intellij.psi.stubs.StubInputStream
import com.intellij.psi.stubs.StubOutputStream
import org.jetbrains.kotlin.constant.*
import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.metadata.deserialization.Flags
import org.jetbrains.kotlin.metadata.deserialization.NameResolver
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.stubs.StubUtils

enum class KotlinConstantValueKind {
    NULL, BOOLEAN, CHAR, BYTE, SHORT, INT, LONG, DOUBLE, FLOAT, ENUM, KCLASS, STRING, ARRAY, UBYTE, USHORT, UINT, ULONG, ANNO;
}

fun createConstantValue(dataStream: StubInputStream): ConstantValue<*>? {
    val kind = dataStream.readInt()
    if (kind == -1) return null
    return when (KotlinConstantValueKind.entries[kind]) {
        KotlinConstantValueKind.NULL -> NullValue
        KotlinConstantValueKind.BOOLEAN -> BooleanValue(dataStream.readBoolean())
        KotlinConstantValueKind.CHAR -> CharValue(dataStream.readChar())
        KotlinConstantValueKind.BYTE -> ByteValue(dataStream.readByte())
        KotlinConstantValueKind.SHORT -> ShortValue(dataStream.readShort())
        KotlinConstantValueKind.INT -> IntValue(dataStream.readInt())
        KotlinConstantValueKind.LONG -> LongValue(dataStream.readLong())
        KotlinConstantValueKind.DOUBLE -> DoubleValue(dataStream.readDouble())
        KotlinConstantValueKind.FLOAT -> FloatValue(dataStream.readFloat())
        KotlinConstantValueKind.ENUM -> EnumValue(
            StubUtils.deserializeClassId(dataStream)!!,
            Name.identifier(dataStream.readNameString()!!)
        )
        KotlinConstantValueKind.KCLASS -> KClassValue(StubUtils.deserializeClassId(dataStream)!!, dataStream.readInt())
        KotlinConstantValueKind.STRING -> StringValue(dataStream.readNameString()!!)
        KotlinConstantValueKind.ARRAY -> {
            val arraySize = dataStream.readInt() - 1
            ArrayValue((0..arraySize).map {
                createConstantValue(dataStream)!!
            })
        }
        KotlinConstantValueKind.UBYTE -> UByteValue(dataStream.readByte())
        KotlinConstantValueKind.USHORT -> UShortValue(dataStream.readShort())
        KotlinConstantValueKind.UINT -> UIntValue(dataStream.readInt())
        KotlinConstantValueKind.ULONG -> ULongValue(dataStream.readLong())
        KotlinConstantValueKind.ANNO -> {
            val classId = StubUtils.deserializeClassId(dataStream)!!
            val numberOfArgs = dataStream.readInt() - 1
            AnnotationValue.create(classId, (0..numberOfArgs).associate {
                Name.identifier(dataStream.readNameString()!!) to createConstantValue(dataStream)!!
            })
        }
    }
}


fun serialize(constantValue: ConstantValue<*>, dataStream: StubOutputStream) {
    constantValue.accept(KotlinConstantValueSerializationVisitor(dataStream), null)
}

class KotlinConstantValueSerializationVisitor(private val dataStream: StubOutputStream) :
    AnnotationArgumentVisitor() {
    override fun visitArrayValue(value: ArrayValue, data: Nothing?) {
        dataStream.writeInt(KotlinConstantValueKind.ARRAY.ordinal)
        dataStream.writeInt(value.value.size)
        for (constantValue in value.value) {
            constantValue.accept(this, data)
        }
    }

    override fun visitBooleanValue(value: BooleanValue, data: Nothing?) {
        dataStream.writeInt(KotlinConstantValueKind.BOOLEAN.ordinal)
        dataStream.writeBoolean(value.value)
    }

    override fun visitByteValue(value: ByteValue, data: Nothing?) {
        dataStream.writeInt(KotlinConstantValueKind.BYTE.ordinal)
        dataStream.writeByte(value.value.toInt())
    }

    override fun visitCharValue(value: CharValue, data: Nothing?) {
        dataStream.writeInt(KotlinConstantValueKind.CHAR.ordinal)
        dataStream.writeChar(value.value.code)
    }

    override fun visitShortValue(value: ShortValue, data: Nothing?) {
        dataStream.writeInt(KotlinConstantValueKind.SHORT.ordinal)
        dataStream.writeShort(value.value.toInt())
    }

    override fun visitIntValue(value: IntValue, data: Nothing?) {
        dataStream.writeInt(KotlinConstantValueKind.INT.ordinal)
        dataStream.writeInt(value.value)
    }

    override fun visitLongValue(value: LongValue, data: Nothing?) {
        dataStream.writeInt(KotlinConstantValueKind.LONG.ordinal)
        dataStream.writeLong(value.value)
    }

    override fun visitDoubleValue(value: DoubleValue, data: Nothing?) {
        dataStream.writeInt(KotlinConstantValueKind.DOUBLE.ordinal)
        dataStream.writeDouble(value.value)
    }

    override fun visitFloatValue(value: FloatValue, data: Nothing?) {
        dataStream.writeInt(KotlinConstantValueKind.FLOAT.ordinal)
        dataStream.writeFloat(value.value)
    }

    override fun visitEnumValue(value: EnumValue, data: Nothing?) {
        dataStream.writeInt(KotlinConstantValueKind.ENUM.ordinal)
        StubUtils.serializeClassId(dataStream, value.enumClassId)
        dataStream.writeName(value.enumEntryName.identifier)
    }

    override fun visitKClassValue(value: KClassValue, data: Nothing?) {
        dataStream.writeInt(KotlinConstantValueKind.KCLASS.ordinal)
        val normalClass = value.value as KClassValue.Value.NormalClass
        StubUtils.serializeClassId(dataStream, normalClass.classId)
        dataStream.writeInt(normalClass.arrayDimensions)
    }

    override fun visitNullValue(value: NullValue, data: Nothing?) {
        dataStream.writeInt(KotlinConstantValueKind.NULL.ordinal)
    }
    override fun visitStringValue(value: StringValue, data: Nothing?) {
        dataStream.writeInt(KotlinConstantValueKind.STRING.ordinal)
        dataStream.writeName(value.value)
    }

    override fun visitUByteValue(value: UByteValue, data: Nothing?) {
        dataStream.writeInt(KotlinConstantValueKind.UBYTE.ordinal)
        dataStream.writeByte(value.value.toInt())
    }

    override fun visitUShortValue(value: UShortValue, data: Nothing?) {
        dataStream.writeInt(KotlinConstantValueKind.USHORT.ordinal)
        dataStream.writeShort(value.value.toInt())
    }

    override fun visitUIntValue(value: UIntValue, data: Nothing?) {
        dataStream.writeInt(KotlinConstantValueKind.UINT.ordinal)
        dataStream.writeInt(value.value)
    }

    override fun visitULongValue(value: ULongValue, data: Nothing?) {
        dataStream.writeInt(KotlinConstantValueKind.ULONG.ordinal)
        dataStream.writeLong(value.value)
    }

    override fun visitAnnotationValue(value: AnnotationValue, data: Nothing?) {
        dataStream.writeInt(KotlinConstantValueKind.ANNO.ordinal)
        StubUtils.serializeClassId(dataStream, value.value.classId)
        val args = value.value.argumentsMapping
        dataStream.writeInt(args.size)
        for (arg in args) {
            dataStream.writeName(arg.key.asString())
            arg.value.accept(this, data)
        }
    }

    override fun visitErrorValue(value: ErrorValue, data: Nothing?) {
        error("Error values should not be reachable in compiled code")
    }
}

data class AnnotationData(val annoClassId: ClassId, val args: Map>)
data class EnumData(val enumClassId: ClassId, val enumEntryName: Name)
data class KClassData(val classId: ClassId, val arrayNestedness: Int)
fun createConstantValue(value: Any?): ConstantValue<*> {
    return when (value) {
        is Byte -> ByteValue(value)
        is Short -> ShortValue(value)
        is Int -> IntValue(value)
        is Long -> LongValue(value)
        is Char -> CharValue(value)
        is Float -> FloatValue(value)
        is Double -> DoubleValue(value)
        is Boolean -> BooleanValue(value)
        is String -> StringValue(value)
        is ByteArray -> ArrayValue(value.map { createConstantValue(it) }.toList())
        is ShortArray -> ArrayValue(value.map { createConstantValue(it) }.toList())
        is IntArray -> ArrayValue(value.map { createConstantValue(it) }.toList())
        is LongArray -> ArrayValue(value.map { createConstantValue(it) }.toList())
        is CharArray -> ArrayValue(value.map { createConstantValue(it) }.toList())
        is FloatArray -> ArrayValue(value.map { createConstantValue(it) }.toList())
        is DoubleArray -> ArrayValue(value.map { createConstantValue(it) }.toList())
        is BooleanArray -> ArrayValue(value.map { createConstantValue(it) }.toList())
        is Array<*> -> ArrayValue(value.map { createConstantValue(it) }.toList())
        is EnumData -> EnumValue(value.enumClassId, value.enumEntryName)
        is KClassData -> KClassValue(value.classId, value.arrayNestedness)
        is AnnotationData -> AnnotationValue.create(value.annoClassId, value.args)
        null -> NullValue
        else -> error("Unsupported value $value")
    }
}

fun createConstantValue(value: ProtoBuf.Annotation.Argument.Value, nameResolver: NameResolver): ConstantValue<*> {
    val isUnsigned = Flags.IS_UNSIGNED.get(value.flags)

    fun  T.letIf(predicate: Boolean, f: (T) -> R, g: (T) -> R): R =
        if (predicate) f(this) else g(this)

    return when (value.type) {
        ProtoBuf.Annotation.Argument.Value.Type.BYTE -> value.intValue.toByte().letIf(isUnsigned, ::UByteValue, ::ByteValue)
        ProtoBuf.Annotation.Argument.Value.Type.CHAR -> CharValue(value.intValue.toInt().toChar())
        ProtoBuf.Annotation.Argument.Value.Type.SHORT -> value.intValue.toShort().letIf(isUnsigned, ::UShortValue, ::ShortValue)
        ProtoBuf.Annotation.Argument.Value.Type.INT -> value.intValue.toInt().letIf(isUnsigned, ::UIntValue, ::IntValue)
        ProtoBuf.Annotation.Argument.Value.Type.LONG -> value.intValue.letIf(isUnsigned, ::ULongValue, ::LongValue)
        ProtoBuf.Annotation.Argument.Value.Type.FLOAT -> FloatValue(value.floatValue)
        ProtoBuf.Annotation.Argument.Value.Type.DOUBLE -> DoubleValue(value.doubleValue)
        ProtoBuf.Annotation.Argument.Value.Type.BOOLEAN -> BooleanValue(value.intValue != 0L)
        ProtoBuf.Annotation.Argument.Value.Type.STRING -> StringValue(nameResolver.getString(value.stringValue))
        ProtoBuf.Annotation.Argument.Value.Type.CLASS -> KClassValue(nameResolver.getClassId(value.classId), value.arrayDimensionCount)
        ProtoBuf.Annotation.Argument.Value.Type.ENUM -> EnumValue(
            nameResolver.getClassId(value.classId),
            nameResolver.getName(value.enumValueId)
        )
        ProtoBuf.Annotation.Argument.Value.Type.ANNOTATION -> {
            val args =
                value.annotation.argumentList.associate { nameResolver.getName(it.nameId) to createConstantValue(it.value, nameResolver) }
            AnnotationValue.create(nameResolver.getClassId(value.annotation.id), args)
        }
        ProtoBuf.Annotation.Argument.Value.Type.ARRAY -> ArrayValue(
            value.arrayElementList.map { createConstantValue(it, nameResolver) }
        )
        else -> error("Unsupported annotation argument type: ${value.type}")
    }
}
private fun NameResolver.getClassId(index: Int): ClassId {
    return ClassId.fromString(getQualifiedClassName(index), isLocalClassName(index))
}

private fun NameResolver.getName(index: Int): Name =
    Name.guessByFirstCharacter(getString(index))





© 2015 - 2024 Weber Informatics LLC | Privacy Policy