Simplify logic of checking that IrGetField can be interpreted
This commit is contained in:
committed by
TeamCityServer
parent
ca1932b3a5
commit
7882fdf232
@@ -369,8 +369,8 @@ class IrInterpreter private constructor(
|
|||||||
field.origin == IrDeclarationOrigin.PROPERTY_BACKING_FIELD && field.correspondingPropertySymbol?.owner?.isConst == true -> {
|
field.origin == IrDeclarationOrigin.PROPERTY_BACKING_FIELD && field.correspondingPropertySymbol?.owner?.isConst == true -> {
|
||||||
callStack.addInstruction(CompoundInstruction(field.initializer?.expression))
|
callStack.addInstruction(CompoundInstruction(field.initializer?.expression))
|
||||||
}
|
}
|
||||||
// receiver is null, for example, for top level fields
|
expression.receiver.let { it == null || it.type.classOrNull?.owner?.isObject == true } -> {
|
||||||
expression.receiver == null -> {
|
// receiver is null, for example, for top level fields
|
||||||
val propertyOwner = field.correspondingPropertySymbol?.owner
|
val propertyOwner = field.correspondingPropertySymbol?.owner
|
||||||
val isConst = propertyOwner?.isConst == true || propertyOwner?.backingField?.initializer?.expression is IrConst<*>
|
val isConst = propertyOwner?.isConst == true || propertyOwner?.backingField?.initializer?.expression is IrConst<*>
|
||||||
assert(isConst) { "Cannot interpret get method on top level non const properties" }
|
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
|
environment.mapOfObjects[objectClass.symbol] = state // must set object's state here to avoid cyclic evaluation
|
||||||
callStack.addVariable(Variable(objectClass.thisReceiver!!.symbol, state))
|
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)
|
val constructorCall = IrConstructorCallImpl.fromSymbolOwner(constructor.returnType, constructor.symbol)
|
||||||
callStack.newSubFrame(constructorCall)
|
callStack.newSubFrame(constructorCall)
|
||||||
callStack.addInstruction(SimpleInstruction(constructorCall))
|
callStack.addInstruction(SimpleInstruction(constructorCall))
|
||||||
|
|||||||
+9
-7
@@ -8,14 +8,12 @@ package org.jetbrains.kotlin.ir.interpreter.checker
|
|||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.interpreter.compileTimeAnnotation
|
import org.jetbrains.kotlin.ir.interpreter.*
|
||||||
import org.jetbrains.kotlin.ir.interpreter.contractsDslAnnotation
|
|
||||||
import org.jetbrains.kotlin.ir.interpreter.evaluateIntrinsicAnnotation
|
|
||||||
import org.jetbrains.kotlin.ir.interpreter.hasAnnotation
|
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.isAny
|
||||||
import org.jetbrains.kotlin.ir.types.isPrimitiveType
|
import org.jetbrains.kotlin.ir.types.isPrimitiveType
|
||||||
import org.jetbrains.kotlin.ir.types.isString
|
import org.jetbrains.kotlin.ir.types.isString
|
||||||
|
import org.jetbrains.kotlin.ir.types.isUnsignedType
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
|
||||||
@@ -47,17 +45,20 @@ enum class EvaluationMode(protected val mustCheckBody: Boolean) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
ONLY_BUILTINS(mustCheckBody = false) {
|
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 {
|
override fun canEvaluateFunction(function: IrFunction, expression: IrCall?): Boolean {
|
||||||
if ((function as? IrSimpleFunction)?.correspondingPropertySymbol?.owner?.isConst == true) return true
|
if ((function as? IrSimpleFunction)?.correspondingPropertySymbol?.owner?.isConst == true) return true
|
||||||
|
|
||||||
val parent = function.parentClassOrNull ?: return false
|
val parent = function.parentClassOrNull ?: return false
|
||||||
val parentType = parent.defaultType
|
val parentType = parent.defaultType
|
||||||
return when {
|
return when {
|
||||||
parentType.isPrimitiveType() -> function.name.asString() !in setOf("inc", "dec", "rangeTo", "hashCode")
|
parentType.isPrimitiveType() -> function.name.asString() !in forbiddenMethodsOnPrimitives
|
||||||
parentType.isString() -> function.name.asString() !in setOf("subSequence", "hashCode")
|
parentType.isString() -> function.name.asString() !in forbiddenMethodsOnStrings
|
||||||
parentType.isAny() -> function.name.asString() == "toString" && expression?.dispatchReceiver !is IrGetObjectValue
|
parentType.isAny() -> function.name.asString() == "toString" && expression?.dispatchReceiver !is IrGetObjectValue
|
||||||
parent.isObject -> parent.parentClassOrNull?.defaultType?.let { it.isPrimitiveType() || it.isUnsigned() } == true
|
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
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,6 +67,7 @@ enum class EvaluationMode(protected val mustCheckBody: Boolean) {
|
|||||||
abstract fun canEvaluateFunction(function: IrFunction, expression: IrCall? = null): Boolean
|
abstract fun canEvaluateFunction(function: IrFunction, expression: IrCall? = null): Boolean
|
||||||
|
|
||||||
fun canEvaluateBody(function: IrFunction): Boolean {
|
fun canEvaluateBody(function: IrFunction): Boolean {
|
||||||
|
if (function is IrSimpleFunction && function.correspondingPropertySymbol != null) return true
|
||||||
return (mustCheckBody || function.isLocal) && !function.isContract() && !function.isMarkedAsEvaluateIntrinsic()
|
return (mustCheckBody || function.isLocal) && !function.isContract() && !function.isMarkedAsEvaluateIntrinsic()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+33
-26
@@ -12,7 +12,6 @@ import org.jetbrains.kotlin.ir.expressions.*
|
|||||||
import org.jetbrains.kotlin.ir.types.*
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||||
import org.jetbrains.kotlin.name.FqName
|
|
||||||
|
|
||||||
class IrCompileTimeChecker(
|
class IrCompileTimeChecker(
|
||||||
containingDeclaration: IrElement? = null, private val mode: EvaluationMode = EvaluationMode.WITH_ANNOTATIONS
|
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 {
|
private fun visitStatements(statements: List<IrStatement>, data: Nothing?): Boolean {
|
||||||
if (mode == EvaluationMode.ONLY_BUILTINS) {
|
if (mode == EvaluationMode.ONLY_BUILTINS) {
|
||||||
val statement = statements.singleOrNull() ?: return false
|
val statement = statements.singleOrNull() ?: return false
|
||||||
return statement is IrConst<*>
|
return statement.accept(this, data)
|
||||||
}
|
}
|
||||||
return statements.all { it.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
|
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 {
|
override fun visitVararg(expression: IrVararg, data: Nothing?): Boolean {
|
||||||
return expression.elements.any { it.accept(this, data) }
|
return expression.elements.any { it.accept(this, data) }
|
||||||
@@ -123,35 +128,36 @@ class IrCompileTimeChecker(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitGetField(expression: IrGetField, data: Nothing?): Boolean {
|
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
|
val owner = expression.symbol.owner
|
||||||
if (owner.origin == IrDeclarationOrigin.PROPERTY_BACKING_FIELD && owner.correspondingPropertySymbol?.owner?.isConst == true) {
|
val property = owner.correspondingPropertySymbol?.owner
|
||||||
val receiverComputable = expression.receiver?.accept(this, null) ?: true
|
val fqName = owner.fqNameForIrSerialization
|
||||||
val initializerComputable = owner.initializer?.accept(this, null) ?: false
|
return when {
|
||||||
if (receiverComputable && initializerComputable) {
|
// TODO fix later; used it here because java boolean resolves very strange,
|
||||||
return true
|
// 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 {
|
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
|
return false
|
||||||
}
|
}
|
||||||
//todo check receiver?
|
//todo check receiver?
|
||||||
@@ -199,6 +205,7 @@ class IrCompileTimeChecker(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitFunctionExpression(expression: IrFunctionExpression, data: Nothing?): Boolean {
|
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 isLambda = expression.origin == IrStatementOrigin.LAMBDA || expression.origin == IrStatementOrigin.ANONYMOUS_FUNCTION
|
||||||
val isCompileTime = mode.canEvaluateFunction(expression.function)
|
val isCompileTime = mode.canEvaluateFunction(expression.function)
|
||||||
return expression.function.asVisited {
|
return expression.function.asVisited {
|
||||||
|
|||||||
Reference in New Issue
Block a user