1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.javac;
18 
19 import com.google.common.collect.Lists;
20 import com.google.common.io.Files;
21 
22 import java.util.stream.Collectors;
23 import org.apache.bcel.classfile.ClassParser;
24 import org.apache.bcel.classfile.JavaClass;
25 
26 import java.io.File;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.net.URI;
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.List;
33 import java.util.Locale;
34 
35 import javax.annotation.processing.Processor;
36 import javax.tools.DiagnosticCollector;
37 import javax.tools.JavaCompiler;
38 import javax.tools.JavaFileObject;
39 import javax.tools.SimpleJavaFileObject;
40 import javax.tools.StandardJavaFileManager;
41 import javax.tools.StandardLocation;
42 import javax.tools.ToolProvider;
43 
44 /**
45  * Helper class for compiling snippets of Java source and providing access to the resulting class
46  * files.
47  */
48 public class Javac {
49 
50     private final JavaCompiler mJavac;
51     private final StandardJavaFileManager mFileMan;
52     private final List<JavaFileObject> mCompilationUnits;
53     private final File mClassOutDir;
54 
Javac()55     public Javac() throws IOException {
56         mJavac = ToolProvider.getSystemJavaCompiler();
57         mFileMan = mJavac.getStandardFileManager(null, Locale.US, null);
58         mClassOutDir = Files.createTempDir();
59         mFileMan.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(mClassOutDir));
60         mFileMan.setLocation(StandardLocation.CLASS_PATH, Arrays.asList(mClassOutDir));
61         mCompilationUnits = new ArrayList<>();
62     }
63 
classToFileName(String classname)64     private String classToFileName(String classname) {
65         return classname.replace('.', '/');
66     }
67 
addSource(String classname, String contents)68     public Javac addSource(String classname, String contents) {
69         JavaFileObject java = new SimpleJavaFileObject(URI.create(
70                 String.format("string:///%s.java", classToFileName(classname))),
71                 JavaFileObject.Kind.SOURCE
72                 ){
73             @Override
74             public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
75                 return contents;
76             }
77         };
78         mCompilationUnits.add(java);
79         return this;
80     }
81 
compile()82     public void compile() {
83         compileWithAnnotationProcessor(null);
84     }
85 
compileWithAnnotationProcessor(Processor processor)86     public void compileWithAnnotationProcessor(Processor processor) {
87         DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<>();
88         JavaCompiler.CompilationTask task = mJavac.getTask(
89                 null,
90                 mFileMan,
91                 diagnosticCollector,
92                 null,
93                 null,
94                 mCompilationUnits);
95         if (processor != null) {
96             task.setProcessors(Lists.newArrayList(processor));
97         }
98         boolean result = task.call();
99         if (!result) {
100             throw new IllegalStateException(
101                     "Compilation failed:" +
102                             diagnosticCollector.getDiagnostics()
103                                     .stream()
104                                     .map(Object::toString)
105                                     .collect(Collectors.joining("\n")));
106         }
107     }
108 
getOutputFile(String filename)109     public InputStream getOutputFile(String filename) throws IOException {
110         Iterable<? extends JavaFileObject> objs = mFileMan.getJavaFileObjects(
111                 new File(mClassOutDir, filename));
112         if (!objs.iterator().hasNext()) {
113             return null;
114         }
115         return objs.iterator().next().openInputStream();
116     }
117 
getClassFile(String classname)118     public InputStream getClassFile(String classname) throws IOException {
119         return getOutputFile(String.format("%s.class", classToFileName(classname)));
120     }
121 
getCompiledClass(String classname)122     public JavaClass getCompiledClass(String classname) throws IOException {
123         return new ClassParser(getClassFile(classname),
124                 String.format("%s.class", classToFileName(classname))).parse();
125     }
126 }
127