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.KotlinPackage;
037 import kotlin.jvm.functions.Function1;
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 JetClassOrObject 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 }