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