ConstantExpressionEvaluator: make evaluate*naryAndCheck public
This commit is contained in:
committed by
max-kammerer
parent
f352a4dcd4
commit
7e4d33be24
+95
-48
@@ -674,20 +674,9 @@ private class ConstantExpressionEvaluatorVisitor(
|
|||||||
ConstantExpressionEvaluator.getConstant(expression, trace.bindingContext)?.isPure ?: false
|
ConstantExpressionEvaluator.getConstant(expression, trace.bindingContext)?.isPure ?: false
|
||||||
|
|
||||||
private fun evaluateUnaryAndCheck(receiver: OperationArgument, name: String, callExpression: KtExpression): Any? {
|
private fun evaluateUnaryAndCheck(receiver: OperationArgument, name: String, callExpression: KtExpression): Any? {
|
||||||
val functions = unaryOperations[UnaryOperationKey(receiver.ctcType, name)] ?: return null
|
return evaluateUnaryAndCheck(name, receiver.ctcType, receiver.value) {
|
||||||
|
|
||||||
val (function, check) = functions
|
|
||||||
val result = function(receiver.value)
|
|
||||||
if (check == emptyUnaryFun) {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
assert(isIntegerType(receiver.value)) { "Only integer constants should be checked for overflow" }
|
|
||||||
assert(name == "minus" || name == "unaryMinus") { "Only negation should be checked for overflow" }
|
|
||||||
|
|
||||||
if (receiver.value == result && !isZero(receiver.value)) {
|
|
||||||
trace.report(Errors.INTEGER_OVERFLOW.on(callExpression.getStrictParentOfType<KtExpression>() ?: callExpression))
|
trace.report(Errors.INTEGER_OVERFLOW.on(callExpression.getStrictParentOfType<KtExpression>() ?: callExpression))
|
||||||
}
|
}
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun evaluateBinaryAndCheck(
|
private fun evaluateBinaryAndCheck(
|
||||||
@@ -696,50 +685,15 @@ private class ConstantExpressionEvaluatorVisitor(
|
|||||||
name: String,
|
name: String,
|
||||||
callExpression: KtExpression
|
callExpression: KtExpression
|
||||||
): Any? {
|
): Any? {
|
||||||
val functions = getBinaryOperation(receiver, parameter, name) ?: return null
|
return evaluateBinaryAndCheck(name, receiver.ctcType, receiver.value, parameter.ctcType, parameter.value) {
|
||||||
|
|
||||||
val (function, checker) = functions
|
|
||||||
val actualResult = try {
|
|
||||||
function(receiver.value, parameter.value)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
if (checker == emptyBinaryFun) {
|
|
||||||
return actualResult
|
|
||||||
}
|
|
||||||
assert(isIntegerType(receiver.value) && isIntegerType(parameter.value)) { "Only integer constants should be checked for overflow" }
|
|
||||||
|
|
||||||
fun toBigInteger(value: Any?) = BigInteger.valueOf((value as Number).toLong())
|
|
||||||
|
|
||||||
val refinedChecker = if (name == OperatorNameConventions.MOD.asString()) {
|
|
||||||
getBinaryOperation(receiver, parameter, OperatorNameConventions.REM.asString())?.second ?: return null
|
|
||||||
} else {
|
|
||||||
checker
|
|
||||||
}
|
|
||||||
|
|
||||||
val resultInBigIntegers = refinedChecker(toBigInteger(receiver.value), toBigInteger(parameter.value))
|
|
||||||
|
|
||||||
if (toBigInteger(actualResult) != resultInBigIntegers) {
|
|
||||||
trace.report(Errors.INTEGER_OVERFLOW.on(callExpression.getStrictParentOfType<KtExpression>() ?: callExpression))
|
trace.report(Errors.INTEGER_OVERFLOW.on(callExpression.getStrictParentOfType<KtExpression>() ?: callExpression))
|
||||||
}
|
}
|
||||||
return actualResult
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getBinaryOperation(receiver: OperationArgument, parameter: OperationArgument, name: String) =
|
|
||||||
binaryOperations[BinaryOperationKey(receiver.ctcType, parameter.ctcType, name)]
|
|
||||||
|
|
||||||
private fun isDivisionByZero(name: String, parameter: Any?): Boolean {
|
private fun isDivisionByZero(name: String, parameter: Any?): Boolean {
|
||||||
return name in DIVISION_OPERATION_NAMES && isZero(parameter)
|
return name in DIVISION_OPERATION_NAMES && isZero(parameter)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isZero(value: Any?): Boolean {
|
|
||||||
return when {
|
|
||||||
isIntegerType(value) -> (value as Number).toLong() == 0L
|
|
||||||
value is Float || value is Double -> (value as Number).toDouble() == 0.0
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitUnaryExpression(expression: KtUnaryExpression, expectedType: KotlinType?): CompileTimeConstant<*>? {
|
override fun visitUnaryExpression(expression: KtUnaryExpression, expectedType: KotlinType?): CompileTimeConstant<*>? {
|
||||||
val leftExpression = expression.baseExpression ?: return null
|
val leftExpression = expression.baseExpression ?: return null
|
||||||
|
|
||||||
@@ -1168,3 +1122,96 @@ fun CompileTimeConstant<*>.isStandaloneOnlyConstant(): Boolean {
|
|||||||
else -> return false
|
else -> return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun isZero(value: Any?): Boolean {
|
||||||
|
return when {
|
||||||
|
isIntegerType(value) -> (value as Number).toLong() == 0L
|
||||||
|
value is Float || value is Double -> (value as Number).toDouble() == 0.0
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun typeStrToCompileTimeType(str: String) = when (str) {
|
||||||
|
BYTE.name -> BYTE
|
||||||
|
SHORT.name -> SHORT
|
||||||
|
INT.name -> INT
|
||||||
|
LONG.name -> LONG
|
||||||
|
DOUBLE.name -> DOUBLE
|
||||||
|
FLOAT.name -> FLOAT
|
||||||
|
CHAR.name -> CHAR
|
||||||
|
BOOLEAN.name -> BOOLEAN
|
||||||
|
STRING.name -> STRING
|
||||||
|
ANY.name -> ANY
|
||||||
|
else -> throw IllegalArgumentException("Unsupported type: $str")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun evaluateUnary(name: String, typeStr: String, value: Any, tracer: () -> Unit = {}): Any? {
|
||||||
|
return evaluateUnaryAndCheck(name, typeStrToCompileTimeType(typeStr), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun evaluateUnaryAndCheck(name: String, type: CompileTimeType<*>, value: Any, tracer: () -> Unit = {}): Any? {
|
||||||
|
val functions = unaryOperations[UnaryOperationKey(type, name)] ?: return null
|
||||||
|
|
||||||
|
val (function, check) = functions
|
||||||
|
val result = function(value)
|
||||||
|
if (check == emptyUnaryFun) {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
assert(isIntegerType(value)) { "Only integer constants should be checked for overflow" }
|
||||||
|
assert(name == "minus" || name == "unaryMinus") { "Only negation should be checked for overflow" }
|
||||||
|
|
||||||
|
if (value == result && !isZero(value)) {
|
||||||
|
tracer()
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun evaluateBinary(
|
||||||
|
name: String,
|
||||||
|
receiverTypeStr: String,
|
||||||
|
receiverValue: Any,
|
||||||
|
parameterTypeStr: String,
|
||||||
|
parameterValue: Any
|
||||||
|
): Any? {
|
||||||
|
val receiverType = typeStrToCompileTimeType(receiverTypeStr)
|
||||||
|
val parameterType = typeStrToCompileTimeType(parameterTypeStr)
|
||||||
|
|
||||||
|
return evaluateBinaryAndCheck(name, receiverType, receiverValue, parameterType, parameterValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun evaluateBinaryAndCheck(
|
||||||
|
name: String,
|
||||||
|
receiverType: CompileTimeType<*>,
|
||||||
|
receiverValue: Any,
|
||||||
|
parameterType: CompileTimeType<*>,
|
||||||
|
parameterValue: Any,
|
||||||
|
tracer: () -> Unit = {}
|
||||||
|
): Any? {
|
||||||
|
val functions = binaryOperations[BinaryOperationKey(receiverType, parameterType, name)] ?: return null
|
||||||
|
|
||||||
|
val (function, checker) = functions
|
||||||
|
val actualResult = try {
|
||||||
|
function(receiverValue, parameterValue)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
if (checker == emptyBinaryFun) {
|
||||||
|
return actualResult
|
||||||
|
}
|
||||||
|
assert(isIntegerType(receiverValue) && isIntegerType(parameterValue)) { "Only integer constants should be checked for overflow" }
|
||||||
|
|
||||||
|
fun toBigInteger(value: Any?) = BigInteger.valueOf((value as Number).toLong())
|
||||||
|
|
||||||
|
val refinedChecker = if (name == OperatorNameConventions.MOD.asString()) {
|
||||||
|
binaryOperations[BinaryOperationKey(receiverType, parameterType, OperatorNameConventions.REM.asString())]?.second ?: return null
|
||||||
|
} else {
|
||||||
|
checker
|
||||||
|
}
|
||||||
|
|
||||||
|
val resultInBigIntegers = refinedChecker(toBigInteger(receiverValue), toBigInteger(parameterValue))
|
||||||
|
|
||||||
|
if (toBigInteger(actualResult) != resultInBigIntegers) {
|
||||||
|
tracer()
|
||||||
|
}
|
||||||
|
return actualResult
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user