diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/TraceBasedJavaResolverCache.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/TraceBasedJavaResolverCache.java index f9db2050795..6e0a6347d34 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/TraceBasedJavaResolverCache.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/TraceBasedJavaResolverCache.java @@ -114,7 +114,7 @@ public class TraceBasedJavaResolverCache implements JavaResolverCache { PsiField psiField = ((JavaFieldImpl) field).getPsi(); trace.record(VARIABLE, psiField, descriptor); - if (AnnotationUtils.isPropertyAcceptableAsAnnotationParameter(descriptor)) { + if (AnnotationUtils.isPropertyCompileTimeConstant(descriptor)) { PsiExpression initializer = psiField.getInitializer(); if (initializer instanceof PsiLiteralExpression) { CompileTimeConstant constant = JavaAnnotationArgumentResolver diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/evaluate/ConstantExpressionEvaluator.java b/compiler/frontend/src/org/jetbrains/jet/lang/evaluate/ConstantExpressionEvaluator.java deleted file mode 100644 index 0090f053e1a..00000000000 --- a/compiler/frontend/src/org/jetbrains/jet/lang/evaluate/ConstantExpressionEvaluator.java +++ /dev/null @@ -1,496 +0,0 @@ -/* - * Copyright 2010-2013 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.jet.lang.evaluate; - -import com.google.common.collect.Lists; -import com.intellij.psi.tree.IElementType; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.jet.lang.descriptors.*; -import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptorImpl; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.jet.lang.descriptors.*; -import org.jetbrains.jet.lang.psi.*; -import org.jetbrains.jet.lang.resolve.*; -import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall; -import org.jetbrains.jet.lang.resolve.calls.model.ResolvedValueArgument; -import org.jetbrains.jet.lang.resolve.constants.*; -import org.jetbrains.jet.lang.resolve.constants.StringValue; -import org.jetbrains.jet.lang.resolve.name.Name; -import org.jetbrains.jet.lang.types.JetType; -import org.jetbrains.jet.lang.types.expressions.OperatorConventions; -import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; -import org.jetbrains.jet.lexer.JetTokens; - -import java.util.List; -import java.util.Map; - -import static org.jetbrains.jet.lang.resolve.BindingContext.COMPILE_TIME_INITIALIZER; - -@SuppressWarnings("StaticMethodReferencedViaSubclass") -public class ConstantExpressionEvaluator extends JetVisitor, Void> { - private final BindingTrace trace; - private final JetType expectedType; - - private ConstantExpressionEvaluator(@NotNull BindingTrace trace, @NotNull JetType expectedType) { - this.trace = trace; - this.expectedType = expectedType; - } - - @Nullable - public static CompileTimeConstant evaluate(@NotNull JetExpression expression, @NotNull BindingTrace trace, @NotNull JetType expectedType) { - ConstantExpressionEvaluator evaluator = new ConstantExpressionEvaluator(trace, expectedType); - return evaluator.evaluate(expression); - } - - @Nullable - public CompileTimeConstant evaluate(@NotNull JetExpression expression) { - CompileTimeConstant recordedCompileTimeConstant = trace.get(BindingContext.COMPILE_TIME_VALUE, expression); - if (recordedCompileTimeConstant != null) { - return recordedCompileTimeConstant; - } - - CompileTimeConstant compileTimeConstant = expression.accept(this, null); - - if (compileTimeConstant != null) { - trace.record(BindingContext.COMPILE_TIME_VALUE, expression, compileTimeConstant); - return compileTimeConstant; - } - return null; - } - - @Override - public CompileTimeConstant visitConstantExpression(@NotNull JetConstantExpression expression, Void data) { - return trace.get(BindingContext.COMPILE_TIME_VALUE, expression); - } - - @Override - public CompileTimeConstant visitParenthesizedExpression(@NotNull JetParenthesizedExpression expression, Void data) { - JetExpression deparenthesizedExpression = getDeparenthesizedExpression(expression); - if (deparenthesizedExpression != null) { - return evaluate(deparenthesizedExpression); - } - return null; - } - - @Override - public CompileTimeConstant visitPrefixExpression(@NotNull JetPrefixExpression expression, Void data) { - JetExpression deparenthesizedExpression = getDeparenthesizedExpression(expression); - if (deparenthesizedExpression != null) { - return evaluate(deparenthesizedExpression); - } - return null; - } - - @Nullable - private static JetExpression getDeparenthesizedExpression(JetExpression expression) { - JetExpression deparenthesizedExpr = JetPsiUtil.deparenthesize(expression); - if (deparenthesizedExpr == expression) { - return null; - } - return deparenthesizedExpr; - } - - @Override - public CompileTimeConstant visitStringTemplateExpression(@NotNull JetStringTemplateExpression expression, Void data) { - JetStringTemplateEntry[] stringTemplateEntries = expression.getEntries(); - if (stringTemplateEntries.length == 1) { - CompileTimeConstant singleEntry = stringTemplateEntries[0].accept(this, null); - if (!(singleEntry instanceof StringValue) && singleEntry != null) { - return new StringValue(singleEntry.getValue().toString()); - } - else { - return singleEntry; - } - } - else if (stringTemplateEntries.length > 1) { - CompileTimeConstant first = stringTemplateEntries[0].accept(this, null); - if (first == null) { - return null; - } - CompileTimeConstant second; - String tmpResult = null; - for (int i = 1; i < stringTemplateEntries.length; i++) { - second = stringTemplateEntries[i].accept(this, null); - if (second == null) { - return null; - } - Object object = EvaluatePackage.evaluateBinaryExpression(first, second, Name.identifier("plus")); - if (object instanceof String) { - tmpResult = (String) object; - } - else { - tmpResult = object.toString(); - } - first = new StringValue(tmpResult); - } - - return new StringValue(tmpResult); - } - return null; - } - - @Override - public CompileTimeConstant visitStringTemplateEntryWithExpression(@NotNull JetStringTemplateEntryWithExpression entry, Void data) { - JetExpression expression = entry.getExpression(); - if (expression != null) { - return evaluate(expression); - } - return null; - } - - @Override - public CompileTimeConstant visitLiteralStringTemplateEntry(@NotNull JetLiteralStringTemplateEntry entry, Void data) { - return new StringValue(entry.getText()); - } - - @Override - public CompileTimeConstant visitEscapeStringTemplateEntry(@NotNull JetEscapeStringTemplateEntry entry, Void data) { - return new StringValue(entry.getUnescapedValue()); - } - - @Override - public CompileTimeConstant visitBinaryExpression(@NotNull JetBinaryExpression expression, Void data) { - JetExpression leftExpression = expression.getLeft(); - if (leftExpression == null) { - return null; - } - - IElementType operationToken = expression.getOperationToken(); - if (OperatorConventions.BOOLEAN_OPERATIONS.containsKey(operationToken)) { - JetType booleanType = KotlinBuiltIns.getInstance().getBooleanType(); - CompileTimeConstant leftConstant = evaluate(leftExpression, trace, booleanType); - if (leftConstant == null) { - return null; - } - JetExpression rightExpression = expression.getRight(); - if (rightExpression == null) { - return null; - } - CompileTimeConstant rightConstant = evaluate(rightExpression, trace, booleanType); - if (rightConstant == null) { - return null; - } - - Name operationName = operationToken == JetTokens.ANDAND ? Name.identifier("andand") : Name.identifier("oror"); - Object result = EvaluatePackage.evaluateBinaryExpression(leftConstant, rightConstant, operationName); - return createCompileTimeConstant(result, expectedType); - } - else { - Object result = evaluateCall(expression.getOperationReference(), leftExpression); - if (OperatorConventions.COMPARISON_OPERATIONS.contains(operationToken)) { - return createCompileTimeConstantForCompareTo(result, operationToken); - } - else if (OperatorConventions.EQUALS_OPERATIONS.contains(operationToken)) { - return createCompileTimeConstantForEquals(result, operationToken); - } - else { - return createCompileTimeConstant(result, expectedType); - } - } - } - - @Nullable - private Object evaluateCall(@NotNull JetExpression resolvedCallExpression, @NotNull JetExpression receiverExpression) { - ResolvedCall resolvedCall = trace.getBindingContext().get(BindingContext.RESOLVED_CALL, resolvedCallExpression); - if (resolvedCall != null) { - CallableDescriptor resultingDescriptor = resolvedCall.getResultingDescriptor(); - JetType receiverExpressionType = getReceiverExpressionType(resolvedCall); - if (receiverExpressionType == null) { - return null; - } - ConstantExpressionEvaluator evaluator = new ConstantExpressionEvaluator(trace, receiverExpressionType); - CompileTimeConstant receiverValue = receiverExpression.accept(evaluator, null); - if (receiverValue == null) { - return null; - } - List> arguments = Lists.newArrayList(); - for (Map.Entry argumentEntry : resolvedCall.getValueArguments().entrySet()) { - arguments.addAll(resolveArguments(argumentEntry.getValue().getArguments(), argumentEntry.getKey().getType(), trace)); - } - Name resultingDescriptorName = resultingDescriptor.getName(); - if (arguments.isEmpty()) { - return EvaluatePackage.evaluateUnaryExpression(receiverValue, resultingDescriptorName); - } - else if (arguments.size() == 1) { - return EvaluatePackage.evaluateBinaryExpression(receiverValue, arguments.iterator().next(), resultingDescriptorName); - } - } - - return null; - } - - @Nullable - private static CompileTimeConstant createCompileTimeConstantForEquals( - @Nullable Object result, - @NotNull IElementType operationToken - ) { - if (result instanceof Boolean) { - boolean resultCode = ((Boolean) result).booleanValue(); - if (operationToken == JetTokens.EQEQ) { - return resultCode ? BooleanValue.TRUE : BooleanValue.FALSE; - } - else if (operationToken == JetTokens.EXCLEQ) { - return resultCode ? BooleanValue.FALSE : BooleanValue.TRUE; - } - } - return null; - } - - @Nullable - private static CompileTimeConstant createCompileTimeConstantForCompareTo( - @Nullable Object result, - @NotNull IElementType operationToken - ) { - if (result instanceof Integer) { - int resultCode = ((Integer) result).intValue(); - if (resultCode == 0) { - if (operationToken == JetTokens.EQEQ || - operationToken == JetTokens.LTEQ || - operationToken == JetTokens.GTEQ) { - return BooleanValue.TRUE; - } - else { - return BooleanValue.FALSE; - } - } - if (resultCode == 1) { - if (operationToken == JetTokens.GT || - operationToken == JetTokens.GTEQ) { - return BooleanValue.TRUE; - } - else { - return BooleanValue.FALSE; - } - } - if (resultCode == -1) { - if (operationToken == JetTokens.LT || - operationToken == JetTokens.LTEQ) { - return BooleanValue.TRUE; - } - else { - return BooleanValue.FALSE; - } - } - } - return null; - } - - @Override - public CompileTimeConstant visitUnaryExpression(@NotNull JetUnaryExpression expression, Void data) { - JetExpression leftExpression = expression.getBaseExpression(); - if (leftExpression == null) { - return null; - } - Object result = evaluateCall(expression.getOperationReference(), leftExpression); - return createCompileTimeConstant(result, expectedType); - } - - @Nullable - public static CompileTimeConstant createCompileTimeConstant(@Nullable Object value, @NotNull JetType expectedType) { - if (value == null) { - return null; - } - else if (value instanceof Integer) { - return getIntegerValue(((Integer) value).longValue(), expectedType); - } - else if (value instanceof Byte) { - return getIntegerValue(((Byte) value).longValue(), expectedType); - } - else if (value instanceof Short) { - return getIntegerValue(((Short) value).longValue(), expectedType); - } - else if (value instanceof Long) { - return new LongValue((Long) value); - } - else if (value instanceof Float) { - if (CompileTimeConstantResolver.noExpectedTypeOrError(expectedType) || - expectedType.equals(KotlinBuiltIns.getInstance().getDoubleType())) { - return new DoubleValue(((Float) value).doubleValue()); - } - else { - return new FloatValue((Float) value); - } - } - else if (value instanceof Double) { - return new DoubleValue((Double) value); - } - else if (value instanceof Boolean) { - return ((Boolean) value) ? BooleanValue.TRUE : BooleanValue.FALSE; - } - else if (value instanceof Character) { - return new CharValue((Character) value); - } - else if (value instanceof String) { - return new StringValue((String) value); - } - return null; - } - - @Nullable - private static CompileTimeConstant getIntegerValue(@NotNull Long value, @NotNull JetType expectedType) { - if (CompileTimeConstantResolver.noExpectedTypeOrError(expectedType)) { - if (Integer.MIN_VALUE <= value && value <= Integer.MAX_VALUE) { - return new IntValue(value.intValue()); - } - return new LongValue(value); - } - KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance(); - if (expectedType.equals(builtIns.getIntType())) { - return new IntValue(value.intValue()); - } - else if (expectedType.equals(builtIns.getLongType())) { - return new LongValue(value); - } - else if (expectedType.equals(builtIns.getShortType())) { - if (Short.MIN_VALUE <= value && value <= Short.MAX_VALUE) { - return new ShortValue(value.shortValue()); - } - else if (Integer.MIN_VALUE <= value && value <= Integer.MAX_VALUE) { - return new IntValue(value.intValue()); - } - return new LongValue(value); - } - else if (expectedType.equals(builtIns.getByteType())) { - if (Byte.MIN_VALUE <= value && value <= Byte.MAX_VALUE) { - return new ByteValue(value.byteValue()); - } - else if (Integer.MIN_VALUE <= value && value <= Integer.MAX_VALUE) { - return new IntValue(value.intValue()); - } - return new LongValue(value); - } - else if (expectedType.equals(builtIns.getCharType())) { - return new IntValue(value.intValue()); - } - return null; - } - - @Override - public CompileTimeConstant visitSimpleNameExpression(@NotNull JetSimpleNameExpression expression, Void data) { - DeclarationDescriptor descriptor = trace.getBindingContext().get(BindingContext.REFERENCE_TARGET, expression); - if (descriptor != null && DescriptorUtils.isEnumEntry(descriptor)) { - return new EnumValue((ClassDescriptor) descriptor); - } - - ResolvedCall resolvedCall = - trace.getBindingContext().get(BindingContext.RESOLVED_CALL, expression); - if (resolvedCall != null) { - CallableDescriptor callableDescriptor = resolvedCall.getResultingDescriptor(); - if (callableDescriptor instanceof PropertyDescriptor) { - PropertyDescriptor propertyDescriptor = (PropertyDescriptor) callableDescriptor; - if (AnnotationUtils.isPropertyAcceptableAsAnnotationParameter(propertyDescriptor)) { - return trace.getBindingContext().get(COMPILE_TIME_INITIALIZER, propertyDescriptor); - } - } - } - - return null; - } - - /* 1.toInt(); 1.plus(1); MyEnum.A */ - @Override - public CompileTimeConstant visitQualifiedExpression(@NotNull JetQualifiedExpression expression, Void data) { - JetExpression receiverExpression = expression.getReceiverExpression(); - JetExpression selectorExpression = expression.getSelectorExpression(); - - - if (selectorExpression instanceof JetCallExpression) { - JetExpression calleeExpression = ((JetCallExpression) selectorExpression).getCalleeExpression(); - if (!(calleeExpression instanceof JetSimpleNameExpression)) { - return null; - } - - if (((JetCallExpression) selectorExpression).getValueArguments().size() < 2) { - Object result = evaluateCall(calleeExpression, receiverExpression); - return createCompileTimeConstant(result, expectedType); - } - } - - if (selectorExpression != null) { - return evaluate(selectorExpression); - } - return null; - } - - @Nullable - private static JetType getReceiverExpressionType(ResolvedCall resolvedCall) { - switch(resolvedCall.getExplicitReceiverKind()) { - case THIS_OBJECT: return resolvedCall.getThisObject().getType(); - case RECEIVER_ARGUMENT: return resolvedCall.getReceiverArgument().getType(); - case NO_EXPLICIT_RECEIVER: return null; - case BOTH_RECEIVERS: return null; - } - return null; - } - - @Override - public CompileTimeConstant visitCallExpression(@NotNull JetCallExpression expression, Void data) { - ResolvedCall call = - trace.getBindingContext().get(BindingContext.RESOLVED_CALL, (expression).getCalleeExpression()); - if (call != null) { - CallableDescriptor resultingDescriptor = call.getResultingDescriptor(); - if (AnnotationUtils.isArrayMethodCall(call)) { - JetType type = resultingDescriptor.getValueParameters().iterator().next().getVarargElementType(); - List> arguments = Lists.newArrayList(); - for (ResolvedValueArgument descriptorToArgument : call.getValueArguments().values()) { - arguments.addAll(resolveArguments(descriptorToArgument.getArguments(), type, trace)); - } - return new ArrayValue(arguments, resultingDescriptor.getReturnType()); - } - - if (resultingDescriptor instanceof ConstructorDescriptor) { - JetType constructorReturnType = resultingDescriptor.getReturnType(); - assert constructorReturnType != null : "Constructor should have return type"; - if (DescriptorUtils.isAnnotationClass(constructorReturnType.getConstructor().getDeclarationDescriptor())) { - AnnotationDescriptorImpl descriptor = new AnnotationDescriptorImpl(); - descriptor.setAnnotationType(constructorReturnType); - AnnotationResolver.resolveAnnotationArgument(descriptor, call, trace); - return new AnnotationValue(descriptor); - } - } - - if (AnnotationUtils.isJavaClassMethodCall(call)) { - return new JavaClassValue(resultingDescriptor.getReturnType()); - } - } - return null; - } - - @NotNull - private static List> resolveArguments( - @NotNull List valueArguments, - @NotNull JetType expectedType, - @NotNull BindingTrace trace - ) { - List> constants = Lists.newArrayList(); - for (ValueArgument argument : valueArguments) { - JetExpression argumentExpression = argument.getArgumentExpression(); - if (argumentExpression != null) { - CompileTimeConstant constant = evaluate(argumentExpression, trace, expectedType); - if (constant != null) { - constants.add(constant); - } - } - } - return constants; - } - - @Override - public CompileTimeConstant visitJetElement(@NotNull JetElement element, Void data) { - return null; - } -} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/evaluate/ConstantExpressionEvaluator.kt b/compiler/frontend/src/org/jetbrains/jet/lang/evaluate/ConstantExpressionEvaluator.kt new file mode 100644 index 00000000000..72e6401c783 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/evaluate/ConstantExpressionEvaluator.kt @@ -0,0 +1,407 @@ +/* + * Copyright 2010-2013 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.jet.lang.evaluate + +import com.intellij.psi.tree.IElementType +import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptorImpl +import org.jetbrains.jet.lang.descriptors.* +import org.jetbrains.jet.lang.psi.* +import org.jetbrains.jet.lang.resolve.* +import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall +import org.jetbrains.jet.lang.resolve.constants.* +import org.jetbrains.jet.lang.resolve.name.Name +import org.jetbrains.jet.lang.types.JetType +import org.jetbrains.jet.lang.types.expressions.OperatorConventions +import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns +import org.jetbrains.jet.lexer.JetTokens +import org.jetbrains.jet.lang.resolve.BindingContext.COMPILE_TIME_INITIALIZER +import org.jetbrains.jet.lang.resolve.calls.tasks.ExplicitReceiverKind +import org.jetbrains.jet.lang.types.TypeUtils +import java.lang.Short as JShort +import java.lang.Byte as JByte + +[suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")] +public class ConstantExpressionEvaluator private (val trace: BindingTrace) : JetVisitor, JetType>() { + + public fun evaluate(expression: JetExpression, expectedType: JetType?): CompileTimeConstant<*>? { + val recordedCompileTimeConstant = trace.get(BindingContext.COMPILE_TIME_VALUE, expression) + if (recordedCompileTimeConstant != null) { + return recordedCompileTimeConstant + } + + val compileTimeConstant = expression.accept(this, expectedType ?: TypeUtils.NO_EXPECTED_TYPE) + if (compileTimeConstant != null) { + trace.record(BindingContext.COMPILE_TIME_VALUE, expression, compileTimeConstant) + return compileTimeConstant + } + + return null + } + + override fun visitConstantExpression(expression: JetConstantExpression, expectedType: JetType?): CompileTimeConstant<*>? { + return trace.get(BindingContext.COMPILE_TIME_VALUE, expression) + } + + override fun visitParenthesizedExpression(expression: JetParenthesizedExpression, expectedType: JetType?): CompileTimeConstant<*>? { + val deparenthesizedExpression = getDeparenthesizedExpression(expression) + if (deparenthesizedExpression != null) { + return evaluate(deparenthesizedExpression, expectedType) + } + return null + } + + override fun visitPrefixExpression(expression: JetPrefixExpression, expectedType: JetType?): CompileTimeConstant<*>? { + val deparenthesizedExpression = getDeparenthesizedExpression(expression) + return if (deparenthesizedExpression != null) { + evaluate(deparenthesizedExpression, expectedType) + } else { + super.visitPrefixExpression(expression, expectedType) + } + } + + override fun visitStringTemplateExpression(expression: JetStringTemplateExpression, expectedType: JetType?): CompileTimeConstant<*>? { + val stringTemplateEntries = expression.getEntries() + if (stringTemplateEntries.size == 1) { + return stringTemplateEntries[0].accept(this, null) + } + else if (stringTemplateEntries.size > 1) { + return stringTemplateEntries.fold(StringValue(""): CompileTimeConstant<*>?) { + f, s -> + if (f != null) { + val second = s.accept(this, null) + if (second == null) { + null + } + else { + createCompileTimeConstant(evaluateBinaryExpression(f, second, Name.identifier("plus")), expectedType) + } + } + else { + null + } + } + } + return null + } + + override fun visitStringTemplateEntryWithExpression(entry: JetStringTemplateEntryWithExpression, expectedType: JetType?): CompileTimeConstant<*>? { + val expression = entry.getExpression() + if (expression != null) { + return createStringConstant(evaluate(expression, expectedType)) + } + return null + } + + override fun visitLiteralStringTemplateEntry(entry: JetLiteralStringTemplateEntry, expectedType: JetType?) = StringValue(entry.getText()) + + override fun visitEscapeStringTemplateEntry(entry: JetEscapeStringTemplateEntry, expectedType: JetType?) = StringValue(entry.getUnescapedValue()) + + override fun visitBinaryExpression(expression: JetBinaryExpression, expectedType: JetType?): CompileTimeConstant<*>? { + val leftExpression = expression.getLeft() + if (leftExpression == null) { + return null + } + + val operationToken = expression.getOperationToken() + if (OperatorConventions.BOOLEAN_OPERATIONS.containsKey(operationToken)) { + val booleanType = KotlinBuiltIns.getInstance().getBooleanType() + val leftConstant = evaluate(leftExpression, booleanType) + if (leftConstant == null) { + return null + } + + val rightExpression = expression.getRight() + if (rightExpression == null) { + return null + } + + val rightConstant = evaluate(rightExpression, booleanType) + if (rightConstant == null) { + return null + } + + val operationName = when(operationToken) { + JetTokens.ANDAND -> Name.identifier("&&") + JetTokens.OROR -> Name.identifier("||") + else -> throw IllegalArgumentException("Unknown boolean operation token ${operationToken}") + } + val result = evaluateBinaryExpression(leftConstant, rightConstant, operationName) + return createCompileTimeConstant(result, expectedType) + } + else { + val result = evaluateCall(expression.getOperationReference(), leftExpression) + return when(operationToken) { + in OperatorConventions.COMPARISON_OPERATIONS -> createCompileTimeConstantForCompareTo(result, operationToken!!) + in OperatorConventions.EQUALS_OPERATIONS -> createCompileTimeConstantForEquals(result, operationToken!!) + else -> createCompileTimeConstant(result, expectedType) + } + } + } + + private fun evaluateCall(callExpression: JetExpression, receiverExpression: JetExpression): Any? { + val resolvedCall = trace.getBindingContext().get(BindingContext.RESOLVED_CALL, callExpression) + if (resolvedCall != null) { + val resultingDescriptor = resolvedCall.getResultingDescriptor() + // TODO getResultingDescriptor has NotNull annotation + if (resultingDescriptor == null) { + return null + } + val receiverExpressionType = getReceiverExpressionType(resolvedCall) + if (receiverExpressionType == null) { + return null + } + + val receiverValue = evaluate(receiverExpression, receiverExpressionType) + if (receiverValue == null) { + return null + } + + val arguments = resolvedCall.getValueArguments().entrySet().flatMap { + entry -> val (parameter, argument) = entry + resolveArguments(argument.getArguments(), parameter.getType()) + } + + val resultingDescriptorName = resultingDescriptor.getName() + if (arguments.isEmpty()) { + return evaluateUnaryExpression(receiverValue, resultingDescriptorName) + } + else if (arguments.size() == 1) { + return evaluateBinaryExpression(receiverValue, arguments.first!!, resultingDescriptorName) + } + } + + return null + } + + override fun visitUnaryExpression(expression: JetUnaryExpression, expectedType: JetType?): CompileTimeConstant<*>? { + val leftExpression = expression.getBaseExpression() + if (leftExpression == null) return null + + val result = evaluateCall(expression.getOperationReference(), leftExpression) + return createCompileTimeConstant(result, expectedType) + } + + override fun visitSimpleNameExpression(expression: JetSimpleNameExpression, expectedType: JetType?): CompileTimeConstant<*>? { + val enumDescriptor = trace.getBindingContext().get(BindingContext.REFERENCE_TARGET, expression); + if (enumDescriptor != null && DescriptorUtils.isEnumEntry(enumDescriptor)) { + return EnumValue(enumDescriptor as ClassDescriptor); + } + + val resolvedCall = trace.getBindingContext().get(BindingContext.RESOLVED_CALL, expression) + if (resolvedCall != null) { + val callableDescriptor = resolvedCall.getResultingDescriptor() + if (callableDescriptor is PropertyDescriptor) { + if (AnnotationUtils.isPropertyCompileTimeConstant(callableDescriptor)) { + return trace.getBindingContext().get(COMPILE_TIME_INITIALIZER, callableDescriptor) + } + } + } + return null + } + + /* 1.toInt(); 1.plus(1); MyEnum.A */ + override fun visitQualifiedExpression(expression: JetQualifiedExpression, expectedType: JetType?): CompileTimeConstant<*>? { + val selectorExpression = expression.getSelectorExpression() + if (selectorExpression is JetCallExpression) { + val calleeExpression = selectorExpression.getCalleeExpression() + if (calleeExpression !is JetSimpleNameExpression) { + return null + } + + val receiverExpression = expression.getReceiverExpression() + val result = evaluateCall(calleeExpression, receiverExpression) + return createCompileTimeConstant(result, expectedType) + } + + // TODO + if (selectorExpression != null) { + return evaluate(selectorExpression, expectedType) + } + + return null + } + + override fun visitCallExpression(expression: JetCallExpression, expectedType: JetType?): CompileTimeConstant<*>? { + val call = trace.getBindingContext().get(BindingContext.RESOLVED_CALL, expression.getCalleeExpression()) + if (call != null) { + val resultingDescriptor = call.getResultingDescriptor() + if (resultingDescriptor != null) { + if (AnnotationUtils.isArrayMethodCall(call)) { + val varargType = resultingDescriptor.getValueParameters().first?.getVarargElementType()!! + + //todo flatmap + val arguments = arrayListOf>() + for (descriptorToArgument in call.getValueArguments().values()) { + arguments.addAll(resolveArguments(descriptorToArgument.getArguments(), varargType)) + } + return ArrayValue(arguments, resultingDescriptor.getReturnType()!!) + } + + // Ann() + if (resultingDescriptor is ConstructorDescriptor) { + val constructorReturnType = resultingDescriptor.getReturnType() + if (DescriptorUtils.isAnnotationClass(resultingDescriptor.getContainingDeclaration())) { + val descriptor = AnnotationDescriptorImpl() + descriptor.setAnnotationType(constructorReturnType) + AnnotationResolver.resolveAnnotationArgument(descriptor, call, trace) + return AnnotationValue(descriptor) + } + } + + if (AnnotationUtils.isJavaClassMethodCall(call)) { + return JavaClassValue(resultingDescriptor.getReturnType()) + } + } + } + + return null + } + + private fun resolveArguments(valueArguments: List, expectedType: JetType): List> { + //todo flatMap + val constants = arrayListOf>() + for (argument in valueArguments) { + val argumentExpression = argument.getArgumentExpression() + if (argumentExpression != null) { + val constant = evaluate(argumentExpression, expectedType) + if (constant != null) { + constants.add(constant) + } + } + } + return constants + } + + override fun visitJetElement(element: JetElement, expectedType: JetType?): CompileTimeConstant<*>? { + return null + } + + class object { + public fun evaluate(expression: JetExpression, trace: BindingTrace, expectedType: JetType? = TypeUtils.NO_EXPECTED_TYPE): CompileTimeConstant<*>? { + val evaluator = ConstantExpressionEvaluator(trace) + return evaluator.evaluate(expression, expectedType) + } + } +} + +private fun getDeparenthesizedExpression(expression: JetExpression): JetExpression? { + val deparenthesizedExpr = JetPsiUtil.deparenthesize(expression) + if (deparenthesizedExpr == expression) { + return null + } + + return deparenthesizedExpr +} + +private fun createCompileTimeConstantForEquals(result: Any?, operationToken: IElementType): CompileTimeConstant<*>? { + if (result is Boolean) { + return when (operationToken) { + JetTokens.EQEQ -> if (result) BooleanValue.TRUE else BooleanValue.FALSE + JetTokens.EXCLEQ -> if (result) BooleanValue.FALSE else BooleanValue.TRUE + else -> throw IllegalStateException("Unknown equals operation token: $operationToken") + } + } + return null +} + +private fun createCompileTimeConstantForCompareTo(result: Any?, operationToken: IElementType): CompileTimeConstant<*>? { + if (result is Int) { + return when (result) { + -1 -> when (operationToken) { + JetTokens.LT -> BooleanValue.TRUE + JetTokens.LTEQ -> BooleanValue.TRUE + else -> BooleanValue.FALSE + } + 0 -> when (operationToken) { + JetTokens.LTEQ -> BooleanValue.TRUE + JetTokens.GTEQ -> BooleanValue.TRUE + else -> BooleanValue.FALSE + } + 1 -> when (operationToken) { + JetTokens.GT -> BooleanValue.TRUE + JetTokens.GTEQ -> BooleanValue.TRUE + else -> BooleanValue.FALSE + } + else -> null + } + } + return null +} + +private fun createStringConstant(value: CompileTimeConstant<*>?): CompileTimeConstant<*>? { + return when (value) { + null -> null + is StringValue -> value + else -> StringValue(value.getValue().toString()) + } +} + +private fun createCompileTimeConstant(value: Any?, expectedType: JetType?): CompileTimeConstant<*>? { + return when(value) { + null -> null + is Byte, is Short, is Int, is Long -> getIntegerValue((value as Number).toLong(), expectedType ?: TypeUtils.NO_EXPECTED_TYPE) + is Char -> CharValue(value) + is Float -> if (CompileTimeConstantResolver.noExpectedTypeOrError(expectedType) || + expectedType == KotlinBuiltIns.getInstance().getDoubleType()) DoubleValue(value.toDouble()) + else FloatValue(value) + is Double -> DoubleValue(value) + is Boolean -> if (value) BooleanValue.TRUE else BooleanValue.FALSE + is String -> StringValue(value) + else -> null + } +} +private fun getIntegerValue(value: Long, expectedType: JetType): CompileTimeConstant<*>? { + if (CompileTimeConstantResolver.noExpectedTypeOrError(expectedType)) { + if (Integer.MIN_VALUE <= value && value <= Integer.MAX_VALUE) { + return IntValue(value.toInt()) + } + + return LongValue(value) + } + + fun defaultIntegerValue(value: Long) = when (value) { + in Integer.MIN_VALUE..Integer.MAX_VALUE.toLong() -> IntValue(value.toInt()) + else -> LongValue(value) + } + + val builtIns = KotlinBuiltIns.getInstance() + return when (TypeUtils.makeNotNullable(expectedType)) { + builtIns.getIntType() -> IntValue(value.toInt()) + builtIns.getLongType() -> LongValue(value) + builtIns.getShortType() -> when (value) { + in JShort.MIN_VALUE..JShort.MAX_VALUE.toLong() -> ShortValue(value.toShort()) + else -> defaultIntegerValue(value) + } + builtIns.getByteType() -> when (value) { + in JByte.MIN_VALUE..JByte.MAX_VALUE.toLong() -> ByteValue(value.toByte()) + else -> defaultIntegerValue(value) + } + builtIns.getCharType() -> IntValue(value.toInt()) + else -> defaultIntegerValue(value) + } +} + +private fun getReceiverExpressionType(resolvedCall: ResolvedCall<*>): JetType? { + return when (resolvedCall.getExplicitReceiverKind()) { + ExplicitReceiverKind.THIS_OBJECT -> resolvedCall.getThisObject().getType() + ExplicitReceiverKind.RECEIVER_ARGUMENT -> resolvedCall.getReceiverArgument().getType() + ExplicitReceiverKind.NO_EXPLICIT_RECEIVER -> null + ExplicitReceiverKind.BOTH_RECEIVERS -> null + else -> null + } +} + diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/evaluate/constantExpressionEvaluator.kt b/compiler/frontend/src/org/jetbrains/jet/lang/evaluate/constantExpressionEvaluatorUtils.kt similarity index 97% rename from compiler/frontend/src/org/jetbrains/jet/lang/evaluate/constantExpressionEvaluator.kt rename to compiler/frontend/src/org/jetbrains/jet/lang/evaluate/constantExpressionEvaluatorUtils.kt index c2fc6a3211b..5dcff0544a3 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/evaluate/constantExpressionEvaluator.kt +++ b/compiler/frontend/src/org/jetbrains/jet/lang/evaluate/constantExpressionEvaluatorUtils.kt @@ -62,20 +62,27 @@ private val CHAR = CompileTimeType() private val BOOLEAN = CompileTimeType() private val STRING = CompileTimeType() -private fun bOp(a: CompileTimeType, b: CompileTimeType, functionNameAsString: String, f: (A, B) -> Any) = BinaryOperation(a, b, Name.identifier(functionNameAsString)) to f as Function2 +[suppress("UNCHECKED_CAST")] +private fun bOp( + a: CompileTimeType, + b: CompileTimeType, + functionNameAsString: String, + f: (A, B) -> Any +) = BinaryOperation(a, b, Name.identifier(functionNameAsString)) to f as Function2 + private fun uOp(a: CompileTimeType, functionNameAsString: String, f: (A) -> Any) = UnaryOperation(a, Name.identifier(functionNameAsString)) to f as Function1 private data class BinaryOperation(val f: CompileTimeType, val s: CompileTimeType, val functionName: Name) private data class UnaryOperation(val f: CompileTimeType, val functionName: Name) private val unaryOperations = hashMapOf, (Any?) -> Any>( - uOp(DOUBLE, "minus", { a -> a.minus() }), - uOp(FLOAT, "minus", { a -> a.minus() }), - uOp(LONG, "minus", { a -> a.minus() }), - uOp(INT, "minus", { a -> a.minus() }), - uOp(SHORT, "minus", { a -> a.minus() }), - uOp(BYTE, "minus", { a -> a.minus() }), - uOp(CHAR, "minus", { a -> a.minus() }), + uOp(DOUBLE, "minus", { -it }), + uOp(FLOAT, "minus", { -it }), + uOp(LONG, "minus", { -it }), + uOp(INT, "minus", { -it }), + uOp(SHORT, "minus", { -it }), + uOp(BYTE, "minus", { -it }), + uOp(CHAR, "minus", { -it }), uOp(DOUBLE, "plus", { a -> a.plus() }), uOp(FLOAT, "plus", { a -> a.plus() }), uOp(LONG, "plus", { a -> a.plus() }), diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/AnnotationResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/AnnotationResolver.java index fe792882d12..98265aac2b4 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/AnnotationResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/AnnotationResolver.java @@ -24,7 +24,7 @@ import org.jetbrains.jet.lang.descriptors.annotations.Annotated; import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor; import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptorImpl; import org.jetbrains.jet.lang.diagnostics.Errors; -import org.jetbrains.jet.lang.evaluate.ConstantExpressionEvaluator; +import org.jetbrains.jet.lang.evaluate.EvaluatePackage; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.calls.CallResolver; import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; @@ -32,7 +32,8 @@ import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall; import org.jetbrains.jet.lang.resolve.calls.model.ResolvedValueArgument; import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults; import org.jetbrains.jet.lang.resolve.calls.util.CallMaker; -import org.jetbrains.jet.lang.resolve.constants.*; +import org.jetbrains.jet.lang.resolve.constants.ArrayValue; +import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant; import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue; import org.jetbrains.jet.lang.types.ErrorUtils; @@ -47,7 +48,6 @@ import java.util.List; import java.util.Map; import static org.jetbrains.jet.lang.resolve.BindingContext.ANNOTATION_DESCRIPTOR_TO_PSI_ELEMENT; -import static org.jetbrains.jet.lang.resolve.BindingContext.COMPILE_TIME_INITIALIZER; import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE; public class AnnotationResolver { @@ -257,7 +257,7 @@ public class AnnotationResolver { @NotNull JetType expectedType, @NotNull BindingTrace trace ) { - return ConstantExpressionEvaluator.evaluate(expression, trace, expectedType); + return EvaluatePackage.evaluate(expression, trace, expectedType); } @NotNull diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/AnnotationUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/AnnotationUtils.java index 35ef85149fb..80013dc570d 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/AnnotationUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/AnnotationUtils.java @@ -114,7 +114,7 @@ public class AnnotationUtils { return false; } - public static boolean isPropertyAcceptableAsAnnotationParameter(@NotNull PropertyDescriptor descriptor) { + public static boolean isPropertyCompileTimeConstant(@NotNull PropertyDescriptor descriptor) { if (descriptor.isVar()) { return false; } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java index 9cda2dcbb0a..eecd22e631d 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java @@ -555,7 +555,7 @@ public class BodyResolver { scope, propertyDescriptor.getTypeParameters(), NO_RECEIVER_PARAMETER, trace); JetType expectedTypeForInitializer = property.getTypeRef() != null ? propertyDescriptor.getType() : NO_EXPECTED_TYPE; expressionTypingServices.getType(propertyDeclarationInnerScope, initializer, expectedTypeForInitializer, context.getOuterDataFlowInfo(), trace); - if (AnnotationUtils.isPropertyAcceptableAsAnnotationParameter(propertyDescriptor)) { + if (AnnotationUtils.isPropertyCompileTimeConstant(propertyDescriptor)) { CompileTimeConstant constant = AnnotationResolver.resolveExpressionToCompileTimeValue(initializer, expectedTypeForInitializer, trace); if (constant != null) { trace.record(BindingContext.COMPILE_TIME_INITIALIZER, propertyDescriptor, constant);