Introduce CompileTimeConstantFactory

This commit is contained in:
Pavel V. Talanov
2015-07-06 20:37:50 +03:00
parent b0a4520710
commit ea1a85e78c
8 changed files with 214 additions and 175 deletions
@@ -25,7 +25,7 @@ import org.jetbrains.kotlin.descriptors.PropertyDescriptor;
import org.jetbrains.kotlin.load.java.structure.JavaField; import org.jetbrains.kotlin.load.java.structure.JavaField;
import org.jetbrains.kotlin.load.java.structure.JavaPropertyInitializerEvaluator; import org.jetbrains.kotlin.load.java.structure.JavaPropertyInitializerEvaluator;
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant; import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
import org.jetbrains.kotlin.resolve.constants.ConstantsPackage; import org.jetbrains.kotlin.resolve.constants.CompileTimeConstantFactory;
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator; import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
public class JavaPropertyInitializerEvaluatorImpl implements JavaPropertyInitializerEvaluator { public class JavaPropertyInitializerEvaluatorImpl implements JavaPropertyInitializerEvaluator {
@@ -35,15 +35,13 @@ public class JavaPropertyInitializerEvaluatorImpl implements JavaPropertyInitial
PsiExpression initializer = ((JavaFieldImpl) field).getInitializer(); PsiExpression initializer = ((JavaFieldImpl) field).getInitializer();
Object evaluatedExpression = JavaConstantExpressionEvaluator.computeConstantExpression(initializer, false); Object evaluatedExpression = JavaConstantExpressionEvaluator.computeConstantExpression(initializer, false);
if (evaluatedExpression != null) { if (evaluatedExpression != null) {
return ConstantsPackage.createCompileTimeConstant( CompileTimeConstantFactory factory = new CompileTimeConstantFactory(
evaluatedExpression,
new CompileTimeConstant.Parameters.Impl( new CompileTimeConstant.Parameters.Impl(
ConstantExpressionEvaluator.isPropertyCompileTimeConstant(descriptor), ConstantExpressionEvaluator.isPropertyCompileTimeConstant(descriptor),
false, false,
true true
), ));
descriptor.getType() return factory.createCompileTimeConstant(evaluatedExpression, descriptor.getType());
);
} }
return null; return null;
} }
@@ -17,26 +17,27 @@
package org.jetbrains.kotlin.resolve.constants.evaluate package org.jetbrains.kotlin.resolve.constants.evaluate
import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.JetNodeTypes
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptorImpl import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptorImpl
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.* import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
import org.jetbrains.kotlin.resolve.constants.* import org.jetbrains.kotlin.resolve.constants.*
import org.jetbrains.kotlin.types.JetType import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.JetNodeTypes
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
import java.math.BigInteger import java.math.BigInteger
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import kotlin.platform.platformStatic import kotlin.platform.platformStatic
public class ConstantExpressionEvaluator private constructor(val trace: BindingTrace) : JetVisitor<CompileTimeConstant<*>, JetType>() { public class ConstantExpressionEvaluator private constructor(val trace: BindingTrace) : JetVisitor<CompileTimeConstant<*>, JetType>() {
@@ -85,6 +86,8 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
} }
private val stringExpressionEvaluator = object : JetVisitor<StringValue, Nothing>() { private val stringExpressionEvaluator = object : JetVisitor<StringValue, Nothing>() {
private val factory = CompileTimeConstantFactory(CompileTimeConstant.Parameters.Impl(true, false, false))
fun evaluate(entry: JetStringTemplateEntry): StringValue? { fun evaluate(entry: JetStringTemplateEntry): StringValue? {
return entry.accept(this, null) return entry.accept(this, null)
} }
@@ -96,14 +99,13 @@ 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(), CompileTimeConstant.Parameters.Impl(true, false, false)) override fun visitLiteralStringTemplateEntry(entry: JetLiteralStringTemplateEntry, data: Nothing?) = factory.createStringValue(entry.getText())
override fun visitEscapeStringTemplateEntry(entry: JetEscapeStringTemplateEntry, data: Nothing?) = StringValue(entry.getUnescapedValue(), CompileTimeConstant.Parameters.Impl(true, false, false)) override fun visitEscapeStringTemplateEntry(entry: JetEscapeStringTemplateEntry, data: Nothing?) = factory.createStringValue(entry.getUnescapedValue())
} }
override fun visitConstantExpression(expression: JetConstantExpression, expectedType: JetType?): CompileTimeConstant<*>? { override fun visitConstantExpression(expression: JetConstantExpression, expectedType: JetType?): CompileTimeConstant<*>? {
val text = expression.getText() val text = expression.getText() ?: return null
if (text == null) return null
val nodeElementType = expression.getNode().getElementType() val nodeElementType = expression.getNode().getElementType()
if (nodeElementType == JetNodeTypes.NULL) return NullValue if (nodeElementType == JetNodeTypes.NULL) return NullValue
@@ -114,8 +116,7 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
JetNodeTypes.BOOLEAN_CONSTANT -> parseBoolean(text) JetNodeTypes.BOOLEAN_CONSTANT -> parseBoolean(text)
JetNodeTypes.CHARACTER_CONSTANT -> CompileTimeConstantChecker.parseChar(expression) JetNodeTypes.CHARACTER_CONSTANT -> CompileTimeConstantChecker.parseChar(expression)
else -> throw IllegalArgumentException("Unsupported constant: " + expression) else -> throw IllegalArgumentException("Unsupported constant: " + expression)
} } ?: return null
if (result == null) return null
fun isLongWithSuffix() = nodeElementType == JetNodeTypes.INTEGER_CONSTANT && hasLongSuffix(text) fun isLongWithSuffix() = nodeElementType == JetNodeTypes.INTEGER_CONSTANT && hasLongSuffix(text)
return createConstant(result, expectedType, CompileTimeConstant.Parameters.Impl(true, !isLongWithSuffix(), false)) return createConstant(result, expectedType, CompileTimeConstant.Parameters.Impl(true, !isLongWithSuffix(), false))
@@ -164,11 +165,11 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
usesVariableAsConstant = usesVariableAsConstant usesVariableAsConstant = usesVariableAsConstant
) )
) )
else null else null
} }
override fun visitBinaryWithTypeRHSExpression(expression: JetBinaryExpressionWithTypeRHS, expectedType: JetType?): CompileTimeConstant<*>? = override fun visitBinaryWithTypeRHSExpression(expression: JetBinaryExpressionWithTypeRHS, expectedType: JetType?): CompileTimeConstant<*>? =
evaluate(expression.getLeft(), expectedType) evaluate(expression.getLeft(), expectedType)
override fun visitBinaryExpression(expression: JetBinaryExpression, expectedType: JetType?): CompileTimeConstant<*>? { override fun visitBinaryExpression(expression: JetBinaryExpression, expectedType: JetType?): CompileTimeConstant<*>? {
val leftExpression = expression.getLeft() val leftExpression = expression.getLeft()
@@ -190,7 +191,7 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
val rightValue = rightConstant.value val rightValue = rightConstant.value
if (leftValue !is Boolean || rightValue !is Boolean) return null if (leftValue !is Boolean || rightValue !is Boolean) return null
val result = when(operationToken) { val result = when (operationToken) {
JetTokens.ANDAND -> leftValue && rightValue JetTokens.ANDAND -> leftValue && rightValue
JetTokens.OROR -> leftValue || rightValue JetTokens.OROR -> leftValue || rightValue
else -> throw IllegalArgumentException("Unknown boolean operation token ${operationToken}") else -> throw IllegalArgumentException("Unknown boolean operation token ${operationToken}")
@@ -237,6 +238,7 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
if (isDivisionByZero(resultingDescriptorName.asString(), argumentForParameter.value)) { if (isDivisionByZero(resultingDescriptorName.asString(), argumentForParameter.value)) {
val parentExpression: JetExpression = PsiTreeUtil.getParentOfType(receiverExpression, javaClass())!! val parentExpression: JetExpression = PsiTreeUtil.getParentOfType(receiverExpression, javaClass())!!
trace.report(Errors.DIVISION_BY_ZERO.on(parentExpression)) trace.report(Errors.DIVISION_BY_ZERO.on(parentExpression))
//TODO_R:
return ErrorValue.create("Division by zero") return ErrorValue.create("Division by zero")
} }
@@ -247,11 +249,12 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
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 parameters = CompileTimeConstant.Parameters.Impl(canBeUsedInAnnotation, areArgumentsPure, usesVariableAsConstant) val parameters = CompileTimeConstant.Parameters.Impl(canBeUsedInAnnotation, areArgumentsPure, usesVariableAsConstant)
return when(resultingDescriptorName) { val factory = CompileTimeConstantFactory(parameters)
OperatorConventions.COMPARE_TO -> createCompileTimeConstantForCompareTo(result, callExpression, parameters) return when (resultingDescriptorName) {
OperatorConventions.EQUALS -> createCompileTimeConstantForEquals(result, callExpression, parameters) OperatorConventions.COMPARE_TO -> createCompileTimeConstantForCompareTo(result, callExpression, factory)
OperatorConventions.EQUALS -> createCompileTimeConstantForEquals(result, callExpression, factory)
else -> { else -> {
createConstant(result, expectedType, CompileTimeConstant.Parameters.Impl(areArgumentsPure, canBeUsedInAnnotation, usesVariableAsConstant)) createConstant(result, expectedType, parameters)
} }
} }
} }
@@ -296,7 +299,8 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
val (function, checker) = functions val (function, checker) = functions
val actualResult = try { val actualResult = try {
function(receiver.value, parameter.value) function(receiver.value, parameter.value)
} catch (e: Exception) { }
catch (e: Exception) {
null null
} }
if (checker == emptyBinaryFun) { if (checker == emptyBinaryFun) {
@@ -402,6 +406,7 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
val varargType = resultingDescriptor.getValueParameters().first().getVarargElementType()!! val varargType = resultingDescriptor.getValueParameters().first().getVarargElementType()!!
val arguments = call.getValueArguments().values().flatMap { resolveArguments(it.getArguments(), varargType) } val arguments = call.getValueArguments().values().flatMap { resolveArguments(it.getArguments(), varargType) }
return ArrayValue(arguments, resultingDescriptor.getReturnType()!!, arguments.any() { it.usesVariableAsConstant() }) return ArrayValue(arguments, resultingDescriptor.getReturnType()!!, arguments.any() { it.usesVariableAsConstant() })
} }
@@ -488,12 +493,12 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
expectedType: JetType?, expectedType: JetType?,
parameters: CompileTimeConstant.Parameters parameters: CompileTimeConstant.Parameters
): CompileTimeConstant<*>? { ): CompileTimeConstant<*>? {
return createCompileTimeConstant(value, parameters, if (parameters.isPure) expectedType ?: TypeUtils.NO_EXPECTED_TYPE else null) return CompileTimeConstantFactory(parameters).createCompileTimeConstant(value, if (parameters.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), CompileTimeConstant.Parameters.Impl(this.canBeUsedInAnnotations(), true, false)) = CompileTimeConstantFactory(CompileTimeConstant.Parameters.Impl(this.canBeUsedInAnnotations(), true, false)).createCompileTimeConstant(this.getValue(expectedType))
private fun hasLongSuffix(text: String) = text.endsWith('l') || text.endsWith('L') private fun hasLongSuffix(text: String) = text.endsWith('l') || text.endsWith('L')
@@ -550,35 +555,36 @@ private fun parseBoolean(text: String): Boolean {
} }
private fun createCompileTimeConstantForEquals(result: Any?, operationReference: JetExpression, parameters: CompileTimeConstant.Parameters): CompileTimeConstant<*>? { private fun createCompileTimeConstantForEquals(result: Any?, operationReference: JetExpression, factory: CompileTimeConstantFactory): 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) { val value: Boolean = when (operationToken) {
JetTokens.EQEQ -> BooleanValue(result, parameters) JetTokens.EQEQ -> result
JetTokens.EXCLEQ -> BooleanValue(!result, parameters) JetTokens.EXCLEQ -> !result
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, parameters) result
} }
else -> throw IllegalStateException("Unknown equals operation token: $operationToken ${operationReference.getText()}") else -> throw IllegalStateException("Unknown equals operation token: $operationToken ${operationReference.getText()}")
} }
return factory.createBooleanValue(value)
} }
return null return null
} }
private fun createCompileTimeConstantForCompareTo(result: Any?, operationReference: JetExpression, parameters: CompileTimeConstant.Parameters): CompileTimeConstant<*>? { private fun createCompileTimeConstantForCompareTo(result: Any?, operationReference: JetExpression, factory: CompileTimeConstantFactory): 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, parameters) JetTokens.LT -> factory.createBooleanValue(result < 0)
JetTokens.LTEQ -> BooleanValue(result <= 0, parameters) JetTokens.LTEQ -> factory.createBooleanValue(result <= 0)
JetTokens.GT -> BooleanValue(result > 0, parameters) JetTokens.GT -> factory.createBooleanValue(result > 0)
JetTokens.GTEQ -> BooleanValue(result >= 0, parameters) JetTokens.GTEQ -> factory.createBooleanValue(result >= 0)
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, parameters) return factory.createIntValue(result)
} }
else -> throw IllegalStateException("Unknown compareTo operation token: $operationToken") else -> throw IllegalStateException("Unknown compareTo operation token: $operationToken")
} }
@@ -588,13 +594,13 @@ 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.parameters) is IntegerValueTypeConstant -> CompileTimeConstantFactory(value.parameters).createStringValue(value.getValue(TypeUtils.NO_EXPECTED_TYPE).toString())
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.parameters) is NullValue -> CompileTimeConstantFactory(value.parameters).createStringValue("${value.value}")
else -> null else -> null
} }
} }
@@ -27,6 +27,9 @@ import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.JetType import org.jetbrains.kotlin.types.JetType
public object AnnotationSerializer { public object AnnotationSerializer {
private val factory = CompileTimeConstantFactory(CompileTimeConstant.Parameters.ThrowException)
public fun serializeAnnotation(annotation: AnnotationDescriptor, stringTable: StringTable): ProtoBuf.Annotation { public fun serializeAnnotation(annotation: AnnotationDescriptor, stringTable: StringTable): ProtoBuf.Annotation {
return with(ProtoBuf.Annotation.newBuilder()) { return with(ProtoBuf.Annotation.newBuilder()) {
val annotationClass = annotation.getType().getConstructor().getDeclarationDescriptor() as? ClassDescriptor val annotationClass = annotation.getType().getConstructor().getDeclarationDescriptor() as? ClassDescriptor
@@ -121,12 +124,11 @@ 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(), parameters) getLongType() -> factory.createLongValue(number.toLong())
getIntType() -> IntValue(number.toInt(), parameters) getIntType() -> factory.createIntValue(number.toInt())
getShortType() -> ShortValue(number.toShort(), parameters) getShortType() -> factory.createShortValue(number.toShort())
getByteType() -> ByteValue(number.toByte(), parameters) getByteType() -> factory.createByteValue(number.toByte())
else -> throw IllegalStateException("Integer constant $constant has non-integer type $type") else -> throw IllegalStateException("Integer constant $constant has non-integer type $type")
} }
} }
@@ -64,6 +64,8 @@ class LazyJavaAnnotationDescriptor(
annotationClass?.getDefaultType() ?: ErrorUtils.createErrorType(fqName.asString()) annotationClass?.getDefaultType() ?: ErrorUtils.createErrorType(fqName.asString())
} }
private val factory = CompileTimeConstantFactory(CompileTimeConstant.Parameters.Impl(true, false, false))
override fun getType(): JetType = type() override fun getType(): JetType = type()
private val allValueArguments = c.storageManager.createLazyValue { private val allValueArguments = c.storageManager.createLazyValue {
@@ -100,7 +102,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, CompileTimeConstant.Parameters.Impl(true, false, false)) is JavaLiteralAnnotationArgument -> factory.createCompileTimeConstant(argument.value)
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())
@@ -110,22 +112,20 @@ class LazyJavaAnnotationDescriptor(
} }
private fun resolveFromAnnotation(javaAnnotation: JavaAnnotation): CompileTimeConstant<*>? { private fun resolveFromAnnotation(javaAnnotation: JavaAnnotation): CompileTimeConstant<*>? {
val descriptor = c.resolveAnnotation(javaAnnotation) val descriptor = c.resolveAnnotation(javaAnnotation) ?: return null
if (descriptor == null) return null
return AnnotationValue(descriptor) return factory.createAnnotationValue(descriptor)
} }
private fun resolveFromArray(argumentName: Name, elements: List<JavaAnnotationArgument>): CompileTimeConstant<*>? { private fun resolveFromArray(argumentName: Name, elements: List<JavaAnnotationArgument>): CompileTimeConstant<*>? {
if (getType().isError()) return null if (getType().isError()) return null
val valueParameter = DescriptorResolverUtils.getAnnotationParameterByName(argumentName, getAnnotationClass()) val valueParameter = DescriptorResolverUtils.getAnnotationParameterByName(argumentName, getAnnotationClass()) ?: return null
if (valueParameter == null) return null
val values = elements.map { val values = elements.map {
argument -> resolveAnnotationArgument(argument) ?: NullValue argument -> resolveAnnotationArgument(argument) ?: factory.createNullValue()
} }
return ArrayValue(values, valueParameter.getType(), CompileTimeConstant.Parameters.Impl(true, false, values.any { it.usesVariableAsConstant() })) return factory.createArrayValue(values, valueParameter.getType())
} }
private fun resolveFromEnumValue(element: JavaField?): CompileTimeConstant<*>? { private fun resolveFromEnumValue(element: JavaField?): CompileTimeConstant<*>? {
@@ -134,13 +134,12 @@ class LazyJavaAnnotationDescriptor(
val containingJavaClass = element.getContainingClass() val containingJavaClass = element.getContainingClass()
//TODO: (module refactoring) moduleClassResolver should be used here //TODO: (module refactoring) moduleClassResolver should be used here
val enumClass = c.javaClassResolver.resolveClass(containingJavaClass) val enumClass = c.javaClassResolver.resolveClass(containingJavaClass) ?: return null
if (enumClass == null) return null
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) return factory.createEnumValue(classifier)
} }
private fun resolveFromJavaClassObjectType(javaType: JavaType): CompileTimeConstant<*>? { private fun resolveFromJavaClassObjectType(javaType: JavaType): CompileTimeConstant<*>? {
@@ -160,7 +159,7 @@ class LazyJavaAnnotationDescriptor(
override fun computeMemberScope() = jlClass.getMemberScope(arguments) override fun computeMemberScope() = jlClass.getMemberScope(arguments)
} }
return KClassValue(javaClassObjectType) return factory.createKClassValue(javaClassObjectType)
} }
override fun toString(): String { override fun toString(): String {
@@ -46,6 +46,7 @@ public class BinaryClassAnnotationAndConstantLoaderImpl(
storageManager, kotlinClassFinder, errorReporter storageManager, kotlinClassFinder, errorReporter
) { ) {
private val annotationDeserializer = AnnotationDeserializer(module) private val annotationDeserializer = AnnotationDeserializer(module)
private val factory = CompileTimeConstantFactory(CompileTimeConstant.Parameters.ThrowException)
override fun loadTypeAnnotation(proto: ProtoBuf.Annotation, nameResolver: NameResolver): AnnotationDescriptor = override fun loadTypeAnnotation(proto: ProtoBuf.Annotation, nameResolver: NameResolver): AnnotationDescriptor =
annotationDeserializer.deserializeAnnotation(proto, nameResolver) annotationDeserializer.deserializeAnnotation(proto, nameResolver)
@@ -65,10 +66,7 @@ public class BinaryClassAnnotationAndConstantLoaderImpl(
initializer initializer
} }
val compileTimeConstant = createCompileTimeConstant( return factory.createCompileTimeConstant(normalizedValue)
normalizedValue, CompileTimeConstant.Parameters.ThrowException
)
return compileTimeConstant
} }
override fun loadAnnotation( override fun loadAnnotation(
@@ -106,7 +104,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(), CompileTimeConstant.Parameters.ThrowException) arguments[parameter] = factory.createArrayValue(elements, parameter.getType())
} }
} }
} }
@@ -129,10 +127,10 @@ 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) return factory.createEnumValue(classifier)
} }
} }
return ErrorValue.create("Unresolved enum entry: $enumClassId.$name") return factory.createErrorValue("Unresolved enum entry: $enumClassId.$name")
} }
override fun visitEnd() { override fun visitEnd() {
@@ -140,10 +138,8 @@ public class BinaryClassAnnotationAndConstantLoaderImpl(
} }
private fun createConstant(name: Name?, value: Any?): CompileTimeConstant<*> { private fun createConstant(name: Name?, value: Any?): CompileTimeConstant<*> {
return createCompileTimeConstant( return factory.createCompileTimeConstant(value) ?:
value, factory.createErrorValue("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<*>) {
@@ -0,0 +1,122 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.resolve.constants
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.TypeUtils
public class CompileTimeConstantFactory(private val parameters: CompileTimeConstant.Parameters) {
fun createLongValue(value: Long) = LongValue(value, parameters)
fun createIntValue(value: Int) = IntValue(value, parameters)
fun createErrorValue(message: String) = ErrorValue.create(message)
fun createShortValue(value: Short) = ShortValue(value, parameters)
fun createByteValue(value: Byte) = ByteValue(value, parameters)
fun createDoubleValue(value: Double) = DoubleValue(value, parameters)
fun createFloatValue(value: Float) = FloatValue(value, parameters)
fun createBooleanValue(value: Boolean) = BooleanValue(value, parameters)
fun createCharValue(value: Char) = CharValue(value, parameters)
fun createStringValue(value: String) = StringValue(value, parameters)
fun createNullValue() = NullValue
fun createEnumValue(enumEntryClass: ClassDescriptor): EnumValue = EnumValue(enumEntryClass)
fun createArrayValue(
value: List<CompileTimeConstant<*>>,
type: JetType
) = ArrayValue(value, type, parameters)
fun createAnnotationValue(value: AnnotationDescriptor) = AnnotationValue(value)
fun createKClassValue(type: JetType) = KClassValue(type)
fun createNumberTypeValue(value: Number) = IntegerValueTypeConstant(value, parameters)
fun createCompileTimeConstant(
value: Any?,
expectedType: JetType? = null
): CompileTimeConstant<*>? {
// TODO: primitive arrays
if (expectedType == null) {
when (value) {
is Byte -> return createByteValue(value)
is Short -> return createShortValue(value)
is Int -> return createIntValue(value)
is Long -> return createLongValue(value)
}
}
return when (value) {
is Byte, is Short, is Int, is Long -> getIntegerValue((value as Number).toLong(), expectedType)
is Char -> createCharValue(value)
is Float -> createFloatValue(value)
is Double -> createDoubleValue(value)
is Boolean -> createBooleanValue(value)
is String -> createStringValue(value)
null -> createNullValue()
else -> null
}
}
private fun getIntegerValue(
value: Long,
expectedType: JetType
): CompileTimeConstant<*>? {
fun defaultIntegerValue(value: Long) = when (value) {
value.toInt().toLong() -> createIntValue(value.toInt())
else -> createLongValue(value)
}
if (TypeUtils.noExpectedType(expectedType) || expectedType.isError()) {
return createNumberTypeValue(value)
}
val notNullExpected = TypeUtils.makeNotNullable(expectedType)
return when {
KotlinBuiltIns.isLong(notNullExpected) -> createLongValue(value)
KotlinBuiltIns.isShort(notNullExpected) ->
if (value == value.toShort().toLong())
createShortValue(value.toShort())
else
defaultIntegerValue(value)
KotlinBuiltIns.isByte(notNullExpected) ->
if (value == value.toByte().toLong())
createByteValue(value.toByte())
else
defaultIntegerValue(value)
KotlinBuiltIns.isChar(notNullExpected) ->
createIntValue(value.toInt())
else -> defaultIntegerValue(value)
}
}
}
@@ -1,84 +0,0 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.resolve.constants
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.types.TypeUtils
public fun createCompileTimeConstant(
value: Any?,
parameters: CompileTimeConstant.Parameters,
expectedType: JetType? = null
): CompileTimeConstant<*>? {
// TODO: primitive arrays
if (expectedType == null) {
when(value) {
is Byte -> return ByteValue(value, parameters)
is Short -> return ShortValue(value, parameters)
is Int -> return IntValue(value, parameters)
is Long -> return LongValue(value, parameters)
}
}
return when(value) {
is Byte, is Short, is Int, is Long -> getIntegerValue((value as Number).toLong(), parameters, expectedType)
is Char -> CharValue(value, parameters)
is Float -> FloatValue(value, parameters)
is Double -> DoubleValue(value, parameters)
is Boolean -> BooleanValue(value, parameters)
is String -> StringValue(value, parameters)
null -> NullValue
else -> null
}
}
private fun getIntegerValue(
value: Long,
parameters: CompileTimeConstant.Parameters,
expectedType: JetType
): CompileTimeConstant<*>? {
fun defaultIntegerValue(value: Long) = when (value) {
value.toInt().toLong() -> IntValue(value.toInt(), parameters)
else -> LongValue(value, parameters)
}
if (TypeUtils.noExpectedType(expectedType) || expectedType.isError()) {
return IntegerValueTypeConstant(value, parameters)
}
val notNullExpected = TypeUtils.makeNotNullable(expectedType)
return when {
KotlinBuiltIns.isLong(notNullExpected) -> LongValue(value, parameters)
KotlinBuiltIns.isShort(notNullExpected) ->
if (value == value.toShort().toLong())
ShortValue(value.toShort(), parameters)
else
defaultIntegerValue(value)
KotlinBuiltIns.isByte(notNullExpected) ->
if (value == value.toByte().toLong())
ByteValue(value.toByte(), parameters)
else
defaultIntegerValue(value)
KotlinBuiltIns.isChar(notNullExpected) ->
IntValue(value.toInt(), parameters)
else -> defaultIntegerValue(value)
}
}
@@ -39,6 +39,8 @@ public class AnnotationDeserializer(private val module: ModuleDescriptor) {
private val builtIns: KotlinBuiltIns private val builtIns: KotlinBuiltIns
get() = module.builtIns get() = module.builtIns
private val factory = CompileTimeConstantFactory(CompileTimeConstant.Parameters.ThrowException)
public fun deserializeAnnotation(proto: Annotation, nameResolver: NameResolver): AnnotationDescriptor { public fun deserializeAnnotation(proto: Annotation, nameResolver: NameResolver): AnnotationDescriptor {
val annotationClass = resolveClass(nameResolver.getClassId(proto.getId())) val annotationClass = resolveClass(nameResolver.getClassId(proto.getId()))
@@ -68,18 +70,17 @@ 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(), parameters) Type.BYTE -> factory.createByteValue(value.getIntValue().toByte())
Type.CHAR -> CharValue(value.getIntValue().toChar(), parameters) Type.CHAR -> factory.createCharValue(value.getIntValue().toChar())
Type.SHORT -> ShortValue(value.getIntValue().toShort(), parameters) Type.SHORT -> factory.createShortValue(value.getIntValue().toShort())
Type.INT -> IntValue(value.getIntValue().toInt(), parameters) Type.INT -> factory.createIntValue(value.getIntValue().toInt())
Type.LONG -> LongValue(value.getIntValue(), parameters) Type.LONG -> factory.createLongValue(value.getIntValue())
Type.FLOAT -> FloatValue(value.getFloatValue(), parameters) Type.FLOAT -> factory.createFloatValue(value.getFloatValue())
Type.DOUBLE -> DoubleValue(value.getDoubleValue(), parameters) Type.DOUBLE -> factory.createDoubleValue(value.getDoubleValue())
Type.BOOLEAN -> BooleanValue(value.getIntValue() != 0L, parameters) Type.BOOLEAN -> factory.createBooleanValue(value.getIntValue() != 0L)
Type.STRING -> { Type.STRING -> {
StringValue(nameResolver.getString(value.getStringValue()), parameters) factory.createStringValue(nameResolver.getString(value.getStringValue()))
} }
Type.CLASS -> { Type.CLASS -> {
// TODO: support class literals // TODO: support class literals
@@ -110,12 +111,11 @@ public class AnnotationDeserializer(private val module: ModuleDescriptor) {
val expectedElementType = builtIns.getArrayElementType(if (expectedIsArray) expectedType else actualArrayType) val expectedElementType = builtIns.getArrayElementType(if (expectedIsArray) expectedType else actualArrayType)
ArrayValue( factory.createArrayValue(
arrayElements.map { arrayElements.map {
resolveValue(expectedElementType, it, nameResolver) resolveValue(expectedElementType, it, nameResolver)
}, },
actualArrayType, actualArrayType
parameters
) )
} }
else -> error("Unsupported annotation argument type: ${value.getType()} (expected $expectedType)") else -> error("Unsupported annotation argument type: ${value.getType()} (expected $expectedType)")
@@ -126,7 +126,7 @@ public class AnnotationDeserializer(private val module: ModuleDescriptor) {
} }
else { else {
// This means that an annotation class has been changed incompatibly without recompiling clients // This means that an annotation class has been changed incompatibly without recompiling clients
return ErrorValue.create("Unexpected argument value") return factory.createErrorValue("Unexpected argument value")
} }
} }
@@ -136,10 +136,10 @@ 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) return factory.createEnumValue(enumEntry)
} }
} }
return ErrorValue.create("Unresolved enum entry: $enumClassId.$enumEntryName") return factory.createErrorValue("Unresolved enum entry: $enumClassId.$enumEntryName")
} }
private fun resolveArrayElementType(value: Value, nameResolver: NameResolver): JetType = private fun resolveArrayElementType(value: Value, nameResolver: NameResolver): JetType =