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