Merge remote-tracking branch 'origin/master' into numpy-array-view

This commit is contained in:
Alexander Marchuk
2014-09-04 12:21:06 +04:00
2087 changed files with 99809 additions and 22257 deletions
+3 -3
View File
@@ -290,9 +290,9 @@
</codeStyleSettings>
<codeStyleSettings language="jet">
<indentOptions>
<option name="INDENT_SIZE" value="2"/>
<option name="CONTINUATION_INDENT_SIZE" value="4"/>
<option name="TAB_SIZE" value="2"/>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="ruby">
+1
View File
@@ -27,6 +27,7 @@
</annotationProcessing>
<bytecodeTargetLevel target="1.6">
<module name="annotations" target="1.5" />
<module name="gradle-jps-plugin" target="1.5" />
<module name="groovy-rt-constants" target="1.5" />
<module name="groovy_rt" target="1.5" />
<module name="intellilang-jps-plugin" target="1.6" />
+3 -3
View File
@@ -1,12 +1,12 @@
<component name="libraryTable">
<library name="JSch">
<CLASSES>
<root url="jar://$PROJECT_DIR$/lib/jsch-0.1.50.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/jsch-0.1.51.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$PROJECT_DIR$/lib/src/jsch-0.1.50.zip!/jsch-0.1.50/src/main/java" />
<root url="jar://$PROJECT_DIR$/lib/src/jsch-0.1.50.zip!/jsch-0.1.50/examples" />
<root url="jar://$PROJECT_DIR$/lib/src/jsch-0.1.51.zip!/jsch-0.1.51/src/main/java" />
<root url="jar://$PROJECT_DIR$/lib/src/jsch-0.1.51.zip!/jsch-0.1.51/examples" />
</SOURCES>
</library>
</component>
+2 -2
View File
@@ -1,11 +1,11 @@
<component name="libraryTable">
<library name="Netty">
<CLASSES>
<root url="jar://$PROJECT_DIR$/lib/netty-all-4.1.0.Beta1.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/netty-all-4.1.0.Beta3.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$PROJECT_DIR$/lib/src/netty-all-4.1.0.Beta1-sources.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/src/netty-all-4.1.0.Beta3-sources.jar!/" />
</SOURCES>
</library>
</component>
Binary file not shown.
Binary file not shown.
Binary file not shown.
+2
View File
@@ -36,4 +36,6 @@
1.5 => plugins/Groovy/lib/groovy_rt.jar
1.5 => lib/annotations.jar
1.5 => plugins/gradle/lib/gradle-jps-plugin.jar
1.3 => plugins/junit/lib/junit-rt.jar
+1 -8
View File
@@ -554,14 +554,7 @@ public def layoutCommunityPlugins(String home) {
}
}
pluginDir("java-decompiler") {
dir("lib") {
jar("java-decompiler.jar") {
module("java-decompiler")
}
fileset(file: "$home/plugins/java-decompiler/lib/fernflower.jar")
}
}
layoutPlugin("java-decompiler")
}
}
+2 -3
View File
@@ -196,7 +196,7 @@ libraryLicense(name: "JGoodies Forms", libraryName: "jgoodies-forms", version: "
libraryLicense(name: "JGoodies Looks", libraryName: "jgoodies-looks", version: "2.4.2", license: "BSD ", url: "http://www.jgoodies.com/freeware/looks/", licenseUrl: "http://www.jgoodies.com/downloads/libraries.html")
libraryLicense(name: "JGoodies Common", libraryName: "jgoodies-common", version: "1.2.1", license: "BSD ", url: "http://www.jgoodies.com/freeware/looks/", licenseUrl: "http://www.jgoodies.com/downloads/libraries.html")
libraryLicense(name: "JNA", libraryName: "jna", version: "3.4.0", license: "LGPL 2.1", url: "https://jna.dev.java.net/", licenseUrl: "http://www.opensource.org/licenses/lgpl-2.1.php")
libraryLicense(name: "JSch", libraryName: "JSch", version: "0.1.50", license: "BSD", url: "http://www.jcraft.com/jsch/", licenseUrl: "http://www.jcraft.com/jsch/LICENSE.txt")
libraryLicense(name: "JSch", libraryName: "JSch", version: "0.1.51", license: "BSD", url: "http://www.jcraft.com/jsch/", licenseUrl: "http://www.jcraft.com/jsch/LICENSE.txt")
libraryLicense(name: "jsch-agent-proxy", libraryName: "jsch-agent-proxy", version: "0.0.6", license: "BSD", url: "https://github.com/ymnk/jsch-agent-proxy", licenseUrl: "https://github.com/ymnk/jsch-agent-proxy/blob/master/LICENSE.txt")
libraryLicense(name: "jsch-agent-proxy for svnkit trilead", libraryName: "jsch.agentproxy.svnkit-trilead-ssh2.jar", version: "0.0.6", license: "BSD", url: "https://github.com/ymnk/jsch-agent-proxy", licenseUrl: "https://github.com/ymnk/jsch-agent-proxy/blob/master/LICENSE.txt")
libraryLicense(name: "JUnit", libraryName: "JUnit3", version: "3.8.1", license: "CPL 1.0", url: "http://junit.org/")
@@ -252,7 +252,7 @@ libraryLicense(name: "XML-RPC", libraryName: "XmlRPC", version: "2.0", license:
libraryLicense(name: "XStream", version: "1.4.3", license: "BSD", url: "http://xstream.codehaus.org/", licenseUrl: "http://xstream.codehaus.org/license.html")
libraryLicense(name: "YourKit Java Profiler", libraryName: "yjp-controller-api-redist.jar", version: "8.0.x", license: "link (commercial license)", url: "http://yourkit.com/", licenseUrl: "http://www.yourkit.com/purchase/license.html")
libraryLicense(name: "protobuf", version: "2.5.0", license: "New BSD", url: "http://code.google.com/p/protobuf/", licenseUrl: "http://code.google.com/p/protobuf/source/browse/trunk/COPYING.txt?r=367")
libraryLicense(name: "Netty", libraryName: "Netty", version: "4.1.0.Beta1", license: "Apache 2.0", url: "http://netty.io", licenseUrl: "http://www.apache.org/licenses/LICENSE-2.0")
libraryLicense(name: "Netty", libraryName: "Netty", version: "4.1.0.Beta3", license: "Apache 2.0", url: "http://netty.io", licenseUrl: "http://www.apache.org/licenses/LICENSE-2.0")
libraryLicense(name: "Kryo", libraryName: "Kryo", version: "2.22", license: "New BSD License", url: "https://github.com/EsotericSoftware/kryo", licenseUrl: "https://github.com/EsotericSoftware/kryo/blob/master/license.txt")
libraryLicense(name: "Snappy-Java", libraryName: "Snappy-Java", version: "0.3", license: "Apache 2.0", url: "https://github.com/dain/snappy", licenseUrl: "http://www.apache.org/licenses/LICENSE-2.0")
libraryLicense(name: "Cucumber-Java", libraryName: "cucumber-java", version: "1.0.14", license: "MIT License", url: "https://github.com/cucumber/cucumber-jvm/", licenseUrl: "http://www.opensource.org/licenses/mit-license.html")
@@ -285,4 +285,3 @@ jetbrainsLibrary("JPS")
jetbrainsLibrary("Maven Embedder")
jetbrainsLibrary("tcServiceMessages")
jetbrainsLibrary("optimizedFileManager.jar")
jetbrainsLibrary("fernflower.jar")
+1
View File
@@ -105,6 +105,7 @@
<orderEntry type="module" module-name="structuralsearch-java" />
<orderEntry type="module" module-name="structuralsearch-tests" scope="TEST" />
<orderEntry type="module" module-name="structuralsearch-groovy" />
<orderEntry type="module" module-name="typeMigration" />
</component>
</module>
@@ -4,6 +4,3 @@ org.jetbrains.idea.svn.*
org.jetbrains.idea.svn16.*
com.intellij.util.net.ssl.*
com.intellij.tasks.integration.live.*
[DECOMPILER_TESTS]
org.jetbrains.java.decompiler.*
@@ -55,7 +55,7 @@ import java.util.*;
public class CompileContextImpl extends UserDataHolderBase implements CompileContextEx {
private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.CompileContextImpl");
private final Project myProject;
private final CompilerTask myTask;
private final CompilerTask myBuildSession;
private final Map<CompilerMessageCategory, Collection<CompilerMessage>> myMessages = new EnumMap<CompilerMessageCategory, Collection<CompilerMessage>>(CompilerMessageCategory.class);
private final boolean myShouldUpdateProblemsView;
private CompileScope myCompileScope;
@@ -77,7 +77,7 @@ public class CompileContextImpl extends UserDataHolderBase implements CompileCon
CompileScope compileScope,
boolean isMake, boolean isRebuild) {
myProject = project;
myTask = compilerSession;
myBuildSession = compilerSession;
myCompileScope = compileScope;
myMake = isMake;
myIsRebuild = isRebuild;
@@ -99,6 +99,10 @@ public class CompileContextImpl extends UserDataHolderBase implements CompileCon
myShouldUpdateProblemsView = workspaceConfig.MAKE_PROJECT_ON_SAVE;
}
public CompilerTask getBuildSession() {
return myBuildSession;
}
public boolean shouldUpdateProblemsView() {
return myShouldUpdateProblemsView;
}
@@ -154,7 +158,7 @@ public class CompileContextImpl extends UserDataHolderBase implements CompileCon
myMessages.put(msg.getCategory(), messages);
}
if (messages.add(msg)) {
myTask.addMessage(msg);
myBuildSession.addMessage(msg);
}
if (myShouldUpdateProblemsView && msg.getCategory() == CompilerMessageCategory.ERROR) {
ProblemsView.SERVICE.getInstance(myProject).addMessage(msg, mySessionId);
@@ -200,7 +204,7 @@ public class CompileContextImpl extends UserDataHolderBase implements CompileCon
}
public ProgressIndicator getProgressIndicator() {
return myTask.getIndicator();
return myBuildSession.getIndicator();
}
public Module getModuleByFile(VirtualFile file) {
@@ -29,6 +29,8 @@ import com.intellij.compiler.ProblemsView;
import com.intellij.compiler.progress.CompilerTask;
import com.intellij.compiler.server.BuildManager;
import com.intellij.compiler.server.DefaultMessageHandler;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationListener;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.compiler.*;
import com.intellij.openapi.compiler.ex.CompilerPathsEx;
@@ -53,10 +55,7 @@ import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.openapi.wm.StatusBar;
import com.intellij.openapi.wm.ToolWindowId;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.openapi.wm.*;
import com.intellij.packaging.artifacts.Artifact;
import com.intellij.packaging.impl.compiler.ArtifactCompilerUtil;
import com.intellij.packaging.impl.compiler.ArtifactsCompiler;
@@ -68,6 +67,7 @@ import com.intellij.util.ThrowableRunnable;
import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.messages.MessageBus;
import com.intellij.util.text.DateFormatUtil;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -79,7 +79,9 @@ import org.jetbrains.jps.api.RequestFuture;
import org.jetbrains.jps.model.java.JavaSourceRootType;
import javax.swing.*;
import javax.swing.event.HyperlinkEvent;
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.*;
import java.util.concurrent.TimeUnit;
@@ -502,9 +504,24 @@ public class CompileDriver {
if (duration > ONE_MINUTE_MS && CompilerWorkspaceConfiguration.getInstance(myProject).DISPLAY_NOTIFICATION_POPUP) {
ToolWindowManager.getInstance(myProject).notifyByBalloon(ToolWindowId.MESSAGES_WINDOW, messageType, statusMessage);
}
CompilerManager.NOTIFICATION_GROUP.createNotification(statusMessage, messageType).notify(myProject);
final String wrappedMessage = _status != ExitStatus.UP_TO_DATE? "<a href='#'>" + statusMessage + "</a>" : statusMessage;
final Notification notification = CompilerManager.NOTIFICATION_GROUP.createNotification(
"", wrappedMessage,
messageType.toNotificationType(),
new MessagesActivationListener(compileContext)
);
compileContext.getBuildSession().registerCloseAction(new Runnable() {
@Override
public void run() {
notification.expire();
}
});
notification.notify(myProject);
if (_status != ExitStatus.UP_TO_DATE && compileContext.getMessageCount(null) > 0) {
compileContext.addMessage(CompilerMessageCategory.INFORMATION, statusMessage, null, -1, -1);
final String msg = DateFormatUtil.formatDateTime(new Date()) + " - " + statusMessage;
compileContext.addMessage(CompilerMessageCategory.INFORMATION, msg, null, -1, -1);
}
}
}
@@ -785,4 +802,28 @@ public class CompileDriver {
private void showConfigurationDialog(String moduleNameToSelect, String tabNameToSelect) {
ProjectSettingsService.getInstance(myProject).showModuleConfigurationDialog(moduleNameToSelect, tabNameToSelect);
}
private static class MessagesActivationListener extends NotificationListener.Adapter {
private final WeakReference<Project> myProjectRef;
private final Object myContentId;
public MessagesActivationListener(CompileContextImpl compileContext) {
myProjectRef = new WeakReference<Project>(compileContext.getProject());
myContentId = compileContext.getBuildSession().getContentId();
}
@Override
protected void hyperlinkActivated(@NotNull Notification notification, @NotNull HyperlinkEvent e) {
final Project project = myProjectRef.get();
if (project != null && !project.isDisposed() && CompilerTask.showCompilerContent(project, myContentId)) {
final ToolWindow tw = ToolWindowManager.getInstance(project).getToolWindow(ToolWindowId.MESSAGES_WINDOW);
if (tw != null) {
tw.activate(null, false);
}
}
else {
notification.expire();
}
}
}
}
@@ -22,12 +22,11 @@
package com.intellij.compiler.progress;
import com.intellij.compiler.CompilerManagerImpl;
import com.intellij.compiler.CompilerMessageImpl;
import com.intellij.compiler.impl.CompilerErrorTreeView;
import com.intellij.ide.errorTreeView.NewErrorTreeViewPanel;
import com.intellij.ide.errorTreeView.impl.ErrorTreeViewConfiguration;
import com.intellij.ide.impl.ProjectUtil;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.compiler.*;
@@ -42,6 +41,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.project.ProjectManagerListener;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
@@ -120,6 +120,26 @@ public class CompilerTask extends Task.Backgroundable {
mySessionId = sessionId;
}
@NotNull
public Object getContentId() {
return myContentId;
}
public void registerCloseAction(final Runnable onClose) {
synchronized (myMessageViewLock) {
if (myErrorTreeView != null) {
Disposer.register(myErrorTreeView, new Disposable() {
@Override
public void dispose() {
onClose.run();
}
});
return;
}
}
onClose.run();
}
@Override
public String getProcessId() {
return "compilation";
@@ -221,13 +241,15 @@ public class CompilerTask extends Task.Backgroundable {
private void addIndicatorDelegate() {
ProgressIndicator indicator = myIndicator;
if (!(indicator instanceof ProgressIndicatorEx)) return;
if (!(indicator instanceof ProgressIndicatorEx)) {
return;
}
((ProgressIndicatorEx)indicator).addStateDelegate(new ProgressIndicatorBase() {
@Override
public void cancel() {
super.cancel();
closeUI();
selectFirstMessage();
stopAppIconProgress();
}
@@ -235,11 +257,26 @@ public class CompilerTask extends Task.Backgroundable {
public void stop() {
super.stop();
if (!isCanceled()) {
closeUI();
selectFirstMessage();
}
stopAppIconProgress();
}
private void selectFirstMessage() {
if (!isHeadlessMode()) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
synchronized (myMessageViewLock) {
if (myErrorTreeView != null) {
myErrorTreeView.selectFirstMessage();
}
}
}
});
}
}
private void stopAppIconProgress() {
UIUtil.invokeLaterIfNeeded(new Runnable() {
@Override
@@ -249,7 +286,8 @@ public class CompilerTask extends Task.Backgroundable {
if (myErrorCount > 0) {
appIcon.setErrorBadge(myProject, String.valueOf(myErrorCount));
appIcon.requestAttention(myProject, true);
} else if (!myCompilationStartedAutomatically) {
}
else if (!myCompilationStartedAutomatically) {
appIcon.setOkBadge(myProject, true);
appIcon.requestAttention(myProject, false);
}
@@ -450,18 +488,22 @@ public class CompilerTask extends Task.Backgroundable {
public void showCompilerContent() {
synchronized (myMessageViewLock) {
if (myErrorTreeView != null) {
final MessageView messageView = MessageView.SERVICE.getInstance(myProject);
Content[] contents = messageView.getContentManager().getContents();
for (Content content : contents) {
if (CONTENT_ID_KEY.get(content) == myContentId) {
messageView.getContentManager().setSelectedContent(content);
return;
}
}
showCompilerContent(myProject, myContentId);
}
}
}
public static boolean showCompilerContent(final Project project, final Object contentId) {
final MessageView messageView = MessageView.SERVICE.getInstance(project);
for (Content content : messageView.getContentManager().getContents()) {
if (CONTENT_ID_KEY.get(content) == contentId) {
messageView.getContentManager().setSelectedContent(content);
return true;
}
}
return false;
}
private void removeAllContents(Project project, Content notRemove) {
if (project.isDisposed()) {
return;
@@ -488,7 +530,7 @@ public class CompilerTask extends Task.Backgroundable {
private void activateMessageView() {
synchronized (myMessageViewLock) {
if (myErrorTreeView != null) {
if (myErrorTreeView != null && myProject != null) {
final ToolWindow tw = ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.MESSAGES_WINDOW);
if (tw != null) {
tw.activate(null, false);
@@ -497,34 +539,6 @@ public class CompilerTask extends Task.Backgroundable {
}
}
private void closeUI() {
if (isHeadlessMode()) {
return;
}
Window window = getWindow();
ModalityState modalityState = window != null ? ModalityState.stateForComponent(window) : ModalityState.NON_MODAL;
final Application application = ApplicationManager.getApplication();
application.invokeLater(new Runnable() {
@Override
public void run() {
synchronized (myMessageViewLock) {
if (myErrorTreeView != null) {
final boolean shouldRetainView = myErrorCount > 0 || myWarningCount > 0 && !myErrorTreeView.isHideWarnings();
if (shouldRetainView) {
addMessage(new CompilerMessageImpl(myProject, CompilerMessageCategory.STATISTICS, CompilerBundle.message("statistics.error.count", myErrorCount)));
addMessage(new CompilerMessageImpl(myProject, CompilerMessageCategory.STATISTICS, CompilerBundle.message("statistics.warnings.count", myWarningCount)));
//activateMessageView();
myErrorTreeView.selectFirstMessage();
}
else {
removeAllContents(myProject, null);
}
}
}
}
}, modalityState);
}
public Window getWindow(){
return null;
}
@@ -607,7 +621,7 @@ public class CompilerTask extends Task.Backgroundable {
if (event.getContent() == myContent) {
synchronized (myMessageViewLock) {
if (myErrorTreeView != null) {
myErrorTreeView.dispose();
Disposer.dispose(myErrorTreeView);
myErrorTreeView = null;
if (myIndicator.isRunning()) {
cancel();
@@ -34,7 +34,9 @@ import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.compiler.CompilationStatusListener;
import com.intellij.openapi.compiler.CompileContext;
import com.intellij.openapi.compiler.CompilerTopics;
import com.intellij.openapi.components.ApplicationComponent;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.EditorFactory;
@@ -64,16 +66,14 @@ import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.openapi.vfs.newvfs.BulkFileListener;
import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
import com.intellij.openapi.vfs.newvfs.impl.FileNameCache;
import com.intellij.openapi.wm.IdeFrame;
import com.intellij.util.Alarm;
import com.intellij.util.Function;
import com.intellij.util.PathUtil;
import com.intellij.util.SmartList;
import com.intellij.util.*;
import com.intellij.util.concurrency.Semaphore;
import com.intellij.util.concurrency.SequentialTaskExecutor;
import com.intellij.util.containers.IntArrayList;
@@ -1117,6 +1117,44 @@ public class BuildManager implements ApplicationComponent{
scheduleAutoMake();
}
});
conn.subscribe(CompilerTopics.COMPILATION_STATUS, new CompilationStatusListener() {
private final Set<String> myRootsToRefresh = new THashSet<String>(FileUtil.PATH_HASHING_STRATEGY);
@Override
public void compilationFinished(boolean aborted, int errors, int warnings, CompileContext compileContext) {
final String[] roots;
synchronized (myRootsToRefresh) {
roots = ArrayUtil.toStringArray(myRootsToRefresh);
myRootsToRefresh.clear();
}
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
@Override
public void run() {
if (project.isDisposed()) {
return;
}
final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex();
final LocalFileSystem lfs = LocalFileSystem.getInstance();
final Set<VirtualFile> filesToRefresh = new HashSet<VirtualFile>();
for (String root : roots) {
final VirtualFile rootFile = lfs.refreshAndFindFileByPath(root);
if (rootFile != null && fileIndex.isInSourceContent(rootFile)) {
filesToRefresh.add(rootFile);
}
}
if (!filesToRefresh.isEmpty()) {
lfs.refreshFiles(filesToRefresh, true, true, null);
}
}
});
}
@Override
public void fileGenerated(String outputRoot, String relativePath) {
synchronized (myRootsToRefresh) {
myRootsToRefresh.add(outputRoot);
}
}
});
final String projectPath = getProjectPath(project);
Disposer.register(project, new Disposable() {
@Override
@@ -20,6 +20,7 @@ import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.util.ProgressIndicatorBase;
import com.intellij.openapi.progress.util.ProgressIndicatorUtils;
import com.intellij.openapi.progress.util.ReadTask;
import com.intellij.openapi.project.DumbService;
@@ -40,7 +41,6 @@ import org.jetbrains.jps.api.CmdlineRemoteProto;
import org.jetbrains.org.objectweb.asm.Opcodes;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* @author Eugene Zhuravlev
@@ -82,12 +82,7 @@ public abstract class DefaultMessageHandler implements BuilderMessageHandler {
break;
case CONSTANT_SEARCH_TASK:
final CmdlineRemoteProto.Message.BuilderMessage.ConstantSearchTask task = msg.getConstantSearchTask();
myTaskExecutor.submit(new Runnable() {
@Override
public void run() {
handleConstantSearchTask(channel, sessionId, task);
}
});
handleConstantSearchTask(channel, sessionId, task);
break;
}
}
@@ -97,30 +92,29 @@ public abstract class DefaultMessageHandler implements BuilderMessageHandler {
protected abstract void handleBuildEvent(UUID sessionId, CmdlineRemoteProto.Message.BuilderMessage.BuildEvent event);
private void handleConstantSearchTask(final Channel channel, final UUID sessionId, final CmdlineRemoteProto.Message.BuilderMessage.ConstantSearchTask task) {
while (true) {
final AtomicBoolean canceled = new AtomicBoolean(false);
DumbService.getInstance(myProject).waitForSmartMode();
ProgressIndicatorUtils.runWithWriteActionPriority(new ReadTask() {
@Override
public void computeInReadAction(@NotNull ProgressIndicator indicator) {
if (DumbService.isDumb(myProject)) {
canceled.set(true);
return;
}
ProgressIndicatorUtils.scheduleWithWriteActionPriority(new ProgressIndicatorBase(), myTaskExecutor, new ReadTask() {
@Override
public void computeInReadAction(@NotNull ProgressIndicator indicator) {
if (DumbService.isDumb(myProject)) {
onCanceled(indicator);
}
else {
doHandleConstantSearchTask(channel, sessionId, task);
}
@Override
public void onCanceled(@NotNull ProgressIndicator indicator) {
canceled.set(true);
}
});
if (!canceled.get()) {
break;
}
}
@Override
public void onCanceled(@NotNull ProgressIndicator indicator) {
DumbService.getInstance(myProject).runWhenSmart(new Runnable() {
@Override
public void run() {
handleConstantSearchTask(channel, sessionId, task);
}
});
}
});
}
private void doHandleConstantSearchTask(Channel channel, UUID sessionId, CmdlineRemoteProto.Message.BuilderMessage.ConstantSearchTask task) {
final String ownerClassName = task.getOwnerClassName();
final String fieldName = task.getFieldName();
@@ -23,12 +23,10 @@ import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl;
import com.intellij.debugger.ui.tree.render.*;
import com.intellij.ide.actions.ShowSettingsUtilImpl;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.ex.SingleConfigurableEditor;
import com.intellij.openapi.options.ShowSettingsUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import org.jetbrains.annotations.Nullable;
public class AdjustArrayRangeAction extends DebuggerAction {
@@ -67,30 +65,25 @@ public class AdjustArrayRangeAction extends DebuggerAction {
title = title + " " + label.substring(index);
}
final ArrayRenderer clonedRenderer = renderer.clone();
final NamedArrayConfigurable configurable = new NamedArrayConfigurable(title, clonedRenderer);
SingleConfigurableEditor editor = new SingleConfigurableEditor(project, configurable,
ShowSettingsUtilImpl.createDimensionKey(configurable), false);
editor.show();
if(editor.getExitCode() == DialogWrapper.OK_EXIT_CODE) {
if (ShowSettingsUtil.getInstance().editConfigurable(project, new NamedArrayConfigurable(title, clonedRenderer))) {
debugProcess.getManagerThread().schedule(new SuspendContextCommandImpl(debuggerContext.getSuspendContext()) {
@Override
public void contextAction() throws Exception {
final ValueDescriptorImpl nodeDescriptor = (ValueDescriptorImpl)selectedNode.getDescriptor();
final Renderer lastRenderer = nodeDescriptor.getLastRenderer();
if (lastRenderer instanceof ArrayRenderer) {
selectedNode.setRenderer(clonedRenderer);
}
else if (lastRenderer instanceof CompoundNodeRenderer) {
final CompoundNodeRenderer compoundRenderer = (CompoundNodeRenderer)lastRenderer;
final ChildrenRenderer childrenRenderer = compoundRenderer.getChildrenRenderer();
if (childrenRenderer instanceof ExpressionChildrenRenderer) {
ExpressionChildrenRenderer.setPreferableChildrenRenderer(nodeDescriptor, clonedRenderer);
selectedNode.calcRepresentation();
}
@Override
public void contextAction() throws Exception {
final ValueDescriptorImpl nodeDescriptor = (ValueDescriptorImpl)selectedNode.getDescriptor();
final Renderer lastRenderer = nodeDescriptor.getLastRenderer();
if (lastRenderer instanceof ArrayRenderer) {
selectedNode.setRenderer(clonedRenderer);
}
else if (lastRenderer instanceof CompoundNodeRenderer) {
final CompoundNodeRenderer compoundRenderer = (CompoundNodeRenderer)lastRenderer;
final ChildrenRenderer childrenRenderer = compoundRenderer.getChildrenRenderer();
if (childrenRenderer instanceof ExpressionChildrenRenderer) {
ExpressionChildrenRenderer.setPreferableChildrenRenderer(nodeDescriptor, clonedRenderer);
selectedNode.calcRepresentation();
}
}
});
}
});
}
}
@@ -19,23 +19,15 @@ import com.intellij.debugger.settings.ThreadsViewConfigurable;
import com.intellij.debugger.settings.ThreadsViewSettings;
import com.intellij.idea.ActionsBundle;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.options.ex.SingleConfigurableEditor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.options.ShowSettingsUtil;
/**
* User: lex
* Date: Sep 26, 2003
* Time: 4:40:12 PM
*/
public class CustomizeThreadsViewAction extends DebuggerAction {
@Override
public void actionPerformed(AnActionEvent e) {
Project project = CommonDataKeys.PROJECT.getData(e.getDataContext());
final SingleConfigurableEditor editor = new SingleConfigurableEditor(project, new ThreadsViewConfigurable(ThreadsViewSettings.getInstance()));
editor.show();
ShowSettingsUtil.getInstance().editConfigurable(e.getProject(), new ThreadsViewConfigurable(ThreadsViewSettings.getInstance()));
}
@Override
public void update(AnActionEvent e) {
e.getPresentation().setVisible(true);
e.getPresentation().setText(ActionsBundle.actionText(DebuggerActions.CUSTOMIZE_THREADS_VIEW));
@@ -71,7 +71,8 @@ public class JavaMarkObjectActionHandler extends MarkObjectActionHandler {
final DebuggerTree tree = node.getTree();
tree.saveState(node);
final Component parent = event.getInputEvent().getComponent();
final ValueDescriptorImpl valueDescriptor = ((ValueDescriptorImpl)descriptor);
final DebuggerContextImpl debuggerContext = tree.getDebuggerContext();
final DebugProcessImpl debugProcess = debuggerContext.getDebugProcess();
@@ -93,7 +94,7 @@ public class JavaMarkObjectActionHandler extends MarkObjectActionHandler {
final boolean suggestAdditionalMarkup = canSuggestAdditionalMarkup(debugProcess, valueDescriptor.getValue());
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
ObjectMarkupPropertiesDialog dialog = new ObjectMarkupPropertiesDialog(defaultText, suggestAdditionalMarkup);
ObjectMarkupPropertiesDialog dialog = new ObjectMarkupPropertiesDialog(parent, defaultText, suggestAdditionalMarkup);
dialog.show();
if (dialog.isOK()) {
result.set(Pair.create(dialog.getConfiguredMarkup(), dialog.isMarkAdditionalFields()));
@@ -0,0 +1,173 @@
/*
* Copyright 2000-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 com.intellij.debugger.actions;
import com.intellij.debugger.DebuggerContext;
import com.intellij.debugger.engine.JavaValue;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
import com.intellij.debugger.ui.impl.watch.FieldDescriptorImpl;
import com.intellij.debugger.ui.impl.watch.NodeManagerImpl;
import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl;
import com.intellij.psi.PsiExpression;
import com.intellij.xdebugger.frame.*;
import com.intellij.xdebugger.frame.presentation.XValuePresentation;
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodePresentationConfigurator;
import com.sun.jdi.Field;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.Value;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.List;
public class JavaReferringObjectsValue extends JavaValue {
private static final long MAX_REFERRING = 100;
private final boolean myIsField;
private JavaReferringObjectsValue(@Nullable JavaValue parent,
@NotNull ValueDescriptorImpl valueDescriptor,
@NotNull EvaluationContextImpl evaluationContext,
NodeManagerImpl nodeManager,
boolean isField) {
super(parent, valueDescriptor, evaluationContext, nodeManager, false);
myIsField = isField;
}
public JavaReferringObjectsValue(@NotNull JavaValue javaValue, boolean isField) {
super(null, javaValue.getDescriptor(), javaValue.getEvaluationContext(), null, false);
myIsField = isField;
}
@Override
public boolean canNavigateToSource() {
return true;
}
@Override
public void computeChildren(@NotNull final XCompositeNode node) {
if (checkContextNotResumed(node)) return;
getEvaluationContext().getDebugProcess().getManagerThread().schedule(
new SuspendContextCommandImpl(getEvaluationContext().getSuspendContext()) {
@Override
public Priority getPriority() {
return Priority.NORMAL;
}
@Override
public void contextAction() throws Exception {
final XValueChildrenList children = new XValueChildrenList();
Value value = getDescriptor().getValue();
List<ObjectReference> references = ((ObjectReference)value).referringObjects(MAX_REFERRING);
int i = 1;
for (final ObjectReference reference : references) {
// try to find field name
Field field = findField(reference, value);
if (field != null) {
ValueDescriptorImpl descriptor = new FieldDescriptorImpl(getProject(), reference, field) {
@Override
public Value calcValue(EvaluationContextImpl evaluationContext) throws EvaluateException {
return reference;
}
};
children.add(new JavaReferringObjectsValue(null, descriptor, getEvaluationContext(), null, true));
i++;
}
else {
ValueDescriptorImpl descriptor = new ValueDescriptorImpl(getProject(), reference) {
@Override
public Value calcValue(EvaluationContextImpl evaluationContext) throws EvaluateException {
return reference;
}
@Override
public String getName() {
return "Ref";
}
@Override
public String calcValueName() {
return "Ref";
}
@Override
public PsiExpression getDescriptorEvaluation(DebuggerContext context) throws EvaluateException {
return null;
}
};
children.add("Referrer " + i++, new JavaReferringObjectsValue(null, descriptor, getEvaluationContext(), null, false));
}
}
node.addChildren(children, true);
}
}
);
}
@Override
public void computePresentation(@NotNull final XValueNode node, @NotNull final XValuePlace place) {
if (!myIsField) {
super.computePresentation(node, place);
}
else {
super.computePresentation(new XValueNodePresentationConfigurator.ConfigurableXValueNodeImpl() {
@Override
public void applyPresentation(@Nullable Icon icon, @NotNull final XValuePresentation valuePresenter, boolean hasChildren) {
node.setPresentation(icon, new XValuePresentation() {
@NotNull
@Override
public String getSeparator() {
return " in ";
}
@Nullable
@Override
public String getType() {
return valuePresenter.getType();
}
@Override
public void renderValue(@NotNull XValueTextRenderer renderer) {
valuePresenter.renderValue(renderer);
}
}, hasChildren);
}
@Override
public void setFullValueEvaluator(@NotNull XFullValueEvaluator fullValueEvaluator) {
}
@Override
public boolean isObsolete() {
return false;
}
}, place);
}
}
private static Field findField(ObjectReference reference, Value value) {
for (Field field : reference.referenceType().allFields()) {
if (reference.getValue(field) == value) {
return field;
}
}
return null;
}
}
@@ -20,6 +20,7 @@ import com.intellij.openapi.ui.ex.MultiLineLabel;
import com.intellij.xdebugger.impl.ui.tree.ValueMarkerPresentationDialogBase;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
@@ -35,8 +36,8 @@ public class ObjectMarkupPropertiesDialog extends ValueMarkerPresentationDialogB
private JPanel myAdditionalPropertiesPanel;
private MultiLineLabel myDescriptionLabel;
public ObjectMarkupPropertiesDialog(@NotNull final String defaultText, boolean suggestAdditionalMarkup) {
super(defaultText);
public ObjectMarkupPropertiesDialog(@Nullable Component parent, @NotNull final String defaultText, boolean suggestAdditionalMarkup) {
super(parent, defaultText);
mySuggestAdditionalMarkup = suggestAdditionalMarkup;
myDescriptionLabel.setText("If the value is referenced by a constant field of an abstract class,\n" +
"IDEA could additionally mark all values referenced from this class with the names of referencing fields.");
@@ -19,68 +19,212 @@
*/
package com.intellij.debugger.actions;
import com.intellij.CommonBundle;
import com.intellij.debugger.DebuggerBundle;
import com.intellij.debugger.SourcePosition;
import com.intellij.debugger.engine.DebugProcessImpl;
import com.intellij.debugger.engine.JavaStackFrame;
import com.intellij.debugger.engine.SuspendContextImpl;
import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.jdi.StackFrameProxyImpl;
import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
import com.intellij.debugger.ui.impl.watch.*;
import com.intellij.debugger.settings.DebuggerSettings;
import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl;
import com.intellij.debugger.ui.impl.watch.ThreadDescriptorImpl;
import com.intellij.idea.ActionsBundle;
import com.intellij.openapi.actionSystem.ActionPlaces;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.MessageDialogBuilder;
import com.intellij.openapi.ui.Messages;
import com.intellij.psi.PsiCodeBlock;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiStatement;
import com.intellij.psi.PsiTryStatement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.UIUtil;
import com.intellij.xdebugger.XDebugSession;
import com.intellij.xdebugger.XDebuggerBundle;
import com.intellij.xdebugger.XDebuggerManager;
import com.intellij.xdebugger.evaluation.EvaluationMode;
import com.intellij.xdebugger.evaluation.XDebuggerEvaluator;
import com.intellij.xdebugger.frame.XStackFrame;
import com.intellij.xdebugger.frame.XValue;
import com.intellij.xdebugger.impl.breakpoints.XExpressionImpl;
import com.sun.jdi.InvalidStackFrameException;
import com.sun.jdi.NativeMethodException;
import com.sun.jdi.VMDisconnectedException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
public class PopFrameAction extends DebuggerAction {
public void actionPerformed(AnActionEvent e) {
Project project = e.getData(CommonDataKeys.PROJECT);
StackFrameProxyImpl stackFrame = getStackFrameProxy(e);
final Project project = e.getData(CommonDataKeys.PROJECT);
final JavaStackFrame stackFrame = getStackFrame(e);
if(stackFrame == null) {
return;
}
try {
DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(e.getDataContext());
DebugProcessImpl debugProcess = debuggerContext.getDebugProcess();
final DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(e.getDataContext());
final DebugProcessImpl debugProcess = debuggerContext.getDebugProcess();
if(debugProcess == null) {
return;
}
debugProcess.getManagerThread().schedule(debugProcess.createPopFrameCommand(debuggerContext, stackFrame));
if (!DebuggerSettings.EVALUATE_FINALLY_NEVER.equals(DebuggerSettings.getInstance().EVALUATE_FINALLY_ON_POP_FRAME)) {
List<PsiStatement> statements = getFinallyStatements(debuggerContext.getSourcePosition());
if (!statements.isEmpty()) {
StringBuilder sb = new StringBuilder();
for (PsiStatement statement : statements) {
sb.append("\n").append(statement.getText());
}
if (DebuggerSettings.EVALUATE_FINALLY_ALWAYS.equals(DebuggerSettings.getInstance().EVALUATE_FINALLY_ON_POP_FRAME)) {
evaluateAndPop(project, stackFrame, debuggerContext, debugProcess, sb);
return;
}
else {
int res = MessageDialogBuilder
.yesNoCancel(UIUtil.removeMnemonic(ActionsBundle.actionText(DebuggerActions.POP_FRAME)),
DebuggerBundle.message("warning.finally.block.detected") + sb)
.project(project)
.icon(Messages.getWarningIcon())
.yesText(DebuggerBundle.message("button.execute.finally"))
.noText(DebuggerBundle.message("button.drop.anyway"))
.cancelText(CommonBundle.message("button.cancel"))
.doNotAsk(
new DialogWrapper.DoNotAskOption() {
@Override
public boolean isToBeShown() {
return !DebuggerSettings.EVALUATE_FINALLY_ALWAYS.equals(DebuggerSettings.getInstance().EVALUATE_FINALLY_ON_POP_FRAME) &&
!DebuggerSettings.EVALUATE_FINALLY_NEVER.equals(DebuggerSettings.getInstance().EVALUATE_FINALLY_ON_POP_FRAME);
}
@Override
public void setToBeShown(boolean value, int exitCode) {
if (!value) {
DebuggerSettings.getInstance().EVALUATE_FINALLY_ON_POP_FRAME =
exitCode == Messages.YES ? DebuggerSettings.EVALUATE_FINALLY_ALWAYS : DebuggerSettings.EVALUATE_FINALLY_NEVER;
}
else {
DebuggerSettings.getInstance().EVALUATE_FINALLY_ON_POP_FRAME = DebuggerSettings.EVALUATE_FINALLY_ASK;
}
}
@Override
public boolean canBeHidden() {
return true;
}
@Override
public boolean shouldSaveOptionsOnCancel() {
return false;
}
@NotNull
@Override
public String getDoNotShowMessage() {
return CommonBundle.message("dialog.options.do.not.show");
}
})
.show();
switch (res) {
case Messages.CANCEL:
return;
case Messages.NO:
break;
case Messages.YES: // evaluate finally
evaluateAndPop(project, stackFrame, debuggerContext, debugProcess, sb);
return;
}
}
}
}
debugProcess.getManagerThread().schedule(debugProcess.createPopFrameCommand(debuggerContext, stackFrame.getStackFrameProxy()));
}
catch (NativeMethodException e2){
Messages.showMessageDialog(project, DebuggerBundle.message("error.native.method.exception"), ActionsBundle.actionText(DebuggerActions.POP_FRAME), Messages.getErrorIcon());
Messages.showMessageDialog(project, DebuggerBundle.message("error.native.method.exception"),
UIUtil.removeMnemonic(ActionsBundle.actionText(DebuggerActions.POP_FRAME)), Messages.getErrorIcon());
}
catch (InvalidStackFrameException ignored) {
}
catch(VMDisconnectedException vde) {
catch(VMDisconnectedException ignored) {
}
}
@Nullable
private static StackFrameProxyImpl getStackFrameProxy(AnActionEvent e) {
DebuggerTreeNodeImpl selectedNode = getSelectedNode(e.getDataContext());
if(selectedNode != null) {
NodeDescriptorImpl descriptor = selectedNode.getDescriptor();
if(descriptor instanceof StackFrameDescriptorImpl) {
if(selectedNode.getNextSibling() != null) {
StackFrameDescriptorImpl frameDescriptor = ((StackFrameDescriptorImpl)descriptor);
return frameDescriptor.getFrameProxy();
}
return null;
}
else if(descriptor instanceof ThreadDescriptorImpl || descriptor instanceof ThreadGroupDescriptorImpl) {
return null;
}
private static void evaluateAndPop(final Project project,
final JavaStackFrame stackFrame,
final DebuggerContextImpl debuggerContext,
final DebugProcessImpl debugProcess, StringBuilder sb) {
XDebuggerEvaluator evaluator = stackFrame.getEvaluator();
if (evaluator != null) {
evaluator.evaluate(XExpressionImpl.fromText(sb.toString(), EvaluationMode.CODE_FRAGMENT),
new XDebuggerEvaluator.XEvaluationCallback() {
@Override
public void evaluated(@NotNull XValue result) {
debugProcess.getManagerThread()
.schedule(debugProcess.createPopFrameCommand(debuggerContext, stackFrame.getStackFrameProxy()));
}
@Override
public void errorOccurred(@NotNull final String errorMessage) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
Messages
.showMessageDialog(project, DebuggerBundle.message("error.executing.finally", errorMessage),
UIUtil.removeMnemonic(ActionsBundle.actionText(DebuggerActions.POP_FRAME)),
Messages.getErrorIcon());
}
});
}
}, stackFrame.getSourcePosition());
}
else {
Messages.showMessageDialog(project, XDebuggerBundle.message("xdebugger.evaluate.stack.frame.has.not.evaluator"),
UIUtil.removeMnemonic(ActionsBundle.actionText(DebuggerActions.POP_FRAME)),
Messages.getErrorIcon());
}
}
private static List<PsiStatement> getFinallyStatements(SourcePosition position) {
List<PsiStatement> res = new ArrayList<PsiStatement>();
PsiElement element = position.getFile().findElementAt(position.getOffset());
PsiTryStatement tryStatement = PsiTreeUtil.getParentOfType(element, PsiTryStatement.class);
while (tryStatement != null) {
PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock();
if (finallyBlock != null) {
ContainerUtil.addAll(res, finallyBlock.getStatements());
}
tryStatement = PsiTreeUtil.getParentOfType(tryStatement, PsiTryStatement.class);
}
return res;
}
@Nullable
private static JavaStackFrame getStackFrame(AnActionEvent e) {
//DebuggerTreeNodeImpl selectedNode = getSelectedNode(e.getDataContext());
//if(selectedNode != null) {
// NodeDescriptorImpl descriptor = selectedNode.getDescriptor();
// if(descriptor instanceof StackFrameDescriptorImpl) {
// if(selectedNode.getNextSibling() != null) {
// StackFrameDescriptorImpl frameDescriptor = ((StackFrameDescriptorImpl)descriptor);
// return frameDescriptor.getFrameProxy();
// }
// return null;
// }
// else if(descriptor instanceof ThreadDescriptorImpl || descriptor instanceof ThreadGroupDescriptorImpl) {
// return null;
// }
//}
Project project = e.getProject();
if (project != null) {
@@ -89,18 +233,19 @@ public class PopFrameAction extends DebuggerAction {
XStackFrame frame = session.getCurrentStackFrame();
if (frame instanceof JavaStackFrame) {
StackFrameProxyImpl proxy = ((JavaStackFrame)frame).getStackFrameProxy();
return !proxy.isBottom() ? proxy : null;
return !proxy.isBottom() ? ((JavaStackFrame)frame) : null;
}
}
}
DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(e.getDataContext());
StackFrameProxyImpl frameProxy = debuggerContext.getFrameProxy();
if(frameProxy == null || frameProxy.isBottom()) {
return null;
}
return frameProxy;
//DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(e.getDataContext());
//StackFrameProxyImpl frameProxy = debuggerContext.getFrameProxy();
//
//if(frameProxy == null || frameProxy.isBottom()) {
// return null;
//}
//return frameProxy;
return null;
}
private static boolean isAtBreakpoint(AnActionEvent e) {
@@ -118,10 +263,10 @@ public class PopFrameAction extends DebuggerAction {
public void update(AnActionEvent e) {
boolean enable = false;
StackFrameProxyImpl stackFrameProxy = getStackFrameProxy(e);
JavaStackFrame stackFrame = getStackFrame(e);
if(stackFrameProxy != null && isAtBreakpoint(e)) {
VirtualMachineProxyImpl virtualMachineProxy = stackFrameProxy.getVirtualMachine();
if(stackFrame != null && isAtBreakpoint(e)) {
VirtualMachineProxyImpl virtualMachineProxy = stackFrame.getStackFrameProxy().getVirtualMachine();
enable = virtualMachineProxy.canPopFrames();
}
@@ -1,125 +0,0 @@
/*
* Copyright 2000-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 com.intellij.debugger.actions;
import com.intellij.debugger.DebuggerContext;
import com.intellij.debugger.engine.JavaValue;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
import com.intellij.debugger.ui.impl.watch.NodeManagerImpl;
import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.psi.PsiExpression;
import com.intellij.xdebugger.frame.*;
import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree;
import com.intellij.xdebugger.impl.ui.tree.XInspectDialog;
import com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase;
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.Value;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* @author egor
*/
public class ShowReferringObjectsAction extends XDebuggerTreeActionBase {
private static final long MAX_REFERRING = 100;
@Override
protected void perform(XValueNodeImpl node, @NotNull String nodeName, AnActionEvent e) {
XValue container = node.getValueContainer();
if (container instanceof JavaValue) {
JavaValue javaValue = ((JavaValue)container);
XDebuggerTree tree = XDebuggerTree.getTree(e.getDataContext());
XInspectDialog dialog = new XInspectDialog(tree.getProject(),
tree.getEditorsProvider(),
tree.getSourcePosition(),
nodeName,
new ReferringObjectsValue(javaValue),
tree.getValueMarkers());
dialog.setTitle("Referring objects for " + nodeName);
dialog.show();
}
}
private static class ReferringObjectsValue extends JavaValue {
private ReferringObjectsValue(JavaValue parent,
@NotNull ValueDescriptorImpl valueDescriptor,
@NotNull EvaluationContextImpl evaluationContext,
NodeManagerImpl nodeManager) {
super(parent, valueDescriptor, evaluationContext, nodeManager);
}
public ReferringObjectsValue(JavaValue javaValue) {
super(null, javaValue.getDescriptor(), javaValue.getEvaluationContext(), null);
}
@Override
public boolean canNavigateToSource() {
return false;
}
@Override
public void computeChildren(@NotNull final XCompositeNode node) {
getEvaluationContext().getDebugProcess().getManagerThread().schedule(
new SuspendContextCommandImpl(getEvaluationContext().getSuspendContext()) {
@Override
public Priority getPriority() {
return Priority.NORMAL;
}
@Override
public void contextAction() throws Exception {
final XValueChildrenList children = new XValueChildrenList();
Value value = getDescriptor().getValue();
List<ObjectReference> references = ((ObjectReference)value).referringObjects(MAX_REFERRING);
int i = 1;
for (final ObjectReference reference : references) {
ValueDescriptorImpl descriptor = new ValueDescriptorImpl(getProject(), reference) {
@Override
public Value calcValue(EvaluationContextImpl evaluationContext) throws EvaluateException {
return reference;
}
@Override
public String getName() {
return "Ref";
}
@Override
public String calcValueName() {
return "Ref";
}
@Override
public PsiExpression getDescriptorEvaluation(DebuggerContext context) throws EvaluateException {
return null;
}
};
children.add("Referrer " + i++ , new ReferringObjectsValue(null, descriptor, getEvaluationContext(), null));
}
node.addChildren(children, true);
}
}
);
}
}
}
@@ -38,6 +38,7 @@ import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiMethod;
import com.intellij.util.DocumentUtil;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.Nullable;
@@ -66,7 +67,7 @@ public class ToggleMethodBreakpointAction extends AnAction {
}
final BreakpointManager manager = debugManager.getBreakpointManager();
final PlaceInDocument place = getPlace(e);
if(place != null) {
if(place != null && DocumentUtil.isValidOffset(place.getOffset(), place.getDocument())) {
Breakpoint breakpoint = manager.findBreakpoint(place.getDocument(), place.getOffset(), MethodBreakpoint.CATEGORY);
if(breakpoint == null) {
final int methodLine = place.getDocument().getLineNumber(place.getOffset());
@@ -15,6 +15,7 @@
*/
package com.intellij.debugger.engine;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.debugger.engine.evaluation.TextWithImportsImpl;
import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
@@ -73,10 +74,19 @@ public class JavaDebuggerEvaluator extends XDebuggerEvaluator {
callback.errorOccurred("Context is not available");
return;
}
JavaDebugProcess process = myDebugProcess.getXdebugProcess();
if (process != null) {
callback.evaluated(JavaValue.create(descriptor, evalContext, process.getNodeManager()));
descriptor.setContext(evalContext);
@SuppressWarnings("ThrowableResultOfMethodCallIgnored")
EvaluateException exception = descriptor.getEvaluateException();
if (exception != null) {
callback.errorOccurred(exception.getMessage());
return;
}
JavaDebugProcess process = myDebugProcess.getXdebugProcess();
if (process == null) {
callback.errorOccurred("No debug process");
return;
}
callback.evaluated(JavaValue.create(null, descriptor, evalContext, process.getNodeManager(), true));
}
});
}
@@ -17,7 +17,6 @@ package com.intellij.debugger.engine;
import com.intellij.debugger.DebuggerBundle;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.events.DebuggerCommandImpl;
import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
import com.intellij.debugger.impl.DebuggerUtilsEx;
@@ -46,7 +45,7 @@ public class JavaExecutionStack extends XExecutionStack {
private final DebugProcessImpl myDebugProcess;
private final NodeManagerImpl myNodeManager;
private volatile JavaStackFrame myTopFrame;
private boolean myTopFrameReady = false;
private volatile boolean myTopFrameReady = false;
private final MethodsTracker myTracker = new MethodsTracker();
public JavaExecutionStack(@NotNull ThreadReferenceProxyImpl threadProxy, @NotNull DebugProcessImpl debugProcess, boolean current) {
@@ -99,25 +98,7 @@ public class JavaExecutionStack extends XExecutionStack {
@Nullable
@Override
public JavaStackFrame getTopFrame() {
if (!myTopFrameReady) {
//TODO: remove sync calculation
if (DebuggerManagerThreadImpl.isManagerThread()) {
myTopFrame = calcTopFrame();
}
else {
myDebugProcess.getManagerThread().invokeAndWait(new DebuggerCommandImpl() {
@Override
public Priority getPriority() {
return Priority.HIGH;
}
@Override
protected void action() throws Exception {
myTopFrame = calcTopFrame();
}
});
}
}
assert myTopFrameReady : "Top frame must be already calculated here";
return myTopFrame;
}
@@ -183,8 +164,20 @@ public class JavaExecutionStack extends XExecutionStack {
@Override
public void contextAction() throws Exception {
if (myStackFramesIterator.hasNext()) {
JavaStackFrame frame = new JavaStackFrame(myStackFramesIterator.next(), myDebugProcess, myTracker, myNodeManager);
if (DebuggerSettings.getInstance().SHOW_LIBRARY_STACKFRAMES || (!frame.getDescriptor().isSynthetic() && !frame.getDescriptor().isInLibraryContent())) {
JavaStackFrame frame;
boolean first = myAdded == 0;
if (first && myTopFrameReady) {
frame = myTopFrame;
myStackFramesIterator.next();
}
else {
frame = new JavaStackFrame(myStackFramesIterator.next(), myDebugProcess, myTracker, myNodeManager);
if (first && !myTopFrameReady) {
myTopFrame = frame;
myTopFrameReady = true;
}
}
if (first || DebuggerSettings.getInstance().SHOW_LIBRARY_STACKFRAMES || (!frame.getDescriptor().isSynthetic() && !frame.getDescriptor().isInLibraryContent())) {
if (++myAdded > mySkip) {
myContainer.addStackFrames(Arrays.asList(frame), false);
}
@@ -370,7 +370,7 @@ public class JavaStackFrame extends XStackFrame {
ArgumentValueDescriptorImpl descriptor = myNodeManager.getArgumentValueDescriptor(null, index, value, name);
// setContext is required to calculate correct name
descriptor.setContext(evaluationContext);
return JavaValue.create(descriptor, evaluationContext, myNodeManager);
return JavaValue.create(null, descriptor, evaluationContext, myNodeManager, true);
}
protected void superBuildVariables(final EvaluationContextImpl evaluationContext, XValueChildrenList children) throws EvaluateException {
@@ -15,8 +15,10 @@
*/
package com.intellij.debugger.engine;
import com.intellij.debugger.DebuggerBundle;
import com.intellij.debugger.DebuggerInvocationUtil;
import com.intellij.debugger.SourcePosition;
import com.intellij.debugger.actions.JavaReferringObjectsValue;
import com.intellij.debugger.actions.JumpToObjectAction;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
@@ -66,27 +68,34 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV
private final ValueDescriptorImpl myValueDescriptor;
private final EvaluationContextImpl myEvaluationContext;
private final NodeManagerImpl myNodeManager;
private final boolean myContextSet;
protected JavaValue(JavaValue parent,
@NotNull ValueDescriptorImpl valueDescriptor,
@NotNull EvaluationContextImpl evaluationContext,
NodeManagerImpl nodeManager) {
NodeManagerImpl nodeManager,
boolean contextSet) {
super(valueDescriptor.getName());
myParent = parent;
myValueDescriptor = valueDescriptor;
myEvaluationContext = evaluationContext;
myNodeManager = nodeManager;
myContextSet = contextSet;
}
private static JavaValue create(JavaValue parent, @NotNull ValueDescriptorImpl valueDescriptor, EvaluationContextImpl evaluationContext, NodeManagerImpl nodeManager, boolean init) {
static JavaValue create(JavaValue parent,
@NotNull ValueDescriptorImpl valueDescriptor,
EvaluationContextImpl evaluationContext,
NodeManagerImpl nodeManager,
boolean contextSet) {
DebuggerManagerThreadImpl.assertIsManagerThread();
return new JavaValue(parent, valueDescriptor, evaluationContext, nodeManager);
return new JavaValue(parent, valueDescriptor, evaluationContext, nodeManager, contextSet);
}
static JavaValue create(@NotNull ValueDescriptorImpl valueDescriptor,
EvaluationContextImpl evaluationContext,
NodeManagerImpl nodeManager) {
return create(null, valueDescriptor, evaluationContext, nodeManager, true);
return create(null, valueDescriptor, evaluationContext, nodeManager, false);
}
public JavaValue getParent() {
@@ -113,7 +122,9 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV
@Override
public void threadAction() {
myValueDescriptor.setContext(myEvaluationContext);
if (!myContextSet) {
myValueDescriptor.setContext(myEvaluationContext);
}
myValueDescriptor.updateRepresentation(myEvaluationContext, new DescriptorLabelListener() {
@Override
public void labelChanged() {
@@ -123,7 +134,7 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV
String type = strings[0];
XValuePresentation presentation;
if (myValueDescriptor.isString()) {
presentation = new TypedStringValuePresentation(StringUtil.unquoteString(value), type);
presentation = new TypedStringValuePresentation(value, type);
}
else {
EvaluateException exception = myValueDescriptor.getEvaluateException();
@@ -263,7 +274,7 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV
@Override
public void computeChildren(@NotNull final XCompositeNode node) {
if (myEvaluationContext.getSuspendContext().isResumed()) return;
if (checkContextNotResumed(node)) return;
myEvaluationContext.getDebugProcess().getManagerThread().schedule(new SuspendContextCommandImpl(myEvaluationContext.getSuspendContext()) {
@Override
public Priority getPriority() {
@@ -325,6 +336,14 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV
});
}
protected boolean checkContextNotResumed(XCompositeNode node) {
if (myEvaluationContext.getSuspendContext().isResumed()) {
node.setErrorMessage(DebuggerBundle.message("error.context.has.changed"));
return true;
}
return false;
}
@Override
public void computeSourcePosition(@NotNull final XNavigatable navigatable) {
if (myEvaluationContext.getSuspendContext().isResumed()) return;
@@ -339,15 +358,16 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV
ApplicationManager.getApplication().runReadAction(new Runnable() {
@Override
public void run() {
final boolean nearest = navigatable instanceof XNearestSourcePosition;
if (myValueDescriptor instanceof FieldDescriptorImpl) {
SourcePosition position = ((FieldDescriptorImpl)myValueDescriptor).getSourcePosition(getProject(), getDebuggerContext());
SourcePosition position = ((FieldDescriptorImpl)myValueDescriptor).getSourcePosition(getProject(), getDebuggerContext(), nearest);
if (position != null) {
navigatable.setSourcePosition(DebuggerUtilsEx.toXSourcePosition(position));
}
}
if (myValueDescriptor instanceof LocalVariableDescriptorImpl) {
SourcePosition position =
((LocalVariableDescriptorImpl)myValueDescriptor).getSourcePosition(getProject(), getDebuggerContext());
((LocalVariableDescriptorImpl)myValueDescriptor).getSourcePosition(getProject(), getDebuggerContext(), nearest);
if (position != null) {
navigatable.setSourcePosition(DebuggerUtilsEx.toXSourcePosition(position));
}
@@ -442,4 +462,14 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV
public String getValueText() {
return myValueDescriptor.getValueText();
}
@Nullable
@Override
public XReferrersProvider getReferrersProvider() {
return new XReferrersProvider() {
@Override
public XValue getReferringObjectsValue() {
return new JavaReferringObjectsValue(JavaValue.this, false);
}
};
}
}
@@ -34,6 +34,7 @@ import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.util.ProgressIndicatorListenerAdapter;
import com.intellij.openapi.progress.util.ProgressWindowWithNotification;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.ui.SimpleColoredComponent;
@@ -62,9 +63,12 @@ public class JavaValueModifier extends XValueModifier {
@Override
public String getInitialValueEditorText() {
Value value = myJavaValue.getDescriptor().getValue();
if (value instanceof PrimitiveValue || value instanceof StringReference) {
if (value instanceof PrimitiveValue) {
return myJavaValue.getValueString();
}
else if (value instanceof StringReference) {
return StringUtil.wrapWithDoubleQuote(DebuggerUtils.translateStringValue(myJavaValue.getValueString()));
}
return null;
}
@@ -31,7 +31,7 @@ import com.intellij.debugger.engine.DebuggerUtils;
import com.intellij.debugger.engine.JVMName;
import com.intellij.debugger.engine.JVMNameUtil;
import com.intellij.debugger.engine.evaluation.*;
import com.intellij.debugger.ui.DebuggerEditorImpl;
import com.intellij.debugger.impl.DebuggerUtilsEx;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
@@ -40,6 +40,7 @@ import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiTypesUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.IncorrectOperationException;
import com.sun.jdi.Value;
@@ -65,8 +66,8 @@ public class EvaluatorBuilderImpl implements EvaluatorBuilder {
final Project project = contextElement.getProject();
CodeFragmentFactory factory = DebuggerEditorImpl.findAppropriateFactory(text, contextElement);
PsiCodeFragment codeFragment = new CodeFragmentFactoryContextWrapper(factory).createCodeFragment(text, contextElement, project);
CodeFragmentFactory factory = DebuggerUtilsEx.findAppropriateCodeFragmentFactory(text, contextElement);
PsiCodeFragment codeFragment = factory.createCodeFragment(text, contextElement, project);
if (codeFragment == null) {
throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", text.getText()));
}
@@ -1036,7 +1037,8 @@ public class EvaluatorBuilderImpl implements EvaluatorBuilder {
final PsiType castType = expression.getCastType().getType();
final PsiType operandType = operandExpr.getType();
if (castType != null && operandType != null && !TypeConversionUtil.areTypesConvertible(operandType, castType)) {
// if operand type can not be resolved in current context - leave it for runtime checks
if (castType != null && operandType != null && !TypeConversionUtil.areTypesConvertible(operandType, castType) && PsiUtil.resolveClassInType(operandType) != null) {
throw new EvaluateRuntimeException(
new EvaluateException(JavaErrorMessages.message("inconvertible.type.cast", JavaHighlightUtil.formatType(operandType), JavaHighlightUtil
.formatType(castType)))
@@ -21,21 +21,19 @@
package com.intellij.debugger.engine.evaluation.expression;
import com.intellij.debugger.DebuggerBundle;
import com.intellij.debugger.engine.DebugProcess;
import com.intellij.debugger.engine.DebugProcessImpl;
import com.intellij.debugger.engine.DebuggerUtils;
import com.intellij.debugger.engine.JVMName;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
import com.intellij.debugger.engine.evaluation.EvaluateRuntimeException;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.debugger.engine.evaluation.*;
import com.intellij.debugger.impl.DebuggerUtilsEx;
import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
import com.intellij.openapi.diagnostic.Logger;
import com.sun.jdi.ClassType;
import com.sun.jdi.Method;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.ReferenceType;
import com.intellij.rt.debugger.DefaultMethodInvoker;
import com.sun.jdi.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class MethodEvaluator implements Evaluator {
@@ -149,7 +147,13 @@ public class MethodEvaluator implements Evaluator {
if (requiresSuperObject) {
return debugProcess.invokeInstanceMethod(context, objRef, jdiMethod, args, ObjectReference.INVOKE_NONVIRTUAL);
}
return debugProcess.invokeMethod(context, objRef, jdiMethod, args);
// fix for default methods in interfaces, see IDEA-124066
if (Boolean.valueOf(System.getProperty("debugger.invoke.default")) && jdiMethod.declaringType() instanceof InterfaceType) {
return invokeDefaultMethod(debugProcess, context, objRef, myMethodName);
}
else {
return debugProcess.invokeMethod(context, objRef, jdiMethod, args);
}
}
catch (Exception e) {
if (LOG.isDebugEnabled()) {
@@ -158,4 +162,22 @@ public class MethodEvaluator implements Evaluator {
throw EvaluateExceptionUtil.createEvaluateException(e);
}
}
// only methods without arguments for now
private static Value invokeDefaultMethod(DebugProcess debugProcess, EvaluationContext evaluationContext,
Value obj, String name)
throws EvaluateException, ClassNotLoadedException, InvalidTypeException {
ClassType invokerClass = (ClassType)debugProcess.findClass(
evaluationContext, DefaultMethodInvoker.class.getName(),
evaluationContext.getClassLoader());
if (invokerClass != null) {
List<Method> methods = invokerClass.methodsByName("invoke");
if (!methods.isEmpty()) {
return debugProcess.invokeMethod(evaluationContext, invokerClass, methods.get(0),
Arrays.asList(obj, ((VirtualMachineProxyImpl)debugProcess.getVirtualMachineProxy()).mirrorOf(name)));
}
}
return null;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-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.
@@ -15,13 +15,27 @@
*/
package com.intellij.debugger.impl;
import com.intellij.codeInsight.daemon.impl.IdentifierHighlighterPass;
import com.intellij.debugger.SourcePosition;
import com.intellij.debugger.engine.SuspendContextImpl;
import com.intellij.debugger.engine.SuspendManagerUtil;
import com.intellij.debugger.jdi.StackFrameProxyImpl;
import com.intellij.debugger.ui.impl.watch.ThreadDescriptorImpl;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.Couple;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiUtilBase;
import com.intellij.xdebugger.XDebugSession;
import com.intellij.xdebugger.XSourcePosition;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class DebuggerContextUtil {
public static void setStackFrame(DebuggerStateManager manager, final StackFrameProxyImpl stackFrame) {
ApplicationManager.getApplication().assertIsDispatchThread();
@@ -53,4 +67,43 @@ public class DebuggerContextUtil {
public static DebuggerContextImpl createDebuggerContext(@NotNull DebuggerSession session, SuspendContextImpl suspendContext){
return DebuggerContextImpl.createDebuggerContext(session, suspendContext, suspendContext != null ? suspendContext.getThread() : null, null);
}
public static SourcePosition findNearest(@NotNull DebuggerContextImpl context, @NotNull PsiElement psi, @NotNull PsiFile file) {
final DebuggerSession session = context.getDebuggerSession();
if (session != null) {
try {
final XDebugSession debugSession = session.getXDebugSession();
if (debugSession != null) {
final XSourcePosition position = debugSession.getCurrentPosition();
final Editor editor = PsiUtilBase.findEditor(psi);
if (editor != null && position != null && file.getVirtualFile().equals(position.getFile())) {
final Couple<Collection<TextRange>> usages = IdentifierHighlighterPass.getHighlightUsages(psi, file);
final List<TextRange> ranges = new ArrayList<TextRange>();
ranges.addAll(usages.first);
ranges.addAll(usages.second);
final int breakPointLine = position.getLine();
int bestLine = -1;
boolean hasSameLine = false;
for (TextRange range : ranges) {
final int line = editor.offsetToLogicalPosition(range.getStartOffset()).line;
if (line > bestLine && line < breakPointLine) {
bestLine = line;
} else if (line == breakPointLine) {
hasSameLine = true;
}
}
if (bestLine > 0) {
if (hasSameLine && breakPointLine - bestLine > 4) {
return SourcePosition.createFromLine(file, breakPointLine);
}
return SourcePosition.createFromLine(file, bestLine);
}
}
}
}
catch (Exception ignore) {
}
}
return SourcePosition.createFromOffset(file, psi.getTextOffset());
}
}
@@ -37,6 +37,7 @@ import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.*;
@@ -403,17 +404,25 @@ public abstract class DebuggerUtilsEx extends DebuggerUtils {
public abstract CompletionEditor createEditor(Project project, PsiElement context, @NonNls String recentsId);
@Nullable
public static CodeFragmentFactory getEffectiveCodeFragmentFactory(final PsiElement psiContext) {
final CodeFragmentFactory factory = ApplicationManager.getApplication().runReadAction(new Computable<CodeFragmentFactory>() {
@NotNull
public static CodeFragmentFactory findAppropriateCodeFragmentFactory(final TextWithImports text, final PsiElement context) {
CodeFragmentFactory factory = ApplicationManager.getApplication().runReadAction(new Computable<CodeFragmentFactory>() {
@Override
public CodeFragmentFactory compute() {
final List<CodeFragmentFactory> codeFragmentFactories = getCodeFragmentFactories(psiContext);
// the list always contains at least DefaultCodeFragmentFactory
return codeFragmentFactories.get(0);
final FileType fileType = text.getFileType();
final List<CodeFragmentFactory> factories = getCodeFragmentFactories(context);
if (fileType == null) {
return factories.get(0);
}
for (CodeFragmentFactory factory : factories) {
if (factory.getFileType().equals(fileType)) {
return factory;
}
}
return DefaultCodeFragmentFactory.getInstance();
}
});
return factory != null? new CodeFragmentFactoryContextWrapper(factory) : null;
return new CodeFragmentFactoryContextWrapper(factory);
}
private static class SigReader {
@@ -86,6 +86,10 @@ public class JavaEditorTextProviderImpl implements EditorTextProvider {
public Pair<PsiElement, TextRange> findExpression(PsiElement element, boolean allowMethodCalls) {
PsiElement expression = null;
PsiElement parent = element.getParent();
if (parent instanceof PsiLiteralExpression) {
element = parent;
parent = parent.getParent();
}
if (parent instanceof PsiVariable) {
expression = element;
}
@@ -107,12 +111,7 @@ public class JavaEditorTextProviderImpl implements EditorTextProvider {
else if (parent instanceof PsiThisExpression) {
expression = parent;
}
else if (parent instanceof PsiInstanceOfExpression
|| parent instanceof PsiBinaryExpression
|| parent instanceof PsiPolyadicExpression
|| parent instanceof PsiPrefixExpression
|| parent instanceof PsiConditionalExpression
) {
else if (parent instanceof PsiExpression && !(parent instanceof PsiNewExpression)) {
if (allowMethodCalls || !DebuggerUtils.hasSideEffects(parent)) {
expression = parent;
}
@@ -41,6 +41,7 @@ public class PositionUtil extends ContextUtil {
return ContextUtil.getSourcePosition(context);
}
@Nullable
public static PsiElement getContextElement(final StackFrameContext context) {
if(context instanceof DebuggerContextImpl) return ((DebuggerContextImpl) context).getContextElement();
@@ -53,7 +53,7 @@ public class DebuggerDataViewsConfigurable implements SearchableConfigurable {
private JCheckBox myCbShowObjectId;
private StateRestoringCheckBox myCbShowStaticFinalFields;
private final ArrayRendererConfigurable myArrayRendererConfigurable;
//private final ArrayRendererConfigurable myArrayRendererConfigurable;
private JCheckBox myCbEnableAlternateViews;
private JCheckBox myCbEnableToString;
@@ -66,12 +66,12 @@ public class DebuggerDataViewsConfigurable implements SearchableConfigurable {
public DebuggerDataViewsConfigurable(@Nullable Project project) {
myProject = project;
myArrayRendererConfigurable = new ArrayRendererConfigurable(NodeRendererSettings.getInstance().getArrayRenderer());
//myArrayRendererConfigurable = new ArrayRendererConfigurable(NodeRendererSettings.getInstance().getArrayRenderer());
}
@Override
public void disposeUIResources() {
myArrayRendererConfigurable.disposeUIResources();
//myArrayRendererConfigurable.disposeUIResources();
myToStringFilterEditor = null;
myProject = null;
}
@@ -165,13 +165,14 @@ public class DebuggerDataViewsConfigurable implements SearchableConfigurable {
panel.add(showPanel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 3, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(4, 0, 0, 0), 0, 0));
final JPanel arraysPanel = new JPanel(new BorderLayout(0, UIUtil.DEFAULT_VGAP));
final JComponent arraysComponent = myArrayRendererConfigurable.createComponent();
assert arraysComponent != null;
arraysPanel.add(arraysComponent, BorderLayout.CENTER);
arraysPanel.add(myCbHideNullArrayElements, BorderLayout.SOUTH);
arraysPanel.setBorder(IdeBorderFactory.createTitledBorder("Arrays", true));
panel.add(arraysPanel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 3, 1, 1.0, 0.0, GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
//final JPanel arraysPanel = new JPanel(new BorderLayout(0, UIUtil.DEFAULT_VGAP));
//final JComponent arraysComponent = myArrayRendererConfigurable.createComponent();
//assert arraysComponent != null;
//arraysPanel.add(arraysComponent, BorderLayout.CENTER);
//arraysPanel.add(myCbHideNullArrayElements, BorderLayout.SOUTH);
//arraysPanel.setBorder(IdeBorderFactory.createTitledBorder("Arrays", true));
//panel.add(arraysPanel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 3, 1, 1.0, 0.0, GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
panel.add(myCbHideNullArrayElements, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 3, 1, 1.0, 0.0, GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
panel.add(myCbEnableAlternateViews, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(4, 0, 0, 10), 0, 0));
// starting 4-th row
@@ -209,7 +210,7 @@ public class DebuggerDataViewsConfigurable implements SearchableConfigurable {
myAutoTooltip.save();
myArrayRendererConfigurable.apply();
//myArrayRendererConfigurable.apply();
rendererSettings.fireRenderersChanged();
}
@@ -250,7 +251,7 @@ public class DebuggerDataViewsConfigurable implements SearchableConfigurable {
myRbFromList.setEnabled(toStringEnabled);
myRbAllThatOverride.setEnabled(toStringEnabled);
myArrayRendererConfigurable.reset();
//myArrayRendererConfigurable.reset();
}
@Override
@@ -266,9 +267,9 @@ public class DebuggerDataViewsConfigurable implements SearchableConfigurable {
}
private boolean areDefaultRenderersModified() {
if (myArrayRendererConfigurable.isModified()) {
return true;
}
//if (myArrayRendererConfigurable.isModified()) {
// return true;
//}
final NodeRendererSettings rendererSettings = NodeRendererSettings.getInstance();
@@ -20,6 +20,7 @@ import com.intellij.openapi.components.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.classFilter.ClassFilter;
import com.intellij.util.containers.hash.LinkedHashMap;
import com.intellij.util.xmlb.SkipDefaultValuesSerializationFilters;
@@ -55,6 +56,10 @@ public class DebuggerSettings implements Cloneable, PersistentStateComponent<Ele
@NonNls public static final String RUN_HOTSWAP_NEVER = "RunHotswapNever";
@NonNls public static final String RUN_HOTSWAP_ASK = "RunHotswapAsk";
@NonNls public static final String EVALUATE_FINALLY_ALWAYS = "EvaluateFinallyAlways";
@NonNls public static final String EVALUATE_FINALLY_NEVER = "EvaluateFinallyNever";
@NonNls public static final String EVALUATE_FINALLY_ASK = "EvaluateFinallyAsk";
public boolean TRACING_FILTERS_ENABLED;
public int DEBUGGER_TRANSPORT;
public boolean FORCE_CLASSIC_VM;
@@ -74,6 +79,8 @@ public class DebuggerSettings implements Cloneable, PersistentStateComponent<Ele
public volatile boolean AUTO_VARIABLES_MODE = false;
public volatile boolean SHOW_LIBRARY_STACKFRAMES = true;
public String EVALUATE_FINALLY_ON_POP_FRAME = EVALUATE_FINALLY_ASK;
private ClassFilter[] mySteppingFilters = ClassFilter.EMPTY_ARRAY;
private Map<String, ContentState> myContentStates = new LinkedHashMap<String, ContentState>();
@@ -142,6 +149,7 @@ public class DebuggerSettings implements Cloneable, PersistentStateComponent<Ele
return
TRACING_FILTERS_ENABLED == secondSettings.TRACING_FILTERS_ENABLED &&
DEBUGGER_TRANSPORT == secondSettings.DEBUGGER_TRANSPORT &&
StringUtil.equals(EVALUATE_FINALLY_ON_POP_FRAME, secondSettings.EVALUATE_FINALLY_ON_POP_FRAME) &&
FORCE_CLASSIC_VM == secondSettings.FORCE_CLASSIC_VM &&
DISABLE_JIT == secondSettings.DISABLE_JIT &&
HOTSWAP_IN_BACKGROUND == secondSettings.HOTSWAP_IN_BACKGROUND &&
@@ -22,6 +22,7 @@ import com.intellij.ui.classFilter.ClassFilterEditor;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.border.Border;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
@@ -33,6 +34,9 @@ class DebuggerSteppingConfigurable implements ConfigurableUi<DebuggerSettings> {
private JCheckBox myCbSkipClassLoaders;
private ClassFilterEditor mySteppingFilterEditor;
private JCheckBox myCbSkipSimpleGetters;
private JRadioButton myRbEvaluateFinallyAlways;
private JRadioButton myRbEvaluateFinallyNever;
private JRadioButton myRbEvaluateFinallyAsk;
@Override
public void reset(@NotNull DebuggerSettings settings) {
@@ -45,6 +49,16 @@ class DebuggerSteppingConfigurable implements ConfigurableUi<DebuggerSettings> {
mySteppingFilterEditor.setFilters(settings.getSteppingFilters());
mySteppingFilterEditor.setEnabled(settings.TRACING_FILTERS_ENABLED);
if (DebuggerSettings.EVALUATE_FINALLY_ALWAYS.equals(settings.EVALUATE_FINALLY_ON_POP_FRAME)) {
myRbEvaluateFinallyAlways.setSelected(true);
}
else if (DebuggerSettings.EVALUATE_FINALLY_NEVER.equals(settings.EVALUATE_FINALLY_ON_POP_FRAME)) {
myRbEvaluateFinallyNever.setSelected(true);
}
else {
myRbEvaluateFinallyAsk.setSelected(true);
}
}
@Override
@@ -59,6 +73,16 @@ class DebuggerSteppingConfigurable implements ConfigurableUi<DebuggerSettings> {
settings.SKIP_CLASSLOADERS = myCbSkipClassLoaders.isSelected();
settings.TRACING_FILTERS_ENABLED = myCbStepInfoFiltersEnabled.isSelected();
if (myRbEvaluateFinallyAlways.isSelected()) {
settings.EVALUATE_FINALLY_ON_POP_FRAME = DebuggerSettings.EVALUATE_FINALLY_ALWAYS;
}
else if (myRbEvaluateFinallyNever.isSelected()) {
settings.EVALUATE_FINALLY_ON_POP_FRAME = DebuggerSettings.EVALUATE_FINALLY_NEVER;
}
else {
settings.EVALUATE_FINALLY_ON_POP_FRAME = DebuggerSettings.EVALUATE_FINALLY_ASK;
}
mySteppingFilterEditor.stopEditing();
settings.setSteppingFilters(mySteppingFilterEditor.getFilters());
}
@@ -94,6 +118,33 @@ class DebuggerSteppingConfigurable implements ConfigurableUi<DebuggerSettings> {
mySteppingFilterEditor.setEnabled(myCbStepInfoFiltersEnabled.isSelected());
}
});
myRbEvaluateFinallyAlways = new JRadioButton(DebuggerBundle.message("label.debugger.general.configurable.evaluate.finally.always"));
myRbEvaluateFinallyNever = new JRadioButton(DebuggerBundle.message("label.debugger.general.configurable.evaluate.finally.never"));
myRbEvaluateFinallyAsk = new JRadioButton(DebuggerBundle.message("label.debugger.general.configurable.evaluate.finally.ask"));
int cbLeftOffset = 0;
final Border border = myCbSkipSimpleGetters.getBorder();
if (border != null) {
final Insets insets = border.getBorderInsets(myCbSkipSimpleGetters);
if (insets != null) {
cbLeftOffset = insets.left;
}
}
final ButtonGroup group = new ButtonGroup();
group.add(myRbEvaluateFinallyAlways);
group.add(myRbEvaluateFinallyNever);
group.add(myRbEvaluateFinallyAsk);
final Box box = Box.createHorizontalBox();
box.add(myRbEvaluateFinallyAlways);
box.add(myRbEvaluateFinallyNever);
box.add(myRbEvaluateFinallyAsk);
final JPanel evalFinallyPanel = new JPanel(new BorderLayout());
evalFinallyPanel.add(box, BorderLayout.CENTER);
evalFinallyPanel.add(new JLabel(DebuggerBundle.message("label.debugger.general.configurable.evaluate.finally.on.pop")), BorderLayout.WEST);
panel.add(evalFinallyPanel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(4, cbLeftOffset, 0, 0), 0, 0));
return panel;
}
}
@@ -294,7 +294,7 @@ public abstract class DebuggerEditorImpl extends CompletionEditor{
}
@NotNull
public static CodeFragmentFactory findAppropriateFactory(@NotNull TextWithImports text, @NotNull PsiElement context) {
private static CodeFragmentFactory findAppropriateFactory(@NotNull TextWithImports text, @Nullable PsiElement context) {
for (CodeFragmentFactory factory : DebuggerUtilsEx.getCodeFragmentFactories(context)) {
if (factory.getFileType().equals(text.getFileType())) {
return factory;
@@ -30,7 +30,6 @@ import com.intellij.debugger.ui.impl.ValueNodeDnD;
import com.intellij.debugger.ui.impl.WatchDebuggerTree;
import com.intellij.debugger.ui.impl.WatchPanel;
import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
import com.intellij.debugger.ui.impl.watch.EvaluationDescriptor;
import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.*;
@@ -110,10 +109,10 @@ public abstract class EvaluationDialog extends DialogWrapper {
try {
setOKActionEnabled(false);
NodeDescriptorImpl descriptor = myEvaluationPanel.getWatchTree().addWatch(codeToEvaluate, "result").getDescriptor();
if (descriptor instanceof EvaluationDescriptor) {
final EvaluationDescriptor evalDescriptor = (EvaluationDescriptor)descriptor;
evalDescriptor.setCodeFragmentFactory(myEditor.getCurrentFactory());
}
//if (descriptor instanceof EvaluationDescriptor) {
// final EvaluationDescriptor evalDescriptor = (EvaluationDescriptor)descriptor;
// evalDescriptor.setCodeFragmentFactory(myEditor.getCurrentFactory());
//}
myEvaluationPanel.getWatchTree().rebuild(getDebuggerContext());
descriptor.myIsExpanded = true;
}
@@ -38,7 +38,7 @@ import java.util.Collections;
import java.util.List;
public class HotSwapProgressImpl extends HotSwapProgress{
static final NotificationGroup NOTIFICATION_GROUP = NotificationGroup.toolWindowGroup("HotSwap", ToolWindowId.DEBUG, true);
static final NotificationGroup NOTIFICATION_GROUP = NotificationGroup.toolWindowGroup("HotSwap", ToolWindowId.DEBUG);
TIntObjectHashMap<List<String>> myMessages = new TIntObjectHashMap<List<String>>();
private final ProgressWindow myProgressWindow;
@@ -94,9 +94,8 @@ public class ValueHint extends AbstractValueHint {
return EvaluatorBuilderImpl.getInstance().build(myCurrentExpression, debuggerContext.getSourcePosition());
}
CodeFragmentFactory factory = DebuggerUtilsEx.getEffectiveCodeFragmentFactory(myCurrentExpression);
TextWithImportsImpl textWithImports = new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, myCurrentExpression.getText());
if (factory == null) return null;
CodeFragmentFactory factory = DebuggerUtilsEx.findAppropriateCodeFragmentFactory(textWithImports, myCurrentExpression);
JavaCodeFragment codeFragment = factory.createCodeFragment(textWithImports, myCurrentExpression.getContext(), getProject());
codeFragment.forceResolveScope(GlobalSearchScope.allScope(getProject()));
return factory.getEvaluatorBuilder().build(codeFragment, debuggerContext.getSourcePosition());
@@ -0,0 +1,217 @@
/*
* Copyright 2000-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 com.intellij.debugger.ui.impl.watch;
import com.intellij.debugger.engine.DebugProcess;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluationContext;
import com.intellij.debugger.engine.evaluation.TextWithImports;
import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator;
import com.intellij.debugger.engine.evaluation.expression.Modifier;
import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
import com.sun.jdi.*;
import javax.tools.*;
import java.io.ByteArrayOutputStream;
import java.net.URI;
import java.util.*;
/**
* @author egor
*/
class CompilingEvaluator implements ExpressionEvaluator {
private final TextWithImports myText;
public CompilingEvaluator(TextWithImports text) {
myText = text;
}
@Override
public Value getValue() {
return null;
}
@Override
public Modifier getModifier() {
return null;
}
@Override
public Value evaluate(EvaluationContext context) throws EvaluateException {
try {
DebugProcess process = context.getDebugProcess();
ThreadReference threadReference = context.getSuspendContext().getThread().getThreadReference();
ClassLoaderReference classLoader = getClassLoader(context);
Collection<OutputFileObject> classes = compile();
ClassType mainClass = defineClasses(classes, context, process, threadReference, classLoader);
Method foo = mainClass.methodsByName(GEN_METHOD_NAME).get(0);
return mainClass.invokeMethod(threadReference, foo, Collections.<Value>emptyList() ,ClassType.INVOKE_SINGLE_THREADED);
}
catch (Exception e) {
throw new EvaluateException(e.getMessage());
}
}
private static ClassLoaderReference getClassLoader(EvaluationContext context)
throws EvaluateException, InvocationException, InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException {
// TODO: cache
DebugProcess process = context.getDebugProcess();
ClassType loaderClass = (ClassType)process.findClass(context, "java.net.URLClassLoader", context.getClassLoader());
Method ctorMethod = loaderClass.concreteMethodByName("<init>", "([Ljava/net/URL;)V");
ThreadReference threadReference = context.getSuspendContext().getThread().getThreadReference();
return (ClassLoaderReference)loaderClass.newInstance(threadReference, ctorMethod,
Arrays.asList(createURLArray(context)), ClassType.INVOKE_SINGLE_THREADED);
}
private static ClassType defineClasses(Collection<OutputFileObject> classes,
EvaluationContext context,
DebugProcess process,
ThreadReference threadReference,
ClassLoaderReference classLoader)
throws EvaluateException, InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException, InvocationException {
VirtualMachineProxyImpl proxy = (VirtualMachineProxyImpl)process.getVirtualMachineProxy();
for (OutputFileObject cls : classes) {
Method defineMethod = ((ClassType)classLoader.referenceType()).concreteMethodByName("defineClass", "(Ljava/lang/String;[BII)Ljava/lang/Class;");
byte[] bytes = cls.toByteArray();
ArrayList<Value> args = new ArrayList<Value>();
args.add(proxy.mirrorOf(cls.myOrigName));
args.add(mirrorOf(bytes, context, process));
args.add(proxy.mirrorOf(0));
args.add(proxy.mirrorOf(bytes.length));
classLoader.invokeMethod(threadReference, defineMethod, args, ClassType.INVOKE_SINGLE_THREADED);
}
return (ClassType)process.findClass(context, GEN_CLASS_FULL_NAME, classLoader);
}
private static ArrayReference mirrorOf(byte[] bytes, EvaluationContext context, DebugProcess process)
throws EvaluateException, InvalidTypeException, ClassNotLoadedException {
ArrayType arrayClass = (ArrayType)process.findClass(context, "byte[]", context.getClassLoader());
ArrayReference reference = process.newInstance(arrayClass, bytes.length);
reference.disableCollection();
for (int i = 0; i < bytes.length; i++) {
reference.setValue(i, ((VirtualMachineProxyImpl)process.getVirtualMachineProxy()).mirrorOf(bytes[i]));
}
return reference;
}
private static final String GEN_CLASS_NAME = "Evaluator";
private static final String GEN_CLASS_PACKAGE = "dummy";
private static final String GEN_CLASS_FULL_NAME = GEN_CLASS_PACKAGE + '.' + GEN_CLASS_NAME;
private static final String GEN_METHOD_NAME = "eval";
private static String createClassCode(TextWithImports body) {
StringBuilder text = new StringBuilder();
text.append("package " + GEN_CLASS_PACKAGE + ";");
String imports = body.getImports();
if (!imports.isEmpty()) {
for (String s : imports.split(",")) {
text.append("import " + s + ";");
}
}
String bodyText = body.getText();
if (!bodyText.endsWith(";")) {
bodyText += ';';
}
text.append("public class " + GEN_CLASS_NAME + " { public static Object " + GEN_METHOD_NAME + "() throws Exception {" + bodyText + "}}");
return text.toString();
}
private static ArrayReference createURLArray(EvaluationContext context)
throws EvaluateException, InvocationException, InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException {
DebugProcess process = context.getDebugProcess();
ArrayType arrayType = (ArrayType)process.findClass(context, "java.net.URL[]", context.getClassLoader());
ArrayReference arrayRef = arrayType.newInstance(1);
ClassType classType = (ClassType)process.findClass(context, "java.net.URL", context.getClassLoader());
VirtualMachineProxyImpl proxy = (VirtualMachineProxyImpl)process.getVirtualMachineProxy();
ThreadReference threadReference = context.getSuspendContext().getThread().getThreadReference();
ObjectReference reference = classType.newInstance(threadReference, classType.concreteMethodByName("<init>", "(Ljava/lang/String;)V"),
Arrays.asList(proxy.mirrorOf("file:a")), ClassType.INVOKE_SINGLE_THREADED);
arrayRef.setValues(Arrays.asList(reference));
return arrayRef;
}
///////////////// Compiler stuff
private Collection<OutputFileObject> compile() throws EvaluateException {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
MemoryFileManager manager = new MemoryFileManager(compiler);
DiagnosticCollector<JavaFileObject> diagnostic = new DiagnosticCollector<JavaFileObject>();
if (!compiler.getTask(null, manager, diagnostic, null, null, Arrays
.asList(new SourceFileObject(GEN_CLASS_NAME, JavaFileObject.Kind.SOURCE, createClassCode(myText)))).call()) {
// TODO: show only errors
throw new EvaluateException(diagnostic.getDiagnostics().get(0).getMessage(Locale.getDefault()));
}
return manager.classes;
}
private static URI getUri(String name, JavaFileObject.Kind kind) {
return URI.create("memo:///" + name.replace('.', '/') + kind.extension);
}
private static class SourceFileObject extends SimpleJavaFileObject {
private final String myContent;
SourceFileObject(String name, Kind kind, String content) {
super(getUri(name, kind), kind);
myContent = content;
}
@Override
public CharSequence getCharContent(boolean ignore) {
return myContent;
}
}
private static class OutputFileObject extends SimpleJavaFileObject {
private final ByteArrayOutputStream myStream = new ByteArrayOutputStream();
private final String myOrigName;
OutputFileObject(String name, Kind kind) {
super(getUri(name, kind), kind);
myOrigName = name;
}
byte[] toByteArray() {
return myStream.toByteArray();
}
@Override
public ByteArrayOutputStream openOutputStream() {
return myStream;
}
}
private static class MemoryFileManager extends ForwardingJavaFileManager {
private final Collection<OutputFileObject> classes = new ArrayList<OutputFileObject>();
MemoryFileManager(JavaCompiler compiler) {
super(compiler.getStandardFileManager(null, null, null));
}
@Override
public OutputFileObject getJavaFileForOutput(Location location, String name, JavaFileObject.Kind kind, FileObject source) {
OutputFileObject mc = new OutputFileObject(name, kind);
classes.add(mc);
return mc;
}
}
}
@@ -21,21 +21,26 @@ import com.intellij.debugger.DebuggerInvocationUtil;
import com.intellij.debugger.EvaluatingComputable;
import com.intellij.debugger.engine.ContextUtil;
import com.intellij.debugger.engine.StackFrameContext;
import com.intellij.debugger.engine.evaluation.*;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.debugger.engine.evaluation.TextWithImports;
import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator;
import com.intellij.debugger.engine.evaluation.expression.Modifier;
import com.intellij.debugger.impl.DebuggerUtilsEx;
import com.intellij.debugger.impl.PositionUtil;
import com.intellij.debugger.jdi.StackFrameProxyImpl;
import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.psi.PsiCodeFragment;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiExpressionCodeFragment;
import com.intellij.psi.search.GlobalSearchScope;
import com.sun.jdi.ObjectCollectedException;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.Value;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author lex
@@ -43,7 +48,6 @@ import org.jetbrains.annotations.Nullable;
public abstract class EvaluationDescriptor extends ValueDescriptorImpl{
private Modifier myModifier;
protected TextWithImports myText;
private CodeFragmentFactory myCodeFragmentFactory = null; // used to force specific context, e.g. from evaluation
protected EvaluationDescriptor(TextWithImports text, Project project, Value value) {
super(project, value);
@@ -56,38 +60,35 @@ public abstract class EvaluationDescriptor extends ValueDescriptorImpl{
myText = text;
}
public final void setCodeFragmentFactory(CodeFragmentFactory codeFragmentFactory) {
myCodeFragmentFactory = codeFragmentFactory != null? new CodeFragmentFactoryContextWrapper(codeFragmentFactory) : null;
}
@Nullable
public final CodeFragmentFactory getCodeFragmentFactory() {
return myCodeFragmentFactory;
}
protected final @NotNull CodeFragmentFactory getEffectiveCodeFragmentFactory(final PsiElement psiContext) {
if (myCodeFragmentFactory != null) {
return myCodeFragmentFactory;
}
return DebuggerUtilsEx.getEffectiveCodeFragmentFactory(psiContext);
}
protected abstract EvaluationContextImpl getEvaluationContext (EvaluationContextImpl evaluationContext);
protected abstract PsiCodeFragment getEvaluationCode(StackFrameContext context) throws EvaluateException;
protected PsiCodeFragment createCodeFragment(PsiElement context) {
TextWithImports text = getEvaluationText();
final PsiCodeFragment fragment =
DebuggerUtilsEx.findAppropriateCodeFragmentFactory(text, context).createCodeFragment(text, context, myProject);
fragment.forceResolveScope(GlobalSearchScope.allScope(myProject));
return fragment;
}
public final Value calcValue(final EvaluationContextImpl evaluationContext) throws EvaluateException {
try {
final EvaluationContextImpl thisEvaluationContext = getEvaluationContext(evaluationContext);
final ExpressionEvaluator evaluator = DebuggerInvocationUtil.commitAndRunReadAction(myProject, new EvaluatingComputable<ExpressionEvaluator>() {
public ExpressionEvaluator compute() throws EvaluateException {
final PsiElement psiContext = PositionUtil.getContextElement(evaluationContext);
return getEffectiveCodeFragmentFactory(psiContext).getEvaluatorBuilder().build(getEvaluationCode(thisEvaluationContext),
ContextUtil.getSourcePosition(thisEvaluationContext));
}
});
final ExpressionEvaluator evaluator;
if (Registry.is("debugger.compiling.evaluator")) {
evaluator = new CompilingEvaluator(getEvaluationText());
}
else {
evaluator = DebuggerInvocationUtil.commitAndRunReadAction(myProject, new EvaluatingComputable<ExpressionEvaluator>() {
public ExpressionEvaluator compute() throws EvaluateException {
final PsiElement psiContext = PositionUtil.getContextElement(evaluationContext);
return DebuggerUtilsEx.findAppropriateCodeFragmentFactory(getEvaluationText(), psiContext).getEvaluatorBuilder()
.build(getEvaluationCode(thisEvaluationContext), ContextUtil.getSourcePosition(thisEvaluationContext));
}
});
}
if (!thisEvaluationContext.getDebugProcess().isAttached()) {
throw EvaluateExceptionUtil.PROCESS_EXITED;
@@ -99,7 +100,11 @@ public abstract class EvaluationDescriptor extends ValueDescriptorImpl{
final Value value = evaluator.evaluate(thisEvaluationContext);
if (value instanceof ObjectReference) {
thisEvaluationContext.getSuspendContext().keep(((ObjectReference)value));
ObjectReference objRef = (ObjectReference)value;
if (VirtualMachineProxyImpl.isCollected(objRef)) {
throw EvaluateExceptionUtil.OBJECT_WAS_COLLECTED;
}
thisEvaluationContext.getSuspendContext().keep(objRef);
}
myModifier = evaluator.getModifier();
setLvalue(myModifier != null);
@@ -109,6 +114,9 @@ public abstract class EvaluationDescriptor extends ValueDescriptorImpl{
catch (final EvaluateException ex) {
throw new EvaluateException(ex.getLocalizedMessage(), ex);
}
catch (ObjectCollectedException ex) {
throw EvaluateExceptionUtil.OBJECT_WAS_COLLECTED;
}
}
public String calcValueName() {
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-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.
@@ -26,6 +26,7 @@ import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.impl.DebuggerContextUtil;
import com.intellij.debugger.impl.DebuggerSession;
import com.intellij.debugger.impl.PositionUtil;
import com.intellij.debugger.settings.NodeRendererSettings;
@@ -73,6 +74,12 @@ public class FieldDescriptorImpl extends ValueDescriptorImpl implements FieldDes
@SuppressWarnings({"HardCodedStringLiteral"})
@Nullable
public SourcePosition getSourcePosition(final Project project, final DebuggerContextImpl context) {
return getSourcePosition(project, context, false);
}
@SuppressWarnings({"HardCodedStringLiteral"})
@Nullable
public SourcePosition getSourcePosition(final Project project, final DebuggerContextImpl context, boolean nearest) {
if (context.getFrameProxy() == null) {
return null;
}
@@ -95,6 +102,9 @@ public class FieldDescriptorImpl extends ValueDescriptorImpl implements FieldDes
if (psiVariable == null) {
return null;
}
if (nearest) {
return DebuggerContextUtil.findNearest(context, psiVariable, aClass.getContainingFile());
}
return SourcePosition.createFromOffset(psiVariable.getContainingFile(), psiVariable.getTextOffset());
}
else {
@@ -125,6 +135,9 @@ public class FieldDescriptorImpl extends ValueDescriptorImpl implements FieldDes
aClass = (PsiClass) aClass.getNavigationElement();
for (PsiField field : aClass.getFields()) {
if (fieldName.equals(field.getName())) {
if (nearest) {
return DebuggerContextUtil.findNearest(context, field, aClass.getContainingFile());
}
return SourcePosition.createFromOffset(field.getContainingFile(), field.getTextOffset());
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-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.
@@ -22,6 +22,7 @@ import com.intellij.debugger.engine.DebuggerUtils;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.impl.DebuggerContextUtil;
import com.intellij.debugger.impl.PositionUtil;
import com.intellij.debugger.jdi.LocalVariableProxyImpl;
import com.intellij.debugger.jdi.StackFrameProxyImpl;
@@ -61,6 +62,11 @@ public class LocalVariableDescriptorImpl extends ValueDescriptorImpl implements
@Nullable
public SourcePosition getSourcePosition(final Project project, final DebuggerContextImpl context) {
return getSourcePosition(project, context, false);
}
@Nullable
public SourcePosition getSourcePosition(final Project project, final DebuggerContextImpl context, boolean nearest) {
StackFrameProxyImpl frame = context.getFrameProxy();
if (frame == null) return null;
@@ -77,7 +83,9 @@ public class LocalVariableDescriptorImpl extends ValueDescriptorImpl implements
PsiFile containingFile = psiVariable.getContainingFile();
if(containingFile == null) return null;
if (nearest) {
return DebuggerContextUtil.findNearest(context, psiVariable, containingFile);
}
return SourcePosition.createFromOffset(containingFile, psiVariable.getTextOffset());
}
@@ -21,19 +21,18 @@
package com.intellij.debugger.ui.impl.watch;
import com.intellij.debugger.DebuggerBundle;
import com.intellij.debugger.engine.DebuggerUtils;
import com.intellij.debugger.engine.StackFrameContext;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.debugger.engine.evaluation.TextWithImports;
import com.intellij.debugger.impl.DebuggerUtilsEx;
import com.intellij.debugger.settings.NodeRendererSettings;
import com.intellij.debugger.ui.tree.UserExpressionDescriptor;
import com.intellij.debugger.ui.tree.render.ClassRenderer;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiCodeFragment;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.StringBuilderSpinAllocator;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.Value;
@@ -78,16 +77,13 @@ public class UserExpressionDescriptorImpl extends EvaluationDescriptor implement
if(value instanceof ObjectReference) {
final String typeName = value.type().name();
final PsiClass psiClass = DebuggerUtilsEx.findClass(myTypeName, myProject, context.getDebugProcess().getSearchScope());
final PsiClass psiClass = DebuggerUtils.findClass(myTypeName, myProject, context.getDebugProcess().getSearchScope());
if (psiClass == null) {
throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.invalid.type.name", typeName));
}
final PsiCodeFragment fragment =
getEffectiveCodeFragmentFactory(psiClass).createCodeFragment(getEvaluationText(), psiClass, myProject);
fragment.forceResolveScope(GlobalSearchScope.allScope(myProject));
return fragment;
return createCodeFragment(psiClass);
}
else {
throw EvaluateExceptionUtil.createEvaluateException(
@@ -203,6 +203,7 @@ public abstract class ValueDescriptorImpl extends NodeDescriptorImpl implements
}
catch (EvaluateException e) {
myValueException = e;
setFailed(e);
myValue = getTargetExceptionWithStackTraceFilled(evaluationContext, e);
myIsExpandable = false;
}
@@ -28,8 +28,6 @@ import com.intellij.debugger.impl.PositionUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.psi.PsiCodeFragment;
import com.intellij.psi.PsiElement;
import com.intellij.psi.search.GlobalSearchScope;
import com.sun.jdi.Value;
import org.jetbrains.annotations.Nullable;
@@ -85,9 +83,6 @@ public class WatchItemDescriptor extends EvaluationDescriptor {
}
protected PsiCodeFragment getEvaluationCode(StackFrameContext context) throws EvaluateException {
final PsiElement psiContext = PositionUtil.getContextElement(context);
final PsiCodeFragment fragment = getEffectiveCodeFragmentFactory(psiContext).createCodeFragment(getEvaluationText(), psiContext, myProject);
fragment.forceResolveScope(GlobalSearchScope.allScope(myProject));
return fragment;
return createCodeFragment(PositionUtil.getContextElement(context));
}
}
@@ -1,11 +1,9 @@
package org.jetbrains.java.debugger;
import com.intellij.debugger.engine.evaluation.CodeFragmentFactory;
import com.intellij.debugger.engine.evaluation.CodeFragmentFactoryContextWrapper;
import com.intellij.debugger.engine.evaluation.TextWithImports;
import com.intellij.debugger.engine.evaluation.TextWithImportsImpl;
import com.intellij.debugger.impl.DebuggerUtilsEx;
import com.intellij.debugger.ui.DebuggerEditorImpl;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.lang.Language;
import com.intellij.openapi.editor.Document;
@@ -73,16 +71,15 @@ public class JavaDebuggerEditorsProvider extends XDebuggerEditorsProviderBase {
boolean isPhysical) {
TextWithImports text = TextWithImportsImpl.fromXExpression(expression);
if (text != null && context != null) {
CodeFragmentFactory factory = new CodeFragmentFactoryContextWrapper(DebuggerEditorImpl.findAppropriateFactory(text, context));
CodeFragmentFactory factory = DebuggerUtilsEx.findAppropriateCodeFragmentFactory(text, context);
JavaCodeFragment codeFragment = factory.createPresentationCodeFragment(text, context, project);
codeFragment.forceResolveScope(GlobalSearchScope.allScope(project));
if (context != null) {
final PsiClass contextClass = PsiTreeUtil.getNonStrictParentOfType(context, PsiClass.class);
if (contextClass != null) {
final PsiClassType contextType =
JavaPsiFacade.getInstance(codeFragment.getProject()).getElementFactory().createType(contextClass);
codeFragment.setThisType(contextType);
}
final PsiClass contextClass = PsiTreeUtil.getNonStrictParentOfType(context, PsiClass.class);
if (contextClass != null) {
final PsiClassType contextType =
JavaPsiFacade.getInstance(codeFragment.getProject()).getElementFactory().createType(contextClass);
codeFragment.setThisType(contextType);
}
return codeFragment;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-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.
@@ -128,7 +128,12 @@ public abstract class SourcePosition implements Navigatable{
return true;
}
final PsiElement psiElement = myPsiElement;
return psiElement != null && !psiElement.isValid();
return psiElement != null && !ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
@Override
public Boolean compute() {
return psiElement.isValid();
}
});
}
@Override
@@ -194,7 +199,7 @@ public abstract class SourcePosition implements Navigatable{
@Nullable
protected PsiElement calcPsiElement() {
PsiFile psiFile = getFile();
final PsiFile psiFile = getFile();
int lineNumber = getLine();
if(lineNumber < 0) {
return psiFile;
@@ -207,47 +212,54 @@ public abstract class SourcePosition implements Navigatable{
if (lineNumber >= document.getLineCount()) {
return psiFile;
}
int startOffset = document.getLineStartOffset(lineNumber);
final int startOffset = document.getLineStartOffset(lineNumber);
if(startOffset == -1) {
return null;
}
PsiElement rootElement = psiFile;
return ApplicationManager.getApplication().runReadAction(new Computable<PsiElement>() {
@Override
public PsiElement compute() {
PsiElement rootElement = psiFile;
List<PsiFile> allFiles = psiFile.getViewProvider().getAllFiles();
if (allFiles.size() > 1) { // jsp & gsp
PsiClassOwner owner = ContainerUtil.findInstance(allFiles, PsiClassOwner.class);
if (owner != null) {
PsiClass[] classes = owner.getClasses();
if (classes.length == 1 && classes[0] instanceof SyntheticElement) {
rootElement = classes[0];
List<PsiFile> allFiles = psiFile.getViewProvider().getAllFiles();
if (allFiles.size() > 1) { // jsp & gsp
PsiClassOwner owner = ContainerUtil.findInstance(allFiles, PsiClassOwner.class);
if (owner != null) {
PsiClass[] classes = owner.getClasses();
if (classes.length == 1 && classes[0] instanceof SyntheticElement) {
rootElement = classes[0];
}
}
}
}
}
PsiElement element;
while(true) {
final CharSequence charsSequence = document.getCharsSequence();
for (; startOffset < charsSequence.length(); startOffset++) {
char c = charsSequence.charAt(startOffset);
if (c != ' ' && c != '\t') {
break;
PsiElement element = null;
int offset = startOffset;
while (true) {
final CharSequence charsSequence = document.getCharsSequence();
for (; offset < charsSequence.length(); offset++) {
char c = charsSequence.charAt(offset);
if (c != ' ' && c != '\t') {
break;
}
}
if (offset >= charsSequence.length()) break;
element = rootElement.findElementAt(offset);
if (element instanceof PsiComment) {
offset = element.getTextRange().getEndOffset() + 1;
}
else {
break;
}
}
if (element != null && element.getParent() instanceof PsiForStatement) {
return ((PsiForStatement)element.getParent()).getInitialization();
}
return element;
}
element = rootElement.findElementAt(startOffset);
if(element instanceof PsiComment) {
startOffset = element.getTextRange().getEndOffset() + 1;
}
else{
break;
}
}
if (element != null && element.getParent() instanceof PsiForStatement) {
return ((PsiForStatement)element.getParent()).getInitialization();
}
return element;
});
}
}
@@ -69,10 +69,7 @@ public class DefaultJavaProgramRunner extends JavaPatchableProgramRunner {
}
@Override
protected RunContentDescriptor doExecute(@NotNull final Project project,
@NotNull final RunProfileState state,
final RunContentDescriptor contentToReuse,
@NotNull final ExecutionEnvironment env) throws ExecutionException {
protected RunContentDescriptor doExecute(@NotNull final RunProfileState state, @NotNull final ExecutionEnvironment env) throws ExecutionException {
FileDocumentManager.getInstance().saveAllDocuments();
ExecutionResult executionResult;
@@ -100,11 +97,11 @@ public class DefaultJavaProgramRunner extends JavaPatchableProgramRunner {
onProcessStarted(env.getRunnerSettings(), executionResult);
final RunContentBuilder contentBuilder = new RunContentBuilder(executionResult, env);
Disposer.register(project, contentBuilder);
Disposer.register(env.getProject(), contentBuilder);
if (shouldAddDefaultActions) {
addDefaultActions(contentBuilder);
}
return contentBuilder.showRunContent(contentToReuse);
return contentBuilder.showRunContent(env.getContentToReuse());
}
@Deprecated
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
* Copyright 2000-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.
@@ -27,6 +27,7 @@ import com.intellij.openapi.fileEditor.TextEditor;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Conditions;
import com.intellij.psi.*;
import com.intellij.psi.search.searches.ClassInheritorsSearch;
import com.intellij.psi.util.PsiClassUtil;
@@ -59,7 +60,7 @@ public class InheritorChooser {
final Runnable performRunnable,
final PsiMethod psiMethod,
final PsiClass containingClass) {
return runMethodInAbstractClass(context, performRunnable, psiMethod, containingClass, Condition.TRUE);
return runMethodInAbstractClass(context, performRunnable, psiMethod, containingClass, Conditions.<PsiClass>alwaysTrue());
}
public boolean runMethodInAbstractClass(final ConfigurationContext context,
@@ -246,7 +246,7 @@ public class LibraryOptionsPanel implements Disposable {
onVersionChanged(getPresentableVersion());
}
});
myExistingLibraryComboBox.setRenderer(new ColoredListCellRenderer() {
myExistingLibraryComboBox.setRenderer(new ColoredListCellRenderer(myExistingLibraryComboBox) {
@Override
protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) {
if (value == null) {
@@ -16,10 +16,7 @@
package com.intellij.ide;
import com.intellij.ide.impl.ProjectUtil;
import com.intellij.openapi.components.RoamingType;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.components.StoragePathMacros;
import com.intellij.openapi.components.*;
import com.intellij.openapi.project.Project;
import com.intellij.util.messages.MessageBus;
import org.jetbrains.annotations.NotNull;
@@ -28,19 +25,22 @@ import org.jetbrains.annotations.NotNull;
name = "RecentProjectsManager",
roamingType = RoamingType.DISABLED,
storages = {
@Storage(
file = StoragePathMacros.APP_CONFIG + "/other.xml"
)}
@Storage(file = StoragePathMacros.APP_CONFIG + "/other.xml"),
@Storage(file = StoragePathMacros.APP_CONFIG + "/recentProjects.xml")
},
storageChooser = LastStorageChooserForWrite.class
)
public class RecentProjectsManager extends RecentProjectsManagerBase {
public RecentProjectsManager(MessageBus messageBus) {
super(messageBus);
}
@Override
protected String getProjectPath(@NotNull Project project) {
return project.getPresentableUrl();
}
@Override
protected void doOpenProject(@NotNull String projectPath, Project projectToClose, boolean forceOpenInNewFrame) {
ProjectUtil.openProject(projectPath, projectToClose, forceOpenInNewFrame);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
* Copyright 2000-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.
@@ -24,6 +24,7 @@ import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.fileChooser.FileChooserDialog;
import com.intellij.openapi.fileChooser.FileChooserFactory;
import com.intellij.openapi.module.Module;
@@ -39,7 +40,6 @@ import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.util.Arrays;
import java.util.Collections;
@@ -94,14 +94,7 @@ public class ImportModuleAction extends AnAction {
@Nullable
public static AddModuleWizard selectFileAndCreateWizard(final Project project, Component dialogParent) {
FileChooserDescriptor descriptor = new FileChooserDescriptor(true, true, true, true, false, false) {
FileChooserDescriptor myDelegate = new OpenProjectFileChooserDescriptor(true);
@Override
public Icon getIcon(VirtualFile file) {
Icon icon = myDelegate.getIcon(file);
return icon == null ? super.getIcon(file) : icon;
}
};
FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleLocalFileDescriptor();
descriptor.setHideIgnored(false);
descriptor.setTitle("Select File or Directory to Import");
ProjectImportProvider[] providers = ProjectImportProvider.PROJECT_IMPORT_PROVIDER.getExtensions();
@@ -123,7 +116,7 @@ public class ImportModuleAction extends AnAction {
if (lastLocation != null) {
toSelect = LocalFileSystem.getInstance().refreshAndFindFileByPath(lastLocation);
}
VirtualFile[] files = chooser.choose(toSelect, project);
VirtualFile[] files = chooser.choose(project, toSelect);
if (files.length == 0) {
return null;
}
@@ -1,84 +0,0 @@
/*
* Copyright 2000-2013 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 com.intellij.ide.actions;
import com.intellij.ide.IdeBundle;
import com.intellij.ide.highlighter.ProjectFileType;
import com.intellij.ide.impl.ProjectUtil;
import com.intellij.openapi.actionSystem.ActionPlaces;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileChooser.FileChooser;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileChooser.PathChooserDialog;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.projectImport.ProjectOpenProcessor;
import com.intellij.projectImport.ProjectOpenProcessorBase;
import com.intellij.util.Consumer;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
public class OpenProjectAction extends AnAction implements DumbAware {
public void actionPerformed(AnActionEvent e) {
final FileChooserDescriptor descriptor = new OpenProjectFileChooserDescriptor(true);
descriptor.setTitle(IdeBundle.message("title.open.project"));
final Set<String> extensions = new LinkedHashSet<String>();
extensions.add(ProjectFileType.DOT_DEFAULT_EXTENSION);
final ProjectOpenProcessor[] openProcessors = Extensions.getExtensions(ProjectOpenProcessor.EXTENSION_POINT_NAME);
for (ProjectOpenProcessor openProcessor : openProcessors) {
if (!(openProcessor instanceof ProjectOpenProcessorBase)) continue;
final String[] supportedExtensions = ((ProjectOpenProcessorBase)openProcessor).getSupportedExtensions();
if (supportedExtensions != null) {
Collections.addAll(extensions, supportedExtensions);
}
}
descriptor.setDescription(IdeBundle.message("filter.project.files", StringUtil.join(extensions, ", ")));
VirtualFile userHomeDir = null;
if (SystemInfo.isUnix) {
userHomeDir = VfsUtil.getUserHomeDir();
}
descriptor.putUserData(PathChooserDialog.PREFER_LAST_OVER_EXPLICIT, Boolean.TRUE);
final Project project = CommonDataKeys.PROJECT.getData(e.getDataContext());
FileChooser.chooseFiles(descriptor, project, userHomeDir, new Consumer<List<VirtualFile>>() {
@Override
public void consume(final List<VirtualFile> files) {
if (files.size() == 1) {
ProjectUtil.openOrImport(files.get(0).getPath(), project, false);
}
}
});
}
@Override
public void update(AnActionEvent e) {
super.update(e);
e.getPresentation().setEnabledAndVisible(ActionPlaces.WELCOME_SCREEN.equals(e.getPlace()));
}
}
@@ -37,6 +37,7 @@ import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
public class ShowStructureSettingsAction extends AnAction implements DumbAware {
@Override
public void actionPerformed(AnActionEvent e) {
Project project = CommonDataKeys.PROJECT.getData(e.getDataContext());
if (project == null) {
@@ -56,12 +57,13 @@ public class ShowStructureSettingsAction extends AnAction implements DumbAware {
protected JComponent createSouthPanel() {
JComponent panel = super.createSouthPanel();
assert panel != null;
CustomLineBorder line = new CustomLineBorder(new JBColor(Gray._153, Gray._80), 1, 0, 0, 0);
CustomLineBorder line = new CustomLineBorder(new JBColor(Gray._153.withAlpha(128), Gray._100.withAlpha(128)), 1, 0, 0, 0);
panel.setBorder(new CompoundBorder(line, new EmptyBorder(10, 5, 5, 5)));
return panel;
}
}.show();
} else {
}
else {
ShowSettingsUtil
.getInstance().editConfigurable(project, OptionsEditorDialog.DIMENSION_KEY, ProjectStructureConfigurable.getInstance(project));
}
@@ -25,6 +25,7 @@ import com.intellij.openapi.project.ex.ProjectManagerEx;
import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
public class TemplateProjectStructureAction extends AnAction implements DumbAware {
@Override
public void actionPerformed(final AnActionEvent e) {
Project defaultProject = ProjectManagerEx.getInstanceEx().getDefaultProject();
ShowSettingsUtil.getInstance().editConfigurable(defaultProject, OptionsEditorDialog.DIMENSION_KEY, ProjectStructureConfigurable.getInstance(defaultProject));
@@ -206,7 +206,9 @@ public abstract class ModuleEditor implements Place.Navigator, Disposable {
myEditors.add(new ModuleConfigurableWrapper(moduleConfigurable));
}
for(ModuleConfigurableEP extension : myModule.getExtensions(MODULE_CONFIGURABLES)) {
myEditors.add(new ModuleConfigurableWrapper(extension.createConfigurable()));
if (extension.isConfigurableProvided()) {
myEditors.add(new ModuleConfigurableWrapper(extension.createConfigurable()));
}
}
}
@@ -130,7 +130,8 @@ public class DefaultLibraryRootsComponentDescriptor extends LibraryRootsComponen
public boolean isAccepted(@NotNull VirtualFile rootCandidate, @NotNull ProgressIndicator progressIndicator) {
if (rootCandidate.isDirectory()) {
for (VirtualFile file : rootCandidate.getChildren()) {
if (NATIVE_LIBRARY_EXTENSIONS.contains(file.getExtension())) {
String extension = file.getExtension();
if (extension != null && NATIVE_LIBRARY_EXTENSIONS.contains(extension)) {
return true;
}
}
@@ -29,10 +29,9 @@ import com.intellij.openapi.roots.ui.OrderRootTypeUIFactory;
import javax.swing.*;
public class NativeLibraryOrderRootTypeUIFactory implements OrderRootTypeUIFactory {
@Override
public Icon getIcon() {
return AllIcons.Modules.Library;
return AllIcons.Nodes.NativeLibrariesFolder;
}
@Override
@@ -76,6 +76,9 @@ public class LanguageLevelModuleExtension extends ModuleExtension<LanguageLevelM
//bad value was stored
}
}
else {
myLanguageLevel = null;
}
}
@Override
@@ -20,6 +20,7 @@ import com.intellij.codeInspection.dataFlow.ContractInference;
import com.intellij.codeInspection.dataFlow.MethodContract;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiModifierListOwner;
import com.intellij.psi.util.PsiUtil;
@@ -32,9 +33,17 @@ import java.util.List;
import static com.intellij.codeInspection.dataFlow.ControlFlowAnalyzer.ORG_JETBRAINS_ANNOTATIONS_CONTRACT;
public class InferredAnnotationsManagerImpl extends InferredAnnotationsManager {
@NotNull
private static PsiModifierListOwner preferCompiledElement(@NotNull PsiModifierListOwner element) {
PsiElement original = element.getOriginalElement();
return original instanceof PsiModifierListOwner ? (PsiModifierListOwner)original : element;
}
@Nullable
@Override
public PsiAnnotation findInferredAnnotation(@NotNull PsiModifierListOwner listOwner, @NotNull String annotationFQN) {
listOwner = preferCompiledElement(listOwner);
PsiAnnotation fromBytecode = ProjectBytecodeAnalysis.getInstance(listOwner.getProject()).findInferredAnnotation(listOwner, annotationFQN);
if (fromBytecode != null) {
return fromBytecode;
@@ -57,6 +66,7 @@ public class InferredAnnotationsManagerImpl extends InferredAnnotationsManager {
@NotNull
@Override
public PsiAnnotation[] findInferredAnnotations(@NotNull PsiModifierListOwner listOwner) {
listOwner = preferCompiledElement(listOwner);
List<PsiAnnotation> result = ContainerUtil.newArrayList();
PsiAnnotation[] fromBytecode = ProjectBytecodeAnalysis.getInstance(listOwner.getProject()).findInferredAnnotations(listOwner);
for (PsiAnnotation annotation : fromBytecode) {
@@ -67,6 +67,9 @@ public class RedundantLambdaCodeBlockInspection extends BaseJavaBatchLocalInspec
if (body instanceof PsiCodeBlock) {
PsiExpression psiExpression = getExpression((PsiCodeBlock)body);
if (psiExpression != null) {
if (!expression.isVoidCompatible() && LambdaUtil.isExpressionStatementExpression(psiExpression)) {
return;
}
final PsiElement errorElement;
final PsiElement parent = psiExpression.getParent();
if (parent instanceof PsiReturnStatement) {
@@ -28,6 +28,8 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.Frame;
import java.util.*;
import static com.intellij.codeInspection.bytecodeAnalysis.Direction.*;
class AbstractValues {
static final class ParamValue extends BasicValue {
ParamValue(Type tp) {
@@ -200,6 +202,14 @@ abstract class Analysis<Res> {
public static final int STEPS_LIMIT = 30000;
public static final int EQUATION_SIZE_LIMIT = 30;
protected static final ThreadLocal<State[]> ourPendingStates = new ThreadLocal<State[]>() {
@Override
protected State[] initialValue() {
return new State[STEPS_LIMIT];
}
};
final RichControlFlow richControlFlow;
final Direction direction;
final ControlFlowGraph controlFlow;
@@ -266,10 +276,8 @@ abstract class Analysis<Res> {
}
for (int i = 0; i < args.length; i++) {
BasicValue value;
if (direction instanceof InOut && ((InOut)direction).paramIndex == i) {
value = new AbstractValues.ParamValue(args[i]);
}
else if (direction instanceof In && ((In)direction).paramIndex == i) {
if (direction instanceof InOut && ((InOut)direction).paramIndex == i ||
direction instanceof In && ((In)direction).paramIndex == i) {
value = new AbstractValues.ParamValue(args[i]);
}
else {
@@ -28,6 +28,7 @@ import java.security.NoSuchAlgorithmException;
import java.util.*;
import static com.intellij.codeInspection.bytecodeAnalysis.ProjectBytecodeAnalysis.LOG;
import static com.intellij.codeInspection.bytecodeAnalysis.Direction.*;
/**
* @author lambdamix
@@ -272,31 +273,42 @@ public class BytecodeAnalysisConverter {
return null;
}
private static int mkDirectionKey(Direction dir) {
if (dir instanceof Out) {
static int mkDirectionKey(Direction dir) {
if (dir == Out) {
return 0;
} else if (dir instanceof In) {
}
else if (dir == NullableOut) {
return 1;
}
else if (dir instanceof In) {
In in = (In)dir;
return 8 * in.paramId() + 1;
} else {
// nullity mask is 0/1
return 2 + 8 * in.paramId() + in.nullityMask;
}
else {
InOut inOut = (InOut)dir;
return 8 * inOut.paramId() + 2 + inOut.valueId();
return 4 + 8 * inOut.paramId() + inOut.valueId();
}
}
@NotNull
private static Direction extractDirection(int directionKey) {
if (directionKey == 0) {
return new Out();
return Out;
}
else if (directionKey == 1) {
return NullableOut;
}
else {
directionKey--;
int paramId = directionKey / 8;
int subDirection = directionKey % 8;
if (subDirection == 1) {
return new In(paramId);
if (subDirection <= 2) {
return new In(paramId, subDirection - 1);
}
else {
return new InOut(paramId, Value.values()[subDirection - 2]);
return new InOut(paramId, Value.values()[subDirection - 3]);
}
}
}
@@ -328,14 +340,14 @@ public class BytecodeAnalysisConverter {
* Given `solution` of all dependencies of a method with the `methodKey`, converts this solution into annotations.
*
* @param solution solution of equations
* @param annotations annotations to which corresponding solutions should be added
* @param methodAnnotations annotations to which corresponding solutions should be added
* @param methodKey a primary key of a method being analyzed
* @param arity arity of this method (hint for constructing @Contract annotations)
*/
public static void addMethodAnnotations(@NotNull HashMap<HKey, Value> solution, @NotNull Annotations annotations, @NotNull HKey methodKey, int arity) {
public static void addMethodAnnotations(@NotNull HashMap<HKey, Value> solution, @NotNull MethodAnnotations methodAnnotations, @NotNull HKey methodKey, int arity) {
List<String> clauses = new ArrayList<String>();
HashSet<HKey> notNulls = annotations.notNulls;
HashMap<HKey, String> contracts = annotations.contracts;
HashSet<HKey> notNulls = methodAnnotations.notNulls;
HashMap<HKey, String> contracts = methodAnnotations.contracts;
for (Map.Entry<HKey, Value> entry : solution.entrySet()) {
HKey key = entry.getKey().mkStable();
Value value = entry.getValue();
@@ -343,7 +355,7 @@ public class BytecodeAnalysisConverter {
continue;
}
Direction direction = extractDirection(key.dirKey);
if (value == Value.NotNull && direction instanceof Out && methodKey.equals(key)) {
if (value == Value.NotNull && direction == Out && methodKey.equals(key)) {
notNulls.add(key);
}
else if (direction instanceof InOut) {
@@ -363,27 +375,6 @@ public class BytecodeAnalysisConverter {
}
}
/**
* Converts solutions for equations over parameters into annotations.
*
* @param solution
* @param annotations
*/
public static void addParameterAnnotations(@NotNull HashMap<HKey, Value> solution, @NotNull Annotations annotations) {
HashSet<HKey> notNulls = annotations.notNulls;
for (Map.Entry<HKey, Value> entry : solution.entrySet()) {
HKey key = entry.getKey().mkStable();
Value value = entry.getValue();
if (value == Value.Top || value == Value.Bot) {
continue;
}
Direction direction = extractDirection(key.dirKey);
if (value == Value.NotNull && (direction instanceof In || direction instanceof Out)) {
notNulls.add(key);
}
}
}
private static String contractValueString(@NotNull Value v) {
switch (v) {
case False: return "false";
@@ -40,7 +40,7 @@ public class BytecodeAnalysisIndex extends FileBasedIndexExtension<Bytes, HEquat
private static final ClassDataIndexer INDEXER = new ClassDataIndexer();
private static final HKeyDescriptor KEY_DESCRIPTOR = new HKeyDescriptor();
private static final int ourInternalVersion = 0;
private static final int ourInternalVersion = 2;
private static boolean ourEnabled = SystemProperties.getBooleanProperty("idea.enable.bytecode.contract.inference", true);
@NotNull
@@ -18,8 +18,6 @@ package com.intellij.codeInspection.bytecodeAnalysis;
import com.intellij.codeInspection.bytecodeAnalysis.asm.*;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.util.NotNullLazyValue;
import com.intellij.openapi.util.NullableLazyValue;
import com.intellij.openapi.util.Pair;
import com.intellij.util.indexing.DataIndexer;
import com.intellij.util.indexing.FileContent;
@@ -31,6 +29,7 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException;
import java.security.MessageDigest;
import java.util.*;
import static com.intellij.codeInspection.bytecodeAnalysis.Direction.*;
import static com.intellij.codeInspection.bytecodeAnalysis.ProjectBytecodeAnalysis.LOG;
/**
@@ -42,6 +41,7 @@ public class ClassDataIndexer implements DataIndexer<Bytes, HEquations, FileCont
public static final Final<Key, Value> FINAL_TOP = new Final<Key, Value>(Value.Top);
public static final Final<Key, Value> FINAL_BOT = new Final<Key, Value>(Value.Bot);
public static final Final<Key, Value> FINAL_NOT_NULL = new Final<Key, Value>(Value.NotNull);
public static final Final<Key, Value> FINAL_NULL = new Final<Key, Value>(Value.Null);
private static final List<Equation<Key, Value>> EMPTY_EQUATIONS = Collections.EMPTY_LIST;
@NotNull
@@ -124,27 +124,26 @@ public class ClassDataIndexer implements DataIndexer<Bytes, HEquations, FileCont
final Method method = new Method(className, methodNode.name, methodNode.desc);
final boolean stable = stableClass || (methodNode.access & STABLE_FLAGS) != 0 || "<init>".equals(methodNode.name);
Key primaryKey = new Key(method, new Out(), stable);
Key primaryKey = new Key(method, Out, stable);
if (argumentTypes.length == 0 && !isInterestingResult) {
return Pair.create(primaryKey, EMPTY_EQUATIONS);
}
try {
final ControlFlowGraph graph = ControlFlowGraph.build(className, methodNode, jsr);
if (graph.transitions.length > 0) {
final DFSTree dfs = DFSTree.build(graph.transitions, graph.edgeCount);
boolean complex = !dfs.back.isEmpty();
if (!complex) {
boolean branching = !dfs.back.isEmpty();
if (!branching) {
for (int[] transition : graph.transitions) {
if (transition != null && transition.length > 1) {
complex = true;
branching = true;
break;
}
}
}
if (complex) {
if (branching) {
RichControlFlow richControlFlow = new RichControlFlow(graph, dfs);
if (richControlFlow.reducible()) {
return Pair.create(primaryKey,
@@ -172,15 +171,15 @@ public class ClassDataIndexer implements DataIndexer<Bytes, HEquations, FileCont
}
private List<Equation<Key, Value>> processBranchingMethod(final Method method,
final MethodNode methodNode,
final RichControlFlow richControlFlow,
Type[] argumentTypes,
boolean isReferenceResult,
boolean isInterestingResult,
final boolean stable,
boolean jsr) throws AnalyzerException {
final MethodNode methodNode,
final RichControlFlow richControlFlow,
Type[] argumentTypes,
boolean isReferenceResult,
boolean isInterestingResult,
final boolean stable,
boolean jsr) throws AnalyzerException {
List<Equation<Key, Value>> result = new ArrayList<Equation<Key, Value>>(argumentTypes.length * 3 + 1);
List<Equation<Key, Value>> result = new ArrayList<Equation<Key, Value>>(argumentTypes.length * 4 + 2);
boolean maybeLeakingParameter = isInterestingResult;
for (Type argType : argumentTypes) {
if (ASMUtils.isReferenceType(argType) || (isReferenceResult && ASMUtils.isBooleanType(argType))) {
@@ -191,39 +190,25 @@ public class ClassDataIndexer implements DataIndexer<Bytes, HEquations, FileCont
final LeakingParameters leakingParametersAndFrames =
maybeLeakingParameter ? leakingParametersAndFrames(method, methodNode, argumentTypes, jsr) : null;
boolean[] leakingParameters =
leakingParametersAndFrames != null ? leakingParametersAndFrames.parameters : null;
boolean[] leakingNullableParameters =
leakingParametersAndFrames != null ? leakingParametersAndFrames.nullableParameters : null;
final NullableLazyValue<boolean[]> origins = new NullableLazyValue<boolean[]>() {
@Override
protected boolean[] compute() {
try {
return OriginsAnalysis.resultOrigins(leakingParametersAndFrames.frames, methodNode.instructions, richControlFlow.controlFlow);
}
catch (AnalyzerException e) {
LOG.debug("when processing " + method + " in " + presentableUrl, e);
return null;
}
}
};
final boolean[] origins =
isInterestingResult ?
OriginsAnalysis.resultOrigins(leakingParametersAndFrames.frames, methodNode.instructions, richControlFlow.controlFlow) :
null;
NotNullLazyValue<Equation<Key, Value>> outEquation = new NotNullLazyValue<Equation<Key, Value>>() {
@NotNull
@Override
protected Equation<Key, Value> compute() {
if (origins.getValue() != null) {
try {
return new InOutAnalysis(richControlFlow, new Out(), origins.getValue(), stable).analyze();
}
catch (AnalyzerException ignored) {
}
}
return new Equation<Key, Value>(new Key(method, new Out(), stable), FINAL_TOP);
}
};
Equation<Key, Value> outEquation =
isInterestingResult ?
new InOutAnalysis(richControlFlow, Out, origins, stable).analyze() :
null;
if (isReferenceResult) {
result.add(outEquation.getValue());
result.add(outEquation);
result.add(new Equation<Key, Value>(new Key(method, NullableOut, stable), NullableMethodAnalysis.analyze(methodNode, origins, jsr)));
}
for (int i = 0; i < argumentTypes.length; i++) {
@@ -231,59 +216,57 @@ public class ClassDataIndexer implements DataIndexer<Bytes, HEquations, FileCont
boolean notNullParam = false;
if (isReferenceArg) {
boolean possibleNPE = false;
if (leakingParameters[i]) {
Equation<Key, Value> notNullParamEquation = new NonNullInAnalysis(richControlFlow, new In(i), stable).analyze();
NonNullInAnalysis notNullInAnalysis = new NonNullInAnalysis(richControlFlow, new In(i, In.NOT_NULL), stable);
Equation<Key, Value> notNullParamEquation = notNullInAnalysis.analyze();
possibleNPE = notNullInAnalysis.possibleNPE;
notNullParam = notNullParamEquation.rhs.equals(FINAL_NOT_NULL);
result.add(notNullParamEquation);
}
else {
// parameter is not leaking, so it is definitely NOT @NotNull
result.add(new Equation<Key, Value>(new Key(method, new In(i), stable), FINAL_TOP));
result.add(new Equation<Key, Value>(new Key(method, new In(i, In.NOT_NULL), stable), FINAL_TOP));
}
if (leakingNullableParameters[i]) {
if (notNullParam || possibleNPE) {
result.add(new Equation<Key, Value>(new Key(method, new In(i, In.NULLABLE), stable), FINAL_TOP));
}
else {
result.add(new NullableInAnalysis(richControlFlow, new In(i, In.NULLABLE), stable).analyze());
}
}
else {
result.add(new Equation<Key, Value>(new Key(method, new In(i, In.NULLABLE), stable), FINAL_NULL));
}
}
if (isReferenceArg && isInterestingResult) {
if (leakingParameters[i]) {
if (origins.getValue() != null) {
// result origins analysis was ok
if (!notNullParam) {
// may be null on some branch, running "null->..." analysis
result.add(new InOutAnalysis(richControlFlow, new InOut(i, Value.Null), origins.getValue(), stable).analyze());
}
else {
// @NotNull, so "null->fail"
result.add(new Equation<Key, Value>(new Key(method, new InOut(i, Value.Null), stable), FINAL_BOT));
}
result.add(new InOutAnalysis(richControlFlow, new InOut(i, Value.NotNull), origins.getValue(), stable).analyze());
if (!notNullParam) {
// may be null on some branch, running "null->..." analysis
result.add(new InOutAnalysis(richControlFlow, new InOut(i, Value.Null), origins, stable).analyze());
}
else {
// result origins analysis failed, approximating to Top
result.add(new Equation<Key, Value>(new Key(method, new InOut(i, Value.Null), stable), FINAL_TOP));
result.add(new Equation<Key, Value>(new Key(method, new InOut(i, Value.NotNull), stable), FINAL_TOP));
// @NotNull, so "null->fail"
result.add(new Equation<Key, Value>(new Key(method, new InOut(i, Value.Null), stable), FINAL_BOT));
}
result.add(new InOutAnalysis(richControlFlow, new InOut(i, Value.NotNull), origins, stable).analyze());
}
else {
// parameter is not leaking, so a contract is the same as for the whole method
result.add(new Equation<Key, Value>(new Key(method, new InOut(i, Value.Null), stable), outEquation.getValue().rhs));
result.add(new Equation<Key, Value>(new Key(method, new InOut(i, Value.NotNull), stable), outEquation.getValue().rhs));
result.add(new Equation<Key, Value>(new Key(method, new InOut(i, Value.Null), stable), outEquation.rhs));
result.add(new Equation<Key, Value>(new Key(method, new InOut(i, Value.NotNull), stable), outEquation.rhs));
}
}
if (ASMUtils.isBooleanType(argumentTypes[i]) && isInterestingResult) {
if (leakingParameters[i]) {
if (origins.getValue() != null) {
// result origins analysis was ok
result.add(new InOutAnalysis(richControlFlow, new InOut(i, Value.False), origins.getValue(), stable).analyze());
result.add(new InOutAnalysis(richControlFlow, new InOut(i, Value.True), origins.getValue(), stable).analyze());
}
else {
// result origins analysis failed, approximating to Top
result.add(new Equation<Key, Value>(new Key(method, new InOut(i, Value.False), stable), FINAL_TOP));
result.add(new Equation<Key, Value>(new Key(method, new InOut(i, Value.True), stable), FINAL_TOP));
}
result.add(new InOutAnalysis(richControlFlow, new InOut(i, Value.False), origins, stable).analyze());
result.add(new InOutAnalysis(richControlFlow, new InOut(i, Value.True), origins, stable).analyze());
}
else {
// parameter is not leaking, so a contract is the same as for the whole method
result.add(new Equation<Key, Value>(new Key(method, new InOut(i, Value.False), stable), outEquation.getValue().rhs));
result.add(new Equation<Key, Value>(new Key(method, new InOut(i, Value.True), stable), outEquation.getValue().rhs));
result.add(new Equation<Key, Value>(new Key(method, new InOut(i, Value.False), stable), outEquation.rhs));
result.add(new Equation<Key, Value>(new Key(method, new InOut(i, Value.True), stable), outEquation.rhs));
}
}
}
@@ -291,22 +274,24 @@ public class ClassDataIndexer implements DataIndexer<Bytes, HEquations, FileCont
}
private List<Equation<Key, Value>> processNonBranchingMethod(Method method,
Type[] argumentTypes,
ControlFlowGraph graph,
boolean isReferenceResult,
boolean isBooleanResult,
boolean stable) throws AnalyzerException {
List<Equation<Key, Value>> result = new ArrayList<Equation<Key, Value>>(argumentTypes.length * 3 + 1);
CombinedSingleAnalysis analyzer = new CombinedSingleAnalysis(method, graph);
Type[] argumentTypes,
ControlFlowGraph graph,
boolean isReferenceResult,
boolean isBooleanResult,
boolean stable) throws AnalyzerException {
List<Equation<Key, Value>> result = new ArrayList<Equation<Key, Value>>(argumentTypes.length * 4 + 2);
CombinedAnalysis analyzer = new CombinedAnalysis(method, graph);
analyzer.analyze();
if (isReferenceResult) {
result.add(analyzer.outContractEquation(stable));
result.add(analyzer.nullableResultEquation(stable));
}
for (int i = 0; i < argumentTypes.length; i++) {
Type argType = argumentTypes[i];
boolean isRefArg = ASMUtils.isReferenceType(argType);
if (isRefArg) {
result.add(analyzer.notNullParamEquation(i, stable));
result.add(analyzer.nullableParamEquation(i, stable));
}
if (isRefArg && (isReferenceResult || isBooleanResult)) {
result.add(analyzer.contractEquation(i, Value.Null, stable));
@@ -321,13 +306,14 @@ public class ClassDataIndexer implements DataIndexer<Bytes, HEquations, FileCont
}
private List<Equation<Key, Value>> topEquations(Method method,
Type[] argumentTypes,
boolean isReferenceResult,
boolean isInterestingResult,
boolean stable) {
List<Equation<Key, Value>> result = new ArrayList<Equation<Key, Value>>(argumentTypes.length * 3 + 1);
Type[] argumentTypes,
boolean isReferenceResult,
boolean isInterestingResult,
boolean stable) {
List<Equation<Key, Value>> result = new ArrayList<Equation<Key, Value>>(argumentTypes.length * 4 + 2);
if (isReferenceResult) {
result.add(new Equation<Key, Value>(new Key(method, new Out(), stable), FINAL_TOP));
result.add(new Equation<Key, Value>(new Key(method, Out, stable), FINAL_TOP));
result.add(new Equation<Key, Value>(new Key(method, NullableOut, stable), FINAL_BOT));
}
for (int i = 0; i < argumentTypes.length; i++) {
Type argType = argumentTypes[i];
@@ -335,7 +321,8 @@ public class ClassDataIndexer implements DataIndexer<Bytes, HEquations, FileCont
boolean isBooleanArg = ASMUtils.isBooleanType(argType);
if (isReferenceArg) {
result.add(new Equation<Key, Value>(new Key(method, new In(i), stable), FINAL_TOP));
result.add(new Equation<Key, Value>(new Key(method, new In(i, In.NOT_NULL), stable), FINAL_TOP));
result.add(new Equation<Key, Value>(new Key(method, new In(i, In.NULLABLE), stable), FINAL_TOP));
}
if (isReferenceArg && isInterestingResult) {
result.add(new Equation<Key, Value>(new Key(method, new InOut(i, Value.Null), stable), FINAL_TOP));
@@ -28,34 +28,122 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.BasicInterpreter;
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue;
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import static com.intellij.codeInspection.bytecodeAnalysis.AbstractValues.*;
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
import static com.intellij.codeInspection.bytecodeAnalysis.Direction.*;
import static com.intellij.codeInspection.bytecodeAnalysis.CombinedData.*;
final class CombinedCall extends BasicValue {
final Method method;
final boolean stableCall;
final List<? extends BasicValue> args;
// additional data structures for combined analysis
interface CombinedData {
CombinedCall(Type tp, Method method, boolean stableCall, List<? extends BasicValue> args) {
super(tp);
this.method = method;
this.stableCall = stableCall;
this.args = args;
final class ParamKey {
final Method method;
final int i;
final boolean stable;
ParamKey(Method method, int i, boolean stable) {
this.method = method;
this.i = i;
this.stable = stable;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ParamKey paramKey = (ParamKey)o;
if (i != paramKey.i) return false;
if (stable != paramKey.stable) return false;
if (!method.equals(paramKey.method)) return false;
return true;
}
@Override
public int hashCode() {
int result = method.hashCode();
result = 31 * result + i;
result = 31 * result + (stable ? 1 : 0);
return result;
}
}
// value knowing at which instruction it was created
interface Trackable {
int getOriginInsnIndex();
}
final class TrackableCallValue extends BasicValue implements Trackable {
private final int originInsnIndex;
final Method method;
final List<? extends BasicValue> args;
final boolean stableCall;
final boolean thisCall;
TrackableCallValue(int originInsnIndex, Type tp, Method method, List<? extends BasicValue> args, boolean stableCall, boolean thisCall) {
super(tp);
this.originInsnIndex = originInsnIndex;
this.method = method;
this.args = args;
this.stableCall = stableCall;
this.thisCall = thisCall;
}
@Override
public int getOriginInsnIndex() {
return originInsnIndex;
}
}
final class NthParamValue extends BasicValue {
final int n;
public NthParamValue(Type type, int n) {
super(type);
this.n = n;
}
}
final class TrackableNullValue extends BasicValue implements Trackable {
static final Type NullType = Type.getObjectType("null");
private final int originInsnIndex;
public TrackableNullValue(int originInsnIndex) {
super(NullType);
this.originInsnIndex = originInsnIndex;
}
@Override
public int getOriginInsnIndex() {
return originInsnIndex;
}
}
final class TrackableValue extends BasicValue implements Trackable {
private final int originInsnIndex;
public TrackableValue(int originInsnIndex, Type type) {
super(type);
this.originInsnIndex = originInsnIndex;
}
@Override
public int getOriginInsnIndex() {
return originInsnIndex;
}
}
BasicValue ThisValue = new BasicValue(Type.getObjectType("java/lang/Object"));
}
final class NParamValue extends BasicValue {
final int n;
public NParamValue(Type type, int n) {
super(type);
this.n = n;
}
}
// specialized class for analyzing methods without branching in single pass
final class CombinedAnalysis {
final class CombinedSingleAnalysis {
private final ControlFlowGraph controlFlow;
private final Method method;
private final CombinedInterpreter interpreter;
@@ -63,11 +151,11 @@ final class CombinedSingleAnalysis {
private boolean exception;
private final MethodNode methodNode;
CombinedSingleAnalysis(Method method, ControlFlowGraph controlFlow) {
CombinedAnalysis(Method method, ControlFlowGraph controlFlow) {
this.method = method;
this.controlFlow = controlFlow;
methodNode = controlFlow.methodNode;
interpreter = new CombinedInterpreter(Type.getArgumentTypes(methodNode.desc).length);
interpreter = new CombinedInterpreter(methodNode.instructions, Type.getArgumentTypes(methodNode.desc).length);
}
final void analyze() throws AnalyzerException {
@@ -106,18 +194,44 @@ final class CombinedSingleAnalysis {
}
final Equation<Key, Value> notNullParamEquation(int i, boolean stable) {
final Key key = new Key(method, new In(i), stable);
final Key key = new Key(method, new In(i, In.NOT_NULL), stable);
final Result<Key, Value> result;
if (interpreter.dereferenced[i]) {
if (interpreter.dereferencedParams[i]) {
result = new Final<Key, Value>(Value.NotNull);
}
else {
Set<Key> calls = interpreter.callDerefs[i];
Set<ParamKey> calls = interpreter.parameterFlow[i];
if (calls == null || calls.isEmpty()) {
result = new Final<Key, Value>(Value.Top);
}
else {
result = new Pending<Key, Value>(new SingletonSet<Product<Key, Value>>(new Product<Key, Value>(Value.Top, calls)));
Set<Key> keys = new HashSet<Key>();
for (ParamKey pk: calls) {
keys.add(new Key(pk.method, new In(pk.i, In.NOT_NULL), pk.stable));
}
result = new Pending<Key, Value>(new SingletonSet<Product<Key, Value>>(new Product<Key, Value>(Value.Top, keys)));
}
}
return new Equation<Key, Value>(key, result);
}
final Equation<Key, Value> nullableParamEquation(int i, boolean stable) {
final Key key = new Key(method, new In(i, In.NULLABLE), stable);
final Result<Key, Value> result;
if (interpreter.dereferencedParams[i] || interpreter.notNullableParams[i] || returnValue instanceof NthParamValue && ((NthParamValue)returnValue).n == i) {
result = new Final<Key, Value>(Value.Top);
}
else {
Set<ParamKey> calls = interpreter.parameterFlow[i];
if (calls == null || calls.isEmpty()) {
result = new Final<Key, Value>(Value.Null);
}
else {
Set<Product<Key, Value>> sum = new HashSet<Product<Key, Value>>();
for (ParamKey pk: calls) {
sum.add(new Product<Key, Value>(Value.Top, Collections.singleton(new Key(pk.method, new In(pk.i, In.NULLABLE), pk.stable))));
}
result = new Pending<Key, Value>(sum);
}
}
return new Equation<Key, Value>(key, result);
@@ -126,7 +240,7 @@ final class CombinedSingleAnalysis {
final Equation<Key, Value> contractEquation(int i, Value inValue, boolean stable) {
final Key key = new Key(method, new InOut(i, inValue), stable);
final Result<Key, Value> result;
if (exception || (inValue == Value.Null && interpreter.dereferenced[i])) {
if (exception || (inValue == Value.Null && interpreter.dereferencedParams[i])) {
result = new Final<Key, Value>(Value.Bot);
}
else if (FalseValue == returnValue) {
@@ -135,29 +249,29 @@ final class CombinedSingleAnalysis {
else if (TrueValue == returnValue) {
result = new Final<Key, Value>(Value.True);
}
else if (NullValue == returnValue) {
else if (returnValue instanceof TrackableNullValue) {
result = new Final<Key, Value>(Value.Null);
}
else if (returnValue instanceof NotNullValue) {
else if (returnValue instanceof NotNullValue || ThisValue == returnValue) {
result = new Final<Key, Value>(Value.NotNull);
}
else if (returnValue instanceof NParamValue && ((NParamValue)returnValue).n == i) {
else if (returnValue instanceof NthParamValue && ((NthParamValue)returnValue).n == i) {
result = new Final<Key, Value>(inValue);
}
else if (returnValue instanceof CombinedCall) {
CombinedCall call = (CombinedCall)returnValue;
else if (returnValue instanceof TrackableCallValue) {
TrackableCallValue call = (TrackableCallValue)returnValue;
HashSet<Key> keys = new HashSet<Key>();
for (int argI = 0; argI < call.args.size(); argI++) {
BasicValue arg = call.args.get(argI);
if (arg instanceof NParamValue) {
NParamValue npv = (NParamValue)arg;
if (arg instanceof NthParamValue) {
NthParamValue npv = (NthParamValue)arg;
if (npv.n == i) {
keys.add(new Key(call.method, new InOut(argI, inValue), call.stableCall));
}
}
}
if (ASMUtils.isReferenceType(call.getType())) {
keys.add(new Key(call.method, new Out(), call.stableCall));
keys.add(new Key(call.method, Out, call.stableCall));
}
if (keys.isEmpty()) {
result = new Final<Key, Value>(Value.Top);
@@ -172,7 +286,7 @@ final class CombinedSingleAnalysis {
}
final Equation<Key, Value> outContractEquation(boolean stable) {
final Key key = new Key(method, new Out(), stable);
final Key key = new Key(method, Out, stable);
final Result<Key, Value> result;
if (exception) {
result = new Final<Key, Value>(Value.Bot);
@@ -183,15 +297,15 @@ final class CombinedSingleAnalysis {
else if (TrueValue == returnValue) {
result = new Final<Key, Value>(Value.True);
}
else if (NullValue == returnValue) {
else if (returnValue instanceof TrackableNullValue) {
result = new Final<Key, Value>(Value.Null);
}
else if (returnValue instanceof NotNullValue) {
else if (returnValue instanceof NotNullValue || returnValue == ThisValue) {
result = new Final<Key, Value>(Value.NotNull);
}
else if (returnValue instanceof CombinedCall) {
CombinedCall call = (CombinedCall)returnValue;
Key callKey = new Key(call.method, new Out(), call.stableCall);
else if (returnValue instanceof TrackableCallValue) {
TrackableCallValue call = (TrackableCallValue)returnValue;
Key callKey = new Key(call.method, Out, call.stableCall);
Set<Key> keys = new SingletonSet<Key>(callKey);
result = new Pending<Key, Value>(new SingletonSet<Product<Key, Value>>(new Product<Key, Value>(Value.Top, keys)));
}
@@ -201,6 +315,28 @@ final class CombinedSingleAnalysis {
return new Equation<Key, Value>(key, result);
}
final Equation<Key, Value> nullableResultEquation(boolean stable) {
final Key key = new Key(method, NullableOut, stable);
final Result<Key, Value> result;
if (exception ||
returnValue instanceof Trackable && interpreter.dereferencedValues[((Trackable)returnValue).getOriginInsnIndex()]) {
result = new Final<Key, Value>(Value.Bot);
}
else if (returnValue instanceof TrackableCallValue) {
TrackableCallValue call = (TrackableCallValue)returnValue;
Key callKey = new Key(call.method, NullableOut, call.stableCall || call.thisCall);
Set<Key> keys = new SingletonSet<Key>(callKey);
result = new Pending<Key, Value>(new SingletonSet<Product<Key, Value>>(new Product<Key, Value>(Value.Null, keys)));
}
else if (returnValue instanceof TrackableNullValue) {
result = new Final<Key, Value>(Value.Null);
}
else {
result = new Final<Key, Value>(Value.Bot);
}
return new Equation<Key, Value>(key, result);
}
final Frame<BasicValue> createStartFrame() {
Frame<BasicValue> frame = new Frame<BasicValue>(methodNode.maxLocals, methodNode.maxStack);
Type returnType = Type.getReturnType(methodNode.desc);
@@ -210,10 +346,10 @@ final class CombinedSingleAnalysis {
Type[] args = Type.getArgumentTypes(methodNode.desc);
int local = 0;
if ((methodNode.access & Opcodes.ACC_STATIC) == 0) {
frame.setLocal(local++, new AbstractValues.NotNullValue(Type.getObjectType(controlFlow.className)));
frame.setLocal(local++, ThisValue);
}
for (int i = 0; i < args.length; i++) {
BasicValue value = new NParamValue(args[i], i);
BasicValue value = new NthParamValue(args[i], i);
frame.setLocal(local++, value);
if (args[i].getSize() == 2) {
frame.setLocal(local++, BasicValue.UNINITIALIZED_VALUE);
@@ -227,23 +363,47 @@ final class CombinedSingleAnalysis {
}
final class CombinedInterpreter extends BasicInterpreter {
final boolean[] dereferenced;
final Set<Key>[] callDerefs;
// Parameters dereferenced during execution of a method, tracked by parameter's indices.
// Dereferenced parameters are @NotNull.
final boolean[] dereferencedParams;
// Parameters, that are written to something or passed to an interface methods.
// This parameters cannot be @Nullable.
final boolean[] notNullableParams;
// parameterFlow(i) for i-th parameter stores a set parameter positions it is passed to
// parameter is @NotNull if any of its usages are @NotNull
final Set<ParamKey>[] parameterFlow;
CombinedInterpreter(int arity) {
dereferenced = new boolean[arity];
callDerefs = new Set[arity];
// Trackable values that were dereferenced during execution of a method
// Values are are identified by `origin` index
final boolean[] dereferencedValues;
private final InsnList insns;
CombinedInterpreter(InsnList insns, int arity) {
dereferencedParams = new boolean[arity];
notNullableParams = new boolean[arity];
parameterFlow = new Set[arity];
this.insns = insns;
dereferencedValues = new boolean[insns.size()];
}
private int insnIndex(AbstractInsnNode insn) {
return insns.indexOf(insn);
}
private static BasicValue track(int origin, BasicValue basicValue) {
return basicValue == null ? null : new TrackableValue(origin, basicValue.getType());
}
@Override
public BasicValue newOperation(AbstractInsnNode insn) throws AnalyzerException {
int origin = insnIndex(insn);
switch (insn.getOpcode()) {
case ICONST_0:
return FalseValue;
case ICONST_1:
return TrueValue;
case ACONST_NULL:
return NullValue;
return new TrackableNullValue(origin);
case LDC:
Object cst = ((LdcInsnNode)insn).cst;
if (cst instanceof Type) {
@@ -266,22 +426,26 @@ final class CombinedInterpreter extends BasicInterpreter {
return new NotNullValue(Type.getObjectType(((TypeInsnNode)insn).desc));
default:
}
return super.newOperation(insn);
return track(origin, super.newOperation(insn));
}
@Override
public BasicValue unaryOperation(AbstractInsnNode insn, BasicValue value) throws AnalyzerException {
int origin = insnIndex(insn);
switch (insn.getOpcode()) {
case GETFIELD:
case ARRAYLENGTH:
case MONITORENTER:
if (value instanceof NParamValue) {
dereferenced[((NParamValue)value).n] = true;
if (value instanceof NthParamValue) {
dereferencedParams[((NthParamValue)value).n] = true;
}
return super.unaryOperation(insn, value);
if (value instanceof Trackable) {
dereferencedValues[((Trackable)value).getOriginInsnIndex()] = true;
}
return track(origin, super.unaryOperation(insn, value));
case CHECKCAST:
if (value instanceof NParamValue) {
return new NParamValue(Type.getObjectType(((TypeInsnNode)insn).desc), ((NParamValue)value).n);
if (value instanceof NthParamValue) {
return new NthParamValue(Type.getObjectType(((TypeInsnNode)insn).desc), ((NthParamValue)value).n);
}
break;
case NEWARRAY:
@@ -289,12 +453,23 @@ final class CombinedInterpreter extends BasicInterpreter {
return new NotNullValue(super.unaryOperation(insn, value).getType());
default:
}
return super.unaryOperation(insn, value);
return track(origin, super.unaryOperation(insn, value));
}
@Override
public BasicValue binaryOperation(AbstractInsnNode insn, BasicValue value1, BasicValue value2) throws AnalyzerException {
switch (insn.getOpcode()) {
case PUTFIELD:
if (value1 instanceof NthParamValue) {
dereferencedParams[((NthParamValue)value1).n] = true;
}
if (value1 instanceof Trackable) {
dereferencedValues[((Trackable)value1).getOriginInsnIndex()] = true;
}
if (value2 instanceof NthParamValue) {
notNullableParams[((NthParamValue)value2).n] = true;
}
break;
case IALOAD:
case LALOAD:
case FALOAD:
@@ -303,14 +478,16 @@ final class CombinedInterpreter extends BasicInterpreter {
case BALOAD:
case CALOAD:
case SALOAD:
case PUTFIELD:
if (value1 instanceof NParamValue) {
dereferenced[((NParamValue)value1).n] = true;
if (value1 instanceof NthParamValue) {
dereferencedParams[((NthParamValue)value1).n] = true;
}
if (value1 instanceof Trackable) {
dereferencedValues[((Trackable)value1).getOriginInsnIndex()] = true;
}
break;
default:
}
return super.binaryOperation(insn, value1, value2);
return track(insnIndex(insn), super.binaryOperation(insn, value1, value2));
}
@Override
@@ -321,33 +498,51 @@ final class CombinedInterpreter extends BasicInterpreter {
case LASTORE:
case FASTORE:
case DASTORE:
case AASTORE:
case BASTORE:
case CASTORE:
case SASTORE:
if (value1 instanceof NParamValue) {
dereferenced[((NParamValue)value1).n] = true;
if (value1 instanceof NthParamValue) {
dereferencedParams[((NthParamValue)value1).n] = true;
}
if (value1 instanceof Trackable) {
dereferencedValues[((Trackable)value1).getOriginInsnIndex()] = true;
}
break;
case AASTORE:
if (value1 instanceof NthParamValue) {
dereferencedParams[((NthParamValue)value1).n] = true;
}
if (value1 instanceof Trackable) {
dereferencedValues[((Trackable)value1).getOriginInsnIndex()] = true;
}
if (value3 instanceof NthParamValue) {
notNullableParams[((NthParamValue)value3).n] = true;
}
break;
default:
}
return super.ternaryOperation(insn, value1, value2, value3);
return null;
}
@Override
public BasicValue naryOperation(AbstractInsnNode insn, List<? extends BasicValue> values) throws AnalyzerException {
int opCode = insn.getOpcode();
int shift = opCode == INVOKESTATIC ? 0 : 1;
int origin = insnIndex(insn);
switch (opCode) {
case INVOKESPECIAL:
case INVOKEINTERFACE:
case INVOKEVIRTUAL:
if (values.get(0) instanceof NParamValue) {
dereferenced[((NParamValue)values.get(0)).n] = true;
BasicValue receiver = values.get(0);
if (receiver instanceof NthParamValue) {
dereferencedParams[((NthParamValue)receiver).n] = true;
}
if (receiver instanceof Trackable) {
dereferencedValues[((Trackable)receiver).getOriginInsnIndex()] = true;
}
default:
}
switch (opCode) {
case INVOKESTATIC:
case INVOKESPECIAL:
@@ -359,26 +554,31 @@ final class CombinedInterpreter extends BasicInterpreter {
Type retType = Type.getReturnType(mNode.desc);
for (int i = shift; i < values.size(); i++) {
if (values.get(i) instanceof NParamValue) {
if (opCode != INVOKEINTERFACE) {
int n = ((NParamValue)values.get(i)).n;
Set<Key> npKeys = callDerefs[n];
if (values.get(i) instanceof NthParamValue) {
int n = ((NthParamValue)values.get(i)).n;
if (opCode == INVOKEINTERFACE) {
notNullableParams[n] = true;
}
else {
Set<ParamKey> npKeys = parameterFlow[n];
if (npKeys == null) {
npKeys = new HashSet<Key>();
callDerefs[n] = npKeys;
npKeys = new HashSet<ParamKey>();
parameterFlow[n] = npKeys;
}
npKeys.add(new Key(method, new In(i - shift), stable));
npKeys.add(new ParamKey(method, i - shift, stable));
}
}
}
BasicValue receiver = null;
if (shift == 1) {
values.remove(0);
receiver = values.remove(0);
}
return new CombinedCall(retType, method, stable, values);
boolean thisCall = (opCode == INVOKEINTERFACE || opCode == INVOKEVIRTUAL) && receiver == ThisValue;
return new TrackableCallValue(origin, retType, method, values, stable, thisCall);
case MULTIANEWARRAY:
return new NotNullValue(super.naryOperation(insn, values).getType());
default:
}
return super.naryOperation(insn, values);
return track(origin, super.naryOperation(insn, values));
}
}
@@ -34,20 +34,14 @@ import java.util.Set;
import static com.intellij.codeInspection.bytecodeAnalysis.AbstractValues.*;
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
import static com.intellij.codeInspection.bytecodeAnalysis.Direction.*;
class InOutAnalysis extends Analysis<Result<Key, Value>> {
private static final ThreadLocal<State[]> ourPending = new ThreadLocal<State[]>() {
@Override
protected State[] initialValue() {
return new State[Analysis.STEPS_LIMIT];
}
};
static final ResultUtil<Key, Value> resultUtil =
new ResultUtil<Key, Value>(new ELattice<Value>(Value.Bot, Value.Top));
final private State[] pending = ourPending.get();
final private State[] pending = ourPendingStates.get();
private final InOutInterpreter interpreter;
private final Value inValue;
private final int generalizeShift;
@@ -403,7 +397,7 @@ class InOutInterpreter extends BasicInterpreter {
}
default:
}
return super.ternaryOperation(insn, value1, value2, value3);
return null;
}
@Override
@@ -443,7 +437,7 @@ class InOutInterpreter extends BasicInterpreter {
}
}
if (isRefRetType) {
keys.add(new Key(method, new Out(), stable));
keys.add(new Key(method, Out, stable));
}
if (!keys.isEmpty()) {
return new CallResultValue(retType, keys);
@@ -451,7 +445,7 @@ class InOutInterpreter extends BasicInterpreter {
}
else if (isRefRetType) {
HashSet<Key> keys = new HashSet<Key>();
keys.add(new Key(method, new Out(), stable));
keys.add(new Key(method, Out, stable));
return new CallResultValue(retType, keys);
}
}
@@ -53,136 +53,108 @@ enum Value {
}
interface Direction {
static final int OUT_DIRECTION = 0;
static final int IN_DIRECTION = 1;
static final int INOUT_DIRECTION = 2;
int directionId();
int paramId();
int valueId();
}
final class In implements Direction {
static final int NOT_NULL = 0;
static final int NULLABLE = 1;
final int paramIndex;
final int nullityMask;
final class In implements Direction {
final int paramIndex;
In(int paramIndex, int nullityMask) {
this.paramIndex = paramIndex;
this.nullityMask = nullityMask;
}
In(int paramIndex) {
this.paramIndex = paramIndex;
@Override
public String toString() {
return "In " + paramIndex;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
In in = (In)o;
if (paramIndex != in.paramIndex) return false;
if (nullityMask != in.nullityMask) return false;
return true;
}
@Override
public int hashCode() {
return 31 * paramIndex + nullityMask;
}
public int paramId() {
return paramIndex;
}
}
@Override
public String toString() {
return "In " + paramIndex;
final class InOut implements Direction {
final int paramIndex;
final Value inValue;
InOut(int paramIndex, Value inValue) {
this.paramIndex = paramIndex;
this.inValue = inValue;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
InOut inOut = (InOut)o;
if (paramIndex != inOut.paramIndex) return false;
if (inValue != inOut.inValue) return false;
return true;
}
@Override
public int hashCode() {
int result = paramIndex;
result = 31 * result + inValue.ordinal();
return result;
}
@Override
public String toString() {
return "InOut " + paramIndex + " " + inValue.toString();
}
public int paramId() {
return paramIndex;
}
public int valueId() {
return inValue.ordinal();
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
In in = (In) o;
if (paramIndex != in.paramIndex) return false;
return true;
}
Direction Out = new Direction() {
@Override
public String toString() {
return "Out";
}
@Override
public int hashCode() {
return paramIndex;
}
@Override
public int hashCode() {
return -1;
}
};
@Override
public int directionId() {
return IN_DIRECTION;
}
Direction NullableOut = new Direction() {
@Override
public String toString() {
return "NullableOut";
}
@Override
public int paramId() {
return paramIndex;
}
@Override
public int valueId() {
return 0;
}
}
final class InOut implements Direction {
final int paramIndex;
final Value inValue;
InOut(int paramIndex, Value inValue) {
this.paramIndex = paramIndex;
this.inValue = inValue;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
InOut inOut = (InOut) o;
if (paramIndex != inOut.paramIndex) return false;
if (inValue != inOut.inValue) return false;
return true;
}
@Override
public int hashCode() {
int result = paramIndex;
result = 31 * result + inValue.ordinal();
return result;
}
@Override
public String toString() {
return "InOut " + paramIndex + " " + inValue.toString();
}
@Override
public int directionId() {
return INOUT_DIRECTION;
}
@Override
public int paramId() {
return paramIndex;
}
@Override
public int valueId() {
return inValue.ordinal();
}
}
final class Out implements Direction {
@Override
public String toString() {
return "Out";
}
@Override
public int hashCode() {
return 1;
}
@Override
public boolean equals(Object obj) {
return obj instanceof Out;
}
@Override
public int directionId() {
return OUT_DIRECTION;
}
@Override
public int paramId() {
return 0;
}
@Override
public int valueId() {
return 0;
}
@Override
public int hashCode() {
return -2;
}
};
}
final class Key {
@@ -219,10 +191,6 @@ final class Key {
@Override
public String toString() {
return "" + method + ' ' + direction + ' ' + stable;
return method + " " + direction + " " + stable;
}
}
class LimitReachedException extends RuntimeException {
}
@@ -63,6 +63,10 @@ final class HKey {
return stable ? this : new HKey(key, dirKey, true);
}
HKey mkUnstable() {
return stable ? new HKey(key, dirKey, false) : this;
}
public HKey mkBase() {
return dirKey == 0 ? this : new HKey(key, 0, stable);
}
@@ -0,0 +1,423 @@
/*
* Copyright 2000-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 com.intellij.codeInspection.bytecodeAnalysis;
import com.intellij.codeInspection.bytecodeAnalysis.asm.AnalyzerExt;
import com.intellij.codeInspection.bytecodeAnalysis.asm.InterpreterExt;
import com.intellij.codeInspection.bytecodeAnalysis.asm.LiteAnalyzerExt;
import org.jetbrains.org.objectweb.asm.Opcodes;
import org.jetbrains.org.objectweb.asm.Type;
import org.jetbrains.org.objectweb.asm.tree.*;
import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException;
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicInterpreter;
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue;
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static com.intellij.codeInspection.bytecodeAnalysis.NullableMethodAnalysisData.*;
interface NullableMethodAnalysisData {
Type NullType = Type.getObjectType("null");
Type ThisType = Type.getObjectType("this");
Type CallType = Type.getObjectType("/Call");
final class LabeledNull extends BasicValue {
final int origins;
public LabeledNull(int origins) {
super(NullType);
this.origins = origins;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
LabeledNull that = (LabeledNull)o;
return origins == that.origins;
}
@Override
public int hashCode() {
return origins;
}
}
final class Calls extends BasicValue {
final int mergedLabels;
public Calls(int mergedLabels) {
super(CallType);
this.mergedLabels = mergedLabels;
}
@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
Calls calls = (Calls)o;
return mergedLabels == calls.mergedLabels;
}
@Override
public int hashCode() {
return mergedLabels;
}
}
final class Constraint {
final static Constraint EMPTY = new Constraint(0, 0);
final int calls;
final int nulls;
public Constraint(int calls, int nulls) {
this.calls = calls;
this.nulls = nulls;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Constraint that = (Constraint)o;
if (calls != that.calls) return false;
if (nulls != that.nulls) return false;
return true;
}
@Override
public int hashCode() {
int result = calls;
result = 31 * result + nulls;
return result;
}
}
BasicValue ThisValue = new BasicValue(ThisType);
}
class NullableMethodAnalysis {
static Result<Key, Value> FinalNull = new Final<Key, Value>(Value.Null);
static Result<Key, Value> FinalBot = new Final<Key, Value>(Value.Bot);
static BasicValue lNull = new LabeledNull(0);
static Result<Key, Value> analyze(MethodNode methodNode, boolean[] origins, boolean jsr) throws AnalyzerException {
InsnList insns = methodNode.instructions;
Constraint[] data = new Constraint[insns.size()];
int[] originsMapping = mapOrigins(origins);
NullableMethodInterpreter interpreter = new NullableMethodInterpreter(insns, origins, originsMapping);
Frame<BasicValue>[] frames =
jsr ?
new AnalyzerExt<BasicValue, Constraint, NullableMethodInterpreter>(interpreter, data, Constraint.EMPTY).analyze("this", methodNode) :
new LiteAnalyzerExt<BasicValue, Constraint, NullableMethodInterpreter>(interpreter, data, Constraint.EMPTY).analyze("this", methodNode);
BasicValue result = BasicValue.REFERENCE_VALUE;
for (int i = 0; i < frames.length; i++) {
Frame<BasicValue> frame = frames[i];
if (frame != null && insns.get(i).getOpcode() == Opcodes.ARETURN) {
BasicValue stackTop = frame.pop();
result = combine(result, stackTop, data[i]);
}
}
if (result instanceof LabeledNull) {
return FinalNull;
}
if (result instanceof Calls) {
Calls calls = ((Calls)result);
int mergedMappedLabels = calls.mergedLabels;
if (mergedMappedLabels != 0) {
Set<Product<Key, Value>> sum = new HashSet<Product<Key, Value>>();
Key[] createdKeys = interpreter.keys;
for (int origin = 0; origin < originsMapping.length; origin++) {
int mappedOrigin = originsMapping[origin];
Key createdKey = createdKeys[origin];
if (createdKey != null && (mergedMappedLabels & (1 << mappedOrigin)) != 0) {
sum.add(new Product<Key, Value>(Value.Null, Collections.singleton(createdKey)));
}
}
if (!sum.isEmpty()) {
return new Pending<Key, Value>(sum);
}
}
}
return FinalBot;
}
private static int[] mapOrigins(boolean[] origins) {
int[] originsMapping = new int[origins.length];
int mapped = 0;
for (int i = 0; i < origins.length; i++) {
originsMapping[i] = origins[i] ? mapped++ : -1;
}
return originsMapping;
}
static BasicValue combine(BasicValue v1, BasicValue v2, Constraint constraint) {
if (v1 instanceof LabeledNull) {
return lNull;
}
else if (v2 instanceof LabeledNull) {
int v2Origins = ((LabeledNull)v2).origins;
int constraintOrigins = constraint.nulls;
int intersect = v2Origins & constraintOrigins;
return intersect == v2Origins ? v1 : lNull;
}
else if (v1 instanceof Calls) {
if (v2 instanceof Calls) {
Calls calls1 = (Calls)v1;
Calls calls2 = (Calls)v2;
int labels2 = calls2.mergedLabels;
int aliveLabels2 = labels2 - (labels2 & constraint.calls);
return new Calls(calls1.mergedLabels | aliveLabels2);
} else {
return v1;
}
}
else if (v2 instanceof Calls) {
Calls calls2 = (Calls)v2;
int labels2 = calls2.mergedLabels;
int aliveLabels2 = labels2 - (labels2 & constraint.calls);
return new Calls(aliveLabels2);
}
return BasicValue.REFERENCE_VALUE;
}
}
class NullableMethodInterpreter extends BasicInterpreter implements InterpreterExt<Constraint> {
final InsnList insns;
final boolean[] origins;
private final int[] originsMapping;
final Key[] keys;
Constraint constraint = null;
int delta = 0;
int nullsDelta = 0;
int notNullInsn = -1;
int notNullCall = 0;
int notNullNull = 0;
NullableMethodInterpreter(InsnList insns, boolean[] origins, int[] originsMapping) {
this.insns = insns;
this.origins = origins;
this.originsMapping = originsMapping;
keys = new Key[originsMapping.length];
}
@Override
public BasicValue newValue(Type type) {
return ThisType.equals(type) ? ThisValue : super.newValue(type);
}
@Override
public BasicValue newOperation(AbstractInsnNode insn) throws AnalyzerException {
if (insn.getOpcode() == Opcodes.ACONST_NULL) {
int insnIndex = insns.indexOf(insn);
if (origins[insnIndex]) {
return new LabeledNull(1 << originsMapping[insnIndex]);
}
}
return super.newOperation(insn);
}
@Override
public BasicValue unaryOperation(AbstractInsnNode insn, BasicValue value) throws AnalyzerException {
switch (insn.getOpcode()) {
case GETFIELD:
case ARRAYLENGTH:
case MONITORENTER:
if (value instanceof Calls) {
delta = ((Calls)value).mergedLabels;
}
break;
case IFNULL:
if (value instanceof Calls) {
notNullInsn = insns.indexOf(insn) + 1;
notNullCall = ((Calls)value).mergedLabels;
}
else if (value instanceof LabeledNull) {
notNullInsn = insns.indexOf(insn) + 1;
notNullNull = ((LabeledNull)value).origins;
}
break;
case IFNONNULL:
if (value instanceof Calls) {
notNullInsn = insns.indexOf(((JumpInsnNode)insn).label);
notNullCall = ((Calls)value).mergedLabels;
}
else if (value instanceof LabeledNull) {
notNullInsn = insns.indexOf(((JumpInsnNode)insn).label);
notNullNull = ((LabeledNull)value).origins;
}
break;
default:
}
return super.unaryOperation(insn, value);
}
@Override
public BasicValue binaryOperation(AbstractInsnNode insn, BasicValue value1, BasicValue value2) throws AnalyzerException {
switch (insn.getOpcode()) {
case PUTFIELD:
case IALOAD:
case LALOAD:
case FALOAD:
case DALOAD:
case AALOAD:
case BALOAD:
case CALOAD:
case SALOAD:
if (value1 instanceof Calls) {
delta = ((Calls)value1).mergedLabels;
}
if (value1 instanceof LabeledNull){
nullsDelta = ((LabeledNull)value1).origins;
}
break;
default:
}
return super.binaryOperation(insn, value1, value2);
}
@Override
public BasicValue ternaryOperation(AbstractInsnNode insn, BasicValue value1, BasicValue value2, BasicValue value3)
throws AnalyzerException {
if (value1 instanceof Calls) {
delta = ((Calls)value1).mergedLabels;
}
if (value1 instanceof LabeledNull){
nullsDelta = ((LabeledNull)value1).origins;
}
return null;
}
@Override
public BasicValue naryOperation(AbstractInsnNode insn, List<? extends BasicValue> values) throws AnalyzerException {
int opCode = insn.getOpcode();
switch (opCode) {
case INVOKESPECIAL:
case INVOKEINTERFACE:
case INVOKEVIRTUAL:
BasicValue receiver = values.get(0);
if (receiver instanceof Calls) {
delta = ((Calls)receiver).mergedLabels;
}
if (receiver instanceof LabeledNull){
nullsDelta = ((LabeledNull)receiver).origins;
}
break;
default:
}
switch (opCode) {
case INVOKESTATIC:
case INVOKESPECIAL:
case INVOKEVIRTUAL:
int insnIndex = insns.indexOf(insn);
if (origins[insnIndex]) {
boolean stable = (opCode == INVOKESTATIC) ||
(opCode == INVOKESPECIAL) ||
(values.get(0) == ThisValue);
MethodInsnNode mNode = ((MethodInsnNode)insn);
Method method = new Method(mNode.owner, mNode.name, mNode.desc);
int label = 1 << originsMapping[insnIndex];
if (keys[insnIndex] == null) {
keys[insnIndex] = new Key(method, Direction.NullableOut, stable);
}
return new Calls(label);
}
break;
default:
}
return super.naryOperation(insn, values);
}
@Override
public BasicValue merge(BasicValue v1, BasicValue v2) {
if (v1 instanceof LabeledNull) {
if (v2 instanceof LabeledNull) {
return new LabeledNull(((LabeledNull)v1).origins | ((LabeledNull)v2).origins);
}
else {
return v1;
}
}
else if (v2 instanceof LabeledNull) {
return v2;
}
else if (v1 instanceof Calls) {
if (v2 instanceof Calls) {
Calls calls1 = (Calls)v1;
Calls calls2 = (Calls)v2;
return new Calls(calls1.mergedLabels | calls2.mergedLabels);
}
else {
return v1;
}
}
else if (v2 instanceof Calls) {
return v2;
}
return super.merge(v1, v2);
}
// ---------- InterpreterExt<Constraint> --------------
@Override
public void init(Constraint previous) {
constraint = previous;
delta = 0;
nullsDelta = 0;
notNullInsn = -1;
notNullCall = 0;
notNullNull = 0;
}
@Override
public Constraint getAfterData(int insn) {
Constraint afterData = mkAfterData();
if (notNullInsn == insn) {
return new Constraint(afterData.calls | notNullCall, afterData.nulls | notNullNull);
}
return afterData;
}
private Constraint mkAfterData() {
if (delta == 0 && nullsDelta == 0 && notNullInsn == -1) {
return constraint;
}
return new Constraint(constraint.calls | delta, constraint.nulls | nullsDelta);
}
@Override
public Constraint merge(Constraint data1, Constraint data2) {
if (data1.equals(data2)) {
return data1;
} else {
return new Constraint(data1.calls | data2.calls, data1.nulls | data2.nulls);
}
}
}
@@ -37,6 +37,7 @@ import static com.intellij.codeInspection.bytecodeAnalysis.AbstractValues.Instan
import static com.intellij.codeInspection.bytecodeAnalysis.AbstractValues.ParamValue;
import static com.intellij.codeInspection.bytecodeAnalysis.PResults.*;
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
import static com.intellij.codeInspection.bytecodeAnalysis.Direction.*;
abstract class PResults {
// SoP = sum of products
@@ -107,6 +108,18 @@ abstract class PResults {
}
}
static PResult combineNullable(PResult r1, PResult r2) throws AnalyzerException {
if (Identity == r1) return r2;
if (Identity == r2) return r1;
if (Return == r1) return r2;
if (Return == r2) return r1;
if (NPE == r1) return NPE;
if (NPE == r2) return NPE;
ConditionalNPE cnpe1 = (ConditionalNPE) r1;
ConditionalNPE cnpe2 = (ConditionalNPE) r2;
return new ConditionalNPE(join(cnpe1.sop, cnpe2.sop));
}
static PResult join(PResult r1, PResult r2) throws AnalyzerException {
if (Identity == r1) return r2;
if (Identity == r2) return r1;
@@ -169,9 +182,12 @@ class NonNullInAnalysis extends Analysis<PResult> {
final private PendingAction[] pending = ourPending.get();
private final NonNullInInterpreter interpreter = new NonNullInInterpreter();
private final NotNullInterpreter interpreter = new NotNullInterpreter();
private PResult[] results;
// Flag saying that at some branch NPE was found. Used later as an evidence that this param is *NOT* @Nullable (optimization).
boolean possibleNPE;
protected NonNullInAnalysis(RichControlFlow richControlFlow, Direction direction, boolean stable) {
super(richControlFlow, direction, stable);
results = ourResults.get();
@@ -289,6 +305,7 @@ class NonNullInAnalysis extends Analysis<PResult> {
if (subResult == NPE) {
results[stateIndex] = NPE;
possibleNPE = true;
addComputed(insnIndex, state);
return;
}
@@ -314,6 +331,7 @@ class NonNullInAnalysis extends Analysis<PResult> {
if (opcode == ATHROW) {
if (taken) {
results[stateIndex] = NPE;
possibleNPE = true;
} else {
results[stateIndex] = Identity;
}
@@ -399,13 +417,216 @@ class NonNullInAnalysis extends Analysis<PResult> {
}
}
class NonNullInInterpreter extends BasicInterpreter {
class NullableInAnalysis extends Analysis<PResult> {
final private State[] pending = ourPendingStates.get();
private final NullableInterpreter interpreter = new NullableInterpreter();
protected NullableInAnalysis(RichControlFlow richControlFlow, Direction direction, boolean stable) {
super(richControlFlow, direction, stable);
}
@NotNull
Equation<Key, Value> mkEquation(PResult result) {
if (NPE == result) {
return new Equation<Key, Value>(aKey, new Final<Key, Value>(Value.Top));
}
if (Identity == result || Return == result) {
return new Equation<Key, Value>(aKey, new Final<Key, Value>(Value.Null));
}
else {
ConditionalNPE condNpe = (ConditionalNPE) result;
Set<Product<Key, Value>> components = new HashSet<Product<Key, Value>>();
for (Set<Key> prod : condNpe.sop) {
components.add(new Product<Key, Value>(Value.Top, prod));
}
return new Equation<Key, Value>(aKey, new Pending<Key, Value>(components));
}
}
private int id = 0;
private Frame<BasicValue> nextFrame = null;
private PResult myResult = Identity;
private PResult subResult = Identity;
private boolean top = false;
@NotNull
protected Equation<Key, Value> analyze() throws AnalyzerException {
pendingPush(createStartState());
int steps = 0;
while (pendingTop > 0 && earlyResult == null) {
steps ++;
if (steps >= STEPS_LIMIT) {
throw new AnalyzerException(null, "limit is reached, steps: " + steps + " in method " + method);
}
State state = pending[--pendingTop];
int insnIndex = state.conf.insnIndex;
Conf conf = state.conf;
List<Conf> history = state.history;
boolean fold = false;
if (dfsTree.loopEnters[insnIndex]) {
for (Conf prev : history) {
if (AbstractValues.isInstance(conf, prev)) {
fold = true;
break;
}
}
}
if (fold) {
addComputed(insnIndex, state);
}
else {
State baseState = null;
List<State> thisComputed = computed[insnIndex];
if (thisComputed != null) {
for (State prevState : thisComputed) {
if (stateEquiv(state, prevState)) {
baseState = prevState;
break;
}
}
}
if (baseState == null) {
processState(state);
}
}
}
if (earlyResult != null) {
return mkEquation(earlyResult);
} else {
return mkEquation(myResult);
}
}
private void processState(State state) throws AnalyzerException {
Conf conf = state.conf;
int insnIndex = conf.insnIndex;
List<Conf> history = state.history;
boolean taken = state.taken;
Frame<BasicValue> frame = conf.frame;
AbstractInsnNode insnNode = methodNode.instructions.get(insnIndex);
List<Conf> nextHistory = dfsTree.loopEnters[insnIndex] ? append(history, conf) : history;
addComputed(insnIndex, state);
execute(frame, insnNode);
if (subResult == NPE || top) {
earlyResult = NPE;
return;
}
if (subResult instanceof ConditionalNPE) {
myResult = combineNullable(myResult, subResult);
}
int opcode = insnNode.getOpcode();
switch (opcode) {
case ARETURN:
if (popValue(frame) instanceof ParamValue) {
earlyResult = NPE;
}
return;
case IRETURN:
case LRETURN:
case FRETURN:
case DRETURN:
case RETURN:
return;
default:
}
if (opcode == ATHROW) {
if (taken) {
earlyResult = NPE;
}
return;
}
if (opcode == IFNONNULL && popValue(frame) instanceof ParamValue) {
int nextInsnIndex = insnIndex + 1;
pendingPush(new State(++id, new Conf(nextInsnIndex, nextFrame), nextHistory, true, false));
return;
}
if (opcode == IFNULL && popValue(frame) instanceof ParamValue) {
int nextInsnIndex = methodNode.instructions.indexOf(((JumpInsnNode)insnNode).label);
pendingPush(new State(++id, new Conf(nextInsnIndex, nextFrame), nextHistory, true, false));
return;
}
if (opcode == IFEQ && popValue(frame) == InstanceOfCheckValue) {
int nextInsnIndex = methodNode.instructions.indexOf(((JumpInsnNode)insnNode).label);
pendingPush(new State(++id, new Conf(nextInsnIndex, nextFrame), nextHistory, true, false));
return;
}
if (opcode == IFNE && popValue(frame) == InstanceOfCheckValue) {
int nextInsnIndex = insnIndex + 1;
pendingPush(new State(++id, new Conf(nextInsnIndex, nextFrame), nextHistory, true, false));
return;
}
// general case
for (int nextInsnIndex : controlFlow.transitions[insnIndex]) {
Frame<BasicValue> nextFrame1 = nextFrame;
if (controlFlow.errors[nextInsnIndex] && controlFlow.errorTransitions.contains(new Edge(insnIndex, nextInsnIndex))) {
nextFrame1 = new Frame<BasicValue>(frame);
nextFrame1.clearStack();
nextFrame1.push(ASMUtils.THROWABLE_VALUE);
}
pendingPush(new State(++id, new Conf(nextInsnIndex, nextFrame1), nextHistory, taken, false));
}
}
private int pendingTop = 0;
private void pendingPush(State state) throws AnalyzerException {
if (pendingTop >= STEPS_LIMIT) {
throw new AnalyzerException(null, "limit is reached in method " + method);
}
pending[pendingTop++] = state;
}
private void execute(Frame<BasicValue> frame, AbstractInsnNode insnNode) throws AnalyzerException {
switch (insnNode.getType()) {
case AbstractInsnNode.LABEL:
case AbstractInsnNode.LINE:
case AbstractInsnNode.FRAME:
nextFrame = frame;
subResult = Identity;
top = false;
break;
default:
nextFrame = new Frame<BasicValue>(frame);
interpreter.reset();
nextFrame.execute(insnNode, interpreter);
subResult = interpreter.getSubResult();
top = interpreter.top;
}
}
}
abstract class NullityInterpreter extends BasicInterpreter {
boolean top = false;
final boolean nullableAnalysis;
final int nullityMask;
private PResult subResult = Identity;
NullityInterpreter(boolean nullableAnalysis, int nullityMask) {
this.nullableAnalysis = nullableAnalysis;
this.nullityMask = nullityMask;
}
abstract PResult combine(PResult res1, PResult res2) throws AnalyzerException;
public PResult getSubResult() {
return subResult;
}
void reset() {
subResult = Identity;
top = false;
}
@Override
@@ -445,10 +666,17 @@ class NonNullInInterpreter extends BasicInterpreter {
case BALOAD:
case CALOAD:
case SALOAD:
if (value1 instanceof ParamValue) {
subResult = NPE;
}
break;
case PUTFIELD:
if (value1 instanceof ParamValue) {
subResult = NPE;
}
if (nullableAnalysis && value2 instanceof ParamValue) {
subResult = NPE;
}
break;
default:
}
@@ -462,16 +690,24 @@ class NonNullInInterpreter extends BasicInterpreter {
case LASTORE:
case FASTORE:
case DASTORE:
case AASTORE:
case BASTORE:
case CASTORE:
case SASTORE:
if (value1 instanceof ParamValue) {
subResult = NPE;
}
break;
case AASTORE:
if (value1 instanceof ParamValue) {
subResult = NPE;
}
if (nullableAnalysis && value3 instanceof ParamValue) {
subResult = NPE;
}
break;
default:
}
return super.ternaryOperation(insn, value1, value2, value3);
return null;
}
@Override
@@ -483,19 +719,54 @@ class NonNullInInterpreter extends BasicInterpreter {
subResult = NPE;
}
switch (opcode) {
case INVOKEINTERFACE:
if (nullableAnalysis) {
for (int i = shift; i < values.size(); i++) {
if (values.get(i) instanceof ParamValue) {
top = true;
return super.naryOperation(insn, values);
}
}
}
break;
case INVOKESTATIC:
case INVOKESPECIAL:
case INVOKEVIRTUAL:
boolean stable = opcode == INVOKESTATIC || opcode == INVOKESPECIAL;
MethodInsnNode methodNode = (MethodInsnNode) insn;
Method method = new Method(methodNode.owner, methodNode.name, methodNode.desc);
for (int i = shift; i < values.size(); i++) {
if (values.get(i) instanceof ParamValue) {
Method method = new Method(methodNode.owner, methodNode.name, methodNode.desc);
subResult = meet(subResult, new ConditionalNPE(new Key(method, new In(i - shift), stable)));
subResult = combine(subResult, new ConditionalNPE(new Key(method, new In(i - shift, nullityMask), stable)));
}
}
break;
default:
}
return super.naryOperation(insn, values);
}
}
class NotNullInterpreter extends NullityInterpreter {
NotNullInterpreter() {
super(false, In.NOT_NULL);
}
@Override
PResult combine(PResult res1, PResult res2) throws AnalyzerException {
return meet(res1, res2);
}
}
class NullableInterpreter extends NullityInterpreter {
NullableInterpreter() {
super(true, In.NULLABLE);
}
@Override
PResult combine(PResult res1, PResult res2) throws AnalyzerException {
return join(res1, res2);
}
}
@@ -23,6 +23,7 @@ import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.ModificationTracker;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.ProjectScope;
@@ -37,10 +38,9 @@ import org.jetbrains.annotations.Nullable;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.*;
import static com.intellij.codeInspection.bytecodeAnalysis.Direction.*;
/**
* @author lambdamix
@@ -48,8 +48,10 @@ import java.util.List;
public class ProjectBytecodeAnalysis {
public static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.bytecodeAnalysis");
public static final Key<Boolean> INFERRED_ANNOTATION = Key.create("INFERRED_ANNOTATION");
public static final String NULLABLE_METHOD_TRANSITIVITY = "java.annotations.inference.nullable.method.transitivity";
public static final int EQUATIONS_LIMIT = 1000;
private final Project myProject;
private final boolean nullableMethodTransitivity;
public static ProjectBytecodeAnalysis getInstance(@NotNull Project project) {
return ServiceManager.getService(project, ProjectBytecodeAnalysis.class);
@@ -57,6 +59,7 @@ public class ProjectBytecodeAnalysis {
public ProjectBytecodeAnalysis(Project project) {
myProject = project;
nullableMethodTransitivity = Registry.is(NULLABLE_METHOD_TRANSITIVITY);
}
@Nullable
@@ -64,7 +67,7 @@ public class ProjectBytecodeAnalysis {
if (!(listOwner instanceof PsiCompiledElement)) {
return null;
}
if (annotationFQN.equals(AnnotationUtil.NOT_NULL) || annotationFQN.equals(ControlFlowAnalyzer.ORG_JETBRAINS_ANNOTATIONS_CONTRACT)) {
if (annotationFQN.equals(AnnotationUtil.NOT_NULL) || annotationFQN.equals(AnnotationUtil.NULLABLE) || annotationFQN.equals(ControlFlowAnalyzer.ORG_JETBRAINS_ANNOTATIONS_CONTRACT)) {
PsiAnnotation[] annotations = findInferredAnnotations(listOwner);
for (PsiAnnotation annotation : annotations) {
if (annotationFQN.equals(annotation.getQualifiedName())) {
@@ -96,34 +99,57 @@ public class ProjectBytecodeAnalysis {
private PsiAnnotation[] collectInferredAnnotations(PsiModifierListOwner listOwner) {
try {
MessageDigest md = BytecodeAnalysisConverter.getMessageDigest();
HKey ownerKey = getKey(listOwner, md);
if (ownerKey == null) {
HKey primaryKey = getKey(listOwner, md);
if (primaryKey == null) {
return PsiAnnotation.EMPTY_ARRAY;
}
ArrayList<HKey> allKeys = contractKeys(listOwner, ownerKey);
Annotations annotations = loadAnnotations(listOwner, ownerKey, allKeys);
boolean notNull = annotations.notNulls.contains(ownerKey);
String contractValue = annotations.contracts.get(ownerKey);
if (notNull && contractValue != null) {
return new PsiAnnotation[]{
getNotNullAnnotation(),
createAnnotationFromText("@" + ControlFlowAnalyzer.ORG_JETBRAINS_ANNOTATIONS_CONTRACT + "(" + contractValue + ")")
};
}
else if (notNull) {
return new PsiAnnotation[]{
getNotNullAnnotation()
};
}
else if (contractValue != null) {
return new PsiAnnotation[]{
createAnnotationFromText("@" + ControlFlowAnalyzer.ORG_JETBRAINS_ANNOTATIONS_CONTRACT + "(" + contractValue + ")")
};
}
else {
return PsiAnnotation.EMPTY_ARRAY;
if (listOwner instanceof PsiMethod) {
ArrayList<HKey> allKeys = contractKeys((PsiMethod)listOwner, primaryKey);
MethodAnnotations methodAnnotations = loadMethodAnnotations((PsiMethod)listOwner, primaryKey, allKeys);
boolean notNull = methodAnnotations.notNulls.contains(primaryKey);
boolean nullable = methodAnnotations.nullables.contains(primaryKey);
String contractValue = methodAnnotations.contracts.get(primaryKey);
if (notNull && contractValue != null) {
return new PsiAnnotation[]{
getNotNullAnnotation(),
createAnnotationFromText("@" + ControlFlowAnalyzer.ORG_JETBRAINS_ANNOTATIONS_CONTRACT + "(" + contractValue + ")")
};
}
if (nullable && contractValue != null) {
return new PsiAnnotation[]{
getNullableAnnotation(),
createAnnotationFromText("@" + ControlFlowAnalyzer.ORG_JETBRAINS_ANNOTATIONS_CONTRACT + "(" + contractValue + ")")
};
}
else if (notNull) {
return new PsiAnnotation[]{
getNotNullAnnotation()
};
}
else if (nullable) {
return new PsiAnnotation[]{
getNullableAnnotation()
};
}
else if (contractValue != null) {
return new PsiAnnotation[]{
createAnnotationFromText("@" + ControlFlowAnalyzer.ORG_JETBRAINS_ANNOTATIONS_CONTRACT + "(" + contractValue + ")")
};
}
} else if (listOwner instanceof PsiParameter) {
ParameterAnnotations parameterAnnotations = loadParameterAnnotations(primaryKey);
if (parameterAnnotations.notNull) {
return new PsiAnnotation[]{
getNotNullAnnotation()
};
}
else if (parameterAnnotations.nullable) {
return new PsiAnnotation[]{
getNullableAnnotation()
};
}
}
return PsiAnnotation.EMPTY_ARRAY;
}
catch (EquationsLimitException e) {
String externalName = PsiFormatUtil.getExternalName(listOwner, false, Integer.MAX_VALUE);
@@ -146,6 +172,16 @@ public class ProjectBytecodeAnalysis {
});
}
private PsiAnnotation getNullableAnnotation() {
return CachedValuesManager.getManager(myProject).getCachedValue(myProject, new CachedValueProvider<PsiAnnotation>() {
@Nullable
@Override
public Result<PsiAnnotation> compute() {
return Result.create(createAnnotationFromText("@" + AnnotationUtil.NULLABLE), ModificationTracker.NEVER_CHANGED);
}
});
}
public PsiAnnotation createContractAnnotation(String contractValue) {
return createAnnotationFromText("@org.jetbrains.annotations.Contract(" + contractValue + ")");
}
@@ -154,7 +190,7 @@ public class ProjectBytecodeAnalysis {
public static HKey getKey(@NotNull PsiModifierListOwner owner, MessageDigest md) {
LOG.assertTrue(owner instanceof PsiCompiledElement, owner);
if (owner instanceof PsiMethod) {
return BytecodeAnalysisConverter.psiKey((PsiMethod)owner, new Out(), md);
return BytecodeAnalysisConverter.psiKey((PsiMethod)owner, Out, md);
}
if (owner instanceof PsiParameter) {
PsiElement parent = owner.getParent();
@@ -162,43 +198,69 @@ public class ProjectBytecodeAnalysis {
PsiElement gParent = parent.getParent();
if (gParent instanceof PsiMethod) {
final int index = ((PsiParameterList)parent).getParameterIndex((PsiParameter)owner);
return BytecodeAnalysisConverter.psiKey((PsiMethod)gParent, new In(index), md);
return BytecodeAnalysisConverter.psiKey((PsiMethod)gParent, new In(index, In.NOT_NULL), md);
}
}
}
return null;
}
public static ArrayList<HKey> contractKeys(@NotNull PsiModifierListOwner owner, HKey primaryKey) {
if (owner instanceof PsiMethod) {
ArrayList<HKey> result = BytecodeAnalysisConverter.mkInOutKeys((PsiMethod)owner, primaryKey);
result.add(primaryKey);
return result;
}
ArrayList<HKey> result = new ArrayList<HKey>(1);
public static ArrayList<HKey> contractKeys(@NotNull PsiMethod owner, HKey primaryKey) {
ArrayList<HKey> result = BytecodeAnalysisConverter.mkInOutKeys(owner, primaryKey);
result.add(primaryKey);
return result;
}
private Annotations loadAnnotations(@NotNull PsiModifierListOwner owner, @NotNull HKey key, ArrayList<HKey> allKeys)
private ParameterAnnotations loadParameterAnnotations(@NotNull HKey notNullKey)
throws EquationsLimitException {
Annotations result = new Annotations();
if (owner instanceof PsiParameter) {
final Solver solver = new Solver(new ELattice<Value>(Value.NotNull, Value.Top));
collectEquations(allKeys, solver);
HashMap<HKey, Value> solutions = solver.solve();
BytecodeAnalysisConverter.addParameterAnnotations(solutions, result);
} else if (owner instanceof PsiMethod) {
final Solver solver = new Solver(new ELattice<Value>(Value.Bot, Value.Top));
collectEquations(allKeys, solver);
HashMap<HKey, Value> solutions = solver.solve();
int arity = ((PsiMethod)owner).getParameterList().getParameters().length;
BytecodeAnalysisConverter.addMethodAnnotations(solutions, result, key, arity);
Map<Bytes, List<HEquations>> equationsCache = new HashMap<Bytes, List<HEquations>>();
final Solver notNullSolver = new Solver(new ELattice<Value>(Value.NotNull, Value.Top), Value.Top);
collectEquations(Collections.singletonList(notNullKey), notNullSolver, equationsCache);
HashMap<HKey, Value> notNullSolutions = notNullSolver.solve();
boolean notNull =
(Value.NotNull == notNullSolutions.get(notNullKey)) || (Value.NotNull == notNullSolutions.get(notNullKey.mkUnstable()));
final Solver nullableSolver = new Solver(new ELattice<Value>(Value.Null, Value.Top), Value.Top);
final HKey nullableKey = new HKey(notNullKey.key, notNullKey.dirKey + 1, true);
collectEquations(Collections.singletonList(nullableKey), nullableSolver, equationsCache);
HashMap<HKey, Value> nullableSolutions = nullableSolver.solve();
boolean nullable =
(Value.Null == nullableSolutions.get(nullableKey)) || (Value.Null == nullableSolutions.get(nullableKey.mkUnstable()));
return new ParameterAnnotations(notNull, nullable);
}
private MethodAnnotations loadMethodAnnotations(@NotNull PsiMethod owner, @NotNull HKey key, ArrayList<HKey> allKeys)
throws EquationsLimitException {
MethodAnnotations result = new MethodAnnotations();
Map<Bytes, List<HEquations>> equationsCache = new HashMap<Bytes, List<HEquations>>();
final Solver outSolver = new Solver(new ELattice<Value>(Value.Bot, Value.Top), Value.Top);
collectEquations(allKeys, outSolver, equationsCache);
HashMap<HKey, Value> solutions = outSolver.solve();
int arity = owner.getParameterList().getParameters().length;
BytecodeAnalysisConverter.addMethodAnnotations(solutions, result, key, arity);
final Solver nullableMethodSolver = new Solver(new ELattice<Value>(Value.Bot, Value.Null), Value.Bot);
HKey nullableKey = key.updateDirection(BytecodeAnalysisConverter.mkDirectionKey(NullableOut));
if (nullableMethodTransitivity) {
collectEquations(Collections.singletonList(nullableKey), nullableMethodSolver, equationsCache);
}
else {
collectSingleEquation(nullableKey, nullableMethodSolver, equationsCache);
}
HashMap<HKey, Value> nullableSolutions = nullableMethodSolver.solve();
if (nullableSolutions.get(nullableKey) == Value.Null || nullableSolutions.get(nullableKey.negate()) == Value.Null) {
result.nullables.add(key);
}
return result;
}
private void collectEquations(ArrayList<HKey> keys, Solver solver) throws EquationsLimitException {
private void collectEquations(List<HKey> keys, Solver solver, @NotNull Map<Bytes, List<HEquations>> cache) throws EquationsLimitException {
GlobalSearchScope librariesScope = ProjectScope.getLibrariesScope(myProject);
HashSet<HKey> queued = new HashSet<HKey>();
Stack<HKey> queue = new Stack<HKey>();
@@ -208,7 +270,6 @@ public class ProjectBytecodeAnalysis {
queued.add(key);
}
HashMap<Bytes, List<HEquations>> cache = new HashMap<Bytes, List<HEquations>>();
FileBasedIndex index = FileBasedIndex.getInstance();
while (!queue.empty()) {
@@ -250,6 +311,32 @@ public class ProjectBytecodeAnalysis {
}
}
private void collectSingleEquation(HKey hKey, Solver solver, @NotNull Map<Bytes, List<HEquations>> cache) throws EquationsLimitException {
GlobalSearchScope librariesScope = ProjectScope.getLibrariesScope(myProject);
FileBasedIndex index = FileBasedIndex.getInstance();
ProgressManager.checkCanceled();
Bytes bytes = new Bytes(hKey.key);
List<HEquations> hEquationss = cache.get(bytes);
if (hEquationss == null) {
hEquationss = index.getValues(BytecodeAnalysisIndex.NAME, bytes, librariesScope);
cache.put(bytes, hEquationss);
}
for (HEquations hEquations : hEquationss) {
boolean stable = hEquations.stable;
for (DirectionResultPair pair : hEquations.results) {
int dirKey = pair.directionKey;
if (dirKey == hKey.dirKey) {
HResult result = pair.hResult;
solver.addEquation(new HEquation(new HKey(bytes.bytes, dirKey, stable), result));
}
}
}
}
@NotNull
private PsiAnnotation createAnnotationFromText(@NotNull final String text) throws IncorrectOperationException {
PsiAnnotation annotation = JavaPsiFacade.getElementFactory(myProject).createAnnotationFromText(text, null);
@@ -258,11 +345,23 @@ public class ProjectBytecodeAnalysis {
}
}
class Annotations {
class MethodAnnotations {
// @NotNull keys
final HashSet<HKey> notNulls = new HashSet<HKey>();
// @Nullable keys
final HashSet<HKey> nullables = new HashSet<HKey>();
// @Contracts
final HashMap<HKey, String> contracts = new HashMap<HKey, String>();
}
class ParameterAnnotations {
final boolean notNull;
final boolean nullable;
ParameterAnnotations(boolean notNull, boolean nullable) {
this.notNull = notNull;
this.nullable = nullable;
}
}
class EquationsLimitException extends Exception {}
@@ -265,9 +265,11 @@ final class Solver {
private final HResultUtil resultUtil;
private final HashMap<CoreHKey, HEquation> equations = new HashMap<CoreHKey, HEquation>();
private final Value unstableValue;
Solver(ELattice<Value> lattice) {
Solver(ELattice<Value> lattice, Value unstableValue) {
this.lattice = lattice;
this.unstableValue = unstableValue;
resultUtil = new HResultUtil(lattice);
}
@@ -324,7 +326,7 @@ final class Solver {
Value value = solved.get(id);
HKey[] pIds = id.stable ? new HKey[]{id, id.negate()} : new HKey[]{id.negate(), id};
Value[] pVals = id.stable ? new Value[]{value, value} : new Value[]{value, lattice.top};
Value[] pVals = id.stable ? new Value[]{value, value} : new Value[]{value, unstableValue};
for (int i = 0; i < pIds.length; i++) {
HKey pId = pIds[i];
@@ -0,0 +1,446 @@
/*
* Copyright 2000-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 com.intellij.codeInspection.bytecodeAnalysis.asm;
import org.jetbrains.org.objectweb.asm.Opcodes;
import org.jetbrains.org.objectweb.asm.Type;
import org.jetbrains.org.objectweb.asm.tree.*;
import org.jetbrains.org.objectweb.asm.tree.analysis.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Extended version of {@link org.jetbrains.org.objectweb.asm.tree.analysis.Analyzer}.
* It handles frames <b>and</b> additional data.
*
* @author lambdamix
*/
public class AnalyzerExt<V extends Value, Data, MyInterpreter extends Interpreter<V> & InterpreterExt<Data>> implements Opcodes {
private final MyInterpreter interpreter;
private int n;
private InsnList insns;
private List<TryCatchBlockNode>[] handlers;
private Frame<V>[] frames;
private Subroutine[] subroutines;
private boolean[] queued;
private int[] queue;
private int top;
public Data[] getData() {
return data;
}
private Data[] data;
public AnalyzerExt(final MyInterpreter interpreter, Data[] data, Data startData) {
this.interpreter = interpreter;
this.data = data;
if (data.length > 0) {
data[0] = startData;
}
}
public Frame<V>[] analyze(final String owner, final MethodNode m) throws AnalyzerException {
if ((m.access & (ACC_ABSTRACT | ACC_NATIVE)) != 0) {
frames = (Frame<V>[]) new Frame<?>[0];
return frames;
}
final V refV = (V) BasicValue.REFERENCE_VALUE;
n = m.instructions.size();
insns = m.instructions;
handlers = (List<TryCatchBlockNode>[]) new List<?>[n];
frames = (Frame<V>[]) new Frame<?>[n];
subroutines = new Subroutine[n];
queued = new boolean[n];
queue = new int[n];
top = 0;
// computes exception handlers for each instruction
for (int i = 0; i < m.tryCatchBlocks.size(); ++i) {
TryCatchBlockNode tcb = m.tryCatchBlocks.get(i);
int begin = insns.indexOf(tcb.start);
int end = insns.indexOf(tcb.end);
for (int j = begin; j < end; ++j) {
List<TryCatchBlockNode> insnHandlers = handlers[j];
if (insnHandlers == null) {
insnHandlers = new ArrayList<TryCatchBlockNode>();
handlers[j] = insnHandlers;
}
insnHandlers.add(tcb);
}
}
// computes the subroutine for each instruction:
Subroutine main = new Subroutine(null, m.maxLocals, null);
List<AbstractInsnNode> subroutineCalls = new ArrayList<AbstractInsnNode>();
Map<LabelNode, Subroutine> subroutineHeads = new HashMap<LabelNode, Subroutine>();
findSubroutine(0, main, subroutineCalls);
while (!subroutineCalls.isEmpty()) {
JumpInsnNode jsr = (JumpInsnNode) subroutineCalls.remove(0);
Subroutine sub = subroutineHeads.get(jsr.label);
if (sub == null) {
sub = new Subroutine(jsr.label, m.maxLocals, jsr);
subroutineHeads.put(jsr.label, sub);
findSubroutine(insns.indexOf(jsr.label), sub, subroutineCalls);
} else {
sub.callers.add(jsr);
}
}
for (int i = 0; i < n; ++i) {
if (subroutines[i] != null && subroutines[i].start == null) {
subroutines[i] = null;
}
}
// initializes the data structures for the control flow analysis
Frame<V> current = newFrame(m.maxLocals, m.maxStack);
Frame<V> handler = newFrame(m.maxLocals, m.maxStack);
current.setReturn(interpreter.newValue(Type.getReturnType(m.desc)));
Type[] args = Type.getArgumentTypes(m.desc);
int local = 0;
if ((m.access & ACC_STATIC) == 0) {
Type ctype = Type.getObjectType(owner);
current.setLocal(local++, interpreter.newValue(ctype));
}
for (int i = 0; i < args.length; ++i) {
current.setLocal(local++, interpreter.newValue(args[i]));
if (args[i].getSize() == 2) {
current.setLocal(local++, interpreter.newValue(null));
}
}
while (local < m.maxLocals) {
current.setLocal(local++, interpreter.newValue(null));
}
interpreter.init(data[0]);
merge(0, current, null);
init(owner, m);
// control flow analysis
while (top > 0) {
int insn = queue[--top];
Frame<V> f = frames[insn];
Subroutine subroutine = subroutines[insn];
queued[insn] = false;
AbstractInsnNode insnNode = null;
try {
insnNode = m.instructions.get(insn);
int insnOpcode = insnNode.getOpcode();
int insnType = insnNode.getType();
if (insnType == AbstractInsnNode.LABEL
|| insnType == AbstractInsnNode.LINE
|| insnType == AbstractInsnNode.FRAME) {
interpreter.init(data[insn]);
merge(insn + 1, f, subroutine);
newControlFlowEdge(insn, insn + 1);
} else {
// delta
interpreter.init(data[insn]);
current.init(f).execute(insnNode, interpreter);
subroutine = subroutine == null ? null : subroutine.copy();
if (insnNode instanceof JumpInsnNode) {
JumpInsnNode j = (JumpInsnNode) insnNode;
if (insnOpcode != GOTO && insnOpcode != JSR) {
merge(insn + 1, current, subroutine);
newControlFlowEdge(insn, insn + 1);
}
int jump = insns.indexOf(j.label);
if (insnOpcode == JSR) {
merge(jump, current, new Subroutine(j.label,
m.maxLocals, j));
} else {
merge(jump, current, subroutine);
}
newControlFlowEdge(insn, jump);
} else if (insnNode instanceof LookupSwitchInsnNode) {
LookupSwitchInsnNode lsi = (LookupSwitchInsnNode) insnNode;
int jump = insns.indexOf(lsi.dflt);
merge(jump, current, subroutine);
newControlFlowEdge(insn, jump);
for (int j = 0; j < lsi.labels.size(); ++j) {
LabelNode label = lsi.labels.get(j);
jump = insns.indexOf(label);
merge(jump, current, subroutine);
newControlFlowEdge(insn, jump);
}
} else if (insnNode instanceof TableSwitchInsnNode) {
TableSwitchInsnNode tsi = (TableSwitchInsnNode) insnNode;
int jump = insns.indexOf(tsi.dflt);
merge(jump, current, subroutine);
newControlFlowEdge(insn, jump);
for (int j = 0; j < tsi.labels.size(); ++j) {
LabelNode label = tsi.labels.get(j);
jump = insns.indexOf(label);
merge(jump, current, subroutine);
newControlFlowEdge(insn, jump);
}
} else if (insnOpcode == RET) {
if (subroutine == null) {
throw new AnalyzerException(insnNode,
"RET instruction outside of a sub routine");
}
for (int i = 0; i < subroutine.callers.size(); ++i) {
JumpInsnNode caller = subroutine.callers.get(i);
int call = insns.indexOf(caller);
if (frames[call] != null) {
merge(call + 1, frames[call], current,
subroutines[call], subroutine.access);
newControlFlowEdge(insn, call + 1);
}
}
} else if (insnOpcode != ATHROW && (insnOpcode < IRETURN || insnOpcode > RETURN)) {
if (subroutine != null) {
if (insnNode instanceof VarInsnNode) {
int var = ((VarInsnNode) insnNode).var;
subroutine.access[var] = true;
if (insnOpcode == LLOAD || insnOpcode == DLOAD
|| insnOpcode == LSTORE
|| insnOpcode == DSTORE) {
subroutine.access[var + 1] = true;
}
} else if (insnNode instanceof IincInsnNode) {
int var = ((IincInsnNode) insnNode).var;
subroutine.access[var] = true;
}
}
merge(insn + 1, current, subroutine);
newControlFlowEdge(insn, insn + 1);
}
}
List<TryCatchBlockNode> insnHandlers = handlers[insn];
if (insnHandlers != null) {
for (int i = 0; i < insnHandlers.size(); ++i) {
TryCatchBlockNode tcb = insnHandlers.get(i);
int jump = insns.indexOf(tcb.handler);
if (newControlFlowExceptionEdge(insn, tcb)) {
handler.init(f);
handler.clearStack();
handler.push(refV);
merge(jump, handler, subroutine);
}
}
}
} catch (AnalyzerException e) {
throw new AnalyzerException(e.node, "Error at instruction "
+ insn + ": " + e.getMessage(), e);
} catch (Exception e) {
throw new AnalyzerException(insnNode, "Error at instruction "
+ insn + ": " + e.getMessage(), e);
}
}
return frames;
}
private void findSubroutine(int insn, final Subroutine sub,
final List<AbstractInsnNode> calls) throws AnalyzerException {
while (true) {
if (insn < 0 || insn >= n) {
throw new AnalyzerException(null,
"Execution can fall off end of the code");
}
if (subroutines[insn] != null) {
return;
}
subroutines[insn] = sub.copy();
AbstractInsnNode node = insns.get(insn);
// calls findSubroutine recursively on normal successors
if (node instanceof JumpInsnNode) {
if (node.getOpcode() == JSR) {
// do not follow a JSR, it leads to another subroutine!
calls.add(node);
} else {
JumpInsnNode jnode = (JumpInsnNode) node;
findSubroutine(insns.indexOf(jnode.label), sub, calls);
}
} else if (node instanceof TableSwitchInsnNode) {
TableSwitchInsnNode tsnode = (TableSwitchInsnNode) node;
findSubroutine(insns.indexOf(tsnode.dflt), sub, calls);
for (int i = tsnode.labels.size() - 1; i >= 0; --i) {
LabelNode l = tsnode.labels.get(i);
findSubroutine(insns.indexOf(l), sub, calls);
}
} else if (node instanceof LookupSwitchInsnNode) {
LookupSwitchInsnNode lsnode = (LookupSwitchInsnNode) node;
findSubroutine(insns.indexOf(lsnode.dflt), sub, calls);
for (int i = lsnode.labels.size() - 1; i >= 0; --i) {
LabelNode l = lsnode.labels.get(i);
findSubroutine(insns.indexOf(l), sub, calls);
}
}
// calls findSubroutine recursively on exception handler successors
List<TryCatchBlockNode> insnHandlers = handlers[insn];
if (insnHandlers != null) {
for (int i = 0; i < insnHandlers.size(); ++i) {
TryCatchBlockNode tcb = insnHandlers.get(i);
findSubroutine(insns.indexOf(tcb.handler), sub, calls);
}
}
// if insn does not falls through to the next instruction, return.
switch (node.getOpcode()) {
case GOTO:
case RET:
case TABLESWITCH:
case LOOKUPSWITCH:
case IRETURN:
case LRETURN:
case FRETURN:
case DRETURN:
case ARETURN:
case RETURN:
case ATHROW:
return;
}
insn++;
}
}
public Frame<V>[] getFrames() {
return frames;
}
public List<TryCatchBlockNode> getHandlers(final int insn) {
return handlers[insn];
}
protected void init(String owner, MethodNode m) throws AnalyzerException {
}
protected Frame<V> newFrame(final int nLocals, final int nStack) {
return new Frame<V>(nLocals, nStack);
}
protected Frame<V> newFrame(final Frame<? extends V> src) {
return new Frame<V>(src);
}
protected void newControlFlowEdge(final int insn, final int successor) {
}
protected boolean newControlFlowExceptionEdge(final int insn,
final int successor) {
return true;
}
protected boolean newControlFlowExceptionEdge(final int insn,
final TryCatchBlockNode tcb) {
return newControlFlowExceptionEdge(insn, insns.indexOf(tcb.handler));
}
// -------------------------------------------------------------------------
private void merge(final int insn, final Frame<V> frame,
final Subroutine subroutine) throws AnalyzerException {
Frame<V> oldFrame = frames[insn];
Subroutine oldSubroutine = subroutines[insn];
boolean changes;
if (oldFrame == null) {
frames[insn] = newFrame(frame);
changes = true;
} else {
changes = oldFrame.merge(frame, interpreter);
}
if (oldSubroutine == null) {
if (subroutine != null) {
subroutines[insn] = subroutine.copy();
changes = true;
}
} else {
if (subroutine != null) {
changes |= oldSubroutine.merge(subroutine);
}
}
if (changes && !queued[insn]) {
queued[insn] = true;
queue[top++] = insn;
}
// delta
mergeData(insn, interpreter);
}
private void merge(final int insn, final Frame<V> beforeJSR,
final Frame<V> afterRET, final Subroutine subroutineBeforeJSR,
final boolean[] access) throws AnalyzerException {
Frame<V> oldFrame = frames[insn];
Subroutine oldSubroutine = subroutines[insn];
boolean changes;
afterRET.merge(beforeJSR, access);
if (oldFrame == null) {
frames[insn] = newFrame(afterRET);
changes = true;
} else {
changes = oldFrame.merge(afterRET, interpreter);
}
if (oldSubroutine != null && subroutineBeforeJSR != null) {
changes |= oldSubroutine.merge(subroutineBeforeJSR);
}
if (changes && !queued[insn]) {
queued[insn] = true;
queue[top++] = insn;
}
// delta
mergeData(insn, interpreter);
}
private void mergeData(int insn, MyInterpreter interpreter) {
boolean changes = false;
Data oldData = data[insn];
Data newData = interpreter.getAfterData(insn);
if (oldData == null) {
data[insn] = newData;
changes = true;
} else if (newData != null) {
Data mergedData = interpreter.merge(oldData, newData);
data[insn] = mergedData;
changes = !oldData.equals(mergedData);
}
if (changes && !queued[insn]) {
queued[insn] = true;
queue[top++] = insn;
}
}
}
@@ -31,52 +31,6 @@ import java.util.Map;
* So, the main point here is handling of subroutines (jsr) and try-catch-finally blocks.
*/
public class FramelessAnalyzer implements Opcodes {
static class Subroutine {
LabelNode start;
boolean[] access;
List<JumpInsnNode> callers;
private Subroutine() {
}
Subroutine(@Nullable final LabelNode start, final int maxLocals,
@Nullable final JumpInsnNode caller) {
this.start = start;
this.access = new boolean[maxLocals];
this.callers = new ArrayList<JumpInsnNode>();
callers.add(caller);
}
public Subroutine copy() {
Subroutine result = new Subroutine();
result.start = start;
result.access = new boolean[access.length];
System.arraycopy(access, 0, result.access, 0, access.length);
result.callers = new ArrayList<JumpInsnNode>(callers);
return result;
}
public boolean merge(final Subroutine subroutine) throws AnalyzerException {
boolean changes = false;
for (int i = 0; i < access.length; ++i) {
if (subroutine.access[i] && !access[i]) {
access[i] = true;
changes = true;
}
}
if (subroutine.start == start) {
for (int i = 0; i < subroutine.callers.size(); ++i) {
JumpInsnNode caller = subroutine.callers.get(i);
if (!callers.contains(caller)) {
callers.add(caller);
changes = true;
}
}
}
return changes;
}
}
private int n;
private InsnList insns;
private List<TryCatchBlockNode>[] handlers;
@@ -0,0 +1,32 @@
/*
* Copyright 2000-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 com.intellij.codeInspection.bytecodeAnalysis.asm;
/**
* @author lambdamix
*/
public interface InterpreterExt<Data> { // self: Interpreter[_] =>
// init interpreter state by passing entry data
void init(Data previous);
// exit data after execution for edge to insn
// there are may be different outcomes for different edges if an instruction was branching one
Data getAfterData(int insn);
// merge two states
Data merge(Data data1, Data data2);
}
@@ -31,10 +31,12 @@ import static org.jetbrains.org.objectweb.asm.Opcodes.*;
public class LeakingParameters {
public final Frame<Value>[] frames;
public final boolean[] parameters;
public final boolean[] nullableParameters;
public LeakingParameters(Frame<Value>[] frames, boolean[] parameters) {
public LeakingParameters(Frame<Value>[] frames, boolean[] parameters, boolean[] nullableParameters) {
this.frames = frames;
this.parameters = parameters;
this.nullableParameters = nullableParameters;
}
public static LeakingParameters build(String className, MethodNode methodNode, boolean jsr) throws AnalyzerException {
@@ -57,7 +59,12 @@ public class LeakingParameters {
}
}
}
return new LeakingParameters((Frame<Value>[])(Frame<?>[])frames, collector.leaking);
boolean[] notNullParameters = collector.leaking;
boolean[] nullableParameters = collector.nullableLeaking;
for (int i = 0; i < nullableParameters.length; i++) {
nullableParameters[i] |= notNullParameters[i];
}
return new LeakingParameters((Frame<Value>[])(Frame<?>[])frames, notNullParameters, nullableParameters);
}
public static LeakingParameters buildFast(String className, MethodNode methodNode, boolean jsr) throws AnalyzerException {
@@ -66,11 +73,14 @@ public class LeakingParameters {
new Analyzer<IParamsValue>(parametersUsage).analyze(className, methodNode) :
new LiteAnalyzer<IParamsValue>(parametersUsage).analyze(className, methodNode);
int leakingMask = parametersUsage.leaking;
boolean[] result = new boolean[parametersUsage.arity];
for (int i = 0; i < result.length; i++) {
result[i] = (leakingMask & (1 << i)) != 0;
int nullableLeakingMask = parametersUsage.nullableLeaking;
boolean[] notNullParameters = new boolean[parametersUsage.arity];
boolean[] nullableParameters = new boolean[parametersUsage.arity];
for (int i = 0; i < notNullParameters.length; i++) {
notNullParameters[i] = (leakingMask & (1 << i)) != 0;
nullableParameters[i] = ((leakingMask | nullableLeakingMask) & (1 << i)) != 0;
}
return new LeakingParameters((Frame<Value>[])frames, result);
return new LeakingParameters((Frame<Value>[])frames, notNullParameters, nullableParameters);
}
}
@@ -252,7 +262,7 @@ class ParametersUsage extends Interpreter<ParamsValue> {
@Override
public ParamsValue ternaryOperation(AbstractInsnNode insn, ParamsValue value1, ParamsValue value2, ParamsValue value3) {
return val1;
return null;
}
@Override
@@ -288,6 +298,7 @@ class IParametersUsage extends Interpreter<IParamsValue> {
static final IParamsValue val1 = new IParamsValue(0, 1);
static final IParamsValue val2 = new IParamsValue(0, 2);
int leaking = 0;
int nullableLeaking = 0;
int called = -1;
final int rangeStart;
final int rangeEnd;
@@ -415,8 +426,12 @@ class IParametersUsage extends Interpreter<IParamsValue> {
case BALOAD:
case CALOAD:
case SALOAD:
leaking |= value1.params;
size = 1;
break;
case PUTFIELD:
leaking |= value1.params;
nullableLeaking |= value2.params;
size = 1;
break;
default:
@@ -432,15 +447,18 @@ class IParametersUsage extends Interpreter<IParamsValue> {
case LASTORE:
case FASTORE:
case DASTORE:
case AASTORE:
case BASTORE:
case CASTORE:
case SASTORE:
leaking |= value1.params;
break;
case AASTORE:
leaking |= value1.params;
nullableLeaking |= value3.params;
break;
default:
}
return val1;
return null;
}
@Override
@@ -479,9 +497,11 @@ class IParametersUsage extends Interpreter<IParamsValue> {
class LeakingParametersCollector extends ParametersUsage {
final boolean[] leaking;
final boolean[] nullableLeaking;
LeakingParametersCollector(MethodNode methodNode) {
super(methodNode);
leaking = new boolean[arity];
nullableLeaking = new boolean[arity];
}
@Override
@@ -518,12 +538,21 @@ class LeakingParametersCollector extends ParametersUsage {
case BALOAD:
case CALOAD:
case SALOAD:
case PUTFIELD:
boolean[] params = value1.params;
for (int i = 0; i < arity; i++) {
leaking[i] |= params[i];
}
break;
case PUTFIELD:
params = value1.params;
for (int i = 0; i < arity; i++) {
leaking[i] |= params[i];
}
params = value2.params;
for (int i = 0; i < arity; i++) {
nullableLeaking[i] |= params[i];
}
break;
default:
}
return super.binaryOperation(insn, value1, value2);
@@ -531,23 +560,33 @@ class LeakingParametersCollector extends ParametersUsage {
@Override
public ParamsValue ternaryOperation(AbstractInsnNode insn, ParamsValue value1, ParamsValue value2, ParamsValue value3) {
boolean[] params;
switch (insn.getOpcode()) {
case IASTORE:
case LASTORE:
case FASTORE:
case DASTORE:
case AASTORE:
case BASTORE:
case CASTORE:
case SASTORE:
boolean[] params = value1.params;
params = value1.params;
for (int i = 0; i < arity; i++) {
leaking[i] |= params[i];
}
break;
case AASTORE:
params = value1.params;
for (int i = 0; i < arity; i++) {
leaking[i] |= params[i];
}
params = value3.params;
for (int i = 0; i < arity; i++) {
nullableLeaking[i] |= params[i];
}
break;
default:
}
return super.ternaryOperation(insn, value1, value2, value3);
return null;
}
@Override
@@ -0,0 +1,222 @@
/*
* Copyright 2000-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 com.intellij.codeInspection.bytecodeAnalysis.asm;
import org.jetbrains.org.objectweb.asm.Opcodes;
import org.jetbrains.org.objectweb.asm.Type;
import org.jetbrains.org.objectweb.asm.tree.*;
import org.jetbrains.org.objectweb.asm.tree.analysis.*;
import java.util.ArrayList;
import java.util.List;
/**
* Extended version of {@link com.intellij.codeInspection.bytecodeAnalysis.asm.LiteAnalyzer}.
* It handles frames <b>and</b> additional data.
*
* @author lambdamix
*/
public class LiteAnalyzerExt<V extends Value, Data, MyInterpreter extends Interpreter<V> & InterpreterExt<Data>> implements Opcodes {
private final MyInterpreter interpreter;
private Frame<V>[] frames;
private boolean[] queued;
private int[] queue;
private int top;
public Data[] getData() {
return data;
}
private Data[] data;
public LiteAnalyzerExt(final MyInterpreter interpreter, Data[] data, Data startData) {
this.interpreter = interpreter;
this.data = data;
if (data.length > 0) {
data[0] = startData;
}
}
public Frame<V>[] analyze(final String owner, final MethodNode m) throws AnalyzerException {
if ((m.access & (ACC_ABSTRACT | ACC_NATIVE)) != 0) {
frames = (Frame<V>[]) new Frame<?>[0];
return frames;
}
final V refV = (V) BasicValue.REFERENCE_VALUE;
int n = m.instructions.size();
InsnList insns = m.instructions;
List<TryCatchBlockNode>[] handlers = (List<TryCatchBlockNode>[]) new List<?>[n];
frames = (Frame<V>[]) new Frame<?>[n];
queued = new boolean[n];
queue = new int[n];
top = 0;
// computes exception handlers for each instruction
for (int i = 0; i < m.tryCatchBlocks.size(); ++i) {
TryCatchBlockNode tcb = m.tryCatchBlocks.get(i);
int begin = insns.indexOf(tcb.start);
int end = insns.indexOf(tcb.end);
for (int j = begin; j < end; ++j) {
List<TryCatchBlockNode> insnHandlers = handlers[j];
if (insnHandlers == null) {
insnHandlers = new ArrayList<TryCatchBlockNode>();
handlers[j] = insnHandlers;
}
insnHandlers.add(tcb);
}
}
// initializes the data structures for the control flow analysis
Frame<V> current = newFrame(m.maxLocals, m.maxStack);
Frame<V> handler = newFrame(m.maxLocals, m.maxStack);
current.setReturn(interpreter.newValue(Type.getReturnType(m.desc)));
Type[] args = Type.getArgumentTypes(m.desc);
int local = 0;
if ((m.access & ACC_STATIC) == 0) {
Type ctype = Type.getObjectType(owner);
current.setLocal(local++, interpreter.newValue(ctype));
}
for (int i = 0; i < args.length; ++i) {
current.setLocal(local++, interpreter.newValue(args[i]));
if (args[i].getSize() == 2) {
current.setLocal(local++, interpreter.newValue(null));
}
}
while (local < m.maxLocals) {
current.setLocal(local++, interpreter.newValue(null));
}
interpreter.init(data[0]);
merge(0, current);
// control flow analysis
while (top > 0) {
int insn = queue[--top];
Frame<V> f = frames[insn];
queued[insn] = false;
AbstractInsnNode insnNode = null;
try {
insnNode = m.instructions.get(insn);
int insnOpcode = insnNode.getOpcode();
int insnType = insnNode.getType();
if (insnType == AbstractInsnNode.LABEL || insnType == AbstractInsnNode.LINE || insnType == AbstractInsnNode.FRAME) {
interpreter.init(data[insn]);
merge(insn + 1, f);
} else {
// delta
interpreter.init(data[insn]);
current.init(f).execute(insnNode, interpreter);
if (insnNode instanceof JumpInsnNode) {
JumpInsnNode j = (JumpInsnNode) insnNode;
if (insnOpcode != GOTO && insnOpcode != JSR) {
merge(insn + 1, current);
}
int jump = insns.indexOf(j.label);
merge(jump, current);
} else if (insnNode instanceof LookupSwitchInsnNode) {
LookupSwitchInsnNode lsi = (LookupSwitchInsnNode) insnNode;
int jump = insns.indexOf(lsi.dflt);
merge(jump, current);
for (int j = 0; j < lsi.labels.size(); ++j) {
LabelNode label = lsi.labels.get(j);
jump = insns.indexOf(label);
merge(jump, current);
}
} else if (insnNode instanceof TableSwitchInsnNode) {
TableSwitchInsnNode tsi = (TableSwitchInsnNode) insnNode;
int jump = insns.indexOf(tsi.dflt);
merge(jump, current);
for (int j = 0; j < tsi.labels.size(); ++j) {
LabelNode label = tsi.labels.get(j);
jump = insns.indexOf(label);
merge(jump, current);
}
} else if (insnOpcode != ATHROW
&& (insnOpcode < IRETURN || insnOpcode > RETURN)) {
merge(insn + 1, current);
}
}
List<TryCatchBlockNode> insnHandlers = handlers[insn];
if (insnHandlers != null) {
for (int i = 0; i < insnHandlers.size(); ++i) {
TryCatchBlockNode tcb = insnHandlers.get(i);
int jump = insns.indexOf(tcb.handler);
handler.init(f);
handler.clearStack();
handler.push(refV);
merge(jump, handler);
}
}
} catch (AnalyzerException e) {
throw new AnalyzerException(e.node, "Error at instruction " + insn + ": " + e.getMessage(), e);
} catch (Exception e) {
throw new AnalyzerException(insnNode, "Error at instruction " + insn + ": " + e.getMessage(), e);
}
}
return frames;
}
public Frame<V>[] getFrames() {
return frames;
}
protected Frame<V> newFrame(final int nLocals, final int nStack) {
return new Frame<V>(nLocals, nStack);
}
protected Frame<V> newFrame(final Frame<? extends V> src) {
return new Frame<V>(src);
}
// -------------------------------------------------------------------------
private void merge(final int insn, final Frame<V> frame) throws AnalyzerException {
Frame<V> oldFrame = frames[insn];
boolean changes;
if (oldFrame == null) {
frames[insn] = newFrame(frame);
changes = true;
} else {
changes = oldFrame.merge(frame, interpreter);
}
Data oldData = data[insn];
Data newData = interpreter.getAfterData(insn);
if (oldData == null) {
data[insn] = newData;
changes = true;
} else if (newData != null) {
Data mergedData = interpreter.merge(oldData, newData);
data[insn] = mergedData;
changes |= !oldData.equals(mergedData);
}
if (changes && !queued[insn]) {
queued[insn] = true;
queue[top++] = insn;
}
}
}
@@ -29,11 +29,11 @@ import java.util.List;
public class LiteFramelessAnalyzer extends FramelessAnalyzer {
@Override
protected void findSubroutine(int insn, FramelessAnalyzer.Subroutine sub, List<AbstractInsnNode> calls) throws AnalyzerException {
protected void findSubroutine(int insn, Subroutine sub, List<AbstractInsnNode> calls) throws AnalyzerException {
}
@Override
protected void merge(final int insn, final FramelessAnalyzer.Subroutine subroutine) throws AnalyzerException {
protected void merge(final int insn, final Subroutine subroutine) throws AnalyzerException {
if (!wasQueued[insn]) {
wasQueued[insn] = true;
if (!queued[insn]) {
@@ -44,7 +44,7 @@ public class LiteFramelessAnalyzer extends FramelessAnalyzer {
}
@Override
protected void merge(final int insn, final FramelessAnalyzer.Subroutine subroutineBeforeJSR, final boolean[] access) throws AnalyzerException {
protected void merge(final int insn, final Subroutine subroutineBeforeJSR, final boolean[] access) throws AnalyzerException {
if (!wasQueued[insn]) {
wasQueued[insn] = true;
if (!queued[insn]) {
@@ -0,0 +1,74 @@
/*
* Copyright 2000-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 com.intellij.codeInspection.bytecodeAnalysis.asm;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.org.objectweb.asm.tree.JumpInsnNode;
import org.jetbrains.org.objectweb.asm.tree.LabelNode;
import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException;
import java.util.ArrayList;
import java.util.List;
/**
* @author lambdamix
*/
public class Subroutine {
LabelNode start;
boolean[] access;
List<JumpInsnNode> callers;
private Subroutine() {
}
Subroutine(@Nullable final LabelNode start, final int maxLocals,
@Nullable final JumpInsnNode caller) {
this.start = start;
this.access = new boolean[maxLocals];
this.callers = new ArrayList<JumpInsnNode>();
callers.add(caller);
}
public Subroutine copy() {
Subroutine result = new Subroutine();
result.start = start;
result.access = new boolean[access.length];
System.arraycopy(access, 0, result.access, 0, access.length);
result.callers = new ArrayList<JumpInsnNode>(callers);
return result;
}
public boolean merge(final Subroutine subroutine) throws AnalyzerException {
boolean changes = false;
for (int i = 0; i < access.length; ++i) {
if (subroutine.access[i] && !access[i]) {
access[i] = true;
changes = true;
}
}
if (subroutine.start == start) {
for (int i = 0; i < subroutine.callers.size(); ++i) {
JumpInsnNode caller = subroutine.callers.get(i);
if (!callers.contains(caller)) {
callers.add(caller);
changes = true;
}
}
}
return changes;
}
}
@@ -27,9 +27,11 @@ import com.intellij.psi.util.CachedValuesManager;
import com.intellij.util.Function;
import com.intellij.util.NullableFunction;
import com.intellij.util.containers.ContainerUtil;
import com.siyeh.ig.psiutils.SideEffectChecker;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -129,12 +131,13 @@ class ContractInferenceInterpreter {
return RecursionManager.doPreventingRecursion(myMethod, true, new Computable<List<MethodContract>>() {
@Override
public List<MethodContract> compute() {
List<MethodContract> delegateContracts = ControlFlowAnalyzer.getMethodContracts(targetMethod);
return ContainerUtil.mapNotNull(delegateContracts, new NullableFunction<MethodContract, MethodContract>() {
final boolean notNull = NullableNotNullManager.isNotNull(targetMethod);
final ValueConstraint[] emptyConstraints = MethodContract.createConstraintArray(myMethod.getParameterList().getParametersCount());
List<MethodContract> fromDelegate = ContainerUtil.mapNotNull(ControlFlowAnalyzer.getMethodContracts(targetMethod), new NullableFunction<MethodContract, MethodContract>() {
@Nullable
@Override
public MethodContract fun(MethodContract delegateContract) {
ValueConstraint[] answer = MethodContract.createConstraintArray(myMethod.getParameterList().getParametersCount());
ValueConstraint[] answer = emptyConstraints;
for (int i = 0; i < delegateContract.arguments.length; i++) {
if (i >= arguments.length) return null;
@@ -154,9 +157,17 @@ class ContractInferenceInterpreter {
}
}
}
return answer == null ? null : new MethodContract(answer, negated ? negateConstraint(delegateContract.returnValue) : delegateContract.returnValue);
ValueConstraint returnValue = negated ? negateConstraint(delegateContract.returnValue) : delegateContract.returnValue;
if (notNull && returnValue != THROW_EXCEPTION) {
returnValue = NOT_NULL_VALUE;
}
return answer == null ? null : new MethodContract(answer, returnValue);
}
});
if (notNull) {
return ContainerUtil.concat(fromDelegate, Arrays.asList(new MethodContract(emptyConstraints, NOT_NULL_VALUE)));
}
return fromDelegate;
}
});
}
@@ -213,6 +224,16 @@ class ContractInferenceInterpreter {
}
}
if (expr instanceof PsiNewExpression) {
return toContracts(states, NOT_NULL_VALUE);
}
if (expr instanceof PsiMethodCallExpression) {
PsiMethod method = ((PsiMethodCallExpression)expr).resolveMethod();
if (method != null && NullableNotNullManager.isNotNull(method)) {
return toContracts(states, NOT_NULL_VALUE);
}
}
final ValueConstraint constraint = getLiteralConstraint(expr);
if (constraint != null) {
return toContracts(states, constraint);
@@ -315,7 +336,7 @@ class ContractInferenceInterpreter {
private List<MethodContract> visitStatements(List<ValueConstraint[]> states, PsiStatement... statements) {
List<MethodContract> result = ContainerUtil.newArrayList();
for (PsiStatement statement : statements) {
if (statement instanceof PsiBlockStatement && ((PsiBlockStatement)statement).getCodeBlock().getStatements().length == 1) {
if (statement instanceof PsiBlockStatement) {
result.addAll(visitStatements(states, ((PsiBlockStatement)statement).getCodeBlock().getStatements()));
}
else if (statement instanceof PsiIfStatement) {
@@ -353,12 +374,30 @@ class ContractInferenceInterpreter {
List<MethodContract> conditionResults = visitExpression(states, ((PsiAssertStatement)statement).getAssertCondition());
result.addAll(toContracts(antecedentsOf(filterReturning(conditionResults, FALSE_VALUE)), THROW_EXCEPTION));
}
else if (statement instanceof PsiDeclarationStatement && !mayHaveSideEffects((PsiDeclarationStatement)statement)) {
continue;
}
else if (statement instanceof PsiDoWhileStatement) {
result.addAll(visitStatements(states, ((PsiDoWhileStatement)statement).getBody()));
}
break; // visit only the first statement unless it's 'if' whose 'then' always returns and the next statement is effectively 'else'
}
return result;
}
private static boolean mayHaveSideEffects(PsiDeclarationStatement statement) {
for (PsiElement element : statement.getDeclaredElements()) {
if (element instanceof PsiVariable) {
PsiExpression initializer = ((PsiVariable)element).getInitializer();
if (initializer != null && SideEffectChecker.mayHaveSideEffects(initializer)) {
return true;
}
}
}
return false;
}
private static boolean alwaysReturns(@Nullable PsiStatement statement) {
if (statement instanceof PsiReturnStatement || statement instanceof PsiThrowStatement) return true;
if (statement instanceof PsiBlockStatement) {
@@ -381,6 +420,7 @@ class ContractInferenceInterpreter {
if (expr.textMatches(PsiKeyword.TRUE)) return TRUE_VALUE;
if (expr.textMatches(PsiKeyword.FALSE)) return FALSE_VALUE;
if (expr.textMatches(PsiKeyword.NULL)) return NULL_VALUE;
if (((PsiLiteralExpression)expr).getValue() instanceof String) return NOT_NULL_VALUE;
}
return null;
}
@@ -591,12 +591,14 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
generateBoxingUnboxingInstructionFor(caseExpression, PsiType.INT);
final PsiClass psiClass = PsiUtil.resolveClassInType(caseExpression.getType());
if (psiClass != null && psiClass.isEnum()) {
if (psiClass != null) {
addInstruction(new FieldReferenceInstruction(caseExpression, "switch statement expression"));
enumValues = new HashSet<PsiEnumConstant>();
for (PsiField f : psiClass.getFields()) {
if (f instanceof PsiEnumConstant) {
enumValues.add((PsiEnumConstant)f);
if (psiClass.isEnum()) {
enumValues = new HashSet<PsiEnumConstant>();
for (PsiField f : psiClass.getFields()) {
if (f instanceof PsiEnumConstant) {
enumValues.add((PsiEnumConstant)f);
}
}
}
} else {
@@ -1432,6 +1434,10 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
final PsiAnnotation contractAnno = findContractAnnotation(method);
final int paramCount = method.getParameterList().getParametersCount();
if (contractAnno != null) {
if (AnnotationUtil.isInferredAnnotation(contractAnno) && PsiUtil.canBeOverriden(method)) {
return Collections.emptyList();
}
return CachedValuesManager.getCachedValue(contractAnno, new CachedValueProvider<List<MethodContract>>() {
@Nullable
@Override
@@ -70,7 +70,9 @@ public class StandardInstructionVisitor extends InstructionVisitor {
DfaValueFactory factory = runner.getFactory();
if (dfaSource instanceof DfaVariableValue && factory.getVarFactory().getAllQualifiedBy(var).contains(dfaSource)) {
dfaSource = factory.createTypeValue(((DfaVariableValue)dfaSource).getVariableType(), ((DfaVariableValue)dfaSource).getInherentNullability());
Nullness nullability = memState.isNotNull(dfaSource) ? Nullness.NOT_NULL
: ((DfaVariableValue)dfaSource).getInherentNullability();
dfaSource = factory.createTypeValue(((DfaVariableValue)dfaSource).getVariableType(), nullability);
}
if (var.getInherentNullability() == Nullness.NOT_NULL) {
@@ -19,6 +19,7 @@ import com.intellij.codeInspection.dataFlow.DfaPsiUtil;
import com.intellij.codeInspection.dataFlow.Nullness;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Conditions;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.psi.*;
import com.intellij.psi.impl.JavaConstantExpressionEvaluator;
@@ -50,8 +51,7 @@ public class DfaExpressionFactory {
}
catch (Exception e) {
LOG.error(e);
//noinspection unchecked
return Condition.FALSE;
return Conditions.alwaysFalse();
}
}
@@ -155,7 +155,7 @@ public class DfaExpressionFactory {
@Nullable
private PsiVariable getArrayIndexVariable(@Nullable PsiExpression indexExpression) {
Object constant = JavaConstantExpressionEvaluator.computeConstantExpression(indexExpression, false);
if (constant instanceof Integer) {
if (constant instanceof Integer && ((Integer)constant).intValue() >= 0) {
PsiVariable mockVar = myMockIndices.get(constant);
if (mockVar == null) {
mockVar = JavaPsiFacade.getElementFactory(indexExpression.getProject()).createField("$array$index$" + constant, PsiType.INT);
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
* Copyright 2000-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.
@@ -15,10 +15,7 @@
*/
package com.intellij.codeInspection.equalsAndHashcode;
import com.intellij.codeInspection.BaseJavaBatchLocalInspectionTool;
import com.intellij.codeInspection.InspectionsBundle;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.codeInspection.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
@@ -36,10 +33,10 @@ import org.jetbrains.annotations.Nullable;
/**
* @author max
*/
public class EqualsAndHashcode extends BaseJavaBatchLocalInspectionTool {
public class EqualsAndHashcodeBase extends BaseJavaBatchLocalInspectionTool {
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, final boolean isOnTheFly) {
final Project project = holder.getProject();
Pair<PsiMethod, PsiMethod> pair = CachedValuesManager.getManager(project).getCachedValue(project, new CachedValueProvider<Pair<PsiMethod, PsiMethod>>() {
@Override
@@ -92,7 +89,7 @@ public class EqualsAndHashcode extends BaseJavaBatchLocalInspectionTool {
hasEquals[0]
? InspectionsBundle.message("inspection.equals.hashcode.only.one.defined.problem.descriptor", "<code>equals()</code>", "<code>hashCode()</code>")
: InspectionsBundle.message("inspection.equals.hashcode.only.one.defined.problem.descriptor","<code>hashCode()</code>", "<code>equals()</code>"),
(LocalQuickFix[])null);
buildFixes(isOnTheFly, hasEquals[0]));
}
}
};
@@ -130,4 +127,8 @@ public class EqualsAndHashcode extends BaseJavaBatchLocalInspectionTool {
public String getShortName() {
return "EqualsAndHashcode";
}
protected LocalQuickFix[] buildFixes(boolean isOnTheFly, boolean hasEquals) {
return LocalQuickFix.EMPTY_ARRAY;
}
}
@@ -0,0 +1,29 @@
/*
* Copyright 2000-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 com.intellij.codeInspection.inheritance;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.psi.PsiClass;
import org.jetbrains.annotations.NotNull;
/**
* @author nik
*/
public abstract class ImplementedAtRuntimeCondition {
public static final ExtensionPointName<ImplementedAtRuntimeCondition> EP_NAME = ExtensionPointName.create("com.intellij.codeInsight.implementedAtRuntime");
public abstract boolean isImplementedAtRuntime(@NotNull PsiClass psiClass);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
* Copyright 2000-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.
@@ -540,7 +540,7 @@ public class JavaDocLocalInspectionBase extends BaseJavaBatchLocalInspectionTool
}
if (required && superMethods.length == 0 && isTagRequired(psiMethod, "@throws") && psiMethod.getThrowsList().getReferencedTypes().length > 0) {
final Map<PsiClassType, PsiClass> declaredExceptions = new HashMap<PsiClassType, PsiClass>();
final Map<PsiClassType, PsiClass> declaredExceptions = new LinkedHashMap<PsiClassType, PsiClass>();
final PsiClassType[] classTypes = psiMethod.getThrowsList().getReferencedTypes();
for (PsiClassType classType : classTypes) {
final PsiClass psiClass = classType.resolve();
@@ -45,7 +45,7 @@ public class UnnecessaryModuleDependencyInspection extends GlobalInspectionTool
if (refEntity instanceof RefModule){
final RefModule refModule = (RefModule)refEntity;
final Module module = refModule.getModule();
if (module.isDisposed()) return CommonProblemDescriptor.EMPTY_ARRAY;
if (module.isDisposed() || !scope.containsModule(module)) return CommonProblemDescriptor.EMPTY_ARRAY;
final ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
final OrderEntry[] declaredDependencies = moduleRootManager.getOrderEntries();
final Module[] declaredModuleDependencies = moduleRootManager.getDependencies();
@@ -70,7 +70,7 @@ public class UnusedLibrariesInspection extends GlobalInspectionTool {
if (refEntity instanceof RefModule) {
final RefModule refModule = (RefModule)refEntity;
final Module module = refModule.getModule();
if (module.isDisposed()) return CommonProblemDescriptor.EMPTY_ARRAY;
if (module.isDisposed() || !scope.containsModule(module)) return CommonProblemDescriptor.EMPTY_ARRAY;
final ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
final Set<VirtualFile> usedRoots = refModule.getUserData(UnusedLibraryGraphAnnotator.USED_LIBRARY_ROOTS);
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
* Copyright 2000-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.
@@ -57,6 +57,9 @@ public abstract class BaseClassesAnalysisAction extends BaseAnalysisAction {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
if (project.isDisposed()) {
return;
}
if (!upToDate) {
final int i = Messages.showYesNoCancelDialog(getProject(), AnalysisScopeBundle.message("recompile.confirmation.message"),
AnalysisScopeBundle.message("project.is.out.of.date"), Messages.getWarningIcon());
@@ -1,109 +0,0 @@
/*
* Copyright 2000-2009 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 com.intellij.application.options;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.intellij.psi.codeStyle.FileTypeIndentOptionsProvider;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiUtil;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.pom.java.LanguageLevel;
import org.jetbrains.annotations.NonNls;
/**
* @author yole
*/
public class JavaIndentOptionsProvider implements FileTypeIndentOptionsProvider {
public CommonCodeStyleSettings.IndentOptions createIndentOptions() {
return new CommonCodeStyleSettings.IndentOptions();
}
public FileType getFileType() {
return StdFileTypes.JAVA;
}
public IndentOptionsEditor createOptionsEditor() {
return new JavaIndentOptionsEditor();
}
@NonNls
public String getPreviewText() {
return "public class Foo {\n" +
" public int[] X = new int[] { 1, 3, 5,\n" +
" 7, 9, 11};\n" +
" public void foo(boolean a, int x,\n" +
" int y, int z) {\n" +
" a = x == 0 &&\n" +
" (y == 0 ||\n" +
" z <= 4) &&\n" +
" z >= 0;" +
" label1: do {\n" +
" try {\n" +
" if(x > 0) {\n" +
" int someVariable = a ? \n" +
" x : \n" +
" y;\n" +
" } else if (x < 0) {\n" +
" int someVariable = (y +\n" +
" z\n" +
" );\n" +
" someVariable = x = \n" +
" x +\n" +
" y;\n" +
" } else {\n" +
" label2:\n" +
" for (int i = 0;\n" +
" i < 5;\n" +
" i++) doSomething(i);\n" +
" }\n" +
" switch(a) {\n" +
" case 0: \n" +
" doCase0();\n" +
" break;\n" +
" default: \n" +
" doDefault();\n" +
" }\n" +
" }\n" +
" catch(Exception e) {\n" +
" processException(e.getMessage(),\n" +
" x + y, z, a);\n" +
" }\n" +
" finally {\n" +
" processFinally();\n" +
" }\n" +
" }while(true);\n" +
"\n" +
" if (2 < 3) return;\n" +
" if (3 < 4)\n" +
" return;\n" +
" do x++ while (x < 10000);\n" +
" while (x < 50000) x++;\n" +
" for (int i = 0; i < 5; i++) System.out.println(i);\n" +
" }\n" +
" private class InnerClass implements I1,\n" +
" I2 {\n" +
" public void bar() throws E1,\n" +
" E2 {\n" +
" }\n" +
" }\n" +
"}";
}
public void prepareForReformat(final PsiFile psiFile) {
psiFile.putUserData(PsiUtil.FILE_LANGUAGE_LEVEL_KEY, LanguageLevel.HIGHEST);
}
}
@@ -16,6 +16,7 @@
package com.intellij.codeInsight;
import com.intellij.codeInsight.completion.AllClassesGetter;
import com.intellij.codeInsight.completion.CompletionUtil;
import com.intellij.codeInsight.completion.JavaCompletionUtil;
import com.intellij.codeInsight.completion.PrefixMatcher;
import com.intellij.lang.Language;
@@ -33,21 +34,21 @@ import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.ClassInheritorsSearch;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.psi.util.*;
import com.intellij.psi.util.proximity.PsiProximityComparator;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.util.Consumer;
import com.intellij.util.FilteredQuery;
import com.intellij.util.Processor;
import com.intellij.util.Query;
import com.intellij.psi.util.FileTypeUtils;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
public class CodeInsightUtil {
@Nullable
@@ -283,6 +284,11 @@ public class CodeInsightUtil {
final Processor<PsiClass> inheritorsProcessor =
createInheritorsProcessor(context, baseType, arrayDim, getRawSubtypes, consumer, baseClass, baseSubstitutor);
addContextTypeArguments(context, baseType, inheritorsProcessor);
if (baseClass.hasModifierProperty(PsiModifier.FINAL)) return;
if (matcher.getPrefix().length() > 2) {
AllClassesGetter.processJavaClasses(matcher, context.getProject(), scope, new Processor<PsiClass>() {
@Override
@@ -307,6 +313,30 @@ public class CodeInsightUtil {
}
private static void addContextTypeArguments(final PsiElement context,
final PsiClassType baseType,
final Processor<PsiClass> inheritorsProcessor) {
ApplicationManager.getApplication().runReadAction(new Runnable() {
@Override
public void run() {
Set<String> usedNames = ContainerUtil.newHashSet();
PsiElementFactory factory = JavaPsiFacade.getElementFactory(context.getProject());
PsiElement each = context;
while (true) {
PsiTypeParameterListOwner typed = PsiTreeUtil.getParentOfType(each, PsiTypeParameterListOwner.class);
if (typed == null) break;
for (PsiTypeParameter parameter : typed.getTypeParameters()) {
if (baseType.isAssignableFrom(factory.createType(parameter)) && usedNames.add(parameter.getName())) {
inheritorsProcessor.process(CompletionUtil.getOriginalOrSelf(parameter));
}
}
each = typed;
}
}
});
}
public static Processor<PsiClass> createInheritorsProcessor(final PsiElement context, final PsiClassType baseType,
final int arrayDim,
final boolean getRawSubtypes,
@@ -51,14 +51,14 @@ public class ExternalAnnotationsLineMarkerProvider implements LineMarkerProvider
if (!(element instanceof PsiModifierListOwner)) return null;
if (element instanceof PsiParameter || element instanceof PsiLocalVariable) return null;
if (!shouldShowSignature(preferCompiledElement((PsiModifierListOwner)element))) {
if (!shouldShowSignature((PsiModifierListOwner)element)) {
return null;
}
final Function<PsiModifierListOwner, String> annotationsCollector = new Function<PsiModifierListOwner, String>() {
@Override
public String fun(PsiModifierListOwner owner) {
return XmlStringUtil.wrapInHtml(JavaDocInfoGenerator.generateSignature(preferCompiledElement(owner)));
return XmlStringUtil.wrapInHtml(JavaDocInfoGenerator.generateSignature(owner));
}
};
return new LineMarkerInfo<PsiModifierListOwner>((PsiModifierListOwner)element, element.getTextOffset(), AllIcons.Gutter.ExtAnnotation,
@@ -67,11 +67,6 @@ public class ExternalAnnotationsLineMarkerProvider implements LineMarkerProvider
GutterIconRenderer.Alignment.LEFT);
}
private static PsiModifierListOwner preferCompiledElement(PsiModifierListOwner element) {
PsiElement original = element.getOriginalElement();
return original instanceof PsiModifierListOwner ? (PsiModifierListOwner)original : element;
}
private static boolean shouldShowSignature(PsiModifierListOwner owner) {
if (hasNonCodeAnnotations(owner)) {
return true;
@@ -91,10 +86,27 @@ public class ExternalAnnotationsLineMarkerProvider implements LineMarkerProvider
private static boolean hasNonCodeAnnotations(@NotNull PsiModifierListOwner element) {
Project project = element.getProject();
PsiAnnotation[] externalAnnotations = ExternalAnnotationsManager.getInstance(project).findExternalAnnotations(element);
if (externalAnnotations != null && externalAnnotations.length > 0) {
return true;
if (externalAnnotations != null) {
for (PsiAnnotation annotation : externalAnnotations) {
if (isVisibleAnnotation(annotation)) {
return true;
}
}
}
return InferredAnnotationsManager.getInstance(project).findInferredAnnotations(element).length > 0;
for (PsiAnnotation annotation : InferredAnnotationsManager.getInstance(project).findInferredAnnotations(element)) {
if (isVisibleAnnotation(annotation)) {
return true;
}
}
return false;
}
private static boolean isVisibleAnnotation(@NotNull PsiAnnotation annotation) {
PsiJavaCodeReferenceElement ref = annotation.getNameReferenceElement();
if (ref == null) return true;
PsiElement target = ref.resolve();
return !(target instanceof PsiClass) || JavaDocInfoGenerator.isDocumentedAnnotationType(target);
}
@Override
@@ -484,7 +484,7 @@ public class JavaCompletionData extends JavaAwareCompletionData {
}
}
if ((isInsideParameterList(position) || isAtResourceVariableStart(position)) &&
if ((isInsideParameterList(position) || isAtResourceVariableStart(position) || isAtCatchVariableStart(position)) &&
!psiElement().afterLeaf(PsiKeyword.FINAL).accepts(position) &&
!AFTER_DOT.accepts(position)) {
result.addElement(TailTypeDecorator.withTail(createKeyword(position, PsiKeyword.FINAL), TailType.HUMBLE_SPACE_BEFORE_WORD));
@@ -673,6 +673,10 @@ public class JavaCompletionData extends JavaAwareCompletionData {
return psiElement().insideStarting(psiElement(PsiTypeElement.class).withParent(PsiResourceList.class)).accepts(position);
}
private static boolean isAtCatchVariableStart(PsiElement position) {
return psiElement().insideStarting(psiElement(PsiTypeElement.class).withParent(PsiCatchSection.class)).accepts(position);
}
private static void addBreakContinue(CompletionResultSet result, PsiElement position) {
PsiLoopStatement loop = PsiTreeUtil.getParentOfType(position, PsiLoopStatement.class);
@@ -278,10 +278,12 @@ public class JavaCompletionSorting {
@NotNull
@Override
public Comparable weigh(@NotNull LookupElement item) {
public MyResult weigh(@NotNull LookupElement item) {
final Object object = item.getObject();
if (object instanceof PsiClass) {
if (object instanceof PsiTypeParameter) return MyResult.typeParameter;
if (myTypeParameter != null && object.equals(PsiUtil.resolveClassInType(TypeConversionUtil.typeParameterErasure(myTypeParameter)))) {
return MyResult.exactlyExpected;
}
@@ -340,6 +342,7 @@ public class JavaCompletionSorting {
private enum MyResult {
expectedNoSelect,
typeParameter,
exactlyDefault,
ofDefaultType,
exactlyExpected,

Some files were not shown because too many files have changed in this diff Show More