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 }