diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt index 8f3400cf7e4..3c68938315b 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt @@ -369,8 +369,8 @@ class IrInterpreter private constructor( field.origin == IrDeclarationOrigin.PROPERTY_BACKING_FIELD && field.correspondingPropertySymbol?.owner?.isConst == true -> { callStack.addInstruction(CompoundInstruction(field.initializer?.expression)) } - // receiver is null, for example, for top level fields - expression.receiver == null -> { + expression.receiver.let { it == null || it.type.classOrNull?.owner?.isObject == true } -> { + // receiver is null, for example, for top level fields val propertyOwner = field.correspondingPropertySymbol?.owner val isConst = propertyOwner?.isConst == true || propertyOwner?.backingField?.initializer?.expression is IrConst<*> assert(isConst) { "Cannot interpret get method on top level non const properties" } @@ -390,7 +390,7 @@ class IrInterpreter private constructor( environment.mapOfObjects[objectClass.symbol] = state // must set object's state here to avoid cyclic evaluation callStack.addVariable(Variable(objectClass.thisReceiver!!.symbol, state)) - val constructor = objectClass.constructors.first() + val constructor = objectClass.constructors.firstOrNull() ?: return@interceptGetObjectValue callStack.pushState(state) val constructorCall = IrConstructorCallImpl.fromSymbolOwner(constructor.returnType, constructor.symbol) callStack.newSubFrame(constructorCall) callStack.addInstruction(SimpleInstruction(constructorCall)) diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/checker/EvaluationMode.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/checker/EvaluationMode.kt index fe2aaa64044..dbffc3494a4 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/checker/EvaluationMode.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/checker/EvaluationMode.kt @@ -8,14 +8,12 @@ package org.jetbrains.kotlin.ir.interpreter.checker import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.ir.interpreter.compileTimeAnnotation -import org.jetbrains.kotlin.ir.interpreter.contractsDslAnnotation -import org.jetbrains.kotlin.ir.interpreter.evaluateIntrinsicAnnotation +import org.jetbrains.kotlin.ir.interpreter.* import org.jetbrains.kotlin.ir.interpreter.hasAnnotation -import org.jetbrains.kotlin.ir.interpreter.isUnsigned 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 @@ -47,17 +45,20 @@ enum class EvaluationMode(protected val mustCheckBody: Boolean) { }, ONLY_BUILTINS(mustCheckBody = false) { + private val forbiddenMethodsOnPrimitives = setOf("inc", "dec", "rangeTo", "hashCode") + private val forbiddenMethodsOnStrings = setOf("subSequence", "hashCode", "") + override fun canEvaluateFunction(function: IrFunction, expression: IrCall?): Boolean { if ((function as? IrSimpleFunction)?.correspondingPropertySymbol?.owner?.isConst == true) return true val parent = function.parentClassOrNull ?: return false val parentType = parent.defaultType return when { - parentType.isPrimitiveType() -> function.name.asString() !in setOf("inc", "dec", "rangeTo", "hashCode") - parentType.isString() -> function.name.asString() !in setOf("subSequence", "hashCode") + parentType.isPrimitiveType() -> function.name.asString() !in forbiddenMethodsOnPrimitives + parentType.isString() -> function.name.asString() !in forbiddenMethodsOnStrings parentType.isAny() -> function.name.asString() == "toString" && expression?.dispatchReceiver !is IrGetObjectValue parent.isObject -> parent.parentClassOrNull?.defaultType?.let { it.isPrimitiveType() || it.isUnsigned() } == true - parentType?.isUnsignedType() == true && function is IrConstructor -> true + parentType.isUnsignedType() && function is IrConstructor -> true else -> false } } @@ -66,6 +67,7 @@ enum class EvaluationMode(protected val mustCheckBody: Boolean) { abstract fun canEvaluateFunction(function: IrFunction, expression: IrCall? = null): Boolean fun canEvaluateBody(function: IrFunction): Boolean { + if (function is IrSimpleFunction && function.correspondingPropertySymbol != null) return true return (mustCheckBody || function.isLocal) && !function.isContract() && !function.isMarkedAsEvaluateIntrinsic() } diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/checker/IrCompileTimeChecker.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/checker/IrCompileTimeChecker.kt index 89a669bdb39..7d148c6c402 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/checker/IrCompileTimeChecker.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/checker/IrCompileTimeChecker.kt @@ -12,7 +12,6 @@ import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementVisitor -import org.jetbrains.kotlin.name.FqName class IrCompileTimeChecker( containingDeclaration: IrElement? = null, private val mode: EvaluationMode = EvaluationMode.WITH_ANNOTATIONS @@ -31,7 +30,7 @@ class IrCompileTimeChecker( private fun visitStatements(statements: List, data: Nothing?): Boolean { if (mode == EvaluationMode.ONLY_BUILTINS) { val statement = statements.singleOrNull() ?: return false - return statement is IrConst<*> + return statement.accept(this, data) } return statements.all { it.accept(this, data) } } @@ -82,7 +81,13 @@ class IrCompileTimeChecker( return body.kind == IrSyntheticBodyKind.ENUM_VALUES || body.kind == IrSyntheticBodyKind.ENUM_VALUEOF } - override fun visitConst(expression: IrConst, data: Nothing?): Boolean = true + override fun visitConst(expression: IrConst, data: Nothing?): Boolean { + if (expression.type.getUnsignedType() != null) { + val constructor = expression.type.classOrNull?.owner?.constructors?.singleOrNull() ?: return false + return mode.canEvaluateFunction(constructor) + } + return true + } override fun visitVararg(expression: IrVararg, data: Nothing?): Boolean { return expression.elements.any { it.accept(this, data) } @@ -123,35 +128,36 @@ class IrCompileTimeChecker( } override fun visitGetField(expression: IrGetField, data: Nothing?): Boolean { - // TODO fix later; used it here because java boolean resolves very strange, - // its type is flexible (so its not primitive) and there is no initializer at backing field - val fqName = expression.symbol.owner.fqNameForIrSerialization - if (fqName.toString().let { it == "java.lang.Boolean.FALSE" || it == "java.lang.Boolean.TRUE" }) { - return true - } - - if (expression.receiver == null) - return expression.symbol.owner.correspondingPropertySymbol?.owner?.let { it.isConst || it.backingField?.initializer?.expression is IrConst<*> } == true - - val property = expression.symbol.owner.correspondingPropertySymbol?.owner val owner = expression.symbol.owner - if (owner.origin == IrDeclarationOrigin.PROPERTY_BACKING_FIELD && owner.correspondingPropertySymbol?.owner?.isConst == true) { - val receiverComputable = expression.receiver?.accept(this, null) ?: true - val initializerComputable = owner.initializer?.accept(this, null) ?: false - if (receiverComputable && initializerComputable) { - return true + val property = owner.correspondingPropertySymbol?.owner + val fqName = owner.fqNameForIrSerialization + return when { + // TODO fix later; used it here because java boolean resolves very strange, + // its type is flexible (so its not primitive) and there is no initializer at backing field + fqName.toString().let { it == "java.lang.Boolean.FALSE" || it == "java.lang.Boolean.TRUE" } -> true + owner.origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB && owner.isStatic && + (owner.type.isPrimitiveType() || owner.type.isStringClassType()) -> { + // if is java primitive static + owner.initializer?.accept(this, data) == true + } + expression.receiver == null -> { + property?.isConst == true && owner.initializer?.accept(this, null) == true + } + owner.origin == IrDeclarationOrigin.PROPERTY_BACKING_FIELD && property?.isConst == true -> { + val receiverComputable = expression.receiver?.accept(this, null) ?: true + val initializerComputable = owner.initializer?.accept(this, null) ?: false + receiverComputable && initializerComputable + } + else -> { + val parent = owner.parent as IrDeclarationContainer + val getter = parent.declarations.filterIsInstance().singleOrNull { it == property }?.getter ?: return false + visitedStack.contains(getter) } } - - val parent = owner.parent as IrDeclarationContainer - val getter = parent.declarations.filterIsInstance().single { it == property }.getter - val isJavaPrimitiveStatic = owner.origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB && owner.isStatic && - (owner.type.isPrimitiveType() || owner.type.isStringClassType()) - return getter?.let { visitedStack.contains(it) } == true || isJavaPrimitiveStatic } override fun visitSetField(expression: IrSetField, data: Nothing?): Boolean { - if (expression.receiver.let { it == null || (it.type.classifierOrNull?.owner as? IrClass)?.isObject == true }) { + if (expression.receiver.let { it == null || it.type.classOrNull?.owner?.isObject == true }) { return false } //todo check receiver? @@ -199,6 +205,7 @@ class IrCompileTimeChecker( } override fun visitFunctionExpression(expression: IrFunctionExpression, data: Nothing?): Boolean { + if (mode == EvaluationMode.ONLY_BUILTINS) return false val isLambda = expression.origin == IrStatementOrigin.LAMBDA || expression.origin == IrStatementOrigin.ANONYMOUS_FUNCTION val isCompileTime = mode.canEvaluateFunction(expression.function) return expression.function.asVisited {