From cec1ce7c9a38620b43d7af8783a36a0a0f13ea6c Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 13 Jul 2011 17:37:11 +0200 Subject: [PATCH] generate line number info (initial); position manager (supports stopping at breakpoints, doesn't support stepping yet) --- idea/src/META-INF/plugin.xml | 1 + .../jetbrains/jet/codegen/ClassCodegen.java | 2 +- .../jet/codegen/ClassFileFactory.java | 3 +- .../jetbrains/jet/codegen/ClosureCodegen.java | 1 + .../jet/codegen/ExpressionCodegen.java | 12 ++ .../jet/codegen/GenerationState.java | 6 +- .../codegen/ImplementationBodyCodegen.java | 1 + .../jet/codegen/InterfaceBodyCodegen.java | 1 + .../jetbrains/jet/codegen/JetTypeMapper.java | 13 +- .../jet/codegen/NamespaceCodegen.java | 7 +- .../org/jetbrains/jet/plugin/JetFileType.java | 5 + .../plugin/debugger/JetPositionManager.java | 152 ++++++++++++++++++ .../debugger/JetPositionManagerFactory.java | 15 ++ 13 files changed, 211 insertions(+), 8 deletions(-) create mode 100644 idea/src/org/jetbrains/jet/plugin/debugger/JetPositionManager.java create mode 100644 idea/src/org/jetbrains/jet/plugin/debugger/JetPositionManagerFactory.java diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 857f5bb1d13..3e7739c9d4e 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -45,5 +45,6 @@ + diff --git a/idea/src/org/jetbrains/jet/codegen/ClassCodegen.java b/idea/src/org/jetbrains/jet/codegen/ClassCodegen.java index 07dee13f7da..4c3ca0a0eea 100644 --- a/idea/src/org/jetbrains/jet/codegen/ClassCodegen.java +++ b/idea/src/org/jetbrains/jet/codegen/ClassCodegen.java @@ -17,7 +17,7 @@ public class ClassCodegen { } public void generate(ClassContext parentContext, JetClassOrObject aClass) { - state.prepareAnonymousClasses((JetElement) aClass); + GenerationState.prepareAnonymousClasses((JetElement) aClass, state.getTypeMapper()); if (aClass instanceof JetObjectDeclaration) { generateImplementation(parentContext, aClass, OwnerKind.IMPLEMENTATION); diff --git a/idea/src/org/jetbrains/jet/codegen/ClassFileFactory.java b/idea/src/org/jetbrains/jet/codegen/ClassFileFactory.java index d485b7f7ce2..bc00c386c08 100644 --- a/idea/src/org/jetbrains/jet/codegen/ClassFileFactory.java +++ b/idea/src/org/jetbrains/jet/codegen/ClassFileFactory.java @@ -49,7 +49,8 @@ public class ClassFileFactory { String fqName = namespace.getFQName(); NamespaceCodegen codegen = ns2codegen.get(fqName); if (codegen == null) { - codegen = new NamespaceCodegen(newVisitor(NamespaceCodegen.getJVMClassName(fqName) + ".class"), fqName, state); + final ClassVisitor classVisitor = newVisitor(NamespaceCodegen.getJVMClassName(fqName) + ".class"); + codegen = new NamespaceCodegen(classVisitor, fqName, state, namespace.getContainingFile()); ns2codegen.put(fqName, codegen); } diff --git a/idea/src/org/jetbrains/jet/codegen/ClosureCodegen.java b/idea/src/org/jetbrains/jet/codegen/ClosureCodegen.java index 9af17666bcc..f93ddd3b44f 100644 --- a/idea/src/org/jetbrains/jet/codegen/ClosureCodegen.java +++ b/idea/src/org/jetbrains/jet/codegen/ClosureCodegen.java @@ -107,6 +107,7 @@ public class ClosureCodegen { funClass, new String[0] ); + cv.visitSource(fun.getContainingFile().getName(), null); generateBridge(name, funDescriptor, cv); diff --git a/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index 2f7d11653f7..2469b476c94 100644 --- a/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -1,5 +1,6 @@ package org.jetbrains.jet.codegen; +import com.intellij.openapi.editor.Document; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.search.ProjectScope; @@ -581,6 +582,7 @@ public class ExpressionCodegen extends JetVisitor { for (int i = 0, statementsSize = statements.size(); i < statementsSize; i++) { JetElement statement = statements.get(i); + markLineNumber(statement); if (i == statements.size() - 1) { gen(statement); } @@ -604,6 +606,16 @@ public class ExpressionCodegen extends JetVisitor { } } + private void markLineNumber(JetElement statement) { + final Document document = statement.getContainingFile().getViewProvider().getDocument(); + if (document != null) { + int lineNumber = document.getLineNumber(statement.getTextRange().getStartOffset()); + Label label = new Label(); + v.visitLabel(label); + v.visitLineNumber(lineNumber, label); + } + } + @Override public void visitReturnExpression(JetReturnExpression expression) { final JetExpression returnedExpression = expression.getReturnedExpression(); diff --git a/idea/src/org/jetbrains/jet/codegen/GenerationState.java b/idea/src/org/jetbrains/jet/codegen/GenerationState.java index a935133f248..631776165d0 100644 --- a/idea/src/org/jetbrains/jet/codegen/GenerationState.java +++ b/idea/src/org/jetbrains/jet/codegen/GenerationState.java @@ -77,7 +77,7 @@ public class GenerationState { } public void compile(JetFile psiFile) { - final JetNamespace namespace = ((JetFile) psiFile).getRootNamespace(); + final JetNamespace namespace = psiFile.getRootNamespace(); NamespaceCodegen codegen = forNamespace(namespace); final BindingContext bindingContext = AnalyzingUtils.analyzeNamespace(namespace, JetControlFlowDataTraceFactory.EMPTY); bindingContexts.push(bindingContext); @@ -101,7 +101,7 @@ public class GenerationState { return new GeneratedAnonymousClassDescriptor(nameAndVisitor.first, new Method("", "()V"), false); } - void prepareAnonymousClasses(JetElement aClass) { + public static void prepareAnonymousClasses(JetElement aClass, final JetTypeMapper typeMapper) { aClass.acceptChildren(new JetVisitor() { @Override public void visitJetElement(JetElement element) { @@ -111,7 +111,7 @@ public class GenerationState { @Override public void visitObjectLiteralExpression(JetObjectLiteralExpression expression) { - getTypeMapper().classNameForAnonymousClass(expression.getObjectDeclaration()); + typeMapper.classNameForAnonymousClass(expression.getObjectDeclaration()); } }); } diff --git a/idea/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java b/idea/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java index 6cfa5fdf0d8..aefc174ed42 100644 --- a/idea/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java +++ b/idea/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java @@ -47,6 +47,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { superClass, interfaces.toArray(new String[interfaces.size()]) ); + v.visitSource(myClass.getContainingFile().getName(), null); } private String jvmName() { diff --git a/idea/src/org/jetbrains/jet/codegen/InterfaceBodyCodegen.java b/idea/src/org/jetbrains/jet/codegen/InterfaceBodyCodegen.java index 52f31470129..a40f28684b6 100644 --- a/idea/src/org/jetbrains/jet/codegen/InterfaceBodyCodegen.java +++ b/idea/src/org/jetbrains/jet/codegen/InterfaceBodyCodegen.java @@ -39,6 +39,7 @@ public class InterfaceBodyCodegen extends ClassBodyCodegen { "java/lang/Object", superInterfaces.toArray(new String[superInterfaces.size()]) ); + v.visitSource(myClass.getContainingFile().getName(), null); } static Set getSuperInterfaces(JetClassOrObject aClass, final BindingContext bindingContext) { diff --git a/idea/src/org/jetbrains/jet/codegen/JetTypeMapper.java b/idea/src/org/jetbrains/jet/codegen/JetTypeMapper.java index 9cdf479a8c4..7cf9e82ff23 100644 --- a/idea/src/org/jetbrains/jet/codegen/JetTypeMapper.java +++ b/idea/src/org/jetbrains/jet/codegen/JetTypeMapper.java @@ -211,7 +211,7 @@ public class JetTypeMapper { return Type.getType("L" + jvmNameForDelegatingImplementation(classDescriptor) + ";"); } - static String jvmName(JetNamespace namespace) { + public static String jvmName(JetNamespace namespace) { return NamespaceCodegen.getJVMClassName(namespace.getFQName()); } @@ -572,4 +572,15 @@ public class JetTypeMapper { classNamesForAnonymousClasses.put(expression, className); return className; } + + public Collection allJvmNames(JetClassOrObject jetClass) { + Set result = new HashSet(); + final ClassDescriptor classDescriptor = bindingContext.getClassDescriptor(jetClass); + if (classDescriptor != null) { + result.add(jvmName(classDescriptor, OwnerKind.INTERFACE)); + result.add(jvmName(classDescriptor, OwnerKind.IMPLEMENTATION)); + result.add(jvmName(classDescriptor, OwnerKind.DELEGATING_IMPLEMENTATION)); + } + return result; + } } diff --git a/idea/src/org/jetbrains/jet/codegen/NamespaceCodegen.java b/idea/src/org/jetbrains/jet/codegen/NamespaceCodegen.java index 01cf982bc57..32d81e65a4c 100644 --- a/idea/src/org/jetbrains/jet/codegen/NamespaceCodegen.java +++ b/idea/src/org/jetbrains/jet/codegen/NamespaceCodegen.java @@ -1,5 +1,6 @@ package org.jetbrains.jet.codegen; +import com.intellij.psi.PsiFile; import org.jetbrains.jet.lang.descriptors.PropertyDescriptor; import org.jetbrains.jet.lang.psi.*; import org.objectweb.asm.ClassVisitor; @@ -15,7 +16,7 @@ public class NamespaceCodegen { private final ClassVisitor v; private final GenerationState state; - public NamespaceCodegen(ClassVisitor v, String fqName, GenerationState state) { + public NamespaceCodegen(ClassVisitor v, String fqName, GenerationState state, PsiFile sourceFile) { this.v = v; this.state = state; @@ -27,6 +28,8 @@ public class NamespaceCodegen { "java/lang/Object", new String[0] ); + // TODO figure something out for a namespace that spans multiple files + v.visitSource(sourceFile.getName(), null); } public void generate(JetNamespace namespace) { @@ -36,7 +39,7 @@ public class NamespaceCodegen { final PropertyCodegen propertyCodegen = new PropertyCodegen(context, v, functionCodegen, state); final ClassCodegen classCodegen = state.forClass(); - state.prepareAnonymousClasses(namespace); + GenerationState.prepareAnonymousClasses(namespace, state.getTypeMapper()); if (hasNonConstantPropertyInitializers(namespace)) { generateStaticInitializers(namespace); diff --git a/idea/src/org/jetbrains/jet/plugin/JetFileType.java b/idea/src/org/jetbrains/jet/plugin/JetFileType.java index 465804366ba..531a8e21314 100644 --- a/idea/src/org/jetbrains/jet/plugin/JetFileType.java +++ b/idea/src/org/jetbrains/jet/plugin/JetFileType.java @@ -34,4 +34,9 @@ public class JetFileType extends LanguageFileType { public Icon getIcon() { return Icons.PROJECT_ICON; } + + @Override + public boolean isJVMDebuggingSupported() { + return true; + } } diff --git a/idea/src/org/jetbrains/jet/plugin/debugger/JetPositionManager.java b/idea/src/org/jetbrains/jet/plugin/debugger/JetPositionManager.java new file mode 100644 index 00000000000..3057038b8f6 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/debugger/JetPositionManager.java @@ -0,0 +1,152 @@ +package org.jetbrains.jet.plugin.debugger; + +import com.intellij.debugger.NoDataException; +import com.intellij.debugger.PositionManager; +import com.intellij.debugger.SourcePosition; +import com.intellij.debugger.engine.DebugProcess; +import com.intellij.debugger.requests.ClassPrepareRequestor; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiFile; +import com.intellij.psi.util.PsiTreeUtil; +import com.sun.jdi.AbsentInformationException; +import com.sun.jdi.Location; +import com.sun.jdi.ReferenceType; +import com.sun.jdi.request.ClassPrepareRequest; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.codegen.GenerationState; +import org.jetbrains.jet.codegen.JetTypeMapper; +import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.lang.resolve.AnalyzingUtils; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.types.JetStandardLibrary; + +import java.util.*; + +/** + * @author yole + */ +public class JetPositionManager implements PositionManager { + private final DebugProcess myDebugProcess; + private WeakHashMap myTypeMappers = new WeakHashMap(); + + public JetPositionManager(DebugProcess debugProcess) { + myDebugProcess = debugProcess; + } + + @Override + @Nullable + public SourcePosition getSourcePosition(@Nullable Location location) throws NoDataException { + PsiFile psiFile = getPsiFileByLocation(location); + throw new NoDataException(); + } + + private PsiFile getPsiFileByLocation(Location location) { + final ReferenceType referenceType = location.declaringType(); + if (referenceType == null) { + return null; + } + + // TODO + return null; + } + + @NotNull + @Override + public List getAllClasses(SourcePosition sourcePosition) throws NoDataException { + if (!(sourcePosition.getFile() instanceof JetFile)) { + throw new NoDataException(); + } + final Collection names = classNamesForPosition(sourcePosition); + List result = new ArrayList(); + for (String name : names) { + result.addAll(myDebugProcess.getVirtualMachineProxy().classesByName(name)); + } + return result; + } + + private Collection classNamesForPosition(SourcePosition sourcePosition) { + final JetFile file = (JetFile) sourcePosition.getFile(); + JetTypeMapper typeMapper = prepareTypeMapper(file); + final Collection names = new ArrayList(); + JetClassOrObject jetClass = PsiTreeUtil.getParentOfType(sourcePosition.getElementAt(), JetClassOrObject.class); + if (jetClass != null) { + names.addAll(typeMapper.allJvmNames(jetClass)); + } + else { + JetNamespace namespace = PsiTreeUtil.getParentOfType(sourcePosition.getElementAt(), JetNamespace.class); + if (namespace != null) { + names.add(JetTypeMapper.jvmName(namespace)); + } + else { + names.add(JetTypeMapper.jvmName(file.getRootNamespace())); + } + } + return names; + } + + private JetTypeMapper prepareTypeMapper(JetFile file) { + final JetTypeMapper mapper = myTypeMappers.get(file); + if (mapper != null) { + return mapper; + } + final BindingContext bindingContext = AnalyzingUtils.analyzeFileWithCache(file); + final JetStandardLibrary standardLibrary = JetStandardLibrary.getJetStandardLibrary(myDebugProcess.getProject()); + final JetTypeMapper typeMapper = new JetTypeMapper(standardLibrary, bindingContext); + file.acceptChildren(new JetVisitor() { + @Override + public void visitJetElement(JetElement element) { + element.acceptChildren(this); + } + + @Override + public void visitClass(JetClass klass) { + GenerationState.prepareAnonymousClasses(klass, typeMapper); + } + }); + myTypeMappers.put(file, typeMapper); + return typeMapper; + } + + @NotNull + @Override + public List locationsOfLine(ReferenceType type, SourcePosition position) throws NoDataException { + if (!(position.getFile() instanceof JetFile)) { + throw new NoDataException(); + } + try { + int line = position.getLine() + 1; + List locations = myDebugProcess.getVirtualMachineProxy().versionHigher("1.4") + ? type.locationsOfLine(DebugProcess.JAVA_STRATUM, null, line) + : type.locationsOfLine(line); + if (locations == null || locations.isEmpty()) throw new NoDataException(); + return locations; + } + catch (AbsentInformationException e) { + throw new NoDataException(); + } + } + + @Override + public ClassPrepareRequest createPrepareRequest(ClassPrepareRequestor classPrepareRequestor, + SourcePosition sourcePosition) throws NoDataException { + if (!(sourcePosition.getFile() instanceof JetFile)) { + throw new NoDataException(); + } + final Collection classNames = classNamesForPosition(sourcePosition); + if (classNames.isEmpty()) { + return null; + } + final Iterator iterator = classNames.iterator(); + boolean wildcard = false; + String namePattern = iterator.next(); + while (iterator.hasNext()) { + namePattern = StringUtil.commonPrefix(namePattern, iterator.next()); + wildcard = true; + } + if (wildcard) { + namePattern += "*"; + } + return myDebugProcess.getRequestsManager().createClassPrepareRequest(classPrepareRequestor, namePattern.replace('/', '.')); + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/debugger/JetPositionManagerFactory.java b/idea/src/org/jetbrains/jet/plugin/debugger/JetPositionManagerFactory.java new file mode 100644 index 00000000000..9cef8c7adc7 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/debugger/JetPositionManagerFactory.java @@ -0,0 +1,15 @@ +package org.jetbrains.jet.plugin.debugger; + +import com.intellij.debugger.PositionManager; +import com.intellij.debugger.PositionManagerFactory; +import com.intellij.debugger.engine.DebugProcess; + +/** + * @author yole + */ +public class JetPositionManagerFactory implements PositionManagerFactory { + @Override + public PositionManager create(DebugProcess debugProcess) { + return new JetPositionManager(debugProcess); + } +}