diff options
author | Roel Spilker <r.spilker@gmail.com> | 2010-08-03 01:04:01 +0200 |
---|---|---|
committer | Roel Spilker <r.spilker@gmail.com> | 2010-08-03 01:04:01 +0200 |
commit | 11fcdbee725a3b900ed7d6b7d09ea18f6af6b961 (patch) | |
tree | 4913fc2ad0c2412db5ff45d8f878f2c5d86307c3 | |
parent | fd8e08cb784db38d527c684681a88fc59931f713 (diff) | |
download | lombok-11fcdbee725a3b900ed7d6b7d09ea18f6af6b961.tar.gz lombok-11fcdbee725a3b900ed7d6b7d09ea18f6af6b961.tar.bz2 lombok-11fcdbee725a3b900ed7d6b7d09ea18f6af6b961.zip |
Added initial support for post-compilation byte code transformations
-rw-r--r-- | src/core/lombok/core/DiagnosticsReceiver.java | 30 | ||||
-rw-r--r-- | src/core/lombok/core/LombokNode.java | 8 | ||||
-rw-r--r-- | src/core/lombok/core/PostCompiler.java | 30 | ||||
-rw-r--r-- | src/core/lombok/javac/apt/InterceptingJavaFileManager.java | 104 | ||||
-rw-r--r-- | src/core/lombok/javac/apt/InterceptingJavaFileObject.java | 129 | ||||
-rw-r--r-- | src/core/lombok/javac/apt/MessagerDiagnosticsReceiver.java | 43 | ||||
-rw-r--r-- | src/core/lombok/javac/apt/Processor.java | 38 |
7 files changed, 373 insertions, 9 deletions
diff --git a/src/core/lombok/core/DiagnosticsReceiver.java b/src/core/lombok/core/DiagnosticsReceiver.java new file mode 100644 index 00000000..cdd16bc0 --- /dev/null +++ b/src/core/lombok/core/DiagnosticsReceiver.java @@ -0,0 +1,30 @@ +/* + * Copyright © 2009-2010 Reinier Zwitserloot and Roel Spilker. + * + * 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 interface DiagnosticsReceiver { + /** Generate a compiler error on this node. */ + void addError(String message); + + /** Generate a compiler warning on this node. */ + void addWarning(String message); +} diff --git a/src/core/lombok/core/LombokNode.java b/src/core/lombok/core/LombokNode.java index 6fe37a6b..6d0f3147 100644 --- a/src/core/lombok/core/LombokNode.java +++ b/src/core/lombok/core/LombokNode.java @@ -38,7 +38,7 @@ import lombok.core.AST.Kind; * @param N The common type of all AST nodes in the internal representation of the target platform. * For example, JCTree for javac, and ASTNode for Eclipse. */ -public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A, L, N>, N> { +public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A, L, N>, N> implements DiagnosticsReceiver { protected final A ast; protected final Kind kind; protected final N node; @@ -316,12 +316,6 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A, return (L) this; } - /** Generate a compiler error on this node. */ - public abstract void addError(String message); - - /** Generate a compiler warning on this node. */ - public abstract void addWarning(String message); - /** * Structurally significant means: LocalDeclaration, TypeDeclaration, MethodDeclaration, ConstructorDeclaration, * FieldDeclaration, Initializer, and CompilationUnitDeclaration. diff --git a/src/core/lombok/core/PostCompiler.java b/src/core/lombok/core/PostCompiler.java new file mode 100644 index 00000000..882430fc --- /dev/null +++ b/src/core/lombok/core/PostCompiler.java @@ -0,0 +1,30 @@ +/* + * Copyright © 2010 Reinier Zwitserloot and Roel Spilker. + * + * 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 PostCompiler { + private PostCompiler() {/* prevent instantiation*/}; + + public static byte[] applyTransformations(byte[] original, String className, DiagnosticsReceiver diagnostics) { + return original; + } +} diff --git a/src/core/lombok/javac/apt/InterceptingJavaFileManager.java b/src/core/lombok/javac/apt/InterceptingJavaFileManager.java new file mode 100644 index 00000000..2b570eb0 --- /dev/null +++ b/src/core/lombok/javac/apt/InterceptingJavaFileManager.java @@ -0,0 +1,104 @@ +/* + * Copyright © 2010 Reinier Zwitserloot and Roel Spilker. + * + * 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.apt; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Set; + +import javax.tools.FileObject; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject; +import javax.tools.JavaFileObject.Kind; + +import lombok.core.DiagnosticsReceiver; + +final class InterceptingJavaFileManager implements JavaFileManager { + private final JavaFileManager delegate; + private final DiagnosticsReceiver diagnostics; + + InterceptingJavaFileManager(JavaFileManager original, DiagnosticsReceiver diagnostics) { + this.delegate = original; + this.diagnostics = diagnostics; + } + + @Override public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException { + JavaFileObject fileObject = delegate.getJavaFileForOutput(location, className, kind, sibling); + if (kind != Kind.CLASS) { + return fileObject; + } + return new InterceptingJavaFileObject(fileObject, className, diagnostics); + } + + + + +/////////////////////// NOTHING CHANGED BELOW ////////////////////////////////////// + + @Override public void close() throws IOException { + delegate.close(); + } + + @Override public void flush() throws IOException { + delegate.flush(); + } + + @Override public ClassLoader getClassLoader(Location location) { + return delegate.getClassLoader(location); + } + + @Override public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException { + return delegate.getFileForInput(location, packageName, relativeName); + } + + @Override public FileObject getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling) throws IOException { + return delegate.getFileForOutput(location, packageName, relativeName, sibling); + } + + @Override public JavaFileObject getJavaFileForInput(Location location, String className, Kind kind) throws IOException { + return delegate.getJavaFileForInput(location, className, kind); + } + + @Override public boolean handleOption(String current, Iterator<String> remaining) { + return delegate.handleOption(current, remaining); + } + + @Override public boolean hasLocation(Location location) { + return delegate.hasLocation(location); + } + + @Override public String inferBinaryName(Location location, JavaFileObject file) { + return delegate.inferBinaryName(location, file); + } + + @Override public boolean isSameFile(FileObject a, FileObject b) { + return delegate.isSameFile(a, b); + } + + @Override public Iterable<JavaFileObject> list(Location location, String packageName, Set<Kind> kinds, boolean recurse) throws IOException { + return delegate.list(location, packageName, kinds, recurse); + } + + @Override public int isSupportedOption(String option) { + return delegate.isSupportedOption(option); + } +}
\ No newline at end of file diff --git a/src/core/lombok/javac/apt/InterceptingJavaFileObject.java b/src/core/lombok/javac/apt/InterceptingJavaFileObject.java new file mode 100644 index 00000000..1e07440a --- /dev/null +++ b/src/core/lombok/javac/apt/InterceptingJavaFileObject.java @@ -0,0 +1,129 @@ +/* + * Copyright © 2010 Reinier Zwitserloot and Roel Spilker. + * + * 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.apt; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.net.URI; + +import javax.lang.model.element.Modifier; +import javax.lang.model.element.NestingKind; +import javax.tools.JavaFileObject; + +import lombok.core.DiagnosticsReceiver; +import lombok.core.PostCompiler; + +final class InterceptingJavaFileObject implements JavaFileObject { + private final JavaFileObject delegate; + private final String className; + private final DiagnosticsReceiver diagnostics; + + public InterceptingJavaFileObject(JavaFileObject original, String className, DiagnosticsReceiver diagnostics) { + this.delegate = original; + this.className = className; + this.diagnostics = diagnostics; + } + + public OutputStream openOutputStream() throws IOException { + // Open it first to make sure we throw an exception if that fails. + final OutputStream originalStream = delegate.openOutputStream(); + + return new ByteArrayOutputStream() { + @Override public void close() throws IOException { + // no need to call super + byte[] original = toByteArray(); + byte[] copy = null; + try { + copy = PostCompiler.applyTransformations(original, className, diagnostics); + } catch (Exception e) { + diagnostics.addWarning(String.format("Error during the transformation of '%s'; no post-compilation has been applied", className)); + } + + if (copy == null) { + copy = original; + } + + // Exceptions below should bubble + originalStream.write(copy); + originalStream.close(); + } + }; + } + + public Writer openWriter() throws IOException { + throw new UnsupportedOperationException("Can't use a write for class files"); +// return original.openWriter(); + } + + + + +/////////////////////// NOTHING CHANGED BELOW ////////////////////////////////////// + + public boolean delete() { + return delegate.delete(); + } + + public Modifier getAccessLevel() { + return delegate.getAccessLevel(); + } + + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { + return delegate.getCharContent(ignoreEncodingErrors); + } + + public Kind getKind() { + return delegate.getKind(); + } + + public long getLastModified() { + return delegate.getLastModified(); + } + + public String getName() { + return delegate.getName(); + } + + public NestingKind getNestingKind() { + return delegate.getNestingKind(); + } + + public boolean isNameCompatible(String simpleName, Kind kind) { + return delegate.isNameCompatible(simpleName, kind); + } + + public InputStream openInputStream() throws IOException { + return delegate.openInputStream(); + } + + public Reader openReader(boolean ignoreEncodingErrors) throws IOException { + return delegate.openReader(ignoreEncodingErrors); + } + + public URI toUri() { + return delegate.toUri(); + } +} diff --git a/src/core/lombok/javac/apt/MessagerDiagnosticsReceiver.java b/src/core/lombok/javac/apt/MessagerDiagnosticsReceiver.java new file mode 100644 index 00000000..5b75b3c5 --- /dev/null +++ b/src/core/lombok/javac/apt/MessagerDiagnosticsReceiver.java @@ -0,0 +1,43 @@ +/* + * Copyright © 2009-2010 Reinier Zwitserloot and Roel Spilker. + * + * 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.apt; + +import javax.annotation.processing.Messager; +import javax.tools.Diagnostic.Kind; + +import lombok.core.DiagnosticsReceiver; + +public class MessagerDiagnosticsReceiver implements DiagnosticsReceiver { + private final Messager messager; + + public MessagerDiagnosticsReceiver(Messager messager) { + this.messager = messager; + } + + @Override public void addWarning(String message) { + messager.printMessage(Kind.WARNING, message); + } + + @Override public void addError(String message) { + messager.printMessage(Kind.ERROR, message); + } +}
\ No newline at end of file diff --git a/src/core/lombok/javac/apt/Processor.java b/src/core/lombok/javac/apt/Processor.java index 31cedf03..ab445309 100644 --- a/src/core/lombok/javac/apt/Processor.java +++ b/src/core/lombok/javac/apt/Processor.java @@ -1,5 +1,5 @@ /* - * Copyright © 2009 Reinier Zwitserloot and Roel Spilker. + * Copyright © 2009-2010 Reinier Zwitserloot and Roel Spilker. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,10 +21,14 @@ */ package lombok.javac.apt; +import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.IdentityHashMap; +import java.util.Map; import java.util.Set; import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.Messager; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; @@ -32,14 +36,17 @@ import javax.annotation.processing.SupportedSourceVersion; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; +import javax.tools.JavaFileManager; +import lombok.Lombok; +import lombok.core.DiagnosticsReceiver; import lombok.javac.JavacTransformer; import com.sun.source.util.TreePath; import com.sun.source.util.Trees; import com.sun.tools.javac.processing.JavacProcessingEnvironment; import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; - +import com.sun.tools.javac.util.Context; /** * This Annotation Processor is the standard injection mechanism for lombok-enabling the javac compiler. @@ -54,6 +61,7 @@ import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; @SupportedAnnotationTypes("*") @SupportedSourceVersion(SourceVersion.RELEASE_6) public class Processor extends AbstractProcessor { + private JavacProcessingEnvironment processingEnv; private JavacTransformer transformer; private Trees trees; @@ -62,9 +70,35 @@ public class Processor extends AbstractProcessor { @Override public void init(ProcessingEnvironment procEnv) { super.init(procEnv); this.processingEnv = (JavacProcessingEnvironment) procEnv; + placePostCompileHook(); transformer = new JavacTransformer(procEnv.getMessager()); trees = Trees.instance(procEnv); } + + private void placePostCompileHook() { + Context context = processingEnv.getContext(); + + try { + Method keyMethod = Context.class.getDeclaredMethod("key", Class.class); + keyMethod.setAccessible(true); + Object key = keyMethod.invoke(context, JavaFileManager.class); + Field htField = Context.class.getDeclaredField("ht"); + htField.setAccessible(true); + @SuppressWarnings("unchecked") + Map<Object,Object> ht = (Map<Object,Object>) htField.get(context); + final JavaFileManager originalFiler = (JavaFileManager) ht.get(key); + + if (!(originalFiler instanceof InterceptingJavaFileManager)) { + final Messager messager = processingEnv.getMessager(); + DiagnosticsReceiver receiver = new MessagerDiagnosticsReceiver(messager); + + JavaFileManager newFiler = new InterceptingJavaFileManager(originalFiler, receiver); + ht.put(key, newFiler); + } + } catch (Exception e) { + throw Lombok.sneakyThrow(e); + } + } /** {@inheritDoc} */ @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { |