mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
compiling evaluator fixes:
- use extract method - enable only when needed
This commit is contained in:
@@ -20,15 +20,21 @@ import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
|
||||
import com.intellij.debugger.engine.evaluation.TextWithImportsImpl;
|
||||
import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
|
||||
import com.intellij.debugger.impl.EditorTextProvider;
|
||||
import com.intellij.debugger.ui.impl.watch.CompilingEvaluator;
|
||||
import com.intellij.debugger.ui.impl.watch.WatchItemDescriptor;
|
||||
import com.intellij.openapi.command.CommandProcessor;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.psi.PsiCodeFragment;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.refactoring.extractMethod.PrepareFailedException;
|
||||
import com.intellij.refactoring.extractMethodObject.ExtractLightMethodObjectHandler;
|
||||
import com.intellij.xdebugger.XExpression;
|
||||
import com.intellij.xdebugger.XSourcePosition;
|
||||
import com.intellij.xdebugger.evaluation.XDebuggerEvaluator;
|
||||
@@ -59,6 +65,29 @@ public class JavaDebuggerEvaluator extends XDebuggerEvaluator {
|
||||
public void evaluate(@NotNull final XExpression expression,
|
||||
@NotNull final XEvaluationCallback callback,
|
||||
@Nullable XSourcePosition expressionPosition) {
|
||||
final Project project = myDebugProcess.getProject();
|
||||
final WatchItemDescriptor descriptor = new WatchItemDescriptor(project, TextWithImportsImpl.fromXExpression(expression));
|
||||
if (Registry.is("debugger.compiling.evaluator")) {
|
||||
try {
|
||||
CommandProcessor.getInstance().executeCommand(project, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
PsiElement element = myDebugProcess.getDebuggerContext().getContextElement();
|
||||
PsiFile psiFile = element.getContainingFile();
|
||||
PsiCodeFragment fragment = descriptor.createCodeFragment(element);
|
||||
PsiDocumentManager.getInstance(project).commitAllDocuments();
|
||||
ExtractLightMethodObjectHandler.ExtractedData data = null;
|
||||
try {
|
||||
data = ExtractLightMethodObjectHandler.extractLightMethodObject(project, psiFile, fragment, "test");
|
||||
}
|
||||
catch (PrepareFailedException ignore) {
|
||||
}
|
||||
descriptor.putUserData(CompilingEvaluator.COMPILING_EVALUATOR_DATA, data);
|
||||
}
|
||||
}, "asdjka", null);
|
||||
}
|
||||
catch (Exception ignore) {}
|
||||
}
|
||||
myDebugProcess.getManagerThread().schedule(new DebuggerContextCommandImpl(myDebugProcess.getDebuggerContext()) {
|
||||
@Override
|
||||
public Priority getPriority() {
|
||||
@@ -67,8 +96,6 @@ public class JavaDebuggerEvaluator extends XDebuggerEvaluator {
|
||||
|
||||
@Override
|
||||
public void threadAction() {
|
||||
WatchItemDescriptor descriptor = new WatchItemDescriptor(myDebugProcess.getProject(), TextWithImportsImpl.fromXExpression(
|
||||
expression));
|
||||
EvaluationContextImpl evalContext = myStackFrame.getFrameDebuggerContext().createEvaluationContext();
|
||||
if (evalContext == null) {
|
||||
callback.errorOccurred("Context is not available");
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.engine.evaluation.expression;
|
||||
|
||||
import com.intellij.debugger.DebuggerBundle;
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateException;
|
||||
|
||||
/**
|
||||
* @author egor
|
||||
*/
|
||||
public class AnonymousClassException extends EvaluateException {
|
||||
private static final AnonymousClassException INSTANCE = new AnonymousClassException();
|
||||
|
||||
public AnonymousClassException() {
|
||||
super(DebuggerBundle.message("evaluation.error.anonymous.class.evaluation.not.supported"));
|
||||
}
|
||||
|
||||
public static AnonymousClassException getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1146,7 +1146,7 @@ public class EvaluatorBuilderImpl implements EvaluatorBuilder {
|
||||
else if (expressionPsiType instanceof PsiClassType){ // must be a class ref
|
||||
PsiClass aClass = ((PsiClassType)expressionPsiType).resolve();
|
||||
if(aClass instanceof PsiAnonymousClass) {
|
||||
throwEvaluateException(DebuggerBundle.message("evaluation.error.anonymous.class.evaluation.not.supported"));
|
||||
throw new EvaluateRuntimeException(AnonymousClassException.getInstance());
|
||||
}
|
||||
PsiExpressionList argumentList = expression.getArgumentList();
|
||||
if (argumentList == null) {
|
||||
|
||||
+136
-22
@@ -15,28 +15,55 @@
|
||||
*/
|
||||
package com.intellij.debugger.ui.impl.watch;
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightUtil;
|
||||
import com.intellij.debugger.DebuggerInvocationUtil;
|
||||
import com.intellij.debugger.EvaluatingComputable;
|
||||
import com.intellij.debugger.engine.ContextUtil;
|
||||
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.*;
|
||||
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.jdi.VirtualMachineProxyImpl;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.refactoring.extractMethodObject.ExtractLightMethodObjectHandler;
|
||||
import com.sun.jdi.*;
|
||||
|
||||
import javax.tools.*;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.net.URI;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* @author egor
|
||||
*/
|
||||
class CompilingEvaluator implements ExpressionEvaluator {
|
||||
public class CompilingEvaluator implements ExpressionEvaluator {
|
||||
private final TextWithImports myText;
|
||||
private final PsiCodeFragment myCodeFragment;
|
||||
private final PsiElement myPsiContext;
|
||||
private final ExtractLightMethodObjectHandler.ExtractedData myData;
|
||||
|
||||
public CompilingEvaluator(TextWithImports text) {
|
||||
public static Key<ExtractLightMethodObjectHandler.ExtractedData> COMPILING_EVALUATOR_DATA = new Key<ExtractLightMethodObjectHandler.ExtractedData>("COMPILING_EVALUATOR_DATA");
|
||||
private final EvaluationDescriptor myDescriptor;
|
||||
|
||||
public CompilingEvaluator(TextWithImports text,
|
||||
PsiCodeFragment codeFragment,
|
||||
PsiElement context,
|
||||
ExtractLightMethodObjectHandler.ExtractedData data,
|
||||
EvaluationDescriptor descriptor) {
|
||||
myText = text;
|
||||
myCodeFragment = codeFragment;
|
||||
myPsiContext = context;
|
||||
myData = data;
|
||||
myDescriptor = descriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -49,20 +76,44 @@ class CompilingEvaluator implements ExpressionEvaluator {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value evaluate(EvaluationContext context) throws EvaluateException {
|
||||
try {
|
||||
DebugProcess process = context.getDebugProcess();
|
||||
ThreadReference threadReference = context.getSuspendContext().getThread().getThreadReference();
|
||||
private TextWithImports getCallCode() {
|
||||
return new TextWithImportsImpl(CodeFragmentKind.CODE_BLOCK, myData.getGeneratedCallText());
|
||||
}
|
||||
|
||||
ClassLoaderReference classLoader = getClassLoader(context);
|
||||
@Override
|
||||
public Value evaluate(final EvaluationContext evaluationContext) throws EvaluateException {
|
||||
try {
|
||||
DebugProcess process = evaluationContext.getDebugProcess();
|
||||
ThreadReference threadReference = evaluationContext.getSuspendContext().getThread().getThreadReference();
|
||||
|
||||
ClassLoaderReference classLoader = getClassLoader(evaluationContext);
|
||||
|
||||
Collection<OutputFileObject> classes = compile();
|
||||
|
||||
ClassType mainClass = defineClasses(classes, context, process, threadReference, classLoader);
|
||||
ClassType mainClass = defineClasses(classes, evaluationContext, process, threadReference, classLoader);
|
||||
|
||||
Method foo = mainClass.methodsByName(GEN_METHOD_NAME).get(0);
|
||||
return mainClass.invokeMethod(threadReference, foo, Collections.<Value>emptyList() ,ClassType.INVOKE_SINGLE_THREADED);
|
||||
//Method foo = mainClass.methodsByName(GEN_METHOD_NAME).get(0);
|
||||
//return mainClass.invokeMethod(threadReference, foo, Collections.<Value>emptyList() ,ClassType.INVOKE_SINGLE_THREADED);
|
||||
|
||||
// invoke base evaluator on call code
|
||||
final Project project = myPsiContext.getProject();
|
||||
ExpressionEvaluator evaluator =
|
||||
DebuggerInvocationUtil.commitAndRunReadAction(project, new EvaluatingComputable<ExpressionEvaluator>() {
|
||||
@Override
|
||||
public ExpressionEvaluator compute() throws EvaluateException {
|
||||
final TextWithImports callCode = getCallCode();
|
||||
PsiFile file = myData.getGeneratedInnerClass().getContainingFile();
|
||||
final TextRange range = myPsiContext.getTextRange();
|
||||
final PsiElement copyContext =
|
||||
CodeInsightUtil.findElementInRange(file, range.getStartOffset(), range.getEndOffset(), myPsiContext.getClass());
|
||||
final CodeFragmentFactory factory = DebuggerUtilsEx.findAppropriateCodeFragmentFactory(callCode, copyContext);
|
||||
return factory.getEvaluatorBuilder().
|
||||
build(factory.createCodeFragment(callCode, copyContext, project),
|
||||
ContextUtil.getSourcePosition(evaluationContext));
|
||||
}
|
||||
});
|
||||
((EvaluationContextImpl)evaluationContext).setClassLoader(classLoader);
|
||||
return evaluator.evaluate(evaluationContext);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new EvaluateException(e.getMessage());
|
||||
@@ -74,13 +125,13 @@ class CompilingEvaluator implements ExpressionEvaluator {
|
||||
// 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");
|
||||
Method ctorMethod = loaderClass.concreteMethodByName("<init>", "([Ljava/net/URL;Ljava/lang/ClassLoader;)V");
|
||||
ThreadReference threadReference = context.getSuspendContext().getThread().getThreadReference();
|
||||
return (ClassLoaderReference)loaderClass.newInstance(threadReference, ctorMethod,
|
||||
Arrays.asList(createURLArray(context)), ClassType.INVOKE_SINGLE_THREADED);
|
||||
Arrays.asList(createURLArray(context), context.getClassLoader()), ClassType.INVOKE_SINGLE_THREADED);
|
||||
}
|
||||
|
||||
private static ClassType defineClasses(Collection<OutputFileObject> classes,
|
||||
private ClassType defineClasses(Collection<OutputFileObject> classes,
|
||||
EvaluationContext context,
|
||||
DebugProcess process,
|
||||
ThreadReference threadReference,
|
||||
@@ -98,7 +149,7 @@ class CompilingEvaluator implements ExpressionEvaluator {
|
||||
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);
|
||||
return (ClassType)process.findClass(context, getGenPackageName() + '.' + getGenClassName(), classLoader);
|
||||
}
|
||||
|
||||
private static ArrayReference mirrorOf(byte[] bytes, EvaluationContext context, DebugProcess process)
|
||||
@@ -112,10 +163,73 @@ class CompilingEvaluator implements ExpressionEvaluator {
|
||||
return reference;
|
||||
}
|
||||
|
||||
private static final String GEN_CLASS_NAME = "Evaluator";
|
||||
private static final String GEN_CLASS_NAME = "Test";
|
||||
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 final String GEN_METHOD_NAME = "invoke";
|
||||
|
||||
private String getClassCode() {
|
||||
if (myData != null) {
|
||||
return ApplicationManager.getApplication().runReadAction(new Computable<String>() {
|
||||
@Override
|
||||
public String compute() {
|
||||
//String text = myData.getGeneratedInnerClass().getText();
|
||||
////TODO: remove
|
||||
//String prefix = "public static";
|
||||
//if (text.startsWith(prefix)) {
|
||||
// text = "public" + text.substring(prefix.length());
|
||||
//}
|
||||
//PsiElement[] children = ((PsiJavaFile)myPsiContext.getContainingFile()).getImportList().getChildren();
|
||||
//StringBuilder imports = new StringBuilder();
|
||||
//for (PsiElement child : children) {
|
||||
// if (child instanceof PsiImportStatement) {
|
||||
// String name = ((PsiImportStatement)child).getImportReference().getQualifiedName();
|
||||
// imports.append("import ").append(name).append(";");
|
||||
// }
|
||||
//}
|
||||
//text = text.replace("class " + GEN_CLASS_NAME, "class " + getGenClassName());
|
||||
//text = text.replace(GEN_CLASS_NAME + "(", getGenClassName() + "(");
|
||||
//text = text.replace(((PsiClass)myData.getGeneratedInnerClass().getParent()).getName() + "." + GEN_CLASS_NAME, getGenClassName());
|
||||
//return "package " + getGenPackageName() + "; " + imports.toString() + text;
|
||||
return myData.getGeneratedInnerClass().getContainingFile().getText();
|
||||
}
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getGenPackageName() {
|
||||
return ApplicationManager.getApplication().runReadAction(new Computable<String>() {
|
||||
@Override
|
||||
public String compute() {
|
||||
return ((PsiJavaFile)myData.getGeneratedInnerClass().getContainingFile()).getPackageName();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String getMainClassName() {
|
||||
return ((PsiClass)myData.getGeneratedInnerClass().getParent()).getName();
|
||||
}
|
||||
|
||||
private String getGenClassName() {
|
||||
return getMainClassName() + '$' + GEN_CLASS_NAME;
|
||||
}
|
||||
|
||||
//private String createClassCode() {
|
||||
// return ApplicationManager.getApplication().runReadAction(new Computable<String>() {
|
||||
// @Override
|
||||
// public String compute() {
|
||||
// try {
|
||||
// myExtractedData =
|
||||
// ExtractLightMethodObjectHandler.extractLightMethodObject(myCodeFragment.getProject(), myFile , myCodeFragment, "test");
|
||||
// }
|
||||
// catch (PrepareFailedException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
// });
|
||||
//}
|
||||
|
||||
private static String createClassCode(TextWithImports body) {
|
||||
StringBuilder text = new StringBuilder();
|
||||
@@ -155,7 +269,7 @@ class CompilingEvaluator implements ExpressionEvaluator {
|
||||
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()) {
|
||||
.asList(new SourceFileObject(getMainClassName(), JavaFileObject.Kind.SOURCE, getClassCode()))).call()) {
|
||||
// TODO: show only errors
|
||||
throw new EvaluateException(diagnostic.getDiagnostics().get(0).getMessage(Locale.getDefault()));
|
||||
}
|
||||
|
||||
+19
-6
@@ -25,6 +25,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.engine.evaluation.TextWithImports;
|
||||
import com.intellij.debugger.engine.evaluation.expression.AnonymousClassException;
|
||||
import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator;
|
||||
import com.intellij.debugger.engine.evaluation.expression.Modifier;
|
||||
import com.intellij.debugger.impl.DebuggerUtilsEx;
|
||||
@@ -38,6 +39,7 @@ import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiExpression;
|
||||
import com.intellij.psi.PsiExpressionCodeFragment;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.refactoring.extractMethodObject.ExtractLightMethodObjectHandler;
|
||||
import com.sun.jdi.ObjectCollectedException;
|
||||
import com.sun.jdi.ObjectReference;
|
||||
import com.sun.jdi.Value;
|
||||
@@ -64,7 +66,7 @@ public abstract class EvaluationDescriptor extends ValueDescriptorImpl{
|
||||
|
||||
protected abstract PsiCodeFragment getEvaluationCode(StackFrameContext context) throws EvaluateException;
|
||||
|
||||
protected PsiCodeFragment createCodeFragment(PsiElement context) {
|
||||
public PsiCodeFragment createCodeFragment(PsiElement context) {
|
||||
TextWithImports text = getEvaluationText();
|
||||
final PsiCodeFragment fragment =
|
||||
DebuggerUtilsEx.findAppropriateCodeFragmentFactory(text, context).createCodeFragment(text, context, myProject);
|
||||
@@ -76,11 +78,9 @@ public abstract class EvaluationDescriptor extends ValueDescriptorImpl{
|
||||
try {
|
||||
final EvaluationContextImpl thisEvaluationContext = getEvaluationContext(evaluationContext);
|
||||
|
||||
final ExpressionEvaluator evaluator;
|
||||
if (Registry.is("debugger.compiling.evaluator")) {
|
||||
evaluator = new CompilingEvaluator(getEvaluationText());
|
||||
}
|
||||
else {
|
||||
ExpressionEvaluator evaluator;
|
||||
final ExtractLightMethodObjectHandler.ExtractedData data = getUserData(CompilingEvaluator.COMPILING_EVALUATOR_DATA);
|
||||
try {
|
||||
evaluator = DebuggerInvocationUtil.commitAndRunReadAction(myProject, new EvaluatingComputable<ExpressionEvaluator>() {
|
||||
public ExpressionEvaluator compute() throws EvaluateException {
|
||||
final PsiElement psiContext = PositionUtil.getContextElement(evaluationContext);
|
||||
@@ -89,6 +89,19 @@ public abstract class EvaluationDescriptor extends ValueDescriptorImpl{
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (AnonymousClassException ex) {
|
||||
if (Registry.is("debugger.compiling.evaluator") && data != null) {
|
||||
evaluator = DebuggerInvocationUtil.commitAndRunReadAction(myProject, new EvaluatingComputable<ExpressionEvaluator>() {
|
||||
public ExpressionEvaluator compute() throws EvaluateException {
|
||||
final PsiElement psiContext = PositionUtil.getContextElement(evaluationContext);
|
||||
return new CompilingEvaluator(getEvaluationText(), getEvaluationCode(thisEvaluationContext), psiContext, data, EvaluationDescriptor.this);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
if (!thisEvaluationContext.getDebugProcess().isAttached()) {
|
||||
throw EvaluateExceptionUtil.PROCESS_EXITED;
|
||||
|
||||
Reference in New Issue
Block a user