Debugger: breakpoints in library source files
This commit is contained in:
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.jetbrains.jet.plugin.debugger;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.intellij.debugger.NoDataException;
|
||||
import com.intellij.debugger.PositionManager;
|
||||
import com.intellij.debugger.SourcePosition;
|
||||
@@ -23,6 +25,7 @@ import com.intellij.debugger.engine.DebugProcess;
|
||||
import com.intellij.debugger.requests.ClassPrepareRequestor;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.libraries.LibraryUtil;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
@@ -53,6 +56,7 @@ import org.jetbrains.jet.lang.resolve.name.FqName;
|
||||
import org.jetbrains.jet.lang.types.lang.InlineStrategy;
|
||||
import org.jetbrains.jet.lang.types.lang.InlineUtil;
|
||||
import org.jetbrains.jet.plugin.caches.resolve.ResolvePackage;
|
||||
import org.jetbrains.jet.plugin.project.ResolveSessionForBodies;
|
||||
import org.jetbrains.jet.plugin.util.DebuggerUtils;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
|
||||
@@ -137,9 +141,9 @@ public class JetPositionManager implements PositionManager {
|
||||
@SuppressWarnings("unchecked")
|
||||
public void run() {
|
||||
JetFile file = (JetFile) sourcePosition.getFile();
|
||||
JetTypeMapper typeMapper = prepareTypeMapper(file);
|
||||
|
||||
result.set(getClassNameForElement(sourcePosition.getElementAt(), typeMapper, file));
|
||||
boolean isInLibrary = LibraryUtil.findLibraryEntry(file.getVirtualFile(), file.getProject()) != null;
|
||||
JetTypeMapper typeMapper = !isInLibrary ? prepareTypeMapper(file) : createTypeMapperForLibraryFile(sourcePosition);
|
||||
result.set(getClassNameForElement(sourcePosition.getElementAt(), typeMapper, file, isInLibrary));
|
||||
}
|
||||
|
||||
});
|
||||
@@ -148,7 +152,12 @@ public class JetPositionManager implements PositionManager {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static String getClassNameForElement(@Nullable PsiElement notPositionedElement, @NotNull JetTypeMapper typeMapper, @NotNull JetFile file) {
|
||||
public static String getClassNameForElement(
|
||||
@Nullable PsiElement notPositionedElement,
|
||||
@NotNull JetTypeMapper typeMapper,
|
||||
@NotNull JetFile file,
|
||||
boolean isInLibrary
|
||||
) {
|
||||
PsiElement element = PsiTreeUtil.getParentOfType(notPositionedElement, JetClassOrObject.class, JetFunctionLiteral.class, JetNamedFunction.class);
|
||||
|
||||
if (element instanceof JetClassOrObject) {
|
||||
@@ -156,7 +165,7 @@ public class JetPositionManager implements PositionManager {
|
||||
}
|
||||
else if (element instanceof JetFunctionLiteral) {
|
||||
if (isInlinedLambda((JetFunctionLiteral) element, typeMapper.getBindingContext())) {
|
||||
return getClassNameForElement(element.getParent(), typeMapper, file);
|
||||
return getClassNameForElement(element.getParent(), typeMapper, file, isInLibrary);
|
||||
} else {
|
||||
Type asmType = asmTypeForAnonymousClass(typeMapper.getBindingContext(), ((JetFunctionLiteral) element));
|
||||
return asmType.getInternalName();
|
||||
@@ -173,10 +182,22 @@ public class JetPositionManager implements PositionManager {
|
||||
}
|
||||
}
|
||||
|
||||
if (isInLibrary) {
|
||||
JetElement elementAtForLibraryFile = getElementToCreateTypeMapperForLibraryFile(notPositionedElement);
|
||||
assert elementAtForLibraryFile != null : "Couldn't find element at breakpoint for library file " + file.getName()
|
||||
+ (notPositionedElement == null ? "" : ", notPositionedElement = " + JetPsiUtil.getElementTextWithContext( (JetElement) notPositionedElement));
|
||||
return DebuggerPackage.findPackagePartInternalNameForLibraryFile(elementAtForLibraryFile);
|
||||
}
|
||||
|
||||
return PackagePartClassUtils.getPackagePartInternalName(file);
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
private static JetElement getElementToCreateTypeMapperForLibraryFile(@Nullable PsiElement notPositionedElement) {
|
||||
return PsiTreeUtil.getParentOfType(notPositionedElement, JetElement.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getJvmInternalNameForImpl(JetTypeMapper typeMapper, JetClassOrObject jetClass) {
|
||||
ClassDescriptor classDescriptor = typeMapper.getBindingContext().get(BindingContext.CLASS, jetClass);
|
||||
@@ -191,6 +212,20 @@ public class JetPositionManager implements PositionManager {
|
||||
return typeMapper.mapClass(classDescriptor).getInternalName();
|
||||
}
|
||||
|
||||
private static JetTypeMapper createTypeMapperForLibraryFile(@NotNull SourcePosition position) {
|
||||
JetElement element = getElementToCreateTypeMapperForLibraryFile(position.getElementAt());
|
||||
ResolveSessionForBodies resolveSession = ResolvePackage.getLazyResolveSession(element);
|
||||
|
||||
JetFile file = (JetFile) position.getFile();
|
||||
GenerationState state = new GenerationState(file.getProject(), ClassBuilderFactories.THROW_EXCEPTION,
|
||||
resolveSession.getModuleDescriptor(),
|
||||
resolveSession.resolveToElement(element),
|
||||
Collections.singletonList(file)
|
||||
);
|
||||
state.beforeCompile();
|
||||
return state.getTypeMapper();
|
||||
}
|
||||
|
||||
private JetTypeMapper prepareTypeMapper(final JetFile file) {
|
||||
final FqName fqName = file.getPackageFqName();
|
||||
CachedValue<JetTypeMapper> value = myTypeMappers.get(fqName);
|
||||
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright 2010-2014 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.plugin.debugger
|
||||
|
||||
import com.intellij.psi.search.FilenameIndex
|
||||
import org.jetbrains.jet.lang.resolve.kotlin.PackagePartClassUtils
|
||||
import org.jetbrains.jet.lang.psi.JetElement
|
||||
import org.jetbrains.jet.descriptors.serialization.JavaProtoBuf
|
||||
import org.jetbrains.jet.renderer.DescriptorRenderer
|
||||
import org.jetbrains.jet.plugin.caches.resolve.getLazyResolveSession
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
|
||||
import com.intellij.openapi.roots.libraries.LibraryUtil
|
||||
import com.intellij.openapi.module.impl.scopes.LibraryScope
|
||||
import com.intellij.openapi.roots.LibraryOrderEntry
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.jet.lang.psi.JetDeclaration
|
||||
import org.jetbrains.jet.lang.descriptors.CallableDescriptor
|
||||
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.jet.descriptors.serialization.descriptors.DeserializedCallableMemberDescriptor
|
||||
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.jet.lang.psi.JetNamedDeclaration
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext
|
||||
import com.intellij.openapi.module.impl.scopes.JdkScope
|
||||
import com.intellij.openapi.roots.JdkOrderEntry
|
||||
import org.jetbrains.jet.lang.psi.JetPsiUtil
|
||||
import org.jetbrains.jet.lang.resolve.java.JvmClassName
|
||||
|
||||
private val LOG = Logger.getInstance("org.jetbrains.jet.plugin.debugger")
|
||||
|
||||
/**
|
||||
* This method finds class name for top-level declaration in source file attached to library.
|
||||
* The problem is that package-part class file has hash in className and it depends on machine where library was built,
|
||||
* so we couldn't predict it.
|
||||
* 1. find all .class files with package-part prefix, if there is one - return it
|
||||
* 2. find all descriptors with same signature, if there is one - return it
|
||||
* 3. else -> return null, because it means that there is more than one function with same signature in project
|
||||
*/
|
||||
fun findPackagePartInternalNameForLibraryFile(elementAt: JetElement): String? {
|
||||
val topLevelDeclaration = PsiTreeUtil.getTopmostParentOfType(elementAt, javaClass<JetDeclaration>())
|
||||
if (topLevelDeclaration == null) {
|
||||
reportError(elementAt, null)
|
||||
return null
|
||||
}
|
||||
|
||||
val packagePartFile = findPackagePartFileNamesForElement(topLevelDeclaration).singleOrNull()
|
||||
if (packagePartFile != null) return packagePartFile
|
||||
|
||||
val resolveSession = topLevelDeclaration.getLazyResolveSession()
|
||||
|
||||
val descriptor = resolveSession.resolveToDescriptor(topLevelDeclaration)
|
||||
if (descriptor !is CallableDescriptor) return null
|
||||
|
||||
val packageFqName = topLevelDeclaration.getContainingJetFile().getPackageFqName()
|
||||
val packageDescriptor = resolveSession.getModuleDescriptor().getPackage(packageFqName)
|
||||
if (packageDescriptor == null) {
|
||||
reportError(topLevelDeclaration, descriptor)
|
||||
return null
|
||||
}
|
||||
|
||||
val descFromSourceText = render(descriptor)
|
||||
|
||||
val descriptors: Collection<CallableDescriptor> = when (descriptor) {
|
||||
is FunctionDescriptor -> packageDescriptor.getMemberScope().getFunctions(descriptor.getName())
|
||||
is PropertyDescriptor -> packageDescriptor.getMemberScope().getProperties(descriptor.getName())
|
||||
else -> {
|
||||
reportError(topLevelDeclaration, descriptor)
|
||||
listOf()
|
||||
}
|
||||
}
|
||||
|
||||
val deserializedDescriptor = descriptors
|
||||
.filterIsInstance(javaClass<DeserializedCallableMemberDescriptor>())
|
||||
.filter { render(it) == descFromSourceText }
|
||||
.singleOrNull()
|
||||
|
||||
if (deserializedDescriptor == null) {
|
||||
reportError(topLevelDeclaration, descriptor)
|
||||
return null
|
||||
}
|
||||
|
||||
val proto = deserializedDescriptor.proto
|
||||
if (proto.hasExtension(JavaProtoBuf.implClassName)) {
|
||||
val name = deserializedDescriptor.nameResolver.getName(proto.getExtension(JavaProtoBuf.implClassName)!!)
|
||||
return JvmClassName.byFqNameWithoutInnerClasses(packageFqName.child(name)).getInternalName()
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun findPackagePartFileNamesForElement(elementAt: JetElement): List<String> {
|
||||
val project = elementAt.getProject()
|
||||
val file = elementAt.getContainingJetFile()
|
||||
|
||||
val packagePartName = PackagePartClassUtils.getPackagePartFqName(file).shortName().asString()
|
||||
val packagePartNameWoHash = packagePartName.substring(0, packagePartName.lastIndexOf("-"))
|
||||
|
||||
val libraryEntry = LibraryUtil.findLibraryEntry(file.getVirtualFile(), project)
|
||||
val scope = if (libraryEntry is LibraryOrderEntry){
|
||||
LibraryScope(project, libraryEntry.getLibrary() ?: throw AssertionError("Cannot find library for file ${file.getVirtualFile()?.getPath()}"))
|
||||
}
|
||||
else {
|
||||
JdkScope(project, libraryEntry as JdkOrderEntry)
|
||||
}
|
||||
|
||||
val packagePartFiles = FilenameIndex.getAllFilesByExt(project, "class", scope)
|
||||
.filter { it.getName().startsWith(packagePartNameWoHash) }
|
||||
.map {
|
||||
val packageFqName = file.getPackageFqName()
|
||||
if (packageFqName.isRoot()) {
|
||||
it.getNameWithoutExtension()
|
||||
} else {
|
||||
"${packageFqName.asString()}.${it.getNameWithoutExtension()}"
|
||||
}
|
||||
}
|
||||
return packagePartFiles
|
||||
}
|
||||
|
||||
private fun render(desc: DeclarationDescriptor) = DescriptorRenderer.FQ_NAMES_IN_TYPES.render(desc)
|
||||
|
||||
private fun reportError(element: JetElement, descriptor: CallableDescriptor?) {
|
||||
LOG.error("Couldn't calculate class name for element in library scope:\n" +
|
||||
JetPsiUtil.getElementTextWithContext(element) +
|
||||
if (descriptor != null) "\ndescriptor = ${render(descriptor)}" else ""
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user