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