From fc6670e1c4c31fbeaf658369c2771a2a93a0a9ba Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Mon, 4 Oct 2010 20:20:39 +0200 Subject: The previous commit (fix for ZIP closed errors) actually breaks lombok on systems with a javac that _does not_ close the annotation processor's jar. Fixed that, too. --- src/core/lombok/javac/apt/Processor.java | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/core/lombok/javac/apt/Processor.java b/src/core/lombok/javac/apt/Processor.java index 1d3d0c34..037f5ba5 100644 --- a/src/core/lombok/javac/apt/Processor.java +++ b/src/core/lombok/javac/apt/Processor.java @@ -157,6 +157,8 @@ public class Processor extends AbstractProcessor { ClassLoader unwrapped = (ClassLoader) f.get(processingEnv); ClassLoader wrapped = new WrappingClassLoader(unwrapped); f.set(processingEnv, wrapped); + } catch (NoSuchFieldException e) { + // Some versions of javac have this (and call close on it), some don't. I guess this one doesn't have it. } catch (Throwable t) { throw Lombok.sneakyThrow(t); } -- cgit From 17bcfda43b9e939ac22d5247ef98905741650432 Mon Sep 17 00:00:00 2001 From: Roel Spilker Date: Tue, 2 Nov 2010 20:39:58 +0100 Subject: Issue 154: Add null-check to @Cleanup --- .../lombok/eclipse/handlers/HandleCleanup.java | 28 +++++++++++++++++++++- src/core/lombok/javac/handlers/HandleCleanup.java | 16 +++++++++---- 2 files changed, 39 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/core/lombok/eclipse/handlers/HandleCleanup.java b/src/core/lombok/eclipse/handlers/HandleCleanup.java index d296e96b..33ab7fb9 100644 --- a/src/core/lombok/eclipse/handlers/HandleCleanup.java +++ b/src/core/lombok/eclipse/handlers/HandleCleanup.java @@ -37,9 +37,13 @@ import org.eclipse.jdt.internal.compiler.ast.Assignment; import org.eclipse.jdt.internal.compiler.ast.Block; import org.eclipse.jdt.internal.compiler.ast.CaseStatement; import org.eclipse.jdt.internal.compiler.ast.CastExpression; +import org.eclipse.jdt.internal.compiler.ast.EqualExpression; +import org.eclipse.jdt.internal.compiler.ast.IfStatement; import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; import org.eclipse.jdt.internal.compiler.ast.MemberValuePair; import org.eclipse.jdt.internal.compiler.ast.MessageSend; +import org.eclipse.jdt.internal.compiler.ast.NullLiteral; +import org.eclipse.jdt.internal.compiler.ast.OperatorIds; import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; import org.eclipse.jdt.internal.compiler.ast.Statement; import org.eclipse.jdt.internal.compiler.ast.SwitchStatement; @@ -157,7 +161,29 @@ public class HandleCleanup implements EclipseAnnotationHandler { } unsafeClose.nameSourcePosition = nameSourcePosition; unsafeClose.selector = cleanupName.toCharArray(); - finallyBlock[0] = unsafeClose; + + + int pS = ast.sourceStart, pE = ast.sourceEnd; + long p = (long)pS << 32 | pE; + + SingleNameReference varName = new SingleNameReference(decl.name, p); + Eclipse.setGeneratedBy(varName, ast); + NullLiteral nullLiteral = new NullLiteral(pS, pE); + Eclipse.setGeneratedBy(nullLiteral, ast); + EqualExpression equalExpression = new EqualExpression(varName, nullLiteral, OperatorIds.NOT_EQUAL); + equalExpression.sourceStart = pS; equalExpression.sourceEnd = pE; + Eclipse.setGeneratedBy(equalExpression, ast); + + Block closeBlock = new Block(0); + closeBlock.statements = new Statement[1]; + closeBlock.statements[0] = unsafeClose; + Eclipse.setGeneratedBy(closeBlock, ast); + IfStatement ifStatement = new IfStatement(equalExpression, closeBlock, 0, 0); + Eclipse.setGeneratedBy(ifStatement, ast); + + + + finallyBlock[0] = ifStatement; tryStatement.finallyBlock = new Block(0); Eclipse.setGeneratedBy(tryStatement.finallyBlock, ast); tryStatement.finallyBlock.statements = finallyBlock; diff --git a/src/core/lombok/javac/handlers/HandleCleanup.java b/src/core/lombok/javac/handlers/HandleCleanup.java index 2c89d9ad..779dd3ea 100644 --- a/src/core/lombok/javac/handlers/HandleCleanup.java +++ b/src/core/lombok/javac/handlers/HandleCleanup.java @@ -30,10 +30,12 @@ import lombok.javac.JavacNode; import org.mangosdk.spi.ProviderFor; +import com.sun.tools.javac.code.TypeTags; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.TreeMaker; import com.sun.tools.javac.tree.JCTree.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCAssign; +import com.sun.tools.javac.tree.JCTree.JCBinary; import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCCase; import com.sun.tools.javac.tree.JCTree.JCCatch; @@ -41,6 +43,7 @@ import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCExpressionStatement; import com.sun.tools.javac.tree.JCTree.JCFieldAccess; import com.sun.tools.javac.tree.JCTree.JCIdent; +import com.sun.tools.javac.tree.JCTree.JCIf; import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.tree.JCTree.JCTypeCast; @@ -108,11 +111,16 @@ public class HandleCleanup implements JavacAnnotationHandler { doAssignmentCheck(annotationNode, tryBlock, decl.name); TreeMaker maker = annotationNode.getTreeMaker(); - JCFieldAccess cleanupCall = maker.Select(maker.Ident(decl.name), annotationNode.toName(cleanupName)); - List finalizerBlock = List.of(maker.Exec( - maker.Apply(List.nil(), cleanupCall, List.nil()))); + JCFieldAccess cleanupMethod = maker.Select(maker.Ident(decl.name), annotationNode.toName(cleanupName)); + List cleanupCall = List.of(maker.Exec( + maker.Apply(List.nil(), cleanupMethod, List.nil()))); + + JCBinary isNull = maker.Binary(JCTree.NE, maker.Ident(decl.name), maker.Literal(TypeTags.BOT, null)); + + JCIf ifNotNullCleanup = maker.If(isNull, maker.Block(0, cleanupCall), null); + + JCBlock finalizer = maker.Block(0, List.of(ifNotNullCleanup)); - JCBlock finalizer = maker.Block(0, finalizerBlock); newStatements = newStatements.append(maker.Try(maker.Block(0, tryBlock), List.nil(), finalizer)); if (blockNode instanceof JCBlock) { -- cgit From 6e2924bcf2ceb390c0f8c712e8ee9c4428173ded Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Wed, 3 Nov 2010 00:40:14 +0100 Subject: Some small framework updates to accomodate resolution. --- src/core/lombok/core/LombokNode.java | 4 ++++ src/core/lombok/javac/HandlerLibrary.java | 8 +++++++- src/core/lombok/javac/JavacASTAdapter.java | 5 +++++ src/core/lombok/javac/JavacASTVisitor.java | 10 ++++++++++ 4 files changed, 26 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/core/lombok/core/LombokNode.java b/src/core/lombok/core/LombokNode.java index 6d0f3147..4a57e080 100644 --- a/src/core/lombok/core/LombokNode.java +++ b/src/core/lombok/core/LombokNode.java @@ -78,6 +78,10 @@ public abstract class LombokNode, L extends LombokNode Date: Wed, 3 Nov 2010 00:42:32 +0100 Subject: big changes to delombok to prep for resolution, when sourcepath and classpath are relevant, and the flow needs to be parse all, then enter all, then transform all, instead of parse->enter->transform sequentially for each file in isolation. --- .../lombok/delombok/CommentCollectingScanner.java | 2 +- .../lombok/delombok/CommentPreservingParser.java | 170 ------------------ src/delombok/lombok/delombok/Delombok.java | 190 +++++++++++++++++---- src/delombok/lombok/delombok/DelombokResult.java | 62 +++++++ .../lombok/delombok/PrettyCommentsPrinter.java | 16 +- src/delombok/lombok/delombok/ant/DelombokTask.java | 51 +++++- 6 files changed, 276 insertions(+), 215 deletions(-) delete mode 100644 src/delombok/lombok/delombok/CommentPreservingParser.java create mode 100644 src/delombok/lombok/delombok/DelombokResult.java (limited to 'src') diff --git a/src/delombok/lombok/delombok/CommentCollectingScanner.java b/src/delombok/lombok/delombok/CommentCollectingScanner.java index 2552cbf7..c930bc62 100644 --- a/src/delombok/lombok/delombok/CommentCollectingScanner.java +++ b/src/delombok/lombok/delombok/CommentCollectingScanner.java @@ -25,7 +25,7 @@ import java.nio.CharBuffer; import lombok.delombok.Comment.EndConnection; import lombok.delombok.Comment.StartConnection; -import lombok.delombok.CommentPreservingParser.Comments; +import lombok.delombok.Delombok.Comments; import com.sun.tools.javac.parser.Scanner; import com.sun.tools.javac.util.Context; diff --git a/src/delombok/lombok/delombok/CommentPreservingParser.java b/src/delombok/lombok/delombok/CommentPreservingParser.java deleted file mode 100644 index 87c02dcd..00000000 --- a/src/delombok/lombok/delombok/CommentPreservingParser.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * 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.delombok; - -import java.io.IOException; -import java.io.Writer; -import java.util.Collections; -import java.util.Date; - -import javax.annotation.processing.Messager; -import javax.lang.model.element.AnnotationMirror; -import javax.lang.model.element.AnnotationValue; -import javax.lang.model.element.Element; -import javax.tools.DiagnosticListener; -import javax.tools.JavaFileObject; -import javax.tools.Diagnostic.Kind; - -import lombok.javac.DeleteLombokAnnotations; -import lombok.javac.JavacTransformer; - -import com.sun.tools.javac.main.JavaCompiler; -import com.sun.tools.javac.main.OptionName; -import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; -import com.sun.tools.javac.util.Context; -import com.sun.tools.javac.util.List; -import com.sun.tools.javac.util.Options; - -public class CommentPreservingParser { - private final String encoding; - private boolean deleteLombokAnnotations = false; - private DiagnosticListener diagnostics = null; - - public CommentPreservingParser() { - this("utf-8"); - } - - public CommentPreservingParser(String encoding) { - this.encoding = encoding; - } - - public void setDeleteLombokAnnotations(boolean deleteLombokAnnotations) { - this.deleteLombokAnnotations = deleteLombokAnnotations; - } - - public void setDiagnosticsListener(DiagnosticListener diagnostics) { - this.diagnostics = diagnostics; - } - - public ParseResult parse(JavaFileObject source, boolean forceProcessing) throws IOException { - return doParse(source, forceProcessing); - } - - public ParseResult parse(String fileName, boolean forceProcessing) throws IOException { - return doParse(fileName, forceProcessing); - } - - private ParseResult doParse(Object source, boolean forceProcessing) throws IOException { - Context context = new Context(); - - Options.instance(context).put(OptionName.ENCODING, encoding); - - if (diagnostics != null) context.put(DiagnosticListener.class, diagnostics); - - CommentCollectingScanner.Factory.preRegister(context); - - JavaCompiler compiler = new JavaCompiler(context) { - @Override - protected boolean keepComments() { - return true; - } - }; - - compiler.genEndPos = true; - - Comments comments = new Comments(); - context.put(Comments.class, comments); - if (deleteLombokAnnotations) context.put(DeleteLombokAnnotations.class, new DeleteLombokAnnotations(true)); - - comments.comments = List.nil(); - - JCCompilationUnit cu; - if (source instanceof JavaFileObject) { - cu = compiler.parse((JavaFileObject) source); - } else { - @SuppressWarnings("deprecation") - JCCompilationUnit unit = compiler.parse((String)source); - cu = unit; - } - - boolean changed = new JavacTransformer(messager).transform(context, Collections.singleton(cu)); - return new ParseResult(comments.comments, cu, forceProcessing || changed); - } - - private static final Messager messager = new Messager() { - @Override public void printMessage(Kind kind, CharSequence msg) { - System.out.printf("%s: %s\n", kind, msg); - } - - @Override public void printMessage(Kind kind, CharSequence msg, Element e) { - System.out.printf("%s: %s\n", kind, msg); - } - - @Override public void printMessage(Kind kind, CharSequence msg, Element e, AnnotationMirror a) { - System.out.printf("%s: %s\n", kind, msg); - } - - @Override public void printMessage(Kind kind, CharSequence msg, Element e, AnnotationMirror a, AnnotationValue v) { - System.out.printf("%s: %s\n", kind, msg); - } - }; - - static class Comments { - List comments = List.nil(); - - void add(Comment comment) { - comments = comments.append(comment); - } - } - - public static class ParseResult { - private final List comments; - private final JCCompilationUnit compilationUnit; - private final boolean changed; - - private ParseResult(List comments, JCCompilationUnit compilationUnit, boolean changed) { - this.comments = comments; - this.compilationUnit = compilationUnit; - this.changed = changed; - } - - public void print(Writer out) throws IOException { - if (!changed) { - JavaFileObject sourceFile = compilationUnit.getSourceFile(); - if (sourceFile != null) { - out.write(sourceFile.getCharContent(true).toString()); - return; - } - } - - out.write("// Generated by delombok at "); - out.write(String.valueOf(new Date())); - out.write(System.getProperty("line.separator")); - - compilationUnit.accept(new PrettyCommentsPrinter(out, compilationUnit, comments)); - } - - public boolean isChanged() { - return changed; - } - } -} diff --git a/src/delombok/lombok/delombok/Delombok.java b/src/delombok/lombok/delombok/Delombok.java index 56994f78..6866d97f 100644 --- a/src/delombok/lombok/delombok/Delombok.java +++ b/src/delombok/lombok/delombok/Delombok.java @@ -30,16 +30,33 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintStream; import java.io.Writer; +import java.net.URI; import java.nio.charset.Charset; import java.nio.charset.UnsupportedCharsetException; import java.util.ArrayList; +import java.util.Collections; +import java.util.IdentityHashMap; +import java.util.LinkedHashMap; import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import javax.annotation.processing.Messager; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.Element; import javax.tools.DiagnosticListener; import javax.tools.JavaFileObject; +import javax.tools.Diagnostic.Kind; -import lombok.delombok.CommentPreservingParser.ParseResult; +import lombok.javac.DeleteLombokAnnotations; +import lombok.javac.JavacTransformer; +import com.sun.tools.javac.main.JavaCompiler; +import com.sun.tools.javac.main.OptionName; +import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; +import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.Options; import com.zwitserloot.cmdreader.CmdReader; import com.zwitserloot.cmdreader.Description; import com.zwitserloot.cmdreader.Excludes; @@ -50,16 +67,24 @@ import com.zwitserloot.cmdreader.Shorthand; public class Delombok { private Charset charset = Charset.defaultCharset(); - private CommentPreservingParser parser = new CommentPreservingParser(); + private Context context = new Context(); + private Writer presetWriter; - { - parser.setDeleteLombokAnnotations(true); + public void setWriter(Writer writer) { + this.presetWriter = writer; + } + + public Delombok() { + context.put(DeleteLombokAnnotations.class, new DeleteLombokAnnotations(true)); } private PrintStream feedback = System.err; private boolean verbose; private boolean noCopy; private boolean force = false; + private String classpath, sourcepath; + private LinkedHashMap fileToBase = new LinkedHashMap(); + private List filesToParse = new ArrayList(); /** If null, output to standard out. */ private File output = null; @@ -88,6 +113,14 @@ public class Delombok { @Mandatory(onlyIfNot={"print", "help"}) private String target; + @Shorthand("c") + @Description("Classpath (analogous to javac -cp option)") + private String classpath; + + @Shorthand("s") + @Description("Sourcepath (analogous to javac -sourcepath option)") + private String sourcepath; + @Description("Files to delombok. Provide either a file, or a directory. If you use a directory, all files in it (recursive) are delombok-ed") @Sequential private List input = new ArrayList(); @@ -138,21 +171,29 @@ public class Delombok { if (args.verbose) delombok.setVerbose(true); if (args.nocopy) delombok.setNoCopy(true); - if (args.print) delombok.setOutputToStandardOut(); - else delombok.setOutput(new File(args.target)); + if (args.print) { + delombok.setOutputToStandardOut(); + } else { + delombok.setOutput(new File(args.target)); + } + + if (args.classpath != null) delombok.setClasspath(args.classpath); + if (args.sourcepath != null) delombok.setSourcepath(args.sourcepath); for (String in : args.input) { try { File f = new File(in); if (f.isFile()) { - delombok.delombok(f.getParentFile(), f.getName()); + delombok.addFile(f.getParentFile(), f.getName()); } else if (f.isDirectory()) { - delombok.delombok(f); + delombok.addDirectory(f); } else if (!f.exists()) { if (!args.quiet) System.err.println("WARNING: does not exist - skipping: " + f); } else { if (!args.quiet) System.err.println("WARNING: not a standard file or directory - skipping: " + f); } + + delombok.delombok(); } catch (Exception e) { if (!args.quiet) { String msg = e.getMessage(); @@ -168,12 +209,12 @@ public class Delombok { } public void setCharset(String charsetName) throws UnsupportedCharsetException { + charset = Charset.forName(charsetName); } - public void setDiagnosticsListener(DiagnosticListener diagnostics) { - parser.setDiagnosticsListener(diagnostics); + if (diagnostics != null) context.put(DiagnosticListener.class, diagnostics); } public void setForceProcess(boolean force) { @@ -184,6 +225,14 @@ public class Delombok { this.feedback = feedback; } + public void setClasspath(String classpath) { + this.classpath = classpath; + } + + public void setSourcepath(String sourcepath) { + this.sourcepath = sourcepath; + } + public void setVerbose(boolean verbose) { this.verbose = verbose; } @@ -204,15 +253,15 @@ public class Delombok { this.output = null; } - public void delombok(File base) throws IOException { - delombok0(false, base, "", 0); + public void addDirectory(File base) throws IOException { + addDirectory0(false, base, "", 0); } - public void process(boolean copy, File base, String name) throws IOException { + public void addDirectory1(boolean copy, File base, String name) throws IOException { File f = new File(base, name); if (f.isFile()) { String extension = getExtension(f); - if (extension.equals("java")) delombok(base, name); + if (extension.equals("java")) addFile(base, name); else if (extension.equals("class")) skipClass(name); else copy(copy, base, name); } else if (!f.exists()) { @@ -222,7 +271,7 @@ public class Delombok { } } - private void delombok0(boolean inHiddenDir, File base, String suffix, int loop) throws IOException { + private void addDirectory0(boolean inHiddenDir, File base, String suffix, int loop) throws IOException { File dir = suffix.isEmpty() ? base : new File(base, suffix); if (dir.isDirectory()) { @@ -236,7 +285,7 @@ public class Delombok { feedback.printf("Only processing java files (not copying non-java files) in %s because it's a hidden directory.\n", canonical(dir)); } for (File f : list) { - delombok0(inHiddenDir || thisDirIsHidden, base, suffix + (suffix.isEmpty() ? "" : File.separator) + f.getName(), loop + 1); + addDirectory0(inHiddenDir || thisDirIsHidden, base, suffix + (suffix.isEmpty() ? "" : File.separator) + f.getName(), loop + 1); } } else { if (!thisDirIsHidden && !noCopy && !inHiddenDir && output != null && !suffix.isEmpty()) { @@ -247,7 +296,7 @@ public class Delombok { } } } else { - process(!inHiddenDir && !noCopy, base, suffix); + addDirectory1(!inHiddenDir && !noCopy, base, suffix); } } @@ -288,34 +337,96 @@ public class Delombok { } } - public void delombok(JavaFileObject file, Writer writer) throws IOException { - ParseResult result = parser.parse(file, force); - result.print(writer); + public void addFile(File base, String fileName) throws IOException { + if (output != null && canonical(base).equals(canonical(output))) throw new IOException( + "DELOMBOK: Output file and input file refer to the same filesystem location. Specify a separate path for output."); + + File f = new File(base, fileName); + filesToParse.add(f); + fileToBase.put(f, base); } - public void delombok(String file, Writer writer) throws IOException { - ParseResult result = parser.parse(file, force); - result.print(writer); + private static com.sun.tools.javac.util.List toJavacList(List list) { + com.sun.tools.javac.util.List out = com.sun.tools.javac.util.List.nil(); + ListIterator li = list.listIterator(list.size()); + while (li.hasPrevious()) out = out.prepend(li.previous()); + return out; } - public void delombok(File base, String fileName) throws IOException { - if (output != null && canonical(base).equals(canonical(output))) throw new IOException( - "DELOMBOK: Output file and input file refer to the same filesystem location. Specify a separate path for output."); + public boolean delombok() throws IOException { + Options options = Options.instance(context); + options.put(OptionName.ENCODING, charset.name()); + if (classpath != null) options.put(OptionName.CLASSPATH, classpath); + if (sourcepath != null) options.put(OptionName.SOURCEPATH, sourcepath); + CommentCollectingScanner.Factory.preRegister(context); - ParseResult result = parser.parse(new File(base, fileName).getAbsolutePath(), force); + JavaCompiler compiler = new JavaCompiler(context); + compiler.keepComments = true; + compiler.genEndPos = true; - if (verbose) feedback.printf("File: %s [%s]\n", fileName, result.isChanged() ? "delombok-ed" : "unchanged"); + List roots = new ArrayList(); + Map commentsMap = new IdentityHashMap(); + Map baseMap = new IdentityHashMap(); + for (File fileToParse : filesToParse) { + Comments comments = new Comments(); + context.put(Comments.class, comments); + + @SuppressWarnings("deprecation") + JCCompilationUnit unit = compiler.parse(fileToParse.getAbsolutePath()); + + commentsMap.put(unit, comments); + baseMap.put(unit, fileToBase.get(fileToParse)); + roots.add(unit); + } - Writer rawWriter = output == null ? createStandardOutWriter() : createFileWriter(output, fileName); - BufferedWriter writer = new BufferedWriter(rawWriter); + if (compiler.errorCount() > 0) return false; + compiler.enterTrees(toJavacList(roots)); - try { - result.print(writer); - } finally { - writer.close(); + for (JCCompilationUnit unit : roots) { + boolean changed = new JavacTransformer(messager).transform(context, Collections.singleton(unit)); + DelombokResult result = new DelombokResult(commentsMap.get(unit).comments, unit, force || changed); + if (verbose) feedback.printf("File: %s [%s]\n", unit.sourcefile.getName(), result.isChanged() ? "delomboked" : "unchanged"); + Writer rawWriter; + if (presetWriter != null) rawWriter = presetWriter; + else if (output == null) rawWriter = createStandardOutWriter(); + else rawWriter = createFileWriter(output, baseMap.get(unit), unit.sourcefile.toUri()); + BufferedWriter writer = new BufferedWriter(rawWriter); + try { + result.print(writer); + } finally { + writer.close(); + } + } + + return true; + } + + public static class Comments { + public com.sun.tools.javac.util.List comments = com.sun.tools.javac.util.List.nil(); + + void add(Comment comment) { + comments = comments.append(comment); } } + private static final Messager messager = new Messager() { + @Override public void printMessage(Kind kind, CharSequence msg) { + System.out.printf("%s: %s\n", kind, msg); + } + + @Override public void printMessage(Kind kind, CharSequence msg, Element e) { + System.out.printf("%s: %s\n", kind, msg); + } + + @Override public void printMessage(Kind kind, CharSequence msg, Element e, AnnotationMirror a) { + System.out.printf("%s: %s\n", kind, msg); + } + + @Override public void printMessage(Kind kind, CharSequence msg, Element e, AnnotationMirror a, AnnotationValue v) { + System.out.printf("%s: %s\n", kind, msg); + } + }; + private static String canonical(File dir) { try { return dir.getCanonicalPath(); @@ -330,8 +441,15 @@ public class Delombok { return idx == -1 ? "" : name.substring(idx+1); } - private Writer createFileWriter(File base, String fileName) throws IOException { - File outFile = new File(base, fileName); + private Writer createFileWriter(File outBase, File inBase, URI file) throws IOException { + URI relative = inBase.toURI().relativize(file); + File outFile; + if (relative.isAbsolute()) { + outFile = new File(outBase, new File(relative).getName()); + } else { + outFile = new File(outBase, relative.getPath()); + } + outFile.getParentFile().mkdirs(); FileOutputStream out = new FileOutputStream(outFile); return createUnicodeEscapeWriter(out); diff --git a/src/delombok/lombok/delombok/DelombokResult.java b/src/delombok/lombok/delombok/DelombokResult.java new file mode 100644 index 00000000..717a3bf1 --- /dev/null +++ b/src/delombok/lombok/delombok/DelombokResult.java @@ -0,0 +1,62 @@ +/* + * 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.delombok; + +import java.io.IOException; +import java.io.Writer; +import java.util.Date; + +import javax.tools.JavaFileObject; + +import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; +import com.sun.tools.javac.util.List; + +public class DelombokResult { + private final List comments; + private final JCCompilationUnit compilationUnit; + private final boolean changed; + + public DelombokResult(List comments, JCCompilationUnit compilationUnit, boolean changed) { + this.comments = comments; + this.compilationUnit = compilationUnit; + this.changed = changed; + } + + public void print(Writer out) throws IOException { + if (!changed) { + JavaFileObject sourceFile = compilationUnit.getSourceFile(); + if (sourceFile != null) { + out.write(sourceFile.getCharContent(true).toString()); + return; + } + } + + out.write("// Generated by delombok at "); + out.write(String.valueOf(new Date())); + out.write(System.getProperty("line.separator")); + + compilationUnit.accept(new PrettyCommentsPrinter(out, compilationUnit, comments)); + } + + public boolean isChanged() { + return changed; + } +} \ No newline at end of file diff --git a/src/delombok/lombok/delombok/PrettyCommentsPrinter.java b/src/delombok/lombok/delombok/PrettyCommentsPrinter.java index 55c0e1bc..16c06f50 100644 --- a/src/delombok/lombok/delombok/PrettyCommentsPrinter.java +++ b/src/delombok/lombok/delombok/PrettyCommentsPrinter.java @@ -670,9 +670,11 @@ public class PrettyCommentsPrinter extends JCTree.Visitor { public void visitMethodDef(JCMethodDecl tree) { try { + boolean isConstructor = tree.name == tree.name.table.fromChars("".toCharArray(), 0, 6); // when producing source output, omit anonymous constructors - if (tree.name == tree.name.table.fromChars("".toCharArray(), 0, 6) && - enclClassName == null) return; + if (isConstructor && enclClassName == null) return; + boolean isGeneratedConstructor = isConstructor && ((tree.mods.flags & Flags.GENERATEDCONSTR) != 0); + if (isGeneratedConstructor) return; println(); align(); printDocComment(tree); printExpr(tree.mods); @@ -1442,9 +1444,13 @@ public class PrettyCommentsPrinter extends JCTree.Visitor { print("@"); printExpr(tree.annotationType); if (tree.args.nonEmpty()) { - print("("); - printExprs(tree.args); - print(")"); + print("("); + if (tree.args.length() == 1 && tree.args.get(0) instanceof JCAssign) { + JCExpression lhs = ((JCAssign)tree.args.get(0)).lhs; + if (lhs instanceof JCIdent && ((JCIdent)lhs).name.toString().equals("value")) tree.args = List.of(((JCAssign)tree.args.get(0)).rhs); + } + printExprs(tree.args); + print(")"); } } catch (IOException e) { throw new UncheckedIOException(e); diff --git a/src/delombok/lombok/delombok/ant/DelombokTask.java b/src/delombok/lombok/delombok/ant/DelombokTask.java index 4a40b874..40c3c63e 100644 --- a/src/delombok/lombok/delombok/ant/DelombokTask.java +++ b/src/delombok/lombok/delombok/ant/DelombokTask.java @@ -32,14 +32,55 @@ import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Task; import org.apache.tools.ant.types.FileSet; import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.types.Reference; import org.apache.tools.ant.types.resources.FileResource; public class DelombokTask extends Task { private File fromDir, toDir; + private Path classpath; + private Path sourcepath; private boolean verbose; private String encoding; private Path path; + public void setClasspath(Path classpath) { + if (this.classpath == null) { + this.classpath = classpath; + } else { + this.classpath.append(classpath); + } + } + + public Path createClasspath() { + if (classpath == null) { + classpath = new Path(getProject()); + } + return classpath.createPath(); + } + + public void setClasspathRef(Reference r) { + createClasspath().setRefid(r); + } + + public void setSourcepath(Path sourcepath) { + if (this.sourcepath == null) { + this.sourcepath = sourcepath; + } else { + this.sourcepath.append(sourcepath); + } + } + + public Path createSourcepath() { + if (sourcepath == null) { + sourcepath = new Path(getProject()); + } + return sourcepath.createPath(); + } + + public void setSourcepathRef(Reference r) { + createSourcepath().setRefid(r); + } + public void setFrom(File dir) { this.fromDir = dir; } @@ -75,9 +116,12 @@ public class DelombokTask extends Task { throw new BuildException("Unknown charset: " + encoding, getLocation()); } + if (classpath != null) delombok.setClasspath(classpath.toString()); + if (sourcepath != null) delombok.setSourcepath(sourcepath.toString()); + delombok.setOutput(toDir); try { - if (fromDir != null) delombok.delombok(fromDir); + if (fromDir != null) delombok.addDirectory(fromDir); else { Iterator it = path.iterator(); while (it.hasNext()) { @@ -85,12 +129,13 @@ public class DelombokTask extends Task { File baseDir = fileResource.getBaseDir(); if (baseDir == null) { File file = fileResource.getFile(); - delombok.process(false, file.getParentFile(), file.getName()); + delombok.addFile(file.getParentFile(), file.getName()); } else { - delombok.process(false, baseDir, fileResource.getName()); + delombok.addFile(baseDir, fileResource.getName()); } } } + delombok.delombok(); } catch (IOException e) { throw new BuildException("I/O problem during delombok", e, getLocation()); } -- cgit From f1cf083890c223bf7245190264438740ee724bc9 Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Wed, 3 Nov 2010 00:42:58 +0100 Subject: val in java, including tests and javac resolution utilities. --- src/core/lombok/javac/JavacResolution.java | 480 ++++++++++++++++++++++++++ src/core/lombok/javac/TreeMirrorMaker.java | 50 +++ src/core/lombok/javac/handlers/HandleVal.java | 104 ++++++ 3 files changed, 634 insertions(+) create mode 100644 src/core/lombok/javac/JavacResolution.java create mode 100644 src/core/lombok/javac/TreeMirrorMaker.java create mode 100644 src/core/lombok/javac/handlers/HandleVal.java (limited to 'src') diff --git a/src/core/lombok/javac/JavacResolution.java b/src/core/lombok/javac/JavacResolution.java new file mode 100644 index 00000000..6c60b11e --- /dev/null +++ b/src/core/lombok/javac/JavacResolution.java @@ -0,0 +1,480 @@ +package lombok.javac; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.lang.reflect.Field; +import java.util.ArrayDeque; +import java.util.Map; + +import javax.lang.model.type.TypeKind; +import javax.tools.DiagnosticListener; + +import com.sun.tools.javac.code.BoundKind; +import com.sun.tools.javac.code.Symbol.TypeSymbol; +import com.sun.tools.javac.code.Type.CapturedType; +import com.sun.tools.javac.code.Type.ClassType; +import com.sun.tools.javac.code.Type; +import com.sun.tools.javac.code.TypeTags; +import com.sun.tools.javac.comp.Attr; +import com.sun.tools.javac.comp.AttrContext; +import com.sun.tools.javac.comp.Enter; +import com.sun.tools.javac.comp.Env; +import com.sun.tools.javac.comp.MemberEnter; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.JCBlock; +import com.sun.tools.javac.tree.JCTree.JCClassDecl; +import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; +import com.sun.tools.javac.tree.JCTree.JCExpression; +import com.sun.tools.javac.tree.JCTree.JCMethodDecl; +import com.sun.tools.javac.tree.JCTree.JCVariableDecl; +import com.sun.tools.javac.tree.TreeMaker; +import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.Log; + +public class JavacResolution { + private final Attr attr; + private final LogDisabler logDisabler; + + public JavacResolution(Context context) { + attr = Attr.instance(context); + logDisabler = new LogDisabler(context); + } + /** + * During resolution, the resolver will emit resolution errors, but without appropriate file names and line numbers. If these resolution errors stick around + * then they will be generated AGAIN, this time with proper names and line numbers, at the end. Therefore, we want to suppress the logger. + */ + private static final class LogDisabler { + private final Log log; + private static final Field errWriterField, warnWriterField, noticeWriterField, dumpOnErrorField, promptOnErrorField, diagnosticListenerField; + private PrintWriter errWriter, warnWriter, noticeWriter; + private Boolean dumpOnError, promptOnError; + private DiagnosticListener contextDiagnosticListener, logDiagnosticListener; + private final Context context; + + // If this is true, the fields changed. Better to print weird error messages than to fail outright. + private static final boolean dontBother; + + static { + boolean z; + Field a = null, b = null, c = null, d = null, e = null, f = null; + try { + a = Log.class.getDeclaredField("errWriter"); + b = Log.class.getDeclaredField("warnWriter"); + c = Log.class.getDeclaredField("noticeWriter"); + d = Log.class.getDeclaredField("dumpOnError"); + e = Log.class.getDeclaredField("promptOnError"); + f = Log.class.getDeclaredField("diagListener"); + z = false; + a.setAccessible(true); + b.setAccessible(true); + c.setAccessible(true); + d.setAccessible(true); + e.setAccessible(true); + f.setAccessible(true); + } catch (Exception x) { + z = true; + } + + errWriterField = a; + warnWriterField = b; + noticeWriterField = c; + dumpOnErrorField = d; + promptOnErrorField = e; + diagnosticListenerField = f; + dontBother = z; + } + + LogDisabler(Context context) { + this.log = Log.instance(context); + this.context = context; + } + + boolean disableLoggers() { + contextDiagnosticListener = context.get(DiagnosticListener.class); + context.put(DiagnosticListener.class, (DiagnosticListener) null); + if (dontBother) return false; + boolean dontBotherInstance = false; + + PrintWriter dummyWriter = new PrintWriter(new OutputStream() { + @Override public void write(int b) throws IOException { + // Do nothing on purpose + } + }); + + if (!dontBotherInstance) try { + errWriter = (PrintWriter) errWriterField.get(log); + errWriterField.set(log, dummyWriter); + } catch (Exception e) { + dontBotherInstance = true; + } + + if (!dontBotherInstance) try { + warnWriter = (PrintWriter) warnWriterField.get(log); + warnWriterField.set(log, dummyWriter); + } catch (Exception e) { + dontBotherInstance = true; + } + + if (!dontBotherInstance) try { + noticeWriter = (PrintWriter) noticeWriterField.get(log); + noticeWriterField.set(log, dummyWriter); + } catch (Exception e) { + dontBotherInstance = true; + } + + if (!dontBotherInstance) try { + dumpOnError = (Boolean) dumpOnErrorField.get(log); + dumpOnErrorField.set(log, false); + } catch (Exception e) { + dontBotherInstance = true; + } + + if (!dontBotherInstance) try { + promptOnError = (Boolean) promptOnErrorField.get(log); + promptOnErrorField.set(log, false); + } catch (Exception e) { + dontBotherInstance = true; + } + + if (!dontBotherInstance) try { + logDiagnosticListener = (DiagnosticListener) diagnosticListenerField.get(log); + diagnosticListenerField.set(log, null); + } catch (Exception e) { + dontBotherInstance = true; + } + + if (dontBotherInstance) enableLoggers(); + return !dontBotherInstance; + } + + void enableLoggers() { + if (contextDiagnosticListener != null) { + context.put(DiagnosticListener.class, contextDiagnosticListener); + contextDiagnosticListener = null; + } + if (errWriter != null) try { + errWriterField.set(log, errWriter); + errWriter = null; + } catch (Exception e) {} + + if (warnWriter != null) try { + warnWriterField.set(log, warnWriter); + warnWriter = null; + } catch (Exception e) {} + + if (noticeWriter != null) try { + noticeWriterField.set(log, noticeWriter); + noticeWriter = null; + } catch (Exception e) {} + + if (dumpOnError != null) try { + dumpOnErrorField.set(log, dumpOnError); + dumpOnError = null; + } catch (Exception e) {} + + if (promptOnError != null) try { + promptOnErrorField.set(log, promptOnError); + promptOnError = null; + } catch (Exception e) {} + + if (logDiagnosticListener != null) try { + diagnosticListenerField.set(log, logDiagnosticListener); + logDiagnosticListener = null; + } catch (Exception e) {} + } + } + + private static final class EnvFinder extends JCTree.Visitor { + private Env env = null; + private Enter enter; + private MemberEnter memberEnter; + private JCTree copyAt = null; + + EnvFinder(Context context) { + this.enter = Enter.instance(context); + this.memberEnter = MemberEnter.instance(context); + } + + Env get() { + return env; + } + + JCTree copyAt() { + return copyAt; + } + + @Override public void visitTopLevel(JCCompilationUnit tree) { + if (copyAt != null) return; + env = enter.getTopLevelEnv(tree); + } + + @Override public void visitClassDef(JCClassDecl tree) { + if (copyAt != null) return; + // The commented out one leaves the 'lint' field unset, which causes NPEs during attrib. So, we use the other one. + //env = enter.classEnv((JCClassDecl) tree, env); + env = enter.getClassEnv(tree.sym); + } + + @Override public void visitMethodDef(JCMethodDecl tree) { + if (copyAt != null) return; + env = memberEnter.getMethodEnv(tree, env); + copyAt = tree; + } + + public void visitVarDef(JCVariableDecl tree) { + if (copyAt != null) return; + env = memberEnter.getInitEnv(tree, env); + copyAt = tree; + } + + @Override public void visitBlock(JCBlock tree) { + if (copyAt != null) return; + copyAt = tree; + } + } + +// /** +// * The {@code Env} object primarily tracks legal symbol names. i.e. its the lexical scope. To build it, we need to go from the top and drill down to the current node, +// * updating the {@code Env} object at each step. This TreeVisitor does that. Requires {@code enterTrees} to be called first (this is done before processors run, normally). +// */ +// private static final class EnvChainer extends JCTree.Visitor { +// private Env env = null; +// private Enter enter; +// private MemberEnter memberEnter; +// private Attr attr; +// private JCTree target; +// private boolean blocksAreInitializers; +// +// EnvChainer(Context context) { +// this.enter = Enter.instance(context); +// this.memberEnter = MemberEnter.instance(context); +// this.attr = Attr.instance(context); +// } +// +// Env get() { +// return env; +// } +// +// @Override public void visitTopLevel(JCCompilationUnit tree) { +// env = enter.getTopLevelEnv(tree); +// } +// +// @Override public void visitClassDef(JCClassDecl tree) { +// // The commented out one leaves the 'lint' field unset, which causes NPEs during attrib. So, we use the other one. +// //env = enter.classEnv((JCClassDecl) tree, env); +// env = enter.getClassEnv(tree.sym); +// blocksAreInitializers = true; +// } +// +// @Override public void visitMethodDef(JCMethodDecl tree) { +// env = memberEnter.getMethodEnv(tree, env); +// blocksAreInitializers = false; +// if (tree.body != null) visitBlock(tree.body); +// } +// +// @Override public void visitBlock(JCBlock tree) { +// if (blocksAreInitializers) attr.attribStat(tree, env); +// for (JCStatement stat : tree.stats) { +// if (stat == target) return; +// attr.attribStat(stat, env); +// } +// } +// +// @Override public void visitTree(JCTree tree) { +// // Do nothing +// } +// +// public void setTarget(JCTree target) { +// this.target = target; +// } +// }; + + public Map resolve(JavacNode node) { + ArrayDeque stack = new ArrayDeque(); + + { + JavacNode n = node; + while (n != null) { + stack.push(n.get()); + n = n.up(); + } + } + + logDisabler.disableLoggers(); + try { + EnvFinder finder = new EnvFinder(node.getContext()); + while (!stack.isEmpty()) stack.pop().accept(finder); + + TreeMirrorMaker mirrorMaker = new TreeMirrorMaker(node); + JCTree copy = mirrorMaker.copy(finder.copyAt()); + + attrib(copy, finder.get()); + return mirrorMaker.getOriginalToCopyMap(); + } finally { + logDisabler.enableLoggers(); + } + } + + private void attrib(JCTree tree, Env env) { + if (tree instanceof JCBlock) attr.attribStat(tree, env); + else if (tree instanceof JCMethodDecl) attr.attribStat(((JCMethodDecl)tree).body, env); + else if (tree instanceof JCVariableDecl) attr.attribStat(tree, env); + else throw new IllegalStateException("Called with something that isn't a block, method decl, or variable decl"); + } + +// public void resolveUpTo(JavacNode statementNode) { +// ArrayDeque stack = new ArrayDeque(); +// +// { +// JavacNode n = statementNode; +// while (n != null) { +// stack.push(n.get()); +// n = n.up(); +// } +// } +// +// logDisabler.disableLoggers(); +// try { +// JCTree tree = stack.isEmpty() ? null : stack.pop(); +// while (!stack.isEmpty()) { +// JCTree target = stack.pop(); +// envChainer.setTarget(target); +// tree.accept(envChainer); +// tree = target; +// } +// if (tree != null) { +// envChainer.setTarget(null); +// tree.accept(envChainer); +// } +// +//// System.out.println("ATTRIBSTAT: " + attr.attribStat(statementNode.get(), envChainer.get())); +//// if (statementNode.get() instanceof JCVariableDecl) { +//// System.out.println("Force-tribbing expr"); +//// JCExpression init = ((JCVariableDecl)statementNode.get()).init; +//// System.out.println("ATTRIBEXPR: " + attr.attribExpr(init, envChainer.get(), Type.noType)); +//// System.out.println("TYPE: " + ((JCVariableDecl)statementNode.get()).init.type); +//// } +// } finally { +// logDisabler.enableLoggers(); +// } +// } +// +// public void resolveExpr(JCExpression expr) { +// logDisabler.disableLoggers(); +// try { +// attr.attribExpr(expr, envChainer.get(), Type.noType); +// } finally { +// logDisabler.enableLoggers(); +// } +// } + + public static class TypeNotConvertibleException extends Exception { + public TypeNotConvertibleException(String msg) { + super(msg); + } + } + + public static JCExpression typeToJCTree(Type type, TreeMaker maker, JavacAST ast) throws TypeNotConvertibleException { + return typeToJCTree(type, maker, ast, false); + } + + public static JCExpression createJavaLangObject(TreeMaker maker, JavacAST ast) { + JCExpression out = maker.Ident(ast.toName("java")); + out = maker.Select(out, ast.toName("lang")); + out = maker.Select(out, ast.toName("Object")); + return out; + } + + private static JCExpression typeToJCTree(Type type, TreeMaker maker, JavacAST ast, boolean allowCompound) throws TypeNotConvertibleException { + // NB: There's such a thing as maker.Type(type), but this doesn't work very well; it screws up anonymous classes, captures, and adds an extra prefix dot for some reason too. + // -- so we write our own take on that here. + + if (type.isPrimitive()) return primitiveToJCTree(type.getKind(), maker); + if (type.isErroneous()) throw new TypeNotConvertibleException("Type cannot be resolved"); + + TypeSymbol symbol = type.asElement(); + List generics = type.getTypeArguments(); + + JCExpression replacement = null; + + if (symbol == null) throw new TypeNotConvertibleException("Null or compound type"); + + if (symbol.name.len == 0) { + // Anonymous inner class + if (type instanceof ClassType) { + List ifaces = ((ClassType)type).interfaces_field; + Type supertype = ((ClassType)type).supertype_field; + if (ifaces != null && ifaces.length() == 1) { + return typeToJCTree(ifaces.get(0), maker, ast, allowCompound); + } + if (supertype != null) return typeToJCTree(supertype, maker, ast, allowCompound); + } + throw new TypeNotConvertibleException("Anonymous inner class"); + } + + if (type instanceof CapturedType) { + if (allowCompound) { + if (type.getLowerBound() == null || type.getLowerBound().tag == TypeTags.BOT) { + if (type.getUpperBound().toString().equals("java.lang.Object")) { + return maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null); + } + return maker.Wildcard(maker.TypeBoundKind(BoundKind.EXTENDS), typeToJCTree(type.getUpperBound(), maker, ast, false)); + } else { + return maker.Wildcard(maker.TypeBoundKind(BoundKind.SUPER), typeToJCTree(type.getLowerBound(), maker, ast, false)); + } + } + if (type.getUpperBound() != null) { + return typeToJCTree(type.getUpperBound(), maker, ast, allowCompound); + } + + return createJavaLangObject(maker, ast); + } + + String qName = symbol.getQualifiedName().toString(); + if (qName.isEmpty()) throw new TypeNotConvertibleException("unknown type"); + if (qName.startsWith("<")) throw new TypeNotConvertibleException(qName); + String[] baseNames = symbol.getQualifiedName().toString().split("\\."); + replacement = maker.Ident(ast.toName(baseNames[0])); + for (int i = 1; i < baseNames.length; i++) { + replacement = maker.Select(replacement, ast.toName(baseNames[i])); + } + + if (generics != null && !generics.isEmpty()) { + List args = List.nil(); + for (Type t : generics) args = args.append(typeToJCTree(t, maker, ast, true)); + replacement = maker.TypeApply(replacement, args); + } + + return replacement; + } + + private static JCExpression primitiveToJCTree(TypeKind kind, TreeMaker maker) throws TypeNotConvertibleException { + switch (kind) { + case BYTE: + return maker.TypeIdent(TypeTags.BYTE); + case CHAR: + return maker.TypeIdent(TypeTags.CHAR); + case SHORT: + return maker.TypeIdent(TypeTags.SHORT); + case INT: + return maker.TypeIdent(TypeTags.INT); + case LONG: + return maker.TypeIdent(TypeTags.LONG); + case FLOAT: + return maker.TypeIdent(TypeTags.FLOAT); + case DOUBLE: + return maker.TypeIdent(TypeTags.DOUBLE); + case BOOLEAN: + return maker.TypeIdent(TypeTags.BOOLEAN); + case VOID: + return maker.TypeIdent(TypeTags.VOID); + case NULL: + case NONE: + case OTHER: + default: + throw new TypeNotConvertibleException("Nulltype"); + } + } +} diff --git a/src/core/lombok/javac/TreeMirrorMaker.java b/src/core/lombok/javac/TreeMirrorMaker.java new file mode 100644 index 00000000..1c0b9311 --- /dev/null +++ b/src/core/lombok/javac/TreeMirrorMaker.java @@ -0,0 +1,50 @@ +package lombok.javac; + +import java.util.Collections; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.Map; + +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.TreeCopier; +import com.sun.tools.javac.util.List; + +public class TreeMirrorMaker extends TreeCopier { + private final IdentityHashMap originalToCopy = new IdentityHashMap(); + + public TreeMirrorMaker(JavacNode node) { + super(node.getTreeMaker()); + } + + @Override public T copy(T original) { + T copy = super.copy(original); + originalToCopy.put(original, copy); + return copy; + } + + @Override public T copy(T original, Void p) { + T copy = super.copy(original, p); + originalToCopy.put(original, copy); + return copy; + } + + @Override public List copy(List originals) { + List copies = super.copy(originals); + Iterator it1 = originals.iterator(); + Iterator it2 = copies.iterator(); + while (it1.hasNext()) originalToCopy.put(it1.next(), it2.next()); + return copies; + } + + @Override public List copy(List originals, Void p) { + List copies = super.copy(originals, p); + Iterator it1 = originals.iterator(); + Iterator it2 = copies.iterator(); + while (it1.hasNext()) originalToCopy.put(it1.next(), it2.next()); + return copies; + } + + public Map getOriginalToCopyMap() { + return Collections.unmodifiableMap(originalToCopy); + } +} diff --git a/src/core/lombok/javac/handlers/HandleVal.java b/src/core/lombok/javac/handlers/HandleVal.java new file mode 100644 index 00000000..a6f22093 --- /dev/null +++ b/src/core/lombok/javac/handlers/HandleVal.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.handlers; + +import lombok.javac.JavacASTAdapter; +import lombok.javac.JavacASTVisitor; +import lombok.javac.JavacNode; +import lombok.javac.JavacResolution; + +import org.mangosdk.spi.ProviderFor; + +import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.code.Type; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.JCEnhancedForLoop; +import com.sun.tools.javac.tree.JCTree.JCExpression; +import com.sun.tools.javac.tree.JCTree.JCVariableDecl; + +@ProviderFor(JavacASTVisitor.class) +public class HandleVal extends JavacASTAdapter { + @Override public boolean isResolutionBased() { + return true; + } + + @Override public void visitLocal(JavacNode localNode, JCVariableDecl local) { + if (local.vartype != null && local.vartype.toString().equals("val")) { + JCExpression rhsOfEnhancedForLoop = null; + if (local.init == null) { + JCTree parentRaw = localNode.directUp().get(); + if (parentRaw instanceof JCEnhancedForLoop) { + JCEnhancedForLoop efl = (JCEnhancedForLoop) parentRaw; + if (efl.var == local) rhsOfEnhancedForLoop = efl.expr; + } + } + + if (rhsOfEnhancedForLoop == null && local.init == null) { + localNode.addError("'val' on a local variable requires an initializer expression"); + return; + } + + local.mods.flags |= Flags.FINAL; + JCExpression oldVarType = local.vartype; + local.vartype = JavacResolution.createJavaLangObject(localNode.getTreeMaker(), localNode.getAst()); + + Type type; + try { + if (rhsOfEnhancedForLoop == null) { + if (local.init.type == null) { + JavacResolution resolver = new JavacResolution(localNode.getContext()); + type = ((JCExpression) resolver.resolve(localNode).get(local.init)).type; + } else { + type = local.init.type; + } + } else { + if (rhsOfEnhancedForLoop.type == null) { + JavacResolution resolver = new JavacResolution(localNode.getContext()); + type = ((JCExpression) resolver.resolve(localNode.directUp()).get(rhsOfEnhancedForLoop)).type; + } else { + type = rhsOfEnhancedForLoop.type; + } + } + + try { + JCExpression replacement; + + if (rhsOfEnhancedForLoop != null) { + localNode.addError("For now 'val' cannot be used in enhanced for loops. Fixing this is a high priority lombok issue though!"); + return; + // TODO: Fix enhanced for loops - then uncomment a bunch of lines in test/transform/resource/*/ValInFor.java + } + replacement = JavacResolution.typeToJCTree(type, localNode.getTreeMaker(), localNode.getAst()); + if (replacement != null) local.vartype = replacement; + else local.vartype = oldVarType; + } catch (JavacResolution.TypeNotConvertibleException e) { + localNode.addError("Cannot use 'val' here because initializer expression does not have a representable type: " + e.getMessage()); + local.vartype = oldVarType; + } + } catch (RuntimeException e) { + local.vartype = oldVarType; + throw e; + } + } + // TODO search for val decls in either kind of for. + } +} -- cgit From f6b60b0cae7f8af2e4598f2bbbd72839e193a36b Mon Sep 17 00:00:00 2001 From: Roel Spilker Date: Wed, 3 Nov 2010 02:13:28 +0100 Subject: Intial support for @Log, for now only slf4j --- .../eclipse/handlers/EclipseHandlerUtil.java | 10 +- .../lombok/eclipse/handlers/HandleSlf4jLog.java | 158 +++++++++++++++++++++ .../eclipse/handlers/HandleSynchronized.java | 2 +- src/core/lombok/javac/handlers/HandleSlf4jLog.java | 114 +++++++++++++++ .../lombok/javac/handlers/HandleSynchronized.java | 2 +- .../lombok/javac/handlers/JavacHandlerUtil.java | 15 +- src/core/lombok/slf4j/Log.java | 34 +++++ 7 files changed, 331 insertions(+), 4 deletions(-) create mode 100644 src/core/lombok/eclipse/handlers/HandleSlf4jLog.java create mode 100644 src/core/lombok/javac/handlers/HandleSlf4jLog.java create mode 100644 src/core/lombok/slf4j/Log.java (limited to 'src') diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java index 5005752b..29937279 100644 --- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java +++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java @@ -406,9 +406,17 @@ public class EclipseHandlerUtil { /** * Inserts a field into an existing type. The type must represent a {@code TypeDeclaration}. + * The field carries the @{@link SuppressWarnings}("all") annotation. */ - public static void injectField(EclipseNode type, FieldDeclaration field) { + public static void injectFieldSuppressWarnings(EclipseNode type, FieldDeclaration field) { field.annotations = createSuppressWarningsAll(field, field.annotations); + injectField(type, field); + } + + /** + * Inserts a field into an existing type. The type must represent a {@code TypeDeclaration}. + */ + public static void injectField(EclipseNode type, FieldDeclaration field) { TypeDeclaration parent = (TypeDeclaration) type.get(); if (parent.fields == null) { diff --git a/src/core/lombok/eclipse/handlers/HandleSlf4jLog.java b/src/core/lombok/eclipse/handlers/HandleSlf4jLog.java new file mode 100644 index 00000000..c9046260 --- /dev/null +++ b/src/core/lombok/eclipse/handlers/HandleSlf4jLog.java @@ -0,0 +1,158 @@ +/* + * Copyright © 2009 Reinier Zwitserloot, Roel Spilker and Robbert Jan Grootjans. + * + * 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.handlers; + +import static lombok.eclipse.Eclipse.fromQualifiedName; +import static lombok.eclipse.handlers.EclipseHandlerUtil.*; + +import java.lang.reflect.Modifier; +import java.util.Arrays; + +import lombok.core.AnnotationValues; +import lombok.core.AST.Kind; +import lombok.eclipse.Eclipse; +import lombok.eclipse.EclipseAnnotationHandler; +import lombok.eclipse.EclipseNode; +import lombok.eclipse.handlers.EclipseHandlerUtil.MemberExistsResult; +import lombok.slf4j.Log; + +import org.eclipse.jdt.internal.compiler.ast.Annotation; +import org.eclipse.jdt.internal.compiler.ast.Expression; +import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; +import org.eclipse.jdt.internal.compiler.ast.MessageSend; +import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; +import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; +import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.ecli