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