001    /*
002     * Copyright 2010-2014 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.backend.common;
018    
019    import org.jetbrains.annotations.NotNull;
020    import org.jetbrains.annotations.Nullable;
021    import org.jetbrains.jet.lang.descriptors.*;
022    import org.jetbrains.jet.lang.psi.JetDelegationSpecifier;
023    import org.jetbrains.jet.lang.psi.JetExpression;
024    import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
025    import org.jetbrains.jet.lang.resolve.BindingContext;
026    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
027    import org.jetbrains.jet.lang.resolve.calls.CallResolverUtil;
028    import org.jetbrains.jet.lang.resolve.calls.callUtil.CallUtilPackage;
029    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
030    import org.jetbrains.jet.lang.resolve.name.Name;
031    import org.jetbrains.jet.lang.types.JetType;
032    import org.jetbrains.jet.lang.types.TypeUtils;
033    import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
034    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
035    
036    import java.util.*;
037    
038    /**
039     * Backend-independent utility class.
040     */
041    public class CodegenUtil {
042    
043        private CodegenUtil() {
044        }
045    
046        // TODO: consider putting predefined method signatures here too.
047        public static final String EQUALS_METHOD_NAME = "equals";
048        public static final String TO_STRING_METHOD_NAME = "toString";
049        public static final String HASH_CODE_METHOD_NAME = "hashCode";
050    
051        @Nullable
052        public static FunctionDescriptor getDeclaredFunctionByRawSignature(
053                @NotNull ClassDescriptor owner,
054                @NotNull Name name,
055                @NotNull ClassifierDescriptor returnedClassifier,
056                @NotNull ClassifierDescriptor... valueParameterClassifiers
057        ) {
058            Collection<FunctionDescriptor> functions = owner.getDefaultType().getMemberScope().getFunctions(name);
059            for (FunctionDescriptor function : functions) {
060                if (!CallResolverUtil.isOrOverridesSynthesized(function)
061                    && function.getTypeParameters().isEmpty()
062                    && valueParameterClassesMatch(function.getValueParameters(), Arrays.asList(valueParameterClassifiers))
063                    && rawTypeMatches(function.getReturnType(), returnedClassifier)) {
064                    return function;
065                }
066            }
067            return null;
068        }
069    
070        public static FunctionDescriptor getAnyEqualsMethod() {
071            ClassDescriptor anyClass = KotlinBuiltIns.getInstance().getAny();
072            FunctionDescriptor function =
073                    getDeclaredFunctionByRawSignature(anyClass, Name.identifier(EQUALS_METHOD_NAME),
074                                                      KotlinBuiltIns.getInstance().getBoolean(),
075                                                      anyClass);
076            assert function != null;
077            return function;
078        }
079    
080        public static FunctionDescriptor getAnyToStringMethod() {
081            ClassDescriptor anyClass = KotlinBuiltIns.getInstance().getAny();
082            FunctionDescriptor function =
083                    getDeclaredFunctionByRawSignature(anyClass, Name.identifier(TO_STRING_METHOD_NAME),
084                                                      KotlinBuiltIns.getInstance().getString());
085            assert function != null;
086            return function;
087        }
088    
089        public static FunctionDescriptor getAnyHashCodeMethod() {
090            ClassDescriptor anyClass = KotlinBuiltIns.getInstance().getAny();
091            FunctionDescriptor function =
092                    getDeclaredFunctionByRawSignature(anyClass, Name.identifier(HASH_CODE_METHOD_NAME),
093                                                      KotlinBuiltIns.getInstance().getInt());
094            assert function != null;
095            return function;
096        }
097    
098        @Nullable
099        public static PropertyDescriptor getDelegatePropertyIfAny(JetExpression expression, ClassDescriptor classDescriptor, BindingContext bindingContext) {
100            PropertyDescriptor propertyDescriptor = null;
101            if (expression instanceof JetSimpleNameExpression) {
102                ResolvedCall<?> call = CallUtilPackage.getResolvedCall(expression, bindingContext);
103                if (call != null) {
104                    CallableDescriptor callResultingDescriptor = call.getResultingDescriptor();
105                    if (callResultingDescriptor instanceof ValueParameterDescriptor) {
106                        ValueParameterDescriptor valueParameterDescriptor = (ValueParameterDescriptor) callResultingDescriptor;
107                        // constructor parameter
108                        if (valueParameterDescriptor.getContainingDeclaration() instanceof ConstructorDescriptor) {
109                            // constructor of my class
110                            if (valueParameterDescriptor.getContainingDeclaration().getContainingDeclaration() == classDescriptor) {
111                                propertyDescriptor = bindingContext.get(BindingContext.VALUE_PARAMETER_AS_PROPERTY, valueParameterDescriptor);
112                            }
113                        }
114                    }
115    
116                    // todo: when and if frontend will allow properties defined not as constructor parameters to be used in delegation specifier
117                }
118            }
119            return propertyDescriptor;
120        }
121    
122        public static boolean isFinalPropertyWithBackingField(PropertyDescriptor propertyDescriptor, BindingContext bindingContext) {
123            return propertyDescriptor != null &&
124                   !propertyDescriptor.isVar() &&
125                   Boolean.TRUE.equals(bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor));
126        }
127    
128        public static Map<CallableMemberDescriptor, CallableMemberDescriptor> getDelegates(ClassDescriptor descriptor, ClassDescriptor toClass) {
129            Map<CallableMemberDescriptor, CallableMemberDescriptor> result = new LinkedHashMap<CallableMemberDescriptor, CallableMemberDescriptor>();
130            for (DeclarationDescriptor declaration : descriptor.getDefaultType().getMemberScope().getAllDescriptors()) {
131                if (declaration instanceof CallableMemberDescriptor) {
132                    CallableMemberDescriptor callableMemberDescriptor = (CallableMemberDescriptor) declaration;
133                    if (callableMemberDescriptor.getKind() == CallableMemberDescriptor.Kind.DELEGATION) {
134                        Set<? extends CallableMemberDescriptor> overriddenDescriptors = callableMemberDescriptor.getOverriddenDescriptors();
135                        for (CallableMemberDescriptor overriddenDescriptor : overriddenDescriptors) {
136                            if (overriddenDescriptor.getContainingDeclaration() == toClass) {
137                                assert !result.containsKey(callableMemberDescriptor) :
138                                        "overridden is already set for " + callableMemberDescriptor;
139                                result.put(callableMemberDescriptor, overriddenDescriptor);
140                            }
141                        }
142                    }
143                }
144            }
145            return result;
146        }
147    
148        @NotNull
149        public static ClassDescriptor getSuperClassByDelegationSpecifier(@NotNull JetDelegationSpecifier specifier, @NotNull BindingContext bindingContext) {
150            JetType superType = bindingContext.get(BindingContext.TYPE, specifier.getTypeReference());
151            assert superType != null : "superType should not be null: " + specifier.getText();
152    
153            ClassDescriptor superClassDescriptor = (ClassDescriptor) superType.getConstructor().getDeclarationDescriptor();
154            assert superClassDescriptor != null : "superClassDescriptor should not be null: " + specifier.getText();
155            return superClassDescriptor;
156        }
157    
158        private static boolean valueParameterClassesMatch(
159                @NotNull List<ValueParameterDescriptor> parameters,
160                @NotNull List<ClassifierDescriptor> classifiers
161        ) {
162            if (parameters.size() != classifiers.size()) return false;
163            for (int i = 0; i < parameters.size(); i++) {
164                ValueParameterDescriptor parameterDescriptor = parameters.get(i);
165                ClassifierDescriptor classDescriptor = classifiers.get(i);
166                if (!rawTypeMatches(parameterDescriptor.getType(), classDescriptor)) {
167                    return false;
168                }
169            }
170            return true;
171        }
172    
173        private static boolean rawTypeMatches(JetType type, ClassifierDescriptor classifier) {
174            return type.getConstructor().equals(classifier.getTypeConstructor());
175        }
176    
177        public static boolean isEnumValueOfMethod(@NotNull FunctionDescriptor functionDescriptor) {
178            List<ValueParameterDescriptor> methodTypeParameters = functionDescriptor.getValueParameters();
179            JetType nullableString = TypeUtils.makeNullable(KotlinBuiltIns.getInstance().getStringType());
180            return DescriptorUtils.ENUM_VALUE_OF.equals(functionDescriptor.getName())
181                   && methodTypeParameters.size() == 1
182                   && JetTypeChecker.DEFAULT.isSubtypeOf(methodTypeParameters.get(0).getType(), nullableString);
183        }
184    
185        public static boolean isEnumValuesMethod(@NotNull FunctionDescriptor functionDescriptor) {
186            List<ValueParameterDescriptor> methodTypeParameters = functionDescriptor.getValueParameters();
187            return DescriptorUtils.ENUM_VALUES.equals(functionDescriptor.getName())
188                   && methodTypeParameters.isEmpty();
189        }
190    }