ReusableContext.java revision 3019:176472b94f2e
11556Srgrimes/* 21556Srgrimes * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 31556Srgrimes * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 41556Srgrimes * 51556Srgrimes * This code is free software; you can redistribute it and/or modify it 61556Srgrimes * under the terms of the GNU General Public License version 2 only, as 71556Srgrimes * published by the Free Software Foundation. 81556Srgrimes * 91556Srgrimes * This code is distributed in the hope that it will be useful, but WITHOUT 101556Srgrimes * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 111556Srgrimes * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 121556Srgrimes * version 2 for more details (a copy is included in the LICENSE file that 131556Srgrimes * accompanied this code). 141556Srgrimes * 151556Srgrimes * You should have received a copy of the GNU General Public License version 161556Srgrimes * 2 along with this work; if not, write to the Free Software Foundation, 171556Srgrimes * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 181556Srgrimes * 191556Srgrimes * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 201556Srgrimes * or visit www.oracle.com if you need additional information or have any 211556Srgrimes * questions. 221556Srgrimes */ 231556Srgrimes 241556Srgrimespackage combo; 251556Srgrimes 261556Srgrimesimport com.sun.source.tree.ClassTree; 271556Srgrimesimport com.sun.source.tree.CompilationUnitTree; 281556Srgrimesimport com.sun.source.util.JavacTask; 291556Srgrimesimport com.sun.source.util.TaskEvent; 301556Srgrimesimport com.sun.source.util.TaskEvent.Kind; 311556Srgrimesimport com.sun.source.util.TaskListener; 321556Srgrimesimport com.sun.source.util.TreeScanner; 331556Srgrimesimport com.sun.tools.javac.api.MultiTaskListener; 341556Srgrimesimport com.sun.tools.javac.code.Symbol; 351556Srgrimesimport com.sun.tools.javac.code.Symtab; 361556Srgrimesimport com.sun.tools.javac.code.Types; 371556Srgrimesimport com.sun.tools.javac.comp.Check; 3836150Scharnierimport com.sun.tools.javac.comp.CompileStates; 3936150Scharnierimport com.sun.tools.javac.comp.Enter; 4036150Scharnierimport com.sun.tools.javac.main.Arguments; 4136150Scharnierimport com.sun.tools.javac.main.JavaCompiler; 4246684Skrisimport com.sun.tools.javac.tree.JCTree.JCClassDecl; 431556Srgrimesimport com.sun.tools.javac.util.Context; 441556Srgrimesimport com.sun.tools.javac.util.Log; 4517987Speter 4617987Speterimport javax.tools.Diagnostic; 4717987Speterimport javax.tools.DiagnosticListener; 4817987Speterimport javax.tools.JavaFileManager; 491556Srgrimesimport javax.tools.JavaFileObject; 501556Srgrimesimport java.util.HashSet; 511556Srgrimesimport java.util.Set; 521556Srgrimes 531556Srgrimes/** 5417987Speter * A reusable context is a context that can be used safely across multiple compilation rounds 551556Srgrimes * arising from execution of a combo test. It achieves reuse by replacing some components 561556Srgrimes * (most notably JavaCompiler and Log) with reusable counterparts, and by exposing a method 571556Srgrimes * to cleanup leftovers from previous compilation. 581556Srgrimes * <p> 591556Srgrimes * There are, however, situations in which reusing the context is not safe: (i) when different 601556Srgrimes * compilations are using different sets of compiler options (as most option values are cached 611556Srgrimes * inside components themselves) and (ii) when the compilation unit happens to redefine classes 621556Srgrimes * in the java.* packages. 631556Srgrimes */ 641556Srgrimesclass ReusableContext extends Context implements TaskListener { 651556Srgrimes 661556Srgrimes Set<CompilationUnitTree> roots = new HashSet<>(); 671556Srgrimes 681556Srgrimes String opts; 691556Srgrimes boolean polluted = false; 701556Srgrimes 711556Srgrimes ReusableContext() { 721556Srgrimes super(); 7346684Skris put(Log.logKey, ReusableLog.factory); 741556Srgrimes put(JavaCompiler.compilerKey, ReusableJavaCompiler.factory); 751556Srgrimes } 761556Srgrimes 7717987Speter void clear() { 7838521Scracauer drop(Arguments.argsKey); 7938536Scracauer drop(DiagnosticListener.class); 8038950Scracauer drop(Log.outKey); 8138521Scracauer drop(JavaFileManager.class); 8238521Scracauer drop(JavacTask.class); 8320902Ssteve 841556Srgrimes if (ht.get(Log.logKey) instanceof ReusableLog) { 8517987Speter //log already inited - not first round 8617987Speter ((ReusableLog)Log.instance(this)).clear(); 8720902Ssteve Enter.instance(this).newRound(); 881556Srgrimes ((ReusableJavaCompiler)ReusableJavaCompiler.instance(this)).clear(); 8920902Ssteve Types.instance(this).newRound(); 9020902Ssteve Check.instance(this).newRound(); 9120902Ssteve CompileStates.instance(this).clear(); 9220902Ssteve MultiTaskListener.instance(this).clear(); 9320902Ssteve 9420902Ssteve //find if any of the roots have redefined java.* classes 9520902Ssteve Symtab syms = Symtab.instance(this); 9620902Ssteve new TreeScanner<Void, Void>() { 9720902Ssteve @Override 9820902Ssteve public Void visitClass(ClassTree node, Void aVoid) { 9920902Ssteve Symbol sym = ((JCClassDecl)node).sym; 10020902Ssteve if (sym != null) { 10120902Ssteve syms.classes.remove(sym.flatName()); 10220902Ssteve if (sym.flatName().toString().startsWith("java.")) { 10320902Ssteve polluted = true; 10420902Ssteve } 10520902Ssteve } 10620902Ssteve return super.visitClass(node, aVoid); 10720902Ssteve } 10820902Ssteve }.scan(roots, null); 10920902Ssteve roots.clear(); 11020902Ssteve } 11120902Ssteve } 11220902Ssteve 11320902Ssteve @Override 11420902Ssteve public void finished(TaskEvent e) { 11520902Ssteve if (e.getKind() == Kind.PARSE) { 11620902Ssteve roots.add(e.getCompilationUnit()); 11720902Ssteve } 11820902Ssteve } 11920902Ssteve 12020902Ssteve @Override 12120902Ssteve public void started(TaskEvent e) { 12220902Ssteve //do nothing 12320902Ssteve } 12420902Ssteve 12520902Ssteve <T> void drop(Key<T> k) { 12620902Ssteve ht.remove(k); 12720902Ssteve } 12820902Ssteve 12920902Ssteve <T> void drop(Class<T> c) { 13020902Ssteve ht.remove(key(c)); 13120902Ssteve } 13220902Ssteve 13320902Ssteve /** 13420902Ssteve * Reusable JavaCompiler; exposes a method to clean up the component from leftovers associated with 1351556Srgrimes * previous compilations. 1361556Srgrimes */ 13717987Speter static class ReusableJavaCompiler extends JavaCompiler { 13817987Speter 13917987Speter static Factory<JavaCompiler> factory = ReusableJavaCompiler::new; 14020425Ssteve 14117987Speter ReusableJavaCompiler(Context context) { 1421556Srgrimes super(context); 1431556Srgrimes } 1441556Srgrimes 1451556Srgrimes @Override 14620902Ssteve public void close() { 1471556Srgrimes //do nothing 14820902Ssteve } 14920902Ssteve 1501556Srgrimes void clear() { 1511556Srgrimes newRound(); 1521556Srgrimes } 15320902Ssteve 15420902Ssteve @Override 15520902Ssteve protected void checkReusable() { 15620902Ssteve //do nothing - it's ok to reuse the compiler 15720902Ssteve } 15820902Ssteve } 15920902Ssteve 16020902Ssteve /** 16120902Ssteve * Reusable Log; exposes a method to clean up the component from leftovers associated with 16220902Ssteve * previous compilations. 16320902Ssteve */ 16420902Ssteve static class ReusableLog extends Log { 16520902Ssteve 16620902Ssteve static Factory<Log> factory = ReusableLog::new; 16720902Ssteve 16820902Ssteve Context context; 16920902Ssteve 17020902Ssteve ReusableLog(Context context) { 17120902Ssteve super(context); 1721556Srgrimes this.context = context; 1731556Srgrimes } 1741556Srgrimes 1751556Srgrimes void clear() { 1761556Srgrimes recorded.clear(); 1771556Srgrimes sourceMap.clear(); 1781556Srgrimes nerrors = 0; 1791556Srgrimes nwarnings = 0; 1801556Srgrimes //Set a fake listener that will lazily lookup the context for the 'real' listener. Since 18120902Ssteve //this field is never updated when a new task is created, we cannot simply reset the field 1821556Srgrimes //or keep old value. This is a hack to workaround the limitations in the current infrastructure. 1831556Srgrimes diagListener = new DiagnosticListener<JavaFileObject>() { 1841556Srgrimes DiagnosticListener<JavaFileObject> cachedListener; 1851556Srgrimes 1861556Srgrimes @Override 1871556Srgrimes @SuppressWarnings("unchecked") 1881556Srgrimes public void report(Diagnostic<? extends JavaFileObject> diagnostic) { 1891556Srgrimes if (cachedListener == null) { 1901556Srgrimes cachedListener = context.get(DiagnosticListener.class); 19120902Ssteve } 19220902Ssteve cachedListener.report(diagnostic); 19338950Scracauer } 1941556Srgrimes }; 19520902Ssteve } 1961556Srgrimes } 1971556Srgrimes} 1981556Srgrimes