001 /*
002 * Copyright 2010-2015 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.resolve.inline;
018
019 import com.intellij.psi.PsiElement;
020 import org.jetbrains.annotations.NotNull;
021 import org.jetbrains.annotations.Nullable;
022 import org.jetbrains.kotlin.builtins.FunctionTypesKt;
023 import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
024 import org.jetbrains.kotlin.descriptors.*;
025 import org.jetbrains.kotlin.psi.*;
026 import org.jetbrains.kotlin.resolve.BindingContext;
027 import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
028 import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
029 import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext;
030 import org.jetbrains.kotlin.resolve.calls.model.ArgumentMapping;
031 import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch;
032 import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
033
034 public class InlineUtil {
035 public static boolean isInlineLambdaParameter(@NotNull ParameterDescriptor valueParameterOrReceiver) {
036 return !(valueParameterOrReceiver instanceof ValueParameterDescriptor
037 && ((ValueParameterDescriptor) valueParameterOrReceiver).isNoinline()) &&
038 FunctionTypesKt.isFunctionType(valueParameterOrReceiver.getOriginal().getType());
039 }
040
041 public static boolean isInline(@Nullable DeclarationDescriptor descriptor) {
042 return descriptor instanceof FunctionDescriptor && getInlineStrategy(descriptor).isInline();
043 }
044
045 public static boolean isInlineOrContainingInline(@Nullable DeclarationDescriptor descriptor) {
046 if (isInline(descriptor)) return true;
047 if (descriptor == null) return false;
048 return isInlineOrContainingInline(descriptor.getContainingDeclaration());
049 }
050
051 @NotNull
052 public static InlineStrategy getInlineStrategy(@NotNull DeclarationDescriptor descriptor) {
053 if (descriptor instanceof FunctionDescriptor &&
054 ((FunctionDescriptor) descriptor).isInline()) {
055 return InlineStrategy.AS_FUNCTION;
056 }
057
058 return InlineStrategy.NOT_INLINE;
059 }
060
061 public static boolean checkNonLocalReturnUsage(
062 @NotNull DeclarationDescriptor fromFunction,
063 @NotNull KtExpression startExpression,
064 @NotNull ResolutionContext<?> context
065 ) {
066 PsiElement containingFunction = context.getContextParentOfType(startExpression, KtClassOrObject.class, KtDeclarationWithBody.class);
067 if (containingFunction == null) {
068 return false;
069 }
070
071 DeclarationDescriptor containingFunctionDescriptor = context.trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, containingFunction);
072 if (containingFunctionDescriptor == null) {
073 return false;
074 }
075
076 BindingContext bindingContext = context.trace.getBindingContext();
077
078 while (canBeInlineArgument(containingFunction) && fromFunction != containingFunctionDescriptor) {
079 if (!isInlinedArgument((KtFunction) containingFunction, bindingContext, true)) {
080 return false;
081 }
082
083 containingFunctionDescriptor = getContainingClassOrFunctionDescriptor(containingFunctionDescriptor, true);
084
085 containingFunction = containingFunctionDescriptor != null
086 ? DescriptorToSourceUtils.descriptorToDeclaration(containingFunctionDescriptor)
087 : null;
088 }
089
090 return fromFunction == containingFunctionDescriptor;
091 }
092
093 public static boolean isInlinedArgument(
094 @NotNull KtFunction argument,
095 @NotNull BindingContext bindingContext,
096 boolean checkNonLocalReturn
097 ) {
098 ValueParameterDescriptor descriptor = getInlineArgumentDescriptor(argument, bindingContext);
099 if (descriptor != null) {
100 return !checkNonLocalReturn || allowsNonLocalReturns(descriptor);
101 }
102
103 return false;
104 }
105
106 @Nullable
107 public static ValueParameterDescriptor getInlineArgumentDescriptor(
108 @NotNull KtFunction argument,
109 @NotNull BindingContext bindingContext
110 ) {
111 if (!canBeInlineArgument(argument)) return null;
112
113 KtExpression call = KtPsiUtil.getParentCallIfPresent(argument);
114 if (call == null) return null;
115
116 ResolvedCall<?> resolvedCall = CallUtilKt.getResolvedCall(call, bindingContext);
117 if (resolvedCall == null) return null;
118
119 CallableDescriptor descriptor = resolvedCall.getResultingDescriptor();
120 if (!isInline(descriptor) && !isArrayConstructorWithLambda(descriptor)) return null;
121
122 ValueArgument valueArgument = CallUtilKt.getValueArgumentForExpression(resolvedCall.getCall(), argument);
123 if (valueArgument == null) return null;
124
125 ArgumentMapping mapping = resolvedCall.getArgumentMapping(valueArgument);
126 if (!(mapping instanceof ArgumentMatch)) return null;
127
128 ValueParameterDescriptor parameter = ((ArgumentMatch) mapping).getValueParameter();
129 return isInlineLambdaParameter(parameter) ? parameter : null;
130 }
131
132 public static boolean canBeInlineArgument(@Nullable PsiElement functionalExpression) {
133 return functionalExpression instanceof KtFunctionLiteral || functionalExpression instanceof KtNamedFunction;
134 }
135
136 /**
137 * @return true if the descriptor is the constructor of one of 9 array classes (Array<T>, IntArray, FloatArray, ...)
138 * which takes the size and an initializer lambda as parameters. Such constructors are marked as 'inline' but they are not loaded
139 * as such because the 'inline' flag is not stored for constructors in the binary metadata. Therefore we pretend that they are inline
140 */
141 public static boolean isArrayConstructorWithLambda(@NotNull CallableDescriptor descriptor) {
142 return descriptor.getValueParameters().size() == 2 &&
143 descriptor instanceof ConstructorDescriptor &&
144 KotlinBuiltIns.isArrayOrPrimitiveArray(((ConstructorDescriptor) descriptor).getContainingDeclaration());
145 }
146
147 @Nullable
148 public static DeclarationDescriptor getContainingClassOrFunctionDescriptor(@NotNull DeclarationDescriptor descriptor, boolean strict) {
149 DeclarationDescriptor current = strict ? descriptor.getContainingDeclaration() : descriptor;
150 while (current != null) {
151 if (current instanceof FunctionDescriptor || current instanceof ClassDescriptor) {
152 return current;
153 }
154 current = current.getContainingDeclaration();
155 }
156
157 return null;
158 }
159
160 public static boolean allowsNonLocalReturns(@NotNull CallableDescriptor lambda) {
161 if (lambda instanceof ValueParameterDescriptor) {
162 if (((ValueParameterDescriptor) lambda).isCrossinline()) {
163 //annotated
164 return false;
165 }
166 }
167 return true;
168 }
169
170 public static boolean containsReifiedTypeParameters(@NotNull CallableDescriptor descriptor) {
171 for (TypeParameterDescriptor typeParameterDescriptor : descriptor.getTypeParameters()) {
172 if (typeParameterDescriptor.isReified()) return true;
173 }
174
175 return false;
176 }
177 }