-
Notifications
You must be signed in to change notification settings - Fork 77
/
GenBlackbirdAccess.java
276 lines (234 loc) · 10 KB
/
GenBlackbirdAccess.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticListener;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import javax.tools.FileObject;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.JavaFileObject.Kind;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
/**
* The following code produces the class file stored in the data arrays in CrossLoaderAccess.
* Loading the Java compiler is extremely expensive so we inline the byte data instead.
*
* This code is not compiled or shipped with Blackbird, just here for maintainer reference.
*/
class GrantAccess {
private static MethodHandles.Lookup grantAccessSlow(final MethodHandles.Lookup lookup, final Package pkg) throws IOException, ReflectiveOperationException {
final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
final BlackbirdDiagnosticListener<JavaFileObject> diagnostics = new BlackbirdDiagnosticListener<>();
final StandardJavaFileManager delegateFileManager = compiler.getStandardFileManager(diagnostics, Locale.ROOT, StandardCharsets.UTF_8);
final String myPackage = pkg.getName();
final BlackbirdFileManager fileManager = new BlackbirdFileManager(delegateFileManager, myPackage);
final JavaFileObject file = new BlackbirdFileManager.BlackbirdInputJavaFileObject("BlackbirdAccess", "package " + myPackage + "; public class BlackbirdAccess { public static final java.lang.invoke.MethodHandles.Lookup LOOKUP = java.lang.invoke.MethodHandles.lookup(); }");
compiler
.getTask(null, fileManager, diagnostics, Arrays.asList("--release", "8"), null, Arrays.asList(file))
.call();
diagnostics.assertSuccess();
final FileObject accessClassFile = fileManager.getFileForInput(StandardLocation.CLASS_PATH, myPackage, "BlackbirdAccess");
final byte[] accessClassBytes = accessClassFile.openInputStream().readAllBytes();
final Class<?> accessClass = lookup.defineClass(accessClassBytes);
return (MethodHandles.Lookup) accessClass.getField("LOOKUP").get(null);
}
}
class BlackbirdDiagnosticListener<S> implements DiagnosticListener<S> {
private static final Set<Diagnostic.Kind> FAILURES;
static {
FAILURES = Collections.unmodifiableSet(
EnumSet.of(Diagnostic.Kind.ERROR, Diagnostic.Kind.WARNING, Diagnostic.Kind.MANDATORY_WARNING));
}
private final List<Diagnostic<? extends S>> failures = new ArrayList<>();
@Override
public void report(final Diagnostic<? extends S> diagnostic) {
if (FAILURES.contains(diagnostic.getKind())) {
failures.add(diagnostic);
}
}
public void assertSuccess() {
if (!failures.isEmpty()) {
throw new RuntimeException("Compilation failed:\n"
+ failures.stream()
.map(Object::toString)
.collect(Collectors.joining("\n")));
}
}
}
class BlackbirdFileManager implements JavaFileManager {
private final StandardJavaFileManager delegate;
private final Map<String, BlackbirdOutputJavaFileObject> createdFiles = new HashMap<>();
private final String myPackage;
public BlackbirdFileManager(final StandardJavaFileManager delegate, final String myPackage) {
this.delegate = delegate;
this.myPackage = myPackage;
}
@Override
public ClassLoader getClassLoader(final Location location) {
return delegate.getClassLoader(location);
}
@Override
public Iterable<JavaFileObject> list(final Location location, final String packageName, final Set<Kind> kinds, final boolean recurse) throws IOException {
final Iterable<JavaFileObject> stdList = delegate.list(location, packageName, kinds, recurse);
if (StandardLocation.CLASS_PATH.equals(location)) {
return () -> new Iterator<JavaFileObject>() {
boolean stdDone;
Iterator<? extends JavaFileObject> iter;
@Override
public boolean hasNext() {
if (iter == null) {
iter = stdList.iterator();
}
if (iter.hasNext()) {
return true;
}
if (stdDone) {
return false;
}
stdDone = true;
iter = createdFiles.values().iterator();
return iter.hasNext();
}
@Override
public JavaFileObject next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return iter.next();
}
};
}
return stdList;
}
@Override
public String inferBinaryName(final Location location, final JavaFileObject file) {
if (file instanceof BlackbirdOutputJavaFileObject) {
return file.getName();
}
return delegate.inferBinaryName(location, file);
}
@Override
public boolean handleOption(final String current, final Iterator<String> remaining) {
return delegate.handleOption(current, remaining);
}
@Override
public boolean hasLocation(final Location location) {
return delegate.hasLocation(location);
}
@Override
public JavaFileObject getJavaFileForInput(final Location location, final String className, final Kind kind) throws IOException {
return delegate.getJavaFileForInput(location, className, kind);
}
@Override
public JavaFileObject getJavaFileForOutput(final Location location, final String className, final Kind kind, final FileObject sibling) throws IOException {
final BlackbirdOutputJavaFileObject out = new BlackbirdOutputJavaFileObject(className, kind);
createdFiles.put(className, out);
return out;
}
@Override
public FileObject getFileForInput(final Location location, final String packageName, final String relativeName) throws IOException {
if (StandardLocation.CLASS_PATH.equals(location) && myPackage.equals(packageName)) {
final BlackbirdOutputJavaFileObject file = createdFiles.get(packageName + "." + relativeName);
if (file != null) {
return file;
}
}
return delegate.getFileForInput(location, packageName, relativeName);
}
@Override
public FileObject getFileForOutput(final Location location, final String packageName, final String relativeName, final FileObject sibling) throws IOException {
throw new UnsupportedOperationException();
}
@Override
public void flush() throws IOException {}
@Override
public void close() throws IOException {}
@Override
public int isSupportedOption(final String option) {
return delegate.isSupportedOption(option);
}
@Override
public boolean isSameFile(final FileObject a, final FileObject b) {
return delegate.isSameFile(a, b);
}
@Override
public Location getLocationForModule(final Location location, final String moduleName) throws IOException {
return delegate.getLocationForModule(location, moduleName);
}
@Override
public Location getLocationForModule(final Location location, final JavaFileObject fo) throws IOException {
return delegate.getLocationForModule(location, fo);
}
@Override
public String inferModuleName(final Location location) throws IOException {
return delegate.inferModuleName(location);
}
@Override
public Iterable<Set<Location>> listLocationsForModules(final Location location) throws IOException {
return delegate.listLocationsForModules(location);
}
@Override
public boolean contains(final Location location, final FileObject file) throws IOException {
return delegate.contains(location, file);
}
public static class BlackbirdJavaFileObject extends SimpleJavaFileObject {
protected BlackbirdJavaFileObject(final String name, final Kind kind) {
super(URI.create("string:///" + name.replace('.', '/')
+ kind.extension), kind);
}
}
public static class BlackbirdInputJavaFileObject extends BlackbirdJavaFileObject {
private final String contents;
protected BlackbirdInputJavaFileObject(final String name, final String contents) {
super(name, Kind.SOURCE);
this.contents = contents;
}
@Override
public CharSequence getCharContent(final boolean ignoreEncodingErrors) throws IOException {
return contents;
}
}
public static class BlackbirdOutputJavaFileObject extends BlackbirdJavaFileObject {
private final String name;
private ByteArrayOutputStream baos = new ByteArrayOutputStream();
private byte[] bytes;
protected BlackbirdOutputJavaFileObject(final String name, final Kind kind) {
super(name, kind);
this.name = name;
}
@Override
public String getName() {
return name;
}
public byte[] getBytes() {
if (bytes == null) {
bytes = baos.toByteArray();
baos = null;
}
return bytes;
}
@Override
public OutputStream openOutputStream() throws IOException {
return baos;
}
@Override
public InputStream openInputStream() throws IOException {
return new ByteArrayInputStream(getBytes());
}
}
}