refactoring to prepare compiling evaluator caching

This commit is contained in:
Egor.Ushakov
2016-10-25 13:27:33 +03:00
parent 54a62c5bf2
commit 5fbd5fb220
4 changed files with 88 additions and 95 deletions
@@ -32,7 +32,7 @@ import com.intellij.debugger.jdi.StackFrameProxyImpl;
import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
import com.intellij.debugger.requests.ClassPrepareRequestor;
import com.intellij.debugger.settings.DebuggerSettings;
import com.intellij.debugger.ui.impl.watch.EvaluationDescriptor;
import com.intellij.debugger.ui.impl.watch.CompilingEvaluatorImpl;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
@@ -245,7 +245,7 @@ public abstract class Breakpoint<P extends JavaBreakpointProperties> implements
}
throw new EventProcessingException(title[0], ex.getMessage(), ex);
}
}
return true;
}
@@ -269,7 +269,7 @@ public abstract class Breakpoint<P extends JavaBreakpointProperties> implements
SourcePosition position = ContextUtil.getSourcePosition(context);
PsiElement element = ContextUtil.getContextElement(context, position);
ExpressionEvaluator evaluator = DebuggerInvocationUtil.commitAndRunReadAction(myProject, () ->
createExpressionEvaluator(myProject, context, element, position, expressionToEvaluate, this::createLogMessageCodeFragment));
createExpressionEvaluator(myProject, element, position, expressionToEvaluate, this::createLogMessageCodeFragment));
Value eval = evaluator.evaluate(context);
buf.append(eval instanceof VoidValue ? "void" : DebuggerUtils.getValueAsString(context, eval));
}
@@ -342,9 +342,8 @@ public abstract class Breakpoint<P extends JavaBreakpointProperties> implements
}
try {
Project project = context.getProject();
SourcePosition contextSourcePosition = ContextUtil.getSourcePosition(context);
ExpressionEvaluator evaluator = DebuggerInvocationUtil.commitAndRunReadAction(project, () -> {
ExpressionEvaluator evaluator = DebuggerInvocationUtil.commitAndRunReadAction(myProject, () -> {
// IMPORTANT: calculate context psi element basing on the location where the exception
// has been hit, not on the location where it was set. (For line breakpoints these locations are the same, however,
// for method, exception and field breakpoints these locations differ)
@@ -352,7 +351,7 @@ public abstract class Breakpoint<P extends JavaBreakpointProperties> implements
if (contextPsiElement == null) {
contextPsiElement = getEvaluationElement(); // as a last resort
}
return createExpressionEvaluator(project, context, contextPsiElement, contextSourcePosition, getCondition(),
return createExpressionEvaluator(myProject, contextPsiElement, contextSourcePosition, getCondition(),
this::createConditionCodeFragment);
});
return DebuggerUtilsEx.evaluateBoolean(evaluator, context);
@@ -368,7 +367,6 @@ public abstract class Breakpoint<P extends JavaBreakpointProperties> implements
}
private static ExpressionEvaluator createExpressionEvaluator(Project project,
EvaluationContextImpl context,
PsiElement contextPsiElement,
SourcePosition contextSourcePosition,
TextWithImports text,
@@ -378,7 +376,7 @@ public abstract class Breakpoint<P extends JavaBreakpointProperties> implements
return EvaluatorBuilderImpl.build(text, contextPsiElement, contextSourcePosition, project);
}
catch (UnsupportedExpressionException ex) {
ExpressionEvaluator eval = EvaluationDescriptor.createCompilingEvaluator(context, contextPsiElement, fragmentFactory);
ExpressionEvaluator eval = CompilingEvaluatorImpl.create(project, contextPsiElement, fragmentFactory);
if (eval != null) {
return eval;
}
@@ -387,11 +385,11 @@ public abstract class Breakpoint<P extends JavaBreakpointProperties> implements
}
private PsiCodeFragment createConditionCodeFragment(PsiElement context) {
return createCodeFragment(getProject(), getCondition(), context);
return createCodeFragment(myProject, getCondition(), context);
}
private PsiCodeFragment createLogMessageCodeFragment(PsiElement context) {
return createCodeFragment(getProject(), getLogMessage(), context);
return createCodeFragment(myProject, getLogMessage(), context);
}
private static PsiCodeFragment createCodeFragment(Project project, TextWithImports text, PsiElement context) {
@@ -51,10 +51,12 @@ import java.util.Collection;
* @author egor
*/
public abstract class CompilingEvaluator implements ExpressionEvaluator {
@NotNull protected final Project myProject;
@NotNull protected final PsiElement myPsiContext;
@NotNull protected final ExtractLightMethodObjectHandler.ExtractedData myData;
public CompilingEvaluator(@NotNull PsiElement context, @NotNull ExtractLightMethodObjectHandler.ExtractedData data) {
public CompilingEvaluator(@NotNull Project project, @NotNull PsiElement context, @NotNull ExtractLightMethodObjectHandler.ExtractedData data) {
myProject = project;
myPsiContext = context;
myData = data;
}
@@ -86,16 +88,15 @@ public abstract class CompilingEvaluator implements ExpressionEvaluator {
try {
// invoke base evaluator on call code
Project project = ApplicationManager.getApplication().runReadAction((Computable<Project>)myPsiContext::getProject);
SourcePosition position = ContextUtil.getSourcePosition(evaluationContext);
ExpressionEvaluator evaluator =
DebuggerInvocationUtil.commitAndRunReadAction(project, new EvaluatingComputable<ExpressionEvaluator>() {
DebuggerInvocationUtil.commitAndRunReadAction(myProject, new EvaluatingComputable<ExpressionEvaluator>() {
@Override
public ExpressionEvaluator compute() throws EvaluateException {
TextWithImports callCode = getCallCode();
PsiElement copyContext = myData.getAnchor();
CodeFragmentFactory factory = DebuggerUtilsEx.findAppropriateCodeFragmentFactory(callCode, copyContext);
return factory.getEvaluatorBuilder().build(factory.createCodeFragment(callCode, copyContext, project), position);
return factory.getEvaluatorBuilder().build(factory.createCodeFragment(callCode, copyContext, myProject), position);
}
});
((EvaluationContextImpl)evaluationContext).setClassLoader(classLoader);
@@ -148,12 +149,8 @@ public abstract class CompilingEvaluator implements ExpressionEvaluator {
protected String getGenClassQName() {
return ApplicationManager.getApplication().runReadAction(new Computable<String>() {
@Override
public String compute() {
return JVMNameUtil.getNonAnonymousClassName(myData.getGeneratedInnerClass());
}
});
return ApplicationManager.getApplication().runReadAction(
(Computable<String>)() -> JVMNameUtil.getNonAnonymousClassName(myData.getGeneratedInnerClass()));
}
///////////////// Compiler stuff
@@ -18,7 +18,7 @@ package com.intellij.debugger.ui.impl.watch;
import com.intellij.compiler.CompilerConfiguration;
import com.intellij.compiler.server.BuildManager;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.compiler.ClassObject;
import com.intellij.openapi.compiler.CompilationException;
@@ -26,15 +26,20 @@ import com.intellij.openapi.compiler.CompilerManager;
import com.intellij.openapi.compiler.CompilerMessageCategory;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.JavaSdkVersion;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.ThrowableComputable;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.PsiCodeFragment;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.refactoring.extractMethod.PrepareFailedException;
import com.intellij.refactoring.extractMethodObject.ExtractLightMethodObjectHandler;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -44,35 +49,31 @@ import org.jetbrains.jps.model.java.compiler.AnnotationProcessingConfiguration;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.function.Function;
// todo: consider batching compilations in order not to start a separate process for every class that needs to be compiled
public class CompilingEvaluatorImpl extends CompilingEvaluator {
private final EvaluationContextImpl myEvaluationContext;
public CompilingEvaluatorImpl(EvaluationContextImpl evaluationContext, @NotNull PsiElement context, @NotNull ExtractLightMethodObjectHandler.ExtractedData data) {
super(context, data);
myEvaluationContext = evaluationContext;
public CompilingEvaluatorImpl(@NotNull Project project,
@NotNull PsiElement context,
@NotNull ExtractLightMethodObjectHandler.ExtractedData data) {
super(project, context, data);
}
@Override
@NotNull
protected Collection<ClassObject> compile(@Nullable JavaSdkVersion debuggeeVersion) throws EvaluateException {
final Module module = ApplicationManager.getApplication().runReadAction(new Computable<Module>() {
@Override
public Module compute() {
return ModuleUtilCore.findModuleForPsiElement(myPsiContext);
}
});
final List<String> options = new ArrayList<>();
Module module = ApplicationManager.getApplication().runReadAction(
(Computable<Module>)() -> ModuleUtilCore.findModuleForPsiElement(myPsiContext));
List<String> options = new ArrayList<>();
options.add("-encoding");
options.add("UTF-8");
final List<File> platformClasspath = new ArrayList<>();
final List<File> classpath = new ArrayList<>();
List<File> platformClasspath = new ArrayList<>();
List<File> classpath = new ArrayList<>();
AnnotationProcessingConfiguration profile = null;
if (module != null) {
profile = CompilerConfiguration.getInstance(module.getProject()).getAnnotationProcessingConfiguration(module);
final ModuleRootManager rootManager = ModuleRootManager.getInstance(module);
assert myProject.equals(module.getProject()) : module + " is from another project";
profile = CompilerConfiguration.getInstance(myProject).getAnnotationProcessingConfiguration(module);
ModuleRootManager rootManager = ModuleRootManager.getInstance(module);
for (String s : rootManager.orderEntries().compileOnly().recursively().exportedOnly().withoutSdk().getPathsList().getPathList()) {
classpath.add(new File(s));
}
@@ -82,31 +83,31 @@ public class CompilingEvaluatorImpl extends CompilingEvaluator {
}
JavaBuilder.addAnnotationProcessingOptions(options, profile);
final Pair<Sdk, JavaSdkVersion> runtime = BuildManager.getJavacRuntimeSdk(myEvaluationContext.getProject());
final JavaSdkVersion buildRuntimeVersion = runtime.getSecond();
Pair<Sdk, JavaSdkVersion> runtime = BuildManager.getJavacRuntimeSdk(myProject);
JavaSdkVersion buildRuntimeVersion = runtime.getSecond();
// if compiler or debuggee version or both are unknown, let source and target be the compiler's defaults
if (buildRuntimeVersion != null && debuggeeVersion != null) {
final JavaSdkVersion minVersion = buildRuntimeVersion.ordinal() > debuggeeVersion.ordinal() ? debuggeeVersion : buildRuntimeVersion;
final String sourceOption = getSourceOption(minVersion.getMaxLanguageLevel());
JavaSdkVersion minVersion = buildRuntimeVersion.ordinal() > debuggeeVersion.ordinal() ? debuggeeVersion : buildRuntimeVersion;
String sourceOption = getSourceOption(minVersion.getMaxLanguageLevel());
options.add("-source");
options.add(sourceOption);
options.add("-target");
options.add(sourceOption);
}
final CompilerManager compilerManager = CompilerManager.getInstance(myEvaluationContext.getProject());
CompilerManager compilerManager = CompilerManager.getInstance(myProject);
File sourceFile = null;
try {
sourceFile = generateTempSourceFile(compilerManager.getJavacCompilerWorkingDir());
final File srcDir = sourceFile.getParentFile();
final List<File> sourcePath = Collections.emptyList();
final Set<File> sources = Collections.singleton(sourceFile);
File srcDir = sourceFile.getParentFile();
List<File> sourcePath = Collections.emptyList();
Set<File> sources = Collections.singleton(sourceFile);
return compilerManager.compileJavaCode(options, platformClasspath, classpath, Collections.emptyList(), sourcePath, sources, srcDir);
}
catch (CompilationException e) {
final StringBuilder res = new StringBuilder("Compilation failed:\n");
StringBuilder res = new StringBuilder("Compilation failed:\n");
for (CompilationException.Message m : e.getMessages()) {
if (m.getCategory() == CompilerMessageCategory.ERROR) {
res.append(m.getText()).append("\n");
@@ -130,7 +131,7 @@ public class CompilingEvaluatorImpl extends CompilingEvaluator {
}
private File generateTempSourceFile(File workingDir) throws IOException {
final Pair<String, String> fileData = ApplicationManager.getApplication().runReadAction((Computable<Pair<String, String>>)() -> {
Pair<String, String> fileData = ApplicationManager.getApplication().runReadAction((Computable<Pair<String, String>>)() -> {
PsiFile file = myData.getGeneratedInnerClass().getContainingFile();
return Pair.create(file.getName(), file.getText());
});
@@ -140,8 +141,34 @@ public class CompilingEvaluatorImpl extends CompilingEvaluator {
if (fileData.second == null) {
throw new IOException("Class source code not specified");
}
final File file = new File(workingDir, "debugger/src/"+fileData.first);
File file = new File(workingDir, "debugger/src/" + fileData.first);
FileUtil.writeToFile(file, fileData.second);
return file;
}
@Nullable
public static ExpressionEvaluator create(@NotNull Project project,
@Nullable PsiElement psiContext,
@NotNull Function<PsiElement, PsiCodeFragment> fragmentFactory)
throws EvaluateException {
if (Registry.is("debugger.compiling.evaluator") && psiContext != null) {
return ApplicationManager.getApplication().runReadAction((ThrowableComputable<ExpressionEvaluator, EvaluateException>)() -> {
try {
ExtractLightMethodObjectHandler.ExtractedData data = ExtractLightMethodObjectHandler.extractLightMethodObject(
project,
psiContext.getContainingFile(),
fragmentFactory.apply(psiContext),
getGeneratedClassName());
if (data != null) {
return new CompilingEvaluatorImpl(project, psiContext, data);
}
}
catch (PrepareFailedException e) {
NodeDescriptorImpl.LOG.info(e);
}
return null;
});
}
return null;
}
}
@@ -30,20 +30,16 @@ import com.intellij.debugger.engine.evaluation.expression.UnsupportedExpressionE
import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.impl.DebuggerUtilsEx;
import com.intellij.debugger.jdi.StackFrameProxyImpl;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.ThrowableComputable;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.psi.*;
import com.intellij.refactoring.extractMethod.PrepareFailedException;
import com.intellij.refactoring.extractMethodObject.ExtractLightMethodObjectHandler;
import com.intellij.psi.PsiCodeFragment;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiExpressionCodeFragment;
import com.intellij.xdebugger.frame.XValueModifier;
import com.sun.jdi.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.Function;
/**
* @author lex
*/
@@ -71,25 +67,26 @@ public abstract class EvaluationDescriptor extends ValueDescriptorImpl {
return DebuggerUtilsEx.findAppropriateCodeFragmentFactory(text, context).createCodeFragment(text, context, myProject);
}
public final Value calcValue(final EvaluationContextImpl evaluationContext) throws EvaluateException {
public final Value calcValue(EvaluationContextImpl evaluationContext) throws EvaluateException {
try {
final EvaluationContextImpl thisEvaluationContext = getEvaluationContext(evaluationContext);
EvaluationContextImpl thisEvaluationContext = getEvaluationContext(evaluationContext);
SourcePosition position = ContextUtil.getSourcePosition(evaluationContext);
PsiElement psiContext = ContextUtil.getContextElement(evaluationContext, position);
ExpressionEvaluator evaluator;
try {
evaluator = DebuggerInvocationUtil.commitAndRunReadAction(myProject, () ->
DebuggerUtilsEx.findAppropriateCodeFragmentFactory(getEvaluationText(), psiContext)
ExpressionEvaluator evaluator = DebuggerInvocationUtil.commitAndRunReadAction(myProject, () -> {
try {
return DebuggerUtilsEx.findAppropriateCodeFragmentFactory(getEvaluationText(), psiContext)
.getEvaluatorBuilder()
.build(getEvaluationCode(thisEvaluationContext), position));
}
catch (UnsupportedExpressionException ex) {
evaluator = createCompilingEvaluator(evaluationContext, psiContext, this::createCodeFragment);
if (evaluator == null) {
.build(getEvaluationCode(thisEvaluationContext), position);
}
catch (UnsupportedExpressionException ex) {
ExpressionEvaluator eval = CompilingEvaluatorImpl.create(myProject, psiContext, this::createCodeFragment);
if (eval != null) {
return eval;
}
throw ex;
}
}
});
if (!thisEvaluationContext.getDebugProcess().isAttached()) {
throw EvaluateExceptionUtil.PROCESS_EXITED;
@@ -115,32 +112,6 @@ public abstract class EvaluationDescriptor extends ValueDescriptorImpl {
}
}
@Nullable
public static ExpressionEvaluator createCompilingEvaluator(EvaluationContextImpl evaluationContext,
@Nullable PsiElement psiContext,
Function<PsiElement, PsiCodeFragment> fragmentFactory)
throws EvaluateException {
if (Registry.is("debugger.compiling.evaluator") && psiContext != null) {
return ApplicationManager.getApplication().runReadAction((ThrowableComputable<ExpressionEvaluator, EvaluateException>)() -> {
try {
ExtractLightMethodObjectHandler.ExtractedData data = ExtractLightMethodObjectHandler.extractLightMethodObject(
evaluationContext.getProject(),
psiContext.getContainingFile(),
fragmentFactory.apply(psiContext),
CompilingEvaluator.getGeneratedClassName());
if (data != null) {
return new CompilingEvaluatorImpl(evaluationContext, psiContext, data);
}
}
catch (PrepareFailedException e) {
LOG.info(e);
}
return null;
});
}
return null;
}
public PsiExpression getDescriptorEvaluation(DebuggerContext context) throws EvaluateException {
PsiElement evaluationCode = getEvaluationCode(context);
if (evaluationCode instanceof PsiExpressionCodeFragment) {