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