diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/AutoCastReceiver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/AutoCastReceiver.java index 00c7fde93b7..e2b7bf3571c 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/AutoCastReceiver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/AutoCastReceiver.java @@ -32,4 +32,9 @@ public class AutoCastReceiver extends AbstractReceiverDescriptor { public R accept(@NotNull ReceiverDescriptorVisitor visitor, D data) { return original.accept(visitor, data); } + + @Override + public String toString() { + return "(" + original + " as " + getType() + ")"; + } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/DefaultValueArgument.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/DefaultValueArgument.java index 2303269f5be..ed2dccb7512 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/DefaultValueArgument.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/DefaultValueArgument.java @@ -19,4 +19,9 @@ public class DefaultValueArgument implements ResolvedValueArgument { public List getArgumentExpressions() { return Collections.emptyList(); //throw new UnsupportedOperationException("Look into the default value of the parameter"); } + + @Override + public String toString() { + return "|DEFAULT|"; + } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ExpressionValueArgument.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ExpressionValueArgument.java index 02abdc635eb..6131c9c1a2c 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ExpressionValueArgument.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ExpressionValueArgument.java @@ -29,4 +29,9 @@ public class ExpressionValueArgument implements ResolvedValueArgument { if (expression == null) return Collections.emptyList(); return Collections.singletonList(expression); } + + @Override + public String toString() { + return expression.getText(); + } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/VarargValueArgument.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/VarargValueArgument.java index 3430aa4f8e5..1b1eb7d56ba 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/VarargValueArgument.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/VarargValueArgument.java @@ -4,6 +4,7 @@ import com.google.common.collect.Lists; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.psi.JetExpression; +import java.util.Iterator; import java.util.List; /** @@ -17,4 +18,17 @@ public class VarargValueArgument implements ResolvedValueArgument { public List getArgumentExpressions() { return values; } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder("vararg:{"); + for (Iterator iterator = values.iterator(); iterator.hasNext(); ) { + JetExpression expression = iterator.next(); + builder.append(expression.getText()); + if (iterator.hasNext()) { + builder.append(", "); + } + } + return builder.append("}").toString(); + } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/AutoCastService.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/AutoCastService.java index 4c0de1c1ca5..c3318690a20 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/AutoCastService.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/AutoCastService.java @@ -11,6 +11,7 @@ import java.util.List; */ public interface AutoCastService { AutoCastService NO_AUTO_CASTS = new AutoCastService() { + @NotNull @Override public DataFlowInfo getDataFlowInfo() { return DataFlowInfo.EMPTY; @@ -21,13 +22,17 @@ public interface AutoCastService { return !receiver.getType().isNullable(); } + @NotNull @Override - public List getVariantsForReceiver(ReceiverDescriptor receiverDescriptor) { + public List getVariantsForReceiver(@NotNull ReceiverDescriptor receiverDescriptor) { return Collections.singletonList(receiverDescriptor); } }; - List getVariantsForReceiver(ReceiverDescriptor receiverDescriptor); + @NotNull + List getVariantsForReceiver(@NotNull ReceiverDescriptor receiverDescriptor); + + @NotNull DataFlowInfo getDataFlowInfo(); boolean isNotNull(@NotNull ReceiverDescriptor receiver); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/AutoCastServiceImpl.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/AutoCastServiceImpl.java index c5cf125e80a..5789419b22e 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/AutoCastServiceImpl.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/AutoCastServiceImpl.java @@ -18,11 +18,13 @@ public class AutoCastServiceImpl implements AutoCastService { this.bindingContext = bindingContext; } + @NotNull @Override - public List getVariantsForReceiver(ReceiverDescriptor receiverDescriptor) { + public List getVariantsForReceiver(@NotNull ReceiverDescriptor receiverDescriptor) { return AutoCastUtils.getAutoCastVariants(bindingContext, dataFlowInfo, receiverDescriptor); } + @NotNull @Override public DataFlowInfo getDataFlowInfo() { return dataFlowInfo; diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/ClassReceiver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/ClassReceiver.java index 2f5f916a626..429a21b184f 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/ClassReceiver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/ClassReceiver.java @@ -37,4 +37,9 @@ public class ClassReceiver implements ThisReceiverDescriptor { public R accept(@NotNull ReceiverDescriptorVisitor visitor, D data) { return visitor.visitClassReceiver(this, data); } + + @Override + public String toString() { + return "Class{" + getType() + "}"; + } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/ExpressionReceiver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/ExpressionReceiver.java index d7a50974481..3ab7933e68a 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/ExpressionReceiver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/ExpressionReceiver.java @@ -25,4 +25,9 @@ public class ExpressionReceiver extends AbstractReceiverDescriptor implements Re public R accept(@NotNull ReceiverDescriptorVisitor visitor, D data) { return visitor.visitExpressionReceiver(this, data); } + + @Override + public String toString() { + return getType() + " {" + expression + ": " + expression.getText() + "}"; + } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/ExtensionReceiver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/ExtensionReceiver.java index ed7e876e29c..b9d801ebc9a 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/ExtensionReceiver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/ExtensionReceiver.java @@ -27,4 +27,9 @@ public class ExtensionReceiver extends AbstractReceiverDescriptor implements Thi public R accept(@NotNull ReceiverDescriptorVisitor visitor, D data) { return visitor.visitExtensionReceiver(this, data); } + + @Override + public String toString() { + return getType() + "Ext{" + descriptor + "}"; + } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/TransientReceiver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/TransientReceiver.java index ca79ac48409..687b45ea0b8 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/TransientReceiver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/TransientReceiver.java @@ -18,4 +18,9 @@ public class TransientReceiver extends AbstractReceiverDescriptor { public R accept(@NotNull ReceiverDescriptorVisitor visitor, D data) { return visitor.visitTransientReceiver(this, data); } + + @Override + public String toString() { + return "{Transient} : " + getType(); + } } diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index fe7b457af02..3c218cee23f 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -61,5 +61,8 @@ + diff --git a/idea/src/org/jetbrains/jet/plugin/internal/codewindow/BytecodeToolwindow.java b/idea/src/org/jetbrains/jet/plugin/internal/codewindow/BytecodeToolwindow.java index ad1f700d7fd..31992d30e86 100644 --- a/idea/src/org/jetbrains/jet/plugin/internal/codewindow/BytecodeToolwindow.java +++ b/idea/src/org/jetbrains/jet/plugin/internal/codewindow/BytecodeToolwindow.java @@ -208,7 +208,7 @@ public class BytecodeToolwindow extends JPanel { } } - private static class Location { + public static class Location { final Editor editor; final long modificationStamp; final int startOffset; @@ -225,6 +225,22 @@ public class BytecodeToolwindow extends JPanel { return new Location(editor); } + public Editor getEditor() { + return editor; + } + + public long getModificationStamp() { + return modificationStamp; + } + + public int getStartOffset() { + return startOffset; + } + + public int getEndOffset() { + return endOffset; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/idea/src/org/jetbrains/jet/plugin/internal/resolvewindow/ResolveToolwindow.java b/idea/src/org/jetbrains/jet/plugin/internal/resolvewindow/ResolveToolwindow.java new file mode 100644 index 00000000000..d5dbe042557 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/internal/resolvewindow/ResolveToolwindow.java @@ -0,0 +1,251 @@ +/* + * @author max + */ +package org.jetbrains.jet.plugin.internal.resolvewindow; + +import com.intellij.openapi.application.Result; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.EditorFactory; +import com.intellij.openapi.editor.ex.EditorEx; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.wm.ToolWindow; +import com.intellij.openapi.wm.ToolWindowFactory; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtilCore; +import com.intellij.ui.content.ContentFactory; +import com.intellij.util.Alarm; +import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.psi.JetElement; +import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.psi.JetReferenceExpression; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.calls.ResolvedCall; +import org.jetbrains.jet.lang.resolve.calls.ResolvedValueArgument; +import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.plugin.JetFileType; +import org.jetbrains.jet.plugin.compiler.WholeProjectAnalyzerFacade; +import org.jetbrains.jet.plugin.internal.codewindow.BytecodeToolwindow; + +import javax.swing.*; +import java.awt.*; +import java.util.Map; + +import static org.jetbrains.jet.lang.resolve.BindingContext.EXPRESSION_TYPE; +import static org.jetbrains.jet.lang.resolve.BindingContext.REFERENCE_TARGET; +import static org.jetbrains.jet.lang.resolve.BindingContext.RESOLVED_CALL; + +public class ResolveToolwindow extends JPanel { + public static class Factory implements ToolWindowFactory { + @Override + public void createToolWindowContent(Project project, ToolWindow toolWindow) { + toolWindow.getContentManager().addContent(ContentFactory.SERVICE.getInstance().createContent(new ResolveToolwindow(project), "", false)); + } + } + + private static final int UPDATE_DELAY = 500; + private final Editor myEditor; + private final Alarm myUpdateAlarm; + private BytecodeToolwindow.Location myCurrentLocation; + private final Project myProject; + + + public ResolveToolwindow(Project project) { + super(new BorderLayout()); + myProject = project; + myEditor = EditorFactory.getInstance().createEditor(EditorFactory.getInstance().createDocument(""), project, JetFileType.INSTANCE, true); + add(myEditor.getComponent()); + myUpdateAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD); + myUpdateAlarm.addRequest(new Runnable() { + @Override + public void run() { + myUpdateAlarm.addRequest(this, UPDATE_DELAY); + BytecodeToolwindow.Location location = BytecodeToolwindow.Location.fromEditor(FileEditorManager.getInstance(myProject).getSelectedTextEditor()); + if (!Comparing.equal(location, myCurrentLocation)) { + render(location, myCurrentLocation); + myCurrentLocation = location; + } + } + }, UPDATE_DELAY); + } + + private void render(BytecodeToolwindow.Location location, BytecodeToolwindow.Location oldLocation) { + Editor editor = location.getEditor(); + if (editor == null) { + setText("No editor"); + } + else { + VirtualFile vFile = ((EditorEx) editor).getVirtualFile(); + if (vFile == null) { + setText(""); + return; + } + + PsiFile psiFile = PsiManager.getInstance(myProject).findFile(vFile); + if (!(psiFile instanceof JetFile)) { + setText(""); + return; + } + + + int startOffset = location.getStartOffset(); + int endOffset = location.getEndOffset(); + if (oldLocation == null || !Comparing.equal(oldLocation.getEditor(), location.getEditor()) + || oldLocation.getStartOffset() != startOffset + || oldLocation.getEndOffset() != endOffset) { + + BindingContext bindingContext = WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile((JetFile) psiFile); + + + PsiElement elementAtOffset; + if (startOffset == endOffset) { + elementAtOffset = PsiUtilCore.getElementAtOffset(psiFile, startOffset); + } + else { + PsiElement start = PsiUtilCore.getElementAtOffset(psiFile, startOffset); + System.out.println("start = " + start.getText()); + PsiElement end = PsiUtilCore.getElementAtOffset(psiFile, endOffset - 1); + System.out.println("end = " + end.getText()); + elementAtOffset = PsiTreeUtil.findCommonParent(start, end); + System.out.println("elementAtOffset = " + elementAtOffset.getText()); + } + + PsiElement currentElement = elementAtOffset; + + boolean callFound = false; + while (currentElement != null && !(currentElement instanceof PsiFile)) { + if (currentElement instanceof JetElement) { + JetElement atOffset = (JetElement) currentElement; + ResolvedCall resolvedCall = bindingContext.get(RESOLVED_CALL, (JetElement) atOffset); + if (resolvedCall != null) { + setText(renderCall(resolvedCall) + "\n===\n" + currentElement + ": " + currentElement.getText()); + callFound = true; + break; + } + + } + currentElement = currentElement.getParent(); + } + if (!callFound) { + + JetExpression parentExpression = (elementAtOffset instanceof JetExpression) ? (JetExpression) elementAtOffset + : PsiTreeUtil.getParentOfType(elementAtOffset, JetExpression.class); + if (parentExpression != null) { + JetType type = bindingContext.get(EXPRESSION_TYPE, parentExpression); + String text = parentExpression + "|" + parentExpression.getText() + "| : " + type; + if (parentExpression instanceof JetReferenceExpression) { + JetReferenceExpression referenceExpression = (JetReferenceExpression) parentExpression; + DeclarationDescriptor target = bindingContext.get(REFERENCE_TARGET, referenceExpression); + text += "\nReference target: \n" + target; + } + setText(text); + } + } + } + } + } + + private String renderCall(ResolvedCall resolvedCall) { + StringBuilder builder = new StringBuilder(); + + CallableDescriptor resultingDescriptor = resolvedCall.getResultingDescriptor(); + ReceiverDescriptor receiverArgument = resolvedCall.getReceiverArgument(); + ReceiverDescriptor thisObject = resolvedCall.getThisObject(); + Map typeArguments = resolvedCall.getTypeArguments(); + Map valueArguments = resolvedCall.getValueArguments(); + + renderReceiver(receiverArgument, thisObject, builder); + builder.append(resultingDescriptor.getName()); + renderTypeArguments(typeArguments, builder); + + if (resultingDescriptor instanceof FunctionDescriptor) { + renderValueArguments(valueArguments, builder); + } + + builder.append(" : ").append(resultingDescriptor.getReturnType()); + + builder.append("\n"); + builder.append("\n"); + + CallableDescriptor candidateDescriptor = resolvedCall.getCandidateDescriptor(); + builder.append("Candidate: \n").append(candidateDescriptor).append("\n"); + if (resultingDescriptor != candidateDescriptor) { + builder.append("Result: \n").append(resultingDescriptor).append("\n"); + } + + builder.append("Receiver: \n").append(receiverArgument).append("\n"); + builder.append("This object: \n").append(thisObject).append("\n"); + builder.append("Type args: \n").append(typeArguments).append("\n"); + builder.append("Value args: \n").append(valueArguments).append("\n"); + + return builder.toString(); + } + + private void renderValueArguments(Map valueArguments, StringBuilder builder) { + ResolvedValueArgument[] args = new ResolvedValueArgument[valueArguments.size()]; + for (Map.Entry entry : valueArguments.entrySet()) { + ValueParameterDescriptor key = entry.getKey(); + ResolvedValueArgument value = entry.getValue(); + + args[key.getIndex()] = value; + } + builder.append("("); + for (int i = 0, argsLength = args.length; i < argsLength; i++) { + ResolvedValueArgument arg = args[i]; + builder.append(arg); + if (i != argsLength - 1) { + builder.append(", "); + } + } + builder.append(")"); + } + + private void renderTypeArguments(Map typeArguments, StringBuilder builder) { + JetType[] args = new JetType[typeArguments.size()]; + for (Map.Entry entry : typeArguments.entrySet()) { + TypeParameterDescriptor key = entry.getKey(); + JetType value = entry.getValue(); + args[key.getIndex()] = value; + } + builder.append("<"); + for (int i = 0, argsLength = args.length; i < argsLength; i++) { + JetType type = args[i]; + builder.append(type); + if (i != argsLength - 1) { + builder.append(", "); + } + } + builder.append(">"); + } + + private void renderReceiver(ReceiverDescriptor receiverArgument, ReceiverDescriptor thisObject, StringBuilder builder) { + if (receiverArgument.exists()) { + builder.append("/").append(receiverArgument); + } + + if (thisObject.exists()) { + builder.append("/this=").append(thisObject); + } + + if (thisObject.exists() || receiverArgument.exists()) { + builder.append("/."); + } + } + + private void setText(final String text) { + new WriteCommandAction(myProject) { + @Override + protected void run(Result result) throws Throwable { + myEditor.getDocument().setText(text); + } + }.execute(); + } +}