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

org.jetbrains.kotlin.ir.interpreter.checker.EvaluationMode.kt Maven / Gradle / Ivy

There is a newer version: 2.0.0
Show newest version
/*
 * Copyright 2010-2020 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.ir.interpreter.checker

import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.interpreter.*
import org.jetbrains.kotlin.ir.interpreter.hasAnnotation
import org.jetbrains.kotlin.ir.types.isAny
import org.jetbrains.kotlin.ir.types.isPrimitiveType
import org.jetbrains.kotlin.ir.types.isString
import org.jetbrains.kotlin.ir.types.isUnsignedType
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.FqName

enum class EvaluationMode(protected val mustCheckBody: Boolean) {
    FULL(mustCheckBody = true) {
        override fun canEvaluateFunction(function: IrFunction, context: IrCall?): Boolean = true
        override fun canEvaluateEnumValue(enumEntry: IrGetEnumValue, context: IrCall?): Boolean = true
        override fun canEvaluateReference(reference: IrCallableReference<*>, context: IrCall?): Boolean = true
    },

    WITH_ANNOTATIONS(mustCheckBody = false) {
        override fun canEvaluateFunction(function: IrFunction, context: IrCall?): Boolean {
            if (function.isCompileTimePropertyAccessor()) return true
            return function.isMarkedAsCompileTime() || function.origin == IrBuiltIns.BUILTIN_OPERATOR ||
                    (function is IrSimpleFunction && function.isOperator && function.name.asString() == "invoke") ||
                    (function is IrSimpleFunction && function.isFakeOverride && function.overriddenSymbols.any { canEvaluateFunction(it.owner) }) ||
                    function.isCompileTimeTypeAlias()
        }

        private fun IrFunction?.isCompileTimePropertyAccessor(): Boolean {
            val property = (this as? IrSimpleFunction)?.correspondingPropertySymbol?.owner ?: return false
            if (property.isConst) return true
            if (property.isMarkedAsCompileTime() || property.isCompileTimeTypeAlias()) return true

            val backingField = property.backingField
            val backingFieldExpression = backingField?.initializer?.expression as? IrGetValue
            return backingFieldExpression?.origin == IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER
        }

        override fun canEvaluateEnumValue(enumEntry: IrGetEnumValue, context: IrCall?): Boolean = true
        override fun canEvaluateReference(reference: IrCallableReference<*>, context: IrCall?): Boolean = true
    },

    ONLY_BUILTINS(mustCheckBody = false) {
        private val forbiddenMethodsOnPrimitives = setOf("inc", "dec", "rangeTo", "hashCode")
        private val forbiddenMethodsOnStrings = setOf("subSequence", "hashCode", "")
        private val allowedExtensionFunctions = setOf("kotlin.floorDiv", "kotlin.mod", "kotlin.NumbersKt.floorDiv", "kotlin.NumbersKt.mod")

        override fun canEvaluateFunction(function: IrFunction, context: IrCall?): Boolean {
            if ((function as? IrSimpleFunction)?.correspondingPropertySymbol?.owner?.isConst == true) return true

            val fqName = function.fqNameWhenAvailable?.asString()
            val parent = function.parentClassOrNull
            val parentType = parent?.defaultType
            return when {
                parentType == null -> fqName in allowedExtensionFunctions
                parentType.isPrimitiveType() -> function.name.asString() !in forbiddenMethodsOnPrimitives
                parentType.isString() -> function.name.asString() !in forbiddenMethodsOnStrings
                parentType.isAny() -> function.name.asString() == "toString" && context?.dispatchReceiver !is IrGetObjectValue
                parent.isObject -> parent.parentClassOrNull?.defaultType?.let { it.isPrimitiveType() || it.isUnsigned() } == true
                parentType.isUnsignedType() && function is IrConstructor -> true
                else -> fqName in allowedExtensionFunctions
            }
        }

        override fun canEvaluateEnumValue(enumEntry: IrGetEnumValue, context: IrCall?): Boolean = false
        override fun canEvaluateReference(reference: IrCallableReference<*>, context: IrCall?): Boolean = false
    },

    ONLY_INTRINSIC_CONST(mustCheckBody = false) {
        override fun canEvaluateFunction(function: IrFunction, context: IrCall?): Boolean {
            return function.isCompileTimePropertyAccessor() || function.isMarkedAsIntrinsicConstEvaluation() || context.isIntrinsicConstEvaluationNameProperty()
        }

        private fun IrFunction?.isCompileTimePropertyAccessor(): Boolean {
            val property = (this as? IrSimpleFunction)?.correspondingPropertySymbol?.owner ?: return false
            return property.isConst || (property.resolveFakeOverride() ?: property).isMarkedAsIntrinsicConstEvaluation()
        }

        override fun canEvaluateEnumValue(enumEntry: IrGetEnumValue, context: IrCall?): Boolean {
            return context.isIntrinsicConstEvaluationNameProperty()
        }

        override fun canEvaluateReference(reference: IrCallableReference<*>, context: IrCall?): Boolean {
            return context.isIntrinsicConstEvaluationNameProperty()
        }

        private fun IrCall?.isIntrinsicConstEvaluationNameProperty(): Boolean {
            if (this == null) return false
            val owner = this.symbol.owner
            val property = (owner as? IrSimpleFunction)?.correspondingPropertySymbol?.owner ?: return false
            return owner.isCompileTimePropertyAccessor() && property.name.asString() == "name"
        }
    };

    abstract fun canEvaluateFunction(function: IrFunction, context: IrCall? = null): Boolean
    abstract fun canEvaluateEnumValue(enumEntry: IrGetEnumValue, context: IrCall? = null): Boolean
    abstract fun canEvaluateReference(reference: IrCallableReference<*>, context: IrCall? = null): Boolean

    fun mustCheckBodyOf(function: IrFunction): Boolean {
        if (function is IrSimpleFunction && function.correspondingPropertySymbol != null) return true
        return (mustCheckBody || function.isLocal) && !function.isContract() && !function.isMarkedAsEvaluateIntrinsic()
    }

    protected val compileTimeTypeAliases = setOf(
        "java.lang.StringBuilder", "java.lang.IllegalArgumentException", "java.util.NoSuchElementException"
    )

    fun IrDeclaration.isMarkedAsCompileTime() = isMarkedWith(compileTimeAnnotation)
    protected fun IrDeclaration.isMarkedAsIntrinsicConstEvaluation() = isMarkedWith(intrinsicConstEvaluationAnnotation)
    private fun IrDeclaration.isContract() = isMarkedWith(contractsDslAnnotation)
    private fun IrDeclaration.isMarkedAsEvaluateIntrinsic() = isMarkedWith(evaluateIntrinsicAnnotation)
    protected fun IrDeclaration.isCompileTimeTypeAlias() = this.parentClassOrNull?.fqName in compileTimeTypeAliases

    protected fun IrDeclaration.isMarkedWith(annotation: FqName): Boolean {
        if (this is IrClass && this.isCompanion) return false
        if (this.hasAnnotation(annotation)) return true
        return (this.parent as? IrClass)?.isMarkedWith(annotation) ?: false
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy