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.io.Files;
20 
21 import java.util.stream.Collectors;
22 import org.apache.bcel.classfile.ClassParser;
23 import org.apache.bcel.classfile.JavaClass;
24 
25 import java.io.File;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.net.URI;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.List;
32 import java.util.Locale;
33 
34 import javax.tools.DiagnosticCollector;
35 import javax.tools.JavaCompiler;
36 import javax.tools.JavaFileObject;
37 import javax.tools.SimpleJavaFileObject;
38 import javax.tools.StandardJavaFileManager;
39 import javax.tools.StandardLocation;
40 import javax.tools.ToolProvider;
41 
42 /**
43  * Helper class for compiling snippets of Java source and providing access to the resulting class
44  * files.
45  */
46 public class Javac {
47 
48     private final JavaCompiler mJavac;
49     private final StandardJavaFileManager mFileMan;
50     private final List<JavaFileObject> mCompilationUnits;
51     private final File mClassOutDir;
52 
Javac()53     public Javac() throws IOException {
54         mJavac = ToolProvider.getSystemJavaCompiler();
55         mFileMan = mJavac.getStandardFileManager(null, Locale.US, null);
56         mClassOutDir = Files.createTempDir();
57         mFileMan.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(mClassOutDir));
58         mFileMan.setLocation(StandardLocation.CLASS_PATH, Arrays.asList(mClassOutDir));
59         mCompilationUnits = new ArrayList<>();
60     }
61 
classToFileName(String classname)62     private String classToFileName(String classname) {
63         return classname.replace('.', '/');
64     }
65 
addSource(String classname, String contents)66     public Javac addSource(String classname, String contents) {
67         JavaFileObject java = new SimpleJavaFileObject(URI.create(
68                 String.format("string:///%s.java", classToFileName(classname))),
69                 JavaFileObject.Kind.SOURCE
70                 ){
71             @Override
72             public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
73                 return contents;
74             }
75         };
76         mCompilationUnits.add(java);
77         return this;
78     }
79 
compile()80     public void compile() {
81         DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<>();
82         JavaCompiler.CompilationTask task = mJavac.getTask(
83                 null,
84                 mFileMan,
85                 diagnosticCollector,
86                 null,
87                 null,
88                 mCompilationUnits);
89         boolean result = task.call();
90         if (!result) {
91             throw new IllegalStateException(
92                 "Compilation failed:" +
93                     diagnosticCollector.getDiagnostics()
94                         .stream()
95                         .map(Object::toString)
96                         .collect(Collectors.joining("\n")));
97         }
98     }
99 
getClassFile(String classname)100     public InputStream getClassFile(String classname) throws IOException {
101         Iterable<? extends JavaFileObject> objs = mFileMan.getJavaFileObjects(
102                 new File(mClassOutDir, String.format("%s.class", classToFileName(classname))));
103         if (!objs.iterator().hasNext()) {
104             return null;
105         }
106         return objs.iterator().next().openInputStream();
107     }
108 
getCompiledClass(String classname)109     public JavaClass getCompiledClass(String classname) throws IOException {
110         return new ClassParser(getClassFile(classname),
111                 String.format("%s.class", classToFileName(classname))).parse();
112     }
113 }
114