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.DescriptorToSourceUtils;
027    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
028    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
029    import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
030    import org.jetbrains.jet.lang.resolve.constants.IntegerValueTypeConstant;
031    import org.jetbrains.jet.lang.types.JetType;
032    import org.jetbrains.jet.lang.types.TypeUtils;
033    
034    import java.util.List;
035    
036    import static org.jetbrains.jet.lang.resolve.BindingContext.INDEXED_LVALUE_GET;
037    import static org.jetbrains.jet.lang.resolve.BindingContext.INDEXED_LVALUE_SET;
038    import static org.jetbrains.k2js.translate.utils.ErrorReportingUtils.message;
039    
040    /**
041     * This class contains some code related to BindingContext use. Intention is not to pollute other classes.
042     * Every call to BindingContext.get() is supposed to be wrapped by this utility class.
043     */
044    public final class BindingUtils {
045    
046        private BindingUtils() {
047        }
048    
049        @NotNull
050        static private <E extends PsiElement, D extends DeclarationDescriptor>
051        D getDescriptorForExpression(@NotNull BindingContext context, @NotNull E expression, Class<D> descriptorClass) {
052            DeclarationDescriptor descriptor = context.get(BindingContext.DECLARATION_TO_DESCRIPTOR, expression);
053            assert descriptor != null;
054            assert descriptorClass.isInstance(descriptor)
055                    : message(expression, expression.toString() + " expected to have of type" + descriptorClass.toString());
056            //noinspection unchecked
057            return (D) descriptor;
058        }
059    
060        @NotNull
061        public static ClassDescriptor getClassDescriptor(@NotNull BindingContext context,
062                @NotNull JetClassOrObject declaration) {
063            return BindingContextUtils.getNotNull(context, BindingContext.CLASS, declaration);
064        }
065    
066        @NotNull
067        public static FunctionDescriptor getFunctionDescriptor(@NotNull BindingContext context,
068                @NotNull JetDeclarationWithBody declaration) {
069            return getDescriptorForExpression(context, declaration, FunctionDescriptor.class);
070        }
071    
072        @NotNull
073        public static PropertyDescriptor getPropertyDescriptor(@NotNull BindingContext context,
074                @NotNull JetProperty declaration) {
075            return getDescriptorForExpression(context, declaration, PropertyDescriptor.class);
076        }
077    
078        @NotNull
079        public static JetFunction getFunctionForDescriptor(@NotNull SimpleFunctionDescriptor descriptor) {
080            PsiElement result = DescriptorToSourceUtils.callableDescriptorToDeclaration(descriptor);
081            assert result instanceof JetFunction : message(descriptor, "SimpleFunctionDescriptor should have declaration of type JetFunction");
082            return (JetFunction) result;
083        }
084    
085        @NotNull
086        private static JetParameter getParameterForDescriptor(@NotNull ValueParameterDescriptor descriptor) {
087            PsiElement result = DescriptorToSourceUtils.descriptorToDeclaration(descriptor);
088            assert result instanceof JetParameter : message(descriptor, "ValueParameterDescriptor should have corresponding JetParameter");
089            return (JetParameter) result;
090        }
091    
092        public static boolean hasAncestorClass(@NotNull BindingContext context, @NotNull JetClassOrObject classDeclaration) {
093            ClassDescriptor classDescriptor = getClassDescriptor(context, classDeclaration);
094            List<ClassDescriptor> superclassDescriptors = DescriptorUtils.getSuperclassDescriptors(classDescriptor);
095            return (JsDescriptorUtils.findAncestorClass(superclassDescriptors) != null);
096        }
097    
098        @NotNull
099        public static JetType getTypeByReference(@NotNull BindingContext context,
100                @NotNull JetTypeReference typeReference) {
101            return BindingContextUtils.getNotNull(context, BindingContext.TYPE, typeReference);
102        }
103    
104        @NotNull
105        public static ClassDescriptor getClassDescriptorForTypeReference(@NotNull BindingContext context,
106                @NotNull JetTypeReference typeReference) {
107            return DescriptorUtils.getClassDescriptorForType(getTypeByReference(context, typeReference));
108        }
109    
110        @Nullable
111        public static PropertyDescriptor getPropertyDescriptorForConstructorParameter(@NotNull BindingContext context,
112                @NotNull JetParameter parameter) {
113            return context.get(BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, parameter);
114        }
115    
116        @Nullable
117        public static DeclarationDescriptor getDescriptorForReferenceExpression(@NotNull BindingContext context,
118                @NotNull JetReferenceExpression reference) {
119            if (BindingContextUtils.isExpressionWithValidReference(reference, context)) {
120                return BindingContextUtils.getNotNull(context, BindingContext.REFERENCE_TARGET, reference);
121            }
122            return null;
123        }
124    
125        @Nullable
126        public static DeclarationDescriptor getNullableDescriptorForReferenceExpression(@NotNull BindingContext context,
127                @NotNull JetReferenceExpression reference) {
128            return context.get(BindingContext.REFERENCE_TARGET, reference);
129        }
130    
131        public static boolean isVariableReassignment(@NotNull BindingContext context, @NotNull JetExpression expression) {
132            return BindingContextUtils.getNotNull(context, BindingContext.VARIABLE_REASSIGNMENT, expression);
133        }
134    
135        @Nullable
136        public static CallableDescriptor getCallableDescriptorForOperationExpression(
137                @NotNull BindingContext context,
138                @NotNull JetOperationExpression expression
139        ) {
140            JetSimpleNameExpression operationReference = expression.getOperationReference();
141            DeclarationDescriptor descriptorForReferenceExpression =
142                    getNullableDescriptorForReferenceExpression(context, operationReference);
143    
144            if (descriptorForReferenceExpression == null) return null;
145    
146            assert descriptorForReferenceExpression instanceof CallableDescriptor :
147                    message(operationReference, "Operation should resolve to callable descriptor");
148            return (CallableDescriptor) descriptorForReferenceExpression;
149        }
150    
151        @NotNull
152        public static DeclarationDescriptor getDescriptorForElement(@NotNull BindingContext context,
153                @NotNull PsiElement element) {
154            return BindingContextUtils.getNotNull(context, BindingContext.DECLARATION_TO_DESCRIPTOR, element);
155        }
156    
157        @Nullable
158        public static Object getCompileTimeValue(@NotNull BindingContext context, @NotNull JetExpression expression) {
159            CompileTimeConstant<?> compileTimeValue = context.get(BindingContext.COMPILE_TIME_VALUE, expression);
160            if (compileTimeValue != null) {
161                return getCompileTimeValue(context, expression, compileTimeValue);
162            }
163            return null;
164        }
165    
166        @Nullable
167        public static Object getCompileTimeValue(@NotNull BindingContext context, @NotNull JetExpression expression, @NotNull CompileTimeConstant<?> constant) {
168            if (constant != null) {
169                if (constant instanceof IntegerValueTypeConstant) {
170                    JetType expectedType = context.get(BindingContext.EXPRESSION_TYPE, expression);
171                    return ((IntegerValueTypeConstant) constant).getValue(expectedType == null ? TypeUtils.NO_EXPECTED_TYPE : expectedType);
172                }
173                return constant.getValue();
174            }
175            return null;
176        }
177    
178        @NotNull
179        public static JetExpression getDefaultArgument(@NotNull ValueParameterDescriptor parameterDescriptor) {
180            ValueParameterDescriptor descriptorWhichDeclaresDefaultValue =
181                    getOriginalDescriptorWhichDeclaresDefaultValue(parameterDescriptor);
182            JetParameter psiParameter = getParameterForDescriptor(descriptorWhichDeclaresDefaultValue);
183            JetExpression defaultValue = psiParameter.getDefaultValue();
184            assert defaultValue != null : message(parameterDescriptor, "No default value found in PSI");
185            return defaultValue;
186        }
187    
188        private static ValueParameterDescriptor getOriginalDescriptorWhichDeclaresDefaultValue(
189                @NotNull ValueParameterDescriptor parameterDescriptor
190        ) {
191            ValueParameterDescriptor result = parameterDescriptor;
192            assert result.hasDefaultValue() : message(parameterDescriptor, "Unsupplied parameter must have default value");
193            while (!result.declaresDefaultValue()) {
194                result = result.getOverriddenDescriptors().iterator().next();
195            }
196            return result;
197        }
198    
199        @NotNull
200        public static ResolvedCall<FunctionDescriptor> getIteratorFunction(@NotNull BindingContext context,
201                @NotNull JetExpression rangeExpression) {
202            return BindingContextUtils.getNotNull(context, BindingContext.LOOP_RANGE_ITERATOR_RESOLVED_CALL, rangeExpression);
203        }
204    
205        @NotNull
206        public static ResolvedCall<FunctionDescriptor> getNextFunction(@NotNull BindingContext context,
207                @NotNull JetExpression rangeExpression) {
208            return BindingContextUtils.getNotNull(context, BindingContext.LOOP_RANGE_NEXT_RESOLVED_CALL, rangeExpression);
209        }
210    
211        @NotNull
212        public static ResolvedCall<FunctionDescriptor> getHasNextCallable(@NotNull BindingContext context,
213                @NotNull JetExpression rangeExpression) {
214            return BindingContextUtils.getNotNull(context, BindingContext.LOOP_RANGE_HAS_NEXT_RESOLVED_CALL, rangeExpression);
215        }
216    
217        @NotNull
218        public static JetType getTypeForExpression(@NotNull BindingContext context,
219                @NotNull JetExpression expression) {
220            return BindingContextUtils.getNotNull(context, BindingContext.EXPRESSION_TYPE, expression);
221        }
222    
223        @NotNull
224        public static ResolvedCall<FunctionDescriptor> getResolvedCallForArrayAccess(@NotNull BindingContext context,
225                @NotNull JetArrayAccessExpression arrayAccessExpression,
226                boolean isGet) {
227            return BindingContextUtils.getNotNull(context, isGet ? INDEXED_LVALUE_GET : INDEXED_LVALUE_SET, arrayAccessExpression);
228        }
229    
230        public static ConstructorDescriptor getConstructor(@NotNull BindingContext bindingContext,
231                @NotNull JetClassOrObject declaration) {
232            ConstructorDescriptor primaryConstructor = getClassDescriptor(bindingContext, declaration).getUnsubstitutedPrimaryConstructor();
233            assert primaryConstructor != null : message(declaration, "Traits do not have initialize methods");
234            return primaryConstructor;
235        }
236    
237        @Nullable
238        public static SimpleFunctionDescriptor getNullableDescriptorForFunction(@NotNull BindingContext bindingContext,
239                @NotNull JetNamedFunction function) {
240            return bindingContext.get(BindingContext.FUNCTION, function);
241        }
242    }