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
017package org.jetbrains.k2js.translate.utils;
018
019import com.intellij.psi.PsiElement;
020import org.jetbrains.annotations.NotNull;
021import org.jetbrains.annotations.Nullable;
022import org.jetbrains.jet.lang.descriptors.*;
023import org.jetbrains.jet.lang.psi.*;
024import org.jetbrains.jet.lang.resolve.BindingContext;
025import org.jetbrains.jet.lang.resolve.BindingContextUtils;
026import org.jetbrains.jet.lang.resolve.DescriptorUtils;
027import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
028import org.jetbrains.jet.lang.resolve.calls.model.VariableAsFunctionResolvedCall;
029import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
030import org.jetbrains.jet.lang.types.JetType;
031
032import java.util.List;
033
034import static org.jetbrains.jet.lang.resolve.BindingContext.INDEXED_LVALUE_GET;
035import static org.jetbrains.jet.lang.resolve.BindingContext.INDEXED_LVALUE_SET;
036import 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 */
042public 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}