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.vfs.StandardFileSystems;
020 import com.intellij.openapi.vfs.VfsUtilCore;
021 import com.intellij.openapi.vfs.VirtualFile;
022 import com.intellij.util.containers.Stack;
023 import kotlin.Function1;
024 import kotlin.KotlinPackage;
025 import org.jetbrains.annotations.NotNull;
026 import org.jetbrains.annotations.Nullable;
027 import org.jetbrains.jet.codegen.binding.CalculatedClosure;
028 import org.jetbrains.jet.codegen.context.CodegenContext;
029 import org.jetbrains.jet.codegen.context.MethodContext;
030 import org.jetbrains.jet.codegen.context.PackageContext;
031 import org.jetbrains.jet.codegen.state.JetTypeMapper;
032 import org.jetbrains.jet.descriptors.serialization.descriptors.DeserializedCallableMemberDescriptor;
033 import org.jetbrains.jet.lang.descriptors.*;
034 import org.jetbrains.jet.lang.psi.JetFile;
035 import org.jetbrains.jet.lang.psi.codeFragmentUtil.CodeFragmentUtilPackage;
036 import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils;
037 import org.jetbrains.jet.lang.resolve.DescriptorUtils;
038 import org.jetbrains.jet.lang.resolve.calls.CallResolverUtil;
039 import org.jetbrains.jet.lang.resolve.java.descriptor.JavaPackageFragmentDescriptor;
040 import org.jetbrains.jet.lang.resolve.java.lazy.descriptors.LazyPackageFragmentScopeForJavaPackage;
041 import org.jetbrains.jet.lang.resolve.kotlin.KotlinJvmBinaryClass;
042 import org.jetbrains.jet.lang.resolve.kotlin.VirtualFileKotlinClass;
043 import org.jetbrains.jet.lang.resolve.kotlin.incremental.IncrementalPackageFragmentProvider;
044 import org.jetbrains.jet.lang.resolve.name.Name;
045 import org.jetbrains.jet.lang.resolve.scopes.JetScope;
046 import org.jetbrains.jet.lang.types.JetType;
047 import org.jetbrains.jet.lang.types.TypeUtils;
048 import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
049 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
050
051 import java.io.File;
052 import java.util.Arrays;
053 import java.util.Collection;
054 import java.util.List;
055
056 import static org.jetbrains.jet.lang.descriptors.Modality.ABSTRACT;
057 import static org.jetbrains.jet.lang.descriptors.Modality.FINAL;
058
059 public class JvmCodegenUtil {
060
061 private JvmCodegenUtil() {
062 }
063
064 public static boolean isInterface(DeclarationDescriptor descriptor) {
065 if (descriptor instanceof ClassDescriptor) {
066 ClassKind kind = ((ClassDescriptor) descriptor).getKind();
067 return kind == ClassKind.TRAIT || kind == ClassKind.ANNOTATION_CLASS;
068 }
069 return false;
070 }
071
072 public static boolean isInterface(JetType type) {
073 return isInterface(type.getConstructor().getDeclarationDescriptor());
074 }
075
076 public static boolean isConst(@NotNull CalculatedClosure closure) {
077 return closure.getCaptureThis() == null && closure.getCaptureReceiverType() == null && closure.getCaptureVariables().isEmpty();
078 }
079
080 public static <T> T peekFromStack(Stack<T> stack) {
081 return stack.empty() ? null : stack.peek();
082 }
083
084 @Nullable
085 public static FunctionDescriptor getDeclaredFunctionByRawSignature(
086 @NotNull ClassDescriptor owner,
087 @NotNull Name name,
088 @NotNull ClassifierDescriptor returnedClassifier,
089 @NotNull ClassifierDescriptor... valueParameterClassifiers
090 ) {
091 Collection<FunctionDescriptor> functions = owner.getDefaultType().getMemberScope().getFunctions(name);
092 for (FunctionDescriptor function : functions) {
093 if (!CallResolverUtil.isOrOverridesSynthesized(function)
094 && function.getTypeParameters().isEmpty()
095 && valueParameterClassesMatch(function.getValueParameters(), Arrays.asList(valueParameterClassifiers))
096 && rawTypeMatches(function.getReturnType(), returnedClassifier)) {
097 return function;
098 }
099 }
100 return null;
101 }
102
103 private static boolean valueParameterClassesMatch(
104 @NotNull List<ValueParameterDescriptor> parameters,
105 @NotNull List<ClassifierDescriptor> classifiers) {
106 if (parameters.size() != classifiers.size()) return false;
107 for (int i = 0; i < parameters.size(); i++) {
108 ValueParameterDescriptor parameterDescriptor = parameters.get(i);
109 ClassifierDescriptor classDescriptor = classifiers.get(i);
110 if (!rawTypeMatches(parameterDescriptor.getType(), classDescriptor)) {
111 return false;
112 }
113 }
114 return true;
115 }
116
117 private static boolean rawTypeMatches(JetType type, ClassifierDescriptor classifier) {
118 return type.getConstructor().getDeclarationDescriptor().getOriginal() == classifier.getOriginal();
119 }
120
121 private static boolean isCallInsideSameClassAsDeclared(@NotNull CallableMemberDescriptor descriptor, @NotNull CodegenContext context) {
122 boolean isFakeOverride = descriptor.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE;
123 boolean isDelegate = descriptor.getKind() == CallableMemberDescriptor.Kind.DELEGATION;
124
125 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration().getOriginal();
126
127 return !isFakeOverride && !isDelegate &&
128 (((context.hasThisDescriptor() && containingDeclaration == context.getThisDescriptor()) ||
129 (context.getParentContext() instanceof PackageContext
130 && isSamePackageInSameModule(context.getParentContext().getContextDescriptor(), containingDeclaration)))
131 && context.getContextKind() != OwnerKind.TRAIT_IMPL);
132 }
133
134 private static boolean isSamePackageInSameModule(
135 @NotNull DeclarationDescriptor callerOwner,
136 @NotNull DeclarationDescriptor calleeOwner
137 ) {
138 if (callerOwner instanceof PackageFragmentDescriptor && calleeOwner instanceof PackageFragmentDescriptor) {
139 PackageFragmentDescriptor callerFragment = (PackageFragmentDescriptor) callerOwner;
140 PackageFragmentDescriptor calleeFragment = (PackageFragmentDescriptor) calleeOwner;
141
142 // backing field should be used directly within same module of same package
143 if (callerFragment == calleeFragment) {
144 return true;
145 }
146 return callerFragment.getFqName().equals(calleeFragment.getFqName())
147 && calleeFragment instanceof IncrementalPackageFragmentProvider.IncrementalPackageFragment;
148 }
149 return false;
150 }
151
152 public static boolean isCallInsideSameModuleAsDeclared(
153 @NotNull CallableMemberDescriptor declarationDescriptor,
154 @NotNull CodegenContext context,
155 @Nullable File outDirectory
156 ) {
157 if (context == CodegenContext.STATIC) {
158 return true;
159 }
160 DeclarationDescriptor contextDescriptor = context.getContextDescriptor();
161
162 CallableMemberDescriptor directMember = getDirectMember(declarationDescriptor);
163 if (directMember instanceof DeserializedCallableMemberDescriptor) {
164 return isContainedByCompiledPartOfOurModule(((DeserializedCallableMemberDescriptor) directMember), outDirectory);
165 }
166 else {
167 return DescriptorUtils.areInSameModule(directMember, contextDescriptor);
168 }
169 }
170
171 private static boolean isContainedByCompiledPartOfOurModule(
172 @NotNull DeserializedCallableMemberDescriptor descriptor,
173 @Nullable File outDirectory
174 ) {
175 if (descriptor.getContainingDeclaration() instanceof IncrementalPackageFragmentProvider.IncrementalPackageFragment) {
176 return true;
177 }
178
179 if (outDirectory == null) {
180 return false;
181 }
182
183 if (!(descriptor.getContainingDeclaration() instanceof JavaPackageFragmentDescriptor)) {
184 return false;
185 }
186 JavaPackageFragmentDescriptor packageFragment = (JavaPackageFragmentDescriptor) descriptor.getContainingDeclaration();
187 JetScope packageScope = packageFragment.getMemberScope();
188 if (!(packageScope instanceof LazyPackageFragmentScopeForJavaPackage)) {
189 return false;
190 }
191 KotlinJvmBinaryClass binaryClass = ((LazyPackageFragmentScopeForJavaPackage) packageScope).getKotlinBinaryClass();
192
193 if (binaryClass instanceof VirtualFileKotlinClass) {
194 VirtualFile file = ((VirtualFileKotlinClass) binaryClass).getFile();
195 if (file.getFileSystem().getProtocol() == StandardFileSystems.FILE_PROTOCOL) {
196 File ioFile = VfsUtilCore.virtualToIoFile(file);
197 return ioFile.getAbsolutePath().startsWith(outDirectory.getAbsolutePath() + File.separator);
198 }
199 }
200 return false;
201 }
202
203 public static boolean hasAbstractMembers(@NotNull ClassDescriptor classDescriptor) {
204 return KotlinPackage.any(classDescriptor.getDefaultType().getMemberScope().getAllDescriptors(),
205 new Function1<DeclarationDescriptor, Boolean>() {
206 @Override
207 public Boolean invoke(DeclarationDescriptor descriptor) {
208 return descriptor instanceof CallableMemberDescriptor &&
209 ((CallableMemberDescriptor) descriptor).getModality() == ABSTRACT;
210 }
211 }
212 );
213 }
214
215 public static boolean couldUseDirectAccessToProperty(
216 @NotNull PropertyDescriptor property,
217 boolean forGetter,
218 boolean isDelegated,
219 @NotNull MethodContext context
220 ) {
221 if (JetTypeMapper.isAccessor(property)) return false;
222
223 // Inline functions can't use direct access because a field may not be visible at the call site
224 if (context.isInlineFunction()) return false;
225
226 // Only properties of the same class can be directly accessed, except when we are evaluating expressions in the debugger
227 if (!isCallInsideSameClassAsDeclared(property, context) && !isDebuggerContext(context)) return false;
228
229 // Delegated and extension properties have no backing fields
230 if (isDelegated || property.getReceiverParameter() != null) return false;
231
232 // Class object properties cannot be accessed directly because their backing fields are stored in the containing class
233 if (DescriptorUtils.isClassObject(property.getContainingDeclaration())) return false;
234
235 PropertyAccessorDescriptor accessor = forGetter ? property.getGetter() : property.getSetter();
236
237 // If there's no accessor declared we can use direct access
238 if (accessor == null) return true;
239
240 // If the accessor is non-default (i.e. it has some code) we should call that accessor and not use direct access
241 if (accessor.hasBody()) return false;
242
243 // If the accessor is private or final, it can't be overridden in the subclass and thus we can use direct access
244 return property.getVisibility() == Visibilities.PRIVATE || accessor.getModality() == FINAL;
245 }
246
247 private static boolean isDebuggerContext(@NotNull MethodContext context) {
248 JetFile file = DescriptorToSourceUtils.getContainingFile(context.getContextDescriptor());
249 return file != null && CodeFragmentUtilPackage.getSkipVisibilityCheck(file);
250 }
251
252 @NotNull
253 public static ImplementationBodyCodegen getParentBodyCodegen(@Nullable MemberCodegen<?> classBodyCodegen) {
254 assert classBodyCodegen != null && classBodyCodegen.getParentCodegen() instanceof ImplementationBodyCodegen
255 : "Class object should have appropriate parent BodyCodegen";
256
257 return (ImplementationBodyCodegen) classBodyCodegen.getParentCodegen();
258 }
259
260 @Nullable
261 public static ClassDescriptor getExpectedThisObjectForConstructorCall(
262 @NotNull ConstructorDescriptor descriptor,
263 @Nullable CalculatedClosure closure
264 ) {
265 //for compilation against sources
266 if (closure != null) {
267 return closure.getCaptureThis();
268 }
269
270 //for compilation against binaries
271 //TODO: It's best to use this code also for compilation against sources
272 // but sometimes structures that have expectedThisObject (bug?) mapped to static classes
273 ReceiverParameterDescriptor expectedThisObject = descriptor.getExpectedThisObject();
274 if (expectedThisObject != null) {
275 ClassDescriptor expectedThisClass = (ClassDescriptor) expectedThisObject.getContainingDeclaration();
276 if (!expectedThisClass.getKind().isSingleton()) {
277 return expectedThisClass;
278 }
279 }
280
281 return null;
282 }
283
284 public static boolean isEnumValueOfMethod(@NotNull FunctionDescriptor functionDescriptor) {
285 List<ValueParameterDescriptor> methodTypeParameters = functionDescriptor.getValueParameters();
286 JetType nullableString = TypeUtils.makeNullable(KotlinBuiltIns.getInstance().getStringType());
287 return "valueOf".equals(functionDescriptor.getName().asString())
288 && methodTypeParameters.size() == 1
289 && JetTypeChecker.DEFAULT.isSubtypeOf(methodTypeParameters.get(0).getType(), nullableString);
290 }
291
292 public static boolean isEnumValuesMethod(@NotNull FunctionDescriptor functionDescriptor) {
293 List<ValueParameterDescriptor> methodTypeParameters = functionDescriptor.getValueParameters();
294 return "values".equals(functionDescriptor.getName().asString())
295 && methodTypeParameters.isEmpty();
296 }
297
298 @NotNull
299 public static CallableMemberDescriptor getDirectMember(@NotNull CallableMemberDescriptor descriptor) {
300 return descriptor instanceof PropertyAccessorDescriptor
301 ? ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty()
302 : descriptor;
303 }
304 }