From 5fbd5fb2208eb6ba0d3566283276cda136068121 Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Tue, 25 Oct 2016 13:02:46 +0300 Subject: [PATCH] refactoring to prepare compiling evaluator caching --- .../debugger/ui/breakpoints/Breakpoint.java | 18 ++-- .../ui/impl/watch/CompilingEvaluator.java | 17 ++-- .../ui/impl/watch/CompilingEvaluatorImpl.java | 85 ++++++++++++------- .../ui/impl/watch/EvaluationDescriptor.java | 63 ++++---------- 4 files changed, 88 insertions(+), 95 deletions(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java index 3ba029ce380d..43b214584b65 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/Breakpoint.java @@ -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

implements } throw new EventProcessingException(title[0], ex.getMessage(), ex); - } + } return true; } @@ -269,7 +269,7 @@ public abstract class Breakpoint

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

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

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

implements } private static ExpressionEvaluator createExpressionEvaluator(Project project, - EvaluationContextImpl context, PsiElement contextPsiElement, SourcePosition contextSourcePosition, TextWithImports text, @@ -378,7 +376,7 @@ public abstract class Breakpoint

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

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) { diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluator.java index 8a945476661c..6a7c25653a0e 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluator.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluator.java @@ -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)myPsiContext::getProject); SourcePosition position = ContextUtil.getSourcePosition(evaluationContext); ExpressionEvaluator evaluator = - DebuggerInvocationUtil.commitAndRunReadAction(project, new EvaluatingComputable() { + DebuggerInvocationUtil.commitAndRunReadAction(myProject, new EvaluatingComputable() { @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() { - @Override - public String compute() { - return JVMNameUtil.getNonAnonymousClassName(myData.getGeneratedInnerClass()); - } - }); + return ApplicationManager.getApplication().runReadAction( + (Computable)() -> JVMNameUtil.getNonAnonymousClassName(myData.getGeneratedInnerClass())); } ///////////////// Compiler stuff diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluatorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluatorImpl.java index e7445b0fc3c6..0fd2b1870129 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluatorImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluatorImpl.java @@ -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 compile(@Nullable JavaSdkVersion debuggeeVersion) throws EvaluateException { - final Module module = ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public Module compute() { - return ModuleUtilCore.findModuleForPsiElement(myPsiContext); - } - }); - final List options = new ArrayList<>(); + Module module = ApplicationManager.getApplication().runReadAction( + (Computable)() -> ModuleUtilCore.findModuleForPsiElement(myPsiContext)); + List options = new ArrayList<>(); options.add("-encoding"); options.add("UTF-8"); - final List platformClasspath = new ArrayList<>(); - final List classpath = new ArrayList<>(); + List platformClasspath = new ArrayList<>(); + List 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 runtime = BuildManager.getJavacRuntimeSdk(myEvaluationContext.getProject()); - final JavaSdkVersion buildRuntimeVersion = runtime.getSecond(); + Pair 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 sourcePath = Collections.emptyList(); - final Set sources = Collections.singleton(sourceFile); + File srcDir = sourceFile.getParentFile(); + List sourcePath = Collections.emptyList(); + Set 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 fileData = ApplicationManager.getApplication().runReadAction((Computable>)() -> { + Pair fileData = ApplicationManager.getApplication().runReadAction((Computable>)() -> { 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 fragmentFactory) + throws EvaluateException { + if (Registry.is("debugger.compiling.evaluator") && psiContext != null) { + return ApplicationManager.getApplication().runReadAction((ThrowableComputable)() -> { + 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; + } } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/EvaluationDescriptor.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/EvaluationDescriptor.java index e9f4592b5e9f..a84617b9aabe 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/EvaluationDescriptor.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/EvaluationDescriptor.java @@ -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 fragmentFactory) - throws EvaluateException { - if (Registry.is("debugger.compiling.evaluator") && psiContext != null) { - return ApplicationManager.getApplication().runReadAction((ThrowableComputable)() -> { - 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) {