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.idea;
018
019 import com.intellij.util.NotNullFunction;
020 import org.jetbrains.annotations.NotNull;
021 import org.jetbrains.annotations.Nullable;
022 import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
023 import org.jetbrains.kotlin.descriptors.*;
024 import org.jetbrains.kotlin.psi.JetAnnotationEntry;
025 import org.jetbrains.kotlin.psi.JetDeclaration;
026 import org.jetbrains.kotlin.psi.JetFile;
027 import org.jetbrains.kotlin.psi.JetNamedFunction;
028 import org.jetbrains.kotlin.resolve.BindingContext;
029 import org.jetbrains.kotlin.resolve.DescriptorUtils;
030 import org.jetbrains.kotlin.resolve.annotations.AnnotationsPackage;
031 import org.jetbrains.kotlin.types.JetType;
032 import org.jetbrains.kotlin.types.TypeProjection;
033 import org.jetbrains.kotlin.types.Variance;
034
035 import java.util.Collection;
036 import java.util.List;
037
038 public class MainFunctionDetector {
039 private final NotNullFunction<JetNamedFunction, FunctionDescriptor> getFunctionDescriptor;
040
041 /** Assumes that the function declaration is already resolved and the descriptor can be found in the {@code bindingContext}. */
042 public MainFunctionDetector(@NotNull final BindingContext bindingContext) {
043 this.getFunctionDescriptor = new NotNullFunction<JetNamedFunction, FunctionDescriptor>() {
044 @NotNull
045 @Override
046 public FunctionDescriptor fun(JetNamedFunction function) {
047 SimpleFunctionDescriptor functionDescriptor = bindingContext.get(BindingContext.FUNCTION, function);
048 if (functionDescriptor == null) {
049 throw new IllegalStateException("No descriptor resolved for " + function + " " + function.getText());
050 }
051 return functionDescriptor;
052 }
053 };
054 }
055
056 public MainFunctionDetector(@NotNull NotNullFunction<JetNamedFunction, FunctionDescriptor> functionResolver) {
057 this.getFunctionDescriptor = functionResolver;
058 }
059
060 public boolean hasMain(@NotNull List<JetDeclaration> declarations) {
061 return findMainFunction(declarations) != null;
062 }
063
064 public boolean isMain(@NotNull JetNamedFunction function) {
065 if (function.isLocal()) {
066 return false;
067 }
068
069 if (function.getValueParameters().size() != 1 || !function.getTypeParameters().isEmpty()) {
070 return false;
071 }
072
073 /* Psi only check for kotlin.jvm.jvmName annotation */
074 if (!"main".equals(function.getName()) && !hasAnnotationWithExactNumberOfArguments(function, 1)) {
075 return false;
076 }
077
078 /* Psi only check for kotlin.jvm.jvmStatic annotation */
079 if (!function.isTopLevel() && !hasAnnotationWithExactNumberOfArguments(function, 0)) {
080 return false;
081 }
082
083 FunctionDescriptor functionDescriptor = getFunctionDescriptor.fun(function);
084 if (!getJVMFunctionName(functionDescriptor).equals("main")) {
085 return false;
086 }
087
088 List<ValueParameterDescriptor> parameters = functionDescriptor.getValueParameters();
089 if (parameters.size() != 1) return false;
090
091 ValueParameterDescriptor parameter = parameters.get(0);
092 JetType parameterType = parameter.getType();
093 if (!KotlinBuiltIns.isArray(parameterType)) return false;
094
095 List<TypeProjection> typeArguments = parameterType.getArguments();
096 if (typeArguments.size() != 1) return false;
097
098 JetType typeArgument = typeArguments.get(0).getType();
099 if (!KotlinBuiltIns.isString(typeArgument)) {
100 return false;
101 }
102 if (typeArguments.get(0).getProjectionKind() == Variance.IN_VARIANCE) {
103 return false;
104 }
105
106 if (DescriptorUtils.isTopLevelDeclaration(functionDescriptor)) return true;
107
108 DeclarationDescriptor containingDeclaration = functionDescriptor.getContainingDeclaration();
109 return containingDeclaration instanceof ClassDescriptor
110 && ((ClassDescriptor) containingDeclaration).getKind().isSingleton()
111 && AnnotationsPackage.hasPlatformStaticAnnotation(functionDescriptor);
112 }
113
114 @Nullable
115 public JetNamedFunction getMainFunction(@NotNull Collection<JetFile> files) {
116 for (JetFile file : files) {
117 JetNamedFunction mainFunction = findMainFunction(file.getDeclarations());
118 if (mainFunction != null) {
119 return mainFunction;
120 }
121 }
122 return null;
123 }
124
125 @Nullable
126 private JetNamedFunction findMainFunction(@NotNull List<JetDeclaration> declarations) {
127 for (JetDeclaration declaration : declarations) {
128 if (declaration instanceof JetNamedFunction) {
129 JetNamedFunction candidateFunction = (JetNamedFunction) declaration;
130 if (isMain(candidateFunction)) {
131 return candidateFunction;
132 }
133 }
134 }
135 return null;
136 }
137
138 @NotNull
139 private static String getJVMFunctionName(FunctionDescriptor functionDescriptor) {
140 String platformName = DescriptorUtils.getJvmName(functionDescriptor);
141 if (platformName != null) {
142 return platformName;
143 }
144
145 return functionDescriptor.getName().asString();
146 }
147
148 private static boolean hasAnnotationWithExactNumberOfArguments(@NotNull JetNamedFunction function, int number) {
149 for (JetAnnotationEntry entry : function.getAnnotationEntries()) {
150 if (entry.getValueArguments().size() == number) {
151 return true;
152 }
153 }
154
155 return false;
156 }
157 }