Skip to content

Commit

Permalink
feat(shadow): Build shadow elements using Reflection API (#569)
Browse files Browse the repository at this point in the history
  • Loading branch information
GerardPaligot authored and monperrus committed Apr 22, 2016
1 parent 0d25fac commit b2518c1
Show file tree
Hide file tree
Showing 47 changed files with 2,053 additions and 62 deletions.
8 changes: 8 additions & 0 deletions doc/first_processor.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ keywords: start, begin, hello world, processor, spoon
last_updated: October 1, 2015
---

Spoon analyzes source code. However, this source code may refer to libraries (as a field, parameter, or method return type). Those library may be or not in the classpath. The boundary between source and libraries is handled by the reference mechanism.

When you're consider a reference object (say, a TypeReference), there are three cases:

- Case 1: the reference points to a code element for which the source code is present. In this case, reference.getDeclaration() returns this code element (e.g. TypeReference.getDeclaration returns the CtType representing the given java file). reference.getTypeDeclaration() is identical to reference.getDeclaration().
- Case 2: the reference points to a code element for which the source code is NOT present, but for which the binary class is in the classpath (either the JVM classpath or the --source-classpath argument). In this case, reference.getDeclaration() returns null and reference.getTypeDeclaration returns a partial CtType built using runtime reflection. Those objects built using runtime reflection are called shadow objects; and you can identify them with method isShadow. (This also holds for getFieldDeclaration and getExecutableDeclaration)
- Case 3: : the reference points to a code element for which the source code is NOT present, but for which the binary class is NOT in the classpath. This is called in Spoon the noclasspath mode. In this case, both reference.getDeclaration() and reference.getTypeDeclaration() return null. (This also holds for getFieldDeclaration and getExecutableDeclaration)

## Creation of the processor

In Spoon, a processor is a combination of query and analysis code.
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/spoon/reflect/declaration/CtAnnotation.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
* @param <A>
* type of represented annotation
*/
public interface CtAnnotation<A extends Annotation> extends CtExpression<A> {
public interface CtAnnotation<A extends Annotation> extends CtExpression<A>, CtShadowable {

/**
* Returns the actual annotation (a dynamic proxy for this element).
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/spoon/reflect/declaration/CtConstructor.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
/**
* This element defines a constructor declaration.
*/
public interface CtConstructor<T> extends CtExecutable<T>, CtTypeMember, CtGenericElement {
public interface CtConstructor<T> extends CtExecutable<T>, CtTypeMember, CtGenericElement, CtShadowable {

/**
* Always returns "&lt;init&gt;".
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/spoon/reflect/declaration/CtField.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
/**
* This element defines a field declaration.
*/
public interface CtField<T> extends CtVariable<T>, CtTypeMember, CtRHSReceiver<T> {
public interface CtField<T> extends CtVariable<T>, CtTypeMember, CtRHSReceiver<T>, CtShadowable {

/**
* The separator for a string representation of a field.
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/spoon/reflect/declaration/CtMethod.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
/**
* This element defines a method declaration.
*/
public interface CtMethod<T> extends CtExecutable<T>, CtTypeMember, CtGenericElement {
public interface CtMethod<T> extends CtExecutable<T>, CtTypeMember, CtGenericElement, CtShadowable {
/**
* Checks if the method is a default method. Default method can be in interfaces from
* Java 8: http://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html.
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/spoon/reflect/declaration/CtPackage.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@
*/
package spoon.reflect.declaration;

import java.util.Set;

import spoon.reflect.reference.CtPackageReference;

import java.util.Set;

/**
* This element defines a package declaration. The packages are represented by a
* tree.
*/
public interface CtPackage extends CtNamedElement {
public interface CtPackage extends CtNamedElement, CtShadowable {

/**
* The separator for a string representation of a package.
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/spoon/reflect/declaration/CtParameter.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
*
* @see CtExecutable
*/
public interface CtParameter<T> extends CtVariable<T> {
public interface CtParameter<T> extends CtVariable<T>, CtShadowable {

/**
* Gets the executable that is the parent declaration of this parameter
Expand Down
37 changes: 37 additions & 0 deletions src/main/java/spoon/reflect/declaration/CtShadowable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* Copyright (C) 2006-2015 INRIA and contributors
* Spoon - http://spoon.gforge.inria.fr/
*
* This software is governed by the CeCILL-C License under French law and
* abiding by the rules of distribution of free software. You can use, modify
* and/or redistribute the software under the terms of the CeCILL-C license as
* circulated by CEA, CNRS and INRIA at http://www.cecill.info.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the CeCILL-C License for more details.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL-C license and that you accept its terms.
*/
package spoon.reflect.declaration;

import spoon.reflect.reference.CtTypeReference;

public interface CtShadowable {
/**
* When an element isn't present in the factory (created in another factory),
* this element is considered as "shadow". e.g., a shadow element can be a
* CtType of java.lang.Class built when we call {@link CtTypeReference#getTypeDeclaration()}
* on a reference of java.lang.Class.
*
* @return true if the element is a shadow element, otherwise false.
*/
boolean isShadow();

/**
* Marks an element as shadow. To know what is a shadow element, see the javadoc of
* {@link #isShadow()}.
*/
<E extends CtShadowable> E setShadow(boolean isShadow);
}
2 changes: 1 addition & 1 deletion src/main/java/spoon/reflect/declaration/CtType.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
*
* The type parameter T refers to the actual class that this type represents.
*/
public interface CtType<T> extends CtNamedElement, CtTypeInformation, CtTypeMember, CtGenericElement {
public interface CtType<T> extends CtNamedElement, CtTypeInformation, CtTypeMember, CtGenericElement, CtShadowable {
/**
* The string separator in a Java innertype qualified name.
*/
Expand Down
13 changes: 11 additions & 2 deletions src/main/java/spoon/reflect/factory/TypeFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,14 @@
import spoon.reflect.reference.CtTypeParameterReference;
import spoon.reflect.reference.CtTypeReference;
import spoon.reflect.visitor.filter.TypeFilter;
import spoon.support.visitor.java.JavaReflectionTreeBuilder;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import static spoon.testing.utils.ModelUtils.createFactory;

/**
* The {@link CtType} sub-factory.
*/
Expand Down Expand Up @@ -260,7 +263,9 @@ private void addNestedType(List<CtType<?>> list, CtType<?> t) {
}

/**
* Gets a type from its runtime Java class.
* Gets a type from its runtime Java class. If the class isn't in the spoon path,
* the class will be build from the Java reflection and will be marked as
* shadow (see {@link spoon.reflect.declaration.CtShadowable}).
*
* @param <T>
* actual type of the class
Expand All @@ -270,7 +275,11 @@ private void addNestedType(List<CtType<?>> list, CtType<?> t) {
*/
@SuppressWarnings("unchecked")
public <T> CtType<T> get(Class<?> cl) {
return (CtType<T>) get(cl.getName());
final CtType<T> aType = get(cl.getName());
if (aType == null) {
return new JavaReflectionTreeBuilder(createFactory()).scan((Class<T>) cl);
}
return aType;
}

/**
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/spoon/reflect/reference/CtExecutableReference.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,18 @@ public interface CtExecutableReference<T> extends CtReference, CtGenericElementR
*/
Constructor<?> getActualConstructor();

@Override
CtExecutable<T> getDeclaration();

/**
* Returns a subtype {@link CtExecutable} that corresponds to the reference
* even if its declaring type isn't in the Spoon source path (in this case,
* the Spoon elements are built with runtime reflection).
*
* @return the executable declaration that corresponds to the reference.
*/
CtExecutable<T> getExecutableDeclaration();

/**
* Gets the reference to the type that declares this executable.
*/
Expand Down
14 changes: 12 additions & 2 deletions src/main/java/spoon/reflect/reference/CtFieldReference.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@
*/
package spoon.reflect.reference;

import java.lang.reflect.Member;

import spoon.reflect.declaration.CtField;

import java.lang.reflect.Member;

/**
* This interface defines a reference to a
* {@link spoon.reflect.declaration.CtField}.
Expand All @@ -32,8 +32,18 @@ public interface CtFieldReference<T> extends CtVariableReference<T> {
*/
Member getActualField();

@Override
CtField<T> getDeclaration();

/**
* Returns the {@link CtField} that corresponds to the reference
* even if its declaring type isn't in the Spoon source path (in this case,
* the Spoon elements are built with runtime reflection)
*
* @return the field declaration that corresponds to the reference.
*/
CtField<T> getFieldDeclaration();

/**
* Gets the type in which the field is declared.
*/
Expand Down
12 changes: 11 additions & 1 deletion src/main/java/spoon/reflect/reference/CtTypeReference.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@
package spoon.reflect.reference;

import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtShadowable;
import spoon.reflect.declaration.CtType;
import spoon.reflect.declaration.CtTypeInformation;

/**
* This interface defines a reference to a
* {@link spoon.reflect.declaration.CtType} or sub-type.
*/
public interface CtTypeReference<T> extends CtReference, CtGenericElementReference, CtTypeInformation {
public interface CtTypeReference<T> extends CtReference, CtGenericElementReference, CtTypeInformation, CtShadowable {

/**
* The name of the null type ("&lt;nulltype&gt;").
Expand Down Expand Up @@ -59,6 +60,15 @@ public interface CtTypeReference<T> extends CtReference, CtGenericElementReferen
*/
CtType<T> getDeclaration();

/**
* Returns the {@link CtType} that corresponds to the reference even if the
* type isn't in the Spoon source path (in this case, the Spoon elements are
* built with runtime reflection)
*
* @return the type declaration that corresponds to the reference.
*/
CtType<T> getTypeDeclaration();

/**
* Gets the type that declares the referenced type.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3395,7 +3395,7 @@ public boolean visit(SingleNameReference singleNameReference, BlockScope scope)
} else {
va = factory.Core().createFieldRead();
}
va.setVariable(references.getVariableReference(singleNameReference.fieldBinding()));
va.setVariable(references.getVariableReference(singleNameReference.fieldBinding().original()));
if (va.getVariable() instanceof CtFieldReference) {
final CtFieldReference<Object> ref = (CtFieldReference<Object>) va.getVariable();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import spoon.reflect.declaration.CtMethod;
import spoon.reflect.declaration.CtPackage;
import spoon.reflect.declaration.CtParameter;
import spoon.reflect.declaration.CtShadowable;
import spoon.reflect.declaration.CtType;
import spoon.reflect.eval.PartialEvaluator;
import spoon.reflect.reference.CtFieldReference;
Expand Down Expand Up @@ -439,4 +440,17 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
}
return (A) Proxy.newProxyInstance(annotationType.getActualClass().getClassLoader(), new Class[] { annotationType.getActualClass() }, new AnnotationInvocationHandler(this));
}

boolean isShadow;

@Override
public boolean isShadow() {
return isShadow;
}

@Override
public <E extends CtShadowable> E setShadow(boolean isShadow) {
this.isShadow = isShadow;
return (E) this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import spoon.reflect.declaration.CtGenericElement;
import spoon.reflect.declaration.CtModifiable;
import spoon.reflect.declaration.CtNamedElement;
import spoon.reflect.declaration.CtShadowable;
import spoon.reflect.declaration.CtType;
import spoon.reflect.declaration.CtTypedElement;
import spoon.reflect.declaration.ModifierKind;
Expand Down Expand Up @@ -165,4 +166,17 @@ public ModifierKind getVisibility() {
}
return null;
}

boolean isShadow;

@Override
public boolean isShadow() {
return isShadow;
}

@Override
public <E extends CtShadowable> E setShadow(boolean isShadow) {
this.isShadow = isShadow;
return (E) this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,6 @@ public Set<String> getMetadataKeys() {
return metadata.keySet();
}


@Override
public List<CtComment> getComments() {
return Collections.unmodifiableList(comments);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,13 @@ public List<CtField<?>> getFields() {
result.addAll(super.getFields());
return result;
}

@Override
public CtField<?> getField(String name) {
final CtField<?> field = super.getField(name);
if (field == null) {
return getEnumValue(name);
}
return field;
}
}
14 changes: 14 additions & 0 deletions src/main/java/spoon/support/reflect/declaration/CtFieldImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtField;
import spoon.reflect.declaration.CtModifiable;
import spoon.reflect.declaration.CtShadowable;
import spoon.reflect.declaration.CtType;
import spoon.reflect.declaration.CtTypedElement;
import spoon.reflect.declaration.CtVariable;
Expand Down Expand Up @@ -164,4 +165,17 @@ public <C extends CtRHSReceiver<T>> C setAssignment(CtExpression<T> assignment)
setDefaultExpression(assignment);
return (C) this;
}

boolean isShadow;

@Override
public boolean isShadow() {
return isShadow;
}

@Override
public <E extends CtShadowable> E setShadow(boolean isShadow) {
this.isShadow = isShadow;
return (E) this;
}
}
14 changes: 14 additions & 0 deletions src/main/java/spoon/support/reflect/declaration/CtMethodImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import spoon.reflect.declaration.CtGenericElement;
import spoon.reflect.declaration.CtMethod;
import spoon.reflect.declaration.CtModifiable;
import spoon.reflect.declaration.CtShadowable;
import spoon.reflect.declaration.CtType;
import spoon.reflect.declaration.CtTypedElement;
import spoon.reflect.declaration.ModifierKind;
Expand Down Expand Up @@ -184,4 +185,17 @@ public ModifierKind getVisibility() {
public <R extends T> void replace(CtMethod<T> element) {
replace((CtElement) element);
}

boolean isShadow;

@Override
public boolean isShadow() {
return isShadow;
}

@Override
public <E extends CtShadowable> E setShadow(boolean isShadow) {
this.isShadow = isShadow;
return (E) this;
}
}
Loading

0 comments on commit b2518c1

Please sign in to comment.