diff -r 7538e419a588 -r d898d9ee352f test/tools/javac/lambdaShapes/org/openjdk/tests/separate/Compiler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/lambdaShapes/org/openjdk/tests/separate/Compiler.java Tue Nov 20 09:58:55 2012 -0800 @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2012 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 org.openjdk.tests.separate; + +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.ConcurrentHashMap; +import java.io.*; +import java.net.URI; +import javax.tools.*; + +import com.sun.source.util.JavacTask; + +import static org.openjdk.tests.separate.SourceModel.Type; +import static org.openjdk.tests.separate.SourceModel.Class; +import static org.openjdk.tests.separate.SourceModel.Extends; +import static org.openjdk.tests.separate.SourceModel.SourceProcessor; + +public class Compiler { + + public enum Flags { + VERBOSE, // Prints out files as they are compiled + USECACHE // Keeps results around for reuse. Only use this is + // you're sure that each compilation name maps to the + // same source code + }; + + private static final AtomicInteger counter = new AtomicInteger(); + private static final String targetDir = "gen-separate"; + private static final File root = new File(targetDir); + private static ConcurrentHashMap cache = + new ConcurrentHashMap<>(); + + Set flags; + + private JavaCompiler systemJavaCompiler; + private StandardJavaFileManager fm; + private List tempDirs; + private List postprocessors; + + private static class SourceFile extends SimpleJavaFileObject { + private final String content; + + public SourceFile(String name, String content) { + super(URI.create("myfo:/" + name + ".java"), Kind.SOURCE); + this.content = content; + } + + public CharSequence getCharContent(boolean ignoreEncodingErrors) { + return toString(); + } + + public String toString() { return this.content; } + } + + public Compiler(Flags ... flags) { + setFlags(flags); + this.tempDirs = new ArrayList<>(); + this.postprocessors = new ArrayList<>(); + this.systemJavaCompiler = ToolProvider.getSystemJavaCompiler(); + this.fm = systemJavaCompiler.getStandardFileManager(null, null, null); + } + + public void setFlags(Flags ... flags) { + this.flags = new HashSet(Arrays.asList(flags)); + } + + public void addPostprocessor(ClassFilePreprocessor cfp) { + this.postprocessors.add(cfp); + } + + /** + * Compile hierarchies starting with each of the 'types' and return + * a ClassLoader that can be used to load the compiled classes. + */ + public ClassLoader compile(Type ... types) { + ClassFilePreprocessor[] cfps = this.postprocessors.toArray( + new ClassFilePreprocessor[0]); + + DirectedClassLoader dcl = new DirectedClassLoader(cfps); + + for (Type t : types) { + for (Map.Entry each : compileHierarchy(t).entrySet()) { + dcl.setLocationFor(each.getKey(), each.getValue()); + } + } + return dcl; + } + + /** + * Compiles and loads a hierarchy, starting at 'type' + */ + public java.lang.Class compileAndLoad(Type type) + throws ClassNotFoundException { + + ClassLoader loader = compile(type); + return java.lang.Class.forName(type.getName(), false, loader); + } + + /** + * Compiles a hierarchy, starting at 'type' and return a mapping of the + * name to the location where the classfile for that type resides. + */ + private Map compileHierarchy(Type type) { + HashMap outputDirs = new HashMap<>(); + + File outDir = compileOne(type); + outputDirs.put(type.getName(), outDir); + + Class superClass = type.getSuperclass(); + if (superClass != null) { + for( Map.Entry each : compileHierarchy(superClass).entrySet()) { + outputDirs.put(each.getKey(), each.getValue()); + } + } + for (Extends ext : type.getSupertypes()) { + Type iface = ext.getType(); + for( Map.Entry each : compileHierarchy(iface).entrySet()) { + outputDirs.put(each.getKey(), each.getValue()); + } + } + + return outputDirs; + } + + private File compileOne(Type type) { + if (this.flags.contains(Flags.USECACHE)) { + File dir = cache.get(type.getName()); + if (dir != null) { + return dir; + } + } + List files = new ArrayList<>(); + SourceProcessor accum = + (name, src) -> { files.add(new SourceFile(name, src)); }; + + for (Type dep : type.typeDependencies()) { + dep.generateAsDependency(accum, type.methodDependencies()); + } + + type.generate(accum); + + JavacTask ct = (JavacTask)this.systemJavaCompiler.getTask( + null, this.fm, null, null, null, files); + File destDir = null; + do { + int value = counter.incrementAndGet(); + destDir = new File(root, Integer.toString(value)); + } while (destDir.exists()); + + if (this.flags.contains(Flags.VERBOSE)) { + System.out.println("Compilation unit for " + type.getName() + + " : compiled into " + destDir); + for (JavaFileObject jfo : files) { + System.out.println(jfo.toString()); + } + } + + try { + destDir.mkdirs(); + this.fm.setLocation( + StandardLocation.CLASS_OUTPUT, Arrays.asList(destDir)); + } catch (IOException e) { + throw new RuntimeException( + "IOException encountered during compilation"); + } + Boolean result = ct.call(); + if (result == Boolean.FALSE) { + throw new RuntimeException( + "Compilation failure in " + type.getName() + " unit"); + } + if (this.flags.contains(Flags.USECACHE)) { + File existing = cache.putIfAbsent(type.getName(), destDir); + if (existing != null) { + deleteDir(destDir); + return existing; + } + } else { + this.tempDirs.add(destDir); + } + return destDir; + } + + private static void deleteDir(File dir) { + for (File f : dir.listFiles()) { + f.delete(); + }; + dir.delete(); + } + + public void cleanup() { + if (!this.flags.contains(Flags.USECACHE)) { + for (File d : tempDirs) { + deleteDir(d); + }; + tempDirs = new ArrayList<>(); + } + } + + // Removes all of the elements in the cache and deletes the associated + // output directories. This may not actually empty the cache if there + // are concurrent users of it. + public static void purgeCache() { + for (Map.Entry entry : cache.entrySet()) { + cache.remove(entry.getKey()); + deleteDir(entry.getValue()); + } + } +}