001    /*
002     * Copyright 2010-2015 JetBrains s.r.o.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.jetbrains.kotlin.resolve;
018    
019    import kotlin.KotlinPackage;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
023    import org.jetbrains.kotlin.descriptors.ClassDescriptor;
024    import org.jetbrains.kotlin.descriptors.VariableDescriptor;
025    import org.jetbrains.kotlin.descriptors.annotations.Annotated;
026    import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
027    import org.jetbrains.kotlin.name.FqName;
028    import org.jetbrains.kotlin.psi.JetExpression;
029    import org.jetbrains.kotlin.psi.JetParameter;
030    import org.jetbrains.kotlin.psi.JetTypeReference;
031    import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
032    import org.jetbrains.kotlin.resolve.constants.BooleanValue;
033    import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
034    import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
035    import org.jetbrains.kotlin.types.JetType;
036    import org.jetbrains.kotlin.types.TypeProjection;
037    import org.jetbrains.kotlin.types.TypeUtils;
038    import org.jetbrains.kotlin.types.typeUtil.TypeUtilPackage;
039    
040    import java.util.Collection;
041    import java.util.List;
042    import java.util.Set;
043    
044    import static org.jetbrains.kotlin.diagnostics.Errors.INVALID_TYPE_OF_ANNOTATION_MEMBER;
045    import static org.jetbrains.kotlin.diagnostics.Errors.JAVA_LANG_CLASS_PARAMETER_IN_ANNOTATION;
046    import static org.jetbrains.kotlin.diagnostics.Errors.NULLABLE_TYPE_OF_ANNOTATION_MEMBER;
047    import static org.jetbrains.kotlin.resolve.BindingContext.VALUE_PARAMETER;
048    import static org.jetbrains.kotlin.resolve.DescriptorUtils.isAnnotationClass;
049    import static org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumClass;
050    
051    public class CompileTimeConstantUtils {
052    
053        private final static Set<String> ARRAY_CALL_NAMES = KotlinPackage.hashSetOf(
054                "kotlin.array", "kotlin.doubleArray", "kotlin.floatArray", "kotlin.longArray", "kotlin.intArray", "kotlin.charArray",
055                "kotlin.shortArray", "kotlin.byteArray", "kotlin.booleanArray",
056                "kotlin.arrayOf", "kotlin.doubleArrayOf", "kotlin.floatArrayOf", "kotlin.longArrayOf", "kotlin.intArrayOf", "kotlin.charArrayOf",
057                "kotlin.shortArrayOf", "kotlin.byteArrayOf", "kotlin.booleanArrayOf"
058        );
059    
060        public static void checkConstructorParametersType(@NotNull List<JetParameter> parameters, @NotNull BindingTrace trace) {
061            for (JetParameter parameter : parameters) {
062                VariableDescriptor parameterDescriptor = trace.getBindingContext().get(VALUE_PARAMETER, parameter);
063                if (parameterDescriptor == null) continue;
064                JetType parameterType = parameterDescriptor.getType();
065                JetTypeReference typeReference = parameter.getTypeReference();
066                if (typeReference != null) {
067                    if (parameterType.isMarkedNullable()) {
068                        trace.report(NULLABLE_TYPE_OF_ANNOTATION_MEMBER.on(typeReference));
069                    }
070                    else if (!isAcceptableTypeForAnnotationParameter(parameterType)) {
071                        trace.report(INVALID_TYPE_OF_ANNOTATION_MEMBER.on(typeReference));
072                    }
073                    else if (TypeUtilPackage.isJavaLangClassOrArray(parameterType)) {
074                        trace.report(JAVA_LANG_CLASS_PARAMETER_IN_ANNOTATION.on(typeReference));
075                    }
076                }
077            }
078        }
079    
080        private static boolean isAcceptableTypeForAnnotationParameter(@NotNull JetType parameterType) {
081            ClassDescriptor typeDescriptor = TypeUtils.getClassDescriptor(parameterType);
082            if (typeDescriptor == null) {
083                return false;
084            }
085    
086            KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance();
087            if (isEnumClass(typeDescriptor) ||
088                isAnnotationClass(typeDescriptor) ||
089                DescriptorUtils.isJavaLangClass(typeDescriptor) ||
090                KotlinBuiltIns.isKClass(typeDescriptor) ||
091                KotlinBuiltIns.isPrimitiveArray(parameterType) ||
092                KotlinBuiltIns.isPrimitiveType(parameterType) ||
093                builtIns.getStringType().equals(parameterType)) {
094                    return true;
095            }
096    
097            if (KotlinBuiltIns.isArray(parameterType)) {
098                List<TypeProjection> arguments = parameterType.getArguments();
099                if (arguments.size() == 1) {
100                    JetType arrayType = arguments.get(0).getType();
101                    if (arrayType.isMarkedNullable()) {
102                        return false;
103                    }
104                    ClassDescriptor arrayTypeDescriptor = TypeUtils.getClassDescriptor(arrayType);
105                    if (arrayTypeDescriptor != null) {
106                        return isEnumClass(arrayTypeDescriptor) ||
107                               isAnnotationClass(arrayTypeDescriptor) ||
108                               DescriptorUtils.isJavaLangClass(arrayTypeDescriptor) ||
109                               KotlinBuiltIns.isKClass(arrayTypeDescriptor) ||
110                               builtIns.getStringType().equals(arrayType);
111                    }
112                }
113            }
114            return false;
115        }
116    
117        @Nullable
118        public static String getIntrinsicAnnotationArgument(@NotNull Annotated annotatedDescriptor) {
119            AnnotationDescriptor intrinsicAnnotation =
120                    annotatedDescriptor.getAnnotations().findAnnotation(new FqName("kotlin.jvm.internal.Intrinsic"));
121            if (intrinsicAnnotation == null) return null;
122    
123            Collection<CompileTimeConstant<?>> values = intrinsicAnnotation.getAllValueArguments().values();
124            if (values.isEmpty()) return null;
125    
126            Object value = values.iterator().next().getValue();
127            return value instanceof String ? (String) value : null;
128        }
129    
130        public static boolean isArrayMethodCall(@NotNull ResolvedCall<?> resolvedCall) {
131            return ARRAY_CALL_NAMES.contains(DescriptorUtils.getFqName(resolvedCall.getCandidateDescriptor()).asString());
132        }
133    
134        public static boolean isJavaClassMethodCall(@NotNull ResolvedCall<?> resolvedCall) {
135            return "kotlin.javaClass.function".equals(getIntrinsicAnnotationArgument(resolvedCall.getResultingDescriptor().getOriginal()));
136        }
137    
138        public static boolean canBeReducedToBooleanConstant(
139                @Nullable JetExpression expression,
140                @NotNull BindingTrace trace,
141                @Nullable Boolean expectedValue
142        ) {
143            if (expression == null) return false;
144            CompileTimeConstant<?> compileTimeConstant =
145                    ConstantExpressionEvaluator.evaluate(expression, trace, KotlinBuiltIns.getInstance().getBooleanType());
146            if (!(compileTimeConstant instanceof BooleanValue) || compileTimeConstant.usesVariableAsConstant()) return false;
147    
148            Boolean value = ((BooleanValue) compileTimeConstant).getValue();
149            return expectedValue == null || expectedValue.equals(value);
150        }
151    
152        private CompileTimeConstantUtils() {
153        }
154    }