IDEA-172425 Add ability to enable reflection replacement with any JDK
authorVitaliy.Bibaev <vitaliy.bibaev@jetbrains.com>
Tue, 27 Feb 2018 16:25:47 +0000 (19:25 +0300)
committerVitaliy.Bibaev <vitaliy.bibaev@jetbrains.com>
Tue, 27 Feb 2018 16:39:28 +0000 (19:39 +0300)
Disable registry: debugger.compiling.evaluator.magic.accessor
Enabled by default after JDK 9

java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluatorImpl.java
java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractLightMethodObjectHandler.java
java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/CompositeReflectionAccessor.java [new file with mode: 0644]
platform/util/resources/misc/registry.properties

index a5ed7e075af799bccd1e6a5994ee2ccfac1823b1..a879c6f4553567b1e41a6654ba2392e937d9fde2 100644 (file)
@@ -3,6 +3,7 @@ package com.intellij.debugger.ui.impl.watch;
 
 import com.intellij.compiler.CompilerConfiguration;
 import com.intellij.compiler.server.BuildManager;
+import com.intellij.debugger.engine.SuspendContextImpl;
 import com.intellij.debugger.engine.evaluation.EvaluateException;
 import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator;
 import com.intellij.openapi.application.ApplicationManager;
@@ -26,6 +27,8 @@ 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.XDebuggerManager;
+import com.intellij.xdebugger.frame.XSuspendContext;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.jetbrains.jps.incremental.java.JavaBuilder;
@@ -140,11 +143,22 @@ public class CompilingEvaluatorImpl extends CompilingEvaluator {
     if (Registry.is("debugger.compiling.evaluator") && psiContext != null) {
       return ApplicationManager.getApplication().runReadAction((ThrowableComputable<ExpressionEvaluator, EvaluateException>)() -> {
         try {
+          boolean useReflection = !Registry.is("debugger.compiling.evaluator.magic.accessor", true);
+          if (!useReflection) {
+            XSuspendContext suspendContext = XDebuggerManager.getInstance(project).getCurrentSession().getSuspendContext();
+            if (suspendContext instanceof SuspendContextImpl) {
+              JavaSdkVersion version =
+                JavaSdkVersion.fromVersionString(((SuspendContextImpl)suspendContext).getDebugProcess().getVirtualMachineProxy().version());
+              useReflection = version != null && version.isAtLeast(JavaSdkVersion.JDK_1_9);
+            }
+          }
+
           ExtractLightMethodObjectHandler.ExtractedData data = ExtractLightMethodObjectHandler.extractLightMethodObject(
             project,
             findPhysicalContext(psiContext),
             fragmentFactory.apply(psiContext),
-            getGeneratedClassName());
+            getGeneratedClassName(),
+            useReflection);
           if (data != null) {
             return new CompilingEvaluatorImpl(project, psiContext, data);
           }
index 94606da170a22f6ad64e074b8dff6265260fb277..ad1719f69c23b78ce3801ce9f7f6db81e12df80e 100644 (file)
@@ -29,6 +29,7 @@ import com.intellij.psi.util.PsiUtil;
 import com.intellij.refactoring.extractMethod.AbstractExtractDialog;
 import com.intellij.refactoring.extractMethod.InputVariables;
 import com.intellij.refactoring.extractMethod.PrepareFailedException;
+import com.intellij.refactoring.extractMethodObject.reflect.CompositeReflectionAccessor;
 import com.intellij.refactoring.util.RefactoringUtil;
 import com.intellij.refactoring.util.VariableData;
 import com.intellij.usageView.UsageInfo;
@@ -72,6 +73,15 @@ public class ExtractLightMethodObjectHandler {
                                                        @Nullable PsiElement originalContext,
                                                        @NotNull final PsiCodeFragment fragment,
                                                        final String methodName) throws PrepareFailedException {
+    return extractLightMethodObject(project, originalContext, fragment, methodName, false);
+  }
+
+  @Nullable
+  public static ExtractedData extractLightMethodObject(final Project project,
+                                                       @Nullable PsiElement originalContext,
+                                                       @NotNull final PsiCodeFragment fragment,
+                                                       final String methodName,
+                                                       boolean useReflection) throws PrepareFailedException {
     final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
     PsiElement[] elements = completeToStatementArray(fragment, elementFactory);
     if (elements == null) {
@@ -196,25 +206,27 @@ public class ExtractLightMethodObjectHandler {
     PsiStatement outStatement = elementFactory.createStatementFromText("System.out.println(" + outputVariables + ");", anchor);
     outStatement = (PsiStatement)container.addAfter(outStatement, elementsCopy[elementsCopy.length - 1]);
 
-    copy.accept(new JavaRecursiveElementWalkingVisitor() {
-      private void makePublic(PsiMember method) {
-        if (method.hasModifierProperty(PsiModifier.PRIVATE)) {
-          VisibilityUtil.setVisibility(method.getModifierList(), PsiModifier.PUBLIC);
+    if (!useReflection) {
+      copy.accept(new JavaRecursiveElementWalkingVisitor() {
+        private void makePublic(PsiMember method) {
+          if (method.hasModifierProperty(PsiModifier.PRIVATE)) {
+            VisibilityUtil.setVisibility(method.getModifierList(), PsiModifier.PUBLIC);
+          }
         }
-      }
 
-      @Override
-      public void visitMethod(PsiMethod method) {
-        super.visitMethod(method);
-        makePublic(method);
-      }
+        @Override
+        public void visitMethod(PsiMethod method) {
+          super.visitMethod(method);
+          makePublic(method);
+        }
 
-      @Override
-      public void visitField(PsiField field) {
-        super.visitField(field);
-        makePublic(field);
-      }
-    });
+        @Override
+        public void visitField(PsiField field) {
+          super.visitField(field);
+          makePublic(field);
+        }
+      });
+    }
 
     final ExtractMethodObjectProcessor extractMethodObjectProcessor = new ExtractMethodObjectProcessor(project, null, elementsCopy, "") {
       @Override
@@ -253,6 +265,16 @@ public class ExtractLightMethodObjectHandler {
     }
 
     final int startOffset = startOffsetInContainer + container.getTextRange().getStartOffset();
+
+    final PsiClass inner = extractMethodObjectProcessor.getInnerClass();
+    final PsiMethod[] methods = inner.findMethodsByName("invoke", false);
+
+    if (useReflection && methods.length == 1) {
+      final PsiMethod method = methods[0];
+      CompositeReflectionAccessor.createAccessorToEverything(inner, elementFactory)
+                                 .accessThroughReflection(method);
+    }
+
     final String generatedCall = copy.getText().substring(startOffset, outStatement.getTextOffset());
     return new ExtractedData(generatedCall,
                              (PsiClass)CodeStyleManager.getInstance(project).reformat(extractMethodObjectProcessor.getInnerClass()),
diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/CompositeReflectionAccessor.java b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/CompositeReflectionAccessor.java
new file mode 100644 (file)
index 0000000..f1a55dd
--- /dev/null
@@ -0,0 +1,41 @@
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package com.intellij.refactoring.extractMethodObject.reflect;
+
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiElementFactory;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Vitaliy.Bibaev
+ */
+public class CompositeReflectionAccessor implements ReflectionAccessor {
+  private final List<ReflectionAccessor> myAccessors = new ArrayList<>();
+
+  private CompositeReflectionAccessor() {
+  }
+
+  private void registerAccessor(@NotNull ReflectionAccessor accessor) {
+    myAccessors.add(accessor);
+  }
+
+  public static ReflectionAccessor createAccessorToEverything(@NotNull PsiClass psiClass, @NotNull PsiElementFactory elementFactory) {
+    CompositeReflectionAccessor compositeAccessor = new CompositeReflectionAccessor();
+    compositeAccessor.registerAccessor(new FieldReflectionAccessor(psiClass, elementFactory));
+    compositeAccessor.registerAccessor(new MethodReflectionAccessor(psiClass, elementFactory));
+    compositeAccessor.registerAccessor(new ConstructorReflectionAccessor(psiClass, elementFactory));
+    compositeAccessor.registerAccessor(new MethodReferenceReflectionAccessor(psiClass, elementFactory));
+
+    return compositeAccessor;
+  }
+
+  @Override
+  public void accessThroughReflection(@NotNull PsiElement element) {
+    for (ReflectionAccessor accessor : myAccessors) {
+      accessor.accessThroughReflection(element);
+    }
+  }
+}
index ff3bdc2ee616bf9e634de9da951cc3bed98055a5..3fa10356a4aac385e597b1bcbf8a0cdcf999ff00 100644 (file)
@@ -360,6 +360,7 @@ debugger.valueTooltipAutoShowOnSelection.description=Auto show tooltip when edit
 debugger.mayBringFrameToFrontOnBreakpoint=true
 debugger.batch.evaluation=false
 debugger.compiling.evaluator=true
+debugger.compiling.evaluator.magic.accessor=true
 debugger.watches.in.variables=true
 debugger.auto.fetch.icons=true
 debugger.close.dialog.on.navigate=true