Properly detect Kotlin built-ins:

Built-in classes like jet.Int and jet.Iterable are loaded from source
files by KotlinBuiltIns class. It makes no sense to create JetLightClass
instances for such classes, moreover, it causes runtime failures.
Now we check the location a file was loaded from before creating a light
class, and skip if it is a built-in.

We also assert this in teh actual generation of light class bodies, and
print out OS information if something fails, because our detection strategy
may depend on OS-specific behavior.
This commit is contained in:
Andrey Breslav
2012-10-18 17:22:57 +04:00
parent a7b6b34e29
commit 7719df1002
4 changed files with 183 additions and 32 deletions
@@ -21,9 +21,12 @@ package org.jetbrains.jet.asJava;
import com.intellij.navigation.ItemPresentation;
import com.intellij.navigation.ItemPresentationProviders;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiClassImplUtil;
import com.intellij.psi.impl.java.stubs.PsiClassStub;
@@ -50,13 +53,14 @@ import org.jetbrains.jet.lang.resolve.java.JetFilesProvider;
import org.jetbrains.jet.lang.resolve.java.JetJavaMirrorMarker;
import org.jetbrains.jet.lang.resolve.java.JvmClassName;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.jet.plugin.JetLanguage;
import javax.swing.*;
import java.util.Collections;
public class JetLightClass extends AbstractLightClass implements JetJavaMirrorMarker {
private static final Logger LOG = Logger.getInstance(JetLightClass.class);
static class JetBadWrapperException extends RuntimeException {
private final String context;
@@ -92,8 +96,11 @@ public class JetLightClass extends AbstractLightClass implements JetJavaMirrorMa
}
@Nullable
public static JetLightClass create(PsiManager manager, JetFile file, FqName qualifiedName) {
return KotlinBuiltIns.getInstance().isStandardClass(qualifiedName) ? null : new JetLightClass(manager, file, qualifiedName);
public static JetLightClass create(@NotNull PsiManager manager, @NotNull JetFile file, @NotNull FqName qualifiedName) {
if (LightClassUtil.belongsToKotlinBuiltIns(file)) {
return null;
}
return new JetLightClass(manager, file, qualifiedName);
}
@Override
@@ -167,6 +174,12 @@ public class JetLightClass extends AbstractLightClass implements JetJavaMirrorMa
}
private PsiJavaFileStub calcStub() {
if (LightClassUtil.belongsToKotlinBuiltIns(file)) {
// We may not fail later due to some luck, but generating JetLightClasses for built-ins is a bad idea anyways
// If it fails later, there will be an exception logged
logErrorWithOSInfo(null);
}
final PsiJavaFileStubImpl answer = new PsiJavaFileStubImpl(JetPsiUtil.getFQName(file).getFqName(), true);
final Project project = getProject();
@@ -205,15 +218,32 @@ public class JetLightClass extends AbstractLightClass implements JetJavaMirrorMa
throw new IllegalStateException("failed to analyze: " + context.getError(), context.getError());
}
final GenerationState state = new GenerationState(project, builderFactory, context, Collections.singletonList(file));
final GenerationStrategy strategy = new LightClassGenerationStrategy(this, stubStack, answer);
try {
GenerationState state = new GenerationState(project, builderFactory, context, Collections.singletonList(file));
GenerationStrategy strategy = new LightClassGenerationStrategy(this, stubStack, answer);
strategy.compileCorrectFiles(state, CompilationErrorHandler.THROW_EXCEPTION);
state.getFactory().files();
strategy.compileCorrectFiles(state, CompilationErrorHandler.THROW_EXCEPTION);
state.getFactory().files();
}
catch (ProcessCanceledException e) {
throw e;
}
catch (RuntimeException e) {
logErrorWithOSInfo(e);
throw e;
}
return answer;
}
private void logErrorWithOSInfo(@Nullable Throwable cause) {
LOG.error(
"Could not generate JetLightClass for " + qualifiedName + " declared in " + file.getVirtualFile().getPath() + "\n" +
"built-ins dir URL is " + LightClassUtil.getBuiltInsDirResourceUrl() + "\n" +
"System: " + SystemInfo.OS_NAME + " " + SystemInfo.OS_VERSION + " Java Runtime: " + SystemInfo.JAVA_RUNTIME_VERSION,
cause);
}
@Override
public boolean isEquivalentTo(PsiElement another) {
return another instanceof PsiClass && Comparing.equal(((PsiClass)another).getQualifiedName(), getQualifiedName());
@@ -0,0 +1,72 @@
/*
* Copyright 2010-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.asJava;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.jet.utils.KotlinVfsUtil;
import java.net.MalformedURLException;
import java.net.URL;
public class LightClassUtil {
private static final Logger LOG = Logger.getInstance(JetLightClass.class);
private static final String DEFINITION_OF_ANY = "Any.jet";
/**
* Checks whether the given file is loaded from the location where Kotlin's built-in classes are defined.
* As of today, this is compiler/frontend/src/jet directory and files such as Any.jet, Nothing.jet etc.
*
* Used to skip JetLightClass creation for built-ins, because built-in classes have no Java counterparts
*/
public static boolean belongsToKotlinBuiltIns(@NotNull JetFile file) {
try {
String jetVfsPathUrl = KotlinVfsUtil.convertFromUrl(getBuiltInsDirResourceUrl());
VirtualFile virtualFile = file.getVirtualFile();
if (virtualFile != null) {
VirtualFile parent = virtualFile.getParent();
if (parent != null) {
String fileDirVfsUrl = parent.getUrl() + "/" + DEFINITION_OF_ANY;
if (jetVfsPathUrl.equals(fileDirVfsUrl)) {
return true;
}
}
}
}
catch (MalformedURLException e) {
LOG.error(e);
}
// We deliberately return false on error: who knows what weird URLs we might come across out there
// it would be a pity if no light classes would be created in such cases
return false;
}
@NotNull
public static URL getBuiltInsDirResourceUrl() {
String pathToAny = "/" + KotlinBuiltIns.BUILT_INS_DIR + "/" + DEFINITION_OF_ANY;
URL url = KotlinBuiltIns.class.getResource(pathToAny);
if (url == null) {
throw new IllegalStateException("Built-ins not found in the classpath: " + pathToAny);
}
return url;
}
private LightClassUtil() {}
}