/* * Copyright 2016-present Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package com.facebook.buck.jvm.java.abi.source; import com.facebook.buck.jvm.java.abi.source.api.StopCompilation; import com.facebook.buck.jvm.java.plugin.adapter.BuckJavacTask; import com.facebook.buck.util.liteinfersupport.Nullable; import com.facebook.buck.util.liteinfersupport.Preconditions; import com.sun.source.tree.CompilationUnitTree; import com.sun.source.tree.Tree; import com.sun.source.util.JavacTask; import com.sun.source.util.Trees; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.StreamSupport; import javax.annotation.processing.Processor; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; import javax.tools.JavaFileObject; /** * An implementation of {@link JavacTask} that implements only the frontend portions of the task, * using only the parse phase of the underlying compiler and without requiring a complete classpath. * This effectively does the same thing as the Enter phase of the compiler (see * http://openjdk.java.net/groups/compiler/doc/compilation-overview/index.html), but applying * heuristics when dependencies are missing. This necessarily requires some assumptions to be made * about references to symbols defined in those dependencies. See the documentation of {@link * com.facebook.buck.jvm.java.abi.source} for details. */ public class FrontendOnlyJavacTask extends BuckJavacTask { private final JavacTask javacTask; @Nullable private TreeBackedElements elements; @Nullable private TreeBackedTrees trees; @Nullable private TreeBackedTypes types; @Nullable private Iterable<? extends CompilationUnitTree> parsedCompilationUnits; @Nullable private List<TreeBackedTypeElement> topLevelElements; private boolean stopCompilationAfterEnter = false; public FrontendOnlyJavacTask(JavacTask task) { super(task); javacTask = task; // Add the entering plugin first so that all other plugins and annotation processors will // run with the TreeBackedElements already entered addPlugin(new EnteringPlugin()); } @Override public Iterable<? extends CompilationUnitTree> parse() throws IOException { if (parsedCompilationUnits == null) { parsedCompilationUnits = javacTask.parse(); } return parsedCompilationUnits; } @Override public Iterable<? extends TypeElement> enter() throws IOException { Iterable<? extends TypeElement> javacTopLevelElements = super.enter(); topLevelElements = StreamSupport.stream(javacTopLevelElements.spliterator(), false) .map(getElements()::getCanonicalElement) .map(element -> (TreeBackedTypeElement) element) .collect(Collectors.toList()); return topLevelElements; } @Override public Iterable<? extends Element> analyze() throws IOException { throw new UnsupportedOperationException("Code analysis not supported"); } @Override public Iterable<? extends JavaFileObject> generate() throws IOException { throw new UnsupportedOperationException("Code generation not supported"); } @Override public TypeMirror getTypeMirror(Iterable<? extends Tree> path) { throw new UnsupportedOperationException(); } @Override public TreeBackedElements getElements() { if (elements == null) { initUtils(); } return Preconditions.checkNotNull(elements); } @Override public TreeBackedTrees getTrees() { if (trees == null) { initUtils(); } return Preconditions.checkNotNull(trees); } @Override public TreeBackedTypes getTypes() { if (types == null) { initUtils(); } return Preconditions.checkNotNull(types); } private void initUtils() { Elements javacElements = javacTask.getElements(); Trees javacTrees = super.getTrees(); types = new TreeBackedTypes(javacTask.getTypes()); elements = new TreeBackedElements(javacElements, javacTrees); trees = new TreeBackedTrees(javacTrees, elements, types); types.setElements(elements); elements.setResolver(new TreeBackedElementResolver(elements, types)); } @Override public void setProcessors(Iterable<? extends Processor> processors) { javacTask.setProcessors(wrap(processors)); } private List<TreeBackedProcessorWrapper> wrap(Iterable<? extends Processor> processors) { List<TreeBackedProcessorWrapper> result = new ArrayList<>(); for (Processor processor : processors) { result.add(new TreeBackedProcessorWrapper(this, processor)); } return result; } @Override public void setLocale(Locale locale) { throw new UnsupportedOperationException("NYI"); } @Override public Boolean call() { try { stopCompilationAfterEnter = true; return javacTask.call(); } catch (RuntimeException e) { if (e.getCause() instanceof StopCompilation) { return true; } throw e; } } @Override protected void onPostEnter(Set<TypeElement> topLevelTypes) { super.onPostEnter(topLevelTypes); if (stopCompilationAfterEnter) { throw new StopCompilation(); } } }