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 }