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