mirror of
https://github.com/tiennm99/lombok.git
synced 2026-10-13 14:16:11 +00:00
Added an optimization to javac: Don't force new resolution rounds if none of the handlers in the next priority level need it.
This commit is contained in:
1 parent
b31ca9606b
commit
0dbc68a5cc
7 files changed
+144
-30
No files matched your search
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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<?>) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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()",
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user