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.k2js.translate.utils;
018    
019    import com.intellij.psi.PsiElement;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.jet.lang.descriptors.*;
023    import org.jetbrains.jet.lang.psi.*;
024    import org.jetbrains.jet.lang.resolve.BindingContext;
025    import org.jetbrains.jet.lang.resolve.BindingContextUtils;
026    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
027    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
028    import org.jetbrains.jet.lang.resolve.calls.model.VariableAsFunctionResolvedCall;
029    import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
030    import org.jetbrains.jet.lang.types.JetType;
031    
032    import java.util.List;
033    
034    import static org.jetbrains.jet.lang.resolve.BindingContext.INDEXED_LVALUE_GET;
035    import static org.jetbrains.jet.lang.resolve.BindingContext.INDEXED_LVALUE_SET;
036    import static org.jetbrains.k2js.translate.utils.ErrorReportingUtils.message;
037    
038    /**
039     * This class contains some code related to BindingContext use. Intention is not to pollute other classes.
040     * Every call to BindingContext.get() is supposed to be wrapped by this utility class.
041     */
042    public final class BindingUtils {
043    
044        private BindingUtils() {
045        }
046    
047        @NotNull
048        static private <E extends PsiElement, D extends DeclarationDescriptor>
049        D getDescriptorForExpression(@NotNull BindingContext context, @NotNull E expression, Class<D> descriptorClass) {
050            DeclarationDescriptor descriptor = context.get(BindingContext.DECLARATION_TO_DESCRIPTOR, expression);
051            assert descriptor != null;
052            assert descriptorClass.isInstance(descriptor)
053                    : message(expression, expression.toString() + " expected to have of type" + descriptorClass.toString());
054            //noinspection unchecked
055            return (D) descriptor;
056        }
057    
058        @NotNull
059        public static ClassDescriptor getClassDescriptor(@NotNull BindingContext context,
060                @NotNull JetClassOrObject declaration) {
061            return getDescriptorForExpression(context, declaration, ClassDescriptor.class);
062        }
063    
064        @NotNull
065        public static FunctionDescriptor getFunctionDescriptor(@NotNull BindingContext context,
066                @NotNull JetDeclarationWithBody declaration) {
067            return getDescriptorForExpression(context, declaration, FunctionDescriptor.class);
068        }
069    
070        @NotNull
071        public static PropertyDescriptor getPropertyDescriptor(@NotNull BindingContext context,
072                @NotNull JetProperty declaration) {
073            return getDescriptorForExpression(context, declaration, PropertyDescriptor.class);
074        }
075    
076        @NotNull
077        public static JetFunction getFunctionForDescriptor(@NotNull BindingContext context,
078                @NotNull SimpleFunctionDescriptor descriptor) {
079            PsiElement result = BindingContextUtils.callableDescriptorToDeclaration(context, descriptor);
080            assert result instanceof JetFunction
081                    : message(context, descriptor, "SimpleFunctionDescriptor should have declaration of type JetFunction");
082            return (JetFunction) result;
083        }
084    
085        @NotNull
086        private static JetParameter getParameterForDescriptor(@NotNull BindingContext context,
087                @NotNull ValueParameterDescriptor descriptor) {
088            PsiElement result = BindingContextUtils.descriptorToDeclaration(context, descriptor);
089            assert result instanceof JetParameter :
090                    message(context, descriptor, "ValueParameterDescriptor should have corresponding JetParameter");
091            return (JetParameter) result;
092        }
093    
094        public static boolean hasAncestorClass(@NotNull BindingContext context, @NotNull JetClassOrObject classDeclaration) {
095            ClassDescriptor classDescriptor = getClassDescriptor(context, classDeclaration);
096            List<ClassDescriptor> superclassDescriptors = DescriptorUtils.getSuperclassDescriptors(classDescriptor);
097            return (JsDescriptorUtils.findAncestorClass(superclassDescriptors) != null);
098        }
099    
100        public static boolean isStatement(@NotNull BindingContext context, @NotNull JetExpression expression) {
101            Boolean isStatement = context.get(BindingContext.STATEMENT, expression);
102            assert isStatement != null : message(expression, "Invalid behaviour of get(BindingContext.STATEMENT) at");
103            return isStatement;
104        }
105    
106        @NotNull
107        public static JetType getTypeByReference(@NotNull BindingContext context,
108                @NotNull JetTypeReference typeReference) {
109            JetType result = context.get(BindingContext.TYPE, typeReference);
110            assert result != null : message(typeReference, "TypeReference should reference a type");
111            return result;
112        }
113    
114        @NotNull
115        public static ClassDescriptor getClassDescriptorForTypeReference(@NotNull BindingContext context,
116                @NotNull JetTypeReference typeReference) {
117            return DescriptorUtils.getClassDescriptorForType(getTypeByReference(context, typeReference));
118        }
119    
120        @Nullable
121        public static PropertyDescriptor getPropertyDescriptorForConstructorParameter(@NotNull BindingContext context,
122                @NotNull JetParameter parameter) {
123            return context.get(BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, parameter);
124        }
125    
126        @Nullable
127        public static DeclarationDescriptor getDescriptorForReferenceExpression(@NotNull BindingContext context,
128                @NotNull JetReferenceExpression reference) {
129            DeclarationDescriptor referencedDescriptor = getNullableDescriptorForReferenceExpression(context, reference);
130            if (BindingContextUtils.isExpressionWithValidReference(reference, context)) {
131                assert referencedDescriptor != null
132                        : message(reference, "Reference expression must reference a descriptor for reference " + reference.getText());
133                return referencedDescriptor;
134            }
135            return null;
136        }
137    
138        @Nullable
139        public static DeclarationDescriptor getNullableDescriptorForReferenceExpression(@NotNull BindingContext context,
140                @NotNull JetReferenceExpression reference) {
141            return context.get(BindingContext.REFERENCE_TARGET, reference);
142        }
143    
144        @NotNull
145        public static ResolvedCall<?> getResolvedCall(@NotNull BindingContext context,
146                @NotNull JetExpression expression) {
147            ResolvedCall<? extends CallableDescriptor> resolvedCall = context.get(BindingContext.RESOLVED_CALL, expression);
148            assert resolvedCall != null : message(expression, expression.getText() + " must resolve to a call");
149            return resolvedCall;
150        }
151    
152        @NotNull
153        public static ResolvedCall<?> getResolvedCallForProperty(@NotNull BindingContext context,
154                @NotNull JetExpression expression) {
155            ResolvedCall<? extends CallableDescriptor> resolvedCall = context.get(BindingContext.RESOLVED_CALL, expression);
156            assert resolvedCall != null : message(expression, expression.getText() + "must resolve to a call");
157            if (resolvedCall instanceof VariableAsFunctionResolvedCall) {
158                return ((VariableAsFunctionResolvedCall) resolvedCall).getVariableCall();
159            }
160            return resolvedCall;
161        }
162    
163        @NotNull
164        public static ResolvedCall<?> getResolvedCallForCallExpression(@NotNull BindingContext context,
165                @NotNull JetCallExpression expression) {
166            JetExpression calleeExpression = PsiUtils.getCallee(expression);
167            return getResolvedCall(context, calleeExpression);
168        }
169    
170        public static boolean isVariableReassignment(@NotNull BindingContext context, @NotNull JetExpression expression) {
171            Boolean result = context.get(BindingContext.VARIABLE_REASSIGNMENT, expression);
172            assert result != null : message(expression);
173            return result;
174        }
175    
176    
177        @Nullable
178        public static FunctionDescriptor getFunctionDescriptorForOperationExpression(@NotNull BindingContext context,
179                @NotNull JetOperationExpression expression) {
180            DeclarationDescriptor descriptorForReferenceExpression = getNullableDescriptorForReferenceExpression
181                    (context, expression.getOperationReference());
182    
183            if (descriptorForReferenceExpression == null) return null;
184    
185            assert descriptorForReferenceExpression instanceof FunctionDescriptor
186                    : message(expression.getOperationReference(), "Operation should resolve to function descriptor");
187            return (FunctionDescriptor) descriptorForReferenceExpression;
188        }
189    
190        @NotNull
191        public static DeclarationDescriptor getDescriptorForElement(@NotNull BindingContext context,
192                @NotNull PsiElement element) {
193            DeclarationDescriptor descriptor = context.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element);
194    
195            assert descriptor != null : message(element, element + " doesn't have a descriptor");
196            return descriptor;
197        }
198    
199        @Nullable
200        public static Object getCompileTimeValue(@NotNull BindingContext context, @NotNull JetExpression expression) {
201            CompileTimeConstant<?> compileTimeValue = context.get(BindingContext.COMPILE_TIME_VALUE, expression);
202            if (compileTimeValue != null) {
203                return compileTimeValue.getValue();
204            }
205            return null;
206        }
207    
208        @NotNull
209        public static JetExpression getDefaultArgument(@NotNull BindingContext context,
210                @NotNull ValueParameterDescriptor parameterDescriptor) {
211            ValueParameterDescriptor descriptorWhichDeclaresDefaultValue =
212                    getOriginalDescriptorWhichDeclaresDefaultValue(context, parameterDescriptor);
213            JetParameter psiParameter = getParameterForDescriptor(context, descriptorWhichDeclaresDefaultValue);
214            JetExpression defaultValue = psiParameter.getDefaultValue();
215            assert defaultValue != null : message(context, parameterDescriptor, "No default value found in PSI");
216            return defaultValue;
217        }
218    
219        private static ValueParameterDescriptor getOriginalDescriptorWhichDeclaresDefaultValue(
220                BindingContext context, @NotNull ValueParameterDescriptor parameterDescriptor) {
221            ValueParameterDescriptor result = parameterDescriptor;
222            assert result.hasDefaultValue() :
223                    message(context, parameterDescriptor, "Unsupplied parameter must have default value");
224            while (!result.declaresDefaultValue()) {
225                result = result.getOverriddenDescriptors().iterator().next();
226            }
227            return result;
228        }
229    
230        @NotNull
231        public static ResolvedCall<FunctionDescriptor> getIteratorFunction(@NotNull BindingContext context,
232                @NotNull JetExpression rangeExpression) {
233            ResolvedCall<FunctionDescriptor> resolvedCall = context.get(BindingContext.LOOP_RANGE_ITERATOR_RESOLVED_CALL, rangeExpression);
234            assert resolvedCall != null :
235                    message(rangeExpression, "Range expression must have a descriptor for iterator function");
236            return resolvedCall;
237        }
238    
239        @NotNull
240        public static ResolvedCall<FunctionDescriptor> getNextFunction(@NotNull BindingContext context,
241                @NotNull JetExpression rangeExpression) {
242            ResolvedCall<FunctionDescriptor> resolvedCall = context.get(BindingContext.LOOP_RANGE_NEXT_RESOLVED_CALL, rangeExpression);
243            assert resolvedCall != null : ErrorReportingUtils
244                    .message(rangeExpression, "Range expression must have a descriptor for next function");
245            return resolvedCall;
246        }
247    
248        @NotNull
249        public static ResolvedCall<FunctionDescriptor> getHasNextCallable(@NotNull BindingContext context,
250                @NotNull JetExpression rangeExpression) {
251            ResolvedCall<FunctionDescriptor> resolvedCall = context.get(BindingContext.LOOP_RANGE_HAS_NEXT_RESOLVED_CALL, rangeExpression);
252            assert resolvedCall != null
253                    : message(rangeExpression, "Range expression must have a descriptor for hasNext function or property");
254            return resolvedCall;
255        }
256    
257        @NotNull
258        public static PropertyDescriptor getPropertyDescriptorForObjectDeclaration(@NotNull BindingContext context,
259                @NotNull JetObjectDeclarationName name) {
260            PropertyDescriptor propertyDescriptor = context.get(BindingContext.OBJECT_DECLARATION, name);
261            assert propertyDescriptor != null : message(name);
262            return propertyDescriptor;
263        }
264    
265        @NotNull
266        public static JetType getTypeForExpression(@NotNull BindingContext context,
267                @NotNull JetExpression expression) {
268            JetType type = context.get(BindingContext.EXPRESSION_TYPE, expression);
269            assert type != null : message(expression);
270            return type;
271        }
272    
273        @NotNull
274        public static ResolvedCall<FunctionDescriptor> getResolvedCallForArrayAccess(@NotNull BindingContext context,
275                @NotNull JetArrayAccessExpression arrayAccessExpression,
276                boolean isGet) {
277            ResolvedCall<FunctionDescriptor> resolvedCall = context.get(isGet
278                                                                        ? INDEXED_LVALUE_GET
279                                                                        : INDEXED_LVALUE_SET, arrayAccessExpression);
280            assert resolvedCall != null : message(arrayAccessExpression);
281            return resolvedCall;
282        }
283    
284        public static ConstructorDescriptor getConstructor(@NotNull BindingContext bindingContext,
285                @NotNull JetClassOrObject declaration) {
286            ConstructorDescriptor primaryConstructor = getClassDescriptor(bindingContext, declaration).getUnsubstitutedPrimaryConstructor();
287            assert primaryConstructor != null : message(declaration, "Traits do not have initialize methods");
288            return primaryConstructor;
289        }
290    
291        @Nullable
292        public static SimpleFunctionDescriptor getNullableDescriptorForFunction(@NotNull BindingContext bindingContext,
293                @NotNull JetNamedFunction function) {
294            return bindingContext.get(BindingContext.FUNCTION, function);
295        }
296    
297        public static boolean isObjectDeclaration(
298                @NotNull BindingContext bindingContext,
299                @NotNull PropertyDescriptor propertyDescriptor
300        ) {
301            return bindingContext.get(BindingContext.OBJECT_DECLARATION_CLASS, propertyDescriptor) != null;
302        }
303    }