001 /*
002 * Copyright 2010-2016 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.kotlin.codegen;
018
019 import com.intellij.psi.PsiElement;
020 import com.intellij.psi.PsiFile;
021 import kotlin.collections.CollectionsKt;
022 import kotlin.jvm.functions.Function1;
023 import kotlin.text.StringsKt;
024 import org.jetbrains.annotations.NotNull;
025 import org.jetbrains.annotations.Nullable;
026 import org.jetbrains.kotlin.codegen.binding.CalculatedClosure;
027 import org.jetbrains.kotlin.codegen.context.CodegenContext;
028 import org.jetbrains.kotlin.codegen.context.FacadePartWithSourceFile;
029 import org.jetbrains.kotlin.codegen.context.MethodContext;
030 import org.jetbrains.kotlin.codegen.context.RootContext;
031 import org.jetbrains.kotlin.codegen.state.GenerationState;
032 import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
033 import org.jetbrains.kotlin.descriptors.*;
034 import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
035 import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor;
036 import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor;
037 import org.jetbrains.kotlin.load.kotlin.*;
038 import org.jetbrains.kotlin.psi.Call;
039 import org.jetbrains.kotlin.psi.KtFile;
040 import org.jetbrains.kotlin.psi.KtFunction;
041 import org.jetbrains.kotlin.psi.codeFragmentUtil.CodeFragmentUtilKt;
042 import org.jetbrains.kotlin.resolve.BindingContext;
043 import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
044 import org.jetbrains.kotlin.resolve.DescriptorUtils;
045 import org.jetbrains.kotlin.resolve.inline.InlineUtil;
046 import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
047 import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor;
048 import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor;
049 import org.jetbrains.kotlin.types.KotlinType;
050 import org.jetbrains.org.objectweb.asm.Opcodes;
051
052 import java.io.File;
053
054 import static org.jetbrains.kotlin.descriptors.ClassKind.ANNOTATION_CLASS;
055 import static org.jetbrains.kotlin.descriptors.ClassKind.INTERFACE;
056 import static org.jetbrains.kotlin.descriptors.Modality.ABSTRACT;
057 import static org.jetbrains.kotlin.descriptors.Modality.FINAL;
058 import static org.jetbrains.kotlin.resolve.BindingContext.DELEGATED_PROPERTY_CALL;
059 import static org.jetbrains.kotlin.resolve.jvm.annotations.AnnotationUtilKt.hasJvmFieldAnnotation;
060
061 public class JvmCodegenUtil {
062
063 private JvmCodegenUtil() {
064 }
065
066 public static boolean isAnnotationOrJvmInterfaceWithoutDefaults(@NotNull DeclarationDescriptor descriptor, @NotNull GenerationState state) {
067 return isAnnotationOrJvmInterfaceWithoutDefaults(descriptor, state.isJvm8Target(), state.isJvm8TargetWithDefaults());
068 }
069
070 private static boolean isAnnotationOrJvmInterfaceWithoutDefaults(@NotNull DeclarationDescriptor descriptor, boolean isJvm8Target, boolean isJvm8TargetWithDefaults) {
071 if (!isJvmInterface(descriptor)) {
072 return false;
073 }
074 if (ANNOTATION_CLASS == ((ClassDescriptor) descriptor).getKind()) return true;
075
076 if (descriptor instanceof DeserializedClassDescriptor) {
077 SourceElement source = ((DeserializedClassDescriptor) descriptor).getSource();
078 if (source instanceof KotlinJvmBinarySourceElement) {
079 KotlinJvmBinaryClass binaryClass = ((KotlinJvmBinarySourceElement) source).getBinaryClass();
080 assert binaryClass instanceof FileBasedKotlinClass :
081 "KotlinJvmBinaryClass should be subclass of FileBasedKotlinClass, but " + binaryClass;
082 /*TODO need add some flags to compiled code*/
083 return true || ((FileBasedKotlinClass) binaryClass).getClassVersion() == Opcodes.V1_6;
084 }
085 }
086 return !isJvm8TargetWithDefaults;
087 }
088
089 public static boolean isJvm8InterfaceWithDefaults(@NotNull DeclarationDescriptor descriptor, @NotNull GenerationState state) {
090 return isJvm8InterfaceWithDefaults(descriptor, state.isJvm8Target(), state.isJvm8TargetWithDefaults());
091 }
092
093 public static boolean isJvm8InterfaceWithDefaults(@NotNull DeclarationDescriptor descriptor, boolean isJvm8Target, boolean isJvm8TargetWithDefaults) {
094 return DescriptorUtils.isInterface(descriptor) && !isAnnotationOrJvmInterfaceWithoutDefaults(descriptor, isJvm8Target, isJvm8TargetWithDefaults);
095 }
096
097 public static boolean isJvm8InterfaceWithDefaultsMember(@NotNull CallableMemberDescriptor descriptor, @NotNull GenerationState state) {
098 DeclarationDescriptor declaration = descriptor.getContainingDeclaration();
099 return isJvm8InterfaceWithDefaults(declaration, state);
100 }
101
102 public static boolean isNonDefaultInterfaceMember(@NotNull CallableMemberDescriptor descriptor, @NotNull GenerationState state) {
103 if (!isJvmInterface(descriptor.getContainingDeclaration())) {
104 return false;
105 }
106 if (descriptor instanceof JavaCallableMemberDescriptor) {
107 return descriptor.getModality() == Modality.ABSTRACT;
108 }
109
110 return !isJvm8InterfaceWithDefaultsMember(descriptor, state);
111 }
112
113 public static boolean isJvmInterface(DeclarationDescriptor descriptor) {
114 if (descriptor instanceof ClassDescriptor) {
115 ClassKind kind = ((ClassDescriptor) descriptor).getKind();
116 return kind == INTERFACE || kind == ANNOTATION_CLASS;
117 }
118 return false;
119 }
120
121 public static boolean isJvmInterface(KotlinType type) {
122 return isJvmInterface(type.getConstructor().getDeclarationDescriptor());
123 }
124
125 public static boolean isConst(@NotNull CalculatedClosure closure) {
126 return closure.getCaptureThis() == null &&
127 closure.getCaptureReceiverType() == null &&
128 closure.getCaptureVariables().isEmpty() &&
129 !closure.isSuspend();
130 }
131
132 private static boolean isCallInsideSameClassAsDeclared(@NotNull CallableMemberDescriptor descriptor, @NotNull CodegenContext context) {
133 boolean isFakeOverride = descriptor.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE;
134 boolean isDelegate = descriptor.getKind() == CallableMemberDescriptor.Kind.DELEGATION;
135
136 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration().getOriginal();
137
138 return !isFakeOverride && !isDelegate &&
139 (((context.hasThisDescriptor() && containingDeclaration == context.getThisDescriptor()) ||
140 ((context.getParentContext() instanceof FacadePartWithSourceFile)
141 && isWithinSameFile(((FacadePartWithSourceFile) context.getParentContext()).getSourceFile(), descriptor)))
142 && context.getContextKind() != OwnerKind.DEFAULT_IMPLS);
143 }
144
145 private static boolean isWithinSameFile(
146 @Nullable KtFile callerFile,
147 @NotNull CallableMemberDescriptor descriptor
148 ) {
149 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration().getOriginal();
150 if (containingDeclaration instanceof PackageFragmentDescriptor) {
151 PsiElement calleeElement = DescriptorToSourceUtils.descriptorToDeclaration(descriptor);
152 PsiFile calleeFile = calleeElement != null ? calleeElement.getContainingFile() : null;
153 return callerFile != null && callerFile != SourceFile.NO_SOURCE_FILE && calleeFile == callerFile;
154
155 }
156 return false;
157 }
158
159 public static boolean isCallInsideSameModuleAsDeclared(
160 @NotNull CallableMemberDescriptor declarationDescriptor,
161 @NotNull CodegenContext context,
162 @Nullable File outDirectory
163 ) {
164 if (context instanceof RootContext) {
165 return true;
166 }
167 DeclarationDescriptor contextDescriptor = context.getContextDescriptor();
168
169 CallableMemberDescriptor directMember = getDirectMember(declarationDescriptor);
170 if (directMember instanceof DeserializedCallableMemberDescriptor) {
171 return ModuleVisibilityUtilsKt.isContainedByCompiledPartOfOurModule(directMember, outDirectory);
172 }
173 else {
174 return DescriptorUtils.areInSameModule(directMember, contextDescriptor);
175 }
176 }
177
178 public static boolean hasAbstractMembers(@NotNull ClassDescriptor classDescriptor) {
179 return CollectionsKt.any(DescriptorUtils.getAllDescriptors(classDescriptor.getDefaultType().getMemberScope()),
180 new Function1<DeclarationDescriptor, Boolean>() {
181 @Override
182 public Boolean invoke(DeclarationDescriptor descriptor) {
183 return descriptor instanceof CallableMemberDescriptor &&
184 ((CallableMemberDescriptor) descriptor).getModality() == ABSTRACT;
185 }
186 }
187 );
188 }
189
190 public static boolean isConstOrHasJvmFieldAnnotation(@NotNull PropertyDescriptor propertyDescriptor) {
191 return propertyDescriptor.isConst() || hasJvmFieldAnnotation(propertyDescriptor);
192 }
193
194 public static boolean couldUseDirectAccessToProperty(
195 @NotNull PropertyDescriptor property,
196 boolean forGetter,
197 boolean isDelegated,
198 @NotNull MethodContext contextBeforeInline,
199 boolean shouldInlineConstVals
200 ) {
201 if (shouldInlineConstVals && property.isConst()) return true;
202
203 if (KotlinTypeMapper.isAccessor(property)) return false;
204
205 CodegenContext context = contextBeforeInline.getFirstCrossInlineOrNonInlineContext();
206 // Inline functions can't use direct access because a field may not be visible at the call site
207 if (context.isInlineMethodContext()) {
208 return false;
209 }
210
211 if (!isCallInsideSameClassAsDeclared(property, context)) {
212 if (!isDebuggerContext(context)) {
213 // Unless we are evaluating expression in debugger context, only properties of the same class can be directly accessed
214 return false;
215 }
216 else {
217 // In debugger we want to access through accessors if they are generated
218
219 // Non default accessors must always be generated
220 for (PropertyAccessorDescriptor accessorDescriptor : property.getAccessors()) {
221 if (!accessorDescriptor.isDefault()) {
222 if (forGetter == accessorDescriptor instanceof PropertyGetterDescriptor) {
223 return false;
224 }
225 }
226 }
227
228 // If property overrides something, accessors must be generated too
229 if (!property.getOverriddenDescriptors().isEmpty()) return false;
230 }
231 }
232
233 // Delegated and extension properties have no backing fields
234 if (isDelegated || property.getExtensionReceiverParameter() != null) return false;
235
236 // Companion object properties cannot be accessed directly because their backing fields are stored in the containing class
237 if (DescriptorUtils.isCompanionObject(property.getContainingDeclaration())) return false;
238
239 PropertyAccessorDescriptor accessor = forGetter ? property.getGetter() : property.getSetter();
240
241 // If there's no accessor declared we can use direct access
242 if (accessor == null) return true;
243
244 // If the accessor is non-default (i.e. it has some code) we should call that accessor and not use direct access
245 if (DescriptorPsiUtilsKt.hasBody(accessor)) return false;
246
247 // If the accessor is private or final, it can't be overridden in the subclass and thus we can use direct access
248 return Visibilities.isPrivate(property.getVisibility()) || accessor.getModality() == FINAL;
249 }
250
251 private static boolean isDebuggerContext(@NotNull CodegenContext context) {
252 KtFile file = DescriptorToSourceUtils.getContainingFile(context.getContextDescriptor());
253 return file != null && CodeFragmentUtilKt.getSuppressDiagnosticsInDebugMode(file);
254 }
255
256 @Nullable
257 public static ClassDescriptor getDispatchReceiverParameterForConstructorCall(
258 @NotNull ConstructorDescriptor descriptor,
259 @Nullable CalculatedClosure closure
260 ) {
261 //for compilation against sources
262 if (closure != null) {
263 return closure.getCaptureThis();
264 }
265
266 //for compilation against binaries
267 //TODO: It's best to use this code also for compilation against sources
268 // but sometimes structures that have dispatchReceiver (bug?) mapped to static classes
269 ReceiverParameterDescriptor dispatchReceiver = descriptor.getDispatchReceiverParameter();
270 if (dispatchReceiver != null) {
271 ClassDescriptor expectedThisClass = (ClassDescriptor) dispatchReceiver.getContainingDeclaration();
272 if (!expectedThisClass.getKind().isSingleton()) {
273 return expectedThisClass;
274 }
275 }
276
277 return null;
278 }
279
280 @NotNull
281 public static CallableMemberDescriptor getDirectMember(@NotNull CallableMemberDescriptor descriptor) {
282 return DescriptorUtils.getDirectMember(descriptor);
283 }
284
285 public static boolean isArgumentWhichWillBeInlined(@NotNull BindingContext bindingContext, @NotNull DeclarationDescriptor descriptor) {
286 PsiElement declaration = DescriptorToSourceUtils.descriptorToDeclaration(descriptor);
287 return InlineUtil.canBeInlineArgument(declaration) &&
288 InlineUtil.isInlinedArgument((KtFunction) declaration, bindingContext, false);
289 }
290
291 @NotNull
292 public static String getModuleName(ModuleDescriptor module) {
293 return StringsKt.removeSurrounding(module.getName().asString(), "<", ">");
294 }
295
296 @NotNull
297 public static String getMappingFileName(@NotNull String moduleName) {
298 return "META-INF/" + moduleName + "." + ModuleMapping.MAPPING_FILE_EXT;
299 }
300
301 public static boolean isInlinedJavaConstProperty(VariableDescriptor descriptor) {
302 if (!(descriptor instanceof JavaPropertyDescriptor)) return false;
303 return descriptor.isConst();
304 }
305
306 @Nullable
307 public static KotlinType getPropertyDelegateType(
308 @NotNull VariableDescriptorWithAccessors descriptor,
309 @NotNull BindingContext bindingContext
310 ) {
311 VariableAccessorDescriptor getter = descriptor.getGetter();
312 if (getter != null) {
313 Call call = bindingContext.get(DELEGATED_PROPERTY_CALL, getter);
314 if (call != null) {
315 assert call.getExplicitReceiver() != null : "No explicit receiver for call:" + call;
316 return ((ReceiverValue) call.getExplicitReceiver()).getType();
317 }
318 }
319 return null;
320 }
321
322 public static boolean isDelegatedLocalVariable(@NotNull DeclarationDescriptor descriptor) {
323 return descriptor instanceof LocalVariableDescriptor && ((LocalVariableDescriptor) descriptor).isDelegated();
324 }
325 }