diff options
-rw-r--r-- | src/core/lombok/core/HandlerPriority.java | 30 | ||||
-rw-r--r-- | src/core/lombok/javac/HandlerLibrary.java | 29 | ||||
-rw-r--r-- | src/core/lombok/javac/JavacTransformer.java | 6 | ||||
-rw-r--r-- | src/core/lombok/javac/ResolutionResetNeeded.java | 37 | ||||
-rw-r--r-- | src/core/lombok/javac/apt/Processor.java | 68 | ||||
-rw-r--r-- | src/core/lombok/javac/handlers/HandleDelegate.java | 2 | ||||
-rw-r--r-- | src/core/lombok/javac/handlers/HandleVal.java | 2 |
7 files changed, 144 insertions, 30 deletions
diff --git a/src/core/lombok/core/HandlerPriority.java b/src/core/lombok/core/HandlerPriority.java index 174d02d4..935b162d 100644 --- a/src/core/lombok/core/HandlerPriority.java +++ b/src/core/lombok/core/HandlerPriority.java @@ -1,3 +1,24 @@ +/* + * Copyright (C) 2012 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package lombok.core; import java.lang.annotation.ElementType; @@ -5,6 +26,15 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * Used to order the way handlers are run. Handlers with a lower priority value are run before handlers with higher priority values. + * For example, {@code @Value} can cause the class to be marked final, and this affects the behaviour of {@code @EqualsAndHashCode}. By ensuring that + * the handler for {@code @Value} always runs before the handler for {@code @EqualsAndHashCode}, the code is simpler: The {@code @EqualsAndHashCode} handler + * does not have to check for the presence of a {@code @Value} annotation to determine whether to generate the {@code canEqual} method or not. + * <p> + * A new priority level can also be used to force a reset of the resolved model, i.e. to add generated methods to the symbol tables. Each platform implementation (javac, ecj, etc) + * may have additional marker annotations required to indicate the need for the reset. + */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface HandlerPriority { diff --git a/src/core/lombok/javac/HandlerLibrary.java b/src/core/lombok/javac/HandlerLibrary.java index 1ce083f3..81618070 100644 --- a/src/core/lombok/javac/HandlerLibrary.java +++ b/src/core/lombok/javac/HandlerLibrary.java @@ -69,28 +69,36 @@ public class HandlerLibrary { private static class VisitorContainer { private final JavacASTVisitor visitor; private final long priority; + private final boolean resolutionResetNeeded; VisitorContainer(JavacASTVisitor visitor) { this.visitor = visitor; HandlerPriority hp = visitor.getClass().getAnnotation(HandlerPriority.class); this.priority = hp == null ? 0L : (((long)hp.value()) << 32) + hp.subValue(); + this.resolutionResetNeeded = visitor.getClass().isAnnotationPresent(ResolutionResetNeeded.class); } public long getPriority() { return priority; } + + public boolean isResolutionResetNeeded() { + return resolutionResetNeeded; + } } private static class AnnotationHandlerContainer<T extends Annotation> { private final JavacAnnotationHandler<T> handler; private final Class<T> annotationClass; private final long priority; + private final boolean resolutionResetNeeded; AnnotationHandlerContainer(JavacAnnotationHandler<T> handler, Class<T> annotationClass) { this.handler = handler; this.annotationClass = annotationClass; HandlerPriority hp = handler.getClass().getAnnotation(HandlerPriority.class); this.priority = hp == null ? 0L : (((long)hp.value()) << 32) + hp.subValue(); + this.resolutionResetNeeded = handler.getClass().isAnnotationPresent(ResolutionResetNeeded.class); } public void handle(final JavacNode node) { @@ -100,19 +108,36 @@ public class HandlerLibrary { public long getPriority() { return priority; } + + public boolean isResolutionResetNeeded() { + return resolutionResetNeeded; + } } private SortedSet<Long> priorities; + private SortedSet<Long> prioritiesRequiringResolutionReset; public SortedSet<Long> getPriorities() { return priorities; } + public SortedSet<Long> getPrioritiesRequiringResolutionReset() { + return prioritiesRequiringResolutionReset; + } + private void calculatePriorities() { SortedSet<Long> set = new TreeSet<Long>(); - for (AnnotationHandlerContainer<?> container : annotationHandlers.values()) set.add(container.getPriority()); - for (VisitorContainer container : visitorHandlers) set.add(container.getPriority()); + SortedSet<Long> resetNeeded = new TreeSet<Long>(); + for (AnnotationHandlerContainer<?> container : annotationHandlers.values()) { + set.add(container.getPriority()); + if (container.isResolutionResetNeeded()) resetNeeded.add(container.getPriority()); + } + for (VisitorContainer container : visitorHandlers) { + set.add(container.getPriority()); + if (container.isResolutionResetNeeded()) resetNeeded.add(container.getPriority()); + } this.priorities = Collections.unmodifiableSortedSet(set); + this.prioritiesRequiringResolutionReset = Collections.unmodifiableSortedSet(resetNeeded); } /** diff --git a/src/core/lombok/javac/JavacTransformer.java b/src/core/lombok/javac/JavacTransformer.java index 9cac787c..004a6035 100644 --- a/src/core/lombok/javac/JavacTransformer.java +++ b/src/core/lombok/javac/JavacTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2011 The Project Lombok Authors. + * Copyright (C) 2009-2012 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -47,6 +47,10 @@ public class JavacTransformer { return handlers.getPriorities(); } + public SortedSet<Long> getPrioritiesRequiringResolutionReset() { + return handlers.getPrioritiesRequiringResolutionReset(); + } + public void transform(long priority, Context context, java.util.List<JCCompilationUnit> compilationUnitsRaw) { List<JCCompilationUnit> compilationUnits; if (compilationUnitsRaw instanceof List<?>) { diff --git a/src/core/lombok/javac/ResolutionResetNeeded.java b/src/core/lombok/javac/ResolutionResetNeeded.java new file mode 100644 index 00000000..dc482e15 --- /dev/null +++ b/src/core/lombok/javac/ResolutionResetNeeded.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2012 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.javac; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Marks the annotated handler as needing a reset on the resolved model (resulting in generated methods' signatures from becoming part of the symbol table and such) before + * the priority level of this handler is reached. + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ResolutionResetNeeded { + +} diff --git a/src/core/lombok/javac/apt/Processor.java b/src/core/lombok/javac/apt/Processor.java index 190a369b..94e0e644 100644 --- a/src/core/lombok/javac/apt/Processor.java +++ b/src/core/lombok/javac/apt/Processor.java @@ -29,6 +29,7 @@ import java.lang.reflect.Method; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; +import java.util.HashSet; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; @@ -85,10 +86,12 @@ public class Processor extends AbstractProcessor { SortedSet<Long> p = transformer.getPriorities(); if (p.isEmpty()) { this.priorityLevels = new long[] {0L}; + this.priorityLevelsRequiringResolutionReset = new HashSet<Long>(); } else { this.priorityLevels = new long[p.size()]; int i = 0; for (Long prio : p) this.priorityLevels[i++] = prio; + this.priorityLevelsRequiringResolutionReset = transformer.getPrioritiesRequiringResolutionReset(); } } @@ -216,6 +219,7 @@ public class Processor extends AbstractProcessor { private final IdentityHashMap<JCCompilationUnit, Long> roots = new IdentityHashMap<JCCompilationUnit, Long>(); private long[] priorityLevels; + private Set<Long> priorityLevelsRequiringResolutionReset; /** {@inheritDoc} */ @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { @@ -232,37 +236,47 @@ public class Processor extends AbstractProcessor { roots.put(unit, priorityLevels[0]); } - // Step 2: For all CUs (in the map, not the roundEnv!), run them across all handlers at their current prio level. - - for (long prio : priorityLevels) { - List<JCCompilationUnit> cusForThisRound = new ArrayList<JCCompilationUnit>(); - for (Map.Entry<JCCompilationUnit, Long> entry : roots.entrySet()) { - Long prioOfCu = entry.getValue(); - if (prioOfCu == null || prioOfCu != prio) continue; - cusForThisRound.add(entry.getKey()); + while (true) { + // Step 2: For all CUs (in the map, not the roundEnv!), run them across all handlers at their current prio level. + + for (long prio : priorityLevels) { + List<JCCompilationUnit> cusForThisRound = new ArrayList<JCCompilationUnit>(); + for (Map.Entry<JCCompilationUnit, Long> entry : roots.entrySet()) { + Long prioOfCu = entry.getValue(); + if (prioOfCu == null || prioOfCu != prio) continue; + cusForThisRound.add(entry.getKey()); + } + transformer.transform(prio, processingEnv.getContext(), cusForThisRound); } - transformer.transform(prio, processingEnv.getContext(), cusForThisRound); - } - - // Step 3: Push up all CUs to the next level. Set level to null if there is no next level. - - boolean nextRoundNeeded = false; - for (int i = priorityLevels.length - 1; i >= 0; i--) { - Long curLevel = priorityLevels[i]; - Long nextLevel = (i == priorityLevels.length - 1) ? null : priorityLevels[i + 1]; - for (Map.Entry<JCCompilationUnit, Long> entry : roots.entrySet()) { - if (curLevel.equals(entry.getValue())) { - entry.setValue(nextLevel); - if (nextLevel != null) nextRoundNeeded = true; + + // Step 3: Push up all CUs to the next level. Set level to null if there is no next level. + + Set<Long> newLevels = new HashSet<Long>(); + for (int i = priorityLevels.length - 1; i >= 0; i--) { + Long curLevel = priorityLevels[i]; + Long nextLevel = (i == priorityLevels.length - 1) ? null : priorityLevels[i + 1]; + for (Map.Entry<JCCompilationUnit, Long> entry : roots.entrySet()) { + if (curLevel.equals(entry.getValue())) { + entry.setValue(nextLevel); + newLevels.add(nextLevel); + } } } + newLevels.remove(null); + + // Step 4: If ALL values are null, quit. Else, either do another loop right now or force a resolution reset by forcing a new round in the annotation processor. + + if (newLevels.isEmpty()) return false; + newLevels.retainAll(priorityLevelsRequiringResolutionReset); + if (newLevels.isEmpty()) { + // None of the new levels need resolution, so just keep going. + continue; + } else { + // Force a new round to reset resolution. The next round will cause this method (process) to be called again. + forceNewRound((JavacFiler) processingEnv.getFiler()); + return false; + } } - - // Step 4: If ALL values are null, quit. Else, force new round. - - if (nextRoundNeeded) forceNewRound((JavacFiler) processingEnv.getFiler()); - - return false; } private int dummyCount = 0; diff --git a/src/core/lombok/javac/handlers/HandleDelegate.java b/src/core/lombok/javac/handlers/HandleDelegate.java index b693a2a3..644b03fb 100644 --- a/src/core/lombok/javac/handlers/HandleDelegate.java +++ b/src/core/lombok/javac/handlers/HandleDelegate.java @@ -48,6 +48,7 @@ import lombok.javac.FindTypeVarScanner; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.JavacResolution; +import lombok.javac.ResolutionResetNeeded; import lombok.javac.JavacResolution.TypeNotConvertibleException; import org.mangosdk.spi.ProviderFor; @@ -76,6 +77,7 @@ import com.sun.tools.javac.util.Name; @ProviderFor(JavacAnnotationHandler.class) @HandlerPriority(65536) //2^16; to make sure that we also delegate generated methods. +@ResolutionResetNeeded public class HandleDelegate extends JavacAnnotationHandler<Delegate> { private static final List<String> METHODS_IN_OBJECT = Collections.unmodifiableList(Arrays.asList( "hashCode()", diff --git a/src/core/lombok/javac/handlers/HandleVal.java b/src/core/lombok/javac/handlers/HandleVal.java index 0ab9d783..d7a31708 100644 --- a/src/core/lombok/javac/handlers/HandleVal.java +++ b/src/core/lombok/javac/handlers/HandleVal.java @@ -29,6 +29,7 @@ import lombok.javac.JavacASTAdapter; import lombok.javac.JavacASTVisitor; import lombok.javac.JavacNode; import lombok.javac.JavacResolution; +import lombok.javac.ResolutionResetNeeded; import org.mangosdk.spi.ProviderFor; @@ -45,6 +46,7 @@ import com.sun.tools.javac.util.List; @ProviderFor(JavacASTVisitor.class) @HandlerPriority(65536) // 2^16; resolution needs to work, so if the RHS expression is i.e. a call to a generated getter, we have to run after that getter has been generated. +@ResolutionResetNeeded public class HandleVal extends JavacASTAdapter { @Override public void visitLocal(JavacNode localNode, JCVariableDecl local) { if (local.vartype == null || (!local.vartype.toString().equals("val") && !local.vartype.toString().equals("lombok.val"))) return; |