jjg@893: /* jjg@893: * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. jjg@893: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. jjg@893: * jjg@893: * This code is free software; you can redistribute it and/or modify it jjg@893: * under the terms of the GNU General Public License version 2 only, as jjg@893: * published by the Free Software Foundation. jjg@893: * jjg@893: * This code is distributed in the hope that it will be useful, but WITHOUT jjg@893: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or jjg@893: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License jjg@893: * version 2 for more details (a copy is included in the LICENSE file that jjg@893: * accompanied this code). jjg@893: * jjg@893: * You should have received a copy of the GNU General Public License version jjg@893: * 2 along with this work; if not, write to the Free Software Foundation, jjg@893: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. jjg@893: * jjg@893: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA jjg@893: * or visit www.oracle.com if you need additional information or have any jjg@893: * questions. jjg@893: */ jjg@893: jjg@893: /** jjg@893: * @test jjg@893: * @bug 7021650 jjg@893: * @summary Fix Context issues darcy@1466: * @library /tools/javac/lib jjg@893: * @build JavacTestingAbstractProcessor T7021650 jjg@893: * @run main T7021650 jjg@893: */ jjg@893: jjg@893: import java.io.*; jjg@893: import java.net.*; jjg@893: import java.util.*; jjg@893: import javax.annotation.processing.*; jjg@893: import javax.lang.model.element.*; jjg@893: import javax.tools.*; jjg@893: jjg@893: import com.sun.tools.javac.comp.Attr; jjg@893: import com.sun.tools.javac.file.JavacFileManager; jjg@893: import com.sun.tools.javac.main.Main; jjg@893: import com.sun.tools.javac.processing.JavacProcessingEnvironment; jjg@893: import com.sun.tools.javac.util.Context; jjg@893: jjg@893: public class T7021650 extends JavacTestingAbstractProcessor { jjg@893: public static void main(String... args) throws Exception { jjg@893: new T7021650().run(); jjg@893: } jjg@893: jjg@893: static File testSrc = new File(System.getProperty("test.src")); jjg@893: static final int MAX_ROUNDS = 3; jjg@893: jjg@893: /** jjg@893: * Perform a compilation with custom factories registered in the context, jjg@893: * and verify that corresponding objects are created in each round. jjg@893: */ jjg@893: void run() throws Exception { jjg@893: Counter demoCounter = new Counter(); jjg@893: Counter myAttrCounter = new Counter(); jjg@893: jjg@893: Context context = new Context(); jjg@893: // Use a custom file manager which creates classloaders for annotation jjg@893: // processors with a sensible delegation parent, so that all instances jjg@893: // of test classes come from the same class loader. This is important jjg@893: // because the test performs class checks on the instances of classes jjg@893: // found in the context for each round or processing. jjg@893: context.put(JavaFileManager.class, new Context.Factory() { jjg@893: public JavaFileManager make(Context c) { jjg@893: return new JavacFileManager(c, true, null) { jjg@893: @Override jjg@893: protected ClassLoader getClassLoader(URL[] urls) { jjg@893: return new URLClassLoader(urls, T7021650.class.getClassLoader()); jjg@893: } jjg@893: }; jjg@893: } jjg@893: }); jjg@893: jjg@893: Demo.preRegister(context, demoCounter); jjg@893: MyAttr.preRegister(context, myAttrCounter); jjg@893: jjg@893: String[] args = { jjg@893: "-d", ".", jjg@893: "-processor", T7021650.class.getName(), jjg@893: "-XprintRounds", jjg@893: new File(testSrc, T7021650.class.getName() + ".java").getPath() jjg@893: }; jjg@893: jjg@893: compile(context, args); jjg@893: jjg@893: // Expect to create Demo for initial round, then MAX_ROUNDS in which jjg@893: // GenX files are generated, then standard final round of processing. jjg@893: checkEqual("demoCounter", demoCounter.count, MAX_ROUNDS + 2); jjg@893: jjg@893: // Expect to create MyAttr for same processing rounds as for Demo, jjg@893: // plus additional context for final compilation. jjg@893: checkEqual("myAttrCounter", myAttrCounter.count, MAX_ROUNDS + 3); jjg@893: } jjg@893: jjg@893: void compile(Context context, String... args) throws Exception { jjg@893: StringWriter sw = new StringWriter(); jjg@893: PrintWriter pw = new PrintWriter(sw); jjg@893: Main m = new Main("javac", pw); jjg@1097: Main.Result res = m.compile(args, context); jjg@893: pw.close(); jjg@893: String out = sw.toString(); jjg@893: if (!out.isEmpty()) jjg@893: System.err.println(out); jjg@1097: if (!res.isOK()) jjg@1097: throw new Exception("compilation failed unexpectedly: result=" + res); jjg@893: } jjg@893: jjg@893: void checkEqual(String label, int found, int expect) throws Exception { jjg@893: if (found != expect) jjg@893: throw new Exception("unexpected value for " + label jjg@893: + ": expected " + expect jjg@893: + ": found " + found); jjg@893: } jjg@893: jjg@893: //--------------- jjg@893: jjg@893: /* jjg@893: * A custom class unknown to javac but nonetheless registered in the context. jjg@893: */ jjg@893: static class Demo { jjg@893: static void preRegister(Context context, final Counter counter) { jjg@893: context.put(Demo.class, new Context.Factory() { jjg@893: public Demo make(Context c) { jjg@893: counter.count++; jjg@893: return new Demo(c); jjg@893: } jjg@893: }); jjg@893: } jjg@893: jjg@893: Demo(Context c) { jjg@893: c.put(Demo.class, this); jjg@893: } jjg@893: jjg@893: static Demo instance(Context context) { jjg@893: return context.get(Demo.class); jjg@893: } jjg@893: } jjg@893: jjg@893: /** jjg@893: * A custom version of a standard javac component. jjg@893: */ jjg@893: static class MyAttr extends Attr { jjg@893: static void preRegister(Context context, final Counter counter) { jjg@893: context.put(attrKey, new Context.Factory() { jjg@893: public Attr make(Context c) { jjg@893: counter.count++; jjg@893: return new MyAttr(c); jjg@893: } jjg@893: }); jjg@893: } jjg@893: jjg@893: MyAttr(Context c) { jjg@893: super(c); jjg@893: } jjg@893: } jjg@893: jjg@893: static class Counter { jjg@893: int count; jjg@893: } jjg@893: jjg@893: //--------------- jjg@893: jjg@893: int round = 0; jjg@893: jjg@893: @Override jjg@893: public boolean process(Set annotations, RoundEnvironment roundEnv) { jjg@893: round++; jjg@893: jjg@893: Context context = ((JavacProcessingEnvironment) processingEnv).getContext(); jjg@893: jjg@893: // verify items in context as expected jjg@893: check("Demo", Demo.instance(context), Demo.class); jjg@893: check("Attr", Attr.instance(context), MyAttr.class); jjg@893: jjg@893: // For a few rounds, generate new source files, so that we can check whether jjg@893: // values in the context are correctly handled in subsequent processing rounds jjg@893: if (round <= MAX_ROUNDS) { jjg@893: String pkg = "p"; jjg@893: String currClass = "Gen" + round; jjg@893: String curr = pkg + "." + currClass; jjg@893: String next = (pkg + ".Gen" + (round + 1)); jjg@893: StringBuilder text = new StringBuilder(); jjg@893: text.append("package ").append(pkg).append(";\n"); jjg@893: text.append("public class ").append(currClass).append(" {\n"); jjg@893: if (round < MAX_ROUNDS) jjg@893: text.append(" ").append(next).append(" x;\n"); jjg@893: text.append("}\n"); jjg@893: jjg@893: try { jjg@893: JavaFileObject fo = filer.createSourceFile(curr); jjg@893: Writer out = fo.openWriter(); jjg@893: try { jjg@893: out.write(text.toString()); jjg@893: } finally { jjg@893: out.close(); jjg@893: } jjg@893: } catch (IOException e) { jjg@893: throw new Error(e); jjg@893: } jjg@893: } jjg@893: jjg@893: return true; jjg@893: } jjg@893: jjg@893: void check(String label, Object o, Class clazz) { jjg@893: if (o == null) jjg@893: throw new IllegalStateException(label + ": no item found"); jjg@893: if (!clazz.isAssignableFrom(o.getClass())) jjg@893: throw new IllegalStateException(label + ": unexpected class: " + o.getClass()); jjg@893: } jjg@893: }