From a6319da145d57f80ca15d77a619c477c6dfe35d5 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Tue, 27 Feb 2018 19:21:24 +0300 Subject: [PATCH 1/9] IDEA-172425 Implement logic to replace members access with reflection Supported expressions to replace: * new InaccessibleConstructor(...) * object.inaccessible[Static]Method(...) * object.inaccessible[Static]Field * InaccessibleClass.field[method()] * Class::refToInaccessibleMethod Not supported expressions: OuterClass.this AnyInaccessibleClass.class method() for method with inaccessible return type field for field with inaccessible type --- .../ItemToReplaceDescriptor.java | 8 + .../PsiReflectionAccessUtil.java | 82 +++++ .../ConstructorReflectionAccessor.java | 92 ++++++ .../reflect/FieldAccessType.java | 9 + .../reflect/FieldReflectionAccessor.java | 145 +++++++++ .../MethodReferenceReflectionAccessor.java | 55 ++++ .../reflect/MethodReflectionAccessor.java | 110 +++++++ .../ReferenceReflectionAccessorBase.java | 39 +++ .../ReflectionAccessMethodBuilder.java | 307 ++++++++++++++++++ .../reflect/ReflectionAccessor.java | 12 + .../reflect/ReflectionAccessorBase.java | 56 ++++ 11 files changed, 915 insertions(+) create mode 100644 java/java-impl/src/com/intellij/refactoring/extractMethodObject/ItemToReplaceDescriptor.java create mode 100644 java/java-impl/src/com/intellij/refactoring/extractMethodObject/PsiReflectionAccessUtil.java create mode 100644 java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ConstructorReflectionAccessor.java create mode 100644 java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/FieldAccessType.java create mode 100644 java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/FieldReflectionAccessor.java create mode 100644 java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/MethodReferenceReflectionAccessor.java create mode 100644 java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/MethodReflectionAccessor.java create mode 100644 java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ReferenceReflectionAccessorBase.java create mode 100644 java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ReflectionAccessMethodBuilder.java create mode 100644 java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ReflectionAccessor.java create mode 100644 java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ReflectionAccessorBase.java diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ItemToReplaceDescriptor.java b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ItemToReplaceDescriptor.java new file mode 100644 index 000000000000..a2252e66fd84 --- /dev/null +++ b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ItemToReplaceDescriptor.java @@ -0,0 +1,8 @@ +// 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; + +/** + * @author Vitaliy.Bibaev + */ +public interface ItemToReplaceDescriptor { +} diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/PsiReflectionAccessUtil.java b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/PsiReflectionAccessUtil.java new file mode 100644 index 000000000000..f52fa99b5b1e --- /dev/null +++ b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/PsiReflectionAccessUtil.java @@ -0,0 +1,82 @@ +// 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; + +import com.intellij.psi.*; +import com.intellij.psi.util.PsiUtil; +import one.util.streamex.StreamEx; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Set; + +/** + * @author Vitaliy.Bibaev + */ +public class PsiReflectionAccessUtil { + public static boolean isAccessibleMember(@NotNull PsiMember classMember) { + return classMember.hasModifierProperty(PsiModifier.PUBLIC) && isAccessible(classMember.getContainingClass()); + } + + @Contract("null -> false") + public static boolean isAccessible(@Nullable PsiClass psiClass) { + if (psiClass == null) return false; + while (psiClass != null) { + if (!psiClass.hasModifierProperty(PsiModifier.PUBLIC)) { + return false; + } + + psiClass = psiClass.getContainingClass(); + } + + return true; + } + + @Nullable + public static String extractQualifier(@NotNull PsiReferenceExpression referenceExpression) { + PsiExpression qualifierExpression = referenceExpression.getQualifierExpression(); + PsiType expressionType = qualifierExpression != null ? qualifierExpression.getType() : null; + return expressionType == null ? null : qualifierExpression.getText(); + } + + @Contract("null -> null") + @Nullable + public static PsiClass nearestAccessedClass(@Nullable PsiClass psiClass) { + while (psiClass != null && !psiClass.hasModifierProperty(PsiModifier.PUBLIC)) { + psiClass = psiClass.getSuperClass(); + } + + return psiClass; + } + + @Nullable + public static String getAccessibleReturnType(@Nullable PsiType type) { + PsiClass psiClass = nearestAccessedClass(PsiUtil.resolveClassInType(type)); + if (psiClass != null) { + return psiClass.getQualifiedName(); + } + + return type != null ? type.getCanonicalText() : null; + } + + @Nullable + public static String getAccessibleReturnType(@Nullable PsiClass psiClass) { + psiClass = nearestAccessedClass(psiClass); + return psiClass == null ? null : psiClass.getQualifiedName(); + } + + private static final Set PRIMITIVE_TYPES = StreamEx.of("byte", "short", "int", "long", "float", "double", "boolean", "char") + .toImmutableSet(); + + @NotNull + @Contract(pure = true) + public static String classForName(@NotNull String typeName) { + return PRIMITIVE_TYPES.contains(typeName) ? typeName + ".class" : "java.lang.Class.forName(\"" + typeName + "\")"; + } + + @NotNull + @Contract(pure = true) + public static String escape(String str) { + return "\"" + str + "\""; + } +} diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ConstructorReflectionAccessor.java b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ConstructorReflectionAccessor.java new file mode 100644 index 000000000000..691a7ece958e --- /dev/null +++ b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ConstructorReflectionAccessor.java @@ -0,0 +1,92 @@ +// 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.openapi.diagnostic.Logger; +import com.intellij.psi.*; +import com.intellij.psi.util.ClassUtil; +import com.intellij.refactoring.extractMethodObject.ItemToReplaceDescriptor; +import com.intellij.refactoring.extractMethodObject.PsiReflectionAccessUtil; +import one.util.streamex.StreamEx; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Vitaliy.Bibaev + */ +public class ConstructorReflectionAccessor extends ReflectionAccessorBase { + private static final Logger LOG = Logger.getInstance(ConstructorReflectionAccessor.class); + + protected ConstructorReflectionAccessor(@NotNull PsiClass psiClass, + @NotNull PsiElementFactory elementFactory) { + super(psiClass, elementFactory); + } + + @Override + protected List findItemsToReplace(@NotNull PsiElement element) { + List result = new ArrayList<>(); + + element.accept(new JavaRecursiveElementVisitor() { + @Override + public void visitNewExpression(PsiNewExpression expression) { + super.visitNewExpression(expression); + if (expression.getAnonymousClass() != null || expression.getArrayInitializer() != null) return; + PsiMethod constructor = expression.resolveConstructor(); + if (constructor != null) { + PsiClass containingClass = constructor.getContainingClass(); + if (containingClass != null && !PsiReflectionAccessUtil.isAccessibleMember(constructor)) { + result.add(new ConstructorDescriptor(expression, constructor, containingClass)); + } + } + else { + PsiJavaCodeReferenceElement classReference = expression.getClassReference(); + if (classReference instanceof PsiClass && !PsiReflectionAccessUtil.isAccessible((PsiClass)classReference)) { + result.add(new ConstructorDescriptor(expression, null, (PsiClass)classReference)); + } + } + } + }); + + return result; + } + + @Override + protected void grantAccess(@NotNull ConstructorDescriptor descriptor, int order) { + String className = ClassUtil.getJVMClassName(descriptor.psiClass); + String returnType = PsiReflectionAccessUtil.getAccessibleReturnType(descriptor.psiClass); + PsiExpressionList argumentList = descriptor.newExpression.getArgumentList(); + if (className == null || argumentList == null || returnType == null) { + LOG.debug("expression is incomplete"); + return; + } + ReflectionAccessMethodBuilder methodBuilder = new ReflectionAccessMethodBuilder("reflectionConstructorAccess" + order); + methodBuilder.accessedConstructor(className) + .setStatic(getOuterClass().hasModifierProperty(PsiModifier.STATIC)) + .setReturnType(returnType); + if (descriptor.constructor != null) { + methodBuilder.addParameters(descriptor.constructor.getParameterList()); + } + + PsiMethod newPsiMethod = methodBuilder.build(getElementFactory(), getOuterClass()); + getOuterClass().add(newPsiMethod); + String args = StreamEx.of(argumentList.getExpressions()).map(x -> x.getText()).joining(", ", "(", ")"); + String newCallExpression = newPsiMethod.getName() + args; + descriptor.newExpression.replace(getElementFactory().createExpressionFromText(newCallExpression, descriptor.newExpression)); + } + + public static class ConstructorDescriptor implements ItemToReplaceDescriptor { + public final PsiNewExpression newExpression; + public final PsiClass psiClass; + + // null if and only if default constructor is used + @Nullable public final PsiMethod constructor; + + public ConstructorDescriptor(@NotNull PsiNewExpression expression, @Nullable PsiMethod constructor, PsiClass psiClass) { + newExpression = expression; + this.constructor = constructor; + this.psiClass = psiClass; + } + } +} diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/FieldAccessType.java b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/FieldAccessType.java new file mode 100644 index 000000000000..cfc83c234140 --- /dev/null +++ b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/FieldAccessType.java @@ -0,0 +1,9 @@ +// 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; + +/** + * @author Vitaliy.Bibaev + */ +public enum FieldAccessType { + GET, SET +} diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/FieldReflectionAccessor.java b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/FieldReflectionAccessor.java new file mode 100644 index 000000000000..e9a6d0841462 --- /dev/null +++ b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/FieldReflectionAccessor.java @@ -0,0 +1,145 @@ +// 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.openapi.diagnostic.Logger; +import com.intellij.psi.*; +import com.intellij.psi.util.ClassUtil; +import com.intellij.refactoring.extractMethodObject.ItemToReplaceDescriptor; +import com.intellij.refactoring.extractMethodObject.PsiReflectionAccessUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + +/** + * @author Vitaliy.Bibaev + */ +public class FieldReflectionAccessor extends ReferenceReflectionAccessorBase { + private static final Logger LOG = Logger.getInstance(FieldReflectionAccessor.class); + + public FieldReflectionAccessor(@NotNull PsiClass psiClass, + @NotNull PsiElementFactory elementFactory) { + super(psiClass, elementFactory); + } + + @Nullable + @Override + protected FieldDescriptor createDescriptor(@NotNull PsiReferenceExpression expression) { + final PsiElement resolved = expression.resolve(); + if (resolved instanceof PsiField) { + final PsiField field = (PsiField)resolved; + if (!Objects.equals(field.getContainingClass(), getOuterClass()) && needReplace(field)) { + return new FieldDescriptor(field, expression); + } + } + + return null; + } + + @Override + protected void grantAccess(@NotNull FieldDescriptor descriptor, int order) { + PsiElement parent = descriptor.expression.getParent(); + if (parent instanceof PsiAssignmentExpression && + Objects.equals(descriptor.expression, ((PsiAssignmentExpression)parent).getLExpression())) { + grantUpdateAccess((PsiAssignmentExpression)parent, descriptor, order); + } + else { + grantReadAccess(descriptor, order); + } + } + + private void grantReadAccess(@NotNull FieldDescriptor descriptor, int order) { + PsiClass outerClass = getOuterClass(); + PsiMethod newMethod = createPsiMethod(descriptor, order, FieldAccessType.GET); + if (newMethod == null) return; + + outerClass.add(newMethod); + + String qualifier = qualify(descriptor); + String methodCall = newMethod.getName() + "(" + (qualifier == null ? "null" : qualifier) + ", null)"; + descriptor.expression.replace(getElementFactory().createExpressionFromText(methodCall, descriptor.expression)); + } + + private void grantUpdateAccess(@NotNull PsiAssignmentExpression assignmentExpression, @NotNull FieldDescriptor descriptor, int order) { + PsiClass outerClass = getOuterClass(); + PsiMethod newMethod = createPsiMethod(descriptor, order, FieldAccessType.SET); + if (newMethod == null) return; + + outerClass.add(newMethod); + PsiExpression rightExpression = assignmentExpression.getRExpression(); + if (rightExpression == null) { + LOG.warn("Expression representing a new field value not found"); + return; + } + + String newValue = rightExpression.getText(); + String qualifier = qualify(descriptor); + String args = (qualifier == null ? "null" : qualifier) + ", " + newValue; + String methodCallExpression = newMethod.getName() + "(" + args + ")"; + + PsiExpression newMethodCallExpression = getElementFactory().createExpressionFromText(methodCallExpression, descriptor.expression); + assignmentExpression.replace(newMethodCallExpression); + } + + @Nullable + private PsiMethod createPsiMethod(@NotNull FieldDescriptor descriptor, int order, FieldAccessType accessType) { + PsiClass outerClass = getOuterClass(); + PsiClass containingClass = descriptor.field.getContainingClass(); + String returnType = PsiReflectionAccessUtil.getAccessibleReturnType(descriptor.field.getType()); + String className = containingClass == null ? null : ClassUtil.getJVMClassName(containingClass); + String fieldName = descriptor.field.getName(); + if (className == null || fieldName == null) { + LOG.warn("Code is incomplete. Class name or field name not found"); + return null; + } + if (returnType == null) { + LOG.warn("Could not resolve return type"); + return null; + } + + ReflectionAccessMethodBuilder methodBuilder = new ReflectionAccessMethodBuilder("reflectionFieldAccess" + order); + if (FieldAccessType.GET.equals(accessType)) { + methodBuilder.accessedField(className, fieldName) + .setReturnType(returnType); + } + else { + methodBuilder.updatedField(className, fieldName) + .setReturnType("void"); + } + + methodBuilder.setStatic(outerClass.hasModifierProperty(PsiModifier.STATIC)) + .addParameter("java.lang.Object", "object") + .addParameter("java.lang.Object", "value"); + + return methodBuilder.build(getElementFactory(), outerClass); + } + + protected boolean needReplace(@NotNull PsiField field) { + return !PsiReflectionAccessUtil.isAccessibleMember(field); + } + + @Nullable + private static String qualify(@NotNull FieldDescriptor descriptor) { + String qualifier = PsiReflectionAccessUtil.extractQualifier(descriptor.expression); + if (qualifier == null) { + if (!descriptor.field.hasModifierProperty(PsiModifier.STATIC)) { + PsiClass containingClass = descriptor.field.getContainingClass(); + if (containingClass != null) { + qualifier = containingClass.getQualifiedName() + ".this"; + } + } + } + + return qualifier; + } + + public static class FieldDescriptor implements ItemToReplaceDescriptor { + public final PsiField field; + public final PsiReferenceExpression expression; + + public FieldDescriptor(@NotNull PsiField field, @NotNull PsiReferenceExpression expression) { + this.field = field; + this.expression = expression; + } + } +} diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/MethodReferenceReflectionAccessor.java b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/MethodReferenceReflectionAccessor.java new file mode 100644 index 000000000000..75aac632e8f2 --- /dev/null +++ b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/MethodReferenceReflectionAccessor.java @@ -0,0 +1,55 @@ +// 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.*; +import com.intellij.refactoring.extractMethodObject.ItemToReplaceDescriptor; +import com.intellij.refactoring.extractMethodObject.PsiReflectionAccessUtil; +import com.intellij.refactoring.util.LambdaRefactoringUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Vitaliy.Bibaev + */ +public class MethodReferenceReflectionAccessor + extends ReferenceReflectionAccessorBase { + public MethodReferenceReflectionAccessor(@NotNull PsiClass psiClass, + @NotNull PsiElementFactory elementFactory) { + super(psiClass, elementFactory); + } + + @Nullable + @Override + protected MethodReferenceDescriptor createDescriptor(@NotNull PsiReferenceExpression expression) { + if (expression instanceof PsiMethodReferenceExpression) { + PsiElement resolvedElement = expression.resolve(); + if (resolvedElement instanceof PsiMethod) { + PsiMethod method = (PsiMethod)resolvedElement; + if (!PsiReflectionAccessUtil.isAccessibleMember(method)) { + return new MethodReferenceDescriptor(method, (PsiMethodReferenceExpression)expression); + } + } + } + + return null; + } + + @Override + protected void grantAccess(@NotNull MethodReferenceDescriptor descriptor, int order) { + MethodReflectionAccessor methodAccessor = new MethodReflectionAccessor(getOuterClass(), getElementFactory()); + PsiLambdaExpression lambda = LambdaRefactoringUtil.convertMethodReferenceToLambda(descriptor.expression, false, true); + if (lambda != null) { + methodAccessor.accessThroughReflection(lambda); + } + } + + public static class MethodReferenceDescriptor implements ItemToReplaceDescriptor { + public final PsiMethod method; + public final PsiMethodReferenceExpression expression; + + public MethodReferenceDescriptor(@NotNull PsiMethod method, @NotNull PsiMethodReferenceExpression expression) { + this.method = method; + this.expression = expression; + } + } +} diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/MethodReflectionAccessor.java b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/MethodReflectionAccessor.java new file mode 100644 index 000000000000..7b13dc574ff7 --- /dev/null +++ b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/MethodReflectionAccessor.java @@ -0,0 +1,110 @@ +// 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.openapi.diagnostic.Logger; +import com.intellij.psi.*; +import com.intellij.psi.util.ClassUtil; +import com.intellij.refactoring.extractMethodObject.ItemToReplaceDescriptor; +import com.intellij.refactoring.extractMethodObject.PsiReflectionAccessUtil; +import one.util.streamex.StreamEx; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * @author Vitaliy.Bibaev + */ +public class MethodReflectionAccessor extends ReflectionAccessorBase { + private static final Logger LOG = Logger.getInstance(MethodReflectionAccessor.class); + + protected MethodReflectionAccessor(@NotNull PsiClass psiClass, + @NotNull PsiElementFactory elementFactory) { + super(psiClass, elementFactory); + } + + @Override + protected List findItemsToReplace(@NotNull PsiElement element) { + List result = new ArrayList<>(); + + element.accept(new JavaRecursiveElementVisitor() { + @Override + public void visitMethodCallExpression(PsiMethodCallExpression expression) { + super.visitMethodCallExpression(expression); + PsiMethod method = expression.resolveMethod(); + if (method != null && !Objects.equals(method.getContainingClass(), getOuterClass()) && needReplace(method)) { + result.add(new MethodCallDescriptor(expression, method)); + } + } + }); + + return result; + } + + @Override + protected void grantAccess(@NotNull MethodCallDescriptor descriptor, int order) { + PsiClass outerClass = getOuterClass(); + String returnType = PsiReflectionAccessUtil.getAccessibleReturnType(descriptor.method.getReturnType()); + PsiClass containingClass = descriptor.method.getContainingClass(); + String containingClassName = containingClass == null ? null : ClassUtil.getJVMClassName(containingClass); + String name = descriptor.method.getName(); + if (returnType == null) { + LOG.warn("return type of" + descriptor.method.getName() + " method is null"); + return; + } + + if (containingClassName == null) { + LOG.warn("containing class for method \"" + name + "\" not found"); + return; + } + + ReflectionAccessMethodBuilder methodBuilder = new ReflectionAccessMethodBuilder(descriptor.method.getName() + "ReflectionCall" + order); + PsiMethod newMethod = methodBuilder.accessedMethod(containingClassName, descriptor.method.getName()) + .setStatic(outerClass.hasModifierProperty(PsiModifier.STATIC)) + .addParameter("java.lang.Object", "object") + .addParameters(descriptor.method.getParameterList()) + .setReturnType(returnType) + .build(getElementFactory(), getOuterClass()); + + outerClass.add(newMethod); + String qualifier = qualify(descriptor); + String args = StreamEx.of(descriptor.callExpression.getArgumentList().getExpressions()) + .map(x -> x.getText()) + .prepend(qualifier == null ? "null" : qualifier) + .joining(", ", "(", ")"); + String newMethodCallExpression = newMethod.getName() + args; + + descriptor.callExpression.replace(getElementFactory().createExpressionFromText(newMethodCallExpression, descriptor.callExpression)); + } + + private static boolean needReplace(@NotNull PsiMethod method) { + return !PsiReflectionAccessUtil.isAccessibleMember(method); + } + + @Nullable + private static String qualify(@NotNull MethodCallDescriptor descriptor) { + String qualifier = PsiReflectionAccessUtil.extractQualifier(descriptor.callExpression.getMethodExpression()); + if (qualifier == null) { + if (!descriptor.method.hasModifierProperty(PsiModifier.STATIC)) { + PsiClass containingClass = descriptor.method.getContainingClass(); + if (containingClass != null) { + qualifier = containingClass.getQualifiedName() + ".this"; + } + } + } + + return qualifier; + } + + public static class MethodCallDescriptor implements ItemToReplaceDescriptor { + public final PsiMethodCallExpression callExpression; + public final PsiMethod method; + + public MethodCallDescriptor(@NotNull PsiMethodCallExpression expression, @NotNull PsiMethod method) { + callExpression = expression; + this.method = method; + } + } +} diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ReferenceReflectionAccessorBase.java b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ReferenceReflectionAccessorBase.java new file mode 100644 index 000000000000..f4c2a9ad3539 --- /dev/null +++ b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ReferenceReflectionAccessorBase.java @@ -0,0 +1,39 @@ +// 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.*; +import com.intellij.refactoring.extractMethodObject.ItemToReplaceDescriptor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Vitaliy.Bibaev + */ +public abstract class ReferenceReflectionAccessorBase extends ReflectionAccessorBase { + public ReferenceReflectionAccessorBase(@NotNull PsiClass psiClass, @NotNull PsiElementFactory elementFactory) { + super(psiClass, elementFactory); + } + + @Override + protected List findItemsToReplace(@NotNull PsiElement element) { + List result = new ArrayList<>(); + element.accept(new JavaRecursiveElementVisitor() { + @Override + public void visitReferenceExpression(PsiReferenceExpression expression) { + super.visitReferenceExpression(expression); + final T descriptor = createDescriptor(expression); + if (descriptor != null) { + result.add(descriptor); + } + } + }); + + return result; + } + + @Nullable + protected abstract T createDescriptor(@NotNull PsiReferenceExpression expression); +} diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ReflectionAccessMethodBuilder.java b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ReflectionAccessMethodBuilder.java new file mode 100644 index 000000000000..c196477e1450 --- /dev/null +++ b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ReflectionAccessMethodBuilder.java @@ -0,0 +1,307 @@ +// 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.*; +import com.intellij.psi.util.ClassUtil; +import com.intellij.psi.util.PsiUtil; +import com.intellij.refactoring.extractMethodObject.PsiReflectionAccessUtil; +import com.intellij.util.SmartList; +import one.util.streamex.StreamEx; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * @author Vitaliy.Bibaev + */ +public class ReflectionAccessMethodBuilder { + private boolean myIsStatic = false; + private String myReturnType = "void"; + private String myName; + private MyMemberAccessor myMemberAccessor; + private final List myParameters = new SmartList<>(); + + public ReflectionAccessMethodBuilder(@NotNull String name) { + myName = name; + } + + public PsiMethod build(@NotNull PsiElementFactory elementFactory, + @Nullable PsiElement context) { + checkRequirements(); + String parameters = StreamEx.of(myParameters).map(p -> p.type + " " + p.name).joining(", ", "(", ")"); + String returnExpression = ("void".equals(myReturnType) ? "" : "return (" + myReturnType + ")") + myMemberAccessor.getAccessExpression(); + String methodBody = " java.lang.Class klass = " + myMemberAccessor.getClassLookupExpression() + ";\n" + + " " + myMemberAccessor.getMemberType() + " member = null;\n" + + " " + myMemberAccessor.getMemberLookupBlock() + + " " + returnExpression + ";\n"; + List possibleExceptions = myMemberAccessor.getPossibleExceptions(); + if (!possibleExceptions.isEmpty()) { + methodBody = "try {\n" + + methodBody + + "}" + + createCatchBlocks(possibleExceptions); + } + + String methodText = + "public" + (myIsStatic ? " static " : " ") + myReturnType + " " + myName + parameters + " { \n" + methodBody + "}\n"; + + return elementFactory.createMethodFromText(methodText, context); + } + + private void checkRequirements() { + } + + public ReflectionAccessMethodBuilder setName(@NotNull String name) { + myName = name; + return this; + } + + public ReflectionAccessMethodBuilder accessedMethod(@NotNull String jvmClassName, @NotNull String methodName) { + myMemberAccessor = new MyMethodAccessor(jvmClassName, methodName); + return this; + } + + public ReflectionAccessMethodBuilder accessedField(@NotNull String jvmClassName, @NotNull String fieldName) { + myMemberAccessor = new MyFieldAccessor(jvmClassName, fieldName, FieldAccessType.GET); + return this; + } + + public ReflectionAccessMethodBuilder updatedField(@NotNull String jvmClassName, @NotNull String fieldName) { + myMemberAccessor = new MyFieldAccessor(jvmClassName, fieldName, FieldAccessType.SET); + return this; + } + + public ReflectionAccessMethodBuilder accessedConstructor(@NotNull String jvmClassName) { + myMemberAccessor = new MyConstructorAccessor(jvmClassName); + return this; + } + + public ReflectionAccessMethodBuilder setReturnType(@NotNull String returnType) { + myReturnType = returnType; + return this; + } + + public ReflectionAccessMethodBuilder setStatic(boolean isStatic) { + myIsStatic = isStatic; + return this; + } + + public ReflectionAccessMethodBuilder addParameter(@NotNull String jvmType, @NotNull String name) { + myParameters.add(new ParameterInfo(jvmType.replace('$', '.'), name, jvmType)); + return this; + } + + public ReflectionAccessMethodBuilder addParameters(@NotNull PsiParameterList parameterList) { + PsiParameter[] parameters = parameterList.getParameters(); + for (int i = 0; i < parameters.length; i++) { + PsiParameter parameter = parameters[i]; + String name = parameter.getName(); + PsiType type = parameter.getType(); + myParameters.add(new ParameterInfo(type.getCanonicalText(), name == null ? "arg" + i : name, extractJvmType(type))); + } + + return this; + } + + @NotNull + private static String extractJvmType(@NotNull PsiType type) { + PsiClass psiClass = PsiUtil.resolveClassInType(type); + String canonicalText = type.getCanonicalText(); + String jvmName = psiClass == null ? canonicalText : ClassUtil.getJVMClassName(psiClass); + return jvmName == null ? canonicalText : jvmName; + } + + private static String createCatchBlocks(@NotNull List exceptions) { + return StreamEx.of(exceptions).map(x -> "catch(" + x + " e) { throw new java.lang.RuntimeException(e); }").joining("\n"); + } + + private static class ParameterInfo { + public final String type; + public final String name; + public final String jvmTypeName; + + public ParameterInfo(@NotNull String type, @NotNull String name) { + this(type, name, type); + } + + public ParameterInfo(@NotNull String type, @NotNull String name, @NotNull String jvmTypeName) { + this.type = type; + this.name = name; + this.jvmTypeName = jvmTypeName; + } + } + + private interface MyMemberAccessor { + String getMemberLookupBlock(); + + String getClassLookupExpression(); + + String getAccessExpression(); + + String getMemberType(); + + List getPossibleExceptions(); + } + + + private static class MyFieldAccessor implements MyMemberAccessor { + private static final List EXCEPTIONS = Collections.unmodifiableList(Arrays.asList("java.lang.NoSuchFieldException", + "java.lang.IllegalAccessException", + "java.lang.ClassNotFoundException")); + private final String myFieldName; + private final String myClassName; + private final FieldAccessType myAccessType; + + public MyFieldAccessor(@NotNull String className, + @NotNull String fieldName, + @NotNull FieldAccessType accessType) { + myFieldName = fieldName; + myClassName = className; + myAccessType = accessType; + } + + @Override + public String getClassLookupExpression() { + String classForName = PsiReflectionAccessUtil.classForName(myClassName); + // emulate applySideEffectAndReturnNull().staticField expression + return "object == null ? " + classForName + " : object.getClass()"; + } + + @Override + public String getMemberLookupBlock() { + return "while (member == null) {\n" + + " try {\n" + + " member = klass.getDeclaredField(" + PsiReflectionAccessUtil.escape(myFieldName) + ");\n" + + " }\n" + + " catch(java.lang.NoSuchFieldException e) {\n" + + " klass = klass.getSuperclass();\n" + + " if (klass == null) throw e;\n" + + " }\n" + + "}\n" + + "member.setAccessible(true);"; + } + + @Override + public String getAccessExpression() { + return FieldAccessType.GET.equals(myAccessType) ? "member.get(object)" : "member.set(object, value)"; + } + + @Override + public String getMemberType() { + return "java.lang.reflect.Field"; + } + + @Override + public List getPossibleExceptions() { + return EXCEPTIONS; + } + } + + + private class MyMethodAccessor implements MyMemberAccessor { + private final String myClassName; + private final String myMethodName; + + public MyMethodAccessor(@NotNull String className, @NotNull String methodName) { + myClassName = className; + myMethodName = methodName; + } + + @Override + public String getMemberLookupBlock() { + String args = StreamEx.of(myParameters).skip(1).map(x -> PsiReflectionAccessUtil.classForName(x.jvmTypeName)) + .prepend(PsiReflectionAccessUtil.escape(myMethodName)) + .joining(", ", "(", ")"); + return "while (member == null) {\n" + + " try {\n" + + " member = klass.getDeclaredMethod" + args + ";\n" + + " }\n" + + " catch(java.lang.NoSuchMethodException e) {\n" + + " klass = klass.getSuperclass();\n" + + " if (klass == null) throw e;\n" + + " }\n" + + "}\n" + + "member.setAccessible(true);\n"; + } + + @Override + public String getClassLookupExpression() { + String classForName = PsiReflectionAccessUtil.classForName(myClassName); + // emulate applySideEffectAndReturnNull().staticMethod() expression + return "object == null ? " + classForName + " : object.getClass()"; + } + + @Override + public String getMemberType() { + return "java.lang.reflect.Method"; + } + + @Override + public List getPossibleExceptions() { + return Collections.unmodifiableList(Arrays.asList( + "java.lang.NoSuchMethodException", + "java.lang.IllegalAccessException", + "java.lang.ClassNotFoundException", + "java.lang.reflect.InvocationTargetException")); + } + + @Override + public String getAccessExpression() { + return StreamEx.of(myParameters).map(x -> x.name).joining(", ", "member.invoke(", ")"); + } + } + + + private class MyConstructorAccessor implements MyMemberAccessor { + private final String myClassName; + + public MyConstructorAccessor(@NotNull String className) { + myClassName = className; + } + + @Override + public String getMemberLookupBlock() { + String args = StreamEx.of(myParameters).map(x -> x.jvmTypeName).map(PsiReflectionAccessUtil::classForName).joining(", ", "(", ")"); + return "while (member == null) {\n" + + " try {\n" + + " member = klass.getDeclaredConstructor" + args + ";\n" + + " }\n" + + " catch(java.lang.NoSuchMethodException e) {\n" + + " klass = klass.getSuperclass();\n" + + " if (klass == null) throw e;\n" + + " }\n" + + "}\n" + + "member.setAccessible(true);\n"; + } + + @Override + public String getClassLookupExpression() { + return PsiReflectionAccessUtil.classForName(myClassName); + } + + @Override + public String getAccessExpression() { + String args = StreamEx.of(myParameters).map(x -> x.name).joining(", ", "(", ")"); + return "member.newInstance" + args; + } + + @Override + public String getMemberType() { + return "java.lang.reflect.Constructor"; + } + + @Override + public List getPossibleExceptions() { + return Collections.unmodifiableList(Arrays.asList( + "java.lang.NoSuchMethodException", + "java.lang.IllegalAccessException", + "java.lang.ClassNotFoundException", + "java.lang.reflect.InvocationTargetException", + "java.lang.InstantiationException" + )); + } + } +} diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ReflectionAccessor.java b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ReflectionAccessor.java new file mode 100644 index 000000000000..53ddb99a3046 --- /dev/null +++ b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ReflectionAccessor.java @@ -0,0 +1,12 @@ +// 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.PsiElement; +import org.jetbrains.annotations.NotNull; + +/** + * @author Vitaliy.Bibaev + */ +public interface ReflectionAccessor { + void accessThroughReflection(@NotNull PsiElement element); +} diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ReflectionAccessorBase.java b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ReflectionAccessorBase.java new file mode 100644 index 000000000000..f7916f210c60 --- /dev/null +++ b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ReflectionAccessorBase.java @@ -0,0 +1,56 @@ +// 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.openapi.diagnostic.Logger; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiElementFactory; +import com.intellij.refactoring.extractMethodObject.ItemToReplaceDescriptor; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * Grants access for inaccessible members using reflection. Adds an additional method per each expression + * with inaccessible members (method calls, field references, constructor calls, etc) + * + * @author Vitaliy.Bibaev + */ +public abstract class ReflectionAccessorBase implements ReflectionAccessor { + private static final Logger LOG = Logger.getInstance(ReferenceReflectionAccessorBase.class); + private final PsiClass myPsiClass; + private final PsiElementFactory myElementFactory; + + protected ReflectionAccessorBase(@NotNull PsiClass psiClass, @NotNull PsiElementFactory elementFactory) { + myPsiClass = psiClass; + myElementFactory = elementFactory; + } + + @Override + public void accessThroughReflection(@NotNull PsiElement element) { + List toReplace = findItemsToReplace(element); + int order = 1; + for (T item : toReplace) { + grantAccess(item, order++); + } + + List remaining = findItemsToReplace(element); + if (!remaining.isEmpty()) { + LOG.warn("Some inaccessible items were not replaced"); + } + } + + @NotNull + protected PsiElementFactory getElementFactory() { + return myElementFactory; + } + + @NotNull + protected PsiClass getOuterClass() { + return myPsiClass; + } + + protected abstract List findItemsToReplace(@NotNull PsiElement element); + + protected abstract void grantAccess(@NotNull T descriptor, int order); +} From da4402acc25482e05d238064b8ccbc33b8e324f4 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Tue, 27 Feb 2018 19:25:47 +0300 Subject: [PATCH 2/9] IDEA-172425 Add ability to enable reflection replacement with any JDK Disable registry: debugger.compiling.evaluator.magic.accessor Enabled by default after JDK 9 --- .../ui/impl/watch/CompilingEvaluatorImpl.java | 16 +++++- .../ExtractLightMethodObjectHandler.java | 54 +++++++++++++------ .../reflect/CompositeReflectionAccessor.java | 41 ++++++++++++++ .../util/resources/misc/registry.properties | 1 + 4 files changed, 95 insertions(+), 17 deletions(-) create mode 100644 java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/CompositeReflectionAccessor.java 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 a5ed7e075af7..a879c6f45535 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 @@ -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)() -> { 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); } diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractLightMethodObjectHandler.java b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractLightMethodObjectHandler.java index 94606da170a2..ad1719f69c23 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractLightMethodObjectHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractLightMethodObjectHandler.java @@ -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 index 000000000000..f1a55dd0f9fb --- /dev/null +++ b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/CompositeReflectionAccessor.java @@ -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 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); + } + } +} diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index ff3bdc2ee616..3fa10356a4aa 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -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 From 49b348d747c74606dd42325ea929109f71f63f68 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Thu, 1 Mar 2018 13:06:31 +0300 Subject: [PATCH 3/9] IDEA-172425 Fix minor issues after review (IDEA-CR-29936) --- .../ExtractLightMethodObjectHandler.java | 8 -------- .../PsiReflectionAccessUtil.java | 15 ++------------- .../reflect/ReflectionAccessMethodBuilder.java | 5 +++-- .../ExtractMethodObject4DebuggerTest.java | 2 +- 4 files changed, 6 insertions(+), 24 deletions(-) diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractLightMethodObjectHandler.java b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractLightMethodObjectHandler.java index ad1719f69c23..1ba69a12ea7e 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractLightMethodObjectHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractLightMethodObjectHandler.java @@ -68,14 +68,6 @@ public class ExtractLightMethodObjectHandler { } } - @Nullable - public static ExtractedData extractLightMethodObject(final Project project, - @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, diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/PsiReflectionAccessUtil.java b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/PsiReflectionAccessUtil.java index f52fa99b5b1e..263dd80d47ef 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/PsiReflectionAccessUtil.java +++ b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/PsiReflectionAccessUtil.java @@ -3,13 +3,11 @@ package com.intellij.refactoring.extractMethodObject; import com.intellij.psi.*; import com.intellij.psi.util.PsiUtil; -import one.util.streamex.StreamEx; +import com.intellij.psi.util.TypeConversionUtil; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Set; - /** * @author Vitaliy.Bibaev */ @@ -65,18 +63,9 @@ public class PsiReflectionAccessUtil { return psiClass == null ? null : psiClass.getQualifiedName(); } - private static final Set PRIMITIVE_TYPES = StreamEx.of("byte", "short", "int", "long", "float", "double", "boolean", "char") - .toImmutableSet(); - @NotNull @Contract(pure = true) public static String classForName(@NotNull String typeName) { - return PRIMITIVE_TYPES.contains(typeName) ? typeName + ".class" : "java.lang.Class.forName(\"" + typeName + "\")"; - } - - @NotNull - @Contract(pure = true) - public static String escape(String str) { - return "\"" + str + "\""; + return TypeConversionUtil.isPrimitive(typeName) ? typeName + ".class" : "java.lang.Class.forName(\"" + typeName + "\")"; } } diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ReflectionAccessMethodBuilder.java b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ReflectionAccessMethodBuilder.java index c196477e1450..5f6ec802e14c 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ReflectionAccessMethodBuilder.java +++ b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ReflectionAccessMethodBuilder.java @@ -1,6 +1,7 @@ // 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.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.util.ClassUtil; import com.intellij.psi.util.PsiUtil; @@ -174,7 +175,7 @@ public class ReflectionAccessMethodBuilder { public String getMemberLookupBlock() { return "while (member == null) {\n" + " try {\n" + - " member = klass.getDeclaredField(" + PsiReflectionAccessUtil.escape(myFieldName) + ");\n" + + " member = klass.getDeclaredField(" + StringUtil.wrapWithDoubleQuote(myFieldName) + ");\n" + " }\n" + " catch(java.lang.NoSuchFieldException e) {\n" + " klass = klass.getSuperclass();\n" + @@ -213,7 +214,7 @@ public class ReflectionAccessMethodBuilder { @Override public String getMemberLookupBlock() { String args = StreamEx.of(myParameters).skip(1).map(x -> PsiReflectionAccessUtil.classForName(x.jvmTypeName)) - .prepend(PsiReflectionAccessUtil.escape(myMethodName)) + .prepend(StringUtil.wrapWithDoubleQuote(myMethodName)) .joining(", ", "(", ")"); return "while (member == null) {\n" + " try {\n" + diff --git a/java/java-tests/testSrc/com/intellij/java/refactoring/ExtractMethodObject4DebuggerTest.java b/java/java-tests/testSrc/com/intellij/java/refactoring/ExtractMethodObject4DebuggerTest.java index c470370328da..dec6518bcba6 100644 --- a/java/java-tests/testSrc/com/intellij/java/refactoring/ExtractMethodObject4DebuggerTest.java +++ b/java/java-tests/testSrc/com/intellij/java/refactoring/ExtractMethodObject4DebuggerTest.java @@ -46,7 +46,7 @@ public class ExtractMethodObject4DebuggerTest extends LightRefactoringTestCase { final JavaCodeFragmentFactory fragmentFactory = JavaCodeFragmentFactory.getInstance(getProject()); final JavaCodeFragment fragment = codeBlock ? fragmentFactory.createCodeBlockCodeFragment(evaluatedText, context, false) : fragmentFactory.createExpressionCodeFragment(evaluatedText, context, null, false); final ExtractLightMethodObjectHandler.ExtractedData extractedData = - ExtractLightMethodObjectHandler.extractLightMethodObject(getProject(), context, fragment, "test"); + ExtractLightMethodObjectHandler.extractLightMethodObject(getProject(), context, fragment, "test", false); assertNotNull(extractedData); assertEquals(expectedCallSite, extractedData.getGeneratedCallText()); final PsiClass innerClass = extractedData.getGeneratedInnerClass(); From 08c86e04d95965f56b3d5165436b7de2b217aa5a Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Thu, 1 Mar 2018 16:28:36 +0300 Subject: [PATCH 4/9] IDEA-172425 Check interfaces members as well (IDEA-CR-29936) --- .../ReflectionAccessMethodBuilder.java | 97 ++++++++----------- 1 file changed, 40 insertions(+), 57 deletions(-) diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ReflectionAccessMethodBuilder.java b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ReflectionAccessMethodBuilder.java index 5f6ec802e14c..34b2c1a3d326 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ReflectionAccessMethodBuilder.java +++ b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ReflectionAccessMethodBuilder.java @@ -11,7 +11,6 @@ import one.util.streamex.StreamEx; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -33,11 +32,32 @@ public class ReflectionAccessMethodBuilder { @Nullable PsiElement context) { checkRequirements(); String parameters = StreamEx.of(myParameters).map(p -> p.type + " " + p.name).joining(", ", "(", ")"); - String returnExpression = ("void".equals(myReturnType) ? "" : "return (" + myReturnType + ")") + myMemberAccessor.getAccessExpression(); - String methodBody = " java.lang.Class klass = " + myMemberAccessor.getClassLookupExpression() + ";\n" + - " " + myMemberAccessor.getMemberType() + " member = null;\n" + - " " + myMemberAccessor.getMemberLookupBlock() + - " " + returnExpression + ";\n"; + String returnExpression = + ("void".equals(myReturnType) ? "member." : "return (" + myReturnType + ")member.") + myMemberAccessor.getAccessExpression(); + String methodBody = " java.lang.Class klass = " + myMemberAccessor.getClassLookupExpression() + ";\n" + + " " + myMemberAccessor.getMemberType() + " member = null;\n" + + " int interfaceNumber = -1;\n" + + " Class[] interfaces = null;\n" + + " while (member == null) {\n" + + " try {\n" + + " member = klass." + myMemberAccessor.getMemberLookupExpression() + ";\n" + + " } catch (java.lang.ReflectiveOperationException e) {\n" + + " if (interfaceNumber == -1) {\n" + + " interfaces = klass.getInterfaces();\n" + + " interfaceNumber = 0;\n" + + " }\n" + + " if (interfaceNumber < interfaces.length) {\n" + + " klass = interfaces[interfaceNumber];\n" + + " interfaceNumber += 1;\n" + + " } else {\n" + + " klass = klass.getSuperclass();\n" + + " if (klass == null) throw e;\n" + + " interfaceNumber = -1;\n" + + " }\n" + + " }\n" + + " }\n" + + " member.setAccessible(true);\n" + + " " + returnExpression + ";\n"; List possibleExceptions = myMemberAccessor.getPossibleExceptions(); if (!possibleExceptions.isEmpty()) { methodBody = "try {\n" + @@ -136,7 +156,7 @@ public class ReflectionAccessMethodBuilder { } private interface MyMemberAccessor { - String getMemberLookupBlock(); + String getMemberLookupExpression(); String getClassLookupExpression(); @@ -149,9 +169,8 @@ public class ReflectionAccessMethodBuilder { private static class MyFieldAccessor implements MyMemberAccessor { - private static final List EXCEPTIONS = Collections.unmodifiableList(Arrays.asList("java.lang.NoSuchFieldException", - "java.lang.IllegalAccessException", - "java.lang.ClassNotFoundException")); + private static final List EXCEPTIONS = Collections.unmodifiableList( + Collections.singletonList("java.lang.ReflectiveOperationException")); private final String myFieldName; private final String myClassName; private final FieldAccessType myAccessType; @@ -172,22 +191,13 @@ public class ReflectionAccessMethodBuilder { } @Override - public String getMemberLookupBlock() { - return "while (member == null) {\n" + - " try {\n" + - " member = klass.getDeclaredField(" + StringUtil.wrapWithDoubleQuote(myFieldName) + ");\n" + - " }\n" + - " catch(java.lang.NoSuchFieldException e) {\n" + - " klass = klass.getSuperclass();\n" + - " if (klass == null) throw e;\n" + - " }\n" + - "}\n" + - "member.setAccessible(true);"; + public String getMemberLookupExpression() { + return "getDeclaredField(" + StringUtil.wrapWithDoubleQuote(myFieldName) + ")"; } @Override public String getAccessExpression() { - return FieldAccessType.GET.equals(myAccessType) ? "member.get(object)" : "member.set(object, value)"; + return FieldAccessType.GET.equals(myAccessType) ? "get(object)" : "set(object, value)"; } @Override @@ -212,20 +222,11 @@ public class ReflectionAccessMethodBuilder { } @Override - public String getMemberLookupBlock() { + public String getMemberLookupExpression() { String args = StreamEx.of(myParameters).skip(1).map(x -> PsiReflectionAccessUtil.classForName(x.jvmTypeName)) .prepend(StringUtil.wrapWithDoubleQuote(myMethodName)) .joining(", ", "(", ")"); - return "while (member == null) {\n" + - " try {\n" + - " member = klass.getDeclaredMethod" + args + ";\n" + - " }\n" + - " catch(java.lang.NoSuchMethodException e) {\n" + - " klass = klass.getSuperclass();\n" + - " if (klass == null) throw e;\n" + - " }\n" + - "}\n" + - "member.setAccessible(true);\n"; + return "getDeclaredMethod" + args; } @Override @@ -242,16 +243,12 @@ public class ReflectionAccessMethodBuilder { @Override public List getPossibleExceptions() { - return Collections.unmodifiableList(Arrays.asList( - "java.lang.NoSuchMethodException", - "java.lang.IllegalAccessException", - "java.lang.ClassNotFoundException", - "java.lang.reflect.InvocationTargetException")); + return Collections.unmodifiableList(Collections.singletonList("java.lang.ReflectiveOperationException")); } @Override public String getAccessExpression() { - return StreamEx.of(myParameters).map(x -> x.name).joining(", ", "member.invoke(", ")"); + return StreamEx.of(myParameters).map(x -> x.name).joining(", ", "invoke(", ")"); } } @@ -264,18 +261,9 @@ public class ReflectionAccessMethodBuilder { } @Override - public String getMemberLookupBlock() { + public String getMemberLookupExpression() { String args = StreamEx.of(myParameters).map(x -> x.jvmTypeName).map(PsiReflectionAccessUtil::classForName).joining(", ", "(", ")"); - return "while (member == null) {\n" + - " try {\n" + - " member = klass.getDeclaredConstructor" + args + ";\n" + - " }\n" + - " catch(java.lang.NoSuchMethodException e) {\n" + - " klass = klass.getSuperclass();\n" + - " if (klass == null) throw e;\n" + - " }\n" + - "}\n" + - "member.setAccessible(true);\n"; + return "getDeclaredConstructor" + args; } @Override @@ -286,7 +274,7 @@ public class ReflectionAccessMethodBuilder { @Override public String getAccessExpression() { String args = StreamEx.of(myParameters).map(x -> x.name).joining(", ", "(", ")"); - return "member.newInstance" + args; + return "newInstance" + args; } @Override @@ -296,12 +284,7 @@ public class ReflectionAccessMethodBuilder { @Override public List getPossibleExceptions() { - return Collections.unmodifiableList(Arrays.asList( - "java.lang.NoSuchMethodException", - "java.lang.IllegalAccessException", - "java.lang.ClassNotFoundException", - "java.lang.reflect.InvocationTargetException", - "java.lang.InstantiationException" + return Collections.unmodifiableList(Collections.singletonList("java.lang.ReflectiveOperationException" )); } } From bc7a94b19ec8509f485969e1309992e59b2f2d3e Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Fri, 2 Mar 2018 12:49:53 +0300 Subject: [PATCH 5/9] IDEA-172425 Fix bug when generated methods have the same name --- .../PsiReflectionAccessUtil.java | 15 ++++++++++++ .../ConstructorReflectionAccessor.java | 6 +++-- .../reflect/FieldReflectionAccessor.java | 23 +++++++++++-------- .../MethodReferenceReflectionAccessor.java | 14 ++++++++--- .../reflect/MethodReflectionAccessor.java | 6 +++-- .../ReflectionAccessMethodBuilder.java | 7 +----- .../reflect/ReflectionAccessorBase.java | 5 ++-- 7 files changed, 50 insertions(+), 26 deletions(-) diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/PsiReflectionAccessUtil.java b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/PsiReflectionAccessUtil.java index 263dd80d47ef..001fcde9d05b 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/PsiReflectionAccessUtil.java +++ b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/PsiReflectionAccessUtil.java @@ -1,6 +1,7 @@ // 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; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; @@ -68,4 +69,18 @@ public class PsiReflectionAccessUtil { public static String classForName(@NotNull String typeName) { return TypeConversionUtil.isPrimitive(typeName) ? typeName + ".class" : "java.lang.Class.forName(\"" + typeName + "\")"; } + + @NotNull + public static String getUniqueMethodName(@NotNull PsiClass psiClass, @NotNull String prefix) { + if (!StringUtil.isJavaIdentifier(prefix)) throw new IllegalArgumentException("prefix must be a correct java identifier: " + prefix); + int i = 1; + String name; + do { + name = prefix + i; + i++; + } + while (psiClass.findMethodsByName(name, false).length != 0); + + return name; + } } diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ConstructorReflectionAccessor.java b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ConstructorReflectionAccessor.java index 691a7ece958e..5194777db84f 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ConstructorReflectionAccessor.java +++ b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ConstructorReflectionAccessor.java @@ -53,7 +53,7 @@ public class ConstructorReflectionAccessor extends ReflectionAccessorBase { + private final MethodReflectionAccessor myMethodAccessor; public MethodReferenceReflectionAccessor(@NotNull PsiClass psiClass, @NotNull PsiElementFactory elementFactory) { super(psiClass, elementFactory); + myMethodAccessor = new MethodReflectionAccessor(psiClass, elementFactory); } @Nullable @@ -35,11 +37,17 @@ public class MethodReferenceReflectionAccessor } @Override - protected void grantAccess(@NotNull MethodReferenceDescriptor descriptor, int order) { - MethodReflectionAccessor methodAccessor = new MethodReflectionAccessor(getOuterClass(), getElementFactory()); + protected void grantAccess(@NotNull MethodReferenceDescriptor descriptor) { PsiLambdaExpression lambda = LambdaRefactoringUtil.convertMethodReferenceToLambda(descriptor.expression, false, true); if (lambda != null) { - methodAccessor.accessThroughReflection(lambda); + PsiElement lambdaBody = lambda.getBody(); + if (lambdaBody instanceof PsiMethodCallExpression) { + PsiMethodCallExpression callExpression = (PsiMethodCallExpression)lambdaBody; + PsiMethod method = callExpression.resolveMethod(); + if (method != null) { + myMethodAccessor.grantAccess(new MethodReflectionAccessor.MethodCallDescriptor(callExpression, method)); + } + } } } diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/MethodReflectionAccessor.java b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/MethodReflectionAccessor.java index 7b13dc574ff7..f009161ea46b 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/MethodReflectionAccessor.java +++ b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/MethodReflectionAccessor.java @@ -2,6 +2,7 @@ package com.intellij.refactoring.extractMethodObject.reflect; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.util.ClassUtil; import com.intellij.refactoring.extractMethodObject.ItemToReplaceDescriptor; @@ -44,7 +45,7 @@ public class MethodReflectionAccessor extends ReflectionAccessorBase myParameters = new SmartList<>(); @@ -75,11 +75,6 @@ public class ReflectionAccessMethodBuilder { private void checkRequirements() { } - public ReflectionAccessMethodBuilder setName(@NotNull String name) { - myName = name; - return this; - } - public ReflectionAccessMethodBuilder accessedMethod(@NotNull String jvmClassName, @NotNull String methodName) { myMemberAccessor = new MyMethodAccessor(jvmClassName, methodName); return this; diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ReflectionAccessorBase.java b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ReflectionAccessorBase.java index f7916f210c60..e627538de79b 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ReflectionAccessorBase.java +++ b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ReflectionAccessorBase.java @@ -29,9 +29,8 @@ public abstract class ReflectionAccessorBase @Override public void accessThroughReflection(@NotNull PsiElement element) { List toReplace = findItemsToReplace(element); - int order = 1; for (T item : toReplace) { - grantAccess(item, order++); + grantAccess(item); } List remaining = findItemsToReplace(element); @@ -52,5 +51,5 @@ public abstract class ReflectionAccessorBase protected abstract List findItemsToReplace(@NotNull PsiElement element); - protected abstract void grantAccess(@NotNull T descriptor, int order); + protected abstract void grantAccess(@NotNull T descriptor); } From 8b68cf26d26dbd6a8766fde3ad784aced4cc0139 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Fri, 2 Mar 2018 18:35:23 +0300 Subject: [PATCH 6/9] IDEA-172425 Ignore reflection warning message in the test outputs --- .../streams/test/TraceExecutionTestCase.java | 14 ++++++++++++-- .../exec/LibraryTraceExecutionTestCase.kt | 10 +++------- .../streams/exec/streamex/StreamExTestCase.kt | 16 +++++++++++++++- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/plugins/stream-debugger/src/com/intellij/debugger/streams/test/TraceExecutionTestCase.java b/plugins/stream-debugger/src/com/intellij/debugger/streams/test/TraceExecutionTestCase.java index f53f92a974cb..6de77acf03cf 100644 --- a/plugins/stream-debugger/src/com/intellij/debugger/streams/test/TraceExecutionTestCase.java +++ b/plugins/stream-debugger/src/com/intellij/debugger/streams/test/TraceExecutionTestCase.java @@ -42,8 +42,18 @@ public abstract class TraceExecutionTestCase extends DebuggerTestCase { private final DebuggerPositionResolver myPositionResolver = new DebuggerPositionResolverImpl(); @Override - protected OutputChecker initOutputChecker() { - return new OutputChecker(getTestAppPath(), getAppOutputPath()); + protected final OutputChecker initOutputChecker() { + return new OutputChecker(getTestAppPath(), getAppOutputPath()) { + @Override + protected String replaceAdditionalInOutput(String str) { + return TraceExecutionTestCase.this.replaceAdditionalInOutput(super.replaceAdditionalInOutput(str)); + } + }; + } + + @NotNull + protected String replaceAdditionalInOutput(@NotNull String str) { + return str; } protected LibrarySupportProvider getLibrarySupportProvider() { diff --git a/plugins/stream-debugger/test/com/intellij/debugger/streams/exec/LibraryTraceExecutionTestCase.kt b/plugins/stream-debugger/test/com/intellij/debugger/streams/exec/LibraryTraceExecutionTestCase.kt index 76afcb183628..d04670b59b0d 100644 --- a/plugins/stream-debugger/test/com/intellij/debugger/streams/exec/LibraryTraceExecutionTestCase.kt +++ b/plugins/stream-debugger/test/com/intellij/debugger/streams/exec/LibraryTraceExecutionTestCase.kt @@ -1,7 +1,6 @@ // Copyright 2000-2017 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.debugger.streams.exec -import com.intellij.debugger.impl.OutputChecker import com.intellij.debugger.streams.test.TraceExecutionTestCase import com.intellij.execution.configurations.JavaParameters import com.intellij.openapi.application.ApplicationManager @@ -23,12 +22,9 @@ abstract class LibraryTraceExecutionTestCase(private val jarName: String) : Trac } } - override fun initOutputChecker(): OutputChecker { - return object : OutputChecker(testAppPath, appOutputPath) { - override fun replaceAdditionalInOutput(str: String): String { - return str.replaceFirst("$libraryDirectory/$jarName", "!LIBRARY_JAR!") - } - } + override fun replaceAdditionalInOutput(str: String): String { + return super.replaceAdditionalInOutput(str) + .replaceFirst("$libraryDirectory/$jarName", "!LIBRARY_JAR!") } override fun createJavaParameters(mainClass: String?): JavaParameters { diff --git a/plugins/stream-debugger/test/com/intellij/debugger/streams/exec/streamex/StreamExTestCase.kt b/plugins/stream-debugger/test/com/intellij/debugger/streams/exec/streamex/StreamExTestCase.kt index 8a9f64b3ff25..58f19fcaef09 100644 --- a/plugins/stream-debugger/test/com/intellij/debugger/streams/exec/streamex/StreamExTestCase.kt +++ b/plugins/stream-debugger/test/com/intellij/debugger/streams/exec/streamex/StreamExTestCase.kt @@ -9,12 +9,26 @@ import com.intellij.debugger.streams.lib.impl.StreamExLibrarySupportProvider * @author Vitaliy.Bibaev */ abstract class StreamExTestCase : LibraryTraceExecutionTestCase("streamex-0.6.5.jar") { - abstract protected val packageName: String + private companion object { + const val STREAM_EX_REFLECTION_WARNING_MESSAGE = + "WARNING: An illegal reflective access operation has occurred\n" + + "WARNING: Illegal reflective access by one.util.streamex.StreamExInternals (file:/C:/Projects/IDEA/community/plugins/" + + "stream-debugger/lib/streamex-0.6.5.jar) to field java.util.stream.AbstractPipeline.sourceSpliterator\n" + + "WARNING: Please consider reporting this to the maintainers of one.util.streamex.StreamExInternals\n" + + "WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations\n" + + "WARNING: All illegal access operations will be denied in a future release\n" + } + + protected abstract val packageName: String override fun getLibrarySupportProvider(): LibrarySupportProvider { return StreamExLibrarySupportProvider() } + override fun replaceAdditionalInOutput(str: String): String { + return super.replaceAdditionalInOutput(str).replace(STREAM_EX_REFLECTION_WARNING_MESSAGE, "") + } + private val className: String get() = packageName + "." + getTestName(false) From 783feba2d2efc2dbc926f36497d707ef8e51162e Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Fri, 2 Mar 2018 18:37:49 +0300 Subject: [PATCH 7/9] IDEA-172425 Enable back TraceStreamAction with JDK 9 --- .../streams/action/TraceStreamAction.java | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/plugins/stream-debugger/src/com/intellij/debugger/streams/action/TraceStreamAction.java b/plugins/stream-debugger/src/com/intellij/debugger/streams/action/TraceStreamAction.java index ded87225648e..2ccd318f1b38 100644 --- a/plugins/stream-debugger/src/com/intellij/debugger/streams/action/TraceStreamAction.java +++ b/plugins/stream-debugger/src/com/intellij/debugger/streams/action/TraceStreamAction.java @@ -16,7 +16,6 @@ import com.intellij.debugger.streams.ui.impl.EvaluationAwareTraceWindow; import com.intellij.debugger.streams.wrapper.StreamChain; import com.intellij.debugger.streams.wrapper.StreamChainBuilder; import com.intellij.internal.statistic.UsageTrigger; -import com.intellij.lang.java.JavaLanguage; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.Presentation; @@ -25,17 +24,11 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.Project; -import com.intellij.openapi.projectRoots.JavaSdk; -import com.intellij.openapi.projectRoots.JavaSdkVersion; -import com.intellij.openapi.projectRoots.Sdk; -import com.intellij.openapi.roots.ProjectRootManager; -import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiEditorUtil; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebuggerManager; -import com.intellij.xdebugger.impl.XDebuggerManagerImpl; import one.util.streamex.StreamEx; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -91,13 +84,6 @@ public class TraceStreamAction extends AnAction { Extensions.getExtensions(LibrarySupportProvider.EP_NAME); final PsiElement element = session == null ? null : myPositionResolver.getNearestElementToBreakpoint(session); - if (element != null && isJdkAtLeast9(session.getProject(), element)) { - XDebuggerManagerImpl.NOTIFICATION_GROUP - .createNotification("This action does not work with JDK 9 yet", MessageType.WARNING) - .notify(session.getProject()); - return; - } - if (element != null) { final List chains = mySupportedLibraries.stream() .filter(library -> library.languageId.equals(element.getLanguage().getID())) @@ -177,18 +163,6 @@ public class TraceStreamAction extends AnAction { return project == null ? null : XDebuggerManager.getInstance(project).getCurrentSession(); } - private static boolean isJdkAtLeast9(@NotNull Project project, @NotNull PsiElement element) { - if (element.getLanguage().is(JavaLanguage.INSTANCE)) { - final Sdk sdk = ProjectRootManager.getInstance(project).getProjectSdk(); - if (sdk != null) { - final JavaSdkVersion javaVersion = JavaSdk.getInstance().getVersion(sdk); - if (javaVersion != null) return javaVersion.isAtLeast(JavaSdkVersion.JDK_1_9); - } - } - - return false; - } - private static class MyStreamChainChooser extends ElementChooserImpl { MyStreamChainChooser(@NotNull Editor editor) { super(editor); From 58b550cec74abbe2655e3b61b060bd4cc5a548a0 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Sun, 4 Mar 2018 22:42:28 +0300 Subject: [PATCH 8/9] IDEA-172425 Make stream debugger tests JDK 9 tolerant Some of tests for stream debugger failed on JDK 9. The reasons are: * The count() termination call does not produce any computations * Stack traces include module name in some frames --- .../streams/exec/AccessibilityFailedTest.java | 8 ++++++++ .../exec/streamex/ConcatenateOperationsTest.kt | 16 ++++++++-------- .../exec/streamex/MappingOperationsTest.kt | 14 +++++++------- .../streamex/MiscIntermediateOperationsTest.kt | 12 ++++++------ .../testData/debug/outs/map.out | 18 ++++++++++-------- .../testData/debug/outs/nulls.out | 15 ++++++++------- .../testData/debug/outs/peek.out | 18 ++++++++++-------- .../debug/outs/primitiveResultLong.out | 10 +++++----- .../testData/debug/src/Map.java | 2 +- .../testData/debug/src/Nulls.java | 2 +- .../testData/debug/src/Peek.java | 2 +- .../debug/src/PrimitiveResultLong.java | 4 ++-- .../testData/streamex/outs/appendMany.out | 7 +++---- .../testData/streamex/outs/appendNone.out | 7 +++---- .../testData/streamex/outs/appendOne.out | 7 +++---- .../testData/streamex/outs/appendToEmpty.out | 7 +++---- .../testData/streamex/outs/invert.out | 7 +++---- .../testData/streamex/outs/keys.out | 7 +++---- .../testData/streamex/outs/mapKeys.out | 7 +++---- .../testData/streamex/outs/mapToEntry.out | 18 ++++++++++-------- .../testData/streamex/outs/mapToKey.out | 7 +++---- .../testData/streamex/outs/mapToValue.out | 7 +++---- .../testData/streamex/outs/mapValues.out | 7 +++---- .../testData/streamex/outs/parallel.out | 7 +++---- .../testData/streamex/outs/prefixKeys.out | 7 +++---- .../testData/streamex/outs/prefixValues.out | 7 +++---- .../testData/streamex/outs/prependMany.out | 7 +++---- .../testData/streamex/outs/prependNone.out | 7 +++---- .../testData/streamex/outs/prependOne.out | 7 +++---- .../testData/streamex/outs/prependToEmpty.out | 7 +++---- .../testData/streamex/outs/withFirst.out | 7 +++---- .../testData/streamex/outs/zipWithGreater.out | 7 +++---- .../testData/streamex/outs/zipWithLesser.out | 7 +++---- .../streamex/outs/zipWithSameSizes.out | 7 +++---- .../streamex/src/concatenate/AppendMany.java | 3 +-- .../streamex/src/concatenate/AppendNone.java | 3 +-- .../streamex/src/concatenate/AppendOne.java | 3 +-- .../src/concatenate/AppendToEmpty.java | 3 +-- .../streamex/src/concatenate/PrependMany.java | 3 +-- .../streamex/src/concatenate/PrependNone.java | 3 +-- .../streamex/src/concatenate/PrependOne.java | 3 +-- .../src/concatenate/PrependToEmpty.java | 3 +-- .../testData/streamex/src/mapping/Invert.java | 3 +-- .../testData/streamex/src/mapping/Keys.java | 3 +-- .../testData/streamex/src/mapping/MapKeys.java | 3 +-- .../streamex/src/mapping/MapToEntry.java | 4 ++-- .../streamex/src/mapping/MapToKey.java | 5 ++--- .../streamex/src/mapping/MapToValue.java | 5 ++--- .../streamex/src/mapping/MapValues.java | 5 ++--- .../streamex/src/mapping/WithFirst.java | 3 +-- .../testData/streamex/src/misc/Parallel.java | 3 +-- .../testData/streamex/src/misc/PrefixKeys.java | 3 +-- .../streamex/src/misc/PrefixValues.java | 3 +-- .../streamex/src/misc/ZipWithGreater.java | 3 +-- .../streamex/src/misc/ZipWithLesser.java | 3 +-- .../streamex/src/misc/ZipWithSameSizes.java | 3 +-- 56 files changed, 166 insertions(+), 193 deletions(-) diff --git a/plugins/stream-debugger/test/com/intellij/debugger/streams/exec/AccessibilityFailedTest.java b/plugins/stream-debugger/test/com/intellij/debugger/streams/exec/AccessibilityFailedTest.java index 64f877a5cc59..da3bceab7643 100644 --- a/plugins/stream-debugger/test/com/intellij/debugger/streams/exec/AccessibilityFailedTest.java +++ b/plugins/stream-debugger/test/com/intellij/debugger/streams/exec/AccessibilityFailedTest.java @@ -1,10 +1,18 @@ // Copyright 2000-2017 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.debugger.streams.exec; +import org.jetbrains.annotations.NotNull; + /** * @author Vitaliy.Bibaev */ public class AccessibilityFailedTest extends FailEvaluationTestCase { + @NotNull + @Override + protected String replaceAdditionalInOutput(@NotNull String str) { + // stack traces in jdk 9 include module name. The following code removes module name from the stack trace. + return super.replaceAdditionalInOutput(str).replace("at java.base/", "at "); + } /** * Now, evaluation of such test case is not supported. MagicAccessorImpl cannot be parent for a subclass of the class "Super" diff --git a/plugins/stream-debugger/test/com/intellij/debugger/streams/exec/streamex/ConcatenateOperationsTest.kt b/plugins/stream-debugger/test/com/intellij/debugger/streams/exec/streamex/ConcatenateOperationsTest.kt index 1e9153a49969..5db1377e1113 100644 --- a/plugins/stream-debugger/test/com/intellij/debugger/streams/exec/streamex/ConcatenateOperationsTest.kt +++ b/plugins/stream-debugger/test/com/intellij/debugger/streams/exec/streamex/ConcatenateOperationsTest.kt @@ -7,13 +7,13 @@ package com.intellij.debugger.streams.exec.streamex class ConcatenateOperationsTest : StreamExTestCase() { override val packageName: String = "concatenate" - fun testAppendToEmpty() = doStreamExWithResultTest() - fun testAppendNone() = doStreamExWithResultTest() - fun testAppendOne() = doStreamExWithResultTest() - fun testAppendMany() = doStreamExWithResultTest() + fun testAppendToEmpty() = doStreamExVoidTest() + fun testAppendNone() = doStreamExVoidTest() + fun testAppendOne() = doStreamExVoidTest() + fun testAppendMany() = doStreamExVoidTest() - fun testPrependToEmpty() = doStreamExWithResultTest() - fun testPrependNone() = doStreamExWithResultTest() - fun testPrependOne() = doStreamExWithResultTest() - fun testPrependMany() = doStreamExWithResultTest() + fun testPrependToEmpty() = doStreamExVoidTest() + fun testPrependNone() = doStreamExVoidTest() + fun testPrependOne() = doStreamExVoidTest() + fun testPrependMany() = doStreamExVoidTest() } \ No newline at end of file diff --git a/plugins/stream-debugger/test/com/intellij/debugger/streams/exec/streamex/MappingOperationsTest.kt b/plugins/stream-debugger/test/com/intellij/debugger/streams/exec/streamex/MappingOperationsTest.kt index e796516b3c10..d848568a230b 100644 --- a/plugins/stream-debugger/test/com/intellij/debugger/streams/exec/streamex/MappingOperationsTest.kt +++ b/plugins/stream-debugger/test/com/intellij/debugger/streams/exec/streamex/MappingOperationsTest.kt @@ -10,15 +10,15 @@ class MappingOperationsTest : StreamExTestCase() { fun testElements() = doStreamExWithResultTest() fun testMapToEntry() = doStreamExWithResultTest() - fun testMapKeys() = doStreamExWithResultTest() - fun testMapToKey() = doStreamExWithResultTest() - fun testMapValues() = doStreamExWithResultTest() - fun testMapToValue() = doStreamExWithResultTest() + fun testMapKeys() = doStreamExVoidTest() + fun testMapToKey() = doStreamExVoidTest() + fun testMapValues() = doStreamExVoidTest() + fun testMapToValue() = doStreamExVoidTest() fun testMapKeyValue() = doStreamExWithResultTest() - fun testInvert() = doStreamExWithResultTest() + fun testInvert() = doStreamExVoidTest() - fun testKeys() = doStreamExWithResultTest() + fun testKeys() = doStreamExVoidTest() fun testValues() = doStreamExWithResultTest() fun testJoin() = doStreamExVoidTest() @@ -30,5 +30,5 @@ class MappingOperationsTest : StreamExTestCase() { fun testMapFirstOrElse() = doStreamExWithResultTest() fun testMapLastOrElse() = doStreamExWithResultTest() - fun testWithFirst() = doStreamExWithResultTest() + fun testWithFirst() = doStreamExVoidTest() } \ No newline at end of file diff --git a/plugins/stream-debugger/test/com/intellij/debugger/streams/exec/streamex/MiscIntermediateOperationsTest.kt b/plugins/stream-debugger/test/com/intellij/debugger/streams/exec/streamex/MiscIntermediateOperationsTest.kt index c3ccba352a06..3287b79eec46 100644 --- a/plugins/stream-debugger/test/com/intellij/debugger/streams/exec/streamex/MiscIntermediateOperationsTest.kt +++ b/plugins/stream-debugger/test/com/intellij/debugger/streams/exec/streamex/MiscIntermediateOperationsTest.kt @@ -13,13 +13,13 @@ class MiscIntermediateOperationsTest : StreamExTestCase() { fun testSkipOrdered() = doStreamExWithResultTest() - fun testParallel() = doStreamExWithResultTest() + fun testParallel() = doStreamExVoidTest() - fun testZipWithSameSizes() = doStreamExWithResultTest() - fun testZipWithLesser() = doStreamExWithResultTest() - fun testZipWithGreater() = doStreamExWithResultTest() + fun testZipWithSameSizes() = doStreamExVoidTest() + fun testZipWithLesser() = doStreamExVoidTest() + fun testZipWithGreater() = doStreamExVoidTest() fun testPrefix() = doStreamExVoidTest() - fun testPrefixKeys() = doStreamExWithResultTest() - fun testPrefixValues() = doStreamExWithResultTest() + fun testPrefixKeys() = doStreamExVoidTest() + fun testPrefixValues() = doStreamExVoidTest() } \ No newline at end of file diff --git a/plugins/stream-debugger/testData/debug/outs/map.out b/plugins/stream-debugger/testData/debug/outs/map.out index af17baae83ec..04a56b2226e8 100644 --- a/plugins/stream-debugger/testData/debug/outs/map.out +++ b/plugins/stream-debugger/testData/debug/outs/map.out @@ -4,13 +4,13 @@ Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socke Map.java:6 Stream.of(1, 2, 3) .map(x -> x * x) -.count() +.toArray() map before: 1,3,5 after: 2,4,6 -count +toArray before: 2,4,6 - after: nothing + after: 7,8,9 mappings for map direct: 1 -> 2 @@ -20,13 +20,15 @@ mappings for map 1 <- 2 3 <- 4 5 <- 6 -mappings for count +mappings for toArray direct: - 2 -> nothing - 4 -> nothing - 6 -> nothing + 2 -> 7 + 4 -> 8 + 6 -> 9 reverse: - empty + 2 <- 7 + 4 <- 8 + 6 <- 9 Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 diff --git a/plugins/stream-debugger/testData/debug/outs/nulls.out b/plugins/stream-debugger/testData/debug/outs/nulls.out index 5e9a44a407e2..15ec196ea4a1 100644 --- a/plugins/stream-debugger/testData/debug/outs/nulls.out +++ b/plugins/stream-debugger/testData/debug/outs/nulls.out @@ -3,16 +3,17 @@ LineBreakpoint created at Nulls.java:6 Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Nulls.java:6 Stream.of(null, null) -.count() -count +.toArray() +toArray before: 1,2 - after: nothing -mappings for count + after: 3,4 +mappings for toArray direct: - 1 -> nothing - 2 -> nothing + 1 -> 3 + 2 -> 4 reverse: - empty + 1 <- 3 + 2 <- 4 Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 diff --git a/plugins/stream-debugger/testData/debug/outs/peek.out b/plugins/stream-debugger/testData/debug/outs/peek.out index feacb5132c0d..5d3da676fffb 100644 --- a/plugins/stream-debugger/testData/debug/outs/peek.out +++ b/plugins/stream-debugger/testData/debug/outs/peek.out @@ -4,13 +4,13 @@ Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socke Peek.java:6 Stream.of(1, 2, 3) .peek(x -> {}) -.count() +.toArray() peek before: 1,3,5 after: 2,4,6 -count +toArray before: 2,4,6 - after: nothing + after: 7,8,9 mappings for peek direct: 1 -> 2 @@ -20,13 +20,15 @@ mappings for peek 1 <- 2 3 <- 4 5 <- 6 -mappings for count +mappings for toArray direct: - 2 -> nothing - 4 -> nothing - 6 -> nothing + 2 -> 7 + 4 -> 8 + 6 -> 9 reverse: - empty + 2 <- 7 + 4 <- 8 + 6 <- 9 Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 diff --git a/plugins/stream-debugger/testData/debug/outs/primitiveResultLong.out b/plugins/stream-debugger/testData/debug/outs/primitiveResultLong.out index d73dfc1bf7ba..cd23783beeff 100644 --- a/plugins/stream-debugger/testData/debug/outs/primitiveResultLong.out +++ b/plugins/stream-debugger/testData/debug/outs/primitiveResultLong.out @@ -2,14 +2,14 @@ LineBreakpoint created at PrimitiveResultLong.java:6 !JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !OUTPUT_PATH!;!RT_JAR! PrimitiveResultLong Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' PrimitiveResultLong.java:6 -IntStream.of(1, 2) -.count() +LongStream.of(1, 2) +.sum() Result type:long -value = 2 -count +value = 3 +sum before: 1,2 after: nothing -mappings for count +mappings for sum direct: 1 -> nothing 2 -> nothing diff --git a/plugins/stream-debugger/testData/debug/src/Map.java b/plugins/stream-debugger/testData/debug/src/Map.java index 2623a2230c49..ab201f82ccd8 100644 --- a/plugins/stream-debugger/testData/debug/src/Map.java +++ b/plugins/stream-debugger/testData/debug/src/Map.java @@ -3,6 +3,6 @@ import java.util.stream.Stream; public class Map { public static void main(String[] args) { // Breakpoint! - final long res = Stream.of(1, 2, 3).map(x -> x * x).count(); + Stream.of(1, 2, 3).map(x -> x * x).toArray(); } } diff --git a/plugins/stream-debugger/testData/debug/src/Nulls.java b/plugins/stream-debugger/testData/debug/src/Nulls.java index 8bce36b4f76e..980027928cc3 100644 --- a/plugins/stream-debugger/testData/debug/src/Nulls.java +++ b/plugins/stream-debugger/testData/debug/src/Nulls.java @@ -3,6 +3,6 @@ import java.util.stream.Stream; public class Nulls { public static void main(String[] args) { // Breakpoint! - Stream.of(null, null).count(); + Stream.of(null, null).toArray(); } } diff --git a/plugins/stream-debugger/testData/debug/src/Peek.java b/plugins/stream-debugger/testData/debug/src/Peek.java index d343e045a214..77995779db14 100644 --- a/plugins/stream-debugger/testData/debug/src/Peek.java +++ b/plugins/stream-debugger/testData/debug/src/Peek.java @@ -3,6 +3,6 @@ import java.util.stream.Stream; public class Peek { public static void main(String[] args) { // Breakpoint! - final long res = Stream.of(1, 2, 3).peek(x -> {}).count(); + Stream.of(1, 2, 3).peek(x -> {}).toArray(); } } diff --git a/plugins/stream-debugger/testData/debug/src/PrimitiveResultLong.java b/plugins/stream-debugger/testData/debug/src/PrimitiveResultLong.java index 0eabcd0bb30c..fca49b98ab5a 100644 --- a/plugins/stream-debugger/testData/debug/src/PrimitiveResultLong.java +++ b/plugins/stream-debugger/testData/debug/src/PrimitiveResultLong.java @@ -1,8 +1,8 @@ -import java.util.stream.IntStream; +import java.util.stream.LongStream; public class PrimitiveResultLong { public static void main(String[] args) { // Breakpoint! - long res = IntStream.of(1, 2).count(); + long res = LongStream.of(1, 2).sum(); } } diff --git a/plugins/stream-debugger/testData/streamex/outs/appendMany.out b/plugins/stream-debugger/testData/streamex/outs/appendMany.out index f4399a8ab116..4b4e6fbe4b58 100644 --- a/plugins/stream-debugger/testData/streamex/outs/appendMany.out +++ b/plugins/stream-debugger/testData/streamex/outs/appendMany.out @@ -4,11 +4,11 @@ Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socke AppendMany.java:10 StreamEx.of(1, 2) .append(Stream.of(3, 4, 5)) -.count() +.forEach(x -> {}) append before: 1,3 after: 2,4,5,6,7 -count +forEach before: 2,4,5,6,7 after: nothing mappings for append @@ -21,7 +21,7 @@ mappings for append nothing <- 5 nothing <- 6 nothing <- 7 -mappings for count +mappings for forEach direct: 2 -> nothing 4 -> nothing @@ -33,4 +33,3 @@ mappings for count Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 -5 diff --git a/plugins/stream-debugger/testData/streamex/outs/appendNone.out b/plugins/stream-debugger/testData/streamex/outs/appendNone.out index b4aaf0b40cc8..6c2a420b6dfd 100644 --- a/plugins/stream-debugger/testData/streamex/outs/appendNone.out +++ b/plugins/stream-debugger/testData/streamex/outs/appendNone.out @@ -4,11 +4,11 @@ Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socke AppendNone.java:8 StreamEx.of(1, 2) .append() -.count() +.forEach(x -> {}) append before: 1,3 after: 2,4 -count +forEach before: 2,4 after: nothing mappings for append @@ -18,7 +18,7 @@ mappings for append reverse: 1 <- 2 3 <- 4 -mappings for count +mappings for forEach direct: 2 -> nothing 4 -> nothing @@ -27,4 +27,3 @@ mappings for count Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 -2 diff --git a/plugins/stream-debugger/testData/streamex/outs/appendOne.out b/plugins/stream-debugger/testData/streamex/outs/appendOne.out index af05a3e91b4b..6b0a16e1fed1 100644 --- a/plugins/stream-debugger/testData/streamex/outs/appendOne.out +++ b/plugins/stream-debugger/testData/streamex/outs/appendOne.out @@ -4,11 +4,11 @@ Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socke AppendOne.java:8 StreamEx.of(1, 2) .append(3) -.count() +.forEach(x -> {}) append before: 1,3 after: 2,4,5 -count +forEach before: 2,4,5 after: nothing mappings for append @@ -19,7 +19,7 @@ mappings for append 1 <- 2 3 <- 4 nothing <- 5 -mappings for count +mappings for forEach direct: 2 -> nothing 4 -> nothing @@ -29,4 +29,3 @@ mappings for count Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 -3 diff --git a/plugins/stream-debugger/testData/streamex/outs/appendToEmpty.out b/plugins/stream-debugger/testData/streamex/outs/appendToEmpty.out index 210b0c572a9c..7db084d04a32 100644 --- a/plugins/stream-debugger/testData/streamex/outs/appendToEmpty.out +++ b/plugins/stream-debugger/testData/streamex/outs/appendToEmpty.out @@ -4,11 +4,11 @@ Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socke AppendToEmpty.java:8 StreamEx.empty() .append(1) -.count() +.forEach(x -> {}) append before: nothing after: 1 -count +forEach before: 1 after: nothing mappings for append @@ -16,7 +16,7 @@ mappings for append empty reverse: nothing <- 1 -mappings for count +mappings for forEach direct: 1 -> nothing reverse: @@ -24,4 +24,3 @@ mappings for count Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 -1 diff --git a/plugins/stream-debugger/testData/streamex/outs/invert.out b/plugins/stream-debugger/testData/streamex/outs/invert.out index 7c1642bd4c80..01f4d7b8e87e 100644 --- a/plugins/stream-debugger/testData/streamex/outs/invert.out +++ b/plugins/stream-debugger/testData/streamex/outs/invert.out @@ -4,11 +4,11 @@ Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socke Invert.java:8 EntryStream.of(1, 1, 2, 4, 3, 9) .invert() -.count() +.forEach(x -> {}) invert before: 1,3,5 after: 2,4,6 -count +forEach before: 2,4,6 after: nothing mappings for invert @@ -20,7 +20,7 @@ mappings for invert 1 <- 2 3 <- 4 5 <- 6 -mappings for count +mappings for forEach direct: 2 -> nothing 4 -> nothing @@ -30,4 +30,3 @@ mappings for count Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 -3 diff --git a/plugins/stream-debugger/testData/streamex/outs/keys.out b/plugins/stream-debugger/testData/streamex/outs/keys.out index 2c61a9330d33..a42c3bc7547e 100644 --- a/plugins/stream-debugger/testData/streamex/outs/keys.out +++ b/plugins/stream-debugger/testData/streamex/outs/keys.out @@ -4,11 +4,11 @@ Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socke Keys.java:8 EntryStream.of(1, 1, 2, 4, 3, 9) .keys() -.count() +.forEach(x -> {}) keys before: 1,3,5 after: 2,4,6 -count +forEach before: 2,4,6 after: nothing mappings for keys @@ -20,7 +20,7 @@ mappings for keys 1 <- 2 3 <- 4 5 <- 6 -mappings for count +mappings for forEach direct: 2 -> nothing 4 -> nothing @@ -30,4 +30,3 @@ mappings for count Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 -3 diff --git a/plugins/stream-debugger/testData/streamex/outs/mapKeys.out b/plugins/stream-debugger/testData/streamex/outs/mapKeys.out index ad6f51a953e7..80e6f6d67ae6 100644 --- a/plugins/stream-debugger/testData/streamex/outs/mapKeys.out +++ b/plugins/stream-debugger/testData/streamex/outs/mapKeys.out @@ -4,11 +4,11 @@ Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socke MapKeys.java:8 EntryStream.of(1, 1, 2, 4, 3, 9) .mapKeys(x -> x - 1) -.count() +.forEach(x -> {}) mapKeys before: 1,3,5 after: 2,4,6 -count +forEach before: 2,4,6 after: nothing mappings for mapKeys @@ -20,7 +20,7 @@ mappings for mapKeys 1 <- 2 3 <- 4 5 <- 6 -mappings for count +mappings for forEach direct: 2 -> nothing 4 -> nothing @@ -30,4 +30,3 @@ mappings for count Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 -3 diff --git a/plugins/stream-debugger/testData/streamex/outs/mapToEntry.out b/plugins/stream-debugger/testData/streamex/outs/mapToEntry.out index 18995c206a17..9a9f8d04c201 100644 --- a/plugins/stream-debugger/testData/streamex/outs/mapToEntry.out +++ b/plugins/stream-debugger/testData/streamex/outs/mapToEntry.out @@ -4,13 +4,13 @@ Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socke MapToEntry.java:8 StreamEx.of(1, 2, 3) .mapToEntry(x -> x * x) -.count() +.toArray() mapToEntry before: 1,3,5 after: 2,4,6 -count +toArray before: 2,4,6 - after: nothing + after: 7,8,9 mappings for mapToEntry direct: 1 -> 2 @@ -20,13 +20,15 @@ mappings for mapToEntry 1 <- 2 3 <- 4 5 <- 6 -mappings for count +mappings for toArray direct: - 2 -> nothing - 4 -> nothing - 6 -> nothing + 2 -> 7 + 4 -> 8 + 6 -> 9 reverse: - empty + 2 <- 7 + 4 <- 8 + 6 <- 9 Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 diff --git a/plugins/stream-debugger/testData/streamex/outs/mapToKey.out b/plugins/stream-debugger/testData/streamex/outs/mapToKey.out index d27b0b142adf..0d7a2939e2b7 100644 --- a/plugins/stream-debugger/testData/streamex/outs/mapToKey.out +++ b/plugins/stream-debugger/testData/streamex/outs/mapToKey.out @@ -4,11 +4,11 @@ Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socke MapToKey.java:8 EntryStream.of(1, 1, 2, 4, 3, 9) .mapToKey((k, v) -> k + v) -.count() +.forEach(x -> {}) mapToKey before: 1,3,5 after: 2,4,6 -count +forEach before: 2,4,6 after: nothing mappings for mapToKey @@ -20,7 +20,7 @@ mappings for mapToKey 1 <- 2 3 <- 4 5 <- 6 -mappings for count +mappings for forEach direct: 2 -> nothing 4 -> nothing @@ -30,4 +30,3 @@ mappings for count Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 -3 diff --git a/plugins/stream-debugger/testData/streamex/outs/mapToValue.out b/plugins/stream-debugger/testData/streamex/outs/mapToValue.out index 5b689c3da074..178fc128cfb7 100644 --- a/plugins/stream-debugger/testData/streamex/outs/mapToValue.out +++ b/plugins/stream-debugger/testData/streamex/outs/mapToValue.out @@ -4,11 +4,11 @@ Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socke MapToValue.java:8 EntryStream.of(1, 1, 2, 4, 3, 9) .mapToValue((k, v) -> k + v) -.count() +.forEach(x -> {}) mapToValue before: 1,3,5 after: 2,4,6 -count +forEach before: 2,4,6 after: nothing mappings for mapToValue @@ -20,7 +20,7 @@ mappings for mapToValue 1 <- 2 3 <- 4 5 <- 6 -mappings for count +mappings for forEach direct: 2 -> nothing 4 -> nothing @@ -30,4 +30,3 @@ mappings for count Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 -3 diff --git a/plugins/stream-debugger/testData/streamex/outs/mapValues.out b/plugins/stream-debugger/testData/streamex/outs/mapValues.out index 176762951708..c3473c36bb25 100644 --- a/plugins/stream-debugger/testData/streamex/outs/mapValues.out +++ b/plugins/stream-debugger/testData/streamex/outs/mapValues.out @@ -4,11 +4,11 @@ Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socke MapValues.java:8 EntryStream.of(1, 1, 2, 4, 3, 9) .mapValues(x -> 0) -.count() +.forEach(x -> {}) mapValues before: 1,3,5 after: 2,4,6 -count +forEach before: 2,4,6 after: nothing mappings for mapValues @@ -20,7 +20,7 @@ mappings for mapValues 1 <- 2 3 <- 4 5 <- 6 -mappings for count +mappings for forEach direct: 2 -> nothing 4 -> nothing @@ -30,4 +30,3 @@ mappings for count Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 -3 diff --git a/plugins/stream-debugger/testData/streamex/outs/parallel.out b/plugins/stream-debugger/testData/streamex/outs/parallel.out index b86b45b534b4..dc7cddf1ba1c 100644 --- a/plugins/stream-debugger/testData/streamex/outs/parallel.out +++ b/plugins/stream-debugger/testData/streamex/outs/parallel.out @@ -4,11 +4,11 @@ Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socke Parallel.java:11 StreamEx.of(1, 2, 3) .parallel((ForkJoinPool) Executors.newWorkStealingPool()) -.count() +.forEach(x -> {}) parallel before: 1,3,5 after: 2,4,6 -count +forEach before: 2,4,6 after: nothing mappings for parallel @@ -20,7 +20,7 @@ mappings for parallel 1 <- 2 3 <- 4 5 <- 6 -mappings for count +mappings for forEach direct: 2 -> nothing 4 -> nothing @@ -30,4 +30,3 @@ mappings for count Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 -3 diff --git a/plugins/stream-debugger/testData/streamex/outs/prefixKeys.out b/plugins/stream-debugger/testData/streamex/outs/prefixKeys.out index 148a318e3562..07feae526387 100644 --- a/plugins/stream-debugger/testData/streamex/outs/prefixKeys.out +++ b/plugins/stream-debugger/testData/streamex/outs/prefixKeys.out @@ -4,11 +4,11 @@ Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socke PrefixKeys.java:8 EntryStream.of(1, 1, 2, 4, 3, 9) .prefixKeys((l, r) -> l + r) -.count() +.forEach(x -> {}) prefixKeys before: 1,3,5 after: 2,4,6 -count +forEach before: 2,4,6 after: nothing mappings for prefixKeys @@ -20,7 +20,7 @@ mappings for prefixKeys nothing <- 2 nothing <- 4 nothing <- 6 -mappings for count +mappings for forEach direct: 2 -> nothing 4 -> nothing @@ -30,4 +30,3 @@ mappings for count Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 -3 diff --git a/plugins/stream-debugger/testData/streamex/outs/prefixValues.out b/plugins/stream-debugger/testData/streamex/outs/prefixValues.out index 885fbea419e1..2b480edcd3ad 100644 --- a/plugins/stream-debugger/testData/streamex/outs/prefixValues.out +++ b/plugins/stream-debugger/testData/streamex/outs/prefixValues.out @@ -4,11 +4,11 @@ Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socke PrefixValues.java:8 EntryStream.of(1, 1, 2, 4, 3, 9) .prefixValues((l, r) -> l + r) -.count() +.forEach(x -> {}) prefixValues before: 1,3,5 after: 2,4,6 -count +forEach before: 2,4,6 after: nothing mappings for prefixValues @@ -20,7 +20,7 @@ mappings for prefixValues nothing <- 2 nothing <- 4 nothing <- 6 -mappings for count +mappings for forEach direct: 2 -> nothing 4 -> nothing @@ -30,4 +30,3 @@ mappings for count Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 -3 diff --git a/plugins/stream-debugger/testData/streamex/outs/prependMany.out b/plugins/stream-debugger/testData/streamex/outs/prependMany.out index 8cec2de1c196..e7fb25f7797a 100644 --- a/plugins/stream-debugger/testData/streamex/outs/prependMany.out +++ b/plugins/stream-debugger/testData/streamex/outs/prependMany.out @@ -4,11 +4,11 @@ Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socke PrependMany.java:10 StreamEx.of(1, 2) .prepend(Stream.of(3, 4, 5)) -.count() +.forEach(x -> {}) prepend before: 4,6 after: 1,2,3,5,7 -count +forEach before: 1,2,3,5,7 after: nothing mappings for prepend @@ -21,7 +21,7 @@ mappings for prepend nothing <- 3 4 <- 5 6 <- 7 -mappings for count +mappings for forEach direct: 1 -> nothing 2 -> nothing @@ -33,4 +33,3 @@ mappings for count Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 -5 diff --git a/plugins/stream-debugger/testData/streamex/outs/prependNone.out b/plugins/stream-debugger/testData/streamex/outs/prependNone.out index ffa3ffee5a67..585ce7eeb0c7 100644 --- a/plugins/stream-debugger/testData/streamex/outs/prependNone.out +++ b/plugins/stream-debugger/testData/streamex/outs/prependNone.out @@ -4,11 +4,11 @@ Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socke PrependNone.java:8 StreamEx.of(1, 2) .prepend() -.count() +.forEach(x -> {}) prepend before: 1,3 after: 2,4 -count +forEach before: 2,4 after: nothing mappings for prepend @@ -18,7 +18,7 @@ mappings for prepend reverse: 1 <- 2 3 <- 4 -mappings for count +mappings for forEach direct: 2 -> nothing 4 -> nothing @@ -27,4 +27,3 @@ mappings for count Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 -2 diff --git a/plugins/stream-debugger/testData/streamex/outs/prependOne.out b/plugins/stream-debugger/testData/streamex/outs/prependOne.out index dd8ad83752e7..0c36c1c60c62 100644 --- a/plugins/stream-debugger/testData/streamex/outs/prependOne.out +++ b/plugins/stream-debugger/testData/streamex/outs/prependOne.out @@ -4,11 +4,11 @@ Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socke PrependOne.java:8 StreamEx.of(1, 2) .prepend(3) -.count() +.forEach(x -> {}) prepend before: 2,4 after: 1,3,5 -count +forEach before: 1,3,5 after: nothing mappings for prepend @@ -19,7 +19,7 @@ mappings for prepend nothing <- 1 2 <- 3 4 <- 5 -mappings for count +mappings for forEach direct: 1 -> nothing 3 -> nothing @@ -29,4 +29,3 @@ mappings for count Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 -3 diff --git a/plugins/stream-debugger/testData/streamex/outs/prependToEmpty.out b/plugins/stream-debugger/testData/streamex/outs/prependToEmpty.out index 42d9ca1cb0e8..1ebebf886c42 100644 --- a/plugins/stream-debugger/testData/streamex/outs/prependToEmpty.out +++ b/plugins/stream-debugger/testData/streamex/outs/prependToEmpty.out @@ -4,11 +4,11 @@ Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socke PrependToEmpty.java:8 StreamEx.empty() .prepend(1) -.count() +.forEach(x -> {}) prepend before: nothing after: 1 -count +forEach before: 1 after: nothing mappings for prepend @@ -16,7 +16,7 @@ mappings for prepend empty reverse: nothing <- 1 -mappings for count +mappings for forEach direct: 1 -> nothing reverse: @@ -24,4 +24,3 @@ mappings for count Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 -1 diff --git a/plugins/stream-debugger/testData/streamex/outs/withFirst.out b/plugins/stream-debugger/testData/streamex/outs/withFirst.out index 01b5f295cd9f..0d54f207a13c 100644 --- a/plugins/stream-debugger/testData/streamex/outs/withFirst.out +++ b/plugins/stream-debugger/testData/streamex/outs/withFirst.out @@ -4,11 +4,11 @@ Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socke WithFirst.java:8 StreamEx.of(10., 2., 3.) .withFirst((first, other) -> first * other) -.count() +.forEach(x -> {}) withFirst before: 1,3,5 after: 2,4,6 -count +forEach before: 2,4,6 after: nothing mappings for withFirst @@ -20,7 +20,7 @@ mappings for withFirst 1 <- 2 3 <- 4 5 <- 6 -mappings for count +mappings for forEach direct: 2 -> nothing 4 -> nothing @@ -30,4 +30,3 @@ mappings for count Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 -3 diff --git a/plugins/stream-debugger/testData/streamex/outs/zipWithGreater.out b/plugins/stream-debugger/testData/streamex/outs/zipWithGreater.out index d5764fe84838..1ab20ea2d4ef 100644 --- a/plugins/stream-debugger/testData/streamex/outs/zipWithGreater.out +++ b/plugins/stream-debugger/testData/streamex/outs/zipWithGreater.out @@ -4,11 +4,11 @@ Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socke ZipWithGreater.java:10 StreamEx.of(1, 2, 3) .zipWith(Stream.of(1, 3, 9, 16, 25)) -.count() +.forEach(x -> {}) zipWith before: 1,3,5 after: 2,4,6 -count +forEach before: 2,4,6 after: nothing mappings for zipWith @@ -20,7 +20,7 @@ mappings for zipWith 1 <- 2 3 <- 4 5 <- 6 -mappings for count +mappings for forEach direct: 2 -> nothing 4 -> nothing @@ -30,4 +30,3 @@ mappings for count Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 -3 diff --git a/plugins/stream-debugger/testData/streamex/outs/zipWithLesser.out b/plugins/stream-debugger/testData/streamex/outs/zipWithLesser.out index c958f50f8872..363069320daf 100644 --- a/plugins/stream-debugger/testData/streamex/outs/zipWithLesser.out +++ b/plugins/stream-debugger/testData/streamex/outs/zipWithLesser.out @@ -4,11 +4,11 @@ Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socke ZipWithLesser.java:10 StreamEx.of(1, 2, 3, 4, 5) .zipWith(Stream.of(1, 3, 9)) -.count() +.forEach(x -> {}) zipWith before: 1,3,5 after: 2,4,6 -count +forEach before: 2,4,6 after: nothing mappings for zipWith @@ -20,7 +20,7 @@ mappings for zipWith 1 <- 2 3 <- 4 5 <- 6 -mappings for count +mappings for forEach direct: 2 -> nothing 4 -> nothing @@ -30,4 +30,3 @@ mappings for count Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 -3 diff --git a/plugins/stream-debugger/testData/streamex/outs/zipWithSameSizes.out b/plugins/stream-debugger/testData/streamex/outs/zipWithSameSizes.out index 952bda792b59..66fec718f020 100644 --- a/plugins/stream-debugger/testData/streamex/outs/zipWithSameSizes.out +++ b/plugins/stream-debugger/testData/streamex/outs/zipWithSameSizes.out @@ -4,11 +4,11 @@ Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socke ZipWithSameSizes.java:10 StreamEx.of(1, 2, 3) .zipWith(Stream.of(1, 3, 9)) -.count() +.forEach(x -> {}) zipWith before: 1,3,5 after: 2,4,6 -count +forEach before: 2,4,6 after: nothing mappings for zipWith @@ -20,7 +20,7 @@ mappings for zipWith 1 <- 2 3 <- 4 5 <- 6 -mappings for count +mappings for forEach direct: 2 -> nothing 4 -> nothing @@ -30,4 +30,3 @@ mappings for count Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 -3 diff --git a/plugins/stream-debugger/testData/streamex/src/concatenate/AppendMany.java b/plugins/stream-debugger/testData/streamex/src/concatenate/AppendMany.java index 3839860e9231..8677bce117de 100644 --- a/plugins/stream-debugger/testData/streamex/src/concatenate/AppendMany.java +++ b/plugins/stream-debugger/testData/streamex/src/concatenate/AppendMany.java @@ -7,7 +7,6 @@ import java.util.stream.Stream; public class AppendMany { public static void main(String[] args) { // Breakpoint! - final long count = StreamEx.of(1, 2).append(Stream.of(3, 4, 5)).count(); - System.out.println(count); + StreamEx.of(1, 2).append(Stream.of(3, 4, 5)).forEach(x -> {}); } } diff --git a/plugins/stream-debugger/testData/streamex/src/concatenate/AppendNone.java b/plugins/stream-debugger/testData/streamex/src/concatenate/AppendNone.java index bb31f39f9041..923934f1ee69 100644 --- a/plugins/stream-debugger/testData/streamex/src/concatenate/AppendNone.java +++ b/plugins/stream-debugger/testData/streamex/src/concatenate/AppendNone.java @@ -5,7 +5,6 @@ import one.util.streamex.StreamEx; public class AppendNone { public static void main(String[] args) { // Breakpoint! - final long count = StreamEx.of(1, 2).append().count(); - System.out.println(count); + StreamEx.of(1, 2).append().forEach(x -> {}); } } diff --git a/plugins/stream-debugger/testData/streamex/src/concatenate/AppendOne.java b/plugins/stream-debugger/testData/streamex/src/concatenate/AppendOne.java index 8fc2efb4c256..99a27d90e48a 100644 --- a/plugins/stream-debugger/testData/streamex/src/concatenate/AppendOne.java +++ b/plugins/stream-debugger/testData/streamex/src/concatenate/AppendOne.java @@ -5,7 +5,6 @@ import one.util.streamex.StreamEx; public class AppendOne { public static void main(String[] args) { // Breakpoint! - final long count = StreamEx.of(1, 2).append(3).count(); - System.out.println(count); + StreamEx.of(1, 2).append(3).forEach(x -> {}); } } diff --git a/plugins/stream-debugger/testData/streamex/src/concatenate/AppendToEmpty.java b/plugins/stream-debugger/testData/streamex/src/concatenate/AppendToEmpty.java index e39b99251940..fc1e9ccd5f6f 100644 --- a/plugins/stream-debugger/testData/streamex/src/concatenate/AppendToEmpty.java +++ b/plugins/stream-debugger/testData/streamex/src/concatenate/AppendToEmpty.java @@ -5,7 +5,6 @@ import one.util.streamex.StreamEx; public class AppendToEmpty { public static void main(String[] args) { // Breakpoint! - final long count = StreamEx.empty().append(1).count(); - System.out.println(count); + StreamEx.empty().append(1).forEach(x -> {}); } } diff --git a/plugins/stream-debugger/testData/streamex/src/concatenate/PrependMany.java b/plugins/stream-debugger/testData/streamex/src/concatenate/PrependMany.java index 31366e6f6aa2..a764cc052138 100644 --- a/plugins/stream-debugger/testData/streamex/src/concatenate/PrependMany.java +++ b/plugins/stream-debugger/testData/streamex/src/concatenate/PrependMany.java @@ -7,7 +7,6 @@ import java.util.stream.Stream; public class PrependMany { public static void main(String[] args) { // Breakpoint! - final long count = StreamEx.of(1, 2).prepend(Stream.of(3, 4, 5)).count(); - System.out.println(count); + StreamEx.of(1, 2).prepend(Stream.of(3, 4, 5)).forEach(x -> {}); } } diff --git a/plugins/stream-debugger/testData/streamex/src/concatenate/PrependNone.java b/plugins/stream-debugger/testData/streamex/src/concatenate/PrependNone.java index 15bc51d34709..7639eb1432d6 100644 --- a/plugins/stream-debugger/testData/streamex/src/concatenate/PrependNone.java +++ b/plugins/stream-debugger/testData/streamex/src/concatenate/PrependNone.java @@ -5,7 +5,6 @@ import one.util.streamex.StreamEx; public class PrependNone { public static void main(String[] args) { // Breakpoint! - final long count = StreamEx.of(1, 2).prepend().count(); - System.out.println(count); + StreamEx.of(1, 2).prepend().forEach(x -> {}); } } diff --git a/plugins/stream-debugger/testData/streamex/src/concatenate/PrependOne.java b/plugins/stream-debugger/testData/streamex/src/concatenate/PrependOne.java index cc575c92c981..163a753749e1 100644 --- a/plugins/stream-debugger/testData/streamex/src/concatenate/PrependOne.java +++ b/plugins/stream-debugger/testData/streamex/src/concatenate/PrependOne.java @@ -5,7 +5,6 @@ import one.util.streamex.StreamEx; public class PrependOne { public static void main(String[] args) { // Breakpoint! - final long count = StreamEx.of(1, 2).prepend(3).count(); - System.out.println(count); + StreamEx.of(1, 2).prepend(3).forEach(x -> {}); } } diff --git a/plugins/stream-debugger/testData/streamex/src/concatenate/PrependToEmpty.java b/plugins/stream-debugger/testData/streamex/src/concatenate/PrependToEmpty.java index d51430bfca12..dc1ceed8f00a 100644 --- a/plugins/stream-debugger/testData/streamex/src/concatenate/PrependToEmpty.java +++ b/plugins/stream-debugger/testData/streamex/src/concatenate/PrependToEmpty.java @@ -5,7 +5,6 @@ import one.util.streamex.StreamEx; public class PrependToEmpty { public static void main(String[] args) { // Breakpoint! - final long count = StreamEx.empty().prepend(1).count(); - System.out.println(count); + StreamEx.empty().prepend(1).forEach(x -> {}); } } diff --git a/plugins/stream-debugger/testData/streamex/src/mapping/Invert.java b/plugins/stream-debugger/testData/streamex/src/mapping/Invert.java index 3488a6e6ff7a..67bacafb2d03 100644 --- a/plugins/stream-debugger/testData/streamex/src/mapping/Invert.java +++ b/plugins/stream-debugger/testData/streamex/src/mapping/Invert.java @@ -5,7 +5,6 @@ import one.util.streamex.EntryStream; public class Invert { public static void main(String[] args) { // Breakpoint! - final long count = EntryStream.of(1, 1, 2, 4, 3, 9).invert().count(); - System.out.println(count); + EntryStream.of(1, 1, 2, 4, 3, 9).invert().forEach(x -> {}); } } diff --git a/plugins/stream-debugger/testData/streamex/src/mapping/Keys.java b/plugins/stream-debugger/testData/streamex/src/mapping/Keys.java index 4833c01db67d..c45921c9d921 100644 --- a/plugins/stream-debugger/testData/streamex/src/mapping/Keys.java +++ b/plugins/stream-debugger/testData/streamex/src/mapping/Keys.java @@ -5,7 +5,6 @@ import one.util.streamex.EntryStream; public class Keys { public static void main(String[] args) { // Breakpoint! - final long count = EntryStream.of(1, 1, 2, 4, 3, 9).keys().count(); - System.out.println(count); + EntryStream.of(1, 1, 2, 4, 3, 9).keys().forEach(x -> {}); } } diff --git a/plugins/stream-debugger/testData/streamex/src/mapping/MapKeys.java b/plugins/stream-debugger/testData/streamex/src/mapping/MapKeys.java index 5a5e424d814d..c72a443710c6 100644 --- a/plugins/stream-debugger/testData/streamex/src/mapping/MapKeys.java +++ b/plugins/stream-debugger/testData/streamex/src/mapping/MapKeys.java @@ -5,7 +5,6 @@ import one.util.streamex.EntryStream; public class MapKeys { public static void main(String[] args) { // Breakpoint! - final long count = EntryStream.of(1, 1, 2, 4, 3, 9).mapKeys(x -> x - 1).count(); - System.out.println(count); + EntryStream.of(1, 1, 2, 4, 3, 9).mapKeys(x -> x - 1).forEach(x -> {}); } } diff --git a/plugins/stream-debugger/testData/streamex/src/mapping/MapToEntry.java b/plugins/stream-debugger/testData/streamex/src/mapping/MapToEntry.java index 5d221c04263c..a16d2741ccba 100644 --- a/plugins/stream-debugger/testData/streamex/src/mapping/MapToEntry.java +++ b/plugins/stream-debugger/testData/streamex/src/mapping/MapToEntry.java @@ -5,7 +5,7 @@ import one.util.streamex.StreamEx; public class MapToEntry { public static void main(String[] args) { // Breakpoint! - final long count = StreamEx.of(1, 2, 3).mapToEntry(x -> x * x).count(); - System.out.println(count); + Object[] result = StreamEx.of(1, 2, 3).mapToEntry(x -> x * x).toArray(); + System.out.println(result.length); } } diff --git a/plugins/stream-debugger/testData/streamex/src/mapping/MapToKey.java b/plugins/stream-debugger/testData/streamex/src/mapping/MapToKey.java index 7a3c50c7cef2..7e31f4417e19 100644 --- a/plugins/stream-debugger/testData/streamex/src/mapping/MapToKey.java +++ b/plugins/stream-debugger/testData/streamex/src/mapping/MapToKey.java @@ -5,9 +5,8 @@ import one.util.streamex.EntryStream; public class MapToKey { public static void main(String[] args) { // Breakpoint! - final long count = EntryStream.of(1, 1, 2, 4, 3, 9) + EntryStream.of(1, 1, 2, 4, 3, 9) .mapToKey((k, v) -> k + v) - .count(); - System.out.println(count); + .forEach(x -> {}); } } diff --git a/plugins/stream-debugger/testData/streamex/src/mapping/MapToValue.java b/plugins/stream-debugger/testData/streamex/src/mapping/MapToValue.java index 9da1aaf147ce..40912ed8a693 100644 --- a/plugins/stream-debugger/testData/streamex/src/mapping/MapToValue.java +++ b/plugins/stream-debugger/testData/streamex/src/mapping/MapToValue.java @@ -5,9 +5,8 @@ import one.util.streamex.EntryStream; public class MapToValue { public static void main(String[] args) { // Breakpoint! - final long count = EntryStream.of(1, 1, 2, 4, 3, 9) + EntryStream.of(1, 1, 2, 4, 3, 9) .mapToValue((k, v) -> k + v) - .count(); - System.out.println(count); + .forEach(x -> {}); } } diff --git a/plugins/stream-debugger/testData/streamex/src/mapping/MapValues.java b/plugins/stream-debugger/testData/streamex/src/mapping/MapValues.java index 138dbd594a9e..57d75c179a91 100644 --- a/plugins/stream-debugger/testData/streamex/src/mapping/MapValues.java +++ b/plugins/stream-debugger/testData/streamex/src/mapping/MapValues.java @@ -5,9 +5,8 @@ import one.util.streamex.EntryStream; public class MapValues { public static void main(String[] args) { // Breakpoint! - final long count = EntryStream.of(1, 1, 2, 4, 3, 9) + EntryStream.of(1, 1, 2, 4, 3, 9) .mapValues(x -> 0) - .count(); - System.out.println(count); + .forEach(x -> {}); } } diff --git a/plugins/stream-debugger/testData/streamex/src/mapping/WithFirst.java b/plugins/stream-debugger/testData/streamex/src/mapping/WithFirst.java index 354a1970e205..3bd7609d88a8 100644 --- a/plugins/stream-debugger/testData/streamex/src/mapping/WithFirst.java +++ b/plugins/stream-debugger/testData/streamex/src/mapping/WithFirst.java @@ -5,7 +5,6 @@ import one.util.streamex.StreamEx; public class WithFirst { public static void main(String[] args) { // Breakpoint! - final long count = StreamEx.of(10., 2., 3.).withFirst((first, other) -> first * other).count(); - System.out.println(count); + StreamEx.of(10., 2., 3.).withFirst((first, other) -> first * other).forEach(x -> {}); } } diff --git a/plugins/stream-debugger/testData/streamex/src/misc/Parallel.java b/plugins/stream-debugger/testData/streamex/src/misc/Parallel.java index 092277244f1f..9134975d6672 100644 --- a/plugins/stream-debugger/testData/streamex/src/misc/Parallel.java +++ b/plugins/stream-debugger/testData/streamex/src/misc/Parallel.java @@ -8,7 +8,6 @@ import java.util.concurrent.ForkJoinPool; public class Parallel { public static void main(String[] args) { // Breakpoint! - final long count = StreamEx.of(1, 2, 3).parallel((ForkJoinPool) Executors.newWorkStealingPool()).count(); - System.out.println(count); + StreamEx.of(1, 2, 3).parallel((ForkJoinPool) Executors.newWorkStealingPool()).forEach(x -> {}); } } diff --git a/plugins/stream-debugger/testData/streamex/src/misc/PrefixKeys.java b/plugins/stream-debugger/testData/streamex/src/misc/PrefixKeys.java index c161c996e67b..acf13c42baea 100644 --- a/plugins/stream-debugger/testData/streamex/src/misc/PrefixKeys.java +++ b/plugins/stream-debugger/testData/streamex/src/misc/PrefixKeys.java @@ -5,7 +5,6 @@ import one.util.streamex.EntryStream; public class PrefixKeys { public static void main(String[] args) { // Breakpoint! - final long count = EntryStream.of(1, 1, 2, 4, 3, 9).prefixKeys((l, r) -> l + r).count(); - System.out.println(count); + EntryStream.of(1, 1, 2, 4, 3, 9).prefixKeys((l, r) -> l + r).forEach(x -> {}); } } diff --git a/plugins/stream-debugger/testData/streamex/src/misc/PrefixValues.java b/plugins/stream-debugger/testData/streamex/src/misc/PrefixValues.java index d5ac4a026f13..f97cc03c4c40 100644 --- a/plugins/stream-debugger/testData/streamex/src/misc/PrefixValues.java +++ b/plugins/stream-debugger/testData/streamex/src/misc/PrefixValues.java @@ -5,7 +5,6 @@ import one.util.streamex.EntryStream; public class PrefixValues { public static void main(String[] args) { // Breakpoint! - final long count = EntryStream.of(1, 1, 2, 4, 3, 9).prefixValues((l, r) -> l + r).count(); - System.out.println(count); + EntryStream.of(1, 1, 2, 4, 3, 9).prefixValues((l, r) -> l + r).forEach(x -> {}); } } diff --git a/plugins/stream-debugger/testData/streamex/src/misc/ZipWithGreater.java b/plugins/stream-debugger/testData/streamex/src/misc/ZipWithGreater.java index aeaa9cbdcc68..f86db4521c71 100644 --- a/plugins/stream-debugger/testData/streamex/src/misc/ZipWithGreater.java +++ b/plugins/stream-debugger/testData/streamex/src/misc/ZipWithGreater.java @@ -7,7 +7,6 @@ import java.util.stream.Stream; public class ZipWithGreater { public static void main(String[] args) { // Breakpoint! - final long count = StreamEx.of(1, 2, 3).zipWith(Stream.of(1, 3, 9, 16, 25)).count(); - System.out.println(count); + StreamEx.of(1, 2, 3).zipWith(Stream.of(1, 3, 9, 16, 25)).forEach(x -> {}); } } diff --git a/plugins/stream-debugger/testData/streamex/src/misc/ZipWithLesser.java b/plugins/stream-debugger/testData/streamex/src/misc/ZipWithLesser.java index 0666014082c4..a2a73769830a 100644 --- a/plugins/stream-debugger/testData/streamex/src/misc/ZipWithLesser.java +++ b/plugins/stream-debugger/testData/streamex/src/misc/ZipWithLesser.java @@ -7,7 +7,6 @@ import java.util.stream.Stream; public class ZipWithLesser { public static void main(String[] args) { // Breakpoint! - final long count = StreamEx.of(1, 2, 3, 4, 5).zipWith(Stream.of(1, 3, 9)).count(); - System.out.println(count); + StreamEx.of(1, 2, 3, 4, 5).zipWith(Stream.of(1, 3, 9)).forEach(x -> {}); } } diff --git a/plugins/stream-debugger/testData/streamex/src/misc/ZipWithSameSizes.java b/plugins/stream-debugger/testData/streamex/src/misc/ZipWithSameSizes.java index 5dd387ec04bf..9337834c74d5 100644 --- a/plugins/stream-debugger/testData/streamex/src/misc/ZipWithSameSizes.java +++ b/plugins/stream-debugger/testData/streamex/src/misc/ZipWithSameSizes.java @@ -7,7 +7,6 @@ import java.util.stream.Stream; public class ZipWithSameSizes { public static void main(String[] args) { // Breakpoint! - final long count = StreamEx.of(1, 2, 3).zipWith(Stream.of(1, 3, 9)).count(); - System.out.println(count); + StreamEx.of(1, 2, 3).zipWith(Stream.of(1, 3, 9)).forEach(x -> {}); } } From b51b2c8b0437f08c5dab4d24b012a28add9703c1 Mon Sep 17 00:00:00 2001 From: "Vitaliy.Bibaev" Date: Mon, 5 Mar 2018 15:01:27 +0300 Subject: [PATCH 9/9] IDEA-172425 Support method references to private constructors --- .../ConstructorReflectionAccessor.java | 34 ++++++++++++------- .../MethodReferenceReflectionAccessor.java | 13 ++++++- 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ConstructorReflectionAccessor.java b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ConstructorReflectionAccessor.java index 5194777db84f..bfe00bc7e7c5 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ConstructorReflectionAccessor.java +++ b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/reflect/ConstructorReflectionAccessor.java @@ -33,18 +33,9 @@ public class ConstructorReflectionAccessor extends ReflectionAccessorBase { private final MethodReflectionAccessor myMethodAccessor; + private final ConstructorReflectionAccessor myConstructorReflectionAccessor; public MethodReferenceReflectionAccessor(@NotNull PsiClass psiClass, @NotNull PsiElementFactory elementFactory) { super(psiClass, elementFactory); myMethodAccessor = new MethodReflectionAccessor(psiClass, elementFactory); + myConstructorReflectionAccessor = new ConstructorReflectionAccessor(psiClass, elementFactory); } @Nullable @@ -41,7 +46,13 @@ public class MethodReferenceReflectionAccessor PsiLambdaExpression lambda = LambdaRefactoringUtil.convertMethodReferenceToLambda(descriptor.expression, false, true); if (lambda != null) { PsiElement lambdaBody = lambda.getBody(); - if (lambdaBody instanceof PsiMethodCallExpression) { + if (lambdaBody instanceof PsiNewExpression) { + ConstructorDescriptor constructorDescriptor = createIfInaccessible((PsiNewExpression)lambdaBody); + if (constructorDescriptor != null) { + myConstructorReflectionAccessor.grantAccess(constructorDescriptor); + } + } + else if (lambdaBody instanceof PsiMethodCallExpression) { PsiMethodCallExpression callExpression = (PsiMethodCallExpression)lambdaBody; PsiMethod method = callExpression.resolveMethod(); if (method != null) {