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 @NotNull 046 public static InlineStrategy getInlineStrategy(@NotNull DeclarationDescriptor descriptor) { 047 if (descriptor instanceof FunctionDescriptor && 048 ((FunctionDescriptor) descriptor).isInline()) { 049 return InlineStrategy.AS_FUNCTION; 050 } 051 052 return InlineStrategy.NOT_INLINE; 053 } 054 055 public static boolean checkNonLocalReturnUsage( 056 @NotNull DeclarationDescriptor fromFunction, 057 @NotNull KtExpression startExpression, 058 @NotNull BindingTrace trace 059 ) { 060 PsiElement containingFunction = PsiTreeUtil.getParentOfType(startExpression, KtClassOrObject.class, KtDeclarationWithBody.class); 061 if (containingFunction == null) { 062 return false; 063 } 064 065 DeclarationDescriptor containingFunctionDescriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, containingFunction); 066 if (containingFunctionDescriptor == null) { 067 return false; 068 } 069 070 BindingContext bindingContext = trace.getBindingContext(); 071 072 while (canBeInlineArgument(containingFunction) && fromFunction != containingFunctionDescriptor) { 073 if (!isInlinedArgument((KtFunction) containingFunction, bindingContext, true)) { 074 return false; 075 } 076 077 containingFunctionDescriptor = getContainingClassOrFunctionDescriptor(containingFunctionDescriptor, true); 078 079 containingFunction = containingFunctionDescriptor != null 080 ? DescriptorToSourceUtils.descriptorToDeclaration(containingFunctionDescriptor) 081 : null; 082 } 083 084 return fromFunction == containingFunctionDescriptor; 085 } 086 087 public static boolean isInlinedArgument( 088 @NotNull KtFunction argument, 089 @NotNull BindingContext bindingContext, 090 boolean checkNonLocalReturn 091 ) { 092 if (!canBeInlineArgument(argument)) return false; 093 094 KtExpression call = KtPsiUtil.getParentCallIfPresent(argument); 095 if (call != null) { 096 ResolvedCall<?> resolvedCall = CallUtilKt.getResolvedCall(call, bindingContext); 097 if (resolvedCall != null && isInline(resolvedCall.getResultingDescriptor())) { 098 ValueArgument valueArgument = CallUtilKt.getValueArgumentForExpression(resolvedCall.getCall(), argument); 099 if (valueArgument != null) { 100 ArgumentMapping mapping = resolvedCall.getArgumentMapping(valueArgument); 101 if (mapping instanceof ArgumentMatch) { 102 ValueParameterDescriptor parameter = ((ArgumentMatch) mapping).getValueParameter(); 103 if (isInlineLambdaParameter(parameter)) { 104 return !checkNonLocalReturn || allowsNonLocalReturns(parameter); 105 } 106 } 107 } 108 } 109 } 110 return false; 111 } 112 113 public static boolean canBeInlineArgument(@Nullable PsiElement functionalExpression) { 114 return functionalExpression instanceof KtFunctionLiteral || functionalExpression instanceof KtNamedFunction; 115 } 116 117 @Nullable 118 public static DeclarationDescriptor getContainingClassOrFunctionDescriptor(@NotNull DeclarationDescriptor descriptor, boolean strict) { 119 DeclarationDescriptor current = strict ? descriptor.getContainingDeclaration() : descriptor; 120 while (current != null) { 121 if (current instanceof FunctionDescriptor || current instanceof ClassDescriptor) { 122 return current; 123 } 124 current = current.getContainingDeclaration(); 125 } 126 127 return null; 128 } 129 130 public static boolean allowsNonLocalReturns(@NotNull CallableDescriptor lambda) { 131 if (lambda instanceof ValueParameterDescriptor) { 132 if (((ValueParameterDescriptor) lambda).isCrossinline()) { 133 //annotated 134 return false; 135 } 136 } 137 return true; 138 } 139 140 public static boolean containsReifiedTypeParameters(@NotNull CallableDescriptor descriptor) { 141 for (TypeParameterDescriptor typeParameterDescriptor : descriptor.getTypeParameters()) { 142 if (typeParameterDescriptor.isReified()) return true; 143 } 144 145 return false; 146 } 147 }