From f30485c91fd3f9553fbcbc02d922e6221182c26e Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Thu, 5 Jun 2014 03:50:26 +0200 Subject: [#688] Bugfix for eclipse: syntax highlighting would break amongst many other features if using @Setter and most other features. --- src/utils/lombok/core/ReferenceFieldAugment.java | 144 ++++++++++++++++++++++- 1 file changed, 140 insertions(+), 4 deletions(-) (limited to 'src/utils') diff --git a/src/utils/lombok/core/ReferenceFieldAugment.java b/src/utils/lombok/core/ReferenceFieldAugment.java index 214817a7..9f4b23f7 100644 --- a/src/utils/lombok/core/ReferenceFieldAugment.java +++ b/src/utils/lombok/core/ReferenceFieldAugment.java @@ -22,6 +22,8 @@ package lombok.core; import java.lang.ref.WeakReference; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.util.Map; import java.util.WeakHashMap; @@ -41,7 +43,8 @@ public abstract class ReferenceFieldAugment { * @throws NullPointerException if {@code type}, {@code fieldType} or {@code name} is {@code null} */ public static ReferenceFieldAugment augment(Class type, Class fieldType, String name) { - return new MapFieldAugment(); + ReferenceFieldAugment ret = tryCreateReflectionAugment(type, fieldType, name); + return ret != null ? ret : new MapFieldAugment(); } /** @@ -58,7 +61,56 @@ public abstract class ReferenceFieldAugment { * @throws NullPointerException if {@code type}, {@code fieldType} or {@code name} is {@code null} */ public static ReferenceFieldAugment augmentWeakField(Class type, Class fieldType, String name) { - return new MapWeakFieldAugment(); + ReferenceFieldAugment ret = tryCreateReflectionAugment(type, fieldType, name); + return ret != null ? ret : new MapWeakFieldAugment(); + } + + /** + * Creates a reflection-based augment which will directly access the listed field name. If this field does not exist or the field + * is not capable of storing the requested type, {@code null} is returned instead. + */ + private static ReferenceFieldAugment tryCreateReflectionAugment(Class type, Class fieldType, String name) { + Field f = findField(type, name); + if (f != null && typeIsAssignmentCompatible(f.getType(), fieldType)) return new ReflectionFieldAugment(f, fieldType); + return null; + } + + /** + * Finds the named instance field in the type, or in any of its supertypes, regardless of its access modifier. It's set as accessible and returned if found. + */ + private static Field findField(Class type, String name) { + while (type != null) { + try { + Field f = type.getDeclaredField(name); + if (!Modifier.isStatic(f.getModifiers())) { + f.setAccessible(true); + return f; + } + } catch (NoSuchFieldException fallthrough) {} + type = type.getSuperclass(); + } + + return null; + } + + private static boolean typeIsAssignmentCompatible(Class fieldType, Class wantedType) { + if (Modifier.isFinal(fieldType.getModifiers())) return false; + if (Modifier.isStatic(fieldType.getModifiers())) return false; + + if (fieldType == java.lang.Object.class) return true; + if (fieldType == wantedType) return true; + + if (fieldType.isPrimitive()) return fieldType == wantedType; + if (wantedType == int.class && (fieldType == Number.class || fieldType == Integer.class)) return true; + if (wantedType == long.class && (fieldType == Number.class || fieldType == Long.class)) return true; + if (wantedType == short.class && (fieldType == Number.class || fieldType == Short.class)) return true; + if (wantedType == byte.class && (fieldType == Number.class || fieldType == Byte.class)) return true; + if (wantedType == char.class && (fieldType == Number.class || fieldType == Character.class)) return true; + if (wantedType == float.class && (fieldType == Number.class || fieldType == Float.class)) return true; + if (wantedType == double.class && (fieldType == Number.class || fieldType == Double.class)) return true; + if (wantedType == boolean.class && fieldType == Boolean.class) return true; + + return fieldType.isAssignableFrom(wantedType); } private ReferenceFieldAugment() { @@ -71,7 +123,7 @@ public abstract class ReferenceFieldAugment { public abstract F get(T object); /** - * @throws NullPointerException if {@code object} or {@code expected} is {@code null} + * @throws NullPointerException if {@code object} or {@code value} is {@code null} */ public final void set(T object, F value) { getAndSet(object, value); @@ -79,7 +131,7 @@ public abstract class ReferenceFieldAugment { /** * @return the value of the field before the operation. - * @throws NullPointerException if {@code object} or {@code expected} is {@code null} + * @throws NullPointerException if {@code object} or {@code value} is {@code null}. */ public abstract F getAndSet(T object, F value); @@ -107,6 +159,90 @@ public abstract class ReferenceFieldAugment { */ public abstract F compareAndSet(T object, F expected, F value); + private static class ReflectionFieldAugment extends ReferenceFieldAugment { + private final Field field; + private final Class targetType; + + @SuppressWarnings("unchecked") + ReflectionFieldAugment(Field field, Class targetType) { + this.field = field; + this.targetType = (Class) targetType; + } + + @Override public F get(T object) { + checkNotNull(object, "object"); + try { + return targetType.cast(field.get(object)); + } catch (IllegalAccessException e) { + throw new IllegalStateException(e); + } + } + + @Override public F getAndSet(T object, F value) { + checkNotNull(object, "object"); + checkNotNull(value, "value"); + try { + F oldValue = targetType.cast(field.get(object)); + field.set(object, value); + return oldValue; + } catch (IllegalAccessException e) { + throw new IllegalStateException(e); + } + } + + @Override public F clear(T object) { + checkNotNull(object, "object"); + try { + F oldValue = targetType.cast(field.get(object)); + field.set(object, null); + return oldValue; + } catch (IllegalAccessException e) { + throw new IllegalStateException(e); + } + } + + @Override public F compareAndClear(T object, F expected) { + checkNotNull(object, "object"); + checkNotNull(expected, "expected"); + try { + F result = targetType.cast(field.get(object)); + if (result == null) return null; + if (!expected.equals(result)) return result; + field.set(object, null); + return null; + } catch (IllegalAccessException e) { + throw new IllegalStateException(e); + } + } + + @Override public F setIfAbsent(T object, F value) { + checkNotNull(object, "object"); + checkNotNull(value, "value"); + try { + F result = targetType.cast(field.get(object)); + if (result != null) return result; + field.set(object, value); + return value; + } catch (IllegalAccessException e) { + throw new IllegalStateException(e); + } + } + + @Override public F compareAndSet(T object, F expected, F value) { + checkNotNull(object, "object"); + checkNotNull(expected, "expected"); + checkNotNull(value, "value"); + try { + F result = targetType.cast(field.get(object)); + if (!expected.equals(result)) return result; + field.set(object, value); + return value; + } catch (IllegalAccessException e) { + throw new IllegalStateException(e); + } + } + } + private static class MapFieldAugment extends ReferenceFieldAugment { final Map values = new WeakHashMap(); -- cgit From 1ce747178b8f24f29f94dd795f09f872aad9272f Mon Sep 17 00:00:00 2001 From: Roel Spilker Date: Thu, 5 Jun 2014 23:45:43 +0200 Subject: Finished refactor of FieldAugment; there's no longer a separate variant for boolean and references, and the code no longer blows up with a bunch of NPEs if you try to use the reference variant (which is now the only variant) with a primitive type. Should have zero effect on features or bugs, 100% refactor. --- src/core/lombok/core/AnnotationProcessor.java | 6 +- src/core/lombok/core/Augments.java | 30 ++ src/core/lombok/eclipse/EclipseAugments.java | 39 ++ src/core/lombok/eclipse/HandlerLibrary.java | 8 +- .../eclipse/handlers/EclipseHandlerUtil.java | 18 +- src/core/lombok/javac/HandlerLibrary.java | 7 +- src/core/lombok/javac/JavacAugments.java | 35 ++ .../lombok/javac/handlers/JavacHandlerUtil.java | 10 +- .../lombok/eclipse/agent/PatchDelegate.java | 10 +- .../lombok/eclipse/agent/PatchExtensionMethod.java | 14 +- src/utils/lombok/core/BooleanFieldAugment.java | 185 ---------- src/utils/lombok/core/FieldAugment.java | 399 +++++++++++++++++++++ src/utils/lombok/core/ReferenceFieldAugment.java | 354 ------------------ src/utils/lombok/javac/CommentCatcher.java | 22 +- .../javac/java6/CommentCollectingParser.java | 9 +- .../java6/CommentCollectingParserFactory.java | 16 +- .../javac/java7/CommentCollectingParser.java | 9 +- .../java7/CommentCollectingParserFactory.java | 15 +- .../javac/java8/CommentCollectingParser.java | 9 +- .../java8/CommentCollectingParserFactory.java | 17 +- 20 files changed, 567 insertions(+), 645 deletions(-) create mode 100644 src/core/lombok/core/Augments.java create mode 100644 src/core/lombok/eclipse/EclipseAugments.java create mode 100644 src/core/lombok/javac/JavacAugments.java delete mode 100644 src/utils/lombok/core/BooleanFieldAugment.java create mode 100644 src/utils/lombok/core/FieldAugment.java delete mode 100644 src/utils/lombok/core/ReferenceFieldAugment.java (limited to 'src/utils') diff --git a/src/core/lombok/core/AnnotationProcessor.java b/src/core/lombok/core/AnnotationProcessor.java index ecce1849..eb44811b 100644 --- a/src/core/lombok/core/AnnotationProcessor.java +++ b/src/core/lombok/core/AnnotationProcessor.java @@ -21,6 +21,8 @@ */ package lombok.core; +import static lombok.core.Augments.ClassLoader_lombokAlreadyAddedTo; + import java.io.File; import java.io.PrintWriter; import java.io.StringWriter; @@ -61,8 +63,6 @@ public class AnnotationProcessor extends AbstractProcessor { private final List active = new ArrayList(); private final List delayedWarnings = new ArrayList(); - private static final BooleanFieldAugment lombokAlreadyAddedTo = BooleanFieldAugment.augment(ClassLoader.class, "lombok$alreadyAddedTo"); - static class JavacDescriptor extends ProcessorDescriptor { private Processor processor; @@ -98,7 +98,7 @@ public class AnnotationProcessor extends AbstractProcessor { private ClassLoader findAndPatchClassLoader(ProcessingEnvironment procEnv) throws Exception { ClassLoader environmentClassLoader = procEnv.getClass().getClassLoader(); if (environmentClassLoader != null && environmentClassLoader.getClass().getCanonicalName().equals("org.codehaus.plexus.compiler.javac.IsolatedClassLoader")) { - if (!lombokAlreadyAddedTo.set(environmentClassLoader)) { + if (!ClassLoader_lombokAlreadyAddedTo.getAndSet(environmentClassLoader, true)) { Method m = environmentClassLoader.getClass().getDeclaredMethod("addURL", URL.class); URL selfUrl = new File(ClassRootFinder.findClassRootOfClass(AnnotationProcessor.class)).toURI().toURL(); m.invoke(environmentClassLoader, selfUrl); diff --git a/src/core/lombok/core/Augments.java b/src/core/lombok/core/Augments.java new file mode 100644 index 00000000..0b0e082d --- /dev/null +++ b/src/core/lombok/core/Augments.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2014 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; + +public final class Augments { + private Augments() { + // prevent instantiation + } + + public static final FieldAugment ClassLoader_lombokAlreadyAddedTo = FieldAugment.augment(ClassLoader.class, boolean.class, "lombok$alreadyAddedTo"); +} diff --git a/src/core/lombok/eclipse/EclipseAugments.java b/src/core/lombok/eclipse/EclipseAugments.java new file mode 100644 index 00000000..f4583ac4 --- /dev/null +++ b/src/core/lombok/eclipse/EclipseAugments.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2014 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.eclipse; + +import org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.eclipse.jdt.internal.compiler.ast.Annotation; +import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; + +import lombok.core.FieldAugment; + +public final class EclipseAugments { + private EclipseAugments() { + // Prevent instantiation + } + + public static final FieldAugment FieldDeclaration_booleanLazyGetter = FieldAugment.augment(FieldDeclaration.class, boolean.class, "lombok$booleanLazyGetter"); + public static final FieldAugment ASTNode_handled = FieldAugment.augment(ASTNode.class, boolean.class, "lombok$handled"); + public static final FieldAugment ASTNode_generatedBy = FieldAugment.augment(ASTNode.class, ASTNode.class, "$generatedBy"); + public static final FieldAugment Annotation_applied = FieldAugment.augment(Annotation.class, boolean.class, "lombok$applied"); +} diff --git a/src/core/lombok/eclipse/HandlerLibrary.java b/src/core/lombok/eclipse/HandlerLibrary.java index 3e5a557f..07c6f97b 100644 --- a/src/core/lombok/eclipse/HandlerLibrary.java +++ b/src/core/lombok/eclipse/HandlerLibrary.java @@ -23,6 +23,7 @@ package lombok.eclipse; import static lombok.eclipse.Eclipse.*; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; +import static lombok.eclipse.EclipseAugments.ASTNode_handled; import java.io.IOException; import java.lang.annotation.Annotation; @@ -38,7 +39,6 @@ import lombok.Lombok; import lombok.core.AnnotationValues; import lombok.core.AnnotationValues.AnnotationValueDecodeFail; import lombok.core.configuration.ConfigurationKeysLoader; -import lombok.core.BooleanFieldAugment; import lombok.core.HandlerPriority; import lombok.core.SpiLoadUtil; import lombok.core.TypeLibrary; @@ -188,14 +188,12 @@ public class HandlerLibrary { } } - private static final BooleanFieldAugment handled = BooleanFieldAugment.augment(ASTNode.class, "lombok$handled"); - private boolean checkAndSetHandled(ASTNode node) { - return !handled.set(node); + return !ASTNode_handled.getAndSet(node, true); } private boolean needsHandling(ASTNode node) { - return !handled.get(node); + return !ASTNode_handled.get(node); } /** diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java index b37dbd81..6a903e4c 100644 --- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java +++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java @@ -21,8 +21,9 @@ */ package lombok.eclipse.handlers; -import static lombok.eclipse.Eclipse.*; import static lombok.core.handlers.HandlerUtil.*; +import static lombok.eclipse.Eclipse.*; +import static lombok.eclipse.EclipseAugments.*; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -43,8 +44,6 @@ import lombok.Lombok; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; import lombok.core.AnnotationValues.AnnotationValue; -import lombok.core.BooleanFieldAugment; -import lombok.core.ReferenceFieldAugment; import lombok.core.TypeResolver; import lombok.core.configuration.NullCheckExceptionType; import lombok.core.handlers.HandlerUtil; @@ -109,7 +108,6 @@ import org.eclipse.jdt.internal.compiler.lookup.TypeIds; import org.eclipse.jdt.internal.compiler.lookup.WildcardBinding; import org.osgi.framework.Bundle; - /** * Container for static utility methods useful to handlers written for eclipse. */ @@ -211,10 +209,8 @@ public class EclipseHandlerUtil { } } - private static ReferenceFieldAugment generatedNodes = ReferenceFieldAugment.augment(ASTNode.class, ASTNode.class, "$generatedBy"); - public static ASTNode getGeneratedBy(ASTNode node) { - return generatedNodes.get(node); + return ASTNode_generatedBy.get(node); } public static boolean isGenerated(ASTNode node) { @@ -222,7 +218,7 @@ public class EclipseHandlerUtil { } public static ASTNode setGeneratedBy(ASTNode node, ASTNode source) { - generatedNodes.set(node, source); + ASTNode_generatedBy.set(node, source); return node; } @@ -844,11 +840,9 @@ public class EclipseHandlerUtil { } } - private static final BooleanFieldAugment generatedLazyGettersWithPrimitiveBoolean = BooleanFieldAugment.augment(FieldDeclaration.class, "lombok$booleanLazyGetter"); - static void registerCreatedLazyGetter(FieldDeclaration field, char[] methodName, TypeReference returnType) { if (isBoolean(returnType)) { - generatedLazyGettersWithPrimitiveBoolean.set(field); + FieldDeclaration_booleanLazyGetter.set(field, true); } } @@ -858,7 +852,7 @@ public class EclipseHandlerUtil { private static GetterMethod findGetter(EclipseNode field) { FieldDeclaration fieldDeclaration = (FieldDeclaration) field.get(); - boolean forceBool = generatedLazyGettersWithPrimitiveBoolean.get(fieldDeclaration); + boolean forceBool = FieldDeclaration_booleanLazyGetter.get(fieldDeclaration); TypeReference fieldType = fieldDeclaration.type; boolean isBoolean = forceBool || isBoolean(fieldType); diff --git a/src/core/lombok/javac/HandlerLibrary.java b/src/core/lombok/javac/HandlerLibrary.java index 7d40204b..30aeff73 100644 --- a/src/core/lombok/javac/HandlerLibrary.java +++ b/src/core/lombok/javac/HandlerLibrary.java @@ -21,6 +21,8 @@ */ package lombok.javac; +import static lombok.javac.JavacAugments.JCTree_handled; + import java.io.IOException; import java.lang.annotation.Annotation; import java.util.ArrayList; @@ -35,7 +37,6 @@ import javax.annotation.processing.Messager; import javax.tools.Diagnostic; import lombok.core.AnnotationValues.AnnotationValueDecodeFail; -import lombok.core.BooleanFieldAugment; import lombok.core.HandlerPriority; import lombok.core.SpiLoadUtil; import lombok.core.TypeLibrary; @@ -206,10 +207,8 @@ public class HandlerLibrary { if (t != null) t.printStackTrace(); } - private static final BooleanFieldAugment handled = BooleanFieldAugment.augment(JCTree.class, "lombok$handled"); - private boolean checkAndSetHandled(JCTree node) { - return !handled.set(node); + return !JCTree_handled.getAndSet(node, true); } /** diff --git a/src/core/lombok/javac/JavacAugments.java b/src/core/lombok/javac/JavacAugments.java new file mode 100644 index 00000000..bc23131b --- /dev/null +++ b/src/core/lombok/javac/JavacAugments.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2014 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 lombok.core.FieldAugment; + +import com.sun.tools.javac.tree.JCTree; + +public final class JavacAugments { + private JavacAugments() { + // Prevent instantiation + } + + public static final FieldAugment JCTree_handled = FieldAugment.augment(JCTree.class, boolean.class, "lombok$handled"); + public static final FieldAugment JCTree_generatedNode = FieldAugment.circularSafeAugment(JCTree.class, JCTree.class, "lombok$generatedNode"); +} diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java index 25b95590..6413e8ef 100644 --- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java +++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java @@ -23,6 +23,7 @@ package lombok.javac.handlers; import static lombok.core.handlers.HandlerUtil.*; import static lombok.javac.Javac.*; +import static lombok.javac.JavacAugments.JCTree_generatedNode; import java.lang.annotation.Annotation; import java.lang.reflect.Method; @@ -41,7 +42,6 @@ import lombok.Getter; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; import lombok.core.AnnotationValues.AnnotationValue; -import lombok.core.ReferenceFieldAugment; import lombok.core.TypeResolver; import lombok.core.configuration.NullCheckExceptionType; import lombok.core.handlers.HandlerUtil; @@ -112,8 +112,6 @@ public class JavacHandlerUtil { } } - private static ReferenceFieldAugment generatedNodes = ReferenceFieldAugment.augmentWeakField(JCTree.class, JCTree.class, "lombok$generatedNodes"); - /** * Contributed by Jan Lahoda; many lombok transformations should not be run (or a lite version should be run) when the netbeans editor * is running javac on the open source file to find inline errors and such. As class files are compiled separately this does not affect @@ -129,7 +127,7 @@ public class JavacHandlerUtil { } public static JCTree getGeneratedBy(JCTree node) { - return generatedNodes.get(node); + return JCTree_generatedNode.get(node); } public static boolean isGenerated(JCTree node) { @@ -145,8 +143,8 @@ public class JavacHandlerUtil { public static T setGeneratedBy(T node, JCTree source, Context context) { if (node == null) return null; - if (source == null) generatedNodes.clear(node); - else generatedNodes.set(node, source); + if (source == null) JCTree_generatedNode.clear(node); + else JCTree_generatedNode.set(node, source); if (source != null && (!inNetbeansEditor(context) || (node instanceof JCVariableDecl && (((JCVariableDecl) node).mods.flags & Flags.PARAMETER) != 0))) node.pos = source.pos; return node; } diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java b/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java index b6e75476..b1f5a43a 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java @@ -23,6 +23,7 @@ package lombok.eclipse.agent; import static lombok.eclipse.Eclipse.*; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; +import static lombok.eclipse.EclipseAugments.Annotation_applied; import java.lang.reflect.Method; import java.util.ArrayList; @@ -33,7 +34,6 @@ import java.util.List; import java.util.Set; import lombok.core.AST.Kind; -import lombok.core.BooleanFieldAugment; import lombok.eclipse.EclipseAST; import lombok.eclipse.EclipseNode; import lombok.eclipse.TransformEclipseAST; @@ -183,10 +183,8 @@ public class PatchDelegate { return null; } - private static BooleanFieldAugment applied = BooleanFieldAugment.augment(Annotation.class, "lombok$applied"); - public static void markHandled(Annotation annotation) { - applied.set(annotation); + Annotation_applied.set(annotation, true); } private static void fillMethodBindingsForFields(CompilationUnitDeclaration cud, ClassScope scope, List methodsToDelegate) { @@ -197,7 +195,7 @@ public class PatchDelegate { if (field.annotations == null) continue; for (Annotation ann : field.annotations) { if (!isDelegate(ann, decl)) continue; - if (applied.set(ann)) continue; + if (Annotation_applied.getAndSet(ann, true)) continue; if ((field.modifiers & ClassFileConstants.AccStatic) != 0) { EclipseAST eclipseAst = TransformEclipseAST.getAST(cud, true); @@ -257,7 +255,7 @@ public class PatchDelegate { if (methodDecl.annotations == null) continue; for (Annotation ann : methodDecl.annotations) { if (!isDelegate(ann, decl)) continue; - if (applied.set(ann)) continue; + if (Annotation_applied.getAndSet(ann, true)) continue; if (!(methodDecl instanceof MethodDeclaration)) { EclipseAST eclipseAst = TransformEclipseAST.getAST(cud, true); eclipseAst.get(ann).addError(LEGALITY_OF_DELEGATE); diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java index 9b3ec5df..8eec27fb 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java @@ -31,7 +31,7 @@ import java.util.List; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; import lombok.core.AnnotationValues.AnnotationValueDecodeFail; -import lombok.core.ReferenceFieldAugment; +import lombok.core.FieldAugment; import lombok.eclipse.EclipseAST; import lombok.eclipse.EclipseNode; import lombok.eclipse.TransformEclipseAST; @@ -178,14 +178,14 @@ public class PatchExtensionMethod { return extensionMethods; } - private static final ReferenceFieldAugment postponedErrors = ReferenceFieldAugment.augment(MessageSend.class, PostponedError.class, "lombok$postponedErrors"); + private static final FieldAugment MessageSend_postponedErrors = FieldAugment.augment(MessageSend.class, PostponedError.class, "lombok$postponedErrors"); public static void errorNoMethodFor(ProblemReporter problemReporter, MessageSend messageSend, TypeBinding recType, TypeBinding[] params) { - postponedErrors.set(messageSend, new PostponedNoMethodError(problemReporter, messageSend, recType, params)); + MessageSend_postponedErrors.set(messageSend, new PostponedNoMethodError(problemReporter, messageSend, recType, params)); } public static void invalidMethod(ProblemReporter problemReporter, MessageSend messageSend, MethodBinding method) { - postponedErrors.set(messageSend, new PostponedInvalidMethodError(problemReporter, messageSend, method)); + MessageSend_postponedErrors.set(messageSend, new PostponedInvalidMethodError(problemReporter, messageSend, method)); } public static TypeBinding resolveType(TypeBinding resolvedType, MessageSend methodCall, BlockScope scope) { @@ -215,7 +215,7 @@ public class PatchExtensionMethod { if (!extension.suppressBaseMethods && !(methodCall.binding instanceof ProblemMethodBinding)) continue; for (MethodBinding extensionMethod : extension.extensionMethods) { if (!Arrays.equals(methodCall.selector, extensionMethod.selector)) continue; - postponedErrors.clear(methodCall); + MessageSend_postponedErrors.clear(methodCall); if (methodCall.receiver instanceof ThisReference) { methodCall.receiver.bits &= ~ASTNode.IsImplicitThis; } @@ -257,10 +257,10 @@ public class PatchExtensionMethod { } } - PostponedError error = postponedErrors.get(methodCall); + PostponedError error = MessageSend_postponedErrors.get(methodCall); if (error != null) error.fire(); - postponedErrors.clear(methodCall); + MessageSend_postponedErrors.clear(methodCall); return resolvedType; } diff --git a/src/utils/lombok/core/BooleanFieldAugment.java b/src/utils/lombok/core/BooleanFieldAugment.java deleted file mode 100644 index d843e9df..00000000 --- a/src/utils/lombok/core/BooleanFieldAugment.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (C) 2014 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.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.Map; -import java.util.WeakHashMap; - -/** - * Augments a instance of a type with a boolean field. - *

- * If the type already declares a boolean field, that field is used. Otherwise the field will be augmented. - * - * @param the type to augment. - */ -public abstract class BooleanFieldAugment { - - /** - * Augments a instance of a type with a boolean field. - *

- * If the type already declares a boolean instance field, that field might be used. Otherwise the field will be augmented. - *

- * This code assumes that for any combination of {@code type} and {@code name} this method is only called once. - * Otherwise, whether state is shared is undefined. - * - * @param type to augment - * @param name of the field - * @throws NullPointerException if {@code type} or {@code name} is {@code null} - */ - public static BooleanFieldAugment augment(Class type, String name) { - checkNotNull(type, "type"); - checkNotNull(name, "name"); - Field booleanField = getBooleanField(type, name); - if (booleanField == null) { - return new MapFieldAugment(); - } - return new ExistingFieldAugment(booleanField); - } - - private BooleanFieldAugment() { - // prevent external instantiation - } - - private static Field getBooleanField(Class type, String name) { - try { - Field result = type.getDeclaredField(name); - if (Modifier.isStatic(result.getModifiers()) || result.getType() != boolean.class) { - return null; - } - result.setAccessible(true); - return result; - } catch (Throwable t) { - return null; - } - } - - /** - * Sets the field to {@code true}. - * @returns the previous value - * @throws NullPointerException if {@code object} is {@code null} - */ - public abstract boolean set(T object); - - /** - * Sets the field to {@code false}. - * @returns the previous value - * @throws NullPointerException if {@code object} is {@code null} - */ - public abstract boolean clear(T object); - - /** - * @eturn {code true} if the field is set, otherwise {@code false}. - * @throws NullPointerException if {@code object} is {@code null} - */ - public abstract boolean get(T object); - - private static class MapFieldAugment extends BooleanFieldAugment { - private static final Object MARKER = new Object(); - - private final Map values = new WeakHashMap(); - - public boolean set(T object) { - checkNotNull(object, "object"); - synchronized (values) { - return values.put(object, MARKER) != null; - } - } - - public boolean clear(T object) { - checkNotNull(object, "object"); - synchronized (values) { - return values.remove(object) != null; - } - } - - public boolean get(T object) { - checkNotNull(object, "object"); - synchronized (values) { - return values.get(object) != null; - } - } - } - - private static class ExistingFieldAugment extends BooleanFieldAugment { - private final Object lock = new Object(); - private final Field booleanField; - - private ExistingFieldAugment(Field booleanField) { - this.booleanField = booleanField; - } - - @Override public boolean set(T object) { - checkNotNull(object, "object"); - try { - synchronized (lock) { - boolean result = booleanField.getBoolean(object); - booleanField.setBoolean(object, true); - return result; - } - } catch (IllegalAccessException e) { - throw sneakyThrow(e); - } - } - - @Override public boolean clear(T object) { - checkNotNull(object, "object"); - try { - synchronized (lock) { - boolean result = booleanField.getBoolean(object); - booleanField.setBoolean(object, false); - return result; - } - } catch (IllegalAccessException e) { - throw sneakyThrow(e); - } - } - - @Override public boolean get(T object) { - checkNotNull(object, "object"); - try { - synchronized (lock) { - return booleanField.getBoolean(object); - } - } catch (IllegalAccessException e) { - throw sneakyThrow(e); - } - } - } - - private static T checkNotNull(T object, String name) { - if (object == null) throw new NullPointerException(name); - return object; - } - - private static RuntimeException sneakyThrow(Throwable t) { - if (t == null) throw new NullPointerException("t"); - BooleanFieldAugment.sneakyThrow0(t); - return null; - } - - @SuppressWarnings("unchecked") - private static void sneakyThrow0(Throwable t) throws T { - throw (T)t; - } -} \ No newline at end of file diff --git a/src/utils/lombok/core/FieldAugment.java b/src/utils/lombok/core/FieldAugment.java new file mode 100644 index 00000000..ee8acf4d --- /dev/null +++ b/src/utils/lombok/core/FieldAugment.java @@ -0,0 +1,399 @@ +/* + * Copyright (C) 2014 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.ref.WeakReference; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Map; +import java.util.WeakHashMap; + +public abstract class FieldAugment { + private static Object getDefaultValue(Class type) { + if (type == boolean.class) return false; + if (type == int.class) return 0; + if (!type.isPrimitive()) return null; + + if (type == long.class) return 0L; + if (type == short.class) return (short) 0; + if (type == byte.class) return (byte) 0; + if (type == char.class) return '\0'; + if (type == float.class) return 0.0F; + if (type == double.class) return 0.0D; + + // We can't get here unless java added some primitive types, but, hey. + return null; + } + + /** + * (Virtually) adds a field to an existing type and returns an object that can be used to read and write this field. + *

+ * If the type already declares a non-final instance field with the given name and a compatible field type, that field will be used. + * Otherwise the field will be provided virtually. + *

+ * WARNING: The values put into the augment should NOT reference in any way the object you've added the augment to, or memory leaks may occur. + * If you do need to add such references, use {@link #circularSafeAugment(Class, Class, String, Object)} instead. + *

+ * This code assumes that for any combination of {@code type} and {@code name} this method is only called once. + * Otherwise, whether state is shared is undefined. + * + * @param type to augment + * @param fieldType type of the field + * @param name of the field + * @param defaultValue the value of the augment if it hasn't been set yet. + * @throws NullPointerException if {@code type}, {@code fieldType} or {@code name} is {@code null} + */ + public static FieldAugment augment(Class type, Class fieldType, String name) { + checkNotNull(type, "type"); + checkNotNull(fieldType, "fieldType"); + checkNotNull(name, "name"); + + @SuppressWarnings("unchecked") + F defaultValue = (F) getDefaultValue(fieldType); + FieldAugment ret = tryCreateReflectionAugment(type, fieldType, name, defaultValue); + return ret != null ? ret : new MapFieldAugment(defaultValue); + } + + /** + * (Virtually) adds a field to an existing type and returns an object that can be used to read and write this field. + *

+ * This method does the same as {@link #augment(Class, Class, String, Object)}, except it is safe to set values that reference back to their containing object. + */ + public static FieldAugment circularSafeAugment(Class type, Class fieldType, String name) { + checkNotNull(type, "type"); + checkNotNull(fieldType, "fieldType"); + checkNotNull(name, "name"); + + @SuppressWarnings("unchecked") + F defaultValue = (F) getDefaultValue(fieldType); + FieldAugment ret = tryCreateReflectionAugment(type, fieldType, name, defaultValue); + return ret != null ? ret : new MapWeakFieldAugment(defaultValue); + } + + /** + * Creates a reflection-based augment which will directly access the listed field name. If this field does not exist or the field + * is not capable of storing the requested type, {@code null} is returned instead. + */ + private static FieldAugment tryCreateReflectionAugment(Class type, Class fieldType, String name, F defaultValue) { + Field f = findField(type, fieldType, name); + if (f != null && typeIsAssignmentCompatible(f.getType(), fieldType)) return new ReflectionFieldAugment(f, fieldType, defaultValue); + return null; + } + + private static Field findField(Class type, Class wantedType, String name) { + try { + Field f = type.getDeclaredField(name); + if (Modifier.isStatic(f.getModifiers()) || Modifier.isFinal(f.getModifiers())) return null; + if (!typeIsAssignmentCompatible(f.getType(), wantedType)) return null; + f.setAccessible(true); + return f; + } catch (Exception e) { + return null; + } + } + + private static boolean typeIsAssignmentCompatible(Class fieldType, Class wantedType) { + if (fieldType == java.lang.Object.class) return true; + if (fieldType == wantedType) return true; + + if (fieldType.isPrimitive()) return fieldType == wantedType; + if (wantedType == int.class && (fieldType == Number.class || fieldType == Integer.class)) return true; + if (wantedType == long.class && (fieldType == Number.class || fieldType == Long.class)) return true; + if (wantedType == short.class && (fieldType == Number.class || fieldType == Short.class)) return true; + if (wantedType == byte.class && (fieldType == Number.class || fieldType == Byte.class)) return true; + if (wantedType == char.class && (fieldType == Number.class || fieldType == Character.class)) return true; + if (wantedType == float.class && (fieldType == Number.class || fieldType == Float.class)) return true; + if (wantedType == double.class && (fieldType == Number.class || fieldType == Double.class)) return true; + if (wantedType == boolean.class && fieldType == Boolean.class) return true; + + return fieldType.isAssignableFrom(wantedType); + } + + private FieldAugment() { + // prevent external instantiation + } + + /** + * @throws NullPointerException if {@code object} is {@code null} + */ + public abstract F get(T object); + + /** + * @throws NullPointerException if {@code object} or {@code value} is {@code null} + */ + public final void set(T object, F value) { + getAndSet(object, value); + } + + /** + * @return the value of the field before the operation. + * @throws NullPointerException if {@code object} or {@code value} is {@code null}. + */ + public abstract F getAndSet(T object, F value); + + /** + * @return the value of the field before the operation. + * @throws NullPointerException if {@code object} is {@code null} + */ + public abstract F clear(T object); + + /** + * @return the value of the field after the operation. If the value was equal to {@code expected} or already cleared {@code null}, otherwise the current value. + * @throws NullPointerException if {@code object} or {@code expected} is {@code null} + */ + public abstract F compareAndClear(T object, F expected); + + /** + * @return the value of the field after the operation. + * @throws NullPointerException if {@code object} or {@code value} is {@code null} + */ + public abstract F setIfAbsent(T object, F value); + + /** + * @return the value of the field after the operation. + * @throws NullPointerException if {@code object}, {@code expected} or {@code value} is {@code null} + */ + public abstract F compareAndSet(T object, F expected, F value); + + private static class ReflectionFieldAugment extends FieldAugment { + private final Object lock = new Object(); + private final Field field; + private final Class targetType; + private final F defaultValue; + + @SuppressWarnings("unchecked") + ReflectionFieldAugment(Field field, Class targetType, F defaultValue) { + this.field = field; + this.targetType = (Class) targetType; + this.defaultValue = defaultValue; + } + + @Override public F get(T object) { + checkNotNull(object, "object"); + try { + F value; + synchronized (lock) { + value = targetType.cast(field.get(object)); + } + return value == null ? defaultValue : value; + } catch (IllegalAccessException e) { + throw new IllegalStateException(e); + } + } + + @Override public F getAndSet(T object, F value) { + checkNotNull(object, "object"); + checkNotNull(value, "value"); + try { + F oldValue; + synchronized (lock) { + oldValue = targetType.cast(field.get(object)); + field.set(object, value); + } + return oldValue == null ? defaultValue : oldValue; + } catch (IllegalAccessException e) { + throw new IllegalStateException(e); + } + } + + @Override public F clear(T object) { + checkNotNull(object, "object"); + try { + F oldValue; + synchronized (lock) { + oldValue = targetType.cast(field.get(object)); + field.set(object, defaultValue); + } + return oldValue == null ? defaultValue : oldValue; + } catch (IllegalAccessException e) { + throw new IllegalStateException(e); + } + } + + @Override public F compareAndClear(T object, F expected) { + checkNotNull(object, "object"); + checkNotNull(expected, "expected"); + try { + F oldValue; + synchronized (lock) { + oldValue = targetType.cast(field.get(object)); + if (expected.equals(oldValue)) { + field.set(object, defaultValue); + return defaultValue; + } + } + return oldValue; + } catch (IllegalAccessException e) { + throw new IllegalStateException(e); + } + } + + @Override public F setIfAbsent(T object, F value) { + checkNotNull(object, "object"); + checkNotNull(value, "value"); + try { + synchronized (lock) { + F oldValue = targetType.cast(field.get(object)); + if (oldValue != null && !oldValue.equals(defaultValue)) return oldValue; + field.set(object, value); + return value; + } + } catch (IllegalAccessException e) { + throw new IllegalStateException(e); + } + } + + @Override public F compareAndSet(T object, F expected, F value) { + checkNotNull(object, "object"); + checkNotNull(expected, "expected"); + checkNotNull(value, "value"); + try { + synchronized (lock) { + F oldValue = targetType.cast(field.get(object)); + if (!expected.equals(oldValue)) return oldValue == null ? defaultValue : oldValue; + field.set(object, value); + return value; + } + } catch (IllegalAccessException e) { + throw new IllegalStateException(e); + } + } + } + + private static class MapFieldAugment extends FieldAugment { + final Map values = new WeakHashMap(); + final F defaultValue; + + MapFieldAugment(F defaultValue) { + this.defaultValue = defaultValue; + } + + @Override + public F get(T object) { + checkNotNull(object, "object"); + synchronized (values) { + return read(object); + } + } + + @Override + public F getAndSet(T object, F value) { + checkNotNull(object, "object"); + checkNotNull(value, "value"); + synchronized (values) { + F result = read(object); + write(object, value); + return result; + } + } + + @Override + public F clear(T object) { + checkNotNull(object, "object"); + synchronized (values) { + F result = read(object); + values.remove(object); + return result; + } + } + + @Override + public F compareAndClear(T object, F expected) { + checkNotNull(object, "object"); + checkNotNull(expected, "expected"); + synchronized (values) { + F result = read(object); + if (result == null) { + return null; + } + if (!expected.equals(result)) { + return result; + } + values.remove(object); + return null; + } + } + + @Override + public F setIfAbsent(T object, F value) { + checkNotNull(object, "object"); + checkNotNull(value, "value"); + synchronized (values) { + F result = read(object); + if (result != null) { + return result; + } + write(object, value); + return value; + } + } + + @Override + public F compareAndSet(T object, F expected, F value) { + checkNotNull(object, "object"); + checkNotNull(expected, "expected"); + checkNotNull(value, "value"); + synchronized (values) { + F result = read(object); + if (!expected.equals(result)) { + return result; + } + write(object, value); + return value; + } + } + + @SuppressWarnings("unchecked") + F read(T object) { + F value = (F) values.get(object); + return value == null ? defaultValue : value; + } + + void write(T object, F value) { + values.put(object, value); + } + } + + static class MapWeakFieldAugment extends MapFieldAugment { + MapWeakFieldAugment(F defaultValue) { + super(defaultValue); + } + + @SuppressWarnings("unchecked") + F read(T object) { + WeakReference read = (WeakReference)values.get(object); + if (read == null) return defaultValue; + F result = read.get(); + if (result == null) values.remove(object); + return result == null ? defaultValue : result; + } + + void write(T object, F value) { + values.put(object, new WeakReference(value)); + } + } + + private static T checkNotNull(T object, String name) { + if (object == null) throw new NullPointerException(name); + return object; + } +} diff --git a/src/utils/lombok/core/ReferenceFieldAugment.java b/src/utils/lombok/core/ReferenceFieldAugment.java deleted file mode 100644 index 9f4b23f7..00000000 --- a/src/utils/lombok/core/ReferenceFieldAugment.java +++ /dev/null @@ -1,354 +0,0 @@ -/* - * Copyright (C) 2014 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.ref.WeakReference; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.Map; -import java.util.WeakHashMap; - -public abstract class ReferenceFieldAugment { - - /** - * Augments a instance of a type with a reference field. - *

- * If the type already declares an instance field with the given name and field type, that field might be used. Otherwise the field will be augmented. - *

- * This code assumes that for any combination of {@code type} and {@code name} this method is only called once. - * Otherwise, whether state is shared is undefined. - * - * @param type to augment - * @param fieldType type of the field - * @param name of the field - * @throws NullPointerException if {@code type}, {@code fieldType} or {@code name} is {@code null} - */ - public static ReferenceFieldAugment augment(Class type, Class fieldType, String name) { - ReferenceFieldAugment ret = tryCreateReflectionAugment(type, fieldType, name); - return ret != null ? ret : new MapFieldAugment(); - } - - /** - * Augments a instance of a type with a weak reference field. - *

- * If the type already declares an instance field with the given name and field type, that field might be used. Otherwise the field will be augmented. - *

- * This code assumes that for any combination of {@code type} and {@code name} this method is only called once. - * Otherwise, whether state is shared is undefined. - * - * @param type to augment - * @param fieldType type of the field - * @param name of the field - * @throws NullPointerException if {@code type}, {@code fieldType} or {@code name} is {@code null} - */ - public static ReferenceFieldAugment augmentWeakField(Class type, Class fieldType, String name) { - ReferenceFieldAugment ret = tryCreateReflectionAugment(type, fieldType, name); - return ret != null ? ret : new MapWeakFieldAugment(); - } - - /** - * Creates a reflection-based augment which will directly access the listed field name. If this field does not exist or the field - * is not capable of storing the requested type, {@code null} is returned instead. - */ - private static ReferenceFieldAugment tryCreateReflectionAugment(Class type, Class fieldType, String name) { - Field f = findField(type, name); - if (f != null && typeIsAssignmentCompatible(f.getType(), fieldType)) return new ReflectionFieldAugment(f, fieldType); - return null; - } - - /** - * Finds the named instance field in the type, or in any of its supertypes, regardless of its access modifier. It's set as accessible and returned if found. - */ - private static Field findField(Class type, String name) { - while (type != null) { - try { - Field f = type.getDeclaredField(name); - if (!Modifier.isStatic(f.getModifiers())) { - f.setAccessible(true); - return f; - } - } catch (NoSuchFieldException fallthrough) {} - type = type.getSuperclass(); - } - - return null; - } - - private static boolean typeIsAssignmentCompatible(Class fieldType, Class wantedType) { - if (Modifier.isFinal(fieldType.getModifiers())) return false; - if (Modifier.isStatic(fieldType.getModifiers())) return false; - - if (fieldType == java.lang.Object.class) return true; - if (fieldType == wantedType) return true; - - if (fieldType.isPrimitive()) return fieldType == wantedType; - if (wantedType == int.class && (fieldType == Number.class || fieldType == Integer.class)) return true; - if (wantedType == long.class && (fieldType == Number.class || fieldType == Long.class)) return true; - if (wantedType == short.class && (fieldType == Number.class || fieldType == Short.class)) return true; - if (wantedType == byte.class && (fieldType == Number.class || fieldType == Byte.class)) return true; - if (wantedType == char.class && (fieldType == Number.class || fieldType == Character.class)) return true; - if (wantedType == float.class && (fieldType == Number.class || fieldType == Float.class)) return true; - if (wantedType == double.class && (fieldType == Number.class || fieldType == Double.class)) return true; - if (wantedType == boolean.class && fieldType == Boolean.class) return true; - - return fieldType.isAssignableFrom(wantedType); - } - - private ReferenceFieldAugment() { - // prevent external instantiation - } - - /** - * @throws NullPointerException if {@code object} is {@code null} - */ - public abstract F get(T object); - - /** - * @throws NullPointerException if {@code object} or {@code value} is {@code null} - */ - public final void set(T object, F value) { - getAndSet(object, value); - } - - /** - * @return the value of the field before the operation. - * @throws NullPointerException if {@code object} or {@code value} is {@code null}. - */ - public abstract F getAndSet(T object, F value); - - /** - * @return the value of the field before the operation. - * @throws NullPointerException if {@code object} is {@code null} - */ - public abstract F clear(T object); - - /** - * @return the value of the field after the operation. If the value was equal to {@code expected} or already cleared {@code null}, otherwise the current value. - * @throws NullPointerException if {@code object} or {@code expected} is {@code null} - */ - public abstract F compareAndClear(T object, F expected); - - /** - * @return the value of the field after the operation. - * @throws NullPointerException if {@code object} or {@code value} is {@code null} - */ - public abstract F setIfAbsent(T object, F value); - - /** - * @return the value of the field after the operation. - * @throws NullPointerException if {@code object}, {@code expected} or {@code value} is {@code null} - */ - public abstract F compareAndSet(T object, F expected, F value); - - private static class ReflectionFieldAugment extends ReferenceFieldAugment { - private final Field field; - private final Class targetType; - - @SuppressWarnings("unchecked") - ReflectionFieldAugment(Field field, Class targetType) { - this.field = field; - this.targetType = (Class) targetType; - } - - @Override public F get(T object) { - checkNotNull(object, "object"); - try { - return targetType.cast(field.get(object)); - } catch (IllegalAccessException e) { - throw new IllegalStateException(e); - } - } - - @Override public F getAndSet(T object, F value) { - checkNotNull(object, "object"); - checkNotNull(value, "value"); - try { - F oldValue = targetType.cast(field.get(object)); - field.set(object, value); - return oldValue; - } catch (IllegalAccessException e) { - throw new IllegalStateException(e); - } - } - - @Override public F clear(T object) { - checkNotNull(object, "object"); - try { - F oldValue = targetType.cast(field.get(object)); - field.set(object, null); - return oldValue; - } catch (IllegalAccessException e) { - throw new IllegalStateException(e); - } - } - - @Override public F compareAndClear(T object, F expected) { - checkNotNull(object, "object"); - checkNotNull(expected, "expected"); - try { - F result = targetType.cast(field.get(object)); - if (result == null) return null; - if (!expected.equals(result)) return result; - field.set(object, null); - return null; - } catch (IllegalAccessException e) { - throw new IllegalStateException(e); - } - } - - @Override public F setIfAbsent(T object, F value) { - checkNotNull(object, "object"); - checkNotNull(value, "value"); - try { - F result = targetType.cast(field.get(object)); - if (result != null) return result; - field.set(object, value); - return value; - } catch (IllegalAccessException e) { - throw new IllegalStateException(e); - } - } - - @Override public F compareAndSet(T object, F expected, F value) { - checkNotNull(object, "object"); - checkNotNull(expected, "expected"); - checkNotNull(value, "value"); - try { - F result = targetType.cast(field.get(object)); - if (!expected.equals(result)) return result; - field.set(object, value); - return value; - } catch (IllegalAccessException e) { - throw new IllegalStateException(e); - } - } - } - - private static class MapFieldAugment extends ReferenceFieldAugment { - final Map values = new WeakHashMap(); - - @Override - public F get(T object) { - checkNotNull(object, "object"); - synchronized (values) { - return read(object); - } - } - - @Override - public F getAndSet(T object, F value) { - checkNotNull(object, "object"); - checkNotNull(value, "value"); - synchronized (values) { - F result = read(object); - write(object, value); - return result; - } - } - - @Override - public F clear(T object) { - checkNotNull(object, "object"); - synchronized (values) { - F result = read(object); - values.remove(object); - return result; - } - } - - @Override - public F compareAndClear(T object, F expected) { - checkNotNull(object, "object"); - checkNotNull(expected, "expected"); - synchronized (values) { - F result = read(object); - if (result == null) { - return null; - } - if (!expected.equals(result)) { - return result; - } - values.remove(object); - return null; - } - } - - @Override - public F setIfAbsent(T object, F value) { - checkNotNull(object, "object"); - checkNotNull(value, "value"); - synchronized (values) { - F result = read(object); - if (result != null) { - return result; - } - write(object, value); - return value; - } - } - - @Override - public F compareAndSet(T object, F expected, F value) { - checkNotNull(object, "object"); - checkNotNull(expected, "expected"); - checkNotNull(value, "value"); - synchronized (values) { - F result = read(object); - if (!expected.equals(result)) { - return result; - } - write(object, value); - return value; - } - } - - @SuppressWarnings("unchecked") - F read(T object) { - return (F)values.get(object); - } - - void write(T object, F value) { - values.put(object, value); - } - } - - static class MapWeakFieldAugment extends MapFieldAugment { - - @SuppressWarnings("unchecked") - F read(T object) { - WeakReference read = (WeakReference)values.get(object); - if (read == null) return null; - F result = read.get(); - if (result == null) values.remove(object); - return result; - } - - void write(T object, F value) { - values.put(object, new WeakReference(value)); - } - } - - private static T checkNotNull(T object, String name) { - if (object == null) throw new NullPointerException(name); - return object; - } -} diff --git a/src/utils/lombok/javac/CommentCatcher.java b/src/utils/lombok/javac/CommentCatcher.java index ff6be7a2..c32da68b 100644 --- a/src/utils/lombok/javac/CommentCatcher.java +++ b/src/utils/lombok/javac/CommentCatcher.java @@ -25,7 +25,7 @@ import java.lang.reflect.InvocationTargetException; import java.util.Collections; import java.util.List; -import lombok.core.ReferenceFieldAugment; +import lombok.core.FieldAugment; import com.sun.tools.javac.main.JavaCompiler; import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; @@ -33,24 +33,22 @@ import com.sun.tools.javac.util.Context; public class CommentCatcher { private final JavaCompiler compiler; - private final ReferenceFieldAugment> commentsField; + public static final FieldAugment> JCCompilationUnit_comments = FieldAugment.augment(JCCompilationUnit.class, List.class, "lombok$comments"); public static CommentCatcher create(Context context) { registerCommentsCollectingScannerFactory(context); JavaCompiler compiler = new JavaCompiler(context); - ReferenceFieldAugment> comments = ReferenceFieldAugment.augment(JCCompilationUnit.class, List.class, "lombok$comments"); - setInCompiler(compiler, context, comments); + setInCompiler(compiler, context); compiler.keepComments = true; compiler.genEndPos = true; - return new CommentCatcher(compiler, comments); + return new CommentCatcher(compiler); } - private CommentCatcher(JavaCompiler compiler, ReferenceFieldAugment> commentsField) { + private CommentCatcher(JavaCompiler compiler) { this.compiler = compiler; - this.commentsField = commentsField; } public JavaCompiler getCompiler() { @@ -59,14 +57,14 @@ public class CommentCatcher { public void setComments(JCCompilationUnit ast, List comments) { if (comments != null) { - commentsField.set(ast, comments); + JCCompilationUnit_comments.set(ast, comments); } else { - commentsField.clear(ast); + JCCompilationUnit_comments.clear(ast); } } public List getComments(JCCompilationUnit ast) { - List list = commentsField.get(ast); + List list = JCCompilationUnit_comments.get(ast); return list == null ? Collections.emptyList() : list; } @@ -89,7 +87,7 @@ public class CommentCatcher { } } - private static void setInCompiler(JavaCompiler compiler, Context context, ReferenceFieldAugment> commentsField) { + private static void setInCompiler(JavaCompiler compiler, Context context) { try { Class parserFactory; int javaCompilerVersion = Javac.getJavaCompilerVersion(); @@ -100,7 +98,7 @@ public class CommentCatcher { } else { parserFactory = Class.forName("lombok.javac.java8.CommentCollectingParserFactory"); } - parserFactory.getMethod("setInCompiler", JavaCompiler.class, Context.class, ReferenceFieldAugment.class).invoke(null, compiler, context, commentsField); + parserFactory.getMethod("setInCompiler", JavaCompiler.class, Context.class).invoke(null, compiler, context); } catch (InvocationTargetException e) { throw Javac.sneakyThrow(e.getCause()); } catch (Exception e) { diff --git a/src/utils/lombok/javac/java6/CommentCollectingParser.java b/src/utils/lombok/javac/java6/CommentCollectingParser.java index 215bf5b6..d5d3c256 100644 --- a/src/utils/lombok/javac/java6/CommentCollectingParser.java +++ b/src/utils/lombok/javac/java6/CommentCollectingParser.java @@ -21,7 +21,7 @@ */ package lombok.javac.java6; -import lombok.core.ReferenceFieldAugment; +import static lombok.javac.CommentCatcher.JCCompilationUnit_comments; import lombok.javac.CommentInfo; import com.sun.tools.javac.parser.EndPosParser; @@ -31,21 +31,18 @@ import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; import com.sun.tools.javac.util.List; class CommentCollectingParser extends EndPosParser { - - private final ReferenceFieldAugment> commentsField; private final Lexer lexer; - protected CommentCollectingParser(Parser.Factory fac, Lexer S, boolean keepDocComments, ReferenceFieldAugment> commentsField) { + protected CommentCollectingParser(Parser.Factory fac, Lexer S, boolean keepDocComments) { super(fac, S, keepDocComments); lexer = S; - this.commentsField = commentsField; } @Override public JCCompilationUnit compilationUnit() { JCCompilationUnit result = super.compilationUnit(); if (lexer instanceof CommentCollectingScanner) { List comments = ((CommentCollectingScanner)lexer).getComments(); - commentsField.set(result, comments); + JCCompilationUnit_comments.set(result, comments); } return result; } diff --git a/src/utils/lombok/javac/java6/CommentCollectingParserFactory.java b/src/utils/lombok/javac/java6/CommentCollectingParserFactory.java index 124a05f7..0cf8fdcd 100644 --- a/src/utils/lombok/javac/java6/CommentCollectingParserFactory.java +++ b/src/utils/lombok/javac/java6/CommentCollectingParserFactory.java @@ -23,43 +23,35 @@ package lombok.javac.java6; import java.lang.reflect.Field; -import lombok.core.ReferenceFieldAugment; -import lombok.javac.CommentInfo; - import com.sun.tools.javac.main.JavaCompiler; import com.sun.tools.javac.parser.Lexer; import com.sun.tools.javac.parser.Parser; -import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; import com.sun.tools.javac.util.Context; -import com.sun.tools.javac.util.List; public class CommentCollectingParserFactory extends Parser.Factory { - private final ReferenceFieldAugment> commentsField; - static Context.Key key() { return parserFactoryKey; } - protected CommentCollectingParserFactory(Context context, ReferenceFieldAugment> commentsField) { + protected CommentCollectingParserFactory(Context context) { super(context); - this.commentsField = commentsField; } @Override public Parser newParser(Lexer S, boolean keepDocComments, boolean genEndPos) { - Object x = new CommentCollectingParser(this, S, true, commentsField); + Object x = new CommentCollectingParser(this, S, true); return (Parser) x; // CCP is based on a stub which extends nothing, but at runtime the stub is replaced with either //javac6's EndPosParser which extends Parser, or javac7's EndPosParser which implements Parser. //Either way this will work out. } - public static void setInCompiler(JavaCompiler compiler, Context context, ReferenceFieldAugment> commentsField) { + public static void setInCompiler(JavaCompiler compiler, Context context) { context.put(CommentCollectingParserFactory.key(), (Parser.Factory)null); Field field; try { field = JavaCompiler.class.getDeclaredField("parserFactory"); field.setAccessible(true); - field.set(compiler, new CommentCollectingParserFactory(context, commentsField)); + field.set(compiler, new CommentCollectingParserFactory(context)); } catch (Exception e) { throw new IllegalStateException("Could not set comment sensitive parser in the compiler", e); } diff --git a/src/utils/lombok/javac/java7/CommentCollectingParser.java b/src/utils/lombok/javac/java7/CommentCollectingParser.java index 27d731ba..de93e5b4 100644 --- a/src/utils/lombok/javac/java7/CommentCollectingParser.java +++ b/src/utils/lombok/javac/java7/CommentCollectingParser.java @@ -21,9 +21,10 @@ */ package lombok.javac.java7; +import static lombok.javac.CommentCatcher.JCCompilationUnit_comments; + import java.util.List; -import lombok.core.ReferenceFieldAugment; import lombok.javac.CommentInfo; import com.sun.tools.javac.parser.EndPosParser; @@ -32,21 +33,19 @@ import com.sun.tools.javac.parser.ParserFactory; import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; class CommentCollectingParser extends EndPosParser { - private final ReferenceFieldAugment> commentsField; private final Lexer lexer; protected CommentCollectingParser(ParserFactory fac, Lexer S, - boolean keepDocComments, boolean keepLineMap, ReferenceFieldAugment> commentsField) { + boolean keepDocComments, boolean keepLineMap) { super(fac, S, keepDocComments, keepLineMap); lexer = S; - this.commentsField = commentsField; } public JCCompilationUnit parseCompilationUnit() { JCCompilationUnit result = super.parseCompilationUnit(); if (lexer instanceof CommentCollectingScanner) { List comments = ((CommentCollectingScanner)lexer).getComments(); - commentsField.set(result, comments); + JCCompilationUnit_comments.set(result, comments); } return result; } diff --git a/src/utils/lombok/javac/java7/CommentCollectingParserFactory.java b/src/utils/lombok/javac/java7/CommentCollectingParserFactory.java index dba5a0fa..ffd68f55 100644 --- a/src/utils/lombok/javac/java7/CommentCollectingParserFactory.java +++ b/src/utils/lombok/javac/java7/CommentCollectingParserFactory.java @@ -22,50 +22,43 @@ package lombok.javac.java7; import java.lang.reflect.Field; -import java.util.List; - -import lombok.core.ReferenceFieldAugment; -import lombok.javac.CommentInfo; import com.sun.tools.javac.main.JavaCompiler; import com.sun.tools.javac.parser.Lexer; import com.sun.tools.javac.parser.Parser; import com.sun.tools.javac.parser.ParserFactory; import com.sun.tools.javac.parser.ScannerFactory; -import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; import com.sun.tools.javac.util.Context; public class CommentCollectingParserFactory extends ParserFactory { - private final ReferenceFieldAugment> commentsField; private final Context context; static Context.Key key() { return parserFactoryKey; } - protected CommentCollectingParserFactory(Context context, ReferenceFieldAugment> commentsField) { + protected CommentCollectingParserFactory(Context context) { super(context); this.context = context; - this.commentsField = commentsField; } public Parser newParser(CharSequence input, boolean keepDocComments, boolean keepEndPos, boolean keepLineMap) { ScannerFactory scannerFactory = ScannerFactory.instance(context); Lexer lexer = scannerFactory.newScanner(input, true); - Object x = new CommentCollectingParser(this, lexer, true, keepLineMap, commentsField); + Object x = new CommentCollectingParser(this, lexer, true, keepLineMap); return (Parser) x; // CCP is based on a stub which extends nothing, but at runtime the stub is replaced with either //javac6's EndPosParser which extends Parser, or javac7's EndPosParser which implements Parser. //Either way this will work out. } - public static void setInCompiler(JavaCompiler compiler, Context context, ReferenceFieldAugment> commentsField) { + public static void setInCompiler(JavaCompiler compiler, Context context) { context.put(CommentCollectingParserFactory.key(), (ParserFactory)null); Field field; try { field = JavaCompiler.class.getDeclaredField("parserFactory"); field.setAccessible(true); - field.set(compiler, new CommentCollectingParserFactory(context, commentsField)); + field.set(compiler, new CommentCollectingParserFactory(context)); } catch (Exception e) { throw new IllegalStateException("Could not set comment sensitive parser in the compiler", e); } diff --git a/src/utils/lombok/javac/java8/CommentCollectingParser.java b/src/utils/lombok/javac/java8/CommentCollectingParser.java index 9a05267c..b49312cb 100644 --- a/src/utils/lombok/javac/java8/CommentCollectingParser.java +++ b/src/utils/lombok/javac/java8/CommentCollectingParser.java @@ -21,9 +21,10 @@ */ package lombok.javac.java8; +import static lombok.javac.CommentCatcher.JCCompilationUnit_comments; + import java.util.List; -import lombok.core.ReferenceFieldAugment; import lombok.javac.CommentInfo; import com.sun.tools.javac.parser.JavacParser; @@ -32,21 +33,19 @@ import com.sun.tools.javac.parser.ParserFactory; import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; class CommentCollectingParser extends JavacParser { - private final ReferenceFieldAugment> commentsField; private final Lexer lexer; protected CommentCollectingParser(ParserFactory fac, Lexer S, - boolean keepDocComments, boolean keepLineMap, boolean keepEndPositions, ReferenceFieldAugment> commentsField) { + boolean keepDocComments, boolean keepLineMap, boolean keepEndPositions) { super(fac, S, keepDocComments, keepLineMap, keepEndPositions); lexer = S; - this.commentsField = commentsField; } public JCCompilationUnit parseCompilationUnit() { JCCompilationUnit result = super.parseCompilationUnit(); if (lexer instanceof CommentCollectingScanner) { List comments = ((CommentCollectingScanner)lexer).getComments(); - commentsField.set(result, comments); + JCCompilationUnit_comments.set(result, comments); } return result; } diff --git a/src/utils/lombok/javac/java8/CommentCollectingParserFactory.java b/src/utils/lombok/javac/java8/CommentCollectingParserFactory.java index 5bed46fc..45f865ad 100644 --- a/src/utils/lombok/javac/java8/CommentCollectingParserFactory.java +++ b/src/utils/lombok/javac/java8/CommentCollectingParserFactory.java @@ -22,50 +22,43 @@ package lombok.javac.java8; import java.lang.reflect.Field; -import java.util.List; - -import lombok.core.ReferenceFieldAugment; -import lombok.javac.CommentInfo; import com.sun.tools.javac.main.JavaCompiler; import com.sun.tools.javac.parser.JavacParser; import com.sun.tools.javac.parser.Lexer; import com.sun.tools.javac.parser.ParserFactory; import com.sun.tools.javac.parser.ScannerFactory; -import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; import com.sun.tools.javac.util.Context; public class CommentCollectingParserFactory extends ParserFactory { - private final ReferenceFieldAugment> commentsField; private final Context context; static Context.Key key() { return parserFactoryKey; } - protected CommentCollectingParserFactory(Context context, ReferenceFieldAugment> commentsField) { + protected CommentCollectingParserFactory(Context context) { super(context); this.context = context; - this.commentsField = commentsField; } public JavacParser newParser(CharSequence input, boolean keepDocComments, boolean keepEndPos, boolean keepLineMap) { ScannerFactory scannerFactory = ScannerFactory.instance(context); Lexer lexer = scannerFactory.newScanner(input, true); - Object x = new CommentCollectingParser(this, lexer, true, keepLineMap, keepEndPos, commentsField); + Object x = new CommentCollectingParser(this, lexer, true, keepLineMap, keepEndPos); return (JavacParser) x; // CCP is based on a stub which extends nothing, but at runtime the stub is replaced with either //javac6's EndPosParser which extends Parser, or javac8's JavacParser which implements Parser. //Either way this will work out. } - public static void setInCompiler(JavaCompiler compiler, Context context, ReferenceFieldAugment> commentsField) { - context.put(CommentCollectingParserFactory.key(), (ParserFactory)null); + public static void setInCompiler(JavaCompiler compiler, Context context) { + context.put(CommentCollectingParserFactory.key(), (ParserFactory) null); Field field; try { field = JavaCompiler.class.getDeclaredField("parserFactory"); field.setAccessible(true); - field.set(compiler, new CommentCollectingParserFactory(context, commentsField)); + field.set(compiler, new CommentCollectingParserFactory(context)); } catch (Exception e) { throw new IllegalStateException("Could not set comment sensitive parser in the compiler", e); } -- cgit