From 9d59b5beda1eb1c86bddabcdb37e7865b3416a95 Mon Sep 17 00:00:00 2001 From: Egor Ushakov Date: Wed, 23 Apr 2025 19:26:43 +0200 Subject: [PATCH] IDEA-371371 Stream trace debugging doesn't work with records GitOrigin-RevId: bbe83739e3db7cd0b7a4b8842b6e5c1cbb8d1f32 --- .../ReflectionAccessMethodBuilder.java | 82 +++++++++++++++---- .../outs/accessConstructor.out | 4 +- .../outs/accessMethodReference.out | 4 +- .../outs/methodWithParameter.out | 4 +- .../outs/methodWithPrimitiveParameter.out | 4 +- 5 files changed, 73 insertions(+), 25 deletions(-) diff --git a/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethodObject/reflect/ReflectionAccessMethodBuilder.java b/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethodObject/reflect/ReflectionAccessMethodBuilder.java index 46773bf8726c..1655de29cf82 100644 --- a/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethodObject/reflect/ReflectionAccessMethodBuilder.java +++ b/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethodObject/reflect/ReflectionAccessMethodBuilder.java @@ -1,10 +1,11 @@ -// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. 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.psi.util.PsiTypesUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.SmartList; @@ -112,27 +113,37 @@ public class ReflectionAccessMethodBuilder { } public ReflectionAccessMethodBuilder addParameter(@NotNull String jvmType, @NotNull String name) { - myParameters.add(new ParameterInfo(jvmType.replace('$', '.'), name, jvmType)); + myParameters.add(new ParameterInfo(jvmType.replace('$', '.'), name, new TypeInfo(jvmType, 0))); return this; } public ReflectionAccessMethodBuilder addParameters(@NotNull PsiParameterList parameterList) { PsiParameter[] parameters = parameterList.getParameters(); - for (PsiParameter parameter : parameters) { - PsiType parameterType = parameter.getType(); + for (int i = 0; i < parameters.length; i++) { + PsiType parameterType = parameters[i].getType(); PsiType erasedType = TypeConversionUtil.erasure(parameterType); String typeName = typeName(parameterType, erasedType); - String jvmType = erasedType != null ? extractJvmType(erasedType) : typeName; + TypeInfo jvmType = erasedType != null ? extractJvmType(erasedType) : new TypeInfo(typeName, 0); - String name = parameter.getName(); + String name = "p" + i; // To avoid confusion with local variables, the real parameter names are not used. - PsiType accessedType = PsiReflectionAccessUtil.nearestAccessibleType(parameterType); - myParameters.add(new ParameterInfo(accessedType.getCanonicalText(), name, jvmType)); + if (requiresObjectType(parameterType) || jvmType.arrayDimension > 0) { + myParameters.add(new ParameterInfo(CommonClassNames.JAVA_LANG_OBJECT, name, jvmType)); + } + else { + PsiType accessedType = PsiReflectionAccessUtil.nearestAccessibleType(parameterType); + myParameters.add(new ParameterInfo(accessedType.getCanonicalText(), name, jvmType)); + } } return this; } + private static boolean requiresObjectType(PsiType type) { + PsiClass psiClass = PsiTypesUtil.getPsiClass(type); + return psiClass != null && (psiClass.isRecord() || psiClass.isEnum()); + } + private static @NotNull String typeName(@NotNull PsiType type, @Nullable PsiType erasedType) { if (erasedType == null) { String typeName = type.getCanonicalText(); @@ -148,18 +159,46 @@ public class ReflectionAccessMethodBuilder { return erasedType.getCanonicalText(); } - private static @NotNull String extractJvmType(@NotNull PsiType type) { + private static class TypeInfo { + final int arrayDimension; + final String typeName; + + private TypeInfo(String name, int dimension) { + arrayDimension = dimension; + typeName = name; + } + + String lookupClass() { + if (TypeConversionUtil.isPrimitive(typeName)) { + return typeName + StringUtil.repeat("[]", arrayDimension) + ".class"; + } + else { + String className = typeName; + if (arrayDimension > 0) { + className = StringUtil.repeat("[", arrayDimension) + "L" + typeName + ";"; + } + return "java.lang.Class.forName(\"" + className + "\")"; + } + } + } + + private static @NotNull TypeInfo extractJvmType(@NotNull PsiType type) { + int arrayDimension = 0; + while (type instanceof PsiArrayType arrayType) { + arrayDimension++; + type = arrayType.getComponentType(); + } PsiClass psiClass = PsiUtil.resolveClassInType(type); String canonicalText = type.getCanonicalText(); String jvmName = psiClass == null ? canonicalText : ClassUtil.getJVMClassName(psiClass); - return jvmName == null ? canonicalText : jvmName; + return new TypeInfo(jvmName == null ? canonicalText : jvmName, arrayDimension); } 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 record ParameterInfo(@NotNull String accessibleType, @NotNull String name, @NotNull String jvmTypeName) { + private record ParameterInfo(@NotNull String accessibleType, @NotNull String name, @NotNull TypeInfo jvmType) { } private interface MyMemberAccessor { @@ -227,7 +266,7 @@ public class ReflectionAccessMethodBuilder { @Override public String getMemberLookupExpression() { - String args = StreamEx.of(myParameters).skip(1).map(x -> PsiReflectionAccessUtil.classForName(x.jvmTypeName)) + String args = StreamEx.of(myParameters).skip(1).map(x -> x.jvmType.lookupClass()) .prepend(StringUtil.wrapWithDoubleQuote(myMethodName)) .joining(", ", "(", ")"); return "getDeclaredMethod" + args; @@ -251,11 +290,10 @@ public class ReflectionAccessMethodBuilder { @Override public String getAccessExpression() { - return StreamEx.of(myParameters).map(x -> x.name).joining(", ", "invoke(", ")"); + return "invoke" + parametersStringForInvoke(); } } - private class MyConstructorAccessor implements MyMemberAccessor { private final String myClassName; @@ -265,7 +303,7 @@ public class ReflectionAccessMethodBuilder { @Override public String getMemberLookupExpression() { - String args = StreamEx.of(myParameters).map(x -> x.jvmTypeName).map(PsiReflectionAccessUtil::classForName).joining(", ", "(", ")"); + String args = StreamEx.of(myParameters).map(x -> x.jvmType.lookupClass()).joining(", ", "(", ")"); return "getDeclaredConstructor" + args; } @@ -276,8 +314,7 @@ public class ReflectionAccessMethodBuilder { @Override public String getAccessExpression() { - String args = StreamEx.of(myParameters).map(x -> x.name).joining(", ", "(", ")"); - return "newInstance" + args; + return "newInstance" + parametersStringForInvoke(); } @Override @@ -290,4 +327,15 @@ public class ReflectionAccessMethodBuilder { return Collections.singletonList("java.lang.ReflectiveOperationException"); } } + + private String parametersStringForInvoke() { + return StreamEx.of(myParameters).map(x -> { + if (x.jvmType.arrayDimension > 0) { + return "(java.lang.Object)" + x.name; // cast arrays to Object to avoid confusion with varargs method invocation + } + else { + return x.name; + } + }).joining(", ", "(", ")"); + } } diff --git a/java/java-tests/testData/refactoring/extractMethodObject4Debugger/outs/accessConstructor.out b/java/java-tests/testData/refactoring/extractMethodObject4Debugger/outs/accessConstructor.out index 2dad82eb15ea..2b09c71c291c 100644 --- a/java/java-tests/testData/refactoring/extractMethodObject4Debugger/outs/accessConstructor.out +++ b/java/java-tests/testData/refactoring/extractMethodObject4Debugger/outs/accessConstructor.out @@ -5,7 +5,7 @@ public class GeneratedEvaluationClass { return newWithReflectionAccess1(50); } - public static Object newWithReflectionAccess1(int value) { + public static Object newWithReflectionAccess1(int p0) { try { Class klass = Class.forName("WithReflectionAccess"); java.lang.reflect.Constructor member = null; @@ -30,7 +30,7 @@ public class GeneratedEvaluationClass { } } member.setAccessible(true); - return (Object) member.newInstance(value); + return (Object) member.newInstance(p0); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } diff --git a/java/java-tests/testData/refactoring/extractMethodObject4Debugger/outs/accessMethodReference.out b/java/java-tests/testData/refactoring/extractMethodObject4Debugger/outs/accessMethodReference.out index 1069d29b062b..72a9352b8155 100644 --- a/java/java-tests/testData/refactoring/extractMethodObject4Debugger/outs/accessMethodReference.out +++ b/java/java-tests/testData/refactoring/extractMethodObject4Debugger/outs/accessMethodReference.out @@ -36,7 +36,7 @@ public class GeneratedEvaluationClass { } } - public static void callApply1(Object object, Runnable runnable) { + public static void callApply1(Object object, Runnable p0) { try { Class klass = Class.forName("WithReflectionAccess"); java.lang.reflect.Method member = null; @@ -61,7 +61,7 @@ public class GeneratedEvaluationClass { } } member.setAccessible(true); - member.invoke(object, runnable); + member.invoke(object, p0); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } diff --git a/java/java-tests/testData/refactoring/extractMethodObject4Debugger/outs/methodWithParameter.out b/java/java-tests/testData/refactoring/extractMethodObject4Debugger/outs/methodWithParameter.out index 3ccb5127c093..cb3b0859ddb0 100644 --- a/java/java-tests/testData/refactoring/extractMethodObject4Debugger/outs/methodWithParameter.out +++ b/java/java-tests/testData/refactoring/extractMethodObject4Debugger/outs/methodWithParameter.out @@ -7,7 +7,7 @@ public class GeneratedEvaluationClass { this.instance = instance; } - public static Object callMethod1(Object object, Object arg) { + public static Object callMethod1(Object object, Object p0) { try { Class klass = Class.forName("WithReflectionAccess"); java.lang.reflect.Method member = null; @@ -32,7 +32,7 @@ public class GeneratedEvaluationClass { } } member.setAccessible(true); - return (Object) member.invoke(object, arg); + return (Object) member.invoke(object, p0); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } diff --git a/java/java-tests/testData/refactoring/extractMethodObject4Debugger/outs/methodWithPrimitiveParameter.out b/java/java-tests/testData/refactoring/extractMethodObject4Debugger/outs/methodWithPrimitiveParameter.out index 17b253d77db6..8b770887ae68 100644 --- a/java/java-tests/testData/refactoring/extractMethodObject4Debugger/outs/methodWithPrimitiveParameter.out +++ b/java/java-tests/testData/refactoring/extractMethodObject4Debugger/outs/methodWithPrimitiveParameter.out @@ -7,7 +7,7 @@ public class GeneratedEvaluationClass { this.instance = instance; } - public static int callMethod1(Object object, int arg) { + public static int callMethod1(Object object, int p0) { try { Class klass = Class.forName("WithReflectionAccess"); java.lang.reflect.Method member = null; @@ -32,7 +32,7 @@ public class GeneratedEvaluationClass { } } member.setAccessible(true); - return (int) member.invoke(object, arg); + return (int) member.invoke(object, p0); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); }