001    /*
002     * Copyright 2010-2013 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.jet.lang.resolve;
018    
019    import org.jetbrains.annotations.NotNull;
020    import org.jetbrains.annotations.Nullable;
021    import org.jetbrains.jet.JetNodeTypes;
022    import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
023    import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
024    import org.jetbrains.jet.lang.descriptors.annotations.Annotated;
025    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
026    import org.jetbrains.jet.lang.evaluate.ConstantExpressionEvaluator;
027    import org.jetbrains.jet.lang.psi.JetConstantExpression;
028    import org.jetbrains.jet.lang.psi.JetExpression;
029    import org.jetbrains.jet.lang.psi.JetParameter;
030    import org.jetbrains.jet.lang.psi.JetTypeReference;
031    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
032    import org.jetbrains.jet.lang.resolve.constants.BooleanValue;
033    import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
034    import org.jetbrains.jet.lang.resolve.name.FqName;
035    import org.jetbrains.jet.lang.types.JetType;
036    import org.jetbrains.jet.lang.types.TypeProjection;
037    import org.jetbrains.jet.lang.types.TypeUtils;
038    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
039    
040    import java.util.Collection;
041    import java.util.List;
042    
043    import static org.jetbrains.jet.lang.diagnostics.Errors.INVALID_TYPE_OF_ANNOTATION_MEMBER;
044    import static org.jetbrains.jet.lang.diagnostics.Errors.NULLABLE_TYPE_OF_ANNOTATION_MEMBER;
045    import static org.jetbrains.jet.lang.resolve.BindingContext.VALUE_PARAMETER;
046    import static org.jetbrains.jet.lang.resolve.DescriptorUtils.*;
047    
048    public class CompileTimeConstantUtils {
049    
050        public static void checkConstructorParametersType(@NotNull List<JetParameter> parameters, @NotNull BindingTrace trace) {
051            for (JetParameter parameter : parameters) {
052                VariableDescriptor parameterDescriptor = trace.getBindingContext().get(VALUE_PARAMETER, parameter);
053                if (parameterDescriptor == null) continue;
054                JetType parameterType = parameterDescriptor.getType();
055                JetTypeReference typeReference = parameter.getTypeReference();
056                if (typeReference != null) {
057                    if (parameterType.isNullable()) {
058                        trace.report(NULLABLE_TYPE_OF_ANNOTATION_MEMBER.on(typeReference));
059                    }
060                    else if (!isAcceptableTypeForAnnotationParameter(parameterType)) {
061                        trace.report(INVALID_TYPE_OF_ANNOTATION_MEMBER.on(typeReference));
062                    }
063                }
064            }
065        }
066    
067        private static boolean isAcceptableTypeForAnnotationParameter(@NotNull JetType parameterType) {
068            ClassDescriptor typeDescriptor = TypeUtils.getClassDescriptor(parameterType);
069            if (typeDescriptor == null) {
070                return false;
071            }
072    
073            KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance();
074            if (isEnumClass(typeDescriptor) ||
075                isAnnotationClass(typeDescriptor) ||
076                isJavaLangClass(typeDescriptor) ||
077                builtIns.isPrimitiveArray(parameterType) ||
078                builtIns.isPrimitiveType(parameterType) ||
079                builtIns.getStringType().equals(parameterType)) {
080                    return true;
081            }
082    
083            if (builtIns.isArray(parameterType)) {
084                List<TypeProjection> arguments = parameterType.getArguments();
085                if (arguments.size() == 1) {
086                    JetType arrayType = arguments.get(0).getType();
087                    if (arrayType.isNullable()) {
088                        return false;
089                    }
090                    ClassDescriptor arrayTypeDescriptor = TypeUtils.getClassDescriptor(arrayType);
091                    if (arrayTypeDescriptor != null) {
092                        return isEnumClass(arrayTypeDescriptor) ||
093                               isAnnotationClass(arrayTypeDescriptor) ||
094                               isJavaLangClass(arrayTypeDescriptor) ||
095                               builtIns.getStringType().equals(arrayType);
096                    }
097                }
098            }
099            return false;
100        }
101    
102        @Nullable
103        public static String getIntrinsicAnnotationArgument(@NotNull Annotated annotatedDescriptor) {
104            AnnotationDescriptor intrinsicAnnotation =
105                    annotatedDescriptor.getAnnotations().findAnnotation(new FqName("kotlin.jvm.internal.Intrinsic"));
106            if (intrinsicAnnotation == null) return null;
107    
108            Collection<CompileTimeConstant<?>> values = intrinsicAnnotation.getAllValueArguments().values();
109            if (values.isEmpty()) return null;
110    
111            Object value = values.iterator().next().getValue();
112            return value instanceof String ? (String) value : null;
113        }
114    
115        public static boolean isArrayMethodCall(@NotNull ResolvedCall<?> resolvedCall) {
116            return "kotlin.arrays.array".equals(getIntrinsicAnnotationArgument(resolvedCall.getResultingDescriptor().getOriginal()));
117        }
118    
119        public static boolean isJavaClassMethodCall(@NotNull ResolvedCall<?> resolvedCall) {
120            return "kotlin.javaClass.function".equals(getIntrinsicAnnotationArgument(resolvedCall.getResultingDescriptor().getOriginal()));
121        }
122    
123        public static boolean isJavaLangClass(ClassDescriptor descriptor) {
124            return "java.lang.Class".equals(DescriptorUtils.getFqName(descriptor).asString());
125        }
126    
127        public static boolean canBeReducedToBooleanConstant(
128                @Nullable JetExpression expression,
129                @NotNull BindingTrace trace,
130                @Nullable Boolean expectedValue
131        ) {
132            if (!(expression instanceof JetConstantExpression) || expression.getNode().getElementType() != JetNodeTypes.BOOLEAN_CONSTANT) {
133                return false;
134            }
135            CompileTimeConstant<?> compileTimeConstant =
136                    ConstantExpressionEvaluator.object$.evaluate(expression, trace, KotlinBuiltIns.getInstance().getBooleanType());
137            if (!(compileTimeConstant instanceof BooleanValue)) return false;
138    
139            Boolean value = ((BooleanValue) compileTimeConstant).getValue();
140            return expectedValue == null || expectedValue.equals(value);
141        }
142    
143        private CompileTimeConstantUtils() {
144        }
145    }