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.asJava; 018 019 import com.intellij.openapi.application.ApplicationManager; 020 import com.intellij.openapi.diagnostic.Logger; 021 import com.intellij.openapi.progress.ProcessCanceledException; 022 import com.intellij.openapi.project.Project; 023 import com.intellij.openapi.util.Comparing; 024 import com.intellij.openapi.util.io.FileUtil; 025 import com.intellij.openapi.vfs.StandardFileSystems; 026 import com.intellij.openapi.vfs.VirtualFile; 027 import com.intellij.psi.*; 028 import com.intellij.psi.impl.java.stubs.PsiClassStub; 029 import com.intellij.psi.impl.light.LightTypeParameterListBuilder; 030 import com.intellij.psi.search.GlobalSearchScope; 031 import com.intellij.psi.stubs.PsiFileStub; 032 import com.intellij.psi.stubs.StubElement; 033 import com.intellij.psi.util.PsiTreeUtil; 034 import com.intellij.util.PathUtil; 035 import com.intellij.util.SmartList; 036 import kotlin.Function1; 037 import kotlin.KotlinPackage; 038 import org.jetbrains.annotations.NotNull; 039 import org.jetbrains.annotations.Nullable; 040 import org.jetbrains.kotlin.builtins.KotlinBuiltIns; 041 import org.jetbrains.kotlin.load.java.JvmAbi; 042 import org.jetbrains.kotlin.load.kotlin.PackageClassUtils; 043 import org.jetbrains.kotlin.name.FqName; 044 import org.jetbrains.kotlin.psi.*; 045 import org.jetbrains.kotlin.utils.KotlinVfsUtil; 046 import org.jetbrains.kotlin.utils.UtilsPackage; 047 048 import java.io.File; 049 import java.net.MalformedURLException; 050 import java.net.URL; 051 import java.util.*; 052 053 public class LightClassUtil { 054 private static final Logger LOG = Logger.getInstance(LightClassUtil.class); 055 056 public static final File BUILT_INS_SRC_DIR = new File("core/builtins/native", KotlinBuiltIns.BUILT_INS_PACKAGE_NAME.asString()); 057 058 private static final class BuiltinsDirUrlHolder { 059 private static final URL BUILT_INS_DIR_URL = computeBuiltInsDir(); 060 } 061 062 /** 063 * Checks whether the given file is loaded from the location where Kotlin's built-in classes are defined. 064 * As of today, this is core/builtins/native/kotlin directory and files such as Any.kt, Nothing.kt etc. 065 * 066 * Used to skip JetLightClass creation for built-ins, because built-in classes have no Java counterparts 067 */ 068 public static boolean belongsToKotlinBuiltIns(@NotNull JetFile file) { 069 VirtualFile virtualFile = file.getVirtualFile(); 070 if (virtualFile != null) { 071 VirtualFile parent = virtualFile.getParent(); 072 if (parent != null) { 073 try { 074 String jetVfsPathUrl = KotlinVfsUtil.convertFromUrl(getBuiltInsDirUrl()); 075 String fileDirVfsUrl = parent.getUrl(); 076 if (jetVfsPathUrl.equals(fileDirVfsUrl)) { 077 return true; 078 } 079 } 080 catch (MalformedURLException e) { 081 LOG.error(e); 082 } 083 } 084 } 085 086 // We deliberately return false on error: who knows what weird URLs we might come across out there 087 // it would be a pity if no light classes would be created in such cases 088 return false; 089 } 090 091 @NotNull 092 public static URL getBuiltInsDirUrl() { 093 return BuiltinsDirUrlHolder.BUILT_INS_DIR_URL; 094 } 095 096 @NotNull 097 private static URL computeBuiltInsDir() { 098 String builtInFilePath = "/" + KotlinBuiltIns.BUILT_INS_PACKAGE_NAME + "/Library.kt"; 099 100 URL url = KotlinBuiltIns.class.getResource(builtInFilePath); 101 102 if (url == null) { 103 if (ApplicationManager.getApplication().isUnitTestMode()) { 104 // HACK: Temp code. Get built-in files from the sources when running from test. 105 try { 106 return new URL(StandardFileSystems.FILE_PROTOCOL, "", 107 FileUtil.toSystemIndependentName(BUILT_INS_SRC_DIR.getAbsolutePath())); 108 } 109 catch (MalformedURLException e) { 110 throw UtilsPackage.rethrow(e); 111 } 112 } 113 114 throw new IllegalStateException("Built-ins file wasn't found at url: " + builtInFilePath); 115 } 116 117 try { 118 return new URL(url.getProtocol(), url.getHost(), PathUtil.getParentPath(url.getFile())); 119 } 120 catch (MalformedURLException e) { 121 throw new AssertionError(e); 122 } 123 } 124 125 @Nullable 126 /*package*/ static PsiClass findClass(@NotNull FqName fqn, @NotNull StubElement<?> stub) { 127 if (stub instanceof PsiClassStub && Comparing.equal(fqn.asString(), ((PsiClassStub) stub).getQualifiedName())) { 128 return (PsiClass) stub.getPsi(); 129 } 130 131 if (stub instanceof PsiClassStub || stub instanceof PsiFileStub) { 132 for (StubElement child : stub.getChildrenStubs()) { 133 PsiClass answer = findClass(fqn, child); 134 if (answer != null) return answer; 135 } 136 } 137 138 return null; 139 } 140 141 @Nullable 142 public static PsiClass getPsiClass(@Nullable JetClassOrObject classOrObject) { 143 if (classOrObject == null) return null; 144 return LightClassGenerationSupport.getInstance(classOrObject.getProject()).getPsiClass(classOrObject); 145 } 146 147 @Nullable 148 public static PsiMethod getLightClassAccessorMethod(@NotNull JetPropertyAccessor accessor) { 149 return getPsiMethodWrapper(accessor); 150 } 151 152 @Nullable 153 public static PsiField getLightFieldForCompanionObject(@NotNull JetClassOrObject companionObject) { 154 PsiClass outerPsiClass = getWrappingClass(companionObject); 155 if (outerPsiClass != null) { 156 for (PsiField fieldOfParent : outerPsiClass.getFields()) { 157 if (((KotlinLightElement<?, ?>) fieldOfParent).getOrigin() == companionObject && 158 fieldOfParent.getName().equals(companionObject.getName())) { // TODO this check is relevant while light class has deprecated OBJECT$ field 159 return fieldOfParent; 160 } 161 } 162 } 163 return null; 164 } 165 166 @NotNull 167 public static PropertyAccessorsPsiMethods getLightClassPropertyMethods(@NotNull JetProperty property) { 168 JetPropertyAccessor getter = property.getGetter(); 169 JetPropertyAccessor setter = property.getSetter(); 170 171 PsiMethod getterWrapper = getter != null ? getLightClassAccessorMethod(getter) : null; 172 PsiMethod setterWrapper = setter != null ? getLightClassAccessorMethod(setter) : null; 173 174 return extractPropertyAccessors(property, getterWrapper, setterWrapper); 175 } 176 177 @NotNull 178 public static PropertyAccessorsPsiMethods getLightClassPropertyMethods(@NotNull JetParameter parameter) { 179 return extractPropertyAccessors(parameter, null, null); 180 } 181 182 @Nullable 183 public static PsiMethod getLightClassMethod(@NotNull JetFunction function) { 184 return getPsiMethodWrapper(function); 185 } 186 187 @Nullable 188 private static PsiMethod getPsiMethodWrapper(@NotNull JetDeclaration declaration) { 189 List<PsiMethod> wrappers = getPsiMethodWrappers(declaration, false); 190 return !wrappers.isEmpty() ? wrappers.get(0) : null; 191 } 192 193 @NotNull 194 private static List<PsiMethod> getPsiMethodWrappers(@NotNull JetDeclaration declaration, boolean collectAll) { 195 PsiClass psiClass = getWrappingClass(declaration); 196 if (psiClass == null) { 197 return Collections.emptyList(); 198 } 199 200 List<PsiMethod> methods = new SmartList<PsiMethod>(); 201 for (PsiMethod method : psiClass.getMethods()) { 202 try { 203 if (method instanceof KotlinLightMethod && ((KotlinLightMethod) method).getOrigin() == declaration) { 204 methods.add(method); 205 if (!collectAll) { 206 return methods; 207 } 208 } 209 } 210 catch (ProcessCanceledException e) { 211 throw e; 212 } 213 catch (Throwable e) { 214 throw new IllegalStateException( 215 "Error while wrapping declaration " + declaration.getName() + 216 "Context\n:" + 217 String.format("=== In file ===\n" + 218 "%s\n" + 219 "=== On element ===\n" + 220 "%s\n" + 221 "=== WrappedElement ===\n" + 222 "%s\n", 223 declaration.getContainingFile().getText(), 224 declaration.getText(), 225 method.toString()), 226 e 227 ); 228 } 229 } 230 231 return methods; 232 } 233 234 @Nullable 235 private static PsiClass getWrappingClass(@NotNull JetDeclaration declaration) { 236 if (declaration instanceof JetParameter) { 237 JetClass constructorClass = JetPsiUtil.getClassIfParameterIsProperty((JetParameter) declaration); 238 if (constructorClass != null) { 239 return getPsiClass(constructorClass); 240 } 241 } 242 243 if (declaration instanceof JetPropertyAccessor) { 244 PsiElement propertyParent = declaration.getParent(); 245 assert propertyParent instanceof JetProperty : "JetProperty is expected to be parent of accessor"; 246 247 declaration = (JetProperty) propertyParent; 248 } 249 250 //noinspection unchecked 251 if (PsiTreeUtil.getParentOfType(declaration, JetFunction.class, JetProperty.class) != null) { 252 // Can't get wrappers for internal declarations. Their classes are not generated during calcStub 253 // with ClassBuilderMode.LIGHT_CLASSES mode, and this produces "Class not found exception" in getDelegate() 254 return null; 255 } 256 257 PsiElement parent = declaration.getParent(); 258 259 if (parent instanceof JetFile) { 260 // top-level declaration 261 FqName fqName = PackageClassUtils.getPackageClassFqName(((JetFile) parent).getPackageFqName()); 262 Project project = declaration.getProject(); 263 return JavaElementFinder.getInstance(project).findClass(fqName.asString(), GlobalSearchScope.allScope(project)); 264 } 265 else if (parent instanceof JetClassBody) { 266 assert parent.getParent() instanceof JetClassOrObject; 267 return getPsiClass((JetClassOrObject) parent.getParent()); 268 } 269 270 return null; 271 } 272 273 @NotNull 274 private static PropertyAccessorsPsiMethods extractPropertyAccessors( 275 @NotNull JetDeclaration jetDeclaration, 276 @Nullable PsiMethod specialGetter, @Nullable PsiMethod specialSetter 277 ) { 278 PsiMethod getterWrapper = specialGetter; 279 PsiMethod setterWrapper = specialSetter; 280 281 if (getterWrapper == null || setterWrapper == null) { 282 // If some getter or setter isn't found yet try to get it from wrappers for general declaration 283 284 List<PsiMethod> wrappers = KotlinPackage.filter( 285 getPsiMethodWrappers(jetDeclaration, true), 286 new Function1<PsiMethod, Boolean>() { 287 @Override 288 public Boolean invoke(PsiMethod method) { 289 return JvmAbi.isAccessorName(method.getName()); 290 } 291 } 292 ); 293 assert wrappers.size() <= 2 : "Maximum two wrappers are expected to be generated for declaration: " + jetDeclaration.getText(); 294 295 for (PsiMethod wrapper : wrappers) { 296 if (wrapper.getName().startsWith(JvmAbi.SETTER_PREFIX)) { 297 assert setterWrapper == null : String.format( 298 "Setter accessor isn't expected to be reassigned (old: %s, new: %s)", setterWrapper, wrapper); 299 300 setterWrapper = wrapper; 301 } 302 else { 303 assert getterWrapper == null : String.format( 304 "Getter accessor isn't expected to be reassigned (old: %s, new: %s)", getterWrapper, wrapper); 305 306 getterWrapper = wrapper; 307 } 308 } 309 } 310 311 return new PropertyAccessorsPsiMethods(getterWrapper, setterWrapper); 312 } 313 314 @NotNull 315 public static PsiTypeParameterList buildLightTypeParameterList( 316 PsiTypeParameterListOwner owner, 317 JetDeclaration declaration) { 318 LightTypeParameterListBuilder builder = new KotlinLightTypeParameterListBuilder(owner.getManager()); 319 if (declaration instanceof JetTypeParameterListOwner) { 320 JetTypeParameterListOwner typeParameterListOwner = (JetTypeParameterListOwner) declaration; 321 List<JetTypeParameter> parameters = typeParameterListOwner.getTypeParameters(); 322 for (int i = 0; i < parameters.size(); i++) { 323 JetTypeParameter jetTypeParameter = parameters.get(i); 324 String name = jetTypeParameter.getName(); 325 String safeName = name == null ? "__no_name__" : name; 326 builder.addParameter(new KotlinLightTypeParameter(owner, i, safeName)); 327 } 328 } 329 return builder; 330 } 331 332 public static class PropertyAccessorsPsiMethods implements Iterable<PsiMethod> { 333 private final PsiMethod getter; 334 private final PsiMethod setter; 335 private final Collection<PsiMethod> accessors = new ArrayList<PsiMethod>(2); 336 337 PropertyAccessorsPsiMethods(@Nullable PsiMethod getter, @Nullable PsiMethod setter) { 338 this.getter = getter; 339 if (getter != null) { 340 accessors.add(getter); 341 } 342 343 this.setter = setter; 344 if (setter != null) { 345 accessors.add(setter); 346 } 347 } 348 349 @Nullable 350 public PsiMethod getGetter() { 351 return getter; 352 } 353 354 @Nullable 355 public PsiMethod getSetter() { 356 return setter; 357 } 358 359 @NotNull 360 @Override 361 public Iterator<PsiMethod> iterator() { 362 return accessors.iterator(); 363 } 364 } 365 366 private LightClassUtil() { 367 } 368 }