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            return BindingContextUtils.getNotNull(context, BindingContext.STATEMENT, expression);
102        }
103    
104        @NotNull
105        public static JetType getTypeByReference(@NotNull BindingContext context,
106                @NotNull JetTypeReference typeReference) {
107            return BindingContextUtils.getNotNull(context, BindingContext.TYPE, typeReference);
108        }
109    
110        @NotNull
111        public static ClassDescriptor getClassDescriptorForTypeReference(@NotNull BindingContext context,
112                @NotNull JetTypeReference typeReference) {
113            return DescriptorUtils.getClassDescriptorForType(getTypeByReference(context, typeReference));
114        }
115    
116        @Nullable
117        public static PropertyDescriptor getPropertyDescriptorForConstructorParameter(@NotNull BindingContext context,
118                @NotNull JetParameter parameter) {
119            return context.get(BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, parameter);
120        }
121    
122        @Nullable
123        public static DeclarationDescriptor getDescriptorForReferenceExpression(@NotNull BindingContext context,
124                @NotNull JetReferenceExpression reference) {
125            if (BindingContextUtils.isExpressionWithValidReference(reference, context)) {
126                return BindingContextUtils.getNotNull(context, BindingContext.REFERENCE_TARGET, reference);
127            }
128            return null;
129        }
130    
131        @Nullable
132        public static DeclarationDescriptor getNullableDescriptorForReferenceExpression(@NotNull BindingContext context,
133                @NotNull JetReferenceExpression reference) {
134            return context.get(BindingContext.REFERENCE_TARGET, reference);
135        }
136    
137        @NotNull
138        public static ResolvedCall<?> getResolvedCall(@NotNull BindingContext context,
139                @NotNull JetExpression expression) {
140            ResolvedCall<? extends CallableDescriptor> resolvedCall = context.get(BindingContext.RESOLVED_CALL, expression);
141            assert resolvedCall != null : message(expression, expression.getText() + " must resolve to a call");
142            return resolvedCall;
143        }
144    
145        @NotNull
146        public static ResolvedCall<?> getResolvedCallForProperty(@NotNull BindingContext context,
147                @NotNull JetExpression expression) {
148            ResolvedCall<? extends CallableDescriptor> resolvedCall = context.get(BindingContext.RESOLVED_CALL, expression);
149            assert resolvedCall != null : message(expression, expression.getText() + "must resolve to a call");
150            if (resolvedCall instanceof VariableAsFunctionResolvedCall) {
151                return ((VariableAsFunctionResolvedCall) resolvedCall).getVariableCall();
152            }
153            return resolvedCall;
154        }
155    
156        @NotNull
157        public static ResolvedCall<?> getResolvedCallForCallExpression(@NotNull BindingContext context,
158                @NotNull JetCallExpression expression) {
159            JetExpression calleeExpression = PsiUtils.getCallee(expression);
160            return getResolvedCall(context, calleeExpression);
161        }
162    
163        public static boolean isVariableReassignment(@NotNull BindingContext context, @NotNull JetExpression expression) {
164            return BindingContextUtils.getNotNull(context, BindingContext.VARIABLE_REASSIGNMENT, expression);
165        }
166    
167        @Nullable
168        public static FunctionDescriptor getFunctionDescriptorForOperationExpression(@NotNull BindingContext context,
169                @NotNull JetOperationExpression expression) {
170            DeclarationDescriptor descriptorForReferenceExpression = getNullableDescriptorForReferenceExpression
171                    (context, expression.getOperationReference());
172    
173            if (descriptorForReferenceExpression == null) return null;
174    
175            assert descriptorForReferenceExpression instanceof FunctionDescriptor
176                    : message(expression.getOperationReference(), "Operation should resolve to function descriptor");
177            return (FunctionDescriptor) descriptorForReferenceExpression;
178        }
179    
180        @NotNull
181        public static DeclarationDescriptor getDescriptorForElement(@NotNull BindingContext context,
182                @NotNull PsiElement element) {
183            return BindingContextUtils.getNotNull(context, BindingContext.DECLARATION_TO_DESCRIPTOR, element);
184        }
185    
186        @Nullable
187        public static Object getCompileTimeValue(@NotNull BindingContext context, @NotNull JetExpression expression) {
188            CompileTimeConstant<?> compileTimeValue = context.get(BindingContext.COMPILE_TIME_VALUE, expression);
189            if (compileTimeValue != null) {
190                return compileTimeValue.getValue();
191            }
192            return null;
193        }
194    
195        @NotNull
196        public static JetExpression getDefaultArgument(@NotNull BindingContext context,
197                @NotNull ValueParameterDescriptor parameterDescriptor) {
198            ValueParameterDescriptor descriptorWhichDeclaresDefaultValue =
199                    getOriginalDescriptorWhichDeclaresDefaultValue(context, parameterDescriptor);
200            JetParameter psiParameter = getParameterForDescriptor(context, descriptorWhichDeclaresDefaultValue);
201            JetExpression defaultValue = psiParameter.getDefaultValue();
202            assert defaultValue != null : message(context, parameterDescriptor, "No default value found in PSI");
203            return defaultValue;
204        }
205    
206        private static ValueParameterDescriptor getOriginalDescriptorWhichDeclaresDefaultValue(
207                BindingContext context, @NotNull ValueParameterDescriptor parameterDescriptor) {
208            ValueParameterDescriptor result = parameterDescriptor;
209            assert result.hasDefaultValue() :
210                    message(context, parameterDescriptor, "Unsupplied parameter must have default value");
211            while (!result.declaresDefaultValue()) {
212                result = result.getOverriddenDescriptors().iterator().next();
213            }
214            return result;
215        }
216    
217        @NotNull
218        public static ResolvedCall<FunctionDescriptor> getIteratorFunction(@NotNull BindingContext context,
219                @NotNull JetExpression rangeExpression) {
220            return BindingContextUtils.getNotNull(context, BindingContext.LOOP_RANGE_ITERATOR_RESOLVED_CALL, rangeExpression);
221        }
222    
223        @NotNull
224        public static ResolvedCall<FunctionDescriptor> getNextFunction(@NotNull BindingContext context,
225                @NotNull JetExpression rangeExpression) {
226            return BindingContextUtils.getNotNull(context, BindingContext.LOOP_RANGE_NEXT_RESOLVED_CALL, rangeExpression);
227        }
228    
229        @NotNull
230        public static ResolvedCall<FunctionDescriptor> getHasNextCallable(@NotNull BindingContext context,
231                @NotNull JetExpression rangeExpression) {
232            return BindingContextUtils.getNotNull(context, BindingContext.LOOP_RANGE_HAS_NEXT_RESOLVED_CALL, rangeExpression);
233        }
234    
235        @NotNull
236        public static PropertyDescriptor getPropertyDescriptorForObjectDeclaration(@NotNull BindingContext context,
237                @NotNull JetObjectDeclarationName name) {
238            return BindingContextUtils.getNotNull(context, BindingContext.OBJECT_DECLARATION, name);
239        }
240    
241        @NotNull
242        public static JetType getTypeForExpression(@NotNull BindingContext context,
243                @NotNull JetExpression expression) {
244            return BindingContextUtils.getNotNull(context, BindingContext.EXPRESSION_TYPE, expression);
245        }
246    
247        @NotNull
248        public static ResolvedCall<FunctionDescriptor> getResolvedCallForArrayAccess(@NotNull BindingContext context,
249                @NotNull JetArrayAccessExpression arrayAccessExpression,
250                boolean isGet) {
251            return BindingContextUtils.getNotNull(context, isGet ? INDEXED_LVALUE_GET : INDEXED_LVALUE_SET, arrayAccessExpression);
252        }
253    
254        public static ConstructorDescriptor getConstructor(@NotNull BindingContext bindingContext,
255                @NotNull JetClassOrObject declaration) {
256            ConstructorDescriptor primaryConstructor = getClassDescriptor(bindingContext, declaration).getUnsubstitutedPrimaryConstructor();
257            assert primaryConstructor != null : message(declaration, "Traits do not have initialize methods");
258            return primaryConstructor;
259        }
260    
261        @Nullable
262        public static SimpleFunctionDescriptor getNullableDescriptorForFunction(@NotNull BindingContext bindingContext,
263                @NotNull JetNamedFunction function) {
264            return bindingContext.get(BindingContext.FUNCTION, function);
265        }
266    
267        public static boolean isObjectDeclaration(
268                @NotNull BindingContext bindingContext,
269                @NotNull PropertyDescriptor propertyDescriptor
270        ) {
271            return bindingContext.get(BindingContext.OBJECT_DECLARATION_CLASS, propertyDescriptor) != null;
272        }
273    }