Skip to content

Commit

Permalink
fix(ThisAccess): shorter this access
Browse files Browse the repository at this point in the history
  • Loading branch information
monperrus committed Dec 8, 2016
1 parent 3a958bb commit 5e332ba
Show file tree
Hide file tree
Showing 17 changed files with 147 additions and 135 deletions.
103 changes: 55 additions & 48 deletions src/main/java/spoon/reflect/visitor/DefaultJavaPrettyPrinter.java
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,6 @@ public void visitCtCatch(CtCatch catchBlock) {
@Override
public <T> void visitCtClass(CtClass<T> ctClass) {
context.pushCurrentThis(ctClass);

if (ctClass.getSimpleName() != null && !CtType.NAME_UNKNOWN.equals(ctClass.getSimpleName()) && !ctClass.isAnonymous()) {
visitCtType(ctClass);
if (ctClass.isLocalType()) {
Expand Down Expand Up @@ -682,8 +681,9 @@ private <T> void printCtFieldAccess(CtFieldAccess<T> f) {
}
if (f.getTarget() != null) {
if (!isInitializeStaticFinalField(f.getTarget())) {
printer.snapshotLength();
scan(f.getTarget());
if (!f.getTarget().isImplicit()) {
if (printer.hasNewContent()) {
printer.write(".");
}
}
Expand Down Expand Up @@ -719,45 +719,50 @@ private <T> boolean isInitializeStaticFinalField(CtExpression<T> targetExp) {

@Override
public <T> void visitCtThisAccess(CtThisAccess<T> thisAccess) {
enterCtExpression(thisAccess);
if (thisAccess.getTarget() != null && thisAccess.getTarget() instanceof CtTypeAccess
&& !tryToInitializeFinalFieldInConstructor(thisAccess)
&& !thisAccess.isImplicit()
&& !thisAccess.getTarget().isImplicit()) {
final CtTypeReference accessedType = ((CtTypeAccess) thisAccess.getTarget()).getAccessedType();
if (accessedType.isLocalType()) {
printer.write(accessedType.getSimpleName().replaceAll("^[0-9]*", "") + ".");
} else if (!accessedType.isAnonymous()) {
visitCtTypeReferenceWithoutGenerics(accessedType);
printer.write(".");
try {
enterCtExpression(thisAccess);

// we only write qualified this when this is required
// this is good both in fully-qualified mode and in readable (with-imports) mode
// the implicit information is used for analysis (eg are visibility caused by implicit bugs?) but
// not for pretty-printing
CtTypeAccess target = (CtTypeAccess) thisAccess.getTarget();
CtTypeReference targetType = target.getAccessedType();

// readable mode as close as possible to the original code
if (thisAccess.isImplicit()) {
// write nothing, "this" is implicit and we unfortunately cannot always know
// what the good target is in JDTTreeBuilder
return;
}
}
if (!thisAccess.isImplicit()) {
printer.write("this");
}
exitCtExpression(thisAccess);
}

/**
* Check if the this access expression is a target of a private final field in a constructor.
*/
private <T> boolean tryToInitializeFinalFieldInConstructor(CtThisAccess<T> thisAccess) {
try {
final CtElement parent = thisAccess.getParent();
if (!(parent instanceof CtFieldWrite) || !thisAccess.equals(((CtFieldWrite) parent).getTarget()) || thisAccess.getParent(CtConstructor.class) == null) {
return false;
// the simplest case: we always print "this" if we're in the top-level class,
// this is shorter (no qualified this), explicit, and less fragile wrt transformation
if (targetType == null || (thisAccess.getParent(CtType.class) != null && thisAccess.getParent(CtType.class).isTopLevel())) {
printer.write("this");
return; // still go through finally block below
}
final CtFieldReference variable = ((CtFieldWrite) parent).getVariable();
if (variable == null) {
return false;

// we cannot have fully-qualified this in anonymous classes
// we simply print "this" and it always works
// this has to come after the implicit test just before
if (targetType.isAnonymous()) {
printer.write("this");
return;
}
final CtField declaration = variable.getDeclaration();
if (declaration == null) {
return true;

// complex case of qualifed this
if (context.currentThis.peekLast() != null
&& !context.currentThis.peekLast().type.getQualifiedName().equals(targetType.getQualifiedName())) {
visitCtTypeReferenceWithoutGenerics(targetType);
printer.write(".this");
return;
}
return declaration.getModifiers().contains(ModifierKind.FINAL);
} catch (ParentNotInitializedException e) {
return false;

// the default super simple case only comes at the end
printer.write("this");
} finally {
exitCtExpression(thisAccess);
}
}

Expand Down Expand Up @@ -855,7 +860,7 @@ public <T> void visitCtFieldReference(CtFieldReference<T> reference) {

if (reference.isFinal() && reference.isStatic()) {
CtTypeReference<?> declTypeRef = reference.getDeclaringType();
if ("".equals(declTypeRef.getSimpleName())) {
if (declTypeRef.isAnonymous()) {
//never print anonymous class ref
printType = false;
} else {
Expand Down Expand Up @@ -980,25 +985,26 @@ public <T> void visitCtInvocation(CtInvocation<T> invocation) {
if (parentType != null && parentType.getQualifiedName() != null && parentType.getQualifiedName().equals(invocation.getExecutable().getDeclaringType().getQualifiedName())) {
printer.write("this");
} else {
if (invocation.getTarget() != null) {
scan(invocation.getTarget());
printer.snapshotLength();
scan(invocation.getTarget());
if (printer.hasNewContent()) {
printer.write(".");
}
printer.write("super");
}
} else {
// It's a method invocation
if (invocation.getTarget() != null) {
try (Writable _context = context.modify()) {
if (invocation.getTarget() instanceof CtTypeAccess) {
_context.ignoreGenerics(true);
}
scan(invocation.getTarget());
}
if (!invocation.getTarget().isImplicit()) {
printer.write(".");
printer.snapshotLength();
try (Writable _context = context.modify()) {
if (invocation.getTarget() instanceof CtTypeAccess) {
_context.ignoreGenerics(true);
}
scan(invocation.getTarget());
}
if (printer.hasNewContent()) {
printer.write(".");
}

elementPrinterHelper.writeActualTypeArguments(invocation);
if (env.isPreserveLineNumbers()) {
printer.adjustPosition(invocation, sourceCompilationUnit);
Expand Down Expand Up @@ -1708,6 +1714,7 @@ public String getResult() {
public void reset() {
printer = new PrinterHelper(env);
elementPrinterHelper.setPrinter(printer);
context = new PrintingContext();
}

@Override
Expand Down
14 changes: 6 additions & 8 deletions src/main/java/spoon/reflect/visitor/PrintingContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,14 @@
*/
package spoon.reflect.visitor;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;

import spoon.reflect.code.CtExpression;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtType;
import spoon.reflect.reference.CtTypeReference;

import java.util.ArrayDeque;
import java.util.Deque;

public class PrintingContext {

private long NO_TYPE_DECL = 1 << 0;
Expand Down Expand Up @@ -92,7 +90,7 @@ public Writable modify() {
return new Writable();
}

List<TypeContext> currentThis = new ArrayList<>();
Deque<TypeContext> currentThis = new ArrayDeque<>();

/**
* @return top level type
Expand All @@ -109,7 +107,7 @@ public CtTypeReference<?> getCurrentTypeReference() {
}
private TypeContext getCurrentTypeContext() {
if (currentThis != null && currentThis.size() > 0) {
TypeContext tc = currentThis.get(currentThis.size() - 1);
TypeContext tc = currentThis.peek();
return tc;
}
return null;
Expand All @@ -119,7 +117,7 @@ public void pushCurrentThis(CtType<?> type) {
currentThis.add(new TypeContext(type));
}
public void popCurrentThis() {
currentThis.remove(currentThis.size() - 1);
currentThis.pop();
}


Expand Down
15 changes: 15 additions & 0 deletions src/main/java/spoon/reflect/visitor/printer/PrinterHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,13 @@
import spoon.reflect.cu.CompilationUnit;
import spoon.reflect.declaration.CtElement;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
import java.util.Queue;

import static com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type.Int;

public class PrinterHelper {
/**
Expand Down Expand Up @@ -389,4 +394,14 @@ public Map<Integer, Integer> getLineNumberMapping() {
public String toString() {
return sbf.toString();
}

private ArrayDeque<Integer> lengths = new ArrayDeque<>();
/** stores the length of the printer */
public void snapshotLength() {
lengths.addLast(toString().length());
}
/** returns true if something has been written since the last call to snapshotLength() */
public boolean hasNewContent() {
return lengths.pollLast() < toString().length();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,12 @@
import spoon.reflect.declaration.ModifierKind;
import spoon.reflect.reference.CtTypeReference;
import spoon.reflect.visitor.CtVisitor;
import spoon.support.comparator.SignatureComparator;
import spoon.support.util.SignatureBasedSortedSet;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

public class CtEnumImpl<T extends Enum<?>> extends CtClassImpl<T> implements CtEnum<T> {
private static final long serialVersionUID = 1L;
Expand All @@ -47,7 +46,7 @@ public void accept(CtVisitor visitor) {

@Override
public Set<CtMethod<?>> getAllMethods() {
Set<CtMethod<?>> allMethods = new TreeSet<>(new SignatureComparator());
Set<CtMethod<?>> allMethods = new SignatureBasedSortedSet();
allMethods.addAll(getMethods());
allMethods.addAll(getFactory().Type().get(Enum.class).getMethods());
allMethods.add(valuesMethod());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
import spoon.reflect.visitor.filter.ReferenceTypeFilter;
import spoon.support.UnsettableProperty;
import spoon.support.SpoonClassNotFoundException;
import spoon.support.comparator.SignatureComparator;
import spoon.support.compiler.SnippetCompilationHelper;
import spoon.support.util.QualifiedNameBasedSortedSet;
import spoon.support.util.SignatureBasedSortedSet;
Expand All @@ -59,7 +58,6 @@
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import static spoon.reflect.ModelElementContainerDefaultCapacities.TYPE_TYPE_PARAMETERS_CONTAINER_DEFAULT_CAPACITY;

Expand Down Expand Up @@ -884,7 +882,7 @@ public Collection<CtExecutableReference<?>> getDeclaredExecutables() {

@Override
public Collection<CtExecutableReference<?>> getAllExecutables() {
Set<CtExecutableReference<?>> l = new TreeSet(new SignatureComparator());
Set<CtExecutableReference<?>> l = new SignatureBasedSortedSet();
for (CtMethod<?> m : getAllMethods()) {
l.add((CtExecutableReference<?>) m.getReference());
}
Expand Down
8 changes: 4 additions & 4 deletions src/test/java/spoon/test/comment/CommentTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ public void testInLineComment() {
CtInvocation ctInvocation = m1.getBody().getStatement(4);
assertEquals(createFakeComment(f, "comment invocation"), ctInvocation.getComments().get(0));
assertEquals("// comment invocation" + newLine
+ "spoon.test.comment.testclasses.InlineComment.this.m()", ctInvocation.toString());
+ "this.m()", ctInvocation.toString());

CtLocalVariable ctLocalVariable = m1.getBody().getStatement(5);
assertEquals(createFakeComment(f, "comment local variable"), ctLocalVariable.getComments().get(0));
Expand Down Expand Up @@ -232,7 +232,7 @@ public void testInLineComment() {
CtSynchronized ctSynchronized = m1.getBody().getStatement(9);
assertEquals(createFakeComment(f, "comment synchronized"), ctSynchronized.getComments().get(0));
assertEquals("// comment synchronized" + newLine
+ "synchronized(spoon.test.comment.testclasses.InlineComment.this) {" + newLine
+ "synchronized(this) {" + newLine
+ " // comment in synchronized" + newLine
+ "}", ctSynchronized.toString());

Expand Down Expand Up @@ -378,7 +378,7 @@ public void testBlockComment() {
CtInvocation ctInvocation = m1.getBody().getStatement(4);
assertEquals(createFakeBlockComment(f, "comment invocation"), ctInvocation.getComments().get(0));
assertEquals("/* comment invocation */" + newLine
+ "spoon.test.comment.testclasses.BlockComment.this.m()", ctInvocation.toString());
+ "this.m()", ctInvocation.toString());

CtLocalVariable ctLocalVariable = m1.getBody().getStatement(5);
assertEquals(createFakeBlockComment(f, "comment local variable"), ctLocalVariable.getComments().get(0));
Expand Down Expand Up @@ -412,7 +412,7 @@ public void testBlockComment() {
CtSynchronized ctSynchronized = m1.getBody().getStatement(9);
assertEquals(createFakeBlockComment(f, "comment synchronized"), ctSynchronized.getComments().get(0));
assertEquals("/* comment synchronized */" + newLine
+ "synchronized(spoon.test.comment.testclasses.BlockComment.this) {" + newLine
+ "synchronized(this) {" + newLine
+ " /* comment in synchronized */" + newLine
+ "}", ctSynchronized.toString());

Expand Down
17 changes: 16 additions & 1 deletion src/test/java/spoon/test/field/FieldTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,24 @@
package spoon.test.field;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static spoon.testing.Assert.assertThat;
import static spoon.testing.utils.ModelUtils.build;
import static spoon.testing.utils.ModelUtils.buildClass;
import static spoon.testing.utils.ModelUtils.createFactory;

import java.io.File;
import java.util.HashSet;
import java.util.List;

import org.junit.Test;

import spoon.reflect.code.CtFieldRead;
import spoon.reflect.declaration.CtClass;
import spoon.reflect.declaration.CtField;
import spoon.reflect.declaration.ModifierKind;
import spoon.reflect.factory.Factory;
import spoon.reflect.visitor.filter.TypeFilter;
import spoon.test.field.testclasses.AddFieldAtTop;

public class FieldTest {
Expand Down Expand Up @@ -76,8 +80,19 @@ public void testAddFieldsAtTop() throws Exception {
assertEquals(generated2, aClass.getTypeMembers().get(0));
assertEquals(generated, aClass.getTypeMembers().get(1));
assertEquals(aClass.getAnonymousExecutables().get(0), aClass.getTypeMembers().get(3));
}

@Test
public void testFieldImplicitTarget() throws Exception {
// contract: no "." when target is implicit
final CtClass<AddFieldAtTop> aClass = (CtClass<AddFieldAtTop>) buildClass(AddFieldAtTop.class);

assertThat(aClass).isEqualTo(build(new File("./src/test/resources/expected/AddFieldAtTop.java")).Type().get("AddFieldAtTop"));
List<CtFieldRead> fieldReads = aClass.getElements(new TypeFilter<>(CtFieldRead.class));
assertEquals(1, fieldReads.size());
assertEquals("i", fieldReads.get(0).toString());
fieldReads.get(0).getTarget().setImplicit(false);
assertEquals(false, fieldReads.get(0).getTarget().isImplicit());
assertEquals("this.i", fieldReads.get(0).toString());
}

private CtField<Integer> createField(Factory factory, HashSet<ModifierKind> modifiers, String name) {
Expand Down
7 changes: 7 additions & 0 deletions src/test/java/spoon/test/field/testclasses/AddFieldAtTop.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,11 @@ public class AddFieldAtTop {

void m() {
}

class Foo {
int i;
void m() {
int x = i;
}
}
}
Loading

0 comments on commit 5e332ba

Please sign in to comment.