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.jet.codegen;
018    
019    import com.intellij.openapi.util.Condition;
020    import com.intellij.openapi.util.io.FileUtil;
021    import com.intellij.openapi.vfs.VirtualFile;
022    import com.intellij.util.ArrayUtil;
023    import com.intellij.util.Function;
024    import com.intellij.util.containers.ContainerUtil;
025    import com.intellij.util.containers.Stack;
026    import org.jetbrains.annotations.NotNull;
027    import org.jetbrains.annotations.Nullable;
028    import org.jetbrains.jet.codegen.binding.CalculatedClosure;
029    import org.jetbrains.jet.codegen.context.CodegenContext;
030    import org.jetbrains.jet.codegen.context.PackageContext;
031    import org.jetbrains.jet.codegen.state.JetTypeMapper;
032    import org.jetbrains.jet.lang.descriptors.*;
033    import org.jetbrains.jet.lang.descriptors.annotations.Annotated;
034    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
035    import org.jetbrains.jet.lang.descriptors.annotations.Annotations;
036    import org.jetbrains.jet.lang.descriptors.impl.SimpleFunctionDescriptorImpl;
037    import org.jetbrains.jet.lang.descriptors.impl.TypeParameterDescriptorImpl;
038    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
039    import org.jetbrains.jet.lang.resolve.calls.CallResolverUtil;
040    import org.jetbrains.jet.lang.resolve.constants.*;
041    import org.jetbrains.jet.lang.resolve.name.FqName;
042    import org.jetbrains.jet.lang.resolve.name.Name;
043    import org.jetbrains.jet.lang.types.JetType;
044    import org.jetbrains.jet.lang.types.TypeUtils;
045    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
046    
047    import java.util.Arrays;
048    import java.util.Collection;
049    import java.util.Collections;
050    import java.util.List;
051    
052    import static org.jetbrains.jet.lang.descriptors.Modality.ABSTRACT;
053    
054    public class CodegenUtil {
055    
056        private CodegenUtil() {
057        }
058    
059        public static boolean isInterface(DeclarationDescriptor descriptor) {
060            if (descriptor instanceof ClassDescriptor) {
061                ClassKind kind = ((ClassDescriptor) descriptor).getKind();
062                return kind == ClassKind.TRAIT || kind == ClassKind.ANNOTATION_CLASS;
063            }
064            return false;
065        }
066    
067        public static boolean isInterface(JetType type) {
068            return isInterface(type.getConstructor().getDeclarationDescriptor());
069        }
070    
071        public static SimpleFunctionDescriptor createInvoke(FunctionDescriptor fd) {
072            int arity = fd.getValueParameters().size();
073            SimpleFunctionDescriptorImpl invokeDescriptor = new SimpleFunctionDescriptorImpl(
074                    fd.getExpectedThisObject() != null
075                    ? KotlinBuiltIns.getInstance().getExtensionFunction(arity) : KotlinBuiltIns.getInstance().getFunction(arity),
076                    Annotations.EMPTY,
077                    Name.identifier("invoke"),
078                    CallableMemberDescriptor.Kind.DECLARATION);
079    
080            invokeDescriptor.initialize(DescriptorUtils.getReceiverParameterType(fd.getReceiverParameter()),
081                                        fd.getExpectedThisObject(),
082                                        Collections.<TypeParameterDescriptorImpl>emptyList(),
083                                        fd.getValueParameters(),
084                                        fd.getReturnType(),
085                                        Modality.FINAL,
086                                        Visibilities.PUBLIC
087            );
088            return invokeDescriptor;
089        }
090    
091        public static boolean isConst(@NotNull CalculatedClosure closure) {
092            return closure.getCaptureThis() == null && closure.getCaptureReceiverType() == null && closure.getCaptureVariables().isEmpty();
093        }
094    
095        public static <T> T peekFromStack(Stack<T> stack) {
096            return stack.empty() ? null : stack.peek();
097        }
098    
099        public static JetType getSuperClass(ClassDescriptor classDescriptor) {
100            List<ClassDescriptor> superclassDescriptors = DescriptorUtils.getSuperclassDescriptors(classDescriptor);
101            for (ClassDescriptor descriptor : superclassDescriptors) {
102                if (descriptor.getKind() != ClassKind.TRAIT) {
103                    return descriptor.getDefaultType();
104                }
105            }
106            return KotlinBuiltIns.getInstance().getAnyType();
107        }
108    
109        @NotNull
110        public static <T extends CallableMemberDescriptor> T unwrapFakeOverride(T member) {
111            while (member.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
112                //noinspection unchecked
113                member = (T) member.getOverriddenDescriptors().iterator().next();
114            }
115            return member;
116        }
117    
118        @Nullable
119        public static FunctionDescriptor getDeclaredFunctionByRawSignature(
120                @NotNull ClassDescriptor owner,
121                @NotNull Name name,
122                @NotNull ClassifierDescriptor returnedClassifier,
123                @NotNull ClassifierDescriptor... valueParameterClassifiers
124        ) {
125            Collection<FunctionDescriptor> functions = owner.getDefaultType().getMemberScope().getFunctions(name);
126            for (FunctionDescriptor function : functions) {
127                if (!CallResolverUtil.isOrOverridesSynthesized(function)
128                    && function.getTypeParameters().isEmpty()
129                    && valueParameterClassesMatch(function.getValueParameters(), Arrays.asList(valueParameterClassifiers))
130                    && rawTypeMatches(function.getReturnType(), returnedClassifier)) {
131                    return function;
132                }
133            }
134            return null;
135        }
136    
137        private static boolean valueParameterClassesMatch(
138                @NotNull List<ValueParameterDescriptor> parameters,
139                @NotNull List<ClassifierDescriptor> classifiers) {
140            if (parameters.size() != classifiers.size()) return false;
141            for (int i = 0; i < parameters.size(); i++) {
142                ValueParameterDescriptor parameterDescriptor = parameters.get(i);
143                ClassifierDescriptor classDescriptor = classifiers.get(i);
144                if (!rawTypeMatches(parameterDescriptor.getType(), classDescriptor)) {
145                    return false;
146                }
147            }
148            return true;
149        }
150    
151        private static boolean rawTypeMatches(JetType type, ClassifierDescriptor classifier) {
152            return type.getConstructor().getDeclarationDescriptor().getOriginal() == classifier.getOriginal();
153        }
154    
155        public static boolean isCallInsideSameClassAsDeclared(CallableMemberDescriptor declarationDescriptor, CodegenContext context) {
156            boolean isFakeOverride = declarationDescriptor.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE;
157            boolean isDelegate = declarationDescriptor.getKind() == CallableMemberDescriptor.Kind.DELEGATION;
158    
159            DeclarationDescriptor containingDeclaration = declarationDescriptor.getContainingDeclaration();
160            containingDeclaration = containingDeclaration.getOriginal();
161    
162            return !isFakeOverride && !isDelegate &&
163                   (((context.hasThisDescriptor() && containingDeclaration == context.getThisDescriptor()) ||
164                     (context.getParentContext() instanceof PackageContext && context.getParentContext().getContextDescriptor() == containingDeclaration))
165                    && context.getContextKind() != OwnerKind.TRAIT_IMPL);
166        }
167    
168        public static boolean isCallInsideSameModuleAsDeclared(CallableMemberDescriptor declarationDescriptor, CodegenContext context) {
169            if (context == CodegenContext.STATIC) {
170                return true;
171            }
172            DeclarationDescriptor contextDescriptor = context.getContextDescriptor();
173            return DescriptorUtils.areInSameModule(declarationDescriptor, contextDescriptor);
174        }
175    
176        public static boolean hasAbstractMembers(@NotNull ClassDescriptor classDescriptor) {
177            return ContainerUtil.exists(classDescriptor.getDefaultType().getMemberScope().getAllDescriptors(),
178                                        new Condition<DeclarationDescriptor>() {
179                                            @Override
180                                            public boolean value(DeclarationDescriptor declaration) {
181                                                if (!(declaration instanceof MemberDescriptor)) {
182                                                    return false;
183                                                }
184                                                return ((MemberDescriptor) declaration).getModality() == ABSTRACT;
185                                            }
186                                        });
187        }
188    
189        /**
190         * A work-around of the generic nullability problem in the type checker
191         * @return true if a value of this type can be null
192         */
193        public static boolean isNullableType(@NotNull JetType type) {
194            if (type.isNullable()) {
195                return true;
196            }
197            if (type.getConstructor().getDeclarationDescriptor() instanceof TypeParameterDescriptor) {
198                return TypeUtils.hasNullableSuperType(type);
199            }
200            return false;
201        }
202    
203        public static boolean couldUseDirectAccessToProperty(@NotNull PropertyDescriptor propertyDescriptor, boolean forGetter, boolean isInsideClass, boolean isDelegated) {
204            PropertyAccessorDescriptor accessorDescriptor = forGetter ? propertyDescriptor.getGetter() : propertyDescriptor.getSetter();
205            boolean isExtensionProperty = propertyDescriptor.getReceiverParameter() != null;
206            boolean specialTypeProperty = isDelegated ||
207                                          isExtensionProperty ||
208                                          DescriptorUtils.isClassObject(propertyDescriptor.getContainingDeclaration()) ||
209                                          JetTypeMapper.isAccessor(propertyDescriptor);
210            return isInsideClass &&
211                   !specialTypeProperty &&
212                   (accessorDescriptor == null ||
213                    accessorDescriptor.isDefault() &&
214                    (!isExternallyAccessible(propertyDescriptor) || accessorDescriptor.getModality() == Modality.FINAL));
215        }
216    
217        private static boolean isExternallyAccessible(@NotNull PropertyDescriptor propertyDescriptor) {
218            return propertyDescriptor.getVisibility() != Visibilities.PRIVATE ||
219                   DescriptorUtils.isClassObject(propertyDescriptor.getContainingDeclaration()) ||
220                   DescriptorUtils.isTopLevelDeclaration(propertyDescriptor);
221        }
222    
223        @NotNull
224        public static ImplementationBodyCodegen getParentBodyCodegen(@Nullable MemberCodegen classBodyCodegen) {
225            assert classBodyCodegen != null &&
226                   classBodyCodegen
227                           .getParentCodegen() instanceof ImplementationBodyCodegen : "Class object should have appropriate parent BodyCodegen";
228    
229            return ((ImplementationBodyCodegen) classBodyCodegen.getParentCodegen());
230        }
231    
232        static int getPathHashCode(@NotNull VirtualFile file) {
233            // Conversion to system-dependent name seems to be unnecessary, but it's hard to check now:
234            // it was introduced when fixing KT-2839, which appeared again (KT-3639).
235            // If you try to remove it, run tests on Windows.
236            return FileUtil.toSystemDependentName(file.getPath()).hashCode();
237        }
238    
239        @Nullable
240        public static ClassDescriptor getExpectedThisObjectForConstructorCall(
241                @NotNull ConstructorDescriptor descriptor,
242                @Nullable CalculatedClosure closure
243        ) {
244            //for compilation against sources
245            if (closure != null) {
246                return closure.getCaptureThis();
247            }
248    
249            //for compilation against binaries
250            //TODO: It's best to use this code also for compilation against sources
251            // but sometimes structures that have expectedThisObject (bug?) mapped to static classes
252            ReceiverParameterDescriptor expectedThisObject = descriptor.getExpectedThisObject();
253            if (expectedThisObject != null) {
254                ClassDescriptor expectedThisClass = (ClassDescriptor) expectedThisObject.getContainingDeclaration();
255                if (!expectedThisClass.getKind().isSingleton()) {
256                    return expectedThisClass;
257                }
258            }
259    
260            return null;
261        }
262    
263        @NotNull
264        public static String[] getExceptions(@NotNull Annotated annotatedDescriptor, @NotNull final JetTypeMapper mapper) {
265            // Can't say 'throws.class', because 'throws' is a reserved work in Java
266            AnnotationDescriptor annotation = annotatedDescriptor.getAnnotations().findAnnotation(new FqName("kotlin.throws"));
267            if (annotation == null) return ArrayUtil.EMPTY_STRING_ARRAY;
268    
269            Collection<CompileTimeConstant<?>> values = annotation.getAllValueArguments().values();
270            if (values.isEmpty()) return ArrayUtil.EMPTY_STRING_ARRAY;
271    
272            Object value = values.iterator().next();
273            if (!(value instanceof ArrayValue)) return ArrayUtil.EMPTY_STRING_ARRAY;
274            ArrayValue arrayValue = (ArrayValue) value;
275    
276            List<String> strings = ContainerUtil.mapNotNull(
277                    arrayValue.getValue(),
278                    new Function<CompileTimeConstant<?>, String>() {
279                        @Override
280                        public String fun(CompileTimeConstant<?> constant) {
281                            if (constant instanceof JavaClassValue) {
282                                JavaClassValue classValue = (JavaClassValue) constant;
283                                ClassDescriptor classDescriptor = DescriptorUtils.getClassDescriptorForType(classValue.getValue());
284                                return mapper.mapClass(classDescriptor).getInternalName();
285                            }
286                            return null;
287                        }
288                    }
289            );
290            return strings.toArray(new String[strings.size()]);
291        }
292    }