# HG changeset patch # User Jaroslav Tulach # Date 1432031451 -7200 # Node ID fb17e716b03c9b7a22fc45864f3137c95205f107 # Parent c1bb8028ff63207807a8180107e8c0b5fa2dd32a The annotation processor should verify proper use of @Child annotation during compilation time and prevent usage of final. diff -r c1bb8028ff63 -r fb17e716b03c graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/processor/Compile.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/processor/Compile.java Tue May 19 12:30:51 2015 +0200 @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.dsl.test.processor; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import javax.tools.Diagnostic; +import javax.tools.DiagnosticListener; +import javax.tools.FileObject; +import javax.tools.ForwardingJavaFileManager; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject; +import javax.tools.JavaFileObject.Kind; +import javax.tools.SimpleJavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.ToolProvider; +import static org.junit.Assert.*; + +/** + * + * @author Jaroslav Tulach + */ +final class Compile implements DiagnosticListener { + private final List> errors = new ArrayList<>(); + private final Map classes; + private final String sourceLevel; + + private Compile(Class processor, String code, String sl) { + this.sourceLevel = sl; + classes = compile(processor, code); + } + + /** + * Performs compilation of given HTML page and associated Java code. + */ + public static Compile create(Class processor, String code) { + return new Compile(processor, code, "1.7"); + } + + /** Checks for given class among compiled resources. */ + public byte[] get(String res) { + return classes.get(res); + } + + /** + * Obtains errors created during compilation. + */ + public List> getErrors() { + List> err; + err = new ArrayList<>(); + for (Diagnostic diagnostic : errors) { + if (diagnostic.getKind() == Diagnostic.Kind.ERROR) { + err.add(diagnostic); + } + } + return err; + } + + private Map compile(Class processor, final String code) { + StandardJavaFileManager sjfm = ToolProvider.getSystemJavaCompiler().getStandardFileManager(this, null, null); + + final Map class2BAOS; + class2BAOS = new HashMap<>(); + + JavaFileObject file = new SimpleJavaFileObject(URI.create("mem://mem"), Kind.SOURCE) { + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) { + return code; + } + }; + + JavaFileManager jfm = new ForwardingJavaFileManager(sjfm) { + @Override + public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException { + if (kind == Kind.CLASS) { + final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + + class2BAOS.put(className.replace('.', '/') + ".class", buffer); + return new SimpleJavaFileObject(sibling.toUri(), kind) { + @Override + public OutputStream openOutputStream() { + return buffer; + } + }; + } + + if (kind == Kind.SOURCE) { + final String n = className.replace('.', '/') + ".java"; + final URI un; + try { + un = new URI("mem://" + n); + } catch (URISyntaxException ex) { + throw new IOException(ex); + } + return new VirtFO(un/* sibling.toUri() */, kind, n); + } + + throw new IllegalStateException(); + } + + @Override + public boolean isSameFile(FileObject a, FileObject b) { + if (a instanceof VirtFO && b instanceof VirtFO) { + return ((VirtFO) a).getName().equals(((VirtFO) b).getName()); + } + + return super.isSameFile(a, b); + } + + class VirtFO extends SimpleJavaFileObject { + + private final String n; + + public VirtFO(URI uri, Kind kind, String n) { + super(uri, kind); + this.n = n; + } + + private final ByteArrayOutputStream data = new ByteArrayOutputStream(); + + @Override + public OutputStream openOutputStream() { + return data; + } + + @Override + public String getName() { + return n; + } + + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { + data.close(); + return new String(data.toByteArray()); + } + } + }; + List args = Arrays.asList("-source", sourceLevel, "-target", "1.7", // + "-processor", processor.getName()); + + ToolProvider.getSystemJavaCompiler().getTask(null, jfm, this, args, null, Arrays.asList(file)).call(); + + Map result = new HashMap<>(); + + for (Map.Entry e : class2BAOS.entrySet()) { + result.put(e.getKey(), e.getValue().toByteArray()); + } + + return result; + } + + @Override + public void report(Diagnostic diagnostic) { + errors.add(diagnostic); + } + + void assertErrors() { + assertFalse("There are supposed to be some errors", getErrors().isEmpty()); + } + + void assertNoErrors() { + assertTrue("There are supposed to be no errors: " + getErrors(), getErrors().isEmpty()); + } + + void assertError(String expMsg) { + StringBuilder sb = new StringBuilder(); + sb.append("Can't find ").append(expMsg).append(" among:"); + for (Diagnostic e : errors) { + String msg = e.getMessage(Locale.US); + if (msg.contains(expMsg)) { + return; + } + sb.append("\n"); + sb.append(msg); + } + fail(sb.toString()); + } +} diff -r c1bb8028ff63 -r fb17e716b03c graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/processor/TruffleProcessorTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/processor/TruffleProcessorTest.java Tue May 19 12:30:51 2015 +0200 @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.dsl.test.processor; + +import com.oracle.truffle.dsl.processor.verify.VerifyTruffleProcessor; +import java.util.Locale; +import java.util.ServiceLoader; +import javax.annotation.processing.Processor; +import javax.tools.Diagnostic; +import javax.tools.JavaFileObject; +import static org.junit.Assert.*; +import org.junit.Test; + +/** + * Verify errors emitted by the processor. + * + * @author Jaroslav Tulach + */ +public class TruffleProcessorTest { + @Test + public void childCannotBeFinal() throws Exception { + // @formatter:off + String code = "package x.y.z;\n" + + "import com.oracle.truffle.api.nodes.Node;\n" + + "abstract class MyNode extends Node {\n" + + " @Child final MyNode first;\n" + + " MyNode(MyNode n) {\n" + + " this.first = n;\n" + + " };\n" + + "}\n"; + // @formatter:on + + Compile c = Compile.create(VerifyTruffleProcessor.class, code); + c.assertErrors(); + boolean ok = false; + StringBuilder msgs = new StringBuilder(); + for (Diagnostic e : c.getErrors()) { + String msg = e.getMessage(Locale.ENGLISH); + if (msg.contains("cannot be final")) { + ok = true; + } + msgs.append("\n").append(msg); + } + if (!ok) { + fail("Should contain warning about final:" + msgs); + } + } + + @Test + public void workAroundCannonicalDependency() throws Exception { + Class myProc = VerifyTruffleProcessor.class; + assertNotNull(myProc); + StringBuilder sb = new StringBuilder(); + sb.append("Cannot find ").append(myProc); + for (Processor load : ServiceLoader.load(Processor.class)) { + sb.append("Found ").append(load); + if (myProc.isInstance(load)) { + return; + } + } + fail(sb.toString()); + } +} diff -r c1bb8028ff63 -r fb17e716b03c graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/verify/VerifyTruffleProcessor.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/verify/VerifyTruffleProcessor.java Tue May 19 11:54:32 2015 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/verify/VerifyTruffleProcessor.java Tue May 19 12:30:51 2015 +0200 @@ -34,9 +34,9 @@ import javax.tools.Diagnostic.Kind; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.nodes.Node.Child; -@SupportedSourceVersion(SourceVersion.RELEASE_7) -@SupportedAnnotationTypes({"com.oracle.truffle.api.CompilerDirectives.TruffleBoundary"}) +@SupportedAnnotationTypes({"com.oracle.truffle.api.CompilerDirectives.TruffleBoundary", "com.oracle.truffle.api.nodes.Node.Child"}) public class VerifyTruffleProcessor extends AbstractProcessor { @Override public SourceVersion getSupportedSourceVersion() { @@ -113,6 +113,12 @@ scope = null; } } + + for (Element e : roundEnv.getElementsAnnotatedWith(Child.class)) { + if (e.getModifiers().contains(Modifier.FINAL)) { + errorMessage(e, "@Child field cannot be final"); + } + } return false; } diff -r c1bb8028ff63 -r fb17e716b03c mx/suite.py --- a/mx/suite.py Tue May 19 11:54:32 2015 +0200 +++ b/mx/suite.py Tue May 19 12:30:51 2015 +0200 @@ -1008,7 +1008,7 @@ "subDir" : "graal", "sourceDirs" : ["src"], "dependencies" : [ - "com.oracle.truffle.api.dsl", + "com.oracle.truffle.dsl.processor", "JUNIT", ], "checkstyle" : "com.oracle.graal.graph",