Refactor compile constants to reduce boolean parameter hell

This commit is contained in:
Pavel V. Talanov
2015-07-03 16:04:41 +03:00
parent f97767e159
commit b0a4520710
25 changed files with 195 additions and 180 deletions
@@ -37,10 +37,13 @@ public class JavaPropertyInitializerEvaluatorImpl implements JavaPropertyInitial
if (evaluatedExpression != null) { if (evaluatedExpression != null) {
return ConstantsPackage.createCompileTimeConstant( return ConstantsPackage.createCompileTimeConstant(
evaluatedExpression, evaluatedExpression,
ConstantExpressionEvaluator.isPropertyCompileTimeConstant(descriptor), new CompileTimeConstant.Parameters.Impl(
false, ConstantExpressionEvaluator.isPropertyCompileTimeConstant(descriptor),
true, false,
descriptor.getType()); true
),
descriptor.getType()
);
} }
return null; return null;
} }
@@ -96,9 +96,9 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
return createStringConstant(this@ConstantExpressionEvaluator.evaluate(expression, KotlinBuiltIns.getInstance().getStringType())) return createStringConstant(this@ConstantExpressionEvaluator.evaluate(expression, KotlinBuiltIns.getInstance().getStringType()))
} }
override fun visitLiteralStringTemplateEntry(entry: JetLiteralStringTemplateEntry, data: Nothing?) = StringValue(entry.getText(), true, false) override fun visitLiteralStringTemplateEntry(entry: JetLiteralStringTemplateEntry, data: Nothing?) = StringValue(entry.getText(), CompileTimeConstant.Parameters.Impl(true, false, false))
override fun visitEscapeStringTemplateEntry(entry: JetEscapeStringTemplateEntry, data: Nothing?) = StringValue(entry.getUnescapedValue(), true, false) override fun visitEscapeStringTemplateEntry(entry: JetEscapeStringTemplateEntry, data: Nothing?) = StringValue(entry.getUnescapedValue(), CompileTimeConstant.Parameters.Impl(true, false, false))
} }
override fun visitConstantExpression(expression: JetConstantExpression, expectedType: JetType?): CompileTimeConstant<*>? { override fun visitConstantExpression(expression: JetConstantExpression, expectedType: JetType?): CompileTimeConstant<*>? {
@@ -118,7 +118,7 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
if (result == null) return null if (result == null) return null
fun isLongWithSuffix() = nodeElementType == JetNodeTypes.INTEGER_CONSTANT && hasLongSuffix(text) fun isLongWithSuffix() = nodeElementType == JetNodeTypes.INTEGER_CONSTANT && hasLongSuffix(text)
return createCompileTimeConstant(result, expectedType, !isLongWithSuffix(), true, false) return createConstant(result, expectedType, CompileTimeConstant.Parameters.Impl(true, !isLongWithSuffix(), false))
} }
override fun visitParenthesizedExpression(expression: JetParenthesizedExpression, expectedType: JetType?): CompileTimeConstant<*>? { override fun visitParenthesizedExpression(expression: JetParenthesizedExpression, expectedType: JetType?): CompileTimeConstant<*>? {
@@ -155,9 +155,15 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
} }
} }
return if (!interupted) return if (!interupted)
createCompileTimeConstant(sb.toString(), expectedType, createConstant(
isPure = true, canBeUsedInAnnotation = canBeUsedInAnnotation, sb.toString(),
usesVariableAsConstant = usesVariableAsConstant) expectedType,
CompileTimeConstant.Parameters.Impl(
isPure = true,
canBeUsedInAnnotation = canBeUsedInAnnotation,
usesVariableAsConstant = usesVariableAsConstant
)
)
else null else null
} }
@@ -190,7 +196,7 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
else -> throw IllegalArgumentException("Unknown boolean operation token ${operationToken}") else -> throw IllegalArgumentException("Unknown boolean operation token ${operationToken}")
} }
val usesVariableAsConstant = leftConstant.usesVariableAsConstant() || rightConstant.usesVariableAsConstant() val usesVariableAsConstant = leftConstant.usesVariableAsConstant() || rightConstant.usesVariableAsConstant()
return createCompileTimeConstant(result, expectedType, true, true, usesVariableAsConstant) return createConstant(result, expectedType, CompileTimeConstant.Parameters.Impl(true, true, usesVariableAsConstant))
} }
else { else {
return evaluateCall(expression.getOperationReference(), leftExpression, expectedType) return evaluateCall(expression.getOperationReference(), leftExpression, expectedType)
@@ -214,11 +220,14 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
val canBeUsedInAnnotation = canBeUsedInAnnotation(argumentForReceiver.expression) val canBeUsedInAnnotation = canBeUsedInAnnotation(argumentForReceiver.expression)
val usesVariableAsConstant = usesVariableAsConstant(argumentForReceiver.expression) val usesVariableAsConstant = usesVariableAsConstant(argumentForReceiver.expression)
val isNumberConversionMethod = resultingDescriptorName in OperatorConventions.NUMBER_CONVERSIONS val isNumberConversionMethod = resultingDescriptorName in OperatorConventions.NUMBER_CONVERSIONS
return createCompileTimeConstant(result, return createConstant(
expectedType, result,
!isNumberConversionMethod && isArgumentPure, expectedType,
canBeUsedInAnnotation, CompileTimeConstant.Parameters.Impl(
usesVariableAsConstant) canBeUsedInAnnotation,
!isNumberConversionMethod && isArgumentPure,
usesVariableAsConstant)
)
} }
else if (argumentsEntrySet.size() == 1) { else if (argumentsEntrySet.size() == 1) {
val (parameter, argument) = argumentsEntrySet.first() val (parameter, argument) = argumentsEntrySet.first()
@@ -237,12 +246,12 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
val areArgumentsPure = isPureConstant(argumentForReceiver.expression) && isPureConstant(argumentForParameter.expression) val areArgumentsPure = isPureConstant(argumentForReceiver.expression) && isPureConstant(argumentForParameter.expression)
val canBeUsedInAnnotation = canBeUsedInAnnotation(argumentForReceiver.expression) && canBeUsedInAnnotation(argumentForParameter.expression) val canBeUsedInAnnotation = canBeUsedInAnnotation(argumentForReceiver.expression) && canBeUsedInAnnotation(argumentForParameter.expression)
val usesVariableAsConstant = usesVariableAsConstant(argumentForReceiver.expression) || usesVariableAsConstant(argumentForParameter.expression) val usesVariableAsConstant = usesVariableAsConstant(argumentForReceiver.expression) || usesVariableAsConstant(argumentForParameter.expression)
val c = EvaluatorContext(canBeUsedInAnnotation, areArgumentsPure, usesVariableAsConstant) val parameters = CompileTimeConstant.Parameters.Impl(canBeUsedInAnnotation, areArgumentsPure, usesVariableAsConstant)
return when(resultingDescriptorName) { return when(resultingDescriptorName) {
OperatorConventions.COMPARE_TO -> createCompileTimeConstantForCompareTo(result, callExpression, c) OperatorConventions.COMPARE_TO -> createCompileTimeConstantForCompareTo(result, callExpression, parameters)
OperatorConventions.EQUALS -> createCompileTimeConstantForEquals(result, callExpression, c) OperatorConventions.EQUALS -> createCompileTimeConstantForEquals(result, callExpression, parameters)
else -> { else -> {
createCompileTimeConstant(result, expectedType, areArgumentsPure, canBeUsedInAnnotation, usesVariableAsConstant) createConstant(result, expectedType, CompileTimeConstant.Parameters.Impl(areArgumentsPure, canBeUsedInAnnotation, usesVariableAsConstant))
} }
} }
} }
@@ -327,7 +336,7 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
override fun visitSimpleNameExpression(expression: JetSimpleNameExpression, expectedType: JetType?): CompileTimeConstant<*>? { override fun visitSimpleNameExpression(expression: JetSimpleNameExpression, expectedType: JetType?): CompileTimeConstant<*>? {
val enumDescriptor = trace.getBindingContext().get(BindingContext.REFERENCE_TARGET, expression); val enumDescriptor = trace.getBindingContext().get(BindingContext.REFERENCE_TARGET, expression);
if (enumDescriptor != null && DescriptorUtils.isEnumEntry(enumDescriptor)) { if (enumDescriptor != null && DescriptorUtils.isEnumEntry(enumDescriptor)) {
return EnumValue(enumDescriptor as ClassDescriptor, false); return EnumValue(enumDescriptor as ClassDescriptor)
} }
val resolvedCall = expression.getResolvedCall(trace.getBindingContext()) val resolvedCall = expression.getResolvedCall(trace.getBindingContext())
@@ -342,9 +351,15 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
compileTimeConstant.getValue(expectedType ?: TypeUtils.NO_EXPECTED_TYPE) compileTimeConstant.getValue(expectedType ?: TypeUtils.NO_EXPECTED_TYPE)
else else
compileTimeConstant.value compileTimeConstant.value
return createCompileTimeConstant(value, expectedType, isPure = false, return createConstant(
canBeUsedInAnnotation = isPropertyCompileTimeConstant(callableDescriptor), value,
usesVariableAsConstant = true) expectedType,
CompileTimeConstant.Parameters.Impl(
canBeUsedInAnnotation = isPropertyCompileTimeConstant(callableDescriptor),
isPure = false,
usesVariableAsConstant = true
)
)
} }
} }
return null return null
@@ -468,18 +483,17 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
return OperationArgument(evaluationResult, compileTimeType, expression) return OperationArgument(evaluationResult, compileTimeType, expression)
} }
fun createCompileTimeConstant(value: Any?, fun createConstant(
expectedType: JetType?, value: Any?,
isPure: Boolean = true, expectedType: JetType?,
canBeUsedInAnnotation: Boolean = true, parameters: CompileTimeConstant.Parameters
usesVariableAsConstant: Boolean = false): CompileTimeConstant<*>? { ): CompileTimeConstant<*>? {
val c = EvaluatorContext(canBeUsedInAnnotation, isPure, usesVariableAsConstant) return createCompileTimeConstant(value, parameters, if (parameters.isPure) expectedType ?: TypeUtils.NO_EXPECTED_TYPE else null)
return createCompileTimeConstant(value, c, if (isPure) expectedType ?: TypeUtils.NO_EXPECTED_TYPE else null)
} }
} }
public fun IntegerValueTypeConstant.createCompileTimeConstantWithType(expectedType: JetType): CompileTimeConstant<*>? public fun IntegerValueTypeConstant.createCompileTimeConstantWithType(expectedType: JetType): CompileTimeConstant<*>?
= createCompileTimeConstant(this.getValue(expectedType), EvaluatorContext(this.canBeUsedInAnnotations(), true)) = createCompileTimeConstant(this.getValue(expectedType), CompileTimeConstant.Parameters.Impl(this.canBeUsedInAnnotations(), true, false))
private fun hasLongSuffix(text: String) = text.endsWith('l') || text.endsWith('L') private fun hasLongSuffix(text: String) = text.endsWith('l') || text.endsWith('L')
@@ -536,16 +550,16 @@ private fun parseBoolean(text: String): Boolean {
} }
private fun createCompileTimeConstantForEquals(result: Any?, operationReference: JetExpression, c: EvaluatorContext): CompileTimeConstant<*>? { private fun createCompileTimeConstantForEquals(result: Any?, operationReference: JetExpression, parameters: CompileTimeConstant.Parameters): CompileTimeConstant<*>? {
if (result is Boolean) { if (result is Boolean) {
assert(operationReference is JetSimpleNameExpression, "This method should be called only for equals operations") assert(operationReference is JetSimpleNameExpression, "This method should be called only for equals operations")
val operationToken = (operationReference as JetSimpleNameExpression).getReferencedNameElementType() val operationToken = (operationReference as JetSimpleNameExpression).getReferencedNameElementType()
return when (operationToken) { return when (operationToken) {
JetTokens.EQEQ -> BooleanValue(result, c.canBeUsedInAnnotation, c.usesVariableAsConstant) JetTokens.EQEQ -> BooleanValue(result, parameters)
JetTokens.EXCLEQ -> BooleanValue(!result, c.canBeUsedInAnnotation, c.usesVariableAsConstant) JetTokens.EXCLEQ -> BooleanValue(!result, parameters)
JetTokens.IDENTIFIER -> { JetTokens.IDENTIFIER -> {
assert (operationReference.getReferencedNameAsName() == OperatorConventions.EQUALS, "This method should be called only for equals operations") assert (operationReference.getReferencedNameAsName() == OperatorConventions.EQUALS, "This method should be called only for equals operations")
return BooleanValue(result, c.canBeUsedInAnnotation, c.usesVariableAsConstant) return BooleanValue(result, parameters)
} }
else -> throw IllegalStateException("Unknown equals operation token: $operationToken ${operationReference.getText()}") else -> throw IllegalStateException("Unknown equals operation token: $operationToken ${operationReference.getText()}")
} }
@@ -553,18 +567,18 @@ private fun createCompileTimeConstantForEquals(result: Any?, operationReference:
return null return null
} }
private fun createCompileTimeConstantForCompareTo(result: Any?, operationReference: JetExpression, c: EvaluatorContext): CompileTimeConstant<*>? { private fun createCompileTimeConstantForCompareTo(result: Any?, operationReference: JetExpression, parameters: CompileTimeConstant.Parameters): CompileTimeConstant<*>? {
if (result is Int) { if (result is Int) {
assert(operationReference is JetSimpleNameExpression, "This method should be called only for compareTo operations") assert(operationReference is JetSimpleNameExpression, "This method should be called only for compareTo operations")
val operationToken = (operationReference as JetSimpleNameExpression).getReferencedNameElementType() val operationToken = (operationReference as JetSimpleNameExpression).getReferencedNameElementType()
return when (operationToken) { return when (operationToken) {
JetTokens.LT -> BooleanValue(result < 0, c.canBeUsedInAnnotation, c.usesVariableAsConstant) JetTokens.LT -> BooleanValue(result < 0, parameters)
JetTokens.LTEQ -> BooleanValue(result <= 0, c.canBeUsedInAnnotation, c.usesVariableAsConstant) JetTokens.LTEQ -> BooleanValue(result <= 0, parameters)
JetTokens.GT -> BooleanValue(result > 0, c.canBeUsedInAnnotation, c.usesVariableAsConstant) JetTokens.GT -> BooleanValue(result > 0, parameters)
JetTokens.GTEQ -> BooleanValue(result >= 0, c.canBeUsedInAnnotation, c.usesVariableAsConstant) JetTokens.GTEQ -> BooleanValue(result >= 0, parameters)
JetTokens.IDENTIFIER -> { JetTokens.IDENTIFIER -> {
assert (operationReference.getReferencedNameAsName() == OperatorConventions.COMPARE_TO, "This method should be called only for compareTo operations") assert (operationReference.getReferencedNameAsName() == OperatorConventions.COMPARE_TO, "This method should be called only for compareTo operations")
return IntValue(result, c.canBeUsedInAnnotation, c.isPure, c.usesVariableAsConstant) return IntValue(result, parameters)
} }
else -> throw IllegalStateException("Unknown compareTo operation token: $operationToken") else -> throw IllegalStateException("Unknown compareTo operation token: $operationToken")
} }
@@ -574,21 +588,17 @@ private fun createCompileTimeConstantForCompareTo(result: Any?, operationReferen
private fun createStringConstant(value: CompileTimeConstant<*>?): StringValue? { private fun createStringConstant(value: CompileTimeConstant<*>?): StringValue? {
return when (value) { return when (value) {
is IntegerValueTypeConstant -> StringValue(value.getValue(TypeUtils.NO_EXPECTED_TYPE).toString(), value.canBeUsedInAnnotations(), value.usesVariableAsConstant()) is IntegerValueTypeConstant -> StringValue(value.getValue(TypeUtils.NO_EXPECTED_TYPE).toString(), value.parameters)
is StringValue -> value is StringValue -> value
is IntValue, is ByteValue, is ShortValue, is LongValue, is IntValue, is ByteValue, is ShortValue, is LongValue,
is CharValue, is CharValue,
is DoubleValue, is FloatValue, is DoubleValue, is FloatValue,
is BooleanValue, is BooleanValue,
is NullValue -> StringValue("${value.value}", value.canBeUsedInAnnotations(), value.usesVariableAsConstant()) is NullValue -> StringValue("${value.value}", value.parameters)
else -> null else -> null
} }
} }
private fun createCompileTimeConstant(value: Any?, c: EvaluatorContext, expectedType: JetType? = null): CompileTimeConstant<*>? {
return createCompileTimeConstant(value, c.canBeUsedInAnnotation, c.isPure, c.usesVariableAsConstant, expectedType)
}
fun isIntegerType(value: Any?) = value is Byte || value is Short || value is Int || value is Long fun isIntegerType(value: Any?) = value is Byte || value is Short || value is Int || value is Long
private fun getReceiverExpressionType(resolvedCall: ResolvedCall<*>): JetType? { private fun getReceiverExpressionType(resolvedCall: ResolvedCall<*>): JetType? {
@@ -618,8 +628,6 @@ private fun getCompileTimeType(c: JetType): CompileTimeType<out Any>? {
} }
} }
private class EvaluatorContext(val canBeUsedInAnnotation: Boolean, val isPure: Boolean, val usesVariableAsConstant: Boolean = false)
private class CompileTimeType<T> private class CompileTimeType<T>
private val BYTE = CompileTimeType<Byte>() private val BYTE = CompileTimeType<Byte>()
@@ -121,11 +121,12 @@ public object AnnotationSerializer {
// TODO: IntegerValueTypeConstant should not occur in annotation arguments // TODO: IntegerValueTypeConstant should not occur in annotation arguments
val number = constant.getValue(type) val number = constant.getValue(type)
val specificConstant = with(KotlinBuiltIns.getInstance()) { val specificConstant = with(KotlinBuiltIns.getInstance()) {
val parameters = CompileTimeConstant.Parameters.ThrowException
when (type) { when (type) {
getLongType() -> LongValue(number.toLong(), true, true, true) getLongType() -> LongValue(number.toLong(), parameters)
getIntType() -> IntValue(number.toInt(), true, true, true) getIntType() -> IntValue(number.toInt(), parameters)
getShortType() -> ShortValue(number.toShort(), true, true, true) getShortType() -> ShortValue(number.toShort(), parameters)
getByteType() -> ByteValue(number.toByte(), true, true, true) getByteType() -> ByteValue(number.toByte(), parameters)
else -> throw IllegalStateException("Integer constant $constant has non-integer type $type") else -> throw IllegalStateException("Integer constant $constant has non-integer type $type")
} }
} }
@@ -100,7 +100,7 @@ class LazyJavaAnnotationDescriptor(
private fun resolveAnnotationArgument(argument: JavaAnnotationArgument?): CompileTimeConstant<*>? { private fun resolveAnnotationArgument(argument: JavaAnnotationArgument?): CompileTimeConstant<*>? {
return when (argument) { return when (argument) {
is JavaLiteralAnnotationArgument -> createCompileTimeConstant(argument.value, true, false, false, null) is JavaLiteralAnnotationArgument -> createCompileTimeConstant(argument.value, CompileTimeConstant.Parameters.Impl(true, false, false))
is JavaEnumValueAnnotationArgument -> resolveFromEnumValue(argument.resolve()) is JavaEnumValueAnnotationArgument -> resolveFromEnumValue(argument.resolve())
is JavaArrayAnnotationArgument -> resolveFromArray(argument.name ?: DEFAULT_ANNOTATION_MEMBER_NAME, argument.getElements()) is JavaArrayAnnotationArgument -> resolveFromArray(argument.name ?: DEFAULT_ANNOTATION_MEMBER_NAME, argument.getElements())
is JavaAnnotationAsAnnotationArgument -> resolveFromAnnotation(argument.getAnnotation()) is JavaAnnotationAsAnnotationArgument -> resolveFromAnnotation(argument.getAnnotation())
@@ -125,7 +125,7 @@ class LazyJavaAnnotationDescriptor(
val values = elements.map { val values = elements.map {
argument -> resolveAnnotationArgument(argument) ?: NullValue argument -> resolveAnnotationArgument(argument) ?: NullValue
} }
return ArrayValue(values, valueParameter.getType(), values.any { it.usesVariableAsConstant() }) return ArrayValue(values, valueParameter.getType(), CompileTimeConstant.Parameters.Impl(true, false, values.any { it.usesVariableAsConstant() }))
} }
private fun resolveFromEnumValue(element: JavaField?): CompileTimeConstant<*>? { private fun resolveFromEnumValue(element: JavaField?): CompileTimeConstant<*>? {
@@ -140,7 +140,7 @@ class LazyJavaAnnotationDescriptor(
val classifier = enumClass.getUnsubstitutedInnerClassesScope().getClassifier(element.getName()) val classifier = enumClass.getUnsubstitutedInnerClassesScope().getClassifier(element.getName())
if (classifier !is ClassDescriptor) return null if (classifier !is ClassDescriptor) return null
return EnumValue(classifier, false) return EnumValue(classifier)
} }
private fun resolveFromJavaClassObjectType(javaType: JavaType): CompileTimeConstant<*>? { private fun resolveFromJavaClassObjectType(javaType: JavaType): CompileTimeConstant<*>? {
@@ -66,8 +66,7 @@ public class BinaryClassAnnotationAndConstantLoaderImpl(
} }
val compileTimeConstant = createCompileTimeConstant( val compileTimeConstant = createCompileTimeConstant(
normalizedValue, canBeUsedInAnnotation = true, isPureIntConstant = true, normalizedValue, CompileTimeConstant.Parameters.ThrowException
usesVariableAsConstant = true, expectedType = null
) )
return compileTimeConstant return compileTimeConstant
} }
@@ -107,7 +106,7 @@ public class BinaryClassAnnotationAndConstantLoaderImpl(
val parameter = DescriptorResolverUtils.getAnnotationParameterByName(name, annotationClass) val parameter = DescriptorResolverUtils.getAnnotationParameterByName(name, annotationClass)
if (parameter != null) { if (parameter != null) {
elements.trimToSize() elements.trimToSize()
arguments[parameter] = ArrayValue(elements, parameter.getType(), false) arguments[parameter] = ArrayValue(elements, parameter.getType(), CompileTimeConstant.Parameters.ThrowException)
} }
} }
} }
@@ -130,7 +129,7 @@ public class BinaryClassAnnotationAndConstantLoaderImpl(
if (enumClass.getKind() == ClassKind.ENUM_CLASS) { if (enumClass.getKind() == ClassKind.ENUM_CLASS) {
val classifier = enumClass.getUnsubstitutedInnerClassesScope().getClassifier(name) val classifier = enumClass.getUnsubstitutedInnerClassesScope().getClassifier(name)
if (classifier is ClassDescriptor) { if (classifier is ClassDescriptor) {
return EnumValue(classifier, false) return EnumValue(classifier)
} }
} }
return ErrorValue.create("Unresolved enum entry: $enumClassId.$name") return ErrorValue.create("Unresolved enum entry: $enumClassId.$name")
@@ -141,9 +140,10 @@ public class BinaryClassAnnotationAndConstantLoaderImpl(
} }
private fun createConstant(name: Name?, value: Any?): CompileTimeConstant<*> { private fun createConstant(name: Name?, value: Any?): CompileTimeConstant<*> {
return createCompileTimeConstant(value, canBeUsedInAnnotation = true, isPureIntConstant = false, return createCompileTimeConstant(
usesVariableAsConstant = false, expectedType = null) value,
?: ErrorValue.create("Unsupported annotation argument: $name") CompileTimeConstant.Parameters.ThrowException
) ?: ErrorValue.create("Unsupported annotation argument: $name")
} }
private fun setArgumentValueByName(name: Name, argumentValue: CompileTimeConstant<*>) { private fun setArgumentValueByName(name: Name, argumentValue: CompileTimeConstant<*>) {
@@ -21,7 +21,8 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationArgumentVisitor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.types.JetType import org.jetbrains.kotlin.types.JetType
public class AnnotationValue(value: AnnotationDescriptor) : CompileTimeConstant<AnnotationDescriptor>(value, true, false, false) { public class AnnotationValue(value: AnnotationDescriptor) :
CompileTimeConstant<AnnotationDescriptor>(value, CompileTimeConstant.Parameters.Impl(true, false, false)) {
override fun getType(kotlinBuiltIns: KotlinBuiltIns) = value.getType() override fun getType(kotlinBuiltIns: KotlinBuiltIns) = value.getType()
override fun <R, D> accept(visitor: AnnotationArgumentVisitor<R, D>, data: D) = visitor.visitAnnotationValue(this, data) override fun <R, D> accept(visitor: AnnotationArgumentVisitor<R, D>, data: D) = visitor.visitAnnotationValue(this, data)
@@ -24,8 +24,17 @@ import java.util.*
public class ArrayValue( public class ArrayValue(
value: List<CompileTimeConstant<*>>, value: List<CompileTimeConstant<*>>,
private val type: JetType, private val type: JetType,
usesVariableAsConstant: Boolean parameters: CompileTimeConstant.Parameters
) : CompileTimeConstant<List<CompileTimeConstant<*>>>(value, true, false, usesVariableAsConstant) { ) : CompileTimeConstant<List<CompileTimeConstant<*>>>(value, parameters) {
public constructor(
value: List<CompileTimeConstant<*>>,
type: JetType,
usesVariableAsConstant: Boolean
) : this(value, type, CompileTimeConstant.Parameters.Impl(true, false, usesVariableAsConstant))
override fun canBeUsedInAnnotations() = true
override fun isPure() = false
init { init {
assert(KotlinBuiltIns.isArray(type) || KotlinBuiltIns.isPrimitiveArray(type)) { "Type should be an array, but was " + type + ": " + value } assert(KotlinBuiltIns.isArray(type) || KotlinBuiltIns.isPrimitiveArray(type)) { "Type should be an array, but was " + type + ": " + value }
@@ -22,9 +22,10 @@ import org.jetbrains.kotlin.types.JetType
public class BooleanValue( public class BooleanValue(
value: Boolean, value: Boolean,
canBeUseInAnnotation: Boolean, parameters: CompileTimeConstant.Parameters
usesVariableAsConstant: Boolean ) : CompileTimeConstant<Boolean>(value, parameters) {
) : CompileTimeConstant<Boolean>(value, canBeUseInAnnotation, false, usesVariableAsConstant) { override fun isPure(): Boolean = false
override fun getType(kotlinBuiltIns: KotlinBuiltIns) = kotlinBuiltIns.getBooleanType() override fun getType(kotlinBuiltIns: KotlinBuiltIns) = kotlinBuiltIns.getBooleanType()
override fun <R, D> accept(visitor: AnnotationArgumentVisitor<R, D>, data: D) = visitor.visitBooleanValue(this, data) override fun <R, D> accept(visitor: AnnotationArgumentVisitor<R, D>, data: D) = visitor.visitBooleanValue(this, data)
@@ -22,10 +22,8 @@ import org.jetbrains.kotlin.types.JetType
public class ByteValue( public class ByteValue(
value: Byte, value: Byte,
canBeUsedInAnnotations: Boolean, parameters: CompileTimeConstant.Parameters
pure: Boolean, ) : IntegerValueConstant<Byte>(value, parameters) {
usesVaraiableAsConstant: Boolean
) : IntegerValueConstant<Byte>(value, canBeUsedInAnnotations, pure, usesVaraiableAsConstant) {
override fun getType(kotlinBuiltIns: KotlinBuiltIns) = kotlinBuiltIns.getByteType() override fun getType(kotlinBuiltIns: KotlinBuiltIns) = kotlinBuiltIns.getByteType()
@@ -22,10 +22,8 @@ import org.jetbrains.kotlin.types.JetType
public class CharValue( public class CharValue(
value: Char, value: Char,
canBeUsedInAnnotations: Boolean, parameters: CompileTimeConstant.Parameters
pure: Boolean, ) : IntegerValueConstant<Char>(value, parameters) {
usesVariableAsConstant: Boolean
) : IntegerValueConstant<Char>(value, canBeUsedInAnnotations, pure, usesVariableAsConstant) {
override fun getType(kotlinBuiltIns: KotlinBuiltIns) = kotlinBuiltIns.getCharType() override fun getType(kotlinBuiltIns: KotlinBuiltIns) = kotlinBuiltIns.getCharType()
@@ -20,24 +20,15 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.annotations.AnnotationArgumentVisitor import org.jetbrains.kotlin.descriptors.annotations.AnnotationArgumentVisitor
import org.jetbrains.kotlin.types.JetType import org.jetbrains.kotlin.types.JetType
public abstract class CompileTimeConstant<T> protected constructor(public open val value: T, canBeUsedInAnnotations: Boolean, isPure: Boolean, usesVariableAsConstant: Boolean) { public abstract class CompileTimeConstant<T> protected constructor(
private val flags: Int public open val value: T,
public val parameters: CompileTimeConstant.Parameters
) {
public open fun canBeUsedInAnnotations(): Boolean = parameters.canBeUsedInAnnotation
init { public open fun isPure(): Boolean = parameters.isPure
flags = (if (isPure) IS_PURE_MASK else 0) or (if (canBeUsedInAnnotations) CAN_BE_USED_IN_ANNOTATIONS_MASK else 0) or (if (usesVariableAsConstant) USES_VARIABLE_AS_CONSTANT_MASK else 0)
}
public fun canBeUsedInAnnotations(): Boolean { public open fun usesVariableAsConstant(): Boolean = parameters.usesVariableAsConstant
return (flags and CAN_BE_USED_IN_ANNOTATIONS_MASK) != 0
}
public fun isPure(): Boolean {
return (flags and IS_PURE_MASK) != 0
}
public fun usesVariableAsConstant(): Boolean {
return (flags and USES_VARIABLE_AS_CONSTANT_MASK) != 0
}
public abstract fun getType(kotlinBuiltIns: KotlinBuiltIns): JetType public abstract fun getType(kotlinBuiltIns: KotlinBuiltIns): JetType
@@ -45,16 +36,24 @@ public abstract class CompileTimeConstant<T> protected constructor(public open v
override fun toString() = value.toString() override fun toString() = value.toString()
companion object { public interface Parameters {
public open val canBeUsedInAnnotation: Boolean
public open val isPure: Boolean
public open val usesVariableAsConstant: Boolean
/* public class Impl(
* if is pure is false then constant type cannot be changed override val canBeUsedInAnnotation: Boolean,
* ex1. val a: Long = 1.toInt() (TYPE_MISMATCH error, 1.toInt() isn't pure) override val isPure: Boolean,
* ex2. val b: Int = a (TYPE_MISMATCH error, a isn't pure) override val usesVariableAsConstant: Boolean
* ) : Parameters
*/
private val IS_PURE_MASK = 1 public object ThrowException : Parameters {
private val CAN_BE_USED_IN_ANNOTATIONS_MASK = 1 shl 1 override val canBeUsedInAnnotation: Boolean
private val USES_VARIABLE_AS_CONSTANT_MASK = 1 shl 2 get() = error("Should not be called")
override val isPure: Boolean
get() = error("Should not be called")
override val usesVariableAsConstant: Boolean
get() = error("Should not be called")
}
} }
} }
@@ -22,27 +22,25 @@ import org.jetbrains.kotlin.types.TypeUtils
public fun createCompileTimeConstant( public fun createCompileTimeConstant(
value: Any?, value: Any?,
canBeUsedInAnnotation: Boolean, parameters: CompileTimeConstant.Parameters,
isPureIntConstant: Boolean,
usesVariableAsConstant: Boolean = false,
expectedType: JetType? = null expectedType: JetType? = null
): CompileTimeConstant<*>? { ): CompileTimeConstant<*>? {
// TODO: primitive arrays // TODO: primitive arrays
if (expectedType == null) { if (expectedType == null) {
when(value) { when(value) {
is Byte -> return ByteValue(value, canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant) is Byte -> return ByteValue(value, parameters)
is Short -> return ShortValue(value, canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant) is Short -> return ShortValue(value, parameters)
is Int -> return IntValue(value, canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant) is Int -> return IntValue(value, parameters)
is Long -> return LongValue(value, canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant) is Long -> return LongValue(value, parameters)
} }
} }
return when(value) { return when(value) {
is Byte, is Short, is Int, is Long -> getIntegerValue((value as Number).toLong(), canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant, expectedType) is Byte, is Short, is Int, is Long -> getIntegerValue((value as Number).toLong(), parameters, expectedType)
is Char -> CharValue(value, canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant) is Char -> CharValue(value, parameters)
is Float -> FloatValue(value, canBeUsedInAnnotation, usesVariableAsConstant) is Float -> FloatValue(value, parameters)
is Double -> DoubleValue(value, canBeUsedInAnnotation, usesVariableAsConstant) is Double -> DoubleValue(value, parameters)
is Boolean -> BooleanValue(value, canBeUsedInAnnotation, usesVariableAsConstant) is Boolean -> BooleanValue(value, parameters)
is String -> StringValue(value, canBeUsedInAnnotation, usesVariableAsConstant) is String -> StringValue(value, parameters)
null -> NullValue null -> NullValue
else -> null else -> null
} }
@@ -50,32 +48,37 @@ public fun createCompileTimeConstant(
private fun getIntegerValue( private fun getIntegerValue(
value: Long, value: Long,
canBeUsedInAnnotation: Boolean, parameters: CompileTimeConstant.Parameters,
isPureIntConstant: Boolean,
usesVariableAsConstant: Boolean,
expectedType: JetType expectedType: JetType
): CompileTimeConstant<*>? { ): CompileTimeConstant<*>? {
fun defaultIntegerValue(value: Long) = when (value) { fun defaultIntegerValue(value: Long) = when (value) {
value.toInt().toLong() -> IntValue(value.toInt(), canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant) value.toInt().toLong() -> IntValue(value.toInt(), parameters)
else -> LongValue(value, canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant) else -> LongValue(value, parameters)
} }
if (TypeUtils.noExpectedType(expectedType) || expectedType.isError()) { if (TypeUtils.noExpectedType(expectedType) || expectedType.isError()) {
return IntegerValueTypeConstant(value, canBeUsedInAnnotation, usesVariableAsConstant) return IntegerValueTypeConstant(value, parameters)
} }
val notNullExpected = TypeUtils.makeNotNullable(expectedType) val notNullExpected = TypeUtils.makeNotNullable(expectedType)
return when { return when {
KotlinBuiltIns.isLong(notNullExpected) -> LongValue(value, canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant) KotlinBuiltIns.isLong(notNullExpected) -> LongValue(value, parameters)
KotlinBuiltIns.isShort(notNullExpected) -> when (value) {
value.toShort().toLong() -> ShortValue(value.toShort(), canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant) KotlinBuiltIns.isShort(notNullExpected) ->
else -> defaultIntegerValue(value) if (value == value.toShort().toLong())
} ShortValue(value.toShort(), parameters)
KotlinBuiltIns.isByte(notNullExpected) -> when (value) { else
value.toByte().toLong() -> ByteValue(value.toByte(), canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant) defaultIntegerValue(value)
else -> defaultIntegerValue(value)
} KotlinBuiltIns.isByte(notNullExpected) ->
KotlinBuiltIns.isChar(notNullExpected) -> IntValue(value.toInt(), canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant) if (value == value.toByte().toLong())
ByteValue(value.toByte(), parameters)
else
defaultIntegerValue(value)
KotlinBuiltIns.isChar(notNullExpected) ->
IntValue(value.toInt(), parameters)
else -> defaultIntegerValue(value) else -> defaultIntegerValue(value)
} }
} }
@@ -22,9 +22,10 @@ import org.jetbrains.kotlin.types.JetType
public class DoubleValue( public class DoubleValue(
value: Double, value: Double,
canBeUsedInAnnotations: Boolean, parameters: CompileTimeConstant.Parameters
usesVariableAsConstant: Boolean ) : CompileTimeConstant<Double>(value, parameters) {
) : CompileTimeConstant<Double>(value, canBeUsedInAnnotations, false, usesVariableAsConstant) {
override fun isPure() = false
override fun getType(kotlinBuiltIns: KotlinBuiltIns) = kotlinBuiltIns.getDoubleType() override fun getType(kotlinBuiltIns: KotlinBuiltIns) = kotlinBuiltIns.getDoubleType()
@@ -24,9 +24,8 @@ import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.utils.sure import org.jetbrains.kotlin.utils.sure
public class EnumValue( public class EnumValue(
value: ClassDescriptor, value: ClassDescriptor
usesVariableAsConstant: Boolean ) : CompileTimeConstant<ClassDescriptor>(value, CompileTimeConstant.Parameters.Impl(true, false, false)) {
) : CompileTimeConstant<ClassDescriptor>(value, true, false, usesVariableAsConstant) {
override fun getType(kotlinBuiltIns: KotlinBuiltIns) = getType() override fun getType(kotlinBuiltIns: KotlinBuiltIns) = getType()
@@ -21,7 +21,7 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationArgumentVisitor
import org.jetbrains.kotlin.types.ErrorUtils import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.JetType import org.jetbrains.kotlin.types.JetType
public abstract class ErrorValue : CompileTimeConstant<Unit>(Unit, true, false, false) { public abstract class ErrorValue : CompileTimeConstant<Unit>(Unit, CompileTimeConstant.Parameters.Impl(true, false, false)) {
deprecated("Should not be called, for this is not a real value, but a indication of an error") deprecated("Should not be called, for this is not a real value, but a indication of an error")
override val value: Unit override val value: Unit
@@ -22,9 +22,9 @@ import org.jetbrains.kotlin.types.JetType
public class FloatValue( public class FloatValue(
value: Float, value: Float,
canBeUsedInAnnotations: Boolean, parameters: CompileTimeConstant.Parameters
usesVariableAsConstant: Boolean ) : CompileTimeConstant<Float>(value, parameters) {
) : CompileTimeConstant<Float>(value, canBeUsedInAnnotations, false, usesVariableAsConstant) { override fun isPure() = false
override fun getType(kotlinBuiltIns: KotlinBuiltIns) = kotlinBuiltIns.getFloatType() override fun getType(kotlinBuiltIns: KotlinBuiltIns) = kotlinBuiltIns.getFloatType()
@@ -22,10 +22,8 @@ import org.jetbrains.kotlin.types.JetType
public class IntValue( public class IntValue(
value: Int, value: Int,
canBeUsedInAnnotations: Boolean, parameters: CompileTimeConstant.Parameters
pure: Boolean, ) : IntegerValueConstant<Int>(value, parameters) {
usesVariableAsConstant: Boolean
) : IntegerValueConstant<Int>(value, canBeUsedInAnnotations, pure, usesVariableAsConstant) {
override fun getType(kotlinBuiltIns: KotlinBuiltIns) = kotlinBuiltIns.getIntType() override fun getType(kotlinBuiltIns: KotlinBuiltIns) = kotlinBuiltIns.getIntType()
@@ -18,7 +18,5 @@ package org.jetbrains.kotlin.resolve.constants
public abstract class IntegerValueConstant<T> protected constructor( public abstract class IntegerValueConstant<T> protected constructor(
value: T, value: T,
canBeUsedInAnnotations: Boolean, parameters: CompileTimeConstant.Parameters
pure: Boolean, ) : CompileTimeConstant<T>(value, parameters)
usesVariableAsConstant: Boolean
) : CompileTimeConstant<T>(value, canBeUsedInAnnotations, pure, usesVariableAsConstant)
@@ -25,9 +25,10 @@ import java.util.Collections
public class IntegerValueTypeConstant( public class IntegerValueTypeConstant(
value: Number, value: Number,
canBeUsedInAnnotations: Boolean, parameters: CompileTimeConstant.Parameters
usesVariableAsConstant: Boolean ) : IntegerValueConstant<Number>(value, parameters) {
) : IntegerValueConstant<Number>(value, canBeUsedInAnnotations, true, usesVariableAsConstant) {
override fun isPure() = true
private val typeConstructor = IntegerValueTypeConstructor(value.toLong()) private val typeConstructor = IntegerValueTypeConstructor(value.toLong())
@@ -20,8 +20,8 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.annotations.AnnotationArgumentVisitor import org.jetbrains.kotlin.descriptors.annotations.AnnotationArgumentVisitor
import org.jetbrains.kotlin.types.JetType import org.jetbrains.kotlin.types.JetType
public class KClassValue(private val type: JetType) :
public class KClassValue(private val type: JetType) : CompileTimeConstant<JetType>(type, true, false, false) { CompileTimeConstant<JetType>(type, CompileTimeConstant.Parameters.Impl(true, false, false)) {
override fun getType(kotlinBuiltIns: KotlinBuiltIns) = type override fun getType(kotlinBuiltIns: KotlinBuiltIns) = type
override val value: JetType override val value: JetType
get() = type.getArguments().single().getType() get() = type.getArguments().single().getType()
@@ -22,10 +22,8 @@ import org.jetbrains.kotlin.types.JetType
public class LongValue( public class LongValue(
value: Long, value: Long,
canBeUsedInAnnotations: Boolean, parameters: CompileTimeConstant.Parameters
pure: Boolean, ) : IntegerValueConstant<Long>(value, parameters) {
usesVariableAsConstant: Boolean
) : IntegerValueConstant<Long>(value, canBeUsedInAnnotations, pure, usesVariableAsConstant) {
override fun getType(kotlinBuiltIns: KotlinBuiltIns) = kotlinBuiltIns.getLongType() override fun getType(kotlinBuiltIns: KotlinBuiltIns) = kotlinBuiltIns.getLongType()
@@ -20,7 +20,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.annotations.AnnotationArgumentVisitor import org.jetbrains.kotlin.descriptors.annotations.AnnotationArgumentVisitor
import org.jetbrains.kotlin.types.JetType import org.jetbrains.kotlin.types.JetType
public object NullValue : CompileTimeConstant<Void?>(null, false, false, false) { public object NullValue : CompileTimeConstant<Void?>(null, CompileTimeConstant.Parameters.Impl(false, false, false)) {
override fun getType(kotlinBuiltIns: KotlinBuiltIns) = kotlinBuiltIns.getNullableNothingType() override fun getType(kotlinBuiltIns: KotlinBuiltIns) = kotlinBuiltIns.getNullableNothingType()
@@ -22,10 +22,8 @@ import org.jetbrains.kotlin.types.JetType
public class ShortValue( public class ShortValue(
value: Short, value: Short,
canBeUsedInAnnotations: Boolean, parameters: CompileTimeConstant.Parameters
pure: Boolean, ) : IntegerValueConstant<Short>(value, parameters) {
usesVariableAsConstant: Boolean
) : IntegerValueConstant<Short>(value, canBeUsedInAnnotations, pure, usesVariableAsConstant) {
override fun getType(kotlinBuiltIns: KotlinBuiltIns) = kotlinBuiltIns.getShortType() override fun getType(kotlinBuiltIns: KotlinBuiltIns) = kotlinBuiltIns.getShortType()
@@ -22,9 +22,9 @@ import org.jetbrains.kotlin.types.JetType
public class StringValue( public class StringValue(
value: String, value: String,
canBeUsedInAnnotations: Boolean, parameters: CompileTimeConstant.Parameters
usesVariableAsConstant: Boolean ) : CompileTimeConstant<String>(value, parameters) {
) : CompileTimeConstant<String>(value, canBeUsedInAnnotations, false, usesVariableAsConstant) { override fun isPure() = false
override fun getType(kotlinBuiltIns: KotlinBuiltIns) = kotlinBuiltIns.getStringType() override fun getType(kotlinBuiltIns: KotlinBuiltIns) = kotlinBuiltIns.getStringType()
@@ -68,17 +68,18 @@ public class AnnotationDeserializer(private val module: ModuleDescriptor) {
value: Value, value: Value,
nameResolver: NameResolver nameResolver: NameResolver
): CompileTimeConstant<*> { ): CompileTimeConstant<*> {
val parameters = CompileTimeConstant.Parameters.ThrowException
val result = when (value.getType()) { val result = when (value.getType()) {
Type.BYTE -> ByteValue(value.getIntValue().toByte(), true, true, true) Type.BYTE -> ByteValue(value.getIntValue().toByte(), parameters)
Type.CHAR -> CharValue(value.getIntValue().toChar(), true, true, true) Type.CHAR -> CharValue(value.getIntValue().toChar(), parameters)
Type.SHORT -> ShortValue(value.getIntValue().toShort(), true, true, true) Type.SHORT -> ShortValue(value.getIntValue().toShort(), parameters)
Type.INT -> IntValue(value.getIntValue().toInt(), true, true, true) Type.INT -> IntValue(value.getIntValue().toInt(), parameters)
Type.LONG -> LongValue(value.getIntValue(), true, true, true) Type.LONG -> LongValue(value.getIntValue(), parameters)
Type.FLOAT -> FloatValue(value.getFloatValue(), true, true) Type.FLOAT -> FloatValue(value.getFloatValue(), parameters)
Type.DOUBLE -> DoubleValue(value.getDoubleValue(), true, true) Type.DOUBLE -> DoubleValue(value.getDoubleValue(), parameters)
Type.BOOLEAN -> BooleanValue(value.getIntValue() != 0L, true, true) Type.BOOLEAN -> BooleanValue(value.getIntValue() != 0L, parameters)
Type.STRING -> { Type.STRING -> {
StringValue(nameResolver.getString(value.getStringValue()), true, true) StringValue(nameResolver.getString(value.getStringValue()), parameters)
} }
Type.CLASS -> { Type.CLASS -> {
// TODO: support class literals // TODO: support class literals
@@ -114,7 +115,7 @@ public class AnnotationDeserializer(private val module: ModuleDescriptor) {
resolveValue(expectedElementType, it, nameResolver) resolveValue(expectedElementType, it, nameResolver)
}, },
actualArrayType, actualArrayType,
true parameters
) )
} }
else -> error("Unsupported annotation argument type: ${value.getType()} (expected $expectedType)") else -> error("Unsupported annotation argument type: ${value.getType()} (expected $expectedType)")
@@ -135,7 +136,7 @@ public class AnnotationDeserializer(private val module: ModuleDescriptor) {
if (enumClass.getKind() == ClassKind.ENUM_CLASS) { if (enumClass.getKind() == ClassKind.ENUM_CLASS) {
val enumEntry = enumClass.getUnsubstitutedInnerClassesScope().getClassifier(enumEntryName) val enumEntry = enumClass.getUnsubstitutedInnerClassesScope().getClassifier(enumEntryName)
if (enumEntry is ClassDescriptor) { if (enumEntry is ClassDescriptor) {
return EnumValue(enumEntry, true) return EnumValue(enumEntry)
} }
} }
return ErrorValue.create("Unresolved enum entry: $enumClassId.$enumEntryName") return ErrorValue.create("Unresolved enum entry: $enumClassId.$enumEntryName")