From 30c61869b6dc83eeaaf37a98ec77c180b6d83909 Mon Sep 17 00:00:00 2001 From: I-Al-Istannen Date: Wed, 4 Oct 2023 18:19:17 +0200 Subject: [PATCH] fix: Use CtSuperAccess when implicitly accessing superclass field (#5406) --- .../spoon/dataflow/utils/CommonUtils.java | 7 +++- .../main/java/spoon/smpl/SmPLMethodCFG.java | 8 +++++ .../visitor/DefaultJavaPrettyPrinter.java | 3 ++ .../visitor/ForceFullyQualifiedProcessor.java | 5 +-- .../compiler/jdt/JDTTreeBuilderHelper.java | 13 +++++++- .../test/targeted/TargetedExpressionTest.java | 33 +++++++++++++++++-- 6 files changed, 62 insertions(+), 7 deletions(-) diff --git a/spoon-dataflow/src/main/java/fr/inria/spoon/dataflow/utils/CommonUtils.java b/spoon-dataflow/src/main/java/fr/inria/spoon/dataflow/utils/CommonUtils.java index ded72f98f9d..bbd47743ef7 100644 --- a/spoon-dataflow/src/main/java/fr/inria/spoon/dataflow/utils/CommonUtils.java +++ b/spoon-dataflow/src/main/java/fr/inria/spoon/dataflow/utils/CommonUtils.java @@ -28,7 +28,9 @@ public static IntExpr getTargetValue(Context context, Map var targets.addFirst(target); target = ((CtTargetedExpression) target).getTarget(); } - targets.addFirst(target); + if (target != null) { + targets.addFirst(target); + } // Traverse all targets left to right IntExpr targetValue = null; @@ -45,6 +47,9 @@ else if (t instanceof CtArrayRead) Expr arrayIndex = (Expr) index.getMetadata("value"); targetValue = (IntExpr) memory.readArray((CtArrayTypeReference) arrayRead.getTarget().getType(), targetValue, arrayIndex); } + else if (t instanceof CtSuperAccess) { + targetValue = context.mkInt(Memory.thisPointer()); + } else if (t instanceof CtVariableRead) { targetValue = (IntExpr) variablesMap.get(((CtVariableRead) t).getVariable()); diff --git a/spoon-smpl/src/main/java/spoon/smpl/SmPLMethodCFG.java b/spoon-smpl/src/main/java/spoon/smpl/SmPLMethodCFG.java index 4fa92ccf917..608332213b4 100644 --- a/spoon-smpl/src/main/java/spoon/smpl/SmPLMethodCFG.java +++ b/spoon-smpl/src/main/java/spoon/smpl/SmPLMethodCFG.java @@ -191,6 +191,14 @@ private void replace(CtElement e) { * @param e Element about to be replaced */ private void logReplacementWarning(CtElement e) { + if (!e.getPosition().isValidPosition()) { + String code = e.toString(); + LoggerFactory.getLogger("spoon-smpl") + .warn("Unsupported element excluded from control flow graph at unknown location: " + + e.getClass().getSimpleName() + + code.substring(0, Math.min(40, code.length()))); + return; + } String file = Optional.ofNullable(e.getPosition()) .map(x -> x.getFile()) .map(x -> x.getName()) diff --git a/src/main/java/spoon/reflect/visitor/DefaultJavaPrettyPrinter.java b/src/main/java/spoon/reflect/visitor/DefaultJavaPrettyPrinter.java index af74cbaaab9..91fcd2ad4e0 100644 --- a/src/main/java/spoon/reflect/visitor/DefaultJavaPrettyPrinter.java +++ b/src/main/java/spoon/reflect/visitor/DefaultJavaPrettyPrinter.java @@ -987,6 +987,9 @@ public void visitCtThisAccess(CtThisAccess thisAccess) { @Override public void visitCtSuperAccess(CtSuperAccess f) { + if (f.isImplicit()) { + return; + } enterCtExpression(f); if (f.getTarget() != null) { scan(f.getTarget()); diff --git a/src/main/java/spoon/reflect/visitor/ForceFullyQualifiedProcessor.java b/src/main/java/spoon/reflect/visitor/ForceFullyQualifiedProcessor.java index 1cb703dc01e..bd23b72458b 100644 --- a/src/main/java/spoon/reflect/visitor/ForceFullyQualifiedProcessor.java +++ b/src/main/java/spoon/reflect/visitor/ForceFullyQualifiedProcessor.java @@ -9,6 +9,7 @@ import spoon.reflect.code.CtExpression; import spoon.reflect.code.CtNewClass; +import spoon.reflect.code.CtSuperAccess; import spoon.reflect.code.CtTargetedExpression; import spoon.reflect.code.CtThisAccess; import spoon.reflect.code.CtTypeAccess; @@ -96,8 +97,8 @@ protected void handleTargetedExpression(CtTargetedExpression targetedExpre if (!target.isImplicit()) { return; } - if (target instanceof CtThisAccess) { - //the non implicit this access is not forced + if (target instanceof CtThisAccess || target instanceof CtSuperAccess) { + //the implicit this/super access is not forced return; } if (target instanceof CtTypeAccess) { diff --git a/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilderHelper.java b/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilderHelper.java index c50618785f2..99d53b12929 100644 --- a/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilderHelper.java +++ b/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilderHelper.java @@ -366,7 +366,18 @@ CtFieldAccess createFieldAccess(SingleNameReference singleNameReference) if (ref.isStatic() && !ref.getDeclaringType().isAnonymous()) { va.setTarget(jdtTreeBuilder.getFactory().Code().createTypeAccess(ref.getDeclaringType(), true)); } else if (!ref.isStatic()) { - va.setTarget(jdtTreeBuilder.getFactory().Code().createThisAccess(jdtTreeBuilder.getReferencesBuilder().getTypeReference(singleNameReference.actualReceiverType), true)); + TypeBinding fieldDeclarerType = singleNameReference.fieldBinding().declaringClass; + TypeBinding actualReceiverType = singleNameReference.actualReceiverType; + CtTypeReference owningType = jdtTreeBuilder.getReferencesBuilder().getTypeReference(fieldDeclarerType); + if (Arrays.equals(fieldDeclarerType.qualifiedSourceName(), actualReceiverType.qualifiedSourceName())) { + va.setTarget(jdtTreeBuilder.getFactory().Code().createThisAccess(owningType, true)); + } else { + va.setTarget( + jdtTreeBuilder.getFactory().createSuperAccess() + .setType(owningType) + .setImplicit(true) + ); + } } } return va; diff --git a/src/test/java/spoon/test/targeted/TargetedExpressionTest.java b/src/test/java/spoon/test/targeted/TargetedExpressionTest.java index 3a580f59345..2e44e599c2f 100644 --- a/src/test/java/spoon/test/targeted/TargetedExpressionTest.java +++ b/src/test/java/spoon/test/targeted/TargetedExpressionTest.java @@ -17,10 +17,7 @@ package spoon.test.targeted; -import java.util.List; - import org.junit.jupiter.api.Test; - import spoon.Launcher; import spoon.reflect.CtModel; import spoon.reflect.code.CtExpression; @@ -44,6 +41,7 @@ import spoon.reflect.visitor.filter.NamedElementFilter; import spoon.reflect.visitor.filter.TypeFilter; import spoon.support.comparator.CtLineElementComparator; +import spoon.support.compiler.VirtualFile; import spoon.support.reflect.code.CtConstructorCallImpl; import spoon.support.reflect.code.CtFieldReadImpl; import spoon.support.reflect.code.CtThisAccessImpl; @@ -56,6 +54,8 @@ import spoon.test.targeted.testclasses.Tapas; import spoon.testing.utils.ModelTest; +import java.util.List; + import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; @@ -539,6 +539,33 @@ public void testClassDeclaredInALambda() throws Exception { assertEqualsFieldAccess(new ExpectedTargetedExpression().declaringType(thirdExpectedInner).target(expectedThisAccess).type(CtFieldWrite.class).result("this.index").isLocal(), elements.get(2)); } + @Test + void testUnqualifiedSuperFieldAccess() { + // contract: Unqualified super field accesses are modeled by an implicit CtSuperAccess + Launcher launcher = new Launcher(); + + launcher.getEnvironment().setComplianceLevel(17); + launcher.addInputResource(new VirtualFile( + "class A {\n" + + " int a;\n" + + "}\n" + + "\n" + + "class B extends A {\n" + + " void foo() {\n" + + " super.a = a;\n" + + " }\n" + + "}\n" + )); + + CtModel ctModel = launcher.buildModel(); + + CtFieldRead read = ctModel.getElements(new TypeFilter<>(CtFieldRead.class)).get(0); + assertTrue(read.getTarget() instanceof CtSuperAccess, "Target was no super access"); + assertTrue(read.getTarget().isImplicit(), "super target was not implicit"); + assertEquals("A", read.getTarget().getType().getQualifiedName()); + assertEquals(read.toString(), "a", "super target was still printed"); + } + private void assertEqualsFieldAccess(ExpectedTargetedExpression expected, CtFieldAccess fieldAccess) { if (expected.declaringType == null) { assertNull(fieldAccess.getVariable().getDeclaringType());