EA-36473 Wrap method for JetLightClass with invalid fq name

This commit is contained in:
Nikolay Krasko
2012-09-11 21:25:38 +04:00
parent 9a645dca3e
commit b6e2381861
13 changed files with 309 additions and 33 deletions
@@ -0,0 +1,176 @@
/*
* 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.codegen;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingTrace;
import org.jetbrains.jet.lang.resolve.java.JvmAbi;
import org.jetbrains.jet.lang.resolve.java.JvmClassName;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.resolve.name.Name;
import static org.jetbrains.jet.lang.resolve.BindingContextUtils.descriptorToDeclaration;
public final class PsiCodegenPredictor {
private PsiCodegenPredictor() {
}
public static boolean checkPredictedNameFromPsi(
@NotNull BindingTrace bindingTrace, @NotNull DeclarationDescriptor descriptor, JvmClassName nameFromDescriptors
) {
PsiElement element = descriptorToDeclaration(bindingTrace.getBindingContext(), descriptor);
if (element instanceof JetDeclaration) {
JvmClassName classNameFromPsi = getPredefinedJvmClassName((JetDeclaration) element);
assert classNameFromPsi == null || classNameFromPsi.equals(nameFromDescriptors) : "Invalid algorithm for getting qualified name from psi!";
}
return true;
}
@Nullable
public static JvmClassName getPredefinedJvmClassName(@NotNull JetFile jetFile, boolean withNamespace) {
String packageName = jetFile.getPackageName();
if (packageName == null) {
return null;
}
JvmClassName packageJvmName = JvmClassName.byFqNameWithoutInnerClasses(packageName);
return !withNamespace ? packageJvmName : addPackageClass(packageJvmName);
}
/**
* TODO: Finish this method for all cases. Now it's only used and tested in JetLightClass.
*
* @return null if no prediction can be done.
*/
@Nullable
public static JvmClassName getPredefinedJvmClassName(@NotNull JetDeclaration declaration) {
// TODO: Method won't work for declarations inside class objects
// TODO: Method won't give correct class name for traits implementations
JetDeclaration parentDeclaration = PsiTreeUtil.getParentOfType(declaration, JetDeclaration.class);
JvmClassName parentClassName = parentDeclaration != null ?
getPredefinedJvmClassName(parentDeclaration) :
getPredefinedJvmClassName((JetFile) declaration.getContainingFile(), false);
if (parentClassName == null) {
return null;
}
if (declaration instanceof JetClassObject) {
// Get parent and assign Class object prefix
return JvmClassName.byInternalName(parentClassName.getInternalName() + JvmAbi.CLASS_OBJECT_SUFFIX);
}
if (declaration instanceof JetNamedDeclaration) {
if (!PsiTreeUtil.instanceOf(declaration, JetClass.class, JetObjectDeclaration.class, JetNamedFunction.class, JetProperty.class) ||
declaration instanceof JetEnumEntry) {
// Other subclasses are not valid for class name prediction.
// For example EnumEntry, JetFunctionLiteral
return null;
}
JetNamedDeclaration namedDeclaration = (JetNamedDeclaration) declaration;
Name name = namedDeclaration.getNameAsName();
if (name == null) {
return null;
}
FqName fqName = parentClassName.getFqName();
if (declaration instanceof JetNamedFunction) {
if (parentDeclaration == null) {
JvmClassName packageClass = addPackageClass(parentClassName);
return JvmClassName.byInternalName(packageClass.getInternalName() + "$" + name.getName());
}
if (!(parentDeclaration instanceof JetClass || parentDeclaration instanceof JetObjectDeclaration)) {
// Can't generate predefined name for internal functions
return null;
}
}
// NOTE: looks like a bug - for class in getter of top level property class name will be $propertyName$ClassName but not
// namespace$propertyName$ClassName
if (declaration instanceof JetProperty) {
return JvmClassName.byInternalName(parentClassName.getInternalName() + "$" + name.getName());
}
if (fqName.isRoot()) {
return JvmClassName.byInternalName(name.getName());
}
return JvmClassName.byInternalName(parentDeclaration == null ?
parentClassName.getInternalName() + "/" + name.getName() :
parentClassName.getInternalName() + "$" + name.getName());
}
return null;
}
private static JvmClassName addPackageClass(JvmClassName packageName) {
FqName name = packageName.getFqName();
return name.isRoot() ?
JvmClassName.byFqNameWithoutInnerClasses(JvmAbi.PACKAGE_CLASS) :
JvmClassName.byInternalName(packageName.getInternalName() + "/" + JvmAbi.PACKAGE_CLASS);
}
public static boolean checkPredictedClassNameForFun(
@NotNull BindingTrace bindingTrace, @NotNull DeclarationDescriptor descriptor, ClassDescriptor classDescriptor
) {
PsiElement element = descriptorToDeclaration(bindingTrace.getBindingContext(), descriptor);
PsiElement classDeclaration = descriptorToDeclaration(bindingTrace.getBindingContext(), classDescriptor);
if (element instanceof JetNamedFunction && classDeclaration instanceof JetDeclaration) {
JvmClassName classNameFromPsi = getPredefinedJvmClassName((JetDeclaration) classDeclaration);
JvmClassName classNameForFun = getPredefinedJvmClassNameForFun((JetNamedFunction) element);
assert classNameForFun == null || classNameForFun.equals(classNameFromPsi) : "Invalid algorithm for getting enclosing method name!";
}
return true;
}
@Nullable
public static JvmClassName getPredefinedJvmClassNameForFun(@NotNull JetNamedFunction function) {
PsiElement parent = function.getParent();
if (parent instanceof JetFile) {
return getPredefinedJvmClassName((JetFile) parent, true);
}
@SuppressWarnings("unchecked")
JetClass containingClass = PsiTreeUtil.getParentOfType(function, JetClass.class, true, JetDeclaration.class);
if (containingClass != null) {
return getPredefinedJvmClassName(containingClass);
}
@SuppressWarnings("unchecked")
JetObjectDeclaration objectDeclaration = PsiTreeUtil.getParentOfType(function, JetObjectDeclaration.class, true, JetDeclaration.class);
if (objectDeclaration != null) {
if (objectDeclaration.getParent() instanceof JetClassObject) {
return getPredefinedJvmClassName((JetClassObject) objectDeclaration.getParent());
}
return getPredefinedJvmClassName(objectDeclaration);
}
return null;
}
}
@@ -17,6 +17,7 @@
package org.jetbrains.jet.codegen.binding;
import com.intellij.util.containers.Stack;
import org.jetbrains.jet.codegen.PsiCodegenPredictor;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.jet.lang.psi.*;
@@ -69,6 +70,7 @@ class CodegenAnnotatingVisitor extends JetVisitorVoid {
: JetStandardClasses.getFunction(arity)).getDefaultType()), JetScope.EMPTY,
Collections.<ConstructorDescriptor>emptySet(), null);
assert PsiCodegenPredictor.checkPredictedClassNameForFun(bindingTrace, funDescriptor, classDescriptor);
bindingTrace.record(CLASS_FOR_FUNCTION, funDescriptor, classDescriptor);
return classDescriptor;
}
@@ -134,7 +136,9 @@ class CodegenAnnotatingVisitor extends JetVisitorVoid {
super.visitEnumEntry(enumEntry);
}
else {
bindingTrace.record(FQN, descriptor, bindingTrace.get(FQN, peekFromStack(classStack)));
JvmClassName jvmClassName = bindingTrace.get(FQN, peekFromStack(classStack));
assert PsiCodegenPredictor.checkPredictedNameFromPsi(bindingTrace, descriptor, jvmClassName);
bindingTrace.record(FQN, descriptor, jvmClassName);
}
}
@@ -20,6 +20,7 @@ import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.codegen.CodegenUtil;
import org.jetbrains.jet.codegen.PsiCodegenPredictor;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.jet.lang.psi.*;
@@ -132,6 +133,7 @@ public class CodegenBinding {
recordClosure(bindingTrace, null, classDescriptor, null, className, false);
assert PsiCodegenPredictor.checkPredictedClassNameForFun(bindingTrace, scriptDescriptor, classDescriptor);
bindingTrace.record(CLASS_FOR_FUNCTION, scriptDescriptor, classDescriptor);
}
@@ -170,6 +172,7 @@ public class CodegenBinding {
final MutableClosure closure = new MutableClosure(superCall, enclosing, enclosingReceiver);
assert PsiCodegenPredictor.checkPredictedNameFromPsi(bindingTrace, classDescriptor, name);
bindingTrace.record(FQN, classDescriptor, name);
bindingTrace.record(CLOSURE, classDescriptor, closure);
@@ -260,6 +263,8 @@ public class CodegenBinding {
}
name = JvmClassName.byInternalName(getJvmInternalFQNameImpl(bindingTrace, descriptor));
assert PsiCodegenPredictor.checkPredictedNameFromPsi(bindingTrace, descriptor, name);
bindingTrace.record(FQN, descriptor, name);
return name;
}
@@ -37,20 +37,15 @@ import com.intellij.util.containers.Stack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.analyzer.AnalyzeExhaust;
import org.jetbrains.jet.codegen.ClassBuilder;
import org.jetbrains.jet.codegen.ClassBuilderFactory;
import org.jetbrains.jet.codegen.ClassBuilderMode;
import org.jetbrains.jet.codegen.CompilationErrorHandler;
import org.jetbrains.jet.codegen.*;
import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.codegen.state.GenerationStrategy;
import org.jetbrains.jet.lang.BuiltinsScopeExtensionMode;
import org.jetbrains.jet.lang.psi.JetClass;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.psi.JetFunction;
import org.jetbrains.jet.lang.psi.JetPsiUtil;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.java.AnalyzerFacadeForJVM;
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.JetStandardClasses;
import org.jetbrains.jet.plugin.JetLanguage;
@@ -246,15 +241,33 @@ public class JetLightClass extends AbstractLightClass implements JetJavaMirrorMa
}
@Nullable
public static JetLightClass wrapDelegate(JetClass jetClass) {
if (jetClass == null) return null;
return create(jetClass.getManager(), (JetFile) jetClass.getContainingFile(), JetPsiUtil.getFQName(jetClass));
public static JetLightClass wrapDelegate(@Nullable JetClass jetClass) {
if (jetClass == null) {
return null;
}
return wrapDelegate(jetClass, PsiCodegenPredictor.getPredefinedJvmClassName(jetClass));
}
@Nullable
public static PsiMethod wrapMethod(JetFunction function) {
JetClass containingClass = PsiTreeUtil.getParentOfType(function, JetClass.class);
JetLightClass wrapper = wrapDelegate(containingClass);
private static JetLightClass wrapDelegate(@NotNull JetDeclaration declaration, @Nullable JvmClassName jvmClassName) {
if (jvmClassName == null) {
return null;
}
return create(declaration.getManager(), (JetFile) declaration.getContainingFile(), jvmClassName.getFqName());
}
@Nullable
public static PsiMethod wrapMethod(@NotNull JetNamedFunction function) {
//noinspection unchecked
if (PsiTreeUtil.getParentOfType(function, JetFunction.class, JetProperty.class) != null) {
// Don't generate method wrappers for internal declarations. Their classes are not generated during calcStub
// with ClassBuilderMode.SIGNATURES mode, and this produces "Class not found exception" in getDelegate()
return null;
}
JetLightClass wrapper = wrapDelegate(function, PsiCodegenPredictor.getPredefinedJvmClassNameForFun(function));
if (wrapper == null) {
return null;
}
@@ -264,6 +277,7 @@ public class JetLightClass extends AbstractLightClass implements JetJavaMirrorMa
return method;
}
}
return null;
}