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 }