001/* 002 * Copyright 2010-2013 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 017package org.jetbrains.jet.asJava; 018 019import com.intellij.openapi.diagnostic.Logger; 020import com.intellij.openapi.progress.ProcessCanceledException; 021import com.intellij.openapi.project.Project; 022import com.intellij.openapi.util.Comparing; 023import com.intellij.openapi.util.SystemInfo; 024import com.intellij.openapi.vfs.VirtualFile; 025import com.intellij.psi.PsiClass; 026import com.intellij.psi.PsiElement; 027import com.intellij.psi.PsiMethod; 028import com.intellij.psi.impl.java.stubs.PsiClassStub; 029import com.intellij.psi.search.GlobalSearchScope; 030import com.intellij.psi.stubs.PsiFileStub; 031import com.intellij.psi.stubs.StubElement; 032import com.intellij.psi.util.PsiTreeUtil; 033import com.intellij.util.SmartList; 034import org.jetbrains.annotations.NotNull; 035import org.jetbrains.annotations.Nullable; 036import org.jetbrains.jet.codegen.binding.PsiCodegenPredictor; 037import org.jetbrains.jet.lang.psi.*; 038import org.jetbrains.jet.lang.resolve.java.JetClsMethod; 039import org.jetbrains.jet.lang.resolve.java.JvmAbi; 040import org.jetbrains.jet.lang.resolve.java.JvmClassName; 041import org.jetbrains.jet.lang.resolve.name.FqName; 042import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; 043import org.jetbrains.jet.utils.KotlinVfsUtil; 044 045import java.net.MalformedURLException; 046import java.net.URL; 047import java.util.*; 048 049public class LightClassUtil { 050 private static final Logger LOG = Logger.getInstance(LightClassUtil.class); 051 private static final String DEFINITION_OF_ANY = "Any.jet"; 052 053 /** 054 * Checks whether the given file is loaded from the location where Kotlin's built-in classes are defined. 055 * As of today, this is compiler/frontend/src/jet directory and files such as Any.jet, Nothing.jet etc. 056 * 057 * Used to skip JetLightClass creation for built-ins, because built-in classes have no Java counterparts 058 */ 059 public static boolean belongsToKotlinBuiltIns(@NotNull JetFile file) { 060 try { 061 String jetVfsPathUrl = KotlinVfsUtil.convertFromUrl(getBuiltInsDirResourceUrl()); 062 VirtualFile virtualFile = file.getVirtualFile(); 063 if (virtualFile != null) { 064 VirtualFile parent = virtualFile.getParent(); 065 if (parent != null) { 066 String fileDirVfsUrl = parent.getUrl() + "/" + DEFINITION_OF_ANY; 067 if (jetVfsPathUrl.equals(fileDirVfsUrl)) { 068 return true; 069 } 070 } 071 } 072 } 073 catch (MalformedURLException e) { 074 LOG.error(e); 075 } 076 // We deliberately return false on error: who knows what weird URLs we might come across out there 077 // it would be a pity if no light classes would be created in such cases 078 return false; 079 } 080 081 @NotNull 082 public static URL getBuiltInsDirResourceUrl() { 083 String pathToAny = "/" + KotlinBuiltIns.BUILT_INS_DIR + "/" + DEFINITION_OF_ANY; 084 URL url = KotlinBuiltIns.class.getResource(pathToAny); 085 if (url == null) { 086 throw new IllegalStateException("Built-ins not found in the classpath: " + pathToAny); 087 } 088 return url; 089 } 090 091 /*package*/ static void logErrorWithOSInfo(@Nullable Throwable cause, @NotNull FqName fqName, @Nullable VirtualFile virtualFile) { 092 String path = virtualFile == null ? "<null>" : virtualFile.getPath(); 093 LOG.error( 094 "Could not generate LightClass for " + fqName + " declared in " + path + "\n" + 095 "built-ins dir URL is " + getBuiltInsDirResourceUrl() + "\n" + 096 "System: " + SystemInfo.OS_NAME + " " + SystemInfo.OS_VERSION + " Java Runtime: " + SystemInfo.JAVA_RUNTIME_VERSION, 097 cause); 098 } 099 100 @Nullable 101 /*package*/ static PsiClass findClass(@NotNull FqName fqn, @NotNull StubElement<?> stub) { 102 if (stub instanceof PsiClassStub && Comparing.equal(fqn.asString(), ((PsiClassStub) stub).getQualifiedName())) { 103 return (PsiClass)stub.getPsi(); 104 } 105 106 if (stub instanceof PsiClassStub || stub instanceof PsiFileStub) { 107 for (StubElement child : stub.getChildrenStubs()) { 108 PsiClass answer = findClass(fqn, child); 109 if (answer != null) return answer; 110 } 111 } 112 113 return null; 114 } 115 116 @Nullable 117 public static PsiClass getPsiClass(@Nullable JetClassOrObject classOrObject) { 118 if (classOrObject == null) return null; 119 return LightClassGenerationSupport.getInstance(classOrObject.getProject()).getPsiClass(classOrObject); 120 } 121 122 @Nullable 123 public static PsiMethod getLightClassAccessorMethod(@NotNull JetPropertyAccessor accessor) { 124 return getPsiMethodWrapper(accessor); 125 } 126 127 @NotNull 128 public static PropertyAccessorsPsiMethods getLightClassPropertyMethods(@NotNull JetProperty property) { 129 JetPropertyAccessor getter = property.getGetter(); 130 JetPropertyAccessor setter = property.getSetter(); 131 132 PsiMethod getterWrapper = getter != null ? getLightClassAccessorMethod(getter) : null; 133 PsiMethod setterWrapper = setter != null ? getLightClassAccessorMethod(setter) : null; 134 135 return extractPropertyAccessors(property, getterWrapper, setterWrapper); 136 } 137 138 public static PropertyAccessorsPsiMethods getLightClassPropertyMethods(@NotNull JetParameter parameter) { 139 return extractPropertyAccessors(parameter, null, null); 140 } 141 142 @Nullable 143 public static PsiMethod getLightClassMethod(@NotNull JetNamedFunction function) { 144 return getPsiMethodWrapper(function); 145 } 146 147 @Nullable 148 private static PsiMethod getPsiMethodWrapper(@NotNull JetDeclaration declaration) { 149 List<PsiMethod> wrappers = getPsiMethodWrappers(declaration, false); 150 return !wrappers.isEmpty() ? wrappers.get(0) : null; 151 } 152 153 @NotNull 154 private static List<PsiMethod> getPsiMethodWrappers(@NotNull JetDeclaration declaration, boolean collectAll) { 155 PsiClass psiClass = getWrappingClass(declaration); 156 if (psiClass == null) { 157 return Collections.emptyList(); 158 } 159 160 List<PsiMethod> methods = new SmartList<PsiMethod>(); 161 for (PsiMethod method : psiClass.getMethods()) { 162 try { 163 if (method instanceof JetClsMethod && ((JetClsMethod) method).getOrigin() == declaration) { 164 methods.add(method); 165 if (!collectAll) { 166 return methods; 167 } 168 } 169 } 170 catch (ProcessCanceledException e) { 171 throw e; 172 } 173 catch (Throwable e) { 174 throw new IllegalStateException( 175 "Error while wrapping declaration " + declaration.getName() + 176 "Context\n:" + 177 String.format("=== In file ===\n" + 178 "%s\n" + 179 "=== On element ===\n" + 180 "%s\n" + 181 "=== WrappedElement ===\n" + 182 "%s\n", 183 declaration.getContainingFile().getText(), 184 declaration.getText(), 185 method.toString()), 186 e 187 ); 188 } 189 } 190 191 return methods; 192 } 193 194 @Nullable 195 private static PsiClass getWrappingClass(@NotNull JetDeclaration declaration) { 196 if (declaration instanceof JetParameter) { 197 JetClass constructorClass = JetPsiUtil.getClassIfParameterIsProperty((JetParameter) declaration); 198 if (constructorClass != null) { 199 return getPsiClass(constructorClass); 200 } 201 } 202 203 if (declaration instanceof JetPropertyAccessor) { 204 PsiElement propertyParent = declaration.getParent(); 205 assert propertyParent instanceof JetProperty : "JetProperty is expected to be parent of accessor"; 206 207 declaration = (JetProperty) propertyParent; 208 } 209 210 //noinspection unchecked 211 if (PsiTreeUtil.getParentOfType(declaration, JetFunction.class, JetProperty.class) != null) { 212 // Can't get wrappers for internal declarations. Their classes are not generated during calcStub 213 // with ClassBuilderMode.SIGNATURES mode, and this produces "Class not found exception" in getDelegate() 214 return null; 215 } 216 217 PsiElement parent = declaration.getParent(); 218 219 if (parent instanceof JetFile) { 220 // top-level declaration 221 JvmClassName jvmName = PsiCodegenPredictor.getPredefinedJvmClassName((JetFile) parent, true); 222 if (jvmName != null) { 223 Project project = declaration.getProject(); 224 225 String fqName = jvmName.getFqName().asString(); 226 return JavaElementFinder.getInstance(project).findClass(fqName, GlobalSearchScope.allScope(project)); 227 } 228 } 229 else if (parent instanceof JetClassBody) { 230 assert parent.getParent() instanceof JetClassOrObject; 231 return getPsiClass((JetClassOrObject) parent.getParent()); 232 } 233 234 return null; 235 } 236 237 private static PropertyAccessorsPsiMethods extractPropertyAccessors( 238 @NotNull JetDeclaration jetDeclaration, 239 @Nullable PsiMethod specialGetter, @Nullable PsiMethod specialSetter) 240 { 241 PsiMethod getterWrapper = specialGetter; 242 PsiMethod setterWrapper = specialSetter; 243 244 if (getterWrapper == null || setterWrapper == null) { 245 // If some getter or setter isn't found yet try to get it from wrappers for general declaration 246 247 List<PsiMethod> wrappers = getPsiMethodWrappers(jetDeclaration, true); 248 assert wrappers.size() <= 2 : "Maximum two wrappers are expected to be generated for declaration: " + jetDeclaration.getText(); 249 250 for (PsiMethod wrapper : wrappers) { 251 if (wrapper.getName().startsWith(JvmAbi.SETTER_PREFIX)) { 252 assert setterWrapper == null : String.format( 253 "Setter accessor isn't expected to be reassigned (old: %s, new: %s)", setterWrapper, wrapper); 254 255 setterWrapper = wrapper; 256 } 257 else { 258 assert getterWrapper == null : String.format( 259 "Getter accessor isn't expected to be reassigned (old: %s, new: %s)", getterWrapper, wrapper); 260 261 getterWrapper = wrapper; 262 } 263 } 264 } 265 266 return new PropertyAccessorsPsiMethods(getterWrapper, setterWrapper); 267 } 268 269 public static class PropertyAccessorsPsiMethods implements Iterable<PsiMethod> { 270 private final PsiMethod getter; 271 private final PsiMethod setter; 272 private final Collection<PsiMethod> accessors = new ArrayList<PsiMethod>(2); 273 274 PropertyAccessorsPsiMethods(@Nullable PsiMethod getter, @Nullable PsiMethod setter) { 275 this.getter = getter; 276 if (getter != null) { 277 accessors.add(getter); 278 } 279 280 this.setter = setter; 281 if (setter != null) { 282 accessors.add(setter); 283 } 284 } 285 286 @Nullable 287 public PsiMethod getGetter() { 288 return getter; 289 } 290 291 @Nullable 292 public PsiMethod getSetter() { 293 return setter; 294 } 295 296 @NotNull 297 @Override 298 public Iterator<PsiMethod> iterator() { 299 return accessors.iterator(); 300 } 301 } 302 303 private LightClassUtil() {} 304}