ConstantExpressionEvaluator: make evaluate*naryAndCheck public

This commit is contained in:
Ting-Yuan Huang
2019-02-27 14:28:21 -08:00
committed by max-kammerer
parent f352a4dcd4
commit 7e4d33be24
@@ -674,20 +674,9 @@ private class ConstantExpressionEvaluatorVisitor(
ConstantExpressionEvaluator.getConstant(expression, trace.bindingContext)?.isPure ?: false
private fun evaluateUnaryAndCheck(receiver: OperationArgument, name: String, callExpression: KtExpression): Any? {
val functions = unaryOperations[UnaryOperationKey(receiver.ctcType, name)] ?: return null
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)) {
return evaluateUnaryAndCheck(name, receiver.ctcType, receiver.value) {
trace.report(Errors.INTEGER_OVERFLOW.on(callExpression.getStrictParentOfType<KtExpression>() ?: callExpression))
}
return result
}
private fun evaluateBinaryAndCheck(
@@ -696,50 +685,15 @@ private class ConstantExpressionEvaluatorVisitor(
name: String,
callExpression: KtExpression
): Any? {
val functions = getBinaryOperation(receiver, parameter, name) ?: return null
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) {
return evaluateBinaryAndCheck(name, receiver.ctcType, receiver.value, parameter.ctcType, parameter.value) {
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 {
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<*>? {
val leftExpression = expression.baseExpression ?: return null
@@ -1168,3 +1122,96 @@ fun CompileTimeConstant<*>.isStandaloneOnlyConstant(): Boolean {
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
}