TaskFactory.java revision 3062:15bdc18525ff
1214921Scognet/* 2185222Ssam * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. 3185222Ssam * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4185222Ssam * 5185222Ssam * This code is free software; you can redistribute it and/or modify it 6185222Ssam * under the terms of the GNU General Public License version 2 only, as 7185222Ssam * published by the Free Software Foundation. Oracle designates this 8185222Ssam * particular file as subject to the "Classpath" exception as provided 9185222Ssam * by Oracle in the LICENSE file that accompanied this code. 10185222Ssam * 11185222Ssam * This code is distributed in the hope that it will be useful, but WITHOUT 12185222Ssam * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13185222Ssam * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14185222Ssam * version 2 for more details (a copy is included in the LICENSE file that 15185222Ssam * accompanied this code). 16185222Ssam * 17185222Ssam * You should have received a copy of the GNU General Public License version 18185222Ssam * 2 along with this work; if not, write to the Free Software Foundation, 19185222Ssam * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20185222Ssam * 21185222Ssam * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22185222Ssam * or visit www.oracle.com if you need additional information or have any 23185222Ssam * questions. 24185222Ssam */ 25185222Ssam 26185222Ssampackage jdk.jshell; 27185222Ssam 28185222Ssamimport com.sun.source.tree.CompilationUnitTree; 29185222Ssamimport com.sun.source.tree.Tree; 30185222Ssamimport com.sun.source.util.Trees; 31185222Ssamimport com.sun.tools.javac.api.JavacTaskImpl; 32185222Ssamimport com.sun.tools.javac.api.JavacTool; 33185222Ssamimport com.sun.tools.javac.util.Context; 34185222Ssamimport java.util.ArrayList; 35185222Ssamimport java.util.Arrays; 36185222Ssamimport java.util.Iterator; 37185222Ssamimport java.util.List; 38185222Ssamimport javax.tools.Diagnostic; 39185222Ssamimport javax.tools.DiagnosticCollector; 40185222Ssamimport javax.tools.JavaCompiler; 41185222Ssamimport javax.tools.JavaFileManager; 42185222Ssamimport javax.tools.JavaFileObject; 43185222Ssamimport javax.tools.ToolProvider; 44185222Ssamimport static jdk.jshell.Util.*; 45185222Ssamimport com.sun.source.tree.ImportTree; 46185222Ssamimport com.sun.tools.javac.code.Types; 47185222Ssamimport com.sun.tools.javac.util.JavacMessages; 48185222Ssamimport jdk.jshell.MemoryFileManager.OutputMemoryJavaFileObject; 49185222Ssamimport java.util.Collections; 50185222Ssamimport java.util.Locale; 51185222Ssamimport static javax.tools.StandardLocation.CLASS_OUTPUT; 52185222Ssamimport static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN; 53185222Ssamimport java.io.File; 54185222Ssamimport java.util.Collection; 55185222Ssamimport java.util.HashMap; 56185222Ssamimport java.util.LinkedHashMap; 57185222Ssamimport java.util.Map; 58185222Ssamimport java.util.stream.Collectors; 59185222Ssamimport java.util.stream.Stream; 60185222Ssamimport javax.lang.model.util.Elements; 61185222Ssamimport javax.tools.FileObject; 62185222Ssamimport jdk.jshell.MemoryFileManager.SourceMemoryJavaFileObject; 63185222Ssamimport jdk.jshell.ClassTracker.ClassInfo; 64185222Ssam 65185222Ssam/** 66185222Ssam * The primary interface to the compiler API. Parsing, analysis, and 67185222Ssam * compilation to class files (in memory). 68185222Ssam * @author Robert Field 69186334Ssam */ 70185222Ssamclass TaskFactory { 71185222Ssam 72185222Ssam private final JavaCompiler compiler; 73185222Ssam private final MemoryFileManager fileManager; 74185222Ssam private final JShell state; 75185222Ssam private String classpath = System.getProperty("java.class.path"); 76185222Ssam 77185222Ssam TaskFactory(JShell state) { 78185222Ssam this.state = state; 79214921Scognet this.compiler = ToolProvider.getSystemJavaCompiler(); 80185222Ssam if (compiler == null) { 81185222Ssam throw new UnsupportedOperationException("Compiler not available, must be run with full JDK 9."); 82185222Ssam } 83217769Smckusick if (!System.getProperty("java.specification.version").equals("1.9")) { 84185222Ssam throw new UnsupportedOperationException("Wrong compiler, must be run with full JDK 9."); 85185222Ssam } 86185222Ssam this.fileManager = new MemoryFileManager( 87214921Scognet compiler.getStandardFileManager(null, null, null), state); 88185222Ssam } 89214921Scognet 90214921Scognet void addToClasspath(String path) { 91214921Scognet classpath = classpath + File.pathSeparator + path; 92214921Scognet List<String> args = new ArrayList<>(); 93185222Ssam args.add(classpath); 94185222Ssam fileManager().handleOption("-classpath", args.iterator()); 95185222Ssam } 96185222Ssam 97214921Scognet MemoryFileManager fileManager() { 98186261Ssam return fileManager; 99185222Ssam } 100185222Ssam 101185222Ssam private interface SourceHandler<T> { 102185222Ssam 103185222Ssam JavaFileObject sourceToFileObject(MemoryFileManager fm, T t); 104185222Ssam 105214921Scognet Diag diag(Diagnostic<? extends JavaFileObject> d); 106185222Ssam } 107185222Ssam 108185222Ssam private class StringSourceHandler implements SourceHandler<String> { 109185222Ssam 110185222Ssam @Override 111185222Ssam public JavaFileObject sourceToFileObject(MemoryFileManager fm, String src) { 112185222Ssam return fm.createSourceFileObject(src, "$NeverUsedName$", src); 113185222Ssam } 114185222Ssam 115185222Ssam @Override 116185222Ssam public Diag diag(final Diagnostic<? extends JavaFileObject> d) { 117185222Ssam return new Diag() { 118185222Ssam 119185222Ssam @Override 120185222Ssam public boolean isError() { 121185222Ssam return d.getKind() == Diagnostic.Kind.ERROR; 122185222Ssam } 123185222Ssam 124185222Ssam @Override 125185222Ssam public long getPosition() { 126185222Ssam return d.getPosition(); 127185222Ssam } 128185222Ssam 129185222Ssam @Override 130185222Ssam public long getStartPosition() { 131185222Ssam return d.getStartPosition(); 132185222Ssam } 133185222Ssam 134185222Ssam @Override 135185222Ssam public long getEndPosition() { 136185222Ssam return d.getEndPosition(); 137185222Ssam } 138185222Ssam 139185222Ssam @Override 140185222Ssam public String getCode() { 141185222Ssam return d.getCode(); 142185222Ssam } 143185222Ssam 144185222Ssam @Override 145185222Ssam public String getMessage(Locale locale) { 146185222Ssam return expunge(d.getMessage(locale)); 147185222Ssam } 148248293Sbrooks 149185222Ssam @Override 150214921Scognet Unit unitOrNull() { 151214921Scognet return null; 152214921Scognet } 153214921Scognet }; 154214921Scognet } 155214921Scognet } 156214921Scognet 157214921Scognet private class WrapSourceHandler implements SourceHandler<OuterWrap> { 158214921Scognet 159214921Scognet final OuterWrap wrap; 160214921Scognet 161214921Scognet WrapSourceHandler(OuterWrap wrap) { 162214921Scognet this.wrap = wrap; 163214921Scognet } 164214921Scognet 165214921Scognet @Override 166214921Scognet public JavaFileObject sourceToFileObject(MemoryFileManager fm, OuterWrap w) { 167214921Scognet return fm.createSourceFileObject(w, w.classFullName(), w.wrapped()); 168214921Scognet } 169214921Scognet 170214921Scognet @Override 171214921Scognet public Diag diag(Diagnostic<? extends JavaFileObject> d) { 172214921Scognet return wrap.wrapDiag(d); 173214921Scognet } 174214921Scognet } 175214921Scognet 176214921Scognet private class UnitSourceHandler implements SourceHandler<Unit> { 177214921Scognet 178214921Scognet @Override 179214921Scognet public JavaFileObject sourceToFileObject(MemoryFileManager fm, Unit u) { 180185222Ssam return fm.createSourceFileObject(u, 181185222Ssam state.maps.classFullName(u.snippet()), 182185222Ssam u.snippet().outerWrap().wrapped()); 183214921Scognet } 184214921Scognet 185185222Ssam @Override 186214921Scognet public Diag diag(Diagnostic<? extends JavaFileObject> d) { 187185222Ssam SourceMemoryJavaFileObject smjfo = (SourceMemoryJavaFileObject) d.getSource(); 188214921Scognet Unit u = (Unit) smjfo.getOrigin(); 189185222Ssam return u.snippet().outerWrap().wrapDiag(d); 190214921Scognet } 191185222Ssam } 192214921Scognet 193185222Ssam /** 194214921Scognet * Parse a snippet of code (as a String) using the parser subclass. Return 195185222Ssam * the parse tree (and errors). 196214921Scognet */ 197185222Ssam class ParseTask extends BaseTask { 198214921Scognet 199185222Ssam private final CompilationUnitTree cut; 200214921Scognet private final List<? extends Tree> units; 201185222Ssam 202214921Scognet ParseTask(final String source) { 203185222Ssam super(Stream.of(source), 204214921Scognet new StringSourceHandler(), 205185222Ssam "-XDallowStringFolding=false", "-proc:none"); 206214921Scognet ReplParserFactory.instance(getContext()); 207185222Ssam Iterable<? extends CompilationUnitTree> asts = parse(); 208185222Ssam Iterator<? extends CompilationUnitTree> it = asts.iterator(); 209185222Ssam if (it.hasNext()) { 210185222Ssam this.cut = it.next(); 211185222Ssam List<? extends ImportTree> imps = cut.getImports(); 212185222Ssam this.units = !imps.isEmpty() ? imps : cut.getTypeDecls(); 213185222Ssam } else { 214214921Scognet this.cut = null; 215185222Ssam this.units = Collections.emptyList(); 216185222Ssam } 217185222Ssam } 218185222Ssam 219185222Ssam private Iterable<? extends CompilationUnitTree> parse() { 220185222Ssam try { 221185222Ssam return task.parse(); 222185222Ssam } catch (Exception ex) { 223185222Ssam throw new InternalError("Exception during parse - " + ex.getMessage(), ex); 224185222Ssam } 225185222Ssam } 226185222Ssam 227185222Ssam List<? extends Tree> units() { 228185222Ssam return units; 229185222Ssam } 230185222Ssam 231214921Scognet @Override 232185222Ssam CompilationUnitTree cuTree() { 233214921Scognet return cut; 234185222Ssam } 235185222Ssam } 236185222Ssam 237185222Ssam /** 238185222Ssam * Run the normal "analyze()" pass of the compiler over the wrapped snippet. 239226275Snwhitehorn */ 240226275Snwhitehorn class AnalyzeTask extends BaseTask { 241226275Snwhitehorn 242185222Ssam private final CompilationUnitTree cut; 243185222Ssam 244185222Ssam AnalyzeTask(final OuterWrap wrap) { 245185222Ssam this(Stream.of(wrap), 246185222Ssam new WrapSourceHandler(wrap), 247185222Ssam "-XDshouldStopPolicy=FLOW", "-proc:none"); 248185222Ssam } 249185222Ssam 250185222Ssam AnalyzeTask(final Collection<Unit> units) { 251185222Ssam this(units.stream(), new UnitSourceHandler(), 252185222Ssam "-XDshouldStopPolicy=FLOW", "-Xlint:unchecked", "-proc:none"); 253185222Ssam } 254185222Ssam 255185222Ssam <T>AnalyzeTask(final Stream<T> stream, SourceHandler<T> sourceHandler, 256185222Ssam String... extraOptions) { 257185222Ssam super(stream, sourceHandler, extraOptions); 258185222Ssam Iterator<? extends CompilationUnitTree> cuts = analyze().iterator(); 259185222Ssam if (cuts.hasNext()) { 260185222Ssam this.cut = cuts.next(); 261185222Ssam //proc.debug("AnalyzeTask element=%s cutp=%s cut=%s\n", e, cutp, cut); 262185222Ssam } else { 263185222Ssam this.cut = null; 264185222Ssam //proc.debug("AnalyzeTask -- no elements -- %s\n", getDiagnostics()); 265185222Ssam } 266185222Ssam } 267185222Ssam 268185222Ssam private Iterable<? extends CompilationUnitTree> analyze() { 269185222Ssam try { 270185222Ssam Iterable<? extends CompilationUnitTree> cuts = task.parse(); 271185222Ssam task.analyze(); 272185222Ssam return cuts; 273185222Ssam } catch (Exception ex) { 274185222Ssam throw new InternalError("Exception during analyze - " + ex.getMessage(), ex); 275185222Ssam } 276185222Ssam } 277185222Ssam 278185222Ssam @Override 279185222Ssam CompilationUnitTree cuTree() { 280185222Ssam return cut; 281185222Ssam } 282185222Ssam 283185222Ssam Elements getElements() { 284185222Ssam return task.getElements(); 285185222Ssam } 286185222Ssam 287185222Ssam javax.lang.model.util.Types getTypes() { 288185222Ssam return task.getTypes(); 289185222Ssam } 290185222Ssam } 291185222Ssam 292185222Ssam /** 293185222Ssam * Unit the wrapped snippet to class files. 294185222Ssam */ 295185222Ssam class CompileTask extends BaseTask { 296185222Ssam 297185222Ssam private final Map<Unit, List<OutputMemoryJavaFileObject>> classObjs = new HashMap<>(); 298185222Ssam 299185222Ssam CompileTask(Collection<Unit> units) { 300185222Ssam super(units.stream(), new UnitSourceHandler(), 301185222Ssam "-Xlint:unchecked", "-proc:none"); 302185222Ssam } 303185222Ssam 304185222Ssam boolean compile() { 305185222Ssam fileManager.registerClassFileCreationListener(this::listenForNewClassFile); 306185222Ssam boolean result = task.call(); 307185222Ssam fileManager.registerClassFileCreationListener(null); 308185222Ssam return result; 309185222Ssam } 310185222Ssam 311185222Ssam 312185222Ssam List<ClassInfo> classInfoList(Unit u) { 313185222Ssam List<OutputMemoryJavaFileObject> l = classObjs.get(u); 314185222Ssam if (l == null) return Collections.emptyList(); 315185222Ssam return l.stream() 316185222Ssam .map(fo -> state.classTracker.classInfo(fo.getName(), fo.getBytes())) 317185222Ssam .collect(Collectors.toList()); 318185222Ssam } 319185222Ssam 320185222Ssam private void listenForNewClassFile(OutputMemoryJavaFileObject jfo, JavaFileManager.Location location, 321185222Ssam String className, JavaFileObject.Kind kind, FileObject sibling) { 322185222Ssam //debug("listenForNewClassFile %s loc=%s kind=%s\n", className, location, kind); 323214921Scognet if (location == CLASS_OUTPUT) { 324185222Ssam state.debug(DBG_GEN, "Compiler generating class %s\n", className); 325185222Ssam Unit u = ((sibling instanceof SourceMemoryJavaFileObject) 326185222Ssam && (((SourceMemoryJavaFileObject) sibling).getOrigin() instanceof Unit)) 327185222Ssam ? (Unit) ((SourceMemoryJavaFileObject) sibling).getOrigin() 328214921Scognet : null; 329185222Ssam classObjs.compute(u, (k, v) -> (v == null)? new ArrayList<>() : v) 330185222Ssam .add(jfo); 331185222Ssam } 332185222Ssam } 333185222Ssam 334185222Ssam @Override 335185222Ssam CompilationUnitTree cuTree() { 336185222Ssam throw new UnsupportedOperationException("Not supported."); 337185222Ssam } 338214921Scognet } 339214921Scognet 340214921Scognet abstract class BaseTask { 341214921Scognet 342214921Scognet final DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>(); 343214921Scognet final JavacTaskImpl task; 344185222Ssam private DiagList diags = null; 345214921Scognet private final SourceHandler<?> sourceHandler; 346185222Ssam private final Context context = new Context(); 347214921Scognet private Types types; 348214921Scognet private JavacMessages messages; 349214921Scognet private Trees trees; 350214921Scognet 351214921Scognet private <T>BaseTask(Stream<T> inputs, 352214921Scognet //BiFunction<MemoryFileManager, T, JavaFileObject> sfoCreator, 353214921Scognet SourceHandler<T> sh, 354214921Scognet String... extraOptions) { 355214921Scognet this.sourceHandler = sh; 356185222Ssam List<String> options = Arrays.asList(extraOptions); 357214921Scognet Iterable<? extends JavaFileObject> compilationUnits = inputs 358214921Scognet .map(in -> sh.sourceToFileObject(fileManager, in)) 359214921Scognet .collect(Collectors.toList()); 360214921Scognet this.task = (JavacTaskImpl) ((JavacTool) compiler).getTask(null, 361214921Scognet fileManager, diagnostics, options, null, 362214921Scognet compilationUnits, context); 363185222Ssam } 364185222Ssam 365185222Ssam abstract CompilationUnitTree cuTree(); 366185222Ssam 367185222Ssam Diag diag(Diagnostic<? extends JavaFileObject> diag) { 368185222Ssam return sourceHandler.diag(diag); 369185222Ssam } 370185222Ssam 371185222Ssam Context getContext() { 372185222Ssam return context; 373185222Ssam } 374185222Ssam 375185222Ssam Types types() { 376185222Ssam if (types == null) { 377185222Ssam types = Types.instance(context); 378185222Ssam } 379185222Ssam return types; 380185222Ssam } 381185222Ssam 382185222Ssam JavacMessages messages() { 383185222Ssam if (messages == null) { 384185222Ssam messages = JavacMessages.instance(context); 385185222Ssam } 386185222Ssam return messages; 387185222Ssam } 388185222Ssam 389185222Ssam Trees trees() { 390214921Scognet if (trees == null) { 391185222Ssam trees = Trees.instance(task); 392214921Scognet } 393214921Scognet return trees; 394185222Ssam } 395185222Ssam 396214921Scognet // ------------------ diags functionality 397214921Scognet 398185222Ssam DiagList getDiagnostics() { 399185222Ssam if (diags == null) { 400214921Scognet LinkedHashMap<String, Diag> diagMap = new LinkedHashMap<>(); 401185222Ssam for (Diagnostic<? extends JavaFileObject> in : diagnostics.getDiagnostics()) { 402214921Scognet Diag d = diag(in); 403185222Ssam String uniqueKey = d.getCode() + ":" + d.getPosition() + ":" + d.getMessage(null); 404185222Ssam diagMap.put(uniqueKey, d); 405185222Ssam } 406185222Ssam diags = new DiagList(diagMap.values()); 407185222Ssam } 408185222Ssam return diags; 409185222Ssam } 410185222Ssam 411214921Scognet boolean hasErrors() { 412185222Ssam return getDiagnostics().hasErrors(); 413185222Ssam } 414214921Scognet 415214921Scognet String shortErrorMessage() { 416185222Ssam StringBuilder sb = new StringBuilder(); 417185222Ssam for (Diag diag : getDiagnostics()) { 418185222Ssam for (String line : diag.getMessage(null).split("\\r?\\n")) { 419185222Ssam if (!line.trim().startsWith("location:")) { 420185222Ssam sb.append(line); 421185222Ssam } 422185222Ssam } 423185222Ssam } 424214921Scognet return sb.toString(); 425214921Scognet } 426214921Scognet 427214921Scognet void debugPrintDiagnostics(String src) { 428214921Scognet for (Diag diag : getDiagnostics()) { 429214921Scognet state.debug(DBG_GEN, "ERROR --\n"); 430185222Ssam for (String line : diag.getMessage(null).split("\\r?\\n")) { 431185222Ssam if (!line.trim().startsWith("location:")) { 432185222Ssam state.debug(DBG_GEN, "%s\n", line); 433185222Ssam } 434185222Ssam } 435185222Ssam int start = (int) diag.getStartPosition(); 436185222Ssam int end = (int) diag.getEndPosition(); 437214921Scognet if (src != null) { 438214921Scognet String[] srcLines = src.split("\\r?\\n"); 439185222Ssam for (String line : srcLines) { 440185222Ssam state.debug(DBG_GEN, "%s\n", line); 441185222Ssam } 442185222Ssam 443185222Ssam StringBuilder sb = new StringBuilder(); 444185222Ssam for (int i = 0; i < start; ++i) { 445185222Ssam sb.append(' '); 446185222Ssam } 447185222Ssam sb.append('^'); 448185222Ssam if (end > start) { 449185222Ssam for (int i = start + 1; i < end; ++i) { 450185222Ssam sb.append('-'); 451185222Ssam } 452185222Ssam sb.append('^'); 453214921Scognet } 454185222Ssam state.debug(DBG_GEN, "%s\n", sb.toString()); 455214921Scognet } 456185222Ssam state.debug(DBG_GEN, "printDiagnostics start-pos = %d ==> %d -- wrap = %s\n", 457214921Scognet diag.getStartPosition(), start, this); 458185222Ssam state.debug(DBG_GEN, "Code: %s\n", diag.getCode()); 459214921Scognet state.debug(DBG_GEN, "Pos: %d (%d - %d) -- %s\n", diag.getPosition(), 460185222Ssam diag.getStartPosition(), diag.getEndPosition(), diag.getMessage(null)); 461185222Ssam } 462185222Ssam } 463185222Ssam } 464185222Ssam 465185222Ssam} 466214921Scognet