Prevent interpretation of call expression with continue/break statement
Example: foo("" + continue)
This commit is contained in:
+22
-19
@@ -11,9 +11,7 @@ import org.jetbrains.kotlin.backend.common.interpreter.builtins.evaluateIntrinsi
|
|||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
import org.jetbrains.kotlin.ir.IrStatement
|
import org.jetbrains.kotlin.ir.IrStatement
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
|
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
|
||||||
import org.jetbrains.kotlin.ir.types.classifierOrNull
|
import org.jetbrains.kotlin.ir.types.classifierOrNull
|
||||||
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
|
||||||
@@ -24,9 +22,9 @@ enum class EvaluationMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class IrCompileTimeChecker(
|
class IrCompileTimeChecker(
|
||||||
containingDeclaration: IrSymbol? = null, private val mode: EvaluationMode = EvaluationMode.FULL
|
containingDeclaration: IrElement? = null, private val mode: EvaluationMode = EvaluationMode.FULL
|
||||||
) : IrElementVisitor<Boolean, Nothing?> {
|
) : IrElementVisitor<Boolean, Nothing?> {
|
||||||
private val callStack = mutableListOf<IrSymbol>().apply { if (containingDeclaration != null) add(containingDeclaration) }
|
private val visitedStack = mutableListOf<IrElement>().apply { if (containingDeclaration != null) add(containingDeclaration) }
|
||||||
|
|
||||||
private val compileTimeTypeAliases = setOf(
|
private val compileTimeTypeAliases = setOf(
|
||||||
"java.lang.StringBuilder", "java.lang.IllegalArgumentException", "java.util.NoSuchElementException"
|
"java.lang.StringBuilder", "java.lang.IllegalArgumentException", "java.util.NoSuchElementException"
|
||||||
@@ -55,10 +53,10 @@ class IrCompileTimeChecker(
|
|||||||
return backingFieldExpression?.origin == IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER
|
return backingFieldExpression?.origin == IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun IrSymbol.withCallStack(block: () -> Boolean): Boolean {
|
private fun IrElement.asVisited(block: () -> Boolean): Boolean {
|
||||||
callStack += this
|
visitedStack += this
|
||||||
val result = block()
|
val result = block()
|
||||||
callStack.removeAt(callStack.lastIndex)
|
visitedStack.removeAt(visitedStack.lastIndex)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,7 +78,7 @@ class IrCompileTimeChecker(
|
|||||||
override fun visitCall(expression: IrCall, data: Nothing?): Boolean {
|
override fun visitCall(expression: IrCall, data: Nothing?): Boolean {
|
||||||
if (expression.symbol.owner.isContract()) return false
|
if (expression.symbol.owner.isContract()) return false
|
||||||
|
|
||||||
val property = (expression.symbol.owner as? IrFunctionImpl)?.correspondingPropertySymbol?.owner
|
val property = (expression.symbol.owner as? IrSimpleFunction)?.correspondingPropertySymbol?.owner
|
||||||
if (expression.symbol.owner.isMarkedAsCompileTime() || property.isCompileTime()) {
|
if (expression.symbol.owner.isMarkedAsCompileTime() || property.isCompileTime()) {
|
||||||
val dispatchReceiverComputable = expression.dispatchReceiver?.accept(this, null) ?: true
|
val dispatchReceiverComputable = expression.dispatchReceiver?.accept(this, null) ?: true
|
||||||
val extensionReceiverComputable = expression.extensionReceiver?.accept(this, null) ?: true
|
val extensionReceiverComputable = expression.extensionReceiver?.accept(this, null) ?: true
|
||||||
@@ -148,7 +146,7 @@ class IrCompileTimeChecker(
|
|||||||
override fun visitGetValue(expression: IrGetValue, data: Nothing?): Boolean {
|
override fun visitGetValue(expression: IrGetValue, data: Nothing?): Boolean {
|
||||||
val parent = expression.symbol.owner.parent as IrSymbolOwner
|
val parent = expression.symbol.owner.parent as IrSymbolOwner
|
||||||
val isObject = (parent as? IrClass)?.isObject == true //used to evaluate constants inside object
|
val isObject = (parent as? IrClass)?.isObject == true //used to evaluate constants inside object
|
||||||
return callStack.contains(parent.symbol) || isObject
|
return visitedStack.contains(parent) || isObject
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitSetVariable(expression: IrSetVariable, data: Nothing?): Boolean {
|
override fun visitSetVariable(expression: IrSetVariable, data: Nothing?): Boolean {
|
||||||
@@ -160,7 +158,7 @@ class IrCompileTimeChecker(
|
|||||||
val parent = owner.parent as IrSymbolOwner
|
val parent = owner.parent as IrSymbolOwner
|
||||||
val isJavaPrimitiveStatic = owner.origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB && owner.isStatic &&
|
val isJavaPrimitiveStatic = owner.origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB && owner.isStatic &&
|
||||||
owner.parentAsClass.fqNameWhenAvailable.isJavaPrimitive()
|
owner.parentAsClass.fqNameWhenAvailable.isJavaPrimitive()
|
||||||
return callStack.contains(parent.symbol) || isJavaPrimitiveStatic
|
return visitedStack.contains(parent) || isJavaPrimitiveStatic
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO find similar method in utils
|
// TODO find similar method in utils
|
||||||
@@ -175,7 +173,7 @@ class IrCompileTimeChecker(
|
|||||||
override fun visitSetField(expression: IrSetField, data: Nothing?): Boolean {
|
override fun visitSetField(expression: IrSetField, data: Nothing?): Boolean {
|
||||||
//todo check receiver?
|
//todo check receiver?
|
||||||
val parent = expression.symbol.owner.parent as IrSymbolOwner
|
val parent = expression.symbol.owner.parent as IrSymbolOwner
|
||||||
return callStack.contains(parent.symbol) && expression.value.accept(this, data)
|
return visitedStack.contains(parent) && expression.value.accept(this, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitConstructorCall(expression: IrConstructorCall, data: Nothing?): Boolean {
|
override fun visitConstructorCall(expression: IrConstructorCall, data: Nothing?): Boolean {
|
||||||
@@ -187,7 +185,7 @@ class IrCompileTimeChecker(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitFunctionReference(expression: IrFunctionReference, data: Nothing?): Boolean {
|
override fun visitFunctionReference(expression: IrFunctionReference, data: Nothing?): Boolean {
|
||||||
return expression.symbol.withCallStack {
|
return expression.asVisited {
|
||||||
expression.symbol.owner.isMarkedAsCompileTime() && expression.symbol.owner.body?.accept(this, data) == true
|
expression.symbol.owner.isMarkedAsCompileTime() && expression.symbol.owner.body?.accept(this, data) == true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -195,7 +193,7 @@ class IrCompileTimeChecker(
|
|||||||
override fun visitFunctionExpression(expression: IrFunctionExpression, data: Nothing?): Boolean {
|
override fun visitFunctionExpression(expression: IrFunctionExpression, data: Nothing?): Boolean {
|
||||||
val isLambda = expression.origin == IrStatementOrigin.LAMBDA || expression.origin == IrStatementOrigin.ANONYMOUS_FUNCTION
|
val isLambda = expression.origin == IrStatementOrigin.LAMBDA || expression.origin == IrStatementOrigin.ANONYMOUS_FUNCTION
|
||||||
val isCompileTime = expression.function.isMarkedAsCompileTime()
|
val isCompileTime = expression.function.isMarkedAsCompileTime()
|
||||||
return expression.function.symbol.withCallStack {
|
return expression.function.asVisited {
|
||||||
if (isLambda || isCompileTime) expression.function.body?.accept(this, data) == true else false
|
if (isLambda || isCompileTime) expression.function.body?.accept(this, data) == true else false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -207,7 +205,7 @@ class IrCompileTimeChecker(
|
|||||||
IrTypeOperator.CAST, IrTypeOperator.IMPLICIT_CAST, IrTypeOperator.SAFE_CAST,
|
IrTypeOperator.CAST, IrTypeOperator.IMPLICIT_CAST, IrTypeOperator.SAFE_CAST,
|
||||||
IrTypeOperator.IMPLICIT_NOTNULL -> {
|
IrTypeOperator.IMPLICIT_NOTNULL -> {
|
||||||
val operand = expression.typeOperand.classifierOrNull?.owner
|
val operand = expression.typeOperand.classifierOrNull?.owner
|
||||||
if (operand is IrTypeParameter && !callStack.contains((operand.parent as IrSymbolOwner).symbol)) return false
|
if (operand is IrTypeParameter && !visitedStack.contains(operand.parent)) return false
|
||||||
expression.argument.accept(this, data)
|
expression.argument.accept(this, data)
|
||||||
}
|
}
|
||||||
IrTypeOperator.IMPLICIT_DYNAMIC_CAST -> false
|
IrTypeOperator.IMPLICIT_DYNAMIC_CAST -> false
|
||||||
@@ -224,25 +222,30 @@ class IrCompileTimeChecker(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitWhileLoop(loop: IrWhileLoop, data: Nothing?): Boolean {
|
override fun visitWhileLoop(loop: IrWhileLoop, data: Nothing?): Boolean {
|
||||||
return loop.condition.accept(this, data) && (loop.body?.accept(this, data) ?: true)
|
return loop.asVisited {
|
||||||
|
loop.condition.accept(this, data) && (loop.body?.accept(this, data) ?: true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitDoWhileLoop(loop: IrDoWhileLoop, data: Nothing?): Boolean {
|
override fun visitDoWhileLoop(loop: IrDoWhileLoop, data: Nothing?): Boolean {
|
||||||
return loop.condition.accept(this, data) && (loop.body?.accept(this, data) ?: true)
|
return loop.asVisited {
|
||||||
|
loop.condition.accept(this, data) && (loop.body?.accept(this, data) ?: true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitTry(aTry: IrTry, data: Nothing?): Boolean {
|
override fun visitTry(aTry: IrTry, data: Nothing?): Boolean {
|
||||||
|
if (mode == EvaluationMode.ONLY_BUILTINS) return false
|
||||||
if (!aTry.tryResult.accept(this, data)) return false
|
if (!aTry.tryResult.accept(this, data)) return false
|
||||||
if (aTry.finallyExpression != null && aTry.finallyExpression?.accept(this, data) == false) return false
|
if (aTry.finallyExpression != null && aTry.finallyExpression?.accept(this, data) == false) return false
|
||||||
return aTry.catches.all { it.result.accept(this, data) }
|
return aTry.catches.all { it.result.accept(this, data) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitBreak(jump: IrBreak, data: Nothing?): Boolean = true
|
override fun visitBreak(jump: IrBreak, data: Nothing?): Boolean = visitedStack.contains(jump.loop)
|
||||||
|
|
||||||
override fun visitContinue(jump: IrContinue, data: Nothing?): Boolean = true
|
override fun visitContinue(jump: IrContinue, data: Nothing?): Boolean = visitedStack.contains(jump.loop)
|
||||||
|
|
||||||
override fun visitReturn(expression: IrReturn, data: Nothing?): Boolean {
|
override fun visitReturn(expression: IrReturn, data: Nothing?): Boolean {
|
||||||
if (!callStack.contains(expression.returnTargetSymbol)) return false
|
if (!visitedStack.contains(expression.returnTargetSymbol.owner)) return false
|
||||||
return expression.value.accept(this, data)
|
return expression.value.accept(this, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user