Simplify logic of checking that IrGetField can be interpreted

This commit is contained in:
Ivan Kylchik
2021-04-19 23:56:29 +03:00
committed by TeamCityServer
parent ca1932b3a5
commit 7882fdf232
3 changed files with 45 additions and 36 deletions
@@ -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))
@@ -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", "<init>")
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()
}
@@ -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<IrStatement>, 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 <T> visitConst(expression: IrConst<T>, data: Nothing?): Boolean = true
override fun <T> visitConst(expression: IrConst<T>, 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<IrProperty>().singleOrNull { it == property }?.getter ?: return false
visitedStack.contains(getter)
}
}
val parent = owner.parent as IrDeclarationContainer
val getter = parent.declarations.filterIsInstance<IrProperty>().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 {