Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: be more protective to avoid wrong usages of sniper mode #3210

Merged
merged 3 commits into from
Jan 27, 2020
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
fix: be more protective to avoid wrong usages of sniper mode
monperrus committed Jan 23, 2020
commit 284d68a42034dc993d6e1f2f89e1097bfb1f795f
23 changes: 15 additions & 8 deletions src/main/java/spoon/support/modelobs/ChangeCollector.java
Original file line number Diff line number Diff line change
@@ -18,6 +18,7 @@
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.ModifierKind;
import spoon.reflect.path.CtRole;
import spoon.reflect.reference.CtPackageReference;
import spoon.reflect.visitor.EarlyTerminatingScanner;
import spoon.reflect.visitor.chain.CtScannerListener;
import spoon.reflect.visitor.chain.ScanningMode;
@@ -74,7 +75,7 @@ public ChangeCollector attachTo(Environment env) {

/**
* @param currentElement the {@link CtElement} whose changes has to be checked
* @return set of {@link CtRole}s whose attribute was directly changed on `currentElement` since this {@link ChangeCollector} was attached
* @return set of {@link CtRole}s in direct children on of `currentElement` where something has changed since this {@link ChangeCollector} was attached
* The 'directly' means that value of attribute of `currentElement` was changed.
* Use {@link #getChanges(CtElement)} to detect changes in child elements too
*/
@@ -87,32 +88,37 @@ public Set<CtRole> getDirectChanges(CtElement currentElement) {
}

/**
* @param currentElement the {@link CtElement} whose changes has to be checked
* @return set of {@link CtRole}s whose attribute was changed on `currentElement`
* or any child of this attribute was changed
* Return the set of {@link CtRole}s for which children have changed from `currentElement`
* since this {@link ChangeCollector} was attached
* Warning: change in IMPLICIT are ignored
* @param currentElement the {@link CtElement} whose changes has to be checked
*/
public Set<CtRole> getChanges(CtElement currentElement) {
final Set<CtRole> changes = new HashSet<>(getDirectChanges(currentElement));
final Scanner scanner = new Scanner();
// collecting the changes deeped in currentElement
scanner.setListener(new CtScannerListener() {
int depth = 0;
CtRole checkedRole;
@Override
public ScanningMode enter(CtElement element) {
if (depth == 0) {
//we are checking children of role checkedRole
// we want the top-level role directly under currentElement
checkedRole = scanner.getScannedRole();
}

// Optimization, in theory could be removed
if (changes.contains(checkedRole)) {
//we already know that some child of `checkedRole` attribute is modified. Skip others
//we already know that some child with `checkedRole` is modified. Skip others
return ScanningMode.SKIP_ALL;
}
if (elementToChangeRole.containsKey(element)) {
//we have found a modified element in children of `checkedRole`

if (elementToChangeRole.containsKey(element) && !elementToChangeRole.get(element).contains(CtRole.IS_IMPLICIT)) {
//we have found a modified element in some sub children of `checkedRole`
changes.add(checkedRole);
return ScanningMode.SKIP_ALL;
}

depth++;
//continue searching for an modification
return ScanningMode.NORMAL;
@@ -164,6 +170,7 @@ public void onObjectUpdate(CtElement currentElement, CtRole role, CtElement newV

@Override
public void onObjectUpdate(CtElement currentElement, CtRole role, Object newValue, Object oldValue) {

onChange(currentElement, role);
}

Original file line number Diff line number Diff line change
@@ -417,14 +417,7 @@ private void runInMutedState(Boolean muted, Runnable code) {
mutableTokenWriter.setMuted(muted);
code.run();
} finally {
//assure that muted status did not changed in between
if (mutableTokenWriter.isMuted() != muted) {
if (mutableTokenWriter.isMuted()) {
throw new SpoonException("Unexpected state: Token writer is muted after scanning"); //NOSONAR
} else {
throw new SpoonException("Unexpected state: Token writer is not muted after scanning"); //NOSONAR
}
}
// restore origin state
mutableTokenWriter.setMuted(originMuted);
}
}