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.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.BindingTrace; 028 import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils; 029 import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt; 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 KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(valueParameterOrReceiver.getOriginal().getType()); 039 } 040 041 public static boolean isInline(@Nullable DeclarationDescriptor descriptor) { 042 return descriptor instanceof SimpleFunctionDescriptor && 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 BindingTrace trace 065 ) { 066 PsiElement containingFunction = PsiTreeUtil.getParentOfType(startExpression, KtClassOrObject.class, KtDeclarationWithBody.class); 067 if (containingFunction == null) { 068 return false; 069 } 070 071 DeclarationDescriptor containingFunctionDescriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, containingFunction); 072 if (containingFunctionDescriptor == null) { 073 return false; 074 } 075 076 BindingContext bindingContext = 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 }