compiling evaluator fixes:

- use extract method
- enable only when needed
This commit is contained in:
Egor.Ushakov
2014-09-08 20:31:16 +04:00
parent 1181b95bde
commit 128fd40945
5 changed files with 219 additions and 31 deletions
@@ -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");
@@ -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;
}
}
@@ -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) {
@@ -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()));
}
@@ -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;