Write constant initializer for java properties

This commit is contained in:
Natalia Ukhorskaya
2014-02-07 15:35:43 +04:00
parent 74c4234c64
commit 17259a052e
18 changed files with 135 additions and 38 deletions
@@ -0,0 +1 @@
org.jetbrains.jet.lang.resolve.java.structure.impl.JavaPropertyInitializerEvaluatorImpl
@@ -77,20 +77,6 @@ public class TraceBasedJavaResolverCache implements JavaResolverCache {
public void recordField(@NotNull JavaField field, @NotNull PropertyDescriptor descriptor) {
PsiField psiField = ((JavaFieldImpl) field).getPsi();
trace.record(VARIABLE, psiField, descriptor);
if (!descriptor.isVar()) {
PsiExpression initializer = psiField.getInitializer();
Object evaluatedExpression = JavaConstantExpressionEvaluator.computeConstantExpression(initializer, false);
if (evaluatedExpression != null) {
CompileTimeConstant<?> constant =
ResolverPackage.resolveCompileTimeConstantValue(evaluatedExpression,
CompileTimeConstantUtils.isPropertyCompileTimeConstant(descriptor),
descriptor.getType());
if (constant != null) {
trace.record(COMPILE_TIME_INITIALIZER, descriptor, constant);
}
}
}
}
@Override
@@ -17,8 +17,10 @@
package org.jetbrains.jet.lang.resolve.java.structure.impl;
import com.intellij.psi.PsiEnumConstant;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiField;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.resolve.java.structure.JavaField;
import org.jetbrains.jet.lang.resolve.java.structure.JavaType;
@@ -37,4 +39,9 @@ public class JavaFieldImpl extends JavaMemberImpl<PsiField> implements JavaField
public JavaType getType() {
return JavaTypeImpl.create(getPsi().getType());
}
@Nullable
public PsiExpression getInitializer() {
return getPsi().getInitializer();
}
}
@@ -0,0 +1,45 @@
/*
* Copyright 2010-2014 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.resolve.java.structure.impl;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.impl.JavaConstantExpressionEvaluator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
import org.jetbrains.jet.lang.resolve.constants.ConstantUtils;
import org.jetbrains.jet.lang.resolve.java.structure.JavaField;
import org.jetbrains.jet.lang.resolve.java.structure.JavaPropertyInitializerEvaluator;
public class JavaPropertyInitializerEvaluatorImpl extends JavaPropertyInitializerEvaluator {
@Nullable
@Override
public CompileTimeConstant<?> getInitializerConstant(@NotNull JavaField field, @NotNull PropertyDescriptor descriptor) {
PsiExpression initializer = ((JavaFieldImpl) field).getInitializer();
Object evaluatedExpression = JavaConstantExpressionEvaluator.computeConstantExpression(initializer, false);
if (evaluatedExpression != null) {
return ConstantUtils.createCompileTimeConstant(
evaluatedExpression,
DescriptorUtils.isPropertyCompileTimeConstant(descriptor),
false,
descriptor.getType());
}
return null;
}
}
@@ -82,17 +82,20 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
override fun visitConstantExpression(expression: JetConstantExpression, expectedType: JetType?): CompileTimeConstant<*>? {
val text = expression.getText()
if (text == null) return null
val result: Any? = when (expression.getNode().getElementType()) {
val nodeElementType = expression.getNode().getElementType()
if (nodeElementType == JetNodeTypes.NULL) return NullValue.NULL
val result: Any? = when (nodeElementType) {
JetNodeTypes.INTEGER_CONSTANT -> parseLong(text)
JetNodeTypes.FLOAT_CONSTANT -> parseFloatingLiteral(text)
JetNodeTypes.BOOLEAN_CONSTANT -> parseBoolean(text)
JetNodeTypes.CHARACTER_CONSTANT -> CompileTimeConstantChecker.parseChar(expression)
JetNodeTypes.NULL -> null
else -> throw IllegalArgumentException("Unsupported constant: " + expression)
}
if (result == null && expression.getNode().getElementType() == JetNodeTypes.NULL) return NullValue.NULL
if (result == null) return null
fun isLongWithSuffix() = expression.getNode().getElementType() == JetNodeTypes.INTEGER_CONSTANT && hasLongSuffix(text)
fun isLongWithSuffix() = nodeElementType == JetNodeTypes.INTEGER_CONSTANT && hasLongSuffix(text)
return createCompileTimeConstant(result, expectedType, !isLongWithSuffix())
}
@@ -181,6 +184,7 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
val argumentsEntrySet = resolvedCall.getValueArguments().entrySet()
if (argumentsEntrySet.isEmpty()) {
val result = evaluateUnaryAndCheck(argumentForReceiver, resultingDescriptorName.asString(), callExpression)
if (result == null) return null
val isArgumentPure = isPureConstant(argumentForReceiver.expression)
val canBeUsedInAnnotation = canBeUsedInAnnotation(argumentForReceiver.expression)
val isNumberConversionMethod = resultingDescriptorName in OperatorConventions.NUMBER_CONVERSIONS
@@ -197,6 +201,7 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
}
val result = evaluateBinaryAndCheck(argumentForReceiver, argumentForParameter, resultingDescriptorName.asString(), callExpression)
if (result == null) return null
val areArgumentsPure = isPureConstant(argumentForReceiver.expression) && isPureConstant(argumentForParameter.expression)
val canBeUsedInAnnotation = canBeUsedInAnnotation(argumentForReceiver.expression) && canBeUsedInAnnotation(argumentForParameter.expression)
@@ -303,7 +308,7 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
else
compileTimeConstant.getValue()
return createCompileTimeConstant(value, expectedType, isPure = false,
canBeUsedInAnnotation = CompileTimeConstantUtils.isPropertyCompileTimeConstant(callableDescriptor))
canBeUsedInAnnotation = DescriptorUtils.isPropertyCompileTimeConstant(callableDescriptor))
}
}
return null
@@ -120,17 +120,6 @@ public class CompileTimeConstantUtils {
return "kotlin.javaClass.function".equals(getIntrinsicAnnotationArgument(resolvedCall.getResultingDescriptor().getOriginal()));
}
public static boolean isPropertyCompileTimeConstant(@NotNull VariableDescriptor descriptor) {
if (descriptor.isVar()) {
return false;
}
if (isClassObject(descriptor.getContainingDeclaration()) || isTopLevelDeclaration(descriptor)) {
JetType type = descriptor.getType();
return KotlinBuiltIns.getInstance().isPrimitiveType(type) || KotlinBuiltIns.getInstance().getStringType().equals(type);
}
return false;
}
public static boolean isJavaLangClass(ClassDescriptor descriptor) {
return "java.lang.Class".equals(DescriptorUtils.getFqName(descriptor).asString());
}
@@ -1,6 +1,6 @@
package test
internal val checkTypeProp: test.PropType?
internal val checkTypeProp: test.PropType? = null
internal final class PropType {
/*primary*/ public constructor PropType()
@@ -13,5 +13,5 @@ public trait StringConstantInParam : java.lang.Object {
}
package test.StringConstantInParam {
public val HEL: kotlin.String
public val HEL: kotlin.String = "hel"
}
@@ -5,5 +5,5 @@ public open class StaticFinal : java.lang.Object {
}
package test.StaticFinal {
public val foo: kotlin.String
public val foo: kotlin.String = "aaa"
}
@@ -2,5 +2,5 @@ package test
public final class FinalFieldAsVal : java.lang.Object {
public constructor FinalFieldAsVal()
public final val f: kotlin.Int
public final val f: kotlin.Int = 1.toInt()
}
@@ -4,5 +4,5 @@ import java.util.*
public open class WrongFieldMutability : Object() {
public var fooNotFinal : String? = ""
public val fooFinal : String? = ""
public val fooFinal : String? = "Test"
}
@@ -2,6 +2,6 @@ package test
public open class WrongFieldMutability : java.lang.Object {
public constructor WrongFieldMutability()
public final val fooFinal: kotlin.String?
public final val fooFinal: kotlin.String? = "Test"
public final var fooNotFinal: kotlin.String?
}
@@ -109,7 +109,7 @@ class LazyJavaAnnotationDescriptor(
private fun resolveAnnotationArgument(argument: JavaAnnotationArgument?): CompileTimeConstant<*>? {
return when (argument) {
is JavaLiteralAnnotationArgument -> resolveCompileTimeConstantValue(argument.getValue(), true, null)
is JavaLiteralAnnotationArgument -> ConstantUtils.createCompileTimeConstant(argument.getValue(), true, false, null)
is JavaReferenceAnnotationArgument -> resolveFromReference(argument.resolve())
is JavaArrayAnnotationArgument -> resolveFromArray(argument.getName() ?: DEFAULT_ANNOTATION_MEMBER_NAME, argument.getElements())
is JavaAnnotationAsAnnotationArgument -> resolveFromAnnotation(argument.getAnnotation())
@@ -34,6 +34,7 @@ import org.jetbrains.jet.lang.resolve.java.resolver.ExternalSignatureResolver
import org.jetbrains.jet.lang.resolve.java.sam.SingleAbstractMethodUtils
import org.jetbrains.jet.utils.Printer
import org.jetbrains.jet.lang.resolve.java.descriptor.JavaPackageFragmentDescriptor
import org.jetbrains.jet.lang.resolve.java.structure.JavaPropertyInitializerEvaluator
public abstract class LazyJavaMemberScope(
protected val c: LazyJavaResolverContextWithTypes,
@@ -265,6 +266,10 @@ public abstract class LazyJavaMemberScope(
propertyDescriptor.setType(effectiveSignature.getReturnType(), Collections.emptyList(), DescriptorUtils.getExpectedThisObjectIfNeeded(getContainingDeclaration()), null : JetType?)
if (!propertyDescriptor.isVar()) {
propertyDescriptor.setCompileTimeInitializer(JavaPropertyInitializerEvaluator.getInstance().getInitializerConstant(field, propertyDescriptor))
}
c.javaResolverCache.recordField(field, propertyDescriptor);
return propertyDescriptor
@@ -0,0 +1,43 @@
/*
* Copyright 2010-2014 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.resolve.java.structure;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
import java.util.Iterator;
import java.util.ServiceLoader;
public abstract class JavaPropertyInitializerEvaluator {
private static JavaPropertyInitializerEvaluator instance;
@NotNull
public static JavaPropertyInitializerEvaluator getInstance() {
if (instance == null) {
Iterator<JavaPropertyInitializerEvaluator> iterator =
ServiceLoader.load(JavaPropertyInitializerEvaluator.class, JavaPropertyInitializerEvaluator.class.getClassLoader()).iterator();
assert iterator.hasNext() : "No service found: " + JavaPropertyInitializerEvaluator.class.getName();
instance = iterator.next();
}
return instance;
}
@Nullable
public abstract CompileTimeConstant<?> getInitializerConstant(@NotNull JavaField field, @NotNull PropertyDescriptor descriptor);
}
@@ -28,6 +28,7 @@ import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptorImpl;
import org.jetbrains.jet.lang.descriptors.annotations.Annotations;
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationsImpl;
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
import org.jetbrains.jet.lang.resolve.constants.ConstantUtils;
import org.jetbrains.jet.lang.resolve.constants.EnumValue;
import org.jetbrains.jet.lang.resolve.constants.ErrorValue;
import org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames;
@@ -38,6 +39,7 @@ import org.jetbrains.jet.lang.resolve.java.resolver.ResolverPackage;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.types.DependencyClassByQualifiedNameResolver;
import org.jetbrains.jet.lang.types.ErrorUtils;
import org.jetbrains.jet.lang.types.JetType;
import javax.inject.Inject;
import java.io.IOException;
@@ -134,7 +136,7 @@ public class AnnotationDescriptorDeserializer extends BaseDescriptorDeserializer
@Override
public void visit(@Nullable Name name, @Nullable Object value) {
if (name != null) {
CompileTimeConstant<?> argument = ResolverPackage.resolveCompileTimeConstantValue(value, true, null);
CompileTimeConstant<?> argument = ConstantUtils.createCompileTimeConstant(value, true, false, null);
setArgumentValueByName(name, argument != null ? argument : ErrorValue.create("Unsupported annotation argument: " + name));
}
}
@@ -484,4 +484,15 @@ public class DescriptorUtils {
}
return descriptor;
}
public static boolean isPropertyCompileTimeConstant(@NotNull VariableDescriptor descriptor) {
if (descriptor.isVar()) {
return false;
}
if (isClassObject(descriptor.getContainingDeclaration()) || isTopLevelDeclaration(descriptor)) {
JetType type = descriptor.getType();
return KotlinBuiltIns.getInstance().isPrimitiveType(type) || KotlinBuiltIns.getInstance().getStringType().equals(type);
}
return false;
}
}
@@ -119,6 +119,9 @@ public class ConstantUtils {
else if (value instanceof String) {
return new StringValue((String) value, canBeUsedInAnnotations);
}
else if (value == null) {
return NullValue.NULL;
}
return null;
}